|
|
|
|
|
|
|
|
|
|
|
Designed with |
|
|
|
Best viewed with |
|
|
Contact: |
author |
|
|
|
|
|
|
|
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
|
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).
|
|
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”).
|
|
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), ...
|
|
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;
}
|
|
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 {};
|
shape is rect (LFMat::Rect) in the general case. If the matrix is necessariliy square, use LFMat::Square
|
|
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.
|
|
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.
|
|
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 {};
|
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.
|
|
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 {};
|
square is type Square if the matrix is
square -- which is often the case with a diagonal matrix. Else, one must choose Rect.
|
|
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.
|
|
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 {};
|
square is type Square if the matrix is
square -- which is often the case with a symmetric matrix. Else, one must choose Rect.
|
|
Stored_part is used to determine if data must be stored using the
lower (Lower) or the upper (Upper) part.
|
|
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.
|
|
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 {};
|
square is type Square if the matrix is
square -- which is often the case with a asymmetric matrix. Else, one must choose Rect.
|
|
Stored_part is used to determine if data must be stored using the
lower (Lower) or the upper (Upper) part.
|
|
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.
|
|
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 {};
|
square is type Square if the matrix is
square -- which is often the case with a hermitian matrix. Else, one must choose Rect.
|
|
Stored_part is used to determine if data must be stored using the
lower (Lower) or the upper (Upper) part.
|
|
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.
|
|
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 {};
|
Stored_part is used to determine where data are. It can be either
in the lower part (Lower) or in the upper one (Upper).
|
|
square is type Square if the matrix is
square -- which is often the case with a triangular matrix. Else, one must choose Rect.
|
|
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.
|
|
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 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 {};
|
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.
|
|
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
|
|
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 {};
|
Allocation can be either
Internal,
Internal_new, or
External (see Dense storage style).
WarningData 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();
|
|
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 {};
|
Allocation can be either
Internal,
Internal_new, or
External (see Dense storage style).
WarningData 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;
}
}
|
|
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 {};
|
If compile_time_band_size is not equal to 0, the band size is fixed
during the compilation, allowing further optimizations.
|
|
Allocation can be either
Internal,
Internal_new, or
External (see Dense storage style).
|
|
Alignment must be a class which specialize the
Alignment one (see Dense storage style).
|
|