78struct BiCGStabLSolverParams
80 using params = BiCGStabLSolverParams;
93 ePreconditionerSideType pside = ePreconditionerSideType::right;
102 double abstol = std::numeric_limits<double>::min();
114 BiCGStabLSolverParams() =
default;
117 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, L)
118 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, delta)
119 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, convex)
120 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, pside)
121 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, maxiter)
122 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, tol)
123 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, abstol)
124 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
ns_search)
125 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p,
verbose)
127 p.check_params({
"L",
"delta",
"convex",
"pside",
"maxiter",
"tol",
"abstol",
"ns_search",
"verbose" });
130 void get(
PropertyTree& p,
const std::string& path)
const
132 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, L);
133 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, delta);
134 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, convex);
135 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, pside);
136 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, maxiter);
137 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tol);
138 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, abstol);
139 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path,
ns_search);
140 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path,
verbose);
155 using backend_type = Backend;
156 using BackendType = Backend;
158 typedef typename Backend::vector vector;
159 typedef typename Backend::value_type value_type;
162 typedef typename math::scalar_of<value_type>::type scalar_type;
165 typename math::rhs_of<value_type>::type>::return_type coef_type;
171 const params& prm = params(),
172 const backend_params& backend_prm = backend_params(),
173 const InnerProduct& inner_product = InnerProduct())
174 : SolverBase(context)
177 , Rt(Backend::create_vector(n, backend_prm))
178 , X(Backend::create_vector(n, backend_prm))
179 , B(Backend::create_vector(n, backend_prm))
180 , T(Backend::create_vector(n, backend_prm))
183 , MZa(prm.L + 1, prm.L + 1)
184 , MZb(prm.L + 1, prm.L + 1)
187 , inner_product(inner_product)
189 precondition(prm.L > 0,
"L in BiCGStab(L) should be >=1");
191 for (
int i = 0; i <= prm.L; ++i) {
192 R[i] = Backend::create_vector(n, backend_prm);
193 U[i] = Backend::create_vector(n, backend_prm);
212 template <
class Matrix,
class Precond,
class Vec1,
class Vec2>
213 SolverResult operator()(
const Matrix& A,
const Precond& P,
const Vec1& rhs, Vec2&& x)
const
215 static const coef_type one = math::identity<coef_type>();
216 static const coef_type zero = math::zero<coef_type>();
222 scalar_type norm_rhs = norm(rhs);
225 if (norm_rhs < Alina::detail::eps<scalar_type>(1)) {
227 norm_rhs = math::identity<scalar_type>();
231 return SolverResult(0, norm_rhs);
235 if (prm.pside == ePreconditionerSideType::left) {
236 backend::residual(rhs, A, x, *T);
240 backend::residual(rhs, A, x, *B);
243 scalar_type zeta0 = norm(*B);
244 scalar_type eps = std::max(prm.tol * norm_rhs, prm.abstol);
246 coef_type alpha = zero;
247 coef_type rho0 = one;
248 coef_type omega = one;
251 backend::copy(*B, *R[0]);
252 backend::copy(*B, *Rt);
254 backend::clear(*U[0]);
256 scalar_type zeta = zeta0;
257 scalar_type rnmax_computed = zeta0;
258 scalar_type rnmax_true = zeta0;
261 for (; iter < prm.maxiter && zeta >= eps; iter += L) {
263 rho0 = -omega * rho0;
265 for (
int j = 0; j < L; ++j) {
266 coef_type rho1 = inner_product(*R[j], *Rt);
267 precondition(!math::is_zero(rho1),
"BiCGStab(L) breakdown: diverged (zero rho)");
269 coef_type beta = alpha * (rho1 / rho0);
272 for (
int i = 0; i <= j; ++i)
273 backend::axpby(one, *R[i], -beta, *U[i]);
275 preconditioner_spmv(prm.pside, P, A, *U[j], *U[j + 1], *T);
277 coef_type sigma = inner_product(*U[j + 1], *Rt);
278 precondition(!math::is_zero(sigma),
"BiCGStab(L) breakdown: diverged (zero sigma)");
279 alpha = rho1 / sigma;
281 backend::axpby(alpha, *U[0], one, *X);
283 for (
int i = 0; i <= j; ++i)
284 backend::axpby(-alpha, *U[i + 1], one, *R[i]);
286 preconditioner_spmv(prm.pside, P, A, *R[j], *R[j + 1], *T);
290 rnmax_computed = std::max(zeta, rnmax_computed);
291 rnmax_true = std::max(zeta, rnmax_true);
301 for (
int i = 0; i <= L; ++i) {
302 for (
int j = 0; j <= i; ++j) {
303 MZa(i, j) = inner_product(*R[i], *R[j]);
308 for (
int i = 0; i <= L; ++i) {
309 for (
int j = i + 1; j <= L; ++j) {
310 MZa(i, j) = MZa(j, i) = math::adjoint(MZa(j, i));
314 std::copy(MZa.data(), MZa.data() + MZa.size(), MZb.data());
316 if (prm.convex || L == 1) {
319 qr.solve(L, L, MZa.stride(0), MZa.stride(1),
320 &MZa(1, 1), &MZb(0, 1), &Y0[1]);
325 qr.solve(L - 1, L - 1, MZa.stride(0), MZa.stride(1),
326 &MZa(1, 1), &MZb(0, 1), &Y0[1]);
330 qr.solve(L - 1, L - 1, MZa.stride(0), MZa.stride(1),
331 &MZa(1, 1), &MZb(L, 1), &YL[1],
true);
333 coef_type dot0 = zero;
334 coef_type dot1 = zero;
335 coef_type dotA = zero;
336 for (
int i = 0; i <= L; ++i) {
340 for (
int j = 0; j <= L; ++j) {
341 coef_type M = MZb(i, j);
351 scalar_type kappa0 =
sqrt(std::abs(std::real(dot0)));
352 scalar_type kappa1 =
sqrt(std::abs(std::real(dot1)));
353 scalar_type kappaA = std::real(dotA);
355 if (!math::is_zero(kappa0) && !math::is_zero(kappa1)) {
357 if (kappaA < 0.7 * kappa0 * kappa1) {
358 ghat = (kappaA < 0) ? -0.7 * kappa0 / kappa1 : 0.7 * kappa0 / kappa1;
361 ghat = kappaA / (kappa1 * kappa1);
364 for (
int i = 0; i <= L; ++i)
365 Y0[i] -= ghat * YL[i];
370 for (
int h = L; h > 0 && math::is_zero(omega); --h)
372 precondition(!math::is_zero(omega),
"BiCGStab(L) breakdown: diverged (zero omega)");
374 backend::lin_comb(L, &Y0[1], &R[0], one, *X);
376 for (
int i = 1; i <= L; ++i)
377 Y0[i] = -one * Y0[i];
379 backend::lin_comb(L, &Y0[1], &U[1], one, *U[0]);
380 backend::lin_comb(L, &Y0[1], &R[1], one, *R[0]);
382 for (
int i = 1; i <= L; ++i)
383 Y0[i] = -one * Y0[i];
389 rnmax_computed = std::max(zeta, rnmax_computed);
390 rnmax_true = std::max(zeta, rnmax_true);
392 bool update_x = zeta < prm.delta * zeta0 && zeta0 <= rnmax_computed;
394 if ((zeta < prm.delta * rnmax_true && zeta <= rnmax_true) || update_x) {
395 preconditioner_spmv(prm.pside, P, A, *X, *R[0], *T);
396 backend::axpby(one, *B, -one, *R[0]);
400 if (prm.pside == ePreconditionerSideType::left) {
401 backend::axpby(one, *X, one, x);
404 backend::axpby(one, *T, one, x);
407 backend::copy(*R[0], *B);
409 rnmax_computed = zeta;
413 if (prm.verbose && iter % 5 == 0)
414 std::cout << iter <<
"\t" << std::scientific << zeta / norm_rhs << std::endl;
418 if (prm.pside == ePreconditionerSideType::left) {
419 backend::axpby(one, *X, one, x);
423 backend::axpby(one, *T, one, x);
426 return SolverResult(iter, zeta / norm_rhs);
439 template <
class Precond,
class Vec1,
class Vec2>
442 return (*
this)(P.system_matrix(), P, rhs, x);
449 b += backend::bytes(*Rt);
450 b += backend::bytes(*X);
451 b += backend::bytes(*B);
452 b += backend::bytes(*T);
454 for (
const auto& v : R)
455 b += backend::bytes(*v);
456 for (
const auto& v : U)
457 b += backend::bytes(*v);
459 b += MZa.size() *
sizeof(coef_type);
460 b += MZb.size() *
sizeof(coef_type);
462 b += backend::bytes(Y0);
463 b += backend::bytes(YL);
470 friend std::ostream& operator<<(std::ostream& os,
const BiCGStabLSolver& s)
472 return os <<
"Type: BiCGStab(" << s.prm.L <<
")"
473 <<
"\nUnknowns: " << s.n
474 <<
"\nMemory footprint: " << human_readable_memory(s.
bytes())
486 mutable std::shared_ptr<vector> Rt;
487 mutable std::shared_ptr<vector> X;
488 mutable std::shared_ptr<vector> B;
489 mutable std::shared_ptr<vector> T;
491 mutable std::vector<std::shared_ptr<vector>> R;
492 mutable std::vector<std::shared_ptr<vector>> U;
494 mutable multi_array<coef_type, 2> MZa, MZb;
495 mutable std::vector<coef_type> Y0, YL;
498 InnerProduct inner_product;
501 scalar_type norm(
const Vec& x)
const
503 return sqrt(math::norm(inner_product(x, x)));