Blaze 3.9
Diagonal

Diagonals provide a view on a specific diagonal of a dense or sparse matrix. As such, diagonals act as a reference to a specific diagonal. This reference is valid and can be used in every way any other vector can be used as long as the matrix containing the diagonal is not resized or entirely destroyed. 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 on the diagonal.


Setup of Diagonals

A reference to a dense or sparse diagonal can be created very conveniently via the diagonal() function. Just as bands it can be included via the header files

#include <blaze/Blaze.h>
// or
#include <blaze/Math.h>
// or
Header file for the complete Band implementation.
Primary include file of the Blaze library.
Header file for the inclusion of the math module of the Blaze library.

and forward declared via the header file

#include <blaze/Forward.h>
Header file for all forward declarations of the Blaze library.

The diagonal() function expects just a single argument:

// ... Resizing and initialization
// Creating a reference to the diagonal of matrix A
auto diag = diagonal( A );
Efficient implementation of a dynamic matrix.
Definition: DynamicMatrix.h:242
decltype(auto) diagonal(Matrix< MT, SO > &matrix, RDAs... args)
Creating a view on the diagonal of the given matrix.
Definition: Band.h:380

The diagonal() function returns an expression representing the diagonal view. The type of this expression depends on the given diagonal arguments, primarily the type of the matrix and the compile time arguments. If the type is required, it can be determined via decltype or via the DiagonalExprTrait class template:

using MatrixType = blaze::DynamicMatrix<int>;
using DiagonalType1 = decltype( blaze::diagonal( std::declval<MatrixType>() ) );
using DiagonalType2 = blaze::DiagonalExprTrait<MatrixType>::Type;

The resulting view can be treated as any other vector, i.e. it can be assigned to, it can be copied from, and it can be used in arithmetic operations. By default, diagonals are considered column vectors, but this setting can be changed via the BLAZE_DEFAULT_TRANSPOSE_FLAG switch (see Default Vector Storage). The reference can also be used on both sides of an assignment: The diagonal can either be used as an alias to grant write access to a specific diagonal of a matrix primitive on the left-hand side of an assignment or to grant read-access to a specific diagonal of a matrix primitive or expression on the right-hand side of an assignment. The following example demonstrates this in detail:

// ... Resizing and initialization
// Setting the diagonal of matrix A to x
auto diag = diagonal( A );
diag = x;
// Setting the diagonal of matrix B to y
diagonal( B ) = y;
// Setting x to the diagonal of the result of the matrix multiplication
x = diagonal( A * B );
// Setting y to the digaonal of the result of the sparse matrix multiplication
y = diagonal( C * D );
Efficient implementation of a compressed matrix.
Definition: CompressedMatrix.h:239
Efficient implementation of an arbitrary sized sparse vector.
Definition: CompressedVector.h:220
Efficient implementation of an arbitrary sized vector.
Definition: DynamicVector.h:223


Element access

The elements of a diagonal can be directly accessed with the subscript operator:

// ... Resizing and initialization
// Creating a view on the diagonal of matrix A
auto diag = diagonal( A );
// Setting the 1st element of the dense diagonal, which corresponds
// to the 1st element on the diagonal of matrix A
diag[1] = 2.0;

The numbering of the diagonal elements is

                         \f[\left(\begin{array}{*{5}{c}}
                         0 & 1 & 2 & \cdots & N-1 \\
                         \end{array}\right),\f]

where N is the number of elements of the referenced diagonal. Alternatively, the elements of a diagonal can be traversed via iterators. Just as with vectors, in case of non-const diagonal, begin() and end() return an iterator, which allows to manipulate the elements, in case of constant diagonals an iterator to immutable elements is returned:

// ... Resizing and initialization
// Creating a reference to the diagonal of matrix A
auto diag = Diagonal( A );
// Traversing the elements via iterators to non-const elements
for( auto it=diag.begin(); it!=diag.end(); ++it ) {
*it = ...; // OK; Write access to the dense diagonal value
... = *it; // OK: Read access to the dense diagonal value.
}
// Traversing the elements via iterators to const elements
for( auto it=diag.cbegin(); it!=diag.cend(); ++it ) {
*it = ...; // Compilation error: Assignment to the value via a ConstIterator is invalid.
... = *it; // OK: Read access to the dense diagonal value.
}
// ... Resizing and initialization
// Creating a reference to the diagonal of matrix A
auto diag = diagonal( A );
// Traversing the elements via iterators to non-const elements
for( auto it=diag.begin(); it!=diag.end(); ++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 via iterators to const elements
for( auto it=diag.cbegin(); it!=diag.cend(); ++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 diagonal can be done by several alternative functions. The following example demonstrates all options:

blaze::CompressedMatrix<double,blaze::rowMajor> A( 10UL, 100UL ); // Non-initialized 10x100 matrix
auto diag( diagonal( A ) ); // Reference to the diagonal of A
// The subscript operator provides access to all possible elements of the sparse diagonal,
// including the zero elements. In case the subscript operator is used to access an element
// that is currently not stored in the sparse diagonal, the element is inserted into the
// diagonal.
diag[42] = 2.0;
// The second operation for inserting elements is the set() function. In case the element is
// not contained in the diagonal it is inserted into the diagonal, if it is already contained
// in the diagonal its value is modified.
diag.set( 45UL, -1.2 );
// An alternative for inserting elements into the diagonal is the insert() function. However,
// it inserts the element only in case the element is not already contained in the diagonal.
diag.insert( 50UL, 3.7 );


Common Operations

The current number of diagonal elements can be obtained via the size() function, the current capacity via the capacity() function, and the number of non-zero elements via the nonZeros() function. However, since diagonals are references to specific diagonals of a matrix, several operations are not possible, such as resizing and swapping. The following example shows this by means of a dense diagonal view:

// ... Resizing and initialization
// Creating a reference to the diagonal of matrix A
auto diag1 = diagonal( A );
diag1.size(); // Returns the number of elements in the diagonal
diag1.capacity(); // Returns the capacity of the diagonal
diag1.nonZeros(); // Returns the number of non-zero elements contained in the diagonal
diag1.resize( 84UL ); // Compilation error: Cannot resize the diagonal of a matrix
auto diag2 = diagonal( B );
swap( diag1, diag2 ); // Compilation error: Swap operation not allowed
void swap(DiagonalMatrix< MT, SO, DF > &a, DiagonalMatrix< MT, SO, DF > &b) noexcept
Swapping the contents of two matrices.
Definition: DiagonalMatrix.h:225


Arithmetic Operations

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

c[1] = 3.0;
blaze::DynamicMatrix<double,blaze::rowMajor> A( 4UL, 2UL ); // Non-initialized 4x2 matrix
blaze::DynamicMatrix<double,blaze::rowMajor> B( 2UL, 4UL ); // Non-initialized 2x4 matrix
auto diag1( diagonal( A ) ); // Reference to the diagonal of A
auto diag2( diagonal( B ) ); // Reference to the diagonal of B
diag1[0] = 0.0; // Manual initialization of the diagonal of A
diag2 = 1.0; // Homogeneous initialization of the diagonal of A
diagonal( A ) = a; // Dense vector initialization of the diagonal of A
diagonal( A ) = c; // Sparse vector initialization of the diagonal of A
b = diag2 + a; // Dense vector/dense vector addition
b = c + diagonal( A ); // Sparse vector/dense vector addition
b = diag2 * diagonal( A ); // Component-wise vector multiplication
diagonal( A ) *= 2.0; // In-place scaling of the diagonal
b = diagonal( A ) * 2.0; // Scaling of the diagonal
b = 2.0 * diagonal( A ); // Scaling of the diagonal
diagonal( A ) += a; // Addition assignment
diagonal( A ) -= c; // Subtraction assignment
diagonal( A ) *= diagonal( A ); // Multiplication assignment
double scalar = trans( c ) * diagonal( A ); // Scalar/dot/inner product between two vectors
A = diagonal( A ) * trans( c ); // Outer product between two vectors
decltype(auto) trans(const DenseMatrix< MT, SO > &dm)
Calculation of the transpose of the given dense matrix.
Definition: DMatTransExpr.h:766