47 <<
TimeStamp() <<
" Smallest value of AOOverlap matrix is "
48 <<
S_->SmallestEigenValue() << std::flush;
50 <<
TimeStamp() <<
" Removed " <<
S_->Removedfunctions()
51 <<
" basisfunction from inverse overlap matrix" << std::flush;
55 const Eigen::MatrixXd&
H)
const {
57 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(H_ortho);
59 if (es.info() != Eigen::ComputationInfo::Success) {
60 throw std::runtime_error(
"Matrix Diagonalisation failed. DiagInfo" +
61 std::to_string(es.info()));
71 const Eigen::VectorXd& v_ov,
Index nao,
Index nocclevels)
const {
72 Index nvirt = nao - nocclevels;
73 Eigen::MatrixXd kappa = Eigen::MatrixXd::Zero(nao, nao);
74 for (
Index i = 0; i < nocclevels; ++i) {
75 for (
Index a = 0; a < nvirt; ++a) {
76 double val = v_ov(i * nvirt + a);
77 kappa(i, nocclevels + a) = val;
78 kappa(nocclevels + a, i) = -val;
84std::pair<Eigen::MatrixXd, Eigen::MatrixXd>
87 Index nocclevels_alpha,
89 Index nocclevels_beta)
const {
90 Index n_ov_alpha = nocclevels_alpha * (nao_alpha - nocclevels_alpha);
91 Eigen::VectorXd v_alpha = v.head(n_ov_alpha);
92 Eigen::VectorXd v_beta = v.tail(v.size() - n_ov_alpha);
93 Eigen::MatrixXd kappa_alpha =
95 Eigen::MatrixXd kappa_beta =
97 return {kappa_alpha, kappa_beta};
101 const Eigen::VectorXd& v,
const Eigen::MatrixXd& C_alpha,
102 Index nocclevels_alpha,
const Eigen::MatrixXd& C_beta,
104 double finite_diff_step)
const {
105 Index nao_alpha = C_alpha.rows();
106 Index nvirt_alpha = nao_alpha - nocclevels_alpha;
107 Index nao_beta = C_beta.rows();
108 Index nvirt_beta = nao_beta - nocclevels_beta;
109 Index n_ov_alpha = nocclevels_alpha * nvirt_alpha;
110 Index n_ov_beta = nocclevels_beta * nvirt_beta;
113 v, nao_alpha, nocclevels_alpha, nao_beta, nocclevels_beta);
114 kappa_alpha_trial *= finite_diff_step;
115 kappa_beta_trial *= finite_diff_step;
126 auto EvaluateBothGradientsAt = [&](
const Eigen::MatrixXd& kappa_alpha,
127 const Eigen::MatrixXd& kappa_beta)
128 -> std::pair<Eigen::MatrixXd, Eigen::MatrixXd> {
129 Eigen::MatrixXd C_alpha_rot =
131 (Eigen::MatrixXd::Identity(nao_alpha, nao_alpha) + kappa_alpha);
132 Eigen::MatrixXd nonortho_alpha =
133 C_alpha_rot.transpose() *
S_->Matrix() * C_alpha_rot;
134 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_alpha(nonortho_alpha);
135 C_alpha_rot = C_alpha_rot * es_alpha.operatorInverseSqrt();
137 Eigen::MatrixXd C_beta_rot =
138 C_beta * (Eigen::MatrixXd::Identity(nao_beta, nao_beta) + kappa_beta);
139 Eigen::MatrixXd nonortho_beta =
140 C_beta_rot.transpose() *
S_->Matrix() * C_beta_rot;
141 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_beta(nonortho_beta);
142 C_beta_rot = C_beta_rot * es_beta.operatorInverseSqrt();
144 Eigen::MatrixXd C_alpha_occ_rot = C_alpha_rot.leftCols(nocclevels_alpha);
145 Eigen::MatrixXd D_alpha_rot = C_alpha_occ_rot * C_alpha_occ_rot.transpose();
146 Eigen::MatrixXd C_beta_occ_rot = C_beta_rot.leftCols(nocclevels_beta);
147 Eigen::MatrixXd D_beta_rot = C_beta_occ_rot * C_beta_occ_rot.transpose();
149 SpinFock H_rot = coupled_fock_builder(D_alpha_rot, D_beta_rot);
150 Eigen::MatrixXd F_MO_alpha_rot =
151 C_alpha_rot.transpose() * H_rot.
alpha * C_alpha_rot;
152 Eigen::MatrixXd F_MO_beta_rot =
153 C_beta_rot.transpose() * H_rot.
beta * C_beta_rot;
154 return {F_MO_alpha_rot, F_MO_beta_rot};
157 auto [F_MO_alpha_plus, F_MO_beta_plus] =
158 EvaluateBothGradientsAt(kappa_alpha_trial, kappa_beta_trial);
159 auto [F_MO_alpha_minus, F_MO_beta_minus] =
160 EvaluateBothGradientsAt(-kappa_alpha_trial, -kappa_beta_trial);
162 Eigen::VectorXd sigma(n_ov_alpha + n_ov_beta);
163 for (
Index i = 0; i < nocclevels_alpha; ++i) {
164 for (
Index a = 0; a < nvirt_alpha; ++a) {
165 double g_plus_ia = F_MO_alpha_plus(i, nocclevels_alpha + a);
166 double g_minus_ia = F_MO_alpha_minus(i, nocclevels_alpha + a);
167 sigma(i * nvirt_alpha + a) =
168 (g_plus_ia - g_minus_ia) / (2.0 * finite_diff_step);
171 for (
Index i = 0; i < nocclevels_beta; ++i) {
172 for (
Index a = 0; a < nvirt_beta; ++a) {
173 double g_plus_ia = F_MO_beta_plus(i, nocclevels_beta + a);
174 double g_minus_ia = F_MO_beta_minus(i, nocclevels_beta + a);
175 sigma(n_ov_alpha + i * nvirt_beta + a) =
176 (g_plus_ia - g_minus_ia) / (2.0 * finite_diff_step);
184 Index nocclevels,
double& predicted_energy_change)
const {
192 Eigen::MatrixXd F_MO = C.transpose() * H_AO * C;
198 Eigen::MatrixXd kappa = Eigen::MatrixXd::Zero(nao, nao);
205 Eigen::MatrixXd h_matrix = Eigen::MatrixXd::Zero(nao, nao);
223 constexpr double kMinGap = 1
e-3;
231 constexpr double kMaxKappaElement = 0.1;
232 for (
Index i = 0; i < nocclevels; ++i) {
233 for (
Index a = nocclevels; a < nao; ++a) {
234 double gap = std::max(std::abs(eps(a) - eps(i)), kMinGap);
238 double h_ia = 2.0 * gap;
239 double kappa_ia = -F_MO(i, a) / h_ia;
240 kappa_ia = std::clamp(kappa_ia, -kMaxKappaElement, kMaxKappaElement);
241 kappa(i, a) = kappa_ia;
242 kappa(a, i) = -kappa_ia;
243 h_matrix(i, a) = h_ia;
247 double knorm = kappa.norm();
258 predicted_energy_change = 0.0;
259 for (
Index i = 0; i < nocclevels; ++i) {
260 for (
Index a = nocclevels; a < nao; ++a) {
261 double kappa_ia = kappa(i, a);
262 predicted_energy_change +=
263 F_MO(i, a) * kappa_ia + 0.5 * h_matrix(i, a) * kappa_ia * kappa_ia;
272 Eigen::MatrixXd C_new = C * (Eigen::MatrixXd::Identity(nao, nao) + kappa);
273 Eigen::MatrixXd nonortho = C_new.transpose() *
S_->Matrix() * C_new;
274 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_ortho(nonortho);
275 return C_new * es_ortho.operatorInverseSqrt();
287struct CoupledAugmentedHessianOperator {
288 const Eigen::VectorXd& g;
289 const Eigen::MatrixXd& C_alpha;
290 Index nocclevels_alpha;
291 const Eigen::MatrixXd& C_beta;
292 Index nocclevels_beta;
296 const Eigen::VectorXd& diag_h;
298 Index rows()
const {
return 1 + g.size(); }
300 Eigen::VectorXd diagonal()
const {
301 Eigen::VectorXd d(1 + g.size());
303 d.tail(g.size()) = diag_h;
307 Eigen::MatrixXd operator*(
const Eigen::MatrixXd&
V)
const {
308 Eigen::MatrixXd AV = Eigen::MatrixXd::Zero(
V.rows(),
V.cols());
309 for (
Index col = 0; col <
V.cols(); ++col) {
310 double v0 =
V(0, col);
311 Eigen::VectorXd v_ov =
V.block(1, col, g.size(), 1);
312 AV(0, col) = alpha_scale * g.dot(v_ov);
313 Eigen::VectorXd sigma =
315 nocclevels_beta, coupled_fock_builder);
316 AV.block(1, col, g.size(), 1) = alpha_scale * g * v0 + sigma;
323std::pair<Eigen::MatrixXd, Eigen::MatrixXd>
326 Index nocclevels_alpha,
const Eigen::MatrixXd& H_AO_beta,
329 double& predicted_energy_change)
const {
331 Index nvirt_alpha = nao_alpha - nocclevels_alpha;
332 Index n_ov_alpha = nocclevels_alpha * nvirt_alpha;
333 const Eigen::MatrixXd& C_alpha = MOs_alpha.
eigenvectors();
334 const Eigen::VectorXd& eps_alpha = MOs_alpha.
eigenvalues();
337 Index nvirt_beta = nao_beta - nocclevels_beta;
338 Index n_ov_beta = nocclevels_beta * nvirt_beta;
339 const Eigen::MatrixXd& C_beta = MOs_beta.
eigenvectors();
340 const Eigen::VectorXd& eps_beta = MOs_beta.
eigenvalues();
342 Index n_ov = n_ov_alpha + n_ov_beta;
344 Eigen::MatrixXd F_MO_alpha = C_alpha.transpose() * H_AO_alpha * C_alpha;
345 Eigen::MatrixXd F_MO_beta = C_beta.transpose() * H_AO_beta * C_beta;
361 Eigen::VectorXd g(n_ov);
362 Eigen::VectorXd diag_h(n_ov);
363 constexpr double kMinGap = 1
e-3;
364 for (
Index i = 0; i < nocclevels_alpha; ++i) {
365 for (
Index a = 0; a < nvirt_alpha; ++a) {
366 g(i * nvirt_alpha + a) = F_MO_alpha(i, nocclevels_alpha + a);
367 double gap = std::max(
368 std::abs(eps_alpha(nocclevels_alpha + a) - eps_alpha(i)), kMinGap);
369 diag_h(i * nvirt_alpha + a) = 2.0 * gap;
372 for (
Index i = 0; i < nocclevels_beta; ++i) {
373 for (
Index a = 0; a < nvirt_beta; ++a) {
374 g(n_ov_alpha + i * nvirt_beta + a) = F_MO_beta(i, nocclevels_beta + a);
375 double gap = std::max(
376 std::abs(eps_beta(nocclevels_beta + a) - eps_beta(i)), kMinGap);
377 diag_h(n_ov_alpha + i * nvirt_beta + a) = 2.0 * gap;
381 double alpha_min = 1.0;
382 double alpha_max = 1000.0;
383 Eigen::VectorXd best_kappa_flat = Eigen::VectorXd::Zero(n_ov);
384 double best_mu = 0.0;
386 Eigen::MatrixXd initial_guess = Eigen::MatrixXd::Zero(1 + n_ov, 2);
387 initial_guess(0, 0) = 1.0;
388 double gnorm = g.norm();
390 initial_guess.block(1, 1, n_ov, 1) = g / gnorm;
392 initial_guess(1, 1) = 1.0;
395 auto SolveForAlpha = [&](
double alpha_try, Eigen::VectorXd& kappa_flat_out,
397 CoupledAugmentedHessianOperator op{g,
403 coupled_fock_builder,
411 solver.
solve(op, 1, initial_guess);
414 double v0 = eigvec(0);
415 if (std::abs(v0) < 1
e-8) {
416 kappa_flat_out = Eigen::VectorXd::Zero(g.size());
419 kappa_flat_out = eigvec.tail(g.size()) / v0;
422 double alpha_try = alpha_min;
423 constexpr int kMaxBisectionIters = 20;
424 for (
int bisection_iter = 0; bisection_iter < kMaxBisectionIters;
426 Eigen::VectorXd kappa_flat;
428 SolveForAlpha(alpha_try, kappa_flat, mu);
429 double step_norm = kappa_flat.norm() / alpha_try;
430 best_kappa_flat = kappa_flat;
432 if (std::abs(step_norm - trust_radius) < 0.01 * trust_radius) {
435 if (step_norm > trust_radius) {
436 alpha_min = alpha_try;
438 alpha_max = alpha_try;
440 alpha_try = 0.5 * (alpha_min + alpha_max);
445 <<
" CoupledAugmentedHessianStep bisection diagnostic: "
448 <<
", achieved step_norm=" << (best_kappa_flat.norm() / alpha_try)
449 <<
", requested trust_radius=" << trust_radius << std::flush;
452 best_kappa_flat, nao_alpha, nocclevels_alpha, nao_beta, nocclevels_beta);
461 predicted_energy_change =
462 0.5 * (g.dot(best_kappa_flat) + best_mu * best_kappa_flat.squaredNorm());
464 Eigen::MatrixXd C_alpha_new =
465 C_alpha * (Eigen::MatrixXd::Identity(nao_alpha, nao_alpha) + kappa_alpha);
466 Eigen::MatrixXd nonortho_alpha =
467 C_alpha_new.transpose() *
S_->Matrix() * C_alpha_new;
468 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_alpha(nonortho_alpha);
469 C_alpha_new = C_alpha_new * es_alpha.operatorInverseSqrt();
471 Eigen::MatrixXd C_beta_new =
472 C_beta * (Eigen::MatrixXd::Identity(nao_beta, nao_beta) + kappa_beta);
473 Eigen::MatrixXd nonortho_beta =
474 C_beta_new.transpose() *
S_->Matrix() * C_beta_new;
475 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_beta(nonortho_beta);
476 C_beta_new = C_beta_new * es_beta.operatorInverseSqrt();
478 return {C_alpha_new, C_beta_new};
482 const Eigen::MatrixXd& MOs,
Index nocclevels)
const {
483 if (nocclevels == 0) {
484 return Eigen::MatrixXd::Zero(MOs.rows(), MOs.rows());
486 Eigen::MatrixXd occstates = MOs.leftCols(nocclevels);
487 return occstates * occstates.transpose();
502 const Eigen::MatrixXd& MOs_old,
507 Eigen::VectorXd virt = Eigen::VectorXd::Zero(
H.rows());
508 for (
Index i = nocclevels; i <
H.rows(); ++i) {
516 Eigen::MatrixXd vir =
S_->Matrix() * MOs_old * virt.asDiagonal() *
517 MOs_old.transpose() *
S_->Matrix();
522 const Eigen::MatrixXd& dmat,
const Eigen::MatrixXd&
H)
const {
523 const Eigen::MatrixXd&
S =
S_->Matrix();
528 const Eigen::MatrixXd& err_beta)
const {
529 return std::max(err_alpha.cwiseAbs().maxCoeff(),
530 err_beta.cwiseAbs().maxCoeff());
557 <<
" Direct-minimization step check: actual dE=" << actual_change
591 <<
" Direct-minimization trust radius fell "
592 "below its own finite-difference resolution floor ("
594 <<
") without an accepted step -- "
595 "falling back to mixing instead of continuing to shrink."
606 }
else if (r <= 0.25) {
609 }
else if (r > 0.75) {
625 totE_.push_back(totE);
716 bool diis_error =
false;
722 Eigen::MatrixXd H_guess_alpha =
H.alpha;
723 Eigen::MatrixXd H_guess_beta =
H.beta;
729 Eigen::VectorXd coeffs;
735 diis_error = !
adiis_.Info() || coeffs.size() == 0;
737 <<
TimeStamp() <<
" Using ADIIS for next UKS guess" << std::flush;
739 coeffs =
diis_.CalcCoeff();
740 diis_error = !
diis_.Info() || coeffs.size() == 0;
742 <<
TimeStamp() <<
" Using DIIS for next UKS guess" << std::flush;
760 bool trailing_average_stalled =
false;
763 double mean_ratio = 0.0;
764 Index ratio_count = 0;
771 if (ratio_count > 0) {
772 mean_ratio /= double(ratio_count);
806 trailing_average_stalled) &&
816 << (trailing_average_stalled ?
" (or trailing average stalled)"
818 <<
", switching to direct-minimization step" << std::flush;
829 double predicted_change_alpha = 0.0;
830 double predicted_change_beta = 0.0;
831 Eigen::MatrixXd C_new_alpha;
832 Eigen::MatrixXd C_new_beta;
851 double predicted_change_combined = 0.0;
855 predicted_change_combined);
863 predicted_change_alpha = 0.5 * predicted_change_combined;
864 predicted_change_beta = 0.5 * predicted_change_combined;
872 predicted_change_alpha + predicted_change_beta;
883 (C_new_alpha.transpose() *
H.alpha * C_new_alpha).diagonal();
885 (C_new_beta.transpose() *
H.beta * C_new_beta).diagonal();
889 return dmatout_direct;
892 <<
TimeStamp() <<
" (A)DIIS failed using mixing instead"
894 H_guess_alpha =
H.alpha;
895 H_guess_beta =
H.beta;
898 H_guess_alpha.setZero();
899 H_guess_beta.setZero();
900 for (
Index i = 0; i < coeffs.size(); ++i) {
901 if (std::abs(coeffs(i)) < 1
e-8) {
941 double ramp_fraction =
944 double mixingparameter_alpha_current =
947 double mixingparameter_beta_current =
950 dmatout.
alpha = mixingparameter_alpha_current * dmat.
alpha +
951 (1.0 - mixingparameter_alpha_current) * dmatout.
alpha;
952 dmatout.
beta = mixingparameter_beta_current * dmat.
beta +
953 (1.0 - mixingparameter_beta_current) * dmatout.
beta;
955 <<
TimeStamp() <<
" Using coupled UKS mixing with adaptive alpha="
956 << mixingparameter_alpha_current
959 <<
", ramp fraction=" << ramp_fraction <<
")" << std::flush;
968 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)
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_
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
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 > 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_
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.
Provides a means for comparing floating point numbers.