Arcane  v4.1.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
IDRSSolver.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* IDDRSolver.h (C) 2000-2026 */
9/* */
10/* IDR(s) (Induced Dimension Reduction) method. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_IDRSSOLVER_H
13#define ARCCORE_ALINA_IDRSSOLVER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * This file is based on the work on AMGCL library (version march 2026)
18 * which can be found at https://github.com/ddemidov/amgcl.
19 *
20 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
21 * SPDX-License-Identifier: MIT
22 */
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25/*
26 * The code is ported from Matlab code published at
27 * http://ta.twi.tudelft.nl/nw/users/gijzen/IDR.html.
28 *
29 * This is a very stable and efficient IDR(s) variant (implemented in the MATLAB
30 * code idrs.m given above) as described in: Martin B. van Gijzen and Peter
31 * Sonneveld, Algorithm 913: An Elegant IDR(s) Variant that Efficiently Exploits
32 * Bi-orthogonality Properties. ACM Transactions on Mathematical Software, Vol.
33 * 38, No. 1, pp. 5:1-5:19, 2011 (copyright ACM).
34 */
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38#include "arccore/alina/SolverUtils.h"
39#include "arccore/alina/AlinaUtils.h"
40
41#include <random>
42
43/*---------------------------------------------------------------------------*/
44/*---------------------------------------------------------------------------*/
45
46namespace Arcane::Alina
47{
48
49/*---------------------------------------------------------------------------*/
50/*---------------------------------------------------------------------------*/
54struct IDRSSolverParams
55{
56 using params = IDRSSolverParams;
57
59 Int32 s = 4;
60
69 double omega = 0.7;
70
72 bool smoothing = false;
73
82 bool replacement = false;
83
86
88 double tol = 1e-8;
89
91 double abstol = std::numeric_limits<double>::min();
92
98 bool ns_search = false;
99
101 bool verbose = false;
102
103 IDRSSolverParams() = default;
104
105 IDRSSolverParams(const PropertyTree& p)
106 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, s)
107 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, omega)
108 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, smoothing)
109 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, replacement)
110 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, maxiter)
111 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, tol)
112 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, abstol)
113 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, ns_search)
114 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, verbose)
115 {
116 p.check_params({ "s", "omega", "smoothing", "replacement", "maxiter", "tol", "abstol", "ns_search", "verbose" });
117 }
118
119 void get(PropertyTree& p, const std::string& path) const
120 {
121 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, s);
122 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, omega);
123 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, smoothing);
124 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, replacement);
125 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, maxiter);
126 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tol);
127 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, abstol);
128 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, ns_search);
129 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, verbose);
130 }
131};
132
133/*---------------------------------------------------------------------------*/
134/*---------------------------------------------------------------------------*/
135
137template <class Backend, class InnerProduct = detail::default_inner_product>
139: public SolverBase
140{
141 public:
142
143 using backend_type = Backend;
144 using BackendType = Backend;
145
146 typedef typename Backend::vector vector;
147 typedef typename Backend::value_type value_type;
148 typedef typename Backend::params backend_params;
149
150 typedef typename math::scalar_of<value_type>::type scalar_type;
151 typedef typename math::rhs_of<value_type>::type rhs_type;
152
153 typedef typename math::inner_product_impl<typename math::rhs_of<value_type>::type>::return_type coef_type;
154
155 using params = IDRSSolverParams;
156
158 IDRSSolver(size_t n,
159 const params& prm = params(),
160 const backend_params& bprm = backend_params(),
161 const InnerProduct& inner_product = InnerProduct())
162 : prm(prm)
163 , n(n)
164 , inner_product(inner_product)
165 , M(prm.s, prm.s)
166 , f(prm.s)
167 , c(prm.s)
168 , r(Backend::create_vector(n, bprm))
169 , v(Backend::create_vector(n, bprm))
170 , t(Backend::create_vector(n, bprm))
171 {
172 static const scalar_type one = math::identity<scalar_type>();
173 static const scalar_type zero = math::zero<scalar_type>();
174
175 if (prm.smoothing) {
176 x_s = Backend::create_vector(n, bprm);
177 r_s = Backend::create_vector(n, bprm);
178 }
179
180 G.reserve(prm.s);
181 U.reserve(prm.s);
182 for (Int32 i = 0; i < prm.s; ++i) {
183 G.push_back(Backend::create_vector(n, bprm));
184 U.push_back(Backend::create_vector(n, bprm));
185 }
186
187 // Initialize P.
188 P.reserve(prm.s);
189 {
190 std::vector<rhs_type> p(n);
191
192 int pid = inner_product.rank();
193 const int nt = 1;
194
195 const int tid = 0;
196 std::mt19937 rng(pid * nt + tid);
197 std::uniform_real_distribution<scalar_type> rnd(-1, 1);
198
199 for (unsigned j = 0; j < prm.s; ++j) {
200 for (ptrdiff_t i = 0; i < n; ++i) {
201 p[i] = math::constant<rhs_type>(rnd(rng));
202 }
203 P.push_back(Backend::copy_vector(p, bprm));
204 }
205
206 for (unsigned j = 0; j < prm.s; ++j) {
207 for (unsigned k = 0; k < j; ++k) {
208 coef_type alpha = inner_product(*P[k], *P[j]);
209 backend::axpby(-alpha, *P[k], one, *P[j]);
210 }
211 scalar_type norm_pj = norm(*P[j]);
212 backend::axpby(math::inverse(norm_pj), *P[j], zero, *P[j]);
213 }
214 }
215 }
216
232 template <class Matrix, class Precond, class Vec1, class Vec2>
233 SolverResult operator()(Matrix const& A, Precond const& Prec, Vec1 const& rhs, Vec2& x) const
234 {
235 static const scalar_type one = math::identity<scalar_type>();
236 static const scalar_type zero = math::zero<scalar_type>();
237
238 ScopedStreamModifier ss(std::cout);
239
240 scalar_type norm_rhs = norm(rhs);
241 if (norm_rhs < Alina::detail::eps<scalar_type>(1)) {
242 if (prm.ns_search) {
243 norm_rhs = math::identity<scalar_type>();
244 }
245 else {
246 backend::clear(x);
247 return SolverResult(0, norm_rhs);
248 }
249 }
250
251 scalar_type eps = std::max(prm.tol * norm_rhs, prm.abstol);
252
253 // Compute initial residual:
254 backend::residual(rhs, A, x, *r);
255
256 scalar_type res_norm = norm(*r);
257 if (res_norm <= eps) {
258 // Initial guess is a good enough solution.
259 return SolverResult(0, res_norm / norm_rhs);
260 }
261
262 if (prm.smoothing) {
263 backend::copy(x, *x_s);
264 backend::copy(*r, *r_s);
265 }
266
267 // Initialization.
268 coef_type om = math::identity<coef_type>();
269
270 for (unsigned i = 0; i < prm.s; ++i) {
271 backend::clear(*G[i]);
272 backend::clear(*U[i]);
273
274 for (unsigned j = 0; j < prm.s; ++j)
275 M(i, j) = (i == j);
276 }
277
278 // Main iteration loop, build G-spaces:
279 size_t iter = 0;
280 while (iter < prm.maxiter && res_norm > eps) {
281 // New righ-hand size for small system:
282 for (unsigned i = 0; i < prm.s; ++i)
283 f[i] = inner_product(*r, *P[i]);
284
285 for (unsigned k = 0; k < prm.s; ++k) {
286 // Compute new v
287 backend::copy(*r, *v);
288
289 // Solve small system (Note: M is lower triangular)
290 // and make v orthogonal to P:
291 for (unsigned i = k; i < prm.s; ++i) {
292 c[i] = f[i];
293 for (unsigned j = k; j < i; ++j)
294 c[i] -= M(i, j) * c[j];
295 c[i] = math::inverse(M(i, i)) * c[i];
296
297 backend::axpby(-c[i], *G[i], one, *v);
298 }
299
300 Prec.apply(*v, *t);
301
302 // Compute new U[k]
303 backend::axpby(om, *t, c[k], *U[k]);
304 for (unsigned i = k + 1; i < prm.s; ++i)
305 backend::axpby(c[i], *U[i], one, *U[k]);
306
307 // Compute new G[k], G[k] is in space G_j
308 backend::spmv(one, A, *U[k], zero, *G[k]);
309
310 // Bi-Orthogonalise the new basis vectors:
311 for (unsigned i = 0; i < k; ++i) {
312 coef_type alpha = inner_product(*G[k], *P[i]) / M(i, i);
313
314 backend::axpby(-alpha, *G[i], one, *G[k]);
315 backend::axpby(-alpha, *U[i], one, *U[k]);
316 }
317
318 // New column of M = P'*G (first k-1 entries are zero)
319 for (unsigned i = k; i < prm.s; ++i)
320 M(i, k) = inner_product(*G[k], *P[i]);
321
322 precondition(!math::is_zero(M(k, k)), "IDR(s) breakdown: zero M[k,k]");
323
324 // Make r orthogonal to q_i, i = [0..k)
325 coef_type beta = math::inverse(M(k, k)) * f[k];
326 backend::axpby(-beta, *G[k], one, *r);
327 backend::axpby(beta, *U[k], one, x);
328
329 res_norm = norm(*r);
330
331 // Smoothing
332 if (prm.smoothing) {
333 backend::axpbypcz(one, *r_s, -one, *r, zero, *t);
334 coef_type gamma = inner_product(*t, *r_s) / inner_product(*t, *t);
335 backend::axpby(-gamma, *t, one, *r_s);
336 backend::axpbypcz(-gamma, *x_s, gamma, x, one, *x_s);
337 res_norm = norm(*r_s);
338 }
339
340 if (prm.verbose && iter % 5 == 0)
341 std::cout << iter << "\t" << std::scientific << res_norm / norm_rhs << std::endl;
342 if (res_norm <= eps || ++iter >= prm.maxiter)
343 break;
344
345 // New f = P'*r (first k components are zero)
346 for (unsigned i = k + 1; i < prm.s; ++i)
347 f[i] -= beta * M(i, k);
348 }
349
350 if (res_norm <= eps || iter >= prm.maxiter)
351 break;
352
353 // Now we have sufficient vectors in G_j to compute residual in G_j+1
354 // Note: r is already perpendicular to P so v = r
355
356 Prec.apply(*r, *v);
357 backend::spmv(one, A, *v, zero, *t);
358
359 // Computation of a new omega
360 om = omega(*t, *r);
361 precondition(!math::is_zero(om), "IDR(s) breakdown: zero omega");
362
363 backend::axpby(-om, *t, one, *r);
364 backend::axpby(om, *v, one, x);
365
366 if (prm.replacement) {
367 backend::residual(rhs, A, x, *r);
368 }
369 res_norm = norm(*r);
370
371 // Smoothing.
372 if (prm.smoothing) {
373 backend::axpbypcz(one, *r_s, -one, *r, zero, *t);
374 coef_type gamma = inner_product(*t, *r_s) / inner_product(*t, *t);
375 backend::axpby(-gamma, *t, one, *r_s);
376 backend::axpbypcz(-gamma, *x_s, gamma, x, one, *x_s);
377 res_norm = norm(*r_s);
378 }
379
380 ++iter;
381 }
382
383 if (prm.smoothing)
384 backend::copy(*x_s, x);
385
386 return SolverResult(iter, res_norm / norm_rhs);
387 }
388
399 template <class Precond, class Vec1, class Vec2>
400 SolverResult operator()(Precond const& P, Vec1 const& rhs, Vec2& x) const
401 {
402 return (*this)(P.system_matrix(), P, rhs, x);
403 }
404
405 size_t bytes() const
406 {
407 size_t b = 0;
408
409 b += M.size() * sizeof(coef_type);
410
411 b += backend::bytes(f);
412 b += backend::bytes(c);
413
414 b += backend::bytes(*r);
415 b += backend::bytes(*v);
416 b += backend::bytes(*t);
417
418 if (x_s)
419 b += backend::bytes(*x_s);
420 if (r_s)
421 b += backend::bytes(*r_s);
422
423 for (const auto& v : P)
424 b += backend::bytes(*v);
425 for (const auto& v : G)
426 b += backend::bytes(*v);
427 for (const auto& v : U)
428 b += backend::bytes(*v);
429
430 return b;
431 }
432
433 friend std::ostream& operator<<(std::ostream& os, const IDRSSolver& s)
434 {
435 return os << "Type: IDR(" << s.prm.s << ")"
436 << "\nUnknowns: " << s.n
437 << "\nMemory footprint: " << human_readable_memory(s.bytes())
438 << std::endl;
439 }
440
441 public:
442
443 params prm;
444
445 private:
446
447 size_t n;
448
449 InnerProduct inner_product;
450
451 mutable multi_array<coef_type, 2> M;
452 mutable std::vector<coef_type> f, c;
453
454 std::shared_ptr<vector> r, v, t;
455 std::shared_ptr<vector> x_s;
456 std::shared_ptr<vector> r_s;
457
458 std::vector<std::shared_ptr<vector>> P, G, U;
459
460 template <class Vec>
461 scalar_type norm(const Vec& x) const
462 {
463 return std::abs(sqrt(inner_product(x, x)));
464 }
465
466 template <class Vector1, class Vector2>
467 coef_type omega(const Vector1& t, const Vector2& s) const
468 {
469 scalar_type norm_t = norm(t);
470 scalar_type norm_s = norm(s);
471
472 coef_type ts = inner_product(t, s);
473 scalar_type rho = math::norm(ts / (norm_t * norm_s));
474 coef_type om = ts / (norm_t * norm_t);
475
476 if (rho < prm.omega)
477 om *= prm.omega / rho;
478
479 return om;
480 }
481};
482
483/*---------------------------------------------------------------------------*/
484/*---------------------------------------------------------------------------*/
485
486} // namespace Arcane::Alina
487
488/*---------------------------------------------------------------------------*/
489/*---------------------------------------------------------------------------*/
490
491#endif
IDR(s) method (Induced Dimension Reduction)
Definition IDRSSolver.h:140
IDRSSolver(size_t n, const params &prm=params(), const backend_params &bprm=backend_params(), const InnerProduct &inner_product=InnerProduct())
Preallocates necessary data structures for the system of size n.
Definition IDRSSolver.h:158
SolverResult operator()(Matrix const &A, Precond const &Prec, Vec1 const &rhs, Vec2 &x) const
Computes the solution for the given system matrix.
Definition IDRSSolver.h:233
SolverResult operator()(Precond const &P, Vec1 const &rhs, Vec2 &x) const
Computes the solution for the given right-hand side.
Definition IDRSSolver.h:400
size_t bytes() const
Memory used in bytes.
Definition IDRSSolver.h:405
Save ostream flags in constructor, restore in destructor.
Base class for solvers.
Definition SolverBase.h:31
Result of a solving.
Definition AlinaUtils.h:52
Matrix class, to be used by user.
std::int32_t Int32
Type entier signé sur 32 bits.
Parameters for IDR(s) solver.
Definition IDRSSolver.h:55
Int32 s
Dimension of the shadow space in IDR(s).
Definition IDRSSolver.h:59
bool ns_search
Ignore the trivial solution x=0 when rhs is zero.
Definition IDRSSolver.h:98
double omega
Computation of omega.
Definition IDRSSolver.h:69
bool verbose
Verbose output (show iterations and error)
Definition IDRSSolver.h:101
double abstol
Target absolute residual error.
Definition IDRSSolver.h:91
Int32 maxiter
Maximum number of iterations.
Definition IDRSSolver.h:85
double tol
Target relative residual error.
Definition IDRSSolver.h:88
bool smoothing
Specifies if residual smoothing must be applied.
Definition IDRSSolver.h:72
bool replacement
Residual replacement.
Definition IDRSSolver.h:82
Default implementation for inner product.