• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #ifndef EIGEN_SELFADJOINT_MATRIX_MATRIX_H
11 #define EIGEN_SELFADJOINT_MATRIX_MATRIX_H
12 
13 namespace Eigen {
14 
15 namespace internal {
16 
17 // pack a selfadjoint block diagonal for use with the gebp_kernel
18 template<typename Scalar, typename Index, int Pack1, int Pack2_dummy, int StorageOrder>
19 struct symm_pack_lhs
20 {
21   template<int BlockRows> inline
packsymm_pack_lhs22   void pack(Scalar* blockA, const const_blas_data_mapper<Scalar,Index,StorageOrder>& lhs, Index cols, Index i, Index& count)
23   {
24     // normal copy
25     for(Index k=0; k<i; k++)
26       for(Index w=0; w<BlockRows; w++)
27         blockA[count++] = lhs(i+w,k);           // normal
28     // symmetric copy
29     Index h = 0;
30     for(Index k=i; k<i+BlockRows; k++)
31     {
32       for(Index w=0; w<h; w++)
33         blockA[count++] = numext::conj(lhs(k, i+w)); // transposed
34 
35       blockA[count++] = numext::real(lhs(k,k));   // real (diagonal)
36 
37       for(Index w=h+1; w<BlockRows; w++)
38         blockA[count++] = lhs(i+w, k);          // normal
39       ++h;
40     }
41     // transposed copy
42     for(Index k=i+BlockRows; k<cols; k++)
43       for(Index w=0; w<BlockRows; w++)
44         blockA[count++] = numext::conj(lhs(k, i+w)); // transposed
45   }
operatorsymm_pack_lhs46   void operator()(Scalar* blockA, const Scalar* _lhs, Index lhsStride, Index cols, Index rows)
47   {
48     enum { PacketSize = packet_traits<Scalar>::size };
49     const_blas_data_mapper<Scalar,Index,StorageOrder> lhs(_lhs,lhsStride);
50     Index count = 0;
51     //Index peeled_mc3 = (rows/Pack1)*Pack1;
52 
53     const Index peeled_mc3 = Pack1>=3*PacketSize ? (rows/(3*PacketSize))*(3*PacketSize) : 0;
54     const Index peeled_mc2 = Pack1>=2*PacketSize ? peeled_mc3+((rows-peeled_mc3)/(2*PacketSize))*(2*PacketSize) : 0;
55     const Index peeled_mc1 = Pack1>=1*PacketSize ? (rows/(1*PacketSize))*(1*PacketSize) : 0;
56 
57     if(Pack1>=3*PacketSize)
58       for(Index i=0; i<peeled_mc3; i+=3*PacketSize)
59         pack<3*PacketSize>(blockA, lhs, cols, i, count);
60 
61     if(Pack1>=2*PacketSize)
62       for(Index i=peeled_mc3; i<peeled_mc2; i+=2*PacketSize)
63         pack<2*PacketSize>(blockA, lhs, cols, i, count);
64 
65     if(Pack1>=1*PacketSize)
66       for(Index i=peeled_mc2; i<peeled_mc1; i+=1*PacketSize)
67         pack<1*PacketSize>(blockA, lhs, cols, i, count);
68 
69     // do the same with mr==1
70     for(Index i=peeled_mc1; i<rows; i++)
71     {
72       for(Index k=0; k<i; k++)
73         blockA[count++] = lhs(i, k);                   // normal
74 
75       blockA[count++] = numext::real(lhs(i, i));       // real (diagonal)
76 
77       for(Index k=i+1; k<cols; k++)
78         blockA[count++] = numext::conj(lhs(k, i));     // transposed
79     }
80   }
81 };
82 
83 template<typename Scalar, typename Index, int nr, int StorageOrder>
84 struct symm_pack_rhs
85 {
86   enum { PacketSize = packet_traits<Scalar>::size };
operatorsymm_pack_rhs87   void operator()(Scalar* blockB, const Scalar* _rhs, Index rhsStride, Index rows, Index cols, Index k2)
88   {
89     Index end_k = k2 + rows;
90     Index count = 0;
91     const_blas_data_mapper<Scalar,Index,StorageOrder> rhs(_rhs,rhsStride);
92     Index packet_cols8 = nr>=8 ? (cols/8) * 8 : 0;
93     Index packet_cols4 = nr>=4 ? (cols/4) * 4 : 0;
94 
95     // first part: normal case
96     for(Index j2=0; j2<k2; j2+=nr)
97     {
98       for(Index k=k2; k<end_k; k++)
99       {
100         blockB[count+0] = rhs(k,j2+0);
101         blockB[count+1] = rhs(k,j2+1);
102         if (nr>=4)
103         {
104           blockB[count+2] = rhs(k,j2+2);
105           blockB[count+3] = rhs(k,j2+3);
106         }
107         if (nr>=8)
108         {
109           blockB[count+4] = rhs(k,j2+4);
110           blockB[count+5] = rhs(k,j2+5);
111           blockB[count+6] = rhs(k,j2+6);
112           blockB[count+7] = rhs(k,j2+7);
113         }
114         count += nr;
115       }
116     }
117 
118     // second part: diagonal block
119     Index end8 = nr>=8 ? (std::min)(k2+rows,packet_cols8) : k2;
120     if(nr>=8)
121     {
122       for(Index j2=k2; j2<end8; j2+=8)
123       {
124         // again we can split vertically in three different parts (transpose, symmetric, normal)
125         // transpose
126         for(Index k=k2; k<j2; k++)
127         {
128           blockB[count+0] = numext::conj(rhs(j2+0,k));
129           blockB[count+1] = numext::conj(rhs(j2+1,k));
130           blockB[count+2] = numext::conj(rhs(j2+2,k));
131           blockB[count+3] = numext::conj(rhs(j2+3,k));
132           blockB[count+4] = numext::conj(rhs(j2+4,k));
133           blockB[count+5] = numext::conj(rhs(j2+5,k));
134           blockB[count+6] = numext::conj(rhs(j2+6,k));
135           blockB[count+7] = numext::conj(rhs(j2+7,k));
136           count += 8;
137         }
138         // symmetric
139         Index h = 0;
140         for(Index k=j2; k<j2+8; k++)
141         {
142           // normal
143           for (Index w=0 ; w<h; ++w)
144             blockB[count+w] = rhs(k,j2+w);
145 
146           blockB[count+h] = numext::real(rhs(k,k));
147 
148           // transpose
149           for (Index w=h+1 ; w<8; ++w)
150             blockB[count+w] = numext::conj(rhs(j2+w,k));
151           count += 8;
152           ++h;
153         }
154         // normal
155         for(Index k=j2+8; k<end_k; k++)
156         {
157           blockB[count+0] = rhs(k,j2+0);
158           blockB[count+1] = rhs(k,j2+1);
159           blockB[count+2] = rhs(k,j2+2);
160           blockB[count+3] = rhs(k,j2+3);
161           blockB[count+4] = rhs(k,j2+4);
162           blockB[count+5] = rhs(k,j2+5);
163           blockB[count+6] = rhs(k,j2+6);
164           blockB[count+7] = rhs(k,j2+7);
165           count += 8;
166         }
167       }
168     }
169     if(nr>=4)
170     {
171       for(Index j2=end8; j2<(std::min)(k2+rows,packet_cols4); j2+=4)
172       {
173         // again we can split vertically in three different parts (transpose, symmetric, normal)
174         // transpose
175         for(Index k=k2; k<j2; k++)
176         {
177           blockB[count+0] = numext::conj(rhs(j2+0,k));
178           blockB[count+1] = numext::conj(rhs(j2+1,k));
179           blockB[count+2] = numext::conj(rhs(j2+2,k));
180           blockB[count+3] = numext::conj(rhs(j2+3,k));
181           count += 4;
182         }
183         // symmetric
184         Index h = 0;
185         for(Index k=j2; k<j2+4; k++)
186         {
187           // normal
188           for (Index w=0 ; w<h; ++w)
189             blockB[count+w] = rhs(k,j2+w);
190 
191           blockB[count+h] = numext::real(rhs(k,k));
192 
193           // transpose
194           for (Index w=h+1 ; w<4; ++w)
195             blockB[count+w] = numext::conj(rhs(j2+w,k));
196           count += 4;
197           ++h;
198         }
199         // normal
200         for(Index k=j2+4; k<end_k; k++)
201         {
202           blockB[count+0] = rhs(k,j2+0);
203           blockB[count+1] = rhs(k,j2+1);
204           blockB[count+2] = rhs(k,j2+2);
205           blockB[count+3] = rhs(k,j2+3);
206           count += 4;
207         }
208       }
209     }
210 
211     // third part: transposed
212     if(nr>=8)
213     {
214       for(Index j2=k2+rows; j2<packet_cols8; j2+=8)
215       {
216         for(Index k=k2; k<end_k; k++)
217         {
218           blockB[count+0] = numext::conj(rhs(j2+0,k));
219           blockB[count+1] = numext::conj(rhs(j2+1,k));
220           blockB[count+2] = numext::conj(rhs(j2+2,k));
221           blockB[count+3] = numext::conj(rhs(j2+3,k));
222           blockB[count+4] = numext::conj(rhs(j2+4,k));
223           blockB[count+5] = numext::conj(rhs(j2+5,k));
224           blockB[count+6] = numext::conj(rhs(j2+6,k));
225           blockB[count+7] = numext::conj(rhs(j2+7,k));
226           count += 8;
227         }
228       }
229     }
230     if(nr>=4)
231     {
232       for(Index j2=(std::max)(packet_cols8,k2+rows); j2<packet_cols4; j2+=4)
233       {
234         for(Index k=k2; k<end_k; k++)
235         {
236           blockB[count+0] = numext::conj(rhs(j2+0,k));
237           blockB[count+1] = numext::conj(rhs(j2+1,k));
238           blockB[count+2] = numext::conj(rhs(j2+2,k));
239           blockB[count+3] = numext::conj(rhs(j2+3,k));
240           count += 4;
241         }
242       }
243     }
244 
245     // copy the remaining columns one at a time (=> the same with nr==1)
246     for(Index j2=packet_cols4; j2<cols; ++j2)
247     {
248       // transpose
249       Index half = (std::min)(end_k,j2);
250       for(Index k=k2; k<half; k++)
251       {
252         blockB[count] = numext::conj(rhs(j2,k));
253         count += 1;
254       }
255 
256       if(half==j2 && half<k2+rows)
257       {
258         blockB[count] = numext::real(rhs(j2,j2));
259         count += 1;
260       }
261       else
262         half--;
263 
264       // normal
265       for(Index k=half+1; k<k2+rows; k++)
266       {
267         blockB[count] = rhs(k,j2);
268         count += 1;
269       }
270     }
271   }
272 };
273 
274 /* Optimized selfadjoint matrix * matrix (_SYMM) product built on top of
275  * the general matrix matrix product.
276  */
277 template <typename Scalar, typename Index,
278           int LhsStorageOrder, bool LhsSelfAdjoint, bool ConjugateLhs,
279           int RhsStorageOrder, bool RhsSelfAdjoint, bool ConjugateRhs,
280           int ResStorageOrder>
281 struct product_selfadjoint_matrix;
282 
283 template <typename Scalar, typename Index,
284           int LhsStorageOrder, bool LhsSelfAdjoint, bool ConjugateLhs,
285           int RhsStorageOrder, bool RhsSelfAdjoint, bool ConjugateRhs>
286 struct product_selfadjoint_matrix<Scalar,Index,LhsStorageOrder,LhsSelfAdjoint,ConjugateLhs, RhsStorageOrder,RhsSelfAdjoint,ConjugateRhs,RowMajor>
287 {
288 
289   static EIGEN_STRONG_INLINE void run(
290     Index rows, Index cols,
291     const Scalar* lhs, Index lhsStride,
292     const Scalar* rhs, Index rhsStride,
293     Scalar* res,       Index resStride,
294     const Scalar& alpha, level3_blocking<Scalar,Scalar>& blocking)
295   {
296     product_selfadjoint_matrix<Scalar, Index,
297       EIGEN_LOGICAL_XOR(RhsSelfAdjoint,RhsStorageOrder==RowMajor) ? ColMajor : RowMajor,
298       RhsSelfAdjoint, NumTraits<Scalar>::IsComplex && EIGEN_LOGICAL_XOR(RhsSelfAdjoint,ConjugateRhs),
299       EIGEN_LOGICAL_XOR(LhsSelfAdjoint,LhsStorageOrder==RowMajor) ? ColMajor : RowMajor,
300       LhsSelfAdjoint, NumTraits<Scalar>::IsComplex && EIGEN_LOGICAL_XOR(LhsSelfAdjoint,ConjugateLhs),
301       ColMajor>
302       ::run(cols, rows,  rhs, rhsStride,  lhs, lhsStride,  res, resStride,  alpha, blocking);
303   }
304 };
305 
306 template <typename Scalar, typename Index,
307           int LhsStorageOrder, bool ConjugateLhs,
308           int RhsStorageOrder, bool ConjugateRhs>
309 struct product_selfadjoint_matrix<Scalar,Index,LhsStorageOrder,true,ConjugateLhs, RhsStorageOrder,false,ConjugateRhs,ColMajor>
310 {
311 
312   static EIGEN_DONT_INLINE void run(
313     Index rows, Index cols,
314     const Scalar* _lhs, Index lhsStride,
315     const Scalar* _rhs, Index rhsStride,
316     Scalar* res,        Index resStride,
317     const Scalar& alpha, level3_blocking<Scalar,Scalar>& blocking);
318 };
319 
320 template <typename Scalar, typename Index,
321           int LhsStorageOrder, bool ConjugateLhs,
322           int RhsStorageOrder, bool ConjugateRhs>
323 EIGEN_DONT_INLINE void product_selfadjoint_matrix<Scalar,Index,LhsStorageOrder,true,ConjugateLhs, RhsStorageOrder,false,ConjugateRhs,ColMajor>::run(
324     Index rows, Index cols,
325     const Scalar* _lhs, Index lhsStride,
326     const Scalar* _rhs, Index rhsStride,
327     Scalar* _res,        Index resStride,
328     const Scalar& alpha, level3_blocking<Scalar,Scalar>& blocking)
329   {
330     Index size = rows;
331 
332     typedef gebp_traits<Scalar,Scalar> Traits;
333 
334     typedef const_blas_data_mapper<Scalar, Index, LhsStorageOrder> LhsMapper;
335     typedef const_blas_data_mapper<Scalar, Index, (LhsStorageOrder == RowMajor) ? ColMajor : RowMajor> LhsTransposeMapper;
336     typedef const_blas_data_mapper<Scalar, Index, RhsStorageOrder> RhsMapper;
337     typedef blas_data_mapper<typename Traits::ResScalar, Index, ColMajor> ResMapper;
338     LhsMapper lhs(_lhs,lhsStride);
339     LhsTransposeMapper lhs_transpose(_lhs,lhsStride);
340     RhsMapper rhs(_rhs,rhsStride);
341     ResMapper res(_res, resStride);
342 
343     Index kc = blocking.kc();                   // cache block size along the K direction
344     Index mc = (std::min)(rows,blocking.mc());  // cache block size along the M direction
345     // kc must be smaller than mc
346     kc = (std::min)(kc,mc);
347     std::size_t sizeA = kc*mc;
348     std::size_t sizeB = kc*cols;
349     ei_declare_aligned_stack_constructed_variable(Scalar, blockA, sizeA, blocking.blockA());
350     ei_declare_aligned_stack_constructed_variable(Scalar, blockB, sizeB, blocking.blockB());
351 
352     gebp_kernel<Scalar, Scalar, Index, ResMapper, Traits::mr, Traits::nr, ConjugateLhs, ConjugateRhs> gebp_kernel;
353     symm_pack_lhs<Scalar, Index, Traits::mr, Traits::LhsProgress, LhsStorageOrder> pack_lhs;
354     gemm_pack_rhs<Scalar, Index, RhsMapper, Traits::nr,RhsStorageOrder> pack_rhs;
355     gemm_pack_lhs<Scalar, Index, LhsTransposeMapper, Traits::mr, Traits::LhsProgress, LhsStorageOrder==RowMajor?ColMajor:RowMajor, true> pack_lhs_transposed;
356 
357     for(Index k2=0; k2<size; k2+=kc)
358     {
359       const Index actual_kc = (std::min)(k2+kc,size)-k2;
360 
361       // we have selected one row panel of rhs and one column panel of lhs
362       // pack rhs's panel into a sequential chunk of memory
363       // and expand each coeff to a constant packet for further reuse
364       pack_rhs(blockB, rhs.getSubMapper(k2,0), actual_kc, cols);
365 
366       // the select lhs's panel has to be split in three different parts:
367       //  1 - the transposed panel above the diagonal block => transposed packed copy
368       //  2 - the diagonal block => special packed copy
369       //  3 - the panel below the diagonal block => generic packed copy
370       for(Index i2=0; i2<k2; i2+=mc)
371       {
372         const Index actual_mc = (std::min)(i2+mc,k2)-i2;
373         // transposed packed copy
374         pack_lhs_transposed(blockA, lhs_transpose.getSubMapper(i2, k2), actual_kc, actual_mc);
375 
376         gebp_kernel(res.getSubMapper(i2, 0), blockA, blockB, actual_mc, actual_kc, cols, alpha);
377       }
378       // the block diagonal
379       {
380         const Index actual_mc = (std::min)(k2+kc,size)-k2;
381         // symmetric packed copy
382         pack_lhs(blockA, &lhs(k2,k2), lhsStride, actual_kc, actual_mc);
383 
384         gebp_kernel(res.getSubMapper(k2, 0), blockA, blockB, actual_mc, actual_kc, cols, alpha);
385       }
386 
387       for(Index i2=k2+kc; i2<size; i2+=mc)
388       {
389         const Index actual_mc = (std::min)(i2+mc,size)-i2;
390         gemm_pack_lhs<Scalar, Index, LhsMapper, Traits::mr, Traits::LhsProgress, LhsStorageOrder,false>()
391           (blockA, lhs.getSubMapper(i2, k2), actual_kc, actual_mc);
392 
393         gebp_kernel(res.getSubMapper(i2, 0), blockA, blockB, actual_mc, actual_kc, cols, alpha);
394       }
395     }
396   }
397 
398 // matrix * selfadjoint product
399 template <typename Scalar, typename Index,
400           int LhsStorageOrder, bool ConjugateLhs,
401           int RhsStorageOrder, bool ConjugateRhs>
402 struct product_selfadjoint_matrix<Scalar,Index,LhsStorageOrder,false,ConjugateLhs, RhsStorageOrder,true,ConjugateRhs,ColMajor>
403 {
404 
405   static EIGEN_DONT_INLINE void run(
406     Index rows, Index cols,
407     const Scalar* _lhs, Index lhsStride,
408     const Scalar* _rhs, Index rhsStride,
409     Scalar* res,        Index resStride,
410     const Scalar& alpha, level3_blocking<Scalar,Scalar>& blocking);
411 };
412 
413 template <typename Scalar, typename Index,
414           int LhsStorageOrder, bool ConjugateLhs,
415           int RhsStorageOrder, bool ConjugateRhs>
416 EIGEN_DONT_INLINE void product_selfadjoint_matrix<Scalar,Index,LhsStorageOrder,false,ConjugateLhs, RhsStorageOrder,true,ConjugateRhs,ColMajor>::run(
417     Index rows, Index cols,
418     const Scalar* _lhs, Index lhsStride,
419     const Scalar* _rhs, Index rhsStride,
420     Scalar* _res,        Index resStride,
421     const Scalar& alpha, level3_blocking<Scalar,Scalar>& blocking)
422   {
423     Index size = cols;
424 
425     typedef gebp_traits<Scalar,Scalar> Traits;
426 
427     typedef const_blas_data_mapper<Scalar, Index, LhsStorageOrder> LhsMapper;
428     typedef blas_data_mapper<typename Traits::ResScalar, Index, ColMajor> ResMapper;
429     LhsMapper lhs(_lhs,lhsStride);
430     ResMapper res(_res,resStride);
431 
432     Index kc = blocking.kc();                   // cache block size along the K direction
433     Index mc = (std::min)(rows,blocking.mc());  // cache block size along the M direction
434     std::size_t sizeA = kc*mc;
435     std::size_t sizeB = kc*cols;
436     ei_declare_aligned_stack_constructed_variable(Scalar, blockA, sizeA, blocking.blockA());
437     ei_declare_aligned_stack_constructed_variable(Scalar, blockB, sizeB, blocking.blockB());
438 
439     gebp_kernel<Scalar, Scalar, Index, ResMapper, Traits::mr, Traits::nr, ConjugateLhs, ConjugateRhs> gebp_kernel;
440     gemm_pack_lhs<Scalar, Index, LhsMapper, Traits::mr, Traits::LhsProgress, LhsStorageOrder> pack_lhs;
441     symm_pack_rhs<Scalar, Index, Traits::nr,RhsStorageOrder> pack_rhs;
442 
443     for(Index k2=0; k2<size; k2+=kc)
444     {
445       const Index actual_kc = (std::min)(k2+kc,size)-k2;
446 
447       pack_rhs(blockB, _rhs, rhsStride, actual_kc, cols, k2);
448 
449       // => GEPP
450       for(Index i2=0; i2<rows; i2+=mc)
451       {
452         const Index actual_mc = (std::min)(i2+mc,rows)-i2;
453         pack_lhs(blockA, lhs.getSubMapper(i2, k2), actual_kc, actual_mc);
454 
455         gebp_kernel(res.getSubMapper(i2, 0), blockA, blockB, actual_mc, actual_kc, cols, alpha);
456       }
457     }
458   }
459 
460 } // end namespace internal
461 
462 /***************************************************************************
463 * Wrapper to product_selfadjoint_matrix
464 ***************************************************************************/
465 
466 namespace internal {
467 
468 template<typename Lhs, int LhsMode, typename Rhs, int RhsMode>
469 struct selfadjoint_product_impl<Lhs,LhsMode,false,Rhs,RhsMode,false>
470 {
471   typedef typename Product<Lhs,Rhs>::Scalar Scalar;
472 
473   typedef internal::blas_traits<Lhs> LhsBlasTraits;
474   typedef typename LhsBlasTraits::DirectLinearAccessType ActualLhsType;
475   typedef internal::blas_traits<Rhs> RhsBlasTraits;
476   typedef typename RhsBlasTraits::DirectLinearAccessType ActualRhsType;
477 
478   enum {
479     LhsIsUpper = (LhsMode&(Upper|Lower))==Upper,
480     LhsIsSelfAdjoint = (LhsMode&SelfAdjoint)==SelfAdjoint,
481     RhsIsUpper = (RhsMode&(Upper|Lower))==Upper,
482     RhsIsSelfAdjoint = (RhsMode&SelfAdjoint)==SelfAdjoint
483   };
484 
485   template<typename Dest>
486   static void run(Dest &dst, const Lhs &a_lhs, const Rhs &a_rhs, const Scalar& alpha)
487   {
488     eigen_assert(dst.rows()==a_lhs.rows() && dst.cols()==a_rhs.cols());
489 
490     typename internal::add_const_on_value_type<ActualLhsType>::type lhs = LhsBlasTraits::extract(a_lhs);
491     typename internal::add_const_on_value_type<ActualRhsType>::type rhs = RhsBlasTraits::extract(a_rhs);
492 
493     Scalar actualAlpha = alpha * LhsBlasTraits::extractScalarFactor(a_lhs)
494                                * RhsBlasTraits::extractScalarFactor(a_rhs);
495 
496     typedef internal::gemm_blocking_space<(Dest::Flags&RowMajorBit) ? RowMajor : ColMajor,Scalar,Scalar,
497               Lhs::MaxRowsAtCompileTime, Rhs::MaxColsAtCompileTime, Lhs::MaxColsAtCompileTime,1> BlockingType;
498 
499     BlockingType blocking(lhs.rows(), rhs.cols(), lhs.cols(), 1, false);
500 
501     internal::product_selfadjoint_matrix<Scalar, Index,
502       EIGEN_LOGICAL_XOR(LhsIsUpper,internal::traits<Lhs>::Flags &RowMajorBit) ? RowMajor : ColMajor, LhsIsSelfAdjoint,
503       NumTraits<Scalar>::IsComplex && EIGEN_LOGICAL_XOR(LhsIsUpper,bool(LhsBlasTraits::NeedToConjugate)),
504       EIGEN_LOGICAL_XOR(RhsIsUpper,internal::traits<Rhs>::Flags &RowMajorBit) ? RowMajor : ColMajor, RhsIsSelfAdjoint,
505       NumTraits<Scalar>::IsComplex && EIGEN_LOGICAL_XOR(RhsIsUpper,bool(RhsBlasTraits::NeedToConjugate)),
506       internal::traits<Dest>::Flags&RowMajorBit  ? RowMajor : ColMajor>
507       ::run(
508         lhs.rows(), rhs.cols(),                 // sizes
509         &lhs.coeffRef(0,0), lhs.outerStride(),  // lhs info
510         &rhs.coeffRef(0,0), rhs.outerStride(),  // rhs info
511         &dst.coeffRef(0,0), dst.outerStride(),  // result info
512         actualAlpha, blocking                   // alpha
513       );
514   }
515 };
516 
517 } // end namespace internal
518 
519 } // end namespace Eigen
520 
521 #endif // EIGEN_SELFADJOINT_MATRIX_MATRIX_H
522