Walk-through introduction to the Matrix class (memory) usage
This
section is intended to set you up and working quickly with some notions you may
want to have right, especially about memory management, before actually using the Matrix
class.
First, as for any library, you have to include the corresponding header file. This
is done by the directive:
#include <CheapMatrix.h>
You also have to specify you are using symbols from a namespace:
using namespace CheapMatrix;
Let's now start the program and do something very basic.
main()
{
Matrix a(4,4);
There, here we are! a is a 4 by 4 matrix, created in memory. Nothing terrific.
a = 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
This example uses an initialization list, to set a to something. You probably won't
use it for huge matrices, and in fact hopefully you don't have to.
a *= 2;
cout << a(3,2) << endl;
This is a multiplication by a scalar, followed by printing the element of a at line
3, column 2, which would be 20 in this example. Let's now do something more useful.
Vector v(4);
v = 0.1, -0.1, 0.1, -0.1;
Don't be confused by the initialisation list here. All the vectors in the package are
for all purposes column vectors. If you want a row vector, just transpose it!
Vector b = a * v;
This is getting more interesting. The multiplication here is a Matrix/Matrix multiplication,
but since all vectors are also matrices, it works on vectors as well (and so do all the other
functions). The assignment is in fact a shallow copy, of the result of the operation.
You have to remember: unless explicitely stated, all copies are shallow copies.
This has two consequences: Assignments are quite fast, but it also means that code like:
Matrix sub = a(2, 3, 2, 4);
actually refer to the same data. Namely, in this case, the submatrix of a from row 2
to row 3 and column 2 to column 4. In Octave, you would write it sub = a(2:3, 2:4).
The difference is that here:
sub(1, 1) = 4.2;
will also modify a(2, 2). If you don't want that, you can call the detach
function, that precisely detaches a matrix into its own memory buffer.
sub.detach();
In practice, this is seldom done for the simple reason that you can achieve a similar effect
by copying explicitely:
Matrix sub2;
sub2 = a(2, 3, 2, 4).copy();
Or another form, for matrices that are already created and the right dimension:
sub.copy( a(2, 3, 2, 4) );
You have noticed that I do it in two lines in the first form. This is purely for
demonstration purpose, and doing it in one go would work equally well. In fact, I just
wanted to show the possibility of creating empty matrices. Those objects are considered
null matrices with 0 lines and 0 columns, and are really useful when you don't know the
dimensions a matrix will have in advance (like when loading from a file). There is
a corresponding boolean test:
if (a) {
cout << "a is a not a null matrix." << endl;
}
Another interesting aspect of the memory management is the ability to use swap files
transparently.
Matrix m(10000, 200, 4000, "b.swapfile");
will create a matrix of 10000 rows and 200 columns, but keep only 4000 elements in memory
(roughly, for practical reasons this is increased to align the memory to the next system page
limit). The file m.swapfile will be created if it doesn't exist. It will be padded or
truncated if it is not the correct dimension, but otherwise you can reuse it as a binary
cache between sessions. There is also a fifth argument, a boolean you can set to true to
delete the file as soon as the data isn't used anymore. Of course, m is a perfectly
valid matrix once it has been created successully. And if you want, you can even detach
an already existing matrix into a file, and back in memory. See also the functions hold
and release.
Another thing you may want to know: All matrices are created column-major.
It means that a(1,1) will be followed by a(1,2) like in Fortran and not
a(2,1) as it would be for a C array. The reason for this is to make it easy to use
Fortran code, like LAPACK. It doesn't really matter in practice, except that you may
find it significantly faster to loop through a Matrix column by column rather than line by
line, in the case it is mapped to a file with a small buffer, since this order will
minimize buffer reloads.
}
I now recommend you to have a look in the example directory. It contains, well,
other examples... You'll find there more
on the coordinate transformations, like transposing and reshaping. You'll also find
practical use for some utilities, like sum, randn,... that are declared in the header
file CM_util.h, together with a presentation of the mapping facilities. Now that you
have the memory management concepts right, those examples will just show you
various features, which are out of scope here.
In particular, you might find the optimize.cpp example quite informative if you
want to be up and running immediately.
The next section highlights a few C++ aspects you probably know already, and their
application when using this Matrix class. You can skip it safely if you just intend to write a
few batch scripts, but you may find useful to read it when writing a complete program.
Nicolas Brodu, 2000-06-17