132 const Eigen::VectorXd& b,
Index max_iter,
double tol,
Logger& log,
133 std::chrono::steady_clock::time_point t_start) {
134 auto elapsed_s = [&]() {
135 return std::chrono::duration<double>(std::chrono::steady_clock::now() -
141 Eigen::VectorXd x = Eigen::VectorXd::Zero(b.size());
142 Eigen::VectorXd residual_vec = b - op * x;
143 const double rhs_norm2 = b.squaredNorm();
144 const double threshold =
145 std::max(tol * tol * rhs_norm2, std::numeric_limits<double>::min());
146 double residual_norm2 = residual_vec.squaredNorm();
164 (rhs_norm2 > 0.0) ? std::sqrt(residual_norm2 / rhs_norm2) : 0.0;
165 bool converged = (residual_norm2 < threshold);
168 std::vector<double> alphas;
169 std::vector<double> betas;
172 Eigen::VectorXd p = precond.solve(residual_vec);
173 Eigen::VectorXd z(b.size()), tmp(b.size());
174 double abs_new = residual_vec.dot(p);
176 while (i < max_iter) {
177 tmp.noalias() = op * p;
178 const double curvature = p.dot(tmp);
180 <<
": curvature p.A.p=" << curvature <<
" ("
181 << elapsed_s() <<
"s)" << std::flush;
182 if (curvature <= 0.0) {
186 <<
TimeStamp() <<
" PCG iter " << (i + 1)
187 <<
": p.A.p = " << curvature
188 <<
" <= 0 -- the operator is NOT positive-definite (this is a "
189 "direct algebraic certificate, not an inference from "
190 "residual behavior). Stopping here rather than continue "
191 "computing iterations that CG's own convergence theory no "
196 const double alpha = abs_new / curvature;
197 alphas.push_back(alpha);
199 residual_vec -= alpha * tmp;
201 residual_norm2 = residual_vec.squaredNorm();
202 tol_error = std::sqrt(residual_norm2 / rhs_norm2);
203 if (residual_norm2 < threshold) {
209 z = precond.solve(residual_vec);
210 const double abs_old = abs_new;
211 abs_new = residual_vec.dot(z);
212 const double beta = abs_new / abs_old;
213 betas.push_back(beta);
228 if (alphas.size() >= 2) {
229 const std::size_t m = alphas.size();
230 Eigen::MatrixXd T = Eigen::MatrixXd::Zero(m, m);
231 T(0, 0) = 1.0 / alphas[0];
232 for (std::size_t i = 1; i < m; ++i) {
233 T(i, i) = 1.0 / alphas[i] + betas[i - 1] / alphas[i - 1];
234 const double off = std::sqrt(betas[i - 1]) / alphas[i - 1];
238 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(T);
400 const Eigen::VectorXd& b,
Index max_iter,
double omega,
402 std::chrono::steady_clock::time_point t_start,
403 bool match_legacy_first_step) {
404 auto elapsed_s = [&]() {
405 return std::chrono::duration<double>(std::chrono::steady_clock::now() -
410 const double kEpsTol = 1
e-3;
413 Eigen::VectorXd x = Eigen::VectorXd::Zero(b.size());
414 const double rhs_norm = b.norm();
415 const Index n_sites = b.size() / 3;
418 for (; i < max_iter; ++i) {
419 Eigen::VectorXd residual_vec = b - op * x;
420 const double residual_norm = residual_vec.norm();
421 const Eigen::VectorXd x_old = x;
422 const double this_step_omega =
423 (i == 0 && match_legacy_first_step) ? 1.0 : omega;
424 x = x_old + this_step_omega * site_p.
Apply(residual_vec);
426 double max_dU = -1.0;
428 for (
Index n = 0; n < n_sites; ++n) {
430 x.segment<3>(3 * n));
436 avg_dU /= double(n_sites);
439 <<
TimeStamp() <<
" JOR iter " << (i + 1) <<
": max_dU=" << max_dU
440 <<
" avg_dU=" << avg_dU <<
" (residual=" << residual_norm / rhs_norm
441 <<
", informational only -- see this function's own documentation "
442 "for why max_dU/avg_dU, not this, is the actual stopping "
444 << elapsed_s() <<
"s)" << std::flush;
446 result.
residual = residual_norm / rhs_norm;
450 bool converged = (max_dU <= kEpsTol);
451 if (avg_dU < kEpsTol * 0.1) {
JorResult SolveWithJOR(const EwaldPeriodicDipoleOperator &op, const EwaldSitePolarizabilityBlocks &site_p, const Eigen::VectorXd &b, Index max_iter, double omega, Logger &log, std::chrono::steady_clock::time_point t_start, bool match_legacy_first_step)
PcgIndefinitenessResult SolveWithIndefinitenessCheck(const EwaldPeriodicDipoleOperator &op, Preconditioner &precond, const Eigen::VectorXd &b, Index max_iter, double tol, Logger &log, std::chrono::steady_clock::time_point t_start)