32 if (
method_ ==
"jacobi-sweeps") {
34 <<
TimeStamp() <<
" Using Jacobi-Sweeps" << std::flush;
36 }
else if (
method_ ==
"unitary-optimizer") {
38 <<
TimeStamp() <<
" Using Unitary Optimizer" << std::flush;
44 const std::vector<Eigen::MatrixXd> &Sat_all,
45 const Index nat)
const {
49 for (
Index iat = 0; iat < nat; iat++) {
50 Eigen::MatrixXd qw = Sat_all[iat] * W;
51 for (
Index i = 0; i < W.cols(); i++) {
52 double Qa = W.col(i).transpose() * qw.col(i);
53 Dinv += std::pow(Qa, p);
60 const Eigen::MatrixXd &W,
const std::vector<Eigen::MatrixXd> &Sat_all,
61 const Index nat)
const {
65 for (
Index iat = 0; iat < nat; iat++) {
66 Eigen::MatrixXd qw = Sat_all[iat] * W;
67 for (
Index i = 0; i < W.cols(); i++) {
68 double qwp = W.col(i).transpose() * qw.col(i);
69 Dinv += std::pow(qwp, p);
70 double t = p * std::pow(qwp, p - 1);
71 for (
Index j = 0; j < W.cols(); j++) {
72 Jderiv(j, i) += t * qw(j, i);
76 return {Dinv, Jderiv};
80 const Eigen::VectorXd &y)
const {
84 if (x.size() != y.size()) {
85 throw std::runtime_error(
"x and y have different dimensions!\n");
91 Eigen::MatrixXd A = Eigen::MatrixXd::Zero(N, deg);
92 for (
Index i = 0; i < N; i++) {
93 for (
Index j = 0; j < deg; j++) {
94 A(i, j) = std::pow(x(i), j);
98 return A.colPivHouseholderQr().solve(y);
104 Eigen::VectorXcd complex_roots =
108 std::vector<double> real_roots;
109 for (
Index i = 0; i < complex_roots.size(); i++) {
110 if (std::abs(std::imag(complex_roots(i))) <
111 10 * std::numeric_limits<double>::epsilon()) {
112 real_roots.push_back(std::real(complex_roots(i)));
117 std::sort(real_roots.begin(), real_roots.end());
121 for (
auto root : real_roots) {
123 if (root > std::sqrt(std::numeric_limits<double>::epsilon())) {
132 const Eigen::VectorXcd &coeff)
const {
135 Index order = coeff.size();
136 while (coeff(order - 1) == 0.0 && order >= 1) order--;
141 <<
TimeStamp() <<
" Polynomial is constant - no zero can be found."
149 Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es(cmat);
152 return es.eigenvalues();
156 const Eigen::VectorXcd &c)
const {
159 Eigen::MatrixXcd dum;
164 Index N = c.size() - 1;
166 throw std::runtime_error(
"Coefficient of highest term vanishes!\n");
169 Eigen::MatrixXcd companion = Eigen::MatrixXcd::Zero(N, N);
172 for (
Index j = 0; j < N; j++) {
173 companion(0, j) = -c(N - (j + 1)) / c(N);
177 for (
Index j = 1; j < N; j++) {
178 companion(j, j - 1) = 1.0;
185 const Eigen::MatrixXd &W,
186 const Eigen::VectorXcd &eval,
187 const Eigen::MatrixXcd &evec)
const {
189 Eigen::VectorXcd temp = (step * eval).array().exp();
190 Eigen::MatrixXd W_rotated =
191 (evec * temp.asDiagonal() * evec.adjoint()).real() * W;
200 Eigen::MatrixXd occupied_orbitals = orbitals.
MOs().
eigenvectors().leftCols(
225 bool converged =
false;
227 std::string update_type;
237 <<
TimeStamp() <<
" Calculated cost function and its W-derivative"
241 G_ = Jderiv *
W_.transpose() -
W_ * Jderiv.transpose();
243 if (iteration == 0 || (iteration - 1) %
W_.cols() == 0) {
245 update_type =
"SDSA";
249 update_type =
"CGPR";
253 H_ = 0.5 * (
H_ -
H_.transpose());
259 <<
TimeStamp() <<
"CG search direction reset." << std::flush;
266 Eigen::EigenSolver<Eigen::MatrixXd> es(
H_);
267 Eigen::VectorXcd Hval = es.eigenvalues();
268 Eigen::MatrixXcd Hvec = es.eigenvectors();
269 double wmax = Hval.cwiseAbs().maxCoeff();
270 double Tmu = 2.0 *
tools::conv::Pi / (
static_cast<double>(orderW) * wmax);
274 double deltaTmu = Tmu /
static_cast<double>(npoints - 1);
280 Eigen::VectorXd mu(npoints);
281 Eigen::VectorXd derivative_points(npoints);
282 Eigen::VectorXd cost_points(npoints);
284 for (
Index i = 0; i < npoints; i++) {
285 mu(i) =
static_cast<double>(i) * deltaTmu;
286 Eigen::MatrixXd W_rotated =
rotate_W(mu(i),
W_, Hval, Hvec);
290 cost_points(i) =
cost;
291 derivative_points(i) =
293 std::real((der * W_rotated.transpose() *
H_.transpose()).trace());
297 if (derivative_points(0) < 0.0) {
299 <<
TimeStamp() <<
"Derivative is negative!" << mu <<
" "
300 << derivative_points << std::flush;
304 Eigen::VectorXd polyfit_coeff =
fit_polynomial(mu, derivative_points);
310 if (step > 0.0 && step <= Tmu) {
312 Eigen::MatrixXd W_new =
rotate_W(step,
W_, Hval, Hvec);
315 double J_new =
cost(W_new, Sat_all, numatoms);
316 double delta_J = J_new -
J_;
328 <<
" WARNING: Cost function is decreasing. deltaJ = "
329 << delta_J << std::flush;
333 <<
TimeStamp() <<
"Trying halved maximum step size "
339 throw std::runtime_error(
340 "Problem in polynomial line search - could not find suitable "
348 <<
"Step went beyond max step, trying reduced max step..."
355 throw std::runtime_error(
356 "Problem in polynomial line search - could not find suitable "
365 << (boost::format(
" UT iteration = %1$6i (%6$4.s) Tmu = %4$4.2e mu_opt "
366 "= %5$1.4f |deltaJ| = %2$4.2e |G| = %3$4.2e") %
367 (iteration) % std::abs(
J_ -
J_old_) % G_norm % Tmu % step %
381 (
W_.transpose() * occupied_orbitals.transpose()).transpose();
387 auto [LMOS, LMOS_energies] =
sort_lmos(energies);
389 for (
Index i = 0; i < LMOS_energies.size(); i++) {
391 << (boost::format(
" LMO index = %1$4i Energy = %2$10.5f Hartree ") %
392 (i) % LMOS_energies(i))
411 <<
TimeStamp() <<
" Starting localization of orbitals" << std::flush;
422 Index maxrow, maxcol;
423 double max_penalty =
PM_penalty_.maxCoeff(&maxrow, &maxcol);
426 <<
"maximum of penalty function: " << max_penalty << std::flush;
431 <<
"Orbitals to be changed: " << maxrow <<
" " << maxcol << std::flush;
436 Eigen::MatrixX2d rotated_orbs =
rotateorbitals(max_orbs, maxrow, maxcol);
445 throw std::runtime_error(
446 "Localization with Jacobi-Sweeps did not converge");
449 << iteration + 1 <<
" iterations" << std::flush;
458 auto [LMOS, LMOS_energies] =
sort_lmos(energies);
460 for (
Index i = 0; i < LMOS_energies.size(); i++) {
462 << (boost::format(
" LMO index = %1$4i Energy = %2$10.5f Hartree ") %
463 (i) % LMOS_energies(i))
473 const Eigen::VectorXd &energies) {
474 Eigen::VectorXd LMOS_energies(energies.size());
478 std::vector<std::pair<double, int>> vp;
482 for (
int i = 0; i < energies.size(); ++i) {
483 vp.push_back(std::make_pair(energies(i), i));
486 std::sort(vp.begin(), vp.end());
488 for (
Index i = 0; i < energies.size(); i++) {
489 LMOS_energies(i) = vp[i].first;
493 return {LMOS, LMOS_energies};
509 Eigen::MatrixXd norm =
511 Eigen::MatrixXd check_norm =
512 norm - Eigen::MatrixXd::Identity(norm.rows(), norm.cols());
513 bool not_orthonormal = (check_norm.cwiseAbs().array() > 1
e-5).any();
514 if (not_orthonormal) {
516 <<
" WARNING: Localized orbtials are not "
517 "orthonormal. Proceed with caution! "
520 for (
Index i = 0; i < norm.rows(); i++) {
521 for (
Index j = 0; j < norm.cols(); j++) {
522 if (std::abs(check_norm(i, j)) > 1
e-5) {
525 << (boost::format(
" Element (%1$4i,%2$4i) = %3$8.2e") % (i) % j %
542 asin(
B_(s, t) / sqrt((
A_(s, t) *
A_(s, t)) + (
B_(s, t) *
B_(s, t))));
543 Eigen::MatrixX2d rotatedorbitals(maxorbs.rows(), 2);
544 rotatedorbitals.col(0) =
545 (std::cos(gamma) * maxorbs.col(0)) + (std::sin(gamma) * maxorbs.col(1));
546 rotatedorbitals.col(1) = -1 * (std::sin(gamma) * maxorbs.col(0)) +
547 (std::cos(gamma) * maxorbs.col(1));
550 return rotatedorbitals;
555 Eigen::RowVectorXd MullikenPop_orb_per_basis =
556 (orbital.asDiagonal() *
overlap_ * orbital.asDiagonal()).colwise().sum();
562 MullikenPop_orb_per_basis.segment(start,
numfuncpatom_[atom_id]).sum();
584#pragma omp parallel for
590#pragma omp parallel for
592 Eigen::MatrixXd s_overlap =
602 A_(s, t) + sqrt((
A_(s, t) *
A_(s, t)) + (
B_(s, t) *
B_(s, t)));
609 const Eigen::MatrixXd &occ_orbitals) {
613 Index noccs = occ_orbitals.cols();
615 std::vector<Eigen::MatrixXd> Qat;
616 for (
Index iat = 0; iat < numatoms; iat++) {
617 Qat.push_back(Eigen::MatrixXd::Zero(noccs, noccs));
625#pragma omp parallel for
626 for (
Index s = 0; s < noccs; s++) {
631 for (
Index iat = 0; iat < numatoms; iat++) {
636#pragma omp parallel for
637 for (
Index s = 0; s < noccs; s++) {
639 Eigen::MatrixXd s_overlap = occ_orbitals.col(s).asDiagonal() *
overlap_;
641 for (
Index t = s + 1; t < noccs; t++) {
643 Eigen::MatrixXd splitwiseMullikenPop_orb_SandT =
644 s_overlap * occ_orbitals.col(t).asDiagonal();
645 Eigen::RowVectorXd MullikenPop_orb_SandT_per_basis =
646 0.5 * (splitwiseMullikenPop_orb_SandT.colwise().sum() +
647 splitwiseMullikenPop_orb_SandT.rowwise().sum().transpose());
653 Qat[atom_id](s, t) = MullikenPop_orb_SandT_per_basis
656 Qat[atom_id](t, s) = Qat[atom_id](s, t);
667 const Eigen::MatrixXd &s_overlap,
Index s,
Index t) {
669 Eigen::MatrixXd splitwiseMullikenPop_orb_SandT =
671 Eigen::RowVectorXd MullikenPop_orb_SandT_per_basis =
672 0.5 * (splitwiseMullikenPop_orb_SandT.colwise().sum() +
673 splitwiseMullikenPop_orb_SandT.rowwise().sum().transpose());
681 double MullikenPop_orb_SandT_per_atom =
682 MullikenPop_orb_SandT_per_basis.segment(start,
numfuncpatom_[atom_id])
685 Ast += MullikenPop_orb_SandT_per_atom * MullikenPop_orb_SandT_per_atom -
691 Bst += MullikenPop_orb_SandT_per_atom *
697 Eigen::Vector2d out(Ast, Bst);
706#pragma omp parallel for
708 if (s == orb1 || s == orb2) {
717#pragma omp parallel for
719 Eigen::MatrixXd s_overlap =
725 if (s == orb1 || s == orb2 || t == orb1 || t == orb2) {
731 A_(s, t) + sqrt((
A_(s, t) *
A_(s, t)) + (
B_(s, t) *
B_(s, t)));
const std::vector< Index > & getFuncPerAtom() const
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
container for molecular orbitals
Index getNumberOfAlphaElectrons() const
void setLMOs_energies(const Eigen::VectorXd &energies)
void setLMOs(const Eigen::MatrixXd &matrix)
const tools::EigenSystem & MOs() const
const AOBasis & getDftBasis() const
Eigen::VectorXcd find_complex_roots(const Eigen::VectorXcd &coeff) const
double inner_prod(const Eigen::MatrixXd &A, const Eigen::MatrixXd &B) const
Eigen::MatrixXd MullikenPop_orb_per_atom_
void check_orthonormality()
void computePML(Orbitals &orbitals)
Eigen::VectorXd calculate_lmo_energies(const Orbitals &orbitals)
void computePML_UT(Orbitals &orbitals)
Eigen::Vector2d offdiag_penalty_elements(const Eigen::MatrixXd &s_overlap, Index s, Index t)
std::pair< Eigen::MatrixXd, Eigen::VectorXd > sort_lmos(const Eigen::VectorXd &energies)
Eigen::MatrixXd PM_penalty_
void update_penalty(Index s, Index t)
double find_smallest_step(const Eigen::VectorXd &coeff) const
void computePML_JS(Orbitals &orbitals)
Eigen::MatrixXcd companion_matrix(const Eigen::VectorXcd &coeff) const
Eigen::VectorXd fit_polynomial(const Eigen::VectorXd &x, const Eigen::VectorXd &y) const
Eigen::MatrixX2d rotateorbitals(const Eigen::MatrixX2d &maxorbs, Index s, Index t)
Eigen::MatrixXd localized_orbitals_
std::vector< Eigen::MatrixXd > setup_pop_matrices(const Eigen::MatrixXd &occ_orbitals)
Eigen::VectorXd pop_per_atom(const Eigen::VectorXd &orbital)
double convergence_limit_
std::vector< Index > numfuncpatom_
std::pair< double, Eigen::MatrixXd > cost_derivative(const Eigen::MatrixXd &W, const std::vector< Eigen::MatrixXd > &Sat_all, const Index nat) const
Eigen::MatrixXd rotate_W(const double step, const Eigen::MatrixXd &W, const Eigen::VectorXcd &eval, const Eigen::MatrixXcd &evec) const
double cost(const Eigen::MatrixXd &W, const std::vector< Eigen::MatrixXd > &Sat_all, const Index nat) const
Timestamp returns the current time as a string Example: cout << TimeStamp()
#define XTP_LOG(level, log)
base class for all analysis tools