Extension: Matrix functions
#include "dg/matrix/matrix.h"
Loading...
Searching...
No Matches
matrixprod.h
Go to the documentation of this file.
1
2#pragma once
3
4#include "dg/algorithm.h"
5#include "lanczos.h"
6#include "contours.h"
7#include "optimise.h"
8
9namespace dg{
10namespace mat{
11
46template<class ContainerType>
48{
49 using container_type = ContainerType;
53
60 ProductMatrixFunction( const ContainerType& copyable, unsigned max_iterations)
61 {
62 m_lanczos.construct( copyable, max_iterations);
63 m_v = m_vp = m_vm = m_f = copyable;
64 }
66 template<class ...Params>
67 void construct( Params&& ...ps)
68 {
69 //construct and swap
70 *this = ProductMatrixFunction( std::forward<Params>( ps)...);
71 }
72
74 void set_benchmark( bool benchmark, std::string message = "ProductFunction"){
75 m_benchmark = benchmark;
76 m_message = message;
77 }
78
106 template<class ContainerType0, class BinaryOp, class ContainerType1,
107 class MatrixType, class ContainerType2, class ContainerType3>
108 unsigned apply(
109 ContainerType0& x,
110 BinaryOp op,
111 const ContainerType1& diag,
112 MatrixType&& A,
113 const ContainerType2& b,
114 const ContainerType3& weights,
115 value_type eps,
116 value_type nrmb_correction = 1.)
117 {
118#ifdef MPI_VERSION
119 int rank;
120 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
121#endif //MPI
122 dg::Timer t;
123 t.tic();
124 auto func = make_FuncEigen_Te1( [&](value_type x) {return op(1., x);});
125 auto T = m_lanczos.tridiag( func, std::forward<MatrixType>(A),
126 b, weights, eps, nrmb_correction,
127 "universal", 1.0, 2);
128 compute_vlcl( op, diag, std::forward<MatrixType>(A), T, x, b,
129 m_lanczos.get_bnorm());
130 t.toc();
131 if( m_benchmark)
132 DG_RANK0 std::cout << "# `"<<m_message<<"` solve with {"<<T.num_rows<<"} iterations took "<<t.diff()<<"s\n";
133 return T.num_rows;
134 }
135
169 template<class ContainerType0, class BinaryOp, class MatrixType,
170 class ContainerType1, class ContainerType2, class ContainerType3>
172 ContainerType0& x,
173 BinaryOp op,
174 MatrixType&& A,
175 const ContainerType1& diag,
176 const ContainerType2& b,
177 const ContainerType3& weights,
178 value_type eps,
179 value_type nrmb_correction = 1.)
180 {
181 // Should this be another class?
182 // if A does not change Lanczos iterations could be reused from apply function!?
183#ifdef MPI_VERSION
184 int rank;
185 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
186#endif //MPI
187 dg::Timer t;
188 t.tic();
189 auto func = make_FuncEigen_Te1( [&](value_type x) {return op( x, 1.);});
190 auto T = m_lanczos.tridiag( func, std::forward<MatrixType>(A),
191 b, weights, eps, nrmb_correction,
192 "universal", 1.0, 2);
193 compute_vlcl_adjoint( op, std::forward<MatrixType>(A), diag, T, x, b,
194 weights, m_lanczos.get_bnorm());
195
196 t.toc();
197 if( m_benchmark)
198 DG_RANK0 std::cout << "# `"<<m_message<<"` solve with {"<<T.num_rows<<"} iterations took "<<t.diff()<<"s\n";
199 return T.num_rows;
200 }
201
235 template< class BinaryOp, class ContainerType0, class MatrixType,
236 class ContainerType1, class ContainerType2>
237 void compute_vlcl( BinaryOp op, const ContainerType0& diag,
238 MatrixType&& A,
239 const TriDiagonal<thrust::host_vector<value_type>>& T,
240 ContainerType1& x,
241 const ContainerType2& b,
242 value_type bnorm)
243 {
244 dg::blas1::copy(0., x);
245 if( 0 == bnorm )
246 {
247 return;
248 }
249 unsigned iter = T.O.size();
250 thrust::host_vector<value_type> evals = T.O, plus = T.P;
251 thrust::host_vector<value_type> work (2*iter-2);
253 //Compute Eigendecomposition
254 lapack::stev('V', evals, plus, EHt.data(), work);
255 dg::blas1::axpby(1./bnorm, b, 0.0, m_v); //m_v[1] = b/||b||
256 dg::blas1::copy(0., m_vm);
257 // compute c_1 v_1
258 for ( unsigned k=0; k<iter; k++)
259 {
260 dg::blas1::evaluate( m_f, dg::equals(), op, diag, evals[k]);
261 dg::blas1::pointwiseDot( bnorm*EHt(k, 0)*EHt(k,0), m_f, m_v, 1.,
262 x);
263 }
264 for ( unsigned i=0; i<iter-1; i++)
265 {
266 dg::blas2::symv( std::forward<MatrixType>(A), m_v, m_vp);
268 -T.M[i]/T.P[i], m_vm,
269 -T.O[i]/T.P[i], m_v,
270 1.0/T.P[i], m_vp);
271 m_vm.swap( m_v);
272 m_v.swap( m_vp);
273 // compute c_l v_l
274 for ( unsigned k=0; k<iter; k++)
275 {
276 dg::blas1::evaluate( m_f, dg::equals(), op, diag, evals[k]);
277 dg::blas1::pointwiseDot( bnorm*EHt(k,0)*EHt(k,i+1), m_f, m_v,
278 1., x);
279 }
280 }
281 }
282
310 template< class BinaryOp, class MatrixType, class ContainerType0,
311 class ContainerType1, class ContainerType2, class ContainerType3>
312 void compute_vlcl_adjoint( BinaryOp op,
313 MatrixType&& A,
314 const ContainerType0& diag,
315 const TriDiagonal<thrust::host_vector<value_type>>& T,
316 ContainerType1& x,
317 const ContainerType2& b,
318 const ContainerType3& weights,
319 value_type bnorm)
320 {
321 dg::blas1::copy(0., x);
322 if( 0 == bnorm )
323 {
324 return;
325 }
326 unsigned iter = T.O.size();
327 thrust::host_vector<value_type> evals = T.O, plus = T.P;
328 thrust::host_vector<value_type> work (2*iter-2);
330 //Compute Eigendecomposition
331 lapack::stev('V', evals, plus, EHt.data(), work);
332 dg::blas1::axpby(1./bnorm, b, 0.0, m_v); //m_v[1] = b/||b||
333 dg::blas1::copy(0., m_vm);
334 // compute alpha_i1
336 for ( unsigned k=0; k<iter; k++)
337 {
338 dg::blas1::evaluate( m_f, dg::equals(), op, evals[k], diag);
339 dg::blas1::pointwiseDot( m_f, m_v, m_f);
340 alpha( k,0) = dg::blas2::dot( m_f, weights, b);
341 }
342 for ( unsigned i=0; i<iter-1; i++)
343 {
344 dg::blas2::symv( std::forward<MatrixType>(A), m_v, m_vp);
346 -T.M[i]/T.P[i], m_vm,
347 -T.O[i]/T.P[i], m_v,
348 1.0/T.P[i], m_vp);
349 m_vm.swap( m_v);
350 m_v.swap( m_vp);
351 for ( unsigned k=0; k<iter; k++)
352 {
353 dg::blas1::evaluate( m_f, dg::equals(), op, evals[k], diag);
354 dg::blas1::pointwiseDot( m_f, m_v, m_f);
355 alpha( k,i+1) = dg::blas2::dot( m_f, weights, b);
356 }
357 }
358 // Observation: With an exponential function the lines of alpha get extremely small (because exp(lambda) gets very small ... so maybe one can save a few scalar products
359 // compute E_li E_ki alpha_ik v_l
360 std::vector<double> cl( iter, 0.0);
361 for( unsigned l=0; l<iter; l++)
362 for( unsigned i=0; i<iter; i++)
363 for( unsigned k=0; k<iter; k++)
364 cl[l] += EHt(k,i)*alpha(k,i)*EHt(k,l);
365 // 3rd Lanczos iteration
366 dg::blas1::axpby(1./bnorm, b, 0.0, m_v); //m_v[1] = b/||b||
367 dg::blas1::copy(0., m_vm);
368 dg::blas1::axpby( cl[0], m_v, 1., x);
369 for ( unsigned i=0; i<iter-1; i++)
370 {
371 dg::blas2::symv( std::forward<MatrixType>(A), m_v, m_vp);
373 -T.M[i]/T.P[i], m_vm,
374 -T.O[i]/T.P[i], m_v,
375 1.0/T.P[i], m_vp);
376 m_vm.swap( m_v);
377 m_v.swap( m_vp);
378 dg::blas1::axpby( cl[i+1], m_v, 1., x);
379 }
380 }
381
386 private:
387
389 bool m_benchmark = true;
390 std::string m_message = "ProductFunction";
391 ContainerType m_v, m_vp, m_vm, m_f;
392};
393
426template<class Geometry, class Matrix, class ComplexContainer>
428{
430 CauchyMatrixProduct( double lm_eps, const Geometry& grid, unsigned stages, bool adjoint = true )
431 : m_eps( lm_eps),
432 m_multi( grid, stages),
433 m_previous( 2, {1, m_multi.copyable()}),
434 m_z( m_multi.copyable()),
435 m_rhs( m_multi.copyable()),
436 m_grid_points(grid.size()),
437 m_cauchy_opt(),
438 m_adjoint(adjoint)
439 {
440 }
441
443 const dg::MultigridCG2d<Geometry, Matrix, ComplexContainer,
444 dg::complex_symmetric>& multigrid() const { return m_multi;}
445
454 m_EV_up2date = false;
455 }
456
461 void set_verbose( bool verbose) {
462 m_verbose = verbose;
463 m_cauchy_opt.set_verbose(verbose);
464 }
465
470 unsigned num_nodes() const { return m_cauchy_opt.num_nodes();}
471
478 void set_adjoint( bool adjoint) {
479 // reset solution cache
480 if( m_adjoint != adjoint)
481 {
482 m_previous.assign( m_previous.size(), {1, m_multi.copyable()});
483 m_adjoint = adjoint;
484 }
485 }
486
488 bool get_adjoint() const { return m_adjoint;}
489
511 template<class MatrixType, class UnaryFunc, class UnaryFuncD,
512 class ContainerType0, class ContainerType1, class ContainerType2>
513 void solve( ContainerType0& x, UnaryFunc func, UnaryFuncD dxfunc, std::vector<MatrixType>& ops,
514 const ContainerType1& d, const ContainerType2& b, std::vector<double> eps)
515 {
516#ifdef MPI_VERSION
517 int rank;
518 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
519#endif //MPI
520 // 1. (re)compute current nodes and weights
521 if( !m_EV_up2date)
522 {
523 // 1. Compute extreme Eigenvalues
524 update_extremeEVs( ops);
525 }
526 double dmin = dg::blas1::reduce( d, +1e300, thrust::minimum());
527 double dmax = dg::blas1::reduce( d, -1e300, thrust::maximum());
528 bool changed = false;
529 if( m_verbose )
530 {
531 DG_RANK0 std::cout << "# "<<dmin<<" < D < "<<dmax<<"\n";
532 DG_RANK0 std::cout << "# "<<m_lmin<<" < Lambda < "<<m_lmax<<"\n";
533 }
534 const auto& zkwk = m_cauchy_opt.update_zkwk( changed, func, dxfunc,
535 m_lmin, m_lmax, dmin, dmax, m_with_zero, m_eps);
536 if( changed) // if the nodes change we need to re-alloced solution space.
537 m_previous.assign( m_cauchy_opt.num_nodes(), {1, m_multi.copyable()});
538
539 if( m_verbose )
540 {
541 DG_RANK0 std::cout << "# Current number of nodes "<<m_cauchy_opt.num_nodes()<<"\n";
542 }
543
544 thrust::complex<double> zk, wk;
546 struct ShiftedOp
547 {
548 ShiftedOp( MatrixType& mat, const thrust::complex<double>& zk)
549 : m_zk(zk), m_mat(mat){}
550 void operator()( const ComplexContainer& x, ComplexContainer& y)
551 {
552 // Question: does COCG not care if matrix is positive/negative definite?
553 // maybe not, since matrix does not have real EV anyways?
554 dg::blas2::symv( m_mat, x, y);
555 dg::blas1::axpby( m_zk, x, -1., y);
556 }
557 auto weights() const { return m_mat.weights();}
558 auto precond() const { return m_mat.precond();}
559 private:
560 const thrust::complex<double>& m_zk;
561 MatrixType& m_mat;
562 };
564 std::vector<ShiftedOp > shifted_ops;
565 for( unsigned u=0; u<m_multi.stages(); u++)
566 shifted_ops.push_back( ShiftedOp{ ops[u], zk});
567
568 dg::blas1::copy( 0., x);
569 for( unsigned k=0; k<m_cauchy_opt.num_nodes(); k++)
570 //for( int k=m_cauchy_opt.num_nodes()-1; k>=0; k--)
571 {
572 zk = zkwk.first[k];
573 wk = zkwk.second[k];
574 // std::cout << "Zk wk "<<zk<<" "<<wk<<"\n";
575 // The very first m_z is zero: this should work in COCG as an allowed initial guess
576 m_previous[k].extrapolate( m_z);
577
578 if( m_adjoint)
579 {
580 dg::blas1::axpby( zk, d, 0., m_rhs);
581 dg::blas1::transform ( m_rhs, m_rhs, func);
582 dg::blas1::pointwiseDot( wk, m_rhs, b, 0., m_rhs);
583 m_multi.solve( shifted_ops, m_z, m_rhs, eps);
584 m_previous[k].update( m_z);
585 }
586 else
587 {
588 dg::blas1::axpby( wk, b, 0., m_rhs);
589 m_multi.solve( shifted_ops, m_z, m_rhs, eps);
590 m_previous[k].update( m_z);
591 dg::blas1::axpby( zk, d, 0., m_rhs);
592 dg::blas1::transform ( m_rhs, m_rhs, func);
593 dg::blas1::pointwiseDot( m_rhs, m_z, m_z);
594 }
595 dg::blas1::subroutine([]DG_DEVICE( thrust::complex<double> z, double& x) {
596 x += 2*z.real();}, m_z, x );
597 }
598 //std::cout << "SOL\n";
599 //for( unsigned u=0; u<10; u++)
600 // std::cout << x[u]<<" ";
601 //std::cout << std::endl;
602 }
603
604 private:
605
606 template<class MatrixType>
607 void update_extremeEVs(std::vector<MatrixType>& ops )
608 {
609 dg::mat::UniversalLanczos<ComplexContainer> lanczos( ops[0].weights(), 2000);
610 if( m_verbose)
611 lanczos.set_verbose(true);
612 auto rnd = ops[0].weights();
614 auto T = lanczos.tridiag( ops[0], rnd, ops[0].weights(), 1e-4, 1., "compute_extreme_EV");
615 //auto T = lanczos.tridiag( ops[0], rnd, ops[0].weights());
616 auto EVs = dg::mat::compute_extreme_EV( T);
617 // Let's use 10% safety range here (maybe test more but initial test show a slight improvement)
618 m_lmax = 1.1*EVs[1];
619 m_lmin = 0.9*EVs[0];
620 m_with_zero = false;
621 if( m_lmin < 1e-10*m_lmax)
622 {
623 if( m_verbose) DG_RANK0 std::cout << "# Found zero EV!\n";
624 m_lmin = m_lmax/ m_grid_points;
625 m_with_zero = true;
626 }
627 m_EV_up2date = true;
628 }
629
630
631 double m_eps;
632 MultigridCG2d<Geometry, Matrix, ComplexContainer, dg::complex_symmetric> m_multi; // does not remember any solutions
633 std::vector<dg::Extrapolation<ComplexContainer, double>> m_previous; // previous solutions for every zk
634 ComplexContainer m_z, m_rhs; // complex vectors
635 unsigned m_grid_points;
636 CauchyOptimizer m_cauchy_opt;
637 bool m_adjoint = true;
638
639 bool m_with_zero = false;
640 double m_lmin = 0, m_lmax = 0;
641 bool m_EV_up2date = false;
642 bool m_verbose = false;
643};
644
645}//namespace mat
646}//namespace dg
const std::vector< value_type > & data() const
Tridiagonalize and approximate via Lanczos algorithm. A is self-adjoint in the weights .
Definition lanczos.h:154
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)
void pointwiseDot(value_type alpha, const ContainerType1 &x1, const ContainerType2 &x2, value_type1 beta, ContainerType &y)
void transform(const ContainerType1 &x, ContainerType &y, UnaryOp op)
OutputType reduce(const ContainerType &x, OutputType zero, BinaryOp binary_op, UnaryOp unary_op=UnaryOp())
void subroutine(Subroutine f, ContainerType &&x, ContainerTypes &&... xs)
void evaluate(ContainerType &y, BinarySubroutine f, Functor g, const ContainerType0 &x0, const ContainerTypes &...xs)
auto dot(const ContainerType1 &x, const MatrixType &m, const ContainerType2 &y)
void symv(MatrixType &&M, const ContainerType1 &x, ContainerType2 &y)
typename TensorTraits< std::decay_t< Vector > >::value_type get_value_type
complex_symmetric
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:727
auto make_FuncEigen_Te1(UnaryOp f)
Create a functor that uses Eigenvalue decomposition to compute for symmetric tridiagonal T.
Definition matrixfunction.h:29
#define DG_DEVICE
dg::DMatrix Matrix
Definition lanczos_b.cpp:18
const double alpha
Definition lanczos_b.cpp:11
Functions for optimizing Contours.
double diff() const
void toc()
void tic()
Computation of or where is a positive (semi)-definite matrix self-adjoint in the weights .
Definition matrixprod.h:428
void solve(ContainerType0 &x, UnaryFunc func, UnaryFuncD dxfunc, std::vector< MatrixType > &ops, const ContainerType1 &d, const ContainerType2 &b, std::vector< double > eps)
Compute the bivariate matrix function.
Definition matrixprod.h:513
void clear_cache()
Clear the cached Eigenvalues of the matrix in the solve method.
Definition matrixprod.h:453
void set_adjoint(bool adjoint)
Determine if adjoint or direct bivariate matrix function is computed.
Definition matrixprod.h:478
unsigned num_nodes() const
Number of (complex) nodes used in the latest call to solve.
Definition matrixprod.h:470
const dg::MultigridCG2d< Geometry, Matrix, ComplexContainer, dg::complex_symmetric > & multigrid() const
Access the internal multigrid method to be able to construct matrices.
Definition matrixprod.h:444
void set_verbose(bool verbose)
Verbose output to std::cout.
Definition matrixprod.h:461
CauchyMatrixProduct(double lm_eps, const Geometry &grid, unsigned stages, bool adjoint=true)
Definition matrixprod.h:430
bool get_adjoint() const
Current value of the adjoint parameter.
Definition matrixprod.h:488
Computation of and where is a positive definite matrix self-adjoint in the weights .
Definition matrixprod.h:48
void compute_vlcl_adjoint(BinaryOp op, MatrixType &&A, const ContainerType0 &diag, const TriDiagonal< thrust::host_vector< value_type > > &T, ContainerType1 &x, const ContainerType2 &b, const ContainerType3 &weights, value_type bnorm)
Compute .
Definition matrixprod.h:312
void compute_vlcl(BinaryOp op, const ContainerType0 &diag, MatrixType &&A, const TriDiagonal< thrust::host_vector< value_type > > &T, ContainerType1 &x, const ContainerType2 &b, value_type bnorm)
Compute .
Definition matrixprod.h:237
ProductMatrixFunction(const ContainerType &copyable, unsigned max_iterations)
Allocate memory for the method.
Definition matrixprod.h:60
void set_benchmark(bool benchmark, std::string message="ProductFunction")
Set or unset performance timings during iterations.
Definition matrixprod.h:74
UniversalLanczos< ContainerType > & lanczos()
Access the Lanczos class that is constructed with the constructor parameters.
Definition matrixprod.h:385
ContainerType container_type
Definition matrixprod.h:49
unsigned apply(ContainerType0 &x, BinaryOp op, const ContainerType1 &diag, MatrixType &&A, const ContainerType2 &b, const ContainerType3 &weights, value_type eps, value_type nrmb_correction=1.)
Compute .
Definition matrixprod.h:108
void construct(Params &&...ps)
Perfect forward parameters to one of the constructors.
Definition matrixprod.h:67
dg::get_value_type< ContainerType > value_type
Definition matrixprod.h:50
ProductMatrixFunction()=default
Construct empty.
unsigned apply_adjoint(ContainerType0 &x, BinaryOp op, MatrixType &&A, const ContainerType1 &diag, const ContainerType2 &b, const ContainerType3 &weights, value_type eps, value_type nrmb_correction=1.)
Compute .
Definition matrixprod.h:171
#define DG_RANK0