Introduce zero matrices into Blaze

Issue #200 resolved
Mikhail Katliar created an issue

Similarly to IdentityMatrix #3, in some cases it is useful to have a ZeroMatrix expression, such that multiplication with ZeroMatrix always results in a ZeroMatrix of appropriate size, and addition of ZeroMatrix equals to the original expression. Below is an example of when ZeroMatrix could be useful:

template <typename MatrixA, typename MatrixB>
auto someFunction(MatrixA const& A, MatrixB const& B)
{
    return trans(A) * A + trans(B) * B;
}


void someOtherFunction()
{
    blaze::DynamicMatrix<double> A(10, 10), B(10, 10);
    A = 0.;
    randomize(B);

    // Here a product of a 10x10 zero matrix with itself will be calculated.
    blaze::DynamicMatrix<double> C = someFunction(A, B);

    // Here no unnecessary calculations are performed
    blaze::DynamicMatrix<double> C1 = someFunction(blaze::ZeroMatrix<double>(10, 10), B);
}

Comments (5)

  1. Klaus Iglberger

    Hi Mikhail!

    Thanks for creating this proposal. We have had ZeroMatrix on our list for a long time, so we can assure you that there will be a ZeroMatrix implementation eventually. Unfortunately we just cannot tell exactly when it will be added. Still, thanks again,

    Best regards,

    Klaus!

  2. Klaus Iglberger

    Summary

    The feature has been implemented, tested, optimized, and documented as required. It is immediately available via cloning the Blaze repository and will be officially released in Blaze 3.5.

    ZeroVector

    The ZeroVector class template is the representation of an immutable, arbitrary sized zero vector with N elements of arbitrary type. The type of the elements and the transpose flag of the vector can be specified via the two template parameters:

    template< typename Type, bool TF >
    class ZeroVector;
    
    • Type: specifies the type of the vector elements. ZeroVector can be used with any non-cv-qualified, non-reference, non-pointer element type.
    • TF : specifies whether the vector is a row vector (blaze::rowVector) or a column vector (blaze::columnVector). The default value is blaze::columnVector.

    It is not possible to insert, erase or modify the elements of a zero vector. It is only possible to read from the elements:

    using blaze::ZeroVector;
    using blaze::columnVector;
    
    // Creating a 4D zero column vector
    ZeroVector<double,columnVector> a( 4 );
    
    // The subscript operator provides access to all possible elements of the zero vector,
    // including the zero elements.
    a[1] = 2.0;       // Compilation error: It is not possible to write to a zero vector
    double d = a[2];  // Access to the element at index 2
    
    // In order to traverse all non-zero elements currently stored in the vector, the begin()
    // and end() functions can be used.
    for( ZeroVector<double,columnVector>::Iterator i=a.begin(); i!=a.end(); ++i ) {
       ... = i->value();  // Access to the value of the non-zero element
       ... = i->index();  // Access to the index of the non-zero element
    }
    

    The use of ZeroVector is very natural and intuitive. All operations (addition, subtraction, multiplication, ...) can be performed on all possible combinations of dense and sparse vectors with fitting element types. The following example gives an impression of the use of ZeroVector:

    using blaze::ZeroVector;
    using blaze::CompressedVector;
    using blaze::DynamicVector;
    
    ZeroVector<double> z( 3 );  // 3-dimensional zero vector
    
    DynamicVector<double,columnMajor> a( 3 );  // 3-dimensional dynamic dense vector
    CompressedVector<double,rowMajor> b( 3 );  // 3-dimensional double precision sparse vector
    CompressedVector<float,rowMajor>  c( 3 );  // 3-dimensional single precision sparse vector
    // ... Initialization of a, b, and c
    
    DynamicVector<double>    d( z );  // Creation of a new dense vector as a copy of z
    CompressedVector<double> e;       // Creation of a default sparse vector
    
    d = z + a;    // Addition of a zero vector and a dense vector
    d = b - z;    // Subtraction of a sparse vector and a zero vector
    e = z * c;    // Vector multiplication between two vectors of different element types
    
    d = 2.0 * z;  // Scaling of a zero vector
    e = z * 2.0;  // Scaling of a zero vector
    

    ZeroMatrix

    The ZeroMatrix class template is the representation of an immutable, arbitrary sized zero matrix with M times N elements of arbitrary type. The type of the elements and the storage order of the matrix can be specified via the two template parameters:

    template< typename Type, bool SO >
    class ZeroMatrix;
    
    • Type: specifies the type of the matrix elements. ZeroMatrix can be used with any non-cv-qualified, non-reference, non-pointer element type.
    • SO : specifies the storage order (blaze::rowMajor, blaze::columnMajor) of the matrix. The default value is blaze::rowMajor.

    It is not possible to insert, erase or modify the elements of a zero matrix. It is only possible to read from the elements:

    using blaze::ZeroMatrix;
    using blaze::rowMajor;
    
    // Creating a row-major 4x6 zero matrix with 4 rows and 6 columns
    ZeroMatrix<double,rowMajor> Z( 4, 6 );
    
    // The function call operator provides access to all possible elements of the zero matrix,
    // including the zero elements.
    Z(1,2) = 2.0;       // Compilation error: It is not possible to write to a zero matrix
    double d = Z(2,1);  // Access to the element (2,1)
    
    // In order to traverse all non-zero elements currently stored in the matrix, the begin()
    // and end() functions can be used. In the example, all non-zero elements of the 2nd row
    // of Z are traversed.
    for( ZeroMatrix<double,rowMajor>::Iterator i=Z.begin(1); i!=Z.end(1); ++i ) {
       ... = i->value();  // Access to the value of the non-zero element
       ... = i->index();  // Access to the index of the non-zero element
    }
    

    The use of ZeroMatrix is very natural and intuitive. All operations (addition, subtraction, multiplication, ...) can be performed on all possible combinations of row-major and column-major dense and sparse matrices with fitting element types. The following example gives an impression of the use of ZeroMatrix:

    using blaze::ZeroMatrix;
    using blaze::CompressedMatrix;
    using blaze::DynamicMatrix;
    using blaze::rowMajor;
    using blaze::columnMajor;
    
    ZeroMatrix<double,rowMajor> Z( 3, 3 );  // Row-major 3x3 zero matrix
    
    DynamicMatrix<double,columnMajor> A( 3, 3 );  // Column-major 3x3 dynamic dense matrix
    CompressedMatrix<double,rowMajor> B( 3, 3 );  // Row-major 3x3 compressed sparse matrix
    CompressedMatrix<float,rowMajor>  C( 3, 5 );  // Row-major 3x5 compressed sparse matrix
    // ... Initialization of A, B, and C
    
    DynamicMatrix<double,rowMajor>       D( Z );  // Creation of a new row-major matrix as a copy of Z
    CompressedMatrix<double,columnMajor> E;       // Creation of a default column-major matrix
    
    D = Z + A;    // Addition of a zero matrix and a dense matrix
    D = B - Z;    // Subtraction of a sparse matrix and a zero matrix
    E = Z * C;    // Matrix multiplication between two matrices of different element types
    
    D = 2.0 * Z;  // Scaling of a zero matrix
    E = Z * 2.0;  // Scaling of a zero matrix
    
  3. Mikhail Katliar reporter

    Nice! Just today I needed ZeroMatrix again, looked for it -- and it's there! I am happy to see how responsive you are to the requests of Blaze users. Keep doing the excellent job!

  4. Log in to comment