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,
47 -0.3399810435848563,
48 0.3399810435848563,
49 0.8611363115940526,
50 0},
51 {-0.9061798459386640,
52 -0.5384693101056831,
53 0.0,
54 0.5384693101056831,
55 0.9061798459386640}};
56static constexpr real GL_WEIGHTS[5][5] = {
57 {2.0, 0, 0, 0, 0},
58 {1.0, 1.0, 0, 0, 0},
59 {0.5555555555555556, 0.8888888888888889, 0.5555555555555556, 0, 0},
60 {0.3478548451374538,
61 0.6521451548625461,
62 0.6521451548625461,
63 0.3478548451374538,
64 0},
65 {0.2369268850561891,
66 0.4786286704993665,
67 0.5688888888888889,
68 0.4786286704993665,
69 0.2369268850561891}};
70
72 if (p < 1 || p > 5)
73 throw std::invalid_argument("gauss_legendre: p must be 1..5");
74 real mid = 0.5 * (a + b);
75 real half = 0.5 * (b - a);
76 real sum = 0.0;
77 for (idx i = 0; i < p; ++i)
78 sum += GL_WEIGHTS[p - 1][i] * f(mid + half * GL_NODES[p - 1][i]);
79 return half * sum;
80}
81
82// Recursive adaptive Simpson helper
83static real adaptive_helper(ScalarFn f,
84 real a,
85 real b,
86 real fa,
87 real fm,
88 real fb,
89 real whole,
90 real tol,
91 idx depth) {
92 real mid_l = 0.5 * (a + b * 0.5 + a * 0.5); // midpoint of [a, mid]
93 real mid_r = 0.5 * (0.5 * (a + b) + b); // midpoint of [mid, b]
94 real mid = 0.5 * (a + b);
95 real fl = f(0.5 * (a + mid));
96 real fr = f(0.5 * (mid + b));
97 (void)mid_l;
98 (void)mid_r;
99
100 real left = (b - a) / 12.0 * (fa + 4.0 * fl + fm);
101 real right = (b - a) / 12.0 * (fm + 4.0 * fr + fb);
102 real delta = left + right - whole;
103
104 if (depth == 0 || std::abs(delta) <= 15.0 * tol)
105 return left + right + delta / 15.0;
106
107 return adaptive_helper(f, a, mid, fa, fl, fm, left, tol * 0.5, depth - 1)
108 + adaptive_helper(f,
109 mid,
110 b,
111 fm,
112 fr,
113 fb,
114 right,
115 tol * 0.5,
116 depth - 1);
117}
118
119real adaptive_simpson(ScalarFn f, real a, real b, real tol, idx max_depth) {
120 real fa = f(a), fb = f(b), fm = f(0.5 * (a + b));
121 real est = (b - a) / 6.0 * (fa + 4.0 * fm + fb);
122 return adaptive_helper(f, a, b, fa, fm, fb, est, tol, max_depth);
123}
124
125real romberg(ScalarFn f, real a, real b, real tol, idx max_levels) {
126 std::vector<std::vector<real>> R(max_levels,
127 std::vector<real>(max_levels, 0.0));
128 R[0][0] = 0.5 * (b - a) * (f(a) + f(b));
129
130 for (idx i = 1; i < max_levels; ++i) {
131 idx n = idx(1) << i; // 2^i panels
132 real h = (b - a) / n;
133 real sum = 0.0;
134 for (idx k = 1; k < n; k += 2)
135 sum += f(a + k * h);
136 R[i][0] = 0.5 * R[i - 1][0] + h * sum;
137
138 // Richardson extrapolation
139 real factor = 1.0;
140 for (idx j = 1; j <= i; ++j) {
141 factor *= 4.0;
142 R[i][j] = R[i][j - 1]
143 + (R[i][j - 1] - R[i - 1][j - 1]) / (factor - 1.0);
144 }
145
146 if (i > 0 && std::abs(R[i][i] - R[i - 1][i - 1]) < tol)
147 return R[i][i];
148 }
149 return R[max_levels - 1][max_levels - 1];
150}
151
152} // 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
Selects which backend handles a linalg operation.
Definition policy.hpp:19
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].