Matrices
|
Matrices are instances of the class Matrix
. Class Matrix
is a template class: Matrix<T, Prop, Storage, Allocator>
. As for vectors, T
is the type of the elements to be stored (e.g. double
).
Prop
indicates that the matrix has given properties (symmetric, hermitian, positive definite or whatever). This template parameter is rarely used by Seldon; so the user may define its own properties. Thanks to this template parameter, the user may overload some functions based on the properties of the matrix. Seldon defines three properties: General
(default), Symmetric
and Hermitian
.
Storage
defines how the matrix is stored. Matrices may be stored in several ways:
ColMajor
: dense matrix stored by columns (as in Fortran);
RowMajor
: dense matrix stored by rows (recommended in C++);
ColUpTriang
: upper triangular matrix stored in a dense matrix and by columns;
ColLoTriang
: lower triangular matrix stored in a dense matrix and by columns;
RowUpTriang
: upper triangular matrix stored in a dense matrix and by rows;
RowLoTriang
: lower triangular matrix stored in a dense matrix and by rows;
ColUpTriangPacked
: upper triangular matrix stored in packed form (Blas format) and by columns;
ColLoTriangPacked
: lower triangular matrix stored in packed form (Blas format) and by columns;
RowUpTriangPacked
: upper triangular matrix stored in packed form (Blas format) and by rows;
RowLoTriangPacked
: lower triangular matrix stored in packed form (Blas format) and by rows;
ColSym
: symmetric matrix stored in a dense matrix and by columns;
RowSym
: symmetric matrix stored in a dense matrix and by rows;
ColSymPacked
: symmetric matrix stored in packed form (Blas format) and by columns;
RowSymPacked
: symmetric matrix stored in packed form (Blas format) and by rows;
ColHerm
: hermitian matrix stored in a dense matrix and by columns;
RowHerm
: hermitian matrix stored in a dense matrix and by rows;
ColHermPacked
: hermitian matrix stored in packed form (Blas format) and by columns;
RowHermPacked
: hermitian matrix stored in packed form (Blas format) and by rows;
ColSparse
: sparse matrix (Harwell-Boeing form) stored by columns;
RowSparse
: sparse matrix (Harwell-Boeing form) stored by rows;
ColSymSparse
: symmetric sparse matrix (Harwell-Boeing form, storing only the upper part) stored by columns;
RowSymSparse
: symmetric sparse matrix (Harwell-Boeing form, storing only the upper part) stored by rows;
ArrayRowSparse
: sparse matrix, each row being stored as a sparse vector.
ArrayColSparse
: sparse matrix, each column being stored as a sparse vector.
ArrayRowSymSparse
: symmetric sparse matrix, each row being stored as a sparse vector. Only upper part of the matrix is stored.
ArrayColSymSparse
: symmetric sparse matrix, each column being stored as a sparse vector. Only upper part of the matrix is stored.
BandedCol
: banded matrix stored by columns as prescribed in Lapack documentation
ArrowCol
: banded matrix stored by columns with additional rows and columns at the end
RowMajor
is the default storage.
Finally, Allocator
defines the way memory is managed. It is close to STL allocators. See the section "Allocators" for further details. Additional storages dedicated to complex matrices are available when including SeldonComplexMatrix.hxx :
ColComplexSparse
: complex sparse matrix (Harwell-Boeing form) stored by columns and for which the real part and the imaginary part are split into two sparse matrices;
RowComplexSparse
: complex sparse matrix (Harwell-Boeing form) stored by rows and for which the real part and the imaginary part are split into two sparse matrices;
ColSymComplexSparse
: symmetric sparse matrix (Harwell-Boeing form, storing only the upper part) stored by columns and for which the real part and the imaginary part are split into two sparse matrices;
RowSymComplexSparse
: symmetric sparse matrix (Harwell-Boeing form, storing only the upper part) stored by rows and for which the real part and the imaginary part are split into two sparse matrices.
ArrayRowComplexSparse
: sparse matrix, each row being stored as a sparse vector. Real part and imaginary part are stored separately.
ArrayRowSymComplexSparse
: symmetric sparse matrix, each row being stored as a sparse vector. Real part and imaginary part are stored separately. There is a default Allocator
(see the section "Allocators"), a default Storage
(RowMajor
) and a default property Prop
(General
). It means that the three last template parameters may be omitted. Then a matrix of integers may be declared thanks to the line:
Matrix<int> M;
This defines a matrix of size 0 x 0, that is to say an empty matrix. To define a matrix of size 5 x 3, one may write:
Matrix<int> M(5, 3);
Other declarations may be:
Matrix<int, Symmetric> M(20, 30); Matrix<int, General, ColMajor> M(20, 20); Matrix<int, General, RowSparse, NewAlloc<int> > M;
Access to elements is achieved through the operator(int, int)
, and indices start at 0:
Matrix<double> M(5, 6); M(0, 1) = -3.1; M(0, 0) = 1.2 * M(0, 1);
However, the operator operator(int, int)
can be used to modify the matrix only for storages RowMajor, ColMajor, RowSymPacked and ColSymPacked (dense symmetric or non-symmetric matrices). In order to propose a coherent and generic way to handle matrices, the four following access methods are available
Here an exemple of use for hermitian matrices and sparse matrices
// hermitian matrix Matrix<complex<double>, Hermitian, RowHermPacked> H(5, 5); // use of Val to modify upper part of H H.Val(0, 1) = complex<double>(-3.1, 0.9); H.Val(0, 0) = 1.2; H.Val(1, 2) = H(0,1)*4.1; // operator () will always return the correct value of H(i, j) cout << "H(2, 1) = " << H(2, 1) << endl; // for Hermitian matrices, Get is equivalent to Val, // and will fail when you will try to modify lower part H.Get(2, 1) = complex<double>(0.5, 0.7); // => exception will be raised // so if you want to set a value of the lower part, the only possible method is Set H.Set(2, 1, complex<double>(0.5, 0.7)); // => CORRECT // sparse matrix Matrix<double, General, ArrayRowSparse> A(4, 4); // use of Get(i, j) to modify A(i, j) A.Get(2, 3) = 8.1; A.Get(1, 0) = -1.5; A.Get(2, 3) += 0.5; // you can use Set as well A.Set(1, 4, 2.3); // operator(int i, int j) can only be used in a read-only mode cout << "A(3, 3) = " << A(3, 3) << endl; // Val(int i, int j) to modify an already existing non-zero entry A.Val(1, 4) = -2.7;
To display matrices, there are two convenient options:
M.Print(); cout << M << endl;
There are lots of methods that are described in the documentation. One may point out:
GetM()
and GetN()
return respectively the number of rows and the number of columns.
Fill
fills with 1, 2, 3, etc. or fills the matrix with a given value.
Reallocate
resizes the matrix (warning, data may be lost, depending on the allocator).
Resize
resizes the matrix while keeping previous entries
Read
, ReadText
, Write
, WriteText
are useful methods for input/ouput operations.
Symmetric matrices in packed form are managed as square full matrices. There is a difference. If one affects the element (i, j) in the matrix, the element (j, i) will be affected at the same time, without any cost. A comprehensive test of full matrices is achieved in file test/program/matrix_test.cpp
(unitary tests are present in test/unit/matrix_test.cc
)..
Sparse matrices are not managed as dense matrices. The access operator operator(int, int)
is still available, but it doesn't allow any affectation, because it may return a (non-stored) zero of the matrix. In practice, one should deal with the underlying vectors that define elements and their indices: the vector of values data_
, the vector of "start indices" ptr_
(i.e. indices - in the vector data_
- of the first element of each row or column) and the vector of row or column indices ind_
. The methods Get(i, j) or Set(i, j, val) can modify the matrix, but they are rather slow, since the arrays are resized for each new non-zero entry.
Six types of storage are available : RowSparse, ColSparse, RowSymSparse, ColSymSparse, RowComplexSparse, RowSymComplexSparse. To deal with sparse matrices, the following methods should be useful:
SetData
(or the constructor with the same arguments) to set the three vectors data_
, ptr_
and ind_
.
GetData
, GetPtr
and GetInd
which return respectively data_
, ptr_
and ind_
described above.
GetDataSize
, GetPtrSize
and GetIndSize
which return respectively sizes of data_
, ptr_
and ind_
.
Since the Harwell-Boeing form is difficult to handle, a more flexible form can be used in Seldon. Four types of storage are available : ArrayRowSparse, ArrayRowSymSparse, ArrayRowComplexSparse, ArrayRowSymComplexSparse. Their equivalents with a storage of columns : ArrayColSparse, ArrayColSymSparse, ArrayColComplexSparse, ArrayColSymComplexSparse are available as well, but sometimes functions are implemented only for storage by rows. Therefore the user is strongly encourage to use only storages by rows. These storages are accessible if you have included SeldonSolver.hxx after the inclusion of Seldon.hxx (or if you have included SeldonLib.hxx
):
// including Seldon and SeldonSolver #include "Seldon.hxx" #include "SeldonSolver.hxx" using namespace Seldon; // then you can use ArrayRowSparse storage Matrix<double, General, ArrayRowSparse> A;
In these storages, each row is stored as a sparse vector, allowing fast insertions of entries. However, the access operator cannot be used as for dense matrices, since it doesn't allow modification, you should use function Get(i, j) instead.
The following methods should be useful:
Reallocate
initialization of the matrix with a number of rows and columns. Previous entries are cleared.
Resize
The number of rows and columns are modified, previous entries are kept.
GetDataSize
returns number of stored values.
AddInteraction
adds/inserts an entry in the matrix
AddInteractionRow
adds/inserts severals entries in a row of the matrix
ReadText/WriteText
reads/writes matrix in ascii. The methods AddInteraction
and AddInteractionRow
inserts or adds entries in the right position, so that each row contains sorted elements (column numbers are sorted in ascending order). The function Copy
converts matrices in any form, especially Harwell-Boeing form, since this last form induces faster matrix-vector product.
int m = 10, n = 5; // format available only if you have included SeldonSolver.hxx Matrix<double, General, ArrayRowSparse> A(m, n); // you can use AddInteraction A.AddInteraction(3, 4, 3.0); // or directly use the method Get(i, j) // if the entry doesn't exist, it is added A.Get(2, 3) = 2.0; // the following instruction also adds an entry : double y = A(3, 0); // or add several entries at the same time int nb_elt = 3; IVect col_number(nb_elt); DVect values(nb_elt); col_number(0) = 1; col_number(0) = 3; col_number(0) = 2; values(0) = 1.0; values(1) = -1.5; values(2) = 3.0; A.AddInteractionRow(2, col_number, values); // you can read/write the matrix A.WriteText("Ah.dat"); A.ReadText("Ah.dat"); // and convert the matrix if you want a faster matrix-vector product Matrix<double, General, RowSparse> Acsr; Copy(A, Acsr); A.Clear(); DVect x(n), b(m); x.Fill(); // b = A*x MltAdd(1.0, A, x, 0, b);
A comprehensive test of sparse matrices is achieved in file test/program/matrix_sparse.cpp
(unitary tests are present in test/unit/matrix_sparse_test.cc
).