limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
methods.hpp
Go to the documentation of this file.
1#pragma once
2
24#include <cstddef>
25#include <optional>
26#include <random>
27#include <functional>
28#include "concepts.hpp"
29#include "../algorithms/integrators/integrators.hpp"
30#include "../algorithms/quadrature/quadrature.hpp"
31
32namespace limes::methods {
33
34// =============================================================================
35// Gauss-Legendre Quadrature Method
36// =============================================================================
37
39template<std::size_t N, typename T = double>
41 static constexpr std::size_t order = N;
42 using value_type = T;
43
44 constexpr gauss_legendre() noexcept = default;
45
46 [[nodiscard]] algorithms::integration_result<T>
47 operator()(std::function<T(T)> const& f, T a, T b) const {
49 return integrator(f, a, b);
50 }
51};
52
53template<std::size_t N, typename T>
54struct is_integration_method<gauss_legendre<N, T>> : std::true_type {};
55
57template<std::size_t N, typename T = double>
58[[nodiscard]] constexpr auto gauss() {
59 return gauss_legendre<N, T>{};
60}
61
62// =============================================================================
63// Adaptive Integration Method
64// =============================================================================
65
67template<typename T = double>
68struct adaptive {
69 using value_type = T;
70
72 std::size_t max_subdivisions;
73
74 constexpr adaptive(T tol = T(1e-10), std::size_t max_sub = 1000) noexcept
75 : tolerance{tol}, max_subdivisions{max_sub} {}
76
77 [[nodiscard]] algorithms::integration_result<T>
78 operator()(std::function<T(T)> const& f, T a, T b) const {
80 return integrator(f, a, b, tolerance);
81 }
82
83 [[nodiscard]] constexpr adaptive with_tolerance(T tol) const noexcept {
84 return adaptive{tol, max_subdivisions};
85 }
86
87 [[nodiscard]] constexpr adaptive with_max_subdivisions(std::size_t max_sub) const noexcept {
88 return adaptive{tolerance, max_sub};
89 }
90};
91
92template<typename T>
93struct is_integration_method<adaptive<T>> : std::true_type {};
94
96template<typename T = double>
97[[nodiscard]] constexpr auto adaptive_method(T tol = T(1e-10)) {
98 return adaptive<T>{tol};
99}
100
101// =============================================================================
102// Monte Carlo Integration Method
103// =============================================================================
104
106template<typename T = double>
108 using value_type = T;
109
110 std::size_t samples;
111 std::optional<std::size_t> seed;
112
113 constexpr monte_carlo(std::size_t n, std::optional<std::size_t> s = std::nullopt) noexcept
114 : samples{n}, seed{s} {}
115
116 [[nodiscard]] algorithms::integration_result<T>
117 operator()(std::function<T(T)> const& f, T a, T b) const {
118 std::mt19937_64 rng;
119 if (seed) {
120 rng.seed(*seed);
121 } else {
122 std::random_device rd;
123 rng.seed(rd());
124 }
125
126 std::uniform_real_distribution<T> dist(a, b);
127
128 T sum = T(0);
129 T sum_sq = T(0);
130
131 for (std::size_t i = 0; i < samples; ++i) {
132 T y = f(dist(rng));
133 sum += y;
134 sum_sq += y * y;
135 }
136
137 T interval_length = b - a;
138 T mean = sum / T(samples);
139 T variance = (sum_sq / T(samples) - mean * mean) / T(samples);
140 T value = mean * interval_length;
141 T error = std::sqrt(variance) * interval_length;
142
144 result.variance_ = variance;
145 return result;
146 }
147
148 [[nodiscard]] constexpr monte_carlo with_seed(std::size_t s) const noexcept {
149 return monte_carlo{samples, s};
150 }
151
152 [[nodiscard]] constexpr monte_carlo with_samples(std::size_t n) const noexcept {
153 return monte_carlo{n, seed};
154 }
155};
156
157template<typename T>
158struct is_integration_method<monte_carlo<T>> : std::true_type {};
159
161template<typename T = double>
162[[nodiscard]] constexpr auto monte_carlo_method(std::size_t n) {
163 return monte_carlo<T>{n};
164}
165
166// =============================================================================
167// Simpson's Rule Method
168// =============================================================================
169
171template<std::size_t N, typename T = double>
172struct simpson {
173 static_assert(N % 2 == 0, "Simpson's rule requires even number of subdivisions");
174
175 static constexpr std::size_t subdivisions = N;
176 using value_type = T;
177
178 constexpr simpson() noexcept = default;
179
180 [[nodiscard]] algorithms::integration_result<T>
181 operator()(std::function<T(T)> const& f, T a, T b) const {
182 T h = (b - a) / T(N);
183 T sum = f(a) + f(b);
184
185 for (std::size_t i = 1; i < N; i += 2) {
186 sum += T(4) * f(a + T(i) * h);
187 }
188 for (std::size_t i = 2; i < N; i += 2) {
189 sum += T(2) * f(a + T(i) * h);
190 }
191
192 T value = sum * h / T(3);
193 T error = std::abs(value) * std::pow(h, 4);
194
195 return algorithms::integration_result<T>{value, error, N + 1, N + 1};
196 }
197};
198
199template<std::size_t N, typename T>
200struct is_integration_method<simpson<N, T>> : std::true_type {};
201
203template<std::size_t N, typename T = double>
204[[nodiscard]] constexpr auto simpson_method() {
205 return simpson<N, T>{};
206}
207
208// =============================================================================
209// Trapezoidal Rule Method
210// =============================================================================
211
213template<std::size_t N, typename T = double>
215 static constexpr std::size_t subdivisions = N;
216 using value_type = T;
217
218 constexpr trapezoidal() noexcept = default;
219
220 [[nodiscard]] algorithms::integration_result<T>
221 operator()(std::function<T(T)> const& f, T a, T b) const {
222 T h = (b - a) / T(N);
223 T sum = (f(a) + f(b)) / T(2);
224
225 for (std::size_t i = 1; i < N; ++i) {
226 sum += f(a + T(i) * h);
227 }
228
229 T value = sum * h;
230 T error = std::abs(value) * h * h;
231
232 return algorithms::integration_result<T>{value, error, N + 1, N + 1};
233 }
234};
235
236template<std::size_t N, typename T>
237struct is_integration_method<trapezoidal<N, T>> : std::true_type {};
238
240template<std::size_t N, typename T = double>
241[[nodiscard]] constexpr auto trapezoidal_method() {
242 return trapezoidal<N, T>{};
243}
244
245// =============================================================================
246// Composed Adaptive Method (wraps another method for adaptive refinement)
247// =============================================================================
248
250template<typename BaseMethod, typename T = double>
252 using value_type = T;
253
254 BaseMethod base;
256 std::size_t max_depth;
257
258 constexpr adaptive_composed(BaseMethod m, T tol = T(1e-10), std::size_t depth = 20) noexcept
259 : base{m}, tolerance{tol}, max_depth{depth} {}
260
261 [[nodiscard]] algorithms::integration_result<T>
262 operator()(std::function<T(T)> const& f, T a, T b) const {
263 return integrate_adaptive(f, a, b, 0);
264 }
265
266 [[nodiscard]] constexpr adaptive_composed with_tolerance(T tol) const noexcept {
267 return adaptive_composed{base, tol, max_depth};
268 }
269
270private:
272 integrate_adaptive(std::function<T(T)> const& f, T a, T b, std::size_t depth) const {
273 auto full = base(f, a, b);
274
275 if (depth >= max_depth) {
276 full.converged_ = false;
277 return full;
278 }
279
280 T mid = (a + b) / T(2);
281 auto left = base(f, a, mid);
282 auto right = base(f, mid, b);
283 auto refined = left + right;
284
285 T error = std::abs(full.value() - refined.value());
286
287 if (error < tolerance) {
288 refined.error_ = error;
289 return refined;
290 }
291
292 auto left_refined = integrate_adaptive(f, a, mid, depth + 1);
293 auto right_refined = integrate_adaptive(f, mid, b, depth + 1);
294
295 return left_refined + right_refined;
296 }
297};
298
299template<typename M, typename T>
300struct is_integration_method<adaptive_composed<M, T>> : std::true_type {};
301
303template<typename M, typename T = double>
304[[nodiscard]] constexpr auto make_adaptive(M base_method, T tol = T(1e-10)) {
305 return adaptive_composed<M, T>{base_method, tol};
306}
307
308// =============================================================================
309// Default method
310// =============================================================================
311
313template<typename T = double>
315
316} // namespace limes::methods
constexpr auto monte_carlo_method(std::size_t n)
Factory for Monte Carlo integration.
Definition methods.hpp:162
constexpr auto adaptive_method(T tol=T(1e-10))
Factory for adaptive integration.
Definition methods.hpp:97
constexpr auto make_adaptive(M base_method, T tol=T(1e-10))
Factory for adaptive composition of any base method.
Definition methods.hpp:304
constexpr auto trapezoidal_method()
Factory for trapezoidal rule.
Definition methods.hpp:241
constexpr auto gauss()
Factory for Gauss-Legendre quadrature.
Definition methods.hpp:58
constexpr auto simpson_method()
Factory for Simpson's rule.
Definition methods.hpp:204
Wraps any base method with adaptive interval subdivision until convergence.
Definition methods.hpp:251
constexpr adaptive_composed(BaseMethod m, T tol=T(1e-10), std::size_t depth=20) noexcept
Definition methods.hpp:258
algorithms::integration_result< T > operator()(std::function< T(T)> const &f, T a, T b) const
Definition methods.hpp:262
constexpr adaptive_composed with_tolerance(T tol) const noexcept
Definition methods.hpp:266
Adaptive integration with recursive interval subdivision until convergence.
Definition methods.hpp:68
algorithms::integration_result< T > operator()(std::function< T(T)> const &f, T a, T b) const
Definition methods.hpp:78
constexpr adaptive with_max_subdivisions(std::size_t max_sub) const noexcept
Definition methods.hpp:87
constexpr adaptive with_tolerance(T tol) const noexcept
Definition methods.hpp:83
constexpr adaptive(T tol=T(1e-10), std::size_t max_sub=1000) noexcept
Definition methods.hpp:74
std::size_t max_subdivisions
Definition methods.hpp:72
N-point Gauss-Legendre quadrature, achieving degree 2N-1 polynomial exactness.
Definition methods.hpp:40
static constexpr std::size_t order
Definition methods.hpp:41
constexpr gauss_legendre() noexcept=default
Type trait to tag types as integration methods (specialized per method struct)
Definition concepts.hpp:17
Monte Carlo integration using random sampling. Error decreases as O(1/sqrt(n)).
Definition methods.hpp:107
std::optional< std::size_t > seed
Definition methods.hpp:111
constexpr monte_carlo with_seed(std::size_t s) const noexcept
Definition methods.hpp:148
constexpr monte_carlo(std::size_t n, std::optional< std::size_t > s=std::nullopt) noexcept
Definition methods.hpp:113
algorithms::integration_result< T > operator()(std::function< T(T)> const &f, T a, T b) const
Definition methods.hpp:117
constexpr monte_carlo with_samples(std::size_t n) const noexcept
Definition methods.hpp:152
Simpson's 1/3 rule with N subdivisions (N must be even). Achieves O(h^4) convergence.
Definition methods.hpp:172
static constexpr std::size_t subdivisions
Definition methods.hpp:175
constexpr simpson() noexcept=default
Trapezoidal rule with N subdivisions. Achieves O(h^2) convergence.
Definition methods.hpp:214
constexpr trapezoidal() noexcept=default
static constexpr std::size_t subdivisions
Definition methods.hpp:215