LightMat  - USER  MANUAL

by Anders Gertz and Vadim  Engelson
 

What is LightMat

LightMat is a collection of C++ classes that provide storage in memory, access and mathematical operations with arrays.
It supports arrays of rank 1,2,3 and 4. Every array is stored in a separate object and you can define it and manipulate with it by corresponding C++ methods.
To use LightMat you need just to write the lines
  #define LM_ALL
  #include "lightmat.h"
at the start of your code and add some object files when linking the application.
If you are interested in more details, read the Implementation notes and Optimization(on a separate page).

Some options are not implemented yet, but they will be available in the next versions.



Installation notes and examples

Available platforms and compilers

Compilers Preprocessor definitions set the compiler and used in code
WorkShop Compilers 4.2 C++ 4.2 (Sun / Solaris 5.6) __SUNPRO_CC
SC4.0 / C++ 4.1 (Sun / Solaris 5.5.1) __SUNPRO_CC
GNU C++ V.2.6.3(Sun / Solaris 5.5.1, 5.6) __GNUG__
GNU C++ 2.7.2.1 (Sun / Solaris 5.5.1, 5.6, Linux 2.0.30) __GNUG__
GNU C++ 2.7.2 (Digital UNIX V3.2) __GNUG__
DEC C++ V1.3B-0 DEC OSF/1 (Alpha) __alpha
MicroSoft Visual C++ 4.0 _WIN32
MicroSoft Visual C++ 5.0 _WIN32
If you use one of these compilers there should be no problems in compilation. Otherwise there is a chance that some small modifications should be introduced in the code. Please, let us know about your modifications.

Download and compilation

You download the library from the LightMat home page at
http://www.ida.liu.se/~pelab/lightmat.
You receive: After un-zipping you can go to the "src" subdirectory.
Compilation can take some 4-5 minutes on a 125MHz processor.
You must have 70MB free on the Windows disk where your swap file is located (usually in C:\WINDOWS) .
The code will not work on 8.3 file systems like Windows 3.1 (i.e. it uses long file names).
The as result of compilation all necessary object files are created.

Testing

It is important to test the library. Then several executable files are created. It can take some 4-5 minutes on a 125MHz processor.
The make automatically runs the test.
If there were no assertion violations nor core dumps, then, presumably you can use the code further, writing from scratch or modifying the test.

How to use the library

The tests show two different ways of using the library : using inlined functions and using non-inlined functions.
The execution is identical, but compilation in different.

Using inlined functions


You specify
#define LM_ALL
#include <lightmat.h>
in your files
You compile your files with usual compilation flags . Compilation usually takes several minutes.
You link your object files with cstring.o and extwin.o
Example: file foo.cc contains
          #define LM_ALL
          #include <lightmat.h>
          doubleNNN f(5,6,7,3.11);
          cout << f; 
Compile with (assuming that LightMat is installed in /home/john/lightmat099)
           g++ foo.cc -I/home/john/lightmat099/include         \
                       /home/john/lightmat099/obj/cstring.o    \
                       /home/john/lightmat099/obj/extwin.o

Using non-inlined functions


You specify
#define LM_ALL
#include <lightmat.h>
in your files
You compile your files with usual flags and -DNOT_INLINED
You link your files with cstring.o , extwin.o , not_inlined.o

All the library functions are non inlined.
This gives reasonable, but not the highest performance.
Every time you compile your file, it will take less than 1 minute.
Example: file foo.cc contains
          #define LM_ALL
          #include <lightmat.h>
          doubleNNN f(5,6,7,3.11);
          cout << f; 
Compile with
          g++ foo.cc -I/home/john/lightmat099/include -DNOT_INLINED  \
                       /home/john/lightmat099/obj/cstring.o          \
                       /home/john/lightmat099/obj/extwin.o           \
                       /home/john/lightmat099/obj/not_inlined.o

Reducing compilation time for non-inlined functions.

If you use only part of LightMat classes you can reduce compilation time for your code.
Use preprocessor definition #define LM_name for every class you use.
Names of these definitions is given in the
table of classes.
Important: This option can be usec only with flag -DNOT_INLINED.
#define LM_ALL means that you use all available classes.
Example: file foo1.cc contains
         #define LM_N
         #include <lightmat.h>
         doubleN xx(5,2.0);
         // doubleNN yy(5,6);  - Forbidden
         // This is not allowed because this class requires LM_NN.
         // Compiler reports an error.
Compile with
          g++ foo1.cc -I/home/john/lightmat099/include -DNOT_INLINED  \
                       /home/john/lightmat099/obj/cstring.o          \
                       /home/john/lightmat099/obj/extwin.o           \
                       /home/john/lightmat099/obj/not_inlined.o

Compiling several files into one executable.

If you compile several files into a single executable, they either

Preprocessor definitions

In the file lightmat.h (or lightmat_id.h) there are a number of preprocessor definitions that can be changed. Those definitions are:

Preprocessor definition
Default value
Meaning if defined
Meaning if undefined
LIGHTMAT_LIMITS_CHECKING defined LightMat does limits checking during calculations. I.e. LightMat will abort computations and dump core if an index is out of bounds. Computation speed is reduced by these checks. An operation with bad index can cause unpredictable results.
LIGHTMAT_INIT_ZERO undefined LightMat will initialize all array elements to zero when constructing a new object. Computation speed is reduced.
Array elements initially may contain any value. Care should be taken if non-initialized values are used in some other interfaces, like MathLink.
LIGHTMAT_DONT_USE_BLAS defined LightMat uses its own function for double array multiplication LightMat calls BLAS libraryroutines. The library should be linked with the application.
(N/A)
LIGHTMAT_OUTPUT_FUNCS defined The ToStr (conversion to Tools.h++ class RWCString) functions are defined and operator<< functions are defined for all arrays. (N/A)
LIGHTMAT_TEMPLATE undefined LightMat classes can be used as templates. This option is not available due to difficulties with various compilers.(N/A) LightMat classes are used as traditional C++ classes.

If preprocessor definitions change, all the files should be recompiled.


The Different Classes

Definitions

We will need some definitions first: There exist 10 different classes for vectors, matrices, tensors of rank 3 and tensors of rank 4.
There are 4 universal classes and 6 specially optimized classes. The specially optimized classes can be used in particular application areas: for instance matrix 3x3 is used in geometry and 4x4 in computer graphics. If you are not sure which classes to use - use universal classes.

CLASS TABLE

Dimension
suffix
Preprocessor
symbol
for
inclusion
Use Default stack allocation size Class names (long form ) Class names
(short form)
Explanations
3 LM_3 special 3 light3double
light3int
double3
int3
Vector of 3 elements
4 LM_4 special 4 light4double
light4int
double4
int4
Vector of 4 elements
N LM_N universal 10 lightNdouble
lightNint
doubleN
intN
Vector of arbitrary size
33 LM_33 special 3 x 3 light33double
light33int
double33
int33
Matrix 3 by 3 elements
44 LM_44 special 4 x 4 light44double
light44int
double44
int44
Matrix 4 by 4 elements
N3 LM_N3 special 10x 3 lightN3double
lightN3int
doubleN3
intN3
Matrix N by 3 elements, i.e. storage for N vectors if 3 elements
NN LM_NN universal 10 x 10 lightNNdouble
lightNNint
doubleNN
intNN
Matrix of arbitrary size
N33 LM_N33 special 10 x 3 x 3 lightN33double
lightN33int
doubleN33
intN33
Tensor N by 3 by 3, i.e. storage for N matrixes of 3 by 3 elements
NNN LM_NNN universal 5 x 5 x 5 lightNNNdouble
lightNNNint
doubleNNN
intNNN
Tensor of arbitrary size
NNNN LM_NNNN universal 4 x 4 x 4 x 4 lightNNNNdouble
lightNNNNint
doubleNNNN
intNNNN
Quansor of arbitrary size

Note on higher ranks: There are no classes for tensors of higher ranks larger than 4. It is possible to add support for that in the future though. Having different classes for vectors, matrices etc is quite natural, and that's what most other packages also have chosen. Other packages sometimes have one class for tensors of high ranks, but that does not exist in LightMat. Having different classes for the different ranks also avoids run-time choices of what algorithm, for the different ranks, that should be used.

Class names

The classes for elements of type int and double are implemented.
The user specifies a variable
double4 foo
This means that foo is a vector with 4 elements of type double.
There are typedef definitions that allow using another, longer notation for class names, for instance light4double instead of double4. The examples below are given in the short form.
All available names are given in the
table.
The properties for classes with elements of type int and double are very similar.
Further, for instance, by classes with siffix 3 usually meant both classes int3 and double3.

Specially optimized classes for certain sizes

Extra fast classes (suffixes 3, 4, 33, 44) have been made for some specific sizes of vectors and matrices. Those are vectors of length 3 and 4, and matrices of size 3x3 and 4x4. They have, of course, a static memory-area for the elements. That means that the the compiler can make code which need a less number of references through pointers. Code for calculations is written especially suited to those sizes. Calculation loops are completely unrolled. This makes these classes extra fast.

These classes, though, can also interface with the other existing classes in lightmat.

There are also classes for matrices and tensors with partially fixed sizes (intN3, doubleN3 and intN33, doubleN33). The usefulness of these classes will be described in Indexing. One is for matrices with three columns and the other one is for tensors of rank 3 which have the size of the two last indices set to three. Internally the matrix-class consist of a number of vectors of length 3 (int3, double3) and the tensor-class consist of a number of 3x3 matrices (int33, double33).

Since the sizes of objects of these classes are partially or totally fixed they cannot dynamically change their sizes as freely as the the universal LightMat classes. Only classes with suffixes N3 andN33 can change the first dimension during calculations.



Constructors and access to arrays

All LightMat classes have some basic member functions like constructors, destructors, assignment operators and a few more.

Constructors

There exist a number of constructors, with different arguments, with which a new object can be created. The reasons for having a number of different constructors are efficiency and ease of use.

Element initialization

If values for the elements are not supplied in the constructor, the behaviour depends on preprocessor definition LIGHTMAT_INIT_ZERO. (See preprocessor definition table)If it is defined, the elements initialized to zero.
Otherwise, the elements are not initialized. This saves some time. It can be a good idea to not define LIGHTMAT_INIT_ZERO if the user knows that objects never are used without explicit initialization. Functions within the classes that do calculations and need a local object use a special internal protected constructor that never initializes the elements.

Memory allocation

The constructors automatically allocate dynamic memory in the heap if the static memory area (area allocated by default in the stack) isn't sufficiently large. The class table describes the size allocated by default on the stack.

Destructors

The destructors free any memory which may have been allocated.

Assignment

The assignment operators copy the values from the elements in one object to another.
The objects can be of different size. But the rank must be the same or the right-hand operand can be scalar.
The size of the left-hand operand is changed to the size of the right-hand operand if needed. More memory is also allocated if needed.
Note: Memory is not deallocated even if the objects need of memory decreases. The reason is that deallocation and the sometimes following allocation of a smaller memory area takes time. There may also, later in the calculations, once again be a need of a larger memory area in which case another reallocation would be needed once again.
         doubleNN a(2,3), b(3,2);
         a = b;          // a becomes 3x2
         a = 5.7;        // all elements of a become 5.7
Assignment from a scalar will set all the elements in the object to that value.
Read section about indexing if you want to assign a value to an element of array.

Set() and Get()

All classes have member functions named Get and Set. The argument to those functions are a pointer to a memory area. Set will set the elements in the object from the values which are stored in the memory area. Get does the opposite. The elements are stored in row major order (C++ style) in the memory area.
    
    doubleNN a(5,6,17.7),  b(5,6);
    double arr[30];
    a.Get(arr);  // a  -->  arr
    b.Set(arr);  // arr  -->  b

No bounds check is performed.

dimension()

The size of an object can be found with the function dimension(). It takes an integer argument which should specify the dimension. The argument should, for example, be 1 for the number of rows in a matrix, and 2 for the number of columns.

Equality and inequality

The usual operators for equality and inequality exist for all classes. Two objects are considered equal if they have the same size and if the values of all corresponding elements are equal. Only arrays of the same rank can be compared.
if(a == b ?? c != d) { /* something */ }

Promoting to higher dimensions

A function named reshape is used to promote an object to another object with higher dimensions. E.g. a vector can be promoted to a matrix and all the columns in the matrix will then be given the values of the vector.
    doubleN v(3);
    doubleNN a;
    a.reshape(3,5,v);  // a becomes 3x5

Constructing arrays in a function call

Function make_lightN() is not a method of a class, but a friend function. You can use it for construction of an array from its components. Given several arrays of rank n it constructs an array of rank n+1. Number of components should be specified as the 1st argument. Obviously, the dimensions and element types of all arrays should be the same.
Not more than 10 parameters can be given in the argument list.
    lightNN a;
    a=make_lightN(2, make_lightN( 3, 11,12,13),
    make_lightN( 3, 14,15,16));

This code creates array 2x3 of integers from 11 to 16. Specification is always given in row-major order.
Note: This function is very time and space consuming. Therefore use Set() if you are interested in higher performance.


Indexing

The indexes in LightMat are 1-based, like in Fortran, not 0-based like in C.
For matrices the first coordinate is usually named "row", the second - "column".
Bound check in indexing operations can be turned on or off by
preprocessor definitions.
There is indexing for individual elements , indexing for array extraction, and special access functions.

Indexing for individual elements

Individual elements can be indexed with the parentheses operator. The indexed element can be either an l-value or an r-value. The number of indexes should be equal to the rank of the array.
   doubleNN a(5,3);
   a(1,1) = a(2,3);

Indexing for array extraction

Arrays can be extracted from arrays of higher dimensions.
The indexing is limited to supplying values for a number of indices in the beginning, the last indices is not supplied. E.g. it is possible to index a whole row in a matrix but not a column. The result can only be used as an r-value, and the indexed elements are copied in the process. The elements need to be copied since the concept of different views of the same data does not exist in this design. Note that this is relatively expensive operation.
If the result is used as a l-value, the assignment result is simply discarded.
The exception to this rule is the classes which have partially fixed sizes. E.g. the class for Nx3 matrices (intN3, doubleN3) contains a number of vectors of size 3 and a reference to one of those can be returned by the operator. There is no need to copy data and the returned reference can also be used as an l-value. The same applies to the classes int33, doubleN33.
      doubleN v;
      doubleNN a(5,5);
      doubleNNN  c(7,8,9);
      v = a(4);
      v=c(4,5);
      a= c(3);
      a(1) = v; //  The result is discarded, and matrix a does not change.
      a.SetRow(1,v) ;// This is the correct way. See "access functions" below.
      double3 w;
      doubleN3 m(5);
      m(3) = w + m(2); // This class is exception, m changes.

Access functions

Several access functions have been designed for specific universal classes. The functions are identical for classes with double elements and classes with int elements. Many other access functions will be added in next versions.

Setting shape - SetShape()

This function changes the shape of array. (Available for
universal classes only)
If necessary, new memory is allocated. The elements are not initialized. They can be accessed by indexing operation.
    doubleNN a;      // Allocates 10x10, has shape 0x0
    doubleNN b(5,6); // Allocates 10x10, has shape 5x6
    a.SetShape(20,40); // Allocates 20x40, has shape 20x40
    b(70,30)=1;        // Causes error
    b.SetShape(70,30); // Allocates 70x30, has shape 70x43
    b(70,30)=1;        // OK

Internal data - data()

This function returns the address of internal presentation of the array. (Available for
universal classes only)
Internally the arrays are stored in column major order. This order is used for interfacing with Fortran routines.

Output functions

All arrays can be printed out to an output stream by usual << operator:
If NOPAR
preprocessor definition is defined, no parentheses are used in prinpouts.
       doubleNN a;
       cout << a;
May produce:
      ( 5.0   7.0
        2.6   7.2 )



Calculations in lightmat

A collection of operators and functions is defined for every class in the LightMat library. The operators and function names are overloaded and perform relevant operations on various arguments. All the operators and functions are available as friend functions. Therefore the notation is similar for scalar arguments and for arrays of different ranks.

Operators available for all classes are:

Unary operators:

Elementwise binary operators.

One of arguments can be scalar. If both are arrays they must be of the same size.

Inner product and multiplication with scalar.

x*y All the possible variants are given in the table (s is a scalar; other symbols are suffixes of class names) :
Arg 1 Arg 2 Result
s s s
3 s 3
s 3 3
3 3 s
33 3 3
3 33 3
3 NN N
4 s 4
s 4 4
4 4 s
4 44 4
44 4 4
4 NN N
s N N
N s N
N N s
NN N N
Arg 1 Arg 2 Result
NN 3 N
NN 4 N
N3 3 N
N3 N N
N3 N3 N3
N3 s N3
s N3 N3
N3 33 N3
44 N3 N3
NN N3 N3
NN s NN
s NN NN
NN NN NN
NN 33 NN
NN 44 NN
N3 NN NN
33 NN NN
Arg 1 Arg 2 Result
44 NN NN
N NNN NN
NNN N NN
3 NNN NN
NNN 3 NN
4 NNN NN
NNN 4 NN
33 s 33
s 33 33
33 33 33
44 s 44
s 44 44
44 44 44
NNN s NNN
s NNN NNN
NN NNN NNN
NNN NN NNN
Arg1 Arg2 Result
NNNN N NNN
N NNNN NNN
NNN 33 NNN
33 NNN NNN
NNNN 3 NNN
3 NNNN NNN
NNN 44 NNN
44 NNN NNN
NNNN 4 NNN
4 NNN N NNN
s NNNN NNNN
NNNN s NNNN
NNNN NN NNNN
NN NNNN NNNN
NNNN 33 NNNN
33 NNNN NNNN
NNNN 44 NNNN
44 NNNN NNNN
NNN NNN NNNN

Operation with assignment.

If y is array, it must have the same dimensions as x. Result has the same dimension as array x.

Division.

Result has the same dimension as array.

Division and multiplication as operations performed only element-wise.

The x and y are arrays. Result has the same dimension.

Other operations with arrays

Mathematical functions, applied element-wise:

Note: (Version 0.76) FractionalPart, IntegerPart, Mod, LightMax, LightMin - implemented for universal types only


Vadim Engelson
Last modified: Tue Feb 24 11:52:28 MET 1998