Extension: Matrix functions
#include "dg/matrix/matrix.h"
Loading...
Searching...
No Matches
lanczos.h
Go to the documentation of this file.
1#pragma once
2
3#include "dg/algorithm.h"
4#include "tridiaginv.h"
5#include "matrixfunction.h"
6
7
12namespace dg{
13namespace mat{
14
152template< class ContainerType >
154{
155 public:
165 UniversalLanczos( const ContainerType& copyable, unsigned max_iterations)
166 {
167 m_v = m_vp = m_vm = copyable;
168 m_max_iter = max_iterations;
169 m_iter = max_iterations;
170 //sub matrix and vector
171 set_iter( max_iterations);
172 }
174 template<class ...Params>
175 void construct( Params&& ...ps)
176 {
177 //construct and swap
178 *this = UniversalLanczos( std::forward<Params>( ps)...);
179 }
180
183 void set_max( unsigned new_max) {
184 m_max_iter = new_max;
185 set_iter( new_max);
186 }
187
190 unsigned get_max() const {return m_max_iter;}
191
194 void set_verbose( bool verbose){ m_verbose = verbose;}
195
198 double get_bnorm() const{return m_bnorm;}
199
203 unsigned get_iter() const {return m_iter;}
204
222 template < class MatrixType, class ContainerType0, class ContainerType1,
223 class ContainerType2, class FuncTe1>
224 unsigned solve(ContainerType0& x, FuncTe1 f,
225 MatrixType&& A, const ContainerType1& b,
226 const ContainerType2& weights, double eps,
227 double nrmb_correction = 1.,
228 std::string error_norm = "universal",
229 double res_fac = 1.,
230 unsigned q = 1 )
231 {
232 tridiag( f, std::forward<MatrixType>(A), b, weights, eps,
233 nrmb_correction, error_norm, res_fac, q);
234 if( "residual" == error_norm)
235 m_yH = f( m_TH);
236 //Compute x = |b|_M V f(T) e1
237 normMbVy(std::forward<MatrixType>(A), m_TH, m_yH, x, b,
238 m_bnorm);
239 return m_iter;
240 }
241
263 template< class MatrixType, class ContainerType0, class ContainerType1>
264 const dg::TriDiagonal<thrust::host_vector<double>>& tridiag( MatrixType&& A, const ContainerType0& b,
265 const ContainerType1& weights, double eps = 1e-4,
266 double nrmb_correction = 1.,
267 std::string error_norm = "compute_extreme_EV",
268 double res_fac = 1.,
269 unsigned q = 1 )
270 {
271 auto op = make_Linear_Te1( -1);
272 tridiag( op, std::forward<MatrixType>(A), b, weights, eps,
273 nrmb_correction, error_norm, res_fac, q);
274 return m_TH;
275 }
276
289 template< class MatrixType, class ContainerType0,
290 class ContainerType1,class ContainerType2>
291 void normMbVy( MatrixType&& A,
292 const dg::TriDiagonal<thrust::host_vector<double>>& T,
293 const ContainerType0& y,
294 ContainerType1& x,
295 const ContainerType2& b, double bnorm)
296 {
297 dg::blas1::copy(0., x);
298 if( 0 == bnorm )
299 {
300 return;
301 }
302 dg::blas1::axpby(1./bnorm, b, 0.0, m_v); //m_v[1] = b/||b||
303 dg::blas1::copy(0., m_vm);
304 // check if (potentially) all higher elements in y are zero
305 unsigned less_iter = 0;
306 for( unsigned i=0; i<y.size(); i++)
307 if( y[i] != 0)
308 less_iter = i+1;
309 dg::blas1::axpby( y[0]*bnorm, m_v, 1., x); //Compute b= |b| V y
310
311 for ( unsigned i=0; i<less_iter-1; i++)
312 {
313 dg::blas2::symv( std::forward<MatrixType>(A), m_v, m_vp);
315 -T.M[i]/T.P[i], m_vm,
316 -T.O[i]/T.P[i], m_v,
317 1.0/T.P[i], m_vp);
318 dg::blas1::axpby( y[i+1]*bnorm, m_vp, 1., x); //Compute b= |b| V y
319 m_vm.swap( m_v);
320 m_v.swap( m_vp);
321 }
322 }
323
335 template < class UnaryOp, class MatrixType,
336 class ContainerType1, class ContainerType2>
338 MatrixType&& A, const ContainerType1& b,
339 const ContainerType2& weights, double eps,
340 double nrmb_correction,
341 std::string error_norm = "residual",
342 double res_fac = 1.,
343 unsigned q = 1 )
344 {
345#ifdef MPI_VERSION
346 int rank;
347 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
348#endif //MPI
349 m_bnorm = nrm( weights, b);
350 if( m_verbose)
351 {
352 DG_RANK0 std::cout << "# Norm of b "<<m_bnorm <<"\n";
353 if ( "compute_extreme_EV" == error_norm || "compute_max_EV" == error_norm)
354 {
355 DG_RANK0 std::cout << "# Rel EV errors: \n";
356 }
357 else
358 {
359 DG_RANK0 std::cout << "# Res factor "<<res_fac <<"\n";
360 DG_RANK0 std::cout << "# Residual errors: \n";
361 }
362 }
363 if( m_bnorm == 0)
364 {
365 set_iter(1);
366 return m_TH;
367 }
368 double residual = 1e300;
369 dg::blas1::axpby(1./m_bnorm, b, 0.0, m_v); //m_v[1] = x/||x||
370 double betaip = 0.;
371 double alphai = 0.; // For Hermitian matrices all dot products in CG are real
372 double lmin = -1e300, lmax = 1e300;
373 for( unsigned i=0; i<m_max_iter; i++)
374 {
375 m_TH.M[i] = betaip; // -1 diagonal
376 dg::blas2::symv(std::forward<MatrixType>(A), m_v, m_vp);
377 dg::blas1::axpby(-betaip, m_vm, 1.0, m_vp); // only - if i>0, therefore no if (i>0)
378 alphai = dot(m_vp, weights, m_v);
379 m_TH.O[i] = alphai;
380 dg::blas1::axpby(-alphai, m_v, 1.0, m_vp);
381 betaip = nrm( weights, m_vp);
382 if (fabs( betaip) <= 1e-12) // what this means is that x = A^{-1}b is exactly in the Krylov subspace
383 {
384 set_iter(i+1);
385 if ( "compute_extreme_EV" == error_norm || "compute_max_EV" == error_norm)
386 {
387 // If we did continue iterating after betaip = 0 the results are rather random
388 throw dg::Error( dg::Message(_ping_) << "betaip = 0 found in tridiagonalisation \n");
389 }
390 else if( m_verbose)
391 DG_RANK0 std::cout << "beta["<<i+1 <<"]=0 encountered\n";
392 break;
393 }
394 m_TH.P[i] = betaip; // +1 diagonal
395
396 double xnorm = 0.;
397 if( "residual" == error_norm)
398 {
399 residual = compute_residual_error( m_TH, i)*m_bnorm;
400 xnorm = m_bnorm;
401 }
402 else if ( "compute_extreme_EV" == error_norm || "compute_max_EV" == error_norm)
403 {
404 if(i%10 == 0)
405 {
406 // We use P as "subdiagonal" because it is symmetric and the first element must be on 0 index
407 // (last is ignored)
408 thrust::host_vector<double> evals( i+1), subdiagonal( i+1), Z, work;
409 for( unsigned u=0; u<i+1; u++)
410 {
411 evals[u] = m_TH.O[u];
412 subdiagonal[u] = m_TH.P[u];
413 }
414 lapack::stev('N', evals, subdiagonal, Z, work);
415 if( m_verbose)
416 DG_RANK0 std::cout << "# lmin = " << evals[0] << " lmax = "<<evals[i]<<"\tat i = " << i << "\n";// << betaip * evecs(i,i)<<" " <<betaip * evecs(0,i)<<"\n";
417 //if( fabs(betaip * evecs(i,i)) < 0.9*eps) // CONVERGES SUPER SLOW
418 if( fabs(lmax - evals[i]) < 10*eps*evals[i])
419 {
420 if( ("compute_extreme_EV" == error_norm &&
421 //fabs( betaip * evecs(0,i)) < eps) // CONVERGES EVEN SLOWER
422 fabs( lmin - evals[0]) < 10 * (eps*fabs(lmin) + 1e-12*lmax))
423 || "compute_max_EV" == error_norm)
424 {
425 set_iter(i+1);
426 break;
427 }
428 }
429 lmin = evals[0], lmax = evals[i];
430 }
431 }
432 else
433 {
434 if( i>=q &&( (i<=10) || (i>10 && i%10 == 0) ))
435 {
436 residual = compute_universal_error( m_TH, i, q, f,
437 m_yH)*m_bnorm;
438 xnorm = dg::fast_l2norm( m_yH)*m_bnorm;
439 }
440 else
441 {
442 residual = 1e10;
443 xnorm = m_bnorm;
444 }
445 }
446 if( m_verbose && "compute_extreme_EV" != error_norm && "compute_max_EV" != error_norm)
447 DG_RANK0 std::cout << "# ||r||_W = " << residual << "\tat i = " << i << "\n";
448 if (res_fac*residual< eps*(xnorm + nrmb_correction) )
449 {
450 set_iter(i+1);
451 break;
452 }
453 dg::blas1::scal(m_vp, 1./betaip);
454 m_vm.swap(m_v);
455 m_v.swap( m_vp);
456 set_iter( m_max_iter);
457 }
458 return m_TH;
459 }
460 private:
461 // !! Calling this "norm" would shadow std::norm in NORM
462 template<class ContainerType1, class ContainerType2>
463 auto nrm( const ContainerType1& w, const ContainerType2& x)
464 {
465 using value_type_x = dg::get_value_type<ContainerType2>;
466 constexpr bool is_complex = dg::is_scalar_v<value_type_x, dg::ComplexTag>;
467 if constexpr (is_complex)
468 return sqrt( blas1::vdot( dg::detail::NORM(), w, x));
469 else
470 return sqrt( blas2::dot( w, x));
471 }
472 template<class ContainerType0, class ContainerType1, class ContainerType2>
473 auto dot( const ContainerType0& x0, const ContainerType1& w, const ContainerType2& x1)
474 {
475 using value_type_x1 = dg::get_value_type<ContainerType2>;
476 constexpr bool is_complex = dg::is_scalar_v<value_type_x1, dg::ComplexTag>;
477 // There is no complex_symmetric in Lanczos...
478 if constexpr (not is_complex ) // or complex_mode == dg::complex_symmetric)
479 return blas2::dot( x0, w, x1); // this returns value_type
480 else // For Hermitian matrices all dot products in CG are real
481 return blas1::vdot( dg::detail::DOT(), x0, w, x1); // this returns floating point
482 }
483 double compute_residual_error( const dg::TriDiagonal<thrust::host_vector<double>>& TH, unsigned iter)
484 {
485 double T1 = compute_Tinv_m1( TH, iter+1);
486 return TH.P[iter]*fabs(T1); //Tinv_i1
487 }
488 template<class UnaryOp>
489 double compute_universal_error( const dg::TriDiagonal<thrust::host_vector<double>>& TH, unsigned iter,
490 unsigned q, UnaryOp f, HVec& yH)
491 {
492 unsigned new_iter = iter + 1 + q;
493 set_iter( iter+1);
495 for( unsigned u=0; u<iter+1; u++)
496 {
497 THtilde.M[u] = TH.M[u];
498 THtilde.O[u] = TH.O[u];
499 THtilde.P[u] = TH.P[u];
500 }
501 for( unsigned u=1; u<=q; u++)
502 {
503 THtilde.M[ iter+u] = u==1 ? TH.P[iter] :
504 TH.O[iter+1-u];
505 THtilde.O[ iter+u] = TH.O[ iter-u];
506 THtilde.P[ iter+u] = TH.M[ iter-u];
507 }
508 yH = f( TH);
509 HVec yHtilde = f( THtilde);
510 for( unsigned u=0; u<yH.size(); u++)
511 yHtilde[u] -= yH[u];
512 double norm = dg::fast_l2norm( yHtilde);
513 return norm;
514 }
515
518 void set_iter( unsigned new_iter) {
519 m_TH.resize(new_iter);
520 m_iter = new_iter;
521 }
522 ContainerType m_v, m_vp, m_vm;
524 HVec m_yH;
525 unsigned m_iter, m_max_iter;
526 bool m_verbose = false;
527 double m_bnorm = 0.;
528};
529
530
531} //namespace mat
532} //namespace dg
533
Tridiagonalize and approximate via Lanczos algorithm. A is self-adjoint in the weights .
Definition lanczos.h:154
void normMbVy(MatrixType &&A, const dg::TriDiagonal< thrust::host_vector< double > > &T, const ContainerType0 &y, ContainerType1 &x, const ContainerType2 &b, double bnorm)
compute from a given tridiagonal matrix T and in-place re-computation of V
Definition lanczos.h:291
const dg::TriDiagonal< thrust::host_vector< double > > & tridiag(MatrixType &&A, const ContainerType0 &b, const ContainerType1 &weights, double eps=1e-4, double nrmb_correction=1., std::string error_norm="compute_extreme_EV", double res_fac=1., unsigned q=1)
Tridiagonalization of A using Lanczos method with .
Definition lanczos.h:264
get_value_type< ContainerType > value_type
Definition lanczos.h:156
double get_bnorm() const
Norm of b from last call to operator()
Definition lanczos.h:198
unsigned solve(ContainerType0 &x, FuncTe1 f, MatrixType &&A, const ContainerType1 &b, const ContainerType2 &weights, double eps, double nrmb_correction=1., std::string error_norm="universal", double res_fac=1., unsigned q=1)
via Lanczos and matrix function computation. A is self-adjoint in the weights .
Definition lanczos.h:224
UniversalLanczos(const ContainerType &copyable, unsigned max_iterations)
Allocate memory for the method.
Definition lanczos.h:165
void set_verbose(bool verbose)
Set or unset debugging output during iterations.
Definition lanczos.h:194
unsigned get_max() const
Get the current maximum number of iterations.
Definition lanczos.h:190
unsigned get_iter() const
Get the number of iterations in the last call to tridiag or solve (same as T.num_rows)
Definition lanczos.h:203
UniversalLanczos()
Allocate nothing, Call construct method before usage.
Definition lanczos.h:158
void set_max(unsigned new_max)
Set the maximum number of iterations.
Definition lanczos.h:183
const dg::TriDiagonal< thrust::host_vector< double > > & tridiag(UnaryOp f, MatrixType &&A, const ContainerType1 &b, const ContainerType2 &weights, double eps, double nrmb_correction, std::string error_norm="residual", double res_fac=1., unsigned q=1)
Tridiagonalization of A using Lanczos method.
Definition lanczos.h:337
void construct(Params &&...ps)
Perfect forward parameters to one of the constructors.
Definition lanczos.h:175
#define _ping_
void copy(const ContainerTypeIn &source, ContainerTypeOut &target)
void axpbypgz(value_type alpha, const ContainerType1 &x, value_type1 beta, const ContainerType2 &y, value_type2 gamma, ContainerType &z)
void axpby(value_type alpha, const ContainerType1 &x, value_type1 beta, ContainerType &y)
auto vdot(Functor f, const ContainerType &x, const ContainerTypes &...xs) -> std::invoke_result_t< Functor, dg::get_value_type< ContainerType >, dg::get_value_type< ContainerTypes >... >
void scal(ContainerType &x, value_type alpha)
auto dot(const ContainerType1 &x, const MatrixType &m, const ContainerType2 &y)
void symv(MatrixType &&M, const ContainerType1 &x, ContainerType2 &y)
constexpr bool is_scalar_v
typename TensorTraits< std::decay_t< Vector > >::value_type get_value_type
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:159
static auto fast_l2norm
auto make_Linear_Te1(int exp)
Create a functor that computes directly.
Definition matrixfunction.h:182
thrust::host_vector< double > HVec
Functions for optimizing Contours.
void resize(unsigned size)
#define DG_RANK0