106 const auto start = std::chrono::steady_clock::now();
107 const Index n = A.rows();
108 nroots = std::min(nroots, n);
112 const Index nwork = std::min(n, nroots + std::max<Index>(2, nroots / 4));
113 const Index max_subspace = std::min(
114 n, std::max(
opt_.max_subspace > 0 ?
opt_.max_subspace : 10 * nroots,
119 const Eigen::VectorXd adiag = A.diagonal();
120 const Eigen::VectorXd bdiag = B.diagonal();
121 std::vector<Index> order(
static_cast<std::size_t
>(n));
122 std::iota(order.begin(), order.end(), 0);
123 auto estimate = [&](
Index i) {
124 return std::max(0.0, (adiag(i) - bdiag(i)) * (adiag(i) + bdiag(i)));
126 std::stable_sort(order.begin(), order.end(), [&](
Index i,
Index j) {
127 return estimate(i) < estimate(j);
129 const Index nguess = std::min(n, 2 * nwork);
130 Eigen::MatrixXd
V = Eigen::MatrixXd::Zero(n, nguess);
131 for (
Index i = 0; i < nguess; ++i) {
132 V(order[std::size_t(i)], i) = 1.0;
139 <<
TimeStamp() <<
" Full BSE (Hermitian form, " << n <<
" pairs, "
140 << nroots <<
" roots, tolerance " <<
opt_.tolerance <<
")"
143 Eigen::VectorXd omega;
148 bool converged =
false;
153 const Eigen::MatrixXd R1 =
P * x - XmY * omega.asDiagonal();
154 const Eigen::MatrixXd R2 = Q * y - XpY * omega.asDiagonal();
155 const Index nr = omega.size();
156 Eigen::VectorXd res(nr);
157 for (
Index i = 0; i < nr; ++i) {
158 res(i) = std::max(R1.col(i).norm() / XpY.col(i).norm(),
159 R2.col(i).norm() / XmY.col(i).norm());
166 auto Done = [&](
Index i) {
167 return res(i) < (i < nroots ? 1.0 : 10.0) *
opt_.tolerance;
170 for (
Index i = 0; i < nroots; ++i) {
171 nconv += Done(i) ? 1 : 0;
173 const double guard_res =
174 (nr > nroots) ? res.tail(nr - nroots).maxCoeff() : 0.0;
175 const bool all_done =
176 nconv == nroots && guard_res < 100.0 *
opt_.tolerance;
180 " iter %1$3d subspace %2$5d max residual "
181 "%3$4.2e converged %4$4d/%5$d guard roots %6$4.2e") %
182 iterations_ %
V.cols() % res.head(nroots).maxCoeff() % nconv %
193 std::vector<Eigen::VectorXd> directions;
194 for (
Index i = 0; i < nr; ++i) {
198 const double w = omega(i);
200 adiag.array().square() - bdiag.array().square() - w * w;
201 det = det.sign() * det.abs().max(1
e-4) +
202 (det == 0.0).cast<double>() * 1
e-4;
203 const Eigen::ArrayXd r1 = R1.col(i).array();
204 const Eigen::ArrayXd r2 = R2.col(i).array();
205 directions.push_back(
206 (-((adiag - bdiag).array() * r1 + w * r2) / det).matrix());
207 directions.push_back(
208 (-(w * r1 + (adiag + bdiag).array() * r2) / det).matrix());
210 Eigen::MatrixXd T(n,
Index(directions.size()));
211 for (
Index i = 0; i < T.cols(); ++i) {
212 T.col(i) = directions[std::size_t(i)];
215 if (
V.cols() + T.cols() > max_subspace) {
217 Eigen::VectorXd omega_keep;
218 Eigen::MatrixXd x_keep;
219 Eigen::MatrixXd y_keep;
224 const Index before =
V.cols();
226 if (
V.cols() == before) {
229 <<
" Full BSE: no new search directions, stopping with max "
231 << res.head(nroots).maxCoeff() << std::flush;
240 <<
TimeStamp() <<
" WARNING: full BSE not converged after "
241 <<
opt_.max_iterations <<
" iterations" << std::flush;
247 const Index nout = std::min(nroots,
Index(omega.size()));
249 result.
eigenvectors() = 0.5 * (XpY + XmY).leftCols(nout);
252 std::chrono::duration<double>(std::chrono::steady_clock::now() - start)
257 <<
" iterations, " <<
applications_ <<
" vectors through A and B, "
258 <<
seconds <<
" s" << std::flush;
267 void Extend(
const OpA& A,
const OpB& B, Eigen::MatrixXd T, Eigen::MatrixXd&
P,
268 Eigen::MatrixXd& Q, Eigen::MatrixXd&
V) {
269 const bool fresh = (
P.cols() == 0);
270 Eigen::MatrixXd basis = fresh ? Eigen::MatrixXd(
V.rows(), 0) :
V;
271 std::vector<Index> kept;
272 Eigen::MatrixXd accepted(
V.rows(), 0);
273 for (
Index i = 0; i < T.cols(); ++i) {
274 Eigen::VectorXd t = T.col(i);
275 const double norm0 = t.norm();
279 for (
int pass = 0; pass < 2; ++pass) {
280 if (basis.cols() > 0) {
281 t -= basis * (basis.transpose() * t);
283 if (accepted.cols() > 0) {
284 t -= accepted * (accepted.transpose() * t);
287 const double norm = t.norm();
288 if (norm < 1
e-8 * norm0 || norm < 1
e-12) {
291 accepted.conservativeResize(Eigen::NoChange, accepted.cols() + 1);
292 accepted.col(accepted.cols() - 1) = t / norm;
294 if (accepted.cols() == 0) {
296 V.resize(
V.rows(), 0);
300 const Eigen::MatrixXd At = A.matmul(accepted);
301 const Eigen::MatrixXd Bt = B.matmul(accepted);
310 const Index k = accepted.cols();
311 V.conservativeResize(Eigen::NoChange, m + k);
312 P.conservativeResize(Eigen::NoChange, m + k);
313 Q.conservativeResize(Eigen::NoChange, m + k);
314 V.rightCols(k) = accepted;
315 P.rightCols(k) = At + Bt;
316 Q.rightCols(k) = At - Bt;
320 const Eigen::MatrixXd& Q,
Index nroots,
321 Eigen::VectorXd& omega, Eigen::MatrixXd& x,
322 Eigen::MatrixXd& y)
const {
323 Eigen::MatrixXd Mp =
V.transpose() *
P;
324 Eigen::MatrixXd Mm =
V.transpose() * Q;
325 Mp = 0.5 * (Mp + Mp.transpose()).eval();
326 Mm = 0.5 * (Mm + Mm.transpose()).eval();
327 Eigen::LLT<Eigen::MatrixXd> llt(Mm);
328 if (llt.info() != Eigen::Success) {
329 throw std::runtime_error(
330 "FullBSEDavidson: projected A-B is not positive definite");
332 const Eigen::MatrixXd
L = llt.matrixL();
333 Eigen::MatrixXd
H =
L.transpose() * Mp *
L;
334 H = 0.5 * (
H +
H.transpose()).eval();
335 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(
H);
336 const Index nr = std::min(nroots,
Index(es.eigenvalues().size()));
337 if (es.eigenvalues()(0) <= 0.0) {
338 throw std::runtime_error(
339 "FullBSEDavidson: non-positive root, A+B is not positive definite "
340 "(instability of the reference)");
342 omega = es.eigenvalues().head(nr).cwiseSqrt();
343 const Eigen::MatrixXd u = es.eigenvectors().leftCols(nr);
345 x =
L * u * omega.cwiseSqrt().cwiseInverse().asDiagonal();
346 y =
L.transpose().triangularView<Eigen::Upper>().solve(u) *
347 omega.cwiseSqrt().asDiagonal();