5#ifndef LINE_API_MC_CTMC_GMRES_H
6#define LINE_API_MC_CTMC_GMRES_H
88constexpr double GMRES_ILUT_DROP_TOL = 1e-4;
90constexpr double GMRES_ILUT_FILL_FACTOR = 10.0;
92constexpr double GMRES_BREAKDOWN_TOL = 1e-14;
93constexpr double GMRES_DEFAULT_TOL = 1e-12;
94constexpr long GMRES_DEFAULT_RESTART = 50;
102inline bool num_isfinite(
const T& v) {
115 std::vector<std::size_t> rowPtr, colIdx;
117 std::vector<long> diagPtr;
120 diagPtr.assign(n, -1);
121 for (std::size_t i = 0; i < n; ++i)
122 for (std::size_t p = rowPtr[i]; p < rowPtr[i + 1]; ++p)
123 if (colIdx[p] == i) {
124 diagPtr[i] =
static_cast<long>(p);
129 static CsrMatrix of(
const Matrix<T>& A) {
132 const T zero = num_traits<T>::from_int(0);
133 c.rowPtr.assign(c.n + 1, 0);
134 for (std::size_t i = 0; i < c.n; ++i) {
136 for (std::size_t j = 0; j < A.cols(); ++j)
137 if (A(i, j) != zero) ++cnt;
138 c.rowPtr[i + 1] = c.rowPtr[i] + cnt;
140 c.colIdx.resize(c.rowPtr[c.n]);
141 c.val.resize(c.rowPtr[c.n]);
143 for (std::size_t i = 0; i < c.n; ++i)
144 for (std::size_t j = 0; j < A.cols(); ++j)
145 if (A(i, j) != zero) {
154 void mult(
const std::vector<T>& v, std::vector<T>& out)
const {
155 for (std::size_t i = 0; i < n; ++i) {
156 T s = num_traits<T>::from_int(0);
157 for (std::size_t p = rowPtr[i]; p < rowPtr[i + 1]; ++p) s += val[p] * v[colIdx[p]];
163 std::vector<T> equilibrate() {
164 std::vector<T> scale(n, num_traits<T>::from_int(1));
165 for (std::size_t i = 0; i < n; ++i) {
166 T m = num_traits<T>::from_int(0);
167 for (std::size_t p = rowPtr[i]; p < rowPtr[i + 1]; ++p) {
168 const T a =
num_abs(T(val[p]));
171 if (m == num_traits<T>::from_int(0))
continue;
173 if (m == num_traits<T>::from_int(1))
continue;
174 for (std::size_t p = rowPtr[i]; p < rowPtr[i + 1]; ++p) val[p] /= m;
180 CsrMatrix permute_symmetric(
const std::vector<std::size_t>& perm,
181 const std::vector<std::size_t>& iperm)
const {
184 c.rowPtr.assign(n + 1, 0);
185 for (std::size_t i = 0; i < n; ++i) {
186 const std::size_t oi = perm[i];
187 c.rowPtr[i + 1] = c.rowPtr[i] + (rowPtr[oi + 1] - rowPtr[oi]);
189 c.colIdx.resize(rowPtr[n]);
190 c.val.resize(rowPtr[n]);
191 std::vector<std::pair<std::size_t, T>> row;
192 for (std::size_t i = 0; i < n; ++i) {
193 const std::size_t oi = perm[i];
195 for (std::size_t p = rowPtr[oi]; p < rowPtr[oi + 1]; ++p)
196 row.push_back(std::make_pair(iperm[colIdx[p]], val[p]));
197 std::sort(row.begin(), row.end(),
198 [](
const std::pair<std::size_t, T>& a,
const std::pair<std::size_t, T>& b) {
199 return a.first < b.first;
201 std::size_t base = c.rowPtr[i];
202 for (std::size_t k = 0; k < row.size(); ++k) {
203 c.colIdx[base + k] = row[k].first;
204 c.val[base + k] = row[k].second;
219std::vector<std::size_t> rcm_order(
const CsrMatrix<T>& a) {
220 const std::size_t n = a.n;
221 std::vector<std::size_t> deg(n, 0);
222 for (std::size_t i = 0; i < n; ++i)
223 for (std::size_t p = a.rowPtr[i]; p < a.rowPtr[i + 1]; ++p) {
224 const std::size_t j = a.colIdx[p];
225 if (j == i)
continue;
229 std::vector<std::size_t> adjPtr(n + 1, 0);
230 for (std::size_t i = 0; i < n; ++i) adjPtr[i + 1] = adjPtr[i] + deg[i];
231 std::vector<std::size_t> adj(adjPtr[n]), fill(adjPtr.begin(), adjPtr.begin() + n);
232 for (std::size_t i = 0; i < n; ++i)
233 for (std::size_t p = a.rowPtr[i]; p < a.rowPtr[i + 1]; ++p) {
234 const std::size_t j = a.colIdx[p];
235 if (j == i)
continue;
242 std::vector<char> seen(n, 0);
243 std::vector<std::size_t> result(n), queue(n);
244 std::size_t count = 0;
246 std::size_t start = n;
247 for (std::size_t i = 0; i < n; ++i)
248 if (!seen[i] && (start == n || deg[i] < deg[start])) start = i;
249 std::size_t head = count, tail = count;
250 queue[tail++] = start;
252 while (head < tail) {
253 const std::size_t v = queue[head++];
255 const std::size_t from = tail;
256 for (std::size_t p = adjPtr[v]; p < adjPtr[v + 1]; ++p) {
257 const std::size_t u = adj[p];
263 std::stable_sort(queue.begin() + from, queue.begin() + tail,
264 [°](std::size_t x, std::size_t y) { return deg[x] < deg[y]; });
267 std::vector<std::size_t> perm(n);
268 for (std::size_t i = 0; i < n; ++i) perm[i] = result[n - 1 - i];
277 std::vector<std::size_t> lPtr, lCol, uPtr, uCol;
278 std::vector<T> lVal, uVal, dInv;
281 void solve(
const std::vector<T>& v, std::vector<T>& out)
const {
282 for (std::size_t i = 0; i < n; ++i) {
284 for (std::size_t p = lPtr[i]; p < lPtr[i + 1]; ++p) s -= lVal[p] * out[lCol[p]];
287 for (std::size_t i = n; i-- > 0;) {
289 for (std::size_t p = uPtr[i]; p < uPtr[i + 1]; ++p) s -= uVal[p] * out[uCol[p]];
290 out[i] = s * dInv[i];
295 static std::size_t keep_largest(std::vector<std::size_t>& cols,
const std::vector<T>& w,
296 std::size_t len, std::size_t keep) {
297 if (len <= keep)
return len;
298 for (std::size_t i = 0; i < keep; ++i) {
299 std::size_t best = i;
300 for (std::size_t j = i + 1; j < len; ++j)
302 std::swap(cols[i], cols[best]);
307 static Ilut factorize(
const CsrMatrix<T>& a,
double dropTol,
double fillFactor) {
309 const std::size_t n = a.n;
311 const T zero = num_traits<T>::from_int(0);
312 const std::size_t nnz = a.rowPtr[n];
313 std::size_t lfil =
static_cast<std::size_t
>(
314 std::ceil(fillFactor *
static_cast<double>(nnz) /
static_cast<double>(n == 0 ? 1 : n)));
315 if (lfil < 1) lfil = 1;
316 const T dropT = num_traits<T>::from_double(dropTol);
318 f.lPtr.assign(n + 1, 0);
319 f.uPtr.assign(n + 1, 0);
320 f.dInv.assign(n, zero);
322 std::vector<T> w(n, zero);
323 std::vector<long> wPos(n, -1);
324 std::vector<std::size_t> wIdx(n), rowsL(n), rowsU(n);
325 std::size_t wCount = 0;
326 std::priority_queue<std::size_t, std::vector<std::size_t>, std::greater<std::size_t>> pending;
328 for (std::size_t i = 0; i < n; ++i) {
330 const std::size_t rowLen = a.rowPtr[i + 1] - a.rowPtr[i];
331 for (std::size_t p = a.rowPtr[i]; p < a.rowPtr[i + 1]; ++p) {
332 const std::size_t j = a.colIdx[p];
334 wPos[j] =
static_cast<long>(wCount);
337 if (j < i) pending.push(j);
339 if (rowLen == 0 || tnorm == zero) {
343 const T tau = dropT * tnorm / num_traits<T>::from_int(
static_cast<long>(rowLen));
345 while (!pending.empty()) {
346 const std::size_t k = pending.top();
348 const T mult = w[k] * f.dInv[k];
349 if (!(
num_abs(T(mult)) > tau)) {
354 for (std::size_t p = f.uPtr[k]; p < f.uPtr[k + 1]; ++p) {
355 const std::size_t j = f.uCol[p];
356 const T upd = mult * f.uVal[p];
360 if (!(
num_abs(T(upd)) > tau))
continue;
362 wPos[j] =
static_cast<long>(wCount);
364 if (j < i) pending.push(j);
369 std::size_t nl = 0, nu = 0;
371 for (std::size_t t = 0; t < wCount; ++t) {
372 const std::size_t j = wIdx[t];
373 if (j == i)
continue;
374 if (!(
num_abs(T(w[j])) > tau))
continue;
380 nl = keep_largest(rowsL, w, nl, lfil);
381 nu = keep_largest(rowsU, w, nu, lfil);
382 std::sort(rowsL.begin(), rowsL.begin() + nl);
383 std::sort(rowsU.begin(), rowsU.begin() + nu);
385 for (std::size_t t = 0; t < nl; ++t) {
386 f.lCol.push_back(rowsL[t]);
387 f.lVal.push_back(w[rowsL[t]]);
389 for (std::size_t t = 0; t < nu; ++t) {
390 f.uCol.push_back(rowsU[t]);
391 f.uVal.push_back(w[rowsU[t]]);
393 f.lPtr[i + 1] = f.lCol.size();
394 f.uPtr[i + 1] = f.uCol.size();
397 if (!(
num_abs(T(diag)) > tau) || !(diag == diag)) {
398 const T substitute = tau > zero ? tau : num_traits<T>::from_double(1e-8);
399 diag = diag < zero ? T(-substitute) : substitute;
401 f.dInv[i] = num_traits<T>::from_int(1) / diag;
402 if (!num_isfinite(f.dInv[i]))
return f;
404 for (std::size_t t = 0; t < wCount; ++t) {
409 while (!pending.empty()) pending.pop();
422 static Precond of(
const CsrMatrix<T>& a) {
424 m.lu = Ilut<T>::factorize(a, GMRES_ILUT_DROP_TOL, GMRES_ILUT_FILL_FACTOR);
425 if (m.lu.ok)
return m;
426 const T zero = num_traits<T>::from_int(0);
427 m.dinv.assign(a.n, num_traits<T>::from_int(1));
428 for (std::size_t i = 0; i < a.n; ++i) {
429 const T d = a.diagPtr[i] < 0 ? zero : a.val[
static_cast<std::size_t
>(a.diagPtr[i])];
430 m.dinv[i] = (d == zero) ? num_traits<T>::from_int(1) : T(num_traits<T>::from_int(1) / d);
435 void apply(
const std::vector<T>& v, std::vector<T>& out)
const {
437 for (std::size_t i = 0; i < dinv.size(); ++i) out[i] = dinv[i] * v[i];
446struct GmresPrepared {
449 std::vector<std::size_t> perm;
450 std::vector<T> rowScale;
453 explicit GmresPrepared(
const Matrix<T>& A) {
455 if (A.cols() != n)
throw InputError(
"ctmc_gmres: matrix is not square");
456 CsrMatrix<T> c = CsrMatrix<T>::of(A);
457 rowScale = c.equilibrate();
459 std::vector<std::size_t> iperm(n);
460 for (std::size_t i = 0; i < n; ++i) iperm[perm[i]] = i;
461 csr = c.permute_symmetric(perm, iperm);
462 M = Precond<T>::of(csr);
467T vec_norm2(
const std::vector<T>& v) {
468 T s = num_traits<T>::from_int(0);
469 for (
const T& x : v) s += x * x;
475T vec_dot(
const std::vector<T>& a,
const std::vector<T>& b) {
476 T s = num_traits<T>::from_int(0);
477 for (std::size_t i = 0; i < a.size(); ++i) s += a[i] * b[i];
483GmresResult<T> gmres_solve(
const GmresPrepared<T>& prep,
const std::vector<T>& rhsIn,
484 const std::vector<T>& x0In,
double tol,
long restart,
long maxit) {
485 const std::size_t n = prep.n;
486 const T zero = num_traits<T>::from_int(0);
487 const T one = num_traits<T>::from_int(1);
488 if (tol <= 0.0) tol = GMRES_DEFAULT_TOL;
489 if (restart <= 0) restart = std::min(
static_cast<long>(n), GMRES_DEFAULT_RESTART);
490 restart = std::min(restart,
static_cast<long>(n));
492 maxit =
static_cast<long>(std::ceil(
static_cast<double>(n) /
static_cast<double>(restart)));
493 maxit = std::max(1L, std::min(maxit,
static_cast<long>(n)));
494 const std::size_t m =
static_cast<std::size_t
>(restart);
495 const T tolT = num_traits<T>::from_double(tol);
499 std::vector<T> rhs(n), x(n);
500 for (std::size_t i = 0; i < n; ++i) rhs[i] = rhsIn[prep.perm[i]] / prep.rowScale[prep.perm[i]];
501 for (std::size_t i = 0; i < n; ++i) x[i] = x0In[prep.perm[i]];
503 T bnorm = vec_norm2(rhs);
504 if (bnorm == zero) bnorm = one;
506 std::vector<T> r(n), w(n), z(n), corr(n);
508 for (std::size_t i = 0; i < n; ++i) r[i] = rhs[i] - r[i];
509 T beta = vec_norm2(r);
514 out.relres = beta / bnorm;
515 if (out.relres <= tolT) {
517 out.x.assign(n, zero);
518 for (std::size_t i = 0; i < n; ++i) out.x[prep.perm[i]] = x[i];
522 std::vector<std::vector<T>> V(m + 1, std::vector<T>(n, zero));
523 std::vector<std::vector<T>> H(m + 1, std::vector<T>(m, zero));
524 std::vector<T> cs(m, zero), sn(m, zero), g(m + 1, zero), y(m, zero);
525 const T breakTol = num_traits<T>::from_double(GMRES_BREAKDOWN_TOL);
526 const T initres = out.relres;
527 bool havePrev =
false;
530 for (
long cycle = 0; cycle < maxit; ++cycle) {
537 for (std::size_t i = 0; i < n; ++i) V[0][i] = r[i] / beta;
538 std::fill(g.begin(), g.end(), zero);
542 for (std::size_t j = 0; j < m; ++j) {
544 prep.M.apply(V[j], z);
548 const T wnorm0 = vec_norm2(w);
550 for (
int pass = 0; pass < 2; ++pass)
551 for (std::size_t i = 0; i <= j; ++i) {
552 const T hij = vec_dot(V[i], w);
554 for (std::size_t q = 0; q < n; ++q) w[q] -= hij * V[i][q];
556 const T hnext = vec_norm2(w);
560 const bool breakdown = !(hnext > breakTol * wnorm0);
562 for (std::size_t q = 0; q < n; ++q) V[j + 1][q] = w[q] / hnext;
566 for (std::size_t i = 0; i < j; ++i) {
567 const T t1 = cs[i] * H[i][j] + sn[i] * H[i + 1][j];
568 H[i + 1][j] = -sn[i] * H[i][j] + cs[i] * H[i + 1][j];
572 const T denom = sqrt(T(H[j][j] * H[j][j] + H[j + 1][j] * H[j + 1][j]));
577 cs[j] = H[j][j] / denom;
578 sn[j] = H[j + 1][j] / denom;
580 H[j][j] = cs[j] * H[j][j] + sn[j] * H[j + 1][j];
582 g[j + 1] = -sn[j] * g[j];
585 out.relres =
num_abs(T(g[j + 1])) / bnorm;
586 if (out.relres <= tolT || breakdown)
break;
591 for (std::size_t i = k; i-- > 0;) {
593 for (std::size_t q = i + 1; q < k; ++q) s -= H[i][q] * y[q];
594 y[i] = (H[i][i] == zero) ? zero : T(s / H[i][i]);
596 std::fill(corr.begin(), corr.end(), zero);
597 for (std::size_t i = 0; i < k; ++i)
598 for (std::size_t q = 0; q < n; ++q) corr[q] += y[i] * V[i][q];
599 prep.M.apply(corr, z);
600 for (std::size_t q = 0; q < n; ++q) x[q] += z[q];
603 for (std::size_t q = 0; q < n; ++q) r[q] = rhs[q] - r[q];
604 out.relres = vec_norm2(r) / bnorm;
606 for (std::size_t i = 0; i <= m; ++i) std::fill(H[i].begin(), H[i].end(), zero);
608 if (out.relres <= tolT) {
613 if (!(out.relres < num_traits<T>::from_int(100) * initres)) {
619 if (havePrev && out.relres >= prevrelres * (one - num_traits<T>::from_double(1e-12))) {
623 prevrelres = out.relres;
627 out.x.assign(n, zero);
628 for (std::size_t i = 0; i < n; ++i) out.x[prep.perm[i]] = x[i];
629 for (std::size_t i = 0; i < n; ++i)
630 if (!num_isfinite(out.x[i])) {
632 out.relres = num_traits<T>::from_double(1e300);
635 if (out.relres <= tolT) out.flag = 0;
654 long restart = 0,
long maxit = 0,
const std::vector<T>& x0 = std::vector<T>()) {
656 "ctmc_gmres requires transcendental arithmetic: the Arnoldi step normalizes by a "
657 "Euclidean norm and the iteration stops on a residual tolerance, so there is no "
658 "exact result to converge to; use ctmc_solve for an exact solve");
659 const std::size_t n = A.
rows();
660 if (A.
cols() != n)
throw InputError(
"ctmc_gmres: matrix is not square");
661 if (b.size() != n)
throw InputError(
"ctmc_gmres: right-hand side has the wrong length");
662 if (!x0.empty() && x0.size() != n)
throw InputError(
"ctmc_gmres: initial guess has the wrong length");
663 std::vector<T> guess = x0;
666 const detail::GmresPrepared<T> prep(A);
667 return detail::gmres_solve(prep, b, guess, tol, restart, maxit);
The exception types the port throws.
Dense matrix and non-owning view.
constexpr std::size_t GMRES_MIN_STATES
Order above which the direct sparse factorization is abandoned in favour of the Krylov path.
GmresResult< T > ctmc_gmres(const Matrix< T > &A, const std::vector< T > &b, double tol=1e-12, long restart=0, long maxit=0, const std::vector< T > &x0=std::vector< T >())
Restarted GMRES with an ILUT preconditioner, for the linear systems a generator produces.
Number-type abstraction for the templated API port.
long iter
total inner iterations
int flag
0 converged, 1 iteration limit, 3 stagnation/divergence
T relres
true relative residual norm(b - A x) / norm(b)
std::vector< T > x
solution