iterative-solver 0.0
 
Loading...
Searching...
No Matches
MetricChecker.h
1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_METRICCHECKER_H
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_METRICCHECKER_H
3#include <Eigen/Dense>
4#include <molpro/linalg/array/Span.h>
5#include <molpro/linalg/itsolv/ArrayHandlers.h>
6#include <molpro/linalg/itsolv/subspace/IXSpace.h>
7#include <molpro/linalg/itsolv/subspace/Matrix.h>
8#include <molpro/linalg/itsolv/subspace/util.h>
9#include <molpro/linalg/itsolv/wrap.h>
10#include <molpro/linalg/scalar_traits.h>
11
12#include <cmath>
13#include <format>
14#include <optional>
15#include <stdexcept>
16#include <utility>
17
19
27template <class R, class Q, class P>
29public:
32
39
43
46 void reset() {
47 m_reference.reset();
48 m_pp_factor.reset();
49 }
50
52 void reset_pspace() { m_pp_factor.reset(); }
53
68 const Q* q = nullptr;
69 const Q* metric_action_q = nullptr;
70 value_type qmq = 0; // <q|M q>
71 if (xspace.stores_metric_actions()) {
72 if (xspace.dimensions().nQ == 0)
73 return;
74 q = &xspace.cparamsq().front().get();
75 metric_action_q = &xspace.cmetric_actionsq().front().get();
76 qmq = xspace.data.at(subspace::EqnData::M)(xspace.dimensions().oQ(), xspace.dimensions().oQ());
77 } else if (m_reference) {
78 q = &m_reference->q;
79 metric_action_q = &m_reference->metric_action_q;
80 qmq = m_reference->qmq;
81 } else
82 return;
83 // <x|M q> and <M x|q> from one pass over q and M q
84 auto left = params;
85 left.insert(left.end(), metric_actions.begin(), metric_actions.end());
86 const auto overlaps =
87 subspace::util::overlap(left, CVecRef<Q>{std::cref(*metric_action_q), std::cref(*q)}, handlers.rq());
88 for (size_t i = 0; i < params.size(); ++i) {
89 const auto xmq = overlaps(i, 0);
90 const auto mxq = overlaps(params.size() + i, 1);
91 // the size of these elements, which the metric bounds with its values on x and q
92 const auto scale = std::sqrt(std::abs(handlers.rr().dot(params[i], metric_actions[i]) * qmq));
93 if (std::abs(xmq - mxq) > hermiticity_tolerance() * scale)
94 throw std::runtime_error(std::format("The metric is not Hermitian: <x|M q> = {:.6e} differs from <M x|q> = "
95 "{:.6e} for a new parameter x and a subspace vector q",
96 double(std::abs(xmq)), double(std::abs(mxq))));
97 }
98 }
99
104 void remember(const CVecRef<R>& params, const CVecRef<R>& metric_actions, const subspace::IXSpace<R, Q, P>& xspace,
106 if (xspace.stores_metric_actions() || params.empty())
107 return;
108 if (m_reference) {
109 handlers.qr().copy(m_reference->q, params.front());
110 handlers.qr().copy(m_reference->metric_action_q, metric_actions.front());
111 } else
112 m_reference.emplace(Reference{handlers.qr().copy(params.front()), handlers.qr().copy(metric_actions.front())});
113 m_reference->qmq = handlers.rr().dot(params.front(), metric_actions.front());
114 }
115
121 static void check_hermitian(const array::Span<value_type>& matrix, const size_t n) {
122 // the diagonal elements, which have to be real, are compared with themselves
123 for (size_t i = 0; i < n; ++i)
124 for (size_t j = 0; j <= i; ++j)
125 if (std::abs(matrix[i * n + j] - conjugate(matrix[j * n + i])) >
126 hermiticity_tolerance() * std::sqrt(std::abs(matrix[i * n + i] * matrix[j * n + j])))
127 throw std::runtime_error("The metric is not Hermitian: its matrix in the P space is not");
128 }
129
155 const size_t nP) {
156 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>;
157 using row_major_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
158 const auto n = Eigen::Index(metric.rows());
159 const auto np = Eigen::Index(nP);
160 if (np == 0 || np >= n)
161 return check_positive(metric, overlap);
162 const matrix_type m_rows = Eigen::Map<const row_major_type>(metric.data().data(), n, n);
163 const matrix_type m = (m_rows + m_rows.adjoint()) / value_type(2);
164 if (!m_pp_factor || m_pp_factor->rows() != np) {
165 const Eigen::LLT<matrix_type> llt(m.topLeftCorner(np, np));
166 if (llt.info() != Eigen::Success)
167 return check_positive(metric, overlap);
168 m_pp_factor = matrix_type(llt.matrixL());
169 }
170 const matrix_type y = m_pp_factor->template triangularView<Eigen::Lower>().solve(m.topRightCorner(np, n - np));
171 const matrix_type schur = m.bottomRightCorner(n - np, n - np) - y.adjoint() * y;
172 if (Eigen::LLT<matrix_type>(schur).info() == Eigen::Success)
173 return;
174 check_positive(metric, overlap);
175 }
176
178 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>;
179 using row_major_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
180 const auto n = Eigen::Index(metric.rows());
181 if (n == 0)
182 return;
183 const matrix_type m_rows = Eigen::Map<const row_major_type>(metric.data().data(), n, n);
184 const matrix_type m = (m_rows + m_rows.adjoint()) / value_type(2);
185 if (Eigen::LLT<matrix_type>(m).info() == Eigen::Success)
186 return;
187 const matrix_type s = Eigen::Map<const row_major_type>(overlap.data().data(), n, n);
188 const Eigen::SelfAdjointEigenSolver<matrix_type> s_eigen(s);
189 const value_type_abs s_threshold = precision_scaled<value_type>(1e-10) * s_eigen.eigenvalues().maxCoeff();
190 Eigen::Index rank = 0;
191 for (Eigen::Index i = 0; i < n; ++i)
192 if (s_eigen.eigenvalues()(i) > s_threshold)
193 ++rank;
194 if (rank == 0)
195 return;
196 // the eigenvalues come in ascending order, so the retained directions are the trailing ones
197 const auto retained = s_eigen.eigenvectors().rightCols(rank);
198 const matrix_type orthonormal =
199 retained * s_eigen.eigenvalues().tail(rank).cwiseSqrt().cwiseInverse().template cast<value_type>().asDiagonal();
200 const Eigen::SelfAdjointEigenSolver<matrix_type> m_eigen(orthonormal.adjoint() * m * orthonormal,
201 Eigen::EigenvaluesOnly);
202 const value_type_abs lowest = m_eigen.eigenvalues().minCoeff();
203 const value_type_abs largest = m_eigen.eigenvalues().cwiseAbs().maxCoeff();
204 // the rounding errors of the elements of the metric matrix, amplified by the orthonormalisation
205 const value_type_abs rounding = precision_scaled<value_type>(1e-13) * m.norm() / s_eigen.eigenvalues()(n - rank);
207 throw std::runtime_error(std::format("The metric is not positive definite: it has the Rayleigh quotient {:.3e} "
208 "on the subspace, against a largest one of {:.3e}",
209 double(lowest), double(largest)));
210 }
211
212private:
214 struct Reference {
215 Q q;
216 Q metric_action_q;
217 value_type qmq = 0;
218 };
219 std::optional<Reference> m_reference;
221 std::optional<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> m_pp_factor;
222};
223
224} // namespace molpro::linalg::itsolv::detail
225
226#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_METRICCHECKER_H
decltype(value_type_L{} *value_type_R{}) value_type
Definition ArrayHandler.h:182
decltype(check_abs< value_type >()) value_type_abs
Definition ArrayHandler.h:183
Non-owning container taking a pointer to the data buffer and its size and exposing routines for itera...
Definition Span.h:31
Class, containing a collection of array handlers used in IterativeSolver Provides a Builder sub-class...
Definition ArrayHandlers.h:25
Checks that the metric of a generalised problem has the properties that its kind declares,...
Definition MetricChecker.h:28
static void check_positive(const subspace::Matrix< value_type > &metric, const subspace::Matrix< value_type > &overlap)
Definition MetricChecker.h:177
static void check_hermitian(const array::Span< value_type > &matrix, const size_t n)
Checks that the metric matrix in the P space is Hermitian.
Definition MetricChecker.h:121
typename array::ArrayHandler< R, R >::value_type_abs value_type_abs
Definition MetricChecker.h:31
void remember(const CVecRef< R > &params, const CVecRef< R > &metric_actions, const subspace::IXSpace< R, Q, P > &xspace, ArrayHandlers< R, Q, P > &handlers)
Keeps the first new parameter and its action of the metric for the next check_hermitian(),...
Definition MetricChecker.h:104
void reset()
Definition MetricChecker.h:46
static value_type_abs hermiticity_tolerance()
The relative tolerance of the checks of Hermiticity, calibrated for double precision and rescaled to ...
Definition MetricChecker.h:38
void check_hermitian(const CVecRef< R > &params, const CVecRef< R > &metric_actions, const subspace::IXSpace< R, Q, P > &xspace, ArrayHandlers< R, Q, P > &handlers) const
Checks that the metric is Hermitian on new parameters before they are added to the subspace.
Definition MetricChecker.h:66
void reset_pspace()
Forgets the factor of the metric matrix in the P space kept by check_positive_subspace(),...
Definition MetricChecker.h:52
static value_type_abs positivity_tolerance()
Definition MetricChecker.h:42
void check_positive_subspace(const subspace::Matrix< value_type > &metric, const subspace::Matrix< value_type > &overlap, const size_t nP)
Checks that the metric matrix of the subspace is consistent with a positive (semi)definite metric.
Definition MetricChecker.h:154
typename array::ArrayHandler< R, R >::value_type value_type
Definition MetricChecker.h:30
Full subspace.
Definition IXSpace.h:21
SubspaceData< value_type > data
Equation data in the subspace.
Definition IXSpace.h:30
virtual CVecRef< Q > cparamsq() const =0
bool stores_metric_actions() const
Whether the actions of the metric on the Q and D vectors are kept.
Definition IXSpace.h:65
virtual const Dimensions & dimensions() const =0
virtual CVecRef< Q > cmetric_actionsq() const =0
Actions of the metric on the Q space parameters, empty unless there is a metric.
Matrix container that allows simple data access, slicing, copying and resizing without loosing data.
Definition Matrix.h:32
Definition helper-dispatch.h:224
auto overlap(const CVecRef< R > &left, const CVecRef< Q > &right, array::ArrayHandler< Z, W > &handler) -> std::enable_if_t< detail::Z_and_W_are_one_of_R_and_Q< R, Q, Z, W >, Matrix< typename array::ArrayHandler< Z, W >::value_type > >
Calculates overlap matrix between left and right vectors.
Definition util.h:57
std::unique_ptr< LinearEigensystem< R, Q, P > > create_LinearEigensystem(const LinearEigensystemOptions &options, const std::shared_ptr< ArrayHandlers< R, Q, P > > &handlers=std::make_shared< molpro::linalg::itsolv::ArrayHandlers< R, Q, P > >())
Definition SolverFactory.h:111
std::vector< std::reference_wrapper< const A > > CVecRef
Definition wrap.h:14
T conjugate(const T &x)
Complex conjugate, staying within the scalar type; the identity for a real type.
Definition scalar_traits.h:47
size_t nQ
Definition Dimensions.h:13
size_t oQ() const
offset of the Q block
Definition Dimensions.h:18