limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
integrators.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <limits>
5#include <functional>
6#include "../concepts/concepts.hpp"
7#include "../core/result.hpp"
8#include "../accumulators/accumulators.hpp"
9#include "../quadrature/quadrature.hpp"
10
11namespace limes::algorithms {
12
13// Basic quadrature integrator using any quadrature rule and accumulator
14template<
15 concepts::Field T,
16 concepts::QuadratureRule<T> Rule,
17 concepts::Accumulator<T> Acc = accumulators::simple_accumulator<T>
18>
20public:
21 using value_type = T;
23 using rule_type = Rule;
24 using accumulator_type = Acc;
25
26 constexpr quadrature_integrator() noexcept = default;
27
28 constexpr explicit quadrature_integrator(Rule rule, Acc acc = {}) noexcept
29 : rule_{std::move(rule)}, accumulator_template_{std::move(acc)} {}
30
31 template<concepts::UnivariateFunction<T> F>
32 constexpr result_type operator()(F&& f, T a, T b) const {
33 return integrate_interval(std::forward<F>(f), a, b);
34 }
35
36 template<concepts::UnivariateFunction<T> F>
37 constexpr result_type operator()(F&& f, T a, T b, T tol) const {
38 return adaptive_integrate(std::forward<F>(f), a, b, tol);
39 }
40
41private:
42 Rule rule_;
43 Acc accumulator_template_;
44
45 template<typename F>
46 constexpr result_type integrate_interval(F&& f, T a, T b) const {
47 Acc acc = accumulator_template_;
48 const T half_width = (b - a) / T(2);
49 const T center = (a + b) / T(2);
50
51 for (std::size_t i = 0; i < rule_.size(); ++i) {
52 T x = center + half_width * rule_.abscissa(i);
53 acc += rule_.weight(i) * f(x);
54 }
55
56 T integral = half_width * acc();
57 return {integral, std::numeric_limits<T>::epsilon() * std::abs(integral), rule_.size()};
58 }
59
60 template<typename F>
61 constexpr result_type adaptive_integrate(F&& f, T a, T b, T tol, std::size_t depth = 0) const {
62 constexpr std::size_t max_depth = 30;
63
64 result_type whole = integrate_interval(f, a, b);
65
66 if (depth >= max_depth) {
67 return whole;
68 }
69
70 T mid = (a + b) / T(2);
71 result_type left = integrate_interval(f, a, mid);
72 result_type right = integrate_interval(f, mid, b);
73 result_type combined = left + right;
74
75 T error = std::abs(whole.value() - combined.value());
76
77 if (error <= tol) {
78 // Richardson extrapolation for better accuracy
79 T refined = combined.value() + (combined.value() - whole.value()) / T(15);
80 return {refined, error, combined.evaluations()};
81 }
82
83 // Recursive subdivision
84 result_type left_refined = adaptive_integrate(f, a, mid, tol / T(2), depth + 1);
85 result_type right_refined = adaptive_integrate(f, mid, b, tol / T(2), depth + 1);
86
87 return left_refined + right_refined;
88 }
89};
90
91// Specialized integrator for infinite intervals using tanh-sinh transform
92template<
93 concepts::Field T,
94 concepts::Accumulator<T> Acc = accumulators::neumaier_accumulator<T>
95>
97public:
98 using value_type = T;
100 using accumulator_type = Acc;
101
102 constexpr tanh_sinh_integrator() noexcept = default;
103
104 template<concepts::UnivariateFunction<T> F>
105 constexpr result_type operator()(F&& f, T a, T b, T tol = default_tolerance()) const {
106 if (std::isinf(a) && std::isinf(b)) {
107 return integrate_infinite(std::forward<F>(f), tol);
108 } else if (std::isinf(a)) {
109 return integrate_left_infinite(std::forward<F>(f), b, tol);
110 } else if (std::isinf(b)) {
111 return integrate_right_infinite(std::forward<F>(f), a, tol);
112 } else {
113 return integrate_finite(std::forward<F>(f), a, b, tol);
114 }
115 }
116
117private:
118 static constexpr T default_tolerance() { return std::sqrt(std::numeric_limits<T>::epsilon()); }
119
120 template<typename F>
121 constexpr result_type integrate_finite(F&& f, T a, T b, T tol) const {
122 const T center = (a + b) / T(2);
123 const T half_width = (b - a) / T(2);
124
125 auto transformed = [&](T t) -> T {
126 return f(center + half_width * t);
127 };
128
129 return integrate_symmetric(transformed, half_width, tol);
130 }
131
132 template<typename F>
133 constexpr result_type integrate_infinite(F&& f, T tol) const {
134 auto transformed = [&](T t) -> T {
135 T scale = T(1) / (T(1) - t * t);
136 return f(t * scale) * scale * scale;
137 };
138
139 return integrate_symmetric(transformed, T(1), tol);
140 }
141
142 template<typename F>
143 constexpr result_type integrate_left_infinite(F&& f, T b, T tol) const {
144 auto transformed = [&](T t) -> T {
145 T scale = T(1) / (T(1) + t);
146 T x = b - scale * (T(1) - t);
147 return f(x) * scale * scale;
148 };
149
150 return integrate_symmetric(transformed, T(1), tol);
151 }
152
153 template<typename F>
154 constexpr result_type integrate_right_infinite(F&& f, T a, T tol) const {
155 auto transformed = [&](T t) -> T {
156 T scale = T(1) / (T(1) - t);
157 T x = a + scale * (T(1) + t);
158 return f(x) * scale * scale;
159 };
160
161 return integrate_symmetric(transformed, T(1), tol);
162 }
163
164 template<typename F>
165 constexpr result_type integrate_symmetric(F&& f, T scale, T tol) const {
166 quadrature::tanh_sinh_nodes<T> nodes;
167 Acc acc{};
168 T h = T(1);
169 T prev_sum = T(0);
170 std::size_t total_evals = 0;
171
172 for (std::size_t level = 0; level < nodes.max_level; ++level) {
173 Acc level_acc{};
174 std::size_t level_evals = 0;
175
176 if (level == 0) {
177 T w = nodes.weight(0, 0);
178 level_acc += w * f(T(0));
179 level_evals = 1;
180 } else {
181 std::size_t n = 1 << (level - 1);
182 for (std::size_t i = 1; i <= n; ++i) {
183 std::size_t index = 2 * i - 1;
184 T x = nodes.abscissa(level, index);
185 T w = nodes.weight(level, index);
186
187 T val = f(x) + f(-x);
188 if (!std::isfinite(val)) break;
189
190 level_acc += w * val;
191 level_evals += 2;
192 }
193 }
194
195 acc += level_acc();
196 total_evals += level_evals;
197
198 T sum = scale * h * acc();
199 T error = std::abs(sum - prev_sum);
200
201 if (level > 3 && error < tol * std::abs(sum)) {
202 return {sum, error, level + 1, total_evals};
203 }
204
205 prev_sum = sum;
206 h /= T(2);
207 }
208
209 return {scale * h * acc(), tol, nodes.max_level, total_evals};
210 }
211};
212
213// Romberg integrator with Richardson extrapolation
214template<
215 concepts::Field T,
216 concepts::Accumulator<T> Acc = accumulators::kahan_accumulator<T>
217>
219public:
220 using value_type = T;
222 using accumulator_type = Acc;
223
224 static constexpr std::size_t max_iterations = 20;
225
226 constexpr romberg_integrator() noexcept = default;
227
228 template<concepts::UnivariateFunction<T> F>
229 constexpr result_type operator()(F&& f, T a, T b, T tol = default_tolerance()) const {
231
232 // Initial trapezoidal estimate
233 R[0][0] = (b - a) * (f(a) + f(b)) / T(2);
234 std::size_t total_evals = 2;
235
236 for (std::size_t i = 1; i < max_iterations; ++i) {
237 // Refine trapezoidal rule
238 std::size_t n = 1 << (i - 1);
239 T h = (b - a) / T(n << 1);
240 Acc sum{};
241
242 for (std::size_t k = 0; k < n; ++k) {
243 sum += f(a + h * (T(2) * k + T(1)));
244 }
245
246 R[i][0] = R[i-1][0] / T(2) + h * sum();
247 total_evals += n;
248
249 // Richardson extrapolation
250 T power = T(4);
251 for (std::size_t j = 1; j <= i; ++j) {
252 R[i][j] = R[i][j-1] + (R[i][j-1] - R[i-1][j-1]) / (power - T(1));
253 power *= T(4);
254 }
255
256 // Check convergence
257 if (i > 3) {
258 T error = std::abs(R[i][i] - R[i-1][i-1]);
259 if (error < tol * std::abs(R[i][i])) {
260 return {R[i][i], error, i + 1, total_evals};
261 }
262 }
263 }
264
265 return {R[max_iterations-1][max_iterations-1], tol, max_iterations, total_evals};
266 }
267
268private:
269 static constexpr T default_tolerance() {
270 return T(1000) * std::numeric_limits<T>::epsilon();
271 }
272};
273
274// Recommended adaptive integrator: Gauss-Kronrod 15 with Neumaier accumulation
275template<concepts::Field T>
277 T,
280>;
281
282} // namespace limes::algorithms
constexpr result_type operator()(F &&f, T a, T b, T tol) const
constexpr quadrature_integrator() noexcept=default
constexpr result_type operator()(F &&f, T a, T b) const
static constexpr std::size_t max_iterations
constexpr romberg_integrator() noexcept=default
constexpr tanh_sinh_integrator() noexcept=default
constexpr auto sum(E body, int lo, int hi)
constexpr Var< 0, T > x
Definition var.hpp:46