LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
numerical_integration.cpp
浏览该文件的文档.
1/**
2 * @file numerical_integration.cpp
3 * @brief 数值积分实现.
4 */
6
8#include "lmmc/quadrature.h"
9#include "symbolic.hpp"
11
12#include <algorithm>
13#include <cmath>
14#include <limits>
15#include <optional>
16#include <string>
17
18namespace LMCAS {
19namespace {
20
23 const std::string& operation) {
24 if (!value) return Result<double>::failure(value.error());
25 if (!value.value().is_finite() || !std::isfinite(value.value().value)) {
28 "numeric evaluation did not produce a finite double",
29 operation);
30 }
31 return Result<double>::success(value.value().value);
32}
33
35 const std::shared_ptr<SymbolicExpr>& expr,
37 const std::string& operation) {
38 if (!expr) {
40 CasErrc::InvalidArgument, "integration bound is null", operation);
41 }
43 evaluate_numeric(*expr, NumericBindings{}, context), operation);
44}
45
47 const std::shared_ptr<SymbolicExpr>& function,
48 const std::string& variable,
49 const std::shared_ptr<SymbolicExpr>& lower,
50 const std::shared_ptr<SymbolicExpr>& upper,
51 const std::string& operation) {
52 if (!function || !lower || !upper) {
55 "integrand and bounds must be non-null",
56 operation);
57 }
58 if (variable.empty()) {
61 "integration variable must not be empty",
62 operation);
63 }
64 return Result<void>::success();
65}
66
68 double lower = 0.0;
69 double upper = 0.0;
70 double sign = 1.0;
71};
72
74 const std::shared_ptr<SymbolicExpr>& lower,
75 const std::shared_ptr<SymbolicExpr>& upper,
77 const std::string& operation) {
78 auto lower_value = eval_bound_checked(lower, context, operation);
79 if (!lower_value) {
80 return Result<NormalizedInterval>::failure(lower_value.error());
81 }
82 auto upper_value = eval_bound_checked(upper, context, operation);
83 if (!upper_value) {
84 return Result<NormalizedInterval>::failure(upper_value.error());
85 }
86 NormalizedInterval interval{lower_value.value(), upper_value.value(), 1.0};
87 if (interval.lower > interval.upper) {
88 std::swap(interval.lower, interval.upper);
89 interval.sign = -1.0;
90 }
92}
93
95 const std::shared_ptr<SymbolicExpr>* function = nullptr;
96 const std::string* variable = nullptr;
98 const char* operation = nullptr;
99 std::optional<CasError> error;
100};
101
102double evaluate_quadrature_callback(double x, void* user_data) {
103 auto& callback = *static_cast<QuadratureCallback*>(user_data);
104 if (callback.error) return std::numeric_limits<double>::quiet_NaN();
106 bindings.emplace(*callback.variable, x);
108 evaluate_numeric(**callback.function, bindings, *callback.context),
109 callback.operation);
110 if (!value) {
111 callback.error = value.error();
112 return std::numeric_limits<double>::quiet_NaN();
113 }
114 return value.value();
115}
116
117CasErrc quadrature_error_code(lmmc_status_t status) {
118 if (status == LMMC_STATUS_INVALID_ARGUMENT) return CasErrc::InvalidArgument;
119 if (status == LMMC_STATUS_ALLOCATION_FAILED ||
120 status == LMMC_STATUS_REFERENCE_LIMIT ||
121 status == LMMC_STATUS_WARNING_MAX_DEPTH) {
123 }
125}
126
127template <typename T>
128Result<T> quadrature_failure(lmmc_status_t status, const std::string& operation) {
129 return Result<T>::failure(
130 quadrature_error_code(status), lmmc_status_string(status), operation);
131}
132
136
138 double value,
139 double estimated_error,
140 double sign,
141 const std::string& operation) {
142 value *= sign;
143 estimated_error = std::abs(estimated_error);
144 if (!std::isfinite(value) || !std::isfinite(estimated_error)) {
147 "quadrature accumulation or error estimate is non-finite",
148 operation);
149 }
150 QuadratureEstimate result;
151 result.value = value;
152 result.estimated_error = estimated_error;
154}
155
156}
157
159 const std::shared_ptr<SymbolicExpr>& function,
160 const std::string& variable,
161 const std::shared_ptr<SymbolicExpr>& lower,
162 const std::shared_ptr<SymbolicExpr>& upper,
164 int subdivisions) {
165 constexpr const char* operation = "quadrature_simpson";
166 auto valid = validate_quadrature_args(
167 function, variable, lower, upper, operation);
168 if (!valid) return Result<QuadratureEstimate>::failure(valid.error());
169 if (subdivisions <= 0 || subdivisions % 2 != 0) {
172 "Simpson subinterval count must be positive and even",
173 operation);
174 }
175 if (subdivisions > std::numeric_limits<int>::max() / 2) {
178 "Simpson refinement count overflows",
179 operation);
180 }
181 auto interval = evaluate_interval(lower, upper, context, operation);
183 if (interval.value().lower == interval.value().upper) {
184 return Result<QuadratureEstimate>::success(zero_quadrature_result());
185 }
186 const auto refined_subdivisions =
187 static_cast<std::size_t>(subdivisions) * 2;
188 const auto samples =
189 static_cast<std::size_t>(subdivisions) + 1 +
190 refined_subdivisions + 1;
191 auto budget = context.consume_steps(samples, operation);
192 if (!budget) return Result<QuadratureEstimate>::failure(budget.error());
193
195 QuadratureCallback callback{
196 &function, &variable, &context, operation, std::nullopt};
197 double value = 0.0;
198 auto status = lmmc_quad_simpson(
199 evaluate_quadrature_callback, &callback,
200 interval.value().lower, interval.value().upper,
201 static_cast<std::size_t>(subdivisions), &value);
202 if (callback.error) return Result<QuadratureEstimate>::failure(*callback.error);
203 if (status != LMMC_STATUS_OK) {
204 return quadrature_failure<QuadratureEstimate>(status, operation);
205 }
206
207 double refined_value = 0.0;
208 status = lmmc_quad_simpson(
209 evaluate_quadrature_callback, &callback,
210 interval.value().lower, interval.value().upper,
211 refined_subdivisions, &refined_value);
212 if (callback.error) return Result<QuadratureEstimate>::failure(*callback.error);
213 if (status != LMMC_STATUS_OK) {
214 return quadrature_failure<QuadratureEstimate>(status, operation);
215 }
216 const double correction = (refined_value - value) / 15.0;
217 const double extrapolated_value = refined_value + correction;
218 return estimated_quadrature_result(
219 extrapolated_value, std::abs(correction),
220 interval.value().sign, operation);
221}
222
224 const std::shared_ptr<SymbolicExpr>& function,
225 const std::string& variable,
226 const std::shared_ptr<SymbolicExpr>& lower,
227 const std::shared_ptr<SymbolicExpr>& upper,
228 int subdivisions) {
231 function, variable, lower, upper, context, subdivisions);
232}
233
235 const std::shared_ptr<SymbolicExpr>& function,
236 const std::string& variable,
237 const std::shared_ptr<SymbolicExpr>& lower,
238 const std::shared_ptr<SymbolicExpr>& upper,
240 int order) {
241 constexpr const char* operation = "quadrature_gaussian";
242 auto valid = validate_quadrature_args(
243 function, variable, lower, upper, operation);
244 if (!valid) return Result<QuadratureEstimate>::failure(valid.error());
245 if (order < 1 || order > 20) {
248 "Gaussian quadrature order must be in [1, 20]",
249 operation);
250 }
251 auto interval = evaluate_interval(lower, upper, context, operation);
253 if (interval.value().lower == interval.value().upper) {
254 return Result<QuadratureEstimate>::success(zero_quadrature_result());
255 }
256 const int comparison_order = order == 20 ? 19 : order + 1;
257 auto budget = context.consume_steps(
258 static_cast<std::size_t>(order + comparison_order + 2), operation);
259 if (!budget) return Result<QuadratureEstimate>::failure(budget.error());
260
262 QuadratureCallback callback{
263 &function, &variable, &context, operation, std::nullopt};
264 double value = 0.0;
265 auto status = lmmc_quad_gauss_legendre(
266 evaluate_quadrature_callback, &callback,
267 interval.value().lower, interval.value().upper,
268 static_cast<std::size_t>(order), &value);
269 if (callback.error) return Result<QuadratureEstimate>::failure(*callback.error);
270 if (status != LMMC_STATUS_OK) {
271 return quadrature_failure<QuadratureEstimate>(status, operation);
272 }
273
274 double comparison_value = 0.0;
275 status = lmmc_quad_gauss_legendre(
276 evaluate_quadrature_callback, &callback,
277 interval.value().lower, interval.value().upper,
278 static_cast<std::size_t>(comparison_order), &comparison_value);
279 if (callback.error) return Result<QuadratureEstimate>::failure(*callback.error);
280 if (status != LMMC_STATUS_OK) {
281 return quadrature_failure<QuadratureEstimate>(status, operation);
282 }
283 return estimated_quadrature_result(
284 value, std::abs(comparison_value - value),
285 interval.value().sign, operation);
286}
287
289 const std::shared_ptr<SymbolicExpr>& function,
290 const std::string& variable,
291 const std::shared_ptr<SymbolicExpr>& lower,
292 const std::shared_ptr<SymbolicExpr>& upper,
293 int order) {
297}
298
300 const std::shared_ptr<SymbolicExpr>& function,
301 const std::string& variable,
302 const std::shared_ptr<SymbolicExpr>& lower,
303 const std::shared_ptr<SymbolicExpr>& upper,
305 double tolerance,
306 int max_depth) {
307 constexpr const char* operation = "adaptive_simpson";
308 auto valid = validate_quadrature_args(
309 function, variable, lower, upper, operation);
310 if (!valid) return Result<QuadratureEstimate>::failure(valid.error());
311 if (!(tolerance > 0.0) || !std::isfinite(tolerance)) {
314 "tolerance must be finite and positive",
315 operation);
316 }
317 if (max_depth < 0) {
320 "maximum recursion depth must be non-negative",
321 operation);
322 }
323 auto interval = evaluate_interval(lower, upper, context, operation);
325 if (interval.value().lower == interval.value().upper) {
326 return Result<QuadratureEstimate>::success(zero_quadrature_result());
327 }
328 auto recursion = context.enter_recursion(operation);
329 if (!recursion) return Result<QuadratureEstimate>::failure(recursion.error());
330 struct RecursionGuard {
332 ~RecursionGuard() { context.leave_recursion(); }
333 } recursion_guard{context};
334 const std::size_t remaining_depth =
335 context.limits().max_recursion_depth > context.recursion_depth()
336 ? context.limits().max_recursion_depth - context.recursion_depth()
337 : 0;
338 const std::size_t effective_depth = std::min(
339 static_cast<std::size_t>(max_depth), remaining_depth);
340
342 QuadratureCallback callback{
343 &function, &variable, &context, operation, std::nullopt};
344 lmmc_quad_result_t backend_result{};
345 const auto status = lmmc_quad_adaptive(
346 evaluate_quadrature_callback, &callback,
347 interval.value().lower, interval.value().upper,
348 tolerance, 0.0, effective_depth, &backend_result);
349 if (callback.error) return Result<QuadratureEstimate>::failure(*callback.error);
350 if (status == LMMC_STATUS_WARNING_MAX_DEPTH) {
353 "adaptive Simpson recursion limit reached before meeting tolerance",
354 operation);
355 }
356 if (status != LMMC_STATUS_OK) {
357 return quadrature_failure<QuadratureEstimate>(status, operation);
358 }
359 QuadratureEstimate result;
360 result.value = interval.value().sign * backend_result.value;
361 result.estimated_error = std::abs(backend_result.error) +
362 std::numeric_limits<double>::epsilon() *
363 std::max(1.0, std::abs(result.value)) * 16.0;
364 if (!std::isfinite(result.value) || !std::isfinite(result.estimated_error)) {
367 "adaptive Simpson result is not finite",
368 operation);
369 }
371}
372
374 const std::shared_ptr<SymbolicExpr>& function,
375 const std::string& variable,
376 const std::shared_ptr<SymbolicExpr>& lower,
377 const std::shared_ptr<SymbolicExpr>& upper,
378 double tolerance,
379 int max_depth) {
382 function, variable, lower, upper, context, tolerance, max_depth);
383}
384
385}
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
Result< QuadratureEstimate > estimated_quadrature_result(double value, double estimated_error, double sign, const std::string &operation)
Result< NormalizedInterval > evaluate_interval(const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper, ComputationContext &context, const std::string &operation)
Result< double > eval_bound_checked(const std::shared_ptr< SymbolicExpr > &expr, ComputationContext &context, const std::string &operation)
Result< double > checked_finite_value(Result< ApproxReal > value, const std::string &operation)
Result< void > validate_quadrature_args(const std::shared_ptr< SymbolicExpr > &function, const std::string &variable, const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper, const std::string &operation)
Result< T > quadrature_failure(lmmc_status_t status, const std::string &operation)
void ensure_lmmc_lifecycle() noexcept
ExprResult comparison_value(const ExprPtr &expression, ComputationContext &context)
const std::string & variable
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
const ExprPtr & lower
const std::vector< FGLMPoly > const MonomialOrder & order
const std::vector< Binding > & bindings
Result< QuadratureEstimate > quadrature_simpson_numeric(const std::shared_ptr< SymbolicExpr > &function, const std::string &variable, const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper, ComputationContext &context, int subdivisions)
Result< QuadratureEstimate > adaptive_simpson_numeric(const std::shared_ptr< SymbolicExpr > &function, const std::string &variable, const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper, ComputationContext &context, double tolerance, int max_depth)
const std::string const ExprPtr & value
Result< QuadratureEstimate > quadrature_gaussian_numeric(const std::shared_ptr< SymbolicExpr > &function, const std::string &variable, const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper, ComputationContext &context, int order)
Result< ApproxReal > evaluate_numeric(const SymbolicExpr &expression, const NumericBindings &bindings={})
ExprResult function(const std::string &name, std::vector< ExprPtr > arguments)
std::unordered_map< std::string, double > NumericBindings
数值积分算法:辛普森法则和高斯求积(符号-数值混合桥接).
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
内部 AST 定义。