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

Table of Contents

Previous: Matrix Types     Next: View Types


Constructors


Matrices are just as easy and intuitive to create as vectors. Still, there are a few rules to be aware of:


Default Construction

// All matrices can be default constructed. Whereas the size of
// a StaticMatrix is fixed via the second and third template
// parameter, the initial size of a constructed DynamicMatrix
// or CompressedMatrix is 0.
StaticMatrix<int,2UL,2UL> M1; // Instantiation of a 2x2 single precision row-major
// matrix. All elements are initialized to 0.
DynamicMatrix<float> M2; // Instantiation of a single precision dynamic
// row-major matrix with 0 rows and 0 columns.
DynamicMatrix<double,columnMajor> M3; // Instantiation of a double precision dynamic
// column-major matrix with 0 rows and 0 columns.
CompressedMatrix<int> M4; // Instantiation of a compressed integer
// row-major matrix of size 0x0.
CompressedMatrix<double,rowVector> M5; // Instantiation of a compressed double precision
// column-major matrix of size 0x0.


Construction with Specific Size

The DynamicMatrix and CompressedMatrix classes offer a constructor that allows to immediately give the matrices a specific number of rows and columns:

DynamicMatrix<int> M6( 5UL, 4UL ); // Instantiation of a 5x4 dynamic row-major
// matrix. The elements are not initialized.
DynamicMatrix<double,columnMajor> M7( 3UL, 7UL ); // Instantiation of a 3x7 dynamic column-major
// matrix. The elements are not initialized.
CompressedMatrix<float,rowMajor> M8( 8UL, 6UL ); // Instantiation of a 8x6 compressed row-major
// matrix. The elements are not initialized.

Note that dense matrices (in this case DynamicMatrix) immediately allocate enough capacity for all matrix elements. Sparse matrices on the other hand (in this example CompressedMatrix) merely acquire the size, but don't necessarily allocate memory.


Initialization Constructors

All dense matrix classes offer a constructor for a direct, homogeneous initialization of all matrix elements. In contrast, for sparse matrices the predicted number of non-zero elements can be specified.

StaticMatrix<int,4UL,3UL,columnMajor> M9( 7 ); // Instantiation of a 4x3 integer row-major
// matrix. All elements are initialized to 7.
DynamicMatrix<float> M10( 2UL, 5UL, 2.0F ); // Instantiation of a 2x5 single precision row-major
// matrix. All elements are initialized to 2.0F.
CompressedMatrix<int> M11( 3UL, 4UL, 4 ); // Instantiation of a 3x4 integer column-major
// matrix. All elements are initialized to 4.

The StaticMatrix class offers a special initialization constructor. For StaticMatrix of up to 10 elements the vector elements can be individually specified in the constructor:

StaticMatrix<int,3UL,1UL> M12( 2, 5, -1 );
StaticMatrix<float,2UL,3UL,columnMajor> M13( -0.1F, 4.2F, -7.1F,
-0.8F, 1.3F, 4.2F );
StaticMatrix<double,3UL,3UL,rowVector> M14( 1.3, -0.4, 8.3,
0.2, -1.5, -2.6,
1.3, 9.3, -7.1 );


Copy Construction

All dense and sparse matrices can be created as a copy of another dense or sparse matrix.

StaticMatrix<int,5UL,4UL,rowMajor> M15( M6 ); // Instantiation of the dense row-major matrix M15
// as copy of the dense row-major matrix M7.
DynamicMatrix<int,columnMajor> M16( M8 ); // Instantiation of the dense column-major matrix M16
// as copy of the sparse row-major matrix M9.
CompressedMatrix<double,rowMajor> M17( M7 ); // Instantiation of the compressed row-major matrix
// M17 as copy of the dense column-major matrix M8.
CompressedMatrix<float,rowMajor> M18( M8 ); // Instantiation of the compressed row-major matrix
// M18 as copy of the compressed row-major matrix M9.

Note that it is not possible to create a StaticMatrix as a copy of a matrix with a different number of rows and/or columns:

StaticMatrix<int,4UL,5UL,rowMajor> M19( M6 ); // Runtime error: Number of rows and columns
// does not match!
StaticMatrix<int,4UL,4UL,columnMajor> M20( M9 ); // Compile time error: Number of columns does
// not match!


Assignment


There are several types of assignment to dense and sparse matrices: Homogeneous Assignment, Array Assignment, Copy Assignment, and Compound Assignment.


Homogeneous Assignment

It is possible to assign the same value to all elements of a dense matrix. All dense matrix classes provide an according assignment operator:

// Setting all integer elements of the StaticMatrix to 4
M1 = 4;
// Setting all double precision elements of the DynamicMatrix to 3.5
M2 = 3.5


Array Assignment

Dense matrices can also be assigned a static array:

int array1[4] = { 1, 2, 3, 4 };
double array2[3] = { 3.1, 6.4, -0.9 };
M1 = array1;
M2 = array1;
M3 = array2;

Note that due to the different storage order, the matrix M1 is initialized differently than matrix M2:

$ M1 = \left(\begin{array}{*{2}{c}} 1 & 2 \\ 3 & 4 \\ \end{array}\right),\quad M2 = \left(\begin{array}{*{2}{c}} 1 & 3 \\ 2 & 4 \\ \end{array}\right)$


Copy Assignment

All kinds of matrices can be assigned to each other. The only restriction is that since a StaticMatrix cannot change its size, the assigned matrix must match both in the number of rows and in the number of columns.

// ... Initialization of the matrices
M1 = M2; // OK: Assignment of a 3x2 dense row-major matrix to another 3x2 dense row-major matrix
M1 = M4; // OK: Assignment of a 3x2 sparse row-major matrix to a 3x2 dense row-major matrix
M1 = M3; // Runtime error: Cannot assign a 5x2 matrix to a 3x2 static matrix
M1 = M5; // OK: Assignment of a 3x2 sparse column-major matrix to a 3x2 dense row-major matrix


Compound Assignment

Compound assignment is also available for matrices: addition assignment, subtraction assignment, and multiplication assignment. In contrast to plain assignment, however, the number of rows and columns of the two operands have to match according to the arithmetic operation.

// ... Initialization of the matrices
M1 += M2; // OK: Addition assignment between two row-major matrices of the same dimensions
M1 -= M3; // OK: Subtraction assignment between between a row-major and a column-major matrix
M1 += M4; // Runtime error: No compound assignment between matrices of different size
M1 -= M5; // Compilation error: No compound assignment between matrices of different size
M2 *= M6; // OK: Multiplication assignment between two row-major matrices

Note that the multiplication assignment potentially changes the number of columns of the target matrix:

$\left(\begin{array}{*{3}{c}} 2 & 0 & 1 \\ 0 & 3 & 2 \\ \end{array}\right) \times \left(\begin{array}{*{2}{c}} 4 & 0 \\ 1 & 0 \\ 0 & 3 \\ \end{array}\right) = \left(\begin{array}{*{2}{c}} 8 & 3 \\ 3 & 6 \\ \end{array}\right)$

Since a StaticMatrix cannot change its size, only a quadratic StaticMatrix can be used in a multiplication assignment with other quadratic matrices of the same dimensions.


Common Matrix Operations


Number of Rows of a Matrix

The current number of rows of a matrix can be acquired via the rows() function:

// Instantiating a dynamic matrix with 10 rows and 8 columns
blaze::DynamicMatrix<int> M1( 10UL, 8UL );
M1.rows(); // Returns 10
// Instantiating a compressed matrix with 5 rows and 12 columns
M2.rows(); // Returns 5


Number of Columns of a Matrix

The current number of columns of a matrix can be acquired via the columns() function:

// Instantiating a dynamic matrix with 6 rows and 8 columns
M1.columns(); // Returns 8
// Instantiating a compressed matrix with 4 rows and 7 columns
M2.columns(); // Returns 7


Capacity of a Matrix

The capacity() function returns the internal capacity of a DynamicMatrix or CompressedMatrix. Note that the capacity of a matrix doesn't have to be equal to the size of a matrix. In case of a dense matrix the capacity will always be greater or equal than the total number of elements of the matrix. In case of a sparse matrix, the capacity will usually be much less than the total number of elements.

M1.capacity(); // Returns at least 35


Number of Non-Zero Elements

For both dense and sparse matrices the current number of non-zero elements can be queried via the nonZeros() function. In case of matrices there are two flavors of the nonZeros() function: One returns the total number of non-zero elements in the matrix, the second returns the number of non-zero elements in a specific row (in case of a row-major matrix) or column (in case of a column-major matrix). Sparse matrices directly return their number of non-zero elements, dense matrices traverse their elements and count the number of non-zero elements.

// ... Initializing the matrices
M1.nonZeros(); // Returns the total number of non-zero elements in the dense matrix
M1.nonZeros( 2 ); // Returns the number of non-zero elements in row 2
M2.nonZeros(); // Returns the total number of non-zero elements in the sparse matrix
M2.nonZeros( 3 ); // Returns the number of non-zero elements in column 3


Resize/Reserve


The dimensions of a StaticMatrix are fixed at compile time by the second and third template parameter. In contrast, the number or rows and/or columns of DynamicMatrix and CompressedMatrix can be changed at runtime:

DynamicMatrix<int,rowMajor> M1;
CompressedMatrix<int,columnMajor> M2( 3UL, 2UL );
// Adapting the number of rows and columns via the resize() function. The (optional)
// third parameter specifies whether the existing elements should be preserved.
M1.resize( 2UL, 2UL ); // Resizing matrix M1 to 2x2 elements. Elements of built-in type
// remain uninitialized, elements of class type are default
// constructed.
M1.resize( 3UL, 1UL, false ); // Resizing M1 to 3x1 elements. The old elements are lost, the
// new elements are NOT initialized!
M2.resize( 5UL, 7UL, true ); // Resizing M2 to 5x7 elements. The old elements are preserved.
M2.resize( 3UL, 2UL, false ); // Resizing M2 to 3x2 elements. The old elements are lost.

When the internal capacity of a matrix is no longer sufficient, the allocation of a larger junk of memory is triggered. In order to avoid frequent reallocations, the reserve() function can be used up front to set the internal capacity:

M1.reserve( 100 );
M1.rows(); // Returns 0
M1.capacity(); // Returns at least 100

Additionally it is possible to reserve memory in a specific row (for a row-major matrix) or column (for a column-major matrix):

M1.reserve( 1, 4 ); // Reserving enough space for four non-zero elements in row 4

Note that resizing a matrix invalidates all existing views (see View Types) on the matrix:

MatrixType M1( 10UL, 20UL ); // Creating a 10x20 matrix
DenseRow<MatrixType> row8 = row( M1, 8UL ); // Creating a view on the 8th row of the matrix
M1.resize( 6UL, 20UL ); // Resizing the matrix invalidates the view


Element Access


The easiest way to access a specific dense or sparse matrix element is via the function call operator. The indices to access a matrix are zero-based:

M1(0,0) = 1;
M1(0,1) = 3;
// ...
M2(0,2) = 4.1;
M2(1,1) = -6.3;

Since dense matrices allocate enough memory for all contained elements, using the function call operator on a dense matrix directly returns a reference to the accessed value. In case of a sparse matrix, if the accessed value is currently not contained in the matrix, the value is inserted into the matrix prior to returning a reference to the value, which can be much more expensive than the direct access to a dense matrix. Consider the following example:

for( size_t i=0UL; i<M1.rows(); ++i ) {
for( size_t j=0UL; j<M1.columns(); ++j ) {
... = M1(i,j);
}
}

Although the compressed matrix is only used for read access within the for loop, using the function call operator temporarily inserts 16 non-zero elements into the matrix. Therefore, all matrices (sparse as well as dense) offer an alternate way via the begin() and end() functions to traverse all contained elements by iterator. Note that it is not possible to traverse all elements of the matrix, but that it is only possible to traverse elements in a row/column-wise fashion. In case of a non-const matrix, begin() and end() return an Iterator, which allows a manipulation of the non-zero value, in case of a constant matrix a ConstIterator is returned:

CompressedMatrix<int,rowMajor> M1( 4UL, 6UL );
// Traversing the matrix by Iterator
for( size_t i=0UL; i<A.rows(); ++i ) {
for( CompressedMatrix<int,rowMajor>::Iterator it=A.begin(i); it!=A.end(i); ++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 non-zero element.
}
}
// Traversing the matrix by ConstIterator
for( size_t i=0UL; i<A.rows(); ++i ) {
for( CompressedMatrix<int,rowMajor>::ConstIterator it=A.begin(i); it!=A.end(i); ++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 non-zero element.
}
}


Element Insertion


Whereas a dense matrix always provides enough capacity to store all matrix elements, a sparse matrix only stores the non-zero elements. Therefore it is necessary to explicitly add elements to the matrix. The first possibility to add elements to a sparse matrix is the function call operator:

CompressedMatrix<int> M1( 3UL, 4UL );
M1(1,2) = 9;

In case the element at the given position is not yet contained in the sparse matrix, it is automatically inserted. Otherwise the old value is replaced by the new value 2. The operator returns a reference to the sparse vector element.
However, insertion of elements can be better controlled via the insert() function. In contrast to the function call operator it emits an exception in case the element is already contained in the matrix. In order to check for this case, the find() function can be used:

// In case the element at position (2,3) is not yet contained in the matrix it is inserted
// with a value of 4.
if( M1.find( 2, 3 ) == M1.end( 2 ) )
M1.insert( 2, 3, 4 );

Although the insert() function is very flexible, due to performance reasons it is not suited for the setup of large sparse matrices. A very efficient, yet also very low-level way to fill a sparse matrix is the append() function. It requires the sparse matrix to provide enough capacity to insert a new element in the specified row. Additionally, the index of the new element must be larger than the index of the previous element in the same row. Violating these conditions results in undefined behavior!

M1.reserve( 0, 3 ); // Reserving space for three non-zero elements in row 0
M1.append( 0, 1, 2 ); // Appending the element 2 in row 0 at column index 1
M1.append( 0, 2, -4 ); // Appending the element -4 in row 0 at column index 2
// ...

The most efficient way to fill a sparse matrix with elements, however, is a combination of reserve(), append(), and the finalize() function:

M1.reserve( 3 ); // Reserving enough space for 3 non-zero elements
M1.append( 0, 1, 1 ); // Appending the value 1 in row 0 with column index 1
M1.finalize( 0 ); // Finalizing row 0
M1.append( 1, 1, 2 ); // Appending the value 2 in row 1 with column index 1
M1.finalize( 1 ); // Finalizing row 1
M1.append( 2, 0, 3 ); // Appending the value 3 in row 2 with column index 0
M1.finalize( 2 ); // Finalizing row 2


Reset/Clear


In order to reset all elements of a dense or sparse matrix, the reset() function can be used. The number of rows and columns of the matrix are preserved:

// Setting up a single precision row-major matrix, whose elements are initialized with 2.0F.
blaze::DynamicMatrix<float> M1( 4UL, 5UL, 2.0F );
// Resetting all elements to 0.0F.
reset( M1 ); // Resetting all elements
M1.rows(); // Returns 4: size and capacity remain unchanged

In order to return a matrix to its default state (i.e. the state of a default constructed matrix), the clear() function can be used:

// Setting up a single precision row-major matrix, whose elements are initialized with 2.0F.
blaze::DynamicMatrix<float> M1( 4UL, 5UL, 2.0F );
// Resetting all elements to 0.0F.
clear( M1 ); // Resetting the entire matrix
M1.rows(); // Returns 0: size is reset, but capacity remains unchanged


Matrix Transpose


Matrices can be transposed via the trans() function. Row-major matrices are transposed into a column-major matrix and vice versa:

M1 = M2; // Assigning a column-major matrix to a row-major matrix
M1 = trans( M2 ); // Assigning the transpose of M2 (i.e. a row-major matrix) to M1
M1 += trans( M2 ); // Addition assignment of two row-major matrices


Swap

Via the swap() function it is possible to completely swap the contents of two matrices of the same type:

swap( M1, M2 ); // Swapping the contents of M1 and M2


Previous: Matrix Types     Next: View Types