IgANet
IGAnets - Isogeometric Analysis Networks
Loading...
Searching...
No Matches
solver.hpp
Go to the documentation of this file.
1
15#pragma once
16
17#include <algorithm>
18#include <cmath>
19#include <concepts>
20#include <vector>
21
22#include <iganet/core/core.hpp>
23
24namespace iganet::utils {
25
28template <typename T>
30 requires(T &preconditioner, const torch::Tensor &residual) {
31 { preconditioner(residual) } -> std::same_as<torch::Tensor>;
32 };
33
34namespace detail {
35
36#ifndef NDEBUG
40inline bool tensor_values_are_finite(const torch::Tensor &tensor) {
41 const auto &values = tensor.layout() == torch::kStrided
42 ? tensor
43 : tensor.values();
44 return torch::isfinite(values).all().item<bool>();
45}
46
52inline void validate_iterative_solver_inputs(const torch::Tensor &A,
53 const torch::Tensor &b,
54 int max_iter, double tol) {
55 TORCH_CHECK(A.dim() == 2 && A.size(0) == A.size(1),
56 "iterative solver requires a square rank-2 matrix");
57 TORCH_CHECK(b.dim() == 1 && b.size(0) == A.size(0),
58 "iterative solver requires a compatible rank-1 right-hand side");
59 TORCH_CHECK(A.scalar_type() == b.scalar_type(),
60 "matrix and right-hand side must have the same dtype");
61 TORCH_CHECK(A.device() == b.device(),
62 "matrix and right-hand side must be on the same device");
63 TORCH_CHECK(A.is_floating_point() && b.is_floating_point(),
64 "iterative solver requires floating-point inputs");
65 TORCH_CHECK(max_iter >= 0, "max_iter must be non-negative");
66 TORCH_CHECK(std::isfinite(tol) && tol > 0.0,
67 "tolerance must be finite and positive");
68 TORCH_CHECK(tensor_values_are_finite(A),
69 "matrix contains NaN or Inf");
70 TORCH_CHECK(tensor_values_are_finite(b),
71 "right-hand side contains NaN or Inf");
72}
73
77inline void check_finite_scalar(const torch::Tensor &value,
78 const char *message) {
79 TORCH_CHECK(torch::isfinite(value).item<bool>(), message);
80}
81
85inline void check_finite_tensor(const torch::Tensor &value,
86 const char *message) {
87 TORCH_CHECK(tensor_values_are_finite(value), message);
88}
89
93inline void validate_preconditioner_output(const torch::Tensor &value,
94 const torch::Tensor &residual) {
95 TORCH_CHECK(value.sizes() == residual.sizes(),
96 "preconditioner must preserve the residual shape");
97 TORCH_CHECK(value.scalar_type() == residual.scalar_type(),
98 "preconditioner must preserve the residual dtype");
99 TORCH_CHECK(value.device() == residual.device(),
100 "preconditioner must preserve the residual device");
101 TORCH_CHECK(value.layout() == torch::kStrided,
102 "preconditioner must return a strided tensor");
103 TORCH_CHECK(tensor_values_are_finite(value),
104 "preconditioner returned NaN or Inf");
105}
106
111 const torch::Tensor &inverse_preconditioner, const torch::Tensor &A) {
112 TORCH_CHECK(inverse_preconditioner.dim() == 2 &&
113 inverse_preconditioner.size(0) == A.size(0) &&
114 inverse_preconditioner.size(1) == A.size(1),
115 "inverse preconditioner must have the same square shape as A");
116 TORCH_CHECK(inverse_preconditioner.scalar_type() == A.scalar_type(),
117 "inverse preconditioner and A must have the same dtype");
118 TORCH_CHECK(inverse_preconditioner.device() == A.device(),
119 "inverse preconditioner and A must be on the same device");
120 TORCH_CHECK(tensor_values_are_finite(inverse_preconditioner),
121 "inverse preconditioner contains NaN or Inf");
122}
123
126inline void validate_gmres_parameters(int restart) {
127 TORCH_CHECK(restart > 0, "GMRES restart must be positive");
128}
129
133inline void check_nonzero_finite_scalar(const torch::Tensor &value,
134 const char *message) {
135 check_finite_scalar(value, message);
136 TORCH_CHECK(value.item<double>() != 0.0, message);
137}
138
142inline void check_positive_finite_scalar(const torch::Tensor &value,
143 const char *message) {
144 check_finite_scalar(value, message);
145 TORCH_CHECK(value.item<double>() > 0.0, message);
146}
147
151inline void check_nonnegative_finite_scalar(const torch::Tensor &value,
152 const char *message) {
153 check_finite_scalar(value, message);
154 TORCH_CHECK(value.item<double>() >= 0.0, message);
155}
156#else
157inline void validate_iterative_solver_inputs(const torch::Tensor &,
158 const torch::Tensor &, int,
159 double) {}
160
161inline void check_finite_scalar(const torch::Tensor &, const char *) {}
162
163inline void check_finite_tensor(const torch::Tensor &, const char *) {}
164
165inline void validate_preconditioner_output(const torch::Tensor &,
166 const torch::Tensor &) {}
167
168inline void validate_inverse_preconditioner(const torch::Tensor &,
169 const torch::Tensor &) {}
170
171inline void validate_gmres_parameters(int) {}
172
173inline void check_nonzero_finite_scalar(const torch::Tensor &,
174 const char *) {}
175
176inline void check_positive_finite_scalar(const torch::Tensor &,
177 const char *) {}
178
179inline void check_nonnegative_finite_scalar(const torch::Tensor &,
180 const char *) {}
181#endif
182
183} // namespace detail
184
192 inline auto cg(const torch::Tensor& A,
193 const torch::Tensor b,
194 int max_iter = 1000,
195 double tol = 1e-10) {
196
197 detail::validate_iterative_solver_inputs(A, b, max_iter, tol);
198
199 auto x = torch::zeros_like(b);
200
201 if (b.norm().item<double>() < tol)
202 return std::make_tuple(x, -1, b.norm().item<double>());
203
204 auto r = b.clone();
205 auto p = b.clone();
206
207 for (int iter = 0; iter < max_iter; iter++) {
208
209 auto Ap = A.matmul(p);
210 auto beta = torch::dot(r, r);
212 "CG numerical breakdown: r.r is zero");
213 auto denominator = torch::dot(Ap, p);
215 denominator, "CG numerical breakdown: p.A.p is zero or non-finite");
216 auto alpha = beta / denominator;
218 "CG numerical breakdown: alpha is non-finite");
219
220 x += alpha * p;
221 r -= alpha * Ap;
222
223 if (r.norm().item<double>() < tol)
224 return std::make_tuple(x, iter, r.norm().item<double>());
225
226 beta = torch::dot(r, r) / beta;
228 "CG numerical breakdown: beta is non-finite");
229 p = r + beta * p;
230 }
231
232 return std::make_tuple(x, max_iter, r.norm().item<double>());
233 }
234
247 template <IterativeSolverPreconditioner Preconditioner>
248 inline auto pcg(const torch::Tensor &A, const torch::Tensor b,
249 Preconditioner &&preconditioner,
250 int max_iter = 1000, double tol = 1e-10) {
251 detail::validate_iterative_solver_inputs(A, b, max_iter, tol);
252
253 auto x = torch::zeros_like(b);
254 const auto initial_residual = b.norm().item<double>();
255 if (initial_residual < tol)
256 return std::make_tuple(x, -1, initial_residual);
257 if (max_iter == 0)
258 return std::make_tuple(x, 0, initial_residual);
259
260 auto r = b.clone();
261 auto z = preconditioner(r);
263 auto p = z.clone();
264 auto rz = torch::dot(r, z);
266 rz, "PCG numerical breakdown: r.z is zero or non-finite");
267
268 for (int iter = 0; iter < max_iter; ++iter) {
269 auto Ap = A.matmul(p);
270 auto denominator = torch::dot(p, Ap);
272 denominator, "PCG numerical breakdown: p.A.p is zero or non-finite");
273 auto alpha = rz / denominator;
275 alpha, "PCG numerical breakdown: alpha is non-finite");
276
277 x += alpha * p;
278 r -= alpha * Ap;
279
280 const auto residual = r.norm().item<double>();
281 if (residual < tol)
282 return std::make_tuple(x, iter, residual);
283
284 z = preconditioner(r);
286 auto rz_next = torch::dot(r, z);
288 rz_next, "PCG numerical breakdown: next r.z is zero or non-finite");
289 auto beta = rz_next / rz;
291 beta, "PCG numerical breakdown: beta is non-finite");
292 p = z + beta * p;
293 rz = rz_next;
294 }
295
296 return std::make_tuple(x, max_iter, r.norm().item<double>());
297 }
298
306 inline auto pcg(const torch::Tensor &A, const torch::Tensor b,
307 const torch::Tensor &inverse_preconditioner,
308 int max_iter = 1000, double tol = 1e-10) {
309 detail::validate_inverse_preconditioner(inverse_preconditioner, A);
310 auto apply = [&inverse_preconditioner](const torch::Tensor &residual) {
311 return inverse_preconditioner.matmul(residual);
312 };
313 return pcg(A, b, apply, max_iter, tol);
314 }
315
323 inline auto bicgstab(const torch::Tensor& A,
324 const torch::Tensor b,
325 int max_iter = 1000,
326 double tol = 1e-10) {
327
328 detail::validate_iterative_solver_inputs(A, b, max_iter, tol);
329
330 auto x = torch::zeros_like(b);
331
332 if (b.norm().item<double>() < tol)
333 return std::make_tuple(x, -1, b.norm().item<double>());
334
335 auto r = b.clone();
336 auto r_hat = b.clone();
337
338 auto alpha = torch::scalar_tensor(1.0, b.options());
339 auto omega = torch::scalar_tensor(1.0, b.options());
340 auto rho = torch::scalar_tensor(1.0, b.options());
341
342 auto p = torch::zeros_like(b);
343 auto v = torch::zeros_like(b);
344
345 for (int iter = 0; iter < max_iter; iter++) {
346
347 auto rho_hat = torch::dot(r_hat, r);
349 rho_hat, "BiCGStab numerical breakdown: rho is zero or non-finite");
351 rho, "BiCGStab numerical breakdown: previous rho is zero or non-finite");
353 omega,
354 "BiCGStab numerical breakdown: omega is zero or non-finite");
355 auto beta = rho_hat / rho * alpha / omega;
357 beta, "BiCGStab numerical breakdown: beta is non-finite");
358
359 p = r + beta * (p - omega * v);
360 v = A.matmul(p);
361
362 auto alpha_denominator = torch::dot(r_hat, v);
364 alpha_denominator,
365 "BiCGStab numerical breakdown: alpha denominator is zero or non-finite");
366 alpha = rho_hat / alpha_denominator;
368 alpha, "BiCGStab numerical breakdown: alpha is non-finite");
369 auto s = r - alpha * v;
370
371 if (s.norm().item<double>() < tol) {
372 x += alpha * p;
373 return std::make_tuple(x, iter, s.norm().item<double>());
374 }
375
376 auto t = A.matmul(s);
377 auto omega_denominator = torch::dot(t, t);
379 omega_denominator,
380 "BiCGStab numerical breakdown: t.t is zero or non-finite");
381 omega = torch::dot(s, t) / omega_denominator;
383 omega, "BiCGStab numerical breakdown: omega is zero or non-finite");
384 x += alpha * p + omega * s;
385 r = s - omega * t;
387 r, "BiCGStab numerical breakdown: residual is non-finite");
388 rho = rho_hat;
389 }
390
391 return std::make_tuple(x, max_iter, r.norm().item<double>());
392 }
393
406 template <IterativeSolverPreconditioner Preconditioner>
407 inline auto pbicgstab(const torch::Tensor &A, const torch::Tensor b,
408 Preconditioner &&preconditioner,
409 int max_iter = 1000, double tol = 1e-10) {
410 detail::validate_iterative_solver_inputs(A, b, max_iter, tol);
411
412 auto x = torch::zeros_like(b);
413 const auto initial_residual = b.norm().item<double>();
414 if (initial_residual < tol)
415 return std::make_tuple(x, -1, initial_residual);
416
417 auto r = b.clone();
418 auto r_hat = b.clone();
419 auto alpha = torch::scalar_tensor(1.0, b.options());
420 auto omega = torch::scalar_tensor(1.0, b.options());
421 auto rho = torch::scalar_tensor(1.0, b.options());
422 auto p = torch::zeros_like(b);
423 auto v = torch::zeros_like(b);
424
425 for (int iter = 0; iter < max_iter; ++iter) {
426 auto rho_hat = torch::dot(r_hat, r);
428 rho_hat, "PBiCGStab numerical breakdown: rho is zero or non-finite");
430 rho, "PBiCGStab numerical breakdown: previous rho is zero or non-finite");
432 omega, "PBiCGStab numerical breakdown: omega is zero or non-finite");
433 auto beta = rho_hat / rho * alpha / omega;
435 beta, "PBiCGStab numerical breakdown: beta is non-finite");
436
437 p = r + beta * (p - omega * v);
438 auto p_hat = preconditioner(p);
440 v = A.matmul(p_hat);
441
442 auto alpha_denominator = torch::dot(r_hat, v);
444 alpha_denominator,
445 "PBiCGStab numerical breakdown: alpha denominator is zero or non-finite");
446 alpha = rho_hat / alpha_denominator;
448 alpha, "PBiCGStab numerical breakdown: alpha is non-finite");
449 auto s = r - alpha * v;
450
451 const auto s_residual = s.norm().item<double>();
452 if (s_residual < tol) {
453 x += alpha * p_hat;
454 return std::make_tuple(x, iter, s_residual);
455 }
456
457 auto s_hat = preconditioner(s);
459 auto t = A.matmul(s_hat);
460 auto omega_denominator = torch::dot(t, t);
462 omega_denominator,
463 "PBiCGStab numerical breakdown: t.t is zero or non-finite");
464 omega = torch::dot(s, t) / omega_denominator;
466 omega, "PBiCGStab numerical breakdown: omega is zero or non-finite");
467 x += alpha * p_hat + omega * s_hat;
468 r = s - omega * t;
470 r, "PBiCGStab numerical breakdown: residual is non-finite");
471 rho = rho_hat;
472 }
473
474 return std::make_tuple(x, max_iter, r.norm().item<double>());
475 }
476
484 inline auto pbicgstab(
485 const torch::Tensor &A, const torch::Tensor b,
486 const torch::Tensor &inverse_preconditioner, int max_iter = 1000,
487 double tol = 1e-10) {
488 detail::validate_inverse_preconditioner(inverse_preconditioner, A);
489 auto apply = [&inverse_preconditioner](const torch::Tensor &residual) {
490 return inverse_preconditioner.matmul(residual);
491 };
492 return pbicgstab(A, b, apply, max_iter, tol);
493 }
494
506 template <IterativeSolverPreconditioner Preconditioner>
507 inline auto pminres(const torch::Tensor &A, const torch::Tensor b,
508 Preconditioner &&preconditioner,
509 int max_iter = 1000, double tol = 1e-10) {
510 detail::validate_iterative_solver_inputs(A, b, max_iter, tol);
511
512 auto x = torch::zeros_like(b);
513 auto residual = b.norm().item<double>();
514 if (residual < tol)
515 return std::make_tuple(x, -1, residual);
516 if (max_iter == 0)
517 return std::make_tuple(x, 0, residual);
518
519 auto r1 = b.clone();
520 auto r2 = r1.clone();
521 auto y = preconditioner(r1);
523 auto beta_squared = torch::dot(r1, y);
525 beta_squared,
526 "MINRES requires a symmetric positive-definite preconditioner");
527 auto beta = torch::sqrt(beta_squared);
528 auto old_beta = torch::zeros_like(beta);
529 auto dbar = torch::zeros_like(beta);
530 auto epsilon = torch::zeros_like(beta);
531 auto cosine = -torch::ones_like(beta);
532 auto sine = torch::zeros_like(beta);
533 auto phibar = beta.clone();
534 auto w = torch::zeros_like(b);
535 auto w_older = torch::zeros_like(b);
536
537 for (int iter = 0; iter < max_iter; ++iter) {
538 auto v = y / beta;
539 y = A.matmul(v);
540 if (iter > 0)
541 y -= (beta / old_beta) * r1;
542 auto alpha = torch::dot(v, y);
543 y -= (alpha / beta) * r2;
544 r1 = r2;
545 r2 = y;
546 y = preconditioner(r2);
548
549 old_beta = beta;
550 beta_squared = torch::dot(r2, y);
552 beta_squared,
553 "MINRES requires a symmetric positive-definite preconditioner");
554 beta = torch::sqrt(torch::clamp_min(beta_squared, 0.0));
555
556 auto old_epsilon = epsilon;
557 auto delta = cosine * dbar + sine * alpha;
558 auto gbar = sine * dbar - cosine * alpha;
559 epsilon = sine * beta;
560 dbar = -cosine * beta;
561 auto gamma = torch::sqrt(gbar * gbar + beta * beta);
563 gamma, "MINRES numerical breakdown: rotation norm is zero or non-finite");
564 cosine = gbar / gamma;
565 sine = beta / gamma;
566 auto phi = cosine * phibar;
567 phibar = sine * phibar;
568
569 auto w_old = w;
570 w = (v - old_epsilon * w_older - delta * w_old) / gamma;
571 w_older = w_old;
572 x += phi * w;
573
574 if (phibar.abs().template item<double>() < tol) {
575 residual = (b - A.matmul(x)).norm().item<double>();
576 if (residual < tol)
577 return std::make_tuple(x, iter, residual);
578 }
579 }
580
581 residual = (b - A.matmul(x)).norm().item<double>();
582 return std::make_tuple(x, max_iter, residual);
583 }
584
592 inline auto pminres(const torch::Tensor &A, const torch::Tensor b,
593 const torch::Tensor &inverse_preconditioner,
594 int max_iter = 1000, double tol = 1e-10) {
595 detail::validate_inverse_preconditioner(inverse_preconditioner, A);
596 auto apply = [&inverse_preconditioner](const torch::Tensor &residual) {
597 return inverse_preconditioner.matmul(residual);
598 };
599 return pminres(A, b, apply, max_iter, tol);
600 }
601
608 inline auto minres(const torch::Tensor &A, const torch::Tensor b,
609 int max_iter = 1000, double tol = 1e-10) {
610 auto identity = [](const torch::Tensor &residual) {
611 return residual.clone();
612 };
613 return pminres(A, b, identity, max_iter, tol);
614 }
615
628 template <IterativeSolverPreconditioner Preconditioner>
629 inline auto fgmres(const torch::Tensor &A, const torch::Tensor b,
630 Preconditioner &&preconditioner,
631 int max_iter = 1000, double tol = 1e-10,
632 int restart = 30) {
633 detail::validate_iterative_solver_inputs(A, b, max_iter, tol);
635
636 auto x = torch::zeros_like(b);
637 auto r = b.clone();
638 auto residual = r.norm().item<double>();
639 if (residual < tol)
640 return std::make_tuple(x, -1, residual);
641 if (max_iter == 0)
642 return std::make_tuple(x, 0, residual);
643
644 int iterations = 0;
645 while (iterations < max_iter) {
646 const int cycle_size = std::min(restart, max_iter - iterations);
647 auto beta = r.norm();
648 std::vector<torch::Tensor> basis;
649 std::vector<torch::Tensor> preconditioned_basis;
650 std::vector<torch::Tensor> cosines;
651 std::vector<torch::Tensor> sines;
652 basis.reserve(cycle_size + 1);
653 preconditioned_basis.reserve(cycle_size);
654 cosines.reserve(cycle_size);
655 sines.reserve(cycle_size);
656 basis.emplace_back(r / beta);
657
658 auto hessenberg = torch::zeros(
659 {cycle_size + 1, cycle_size}, b.options());
660 auto transformed_rhs = torch::zeros({cycle_size + 1}, b.options());
661 transformed_rhs.index_put_({0}, beta);
662
663 int inner_steps = 0;
664 bool estimated_convergence = false;
665 for (int j = 0; j < cycle_size; ++j) {
666 auto z = preconditioner(basis[j]);
668 preconditioned_basis.emplace_back(z);
669 auto w = A.matmul(z);
670
671 for (int i = 0; i <= j; ++i) {
672 auto coefficient = torch::dot(basis[i], w);
673 hessenberg.index_put_({i, j}, coefficient);
674 w -= coefficient * basis[i];
675 }
676
677 auto next_norm = w.norm();
678 hessenberg.index_put_({j + 1, j}, next_norm);
679 const bool happy_breakdown = next_norm.template item<double>() == 0.0;
680 if (!happy_breakdown)
681 basis.emplace_back(w / next_norm);
682
683 for (int i = 0; i < j; ++i) {
684 auto upper = hessenberg.index({i, j}).clone();
685 auto lower = hessenberg.index({i + 1, j}).clone();
686 hessenberg.index_put_({i, j},
687 cosines[i] * upper + sines[i] * lower);
688 hessenberg.index_put_({i + 1, j},
689 -sines[i] * upper + cosines[i] * lower);
690 }
691
692 auto diagonal = hessenberg.index({j, j}).clone();
693 auto subdiagonal = hessenberg.index({j + 1, j}).clone();
694 auto rotation_norm = torch::sqrt(diagonal * diagonal +
695 subdiagonal * subdiagonal);
697 rotation_norm,
698 "GMRES numerical breakdown: Givens rotation norm is zero or non-finite");
699 auto cosine = diagonal / rotation_norm;
700 auto sine = subdiagonal / rotation_norm;
701 cosines.emplace_back(cosine);
702 sines.emplace_back(sine);
703 hessenberg.index_put_({j, j},
704 cosine * diagonal + sine * subdiagonal);
705 hessenberg.index_put_({j + 1, j}, torch::zeros_like(subdiagonal));
706
707 auto rhs_entry = transformed_rhs.index({j}).clone();
708 auto rhs_next = transformed_rhs.index({j + 1}).clone();
709 transformed_rhs.index_put_({j},
710 cosine * rhs_entry + sine * rhs_next);
711 transformed_rhs.index_put_({j + 1},
712 -sine * rhs_entry + cosine * rhs_next);
713
714 ++iterations;
715 inner_steps = j + 1;
716 residual = transformed_rhs.index({j + 1}).abs().item<double>();
717 if (residual < tol || happy_breakdown) {
718 estimated_convergence = true;
719 break;
720 }
721 }
722
723 using torch::indexing::Slice;
724 auto upper = hessenberg.index(
725 {Slice(0, inner_steps), Slice(0, inner_steps)});
726 auto rhs = transformed_rhs.index({Slice(0, inner_steps)}).unsqueeze(1);
727 auto coefficients =
728 torch::linalg_solve_triangular(upper, rhs, true).squeeze(1);
729 for (int i = 0; i < inner_steps; ++i)
730 x += coefficients.index({i}) * preconditioned_basis[i];
731
732 r = b - A.matmul(x);
733 residual = r.norm().item<double>();
734 if (residual < tol)
735 return std::make_tuple(x, iterations - 1, residual);
736
737 if (estimated_convergence)
739 r, "GMRES numerical breakdown: true residual is non-finite");
740 }
741
742 return std::make_tuple(x, max_iter, residual);
743 }
744
753 inline auto fgmres(const torch::Tensor &A, const torch::Tensor b,
754 const torch::Tensor &inverse_preconditioner,
755 int max_iter = 1000, double tol = 1e-10,
756 int restart = 30) {
757 detail::validate_inverse_preconditioner(inverse_preconditioner, A);
758 auto apply = [&inverse_preconditioner](const torch::Tensor &residual) {
759 return inverse_preconditioner.matmul(residual);
760 };
761 return fgmres(A, b, apply, max_iter, tol, restart);
762 }
763
771 inline auto gmres(const torch::Tensor &A, const torch::Tensor b,
772 int max_iter = 1000, double tol = 1e-10,
773 int restart = 30) {
774 auto identity = [](const torch::Tensor &residual) {
775 return residual.clone();
776 };
777 return fgmres(A, b, identity, max_iter, tol, restart);
778 }
779} // namespace iganet::utils
Specifies the callable interface required by the preconditioned iterative solvers.
Definition solver.hpp:29
Core components.
void validate_gmres_parameters(int restart)
Provides the validate_gmres_parameters operation.
Definition solver.hpp:126
void check_finite_tensor(const torch::Tensor &value, const char *message)
Provides the check_finite_tensor operation.
Definition solver.hpp:85
void check_finite_scalar(const torch::Tensor &value, const char *message)
Provides the check_finite_scalar operation.
Definition solver.hpp:77
void validate_iterative_solver_inputs(const torch::Tensor &A, const torch::Tensor &b, int max_iter, double tol)
Provides the validate_iterative_solver_inputs operation.
Definition solver.hpp:52
void check_nonnegative_finite_scalar(const torch::Tensor &value, const char *message)
Provides the check_nonnegative_finite_scalar operation.
Definition solver.hpp:151
void validate_preconditioner_output(const torch::Tensor &value, const torch::Tensor &residual)
Provides the validate_preconditioner_output operation.
Definition solver.hpp:93
void validate_inverse_preconditioner(const torch::Tensor &inverse_preconditioner, const torch::Tensor &A)
Provides the validate_inverse_preconditioner operation.
Definition solver.hpp:110
void check_nonzero_finite_scalar(const torch::Tensor &value, const char *message)
Provides the check_nonzero_finite_scalar operation.
Definition solver.hpp:133
bool tensor_values_are_finite(const torch::Tensor &tensor)
Provides the tensor_values_are_finite operation.
Definition solver.hpp:40
void check_positive_finite_scalar(const torch::Tensor &value, const char *message)
Provides the check_positive_finite_scalar operation.
Definition solver.hpp:142
Definition blocktensor.hpp:24
auto pcg(const torch::Tensor &A, const torch::Tensor b, Preconditioner &&preconditioner, int max_iter=1000, double tol=1e-10)
Solves the linear system A * x = b using the preconditioned Conjugate Gradient (PCG) method.
Definition solver.hpp:248
auto pbicgstab(const torch::Tensor &A, const torch::Tensor b, Preconditioner &&preconditioner, int max_iter=1000, double tol=1e-10)
Solves the linear system A * x = b using the preconditioned Bi-Conjugate Gradient Stabilized (PBiCGSt...
Definition solver.hpp:407
auto cg(const torch::Tensor &A, const torch::Tensor b, int max_iter=1000, double tol=1e-10)
Solves the linear system A * x = b using the Conjugate Gradient (CG) method.
Definition solver.hpp:192
auto minres(const torch::Tensor &A, const torch::Tensor b, int max_iter=1000, double tol=1e-10)
Solves A * x = b using MINRES.
Definition solver.hpp:608
auto gmres(const torch::Tensor &A, const torch::Tensor b, int max_iter=1000, double tol=1e-10, int restart=30)
Solves A * x = b using restarted GMRES.
Definition solver.hpp:771
auto bicgstab(const torch::Tensor &A, const torch::Tensor b, int max_iter=1000, double tol=1e-10)
Solves the linear system A * x = b using the Bi-Conjugate Gradient Stabilized (BiCGStab) method.
Definition solver.hpp:323
auto pminres(const torch::Tensor &A, const torch::Tensor b, Preconditioner &&preconditioner, int max_iter=1000, double tol=1e-10)
Solves A * x = b using preconditioned MINRES.
Definition solver.hpp:507
auto abs(const BlockTensor< T, Dims... > &input)
Returns a new block tensor with the absolute value of the elements of input.
Definition blocktensor.hpp:1250
auto fgmres(const torch::Tensor &A, const torch::Tensor b, Preconditioner &&preconditioner, int max_iter=1000, double tol=1e-10, int restart=30)
Solves A * x = b using restarted, right-preconditioned GMRES.
Definition solver.hpp:629