numerics 0.1.0
Loading...
Searching...
No Matches
quadrature.cpp
Go to the documentation of this file.
1/// @file quadrature.cpp
3#include "analysis/types.hpp"
4#include <cmath>
5#include <stdexcept>
6#include <vector>
7
8#ifdef NUMERICS_HAS_OMP
9 #include <omp.h>
10#endif
11
12namespace num {
13
14real trapz(ScalarFn f, real a, real b, idx n, Backend backend) {
15 real h = (b - a) / n;
16 real sum = 0.0;
17#ifdef NUMERICS_HAS_OMP
18 #pragma omp parallel for reduction(+ : sum) \
19 schedule(static) if (backend == Backend::omp)
20#endif
21 for (idx i = 1; i < n; ++i)
22 sum += f(a + i * h);
23 return h * (0.5 * (f(a) + f(b)) + sum);
24}
25
26real simpson(ScalarFn f, real a, real b, idx n, Backend backend) {
27 if (n % 2 != 0)
28 throw std::invalid_argument("simpson: n must be even");
29 real h = (b - a) / n;
30 real sum = f(a) + f(b);
31#ifdef NUMERICS_HAS_OMP
32 #pragma omp parallel for reduction(+ : sum) \
33 schedule(static) if (backend == Backend::omp)
34#endif
35 for (idx i = 1; i < n; ++i)
36 sum += f(a + i * h) * (i % 2 == 0 ? 2.0 : 4.0);
37 return h / 3.0 * sum;
38}
39
40// Gauss-Legendre nodes and weights on [-1, 1] for p = 1..5
41// Source: Abramowitz & Stegun, Table 25.4
42static constexpr real GL_NODES[5][5] = {
43 {0.0, 0, 0, 0, 0},
44 {-0.5773502691896257, 0.5773502691896257, 0, 0, 0},
45 {-0.7745966692414834, 0.0, 0.7745966692414834, 0, 0},
46 {-0.8611363115940526, -0.3399810435848563, 0.3399810435848563, 0.8611363115940526, 0},
47 {-0.9061798459386640,
48 -0.5384693101056831,
49 0.0,
50 0.5384693101056831,
51 0.9061798459386640}};
52static constexpr real GL_WEIGHTS[5][5] = {
53 {2.0, 0, 0, 0, 0},
54 {1.0, 1.0, 0, 0, 0},
55 {0.5555555555555556, 0.8888888888888889, 0.5555555555555556, 0, 0},
56 {0.3478548451374538, 0.6521451548625461, 0.6521451548625461, 0.3478548451374538, 0},
57 {0.2369268850561891,
58 0.4786286704993665,
59 0.5688888888888889,
60 0.4786286704993665,
61 0.2369268850561891}};
62
64 if (p < 1 || p > 5)
65 throw std::invalid_argument("gauss_legendre: p must be 1..5");
66 real mid = 0.5 * (a + b);
67 real half = 0.5 * (b - a);
68 real sum = 0.0;
69 for (idx i = 0; i < p; ++i)
70 sum += GL_WEIGHTS[p - 1][i] * f(mid + half * GL_NODES[p - 1][i]);
71 return half * sum;
72}
73
74// Recursive adaptive Simpson helper
75static real adaptive_helper(ScalarFn f,
76 real a,
77 real b,
78 real fa,
79 real fm,
80 real fb,
81 real whole,
82 real tol,
83 idx depth) {
84 real mid_l = 0.5 * (a + b * 0.5 + a * 0.5); // midpoint of [a, mid]
85 real mid_r = 0.5 * (0.5 * (a + b) + b); // midpoint of [mid, b]
86 real mid = 0.5 * (a + b);
87 real fl = f(0.5 * (a + mid));
88 real fr = f(0.5 * (mid + b));
89 (void)mid_l;
90 (void)mid_r;
91
92 real left = (b - a) / 12.0 * (fa + 4.0 * fl + fm);
93 real right = (b - a) / 12.0 * (fm + 4.0 * fr + fb);
94 real delta = left + right - whole;
95
96 if (depth == 0 || std::abs(delta) <= 15.0 * tol)
97 return left + right + delta / 15.0;
98
99 return adaptive_helper(f, a, mid, fa, fl, fm, left, tol * 0.5, depth - 1)
100 + adaptive_helper(f, mid, b, fm, fr, fb, right, tol * 0.5, depth - 1);
101}
102
103real adaptive_simpson(ScalarFn f, real a, real b, real tol, idx max_depth) {
104 real fa = f(a), fb = f(b), fm = f(0.5 * (a + b));
105 real est = (b - a) / 6.0 * (fa + 4.0 * fm + fb);
106 return adaptive_helper(f, a, b, fa, fm, fb, est, tol, max_depth);
107}
108
109real romberg(ScalarFn f, real a, real b, real tol, idx max_levels) {
110 std::vector<std::vector<real>> R(max_levels, std::vector<real>(max_levels, 0.0));
111 R[0][0] = 0.5 * (b - a) * (f(a) + f(b));
112
113 for (idx i = 1; i < max_levels; ++i) {
114 idx n = idx(1) << i; // 2^i panels
115 real h = (b - a) / n;
116 real sum = 0.0;
117 for (idx k = 1; k < n; k += 2)
118 sum += f(a + k * h);
119 R[i][0] = 0.5 * R[i - 1][0] + h * sum;
120
121 // Richardson extrapolation
122 real factor = 1.0;
123 for (idx j = 1; j <= i; ++j) {
124 factor *= 4.0;
125 R[i][j] = R[i][j - 1] + (R[i][j - 1] - R[i - 1][j - 1]) / (factor - 1.0);
126 }
127
128 if (i > 0 && std::abs(R[i][i] - R[i - 1][i - 1]) < tol)
129 return R[i][i];
130 }
131 return R[max_levels - 1][max_levels - 1];
132}
133
134} // namespace num
Common function type aliases for the analysis module.
real trapz(ScalarFn f, real a, real b, idx n=100, Backend backend=Backend::seq)
Trapezoidal rule with n panels.
double real
Definition types.hpp:10
Backend
Definition policy.hpp:7
std::size_t idx
Definition types.hpp:11
real gauss_legendre(ScalarFn f, real a, real b, idx p=5)
Gauss-Legendre quadrature (exact for polynomials up to degree 2p-1)
real adaptive_simpson(ScalarFn f, real a, real b, real tol=1e-8, idx max_depth=50)
Adaptive Simpson quadrature.
real simpson(ScalarFn f, real a, real b, idx n=100, Backend backend=Backend::seq)
Simpson's 1/3 rule with n panels (n must be even)
std::function< real(real)> ScalarFn
Real-valued scalar function f(x)
Definition types.hpp:11
real romberg(ScalarFn f, real a, real b, real tol=1e-10, idx max_levels=12)
Romberg integration (Richardson extrapolation on trapezoidal rule)
Numerical integration (quadrature) on [a, b].