numerics 0.1.0
Loading...
Searching...
No Matches
expv.cpp
Go to the documentation of this file.
1/// @file expv.cpp
2/// @brief Implementation of Krylov-Pade matrix exponential-vector product
4#include "kernel/subspace.hpp"
5#include "core/matrix.hpp"
6#include "core/vector.hpp"
8#include <algorithm>
9#include <cmath>
10#include <vector>
11
12namespace num {
13
14/// @brief Dense matrix exponential via scaling+squaring + Pade [6/6]
15///
16/// Pade [6/6] numerator/denominator coefficients:
17/// c[0]=1, c[1]=1/2, c[2]=5/44, c[3]=1/66,
18/// c[4]=1/792, c[5]=1/15840, c[6]=1/665280
19static Matrix dense_expm(const Matrix &A) {
20 const idx m = A.rows();
21
22 // Pade [6/6] coefficients
23 static constexpr double c[7] = {1.0, 0.5, 5.0 / 44.0,
24 1.0 / 66.0, 1.0 / 792.0, 1.0 / 15840.0,
25 1.0 / 665280.0};
26
27 // 1. Compute inf-norm (max absolute row sum)
28 double norm_inf = 0.0;
29 for (idx i = 0; i < m; i++) {
30 double row_sum = 0.0;
31 for (idx j = 0; j < m; j++)
32 row_sum += std::abs(A(i, j));
33 norm_inf = std::max(norm_inf, row_sum);
34 }
35
36 // 2. Scaling: s = max(0, ceil(log2(norm_inf / 0.5)))
37 int s = 0;
38 if (norm_inf > 0.5) {
39 s = (int)std::max(0.0, std::ceil(std::log2(norm_inf / 0.5)));
40 }
41
42 // 3. As = A / 2^s
43 double scale = std::ldexp(1.0, -s); // 1/2^s
44 Matrix As(m, m, 0.0);
45 for (idx i = 0; i < m; i++)
46 for (idx j = 0; j < m; j++)
47 As(i, j) = A(i, j) * scale;
48
49 // 4. Build powers: B = As^2, B2 = As^4, B3 = As^6
50 Matrix B(m, m, 0.0);
51 matmul(As, As, B); // B = As^2
52
53 Matrix B2(m, m, 0.0);
54 matmul(B, B, B2); // B2 = As^4
55
56 Matrix B3(m, m, 0.0);
57 matmul(B2, B, B3); // B3 = As^6
58
59 // 5. V_mat = c[6]*B3 + c[4]*B2 + c[2]*B + c[0]*I (even terms)
60 Matrix V_mat(m, m, 0.0);
61 for (idx i = 0; i < m; i++) {
62 for (idx j = 0; j < m; j++) {
63 V_mat(i, j) = c[6] * B3(i, j) + c[4] * B2(i, j) + c[2] * B(i, j);
64 }
65 V_mat(i, i) += c[0];
66 }
67
68 // 6. W = c[5]*B2 + c[3]*B + c[1]*I (inner odd terms, without As factor)
69 Matrix W(m, m, 0.0);
70 for (idx i = 0; i < m; i++) {
71 for (idx j = 0; j < m; j++) {
72 W(i, j) = c[5] * B2(i, j) + c[3] * B(i, j);
73 }
74 W(i, i) += c[1];
75 }
76
77 // 7. U = As * W (full odd numerator terms)
78 Matrix U(m, m, 0.0);
79 matmul(As, W, U);
80
81 // 8. VpU = V_mat + U, VmU = V_mat - U
82 Matrix VpU(m, m, 0.0);
83 Matrix VmU(m, m, 0.0);
84 for (idx i = 0; i < m; i++) {
85 for (idx j = 0; j < m; j++) {
86 VpU(i, j) = V_mat(i, j) + U(i, j);
87 VmU(i, j) = V_mat(i, j) - U(i, j);
88 }
89 }
90
91 // 9. Solve VmU * E = VpU (Pade approximant: E = (V-U)^{-1}*(V+U))
92 LUResult fac = lu(VmU);
93 Matrix E(m, m, 0.0);
94 lu_solve(fac, VpU, E);
95
96 // 10. Squaring: E = E^(2^s)
97 for (int i = 0; i < s; i++) {
98 Matrix E2(m, m, 0.0);
99 matmul(E, E, E2);
100 E = std::move(E2);
101 }
102
103 return E;
104}
105
106Vector expv(real t, const MatVecFn &matvec, idx n, const Vector &v, int m_max,
107 real tol) {
108 // Handle zero vector
109 real beta = norm(v);
110 if (beta < 1e-300) {
111 return Vector(n, 0.0);
112 }
113
114 // Krylov basis vectors: V[0..m_max]
115 std::vector<Vector> V;
116 V.reserve(m_max + 1);
117
118 // V[0] = v / beta
119 Vector v0(n);
120 for (idx i = 0; i < n; i++)
121 v0[i] = v[i] / beta;
122 V.push_back(std::move(v0));
123
124 // Upper Hessenberg matrix H: (m_max+1) x m_max
125 Matrix H(m_max + 1, m_max, 0.0);
126
127 int m_actual = m_max;
128
129 // h_col: scratch buffer for one Hessenberg column; reused each Arnoldi step
130 std::vector<real> h_col(m_max + 1, 0.0);
131
132 // Arnoldi process
133 for (int j = 0; j < m_max; j++) {
134 Vector w(n, 0.0);
135 matvec(V[j], w);
136
137 // MGS: fills h_col[0..j], returns ||w|| after projections removed
138 const real h_next =
139 kernel::subspace::mgs_orthogonalize(V, w, h_col, j + 1);
140 for (int i = 0; i <= j; i++) { H(i, j) = h_col[i]; }
141 H(j + 1, j) = h_next;
142
143 if (h_next < tol) {
144 m_actual = j + 1;
145 break;
146 }
147
148 scale(w, real(1) / h_next);
149 V.push_back(std::move(w));
150 }
151
152 // Extract H_m = t * H[0..m_actual-1, 0..m_actual-1]
153 Matrix Hm(m_actual, m_actual, 0.0);
154 for (int i = 0; i < m_actual; i++) {
155 for (int j = 0; j < m_actual; j++) {
156 Hm(i, j) = t * H(i, j);
157 }
158 }
159
160 // Compute dense expm of Hm
161 Matrix E = dense_expm(Hm);
162
163 // result = sum_j (beta * E[j,0]) * V[j]
164 Vector result(n, 0.0);
165 for (int j = 0; j < m_actual; j++) {
166 real coeff = beta * E(j, 0);
167 axpy(coeff, V[j], result);
168 }
169
170 return result;
171}
172
173Vector expv(real t, const SparseMatrix &A, const Vector &v, int m_max,
174 real tol) {
175 MatVecFn fn = [&A](const Vector &x, Vector &y) { sparse_matvec(A, x, y); };
176 return expv(t, fn, A.n_rows(), v, m_max, tol);
177}
178
179} // namespace num
Dense row-major matrix with optional GPU storage.
Definition matrix.hpp:12
constexpr idx rows() const noexcept
Definition matrix.hpp:24
Sparse matrix in Compressed Sparse Row (CSR) format.
Definition sparse.hpp:15
idx n_rows() const
Definition sparse.hpp:33
Krylov subspace matrix exponential-vector product: compute exp(t*A)*v.
LU factorization with partial pivoting.
Matrix operations.
real mgs_orthogonalize(const std::vector< Vector > &basis, Vector &v, std::vector< real > &h, idx k)
Modified Gram-Schmidt: orthogonalize v against basis[0..k-1].
Definition subspace.cpp:32
double real
Definition types.hpp:10
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 scale(Vector &v, real alpha, Backend b=default_backend)
v *= alpha
Definition vector.cpp:29
void sparse_matvec(const SparseMatrix &A, const Vector &x, Vector &y)
y = A * x
Definition sparse.cpp:124
constexpr real e
Definition math.hpp:43
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
Definition vector.hpp:130
real norm(const Vector &x, Backend b=default_backend)
Euclidean norm.
Definition vector.cpp:97
void axpy(real alpha, const Vector &x, Vector &y, Backend b=default_backend)
y += alpha * x
Definition vector.cpp:58
void matmul(const Matrix &A, const Matrix &B, Matrix &C, Backend b=default_backend)
C = A * B.
Definition matrix.cpp:95
Vector expv(real t, const MatVecFn &matvec, idx n, const Vector &v, int m_max=30, real tol=1e-8)
Compute exp(t*A)*v via Krylov-Pade approximation (matrix-free)
Definition expv.cpp:106
void lu_solve(const LUResult &f, const Vector &b, Vector &x)
Solve A*x = b using a precomputed LU factorization.
Definition lu.cpp:28
LUResult lu(const Matrix &A, Backend backend=lapack_backend)
LU factorization of a square matrix A with partial pivoting.
Definition lu.cpp:17
Subspace construction and orthogonalization kernels. (namespace num::kernel::subspace)
Vector operations.