51 const Eigen::VectorXd& s_eig = es.
values;
52 Eigen::VectorXd inv_sqrt = Eigen::VectorXd::Zero(s_eig.size());
54 for (
Index i = 0; i < s_eig.size(); ++i) {
55 if (s_eig(i) < etol) {
58 inv_sqrt(i) = 1.0 / std::sqrt(s_eig(i));
64 const Eigen::MatrixXd U = es.
vectors.leftCols(removed);
68 <<
TimeStamp() <<
" Smallest value of AOOverlap matrix is " << s_eig(0)
72 <<
" basisfunction from inverse overlap matrix (threshold " << etol <<
")"
74 if (removed == 0 && s_eig(0) < 1e3 * etol) {
77 <<
" WARNING: the overlap matrix is nearly singular; if the SCF is "
78 "unstable, raise xtpdft.overlap_tolerance above "
79 << s_eig(0) <<
"." << std::flush;
84 const Eigen::MatrixXd&
H)
const {
90 if (!es.
values.allFinite()) {
91 throw std::runtime_error(
"Matrix Diagonalisation failed");
101 const Eigen::VectorXd& v_ov,
Index nao,
Index nocclevels)
const {
102 Index nvirt = nao - nocclevels;
103 Eigen::MatrixXd kappa = Eigen::MatrixXd::Zero(nao, nao);
104 for (
Index i = 0; i < nocclevels; ++i) {
105 for (
Index a = 0; a < nvirt; ++a) {
106 double val = v_ov(i * nvirt + a);
107 kappa(i, nocclevels + a) = val;
108 kappa(nocclevels + a, i) = -val;
114std::pair<Eigen::MatrixXd, Eigen::MatrixXd>
117 Index nocclevels_alpha,
119 Index nocclevels_beta)
const {
120 Index n_ov_alpha = nocclevels_alpha * (nao_alpha - nocclevels_alpha);
121 Eigen::VectorXd v_alpha = v.head(n_ov_alpha);
122 Eigen::VectorXd v_beta = v.tail(v.size() - n_ov_alpha);
123 Eigen::MatrixXd kappa_alpha =
125 Eigen::MatrixXd kappa_beta =
127 return {kappa_alpha, kappa_beta};
131 const Eigen::VectorXd& v,
const Eigen::MatrixXd& C_alpha,
132 Index nocclevels_alpha,
const Eigen::MatrixXd& C_beta,
134 double finite_diff_step)
const {
135 Index nao_alpha = C_alpha.rows();
136 Index nvirt_alpha = nao_alpha - nocclevels_alpha;
137 Index nao_beta = C_beta.rows();
138 Index nvirt_beta = nao_beta - nocclevels_beta;
139 Index n_ov_alpha = nocclevels_alpha * nvirt_alpha;
140 Index n_ov_beta = nocclevels_beta * nvirt_beta;
143 v, nao_alpha, nocclevels_alpha, nao_beta, nocclevels_beta);
144 kappa_alpha_trial *= finite_diff_step;
145 kappa_beta_trial *= finite_diff_step;
156 auto EvaluateBothGradientsAt = [&](
const Eigen::MatrixXd& kappa_alpha,
157 const Eigen::MatrixXd& kappa_beta)
158 -> std::pair<Eigen::MatrixXd, Eigen::MatrixXd> {
159 Eigen::MatrixXd C_alpha_rot =
161 (Eigen::MatrixXd::Identity(nao_alpha, nao_alpha) + kappa_alpha);
162 Eigen::MatrixXd nonortho_alpha =
163 C_alpha_rot.transpose() *
S_->Matrix() * C_alpha_rot;
164 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_alpha(nonortho_alpha);
165 C_alpha_rot = C_alpha_rot * es_alpha.operatorInverseSqrt();
167 Eigen::MatrixXd C_beta_rot =
168 C_beta * (Eigen::MatrixXd::Identity(nao_beta, nao_beta) + kappa_beta);
169 Eigen::MatrixXd nonortho_beta =
170 C_beta_rot.transpose() *
S_->Matrix() * C_beta_rot;
171 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_beta(nonortho_beta);
172 C_beta_rot = C_beta_rot * es_beta.operatorInverseSqrt();
174 Eigen::MatrixXd C_alpha_occ_rot = C_alpha_rot.leftCols(nocclevels_alpha);
175 Eigen::MatrixXd D_alpha_rot = C_alpha_occ_rot * C_alpha_occ_rot.transpose();
176 Eigen::MatrixXd C_beta_occ_rot = C_beta_rot.leftCols(nocclevels_beta);
177 Eigen::MatrixXd D_beta_rot = C_beta_occ_rot * C_beta_occ_rot.transpose();
179 SpinFock H_rot = coupled_fock_builder(D_alpha_rot, D_beta_rot);
180 Eigen::MatrixXd F_MO_alpha_rot =
181 C_alpha_rot.transpose() * H_rot.
alpha * C_alpha_rot;
182 Eigen::MatrixXd F_MO_beta_rot =
183 C_beta_rot.transpose() * H_rot.
beta * C_beta_rot;
184 return {F_MO_alpha_rot, F_MO_beta_rot};
187 auto [F_MO_alpha_plus, F_MO_beta_plus] =
188 EvaluateBothGradientsAt(kappa_alpha_trial, kappa_beta_trial);
189 auto [F_MO_alpha_minus, F_MO_beta_minus] =
190 EvaluateBothGradientsAt(-kappa_alpha_trial, -kappa_beta_trial);
192 Eigen::VectorXd sigma(n_ov_alpha + n_ov_beta);
193 for (
Index i = 0; i < nocclevels_alpha; ++i) {
194 for (
Index a = 0; a < nvirt_alpha; ++a) {
195 double g_plus_ia = F_MO_alpha_plus(i, nocclevels_alpha + a);
196 double g_minus_ia = F_MO_alpha_minus(i, nocclevels_alpha + a);
197 sigma(i * nvirt_alpha + a) =
198 (g_plus_ia - g_minus_ia) / (2.0 * finite_diff_step);
201 for (
Index i = 0; i < nocclevels_beta; ++i) {
202 for (
Index a = 0; a < nvirt_beta; ++a) {
203 double g_plus_ia = F_MO_beta_plus(i, nocclevels_beta + a);
204 double g_minus_ia = F_MO_beta_minus(i, nocclevels_beta + a);
205 sigma(n_ov_alpha + i * nvirt_beta + a) =
206 (g_plus_ia - g_minus_ia) / (2.0 * finite_diff_step);
214 Index nocclevels,
double& predicted_energy_change)
const {
222 Eigen::MatrixXd F_MO = C.transpose() * H_AO * C;
228 Eigen::MatrixXd kappa = Eigen::MatrixXd::Zero(nao, nao);
235 Eigen::MatrixXd h_matrix = Eigen::MatrixXd::Zero(nao, nao);
253 constexpr double kMinGap = 1
e-3;
261 constexpr double kMaxKappaElement = 0.1;
262 for (
Index i = 0; i < nocclevels; ++i) {
263 for (
Index a = nocclevels; a < nao; ++a) {
264 double gap = std::max(std::abs(eps(a) - eps(i)), kMinGap);
268 double h_ia = 2.0 * gap;
269 double kappa_ia = -F_MO(i, a) / h_ia;
270 kappa_ia = std::clamp(kappa_ia, -kMaxKappaElement, kMaxKappaElement);
271 kappa(i, a) = kappa_ia;
272 kappa(a, i) = -kappa_ia;
273 h_matrix(i, a) = h_ia;
277 double knorm = kappa.norm();
288 predicted_energy_change = 0.0;
289 for (
Index i = 0; i < nocclevels; ++i) {
290 for (
Index a = nocclevels; a < nao; ++a) {
291 double kappa_ia = kappa(i, a);
292 predicted_energy_change +=
293 F_MO(i, a) * kappa_ia + 0.5 * h_matrix(i, a) * kappa_ia * kappa_ia;
302 Eigen::MatrixXd C_new = C * (Eigen::MatrixXd::Identity(nao, nao) + kappa);
303 Eigen::MatrixXd nonortho = C_new.transpose() *
S_->Matrix() * C_new;
304 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_ortho(nonortho);
305 return C_new * es_ortho.operatorInverseSqrt();
317struct CoupledAugmentedHessianOperator {
318 const Eigen::VectorXd& g;
319 const Eigen::MatrixXd& C_alpha;
320 Index nocclevels_alpha;
321 const Eigen::MatrixXd& C_beta;
322 Index nocclevels_beta;
326 const Eigen::VectorXd& diag_h;
328 Index rows()
const {
return 1 + g.size(); }
330 Eigen::VectorXd diagonal()
const {
331 Eigen::VectorXd d(1 + g.size());
333 d.tail(g.size()) = diag_h;
337 Eigen::MatrixXd operator*(
const Eigen::MatrixXd&
V)
const {
338 Eigen::MatrixXd AV = Eigen::MatrixXd::Zero(
V.rows(),
V.cols());
339 for (
Index col = 0; col <
V.cols(); ++col) {
340 double v0 =
V(0, col);
341 Eigen::VectorXd v_ov =
V.block(1, col, g.size(), 1);
342 AV(0, col) = alpha_scale * g.dot(v_ov);
343 Eigen::VectorXd sigma =
345 nocclevels_beta, coupled_fock_builder);
346 AV.block(1, col, g.size(), 1) = alpha_scale * g * v0 + sigma;
353std::pair<Eigen::MatrixXd, Eigen::MatrixXd>
356 Index nocclevels_alpha,
const Eigen::MatrixXd& H_AO_beta,
359 double& predicted_energy_change)
const {
361 Index nvirt_alpha = nao_alpha - nocclevels_alpha;
362 Index n_ov_alpha = nocclevels_alpha * nvirt_alpha;
363 const Eigen::MatrixXd& C_alpha = MOs_alpha.
eigenvectors();
364 const Eigen::VectorXd& eps_alpha = MOs_alpha.
eigenvalues();
367 Index nvirt_beta = nao_beta - nocclevels_beta;
368 Index n_ov_beta = nocclevels_beta * nvirt_beta;
369 const Eigen::MatrixXd& C_beta = MOs_beta.
eigenvectors();
370 const Eigen::VectorXd& eps_beta = MOs_beta.
eigenvalues();
372 Index n_ov = n_ov_alpha + n_ov_beta;
374 Eigen::MatrixXd F_MO_alpha = C_alpha.transpose() * H_AO_alpha * C_alpha;
375 Eigen::MatrixXd F_MO_beta = C_beta.transpose() * H_AO_beta * C_beta;
391 Eigen::VectorXd g(n_ov);
392 Eigen::VectorXd diag_h(n_ov);
393 constexpr double kMinGap = 1
e-3;
394 for (
Index i = 0; i < nocclevels_alpha; ++i) {
395 for (
Index a = 0; a < nvirt_alpha; ++a) {
396 g(i * nvirt_alpha + a) = F_MO_alpha(i, nocclevels_alpha + a);
397 double gap = std::max(
398 std::abs(eps_alpha(nocclevels_alpha + a) - eps_alpha(i)), kMinGap);
399 diag_h(i * nvirt_alpha + a) = 2.0 * gap;
402 for (
Index i = 0; i < nocclevels_beta; ++i) {
403 for (
Index a = 0; a < nvirt_beta; ++a) {
404 g(n_ov_alpha + i * nvirt_beta + a) = F_MO_beta(i, nocclevels_beta + a);
405 double gap = std::max(
406 std::abs(eps_beta(nocclevels_beta + a) - eps_beta(i)), kMinGap);
407 diag_h(n_ov_alpha + i * nvirt_beta + a) = 2.0 * gap;
411 double alpha_min = 1.0;
412 double alpha_max = 1000.0;
413 Eigen::VectorXd best_kappa_flat = Eigen::VectorXd::Zero(n_ov);
414 double best_mu = 0.0;
416 Eigen::MatrixXd initial_guess = Eigen::MatrixXd::Zero(1 + n_ov, 2);
417 initial_guess(0, 0) = 1.0;
418 double gnorm = g.norm();
420 initial_guess.block(1, 1, n_ov, 1) = g / gnorm;
422 initial_guess(1, 1) = 1.0;
435 auto SolveForAlpha = [&](
double alpha_try, Eigen::VectorXd& kappa_flat_out,
436 double& mu_out) ->
bool {
437 CoupledAugmentedHessianOperator op{g,
443 coupled_fock_builder,
451 solver.
solve(op, 1, initial_guess);
452 if (solver.
info() != Eigen::ComputationInfo::Success) {
457 double v0 = eigvec(0);
458 if (std::abs(v0) < 1
e-8) {
459 kappa_flat_out = Eigen::VectorXd::Zero(g.size());
462 kappa_flat_out = eigvec.tail(g.size()) / v0;
466 double alpha_try = alpha_min;
467 constexpr int kMaxBisectionIters = 20;
468 bool have_converged_once =
false;
469 for (
int bisection_iter = 0; bisection_iter < kMaxBisectionIters;
471 Eigen::VectorXd kappa_flat;
473 bool converged = SolveForAlpha(alpha_try, kappa_flat, mu);
485 alpha_min = alpha_try;
486 alpha_try = 0.5 * (alpha_min + alpha_max);
489 have_converged_once =
true;
490 double step_norm = kappa_flat.norm() / alpha_try;
491 best_kappa_flat = kappa_flat;
493 if (std::abs(step_norm - trust_radius) < 0.01 * trust_radius) {
496 if (step_norm > trust_radius) {
497 alpha_min = alpha_try;
499 alpha_max = alpha_try;
501 alpha_try = 0.5 * (alpha_min + alpha_max);
504 if (!have_converged_once) {
512 throw std::runtime_error(
513 "CoupledAugmentedHessianStep: DavidsonSolver failed to converge "
514 "for every bisection trial (all " +
515 std::to_string(kMaxBisectionIters) +
516 " attempts) -- no genuine augmented-Hessian step could be "
522 <<
" CoupledAugmentedHessianStep bisection diagnostic: "
525 <<
", achieved step_norm=" << (best_kappa_flat.norm() / alpha_try)
526 <<
", requested trust_radius=" << trust_radius << std::flush;
529 best_kappa_flat, nao_alpha, nocclevels_alpha, nao_beta, nocclevels_beta);
538 predicted_energy_change =
539 0.5 * (g.dot(best_kappa_flat) + best_mu * best_kappa_flat.squaredNorm());
541 Eigen::MatrixXd C_alpha_new =
542 C_alpha * (Eigen::MatrixXd::Identity(nao_alpha, nao_alpha) + kappa_alpha);
543 Eigen::MatrixXd nonortho_alpha =
544 C_alpha_new.transpose() *
S_->Matrix() * C_alpha_new;
545 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_alpha(nonortho_alpha);
546 C_alpha_new = C_alpha_new * es_alpha.operatorInverseSqrt();
548 Eigen::MatrixXd C_beta_new =
549 C_beta * (Eigen::MatrixXd::Identity(nao_beta, nao_beta) + kappa_beta);
550 Eigen::MatrixXd nonortho_beta =
551 C_beta_new.transpose() *
S_->Matrix() * C_beta_new;
552 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_beta(nonortho_beta);
553 C_beta_new = C_beta_new * es_beta.operatorInverseSqrt();
555 return {C_alpha_new, C_beta_new};
559 const Eigen::MatrixXd& MOs,
Index nocclevels)
const {
560 if (nocclevels == 0) {
561 return Eigen::MatrixXd::Zero(MOs.rows(), MOs.rows());
563 Eigen::MatrixXd occstates = MOs.leftCols(nocclevels);
564 return occstates * occstates.transpose();
579 const Eigen::MatrixXd& MOs_old,
584 Eigen::VectorXd virt = Eigen::VectorXd::Zero(
H.rows());
585 for (
Index i = nocclevels; i <
H.rows(); ++i) {
593 Eigen::MatrixXd vir =
S_->Matrix() * MOs_old * virt.asDiagonal() *
594 MOs_old.transpose() *
S_->Matrix();
599 const Eigen::MatrixXd& dmat,
const Eigen::MatrixXd&
H)
const {
600 const Eigen::MatrixXd&
S =
S_->Matrix();
605 const Eigen::MatrixXd& err_beta)
const {
606 return std::max(err_alpha.cwiseAbs().maxCoeff(),
607 err_beta.cwiseAbs().maxCoeff());
634 <<
" Direct-minimization step check: actual dE=" << actual_change
668 <<
" Direct-minimization trust radius fell "
669 "below its own finite-difference resolution floor ("
671 <<
") without an accepted step -- "
672 "falling back to mixing instead of continuing to shrink."
683 }
else if (r <= 0.25) {
686 }
else if (r > 0.75) {
714 totE_.push_back(totE);
796 diis_.Update(drop, err_alpha, err_beta);
798 bool diis_error =
false;
804 Eigen::MatrixXd H_guess_alpha =
H.alpha;
805 Eigen::MatrixXd H_guess_beta =
H.beta;
810 const std::size_t min_history = near_convergence ? 1 : 2;
815 Eigen::VectorXd coeffs;
821 diis_error = !
adiis_.Info() || coeffs.size() == 0;
823 <<
TimeStamp() <<
" Using ADIIS for next UKS guess" << std::flush;
825 coeffs =
diis_.CalcCoeff();
826 diis_error = !
diis_.Info() || coeffs.size() == 0;
828 <<
TimeStamp() <<
" Using DIIS for next UKS guess" << std::flush;
846 bool trailing_average_stalled =
false;
849 double mean_ratio = 0.0;
850 Index ratio_count = 0;
857 if (ratio_count > 0) {
858 mean_ratio /= double(ratio_count);
892 trailing_average_stalled) &&
902 << (trailing_average_stalled ?
" (or trailing average stalled)"
904 <<
", switching to direct-minimization step" << std::flush;
915 double predicted_change_alpha = 0.0;
916 double predicted_change_beta = 0.0;
917 Eigen::MatrixXd C_new_alpha;
918 Eigen::MatrixXd C_new_beta;
937 double predicted_change_combined = 0.0;
941 predicted_change_combined);
949 predicted_change_alpha = 0.5 * predicted_change_combined;
950 predicted_change_beta = 0.5 * predicted_change_combined;
958 predicted_change_alpha + predicted_change_beta;
969 (C_new_alpha.transpose() *
H.alpha * C_new_alpha).diagonal();
971 (C_new_beta.transpose() *
H.beta * C_new_beta).diagonal();
975 return dmatout_direct;
978 <<
TimeStamp() <<
" (A)DIIS failed using mixing instead"
980 H_guess_alpha =
H.alpha;
981 H_guess_beta =
H.beta;
984 H_guess_alpha.setZero();
985 H_guess_beta.setZero();
986 for (
Index i = 0; i < coeffs.size(); ++i) {
987 if (std::abs(coeffs(i)) < 1
e-8) {
1027 double ramp_fraction =
1030 double mixingparameter_alpha_current =
1033 double mixingparameter_beta_current =
1036 dmatout.
alpha = mixingparameter_alpha_current * dmat.
alpha +
1037 (1.0 - mixingparameter_alpha_current) * dmatout.
alpha;
1038 dmatout.
beta = mixingparameter_beta_current * dmat.
beta +
1039 (1.0 - mixingparameter_beta_current) * dmatout.
beta;
1041 <<
TimeStamp() <<
" Using coupled UKS mixing with adaptive alpha="
1042 << mixingparameter_alpha_current
1045 <<
", ramp fraction=" << ramp_fraction <<
")" << std::flush;
1054 if (
totE_.size() < 2) {
Use Davidson algorithm to solve A*V=E*V.
void set_max_search_space(Index N)
Eigen::VectorXd eigenvalues() const
void solve(const MatrixReplacement &A, Index neigen, Index size_initial_guess=0)
void set_iter_max(Index N)
void set_matrix_type(std::string mt)
Eigen::ComputationInfo info() const
void set_tolerance(std::string tol)
Eigen::MatrixXd eigenvectors() const
Logger is used for thread-safe output of messages.
Timestamp returns the current time as a string Example: cout << TimeStamp().
double CombinedError(const Eigen::MatrixXd &err_alpha, const Eigen::MatrixXd &err_beta) const
tools::EigenSystem SolveFockmatrix(const Eigen::MatrixXd &H) const
double direct_min_pre_energy_
std::pair< Eigen::MatrixXd, Eigen::MatrixXd > UnflattenCoupledRotation(const Eigen::VectorXd &v, Index nao_alpha, Index nocclevels_alpha, Index nao_beta, Index nocclevels_beta) const
static constexpr Index kAutoStartIteration
Index consecutive_adiis_failures_
SpinDensity DensityMatrix(const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta) const
void Levelshift(Eigen::MatrixXd &H, const Eigen::MatrixXd &MOs_old, const options &opt, Index nocclevels) const
std::vector< Eigen::MatrixXd > mathist_beta_
Eigen::MatrixXd removed_projector_
static constexpr double kMinTrustRadius
static constexpr Index kMaxConsecutiveADIISFailures
void setOverlap(AOOverlap &S, double etol)
void setLogger(Logger *log)
void Configure(const options &opt_alpha, const options &opt_beta)
Eigen::MatrixXd direct_min_pre_MOs_beta_
Eigen::VectorXd BuildCoupledSigmaVector(const Eigen::VectorXd &v, const Eigen::MatrixXd &C_alpha, Index nocclevels_alpha, const Eigen::MatrixXd &C_beta, Index nocclevels_beta, const CoupledFockBuilder &coupled_fock_builder, double finite_diff_step=1e-3) const
static constexpr double kEnergyRiseForADIIS
Eigen::MatrixXd BuildErrorMatrix(const Eigen::MatrixXd &dmat, const Eigen::MatrixXd &H) const
ConvergenceAcc::options options
SpinDensity Iterate(const SpinDensity &dmat, SpinFock &H, tools::EigenSystem &MOs_alpha, tools::EigenSystem &MOs_beta, double totE)
std::vector< Eigen::MatrixXd > dmatHist_alpha_
Eigen::MatrixXd direct_min_pre_MOs_alpha_
bool direct_min_floor_hit_
Eigen::MatrixXd DirectMinimizationRotation(const Eigen::MatrixXd &H_AO, const tools::EigenSystem &MOs, Index nocclevels, double &predicted_energy_change) const
Eigen::MatrixXd UnflattenRotation(const Eigen::VectorXd &v_ov, Index nao, Index nocclevels) const
Eigen::VectorXd direct_min_pre_MOs_beta_energies_
double trust_radius_current_
std::vector< double > errhist_
std::vector< double > diiserror_history_
std::vector< Eigen::MatrixXd > dmatHist_beta_
std::vector< double > totE_
CoupledFockBuilder coupled_fock_builder_
Eigen::VectorXd direct_min_pre_MOs_alpha_energies_
Eigen::MatrixXd DensityMatrixGroundState_unres(const Eigen::MatrixXd &MOs, Index nocclevels) const
static constexpr Index kTrailingWindowSize
double direct_min_predicted_change_
static constexpr double kMeanRatioTolerance
std::function< SpinFock(const Eigen::MatrixXd &, const Eigen::MatrixXd &)> CoupledFockBuilder
std::vector< Eigen::MatrixXd > mathist_alpha_
Eigen::MatrixXd Sminusahalf
Index total_iteration_count_
static constexpr double kRemovedShift
std::pair< Eigen::MatrixXd, Eigen::MatrixXd > CoupledAugmentedHessianStep(const Eigen::MatrixXd &H_AO_alpha, const tools::EigenSystem &MOs_alpha, Index nocclevels_alpha, const Eigen::MatrixXd &H_AO_beta, const tools::EigenSystem &MOs_beta, Index nocclevels_beta, const CoupledFockBuilder &coupled_fock_builder, double trust_radius, double &predicted_energy_change) const
#define XTP_LOG(level, log)
Charge transport classes.
SymmetricEigenSystem SymmetricEigen(const Eigen::MatrixXd &A, bool vectors=true)
With vectors = false only the eigenvalues are computed.
Provides a means for comparing floating point numbers.