The ROOT Matrix Linear Algebra package.
The ROOT linear algebra package provides a complete environment in ROOT to perform matrix calculations such as matrix-vector and matrix-matrix multiplications and other linear algebra calculations like equation solving and eigenvalue decompositions.
The present package implements all the basic algorithms dealing with vectors, matrices, matrix columns, rows, diagonals, etc. In addition eigen-Vector analysis and several matrix decomposition have been added (LU,QRH,Cholesky,Bunch-Kaufman and SVD) . The decompositions are used in matrix inversion, equation solving.
ROOT provides the following matrix classes, among others:
TMatrixDBase
TMatrixF
TMatrixFSym
TVectorF
TMatrixD
TMatrixDSym
TMatrixDSparse
TDecompBase
TDecompChol
For a dense matrix, elements are arranged in memory in a ROW-wise fashion . For (n x m) matrices where n*m <=kSizeMax (=25 currently) storage space is available on the stack, thus avoiding expensive allocation/deallocation of heap space . However, this introduces of course kSizeMax overhead for each matrix object . If this is an issue recompile with a new appropriate value (>=0) for kSizeMax
Sparse matrices are also stored in row-wise fashion but additional row/column information is stored, see TMatrixTSparse source for additional details .
Another way to assign and store matrix data is through Use see for instance stressLinear.cxx file .
Unless otherwise specified, matrix and vector indices always start with 0, spanning up to the specified limit-1. However, there are constructors to which one can specify arbitrary lower and upper bounds, e.g. TMatrixD m(1,10,1,5) defines a matrix that ranges from 1..10, 1..5 (a(1,1)..a(10,5)).
A matrix has five properties, which are all set in the constructor:
precision
precision
is float (i.e. single precision), use the TMatrixF
class family. If the precision is double, use the TMatrixD
class family.type
general
(TMatrixD
), symmetric
(TMatrixDSym
) or sparse
(TMatrixDSparse
).size
index
sparse map
Use one of the following methods to access the information about the relevant matrix property:
Int_t GetRowLwb()
: Row lower-bound index.Int_t GetRowUpb()
: Row upper-bound index.Int_t GetNrows()
: Number of rows.Int_t GetColLwb()
: Column lower-bound index.Int_t GetColUpb()
: Column upper-bound index.Int_t GetNcols()
: Number of columns.Int_t GetNoElements()
: Number of elements, for a dense matrix this equals: fNrows x fNcols
.Double_t GetTol()
: Tolerance number that is used in decomposition operations.Int_t *GetRowIndexArray()
: For sparse matrices, access to the row index of fNrows+1
entries.Int_t *GetColIndexArray()
: For sparse matrices, access to the column index of fNelems
entries.Use one of the following methods to set a matrix property:
SetTol (Double_t tol)
ResizeTo (Int_t nrows,Int_t ncols, Int_t nr_nonzeros=-1)
nrows x ncols
. Index will start at 0.ResizeTo(Int_t row_lwb,Int_t row_upb, Int_t col_lwb,Int_t col_upb, Int_t nr_nonzeros=-1)
row_lwb:row_upb x col_lwb:col_upb
.SetRowIndexArray (Int_t *data)
fNrows+1
entries column lower-bound index.SetColIndexArray (Int_t *data)
fNelems
entries.SetSparseIndex (Int_t nelems new)
nelems_new
elements and copies (if exists) at most nelems_new
matrix elements over to the new structure.SetSparseIndex (const TMatrixDBase &a)
a
.SetSparseIndexAB (const TMatrixDSparse &a, const TMatrixDSparse &b)
a
and b
.Use one of the following constructors to create a matrix:
TMatrixD(Int_t nrows,Int_t ncols)
TMatrixD(Int_t row_lwb,Int_t row_upb,Int_t col_lwb,Int_t col_upb)
TMatrixD(Int_t nrows,Int_t ncols,const Double_t *data, Option_t option= "")
TMatrixD(Int_t row_lwb,Int_t row_upb,Int_t col_lwb,Int_t col_upb, const Double_t *data,Option_t *option="")
TMatrixDSym(Int_t nrows)
TMatrixDSym(Int_t row_lwb,Int_t row_upb)
TMatrixDSym(Int_t nrows,const Double_t *data,Option_t *option="")
TMatrixDSym(Int_t row_lwb,Int_t row_upb, const Double_t *data, Option_t *opt="")
TMatrixDSparse(Int_t nrows,Int_t ncols)
TMatrixDSparse(Int_t row_lwb,Int_t row_upb,Int_t col_lwb, Int_t col_upb)
TMatrixDSparse(Int_t row_lwb,Int_t row_upb,Int_t col_lwb,Int_t col_upb, Int_t nr_nonzeros,Int_t *row,Int_t *col,Double_t *data)
Use one of the following methods to fill a matrix:
SetMatrixArray(const Double_t*data,Option_t*option="")
option="F"
, the array fills the matrix column-wise else row-wise. This option is implemented for TMatrixD
and TMatrixDSym
. It is expected that the array data contains at least fNelems
entries.SetMatrixArray(Int_t nr,Int_t *irow,Int_t *icol,Double_t *data)
nr
entries with row index, column index and data entry. Only the entries with non-zero data value are inserted.operator()
, operator[]
i
,j
) is found in the sparse index table it will be entered, which involves some memory management. Therefore, before invoking this method in a loop set the index table first through a call to the SetSparseIndex()
method.SetSub(Int_t row_lwb,Int_t col_lwb,const TMatrixDBase &source)
row_lwb
,col_lwb
) can be both, dense or sparse.Use()
Use()
method.Use(TMatrixD &a)
Use(Int_t row_lwb,Int_t row_upb,Int_t col_lwb,Int_t col_upb,Double_t *data)
Use(Int_t nrows,Int_t ncols,Double_t *data)
Use(TMatrixDSym &a)
Use(Int_t nrows,Double_t *data)
Use(Int_t row_lwb,Int_t row_upb,Double_t *data)
Use(TMatrixDSparse &a)
Use(Int_t row_lwb,Int_t row_upb,Int_t col_lwb,Int_t col_upb,Int_t nr_no nzeros, Int_t *pRowIndex,Int_t *pColIndex,Double_t *pData)
Use(Int_t nrows,Int_t ncols,Int_t nr_nonzeros,Int_t *pRowIndex,Int_t *pColIndex,Double_t *pData)
Example
A Hilbert matrix is created by copying an array.
You can also assign the data array to the matrix without actually copying it.
The array data now contains the inverted matrix.
Now a unit matrix in sparse format is created.
Invert(Double_t &det=0)
function to invert a matrix:– or –
Both methods are available for general and symmetric matrices.
For matrices whose size is less than or equal to 6x6, the InvertFast(Double_t &det=0)
function is available. Here the Cramer algorithm will be applied, which is faster but less accurate.
You can also use the following decomposition classes (see → Matrix decompositions) for inverting a matrix:
Name | Matrix | Comment |
---|---|---|
TDecompLU | General | |
TDecompQRH | General | |
TDecompSVD | General | Can manipulate singular matrix. |
TDecompBK | Symmetric | |
TDecompChol | Symmetric | Matrix should also be positive definite. |
TDecompSparse | Sparse |
If the required matrix type is general, you also can handle symmetric matrices.
Example
This example shows how to check whether the matrix is singular before attempting to invert it.
The matrix/vector operations are classified according to BLAS (basic linear algebra subroutines) levels.
Description | Format | Comment |
---|---|---|
Element | C=A+B | overwrites A |
Wise sum | A+=B Add (A,alpha,B) TMatrixD(A,TMatrixD::kPlus,B) | A = A + α B constructor |
Element wise subtraction | C=A-B A-=B TMatrixD(A,TMatrixD::kMinus,B) | overwrites A constructor |
Matrix multiplication | C=A*B A*=B C.Mult(A,B) TMatrixD(A,TMatrixD::kMult,B) TMatrixD(A, TMatrixD(A, TMatrixD::kTransposeMult,B) TMatrixD(A, TMatrixD::kMultTranspose,B) | overwrites A constructor of A·B constructor of AT·B constructor of A·BT |
Element wise multiplication | ElementMult(A,B) | A(i,j)*= B(i,j) |
Element wise division | ElementDiv(A,B) | A(i,j)/= B(i,j) |
Description | Format | Comment |
---|---|---|
Element wise sum | C=r+A C=A+r A+=r | overwrites A |
Element wise subtraction | C=r-A C=A-r A-=r | overwrites A |
Matrix multiplication | C=r*A C=A*r A*=r | overwrites A |
Comparison between two matrices
Description | Output | Descriptions |
---|---|---|
A == B | Bool_t | equal to |
A != B | matrix | not equal |
A > B | matrix | greater than |
A >= B | matrix | greater than or equal to |
A < B | matrix | smaller than |
A <= B | matrix | smaller than or equal to |
AreCompatible(A,B) | Bool_t | compare matrix properties |
Compare(A,B) | Bool_t | return summary of comparison |
VerifyMatrixIdentity(A,B,verb, maxDev) | check matrix identity within maxDev tolerance |
Comparison between matrix and real number
Format | Output | Description |
---|---|---|
A == r | Bool_t | equal to |
A != r | Bool_t | not equal |
A > r | Bool_t | greater than |
A >= r | Bool_t | greater than or equal to |
A < r | Bool_t | smaller than |
A <= r | Bool_t | smaller than or equal to |
VerifyMatrixValue(A,r,verb, maxDev) | Bool_t | compare matrix value with r within maxDev tolerance |
A.RowNorm() | Double_t | norm induced by the infinity vector norm |
A.NormInf() | Double_t | |
A.ColNorm() | Double_t | norm induced by the 1 vector norm |
A.Norm1() | Double_t | |
A.E2Norm() | Double_t | square of the Euclidean norm |
A.NonZeros() | Int_t | |
A.Sum() | Double_t | number of elements unequal zero |
A.Min() | Double_t | |
A.Max() | Double_t | |
A.NormByColumn (v,"D") | TMatrixD | |
A.NormByRow (v,"D") | TMatrixD |
With the following matrix view classes, you can access the matrix elements:
TMatrixDRow
TMatrixDColumn
TMatrixDDiag
TMatrixDSub
For the matrix view classes TMatrixDRow
, TMatrixDColumn
and TMatrixDDiag
, the necessary assignment operators are available to interact with the vector class TVectorD
.
The sub matrix view classes TMatrixDSub
has links to the matrix classes TMatrixD
and TMatrixDSym.
The next table summarizes how to access the individual matrix elements in the matrix view classes.
Format | Comment |
---|---|
TMatrixDRow(A,i)(j) TMatrixDRow(A,i)[j] | element Aij |
TMatrixDColumn(A,j)(i) TMatrixDColumn(A,j)[i] | element Aij |
TMatrixDDiag(A(i) TMatrixDDiag(A[i] | element Aij |
TMatrixDSub(A(i) TMatrixDSub(A,rl,rh,cl,ch)(i,j) | element Aij element Arl+i,cl+j |
There are the following classes available for matrix decompositions:
n x n
matrix A
into P A = L U
.A
.A = U^T * U
where U
is a upper triangular matrix.With the TMatrixDEigen
and TMatrixDSymEigen
classes, you can compute eigenvalues and eigenvectors for general dense and symmetric real matrices.
The present package provides all facilities to completely AVOID returning matrices. Use "TMatrixD A(TMatrixD::kTransposed,B);" and other fancy constructors as much as possible. If one really needs to return a matrix, return a TMatrixTLazy object instead. The conversion is completely transparent to the end user, e.g. "TMatrixT m = THaarMatrixT(5);" and is efficient.
Since TMatrixT et al. are fully integrated in ROOT, they of course can be stored in a ROOT database.
Danger: For example, when the following snippet:
runs, it constructs matrix foo:foom, copies it onto stack as a return value and destroys foo:foom. Return value (a matrix) from foo() is then copied over to m (via a copy constructor), and the return value is destroyed. So, the matrix constructor is called 3 times and the destructor 2 times. For big matrices, the cost of multiple constructing/copying/destroying of objects may be very large. Some optimized compilers can cut down on 1 copying/destroying, but still it leaves at least two calls to the constructor. Note, TMatrixDLazy (see below) can construct TMatrixD m "inplace", with only a single call to the constructor.
as much as possible. That is, to add two matrices, it's much more efficient to write
than
(if both operand should be preserved, TMatrixD C = A; C += B;
is still better).
like in the following snippet (from $ROOTSYS/test/vmatrix.cxx
) that verifies that for an orthogonal matrix T, T'T = TT' = E.
(and without moving a lot of stuff around):
Note, constructing of, say, TMatrixDDiag does not involve any copying of any elements of the source matrix.
For example, creating of a Hilbert matrix can be done as follows:
of course, using a special method THilbertMatrixD() is still more optimal, but not by a whole lot. And that's right, class MakeHilbert is declared within a function and local to that function. It means one can define another MakeHilbert class (within another function or outside of any function, that is, in the global scope), and it still will be OK. Note, this currently is not yet supported by the interpreter CINT.
Another example is applying of a simple function to each matrix element:
Validation code $ROOTSYS/test/vmatrix.cxx
and vvector.cxx
contain a few more examples of that kind.
instead of returning an object return a "recipe" how to make it. The full matrix would be rolled out only when and where it's needed:
THaarMatrixD() is a class, not a simple function. However similar this looks to a returning of an object (see note #1 above), it's dramatically different. THaarMatrixD() constructs a TMatrixDLazy, an object of just a few bytes long. A special "TMatrixD(const TMatrixDLazy &recipe)" constructor follows the recipe and makes the matrix haar() right in place. No matrix element is moved whatsoever!