Documentation Section for "How to pass Blaze types in Interfaces"

Issue #173 resolved
Maikel Nadolski created an issue

Hi,

I was missing a section in the documentation about how to pass blaze types in function interfaces.

In my current application, I want to assemble a matrix using the Kronecker product (https://en.wikipedia.org/wiki/Kronecker_product)

To achieve this I naively wrote this function

template <typename Left, typename Right, typename Out>
void kronecker_product(const Left& left, const Right& right, Out& out) {
  for (std::size_t i = 0; i < blaze::rows(left); ++i) {
    for (std::size_t j = 0; j < blaze::columns(left); ++j) {
      const std::size_t row_lower = i * blaze::rows(right);
      const std::size_t col_lower = j * blaze::columns(right);
      auto block = blaze::submatrix(out, row_lower, col_lower,
                                    blaze::rows(right), blaze::columns(right));
      block = left(i, j) * right;
    }
  }
}

Now I think this won't behave well for passing sub expression types for left. Am I wrong? I wished to find some examples to get some orientation.

Also, does a mailing list or similar exist for Blaze?

Comments (10)

  1. Klaus Iglberger

    Hi Maikel!

    Thanks for creating this issue. We very much appreciate hints to what information is missing and we agree that this would be a good addition to the tutorial and wiki. We will provide the according documentation as soon as possible.

    Your implementation is perfectly correctly for both dense and sparse matrices and even for expression types (e.g. additions, subtractions, ...). However, in order to guarantee that only dense matrices are passed and in order to prevent duplicate expression evaluations you can update your code in the following way:

    template <typename MT1  // Type of the left-hand side dense matrix operand
             ,bool SO1      // Storage order of the left-hand side dense matrix operand
             ,typename MT2  // Type of the right-hand side dense matrix operand
             ,bool SO2      // Storage order of the right-hand side dense matrix operand
             ,typename MT3  // Type of the target dense matrix
             ,bool SO3>     // Storage order of the target dense matrix
    void kronecker_product( const DenseMatrix<MT1,SO1>& left
                          , const DenseMatrix<MT2,SO2>& right
                          , DenseMatrix<MT3,SO3>& out)
    {
      using namespace blaze;
    
      using Left  = If_t< IsMatMatMultExpr<MT1>::value, typename MT1::ResultType, const MT1& >;
      using Right = If_t< IsExpression<MT2>::value, typename MT2::ResultType, const MT2& >;
    
      Left  A( ~left  );
      Right B( ~right );
    
      for (std::size_t i = 0; i < rows(A); ++i) {
        for (std::size_t j = 0; j < columns(A); ++j) {
          const std::size_t row_lower = i * rows(B);
          const std::size_t col_lower = j * columns(B);
          auto block = submatrix(out, row_lower, col_lower,
                                 rows(B), columns(B));
          (~block) = A(i, j) * B;
        }
      }
    }
    

    The function parameters have been changed to DenseMatrix, which restricts the arguments to dense matrices. DenseMatrix is a CRTP base class and via operator~() it is possible to return to the original type.

    In the first two lines of the function the decision is made whether or not the matrices have to be evaluated into temporary matrices. For the left matrix this in only necessary and reasonable if it is a matrix multiplication, the right matrix should be evaluated if it is any kind of computation (addition, subtraction, multiplication, ...). The rest of the function remains unchanged.

    We hope this helps. Thanks again for giving us feedback how to improve Blaze.

    Best regards,

    Klaus!

  2. Klaus Iglberger

    Hi Maikel!

    Thanks again for raising this issue. Both the tutorial and wiki now provide a section about how to pass Blaze vector and matrix types to functions. Hopefully this helps you to extend Blaze with custom functionality.

    We are planning to provide native support for the Kronecker product and therefore have created issue #193. Please feel free to watch this issue in order to keep track about our progress.

    Best regards,

    Klaus!

  3. Klaus Iglberger

    Summary

    Both the tutorial and the wiki have been updated by a section, which describes how to pass Blaze vector and matrix types to functions. The updated tutorial is immediately available via cloning the Blaze repository. The updated wiki is available online.

    Free Functions

    In order to extend Blaze with new functionality it is possible to add free functions. Free functions can be used either as wrappers around calls to the map() function or to implement general, non-componentwise operations. The following two examples will demonstrate both ideas.

    The first example shows the setToZero() function, which resets a sparse matrix to zero without affecting the sparsity pattern. It is implemented as a convenience wrapper around the map() function:

    template< typename MT  // Type of the sparse matrix
            , bool SO >    // Storage order
    void setToZero( blaze::SparseMatrix<MT,SO>& mat )
    {
       (~mat) = blaze::map( ~mat, []( int ){ return 0; } );
    }
    

    The blaze::SparseMatrix class template is the base class for all kinds of sparse matrices and provides an abstraction from the actual type MT of the sparse matrix. However, due to the Curiously Recurring Template Pattern (CRTP) it also enables a conversion back to the actual type. This downcast is performed via the tilde operator (i.e. operator~()). The template parameter SO represents the storage order (blaze::rowMajor or blaze::columnMajor) of the matrix.

    The second example shows the countZeros() function, which counts the number of values, which are exactly zero, in a dense, row-major matrix:

    template< typename MT >
    size_t countZeros( blaze::DenseMatrix<MT,rowMajor>& mat )
    {
       const size_t M( (~mat).rows() );
       const size_t N( (~mat).columns() );
       size_t count( 0UL );
    
       for( size_t i=0UL; i<M; ++i ) {
          for( size_t j=0UL; j<N; ++j ) {
             if( blaze::isDefault<strict>( (~mat)(i,j) ) )
                ++count;
          }
       }
    
       return count;
    }
    

    The blaze::DenseMatrix class template is the base class for all kinds of dense matrices. Again, it is possible to perform the conversion to the actual type via the tilde operator.

    The following two listings show the declarations of all vector and matrix base classes, which can be used for custom free functions:

    template< typename VT  // Concrete type of the dense or sparse vector
            , bool TF >    // Transpose flag (blaze::columnVector or blaze::rowVector)
    class Vector;
    
    template< typename VT  // Concrete type of the dense vector
            , bool TF >    // Transpose flag (blaze::columnVector or blaze::rowVector)
    class DenseVector;
    
    template< typename VT  // Concrete type of the sparse vector
            , bool TF >    // Transpose flag (blaze::columnVector or blaze::rowVector)
    class SparseVector;
    
    template< typename MT  // Concrete type of the dense or sparse matrix
            , bool SO >    // Storage order (blaze::rowMajor or blaze::columnMajor)
    class Matrix;
    
    template< typename MT  // Concrete type of the dense matrix
            , bool SO >    // Storage order (blaze::rowMajor or blaze::columnMajor)
    class DenseMatrix;
    
    template< typename MT  // Concrete type of the sparse matrix
            , bool SO >    // Storage order (blaze::rowMajor or blaze::columnMajor)
    class SparseMatrix;
    
  4. Youyuan Deng

    Forgive me if the following question is too naive or has been raised. How may I write a free function that accepts both a matrix/vector and a sub matrix view? i.e. Either it’s a concrete actual matrix object or a view object, the function parameters can be properly initialized without deep copy?

  5. Klaus Iglberger

    Hi Youyuan!

    All matrices, including views such as submatrices, derive from the Matrix CRTP base class. All dense matrices, including submatrices, that represent a dense matrix, derive from the DenseMatrix CRTP base class. Vectors on the other hand derive from the Vector CRTP base class, and dense vectors from the DenseVector CRTP base class.

    In order to accept any kind of matrix (dense or sparse) you can write a function accepting Matrix, in order to accept dense matrices only you can write a function accepting DenseMatrix:

    template< typename MT  // Concrete type of the dense or sparse matrix
            , bool SO >    // Storage order (blaze::rowMajor or blaze::columnMajor)
    void f( const blaze::Matrix<MT,SO>& ) {}
    
    template< typename MT  // Concrete type of the dense matrix
            , bool SO >    // Storage order (blaze::rowMajor or blaze::columnMajor)
    void g( const blaze::DenseMatrix<MT,SO>& ) {}
    
    int main()
    {
       blaze::DynamicMatrix<int> A( 5UL, 5UL );  // A dynamic matrix is a dense matrix
    
    
       auto S = submatrix( A, 1UL, 1UL, 3UL, 3UL );  // A submatrix on a dense matrix is a dense matrix itself
    
       f( A );  // Works, since a 'DynamicMatrix' is a 'Matrix'
       g( A );  // Works, since a 'DynamicMatrix' is a 'DenseMatrix'
    
       f( S );  // Works, since a submatrix is a 'Matrix'
       g( S );  // Works, since a submatrix on a dense matrix is a 'DenseMatrix'
    }
    

    Similarly, you can write functions that accept a Vector or DenseVector, depending on what you want to do. There is no way to accept both vectors and matrices, i.e. there is no common base class for vectors and matrices.

    I hope this helps,

    Best regards,

    Klaus!

  6. Youyuan Deng

    Thanks very much for your timely and detailed response! I actually looked at the doc of View class before asking.

    doc of View:

    Base class for all views. 

    The View class serves as a tag for all views (subvectors, submatrices, rows, columns, ...). All classes that represent a view and that are used within the expression template environment of the Blaze library have to derive publicly from this class in order to qualify as a view. Only in case a class is derived publicly from the View base class, the IsView type trait recognizes the class as valid view.

    doc of Matrix:

    Base class for matrices. 

    The Matrix class is a base class for all dense and sparse matrix classes within the Blaze library. It provides an abstraction from the actual type of the matrix, but enables a conversion back to this type via the 'Curiously Recurring Template Pattern' (CRTP). 

    I wasn’t sure Matrix is the more base one from these words except from your latest answer. Maybe I’m just being naive here.

    Thanks again!

  7. Log in to comment