iterative-solver 0.0
Interpolate-implementation.h
1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_INTERPOLATE_IMPLEMENTATION_H_
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_INTERPOLATE_IMPLEMENTATION_H_
3
4#include <molpro/linalg/itsolv/Interpolate.h>
5
6#include <molpro/linalg/itsolv/IterativeSolver.h>
7#include <molpro/linalg/itsolv/SolverFactory-implementation.h>
8#include <molpro/linalg/itsolv/SolverFactory.h>
9#include <molpro/linalg/itsolv/helper.h>
10
11#include <algorithm>
12#include <cmath>
13#include <limits>
14#include <stdexcept>
15#include <utility>
16
17namespace molpro::linalg::itsolv {
18namespace interpolate_detail {
19
21template <typename value_type>
22typename Interpolator<value_type>::point Morse(value_type y, const std::vector<value_type>& parameters) {
23 using std::exp;
24 using std::pow;
25 typename Interpolator<value_type>::point result;
26 result.x = y;
27 result.f = parameters[0] +
28 (parameters[1] / 2) * pow((1 - exp(-parameters[2] * (y - parameters[3]))) / parameters[2], 2);
29 result.f1 = (parameters[1] / parameters[2]) * exp(-parameters[2] * (y - parameters[3])) *
30 (1 - exp(-parameters[2] * (y - parameters[3])));
31 result.f2 = -parameters[1] * (1 - 2 * exp(-parameters[2] * (y - parameters[3])));
32 return result;
33}
34
36template <typename value_type>
37class Morse_problem : public Problem<std::vector<value_type>> {
38 using R = std::vector<value_type>;
39 using point = typename Interpolator<value_type>::point;
40 point p0, p1;
41
42public:
43 Morse_problem(point p0, point p1) {
44 this->p0 = p0;
45 this->p1 = p1;
46 }
47
48 typename Problem<R>::value_t residual(const R& parameters, R& residual) const override {
49 auto pp0 = Morse<value_type>(p0.x, parameters);
50 auto pp1 = Morse<value_type>(p1.x, parameters);
51 residual[0] = pp0.f - p0.f;
52 residual[1] = pp1.f - p1.f;
53 residual[2] = pp0.f1 - p0.f1;
54 residual[3] = pp1.f1 - p1.f1;
55 return 0;
56 }
57};
59} // namespace interpolate_detail
60
61template <typename value_type>
62Interpolator<value_type>::Interpolator(point p0, point p1, std::string interpolant, int verbosity)
63 : m_p0(std::move(p0)), m_p1(std::move(p1)), m_interpolant(std::move(interpolant)), m_parameters(4) {
64 using std::pow;
65 if (m_interpolant == "cubic") {
66 // c0 + c1(x-xbar) + c2(x-xbar)^2 + c3(x-xbar)^3 where xbar=(x0+x1)/2
67 auto x1mx0 = m_p1.x - m_p0.x;
68 auto f1pf0 = m_p1.f + m_p0.f;
69 auto f1mf0 = m_p1.f - m_p0.f;
70 auto g1pg0 = m_p1.f1 + m_p0.f1;
71 auto g1mg0 = m_p1.f1 - m_p0.f1;
72 m_parameters[0] = value_type(0.5) * f1pf0 - value_type(0.125) * g1mg0 * x1mx0;
73 m_parameters[1] = value_type(-0.25) * g1pg0 + value_type(1.5) * f1mf0 / x1mx0;
74 m_parameters[2] = value_type(0.5) * g1mg0 / x1mx0;
75 m_parameters[3] = (-2 * f1mf0 + g1pg0 * x1mx0) / pow(x1mx0, 3);
76 } else if (m_interpolant == "morse") {
77 // L0 + (k/2a^2)*(1-exp(-a(y-y0)))^2
78 // m_parameters: L0, k, a, y0
79 using R = std::vector<value_type>;
80 R residual(4);
81 auto solver = molpro::linalg::itsolv::create_NonLinearEquations<R>("DIIS");
82 auto cubic = Interpolator<value_type>(p0, p1, "cubic", 0);
83 auto cubic_minimum = cubic.minimize(p0.x, p1.x);
84 auto cubic_at_minimum = cubic(cubic_minimum.x);
85 m_parameters[1] = cubic_at_minimum.f2;
86 m_parameters[2] = -3 * cubic.parameters()[3] / (cubic_at_minimum.f2);
87 m_parameters[3] = cubic_minimum.x;
88 m_parameters[0] = cubic_at_minimum.f;
90 solver->set_verbosity(verbosity);
91 if (!solver->solve(m_parameters, residual, problem))
92 throw std::runtime_error("Cannot find Morse interpolant");
93 solver->solution(m_parameters, residual);
94 } else
95 throw std::runtime_error("Unknown interpolant: " + m_interpolant);
96}
97
98template <typename value_type>
99std::vector<std::string> Interpolator<value_type>::interpolants() {
100 return std::vector<std::string>{"cubic", "morse"};
101}
102
103template <typename value_type>
105 if (m_interpolant == "cubic") {
106 auto xbar = value_type(0.5) * (m_p1.x + m_p0.x);
107 auto f = m_parameters[0] +
108 (x - xbar) * (m_parameters[1] + (x - xbar) * (m_parameters[2] + (x - xbar) * m_parameters[3]));
109 auto f1 = m_parameters[1] + (x - xbar) * (2 * m_parameters[2] + 3 * (x - xbar) * m_parameters[3]);
110 auto f2 = 2 * m_parameters[2] + 6 * (x - xbar) * m_parameters[3];
111 return point{x, f, f1, f2};
112 } else if (m_interpolant == "morse") {
113 return interpolate_detail::Morse<value_type>(x, m_parameters);
114 }
115 throw std::logic_error("Unknown interpolant: " + m_interpolant);
116}
117
118template <typename value_type>
120 using std::abs;
121 using std::isnan;
122 using std::sqrt;
123 if (m_interpolant != "cubic")
124 throw std::logic_error("minimize_cubic called with non-cubic interpolant");
125 const auto c = m_parameters[1];
126 const auto b = 2 * m_parameters[2];
127 const auto a = 3 * m_parameters[3];
128 // the criterion for "the cubic term is negligible" is a relative one, so it scales with the precision
129 if (abs(a) < precision_scaled<value_type>(1e-10) * std::max(abs(c), abs(b))) { // quadratic not cubic
130 return point{-c / b};
131 }
132 auto discriminant = b * b / (4 * a * a) - c / a;
133 if (isnan(discriminant) || discriminant < 0)
134 return {std::numeric_limits<value_type>::quiet_NaN()};
135 auto xbar = value_type(0.5) * (m_p1.x + m_p0.x);
136 point pm = (*this)(xbar - (b / (2 * a)) + sqrt(discriminant));
137 point pp = (*this)(xbar - (b / (2 * a)) - sqrt(discriminant));
138 return pm.f < pp.f ? pm : pp;
139}
140
141template <typename value_type>
143 size_t bracket_grid,
144 size_t max_bracket_grid,
145 bool analytic) const {
146 using std::abs;
147 if (xa > xb)
148 std::swap(xa, xb);
149 if (analytic && m_interpolant == "cubic")
150 return minimize_cubic();
151 for (size_t ngrid = bracket_grid; ngrid < std::max(bracket_grid, max_bracket_grid) + 1; ngrid *= 2) {
152 auto gridstep = (xb - xa) / ngrid;
153 auto plow = (*this)(xa);
154 auto p0 = (*this)(xa).f > (*this)(xb).f ? plow : (*this)(xb);
155 auto p1 = p0;
156 for (size_t igrid = 0; igrid < ngrid; igrid++) {
157 auto phigh = (*this)(plow.x + gridstep);
158 if (std::min(phigh.f, plow.f) < p0.f and plow.f1 <= 0 and phigh.f1 >= 0) {
159 p1 = phigh;
160 p0 = plow;
161 }
162 std::swap(plow, phigh);
163 }
164 if (p0.f1 < 0 and p1.f1 > 0) {
165 auto pnew = p1;
166 // a couple of units in the last place of pnew.x, expressed without std::nextafter so that it
167 // is also defined for arbitrary-precision types
168 auto tolerance =
169 2 * std::numeric_limits<value_type>::epsilon() * std::max(value_type(1), abs(pnew.x));
170 while (abs(p0.x - pnew.x) > tolerance) {
171 pnew = (*this)((p1.x * p0.f1 - p0.x * p1.f1) / (p0.f1 - p1.f1));
172 if (pnew.f1 * p0.f1 < 0)
173 std::swap(p0, p1);
174 std::swap(p0, pnew);
175 }
176 return p0;
177 }
178 }
179 // nothing found; return lowest end point
180 return (*this)(xa).f > (*this)(xb).f ? (*this)(xb) : (*this)(xa);
181}
182
183} // namespace molpro::linalg::itsolv
184
185#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_INTERPOLATE_IMPLEMENTATION_H_
The interpolant, in the precision of the problem it is used for.
Definition: Interpolate.h:31
Interpolator(point p0, point p1, std::string interpolant="cubic", int verbosity=0)
Construct the interpolant.
Definition: Interpolate-implementation.h:62
static std::vector< std::string > interpolants()
Definition: Interpolate-implementation.h:99
Abstract class defining the problem-specific interface for the simplified solver interface to Iterati...
Definition: IterativeSolver.h:85
typename R::value_type value_t
Definition: IterativeSolver.h:91
Fits the four Morse parameters to the two defining points.
Definition: Interpolate-implementation.h:37
Morse_problem(point p0, point p1)
Definition: Interpolate-implementation.h:43
Problem< R >::value_t residual(const R &parameters, R &residual) const override
Calculate the residual vector. Used by non-linear solvers (NonLinearEquations, Optimize) only.
Definition: Interpolate-implementation.h:48
Interpolator< value_type >::point Morse(value_type y, const std::vector< value_type > &parameters)
The Morse interpolant and its first two derivatives at y.
Definition: Interpolate-implementation.h:22
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:14
value_type f
Definition: Interpolate.h:41
value_type f1
Definition: Interpolate.h:42
value_type x
Definition: Interpolate.h:40