All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TSMatTSMatMultExpr.h
Go to the documentation of this file.
1 //=================================================================================================
20 //=================================================================================================
21 
22 #ifndef _BLAZE_MATH_EXPRESSIONS_TSMATTSMATMULTEXPR_H_
23 #define _BLAZE_MATH_EXPRESSIONS_TSMATTSMATMULTEXPR_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>
43 #include <blaze/math/Infinity.h>
45 #include <blaze/math/shims/Reset.h>
61 #include <blaze/util/Assert.h>
62 #include <blaze/util/DisableIf.h>
63 #include <blaze/util/EnableIf.h>
64 #include <blaze/util/InvalidType.h>
65 #include <blaze/util/SelectType.h>
66 #include <blaze/util/Types.h>
68 
69 
70 namespace blaze {
71 
72 //=================================================================================================
73 //
74 // CLASS TSMATTSMATMULTEXPR
75 //
76 //=================================================================================================
77 
78 //*************************************************************************************************
85 template< typename MT1 // Type of the left-hand side sparse matrix
86  , typename MT2 > // Type of the right-hand side sparse matrix
87 class TSMatTSMatMultExpr : public SparseMatrix< TSMatTSMatMultExpr<MT1,MT2>, true >
88  , private Expression
89  , private Computation
90 {
91  private:
92  //**Type definitions****************************************************************************
93  typedef typename MT1::ResultType RT1;
94  typedef typename MT2::ResultType RT2;
95  typedef typename MT1::CompositeType CT1;
96  typedef typename MT2::CompositeType CT2;
97  //**********************************************************************************************
98 
99  public:
100  //**Type definitions****************************************************************************
103  typedef typename ResultType::OppositeType OppositeType;
104  typedef typename ResultType::TransposeType TransposeType;
105  typedef typename ResultType::ElementType ElementType;
106  typedef const ElementType ReturnType;
107  typedef const ResultType CompositeType;
108 
110  typedef typename SelectType< IsExpression<MT1>::value, const MT1, const MT1& >::Type LeftOperand;
111 
113  typedef typename SelectType< IsExpression<MT2>::value, const MT2, const MT2& >::Type RightOperand;
114  //**********************************************************************************************
115 
116  //**Compilation flags***************************************************************************
120  //**********************************************************************************************
121 
122  //**Constructor*********************************************************************************
128  explicit inline TSMatTSMatMultExpr( const MT1& lhs, const MT2& rhs )
129  : lhs_( lhs ) // Left-hand side sparse matrix of the multiplication expression
130  , rhs_( rhs ) // Right-hand side sparse matrix of the multiplication expression
131  {
132  BLAZE_INTERNAL_ASSERT( lhs.columns() == rhs.rows(), "Invalid matrix sizes" );
133  }
134  //**********************************************************************************************
135 
136  //**Access operator*****************************************************************************
143  inline ReturnType operator()( size_t i, size_t j ) const {
144  BLAZE_INTERNAL_ASSERT( i < lhs_.rows() , "Invalid row access index" );
145  BLAZE_INTERNAL_ASSERT( j < rhs_.columns(), "Invalid column access index" );
146 
147  typedef typename boost::remove_reference<CT2>::type::ConstIterator ConstIterator;
148 
149  ElementType tmp = ElementType();
150 
151  // Early exit
152  if( lhs_.columns() == 0UL )
153  return tmp;
154 
155  // Fast computation in case the right-hand side sparse matrix directly provides iterators
157  {
158  // Evaluation of the right-hand side sparse matrix operand
159  CT2 B( rhs_ );
160 
161  const ConstIterator end( B.end(j) );
162  ConstIterator element( B.begin(j) );
163 
164  // Early exit in case row i is empty
165  if( element == end )
166  return tmp;
167 
168  // Calculating element (i,j)
169  tmp = lhs_(i,element->index()) * element->value();
170  ++element;
171  for( ; element!=end; ++element )
172  tmp += lhs_(i,element->index()) * element->value();
173  }
174 
175  // Default computation in case the right-hand side sparse matrix doesn't provide iterators
176  else {
177  tmp = lhs_(i,0UL) * rhs_(0UL,j);
178  for( size_t k=1UL; k<lhs_.columns(); ++k ) {
179  tmp += lhs_(i,k) * rhs_(k,j);
180  }
181  }
182 
183  return tmp;
184  }
185  //**********************************************************************************************
186 
187  //**Rows function*******************************************************************************
192  inline size_t rows() const {
193  return lhs_.rows();
194  }
195  //**********************************************************************************************
196 
197  //**Columns function****************************************************************************
202  inline size_t columns() const {
203  return rhs_.columns();
204  }
205  //**********************************************************************************************
206 
207  //**NonZeros function***************************************************************************
212  inline size_t nonZeros() const {
213  return 0UL;
214  }
215  //**********************************************************************************************
216 
217  //**NonZeros function***************************************************************************
223  inline size_t nonZeros( size_t i ) const {
224  return 0UL;
225  }
226  //**********************************************************************************************
227 
228  //**Left operand access*************************************************************************
233  inline LeftOperand leftOperand() const {
234  return lhs_;
235  }
236  //**********************************************************************************************
237 
238  //**Right operand access************************************************************************
243  inline RightOperand rightOperand() const {
244  return rhs_;
245  }
246  //**********************************************************************************************
247 
248  //**********************************************************************************************
254  template< typename T >
255  inline bool isAliased( const T* alias ) const {
257  !RequiresEvaluation<MT1>::value && lhs_.isAliased( alias ) ) ||
259  !RequiresEvaluation<MT2>::value && rhs_.isAliased( alias ) );
260  }
261  //**********************************************************************************************
262 
263  private:
264  //**Member variables****************************************************************************
267  //**********************************************************************************************
268 
269  //**Default assignment to dense matrices********************************************************
283  template< typename MT // Type of the target dense matrix
284  , bool SO > // Storage order of the target dense matrix
285  friend inline typename EnableIf< IsResizable<typename MT::ElementType> >::Type
286  assign( DenseMatrix<MT,SO>& lhs, const TSMatTSMatMultExpr& rhs )
287  {
288  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
289  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
290 
291  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
292  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
293 
294  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
295  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
296 
297  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
298  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
299  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
300  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
301  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
302  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
303 
304  for( size_t j=0UL; j<(~lhs).columns(); ++j ) {
305  const RightIterator rend( B.end(j) );
306  for( RightIterator relem=B.begin(j); relem!=rend; ++relem ) {
307  const LeftIterator lend( A.end( relem->index() ) );
308  for( LeftIterator lelem=A.begin( relem->index() ); lelem!=lend; ++lelem )
309  {
310  if( isDefault( (~lhs)(lelem->index(),j) ) ) {
311  (~lhs)(lelem->index(),j) = lelem->value() * relem->value();
312  }
313  else {
314  (~lhs)(lelem->index(),j) += lelem->value() * relem->value();
315  }
316  }
317  }
318  }
319  }
321  //**********************************************************************************************
322 
323  //**Optimized assignment to dense matrices******************************************************
337  template< typename MT // Type of the target dense matrix
338  , bool SO > // Storage order of the target dense matrix
339  friend inline typename DisableIf< IsResizable<typename MT::ElementType> >::Type
340  assign( DenseMatrix<MT,SO>& lhs, const TSMatTSMatMultExpr& rhs )
341  {
342  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
343  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
344 
345  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
346  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
347 
348  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
349  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
350 
351  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
352  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
353  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
354  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
355  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
356  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
357 
358  for( size_t j=0UL; j<(~lhs).columns(); ++j ) {
359  const RightIterator rend( B.end(j) );
360  for( RightIterator relem=B.begin(j); relem!=rend; ++relem ) {
361  const LeftIterator lend( A.end( relem->index() ) );
362  for( LeftIterator lelem=A.begin( relem->index() ); lelem!=lend; ++lelem ) {
363  (~lhs)(lelem->index(),j) += lelem->value() * relem->value();
364  }
365  }
366  }
367  }
369  //**********************************************************************************************
370 
371  //**Assignment to row-major sparse matrices*****************************************************
384  template< typename MT > // Type of the target sparse matrix
385  friend inline void assign( SparseMatrix<MT,false>& lhs, const TSMatTSMatMultExpr& rhs )
386  {
387  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
388  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
389 
390  BLAZE_CONSTRAINT_MUST_BE_ROW_MAJOR_MATRIX_TYPE( typename MT1::OppositeType );
391  BLAZE_CONSTRAINT_MUST_BE_ROW_MAJOR_MATRIX_TYPE( typename MT2::OppositeType );
392 
393  const typename MT1::OppositeType A( rhs.lhs_ );
394  const typename MT2::OppositeType B( rhs.rhs_ );
395  assign( ~lhs, A * B );
396  }
398  //**********************************************************************************************
399 
400  //**Assignment to column-major sparse matrices**************************************************
413  template< typename MT > // Type of the target sparse matrix
414  friend inline void assign( SparseMatrix<MT,true>& lhs, const TSMatTSMatMultExpr& rhs )
415  {
416  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
417  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
418 
419  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
420  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
421 
422  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
423  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
424 
425  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
426  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
427  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
428  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
429  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
430  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
431 
432  // (Over-)Estimating the number of non-zero entries in the resulting matrix
433  size_t nonzeros( 0UL );
434 
435  for( size_t j=0UL; j<(~lhs).columns(); ++j ) {
436  const RightIterator rend( B.end(j) );
437  for( RightIterator relem=B.begin(j); relem!=rend; ++relem ) {
438  nonzeros += A.nonZeros( relem->index() );
439  }
440  }
441 
442  if( nonzeros > (~lhs).rows() * (~lhs).columns() ) {
443  nonzeros = (~lhs).rows() * (~lhs).columns();
444  }
445 
446  (~lhs).reserve( nonzeros );
447  nonzeros = 0UL;
448 
449  // Performing the matrix-matrix multiplication
450  std::vector<ElementType> values ( (~lhs).rows(), ElementType() );
451  std::vector<size_t> indices( (~lhs).rows(), 0UL );
452  size_t minIndex( inf ), maxIndex( 0UL );
453 
454  for( size_t j=0UL; j<(~lhs).columns(); ++j )
455  {
456  const RightIterator rend( B.end(j) );
457  for( RightIterator relem=B.begin(j); relem!=rend; ++relem )
458  {
459  const LeftIterator lend( A.end( relem->index() ) );
460  for( LeftIterator lelem=A.begin( relem->index() ); lelem!=lend; ++lelem )
461  {
462  if( isDefault( values[lelem->index()] ) ) {
463  values[lelem->index()] = lelem->value() * relem->value();
464  indices[nonzeros] = lelem->index();
465  ++nonzeros;
466  if( lelem->index() < minIndex ) minIndex = lelem->index();
467  if( lelem->index() > maxIndex ) maxIndex = lelem->index();
468  }
469  else {
470  values[lelem->index()] += lelem->value() * relem->value();
471  }
472  }
473  }
474 
475  if( nonzeros > 0UL )
476  {
477  BLAZE_INTERNAL_ASSERT( minIndex <= maxIndex, "Invalid index detected" );
478 
479  if( ( nonzeros + nonzeros ) < ( maxIndex - minIndex ) )
480  {
481  std::sort( indices.begin(), indices.begin() + nonzeros );
482 
483  for( size_t i=0UL; i<nonzeros; ++i ) {
484  const size_t index( indices[i] );
485  (~lhs).append( index, j, values[index] );
486  reset( values[index] );
487  }
488  }
489  else {
490  for( size_t i=minIndex; i<=maxIndex; ++i ) {
491  if( !isDefault( values[i] ) ) {
492  (~lhs).append( i, j, values[i] );
493  reset( values[i] );
494  }
495  }
496  }
497 
498  nonzeros = 0UL;
499  minIndex = inf;
500  maxIndex = 0UL;
501  }
502 
503  (~lhs).finalize( j );
504  }
505  }
507  //**********************************************************************************************
508 
509  //**Addition assignment to dense matrices*******************************************************
522  template< typename MT // Type of the target dense matrix
523  , bool SO > // Storage order of the target dense matarix
524  friend inline void addAssign( DenseMatrix<MT,SO>& lhs, const TSMatTSMatMultExpr& rhs )
525  {
526  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
527  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
528 
529  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
530  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
531 
532  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
533  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
534 
535  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
536  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
537  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
538  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
539  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
540  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
541 
542  for( size_t j=0UL; j<(~lhs).columns(); ++j ) {
543  const RightIterator rend( B.end(j) );
544  for( RightIterator relem=B.begin(j); relem!=rend; ++relem ) {
545  const LeftIterator lend( A.end( relem->index() ) );
546  for( LeftIterator lelem=A.begin( relem->index() ); lelem!=lend; ++lelem ) {
547  (~lhs)(lelem->index(),j) += lelem->value() * relem->value();
548  }
549  }
550  }
551  }
553  //**********************************************************************************************
554 
555  //**Addition assignment to sparse matrices******************************************************
556  // No special implementation for the addition assignment to sparse matrices.
557  //**********************************************************************************************
558 
559  //**Subtraction assignment to dense matrices****************************************************
572  template< typename MT // Type of the target dense matrix
573  , bool SO > // Storage order of the target dense matarix
574  friend inline void subAssign( DenseMatrix<MT,SO>& lhs, const TSMatTSMatMultExpr& rhs )
575  {
576  BLAZE_INTERNAL_ASSERT( (~lhs).rows() == rhs.rows() , "Invalid number of rows" );
577  BLAZE_INTERNAL_ASSERT( (~lhs).columns() == rhs.columns(), "Invalid number of columns" );
578 
579  typedef typename boost::remove_reference<CT1>::type::ConstIterator LeftIterator;
580  typedef typename boost::remove_reference<CT2>::type::ConstIterator RightIterator;
581 
582  CT1 A( rhs.lhs_ ); // Evaluation of the left-hand side sparse matrix operand
583  CT2 B( rhs.rhs_ ); // Evaluation of the right-hand side sparse matrix operand
584 
585  BLAZE_INTERNAL_ASSERT( A.rows() == rhs.lhs_.rows() , "Invalid number of rows" );
586  BLAZE_INTERNAL_ASSERT( A.columns() == rhs.lhs_.columns(), "Invalid number of columns" );
587  BLAZE_INTERNAL_ASSERT( B.rows() == rhs.rhs_.rows() , "Invalid number of rows" );
588  BLAZE_INTERNAL_ASSERT( B.columns() == rhs.rhs_.columns(), "Invalid number of columns" );
589  BLAZE_INTERNAL_ASSERT( A.rows() == (~lhs).rows() , "Invalid number of rows" );
590  BLAZE_INTERNAL_ASSERT( B.columns() == (~lhs).columns() , "Invalid number of columns" );
591 
592  for( size_t j=0UL; j<(~lhs).columns(); ++j ) {
593  const RightIterator rend( B.end(j) );
594  for( RightIterator relem=B.begin(j); relem!=rend; ++relem ) {
595  const LeftIterator lend( A.end( relem->index() ) );
596  for( LeftIterator lelem=A.begin( relem->index() ); lelem!=lend; ++lelem ) {
597  (~lhs)(lelem->index(),j) -= lelem->value() * relem->value();
598  }
599  }
600  }
601  }
603  //**********************************************************************************************
604 
605  //**Subtraction assignment to sparse matrices***************************************************
606  // No special implementation for the subtraction assignment to sparse matrices.
607  //**********************************************************************************************
608 
609  //**Multiplication assignment to dense matrices*************************************************
610  // No special implementation for the multiplication assignment to dense matrices.
611  //**********************************************************************************************
612 
613  //**Multiplication assignment to sparse matrices************************************************
614  // No special implementation for the multiplication assignment to sparse matrices.
615  //**********************************************************************************************
616 
617  //**Compile time checks*************************************************************************
624  //**********************************************************************************************
625 };
626 //*************************************************************************************************
627 
628 
629 
630 
631 //=================================================================================================
632 //
633 // GLOBAL BINARY ARITHMETIC OPERATORS
634 //
635 //=================================================================================================
636 
637 //*************************************************************************************************
664 template< typename T1 // Type of the left-hand side sparse matrix
665  , typename T2 > // Type of the right-hand side sparse matrix
666 inline const TSMatTSMatMultExpr<T1,T2>
668 {
669  if( (~lhs).columns() != (~rhs).rows() )
670  throw std::invalid_argument( "Matrix sizes do not match" );
671 
672  return TSMatTSMatMultExpr<T1,T2>( ~lhs, ~rhs );
673 }
674 //*************************************************************************************************
675 
676 
677 
678 
679 //=================================================================================================
680 //
681 // EXPRESSION TRAIT SPECIALIZATIONS
682 //
683 //=================================================================================================
684 
685 //*************************************************************************************************
687 template< typename MT1, typename MT2, typename VT >
688 struct TSMatDVecMultExprTrait< TSMatTSMatMultExpr<MT1,MT2>, VT >
689 {
690  public:
691  //**********************************************************************************************
692  typedef typename SelectType< IsSparseMatrix<MT1>::value && IsColumnMajorMatrix<MT1>::value &&
693  IsSparseMatrix<MT2>::value && IsColumnMajorMatrix<MT2>::value &&
694  IsDenseVector<VT>::value && !IsTransposeVector<VT>::value
695  , typename TSMatDVecMultExprTrait< MT1, typename TSMatDVecMultExprTrait<MT2,VT>::Type >::Type
696  , INVALID_TYPE >::Type Type;
697  //**********************************************************************************************
698 };
700 //*************************************************************************************************
701 
702 
703 //*************************************************************************************************
705 template< typename MT1, typename MT2, typename VT >
706 struct TSMatSVecMultExprTrait< TSMatTSMatMultExpr<MT1,MT2>, VT >
707 {
708  public:
709  //**********************************************************************************************
710  typedef typename SelectType< IsSparseMatrix<MT1>::value && IsColumnMajorMatrix<MT1>::value &&
711  IsSparseMatrix<MT2>::value && IsColumnMajorMatrix<MT2>::value &&
712  IsSparseVector<VT>::value && !IsTransposeVector<VT>::value
713  , typename TSMatDVecMultExprTrait< MT1, typename TSMatDVecMultExprTrait<MT2,VT>::Type >::Type
714  , INVALID_TYPE >::Type Type;
715  //**********************************************************************************************
716 };
718 //*************************************************************************************************
719 
720 
721 //*************************************************************************************************
723 template< typename VT, typename MT1, typename MT2 >
724 struct TDVecTSMatMultExprTrait< VT, TSMatTSMatMultExpr<MT1,MT2> >
725 {
726  public:
727  //**********************************************************************************************
728  typedef typename SelectType< IsDenseVector<VT>::value && IsTransposeVector<VT>::value &&
729  IsSparseMatrix<MT1>::value && IsColumnMajorMatrix<MT1>::value &&
730  IsSparseMatrix<MT2>::value && IsColumnMajorMatrix<MT2>::value
731  , typename TDVecTSMatMultExprTrait< typename TDVecTSMatMultExprTrait<VT,MT1>::Type, MT2 >::Type
732  , INVALID_TYPE >::Type Type;
733  //**********************************************************************************************
734 };
736 //*************************************************************************************************
737 
738 
739 //*************************************************************************************************
741 template< typename VT, typename MT1, typename MT2 >
742 struct TSVecTSMatMultExprTrait< VT, TSMatTSMatMultExpr<MT1,MT2> >
743 {
744  public:
745  //**********************************************************************************************
746  typedef typename SelectType< IsSparseVector<VT>::value && IsTransposeVector<VT>::value &&
747  IsSparseMatrix<MT1>::value && IsColumnMajorMatrix<MT1>::value &&
748  IsSparseMatrix<MT2>::value && IsColumnMajorMatrix<MT2>::value
749  , typename TSVecTSMatMultExprTrait< typename TSVecTSMatMultExprTrait<VT,MT1>::Type, MT2 >::Type
750  , INVALID_TYPE >::Type Type;
751  //**********************************************************************************************
752 };
754 //*************************************************************************************************
755 
756 } // namespace blaze
757 
758 #endif