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>
40 #include <blaze/math/Infinity.h>
42 #include <blaze/math/shims/Reset.h>
59 #include <blaze/util/Assert.h>
60 #include <blaze/util/Byte.h>
61 #include <blaze/util/DisableIf.h>
62 #include <blaze/util/EnableIf.h>
63 #include <blaze/util/InvalidType.h>
65 #include <blaze/util/SelectType.h>
66 #include <blaze/util/Types.h>
68 #include <blaze/util/Unused.h>
69 
70 
71 namespace blaze {
72 
73 //=================================================================================================
74 //
75 // CLASS SMATSMATMULTEXPR
76 //
77 //=================================================================================================
78 
79 //*************************************************************************************************
86 template< typename MT1 // Type of the left-hand side sparse matrix
87  , typename MT2 > // Type of the right-hand side sparse matrix
88 class SMatSMatMultExpr : public SparseMatrix< SMatSMatMultExpr<MT1,MT2>, false >
89  , private MatMatMultExpr
90  , private Computation
91 {
92  private:
93  //**Type definitions****************************************************************************
94  typedef typename MT1::ResultType RT1;
95  typedef typename MT2::ResultType RT2;
96  typedef typename MT1::CompositeType CT1;
97  typedef typename MT2::CompositeType CT2;
98  //**********************************************************************************************
99 
100  public:
101  //**Type definitions****************************************************************************
104  typedef typename ResultType::OppositeType OppositeType;
105  typedef typename ResultType::TransposeType TransposeType;
106  typedef typename ResultType::ElementType ElementType;
107  typedef const ElementType ReturnType;
108  typedef const ResultType CompositeType;
109 
111  typedef typename SelectType< IsExpression<MT1>::value, const MT1, const MT1& >::Type LeftOperand;
112 
114  typedef typename SelectType< IsExpression<MT2>::value, const MT2, const MT2& >::Type RightOperand;
115  //**********************************************************************************************
116 
117  //**Constructor*********************************************************************************
123  explicit inline SMatSMatMultExpr( const MT1& lhs, const MT2& rhs )
124  : lhs_( lhs ) // Left-hand side sparse matrix of the multiplication expression
125  , rhs_( rhs ) // Right-hand side sparse matrix of the multiplication expression
126  {
127  BLAZE_INTERNAL_ASSERT( lhs.columns() == rhs.rows(), "Invalid matrix sizes" );
128  }
129  //**********************************************************************************************
130 
131  //**Access operator*****************************************************************************
138  inline ReturnType operator()( size_t i, size_t j ) const {
139  BLAZE_INTERNAL_ASSERT( i < lhs_.rows() , "Invalid row access index" );
140  BLAZE_INTERNAL_ASSERT( j < rhs_.columns(), "Invalid column access index" );
141 
142  typedef typename RemoveReference<CT1>::Type::ConstIterator ConstIterator;
143 
144  ElementType tmp = ElementType();
145 
146  // Early exit
147  if( lhs_.columns() == 0UL )
148  return tmp;
149 
150  // Fast computation in case the left-hand side sparse matrix directly provides iterators
152  {
153  // Evaluation of the left-hand side sparse matrix operand
154  CT1 A( lhs_ );
155 
156  const ConstIterator end( A.end(i) );
157  ConstIterator element( A.begin(i) );
158 
159  // Early exit in case row i is empty
160  if( element == end )
161  return tmp;
162 
163  // Calculating element (i,j)
164  tmp = element->value() * rhs_(element->index(),j);
165  ++element;
166  for( ; element!=end; ++element )
167  tmp += element->value() * rhs_(element->index(),j);
168  }
169 
170  // Default computation in case the left-hand side sparse matrix doesn't provide iterators
171  else {
172  tmp = lhs_(i,0UL) * rhs_(0UL,j);
173  for( size_t k=1UL; k<lhs_.columns(); ++k ) {
174  tmp += lhs_(i,k) * rhs_(k,j);
175  }
176  }
177 
178  return tmp;
179  }
180  //**********************************************************************************************
181 
182  //**Rows function*******************************************************************************
187  inline size_t rows() const {
188  return lhs_.rows();
189  }
190  //**********************************************************************************************
191 
192  //**Columns function****************************************************************************
197  inline size_t columns() const {
198  return rhs_.columns();
199  }
200  //**********************************************************************************************
201 
202  //**NonZeros function***************************************************************************
207  inline size_t nonZeros() const {
208  return 0UL;
209  }
210  //**********************************************************************************************
211 
212  //**NonZeros function***************************************************************************
218  inline size_t nonZeros( size_t i ) const {
219  UNUSED_PARAMETER( i );
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 canAlias( const T* alias ) const {
252  return ( lhs_.isAliased( alias ) || rhs_.isAliased( alias ) );
253  }
254  //**********************************************************************************************
255 
256  //**********************************************************************************************
262  template< typename T >
263  inline bool isAliased( const T* alias ) const {
264  return ( lhs_.isAliased( alias ) || rhs_.isAliased( alias ) );
265  }
266  //**********************************************************************************************
267 
268  private:
269  //**Member variables****************************************************************************
272  //**********************************************************************************************
273 
274  //**Default assignment to dense matrices********************************************************
287  template< typename MT // Type of the target dense matrix
288  , bool SO > // Storage order of the target dense matrix
289  friend inline typename EnableIf< IsResizable<typename MT::ElementType> >::Type
290  assign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
291  {
293 
294  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
295  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
296 
297  typedef typename RemoveReference<CT1>::Type::ConstIterator LeftIterator;
298  typedef typename RemoveReference<CT2>::Type::ConstIterator RightIterator;
299 
300  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
301  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
302 
303  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
304  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
305  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
306  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
307  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
308  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
309 
310  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
311  const LeftIterator lend( A.end(i) );
312  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
313  const RightIterator rend( B.end( lelem->index() ) );
314  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem )
315  {
316  if( isDefault( (~lhs)(i,relem->index()) ) ) {
317  (~lhs)(i,relem->index()) = lelem->value() * relem->value();
318  }
319  else {
320  (~lhs)(i,relem->index()) += lelem->value() * relem->value();
321  }
322  }
323  }
324  }
325  }
327  //**********************************************************************************************
328 
329  //**Optimized assignment to dense matrices******************************************************
342  template< typename MT // Type of the target dense matrix
343  , bool SO > // Storage order of the target dense matrix
344  friend inline typename DisableIf< IsResizable<typename MT::ElementType> >::Type
345  assign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
346  {
348 
349  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
350  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
351 
352  typedef typename RemoveReference<CT1>::Type::ConstIterator LeftIterator;
353  typedef typename RemoveReference<CT2>::Type::ConstIterator RightIterator;
354 
355  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
356  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
357 
358  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
359  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
360  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
361  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
362  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
363  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
364 
365  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
366  const LeftIterator lend( A.end(i) );
367  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
368  const RightIterator rend( B.end( lelem->index() ) );
369  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem ) {
370  (~lhs)(i,relem->index()) += lelem->value() * relem->value();
371  }
372  }
373  }
374  }
376  //**********************************************************************************************
377 
378  //**Assignment to row-major sparse matrices*****************************************************
390  template< typename MT > // Type of the target sparse matrix
391  friend inline void assign( SparseMatrix<MT,false>& lhs, const SMatSMatMultExpr& rhs )
392  {
394 
395  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
396  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
397 
398  typedef typename RemoveReference<CT1>::Type::ConstIterator LeftIterator;
399  typedef typename RemoveReference<CT2>::Type::ConstIterator RightIterator;
400 
401  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
402  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
403 
404  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
405  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
406  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
407  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
408  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
409  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
410 
411  // (Over-)Estimating the number of non-zero entries in the resulting matrix
412  size_t nonzeros( 0UL );
413 
414  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
415  const LeftIterator lend( A.end(i) );
416  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
417  nonzeros += B.nonZeros( lelem->index() );
418  }
419  }
420 
421  if( nonzeros > (~lhs).rows() * (~lhs).columns() ) {
422  nonzeros = (~lhs).rows() * (~lhs).columns();
423  }
424 
425  (~lhs).reserve( nonzeros );
426  nonzeros = 0UL;
427 
428  // Performing the matrix-matrix multiplication
429  std::vector<ElementType> values ( (~lhs).columns(), ElementType() );
430  std::vector<byte> valid ( (~lhs).columns(), 0 );
431  std::vector<size_t> indices( (~lhs).columns(), 0UL );
432  size_t minIndex( inf ), maxIndex( 0UL );
433 
434  for( size_t i=0UL; i<(~lhs).rows(); ++i )
435  {
436  const LeftIterator lend( A.end(i) );
437  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem )
438  {
439  const RightIterator rend( B.end( lelem->index() ) );
440  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem )
441  {
442  if( !valid[relem->index()] ) {
443  values[relem->index()] = lelem->value() * relem->value();
444  valid [relem->index()] = 1;
445  indices[nonzeros] = relem->index();
446  ++nonzeros;
447  if( relem->index() < minIndex ) minIndex = relem->index();
448  if( relem->index() > maxIndex ) maxIndex = relem->index();
449  }
450  else {
451  values[relem->index()] += lelem->value() * relem->value();
452  }
453  }
454  }
455 
456  BLAZE_INTERNAL_ASSERT( nonzeros <= (~lhs).columns(), "Invalid number of non-zero elements" );
457 
458  if( nonzeros > 0UL )
459  {
460  BLAZE_INTERNAL_ASSERT( minIndex <= maxIndex, "Invalid index detected" );
461 
462  if( ( nonzeros + nonzeros ) < ( maxIndex - minIndex ) )
463  {
464  std::sort( indices.begin(), indices.begin() + nonzeros );
465 
466  for( size_t j=0UL; j<nonzeros; ++j )
467  {
468  const size_t index( indices[j] );
469  if( !isDefault( values[index] ) ) {
470  (~lhs).append( i, index, values[index] );
471  reset( values[index] );
472  }
473 
474  reset( valid [index] );
475  }
476  }
477  else {
478  for( size_t j=minIndex; j<=maxIndex; ++j )
479  {
480  if( !isDefault( values[j] ) ) {
481  (~lhs).append( i, j, values[j] );
482  reset( values[j] );
483  }
484 
485  reset( valid [j] );
486  }
487  }
488 
489  nonzeros = 0UL;
490  minIndex = inf;
491  maxIndex = 0UL;
492  }
493 
494  (~lhs).finalize( i );
495  }
496  }
498  //**********************************************************************************************
499 
500  //**Assignment to column-major sparse matrices**************************************************
512  template< typename MT > // Type of the target sparse matrix
513  friend inline void assign( SparseMatrix<MT,true>& lhs, const SMatSMatMultExpr& rhs )
514  {
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 
522 
523  const ResultType tmp( rhs );
524  (~lhs).reserve( tmp.nonZeros() );
525  assign( ~lhs, tmp );
526  }
528  //**********************************************************************************************
529 
530  //**Addition assignment to dense matrices*******************************************************
542  template< typename MT // Type of the target dense matrix
543  , bool SO > // Storage order of the target dense matarix
544  friend inline void addAssign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
545  {
547 
548  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
549  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
550 
551  typedef typename RemoveReference<CT1>::Type::ConstIterator LeftIterator;
552  typedef typename RemoveReference<CT2>::Type::ConstIterator RightIterator;
553 
554  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
555  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
556 
557  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
558  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
559  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
560  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
561  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
562  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
563 
564  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
565  const LeftIterator lend( A.end(i) );
566  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
567  const RightIterator rend( B.end( lelem->index() ) );
568  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem ) {
569  (~lhs)(i,relem->index()) += lelem->value() * relem->value();
570  }
571  }
572  }
573  }
575  //**********************************************************************************************
576 
577  //**Addition assignment to sparse matrices******************************************************
578  // No special implementation for the addition assignment to sparse matrices.
579  //**********************************************************************************************
580 
581  //**Subtraction assignment to dense matrices****************************************************
594  template< typename MT // Type of the target dense matrix
595  , bool SO > // Storage order of the target dense matrix
596  friend inline void subAssign( DenseMatrix<MT,SO>& lhs, const SMatSMatMultExpr& rhs )
597  {
599 
600  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
601  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
602 
603  typedef typename RemoveReference<CT1>::Type::ConstIterator LeftIterator;
604  typedef typename RemoveReference<CT2>::Type::ConstIterator RightIterator;
605 
606  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
607  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
608 
609  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
610  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
611  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
612  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
613  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
614  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
615 
616  for( size_t i=0UL; i<(~lhs).rows(); ++i ) {
617  const LeftIterator lend( A.end(i) );
618  for( LeftIterator lelem=A.begin(i); lelem!=lend; ++lelem ) {
619  const RightIterator rend( B.end( lelem->index() ) );
620  for( RightIterator relem=B.begin( lelem->index() ); relem!=rend; ++relem ) {
621  (~lhs)(i,relem->index()) -= lelem->value() * relem->value();
622  }
623  }
624  }
625  }
627  //**********************************************************************************************
628 
629  //**Subtraction assignment to sparse matrices***************************************************
630  // No special implementation for the subtraction assignment to sparse matrices.
631  //**********************************************************************************************
632 
633  //**Multiplication assignment to dense matrices*************************************************
634  // No special implementation for the multiplication assignment to dense matrices.
635  //**********************************************************************************************
636 
637  //**Multiplication assignment to sparse matrices************************************************
638  // No special implementation for the multiplication assignment to sparse matrices.
639  //**********************************************************************************************
640 
641  //**Compile time checks*************************************************************************
648  //**********************************************************************************************
649 };
650 //*************************************************************************************************
651 
652 
653 
654 
655 //=================================================================================================
656 //
657 // GLOBAL BINARY ARITHMETIC OPERATORS
658 //
659 //=================================================================================================
660 
661 //*************************************************************************************************
688 template< typename T1 // Type of the left-hand side sparse matrix
689  , typename T2 > // Type of the right-hand side sparse matrix
690 inline const SMatSMatMultExpr<T1,T2>
692 {
694 
695  if( (~lhs).columns() != (~rhs).rows() )
696  throw std::invalid_argument( "Matrix sizes do not match" );
697 
698  return SMatSMatMultExpr<T1,T2>( ~lhs, ~rhs );
699 }
700 //*************************************************************************************************
701 
702 
703 
704 
705 //=================================================================================================
706 //
707 // EXPRESSION TRAIT SPECIALIZATIONS
708 //
709 //=================================================================================================
710 
711 //*************************************************************************************************
713 template< typename MT1, typename MT2, typename VT >
714 struct SMatDVecMultExprTrait< SMatSMatMultExpr<MT1,MT2>, VT >
715 {
716  public:
717  //**********************************************************************************************
718  typedef typename SelectType< IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
719  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value &&
720  IsDenseVector<VT>::value && !IsTransposeVector<VT>::value
721  , typename SMatDVecMultExprTrait< MT1, typename SMatDVecMultExprTrait<MT2,VT>::Type >::Type
722  , INVALID_TYPE >::Type Type;
723  //**********************************************************************************************
724 };
726 //*************************************************************************************************
727 
728 
729 //*************************************************************************************************
731 template< typename MT1, typename MT2, typename VT >
732 struct SMatSVecMultExprTrait< SMatSMatMultExpr<MT1,MT2>, VT >
733 {
734  public:
735  //**********************************************************************************************
736  typedef typename SelectType< IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
737  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value &&
738  IsSparseVector<VT>::value && !IsTransposeVector<VT>::value
739  , typename SMatSVecMultExprTrait< MT1, typename SMatSVecMultExprTrait<MT2,VT>::Type >::Type
740  , INVALID_TYPE >::Type Type;
741  //**********************************************************************************************
742 };
744 //*************************************************************************************************
745 
746 
747 //*************************************************************************************************
749 template< typename VT, typename MT1, typename MT2 >
750 struct TDVecSMatMultExprTrait< VT, SMatSMatMultExpr<MT1,MT2> >
751 {
752  public:
753  //**********************************************************************************************
754  typedef typename SelectType< IsDenseVector<VT>::value && IsTransposeVector<VT>::value &&
755  IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
756  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value
757  , typename TDVecSMatMultExprTrait< typename TDVecSMatMultExprTrait<VT,MT1>::Type, MT2 >::Type
758  , INVALID_TYPE >::Type Type;
759  //**********************************************************************************************
760 };
762 //*************************************************************************************************
763 
764 
765 //*************************************************************************************************
767 template< typename VT, typename MT1, typename MT2 >
768 struct TSVecSMatMultExprTrait< VT, SMatSMatMultExpr<MT1,MT2> >
769 {
770  public:
771  //**********************************************************************************************
772  typedef typename SelectType< IsSparseVector<VT>::value && IsTransposeVector<VT>::value &&
773  IsSparseMatrix<MT1>::value && IsRowMajorMatrix<MT1>::value &&
774  IsSparseMatrix<MT2>::value && IsRowMajorMatrix<MT2>::value
775  , typename TSVecSMatMultExprTrait< typename TSVecSMatMultExprTrait<VT,MT1>::Type, MT2 >::Type
776  , INVALID_TYPE >::Type Type;
777  //**********************************************************************************************
778 };
780 //*************************************************************************************************
781 
782 
783 //*************************************************************************************************
785 template< typename MT1, typename MT2 >
786 struct RowExprTrait< SMatSMatMultExpr<MT1,MT2> >
787 {
788  public:
789  //**********************************************************************************************
790  typedef typename MultExprTrait< typename RowExprTrait<const MT1>::Type, MT2 >::Type Type;
791  //**********************************************************************************************
792 };
794 //*************************************************************************************************
795 
796 
797 //*************************************************************************************************
799 template< typename MT1, typename MT2 >
800 struct ColumnExprTrait< SMatSMatMultExpr<MT1,MT2> >
801 {
802  public:
803  //**********************************************************************************************
804  typedef typename MultExprTrait< MT1, typename ColumnExprTrait<const MT2>::Type >::Type Type;
805  //**********************************************************************************************
806 };
808 //*************************************************************************************************
809 
810 } // namespace blaze
811 
812 #endif