belog elog
About
Benchmarks
News
Tutorial
Download
Links
Designed with
quanta
gimp
Best viewed with
konqueror
Contact:
author

Hello world

This is a simple hello world program:

#include "lfmat/matrix.h"

int main() {
  LFMat::Matrix<const char *> mat(3,3);

  mat.fill(".....");
  mat(0,0)="hello";
  mat(1,1)="world";
  mat(2,2)="!!!!!";

  std::cout << mat << std::endl;
}
This produces the following output:
     hello      .....      .....
     .....      world      .....
     .....      .....      !!!!!
It does not need particular linkage parameter since all data are in the headers in this case. liblfmat.a is needed only for some double and float operations. In fact, the matrix class is declared as following:
template<
  class T=float,
  class Structure=Generic<>,
  class Storage_style=Dense<>,
  class Orientation=Row_oriented>
class Matrix {};

Template parameters

1 The kind of data which is stored in each element of the matrix. It can be float, double, complex<cln::...>, banana, ... For solvers or operators on matrix and vectors, T must support +, -, *, / and sqrt(), depending on procedures. Let's note that there's a parameter in storage style definition classes which allow to specify if allocation must be done using the new operator or the malloc() procedure (faster than new).
2 The structure represents the properties of the matrix. Is it square, symmetric, triangular, diagonal ? Furthermore, the structure parameter allow to specify if the matrix is of fixed size (see the section called “Structure”).
3 The storage style represents how data is stored (as one can guess...). There's several possibilities: it can be dense, sparse, sky line, band, ... The storage style influence considerably the speed of procedures like solve(mat,vec) (often faster with sky line if matrices are not full) or multiply(mat,vec) (often faster with a sparse matrix), ...
4 For now, matrices can be row_oriented, col_oriented, or diag_oriented. Differences are shown in
It means as example that if one wants a triangular float matrix, row_oriented and with a sky_line storage style, it could be declared as:
Matrix<float,Triangular<>,Sky_line<> > mat;

It is recommended to look on examples in the ./src directory in order to get a better idea on how to use some particular storage...

Here is one another simple example :

#include "lfmat/matrix.h"
#include "lfmat/solve_skyline.h"
#include <iostream>

using namespace std;
using namespace LFMat;

void my_example(unsigned size) {
  cout << "example_solve_simple" << endl;
  
  Matrix<float,Symmetric<>,Sky_line<> > mat(size);
  Vector<float> vec(size);

  for(int i=0;i<size;++i)
    mat.data.set_bounds( i, max(0,i-5) );
  
  mat.set_values(0.1);
  vec.set_values(1.0);
  for(int i=0;i<size;++i)
    mat(i,i) = size;

  cout << mat << endl;    
  solve(mat,vec);
  cout << vec << endl;
}

Structure

For now structures can be defined using one of the following class:

template<
  class shape=Rect,
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0,
  unsigned compile_time_nb_columns=0>
struct Generic {};
1 shape is rect (LFMat::Rect) in the general case. If the matrix is necessariliy square, use LFMat::Square
1 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
2 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary). 0 means that this the number of rows is dynamically set.
3 Same thing. Let's note that if the number of rows are fixed, the number of columns must also be fixed, too.

template<
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0>
struct Generic_square {};
1 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
2 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary).

template<
  class square=Square,
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0>
struct Diagonal {};
1 square is type Square if the matrix is square -- which is often the case with a diagonal matrix. Else, one must choose Rect.
2 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
3 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary).

template<
  class square=Square,
  class stored_part=Lower,
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0>
struct Symmetric {};
1 square is type Square if the matrix is square -- which is often the case with a symmetric matrix. Else, one must choose Rect.
2 Stored_part is used to determine if data must be stored using the lower (Lower) or the upper (Upper) part.
3 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
4 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary).

template<
  class square=Square,
  class stored_part=Lower,
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0>
struct Asymmetric {};
1 square is type Square if the matrix is square -- which is often the case with a asymmetric matrix. Else, one must choose Rect.
2 Stored_part is used to determine if data must be stored using the lower (Lower) or the upper (Upper) part.
3 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
4 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary).

template<
  class square=Square,
  class stored_part=Lower,
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0>
struct Hermitian {};
1 square is type Square if the matrix is square -- which is often the case with a hermitian matrix. Else, one must choose Rect.
2 Stored_part is used to determine if data must be stored using the lower (Lower) or the upper (Upper) part.
3 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
4 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary).

template<
  class stored_part=Lower,
  class square=Square,
  class size_type=unsigned,
  unsigned compile_time_nb_rows=0>
struct Triangular {};
1 Stored_part is used to determine where data are. It can be either in the lower part (Lower) or in the upper one (Upper).
2 square is type Square if the matrix is square -- which is often the case with a triangular matrix. Else, one must choose Rect.
3 size_type is used for indexes. It must be able to represent the total number of elements stored in the matrix -- n*m for a dense rectangular matrix. It is advised to take an unsigned type since it often leads to faster code -- the operator % is a great example.
4 The number of rows and columns can be fixed during the compilation. If these values are known and fixed, it can lead to faster code -- loops can be totally unrolled, leading to less register pressure, and if storage is internal, allocation is as fast as allocating the room for a base type in the stack (no malloc() nor new are necessary).

Storage styles

Storage style represents the way data are stored. It allows to define more information in the matrix like position of non-null elements... For now storage styles can be defined using one of the following class:

template<
  class Allocation=Internal,
  class Alignement=SIMD_Alignement,
  class Compression=Compressed>
struct Dense {};
1 Allocation can be either Internal, Internal_new, or External. Internal makes use of malloc() to allocate data and Internal_new make use of new, which is slower but mandatory for some kind of data (when constructors must be called). External let the user give pointers on data. The later allows to make references on matrices and vectors -- which if facilitated using method reference(...) of classes LFMat::Matrix and LFMat::Vector.
2 Alignment must be a class which specialize the Alignment one. SIMD_Alignment is defined as follow:
struct SIMD_Alignment {};
template<class T> struct Alignment<SIMD_Alignment,T> { static const int val = 1; };
///
#if HAS_SSE==1
struct Alignment<SIMD_Alignment,double> { static const int val = 2; };
struct Alignment<SIMD_Alignment,float> { static const int val = 4; };
struct Alignment<SIMD_Alignment,int> { static const int val = 4; };
#else
#if HAS_MMX==1
struct Alignment<SIMD_Alignment,float> { static const int val = 2; };
struct Alignment<SIMD_Alignment,int> { static const int val = 2; };
#endif
#endif
3 Compression equals Compressed if zeroes of structure have not to be stored. Else, it must be Uncompressed. In the first case, a symmetric matrix occupies n*(n+1)/2 values; in the second, it takes n*n values.

template<
  class Allocation=Internal,
  class Alignement=SIMD_Alignement>
struct Sky_line {};
1 Allocation can be either Internal, Internal_new, or External (see Dense storage style).

Warning

Data are not allocated after resize(): user must call mat.data.reserve() after having set the bounds. To allocate space for a sky line row oriented symmetric matrix, the procedure is the following:
  Matrix<...,...,Sky_line<...> > mat;
  mat.resize(nb_rows);
  for(size_type line=0;line<nb_rows;++line)
    mat.data.set_bounds( line, beginning_index[line] );
  mat.data.reserve();
2 Alignment must be a class which specialize the Alignment one (see Dense storage style).

template<
  class Allocation=Internal,
  class Alignment=SIMD_Alignment>
struct Sparse_line {};
1 Allocation can be either Internal, Internal_new, or External (see Dense storage style).

Warning

Data are not allocated after resize(): user must call mat.data.reserve(size_type nb_values) before filling the matrix. To allocate and fill a row oriented sparse line matrix, the procedure can be:
  Matrix<...,...,Sparse_line<...> > mat;
  mat.resize(nb_rows);
  mat.data.reserve(nb_values);
  size_type ind=0;
  for(size_type line=0;line<nb_rows;++line) {
    mat.data.row_ind[i]=ind;
    for(...;...;...,++ind) { // for each value in $line
      mat.data.ind[ind]=ind;
      mat.data.values[ind]=val;
    }
  }
2 Alignment must be a class which specialize the Alignment one (see Dense storage style).

template<
  unsigned compile_time_band_size=0,
  class Allocation=Internal,
  class Alignment=SIMD_Alignment>
struct Band {};
3 If compile_time_band_size is not equal to 0, the band size is fixed during the compilation, allowing further optimizations.
2 Allocation can be either Internal, Internal_new, or External (see Dense storage style).
3 Alignment must be a class which specialize the Alignment one (see Dense storage style).