decompositions.hpp
Go to the documentation of this file.
1 /*!
2  * \brief Defines types for matrix decompositions and solvers
3  *
4  * \author O. Krause
5  * \date 2016
6  *
7  *
8  * \par Copyright 1995-2015 Shark Development Team
9  *
10  * <BR><HR>
11  * This file is part of Shark.
12  * <http://image.diku.dk/shark/>
13  *
14  * Shark is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License as published
16  * by the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * Shark is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
26  *
27  */
28 #ifndef REMORA_DECOMPOSITIONS_HPP
29 #define REMORA_DECOMPOSITIONS_HPP
30 
31 #include "kernels/trsm.hpp"
32 #include "kernels/trsv.hpp"
33 #include "kernels/potrf.hpp"
34 #include "kernels/pstrf.hpp"
35 #include "kernels/getrf.hpp"
36 #include "kernels/syev.hpp"
37 #include "assignment.hpp"
38 #include "permutation.hpp"
39 #include "matrix_expression.hpp"
40 #include "vector_proxy.hpp"
41 #include "vector_expression.hpp"
42 #include "io.hpp"
43 
44 namespace remora{
45 template<class D, class Device>
46 struct solver_expression{
47  typedef Device device_type;
48 
49  D const& operator()() const {
50  return *static_cast<D const*>(this);
51  }
52 
53  D& operator()() {
54  return *static_cast<D*>(this);
55  }
56 };
57 
58 
59 /// \brief Lower triangular Cholesky decomposition.
60 ///
61 /// Given an \f$ m \times m \f$ symmetric positive definite matrix
62 /// \f$A\f$, represents the lower triangular matrix \f$L\f$ such that
63 /// \f$A = LL^T \f$.
64 ///
65 /// This decomposition is a corner block of many linear algebra routines
66 /// especially for solving symmetric positive definite systems of equations.
67 ///
68 /// Unlike many other decompositions, this decomposition is fast,
69 /// numerically stable and can be updated when the original matrix is changed
70 /// (rank-k updates of the form A<-alpha A + VCV^T)
71 template<class MatrixStorage>
72 class cholesky_decomposition:
73  public solver_expression<
74  cholesky_decomposition<MatrixStorage>,
75  typename MatrixStorage::device_type
76 >{
77 private:
78  typedef typename MatrixStorage::value_type value_type;
79 public:
80  typedef typename MatrixStorage::device_type device_type;
81  cholesky_decomposition(){}
82  template<class E>
83  cholesky_decomposition(matrix_expression<E,device_type> const& e):m_cholesky(e){
84  kernels::potrf<lower>(m_cholesky);
85  }
86 
87  template<class E>
88  void decompose(matrix_expression<E,device_type> const& e){
89  m_cholesky.resize(e().size1(), e().size2());
90  noalias(m_cholesky) = e;
91  kernels::potrf<lower>(m_cholesky);
92  }
93 
94  MatrixStorage const& lower_factor()const{
95  return m_cholesky;
96  }
97 
98  auto upper_factor()const -> decltype(trans(std::declval<MatrixStorage const&>())){
99  return trans(m_cholesky);
100  }
101 
102  template<class MatB>
103  void solve(matrix_expression<MatB,device_type>& B, left)const{
104  kernels::trsm<lower,left >(lower_factor(),B);
105  kernels::trsm<upper,left >(upper_factor(),B);
106  }
107  template<class MatB>
108  void solve(matrix_expression<MatB,device_type>& B, right)const{
109  kernels::trsm<upper,right >(upper_factor(),B);
110  kernels::trsm<lower,right >(lower_factor(),B);
111  }
112  template<class VecB, bool Left>
113  void solve(vector_expression<VecB,device_type>&b, system_tag<Left>)const{
114  kernels::trsv<lower, left>(lower_factor(),b);
115  kernels::trsv<upper,left>(upper_factor(),b);
116  }
117 
118  /// \brief Updates a covariance factor by a rank one update
119  ///
120  /// Let \f$ A=LL^T \f$ be a matrix with its lower cholesky factor. Assume we want to update
121  /// A using a simple rank-one update \f$ A = \alpha A+ \beta vv^T \f$. This invalidates L and
122  /// it needs to be recomputed which is O(n^3). instead we can update the factorisation
123  /// directly by performing a similar, albeit more complex algorithm on L, which can be done
124  /// in O(L^2).
125  ///
126  /// Alpha is not required to be positive, but if it is not negative, one has to be carefull
127  /// that the update would keep A positive definite. Otherwise the decomposition does not
128  /// exist anymore and an exception is thrown.
129  ///
130  /// \param v the update vector
131  /// \param alpha the scaling factor, must be positive.
132  /// \param beta the update factor. it Can be positive or negative
133  template<class VecV>
134  void update(value_type alpha, value_type beta, vector_expression<VecV,device_type> const& v){
135  if(beta == 0){
136  m_cholesky *= std::sqrt(alpha);
137  return;
138  }
139  //implementation blatantly stolen from Eigen
140  std::size_t n = v().size();
141  auto& L = m_cholesky;
142  typename vector_temporary<VecV>::type temp = v;
143  double beta_prime = 1;
144  double a = std::sqrt(alpha);
145  for(std::size_t j=0; j != n; ++j)
146  {
147  double Ljj = a * L(j,j);
148  double dj = Ljj * Ljj;
149  double wj = temp(j);
150  double swj2 = beta * wj * wj;
151  double gamma = dj * beta_prime + swj2;
152 
153  double x = dj + swj2/beta_prime;
154  if (x <= 0.0)
155  throw std::invalid_argument("[cholesky_decomposition::update] update makes matrix indefinite, no update available");
156  double nLjj = std::sqrt(x);
157  L(j,j) = nLjj;
158  beta_prime += swj2/dj;
159 
160  // Update the terms of L
161  if(j+1 <n)
162  {
163  subrange(column(L,j),j+1,n) *= a;
164  noalias(subrange(temp,j+1,n)) -= (wj/Ljj) * subrange(column(L,j),j+1,n);
165  if(gamma == 0)
166  continue;
167  subrange(column(L,j),j+1,n) *= nLjj/Ljj;
168  noalias(subrange(column(L,j),j+1,n))+= (nLjj * beta*wj/gamma)*subrange(temp,j+1,n);
169  }
170  }
171  }
172 
173  template<typename Archive>
174  void serialize( Archive & ar, const std::size_t version ) {
175  ar & m_cholesky;
176  }
177 private:
178  MatrixStorage m_cholesky;
179 };
180 
181 
182 /// \brief Symmetric eigenvalue decomposition A=QDQ^T
183 ///
184 /// every symmetric matrix can be decomposed into its eigenvalue decomposition
185 /// A=QDQ^T, where Q is an orthogonal matrix with Q^TQ=QQ^T=I
186 /// and D is the diagonal matrix of eigenvalues of A.
187 template<class MatrixStorage>
188 class symm_eigenvalue_decomposition:
189  public solver_expression<
190  symm_eigenvalue_decomposition<MatrixStorage>,
191  typename MatrixStorage::device_type
192 >{
193 private:
194  typedef typename MatrixStorage::value_type value_type;
195  typedef typename vector_temporary<MatrixStorage>::type VectorStorage;
196 public:
197  typedef typename MatrixStorage::device_type device_type;
198  symm_eigenvalue_decomposition(){}
199  template<class E>
200  symm_eigenvalue_decomposition(matrix_expression<E,device_type> const& e){
201  decompose(e);
202  }
203 
204  template<class E>
205  void decompose(matrix_expression<E,device_type> const& e){
206  REMORA_SIZE_CHECK(e().size1() == e().size2());
207  m_eigenvectors.resize(e().size1(),e().size1());
208  m_eigenvalues.resize(e().size1());
209  noalias(m_eigenvectors) = e;
210 
211  kernels::syev(m_eigenvectors,m_eigenvalues);
212  }
213 
214  MatrixStorage const& Q()const{
215  return m_eigenvectors;
216  }
217  VectorStorage const& D()const{
218  return m_eigenvalues;
219  }
220 
221 
222  template<class MatB>
223  void solve(matrix_expression<MatB,device_type>& B, left)const{
224  B() = Q() % to_diagonal(elem_inv(D()))% trans(Q()) % B;
225  }
226  template<class MatB>
227  void solve(matrix_expression<MatB,device_type>& B, right)const{
228  auto transB = trans(B);
229  solve(transB,left());
230  }
231  template<class VecB>
232  void solve(vector_expression<VecB,device_type>&b, left)const{
233  b() = Q() % safe_div(trans(Q()) % b,D() ,0.0);
234  }
235 
236  template<class VecB>
237  void solve(vector_expression<VecB,device_type>&b, right)const{
238  solve(b,left());
239  }
240 
241  template<typename Archive>
242  void serialize( Archive & ar, const std::size_t version ) {
243  ar & m_eigenvectors;
244  ar & m_eigenvalues;
245  }
246 private:
247  MatrixStorage m_eigenvectors;
248  VectorStorage m_eigenvalues;
249 };
250 
251 
252 
253 
254 template<class MatrixStorage>
255 class pivoting_lu_decomposition:
256 public solver_expression<
257  pivoting_lu_decomposition<MatrixStorage>,
258  typename MatrixStorage::device_type
259 >{
260 public:
261  typedef typename MatrixStorage::device_type device_type;
262  template<class E>
263  pivoting_lu_decomposition(matrix_expression<E,device_type> const& e)
264  :m_factor(e), m_permutation(e().size1()){
265  kernels::getrf(m_factor,m_permutation);
266  }
267 
268  MatrixStorage const& factor()const{
269  return m_factor;
270  }
271 
272  permutation_matrix const& permutation() const{
273  return m_permutation;
274  }
275 
276  template<class MatB>
277  void solve(matrix_expression<MatB,device_type>& B, left)const{
278  swap_rows(m_permutation,B);
279  kernels::trsm<unit_lower,left>(m_factor,B);
280  kernels::trsm<upper,left>(m_factor,B);
281  }
282  template<class MatB>
283  void solve(matrix_expression<MatB,device_type>& B, right)const{
284  kernels::trsm<upper,right>(m_factor,B);
285  kernels::trsm<unit_lower,right>(m_factor,B);
286  swap_columns_inverted(m_permutation,B);
287  }
288  template<class VecB>
289  void solve(vector_expression<VecB,device_type>& b, left)const{
290  swap_rows(m_permutation,b);
291  kernels::trsv<unit_lower,left>(m_factor,b);
292  kernels::trsv<upper,left>(m_factor,b);
293  }
294  template<class VecB>
295  void solve(vector_expression<VecB,device_type>& b, right)const{
296  kernels::trsv<upper,right>(m_factor,b);
297  kernels::trsv<unit_lower,right>(m_factor,b);
298  swap_rows_inverted(m_permutation,b);
299  }
300 private:
301  MatrixStorage m_factor;
302  permutation_matrix m_permutation;
303 };
304 
305 
306 // This is an implementation suggested by
307 // "Fast Computation of Moore-Penrose Inverse Matrices"
308 // applied to the special case of symmetric pos semi-def matrices
309 // trading numerical accuracy vs speed. We go for speed.
310 //
311 // The fact that A is not full rank means it is not invertable,
312 // so we solve it in a least squares sense.
313 //
314 // We use the formula for the pseudo-inverse:
315 // (P^T A P)^-1 = L(L^TL)^-1(L^TL)^-1 L^T
316 // where L is a matrix obtained by some rank revealing factorization
317 // P^T A P = L L^T
318 // we chose a pivoting cholesky to make use of the fact that A is symmetric
319 // and all eigenvalues are >=0. If A has full rank, this reduces to
320 // the cholesky factor where the pivoting leads to slightly smaller numerical errors
321 // At a higher computational cost compared to the normal cholesky decomposition.
322 //
323 // In many cases, the small eigenvalues tgend to be unstable, e.g. when A is estimated from
324 // strongly correlated data. in this case, it is wise to limit the conditioning of L, i.e. throw away
325 // eigenvalues which are more than c times smaller than the largest eigenvalue.
326 template<class MatrixStorage>
327 class symm_pos_semi_definite_solver:
328  public solver_expression<symm_pos_semi_definite_solver<MatrixStorage>, typename MatrixStorage::device_type>{
329 public:
330  typedef typename MatrixStorage::device_type device_type;
331  template<class E>
332  symm_pos_semi_definite_solver(matrix_expression<E,device_type> const& e, double max_conditioning = 0.0)
333  :m_factor(e), m_permutation(e().size1()){
334  m_rank = kernels::pstrf<lower>(m_factor,m_permutation);
335  if(m_rank == e().size1()) return; //full rank, so m_factor is lower triangular and we are done
336  if(max_conditioning > 0){
337  while(m_factor(0,0) > max_conditioning * m_factor(m_rank-1,m_rank -1))
338  --m_rank;
339  }
340 
341  auto L = columns(m_factor,0,m_rank);
342  m_cholesky.decompose(trans(L) % L);
343  }
344 
345  std::size_t rank()const{
346  return m_rank;
347  }
348 
349  //compute C so that A^dagger = CC^T
350  //where A^dagger is the moore-penrose inverse
351  // m must be of size rank x n
352  template<class Mat>
353  void compute_inverse_factor(matrix_expression<Mat,device_type>& C)const{
354  REMORA_SIZE_CHECK(C().size1() == m_rank);
355  REMORA_SIZE_CHECK(C().size2() == m_factor.size1());
356  if(m_rank == m_factor.size1()){//matrix has full rank
357  //initialize as identity matrix and solve
358  noalias(C) = identity_matrix<double>( m_factor.size1());
359  swap_columns_inverted(m_permutation,C);
360  kernels::trsm<lower,left>(m_factor,C);
361  }else{
362  auto L = columns(m_factor,0,m_rank);
363  noalias(C) = trans(L);
364  m_cholesky.solve(C,left());
365  swap_columns_inverted(m_permutation,C);
366  }
367 
368  }
369  template<class MatB>
370  void solve(matrix_expression<MatB,device_type>& B, left)const{
371  swap_rows(m_permutation,B);
372  if(m_rank == 0){//matrix is zero
373  B().clear();
374  }else if(m_rank == m_factor.size1()){//matrix has full rank
375  kernels::trsm<lower,left>(m_factor,B);
376  kernels::trsm<upper,left>(trans(m_factor),B);
377  }else{//matrix is missing rank
378  auto L = columns(m_factor,0,m_rank);
379  auto Z = eval_block(trans(L) % B);
380  m_cholesky.solve(Z,left());
381  m_cholesky.solve(Z,left());
382  noalias(B) = L % Z;
383  }
384  swap_rows_inverted(m_permutation,B);
385  }
386  template<class MatB>
387  void solve(matrix_expression<MatB,device_type>& B, right)const{
388  //compute using symmetry of the system of equations
389  auto transB = trans(B);
390  solve(transB, left());
391  }
392  template<class VecB, bool Left>
393  void solve(vector_expression<VecB,device_type>& b, system_tag<Left>)const{
394  swap_rows(m_permutation,b);
395  if(m_rank == 0){//matrix is zero
396  b().clear();
397  }else if(m_rank == m_factor.size1()){//matrix has full rank
398  kernels::trsv<lower,left >(m_factor,b);
399  kernels::trsv<upper,left >(trans(m_factor),b);
400  }else{//matrix is missing rank
401  auto L = columns(m_factor,0,m_rank);
402  auto z = eval_block(prod(trans(L),b));
403  m_cholesky.solve(z,left());
404  m_cholesky.solve(z,left());
405  noalias(b) = prod(L,z);
406  }
407  swap_rows_inverted(m_permutation,b);
408  }
409 private:
410  std::size_t m_rank;
411  MatrixStorage m_factor;
412  cholesky_decomposition<MatrixStorage> m_cholesky;
413  permutation_matrix m_permutation;
414 };
415 
416 template<class MatA>
417 class cg_solver:public solver_expression<cg_solver<MatA>, typename MatA::device_type>{
418 public:
419  typedef typename MatA::const_closure_type matrix_closure_type;
420  typedef typename MatA::device_type device_type;
421  cg_solver(matrix_closure_type const& e, double epsilon = 1.e-10, unsigned int max_iterations = 0)
422  :m_expression(e), m_epsilon(epsilon), m_max_iterations(max_iterations){}
423 
424  template<class MatB>
425  void solve(
426  matrix_expression<MatB,device_type>& B, left,
427  double epsilon, unsigned max_iterations
428  )const{
429  typename matrix_temporary<MatB>::type X = B;
430  cg(m_expression,X, B, epsilon, max_iterations);
431  noalias(B) = X;
432  }
433  template<class MatB>
434  void solve(
435  matrix_expression<MatB,device_type>& B, right,
436  double epsilon, unsigned max_iterations
437  )const{
438  auto transB = trans(B);
439  typename transposed_matrix_temporary<MatB>::type X = transB;
440  cg(m_expression,X,transB, epsilon, max_iterations);
441  noalias(transB) = X;
442  }
443  template<class VecB, bool Left>
444  void solve(
445  vector_expression<VecB,device_type>&b, system_tag<Left>,
446  double epsilon, unsigned max_iterations
447  )const{
448  typename vector_temporary<VecB>::type x = b;
449  cg(m_expression,x,b,epsilon,max_iterations);
450  noalias(b) = x;
451  }
452 
453  template<class VecB, bool Left>
454  void solve(vector_expression<VecB,device_type>&b, system_tag<Left> tag)const{
455  solve(b, tag, m_epsilon, m_max_iterations);
456  }
457  template<class MatB, bool Left>
458  void solve(matrix_expression<MatB,device_type>&B, system_tag<Left> tag)const{
459  solve(B, tag, m_epsilon, m_max_iterations);
460  }
461 private:
462  template<class MatT, class VecB, class VecX>
463  void cg(
464  matrix_expression<MatT, device_type> const& A,
465  vector_expression<VecX, device_type>& x,
466  vector_expression<VecB, device_type> const& b,
467  double epsilon,
468  unsigned int max_iterations
469  )const{
470  REMORA_SIZE_CHECK(A().size1() == A().size2());
471  REMORA_SIZE_CHECK(A().size1() == b().size());
472  REMORA_SIZE_CHECK(A().size1() == x().size());
473  typedef typename vector_temporary<VecX>::type vector_type;
474 
475  std::size_t dim = b().size();
476 
477  //initialize point.
478  vector_type residual = b - prod(A,x);
479  //check if provided solution is better than starting at 0
480  if(norm_inf(residual) > norm_inf(b)){
481  x().clear();
482  residual = b;
483  }
484  vector_type next_residual(dim); //the next residual
485  vector_type p = residual; //the search direction- initially it is the gradient direction
486  vector_type Ap(dim); //stores prod(A,p)
487 
488  for(std::size_t iter = 0;; ++iter){
489  if(max_iterations != 0 && iter >= max_iterations) break;
490  noalias(Ap) = prod(A,p);
491  double rsqr = norm_sqr(residual);
492  double alpha = rsqr/inner_prod(p,Ap);
493  noalias(x) += alpha * p;
494  noalias(next_residual) = residual - alpha * Ap;
495  if(norm_inf(next_residual) < epsilon)
496  break;
497 
498  double beta = inner_prod(next_residual,next_residual)/rsqr;
499  p *= beta;
500  noalias(p) += next_residual;
501  swap(residual,next_residual);
502  }
503  }
504 
505  template<class MatT, class MatB, class MatX>
506  void cg(
507  matrix_expression<MatT, device_type> const& A,
508  matrix_expression<MatX, device_type>& X,
509  matrix_expression<MatB, device_type> const& B,
510  double epsilon,
511  unsigned int max_iterations
512  )const{
513  REMORA_SIZE_CHECK(A().size1() == A().size2());
514  REMORA_SIZE_CHECK(A().size1() == B().size1());
515  REMORA_SIZE_CHECK(A().size1() == X().size1());
516  REMORA_SIZE_CHECK(B().size2() == X().size2());
517  typedef typename vector_temporary<MatX>::type vector_type;
518  typedef typename matrix_temporary<MatX>::type matrix_type;
519 
520  std::size_t dim = B().size1();
521  std::size_t num_rhs = B().size2();
522 
523  //initialize gradient given the starting point
524  matrix_type residual = B - prod(A,X);
525  //check for each rhs whether the starting point is better than starting from scratch
526  for(std::size_t i = 0; i != num_rhs; ++i){
527  if(norm_inf(column(residual,i)) <= norm_inf(column(residual,i))){
528  column(X,i).clear();
529  noalias(column(residual,i)) = column(B,i);
530  }
531  }
532 
533  vector_type next_residual(dim); //the next residual of a column
534  matrix_type P = residual; //the search direction- initially it is the gradient direction
535  matrix_type AP(dim, num_rhs); //stores prod(A,p)
536 
537  for(std::size_t iter = 0;; ++iter){
538  if(max_iterations != 0 && iter >= max_iterations) break;
539  //compute the product for all rhs at the same time
540  noalias(AP) = prod(A,P);
541  //for each rhs apply a step of cg
542  for(std::size_t i = 0; i != num_rhs; ++i){
543  auto r = column(residual,i);
544  //skip this if we are done already
545  //otherwise we might run into numerical troubles later on
546  if(norm_inf(r) < epsilon) continue;
547 
548  auto x = column(X,i);
549  auto p = column(P,i);
550  auto Ap = column(AP,i);
551  double rsqr = norm_sqr(r);
552  double alpha = rsqr/inner_prod(p,Ap);
553  noalias(x) += alpha * p;
554  noalias(next_residual) = r - alpha * Ap;
555  double beta = inner_prod(next_residual,next_residual)/rsqr;
556  p *= beta;
557  noalias(p) += next_residual;
558  noalias(r) = next_residual;
559  }
560  //if all solutions are within tolerance, we are done
561  if(max(abs(residual)) < epsilon)
562  break;
563  }
564  }
565 
566  matrix_closure_type m_expression;
567  double m_epsilon;
568  unsigned int m_max_iterations;
569 };
570 
571 
572 /////////////////////////////////////////////////////////////////
573 ////Traits connecting decompositions/solvers with tags
574 /////////////////////////////////////////////////////////////////
575 struct symm_pos_def{ typedef symm_pos_def transposed_orientation;};
576 struct symm_semi_pos_def{ typedef symm_semi_pos_def transposed_orientation;};
577 struct indefinite_full_rank{ typedef indefinite_full_rank transposed_orientation;};
578 struct conjugate_gradient{
579  typedef conjugate_gradient transposed_orientation;
580  double epsilon;
581  unsigned max_iterations;
582  conjugate_gradient(double epsilon = 1.e-10, unsigned max_iterations = 0)
583  :epsilon(epsilon), max_iterations(max_iterations){}
584 };
585 
586 
587 namespace detail{
588 template<class MatA, class SolverTag>
589 struct solver_traits;
590 
591 template<class MatA, bool Upper, bool Unit>
592 struct solver_traits<MatA,triangular_tag<Upper,Unit> >{
593  class type: public solver_expression<type,typename MatA::device_type>{
594  public:
595  typedef typename MatA::const_closure_type matrix_closure_type;
596  typedef typename MatA::device_type device_type;
597  type(matrix_closure_type const& e, triangular_tag<Upper,Unit>):m_matrix(e){}
598 
599  template<class MatB, bool Left>
600  void solve(matrix_expression<MatB, device_type>& B, system_tag<Left> ){
601  kernels::trsm<triangular_tag<Upper,Unit>,system_tag<Left> >(m_matrix,B);
602  }
603  template<class VecB, bool Left>
604  void solve(vector_expression<VecB, device_type>& b, system_tag<Left> ){
605  kernels::trsv<triangular_tag<Upper,Unit>,system_tag<Left> >(m_matrix,b);
606  }
607  private:
608  matrix_closure_type m_matrix;
609  };
610 };
611 
612 template<class MatA>
613 struct solver_traits<MatA,symm_pos_def>{
614  struct type : public cholesky_decomposition<typename matrix_temporary<MatA>::type>{
615  template<class M>
616  type(M const& m, symm_pos_def)
617  :cholesky_decomposition<typename matrix_temporary<MatA>::type>(m){}
618  };
619 };
620 
621 template<class MatA>
622 struct solver_traits<MatA,indefinite_full_rank>{
623  struct type : public pivoting_lu_decomposition<typename matrix_temporary<MatA>::type>{
624  template<class M>
625  type(M const& m, indefinite_full_rank)
626  :pivoting_lu_decomposition<typename matrix_temporary<MatA>::type>(m){}
627  };
628 };
629 
630 template<class MatA>
631 struct solver_traits<MatA,symm_semi_pos_def>{
632  struct type : public symm_pos_semi_definite_solver<typename matrix_temporary<MatA>::type>{
633  template<class M>
634  type(M const& m, symm_semi_pos_def)
635  :symm_pos_semi_definite_solver<typename matrix_temporary<MatA>::type>(m){}
636  };
637 };
638 
639 template<class MatA>
640 struct solver_traits<MatA,conjugate_gradient>{
641  struct type : public cg_solver<MatA>{
642  template<class M>
643  type(M const& m, conjugate_gradient t):cg_solver<MatA>(m,t.epsilon,t.max_iterations){}
644  };
645 };
646 
647 }
648 
649 template<class MatA, class SolverType>
650 struct solver:public detail::solver_traits<MatA,SolverType>::type{
651  template<class M>
652  solver(M const& m, SolverType t = SolverType()): detail::solver_traits<MatA,SolverType>::type(m,t){}
653 };
654 
655 
656 }
657 #endif