Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
finite_quadrature.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "math.hpp"
4#include <algorithm>
5#include <array>
6#include <cstddef>
7#include <limits>
8#include <stdexcept>
9
10namespace uni20
11{
16template <Real R> struct quadrature_point
17{
21};
22
33
38template <Real R, std::size_t N = 1> struct tanh_sinh_options
39{
40 static_assert(N > 0);
42 {
43 if (p.is_exact()) throw std::invalid_argument("quadrature requires finite precision");
44 relative_tolerance = numeric_limits<R>::epsilon(p) * scalar_at_precision<R>(64, p);
45 absolute_tolerance.fill(scalar_at_precision<R>(0, p));
46 }
48 requires(!has_runtime_precision_v<R>)
50 {}
51
53 std::array<R, N> absolute_tolerance;
54 std::size_t max_evaluations = 200000;
55 std::size_t max_levels = 12;
56 unsigned endpoint_power = 3;
57};
58
65template <Real R, std::size_t N = 1> struct quadrature_result
66{
67 std::array<R, N> value;
68 std::array<R, N> error;
69 std::array<R, N> absolute_integral;
70 std::array<R, N> endpoint_indicator;
71 std::size_t evaluations = 0;
72 std::size_t levels = 0;
74};
75
76namespace detail
77{
78template <class R> struct quadrature_sum
79{
80 R sum, correction;
81 void add(R const& value)
82 {
83 R const adjusted = value - correction, next = sum + adjusted;
84 correction = (next - sum) - adjusted;
85 sum = next;
86 }
87};
88} // namespace detail
89
99template <Real R, std::size_t N, class F>
100quadrature_result<R, N> tanh_sinh_integrate(F const& f, R const& lo, R const& hi,
101 tanh_sinh_options<R, N> const& options)
102{
103 auto const p = precision_of(lo);
104 if (p.is_exact() || precision_of(hi) != p || precision_of(options.relative_tolerance) != p)
105 throw std::invalid_argument("quadrature requires matching finite precision");
106 auto number = [p](auto x) { return scalar_at_precision<R>(x, p); };
107 R const zero = number(0), one = number(1), two = number(2);
108 if (!isfinite(lo) || !isfinite(hi) || hi < lo || !isfinite(options.relative_tolerance) ||
109 options.relative_tolerance < zero || options.endpoint_power < 2)
110 throw std::invalid_argument("invalid quadrature interval, tolerance or endpoint power");
111 for (auto const& tolerance : options.absolute_tolerance)
112 if (precision_of(tolerance) != p || !isfinite(tolerance) || tolerance < zero ||
113 (tolerance == zero && options.relative_tolerance == zero))
114 throw std::invalid_argument("quadrature requires nonnegative tolerances, with a positive target per component");
115
117 // Infinity is only a diagnostic sentinel; no finite input is converted via double.
118 R const infinity = number(numeric_limits<double>::infinity());
119 out.value.fill(zero);
120 out.error.fill(infinity);
121 out.absolute_integral.fill(zero);
122 out.endpoint_indicator.fill(infinity);
123 if (lo == hi)
124 {
125 out.error.fill(zero);
126 out.endpoint_indicator.fill(zero);
127 out.status = quadrature_status::converged;
128 return out;
129 }
130 R const width = hi - lo, eps = numeric_limits<R>::epsilon(p), pi_value = pi<R> * one;
131 R const minimum = math::next_up(zero);
132 R const limit = -number(options.endpoint_power) * math::log(eps) + number(4);
133 if (!isfinite(width) || !isfinite(limit) || !(width / two > zero) || !(limit > zero))
134 {
136 return out;
137 }
138 std::array<R, N> previous = out.value;
139 unsigned stable = 0, endpoint_stable = 0;
140 R h = one;
141 for (std::size_t level = 0; level < options.max_levels; ++level)
142 {
143 std::array<detail::quadrature_sum<R>, N> sum, absolute;
144 for (std::size_t j = 0; j < N; ++j)
145 sum[j] = absolute[j] = {zero, zero};
146 std::array<R, N> edge;
147 std::array<std::size_t, N> nonzero_samples{};
148 edge.fill(zero);
149 auto sample = [&](R const& x, R const& lower, R const& upper, R const& unscaled_weight, bool endpoint) {
150 if (out.evaluations == options.max_evaluations)
151 {
153 return false;
154 }
155 R const weight = unscaled_weight * h;
156 if (!(lower > zero) || !(upper > zero) || !(weight > zero) || !isfinite(weight))
157 {
159 return false;
160 }
161 ++out.evaluations;
162 std::array<R, N> const y = f(quadrature_point<R>{x, lower, upper});
163 for (std::size_t j = 0; j < N; ++j)
164 {
165 if (precision_of(y[j]) != p) throw std::invalid_argument("quadrature integrand changed working precision");
166 if (!isfinite(y[j]))
167 {
169 return false;
170 }
171 R const term = weight * y[j];
172 if (y[j] != zero) ++nonzero_samples[j];
173 sum[j].add(term);
174 absolute[j].add(math::abs(term));
175 // Evaluate this diagnostic only at the final pair. At interior nodes
176 // the unscaled product may overflow while the weighted term fits.
177 if (endpoint) edge[j] += math::abs(unscaled_weight * y[j]);
178 if (!isfinite(sum[j].sum) || !isfinite(absolute[j].sum) || !isfinite(edge[j]))
179 {
181 return false;
182 }
183 }
184 return true;
185 };
186 R const half = width / two;
187 if (!sample(lo + half, half, half, width * (pi_value / number(4)), false)) return out;
188 R t = h, argument = pi_value * math::sinh(t);
189 while (argument <= limit)
190 {
191 R const next = t + h;
192 if (!(next > t))
193 {
195 return out;
196 }
197 R const next_argument = pi_value * math::sinh(next);
198 bool const endpoint = next_argument > limit;
199 R const z = math::exp(-argument), denominator = one + z;
200 R const distance = width * (z / denominator), far = width / denominator;
201 R const unscaled_weight = distance * (pi_value * math::cosh(t) / denominator);
202 if (!sample(lo + distance, distance, far, unscaled_weight, endpoint) ||
203 !sample(hi - distance, far, distance, unscaled_weight, endpoint))
204 return out;
205 t = next;
206 argument = next_argument;
207 }
208 bool mesh_ok = level >= 2, endpoints_ok = true, tail_limited_mesh = level >= 2;
209 for (std::size_t j = 0; j < N; ++j)
210 {
211 out.value[j] = sum[j].sum;
212 out.absolute_integral[j] = absolute[j].sum;
213 out.endpoint_indicator[j] = edge[j];
214 // Relative roundoff alone can underflow to zero and falsely certify an
215 // entirely subnormal integral. Include an absolute quantization allowance
216 // for each nonzero component sample, even when its weighted term vanishes.
217 R const roundoff = number(8) * (eps * out.absolute_integral[j] + minimum * number(nonzero_samples[j]));
218 R const mesh_error = level == 0 ? infinity : std::max(math::abs(out.value[j] - previous[j]), roundoff);
219 out.error[j] = std::max(mesh_error, edge[j]);
220 R const target = std::max(options.absolute_tolerance[j], options.relative_tolerance * math::abs(out.value[j]));
221 mesh_ok = mesh_ok && mesh_error <= target;
222 tail_limited_mesh = tail_limited_mesh && mesh_error <= std::max(target, edge[j]);
223 endpoints_ok = endpoints_ok && edge[j] <= target;
224 }
225 ++out.levels;
226 stable = mesh_ok ? stable + 1 : 0;
227 // A varying last abscissa can prevent mesh agreement even when the missing
228 // endpoint tail dominates the discrepancy. Diagnose that limitation rather
229 // than spending the refinement budget on the same insufficient cutoff.
230 endpoint_stable = tail_limited_mesh && !endpoints_ok ? endpoint_stable + 1 : 0;
231 if (stable == 2)
232 {
234 return out;
235 }
236 if (endpoint_stable == 2)
237 {
239 return out;
240 }
241 previous = out.value;
242 h /= two;
243 if (!(h > zero))
244 {
246 return out;
247 }
248 }
249 return out;
250}
251} // namespace uni20
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
locale_t value
Definition data_table.cpp:49
std::size_t width
Definition display.cpp:142
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Scalar math helper utilities.
constexpr detail::scalar_math::exp_fn exp
Natural exponential with scalar-specific precision handling.
Definition math.hpp:465
constexpr detail::scalar_math::cosh_fn cosh
Hyperbolic cosine.
Definition math.hpp:491
constexpr detail::scalar_math::sinh_fn sinh
Hyperbolic sine.
Definition math.hpp:489
constexpr detail::scalar_math::next_up_fn next_up
Adjacent value toward positive infinity.
Definition math.hpp:551
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
constexpr detail::scalar_math::log_fn log
Natural logarithm with scalar-specific precision handling.
Definition math.hpp:467
Definition data_table.cpp:8
Definition async/async.hpp:26
quadrature_status
Termination of finite-interval tanh-sinh integration.
Definition finite_quadrature.hpp:25
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
constexpr Precision precision_of(T const &value)
Read scalar precision without changing any scalar or provider state.
Definition runtime_precision.hpp:53
constexpr S scalar_at_precision(V const &value, Precision p)
Construct or convert a scalar at one explicit precision.
Definition runtime_precision.hpp:76
int add(int a, int b)
Definition dummy.cpp:6
constexpr Precision native_precision()
Concrete precision of a fixed scalar family, including complex components.
Definition runtime_precision.hpp:41
quadrature_result< R, N > tanh_sinh_integrate(F const &f, R const &lo, R const &hi, tanh_sinh_options< R, N > const &options)
Integrate N real components over lo<=hi using successive tanh-sinh meshes.
Definition finite_quadrature.hpp:100
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
An abscissa and independently evaluated distances to its finite endpoints.
Definition finite_quadrature.hpp:17
R upper_distance
Definition finite_quadrature.hpp:20
R value
Definition finite_quadrature.hpp:18
R lower_distance
Definition finite_quadrature.hpp:19
Last complete mesh, its diagnostic estimates and total work, including an interrupted mesh.
Definition finite_quadrature.hpp:66
std::size_t levels
Definition finite_quadrature.hpp:72
std::array< R, N > endpoint_indicator
Definition finite_quadrature.hpp:70
std::array< R, N > absolute_integral
Definition finite_quadrature.hpp:69
quadrature_status status
Definition finite_quadrature.hpp:73
std::array< R, N > error
Definition finite_quadrature.hpp:68
std::size_t evaluations
Definition finite_quadrature.hpp:71
std::array< R, N > value
Definition finite_quadrature.hpp:67
Explicit precision, component tolerances and work limits for tanh-sinh quadrature.
Definition finite_quadrature.hpp:39
unsigned endpoint_power
Definition finite_quadrature.hpp:56
std::size_t max_evaluations
Definition finite_quadrature.hpp:54
tanh_sinh_options(Precision p)
Definition finite_quadrature.hpp:41
tanh_sinh_options()
Definition finite_quadrature.hpp:47
R relative_tolerance
Definition finite_quadrature.hpp:52
std::array< R, N > absolute_tolerance
Definition finite_quadrature.hpp:53
std::size_t max_levels
Definition finite_quadrature.hpp:55