248 inline auto pcg(
const torch::Tensor &A,
const torch::Tensor b,
249 Preconditioner &&preconditioner,
250 int max_iter = 1000,
double tol = 1e-10) {
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);
258 return std::make_tuple(x, 0, initial_residual);
261 auto z = preconditioner(r);
264 auto rz = torch::dot(r, z);
266 rz,
"PCG numerical breakdown: r.z is zero or non-finite");
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");
280 const auto residual = r.norm().item<
double>();
282 return std::make_tuple(x, iter, residual);
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");
296 return std::make_tuple(x, max_iter, r.norm().item<
double>());
324 const torch::Tensor b,
326 double tol = 1e-10) {
330 auto x = torch::zeros_like(b);
332 if (b.norm().item<
double>() < tol)
333 return std::make_tuple(x, -1, b.norm().item<
double>());
336 auto r_hat = b.clone();
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());
342 auto p = torch::zeros_like(b);
343 auto v = torch::zeros_like(b);
345 for (
int iter = 0; iter < max_iter; iter++) {
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");
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");
359 p = r + beta * (p - omega * v);
362 auto alpha_denominator = torch::dot(r_hat, v);
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;
371 if (s.norm().item<
double>() < tol) {
373 return std::make_tuple(x, iter, s.norm().item<
double>());
376 auto t = A.matmul(s);
377 auto omega_denominator = torch::dot(t, t);
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;
387 r,
"BiCGStab numerical breakdown: residual is non-finite");
391 return std::make_tuple(x, max_iter, r.norm().item<
double>());
407 inline auto pbicgstab(
const torch::Tensor &A,
const torch::Tensor b,
408 Preconditioner &&preconditioner,
409 int max_iter = 1000,
double tol = 1e-10) {
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);
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);
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");
437 p = r + beta * (p - omega * v);
438 auto p_hat = preconditioner(p);
442 auto alpha_denominator = torch::dot(r_hat, v);
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;
451 const auto s_residual = s.norm().item<
double>();
452 if (s_residual < tol) {
454 return std::make_tuple(x, iter, s_residual);
457 auto s_hat = preconditioner(s);
459 auto t = A.matmul(s_hat);
460 auto omega_denominator = torch::dot(t, t);
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;
470 r,
"PBiCGStab numerical breakdown: residual is non-finite");
474 return std::make_tuple(x, max_iter, r.norm().item<
double>());
507 inline auto pminres(
const torch::Tensor &A,
const torch::Tensor b,
508 Preconditioner &&preconditioner,
509 int max_iter = 1000,
double tol = 1e-10) {
512 auto x = torch::zeros_like(b);
513 auto residual = b.norm().item<
double>();
515 return std::make_tuple(x, -1, residual);
517 return std::make_tuple(x, 0, residual);
520 auto r2 = r1.clone();
521 auto y = preconditioner(r1);
523 auto beta_squared = torch::dot(r1, y);
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);
537 for (
int iter = 0; iter < max_iter; ++iter) {
541 y -= (beta / old_beta) * r1;
542 auto alpha = torch::dot(v, y);
543 y -= (alpha / beta) * r2;
546 y = preconditioner(r2);
550 beta_squared = torch::dot(r2, y);
553 "MINRES requires a symmetric positive-definite preconditioner");
554 beta = torch::sqrt(torch::clamp_min(beta_squared, 0.0));
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;
566 auto phi = cosine * phibar;
567 phibar = sine * phibar;
570 w = (v - old_epsilon * w_older - delta * w_old) / gamma;
574 if (phibar.abs().template item<double>() < tol) {
575 residual = (b - A.matmul(x)).norm().item<
double>();
577 return std::make_tuple(x, iter, residual);
581 residual = (b - A.matmul(x)).norm().item<
double>();
582 return std::make_tuple(x, max_iter, residual);
629 inline auto fgmres(
const torch::Tensor &A,
const torch::Tensor b,
630 Preconditioner &&preconditioner,
631 int max_iter = 1000,
double tol = 1e-10,
636 auto x = torch::zeros_like(b);
638 auto residual = r.norm().item<
double>();
640 return std::make_tuple(x, -1, residual);
642 return std::make_tuple(x, 0, residual);
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);
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);
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);
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];
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);
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);
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);
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));
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);
716 residual = transformed_rhs.index({j + 1}).
abs().item<
double>();
717 if (residual < tol || happy_breakdown) {
718 estimated_convergence =
true;
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);
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];
733 residual = r.norm().item<
double>();
735 return std::make_tuple(x, iterations - 1, residual);
737 if (estimated_convergence)
739 r,
"GMRES numerical breakdown: true residual is non-finite");
742 return std::make_tuple(x, max_iter, residual);