All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SMatSMatMultExpr.h
Go to the documentation of this file.
1 //=================================================================================================
20 //=================================================================================================
21 
22 #ifndef _BLAZE_MATH_EXPRESSIONS_SMATSMATMULTEXPR_H_
23 #define _BLAZE_MATH_EXPRESSIONS_SMATSMATMULTEXPR_H_
24 
25 
26 //*************************************************************************************************
27 // Includes
28 //*************************************************************************************************
29 
30 #include <algorithm>
31 #include <stdexcept>
32 #include <vector>
33 #include <boost/type_traits/remove_reference.hpp>
41 #include <blaze/math/Infinity.h>
43 #include <blaze/math/shims/Reset.h>
58 #include <blaze/util/Assert.h>
59 #include <blaze/util/DisableIf.h>
60 #include <blaze/util/EnableIf.h>
61 #include <blaze/util/InvalidType.h>
62 #include <blaze/util/SelectType.h>
63 #include <blaze/util/Types.h>
64 
65 
66 namespace blaze {
67 
68 //=================================================================================================
69 //
70 // CLASS SMATSMATMULTEXPR
71 //
72 //=================================================================================================
73 
74 //*************************************************************************************************
81 template< typename MT1 // Type of the left-hand side sparse matrix
82  , typename MT2 > // Type of the right-hand side sparse matrix
83 class SMatSMatMultExpr : public SparseMatrix< SMatSMatMultExpr<MT1,MT2>, false >
84  , private Expression
85  , private Computation
86 {
87  private:
88  //**Type definitions****************************************************************************
89  typedef typename MT1::ResultType RT1;
90  typedef typename MT2::ResultType RT2;
91  typedef typename MT1::CompositeType CT1;
92  typedef typename MT2::CompositeType CT2;
93  //**********************************************************************************************
94 
95  public:
96  //**Type definitions****************************************************************************
99  typedef typename ResultType::OppositeType OppositeType;
100  typedef typename ResultType::TransposeType TransposeType;
101  typedef typename ResultType::ElementType ElementType;
102  typedef const ElementType ReturnType;
103  typedef const ResultType CompositeType;
104 
106  typedef typename SelectType< IsExpression<MT1>::value, const MT1, const MT1& >::Type LeftOperand;
107 
109  typedef typename SelectType< IsExpression<MT2>::value, const MT2, const MT2& >::Type RightOperand;
110  //**********************************************************************************************
111 
112  //**Compilation flags***************************************************************************
116  //**********************************************************************************************
117 
118  //**Constructor*********************************************************************************
124  explicit inline SMatSMatMultExpr( const MT1& lhs, const MT2& rhs )
125  : lhs_( lhs ) // Left-hand side sparse matrix of the multiplication expression
126  , rhs_( rhs ) // Right-hand side sparse matrix of the multiplication expression
127  {
128  BLAZE_INTERNAL_ASSERT( lhs.columns() == rhs.rows(), "Invalid matrix sizes" );
129  }
130  //**********************************************************************************************
131 
132  //**Access operator*****************************************************************************
139  inline ReturnType operator()( size_t i, size_t j ) const {
140  BLAZE_INTERNAL_ASSERT( i < lhs_.rows() , "Invalid row access index" );
141  BLAZE_INTERNAL_ASSERT( j < rhs_.columns(), "Invalid column access index" );
142 
143  typedef typename boost::remove_reference<CT1>::type::ConstIterator ConstIterator;
144 
145  ElementType tmp = ElementType();
146 
147  // Early exit
148  if( lhs_.columns() == 0UL )
149  return tmp;
150 
151  // Fast computation in case the left-hand side sparse matrix directly provides iterators
153  {
154  // Evaluation of the left-hand side sparse matrix operand
155  CT1 A( lhs_ );
156 
157  const ConstIterator end( A.end(i) );
158  ConstIterator element( A.begin(i) );
159 
160  // Early exit in case row i is empty
161  if( element == end )
162  return tmp;
163 
164  // Calculating element (i,j)
165  tmp = element->value() * rhs_(element->index(),j);
166  ++element;
167  for( ; element!=end; ++element )
168  tmp += element->value() * rhs_(element->index(),j);
169  }
170 
171  // Default computation in case the left-hand side sparse matrix doesn't provide iterators
172  else {
173  tmp = lhs_(i,0UL) * rhs_(0UL,j);
174  for( size_t k=1UL; k<lhs_.columns(); ++k ) {
175  tmp += lhs_(i,k) * rhs_(k,j);
176  }
177  }
178 
179  return tmp;
180  }
181  //**********************************************************************************************
182 
183  //**Rows function*******************************************************************************
188  inline size_t rows() const {
189  return lhs_.rows();
190  }
191  //**********************************************************************************************
192 
193  //**Columns function****************************************************************************
198  inline size_t columns() const {
199  return rhs_.columns();
200  }
201  //**********************************************************************************************
202 
203  //**NonZeros function***************************************************************************
208  inline size_t nonZeros() const {
209  return 0UL;
210  }
211  //**********************************************************************************************
212 
213  //**NonZeros function***************************************************************************
219  inline size_t nonZeros( size_t i ) const {
220  return 0UL;
221  }
222  //**********************************************************************************************
223 
224  //**Left operand access*************************************************************************
229  inline LeftOperand leftOperand() const {
230  return lhs_;
231  }
232  //**********************************************************************************************
233 
234  //**Right operand access************************************************************************
239  inline RightOperand rightOperand() const {
240  return rhs_;
241  }
242  //**********************************************************************************************
243 
244  //**********************************************************************************************
250  template< typename T >
251  inline bool isAliased( const T* alias ) const {
253  !RequiresEvaluation<MT1>::value && lhs_.isAliased( alias ) ) ||
255  !RequiresEvaluation<MT2>::value && rhs_.isAliased( alias ) );
256  }
257  //**********************************************************************************************
258 
259  private:
260  //**Member variables****************************************************************************
263  //**********************************************************************************************
264 
265  //**Default assignment to dense matrices********************************************************
278  template< typename MT // Type of the target dense matrix
279  , bool SO > // Storage order of the target dense matrix
280  friend inline typename EnableIf< IsResizable<typename MT::ElementType> >::Type
281  assign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
282  {
283  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
284  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
285 
286  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
287  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
288 
289  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
290  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
291 
292  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
293  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
294  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
295  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
296  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
297  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
298 
299  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
300  const LeftIterator lend( A.end(i) );
301  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
302  const RightIterator rend( B.end( lelem->index() ) );
303  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem )
304  {
305  if( isDefault( (~lhs)(i,relem->index()) ) ) {
306  (~lhs)(i,relem->index()) = lelem->value() * relem->value();
307  }
308  else {
309  (~lhs)(i,relem->index()) += lelem->value() * relem->value();
310  }
311  }
312  }
313  }
314  }
316  //**********************************************************************************************
317 
318  //**Optimized assignment to dense matrices******************************************************
331  template< typename MT // Type of the target dense matrix
332  , bool SO > // Storage order of the target dense matrix
333  friend inline typename DisableIf< IsResizable<typename MT::ElementType> >::Type
334  assign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
335  {
336  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
337  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
338 
339  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
340  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
341 
342  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
343  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
344 
345  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
346  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
347  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
348  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
349  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
350  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
351 
352  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
353  const LeftIterator lend( A.end(i) );
354  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
355  const RightIterator rend( B.end( lelem->index() ) );
356  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem ) {
357  (~lhs)(i,relem->index()) += lelem->value() * relem->value();
358  }
359  }
360  }
361  }
363  //**********************************************************************************************
364 
365  //**Assignment to row-major sparse matrices*****************************************************
377  template< typename MT > // Type of the target sparse matrix
378  friend inline void assign( SparseMatrix<MT,false>& lhs, const SMatSMatMultExpr& rhs )
379  {
380  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
381  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
382 
383  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
384  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
385 
386  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
387  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
388 
389  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
390  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
391  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
392  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
393  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
394  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
395 
396  // (Over-)Estimating the number of non-zero entries in the resulting matrix
397  size_t nonzeros( 0UL );
398 
399  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
400  const LeftIterator lend( A.end(i) );
401  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
402  nonzeros += B.nonZeros( lelem->index() );
403  }
404  }
405 
406  if( nonzeros > (~lhs).rows() * (~lhs).columns() ) {
407  nonzeros = (~lhs).rows() * (~lhs).columns();
408  }
409 
410  (~lhs).reserve( nonzeros );
411  nonzeros = 0UL;
412 
413  // Performing the matrix-matrix multiplication
414  std::vector<ElementType> values ( (~lhs).columns(), ElementType() );
415  std::vector<size_t> indices( (~lhs).columns(), 0UL );
416  size_t minIndex( inf ), maxIndex( 0UL );
417 
418  for( size_t i=0UL; i<(~lhs).rows(); ++i )
419  {
420  const LeftIterator lend( A.end(i) );
421  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem )
422  {
423  const RightIterator rend( B.end( lelem->index() ) );
424  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem )
425  {
426  if( isDefault( values[relem->index()] ) ) {
427  values[relem->index()] = lelem->value() * relem->value();
428  indices[nonzeros] = relem->index();
429  ++nonzeros;
430  if( relem->index() < minIndex ) minIndex = relem->index();
431  if( relem->index() > maxIndex ) maxIndex = relem->index();
432  }
433  else {
434  values[relem->index()] += lelem->value() * relem->value();
435  }
436  }
437  }
438 
439  if( nonzeros > 0UL )
440  {
441  BLAZE_INTERNAL_ASSERT( minIndex <= maxIndex, "Invalid index detected" );
442 
443  if( ( nonzeros + nonzeros ) < ( maxIndex - minIndex ) )
444  {
445  std::sort( indices.begin(), indices.begin() + nonzeros );
446 
447  for( size_t j=0UL; j<nonzeros; ++j ) {
448  const size_t index( indices[j] );
449  (~lhs).append( i, index, values[index] );
450  reset( values[index] );
451  }
452  }
453  else {
454  for( size_t j=minIndex; j<=maxIndex; ++j ) {
455  if( !isDefault( values[j] ) ) {
456  (~lhs).append( i, j, values[j] );
457  reset( values[j] );
458  }
459  }
460  }
461 
462  nonzeros = 0UL;
463  minIndex = inf;
464  maxIndex = 0UL;
465  }
466 
467  (~lhs).finalize( i );
468  }
469  }
471  //**********************************************************************************************
472 
473  //**Assignment to column-major sparse matrices**************************************************
485  template< typename MT > // Type of the target sparse matrix
486  friend inline void assign( SparseMatrix<MT,true>& lhs, const SMatSMatMultExpr& rhs )
487  {
488  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
489  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
490 
491  BLAZE_CONSTRAINT_MUST_BE_COLUMN_MAJOR_MATRIX_TYPE( typename MT1::OppositeType );
492  BLAZE_CONSTRAINT_MUST_BE_COLUMN_MAJOR_MATRIX_TYPE( typename MT2::OppositeType );
493 
494  const typename MT1::OppositeType A( rhs.lhs_ );
495  const typename MT2::OppositeType B( rhs.rhs_ );
496  assign( ~lhs, A * B );
497  }
499  //**********************************************************************************************
500 
501  //**Addition assignment to dense matrices*******************************************************
513  template< typename MT // Type of the target dense matrix
514  , bool SO > // Storage order of the target dense matarix
515  friend inline void addAssign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
516  {
517  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
518  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
519 
520  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
521  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
522 
523  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
524  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
525 
526  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
527  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
528  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
529  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
530  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
531  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
532 
533  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
534  const LeftIterator lend( A.end(i) );
535  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
536  const RightIterator rend( B.end( lelem->index() ) );
537  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem ) {
538  (~lhs)(i,relem->index()) += lelem->value() * relem->value();
539  }
540  }
541  }
542  }
544  //**********************************************************************************************
545 
546  //**Addition assignment to sparse matrices******************************************************
547  // No special implementation for the addition assignment to sparse matrices.
548  //**********************************************************************************************
549 
550  //**Subtraction assignment to dense matrices****************************************************
563  template< typename MT // Type of the target dense matrix
564  , bool SO > // Storage order of the target dense matrix
565  friend inline void subAssign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
566  {
567  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
568  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
569 
570  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
571  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
572 
573  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
574  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
575 
576  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
577  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
578  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
579  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
580  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
581  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
582 
583  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
584  const LeftIterator lend( A.end(i) );
585  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
586  const RightIterator rend( B.end( lelem->index() ) );
587  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem ) {
588  (~lhs)(i,relem->index()) -= lelem->value() * relem->value();
589  }
590  }
591  }
592  }
594  //**********************************************************************************************
595 
596  //**Subtraction assignment to sparse matrices***************************************************
597  // No special implementation for the subtraction assignment to sparse matrices.
598  //**********************************************************************************************
599 
600  //**Multiplication assignment to dense matrices*************************************************
601  // No special implementation for the multiplication assignment to dense matrices.
602  //**********************************************************************************************
603 
604  //**Multiplication assignment to sparse matrices************************************************
605  // No special implementation for the multiplication assignment to sparse matrices.
606  //**********************************************************************************************
607 
608  //**Compile time checks*************************************************************************
615  //**********************************************************************************************
616 };
617 //*************************************************************************************************
618 
619 
620 
621 
622 //=================================================================================================
623 //
624 // GLOBAL BINARY ARITHMETIC OPERATORS
625 //
626 //=================================================================================================
627 
628 //*************************************************************************************************
655 template< typename T1 // Type of the left-hand side sparse matrix
656  , typename T2 > // Type of the right-hand side sparse matrix
657 inline const SMatSMatMultExpr<T1,T2>
659 {
660  if( (~lhs).columns() != (~rhs).rows() )
661  throw std::invalid_argument( "Matrix sizes do not match" );
662 
663  return SMatSMatMultExpr<T1,T2>( ~lhs, ~rhs );
664 }
665 //*************************************************************************************************
666 
667 
668 
669 
670 //=================================================================================================
671 //
672 // EXPRESSION TRAIT SPECIALIZATIONS
673 //
674 //=================================================================================================
675 
676 //*************************************************************************************************
678 template< typename MT1, typename MT2, typename VT >
679 struct SMatDVecMultExprTrait< SMatSMatMultExpr<MT1,MT2>, VT >
680 {
681  public:
682  //**********************************************************************************************
683  typedef typename SelectType< IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
684  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value &&
685  IsDenseVector<VT>::value && !IsTransposeVector<VT>::value
686  , typename SMatDVecMultExprTrait< MT1, typename SMatDVecMultExprTrait<MT2,VT>::Type >::Type
687  , INVALID_TYPE >::Type Type;
688  //**********************************************************************************************
689 };
691 //*************************************************************************************************
692 
693 
694 //*************************************************************************************************
696 template< typename MT1, typename MT2, typename VT >
697 struct SMatSVecMultExprTrait< SMatSMatMultExpr<MT1,MT2>, VT >
698 {
699  public:
700  //**********************************************************************************************
701  typedef typename SelectType< IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
702  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value &&
703  IsSparseVector<VT>::value && !IsTransposeVector<VT>::value
704  , typename SMatSVecMultExprTrait< MT1, typename SMatSVecMultExprTrait<MT2,VT>::Type >::Type
705  , INVALID_TYPE >::Type Type;
706  //**********************************************************************************************
707 };
709 //*************************************************************************************************
710 
711 
712 //*************************************************************************************************
714 template< typename VT, typename MT1, typename MT2 >
715 struct TDVecSMatMultExprTrait< VT, SMatSMatMultExpr<MT1,MT2> >
716 {
717  public:
718  //**********************************************************************************************
719  typedef typename SelectType< IsDenseVector<VT>::value && IsTransposeVector<VT>::value &&
720  IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
721  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value
722  , typename TDVecSMatMultExprTrait< typename TDVecSMatMultExprTrait<VT,MT1>::Type, MT2 >::Type
723  , INVALID_TYPE >::Type Type;
724  //**********************************************************************************************
725 };
727 //*************************************************************************************************
728 
729 
730 //*************************************************************************************************
732 template< typename VT, typename MT1, typename MT2 >
733 struct TSVecSMatMultExprTrait< VT, SMatSMatMultExpr<MT1,MT2> >
734 {
735  public:
736  //**********************************************************************************************
737  typedef typename SelectType< IsSparseVector<VT>::value && IsTransposeVector<VT>::value &&
738  IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
739  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value
740  , typename TSVecSMatMultExprTrait< typename TSVecSMatMultExprTrait<VT,MT1>::Type, MT2 >::Type
741  , INVALID_TYPE >::Type Type;
742  //**********************************************************************************************
743 };
745 //*************************************************************************************************
746 
747 } // namespace blaze
748 
749 #endif