Back Table of content Next

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.


Back Table of content Next

Nicolas Brodu, 2000-06-17