numerics 0.1.0
Loading...
Searching...
No Matches
krylov.cpp
Go to the documentation of this file.
2#include "kernel/subspace.hpp"
3#include "core/vector.hpp"
4#include <algorithm>
5#include <cmath>
6#include <stdexcept>
7#include <vector>
8
9namespace num {
10
11// Core restarted GMRES implementation (matrix-free)
12SolverResult gmres(MatVecFn matvec_fn, idx n, const Vector &b, Vector &x,
13 real tol, idx max_iter, idx restart) {
14 if (x.size() != n || b.size() != n)
15 throw std::invalid_argument("Dimension mismatch in GMRES");
16
17 restart = std::min(restart, n);
18 SolverResult result{0, 0.0, false};
19
20 std::vector<Vector> V;
21 V.reserve(restart + 1);
22 std::vector<std::vector<real>> H(restart,
23 std::vector<real>(restart + 1, 0.0));
24 std::vector<real> cs(restart, 0.0);
25 std::vector<real> sn(restart, 0.0);
26 std::vector<real> g(restart + 1, 0.0);
27
28 // Wrap matvec_fn once; scratch buffer reused across all Arnoldi steps
29 auto A_op = kernel::subspace::make_op(
30 [&](const Vector& in, Vector& out) { matvec_fn(in, out); }, n);
31 Vector scratch(n);
32
33 idx total_iters = 0;
34
35 while (total_iters < max_iter) {
36 Vector r(n);
37 matvec_fn(x, r);
38 for (idx i = 0; i < n; ++i)
39 r[i] = b[i] - r[i];
40
41 real beta = 0.0;
42 for (idx i = 0; i < n; ++i)
43 beta += r[i] * r[i];
44 beta = std::sqrt(beta);
45
46 result.residual = beta;
47 if (beta < tol) {
48 result.converged = true;
49 break;
50 }
51
52 V.clear();
53 V.emplace_back(n);
54 for (idx i = 0; i < n; ++i)
55 V[0][i] = r[i] / beta;
56
57 for (auto &col : H)
58 std::fill(col.begin(), col.end(), 0.0);
59 std::fill(cs.begin(), cs.end(), 0.0);
60 std::fill(sn.begin(), sn.end(), 0.0);
61 std::fill(g.begin(), g.end(), 0.0);
62 g[0] = beta;
63
64 idx j = 0;
65 for (; j < restart && total_iters < max_iter; ++j, ++total_iters) {
66 result.iterations = total_iters + 1;
67
69 A_op, V, H[j], j, scratch, real(1e-15));
70
71 if (h_next < 1e-15) {
72 ++j;
73 break;
74 }
75
76 for (idx i = 0; i < j; ++i) {
77 real tmp = cs[i] * H[j][i] + sn[i] * H[j][i + 1];
78 H[j][i + 1] = -sn[i] * H[j][i] + cs[i] * H[j][i + 1];
79 H[j][i] = tmp;
80 }
81
82 real h0 = H[j][j], h1 = H[j][j + 1];
83 real denom = std::sqrt(h0 * h0 + h1 * h1);
84 if (denom < 1e-15) {
85 cs[j] = 1.0;
86 sn[j] = 0.0;
87 } else {
88 cs[j] = h0 / denom;
89 sn[j] = h1 / denom;
90 }
91
92 H[j][j] = cs[j] * h0 + sn[j] * h1;
93 H[j][j + 1] = 0.0;
94
95 g[j + 1] = -sn[j] * g[j];
96 g[j] = cs[j] * g[j];
97
98 result.residual = std::abs(g[j + 1]);
99 if (result.residual < tol) {
100 result.converged = true;
101 ++j;
102 break;
103 }
104 }
105
106 idx m = j;
107 std::vector<real> y(m, 0.0);
108 for (idx i = m; i > 0;) {
109 --i;
110 y[i] = g[i];
111 for (idx k = i + 1; k < m; ++k)
112 y[i] -= H[k][i] * y[k];
113 y[i] /= H[i][i];
114 }
115
116 for (idx i = 0; i < m; ++i)
117 for (idx k = 0; k < n; ++k)
118 x[k] += y[i] * V[i][k];
119
120 if (result.converged)
121 break;
122 }
123
124 return result;
125}
126
127// Sparse overload
128SolverResult gmres(const SparseMatrix &A, const Vector &b, Vector &x, real tol,
129 idx max_iter, idx restart) {
130 if (A.n_rows() != A.n_cols())
131 throw std::invalid_argument("GMRES requires a square matrix");
132 idx n = A.n_rows();
133 MatVecFn mv = [&](const Vector &in, Vector &out) {
134 out = Vector(n);
135 sparse_matvec(A, in, out);
136 };
137 return gmres(mv, n, b, x, tol, max_iter, restart);
138}
139
140// Dense overload -- wraps the matrix-free core with a backend-parameterized
141// matvec
142SolverResult gmres(const Matrix &A, const Vector &b, Vector &x, real tol,
143 idx max_iter, idx restart, Backend backend) {
144 if (A.rows() != A.cols())
145 throw std::invalid_argument("GMRES requires a square matrix");
146 idx n = A.rows();
147 MatVecFn mv = [&](const Vector &in, Vector &out) {
148 out = Vector(n);
149 matvec(A, in, out, backend);
150 };
151 return gmres(mv, n, b, x, tol, max_iter, restart);
152}
153
154} // namespace num
constexpr idx size() const noexcept
Definition vector.hpp:80
Dense row-major matrix with optional GPU storage.
Definition matrix.hpp:12
constexpr idx rows() const noexcept
Definition matrix.hpp:24
constexpr idx cols() const noexcept
Definition matrix.hpp:25
Sparse matrix in Compressed Sparse Row (CSR) format.
Definition sparse.hpp:15
idx n_cols() const
Definition sparse.hpp:36
idx n_rows() const
Definition sparse.hpp:33
Restarted GMRES – a Krylov subspace solver for general Ax = b.
CallableOp< F > make_op(F f, idx n)
Factory: wrap a callable as a stack-allocated CallableOp<F>.
Definition subspace.hpp:128
real arnoldi_step(const LinearOp &A, std::vector< Vector > &basis, std::vector< real > &h, idx k, Vector &scratch, real breakdown_tol=real(1e-14))
One Arnoldi step: expand the orthonormal basis by one vector.
Definition subspace.cpp:73
double real
Definition types.hpp:10
Backend
Selects which backend handles a linalg operation.
Definition policy.hpp:19
real beta(real a, real b)
B(a, b) – beta function.
Definition math.hpp:248
std::function< void(const Vector &, Vector &)> MatVecFn
Callable type for matrix-free matvec: computes y = A*x.
Definition cg.hpp:13
std::size_t idx
Definition types.hpp:11
void matvec(const Matrix &A, const Vector &x, Vector &y, Backend b=default_backend)
y = A * x
Definition matrix.cpp:120
void sparse_matvec(const SparseMatrix &A, const Vector &x, Vector &y)
y = A * x
Definition sparse.cpp:124
SolverResult gmres(MatVecFn matvec, idx n, const Vector &b, Vector &x, real tol=1e-6, idx max_iter=1000, idx restart=30)
Restarted GMRES(restart) – matrix-free interface.
Definition krylov.cpp:12
constexpr real e
Definition math.hpp:43
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
Definition vector.hpp:130
Subspace construction and orthogonalization kernels. (namespace num::kernel::subspace)
Vector operations.