 marked as minor
Documentation Section for "How to pass Blaze types in Interfaces"
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)

reporter 
reporter  edited description
Set code highlight to C++.

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 lefthand side dense matrix operand ,bool SO1 // Storage order of the lefthand side dense matrix operand ,typename MT2 // Type of the righthand side dense matrix operand ,bool SO2 // Storage order of the righthand 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 viaoperator~()
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!

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!


assigned issue to

assigned issue to

 changed status to open

 changed status to resolved
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, noncomponentwise 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 themap()
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 typeMT
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 parameterSO
represents the storage order (blaze::rowMajor
orblaze::columnMajor
) of the matrix.The second example shows the
countZeros()
function, which counts the number of values, which are exactly zero, in a dense, rowmajor 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;

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?

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 theDenseMatrix
CRTP base class. Vectors on the other hand derive from theVector
CRTP base class, and dense vectors from theDenseVector
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 acceptingDenseMatrix
: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
orDenseVector
, 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!

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!
 Log in to comment