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 <cmath>
4#include <iostream>
5#include <molpro/Profiler.h>
6#include <molpro/iostream.h>
7#include <molpro/linalg/itsolv/IterativeSolver.h>
8#include <molpro/linalg/itsolv/Logger.h>
9#include <molpro/linalg/itsolv/subspace/ISubspaceSolver.h>
10#include <molpro/linalg/itsolv/subspace/IXSpace.h>
11#include <molpro/linalg/itsolv/subspace/Matrix.h>
12#include <molpro/linalg/itsolv/subspace/util.h>
13#include <molpro/linalg/itsolv/util.h>
14#include <molpro/linalg/itsolv/wrap.h>
15#include <molpro/profiler/Profiler.h>
16#include <stack>
17
18namespace molpro::linalg::itsolv {
19namespace detail {
20
21inline std::vector<std::pair<size_t, size_t>> parameter_batches(const size_t nsol, const size_t nparam) {
22 auto batches = std::vector<std::pair<size_t, size_t>>{};
23 if (nparam && nsol) {
24 auto n_batch = nsol / nparam + (nsol % nparam ? 1 : 0);
25 for (size_t ib = 0, start_sol = 0, end_sol = 0; ib < n_batch; ++ib, start_sol = end_sol) {
26 end_sol = std::min(start_sol + nparam, nsol);
27 batches.emplace_back(start_sol, end_sol);
28 }
29 }
30 return batches;
31}
32
33template <class R, class Q, class P>
34void construct_solution(const VecRef<R>& params, const std::vector<int>& roots,
35 const subspace::Matrix<double>& solutions,
36 const std::vector<std::reference_wrapper<P>>& pparams,
37 const std::vector<std::reference_wrapper<Q>>& qparams,
38 const std::vector<std::reference_wrapper<Q>>& dparams, size_t oP, size_t oQ, size_t oD,
39 ArrayHandlers<R, Q, P>& handlers) {
40 auto prof = molpro::Profiler::single();
41 prof->start("get rd_mat");
42 if (roots.empty())
43 return;
44 assert(params.size() >= roots.size());
45 for (size_t i = 0; i < roots.size(); ++i) {
46 handlers.rr().fill(0, params.at(i));
47 }
48 subspace::Matrix<double> rp_mat(std::make_pair(pparams.size(), roots.size())),
49 rq_mat(std::make_pair(qparams.size(), roots.size())), rd_mat(std::make_pair(dparams.size(), roots.size()));
50 for (size_t i = 0; i < roots.size(); ++i) {
51 for (size_t j = 0; j < pparams.size(); ++j) {
52 rp_mat(j, i) = solutions(roots[i], oP + j);
53 }
54 for (size_t j = 0; j < qparams.size(); ++j) {
55 rq_mat(j, i) = solutions(roots[i], oQ + j);
56 }
57 for (size_t j = 0; j < dparams.size(); ++j) {
58 rd_mat(j, i) = solutions(roots[i], oD + j);
59 }
60 }
61 prof->stop();
62 handlers.rp().gemm_outer(rp_mat, cwrap(pparams), params);
63 handlers.rq().gemm_outer(rq_mat, cwrap(qparams), params);
64 handlers.rq().gemm_outer(rd_mat, cwrap(dparams), params);
65}
66
67template <typename T>
68std::vector<std::vector<T>> construct_vectorP(const std::vector<int>& roots, const subspace::Matrix<T>& solutions,
69 const size_t oP, const size_t nP) {
70 auto vectorP = std::vector<std::vector<T>>{};
71 for (auto root : roots) {
72 vectorP.emplace_back();
73 for (size_t j = 0; j < nP; ++j)
74 vectorP.back().push_back(solutions(root, oP + j));
75 }
76 return vectorP;
77}
78
79template <class R>
80void normalise(const size_t n_roots, const VecRef<R>& params, const VecRef<R>& actions,
81 array::ArrayHandler<R, R>& handler, Logger& logger) {
82 assert(params.size() >= n_roots && actions.size() >= n_roots);
83 for (size_t i = 0; i < n_roots; ++i) {
84 auto dot = handler.dot(params.at(i), params.at(i));
85 dot = std::sqrt(std::abs(dot));
86 if (dot > 1.0e-14) {
87 handler.scal(1. / dot, params.at(i));
88 handler.scal(1. / dot, actions.at(i));
89 } else {
90 logger.msg("solution parameter's length is too small, dot = " + Logger::scientific(dot), Logger::Warn);
91 }
92 }
93}
94
95template <class R, typename T>
96void update_errors(std::vector<T>& errors, const CVecRef<R>& residual, array::ArrayHandler<R, R>& handler) {
97 assert(residual.size() >= errors.size());
98 for (size_t i = 0; i < errors.size(); ++i) {
99 auto a = handler.dot(residual[i], residual[i]);
100 errors[i] = std::sqrt(std::abs(a));
101 }
102}
103
104template <typename T>
105std::vector<int> select_working_set(const size_t nw, const std::vector<T>& errors, const T threshold,
106 const std::vector<T>& value_errors, const T value_threshold) {
107 auto ordered_errors = std::multimap<T, size_t, std::greater<T>>{};
108 for (size_t i = 0; i < errors.size(); ++i) {
109 if (errors[i] > threshold or (i < value_errors.size() and value_errors[i] > value_threshold))
110 ordered_errors.emplace(errors[i], i);
111 }
112 auto working_set = std::vector<int>{};
113 auto end = (ordered_errors.size() < nw ? ordered_errors.end() : next(begin(ordered_errors), nw));
114 std::transform(begin(ordered_errors), end, std::back_inserter(working_set), [](const auto& el) { return el.second; });
115 std::sort(working_set.begin(), working_set.end());
116 return working_set;
117}
118
119} // namespace detail
120
126template <template <class, class, class> class Solver, class R, class Q, class P>
127class IterativeSolverTemplate : public Solver<R, Q, P> {
128public:
129 using typename Solver<R, Q, P>::fapply_on_p_type;
130 using typename Solver<R, Q, P>::scalar_type;
131 using typename Solver<R, Q, P>::value_type;
132 using typename Solver<R, Q, P>::VectorP;
133
137 IterativeSolverTemplate<Solver, R, Q, P>& operator=(const IterativeSolverTemplate<Solver, R, Q, P>&) = delete;
138 IterativeSolverTemplate<Solver, R, Q, P>& operator=(IterativeSolverTemplate<Solver, R, Q, P>&&) noexcept = default;
139
140 int add_vector(const VecRef<R>& parameters, const VecRef<R>& actions) override {
141 profiler()->push("itsolv::add_vector");
142 auto prof = molpro::Profiler::single();
143 m_logger->msg("IterativeSolverTemplate::add_vector iteration = " + std::to_string(m_stats->iterations),
145 m_logger->msg("IterativeSolverTemplate::add_vector size of {params, actions, working_set} = " +
146 std::to_string(parameters.size()) + ", " + std::to_string(actions.size()) + ", " +
147 std::to_string(m_working_set.size()) + ", ",
149 if (m_xspace->dimensions().nP != 0 && !m_apply_p)
150 throw std::runtime_error(
151 "Solver contains P space but no valid apply_p function. Make sure add_p was called correctly.");
152 auto nW = std::min(m_working_set.size(), parameters.size());
153 auto cwparams = cwrap(begin(parameters), begin(parameters) + nW);
154 auto cwactions = cwrap(begin(actions), begin(actions) + nW);
155 m_stats->r_creations += nW;
156 prof->start("update_qspace");
157 m_xspace->update_qspace(cwparams, cwactions);
158 m_stats->q_creations += 2 * nW;
159 prof->stop();
160 prof->start("solve_and_generate_working_set");
161 auto working_set = solve_and_generate_working_set(parameters, actions);
162 prof->stop();
164 this->m_end_iteration_needed = true;
165 return working_set;
166 }
167
168 int add_vector(std::vector<R>& parameters, std::vector<R>& actions) override {
169 return add_vector(wrap(parameters), wrap(actions));
170 }
171 int add_vector(R& parameters, R& actions, value_type value = 0) override {
172 return add_vector(wrap_arg(parameters), wrap_arg(actions));
173 }
174
175 // FIXME Currently only works if called on an empty subspace. Either enforce it or generalise.
176 size_t add_p(const CVecRef<P>& pparams, const array::Span<value_type>& pp_action_matrix, const VecRef<R>& parameters,
177 const VecRef<R>& actions, fapply_on_p_type apply_p) override {
178 auto prof = profiler()->push("itsolv::add_p");
179 if (not pparams.empty() and pparams.size() < n_roots())
180 throw std::runtime_error("P space must be empty or at least as large as number of roots sought");
181 if (apply_p)
182 m_apply_p = std::move(apply_p);
183 m_xspace->update_pspace(pparams, pp_action_matrix);
184 auto working_set = solve_and_generate_working_set(parameters, actions);
186 return working_set;
187 };
188
189 void clearP() override {}
190
191 void solution(const std::vector<int>& roots, const VecRef<R>& parameters, const VecRef<R>& residual) override {
192 // auto prof = profiler()->push("itsolv::solution"); // FIXME two profilers
193 auto prof = molpro::Profiler::single();
194 check_consistent_number_of_roots_and_solutions(roots, parameters.size());
195 prof->start("construct_solution (parameters)");
196 detail::construct_solution(parameters, roots, m_subspace_solver->solutions(), m_xspace->paramsp(),
197 m_xspace->paramsq(), m_xspace->paramsd(), m_xspace->dimensions().oP,
198 m_xspace->dimensions().oQ, m_xspace->dimensions().oD, *m_handlers);
199 prof->stop();
200 prof->start("construct_solution (residual)");
201 detail::construct_solution(residual, roots, m_subspace_solver->solutions(), {}, m_xspace->actionsq(),
202 m_xspace->actionsd(), m_xspace->dimensions().oP, m_xspace->dimensions().oQ,
203 m_xspace->dimensions().oD, *m_handlers);
204 prof->stop();
205 prof->start("apply");
206 auto pvectors = detail::construct_vectorP(roots, m_subspace_solver->solutions(), m_xspace->dimensions().oP,
207 m_xspace->dimensions().nP);
209 detail::normalise(roots.size(), parameters, residual, m_handlers->rr(), *m_logger);
210 if (m_apply_p)
211 m_apply_p(pvectors, m_xspace->cparamsp(), residual);
212 construct_residual(roots, cwrap(parameters), residual);
214 prof->stop();
215 };
216
217 void solution(const std::vector<int>& roots, std::vector<R>& parameters, std::vector<R>& residual) override {
218 return solution(roots, wrap(parameters), wrap(residual));
219 }
220 void solution(R& parameters, R& residual) override {
221 return solution(std::vector<int>(1, 0), wrap_arg(parameters), wrap_arg(residual));
222 }
223
224 void solution_params(const std::vector<int>& roots, std::vector<R>& parameters) override {
225 return solution_params(roots, wrap(parameters));
226 }
227
228 void solution_params(const std::vector<int>& roots, const VecRef<R>& parameters) override {
229 check_consistent_number_of_roots_and_solutions(roots, parameters.size());
230 detail::construct_solution(parameters, roots, m_subspace_solver->solutions(), m_xspace->paramsp(),
231 m_xspace->paramsq(), m_xspace->paramsd(), m_xspace->dimensions().oP,
232 m_xspace->dimensions().oQ, m_xspace->dimensions().oD, *m_handlers);
233 };
234
235 void solution_params(R& parameters) override { return solution_params(std::vector<int>(1, 0), wrap_arg(parameters)); }
236
237 // TODO Implement this
238 std::vector<size_t> suggest_p(const CVecRef<R>& solution, const CVecRef<R>& residual, size_t max_number,
239 double threshold) override {
240 return {};
241 }
242
243 const std::vector<int>& working_set() const override { return m_working_set; }
244
245 size_t n_roots() const override { return m_nroots; }
246
247 void set_n_roots(size_t roots) override {
248 m_nroots = roots;
249 m_working_set.resize(roots);
250 std::iota(begin(m_working_set), end(m_working_set), (int)0);
251 }
252
253 void set_options(const Options& options) override {
254 if (options.n_roots)
255 set_n_roots(options.n_roots.value());
256 if (options.convergence_threshold)
257 set_convergence_threshold(options.convergence_threshold.value());
258 if (options.verbosity)
259 set_verbosity(options.verbosity.value());
260 }
261
262 std::shared_ptr<Options> get_options() const override {
263 auto options = std::make_shared<Options>();
264 options->n_roots = n_roots();
265 options->convergence_threshold = convergence_threshold();
266 return options;
267 }
268
269 const std::vector<scalar_type>& errors() const override { return m_errors; }
270
271 const Statistics& statistics() const override { return *m_stats; }
272
273 void report(std::ostream& cout, bool endl = true) const override {
274 cout << "iteration " << m_stats->iterations;
275 if (not m_errors.empty()) {
276 auto it_max_error = std::max_element(m_errors.cbegin(), m_errors.cend());
277 if (n_roots() > 1)
278 cout << ", |residual[" << std::distance(m_errors.cbegin(), it_max_error) << "]| = ";
279 else
280 cout << ", |residual| = ";
281 cout << std::scientific << *it_max_error << std::defaultfloat;
282 }
283 if (endl)
284 cout << std::endl;
285 }
286
287 void report() const override { report(molpro::cout); }
288
289 void set_convergence_threshold(double thresh) override { m_convergence_threshold = thresh; }
290 double convergence_threshold() const override { return m_convergence_threshold; }
291 void set_convergence_threshold_value(double thresh) override { m_convergence_threshold_value = thresh; }
293 void set_verbosity(Verbosity v) override { m_verbosity = v; }
294 void set_verbosity(int v) override {
296 if (v > 0)
298 if (v > 1)
300 if (v > 2)
302 }
303 Verbosity get_verbosity() const override { return m_verbosity; }
304 void set_max_iter(int n) override { m_max_iter = n; }
305 int get_max_iter() const override { return m_max_iter; }
306 void set_max_p(int n) override { m_max_p = n; }
307 int get_max_p() const override { return m_max_p; }
308 void set_p_threshold(double threshold) override { m_p_threshold = threshold; }
309 double get_p_threshold() const override { return m_p_threshold; }
311 const subspace::Dimensions& dimensions() const override { return m_xspace->dimensions(); }
312 scalar_type value() const override {
313 return m_xspace->data.count(subspace::EqnData::value) > 0 ? m_xspace->data[subspace::EqnData::value](0, 0)
314 : nan("molpro::linalg::itsolv::IterativeSolver::value");
315 }
316 // void set_profiler(molpro::profiler::Profiler& profiler) override { m_profiler.reset(&profiler); }
318 m_profiler = std::shared_ptr<molpro::profiler::Profiler>(&profiler);
319 }
320 const std::shared_ptr<molpro::profiler::Profiler>& profiler() const override { return m_profiler; }
321
322 bool solve(const VecRef<R>& parameters, const VecRef<R>& actions, const Problem<R>& problem,
323 bool generate_initial_guess = false) override {
324 if (parameters.empty())
325 throw std::runtime_error("Empty container passed to IterativeSolver::solve()");
326 if (parameters.size() != actions.size())
327 throw std::runtime_error("Inconsistent container sizes in IterativeSolver::solve()");
328 this->m_logger->max_trace_level = Logger::None;
329 if (this->m_verbosity == Verbosity::Detailed) {
330 this->m_logger->max_trace_level = Logger::Info;
331 this->m_logger->data_dump = true;
332 }
333 bool use_diagonals = problem.diagonals(actions.at(0));
334 std::unique_ptr<Q> diagonals;
335 if (use_diagonals)
336 diagonals.reset(new Q{m_handlers->qr().copy(actions.at(0))});
337// std::cout << "solve() generate_initial_guess "<<generate_initial_guess<<", roots "<<this->n_roots()<<std::endl;
338 if (generate_initial_guess) {
339 if (not use_diagonals)
340 throw std::runtime_error("Default initial guess requested, but diagonal elements are not available");
341 auto guess = m_handlers->qq().select(parameters.size(), *diagonals);
342 size_t root = 0;
343 if (this->m_verbosity >= Verbosity::Summary)
344 molpro::cout << "Initial guess generated from diagonal elements" << std::endl;
345 for (const auto& g : guess) {
346 m_handlers->rp().copy(parameters[root], P{{g.first, 1}});
347 if (this->m_verbosity >= Verbosity::Detailed)
348 molpro::cout << " initial guess " << g.first << std::endl;
349 root++;
350 }
351 }
352 int nwork = parameters.size();
353 std::vector<P> pspace;
354 if (use_diagonals and m_max_p > 0) {
355 auto selectp = m_handlers->qq().select(m_max_p, *diagonals);
356 for (auto s = selectp.begin(); s != selectp.end(); s++)
357 if (s->second > selectp.begin()->second + m_p_threshold) {
358 selectp.erase(s, selectp.end());
359 break;
360 }
361 if (this->m_verbosity >= Verbosity::Summary and not selectp.empty())
362 molpro::cout << selectp.size() << "-dimensional P space selected with threshold "
363 << (m_p_threshold < 1e20 ? std::to_string(m_p_threshold) : "infinity") << " and limit " << m_max_p
364 << std::endl;
365 if (this->m_verbosity >= Verbosity::Detailed)
366 for (const auto& s : selectp)
367 molpro::cout << "P space element " << s.first << " : " << s.second << std::endl;
368 for (const auto& s : selectp)
369 pspace.emplace_back((P){{s.first, 1}});
370 fapply_on_p_type apply_on_p = [&problem](const std::vector<std::vector<value_type>>& pcoeff,
371 const CVecRef<P>& pparams, const VecRef<R>& actions) {
372 problem.p_action(pcoeff, pparams, actions);
373 };
374 auto action_matrix = problem.pp_action_matrix(pspace);
375 nwork = add_p(cwrap(pspace), array::Span<value_type>(action_matrix.data(), action_matrix.size()), parameters,
376 actions, apply_on_p);
377 }
378 for (auto iter = 0; iter < this->m_max_iter && nwork > 0; iter++) {
379 value_type value;
380 if (this->nonlinear()) {
381 value = problem.residual(*parameters.begin(), *actions.begin());
382 nwork = this->add_vector(*parameters.begin(), *actions.begin(), value);
383 } else if (iter > 0 or pspace.empty()) {
384 problem.action(cwrap(parameters.begin(), parameters.begin() + nwork),
385 wrap(actions.begin(), actions.begin() + nwork));
386 nwork = this->add_vector(parameters, actions);
387 }
388 // std::cout << "** nwork="<<nwork<<"use_diagonals="<<use_diagonals<<std::endl;
389 while (this->end_iteration_needed()) {
390 if (nwork > 0) {
391 if (use_diagonals) {
392 m_handlers->rq().copy(parameters.at(0), *diagonals);
393 problem.precondition(wrap(actions.begin(), actions.begin() + nwork), this->working_set_eigenvalues(),
394 parameters.at(0));
395 } else
396 problem.precondition(wrap(actions.begin(), actions.begin() + nwork), this->working_set_eigenvalues());
397 }
398 nwork = this->end_iteration(parameters, actions);
399 }
401 report();
402 }
403 if (this->m_verbosity == Verbosity::Summary)
404 report();
405 if (this->m_verbosity >= Verbosity::Summary and
406 *std::max_element(m_errors.begin(), m_errors.end()) > m_convergence_threshold)
407 std::cerr << "Solver has not converged to threshold " << m_convergence_threshold << std::endl;
408 return nwork == 0 and *std::max_element(m_errors.begin(), m_errors.end()) <= m_convergence_threshold;
409 }
410
411 bool solve(R& parameters, R& actions, const Problem<R>& problem, bool generate_initial_guess = false) override {
412 auto wparams = std::vector<std::reference_wrapper<R>>{std::ref(parameters)};
413 auto wactions = std::vector<std::reference_wrapper<R>>{std::ref(actions)};
414 return solve(wparams, wactions, problem, generate_initial_guess);
415 }
416 bool solve(std::vector<R>& parameters, std::vector<R>& actions, const Problem<R>& problem,
417 bool generate_initial_guess = false) override {
418 return solve(wrap(parameters), wrap(actions), problem, generate_initial_guess);
419 }
420
421 bool test_problem(const Problem<R>& problem, R& v0, R& v1, int verbosity, double threshold) const override {
422 bool success = true;
423 if (this->nonlinear()) {
424 if (!problem.test_parameters(0, v0))
425 return true;
426 auto value0 = problem.residual(v0, v1);
427 if (verbosity > 1) {
428 std::cout << "value0 " << value0 << std::endl;
429 // std::cout << "parameters0 " << v0[0] << "," << v0[1] << ",..." << std::endl;
430 // std::cout << "residual0 " << v1[0] << "," << v1[1] << ",..." << std::endl;
431 }
432 Q parameters0{v0};
433 Q residual0{v1};
434 for (int instance = 1; problem.test_parameters(instance, v0); ++instance) {
435 auto value1 = problem.residual(v0, v1);
436 if (verbosity > 1) {
437 std::cout << "testing instance" << instance << std::endl;
438 std::cout << "value1 " << value1 << std::endl;
439 // std::cout << "parameters1 " << v0[0] << "," << v0[1] << ",..." << std::endl;
440 // std::cout << "residual1 " << v1[0] << "," << v1[1] << ",..." << std::endl;
441 }
442 Q parameters1{v0};
443 Q residual1{v1};
444 m_handlers->rq().copy(v0, residual1);
445 m_handlers->rr().scal(0.5, v0);
446 m_handlers->rq().axpy(0.5, residual0, v0);
447 m_handlers->rq().copy(v1, parameters1);
448 m_handlers->rq().axpy(-1, parameters0, v1);
449 auto dv_analytic = m_handlers->rr().dot(v0, v1);
450 // if (verbosity > 1) {
451 // std::cout << "mean residual " << v0[0] << "," << v0[1] << ",..." << std::endl;
452 // std::cout << "step " << v1[0] << "," << v1[1] << ",..." << std::endl;
453 // }
454 success = success && std::abs(dv_analytic - (value1 - value0)) < threshold;
455 if (verbosity > 0 or (verbosity > -1 and not success))
456 std::cout << "{actual, extrapolated} value change: {" << value1 - value0 << ", " << dv_analytic << "}"
457 << std::endl;
458 }
459 } else {
460 for (int instance = 0; problem.test_parameters(instance, v0); ++instance) {
461 problem.action({v0}, {v1});
462 Q residual{v1};
463 auto norm2_residual = std::sqrt(m_handlers->rr().dot(v1, v1));
464 constexpr double scale_factor{10.0};
465 m_handlers->rr().scal(scale_factor, v0);
466 problem.action({v0}, {v1});
467 m_handlers->rq().axpy(-scale_factor, residual, v1);
468 auto norm2 = std::sqrt(m_handlers->rr().dot(v1, v1));
469 success = success && std::abs(norm2 / norm2_residual) < threshold;
470 if (verbosity > 0 or (verbosity > -1 and not success))
471 std::cout << "Length of residual: " << norm2_residual << ", scaling defect: " << norm2 << std::endl;
472 }
473 }
474 return success;
475 }
476
477protected:
479 std::shared_ptr<subspace::ISubspaceSolver<R, Q, P>> solver,
480 std::shared_ptr<ArrayHandlers<R, Q, P>> handlers, std::shared_ptr<Statistics> stats,
481 std::shared_ptr<Logger> logger)
482 : m_handlers(std::move(handlers)), m_xspace(std::move(xspace)), m_subspace_solver(std::move(solver)),
483 m_stats(std::move(stats)), m_logger(std::move(logger)), m_profiler(molpro::Profiler::single()),
484 m_profiler_saved_depth(m_profiler->get_max_depth()) {
485 set_n_roots(1);
486 m_profiler->set_max_depth(options()->parameter("PROFILER_DEPTH", 0));
487 }
488
490 if (molpro::mpi::rank_global() == 0) {
491 auto file = options()->parameter("PROFILER_OUTPUT", "");
492 if (profiler()->get_max_depth() > 0 and
493 std::find_if(file.begin(), file.end(), [](unsigned char ch) { return !std::isspace(ch); }) != file.end())
494 std::ofstream(file) << *profiler() << std::endl;
495 }
496 if (molpro::mpi::rank_global() == 0) {
497 auto file = options()->parameter("PROFILER_DOTGRAPH", "");
498 if (profiler()->get_max_depth() > 0 and
499 std::find_if(file.begin(), file.end(), [](unsigned char ch) { return !std::isspace(ch); }) != file.end())
500 profiler()->dotgraph(file, options()->parameter("PROFILER_THRESHOLD", .01));
501 }
502 molpro::Profiler::single()->set_max_depth(m_profiler_saved_depth);
503 }
504
506 virtual void set_value_errors() {}
508 virtual void construct_residual(const std::vector<int>& roots, const CVecRef<R>& params,
509 const VecRef<R>& actions) = 0;
510 virtual bool linearEigensystem() const { return false; }
519 size_t solve_and_generate_working_set(const VecRef<R>& parameters, const VecRef<R>& action) {
520 auto prof = profiler();
521 auto p = prof->push("itsolv::solve_and_generate_working_set");
522 prof->start("itsolv::solve");
524 prof->stop();
525 prof->start("itsolv::temp_solutions");
526 auto nsol = m_subspace_solver->size();
527 std::vector<std::pair<Q, Q>> temp_solutions{};
528 const auto batches = detail::parameter_batches(nsol, parameters.size());
529 for (const auto& batch : batches) {
530 auto [start_sol, end_sol] = batch;
531 auto roots = std::vector<int>(end_sol - start_sol);
532 std::iota(begin(roots), end(roots), start_sol);
533 solution(roots, parameters, action);
534 auto errors = std::vector<scalar_type>(roots.size(), 0);
536 if (batches.size() > 1) {
537 for (size_t i = 0; i < roots.size(); ++i)
538 temp_solutions.emplace_back(m_handlers->qr().copy(parameters[i]), m_handlers->qr().copy(action[i]));
539 m_stats->q_creations += 2 * roots.size();
540 }
541 m_subspace_solver->set_error(roots, errors);
542 }
543 prof->stop();
545 m_errors = m_subspace_solver->errors();
548 for (size_t i = 0; i < m_working_set.size(); ++i) {
549 size_t root = m_working_set[i];
550 if (batches.size() > 1) {
551 m_handlers->rq().copy(parameters[i], temp_solutions.at(root).first);
552 m_handlers->rq().copy(action[i], temp_solutions.at(root).second);
553 } else {
554 if (root < i)
555 throw std::logic_error("incorrect ordering of roots");
556 if (root > i) {
557 m_handlers->rr().copy(parameters[i], parameters[root]);
558 m_handlers->rr().copy(action[i], action[root]);
559 }
560 }
561 }
562 m_logger->msg("add_vector::errors = ", begin(m_errors), end(m_errors), Logger::Trace, 6);
563 return m_working_set.size();
564 }
565
566 template <typename TTT>
567 void check_consistent_number_of_roots_and_solutions(const std::vector<TTT>& roots, const size_t nparams) {
568 if (roots.size() > nparams)
569 throw std::runtime_error("asking for more roots than parameters");
570 if (!roots.empty() &&
571 size_t(*std::max_element(roots.begin(), roots.end())) >= m_subspace_solver->solutions().size())
572 throw std::runtime_error("asking for more roots than there are solutions");
573 }
574
576
577
578 std::shared_ptr<ArrayHandlers<R, Q, P>> m_handlers;
579 std::shared_ptr<subspace::IXSpace<R, Q, P>> m_xspace;
580 std::shared_ptr<subspace::ISubspaceSolver<R, Q, P>> m_subspace_solver;
581 std::vector<double> m_errors;
582 std::vector<double> m_value_errors;
583 std::vector<int> m_working_set;
584 size_t m_nroots{0};
585 double m_convergence_threshold{1.0e-8};
587 std::numeric_limits<double>::max()};
588 std::shared_ptr<Statistics> m_stats;
589 std::shared_ptr<Logger> m_logger;
590 bool m_normalise_solution = false;
591 fapply_on_p_type m_apply_p = {};
593 int m_max_iter = 100;
594 size_t m_max_p = 0;
595 double m_p_threshold = std::numeric_limits<double>::max();
596private:
597 mutable std::shared_ptr<molpro::profiler::Profiler> m_profiler;
598 int m_profiler_saved_depth;
599protected:
601};
602
603} // namespace molpro::linalg::itsolv
604
605#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:28
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:127
void set_profiler(molpro::profiler::Profiler &profiler) override
Definition: IterativeSolverTemplate.h:317
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:176
void set_convergence_threshold(double thresh) override
Definition: IterativeSolverTemplate.h:289
int get_max_iter() const override
Definition: IterativeSolverTemplate.h:305
void solution_params(const std::vector< int > &roots, std::vector< R > &parameters) override
Definition: IterativeSolverTemplate.h:224
void report(std::ostream &cout, bool endl=true) const override
Definition: IterativeSolverTemplate.h:273
double get_p_threshold() const override
Definition: IterativeSolverTemplate.h:309
std::vector< int > m_working_set
indices of roots in the working set
Definition: IterativeSolverTemplate.h:583
scalar_type value() const override
Definition: IterativeSolverTemplate.h:312
std::shared_ptr< subspace::IXSpace< R, Q, P > > m_xspace
manages the subspace and associated data
Definition: IterativeSolverTemplate.h:579
void set_n_roots(size_t roots) override
Definition: IterativeSolverTemplate.h:247
std::shared_ptr< ArrayHandlers< R, Q, P > > m_handlers
Array handlers.
Definition: IterativeSolverTemplate.h:578
fapply_on_p_type m_apply_p
function that evaluates effect of action on the P space projection
Definition: IterativeSolverTemplate.h:591
void solution(const std::vector< int > &roots, std::vector< R > &parameters, std::vector< R > &residual) override
Definition: IterativeSolverTemplate.h:217
void set_max_iter(int n) override
Definition: IterativeSolverTemplate.h:304
void check_consistent_number_of_roots_and_solutions(const std::vector< TTT > &roots, const size_t nparams)
Definition: IterativeSolverTemplate.h:567
bool test_problem(const Problem< R > &problem, R &v0, R &v1, int verbosity, double threshold) const override
Definition: IterativeSolverTemplate.h:421
void set_max_p(int n) override
Definition: IterativeSolverTemplate.h:306
std::shared_ptr< subspace::ISubspaceSolver< R, Q, P > > m_subspace_solver
solves the subspace problem
Definition: IterativeSolverTemplate.h:580
int get_max_p() const override
Definition: IterativeSolverTemplate.h:307
size_t n_roots() const override
Definition: IterativeSolverTemplate.h:245
double convergence_threshold() const override
Definition: IterativeSolverTemplate.h:290
std::shared_ptr< Options > get_options() const override
Definition: IterativeSolverTemplate.h:262
size_t m_max_p
maximum size of P space
Definition: IterativeSolverTemplate.h:594
double convergence_threshold_value() const override
Definition: IterativeSolverTemplate.h:292
const subspace::Dimensions & dimensions() const override
Access dimensions of the subspace.
Definition: IterativeSolverTemplate.h:311
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:519
const std::shared_ptr< molpro::profiler::Profiler > & profiler() const override
Definition: IterativeSolverTemplate.h:320
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:478
virtual ~IterativeSolverTemplate()
Definition: IterativeSolverTemplate.h:489
void set_p_threshold(double threshold) override
Definition: IterativeSolverTemplate.h:308
int add_vector(std::vector< R > &parameters, std::vector< R > &actions) override
Definition: IterativeSolverTemplate.h:168
void solution_params(R &parameters) override
Definition: IterativeSolverTemplate.h:235
virtual bool linearEigensystem() const
Definition: IterativeSolverTemplate.h:510
const Statistics & statistics() const override
Definition: IterativeSolverTemplate.h:271
void solution(R &parameters, R &residual) override
Definition: IterativeSolverTemplate.h:220
size_t m_nroots
number of roots the solver is searching for
Definition: IterativeSolverTemplate.h:584
double m_p_threshold
threshold for selecting P space
Definition: IterativeSolverTemplate.h:595
bool m_end_iteration_needed
whether end_iteration should be called after any preconditioner
Definition: IterativeSolverTemplate.h:600
void set_convergence_threshold_value(double thresh) override
Definition: IterativeSolverTemplate.h:291
void solution_params(const std::vector< int > &roots, const VecRef< R > &parameters) override
Definition: IterativeSolverTemplate.h:228
int add_vector(const VecRef< R > &parameters, const VecRef< R > &actions) override
Definition: IterativeSolverTemplate.h:140
void clearP() override
Definition: IterativeSolverTemplate.h:189
bool solve(R &parameters, R &actions, const Problem< R > &problem, bool generate_initial_guess=false) override
Definition: IterativeSolverTemplate.h:411
bool end_iteration_needed() override
Definition: IterativeSolverTemplate.h:575
IterativeSolverTemplate(const IterativeSolverTemplate< Solver, R, Q, P > &)=delete
const std::vector< scalar_type > & errors() const override
Definition: IterativeSolverTemplate.h:269
void report() const override
Definition: IterativeSolverTemplate.h:287
bool m_normalise_solution
whether to normalise the solutions
Definition: IterativeSolverTemplate.h:590
double m_convergence_threshold
residual norms less than this mark a converged solution
Definition: IterativeSolverTemplate.h:585
std::vector< size_t > suggest_p(const CVecRef< R > &solution, const CVecRef< R > &residual, size_t max_number, double threshold) override
Definition: IterativeSolverTemplate.h:238
const std::vector< int > & working_set() const override
Definition: IterativeSolverTemplate.h:243
std::shared_ptr< Logger > m_logger
logger
Definition: IterativeSolverTemplate.h:589
virtual void set_value_errors()
Implementation class should overload this to set errors in the current values (e.g....
Definition: IterativeSolverTemplate.h:506
bool solve(std::vector< R > &parameters, std::vector< R > &actions, const Problem< R > &problem, bool generate_initial_guess=false) override
Definition: IterativeSolverTemplate.h:416
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.
Verbosity m_verbosity
how much output to print in solve()
Definition: IterativeSolverTemplate.h:592
void set_options(const Options &options) override
Definition: IterativeSolverTemplate.h:253
std::vector< double > m_errors
errors from the most recent solution
Definition: IterativeSolverTemplate.h:581
int add_vector(R &parameters, R &actions, value_type value=0) override
Definition: IterativeSolverTemplate.h:171
void set_verbosity(Verbosity v) override
Definition: IterativeSolverTemplate.h:293
bool solve(const VecRef< R > &parameters, const VecRef< R > &actions, const Problem< R > &problem, bool generate_initial_guess=false) override
Definition: IterativeSolverTemplate.h:322
int m_max_iter
maximum number of iterations in solve()
Definition: IterativeSolverTemplate.h:593
std::shared_ptr< Statistics > m_stats
accumulates statistics of operations performed by the solver
Definition: IterativeSolverTemplate.h:588
double m_convergence_threshold_value
value changes less than this mark a converged solution
Definition: IterativeSolverTemplate.h:586
void set_verbosity(int v) override
Definition: IterativeSolverTemplate.h:294
Verbosity get_verbosity() const override
Definition: IterativeSolverTemplate.h:303
void solution(const std::vector< int > &roots, const VecRef< R > &parameters, const VecRef< R > &residual) override
Definition: IterativeSolverTemplate.h:191
std::vector< double > m_value_errors
value errors from the most recent solution
Definition: IterativeSolverTemplate.h:582
IterativeSolverTemplate(IterativeSolverTemplate< Solver, R, Q, P > &&) noexcept=default
Abstract class defining the problem-specific interface for the simplified solver interface to Iterati...
Definition: IterativeSolver.h:77
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:100
virtual std::vector< double > pp_action_matrix(const std::vector< P > &pparams) const
Calculate the kernel matrix in the P space.
Definition: IterativeSolver.h:153
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:123
virtual bool diagonals(container_t &d) const
Optionally provide the diagonal elements of the underlying kernel. If implemented and returning true,...
Definition: IterativeSolver.h:112
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:165
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:92
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:180
Matrix container that allows simple data access, slicing, copying and resizing without loosing data.
Definition: Matrix.h:28
static std::shared_ptr< Profiler > single()
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:68
void construct_solution(const VecRef< R > &params, const std::vector< int > &roots, const subspace::Matrix< double > &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:34
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:80
std::vector< std::pair< size_t, size_t > > parameter_batches(const size_t nsol, const size_t nparam)
Definition: IterativeSolverTemplate.h:21
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:105
void update_errors(std::vector< T > &errors, const CVecRef< R > &residual, array::ArrayHandler< R, R > &handler)
Definition: IterativeSolverTemplate.h:96
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:10
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: options.cpp:4
A dummy structured logger.
Definition: Logger.h:40
void msg(const std::string &message, Level log_lvl)
Definition: Logger.cpp:16
static std::string scientific(double val)
Converts double to a string in scientific notation.
Definition: Logger.cpp:33
@ Info
Definition: Logger.h:49
@ Trace
Definition: Logger.h:49
@ None
Definition: Logger.h:49
@ Debug
Definition: Logger.h:49
@ Warn
Definition: Logger.h:49
Access point for different options in iterative solvers.
Definition: Options.h:20
Information about performance of IterativeSolver instance.
Definition: Statistics.h:10
Stores partitioning of XSpace into P, Q and R blocks with sizes and offsets for each one.
Definition: Dimensions.h:5
Manages solution of the subspace problem and storage of those solutions.
Definition: ISubspaceSolver.h:15