![]() |
Matrix adapter for symmetric matrices.
More...
#include <BaseTemplate.h>
Matrix adapter for symmetric matrices.
The SymmetricMatrix class template is an adapter for existing dense and sparse matrix types. It inherits the properties and the interface of the given matrix type MT and extends it by enforcing the additional invariant of symmetry (i.e. the matrix is always equal to its transpose ). The type of the adapted matrix can be specified via the first template parameter:
The following examples give an impression of several possible symmetric matrices:
The storage order of a symmetric matrix is depending on the storage order of the adapted matrix type MT. In case the adapted matrix is stored in a row-wise fashion (i.e. is specified as blaze::rowMajor), the symmetric matrix will also be a row-major matrix. Otherwise, if the adapted matrix is column-major (i.e. is specified as blaze::columnMajor), the symmetric matrix will also be a column-major matrix.
The blaze::SymmetricMatrix adaptor and the blaze::HermitianMatrix adaptor share several traits. However, there are a couple of differences, both from a mathematical point of view as well as from an implementation point of view.
From a mathematical point of view, a matrix is called symmetric when it is equal to its transpose ( ) and it is called Hermitian when it is equal to its conjugate transpose (
). For matrices of real values, however, these two conditions coincide, which means that symmetric matrices of real values are also Hermitian and Hermitian matrices of real values are also symmetric.
From an implementation point of view, Blaze restricts Hermitian matrices to numeric data types (i.e. all integral types except bool, floating point and complex types), whereas symmetric matrices can also be block structured (i.e. can have vector or matrix elements). For built-in element types, the HermitianMatrix adaptor behaves exactly like the according SymmetricMatrix implementation. For complex element types, however, the Hermitian property is enforced (see also The Hermitian Property is Always Enforced!).
A symmetric matrix is used exactly like a matrix of the underlying, adapted matrix type MT. It also provides (nearly) the same interface as the underlying matrix type. However, there are some important exceptions resulting from the symmetry constraint:
In case a resizable matrix is used (as for instance blaze::HybridMatrix, blaze::DynamicMatrix, or blaze::CompressedMatrix), this means that the according constructors, the resize()
and the extend()
functions only expect a single parameter, which specifies both the number of rows and columns, instead of two (one for the number of rows and one for the number of columns):
In case a matrix with a fixed size is used (as for instance blaze::StaticMatrix), the number of rows and number of columns must be specified equally:
This means that modifying the element of a symmetric matrix also modifies its counterpart element
. Also, it is only possible to assign matrices that are symmetric themselves:
The same restriction also applies to the append()
function for sparse matrices: Appending the element additionally inserts the element
into the matrix. Despite the additional insertion, the
append()
function still provides the most efficient way to set up a symmetric sparse matrix. In order to achieve the maximum efficiency, the capacity of the individual rows/columns of the matrix should to be specifically prepared with reserve()
calls:
The symmetry property is also enforced for symmetric custom matrices: In case the given array of elements does not represent a symmetric matrix, a std::invalid_argument exception is thrown:
Finally, the symmetry property is enforced for views (rows, columns, submatrices, ...) on the symmetric matrix. The following example demonstrates that modifying the elements of an entire row of the symmetric matrix also affects the counterpart elements in the according column of the matrix:
The next example demonstrates the (compound) assignment to submatrices of symmetric matrices. Since the modification of element of a symmetric matrix also modifies the element
, the matrix to be assigned must be structured such that the symmetry of the symmetric matrix is preserved. Otherwise a std::invalid_argument exception is thrown:
Although this results in a small loss of efficiency (especially in case all default values are overridden afterwards), this property is important since otherwise the symmetric property of dense symmetric matrices could not be guaranteed:
A SymmetricMatrix can participate in numerical operations in any way any other dense or sparse matrix can participate. It can also be combined with any other dense or sparse vector or matrix. The following code example gives an impression of the use of SymmetricMatrix within arithmetic operations:
It is also possible to use block-structured symmetric matrices:
Also in this case, the SymmetricMatrix class template enforces the invariant of symmetry and guarantees that a modifications of element of the adapted matrix is also applied to element
:
When the symmetric property of a matrix is known beforehands using the SymmetricMatrix adaptor instead of a general matrix can be a considerable performance advantage. The Blaze library tries to exploit the properties of symmetric matrices whenever possible. However, there are also situations when using a symmetric matrix introduces some overhead. The following examples demonstrate several situations where symmetric matrices can positively or negatively impact performance.
When multiplying two matrices, at least one of which is symmetric, Blaze can exploit the fact that and choose the fastest and most suited combination of storage orders for the multiplication. The following example demonstrates this by means of a dense matrix/sparse matrix multiplication:
Intuitively, the chosen combination of a row-major and a column-major matrix is the most suited for maximum performance. However, Blaze evaluates the multiplication as
which significantly increases the performance since in contrast to the original formulation the optimized form can be vectorized. Therefore, in the context of matrix multiplications, using the SymmetricMatrix adapter is obviously an advantage.
A similar optimization is possible in case of matrix/vector multiplications:
In this example it is not intuitively apparent that using a row-major matrix is not the best possible choice in terms of performance since the computation cannot be vectorized. Choosing a column-major matrix instead, however, would enable a vectorized computation. Therefore Blaze exploits the fact that A
is symmetric, selects the best suited storage order and evaluates the multiplication as
which also significantly increases the performance.
Another example is the optimization of a row view on a column-major symmetric matrix:
Usually, a row view on a column-major matrix results in a considerable performance decrease in comparison to a row view on a row-major matrix due to the non-contiguous storage of the matrix elements. However, in case of symmetric matrices, Blaze instead uses the according column of the matrix, which provides the same performance as if the matrix would be row-major. Note that this also works for column views on row-major matrices, where Blaze can use the according row instead of a column in order to provide maximum performance.
In contrast to using a symmetric matrix on the right-hand side of an assignment (i.e. for read access), which introduces absolutely no performance penalty, using a symmetric matrix on the left-hand side of an assignment (i.e. for write access) may introduce additional overhead when it is assigned a general matrix, which is not symmetric at compile time:
When assigning a general, potentially not symmetric matrix to a symmetric matrix it is necessary to check whether the matrix is symmetric at runtime in order to guarantee the symmetry property of the symmetric matrix. In case it turns out to be symmetric, it is assigned as efficiently as possible, if it is not, an exception is thrown. In order to prevent this runtime overhead it is therefore generally advisable to assign symmetric matrices to other symmetric matrices.
In this context it is especially noteworthy that in contrast to additions and subtractions the multiplication of two symmetric matrices does not necessarily result in another symmetric matrix: