Matrix storages#

The largeMatrix library deals with large matrices stored in different ways: dense storages (all the matrix values are stored), skyline storages (values on a row or a column are stored from the first nonzero values) and compressed sparse storage (only nonzero values are stored). For each of these storages, different “access” methods, driving how the matrix values are stored, are proposed: by rows, by columns, by rows and columns (say dual access) and symmetric access which is like dual access but with same row and column access. The symmetric access is well suited for matrices having symmetry property (symmetric, skew-symmetric, self-adjoint or skew-adjoint), or matrix with symmetric storage property, currently met in finite element approximation. Note that the row dense storage corresponds to the storage used by the Matrix class defined in The Matrix class. As other access methods may be useful in finite element methods, it is the reason why the largeMatrix library provides dense storages.

Large matrices under consideration may be real or complex value matrices, but also matrices of real/complex matrices. This last case allows dealing with vector variational problems where the unknown and test function are vector functions, using the same storage structure as in the scalar case.

More precisely, the largeMatrix library is based on the template class LargeMatrix<T> which mainly handles a vector to store the matrix values and a pointer to a storage describing how the matrix values are stored. Different storage structures are organized in a hierarchy of classes, terminal classes carrying specific data and methods of storages, in particular the product of a matrix and a vector.

Figure made with TikZ

Figure made with TikZ

The LargeMatrix class#

The LargeMatrix<T> class deals with large matrices with different types of storages. Its template parameter T is one of the following types: Real, Complex, RealMatrix or ComplexMatrix. Although it is possible to instantiate LargeMatrix<T> with other types, most functionalities do not work because some member functions are overloaded only for the previous types.

The class mainly manages a vector storing the matrix values and a pointer to a storage structure (MatrixStorage base class):

template <typename T>
class LargeMatrix
{
  public :
    ValueType valueType;      // type of values (real, complex)
    StrucType strucType;      // struc of values (scalar, matrix)
    Number nbRows;            // number of rows counted in T
    Number nbCols;            // number of columns counted in T
    SymType sym;              // type of symmetry
    Dimen nbRowsSub;          // number of rows of submatrices (1 if scalar values)
    Dimen nbColsSub;          // number of columns of submatrices (1 if scalar values)
    String name;              // optionnal name, useful for documentation
  protected :
    vector<T> values_;   // list of values ordered along storage method
    MatrixStorage* storage_p; // pointer to the matrix storage
    ....
}

When T=Matrix<Real> or T=Matrix<Complex>, the number of rows and columns are counted in Matrix not in scalar!

Hint

The first value (index 0) of the vector values_ is not used for storing a matrix value. It contains the default value (in general T) returned when trying to access to a matrix value outside the storage. Be care with default Matrix<T>(), it is a \(0 \times 0\) matrix!

There are various constructors : three from an existing storage, some from file where the large matrix is loaded from the file and some from matrix dimensions and a default value. A large matrix which is created by the copy constructor (shallow copy) will share a same storage with the input one. Be care with these last constructors, they allocate the vector of values only for dense storage.

LargeMatrix();
LargeMatrix(const LargeMatrix<T>&);
LargeMatrix(MatrixStorage*, const T&= T(), SymType= _noSymmetry);
LargeMatrix(MatrixStorage* ms, dimPair dims, SymType sy = _noSymmetry);
LargeMatrix(MatrixStorage*, SymType);
LargeMatrix(Number, Number, StorageType = _dense, AccessType = _row, const T& = T());
LargeMatrix(Number, Number, StorageType = _dense, SymType = _symmetric, const T& = T());
LargeMatrix(const String&, StorageType, Number , Number, StorageType = _dense, AccessType = _row,  Number nsr=1, Number nsc=1);
LargeMatrix(const String&, StorageType, Number, StorageType = _dense, SymType = _symmetric,  Number nsr=1);
~LargeMatrix();

void init(MatrixStorage*, const T&, SymType);
void allocateStorage(StorageType, AccessType, const T& = T());
void setType(const T&);

The StorageType , AccessType and SymType are enumerations :

enum StorageType { _noStorage =0 , _dense , _cs , _skyline , _coo } ;
enum AccessType { _noAccess =0 ,_sym , _row , _col , _dual } ;
enum SymType { _noSymmetry=0 , _symmetric , _skewSymmetric , _ selfAdjoint , _skewAdjoint , _diagonal } ;

The storage type _cs means compressed sparse. It refers to the well known CSR/CSC storage (see the next section for details on storage). The storage type _coo means coordinates storage \((i , j , a i j )\). So far, it is not proposed as a storage method for large matrix, but large matrix may be saved to file in this format. Constructors use the utilities: init , allocateStorage and setType . The setType function uses some utilities (external functions) to get the dimensions of sub-matrices:

pair <Dimen, Dimen> dimsOf ( const Real&) ;
pair <Dimen, Dimen> dimsOf ( const Complex&) ;
pair <Dimen, Dimen> dimsOf ( const Matrix<Real >&) ;
pair <Dimen, Dimen> dimsOf ( const Matrix<Complex>&) ;
bool isDiagonal ( ) const ;
bool i s I d ( const double & tol = 0 . ) const ;

Hint

Note that for the moment, it is not possible to load from a file a matrix as a matrix of matrices (use only nsr=1 and nsc=1 arguments in constructors from file).

There are few facilities to access to matrix values :

Number pos (Number i , Number j ) const ;
void positions ( const vector <Number>&, const vector <Number>&,
vector <Number>&, bool = f a l s e ) const ;
vector <pair <Number , Number>> getCol ( Number , Number =1 , Number=0) const ;
vector <pair <Number , Number>> getRow ( Number , Number =1 , Number =0) const ;
T operator() ( Number adr ) const;
T& operator() ( Number adr );
typename vector <T>::iteratorat( Number , Number );
typename vector <T>::const_iterator at (Number i, Number j ) const;
T operator() (Number i , Number j ) const;
T& operator() (Number i , Number j , bool =true );
Number nbNonZero() const;
void resetZero( );

The member function pos(i,j) returns the address in vector values_ of the matrix value \(M_{ij}\) for \(i,j\ge 1\). The returned address begins at 1 except when the value is outside the storage where the function returns 0. The access operator (i,j) returns the matrix value or a reference to it with the non-const version. In that case, it is possible to activate (errorOn=true) an error handler to prevent access to values outside the storage. It is the default behavior. When errorOn=false, the access operator returns a reference to values_[0], thus you can modify its value. This trick avoids performing tests like if(pos(i,j)!=0)... before writing matrix values. To be safe at end, reset the values_[0] to 0 with resetZero function. Note that the skyline or compressed storage structure can not be dynamically changed when “inserting” a new value.

Hint

pos(), getRow() and getCol() function should not be used in heavy computations because it is time-consuming. In heavy computation, write codes based on storage structure!

As LargeMatrix class provides a lot of functions to modify its contents, some being time expansive regarding the storage:

void deleteRows ( Number , Number ) ;
void deleteCols ( Number , Number ) ;
void setColToZero ( Number c1 =0 , Number c2 =0) ;
void setRowToZero ( Number r1 =0 , Number r2 =0) ;
void roundToZero ( r e a l _ t aszero =10 * theEpsilon ) ;

LargeMatrix<T>& operator * =( const K&) ;
LargeMatrix<T>& operator /=( const K&) ;
LargeMatrix<T>& operator +=( LargeMatrix<T>&) ;
LargeMatrix<T>& operator -=( LargeMatrix<T>&) ;
LargeMatrix<T>& add( i t e r a t o r &, i t e r a t o r &);
LargeMatrix<T>& add( const vector <T>&, const vector <Number>&,const vector <Number>&);
LargeMatrix<T>& add( const T& c , const vector <Number>&, const vector <Number>&);
LargeMatrix<T>& add( const LargeMatrix<K>&, const vector <Number>&,const vector <Number>&, C);
void addColToCol(Number ,Number ,Complex a, bool=false );
void addRowToRow(Number ,Number ,Complex a, bool=false );
void copyVal( const LargeMatrix<T>&, const vector <Number>&, const vector <Number>&);
LargeMatrix<T>& assign ( const LargeMatrix<K>&, const vector <Number>&, const vector <Number>&);

Some functions are provided to change the scalar/matrix representation of the matrix or the storage:

void toSkyline ( ) ;
void toStorage ( MatrixStorage * ) ;
LargeMatrix<K> * toScalar (K) ;
void toScalarEntries ( const vector <Matrix<K> >&, vector <K>&, const MatrixStorage&) ;
void toUnsymmetric ( AccessType at=_sym) ;
void extract2UmfPack ( vector <Int >& colPointer , vector <Int >& rowIndex , vector <T>& matA) const ;
bool getCsColUmfPack ( vector <Int >& colPointer , vector <Int >& rowIndex , const T*& matA) const ;

Matrices that are stored as Dense or Skyline can be factorized as LU, LDLt, LDL* and matrices that are stored as Skyline or Sparse can be factorized using UMFPack if it is available. All the stuff related to solve linear system is also available.

void ldltFactorize();
void ldlstarFactorize();
void luFactorize(bool withPermutation=true);
void umfpackFactorize();

void ldltSolve(vector<S1>& vec, vector<S2>& res) const;
void ldlstarSolve(vector<S>& vec, vector<T>& res) const;
void luSolve(vector<S1>& vec, vector<S2>& res) const;
void umfluSolve(vector<S1>& vec, vector<S2>& res) const;

void sorLowerMatrixVector(const vector<S1>& vec, vector<S2>& res, Real) const;
void sorDiagonalMatrixVector(const vector<S1>& vec, vector<S2>& res,Real) const;
void sorUpperMatrixVector(const vector<S1>& vec, vector<S2>& res,Real) const;
void sorLowerSolve(const vector<S1>& vec, vector<S2>& res,Real) const;
void sorDiagonalSolve(const vector<S1>& vec, vector<S2>& res,Real) const;
void sorUpperSolve(const vector<S1>& vec, vector<S2>& res,Real ) const;

Real umfpackSolve(const vector<S>& vec,
vector<typename Conditional<NumTraits<ScalarType>::IsComplex, ScalarType, S>::type>& res, bool soa=true);
Real umfpackSolve(const vector<vector<S>*>&, vector<vector<typename Conditional<NumTraits<ScalarType>::IsComplex, ScalarType,S>::type>* >&, bool soa=true);
Real umfpackSolve(const vector<Int>& colPointer, const vector<Int>& rowIndex, const vector<T>& values, const vector<S>& vec, vector<typename Conditional<NumTraits<ScalarType>::IsComplex, ScalarType, S>::type>& res,
bool soa=true);

In a same way, some functions interfaces eigensolvers:

friend Number eigenDavidsonSolve(const LargeMatrix<K>* pA, const LargeMatrix<K>* pB, vector<pair<Complex, Vector<Complex> > >& res, Number nev, Real tol, String which, bool isInverted, FactorizationType fac, bool isShift);
friend Number eigenKrylovSchurSolve(const LargeMatrix<K>* pA, const LargeMatrix<K>* pB, vector<pair<Complex, Vector<Complex> > >& res, Number nev, Real tol, String which, bool isInverted, FactorizationType fac, bool isShift);

The Frobenius norm \(\sqrt{\sum{|a_{ij}|^2}}\) and infinity norm \(\max_{|a_{ij}|}\) are provided:

Real norm2() const;
Real norminfty() const;
Real partialNormOfCol(Number, Number, Number) const;

Some operations are available as external functions to the class.

The LargeMatrix supplies methods to add two large matrices, which share a same storage. The matrix result will use the same storage as the two added matrices. If the result doesn’t point to the storage of addend, after the addition, it will be forced to use this storage, and the number of object sharing this storage increases by one. Similar to multiplication of matrix-vector, these functions are also external templates, with specializations to mixed real and complex types.

friend void addMatrixMatrix(const LargeMatrix<S>& matA, const LargeMatrix<S>& matB, LargeMatrix<S>& matC);
friend void addMatrixMatrix(const LargeMatrix<Complex>& matA, const LargeMatrix<Real>& matB, LargeMatrix<Complex>& matC);
friend void addMatrixMatrix(const LargeMatrix<Real>& matA, const LargeMatrix<Complex>& matB, LargeMatrix<Complex>& matC);

The operator + is overloaded for LargeMatrix in the same manner.

LargeMatrix<T> operator+(const LargeMatrix<T>& matA, const LargeMatrix<T>& matB);
LargeMatrix<Complex> operator+(const LargeMatrix<Real>& matA, const LargeMatrix<Complex>& matB);
LargeMatrix<Complex> operator+(const LargeMatrix<Complex>& matA, const LargeMatrix<Real>& matB);

Like addMatrixMatrix, the result of multMatrixScalar shares the same storage of the input large matrix.

The operator * can be also overloaded in the same manner.

LargeMatrix<T> operator*(const LargeMatrix<T>& mat, const T v);
LargeMatrix<T> operator*(const T v, const LargeMatrix<T>& mat);
LargeMatrix<Complex> operator*(const LargeMatrix<Complex>& mat, const Real v);
LargeMatrix<Complex> operator*(const Real v, const LargeMatrix<Complex>& mat);
LargeMatrix<Complex> operator*(const LargeMatrix<Real>& mat, const Complex v);
LargeMatrix<Complex> operator*(const Complex v, const LargeMatrix<Real>& mat);

The product of matrix and vector is one of the most important operation required. The LargeMatrix class interfaces matrix-vector product and the vector-matrix matrix; the product being really done by the storage classes. They are external template functions to the class (declared friend of class), with specializations to mixed real and complex types (only casting from real to complex is allowed) :

void multMatrixVector(const LargeMatrix<S>&, const vector<S>&, vector<S>&);
void multVectorMatrix(const LargeMatrix<S>&, const vector<S>&, vector<S>&);
void multMatrixVector(const LargeMatrix<Matrix<S> >&, const vector<Vector<S> >&, vector<Vector<S> >&);
void multVectorMatrix(const LargeMatrix<Matrix<S> >&, const vector<Vector<S> >&, vector<Vector<S> >&);

The operator * is overloaded for LargeMatrix and vector or vector and LargeMatrix in a same way:

friend vector<S> operator*(const LargeMatrix<S>&, const vector<S>&);
friend vector<S> operator*(const vector<S>&, const LargeMatrix<S>&);
friend vector<Vector<S> operator*(const LargeMatrix<Matrix<S> >&, const vector<Vector<S> >&);
friend vector<Vector<S> operator*(const vector<Vector<S> >&, const LargeMatrix<Matrix<S> >&);

The product of two matrices is provided, but be cautious, up to now the result matrix is a dense matrix (except if one matrix is a diagonal one):

void multMatrixMatrix(const LargeMatrix<SA>&, const LargeMatrix<SB>&, LargeMatrix<SR>&);

The LargeMatrix also provides some functions to factorize a matrix and solve the factorized system. Like multMatrixVector, these functions are external (friend of class), template with hybrid complex-real specialization.

void multMatrixMatrix(const LargeMatrix<SA>&, const LargeMatrix<SB>&, LargeMatrix<SR>&);

The LargeMatrix also provides some functions to factorize a matrix and solve the factorized system. Like multMatrixVector, these functions are external (friend of class), template with hybrid complex-real specialization.

void ldltFactorize(LargeMatrix<S>& mat);
void ldlstarFactorize(LargeMatrix<S>& mat);
void luFactorize(LargeMatrix<S>& mat);

Hint

These functions are available for SkylineStorage and DenseStorage.

If a matrix is factorized, some other operation are available

void multFactMatrixVector(const LargeMatrix<S1>&, const vector<S2>&, vector<S3>&);
void multVectorFactMatrix(const LargeMatrix<S1>&, const vector<S2>&, vector<S3>&);
void multInverMatrixVector(const LargeMatrix<S1>& mat, const vector<S2>& vec, vector<typename Conditional<NumTraits<S1>::IsComplex, S1, S2>::type>& res, FactorizationType);

In order to generate a diagonal matrix which uses an existent matrix as prototype, LargeMatrix class provides function

LargeMatrix<S> diagonalMatrix(const LargeMatrix<S>&, const S);

A special version of this function is a function to generate an identity matrix from an existing one.

LargeMatrix<S> identityMatrix(const LargeMatrix<S>&);

Hint

These functions work with LargeMatrix class having all kinds of matrix storage except RowCsStorage class and ColCsStorage class.

Finally, the class has input/output member functions :

void print(ostream&) const;
void viewStorage(ostream&) const;
ostream& operator<<(ostream&, const LargeMatrix<S>&);
void saveToFile(const String &, StorageType, bool encode=false) const;
void loadFromFile(const String&, StorageType);
String encodeFileName(const String&, StorageType) const;

Only two formats are allowed for saving matrix to file or loading matrix from file :

  • the dense format (_dense), where all the matrix values are written row by row (one row by line), space character separates each value :

    A11 A12 ... A1n                       re(A11) im(A11)  ... re(A1n) im(A1n)
    A21 A22 ... A2n   if complex values   re(A21) im(A21)  ... re(A2n) im(A2n)
    ...                                   ...
    Am1 Am2 ... Amn                       re(Am1) im(Am1)  ... re(Amn) im(Amn)
    
  • the coordinates format (_coo)

    i1 j1 Ai1j1                           i1 j1 re(Ai1j1) im(Ai1j1)
    i2 j2 Ai2j2     if complex values     i2 j2 re(Ai2j2) im(Ai2j2)
    ...
    

Hint

For matrix of matrices (T=Matrix<Real> or T=Matrix<Complex>), submatrix structure is not preserved when writing matrix values!

To keep some general information about matrix, with the argument encode=true in the saveFile function, the name of file may be modified by inserting the string :(m_n_storageType_valueType_strucType_p_q)

where m and n are the “dimensions” of the matrix, storageType = dense or coo valueType = real or complex , strucType = scalar or matrix and p and q are the dimensions of sub-matrices. In case of scalar value, these parameters are omitted. This trick avoids including information in file in order that it is easily loadable by other software programs, in particular Matlab.

Some examples#

  • Manipulate a \(3\times 2\) matrix with row dense storage:

Vector<Real> x(2,2);
LargeMatrix<Real> A(3,2,_dense,_row,1.);
out<<"matrix A : "<<A;
out<<"access A(1,1)= "<<A(1,1);
out<<"product A*x : "<<A*x;
A.saveToFile("A_dense.mat",_dense);
LargeMatrix<Real> Areload("A_dense.mat",_dense,3,2,_dense,_row);
  • Manipulate a \(3\times 2\) matrix of \(2\times 2\) real matrices with dual dense storage:

Matrix<Real> I1(2,_idMatrix);
Vector<Real> x(6,3);
LargeMatrix<Matrix<Real> > B(6,3,_dense,_dual,2*I1);
out<<"product X*B : "<<X*B;
  • Manipulate a \(9\times 9\) symmetric matrix with symmetric compressed storage:

//construct storage for a Q1 mesh
Number n=3;
vector<vector<Number> > elts((n-1)*(n-1),vector<Number>(4));
for(Number j=1; j<=n-1; j++)
  for(Number i=1; i<=n-1; i++) {
     Number e=(j-1)*(n-1)+i-1, p=(j-1)*n+i;
     elts[e][0]=p; elts[e][1]=p+1; elts[e][2]=p+n; elts[e][3]=p+n+1;
  }
SymCsStorage* cssym= new SymCsStorage(n*n,elts,elts);
//construct large matrix with symmetric compressed storage
LargeMatrix<Real> C(cssym,1.,_symmetric);
C.viewStorage(out);
Vector<Real> x(n*n,0.);for(Number i=0;i<n*n;i++) x1[i]=i;
out<<"product C*x : "<<C*x;
C.saveToFile("C_coo.mat"),_coo);
//construct large matrix with symmetric compressed storage
LargeMatrix<Real> C1(cssym,1.,_symmetric);
out<<"addition C+C1 "<< C+C1;
// multiply a matrix with a scalar
out<<"product C*10.0 "<< C*10.0;
// generate a diagonal matrix
out<<"diagonal matrix from matrix C " << diagonalMatrix(C, 10.0);
// generate an identity matrix
out<<"identity matrix from matrix C " << identityMatrix(C);

Note that compressed storage has to be built before to instantiate a large matrix with compressed storage. The function viewStorage produced the output:

symmetric_compressed sparse (csr,csc), size = 29, 9 rows, 9 columns
 (shared by 1 objects).
        123456789
     1  d........
     2  xd.......
     3  .xd......
     4  xx.d.....
     5  xxxxd....
     6  .xx.xd...
     7  ...xx.d..
     8  ...xxxxd.
     9  ....xx.xd
        123456789

LDLt-Factorize a positive definite 3x3 matrix with symmetric skyline storage:

LargeMatrix<Real> rResSymSkylineLdlt(inputsPathTo("matrix3x3SymPosDef.data"), _dense, rowNum, _skyline, _symmetric);
ldltFactorize(rResSymSkylineLdlt);
out << "The result of LDLt factorizing sym skyline matrix is " << rResSymSkylineLdlt << endl;

See also

  • library=largeMatrix,

  • header=LargeMatrix.hpp,

  • implementation=LargeMatrix.cpp,

  • test=test_LargeMatrixDenseStorage.cpp, test_LargeMatrixCsStorage.cpp,

  • header dep= config.h, utils.h, MatrixStorage.hpp, DenseStorage.hpp, CsStorage.hpp, SkylineStorage.hpp, config.h, utils.h

The MatrixStorage class#

The MatrixStorage is the abstract base class of all supported matrix storages. Vectors carrying storage pointers are defined in child classes and the vector storing the matrix entries is defined outside the storage (see LargeMatrix<T> class for instance).

Various matrix storages are defined by three characteristics :

  • A StorageType: _dense for dense storage, _cs for compressed sparse storage (CSR/CSC) and _skyline for skyline storage;

  • An AccessType: _row for a row wise access, _col for a column wise access, _dual for a row wise access for lower triangular part of the matrix and a column wise access for the upper triangular part of the matrix, and _sym if the storage is symmetric (only the lower triangular part is described with a row wise access;

  • A BuildStorageType: describing the way the storage is constructed :_undefBuild, _feBuild, _dgBuild,_diagBuild, _ecBuild, _globalBuild, _otherBuild;

  • A flag telling if the storage comes from a vector to scalar conversion.

class MatrixStorage
{
  protected:
  StorageType storageType_;     // storage type
  AccessType accessType_;       // access type
  StorageBuildType buildType_;  // storage build type
  bool scalarFlag_;             // scalar conversion flag
  Number nbRows_;               // number of rows
  Number nbCols_;               // number of columns
  Number nbObjectsSharingThis_; // number of matrices sharing "this" storage
  ...
}

The number of rows and columns are counted in value type of the matrix.

It manages also a string index to identify the storage (generally encoding the row/col space pointers)

public:
 String stringId; // string identifier based on characteristic pointers

The class provides some constructors which only initialize members of the class.

MatrixStorage();
MatrixStorage(StorageType, AccessType at = _noAccess);
MatrixStorage(StorageType, AccessType, Number, Number);
virtual ~MatrixStorage();

Protected members may be read by accessors:

String name() const;
StorageType storageType() const;
AccessType accessType() const;
StorageBuildType buildType() const;
Number nbOfRows() const;
Number nbOfColumns() const;
Number numberOfObjects() const;
void objectPlus();
void objectMinus();

All storage object pointers are stored in a static vector

static vector<MatrixStorage*> theMatrixStorages; // list of all matrix storages
static void clearGlobalVector();                 // delete all MatrixStorage objects
static void printAllStorages(ostream&);          // print the list of MatrixStorage in memory
static void printAllStorages(PrintStream& os);

and some static functions dealing with are available.

When building a new storage, it is possible to look for an existing storage by using the external function:

MatrixStorage* findMatrixStorage(const String& id, StorageType st, AccessType at, StorageBuildType sb, bool scal=false, Number nbr=0, Number nbc=0);

Travelling theMatrixStorages vector this function checks if it exists a storage having the same characteristics (id, storage type, access type, build type) and may check the scalar flag and the number of rows or columns if nonzero values are given.

The class provides virtual member functions returning the sizes of storage (number of matrix entries stored) :

virtual Number size() const = 0;
Number diagonalSize() const;
virtual Number lowerPartSize() const=0;
virtual Number upperPartSize() const=0;

Most of the member functions depend on type of storage, so they are virtual member functions. Besides, as storages do not depend on value type of matrix, the storage classes are not templated by the matrix value type. Because virtual template functions are not allowed in C++, all the value type specializations are explicit in base class and in child classes. Up to now, the supported specializations are Real, Complex, RealMatrix and ComplexMatrix.

We describe here only the :cpp:type:~xlifepp::Real specialization. There are two functions related to matrix entry position in storage, the first one gives the position of entry \((i,j)\) with \(i,j\ge 1\) and the second one compute the positions of the entries of the submatrix given by its row indices and column indices. These functions give positions from index 1 if the entry is inside the storage else the returned position is 0. In the second function, it is possible to activate an error when entry index is outside the storage.

In both functions, the symmetry has to be specified if the matrix has a symmetry property, in that case the position is always in lower triangular part. Be care with the fact that a matrix with no symmetry may have a symmetric storage!

virtual Number pos(Number i, Number j, SymType sym=_noSymmetry) const;
virtual void positions(const std::vector<Number>&, const std::vector<Number>&, std::vector<Number>&, bool errorOn=true, SymType=_noSymmetry) const;

To print, to save to file or load from file large matrices, the following functions are proposed :

virtual void printEntries(std::ostream&, const std::vector<Real>&, Number vb = 0, SymType sym = _noSymmetry) const;
template<typename T>
void printDenseMatrix(std::ostream&, const std::vector<T>&, SymType s=_noSymmetry) const;
void printCooMatrix(std::ostream&, const std::vector<T>&, SymType s=_noSymmetry) const;
virtual void loadFromFileDense(std::istream&, std::vector<Real>&, SymType, bool);
virtual void loadFromFileCoo(std::istream&, std::vector<Real>&, SymType, bool);
virtual void visual(std::ostream&) const;

The printEntries function print on output stream the stored matrix entries in a well suited presentation, the printDenseMatrix function print the matrix entries in a dense format as it was dense and the printCooMatrix function print the matrix entries in a coordinate format (\(i,j,A_{ij}\)). An \(m\times n\) matrix saved as dense produced a file like:

A11 A12 .... A1n                       re(A11) im(A11)  .... re(A1n) im(A1n)
A21 A22 .... A2n   if complex values   re(A21) im(A21)  .... re(A2n) im(A2n)
....                                     ....
Am1 Am2 .... Amn                       re(Am1) im(Am1)  .... re(Amn) im(Amn)

and when saved as coordinates format :

i1 j1 Ai1j1                           i1 j1 re(Ai1j1) im(Ai1j1)
i2 j2 Ai2j2     if complex values     i2 j2 re(Ai2j2) im(Ai2j2)
...                                   ....

Note that printDenseMatrix and printCooMatrix functions are template functions. Their coding is not optimal because they use the virtual pos() function and other external utilities which “interpret” the type of the template:

void printDense(ostream&, const Real&, Number);
void printDense(ostream&, const Complex&, Number);
template<typename T>
  void printDense(ostream&, const Matrix<T>&, Number);
  void printCoo(ostream&, const Matrix<T>&, Number);
void printCoo(ostream&, const Real&, Number);
void printCoo(ostream&, const Complex&, Number);
Number numberOfRows(const Real&);
Number numberOfRows(const Complex&);
Number numberOfCols(const Real&);
Number numberOfCols(const Complex&);
template<typename T> Number numberOfRows(const Matrix<T>&);
template<typename T> Number numberOfCols(const Matrix<T>& m);

The visual() function prints only the matrix entries inside the storage as ‘x’ characters (d for diagonal entries):

matrix storage :row_compressed sparse (csr,csc), size = 49, 9 rows, 9 columns (shared by 0 objects).
        123456789
     1  dx.xx....
     2  xdxxxx...
     3  .xd.xx...
     4  xx.dx.xx.
     5  xxxxdxxxx
     6  .xx.xd.xx
     7  ...xx.dx.
     8  ...xxxxdx
     9  ....xx.xd
        123456789

The MatrixStorage class provides the product matrix \(\times\) vector and the product vector \(\times\) matrix:

virtual
  void multMatrixVector(const vector<Real>&, const vector<Real>&, vector<Real>&, SymType) const;
  void multMatrixVector(const vector< Matrix<Real> >&, const vector<Vector<Real>>&, vector Vector<Real>>&, SymType) const;
  void multVectorMatrix(const vector<Real>&, const vector<Real>&, vector<Real>&, SymType) const;
  void multVectorMatrix(const vector<Matrix<Real>>&, const vector<Vector<Real>>&, vector<Vector<Real>>&, SymType) const;

Note that mixing real vector/matrix and complex vector/matrix is also provided. Each child class implements the best algorithm for the product.

The addition of two matrices is supplied with:

virtual void addMatrixMatrix(const vector<Real>&, const vector<Real>&, vector<Real>&) const;

Similar to multiplication of matrix-vector, this function is implemented in each child class. The MatrixStorage class also provides the matrix factorizations:

virtual
 void ldlt(vector<Real>&, vector<Real>&, const SymType sym = _symmetric) const;
 void ldlt(vector<Complex>&, vector<Complex>&, const SymType sym = _symmetric) const;
 void lu(vector<Real>&, vector<Real>&, const SymType sym = _noSymmetry) const;
 void lu(vector<Complex>&, vector<Complex>&, const SymType sym = _noSymmetry) const;
 void ldlstar(vector<Real>&, vector<Real>&) const;
 void ldlstar(vector<Complex>&, vector<Complex>&) const;

as well as some special solvers

virtual
 void lowerD1Solver(const vector<Real>&, vector<Real>&, vector<Real>&) const;
 void diagonalSolver(const vector<Real>&, vector<Real>&, vector<Real>&) const
 void upperD1Solver(const vector<Real>&, vector<Real>&, vector<Real>&, const SymType sym = _noSymmetry) const;
 void upperSolver(const vector<Real>&, vector<Real>&, vector<Real>&, const SymType sym = _noSymmetry) const;
 void sorDiagonalMatrixVector(const vector<Real>&, const vector<Real>&, vector<Real>&, const Real) const;
 void sorLowerMatrixVector(const vector<Real>&, const vector<Real>&, vector<Real>& ,Real, const SymType = _noSymmetry) const;
 void sorUpperMatrixVector(const vector<Real>&, const vector<Real>&, vector<Real>&, Real, const SymType = _noSymmetry) const;
 void sorDiagonalSolve(const vector<Real>&, const vector<Real>&, vector<Real>&, Real) const;
 void sorLowerSolve(const vector<Real>&, const vector<Real>&, vector<Real>&, Real) const;
 void sorUpperSolve(const vector<Real>&, const vector<Real>&, vector<Real>&, Real, const SymType sym = _noSymmetry) const;

Each descendant will implement the specific algorithm for these virtual functions.

Values on the diagonal of a matrix is set with the function

virtual void setDiagValue(vector<Real>&, Real);

See also

  • library=largeMatrix,

  • header=MatrixStorage.hpp,

  • implementation=MatrixStorage.cpp,

  • test=test_LargeMatrixDenseStorage.cpp, test_LargeMatrixCsStorage.cpp,

  • header dep= config.h, utils.h

Dense storages#

The DenseStorage class#

Dense storages are proposed as children of MatrixStorage class in order to be consistent with other storages. Contrary to the Matrix class (see utils lib) which is a row dense storage, several dense storages are offered for an \(m\times n\) matrix (say \(A\)) :

  • the row dense storage (RowDenseStorage class)

    \((A_{11}, A_{12}, \cdots, A_{1n},A_{21}, A_{22}, \cdots, A_{2n}, \cdots, A_{m1}, A_{m2}, \cdots, A_{mn})\)

  • The column dense storage (ColDenseStorage class)

    \((A_{11}, A_{21}, \cdots, A_{2m},A_{12}, A_{22}, \cdots, A_{m2}, \cdots, A_{1n}, A_{2n}, \cdots, A_{mn})\)

  • The dual dense storage (DualDenseStorage class), diagonal part is first stored, then strict lower triangular part and strict upper triangular part:

    \((A_{11}, A_{22}, \cdots, A_{mm}, A_{21}, \cdots, A_{i1},\cdots, A_{i,i-1}, \cdots, A_{12}, \cdots, A_{1j}, \cdots, A_{j-1,j}, \cdots) \ \) \(\text{case }m=n\)

  • The symmetric dense storage (SymDenseStorage class), diagonal part is first stored, then strict lower triangular part :

    \((A_{11}, A_{22}, \cdots, A_{mm}, A_{21}, \cdots, A_{i1},\cdots, A_{i,i-1}, \cdots) \ \text{case }m=n\)

Dual storage works also for non-square matrices. Obviously, symmetric storage works only for square matrices!

All dense storages inherit from the DenseStorage abstract class which inherits from MatrixStorage abstract class.

The DenseStorage class#

The DenseStorage class has no member attribute. There are some basic constructors just calling MatrixStorage constructors:

class DenseStorage : public MatrixStorage
{
  public :
    DenseStorage();
    DenseStorage(AccessType);
    DenseStorage(AccessType, Number);
    DenseStorage(AccessType, Number, Number);
    virtual ~DenseStorage() {}
    ....
}

DenseStorage objects do not have to be instantiated. This class acts as an interface to particular dense storages and gathers all common functionalities of dense storages, some being virtual:

  • Public size functions:

    Number size() const;
    virtual Number lowerPartSize() const;
    virtual Number upperPartSize() const;
    
  • Protected input/output functions (template line is written once):

    template<typename Iterator>
    void printScalarEntries(Iterator&, Number, Number, Number, Number, Number, const String&, Number, std::ostream&) const;
    void printScalarEntriesTriangularPart(Iterator& , Iterator&, Number, Number, Number, Number, Number, const String&, Number, std::ostream&) const;
    void printMatrixEntries(Iterator&, Number, Number, const String&, Number, std::ostream&) const;
    void printMatrixEntriesTriangularPart(Iterator&, Iterator&, Number, Number, const String&, Number, std::ostream&) const;
    void loadFromFileDense(std::istream&, std::vector<Real>&, SymType, bool);
    void loadFromFileDense(std::istream&, std::vector<Complex>&, SymType, bool);
    void loadFromFileCoo(std::istream&, std::vector<Real>&, SymType, bool);
    void loadFromFileCoo(std::istream&, std::vector<Complex>&, SymType, bool);
    
  • product of matrix and vector (template line is written once):

    template<typename MatIterator, typename VecIterator, typename ResIterator>
    void diagonalMatrixVector(MatIterator&, VecIterator&, ResIterator&, ResIterator&) const;
    void diagonalVectorMatrix(MatIterator&, VecIterator&, ResIterator&, ResIterator&) const;
    void lowerMatrixVector(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&, SymType) const;
    void lowerVectorMatrix(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&, SymType) const;
    void upperMatrixVector(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&, SymType) const;
    void upperVectorMatrix(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&, SymType) const;
    void rowMatrixVector(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&)  const;
    void rowVectorMatrix(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&)  const;
    void columnMatrixVector(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&)  const;
    void columnVectorMatrix(MatIterator&, VecIterator&, VecIterator&, ResIterator&, ResIterator&)  const;
    

Note that the product of matrix and vector are performed either by row or column access, for diagonal part, lower part or upper part. They use iterators to by-pass the templated matrix values vector! With this trick, product algorithms are all located in DenseStorage class.

  • Addition of two matrices:

template<typename Mat1Iterator, typename Mat2Iterator, typename ResIterator>
void sumMatrixMatrix(Mat1Iterator&, Mat2Iterator&, ResIterator&, ResIterator&) const;

See also

  • library=largeMatrix,

  • header=DenseStorage.hpp,

  • implementation=DenseStorage.cpp,

  • test=,test_LargeMatrixDenseStorage.cpp,

  • header dep= MatrixStorage.hpp, config.h, utils.h

The RowDenseStorage/ColDenseStorage/DualDenseStorage/SymDenseStorage classes#

All child classes (RowDenseStorage, ColDenseStorage, DualDenseStorage and SymDenseStorage) of DenseStorage class have the same structure. We give here only the description of RowDenseStorage class.

RowDenseStorage class manages dense storage where matrix values are stored row by row in values vector. It has no member attribute and has constructors just calling DenseStorage constructors:

class RowDenseStorage : public DenseStorage
{
  public :
   RowDenseStorage();
   RowDenseStorage(Number);
   RowDenseStorage(Number, Number);
   virtual ~RowDenseStorage() {}
   ....
}

The class provides all the virtual methods declared in MatrixStorage class :

Number pos(Number i, Number j, SymType s=_noSymmetry) const;
void positions(const std::vector<Number>&, const std::vector<Number>&, std::vector<Number>&, bool errorOn=true, SymType=_noSymmetry ) const;
void printEntries(std::ostream&, const std::vector<Real>&, Number vb = 0, SymType sym = _noSymmetry) const;
template<typename M, typename V, typename R>
void multMatrixVector(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
void multVectorMatrix(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
template<typename T>
void setDiagValueColDense(std::vector<T>& m, const T k);
template<typename M1, typename M2, typename R>
void addMatrixMatrix(const std::vector<M1>&, const std::vector<M2>&, std::vector<R>&) const;
...

Only version of multMatrixVector and multVectorMatrix are written here for Real type but versions for Complex, RealMatrix, ComplexMatrix and mixed types are also provided.

DualDenseStorage and SymDenseStorage classes provides also the size of strict lower and strict upper triangular part:

Number lowerPartSize() const;
Number upperPartSize() const;

See also

XXX=Row, Col, Dual or Sym

  • library=largeMatrix,

  • header=XXXDenseStorage.hpp,

  • implementation=XXXDenseStorage.cpp,

  • test=test_LargeMatrixDenseStorage.cpp, test_LargeMatrixCsStorage.cpp,

  • header dep= DenseStorage.hpp, MatrixStorage.hpp, config.h, utils.h

Compressed sparse storages#

Compressed sparse storages are designed to store only nonzero values of a matrix. It exists several methods to do it. The well known CSR/CSC storage being the most common one, we chose it. It consists in storing nonzero values of a row (resp. column) in a compressed vector and their column indices (resp. row indices) in a compressed vector. A vector of addresses where begin rows (resp. columns) is also required. More precisely, for an \(m\times n\) matrix

  • Row compressed storage is defined by the three vectors:

    values = \((0, A_{1\mathcal{C}_1}, \ldots, A_{i\mathcal{C}_i}, \ldots, A_{m\mathcal{C}_m})\)

    colIndex = \((\mathcal{C}_1, \ldots, \mathcal{C}_i, \ldots, \mathcal{C}_m)\)

    rowPointer = \((p_1, \ldots, p_i, \ldots, p_m, p_{m+1})\)

    where \(\mathcal{C}_i\) is the set of index \(j\) where \(A_{ij}\ne 0\) and \(p_i\) is the position in vector colIndex of the first entry of row \(i\le m\); \(p_{m+1}\) gives the number of nonzero values.

    By convention, all the indices begins at 0 and first entry of values vector is always 0!

    For instance, the following \(5\times 6\) non-symmetric matrix

    \[\begin{split}A=\left[ \begin{array}{cccccc} 11 & 12 & 0 & 14 & 0 & 16\\ 0 & 22 & 23 & 0 & 0 & 26 \\ 31 & 32 & 33 & 0 & 0 & 0 \\ 0 & 0 & 43 & 44 & 0 & 0 \\ 51 & 0 & 53 & 54 & 55 & 0 \end{array} \right]\end{split}\]

    has the row compressed storage vectors:

    values = \((0\ 11\ 12\ 14\ 16\ \ 22\ 23\ 26\ \ 31\ 32\ 33\ \ 43\ 44\ \ 51\ 53\ 54\ 55)\)

    colIndex = \((0\ 1\ 3\ 5\ \ 1\ 2\ 5\ \ 0\ 1\ 2\ \ 2\ 3\ \ 0\ 2\ 3\ 4)\)

    rowPointer = \((0\ 4\ 7\ 10\ 12\ 16)\)

  • The Col compressed storage is defined by the three vectors:

    values = \((0,\ A_{\mathcal{R}_11}, \ldots , \ A_{\mathcal{R}_jj}, \ldots, \ A_{\mathcal{R}_nn})\)

    rowIndex = \((\mathcal{R}_1, \ldots , \ \mathcal{R}_j, \ldots,\ \mathcal{R}_n)\)

    colPointer = \((p_1, \ldots ,\ p_j, \ldots ,\ p_n,\ p_{n+1})\)

    where \(\mathcal{R}_j\) is the set of index \(i\) where \(A_{ij}\ne 0\) and \(p_j\) is the position in rowIndex vector of the first entry of column \(j\le n\); \(p_{n+1}\) gives the number of nonzero values.

    By convention, all the indices begins at 0 and first entry of values vector is always 0!

    For instance, the previous \(5\times 6\) non-symmetric matrix \(A\) has the column compress storage vectors:

    values = \((0\ 11\ 31\ 51\ \ 12\ 22\ 32\ \ 23\ 33\ 43\ 53\ \ 55\ \ 16\ 26)\)

    rowIndex = \((0\ 2\ 4\ \ 0\ 1\ 2\ \ 1\ 2\ 3\ 4\ \ 0\ 3\ 4\ \ 4\ \ 0\ 1)\)

    colPointer = \((0\ 4\ 7\ 10\ 12\ 16)\)

  • The Dual compressed storage is defined by the five vectors:

    values = \((0,\text{diag}(A), A_{\mathcal{C}_11}, \ldots, A_{\mathcal{C}_ii}, \ldots, A_{\mathcal{C}_mm}, A_{\mathcal{R}_11}, \ldots, A_{\mathcal{R}_jj}, \ldots, A_{\mathcal{R}_nn})\)

    colIndex = \((\mathcal{C}_1, \ldots, \mathcal{C}_i, \ldots, \mathcal{C}_m)\)

    rowPointer = \((p_1, \ldots ,\ p_i,\ldots, p_m, p_{m+1})\)

    rowIndex = \((\mathcal{R}_1, \ldots, \mathcal{R}_j, \ldots, \mathcal{R}_n)\)

    colPointer = \((q_1, \ldots, q_j, \ldots, ,q_n, q_{n+1})\)

    where \(\mathcal{C}_i\) is the set of index \(j\) where \(A_{ij}\ne 0\) and \(j\le\min(i-1,n)\), \(\mathcal{R}_j\) is the set of index \(i\) where \(A_{ij}\ne 0\) and \(i\le\min(j-1,m)\), \(p_i\) (resp. \(q_j\)) is the position in colIndex (resp. rowIndex) vector of the first entry of row \(i\le m\) (resp. column \(j\le n\)). The colIndex and rowPointer vectors describes the row compressed storage of the strict lower part of matrix while the rowIndex and colPointer vectors describes the column compressed storage of the strict upper part of matrix.

    In this storage, all the diagonal entries of matrix are always first stored even the null entries!

    For instance, the previous \(5\times 6\) non-symmetric matrix \(A\) has the column compress storage vectors:

    values = \((0\ 11\ 22\ 33\ 44\ 55\ \ 31\ 32\ \ 43\ \ 51\ 53\ 54\ \ \ 23\ \ \ 14\ 44\ \ \ 16\ 26)\)

    colIndex = \((0\ 1\ \ 2\ \ 0\ 2\ 3)\)

    rowPointer = \((0\ 0\ 0\ 2\ 3\ 6)\)

    rowIndex = \((0\ \ 1\ \ 0\ \ 0\ 1)\)

    colPointer = \((0\ 0\ 1\ 2\ 2\ 3\ 5)\)

    Note

    For an empty row \(i\) (resp. column \(j\)), rowPointer(\(i\))=rowPointer(\(i-1\)) (resp. colPointer(\(j\))=colPointer(\(j-1\))).

    By the way, rowPointer(\(i\))-rowPointer(\(i-1\)) gives the number of stored entries on row \(i-1\).

  • The Symmetric compressed storage is devoted to square matrix with symmetric storage, the matrix may be not symmetric.

    In that case, only the storage of lower triangular part of matrix is described by the three vectors (those of row part of dual storage):

    values = \((0,\text{diag}(A), A_{\mathcal{C}_11}, \ldots, A_{\mathcal{C}_ii}, \ldots, A_{\mathcal{C}_mm})\) if \(A\) has a symmetry property

    values = \((0,\text{diag}(A), A_{\mathcal{C}_11}, \ldots, A_{\mathcal{C}_ii}, \ldots, A_{\mathcal{C}_mm}, A_{\mathcal{C}_11}, \ldots, A_{\mathcal{C}_jj}, \ldots, A_{\mathcal{C}_mm})\) if \(A\) has no symmetry property

    colIndex = \((\mathcal{C}_1, \ldots, \mathcal{C}_i, \ldots, \mathcal{C}_m)\)

    rowPointer = \((p_1, \ldots, p_i, \ldots ,p_m, p_{m+1})\)

    where the meaning of \(\mathcal{C}_i\) and \(p_i\) is the same as dual storage.

    For instance, the \(5\times 5\) non-symmetric matrix with symmetric storage

    \[\begin{split}B=\left[ \begin{array}{ccccc} 11 & 0 & 13 & 0 & 15 \\ 0 & 22 & 23 & 0 & 0 \\ 31 & 32 & 33 & 34 & 35\\ 0 & 0 & 43 & 44 & 45 \\ 51 & 0 & 53 & 54 & 55 \end{array} \right]\end{split}\]

    has the symmetric compressed storage vectors:

    values = \((0\ 11\ 22\ 33\ 44\ 55\ \ 31\ 32\ \ 43\ \ 51\ 53\ 54\ \ 13\ 23\ \ 34\ \ 15\ 35\ 45)\)

    colIndex = \((0\ 1\ \ 2\ \ 0\ 2\ 3)\)

    rowPointer = \((0\ 0\ 0\ 2\ 3\ 6)\)

    Warning

    Be careful with the fact that actual matrix values begins at address 1 in values vector defined outside the storage stuff.

The CsStorage class#

The CsStorage class has no member attribute. There are some basic constructors just calling MatrixStorage constructors:

class CsStorage : public MatrixStorage
{
  public :
    CsStorage(AccessType = _dual);
    CsStorage(Number, AccessType = _dual);
    CsStorage(Number, Number, AccessType = _dual);
    virtual ~CsStorage() {}
  protected :
    void buildCsStorage(const std::vector<std::vector<Number> >&, std::vector<Number>&, std::vector<Number>&);
  ...
}

The buildCsStorage() is a general member function building storage vectors from lists of column indices by row. It is used by any child class of CsStorage class.

CsStorage objects do not have to be instantiated. This class acts as an interface to particular compressed storages and gathers all common functionalities of dense storages, some being virtual (some template line declaration are omitted):

virtual Number size() const = 0;
virtual Number lowerPartSize() const;
virtual Number upperPartSize() const;
template<typename Iterator>
  void printEntriesAll(StrucType, Iterator& , const std::vector<Number>&, const std::vector<Number>&, Number, Number, Number, const String&, Number, std::ostream&) const;
  void printEntriesTriangularPart(StrucType, Iterator&, Iterator&, const std::vector<Number>&, const std::vector<Number>&, Number, Number, Number, const String&, Number, std::ostream&) const;
  void printCooTriangularPart(std::ostream&, Iterator&, const std::vector<Number>&, const std::vector<Number>&, bool, SymType sym = _noSymmetry) const;
  void printCooDiagonalPart(std::ostream&, Iterator&, Number) const;

template <typename T>
  void loadCsFromFileDense(std::istream&, std::vector<T>&, std::vector<Number>&, std::vector<Number>&, SymType, bool);
  void loadCsFromFileCoo(std::istream&, std::vector<T>&, std::vector<Number>&, std::vector<Number>&, SymType, bool);
  void loadCsFromFileDense(std::istream&, std::vector<T>&, std::vector<Number>&, std::vector<Number>&, std::vector<Number>&, std::vector<Number>&, SymType, bool);
  void loadCsFromFileCoo(std::istream&, std::vector<T>&, std::vector<Number>&, std::vector<Number>&, std::vector<Number>&, std::vector<Number>&, SymType, bool);

The CsStorage class provides the real code of product of matrix and vector, using iterators as arguments to by-pass the template type of matrix values. For dual and symmetric storage, the computation is performed by splitting the matrix in its diagonal, lower and upper part (template line are omitted):

template<typename MatIterator, typename VecIterator, typename ResIterator>
  void diagonalMatrixVector(MatIterator&, VecIterator&, ResIterator&, ResIterator&) const;
  void lowerMatrixVector(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType sym) const;
  void upperMatrixVector(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType) const;
  void rowMatrixVector(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&)  const;
  void columnMatrixVector(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&)  const;
  void diagonalVectorMatrix(MatIterator&, VecIterator&, ResIterator&, ResIterator&) const;
  void lowerVectorMatrix(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType sym) const;
  void upperVectorMatrix(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType) const;
  void rowVectorMatrix(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&)  const;
  void columnVectorMatrix(const std::vector<Number>&, const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&)  const;

Besides the product of matrix and vector, the CsStorage provides solvers for matrix in its diagonal, lower and upper part

template<typename MatIterator, typename VecIterator, typename ResIterator>
  void bzSorDiagonalMatrixVector(MatIterator& itd, const VecIterator& itvb, ResIterator& itrb, const Real w) const;
template<typename MatIterator, typename VecIterator, typename XIterator>
  void bzSorLowerSolver(const MatIterator&, const MatIterator&, VecIterator&, XIterator&, XIterator&, const std::vector<Number>&, const std::vector<Number>&, const Real) const;
template<typename MatIterator, typename VecIterator, typename XIterator>
  void bzSorDiagonalSolver(const MatIterator& itdb, VecIterator& itbb, XIterator& itxb, XIterator& itxe, const Real w) const;
template<typename MatRevIterator, typename VecRevIterator, typename XRevIterator>
  void bzSorUpperSolver(const MatRevIterator&, const MatRevIterator&, VecRevIterator&, XRevIterator&, XRevIterator&, const std::vector<Number>&, const std::vector<Number>&, const Real) const;

Addition of two matrices are implemented by

template<typename Mat1Iterator, typename Mat2Iterator, typename ResIterator>
  void sumMatrixMatrix(Mat1Iterator&, Mat2Iterator&, ResIterator&, ResIterator&)

See also

  • library=largeMatrix,

  • header=CsStorage.hpp,

  • implementation=CsStorage.cpp,

  • test=test_LargeMatrixCsStorage.cpp,

  • header dep= MatrixStorage.hpp, config.h, utils.h

The RowCsStorage / ColCsStorage classes#

As mentioned before, row compressed storage are based on the column indices vector and the row pointers vector. So the RowCsStorage class carries these two vectors:

class RowCsStorage : public CsStorage
{
  protected :
    std::vector<Number> colIndex_; // vector of column index of nonzero value
    std::vector<Number> rowPointer_; // vector of positions of begining of rows
  ...
}

The class provides a default constructor with no construction of storage vectors and constructors from a list of \((i,j)\) index (coordinates) or a list of column indices by row, constructing the compressed storage vectors:

RowCsStorage(Number nr=0, Number nc=0);
RowCsStorage(Number, Number, const std::vector< std::vector<Number> >&, const std::vector< std::vector<Number> >&);
RowCsStorage(Number, Number, const std::vector< std::vector<Number> >&);
~RowCsStorage() {};

The class provides most of the virtual methods declared in MatrixStorage class. For sake of simplicity, we report here only function with Real type but versions with Complex, Matrix<Real>, Matrix<Complex> and mixed types are also provided. Some template line declarations are also omitted.

Number size() const;
Number pos(Number i, Number j, SymType s=_noSymmetry) const;
void positions(const std::vector<Number>&, const std::vector<Number>&, std::vector<Number>&, bool errorOn=true, SymType s=_noSymmetry) const;
void printEntries(std::ostream&, const std::vector<Real>&, Number, SymType) const;
void printCooMatrix(std::ostream&, const std::vector<Real>&, SymType s=_noSymmetry) const;
void loadFromFileDense(std::istream&, std::vector<Real>&, SymType, bool );
void loadFromFileCoo(std::istream& , std::vector<Real>&, SymType, bool);
template<typename M, typename V, typename R>
  void multMatrixVector(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
  void multVectorMatrix(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
void multMatrixVector(const std::vector<Real>&, const std::vector<Real>&, std::vector<Real>&, SymType) const;
void multVectorMatrix(const std::vector<Real>&, const std::vector<Real>&, std::vector<Real>&, SymType) const;
template<typename M1, typename M2, typename R>
  void addMatrixMatrix(const std::vector<M1>&, const std::vector<M2>&, std::vector<R>&) const; //!< templated row dense Matrix + Matrix
void addMatrixMatrix(const std::vector<Real>& m, const std::vector<Real>& v, std::vector<Real>& rv) const
  { addMatrixMatrix<>(m, v, rv);}

There is no printDenseMatrix() function because the general one defined in MatrixStorage class is sufficiently efficient.

The ColCsStorage class is very similar to the RowCsStorage class, except that it manages a row indices vector and a column pointers vector:

class ColCsStorage : public CsStorage
{
 protected :
  std::vector<Number> rowIndex_; // vector of row index of nonzero value
  std::vector<Number> colPointer_; // vector of positions of begining of cols
  ...
}

Its constructors have a similar structure (same arguments) and all prototypes of member functions are the same as ColCsStorage member functions. We do not repeat them.

See also

XXX=Row or Col

  • library=largeMatrix,

  • header=XXXCsStorage.hpp,

  • implementation=XXXCsStorage.cpp,

  • test=test_LargeMatrixCsStorage.cpp,

  • header dep= CsStorage.hpp, MatrixStorage.hpp, config.h, utils.h

The DualCsStorage / SymCsStorage classes#

Dual compressed storage travels the matrix as diagonal, strict lower and upper parts. Column indices and row pointers vectors are attached to strict lower part and row indices and column pointers vectors are attached to strict upper part:

class DualCsStorage : public CsStorage
{
  protected :
   std::vector<Number> colIndex_;   //vector of column index of nonzero value
   std::vector<Number> rowPointer_; //vector of positions of begining of rows
   std::vector<Number> rowIndex_;   //vector of row index of nonzero value
   std::vector<Number> colPointer_; //vector of positions of begining of cols
 ...
}

DualCsStorage class provides basic constructor (no construction of storage vectors), constructor from a pair of global numbering vectors or list of column indices by rows.

Both of them create the compressed storage vector using the auxiliary function buildStorage():

DualCsStorage(Number nr=0, Number nc=0);
DualCsStorage(Number, Number, const std::vector< std::vector<Number> >&, const std::vector< std::vector<Number> >&);
DualCsStorage(Number, Number, const std::vector<std::vector<Number> >&);
void buildStorage(const std::vector<std::vector<Number> >&);
~DualCsStorage() {};

The class provides most of the virtual methods declared in MatrixStorage class. For sake of simplicity, we report here only function with Real type but versions with Complex, RealMatrix, ComplexMatrix and mixed types are also provided. Some template line declarations are also omitted.

Number size() const;
Number lowerPartSize() const ;
Number upperPartSize() const;
Number pos(Number i, Number j, SymType s=_noSymmetry) const;
void positions(const std::vector<Number>&, const std::vector<Number>&, std::vector<Number>&, bool errorOn=true, SymType s=_noSymmetry) const;
void printEntries(std::ostream&, const std::vector<Real>&, Number, SymType) const;
void printCooMatrix(std::ostream&, const std::vector<Real>&, SymType s=_noSymmetry) const;
void loadFromFileDense(std::istream&, std::vector<Real>&, SymType, bool );
void loadFromFileCoo(std::istream& , std::vector<Real>&, SymType, bool);
template<typename M, typename V, typename R>
  void multMatrixVector(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
void multVectorMatrix(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
void multMatrixVector(const std::vector<Real>&, const std::vector<Real>&, std::vector<Real>&, SymType) const;
void multVectorMatrix(const std::vector<Real>&, const std::vector<Real>&, std::vector<Real>&, SymType) const;
template<typename M1, typename M2, typename R>
  void addMatrixMatrix(const std::vector<M1>&, const std::vector<M2>&, std::vector<R>&) const;
void addMatrixMatrix(const std::vector<Real>& m, const std::vector<Real>& v, std::vector<Real>& rv) const
  { addMatrixMatrix<>(m, v, rv);}

The SymCsStorage class is very similar to the DualCsStorage class, except that it addresses only square matrix with symmetric storage and, thus, does not manage row indices vector and column pointers vectors because the upper part has same storage as lower part (transposed).

Note that matrix may have no symmetry property but a symmetric storage. In that case, the upper part is stored.

class SymCsStorage : public CsStorage
{
  protected :
   std::vector<Number> colndex_;   //!< vector of row index of nonzero value
   std::vector<Number> rowPointer_; //!< vector of positions of begining of cols
  ...
}

Contrary to the row compressed storage, in the SymCsStorage, the diagonal is first stored then the lower part of the matrix.

Its constructors have a similar structure (same arguments) to the DualCsStorage constructors and all prototypes of member functions are the same as DualCsStorage member functions. We do not repeat them.

Different from other CsStorage classes, the SymCsStorage class provides some functions to deal with the SorSolver class and SsorSolver class. Only specialized functions with Real are listed in the following

void sorDiagonalMatrixVector(const std::vector<Real>& m, const std::vector<Real>& v, std::vector<Real>& rv, const Real w) const
{ sorDiagonalMatrixVector<>(m, v, rv, w); }
void sorLowerMatrixVector(const std::vector<Real>& m, const std::vector<Real>& v, std::vector<Real>& rv, const Real w, const SymType sym) const
{ sorLowerMatrixVector<>(m, v, rv, w, sym); }
void sorUpperMatrixVector(const std::vector<Real>& m, const std::vector<Real>& v, std::vector<Real>& rv, const Real w, const SymType sym) const
{ sorUpperMatrixVector<>(m, v, rv, w, sym); }
void sorDiagonalSolve(const std::vector<Real>& m, const std::vector<Real>& b, std::vector<Real>& x, const Real w) const
{ sorDiagonalSolve<>(m, b, x, w); }
void sorLowerSolve(const std::vector<Real>& m, const std::vector<Real>& b, std::vector<Real>& x, const Real w) const
{ sorLowerSolve<>(m, b, x, w); }
void sorUpperSolve(const std::vector<Real>& m, const std::vector<Real>& b, std::vector<Real>& x, const Real w, const SymType sym) const
{ sorUpperSolve<>(m, b, x, w, sym); }

See also

XXX=Dual or Sym

  • library=largeMatrix,

  • header=XXXCsStorage.hpp,

  • implementation=XXXCsStorage.cpp,

  • test=test_LargeMatrixCsStorage.cpp,

  • header dep= CsStorage.hpp, MatrixStorage.hpp, config.h, utils.h

Skyline storages#

With dense storages, skyline storages are the only storages that are compatibles with factorization methods like LU. Skyline storages consist in storage rows or columns from its first nonzero value up to its last nonzero value. When lower triangular part is addressed, the last nonzero value on a row is the diagonal matrix entry. When upper triangular part is addressed, the last nonzero value on a column is the diagonal matrix entry. Only dual and symmetric skyline storages are proposed here (resp. DualSkylineStorage and SymSkylineStorage classes).

  • Dual skyline stores first the diagonal of the matrix, then the strict lower “triangular” part and the strict upper “triangular” part of the matrix using the following storage vectors (\(A\) is a \(m\times n\) matrix):

    values = \((0, A_{11}, \ldots,A_{ii}, \ldots, A_{il},\ldots, A_{i,p_i}, \ldots, A_{i,l_i},\ldots, A_{q_j,j}, \ldots, A_{q_j,c_j})\)

    rowPointer = \((s_1,\ldots,s_i,\ldots,s_m,s_{m+1})\)

    colPointer = \((t_1,\ldots,t_j,\ldots,t_n,t_{n+1})\)

    where

    \[\begin{split}\begin{array}{l} l_i=\min(i-1,n-1),\ c_j=\min(j-1,m-1)\\ p_i=\min{0<j<l_i,\ A_{ij}\ne 0} \mathrm{\;and\;} q_j=\min{0<i<c_j,\ A_{ij}\ne 0}\\ s_1=0;\ s_i=s_{i-1}+i-s_{i-1}\ \forall i=2,m, \mathrm{\;and\;} t_1=0;\ t_j=s_{j-1}+j-t_{j-1}\ \forall j=2,n \end{array}\end{split}\]

    The rowPointer (resp. colPointer) vector gives the position in lower (resp. upper) part of the first nonzero entry of a row (resp. a column); \(s_{m+1}\) (resp. \(t_{n+1}\)) gives the number of entries stored in lower part (resp. upper part).

    For instance, the following \(5\times 6\) non-symmetric matrix

    \[\begin{split}A=\left[ \begin{array}{cccccc} 11 & 12 & 0 & 14 & 0 & 16\\ 0 & 22 & 23 & 0 & 0 & 26 \\ 31 & 32 & 33 & 0 & 0 & 0 \\ 0 & 0 & 43 & 44 & 0 & 0 \\ 51 & 0 & 53 & 54 & 55 & 0 \end{array} \right]\end{split}\]

    has the following storage vectors:

    values = \((0\ 11\ 22\ 33\ 44\ 45\ \ 22\ \ 31\ 32\ \ 43\ \ 51\ 0\ 53\ 54\ \ \ 12\ \ 23\ \ 14\ 0\ 0\ \ \ 16\ 26\ 0\ 0)\)

    rowPointer = \((0\ 0\ 0\ 2\ 3\ 7)\)

    colPointer = \((0\ 0\ 1\ 2\ 5\ 5\ 8)\)

The length of row \(i\) (resp. column \(j\)) is given by \(s_{i+1}-s_i\) (resp. \(t_{t+1}-t_j\)). The position in values vector of entry \((i,j)\) is given by the following relations (\(1\le i\le m\), \(1\le j\le n\)):

\[\begin{split}\begin{array}{l} \text{if } i=j\ \ \text{adr}(i,j)=i \\ \text{if } p_i\le j\le l_i \ \ \text{adr}(i,j)=\min(m,n)+s_{i+1}+j-i\\ \text{if } q_j\le i\le c_j \ \ \text{adr}(i,j)=\min(m,n)+s_{m+1}+t_{j+1}+i-j\\ \end{array}\end{split}\]

The SkylineStorage class#

The SkylineStorage class has no member attribute. It has some basic constructors just calling MatrixStorage constructors:

class SkylineStorage : public MatrixStorage
{
  public :
   SkylineStorage(AccessType = _dual);
   SkylineStorage(Number, AccessType = _dual);
   SkylineStorage(Number, Number, AccessType = _dual);
   virtual ~SkylineStorage() {}
  ...
}

SkylineStorage objects do not have to be instantiated. This abstract class acts as an interface to particular skyline storages and gathers all common functionalities of skyline storages, some being virtuals (some template line declarations are omitted):

virtual Number size() const = 0;
virtual Number lowerPartSize() const = 0;
virtual Number upperPartSize() const {return 0;}
template<typename Iterator>
 void printEntriesTriangularPart (StrucType, Iterator&, Iterator&, const std::vector<Number>&, Number, Number, Number, const String&, Number, std::ostream&) const;
 void printCooTriangularPart(std::ostream&, Iterator&, const std::vector<Number>&, Number, Number, bool, SymType sym = _noSymmetry) const;
 void printCooDiagonalPart(std::ostream&, Iterator&, Number) const;

template <typename T>
 void loadSkylineFromFileDense(std::istream&, std::vector<T>&, std::vector<Number>&, std::vector<Number>&, SymType, bool);
 void loadSkylineFromFileCoo(std::istream&, std::vector<T>&, std::vector<Number>&, std::vector<Number>&, SymType, bool);

template<typename MatIterator, typename VecIterator, typename ResIterator>
 void diagonalMatrixVector(MatIterator&, VecIterator&, ResIterator&, ResIterator&) const;
 void lowerMatrixVector(const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType sym) const;
 void upperMatrixVector(const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType) const;
 void diagonalVectorMatrix(MatIterator&, VecIterator&, ResIterator&, ResIterator&) const;
 void lowerVectorMatrix(const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType sym) const;
 void upperVectorMatrix(const std::vector<Number>&, MatIterator&, VecIterator&, ResIterator&, SymType) const;
 void sumMatrixMatrix(Mat1Iterator&, Mat2Iterator&, ResIterator&, ResIterator&) const;

The SkylineStorage class provides the real code of product of matrix and vector, using iterators as arguments to by-pass the template type of matrix values.

In order to deal with some specific solvers, this class also comes with several following functions. For simplicity, we only declare template line once.

template<typename MatIterator, typename VecIterator, typename XIterator>
 void bzLowerSolver(const MatIterator&, const MatIterator&, const VecIterator&,  const XIterator&, const XIterator&, const std::vector<size_t>::const_iterator) const;
 void bzLowerD1Solver(const MatIterator&, const VecIterator&, const XIterator&, const XIterator&, const std::vector<size_t>::const_iterator) const;
 void bzLowerConjD1Solver(const MatIterator&, const VecIterator&,  const XIterator&, const XIterator&,  const std::vector<size_t>::const_iterator) const;
 void bzDiagonalSolver(const MatIterator&, const VecIterator&,  const XIterator&, const XIterator&) const;
 void bzUpperSolver(const MatRevIterator&, const MatRevIterator&, const VecRevIterator&,  const XRevIterator&, const XRevIterator&, const std::vector<size_t>::const_reverse_iterator) const;
 void bzUpperD1Solver(const MatRevIterator&, const VecRevIterator&, const XRevIterator&, const XRevIterator&, const std::vector<size_t>::const_reverse_iterator) const;
 void bzUpperConjD1Solver(const MatRevIterator&, const VecRevIterator&, const XRevIterator&, const XRevIterator&, const std::vector<size_t>::const_reverse_iterator) const;

Like the product of matrix and vector, these functions take advantage of iterators as arguments to by-pass the template type of matrix values.

See also

  • library=largeMatrix,

  • header=SkylineStorage.hpp,

  • implementation=SkylineStorage.cpp,

  • test=test_LargeMatrixSkylineStorage.cpp,

  • header dep= MatrixStorage.hpp, config.h, utils.h

The DualSkylineStorage/SymSkylineStorage classes#

Dual skyline storage travels the matrix as diagonal, strict lower and upper parts. Row pointers vectors are attached to strict lower part and column pointers vectors are attached to strict upper part:

class DualSkylineStorage : public SkylineStorage
{
  protected :
   std::vector<Number> rowPointer_; // vector of positions of begining of rows
   std::vector<Number> colPointer_; // vector of positions of begining of cols
  ...
}

DualSkylineStorage class provides basic constructor (no construction of storage vectors), constructor from a pair of global numbering vectors or list of column indices by rows.

Both of them create the compressed storage vector using the auxiliary function buildStorage():

DualSkylineStorage(Number nr = 0, Number nc = 0);
DualSkylineStorage(Number, Number, const std::vector< std::vector<Number> >&, const std::vector< std::vector<Number> >&);
DualSkylineStorage(Number, Number, const std::vector< std::vector<Number> >&);
~DualSkylineStorage() {};

The class provides most of the virtual methods declared in MatrixStorage class. For sake of simplicity, we report here only function with Real type but versions with Complex, RealMatrix, ComplexMatrix and mixed types are also provided. Some template line declarations are also omitted.

Number size() const;
Number lowerPartSize() const ;
Number upperPartSize() const;
Number pos(Number i, Number j, SymType s=_noSymmetry) const;
void positions(const std::vector<Number>&, const std::vector<Number>&, std::vector<Number>&, bool errorOn=true, SymType s=_noSymmetry) const;
void printEntries(std::ostream&, const std::vector<Real>&, Number, SymType) const;
void printCooMatrix(std::ostream&, const std::vector<Real>&, SymType s=_noSymmetry) const;
void loadFromFileDense(std::istream&, std::vector<Real>&, SymType, bool );
void loadFromFileCoo(std::istream& , std::vector<Real>&, SymType, bool);
template<typename M, typename V, typename R>
  void multMatrixVector(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
void multVectorMatrix(const std::vector<M>&, const std::vector<V>&, std::vector<R>&) const;
void multMatrixVector(const std::vector<Real>&, const std::vector<Real>&, std::vector<Real>&, SymType) const;
void multVectorMatrix(const std::vector<Real>&, const std::vector<Real>&, std::vector<Real>&, SymType) const;
template<typename M1, typename M2, typename R>
  void addMatrixMatrix(const std::vector<M1>&, const std::vector<M2>&, std::vector<R>&) const;
template<typename T>
  void setDiagValueDualSkyline(std::vector<T>&, const T);
template<typename M>
  void lu(std::vector<M>& m, std::vector<M>& fa) const;
template<typename M, typename V, typename X>
  void lowerD1Solver(const std::vector<M>&, std::vector<V>&, std::vector<X>&) const;
  void diagonalSolver(const std::vector<M>&, std::vector<V>&, std::vector<X>&) const;
  void upperD1Solver(const std::vector<M>&, std::vector<V>&, std::vector<X>&, const SymType) const;
  void upperSolver(const std::vector<M>&, std::vector<V>&, std::vector<X>&) const;

The SymSkylineStorage class is very similar to the DualSkylineStorage class, except that it addresses only square matrix with symmetric storage and, thus, does not manage column pointers vectors because the upper part has same storage as lower part (with transposition) :

class SymSkylineStorage : public SkylineStorage
{
  protected :
   std::vector<Number> rowPointer_; //!< vector of positions of begining of cols
  ...
}

Note that the matrix may be not symmetric. In that case its upper part is stored.

Besides some similar functions to DualSkylineStorage class, the SymSkylineStorage provides more several functions to factorize matrix: LDLt and LDL* factorization

template<typename M>
  void ldlt(std::vector<M>& m, std::vector<M>& fa, const SymType sym = _symmetric) const;
  void ldlstar(std::vector<M>& m, std::vector<M>& fa) const;

See also

XXX=Dual or Sym

  • library=largeMatrix,

  • header=XXXSkylineStorage.hpp,

  • implementation=XXXSkylineStorage.cpp,

  • test=test_LargeMatrixSkylineStorage.cpp,

  • header dep= SkylineStorage.hpp, MatrixStorage.hpp, config.h, utils.h