iterative-solver 0.0
IterativeSolverTemplate.h
1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_ITERATIVESOLVERTEMPLATE_H
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_ITERATIVESOLVERTEMPLATE_H
3#include <molpro/Profiler.h>
4#include <molpro/iostream.h>
5#include <molpro/linalg/itsolv/IterativeSolver.h>
6#include <molpro/linalg/itsolv/Logger.h>
7#include <molpro/linalg/itsolv/subspace/ISubspaceSolver.h>
8#include <molpro/linalg/itsolv/subspace/IXSpace.h>
9#include <molpro/linalg/itsolv/subspace/Matrix.h>
10#include <molpro/linalg/itsolv/subspace/util.h>
11#include <molpro/linalg/itsolv/util.h>
12#include <molpro/linalg/itsolv/wrap.h>
13#include <molpro/profiler/Profiler.h>
14
15#include <cassert>
16#include <cmath>
17#include <format>
18#include <iostream>
19#include <optional>
20#include <sstream>
21
22namespace molpro::linalg::itsolv {
23namespace detail {
24
25inline std::vector<std::pair<size_t, size_t>> parameter_batches(const size_t nsol, const size_t nparam) {
26 auto batches = std::vector<std::pair<size_t, size_t>>{};
27 if (nparam && nsol) {
28 auto n_batch = nsol / nparam + (nsol % nparam ? 1 : 0);
29 for (size_t ib = 0, start_sol = 0, end_sol = 0; ib < n_batch; ++ib, start_sol = end_sol) {
30 end_sol = std::min(start_sol + nparam, nsol);
31 batches.emplace_back(start_sol, end_sol);
32 }
33 }
34 return batches;
35}
36
37template <class R, class Q, class P, typename value_type>
38void construct_solution(const VecRef<R>& params, const std::vector<int>& roots,
39 const subspace::Matrix<value_type>& solutions,
40 const std::vector<std::reference_wrapper<P>>& pparams,
41 const std::vector<std::reference_wrapper<Q>>& qparams,
42 const std::vector<std::reference_wrapper<Q>>& dparams, size_t oP, size_t oQ, size_t oD,
43 ArrayHandlers<R, Q, P>& handlers) {
44 auto prof = molpro::Profiler::single();
45 prof->start("get rd_mat");
46 if (roots.empty())
47 return;
48 assert(params.size() >= roots.size());
49 for (size_t i = 0; i < roots.size(); ++i) {
50 handlers.rr().fill(0, params.at(i));
51 }
52 subspace::Matrix<value_type> rp_mat(std::make_pair(pparams.size(), roots.size())),
53 rq_mat(std::make_pair(qparams.size(), roots.size())), rd_mat(std::make_pair(dparams.size(), roots.size()));
54 for (size_t i = 0; i < roots.size(); ++i) {
55 for (size_t j = 0; j < pparams.size(); ++j) {
56 rp_mat(j, i) = solutions(roots[i], oP + j);
57 }
58 for (size_t j = 0; j < qparams.size(); ++j) {
59 rq_mat(j, i) = solutions(roots[i], oQ + j);
60 }
61 for (size_t j = 0; j < dparams.size(); ++j) {
62 rd_mat(j, i) = solutions(roots[i], oD + j);
63 }
64 }
65 prof->stop();
66 handlers.rp().gemm_outer(rp_mat, cwrap(pparams), params);
67 handlers.rq().gemm_outer(rq_mat, cwrap(qparams), params);
68 handlers.rq().gemm_outer(rd_mat, cwrap(dparams), params);
69}
70
71template <typename T>
72std::vector<std::vector<T>> construct_vectorP(const std::vector<int>& roots, const subspace::Matrix<T>& solutions,
73 const size_t oP, const size_t nP) {
74 auto vectorP = std::vector<std::vector<T>>{};
75 for (auto root : roots) {
76 vectorP.emplace_back();
77 for (size_t j = 0; j < nP; ++j)
78 vectorP.back().push_back(solutions(root, oP + j));
79 }
80 return vectorP;
81}
82
83template <class R>
84void normalise(const size_t n_roots, const VecRef<R>& params, const VecRef<R>& actions,
85 array::ArrayHandler<R, R>& handler, Logger& logger) {
86 assert(params.size() >= n_roots && actions.size() >= n_roots);
87 // a solution shorter than this is taken to be null; the threshold follows the working precision
88 const auto norm_thresh = precision_scaled<typename array::ArrayHandler<R, R>::value_type_abs>(1e-14);
89 for (size_t i = 0; i < n_roots; ++i) {
90 auto dot = handler.dot(params.at(i), params.at(i));
91 dot = std::sqrt(std::abs(dot));
92 if (dot > norm_thresh) {
93 handler.scal(1. / dot, params.at(i));
94 handler.scal(1. / dot, actions.at(i));
95 } else {
96 logger.warn("solution parameter's length is too small, dot = " + std::format("{:.2e}", double(dot)));
97 }
98 }
99}
100
101template <class R, typename T>
102void update_errors(std::vector<T>& errors, const CVecRef<R>& residual, array::ArrayHandler<R, R>& handler) {
103 assert(residual.size() >= errors.size());
104 for (size_t i = 0; i < errors.size(); ++i) {
105 auto a = handler.dot(residual[i], residual[i]);
106 errors[i] = std::sqrt(std::abs(a));
107 }
108}
109
110template <typename T>
111std::vector<int> select_working_set(const size_t nw, const std::vector<T>& errors, const T threshold,
112 const std::vector<T>& value_errors, const T value_threshold) {
113 auto ordered_errors = std::multimap<T, size_t, std::greater<T>>{};
114 for (size_t i = 0; i < errors.size(); ++i) {
115 if (errors[i] > threshold or (i < value_errors.size() and value_errors[i] > value_threshold))
116 ordered_errors.emplace(errors[i], i);
117 }
118 auto working_set = std::vector<int>{};
119 auto end = (ordered_errors.size() < nw ? ordered_errors.end() : next(begin(ordered_errors), nw));
120 std::transform(begin(ordered_errors), end, std::back_inserter(working_set), [](const auto& el) { return el.second; });
121 std::sort(working_set.begin(), working_set.end());
122 return working_set;
123}
124
125} // namespace detail
126
127namespace log {
128
132struct NewIteration : ContextBase<NewIteration, true, int, std::vector<double>> {
133 static constexpr const char *name = "NewIteration";
134 static constexpr std::size_t iter = 0;
135 static constexpr std::size_t errors = 1;
136};
137static_assert(context<NewIteration>);
138
142struct IterationReport : ContextBase<IterationReport, true> {
143 static constexpr const char *name = "IterationReport";
144};
145static_assert(context<IterationReport>);
146
150struct SummaryReport : ContextBase<SummaryReport, true> {
151 static constexpr const char *name = "SummaryReport";
152};
153static_assert(context<SummaryReport>);
154
155}
156
162template <template <class, class, class> class Solver, class R, class Q, class P>
163class IterativeSolverTemplate : public Solver<R, Q, P> {
164public:
165 using typename Solver<R, Q, P>::fapply_on_p_type;
166 using typename Solver<R, Q, P>::scalar_type;
167 using typename Solver<R, Q, P>::value_type;
168 using typename Solver<R, Q, P>::value_type_abs;
169 using typename Solver<R, Q, P>::VectorP;
170
174 IterativeSolverTemplate<Solver, R, Q, P>& operator=(const IterativeSolverTemplate<Solver, R, Q, P>&) = delete;
175 IterativeSolverTemplate<Solver, R, Q, P>& operator=(IterativeSolverTemplate<Solver, R, Q, P>&&) noexcept = default;
176
177 void set_logger(std::shared_ptr<Logger> logger) override {
178 assert(logger);
179 m_logger = std::move(logger);
180 m_xspace->set_logger(m_logger);
181 m_subspace_solver->set_logger(m_logger);
182 }
183
184 Logger &logger() override { return *m_logger; }
185
186 int add_vector(const VecRef<R>& parameters, const VecRef<R>& actions) override {
187 auto outer = profiler()->push("itsolv::add_vector");
188 auto prof = molpro::Profiler::single();
189 m_logger->trace("IterativeSolverTemplate::add_vector iteration = ", m_stats->iterations);
190 m_logger->debug("IterativeSolverTemplate::add_vector size of {params, actions, working_set} = ",
191 parameters.size(), actions.size(), m_working_set.size());
192 if (m_xspace->dimensions().nP != 0 && !m_apply_p)
193 throw std::runtime_error(
194 "Solver contains P space but no valid apply_p function. Make sure add_p was called correctly.");
195 auto nW = std::min(m_working_set.size(), parameters.size());
196 auto cwparams = cwrap(begin(parameters), begin(parameters) + nW);
197 auto cwactions = cwrap(begin(actions), begin(actions) + nW);
198 m_stats->r_creations += nW;
199 prof->start("update_qspace");
200 m_xspace->update_qspace(cwparams, cwactions);
201 m_stats->q_creations += 2 * nW;
202 prof->stop();
203 prof->start("solve_and_generate_working_set");
204 auto working_set = solve_and_generate_working_set(parameters, actions);
205 prof->stop();
207 this->m_end_iteration_needed = true;
208 return working_set;
209 }
210
211 int add_vector(std::vector<R>& parameters, std::vector<R>& actions) override {
212 return add_vector(wrap(parameters), wrap(actions));
213 }
214 int add_vector(R& parameters, R& actions, value_type value = 0) override {
215 return add_vector(wrap_arg(parameters), wrap_arg(actions));
216 }
217
218 // FIXME Currently only works if called on an empty subspace. Either enforce it or generalise.
219 size_t add_p(const CVecRef<P>& pparams, const array::Span<value_type>& pp_action_matrix, const VecRef<R>& parameters,
220 const VecRef<R>& actions, fapply_on_p_type apply_p) override {
221 auto prof = profiler()->push("itsolv::add_p");
222 if (not pparams.empty() and pparams.size() < n_roots())
223 throw std::runtime_error("P space must be empty or at least as large as number of roots sought");
224 if (apply_p)
225 m_apply_p = std::move(apply_p);
226 m_xspace->update_pspace(pparams, pp_action_matrix);
227 auto working_set = solve_and_generate_working_set(parameters, actions);
229 return working_set;
230 };
231
232 void clearP() override {}
233
234 void solution(const std::vector<int>& roots, const VecRef<R>& parameters, const VecRef<R>& residual) override {
235 // auto prof = profiler()->push("itsolv::solution"); // FIXME two profilers
236 auto prof = molpro::Profiler::single();
237 check_consistent_number_of_roots_and_solutions(roots, parameters.size());
238 prof->start("construct_solution (parameters)");
239 detail::construct_solution(parameters, roots, m_subspace_solver->solutions(), m_xspace->paramsp(),
240 m_xspace->paramsq(), m_xspace->paramsd(), m_xspace->dimensions().oP,
241 m_xspace->dimensions().oQ, m_xspace->dimensions().oD, *m_handlers);
242 prof->stop();
243 prof->start("construct_solution (residual)");
244 detail::construct_solution(residual, roots, m_subspace_solver->solutions(), {}, m_xspace->actionsq(),
245 m_xspace->actionsd(), m_xspace->dimensions().oP, m_xspace->dimensions().oQ,
246 m_xspace->dimensions().oD, *m_handlers);
247 prof->stop();
248 prof->start("apply");
249 auto pvectors = detail::construct_vectorP(roots, m_subspace_solver->solutions(), m_xspace->dimensions().oP,
250 m_xspace->dimensions().nP);
252 detail::normalise(roots.size(), parameters, residual, m_handlers->rr(), *m_logger);
253 if (m_apply_p)
254 m_apply_p(pvectors, m_xspace->cparamsp(), residual);
255 construct_residual(roots, cwrap(parameters), residual);
257 prof->stop();
258 };
259
260 void solution(const std::vector<int>& roots, std::vector<R>& parameters, std::vector<R>& residual) override {
261 return solution(roots, wrap(parameters), wrap(residual));
262 }
263 void solution(R& parameters, R& residual) override {
264 return solution(std::vector<int>(1, 0), wrap_arg(parameters), wrap_arg(residual));
265 }
266
267 void solution_params(const std::vector<int>& roots, std::vector<R>& parameters) override {
268 return solution_params(roots, wrap(parameters));
269 }
270
271 void solution_params(const std::vector<int>& roots, const VecRef<R>& parameters) override {
272 check_consistent_number_of_roots_and_solutions(roots, parameters.size());
273 detail::construct_solution(parameters, roots, m_subspace_solver->solutions(), m_xspace->paramsp(),
274 m_xspace->paramsq(), m_xspace->paramsd(), m_xspace->dimensions().oP,
275 m_xspace->dimensions().oQ, m_xspace->dimensions().oD, *m_handlers);
276 };
277
278 void solution_params(R& parameters) override { return solution_params(std::vector<int>(1, 0), wrap_arg(parameters)); }
279
280 // TODO Implement this
281 std::vector<size_t> suggest_p(const CVecRef<R>& solution, const CVecRef<R>& residual, size_t max_number,
282 value_type_abs threshold) override {
283 return {};
284 }
285
286 const std::vector<int>& working_set() const override { return m_working_set; }
287
288 size_t n_roots() const override { return m_nroots; }
289
290 void set_n_roots(size_t roots) override {
291 m_nroots = roots;
292 m_working_set.resize(roots);
293 std::iota(begin(m_working_set), end(m_working_set), (int)0);
294 }
295
296 void set_options(const Options& options) override {
297 if (options.n_roots)
298 set_n_roots(options.n_roots.value());
299 if (options.convergence_threshold)
300 set_convergence_threshold(options.convergence_threshold.value());
301 if (options.verbosity)
302 set_verbosity(options.verbosity.value());
303 if (options.max_iter)
304 set_max_iter(options.max_iter.value());
305 if (options.max_p)
306 set_max_p(options.max_p.value());
307 if (options.p_threshold)
308 set_p_threshold(options.p_threshold.value());
309 }
310
311 std::shared_ptr<Options> get_options() const override {
312 auto options = std::make_shared<Options>();
313 options->n_roots = n_roots();
314 options->convergence_threshold = convergence_threshold();
315 options->verbosity = get_verbosity();
316 options->max_iter = get_max_iter();
317 options->max_p = get_max_p();
318 options->p_threshold = get_p_threshold();
319 return options;
320 }
321
322 const std::vector<scalar_type>& errors() const override { return m_errors; }
323
324 const Statistics& statistics() const override { return *m_stats; }
325
326 void report(std::ostream& cout, bool endl = true) const override {
327 cout << "iteration " << m_stats->iterations;
328 if (not m_errors.empty()) {
329 auto it_max_error = std::max_element(m_errors.cbegin(), m_errors.cend());
330 if (n_roots() > 1)
331 cout << ", |residual[" << std::distance(m_errors.cbegin(), it_max_error) << "]| = ";
332 else
333 cout << ", |residual| = ";
334 cout << std::scientific << *it_max_error << std::defaultfloat;
335 }
336 if (endl)
337 cout << std::endl;
338 }
339
340 void iteration_report() const {
341 std::stringstream sstream;
342 report(sstream, false);
343 this->m_logger->info<log::IterationReport>(sstream.str());
344 }
345
346 void summary_report() const {
347 std::stringstream sstream;
348 report(sstream, false);
349 this->m_logger->info<log::SummaryReport>(sstream.str());
350 }
351
352 void report() const override {
353 // We don't know what kind of report this is supposed to be -> default to summary
355 }
356
357 void set_convergence_threshold(value_type_abs thresh) override { m_convergence_threshold = thresh; }
358 value_type_abs convergence_threshold() const override { return m_convergence_threshold; }
359 void set_convergence_threshold_value(value_type_abs thresh) override { m_convergence_threshold_value = thresh; }
360 value_type_abs convergence_threshold_value() const override { return m_convergence_threshold_value; }
361 void set_verbosity(Verbosity v) override { m_verbosity = v; }
362 void set_verbosity(int v) override {
363 if (v == 0) {
364 m_logger->set_verbosity(log::Verbosity::None);
365 m_logger->set_min_severity(log::Severity::Error);
366 } else if (v == 1) {
367 m_logger->set_verbosity(log::Verbosity::None);
368 m_logger->set_min_severity(log::Severity::Warning);
369 } else if (v == 2) {
370 m_logger->set_verbosity(log::Verbosity::Info);
371 m_logger->set_min_severity(log::Severity::Normal);
372 } else if (v == 3) {
373 m_logger->set_verbosity(log::Verbosity::Debug);
374 m_logger->set_min_severity(log::Severity::Normal);
375 } else {
376 m_logger->set_verbosity(log::Verbosity::Trace);
377 m_logger->set_min_severity(log::Severity::Normal);
378 }
379 }
380 Verbosity get_verbosity() const override { return m_verbosity.value_or(Verbosity::None); }
381 void set_max_iter(int n) override { m_max_iter = n; }
382 int get_max_iter() const override { return m_max_iter; }
383 void set_max_p(int n) override { m_max_p = n; }
384 int get_max_p() const override { return m_max_p; }
385 void set_p_threshold(value_type_abs threshold) override { m_p_threshold = threshold; }
386 value_type_abs get_p_threshold() const override { return m_p_threshold; }
388 const subspace::Dimensions& dimensions() const override { return m_xspace->dimensions(); }
389 scalar_type value() const override {
390 return m_xspace->data.count(subspace::EqnData::value) > 0 ? m_xspace->data[subspace::EqnData::value](0, 0)
391 : nan("molpro::linalg::itsolv::IterativeSolver::value");
392 }
393 // void set_profiler(molpro::profiler::Profiler& profiler) override { m_profiler.reset(&profiler); }
395 m_profiler = std::shared_ptr<molpro::profiler::Profiler>(&profiler);
396 }
397 const std::shared_ptr<molpro::profiler::Profiler>& profiler() const override { return m_profiler; }
398
399 bool solve(const VecRef<R>& parameters, const VecRef<R>& actions, const Problem<R>& problem,
400 bool generate_initial_guess = false) override {
401 if (parameters.empty())
402 throw std::runtime_error("Empty container passed to IterativeSolver::solve()");
403 if (parameters.size() != actions.size())
404 throw std::runtime_error("Inconsistent container sizes in IterativeSolver::solve()");
405 if (this->m_verbosity == Verbosity::None) {
406 // Backwards compatibility
407 this->m_logger->set_verbosity(log::Verbosity::None);
408 }
409 if (this->m_verbosity == Verbosity::Detailed) {
410 this->m_logger->set_verbosity(log::Verbosity::Trace);
411 this->m_logger->enable_data_dumps(true);
412 }
413 bool use_diagonals = problem.diagonals(actions.at(0));
414 std::unique_ptr<Q> diagonals;
415 if (use_diagonals)
416 diagonals.reset(new Q{m_handlers->qr().copy(actions.at(0))});
417// std::cout << "solve() generate_initial_guess "<<generate_initial_guess<<", roots "<<this->n_roots()<<std::endl;
418 if (generate_initial_guess) {
419 if (not use_diagonals)
420 throw std::runtime_error("Default initial guess requested, but diagonal elements are not available");
421 auto guess = m_handlers->qq().select(parameters.size(), *diagonals);
422 size_t root = 0;
423 if (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Summary) {
424 this->m_logger->info("Initial guess generated from diagonal elements");
425 }
426 for (const auto& g : guess) {
427 m_handlers->rp().copy(parameters[root], P{{g.first, 1}});
428 if (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Detailed) {
429 m_logger->debug("-> initial guess with index ", g.first);
430 }
431 root++;
432 }
433 }
434 int nwork = parameters.size();
435 std::vector<P> pspace;
436 if (use_diagonals and m_max_p > 0) {
437 auto selectp = m_handlers->qq().select(m_max_p, *diagonals);
438 if (!selectp.empty()) {
439 // selectp is keyed by index, not value; find the smallest selected
440 // diagonal explicitly before applying the threshold.
441 auto min_val = std::min_element(selectp.begin(), selectp.end(),
442 [](const auto& a, const auto& b) { return a.second < b.second; })
443 ->second;
444 for (auto s = selectp.begin(); s != selectp.end();) {
445 if (s->second > min_val + m_p_threshold)
446 s = selectp.erase(s);
447 else
448 ++s;
449 }
450 }
451 if (!selectp.empty() && (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Summary)) {
452 this->m_logger->info("P-space dimension, threshold and limit", selectp.size(), m_p_threshold, m_max_p);
453 }
454 if (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Detailed) {
455 for (const auto& s : selectp) {
456 this->m_logger->debug(std::format("P space element {}: {:.6e}", s.first, double(s.second)));
457 }
458 }
459 for (const auto& s : selectp)
460 pspace.emplace_back((P){{s.first, 1}});
461 fapply_on_p_type apply_on_p = [&problem](const std::vector<std::vector<value_type>>& pcoeff,
462 const CVecRef<P>& pparams, const VecRef<R>& actions) {
463 problem.p_action(pcoeff, pparams, actions);
464 };
465 auto action_matrix = problem.pp_action_matrix(pspace);
466 nwork = add_p(cwrap(pspace), array::Span<value_type>(action_matrix.data(), action_matrix.size()), parameters,
467 actions, apply_on_p);
468 }
469 for (auto iter = 0; iter < this->m_max_iter && nwork > 0; iter++) {
470 // the logging context carries the errors as doubles: a log line only needs their magnitude
471 m_logger->info<log::NewIteration>("Iteration, Current errors", iter,
472 std::vector<double>(m_errors.begin(), m_errors.end()));
473 value_type value;
474 if (this->nonlinear()) {
475 value = problem.residual(*parameters.begin(), *actions.begin());
476 nwork = this->add_vector(*parameters.begin(), *actions.begin(), value);
477 } else if (iter > 0 or pspace.empty()) {
478 problem.action(cwrap(parameters.begin(), parameters.begin() + nwork),
479 wrap(actions.begin(), actions.begin() + nwork));
480 nwork = this->add_vector(parameters, actions);
481 }
482 // std::cout << "** nwork="<<nwork<<"use_diagonals="<<use_diagonals<<std::endl;
483 while (this->end_iteration_needed()) {
484 if (nwork > 0) {
485 if (use_diagonals) {
486 m_handlers->rq().copy(parameters.at(0), *diagonals);
487 problem.precondition(wrap(actions.begin(), actions.begin() + nwork), this->working_set_eigenvalues(),
488 parameters.at(0));
489 } else
490 problem.precondition(wrap(actions.begin(), actions.begin() + nwork), this->working_set_eigenvalues());
491 }
492 nwork = this->end_iteration(parameters, actions);
493 }
494 if (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Iteration) {
496 }
497 }
498
499 this->finalize();
500
501 if (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Summary) {
503 }
504 if (!this->m_verbosity.has_value() || this->m_verbosity >= Verbosity::Summary) {
505 if (*std::max_element(m_errors.begin(), m_errors.end()) > m_convergence_threshold) {
506 this->m_logger->warn("Solver has not converged to threshold ", m_convergence_threshold);
507 } else {
508 this->m_logger->info("Solver converged");
509 }
510 }
511 return nwork == 0 and *std::max_element(m_errors.begin(), m_errors.end()) <= m_convergence_threshold;
512 }
513
514 bool solve(R& parameters, R& actions, const Problem<R>& problem, bool generate_initial_guess = false) override {
515 auto wparams = std::vector<std::reference_wrapper<R>>{std::ref(parameters)};
516 auto wactions = std::vector<std::reference_wrapper<R>>{std::ref(actions)};
517 return solve(wparams, wactions, problem, generate_initial_guess);
518 }
519 bool solve(std::vector<R>& parameters, std::vector<R>& actions, const Problem<R>& problem,
520 bool generate_initial_guess = false) override {
521 return solve(wrap(parameters), wrap(actions), problem, generate_initial_guess);
522 }
523
524 bool test_problem(const Problem<R>& problem, R& v0, R& v1, int verbosity, value_type_abs threshold) const override {
525 bool success = true;
526 value_type_abs step = 1e-4;
527 if (this->nonlinear()) {
528 if (!problem.test_parameters(0, v0))
529 return true;
530 auto value0 = problem.residual(v0, v1);
531 if (verbosity > 1) {
532 std::cout << "value0 " << value0 << std::endl;
533 // std::cout << "parameters0 " << v0[0] << "," << v0[1] << ",..." << std::endl;
534 // std::cout << "residual0 " << v1[0] << "," << v1[1] << ",..." << std::endl;
535 }
536 Q parameters0 = m_handlers->qr().copy(v0);
537 Q residual0 = m_handlers->qr().copy(v1);
538 for (int instance = 1; problem.test_parameters(instance, v0); ++instance) {
539 m_handlers->rq().axpy(-1.0, parameters0, v0);
540 m_handlers->rr().scal(1 / std::sqrt(m_handlers->rr().dot(v0, v0)), v0);
541 Q step1 = m_handlers->qr().copy(v0);
542 auto residual_analytic = m_handlers->rq().dot(v0, residual0);
543 m_handlers->rq().copy(v0, parameters0);
544 m_handlers->rq().axpy(-2*step, step1, v0);
545 auto valuem2 = problem.residual(v0, v1);
546 m_handlers->rq().copy(v0, parameters0);
547 m_handlers->rq().axpy(-1*step, step1, v0);
548 auto valuem1 = problem.residual(v0, v1);
549 m_handlers->rq().copy(v0, parameters0);
550 m_handlers->rq().axpy(+1*step, step1, v0);
551 auto valuep1 = problem.residual(v0, v1);
552 m_handlers->rq().copy(v0, parameters0);
553 m_handlers->rq().axpy(+2*step, step1, v0);
554 auto valuep2 = problem.residual(v0, v1);
555 auto residual_numerical = (valuem2 - 8*valuem1 + 8*valuep1 - valuep2) / (12*step);
556 if (verbosity > 1)
557 std::cout << "testing problem class, instance: " << instance << ", numerical: " << residual_numerical <<", analytical: "<<residual_analytic <<", difference: "<<residual_numerical-residual_analytic << std::endl;
558 success = success && std::abs(residual_numerical - residual_analytic) < threshold;
559 }
560 } else {
561 for (int instance = 0; problem.test_parameters(instance, v0); ++instance) {
562 problem.action({v0}, {v1});
563 Q residual = m_handlers->qr().copy(v1);
564 auto norm2_residual = std::sqrt(m_handlers->rr().dot(v1, v1));
565 const value_type_abs scale_factor{10.0};
566 m_handlers->rr().scal(scale_factor, v0);
567 problem.action({v0}, {v1});
568 m_handlers->rq().axpy(-scale_factor, residual, v1);
569 auto norm2 = std::sqrt(m_handlers->rr().dot(v1, v1));
570 success = success && std::abs(norm2 / norm2_residual) < threshold;
571 if (verbosity > 0 or (verbosity > -1 and not success))
572 std::cout << "Length of residual: " << norm2_residual << ", scaling defect: " << norm2 << std::endl;
573 }
574 }
575 return success;
576 }
577
578protected:
580 std::shared_ptr<subspace::ISubspaceSolver<R, Q, P>> solver,
581 std::shared_ptr<ArrayHandlers<R, Q, P>> handlers, std::shared_ptr<Statistics> stats,
582 std::shared_ptr<Logger> logger)
583 : m_handlers(std::move(handlers)), m_xspace(std::move(xspace)), m_subspace_solver(std::move(solver)),
584 m_stats(std::move(stats)), m_logger(std::move(logger)), m_profiler(molpro::Profiler::single()),
585 m_profiler_saved_depth(m_profiler->get_max_depth()) {
586 set_n_roots(1);
587 m_profiler->set_max_depth(options()->parameter("PROFILER_DEPTH", 0));
588 }
589
591 if (molpro::mpi::rank_global() == 0) {
592 auto file = options()->parameter("PROFILER_OUTPUT", "");
593 if (profiler()->get_max_depth() > 0 and
594 std::find_if(file.begin(), file.end(), [](unsigned char ch) { return !std::isspace(ch); }) != file.end())
595 std::ofstream(file) << *profiler() << std::endl;
596 }
597 if (molpro::mpi::rank_global() == 0) {
598 auto file = options()->parameter("PROFILER_DOTGRAPH", "");
599 if (profiler()->get_max_depth() > 0 and
600 std::find_if(file.begin(), file.end(), [](unsigned char ch) { return !std::isspace(ch); }) != file.end())
601 profiler()->dotgraph(file, options()->parameter("PROFILER_THRESHOLD", .01));
602 }
603 molpro::Profiler::single()->set_max_depth(m_profiler_saved_depth);
604 }
605
607 virtual void set_value_errors() {}
609 virtual void construct_residual(const std::vector<int>& roots, const CVecRef<R>& params,
610 const VecRef<R>& actions) = 0;
611 virtual bool linearEigensystem() const { return false; }
620 size_t solve_and_generate_working_set(const VecRef<R>& parameters, const VecRef<R>& action) {
621 auto prof = profiler();
622 auto p = prof->push("itsolv::solve_and_generate_working_set");
623 prof->start("itsolv::solve");
625 prof->stop();
626 prof->start("itsolv::temp_solutions");
627 auto nsol = m_subspace_solver->size();
628 std::vector<std::pair<Q, Q>> temp_solutions{};
629 const auto batches = detail::parameter_batches(nsol, parameters.size());
630 for (const auto& batch : batches) {
631 auto [start_sol, end_sol] = batch;
632 auto roots = std::vector<int>(end_sol - start_sol);
633 std::iota(begin(roots), end(roots), start_sol);
634 solution(roots, parameters, action);
635 auto errors = std::vector<scalar_type>(roots.size(), 0);
637 if (batches.size() > 1) {
638 for (size_t i = 0; i < roots.size(); ++i)
639 temp_solutions.emplace_back(m_handlers->qr().copy(parameters[i]), m_handlers->qr().copy(action[i]));
640 m_stats->q_creations += 2 * roots.size();
641 }
642 m_subspace_solver->set_error(roots, errors);
643 }
644 prof->stop();
646 m_errors = m_subspace_solver->errors();
649 for (size_t i = 0; i < m_working_set.size(); ++i) {
650 size_t root = m_working_set[i];
651 if (batches.size() > 1) {
652 m_handlers->rq().copy(parameters[i], temp_solutions.at(root).first);
653 m_handlers->rq().copy(action[i], temp_solutions.at(root).second);
654 } else {
655 if (root < i)
656 throw std::logic_error("incorrect ordering of roots");
657 if (root > i && root < parameters.size()) {
658 m_handlers->rr().copy(parameters[i], parameters[root]);
659 m_handlers->rr().copy(action[i], action[root]);
660 }
661 }
662 }
663 m_logger->trace("add_vector::errors = ", m_errors);
664 return m_working_set.size();
665 }
666
667 template <typename TTT>
668 void check_consistent_number_of_roots_and_solutions(const std::vector<TTT>& roots, const size_t nparams) {
669 if (roots.size() > nparams)
670 throw std::runtime_error("asking for more roots than parameters");
671 if (!roots.empty() &&
672 size_t(*std::max_element(roots.begin(), roots.end())) >= m_subspace_solver->solutions().size())
673 throw std::runtime_error("asking for more roots than there are solutions");
674 }
675
677
678 void finalize() override {}
679
680
681 std::shared_ptr<ArrayHandlers<R, Q, P>> m_handlers;
682 std::shared_ptr<subspace::IXSpace<R, Q, P>> m_xspace;
683 std::shared_ptr<subspace::ISubspaceSolver<R, Q, P>> m_subspace_solver;
684 std::vector<value_type_abs> m_errors;
685 std::vector<value_type_abs> m_value_errors;
686 std::vector<int> m_working_set;
687 size_t m_nroots{0};
688 value_type_abs m_convergence_threshold{1.0e-8};
690 std::numeric_limits<value_type_abs>::max()};
691 std::shared_ptr<Statistics> m_stats;
692 std::shared_ptr<Logger> m_logger;
693 bool m_normalise_solution = false;
694 fapply_on_p_type m_apply_p = {};
695 std::optional<Verbosity> m_verbosity = {};
696 int m_max_iter = 100;
697 size_t m_max_p = 0;
698 value_type_abs m_p_threshold = std::numeric_limits<value_type_abs>::max();
699private:
700 mutable std::shared_ptr<molpro::profiler::Profiler> m_profiler;
701 int m_profiler_saved_depth;
702protected:
704};
705
706} // namespace molpro::linalg::itsolv
707
708#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_ITERATIVESOLVERTEMPLATE_H
Enhances various operations between pairs of arrays and allows dynamic code injection with uniform in...
Definition: ArrayHandler.h:162
virtual value_type dot(const AL &x, const AR &y)=0
virtual void scal(value_type alpha, AL &x)=0
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
auto & rp()
Definition: ArrayHandlers.h:44
auto & rr()
Definition: ArrayHandlers.h:39
auto & rq()
Definition: ArrayHandlers.h:42
Implements IterativeSolver interface that is common to all solvers.
Definition: IterativeSolverTemplate.h:163
std::vector< value_type_abs > m_value_errors
value errors from the most recent solution
Definition: IterativeSolverTemplate.h:685
void set_convergence_threshold(value_type_abs thresh) override
Definition: IterativeSolverTemplate.h:357
void iteration_report() const
Definition: IterativeSolverTemplate.h:340
void set_profiler(molpro::profiler::Profiler &profiler) override
Definition: IterativeSolverTemplate.h:394
std::optional< Verbosity > m_verbosity
how much output to print in solve()
Definition: IterativeSolverTemplate.h:695
value_type_abs m_p_threshold
threshold for selecting P space
Definition: IterativeSolverTemplate.h:698
size_t add_p(const CVecRef< P > &pparams, const array::Span< value_type > &pp_action_matrix, const VecRef< R > &parameters, const VecRef< R > &actions, fapply_on_p_type apply_p) override
Definition: IterativeSolverTemplate.h:219
value_type_abs convergence_threshold_value() const override
Definition: IterativeSolverTemplate.h:360
int get_max_iter() const override
Definition: IterativeSolverTemplate.h:382
void solution_params(const std::vector< int > &roots, std::vector< R > &parameters) override
Definition: IterativeSolverTemplate.h:267
void report(std::ostream &cout, bool endl=true) const override
Definition: IterativeSolverTemplate.h:326
std::vector< int > m_working_set
indices of roots in the working set
Definition: IterativeSolverTemplate.h:686
value_type_abs convergence_threshold() const override
Definition: IterativeSolverTemplate.h:358
void summary_report() const
Definition: IterativeSolverTemplate.h:346
scalar_type value() const override
Definition: IterativeSolverTemplate.h:389
std::shared_ptr< subspace::IXSpace< R, Q, P > > m_xspace
manages the subspace and associated data
Definition: IterativeSolverTemplate.h:682
void set_n_roots(size_t roots) override
Definition: IterativeSolverTemplate.h:290
std::shared_ptr< ArrayHandlers< R, Q, P > > m_handlers
Array handlers.
Definition: IterativeSolverTemplate.h:681
fapply_on_p_type m_apply_p
function that evaluates effect of action on the P space projection
Definition: IterativeSolverTemplate.h:694
void solution(const std::vector< int > &roots, std::vector< R > &parameters, std::vector< R > &residual) override
Definition: IterativeSolverTemplate.h:260
void set_max_iter(int n) override
Definition: IterativeSolverTemplate.h:381
void check_consistent_number_of_roots_and_solutions(const std::vector< TTT > &roots, const size_t nparams)
Definition: IterativeSolverTemplate.h:668
void set_max_p(int n) override
Definition: IterativeSolverTemplate.h:383
std::shared_ptr< subspace::ISubspaceSolver< R, Q, P > > m_subspace_solver
solves the subspace problem
Definition: IterativeSolverTemplate.h:683
void set_p_threshold(value_type_abs threshold) override
Definition: IterativeSolverTemplate.h:385
std::vector< size_t > suggest_p(const CVecRef< R > &solution, const CVecRef< R > &residual, size_t max_number, value_type_abs threshold) override
Definition: IterativeSolverTemplate.h:281
int get_max_p() const override
Definition: IterativeSolverTemplate.h:384
size_t n_roots() const override
Definition: IterativeSolverTemplate.h:288
std::vector< value_type_abs > m_errors
errors from the most recent solution
Definition: IterativeSolverTemplate.h:684
std::shared_ptr< Options > get_options() const override
Definition: IterativeSolverTemplate.h:311
size_t m_max_p
maximum size of P space
Definition: IterativeSolverTemplate.h:697
const subspace::Dimensions & dimensions() const override
Access dimensions of the subspace.
Definition: IterativeSolverTemplate.h:388
size_t solve_and_generate_working_set(const VecRef< R > &parameters, const VecRef< R > &action)
Solves the subspace problems and selects the working set of roots, returning their parameters and res...
Definition: IterativeSolverTemplate.h:620
const std::shared_ptr< molpro::profiler::Profiler > & profiler() const override
Definition: IterativeSolverTemplate.h:397
IterativeSolverTemplate(std::shared_ptr< subspace::IXSpace< R, Q, P > > xspace, std::shared_ptr< subspace::ISubspaceSolver< R, Q, P > > solver, std::shared_ptr< ArrayHandlers< R, Q, P > > handlers, std::shared_ptr< Statistics > stats, std::shared_ptr< Logger > logger)
Definition: IterativeSolverTemplate.h:579
virtual ~IterativeSolverTemplate()
Definition: IterativeSolverTemplate.h:590
int add_vector(std::vector< R > &parameters, std::vector< R > &actions) override
Definition: IterativeSolverTemplate.h:211
bool test_problem(const Problem< R > &problem, R &v0, R &v1, int verbosity, value_type_abs threshold) const override
Definition: IterativeSolverTemplate.h:524
void solution_params(R &parameters) override
Definition: IterativeSolverTemplate.h:278
virtual bool linearEigensystem() const
Definition: IterativeSolverTemplate.h:611
const Statistics & statistics() const override
Definition: IterativeSolverTemplate.h:324
void solution(R &parameters, R &residual) override
Definition: IterativeSolverTemplate.h:263
size_t m_nroots
number of roots the solver is searching for
Definition: IterativeSolverTemplate.h:687
bool m_end_iteration_needed
whether end_iteration should be called after any preconditioner
Definition: IterativeSolverTemplate.h:703
void solution_params(const std::vector< int > &roots, const VecRef< R > &parameters) override
Definition: IterativeSolverTemplate.h:271
int add_vector(const VecRef< R > &parameters, const VecRef< R > &actions) override
Definition: IterativeSolverTemplate.h:186
void clearP() override
Definition: IterativeSolverTemplate.h:232
bool solve(R &parameters, R &actions, const Problem< R > &problem, bool generate_initial_guess=false) override
Definition: IterativeSolverTemplate.h:514
bool end_iteration_needed() override
Definition: IterativeSolverTemplate.h:676
IterativeSolverTemplate(const IterativeSolverTemplate< Solver, R, Q, P > &)=delete
const std::vector< scalar_type > & errors() const override
Definition: IterativeSolverTemplate.h:322
void report() const override
Definition: IterativeSolverTemplate.h:352
bool m_normalise_solution
whether to normalise the solutions
Definition: IterativeSolverTemplate.h:693
const std::vector< int > & working_set() const override
Definition: IterativeSolverTemplate.h:286
std::shared_ptr< Logger > m_logger
logger
Definition: IterativeSolverTemplate.h:692
void set_convergence_threshold_value(value_type_abs thresh) override
Definition: IterativeSolverTemplate.h:359
virtual void set_value_errors()
Implementation class should overload this to set errors in the current values (e.g....
Definition: IterativeSolverTemplate.h:607
bool solve(std::vector< R > &parameters, std::vector< R > &actions, const Problem< R > &problem, bool generate_initial_guess=false) override
Definition: IterativeSolverTemplate.h:519
void set_logger(std::shared_ptr< Logger > logger) override
Definition: IterativeSolverTemplate.h:177
virtual void construct_residual(const std::vector< int > &roots, const CVecRef< R > &params, const VecRef< R > &actions)=0
Constructs residual for given roots provided their parameters and actions.
void set_options(const Options &options) override
Definition: IterativeSolverTemplate.h:296
int add_vector(R &parameters, R &actions, value_type value=0) override
Definition: IterativeSolverTemplate.h:214
value_type_abs m_convergence_threshold_value
value changes less than this mark a converged solution
Definition: IterativeSolverTemplate.h:689
void finalize() override
Definition: IterativeSolverTemplate.h:678
void set_verbosity(Verbosity v) override
Definition: IterativeSolverTemplate.h:361
bool solve(const VecRef< R > &parameters, const VecRef< R > &actions, const Problem< R > &problem, bool generate_initial_guess=false) override
Definition: IterativeSolverTemplate.h:399
int m_max_iter
maximum number of iterations in solve()
Definition: IterativeSolverTemplate.h:696
std::shared_ptr< Statistics > m_stats
accumulates statistics of operations performed by the solver
Definition: IterativeSolverTemplate.h:691
value_type_abs get_p_threshold() const override
Definition: IterativeSolverTemplate.h:386
Logger & logger() override
Definition: IterativeSolverTemplate.h:184
value_type_abs m_convergence_threshold
residual norms less than this mark a converged solution
Definition: IterativeSolverTemplate.h:688
void set_verbosity(int v) override
Definition: IterativeSolverTemplate.h:362
Verbosity get_verbosity() const override
Definition: IterativeSolverTemplate.h:380
void solution(const std::vector< int > &roots, const VecRef< R > &parameters, const VecRef< R > &residual) override
Definition: IterativeSolverTemplate.h:234
IterativeSolverTemplate(IterativeSolverTemplate< Solver, R, Q, P > &&) noexcept=default
Definition: Logger.h:442
void warn(std::string_view message, Ts &&...args) const
Definition: Logger.h:510
Abstract class defining the problem-specific interface for the simplified solver interface to Iterati...
Definition: IterativeSolver.h:85
virtual void action(const CVecRef< R > &parameters, const VecRef< R > &action) const
Calculate the action of the kernel matrix on a set of parameters. Used by linear solvers,...
Definition: IterativeSolver.h:109
virtual void precondition(const VecRef< R > &residual, const std::vector< value_t > &shift) const
Apply preconditioning to a residual vector in order to predict a step towards the solution.
Definition: IterativeSolver.h:132
virtual bool diagonals(container_t &d) const
Optionally provide the diagonal elements of the underlying kernel. If implemented and returning true,...
Definition: IterativeSolver.h:121
virtual void p_action(const std::vector< std::vector< value_t > > &p_coefficients, const CVecRef< P > &pparams, const VecRef< container_t > &actions) const
Calculate the action of the kernel matrix on a set of vectors in the P space.
Definition: IterativeSolver.h:174
virtual std::vector< value_t > pp_action_matrix(const std::vector< P > &pparams) const
Calculate the kernel matrix in the P space.
Definition: IterativeSolver.h:162
virtual value_t residual(const R &parameters, R &residual) const
Calculate the residual vector. Used by non-linear solvers (NonLinearEquations, Optimize) only.
Definition: IterativeSolver.h:101
virtual bool test_parameters(unsigned int instance, R &parameters) const
Provide values of R vectors for testing the problem class. For use in a non-linear solver,...
Definition: IterativeSolver.h:189
static std::shared_ptr< Profiler > single()
Definition: Logger.h:123
void construct_solution(const VecRef< R > &params, const std::vector< int > &roots, const subspace::Matrix< value_type > &solutions, const std::vector< std::reference_wrapper< P > > &pparams, const std::vector< std::reference_wrapper< Q > > &qparams, const std::vector< std::reference_wrapper< Q > > &dparams, size_t oP, size_t oQ, size_t oD, ArrayHandlers< R, Q, P > &handlers)
Definition: IterativeSolverTemplate.h:38
std::vector< std::vector< T > > construct_vectorP(const std::vector< int > &roots, const subspace::Matrix< T > &solutions, const size_t oP, const size_t nP)
Definition: IterativeSolverTemplate.h:72
void normalise(const size_t n_roots, const VecRef< R > &params, const VecRef< R > &actions, array::ArrayHandler< R, R > &handler, Logger &logger)
Definition: IterativeSolverTemplate.h:84
std::vector< std::pair< size_t, size_t > > parameter_batches(const size_t nsol, const size_t nparam)
Definition: IterativeSolverTemplate.h:25
std::vector< int > select_working_set(const size_t nw, const std::vector< T > &errors, const T threshold, const std::vector< T > &value_errors, const T value_threshold)
Definition: IterativeSolverTemplate.h:111
void update_errors(std::vector< T > &errors, const CVecRef< R > &residual, array::ArrayHandler< R, R > &handler)
Definition: IterativeSolverTemplate.h:102
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:14
auto cwrap(ForwardIt begin, ForwardIt end)
Takes a begin and end iterators and returns a vector of references to each element.
Definition: wrap.h:52
auto wrap_arg(T &&arg, S &&... args) -> std::enable_if_t< std::conjunction_v< std::is_same< decay_t< T >, decay_t< S > >... >, VecRef< decay_t< T > > >
Constructs a vector of reference wrappers with provided arguments.
Definition: wrap.h:135
auto wrap(ForwardIt begin, ForwardIt end)
Takes a begin and end iterators and returns a vector of references to each element.
Definition: wrap.h:32
std::vector< std::reference_wrapper< const A > > CVecRef
Definition: wrap.h:14
std::vector< std::reference_wrapper< A > > VecRef
Definition: wrap.h:11
Verbosity
Specifies how much detail IterativeSolver::solve should write to output.
Definition: Options.h:12
@ Summary
only summary at the start and end of solve
@ Detailed
Iteration plus more detail such as operation count.
@ Iteration
Summary plus minimal results at each iteration.
void read_handler_counts(std::shared_ptr< Statistics > stats, std::shared_ptr< ArrayHandlers< R, Q, P > > handlers)
Definition: Statistics.h:30
const std::shared_ptr< const molpro::Options > options()
Get the Options object associated with iterative-solver.
Definition: linalg_options.cpp:4
Access point for different options in iterative solvers.
Definition: Options.h:20
Information about performance of IterativeSolver instance.
Definition: Statistics.h:10
Definition: IterativeSolverTemplate.h:142
static constexpr const char * name
Definition: IterativeSolverTemplate.h:143
Definition: IterativeSolverTemplate.h:132
static constexpr std::size_t iter
Definition: IterativeSolverTemplate.h:134
static constexpr std::size_t errors
Definition: IterativeSolverTemplate.h:135
static constexpr const char * name
Definition: IterativeSolverTemplate.h:133
Definition: IterativeSolverTemplate.h:150
static constexpr const char * name
Definition: IterativeSolverTemplate.h:151
Stores partitioning of XSpace into P, Q and R blocks with sizes and offsets for each one.
Definition: Dimensions.h:8
Manages solution of the subspace problem and storage of those solutions.
Definition: ISubspaceSolver.h:21