TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
adaptive.hpp
1#pragma once
2
3#include <nda/nda.hpp>
4
5#include <array>
6#include <cmath>
7#include <iostream>
8#include <utility>
9#include <vector>
10
11namespace triqs::experimental::utility {
12
17
31 template <typename T> class integrate_1d_adapt {
32
33 double tolerance = 1e-6;
34 // abscissas for GLK quadrature
35 double alpha = sqrt(2.0 / 3.0);
36 double beta = 1.0 / sqrt(5.0);
37 double x1 = 0.942882415695480;
38 double x2 = 0.641853342345781;
39 double x3 = 0.236383199662150;
40
41 mutable std::vector<double> all_x_midpoints;
42 mutable std::vector<T> all_int;
43
44 // store the evaluations for the 13 pt formula
45 static constexpr std::array<double, 4> coefficients = {11. / 210., 432.0 / 1470.0, 125. / 294., 16. / 35.};
46 // 13 pt Konrod / 4pt Gauss abscissa
47 std::vector<double> x_init = {-x1, -alpha, -x2, -beta, -x3, 0.0, x3, beta, x2, alpha, x1};
48
49 public:
54 integrate_1d_adapt(double tolerance = 1e-6) : tolerance{tolerance} {}
55
66 auto operator()(auto f, const std::pair<double, double> &domain) const
67 requires(nda::clef::is_lazy<decltype(f)>)
68 {
69 return nda::clef::make_expr_call(*this, std::move(f), domain);
70 }
71
82 auto operator()(auto const &f, const std::pair<double, double> &domain) const
83 requires(not nda::clef::is_lazy<decltype(f)>)
84 {
85
86 // denominator of the function
87 auto h = 0.5 * (domain.second - domain.first);
88 // midpoint -- to shift from domain to zero-centered domain
89 auto m = 0.5 * (domain.first + domain.second);
90
91 // fill in all the function evaluations
92 std::vector<T> fx(11);
93 for (int i = 0; i < 11; ++i) fx[i] = f(m + x_init[i] * h);
94
95 T fa = f(domain.first);
96 T fb = f(domain.second);
97
98 // 13 pt Kronrod -- done once for error estimate
99 T is = h
100 * (0.0158271919734802 * (fa + fb) + 0.0942738402188500 * (fx[0] + fx[10]) + 0.155071987336585 * (fx[1] + fx[9])
101 + 0.188821573960182 * (fx[2] + fx[8]) + 0.199773405226859 * (fx[3] + fx[7]) + 0.224926465333340 * (fx[4] + fx[6])
102 + 0.242611071901408 * fx[5]);
103
104 // scalar or elementwise check for a zero value of is
105 if constexpr (nda::Scalar<T>) {
106 if (std::abs(is) == 0.0) { is = domain.second - domain.first; }
107 } else {
108 nda::for_each(is.shape(), [&is, &domain](auto... args) {
109 if (std::abs(is(args...)) == 0) is(args...) = domain.second - domain.first;
110 });
111 }
112 // start the recursive call
113 return adaptive_recursion(f, domain.first, domain.second, fx[0], fx[10], abs(is));
114 }
115
116 private:
129 T adaptive_recursion(auto &f, const double xa, const double xb, const T fa, const T fb, const T is) const {
130
131 // step spacing
132 double h = 0.5 * (xb - xa);
133 // midpoint -- to shift from domain to zero-centered domain
134 double midpoint = 0.5 * (xa + xb);
135
136 // array of x and fx points on the segment
137 // 2 pts left and right of midpoint at sqrt(2/3) and 1/sqrt(5)
138 std::array<double, 5> x; //NOLINT
139 std::array<T, 5> fx;
140 x[0] = midpoint - alpha * h;
141 x[1] = midpoint - beta * h;
142 x[2] = midpoint;
143 x[3] = midpoint + beta * h;
144 x[4] = midpoint + alpha * h;
145
146 // 5 pt function evaluations (+ endpts provided to the function )
147 for (int i = 0; i < 5; ++i) fx[i] = f(x[i]);
148
149 // 4 pt Gauss-Lobatto
150 T i2 = (1. / 6. * h) * (fa + fb + 5.0 * (fx[1] + fx[3]));
151 // 7 pt Kronrod
152 T i1 = h * (coefficients[0] * (fa + fb) + coefficients[1] * (fx[0] + fx[4]) + coefficients[2] * (fx[1] + fx[3]) + coefficients[3] * fx[2]);
153
154 // if the two are close in value, end the iteration
155 bool tolerance_met = false;
156 if constexpr (nda::Scalar<T>) {
157 tolerance_met = std::abs(i1 - i2) <= std::abs(tolerance * is);
158 } else {
159 tolerance_met = nda::max_element(nda::abs(i1 - i2)) <= nda::max_element(nda::abs(tolerance * is));
160 }
161 if (tolerance_met || x[0] <= xa || xb <= x[4]) {
162 if ((midpoint <= xa || xb <= midpoint)) {
163 std::cerr << "Cannot subdivide integration domain further; however, desired acccuracy was not reached." << std::endl;
164 }
165 return i1; // recursive base case
166 }
167
168 return adaptive_recursion(f, xa, x[0], fa, fx[0], is) + adaptive_recursion(f, x[0], x[1], fx[0], fx[1], is)
169 + adaptive_recursion(f, x[1], x[2], fx[1], fx[2], is) + adaptive_recursion(f, x[2], x[3], fx[2], fx[3], is)
170 + adaptive_recursion(f, x[3], x[4], fx[3], fx[4], is) + adaptive_recursion(f, x[4], xb, fx[4], fb, is);
171 }
172 };
173
175
176} // namespace triqs::experimental::utility
integrate_1d_adapt(double tolerance=1e-6)
Construct an adaptive one-dimensional integrator with the given relative tolerance.
Definition adaptive.hpp:54
auto operator()(auto f, const std::pair< double, double > &domain) const
Build a lazy CLEF call expression for the integral of a lazy integrand over a given domain.
Definition adaptive.hpp:66
auto operator()(auto const &f, const std::pair< double, double > &domain) const
Perform the adaptive one-dimensional integration of a callable integrand over a given domain.
Definition adaptive.hpp:82