Extension: Matrix functions
#include "dg/matrix/matrix.h"
Loading...
Searching...
No Matches
tridiaginv.h
Go to the documentation of this file.
1#pragma once
2
3#include <boost/math/special_functions.hpp> // has to be included before lapack in certain versions
4#include "dg/algorithm.h"
5
6#include "functors.h"
11namespace dg{
12namespace mat{
14namespace lapack
15{
16// MW: Update 27.5.2025
17// Unfortunately, the CMake support for LAPACKE is rather not straightforward
18// while LAPACK is supported out of the box with find_package( LAPACK)
19// so we rather call the fortran functions from C directly ourselves.
20//
21// This is how you add new routines:
22// 1. Go find relevant Fortran routine ( usually there are separate routine for
23// each value type; single, double ,complex )
24// FORTRAN https://www.netlib.org/lapack/explore-html/index.html
25//
26// 2. Call the fortran function from C packaged in a nice C++ interface!
27// We here follow
28// https://scicomp.stackexchange.com/questions/26395/how-to-start-using-lapack-in-c
29// 2.1 Add the extern "C" binding below, where all parameters are pointers
30// 2.2 When matrices are involved note that the Fortran ordering is "column
31// major", which is the transpose of how e.g. our SquareMatrix is ordered)
32// 2.3. Replace all arrays with a ContainerType in our interface
33// 2.4. Use C++-17 if constexpr to dispatch value type
34//
35extern "C" {
36extern void dstev_(char*,int*,double*,double*,double*,int*,double*,int*);
37extern void sstev_(char*,int*,float*,float*,float*,int*,float*,int*);
38}
39// Compute Eigenvalues and, optionally, Eigenvectors of a real symmetric tridiagonal matrix A
40template<class ContainerType0, class ContainerType1, class ContainerType2, class ContainerType3>
41void stev(
42 char job, // 'N' Compute Eigenvalues only, 'V' Compute Eigenvalues and Eigenvectors
43 ContainerType0& D, // diagonal of T on input, Eigenvalues on output
44 ContainerType1& E, // subdiagonal of T on input |size D.size()-1 ; in E[0] - E[D.size()-2]|; destroyed on output
45 ContainerType2& Z, // IF job = 'V' && column major then the i-th column contains i-th EV, if job = 'N' not referenced
46 ContainerType3& work // If job = 'V' needs size max( 1, 2*D.size() - 2), else not referenced
47 )
48{
50 static_assert( std::is_same_v<value_type, double> or std::is_same_v<value_type, float>,
51 "Value type must be either float or double");
52 static_assert( std::is_same_v<dg::get_value_type<ContainerType1>, value_type> &&
53 std::is_same_v<dg::get_value_type<ContainerType2>, value_type> &&
54 std::is_same_v<dg::get_value_type<ContainerType3>, value_type>,
55 "All Vectors must have same value type");
56 static_assert( std::is_same_v<dg::get_execution_policy<ContainerType0>, dg::SerialTag> &&
57 std::is_same_v<dg::get_execution_policy<ContainerType1>, dg::SerialTag> &&
58 std::is_same_v<dg::get_execution_policy<ContainerType2>, dg::SerialTag> &&
59 std::is_same_v<dg::get_execution_policy<ContainerType3>, dg::SerialTag>,
60 "All Vectors must have serial execution policy");
61
62 // job = 'N' Compute Eigenvalues only
63 // job = 'V' Compute Eigenvalues and Eigenvectors
64 int N = D.size();
65 value_type * D_ptr = thrust::raw_pointer_cast( &D[0]);
66 value_type * E_ptr = thrust::raw_pointer_cast( &E[0]);
67 value_type * Z_ptr = nullptr;
68 int ldz = N;
69 value_type * work_ptr = nullptr;
70 if( job == 'V')
71 {
72 Z_ptr = thrust::raw_pointer_cast( &Z[0]);
73 work_ptr = thrust::raw_pointer_cast( &work[0]);
74 }
75
76 int info;
77 if constexpr ( std::is_same_v<value_type, double>)
78 dstev_( &job, &N, D_ptr, E_ptr, Z_ptr, &ldz, work_ptr, &info);
79 else if constexpr ( std::is_same_v<value_type, float>)
80 sstev_( &job, &N, D_ptr, E_ptr, Z_ptr, ldz, work_ptr, &info);
81 if( info != 0)
82 {
83 throw dg::Error( dg::Message(_ping_) << "stev failed with error code "<<info<<"\n");
84 }
85}
86
87}
89
92
104template<class value_type>
105value_type compute_Tinv_m1( const dg::TriDiagonal<thrust::host_vector<value_type>>
106 & T, unsigned size)
107{
108 // P = Plus diagonal
109 // O = zerO diagonal
110 // M = Minus diagonal
111 value_type ci = T.P[0]/T.O[0], ciold = 0.;
112 value_type di = 1./T.O[0], diold = 0.;
113 for( unsigned i=1; i<size; i++)
114 {
115 ciold = ci, diold = di;
116 ci = T.P[i]/ ( T.O[i]-T.M[i]*ciold);
117 di = -T.M[i]*diold/(T.O[i]-T.M[i]*ciold);
118 }
119 return di;
120}
133template<class value_type>
134void compute_Tinv_y( const dg::TriDiagonal<thrust::host_vector<value_type>>
135 & T,
136 thrust::host_vector<value_type>& x,
137 const thrust::host_vector<value_type>& y, value_type a = 1.,
138 value_type d = 0.)
139{
140 unsigned size = y.size();
141 x.resize(size);
142 thrust::host_vector<value_type> ci(size), di(size);
143 ci[0] = a*T.P[0]/( a*T.O[0] + d);
144 di[0] = y[0]/( a*T.O[0] + d);
145 for( unsigned i=1; i<size; i++)
146 {
147 ci[i] = a*T.P[i]/ ( a*T.O[i] + d -a*T.M[i]*ci[i-1]);
148 di[i] = (y[i]-a*T.M[i]*di[i-1])/(a*T.O[i] + d
149 -a*T.M[i]*ci[i-1]);
150 }
151 x[size-1] = di[size-1];
152 for( int i=size-2; i>=0; i--)
153 x[i] = di[i] - ci[i]*x[i+1];
154}
155
156
167template< class real_type>
169{
170 public:
171 using value_type = real_type;
179 TridiagInvHMGTI(const thrust::host_vector<real_type>& copyable)
180 {
181 m_size = copyable.size();
182 m_alphas.assign(m_size+1,0.);
183 m_betas.assign(m_size+1,0.);
184 }
190 TridiagInvHMGTI(unsigned size)
191 {
192 m_size = size;
193 m_alphas.assign(m_size+1,0.);
194 m_betas.assign(m_size+1,0.);
195 }
201 void resize(unsigned new_size) {
202 m_size = new_size;
203 m_alphas.resize(m_size+1,0.);
204 m_betas.resize(m_size+1,0.);
205 }
213 void operator()(const dg::TriDiagonal<thrust::host_vector<real_type>>& T, dg::SquareMatrix<real_type>& Tinv)
214 {
215 this->operator()(
216 T.O, // 0 diagonal
217 T.P, // +1 diagonal
218 T.M, // -1 diagonal
219 Tinv);
220 }
227 dg::SquareMatrix<real_type> operator()(const dg::TriDiagonal<thrust::host_vector<real_type>>& T)
228 {
230 this->operator()( T, Tinv);
231 return Tinv;
232 }
243 template<class ContainerType0, class ContainerType1, class ContainerType2>
244 void operator()(const ContainerType0& a, const ContainerType1& b,
245 const ContainerType2& c, dg::SquareMatrix<real_type>& Tinv)
246 {
247 unsigned ss = m_size;
248 Tinv.resize(ss);
249 if( ss == 1)
250 {
251 Tinv(0,0) = 1./b[0];
252 return;
253 }
254 //fill alphas
255 m_alphas[0]=1.0;
256 m_alphas[1]=a[0];
257 for( unsigned i = 2; i<ss+1; i++)
258 {
259 m_alphas[i] = a[i-1]*m_alphas[i-1] - c[i-1]*b[i-2]*m_alphas[i-2];
260 if (m_alphas[i] ==0 && i<ss) {
261 throw dg::Error( dg::Message(_ping_) << "# Failure in alpha["<<i<<"] !");
262 }
263 }
264 if (m_alphas[ss] ==0)
265 throw dg::Error( dg::Message(_ping_) << "# No Inverse of tridiagonal matrix exists !");
266
267 //fill betas
268 m_betas[ss]=1.0;
269 m_betas[ss-1]=a[ss-1];
270 m_betas[0] = m_alphas[ss];
271 for( int i = ss-2; i>0; i--)
272 {
273 m_betas[i] = a[i]*m_betas[i+1] - c[i+1]*b[i]*m_betas[i+2];
274 if (m_betas[i] ==0)
275 {
276 throw dg::Error( dg::Message(_ping_) << "# Failure in beta["<<i<<"] !");
277 }
278 }
279 //Diagonal entries
280 Tinv(0, 0) = 1.0/(a[0]-c[1]*b[0]*m_betas[2]/m_betas[1]);
281 Tinv(ss-1, ss-1) = 1.0/(a[ss-1] -
282 c[ss-1]*b[ss-2]*m_alphas[ss-2]/m_alphas[ss-1]);
283 for( unsigned i=1; i<ss-1; i++)
284 {
285 Tinv( i,i) =
286 1.0/(a[i]-c[i]*b[i-1]*m_alphas[i-1]/m_alphas[i]
287 -c[i+1]*b[i]*m_betas[i+2]/m_betas[i+1]);
288 }
289 //Off-diagonal entries
290 for( unsigned i=0; i<ss; i++)
291 {
292 for( unsigned j=0; j<ss; j++)
293 {
294 Tinv.row_indices[i*ss+j] = j;
295 Tinv.column_indices[i*ss+j] = i;
296 if (i<j) {
297 Tinv(j, i) =
298 sign(j-i)*std::accumulate(std::next(b.begin(),i),
299 std::next(b.begin(),j), 1.,
300 std::multiplies<value_type>())*
301 m_alphas[i]/m_alphas[j]*Tinv.values[j*ss+j];
302 }
303 else if (i>j)
304 {
305 Tinv(j, i) =
306 sign(i-j)*std::accumulate(std::next(c.begin(),j+1),
307 std::next(c.begin(),i+1), 1.,
308 std::multiplies<value_type>())*
309 m_betas[i+1]/m_betas[j+1]*Tinv.values[j*ss+j];
310 }
311 }
312 }
313 }
314 private:
316 int sign(unsigned i)
317 {
318 if (i%2==0) return 1;
319 else return -1;
320 }
321 thrust::host_vector<real_type> m_alphas, m_betas;
322 unsigned m_size;
323};
324
325
334template< class real_type>
336{
337 public:
338 using value_type = real_type;
346 TridiagInvDF(const thrust::host_vector<real_type>& copyable)
347 {
348 m_size = copyable.size();
349 m_phi.assign(m_size,0.);
350 m_theta.assign(m_size,0.);
351 }
357 TridiagInvDF(unsigned size)
358 {
359 m_size = size;
360 m_phi.assign(m_size,0.);
361 m_theta.assign(m_size,0.);
362 }
368 void resize(unsigned new_size) {
369 m_size = new_size;
370 m_phi.resize(m_size,0.);
371 m_theta.resize(m_size,0.);
372 }
380 void operator()(const dg::TriDiagonal<thrust::host_vector<real_type>>& T, dg::SquareMatrix<real_type>& Tinv)
381 {
382 this->operator()(
383 T.O, // 0 diagonal
384 T.P, // +1 diagonal
385 T.M, // -1 diagonal
386 Tinv);
387 }
394 dg::SquareMatrix<real_type> operator()(const dg::TriDiagonal<thrust::host_vector<real_type>>& T)
395 {
397 this->operator()( T, Tinv);
398 return Tinv;
399 }
410 template<class ContainerType0, class ContainerType1, class ContainerType2>
411 void operator()(const ContainerType0& a, const ContainerType1& b,
412 const ContainerType2& c, dg::SquareMatrix<real_type>& Tinv)
413 {
414 Tinv.resize(m_size);
415 value_type helper = 0.0;
416 //fill phi values
417 m_phi[0] = - b[0]/a[0];
418 for( unsigned i = 1; i<m_size; i++)
419 {
420 helper = m_phi[i-1]* c[i] + a[i];
421 if (helper==0) throw dg::Error( dg::Message(_ping_)<< "Failure: Division by zero\n");
422 else m_phi[i] = -b[i]/helper;
423 }
424// m_phi[m_size] = 0.0;
425
426 //fill theta values
427 if (m_size == 1) m_theta[m_size-1] = 0.0;
428 else
429 {
430 m_theta[m_size-1] = - c[m_size-1]/a[m_size-1];
431 for( int i = m_size-2; i>=0; i--)
432 {
433 helper = m_theta[i+1]*b[i] + a[i];
434 if (helper==0) throw dg::Error( dg::Message(_ping_)<< "Failure: Division by zero\n");
435 else m_theta[i] = -c[i]/helper;
436 }
437 }
438// m_theta[0] = 0.0;
439 //Diagonal entries
440 helper = a[0] + b[0]* m_theta[1];
441 if (helper==0) throw dg::Error( dg::Message(_ping_)<< "Failure: No inverse exists\n");
442 else Tinv(0,0) = 1.0/helper;
443
444 if (m_size == 1) helper = a[m_size-1];
445 else helper = a[m_size-1] + c[m_size-1]*m_phi[m_size-2];
446
447 if (helper==0) throw dg::Error( dg::Message(_ping_)<< "Failure: No inverse exists\n");
448 else Tinv( m_size -1 , m_size - 1) = 1.0/helper;
449
450 for( unsigned i=1; i<m_size-1; i++)
451 {
452 helper = a[i] + c[i]*m_phi[i-1] + b[i]* m_theta[i+1];
453 if (helper==0) throw dg::Error( dg::Message(_ping_)<< "Failure: No inverse exists\n");
454 else Tinv(i,i) = 1.0/helper;
455 }
456 //Off-diagonal entries
457 for( unsigned j=0; j<m_size-1; j++) //row index
458 {
459 for (unsigned i=j+1; i<m_size; i++)
460 {
461 Tinv(i,j) = m_theta[i]*Tinv(i-1, j);
462 }
463 }
464 for( unsigned j=1; j<m_size; j++) //row index
465 {
466 for (int i=j-1; i>=0; i--)
467 {
468 Tinv(i,j) = m_phi[i]*Tinv(i+1,j);
469 }
470 }
471 }
472 private:
473 thrust::host_vector<real_type> m_phi, m_theta;
474 unsigned m_size;
475};
476
486template< class real_type>
488{
489 public:
490 using value_type = real_type;
498 TridiagInvD(const thrust::host_vector<real_type>& copyable)
499 {
500 m_size = copyable.size();
501 m_phi.assign(m_size+1,0.);
502 m_theta.assign(m_size+1,0.);
503 }
509 TridiagInvD(unsigned size)
510 {
511 m_size = size;
512 m_phi.assign(m_size+1,0.);
513 m_theta.assign(m_size+1,0.);
514 }
520 void resize(unsigned new_size) {
521 m_size = new_size;
522 m_phi.resize(m_size+1,0.);
523 m_theta.resize(m_size+1,0.);
524 }
532 void operator()(const dg::TriDiagonal<thrust::host_vector<real_type>>& T, dg::SquareMatrix<real_type>& Tinv)
533 {
534 this->operator()(
535 T.O, // 0 diagonal
536 T.P, // +1 diagonal
537 T.M, // -1 diagonal
538 Tinv);
539 }
546 dg::SquareMatrix<real_type> operator()(const dg::TriDiagonal<thrust::host_vector<real_type>>& T)
547 {
549 this->operator()( T, Tinv);
550 return Tinv;
551 }
562 template<class ContainerType0, class ContainerType1, class ContainerType2>
563 void operator()(const ContainerType0& a, const ContainerType1& b,
564 const ContainerType2& c, dg::SquareMatrix<real_type>& Tinv)
565 {
566 Tinv.resize( m_size);
567 unsigned is=0;
568 for( unsigned i = 0; i<m_size+1; i++)
569 {
570 is = m_size - i;
571 if (i==0)
572 {
573 m_theta[0] = 1.;
574 m_phi[is] = 1.;
575 }
576 else if (i==1)
577 {
578 m_theta[1] = a[0];
579 m_phi[is] = a[is];
580 }
581 else
582 {
583 m_theta[i] = a[i-1] * m_theta[i-1] - b[i-2] * c[i-1] * m_theta[i-2];
584 m_phi[is] = a[is] * m_phi[is+1] - b[is] * c[is+1] * m_phi[is+2];
585 }
586 }
587
588 //Compute inverse tridiagonal matrix elements
589 for( unsigned i=0; i<m_size; i++) //row index
590 {
591 for( unsigned j=0; j<m_size; j++) //column index
592 {
593 if (i<j) {
594 Tinv(i,j) =
595 std::accumulate(std::next(b.begin(),i),
596 std::next(b.begin(),j), 1.,
597 std::multiplies<value_type>())*sign(i+j) *
598 m_theta[i] * m_phi[j+1]/m_theta[m_size];
599 }
600 else if (i==j)
601 {
602 Tinv(i,j) = m_theta[i] * m_phi[i+1]/m_theta[m_size];
603 }
604 else // if (i>j)
605 {
606 Tinv(i,j) =
607 std::accumulate(std::next(c.begin(),j+1),
608 std::next(c.begin(),i+1), 1.,
609 std::multiplies<value_type>())*sign(i+j) *
610 m_theta[j] * m_phi[i+1]/m_theta[m_size];
611 }
612 }
613 }
614 }
615 private:
617 int sign(unsigned i)
618 {
619 if (i%2==0) return 1;
620 else return -1;
621 }
622 thrust::host_vector<real_type> m_phi, m_theta;
623 unsigned m_size;
624};
625
637template<class value_type>
638void invert(const dg::TriDiagonal<thrust::host_vector<value_type>>& T,
640{
641 TridiagInvDF<value_type>( T.O.size())(T, Tinv);
642}
654template<class value_type>
656 const dg::TriDiagonal<thrust::host_vector<value_type>>& T)
657{
658 return TridiagInvDF<value_type>( T.O.size())(T);
659}
660
674template<class value_type>
675std::array<value_type, 2> compute_extreme_EV( const dg::TriDiagonal<thrust::host_vector<value_type>>& T)
676{
678 // We use P as "subdiagonal" because it is symmetric and the first element must be on 0 index
679 thrust::host_vector<value_type> evals( T.O), subdiagonal( T.P), Z, work;
680 lapack::stev('N', evals, subdiagonal, Z, work);
682 value_type EVmin = dg::blas1::reduce( evals, EVmax, dg::AbsMin<value_type>());
683 return std::array<value_type, 2>{EVmin, EVmax};
684}
685
686
688
689} // namespace mat
690} // namespace dg
void resize(unsigned m, T val=T())
USE THIS ONE Compute the inverse of a general tridiagonal matrix. The algorithm does not rely on the ...
Definition tridiaginv.h:336
void resize(unsigned new_size)
Resize inverse tridiagonal matrix and helper vectors.
Definition tridiaginv.h:368
TridiagInvDF(unsigned size)
Construct from size of vector.
Definition tridiaginv.h:357
void operator()(const dg::TriDiagonal< thrust::host_vector< real_type > > &T, dg::SquareMatrix< real_type > &Tinv)
Compute the inverse of a tridiagonal matrix T.
Definition tridiaginv.h:380
dg::SquareMatrix< real_type > operator()(const dg::TriDiagonal< thrust::host_vector< real_type > > &T)
Compute the inverse of a tridiagonal matrix T.
Definition tridiaginv.h:394
TridiagInvDF(const thrust::host_vector< real_type > &copyable)
Construct from vector.
Definition tridiaginv.h:346
TridiagInvDF()
Allocate nothing, Call construct method before usage.
Definition tridiaginv.h:340
real_type value_type
Definition tridiaginv.h:338
void operator()(const ContainerType0 &a, const ContainerType1 &b, const ContainerType2 &c, dg::SquareMatrix< real_type > &Tinv)
Compute the inverse of a tridiagonal matrix with diagonal vectors a,b,c.
Definition tridiaginv.h:411
Compute the inverse of a general tridiagonal matrix.
Definition tridiaginv.h:488
void operator()(const dg::TriDiagonal< thrust::host_vector< real_type > > &T, dg::SquareMatrix< real_type > &Tinv)
Compute the inverse of a tridiagonal matrix T.
Definition tridiaginv.h:532
void resize(unsigned new_size)
Resize inverse tridiagonal matrix and helper vectors.
Definition tridiaginv.h:520
real_type value_type
Definition tridiaginv.h:490
TridiagInvD(unsigned size)
Construct from size of vector.
Definition tridiaginv.h:509
void operator()(const ContainerType0 &a, const ContainerType1 &b, const ContainerType2 &c, dg::SquareMatrix< real_type > &Tinv)
Compute the inverse of a tridiagonal matrix with diagonal vectors a,b,c.
Definition tridiaginv.h:563
dg::SquareMatrix< real_type > operator()(const dg::TriDiagonal< thrust::host_vector< real_type > > &T)
Compute the inverse of a tridiagonal matrix T.
Definition tridiaginv.h:546
TridiagInvD(const thrust::host_vector< real_type > &copyable)
Construct from vector.
Definition tridiaginv.h:498
TridiagInvD()
Allocate nothing, Call construct method before usage.
Definition tridiaginv.h:492
Compute the inverse of a general tridiagonal matrix.
Definition tridiaginv.h:169
TridiagInvHMGTI(const thrust::host_vector< real_type > &copyable)
Construct from vector.
Definition tridiaginv.h:179
TridiagInvHMGTI(unsigned size)
Construct from size of vector.
Definition tridiaginv.h:190
void operator()(const dg::TriDiagonal< thrust::host_vector< real_type > > &T, dg::SquareMatrix< real_type > &Tinv)
Compute the inverse of a tridiagonal matrix T.
Definition tridiaginv.h:213
void operator()(const ContainerType0 &a, const ContainerType1 &b, const ContainerType2 &c, dg::SquareMatrix< real_type > &Tinv)
Compute the inverse of a tridiagonal matrix with diagonal vectors a,b,c.
Definition tridiaginv.h:244
dg::SquareMatrix< real_type > operator()(const dg::TriDiagonal< thrust::host_vector< real_type > > &T)
Compute the inverse of a tridiagonal matrix T.
Definition tridiaginv.h:227
real_type value_type
Definition tridiaginv.h:171
TridiagInvHMGTI()
Allocate nothing, Call construct method before usage.
Definition tridiaginv.h:173
void resize(unsigned new_size)
Resize inverse tridiagonal matrix and helper vectors.
Definition tridiaginv.h:201
#define _ping_
OutputType reduce(const ContainerType &x, OutputType zero, BinaryOp binary_op, UnaryOp unary_op=UnaryOp())
typename TensorTraits< std::decay_t< Vector > >::value_type get_value_type
std::array< value_type, 2 > compute_extreme_EV(const dg::TriDiagonal< thrust::host_vector< value_type > > &T)
Compute extreme Eigenvalues of a symmetric tridiangular matrix.
Definition tridiaginv.h:675
void compute_Tinv_y(const dg::TriDiagonal< thrust::host_vector< value_type > > &T, thrust::host_vector< value_type > &x, const thrust::host_vector< value_type > &y, value_type a=1., value_type d=0.)
Computes the value of via Thomas algorithm.
Definition tridiaginv.h:134
value_type compute_Tinv_m1(const dg::TriDiagonal< thrust::host_vector< value_type > > &T, unsigned size)
Computes the value of via a Thomas algorithm.
Definition tridiaginv.h:105
void invert(const dg::TriDiagonal< thrust::host_vector< value_type > > &T, dg::SquareMatrix< value_type > &Tinv)
Invert a tridiagonal matrix.
Definition tridiaginv.h:638
Classes for Krylov space approximations of a Matrix-Vector product.
double value_type
Definition tridiaginv_b.cpp:6