All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Submatrices

Table of Contents

Previous: Subvectors     Next: Rows


Submatrices provide views on a specific part of a dense or sparse matrix just as subvectors provide views on specific parts of vectors. As such, submatrices act as a reference to a specific block within a matrix. This reference is valid and can be used in evary way any other dense or sparse matrix can be used as long as the matrix containing the submatrix is not resized or entirely destroyed. The submatrix also acts as an alias to the matrix elements in the specified block: Changes made to the elements (e.g. modifying values, inserting or erasing elements) are immediately visible in the matrix and changes made via the matrix are immediately visible in the submatrix. Blaze provides two submatrix types: DenseSubmatrix and SparseSubmatrix.


DenseSubmatrix


The blaze::DenseSubmatrix template represents a view on a specific submatrix of a dense matrix primitive. It can be included via the header file

The type of the dense matrix is specified via the template parameter:

template< typename MT >
class DenseSubmatrix;

MT specifies the type of the dense matrix primitive. DenseSubmatrix can be used with every dense matrix primitive, but does not work with any matrix expression type.


SparseSubmatrix


The blaze::SparseSubmatrix template represents a view on a specific submatrix of a sparse matrix primitive. It can be included via the header file

The type of the sparse matrix is specified via the template parameter:

template< typename MT >
class SparseSubmatrix;

MT specifies the type of the sparse matrix primitive. SparseSubmatrix can be used with every sparse matrix primitive, but does not work with any matrix expression type.


Setup of Submatrices


A view on a submatrix can be created very conveniently via the submatrix() function. This view can be treated as any other matrix, i.e. it can be assigned to, it can be copied from, and it can be used in arithmetic operations. A submatrix created from a row-major matrix will itself be a row-major matrix, a submatrix created from a column-major matrix will be a column-major matrix. The view can also be used on both sides of an assignment: The submatrix can either be used as an alias to grant write access to a specific submatrix of a dense matrix primitive on the left-hand side of an assignment or to grant read-access to a specific submatrix of a matrix primitive or expression on the right-hand side of an assignment. The following example demonstrates this in detail:

DenseMatrixType D1, D2;
SparseMatrixType S1, S2;
// ... Resizing and initialization
// Creating a view on the first 8x16 block of the dense matrix D1
blaze::DenseSubmatrix<DenseMatrixType> dsm = submatrix( D1, 0UL, 0UL, 8UL, 16UL );
// Creating a view on the second 8x16 block of the sparse matrix S1
blaze::SparseSubmatrix<SparseMatrixType> ssm = submatrix( S1, 0UL, 16UL, 8UL, 16UL );
// Creating a view on the addition of D2 and S2
dsm = submatrix( D2 + S2, 5UL, 10UL, 8UL, 16UL );
// Creating a view on the multiplication of D2 and S2
ssm = submatrix( D2 * S2, 7UL, 13UL, 8UL, 16UL );


Common Operations


The current size of the matrix, i.e. the number of rows or columns can be obtained via the rows() and columns() functions, the current total capacity via the capacity() function, and the number of non-zero elements via the nonZeros() function. However, since submatrices are views on a specific submatrix of a matrix, several operations are not possible on views, such as resizing and swapping:

typedef blaze::DenseSubmatrix<MatrixType> SubmatrixType;
MatrixType A;
// ... Resizing and initialization
// Creating a view on the a 8x12 submatrix of matrix A
SubmatrixType sm = submatrix( A, 0UL, 0UL, 8UL, 12UL );
sm.rows(); // Returns the number of rows of the submatrix
sm.columns(); // Returns the number of columns of the submatrix
sm.capacity(); // Returns the capacity of the submatrix
sm.nonZeros(); // Returns the number of non-zero elements contained in the submatrix
sm.resize( 10UL, 8UL ); // Compilation error: Cannot resize a submatrix of a matrix
SubmatrixType sm2 = submatrix( A, 8UL, 0UL, 12UL, 8UL );
swap( sm, sm2 ); // Compilation error: Swap operation not allowed


Element Access


The elements of a submatrix can be directly accessed with the function call operator:

MatrixType A;
// ... Resizing and initialization
// Creating a 8x8 submatrix, starting from position (4,4)
blaze::DenseSubmatrix<MatrixType> sm = submatrix( A, 4UL, 4UL, 8UL, 8UL );
// Setting the element (0,0) of the submatrix, which corresponds to
// the element at position (4,4) in matrix A
sm(0,0) = 2.0;
MatrixType A;
// ... Resizing and initialization
// Creating a 8x8 submatrix, starting from position (4,4)
blaze::SparseSubmatrix<MatrixType> sm = submatrix( A, 4UL, 4UL, 8UL, 8UL );
// Setting the element (0,0) of the submatrix, which corresponds to
// the element at position (4,4) in matrix A
sm(0,0) = 2.0;

Alternatively, the elements of a submatrix can be traversed via (const) iterators. Just as with matrices, in case of non-const submatrices, begin() and end() return an Iterator, which allows a manipulation of the non-zero values, in case of constant submatrices a ConstIterator is returned:

typedef blaze::DenseSubmatrix<MatrixType> SubmatrixType;
MatrixType A( 256UL, 512UL );
// ... Resizing and initialization
// Creating a reference to a specific submatrix of the dense matrix A
SubmatrixType sm = submatrix( A, 16UL, 16UL, 64UL, 128UL );
// Traversing the elements of the 0th row via iterators to non-const elements
for( SubmatrixType::Iterator it=sm.begin(0); it!=sm.end(0); ++it ) {
*it = ...; // OK: Write access to the dense submatrix value.
... = *it; // OK: Read access to the dense submatrix value.
}
// Traversing the elements of the 1st row via iterators to const elements
for( SubmatrixType::ConstIterator it=sm.begin(1); it!=sm.end(1); ++it ) {
*it = ...; // Compilation error: Assignment to the value via a ConstIterator is invalid.
... = *it; // OK: Read access to the dense submatrix value.
}
typedef blaze::SparseSubmatrix<MatrixType> SubmatrixType;
MatrixType A( 256UL, 512UL );
// ... Resizing and initialization
// Creating a reference to a specific submatrix of the sparse matrix A
SubmatrixType sm = submatrix( A, 16UL, 16UL, 64UL, 128UL );
// Traversing the elements of the 0th row via iterators to non-const elements
for( SubmatrixType::Iterator it=sm.begin(0); it!=sm.end(0); ++it ) {
it->value() = ...; // OK: Write access to the value of the non-zero element.
... = it->value(); // OK: Read access to the value of the non-zero element.
it->index() = ...; // Compilation error: The index of a non-zero element cannot be changed.
... = it->index(); // OK: Read access to the index of the sparse element.
}
// Traversing the elements of the 1st row via iterators to const elements
for( SubmatrixType::ConstIterator it=sm.begin(1); it!=sm.end(1); ++it ) {
it->value() = ...; // Compilation error: Assignment to the value via a ConstIterator is invalid.
... = it->value(); // OK: Read access to the value of the non-zero element.
it->index() = ...; // Compilation error: The index of a non-zero element cannot be changed.
... = it->index(); // OK: Read access to the index of the sparse element.
}


Element Insertion


Inserting/accessing elements in a sparse submatrix can be done by several alternative functions. The following example demonstrates all options:

MatrixType A( 256UL, 512UL ); // Non-initialized matrix of size 256x512
typedef blaze::SparseSubmatrix<MatrixType> SubmatrixType;
SubmatrixType sm = submatrix( A, 10UL, 10UL, 16UL, 16UL ); // View on a 16x16 submatrix of A
// The function call operator provides access to all possible elements of the sparse submatrix,
// including the zero elements. In case the subscript operator is used to access an element
// that is currently not stored in the sparse submatrix, the element is inserted into the
// submatrix.
sm(2,4) = 2.0;
// An alternative for inserting elements into the submatrix is the insert() function. However,
// it inserts the element only in case the element is not already contained in the submatrix.
sm.insert( 2UL, 6UL, 3.7 );
// Just as in case of sparse matrices, elements can also be inserted via the append() function.
// In case of submatrices, append() also requires that the appended element's index is strictly
// larger than the currently largest non-zero index in the according row or column of the
// submatrix and that the according row's or column's capacity is large enough to hold the new
// element. Note however that due to the nature of a submatrix, which may be an alias to the
// middle of a sparse matrix, the append() function does not work as efficiently for a
// submatrix as it does for a matrix.
sm.reserve( 2UL, 10UL );
sm.append( 2UL, 10UL, -2.1 );


Arithmetic Operations


Both dense and sparse submatrices can be used in all arithmetic operations that any other dense or sparse matrix can be used in. The following example gives an impression of the use of dense submatrices within arithmetic operations. All operations (addition, subtraction, multiplication, scaling, ...) can be performed on all possible combinations of dense and sparse matrices with fitting element types:

DenseMatrixType D1, D2, D3;
SparseMatrixType S1, S2;
SparseVectorType a, b;
// ... Resizing and initialization
typedef DenseSubmatrix<DenseMatrixType> SubmatrixType;
SubmatrixType sm = submatrix( D1, 0UL, 0UL, 8UL, 8UL ); // View on the 8x8 submatrix of matrix D1
// starting from row 0 and column 0
submatrix( D1, 0UL, 8UL, 8UL, 8UL ) = D2; // Dense matrix initialization of the 8x8 submatrix
// starting in row 0 and column 8
sm = S1; // Sparse matrix initialization of the second 8x8 submatrix
D3 = sm + D2; // Dense matrix/dense matrix addition
S2 = S1 - submatrix( D1, 8UL, 0UL, 8UL, 8UL ); // Sparse matrix/dense matrix subtraction
D2 = sm * submatrix( D1, 8UL, 8UL, 8UL, 8UL ); // Dense matrix/dense matrix multiplication
submatrix( D1, 8UL, 0UL, 8UL, 8UL ) *= 2.0; // In-place scaling of a submatrix of D1
D2 = submatrix( D1, 8UL, 8UL, 8UL, 8UL ) * 2.0; // Scaling of the a submatrix of D1
D2 = 2.0 * sm; // Scaling of the a submatrix of D1
submatrix( D1, 0UL, 8UL, 8UL, 8UL ) += D2; // Addition assignment
submatrix( D1, 8UL, 0UL, 8UL, 8UL ) -= S1; // Subtraction assignment
submatrix( D1, 8UL, 8UL, 8UL, 8UL ) *= sm; // Multiplication assignment
a = submatrix( D1, 4UL, 4UL, 8UL, 8UL ) * b; // Dense matrix/sparse vector multiplication


Submatrices on Submatrices


It is also possible to create a submatrix view on another submatrix. In this context it is important to remember that the type returned by the submatrix() function is the same type as the type of the given submatrix, since the view on a submatrix is just another view on the underlying matrix:

typedef blaze::DenseSubmatrix<MatrixType> SubmatrixType;
MatrixType D1;
// ... Resizing and initialization
// Creating a submatrix view on the dense matrix D1
SubmatrixType sm1 = submatrix( D1, 4UL, 4UL, 8UL, 16UL );
// Creating a submatrix view on the dense submatrix sm1
SubmatrixType sm2 = submatrix( sm1, 1UL, 1UL, 4UL, 8UL );


Performance Considerations


As powerful and convenient submatrices can be, in terms of performance they may have some disadvantages in comparison to matrix primitives. Whereas matrix primitives are guaranteed to be properly aligned and therefore provide maximum performance in all operations, due to its enormous flexibility a view on a matrix might not be properly aligned. This may cause a performance penalty on some platforms and/or for some operations. Blaze tries to also handle special cases as efficiently as possible, but please remember that some overhead may still occur!


Previous: Subvectors     Next: Rows