LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
newton_raphson.cpp
浏览该文件的文档.
1#include <stdexcept>
3#include "newton_raphson.hpp"
4
8#include "lmmc/nonlinear.h"
10#include "poly_utils.hpp"
11
12#include <algorithm>
13#include <cmath>
14#include <exception>
15#include <limits>
16#include <optional>
17
18namespace LMCAS {
19namespace {
20
21constexpr const char* kBisectionOperation = "bisection";
22constexpr const char* kNewtonOperation = "newton_raphson";
23
24
26 const std::shared_ptr<SymbolicExpr>& expression,
27 const std::string& variable,
28 lmmc_real_t value,
30 const char* operation) {
31 if (!expression) {
34 "root function expression cannot be null",
35 operation);
36 }
37 if (variable.empty()) {
40 "root variable cannot be empty",
41 operation);
42 }
43 auto evaluated = evaluate_numeric(
44 *expression, NumericBindings{{variable, static_cast<double>(value)}},
45 context);
46 if (!evaluated) {
47 return Result<double>::failure(evaluated.error());
48 }
49 if (!evaluated.value().is_finite() ||
50 !std::isfinite(evaluated.value().value)) {
53 "root function evaluation is not finite",
54 operation);
55 }
56 return Result<double>::success(evaluated.value().value);
57}
58
60 const SolveOptions& options,
61 const char* operation) {
62 if (!std::isfinite(options.tolerance) || options.tolerance <= 0.0) {
65 "root tolerance must be finite and positive",
66 operation);
67 }
68 if (options.max_newton_iterations <= 0) {
71 "maximum iteration count must be positive",
72 operation);
73 }
74 return NumericRootResult::success(std::nullopt);
75}
76
78 const std::shared_ptr<SymbolicExpr>* function = nullptr;
79 const std::shared_ptr<SymbolicExpr>* derivative = nullptr;
80 const std::string* variable = nullptr;
82 const char* operation = nullptr;
83 bool enforce_bracket = false;
84 double bracket_lower = 0.0;
85 double bracket_upper = 0.0;
86 bool left_bracket = false;
87 std::optional<CasError> error;
88};
89
91 const std::shared_ptr<SymbolicExpr>& expression,
92 double x,
93 RootCallbacks& callbacks) {
94 if (callbacks.error) {
95 return std::numeric_limits<double>::quiet_NaN();
96 }
97 if (callbacks.enforce_bracket &&
98 (x < callbacks.bracket_lower || x > callbacks.bracket_upper)) {
99 callbacks.left_bracket = true;
100 return std::numeric_limits<double>::quiet_NaN();
101 }
103 expression, *callbacks.variable, x, *callbacks.context,
104 callbacks.operation);
105 if (!value) {
106 callbacks.error = value.error();
107 return std::numeric_limits<double>::quiet_NaN();
108 }
109 return value.value();
110}
111
112double root_function_callback(double x, void* user_data) {
113 auto& callbacks = *static_cast<RootCallbacks*>(user_data);
114 return evaluate_root_callback(*callbacks.function, x, callbacks);
115}
116
117double root_derivative_callback(double x, void* user_data) {
118 auto& callbacks = *static_cast<RootCallbacks*>(user_data);
119 return evaluate_root_callback(*callbacks.derivative, x, callbacks);
120}
121
122lmmc_nonlinear_config_t root_config(
123 const SolveOptions& options,
124 std::size_t max_iterations) {
125 lmmc_nonlinear_config_t config{};
126 if (lmmc_nonlinear_default_config(&config) != LMMC_STATUS_OK) {
127 std::terminate();
128 }
129 config.abs_tol = options.tolerance;
130 config.rel_tol = 0.0;
131 config.max_iter = max_iterations;
132 config.min_derivative = 1.0e-15;
133 config.min_step = 0.0;
134 return config;
135}
136
138 const lmmc_nonlinear_result_t& backend,
139 double residual_limit) {
140 if (!backend.converged || !std::isfinite(backend.root) ||
141 !std::isfinite(backend.residual_norm) ||
142 backend.residual_norm > residual_limit) {
143 return NumericRootResult::success(std::nullopt);
144 }
145 const int iterations =
146 backend.num_iter > static_cast<std::size_t>(std::numeric_limits<int>::max())
147 ? std::numeric_limits<int>::max()
148 : static_cast<int>(backend.num_iter);
150 NumericRoot{backend.root, backend.residual_norm, iterations});
151}
152
154 const std::shared_ptr<SymbolicExpr>& function,
155 const std::string& variable,
156 double lower,
157 double upper,
159 const SolveOptions& options,
160 const char* operation) {
161 if (lower == upper) {
163 function, variable, lower, context, operation);
164 if (!value) {
165 return NumericRootResult::failure(value.error());
166 }
167 if (std::abs(value.value()) <= options.tolerance) {
169 NumericRoot{lower, std::abs(value.value()), 0});
170 }
171 return NumericRootResult::success(std::nullopt);
172 }
173
175 RootCallbacks callbacks{
176 &function, nullptr, &variable, &context, operation,
177 false, lower, upper, false, std::nullopt};
178 const std::size_t max_iterations =
179 static_cast<std::size_t>(options.max_newton_iterations) * 3;
180 auto config = root_config(options, max_iterations);
181 lmmc_nonlinear_result_t backend{};
182 const auto status = lmmc_bisection_solve(
183 root_function_callback, &callbacks, lower, upper, &config, &backend);
184 if (callbacks.error) {
185 return NumericRootResult::failure(*callbacks.error);
186 }
187 if (status == LMMC_STATUS_INVALID_ARGUMENT &&
188 backend.failure_reason == LMMC_NONLINEAR_FAILURE_INVALID_BRACKET) {
189 return NumericRootResult::success(std::nullopt);
190 }
191 return backend_root_result(backend, options.tolerance * 100.0);
192}
193
195 const std::shared_ptr<SymbolicExpr>& function,
196 const std::shared_ptr<SymbolicExpr>& derivative,
197 const std::string& variable,
198 double initial,
200 const SolveOptions& options,
201 bool enforce_bracket,
202 double bracket_lower,
203 double bracket_upper) {
205 RootCallbacks callbacks{
206 &function, &derivative, &variable, &context, kNewtonOperation,
207 enforce_bracket, bracket_lower, bracket_upper, false, std::nullopt};
208 auto config = root_config(
209 options, static_cast<std::size_t>(options.max_newton_iterations));
210 lmmc_nonlinear_result_t backend{};
211 (void)lmmc_newton_solve(
213 &config, &backend);
214 if (callbacks.error) {
215 return NumericRootResult::failure(*callbacks.error);
216 }
217 return backend_root_result(backend, options.tolerance);
218}
219
220std::optional<CasError> append_verified_root(
221 std::vector<NumericRoot>& results, const NumericRootResult& candidate,
222 const std::shared_ptr<SymbolicExpr>& expr, const std::string& var,
223 ComputationContext& context, lmmc_real_t tolerance) {
224 if (!candidate) {
225 return candidate.error();
226 }
227 if (!candidate.value()) {
228 return std::nullopt;
229 }
230 auto root = *candidate.value();
231 auto verified = evaluate_root_function(expr, var, root.value, context, "solve_numeric");
232 if (!verified) {
233 return std::move(verified.error());
234 }
235 root.residual = std::abs(verified.value());
236 if (root.residual <= tolerance) {
237 results.push_back(root);
238 }
239 return std::nullopt;
240}
241
243 const std::shared_ptr<SymbolicExpr>& expr, const std::string& var,
245 const SolveOptions& opts) try {
246 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
247 (context).limits().max_ast_nodes, "transform");
248 std::vector<NumericRoot> results;
249 auto derivative = detail::make_expression_ptr(detail::differentiate_node(detail::node(expr), var, transform_budget));
250 auto isolated = isolate_real_roots_checked(polynomial, context);
251 if (!isolated) {
252 return NumericRootsResult::failure(isolated.error());
253 }
254 for (const auto& [lower, upper] : isolated.value()) {
255 if (opts.max_roots > 0 &&
256 static_cast<int>(results.size()) >= opts.max_roots) {
257 break;
258 }
259 auto step = context.consume_steps(1, "solve_numeric");
260 if (!step) {
261 return NumericRootsResult::failure(step.error());
262 }
263 const lmmc_real_t lo = lower.to_double();
264 const lmmc_real_t hi = upper.to_double();
265 const lmmc_real_t initial = solver_detail::finite_midpoint(lo, hi);
266 auto candidate = newton_raphson_checked(
267 expr, derivative, var, initial, lo, hi, context, opts);
268 auto failure = append_verified_root(
269 results, candidate, expr, var, context, opts.tolerance * 100.0);
270 if (failure) {
271 return NumericRootsResult::failure(std::move(*failure));
272 }
273 }
274 return NumericRootsResult::success(std::move(results));
275} catch (const CasError& error) {
276 return NumericRootsResult::failure(error);
277} catch (const std::bad_alloc&) {
278 return NumericRootsResult::failure(CasErrc::ResourceLimit, "allocation failed", "solve_isolated_polynomial");
279} catch (const std::length_error& error) {
280 return NumericRootsResult::failure(CasErrc::ResourceLimit, error.what(), "solve_isolated_polynomial");
281} catch (const std::exception& error) {
282 return NumericRootsResult::failure(CasErrc::InternalInvariant, error.what(), "solve_isolated_polynomial");
283}
284
286 const std::shared_ptr<SymbolicExpr>& expr, const std::string& var,
287 ComputationContext& context, const SolveOptions& opts) try {
288 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
289 (context).limits().max_ast_nodes, "transform");
290 std::vector<NumericRoot> results;
291 const lmmc_real_t initial = opts.has_initial_guess ? opts.initial_guess : 0.0;
292 auto derivative = detail::make_expression_ptr(detail::differentiate_node(detail::node(expr), var, transform_budget));
293 auto candidate = newton_raphson_checked(expr, derivative, var, initial, context, opts);
294 auto failure = append_verified_root(
295 results, candidate, expr, var, context, opts.tolerance);
296 if (failure) {
297 return NumericRootsResult::failure(std::move(*failure));
298 }
299 return NumericRootsResult::success(std::move(results));
300} catch (const CasError& error) {
301 return NumericRootsResult::failure(error);
302} catch (const std::bad_alloc&) {
303 return NumericRootsResult::failure(CasErrc::ResourceLimit, "allocation failed", "solve_initial_guess");
304} catch (const std::length_error& error) {
305 return NumericRootsResult::failure(CasErrc::ResourceLimit, error.what(), "solve_initial_guess");
306} catch (const std::exception& error) {
307 return NumericRootsResult::failure(CasErrc::InternalInvariant, error.what(), "solve_initial_guess");
308}
309
310} // namespace
311
314 const Polynomial<Rational>& polynomial,
317 polynomial, context, "isolate_real_roots");
318}
319
320Result<std::vector<std::pair<Rational, Rational>>>
325
327 const std::shared_ptr<SymbolicExpr>& function,
328 const std::string& variable,
329 lmmc_real_t lower,
330 lmmc_real_t upper,
332 const SolveOptions& options) {
333 auto valid = invalid_root_options(options, kBisectionOperation);
334 if (!valid) {
335 return valid;
336 }
337 if (!function || variable.empty() || !std::isfinite(lower) ||
338 !std::isfinite(upper) || lower > upper) {
341 "bisection function, variable, and ordered finite interval are required",
342 kBisectionOperation);
343 }
344 return run_bisection_backend(
346 kBisectionOperation);
347}
348
350 const std::shared_ptr<SymbolicExpr>& function,
351 const std::string& variable,
352 lmmc_real_t lower,
353 lmmc_real_t upper,
354 const SolveOptions& options) {
356 return bisection_checked(
358}
359
361 lmmc_real_t initial, lmmc_real_t lower, lmmc_real_t upper) {
362 if (!std::isfinite(initial) || !std::isfinite(lower) ||
363 !std::isfinite(upper)) {
364 return false;
365 }
366 return lower <= upper && initial >= lower && initial <= upper;
367}
368
370 const std::shared_ptr<SymbolicExpr>& function,
371 const std::shared_ptr<SymbolicExpr>& derivative,
372 const std::string& variable,
373 lmmc_real_t initial,
374 lmmc_real_t bracket_lower,
375 lmmc_real_t bracket_upper,
377 const SolveOptions& options) {
378 auto valid = invalid_root_options(options, kNewtonOperation);
379 if (!valid) {
380 return valid;
381 }
382 if (!function || !derivative || variable.empty() ||
383 !valid_newton_bracket(initial, bracket_lower, bracket_upper)) {
386 "Newton function, derivative, variable, bracket, and initial value are invalid",
387 kNewtonOperation);
388 }
389 auto newton = run_newton_backend(
390 function, derivative, variable, initial, context, options, true,
391 bracket_lower, bracket_upper);
392 if (!newton || newton.value()) {
393 return newton;
394 }
395 return run_bisection_backend(
396 function, variable, bracket_lower, bracket_upper, context, options,
397 kBisectionOperation);
398}
399
401 const std::shared_ptr<SymbolicExpr>& function,
402 const std::shared_ptr<SymbolicExpr>& derivative,
403 const std::string& variable,
404 lmmc_real_t initial,
405 lmmc_real_t bracket_lower,
406 lmmc_real_t bracket_upper,
407 const SolveOptions& options) {
410 function, derivative, variable, initial, bracket_lower, bracket_upper,
412}
413
415 const std::shared_ptr<SymbolicExpr>& function,
416 const std::shared_ptr<SymbolicExpr>& derivative,
417 const std::string& variable,
418 lmmc_real_t initial,
420 const SolveOptions& options) {
421 auto valid = invalid_root_options(options, kNewtonOperation);
422 if (!valid) {
423 return valid;
424 }
425 if (!function || !derivative || variable.empty() ||
426 !std::isfinite(initial)) {
429 "Newton function, derivative, variable, and finite initial value are required",
430 kNewtonOperation);
431 }
432 return run_newton_backend(
433 function, derivative, variable, initial, context, options, false, 0.0,
434 0.0);
435}
436
438 const std::shared_ptr<SymbolicExpr>& function,
439 const std::shared_ptr<SymbolicExpr>& derivative,
440 const std::string& variable,
441 lmmc_real_t initial,
442 const SolveOptions& options) {
445 function, derivative, variable, initial, context, options);
446}
447
448
450 const std::shared_ptr<SymbolicExpr>& expr,
451 const std::string& var,
452 const SolveOptions& opts)
453{
455 return solve_numeric_checked(expr, var, context, opts);
456}
457
459 const std::shared_ptr<SymbolicExpr>& expr,
460 const std::string& var,
462 const SolveOptions& opts)
463{
464 constexpr const char* operation = "solve_numeric";
465 auto options = invalid_root_options(opts, operation);
466 if (!options) {
467 return NumericRootsResult::failure(options.error());
468 }
469 if (!expr) {
471 "expression cannot be null",
472 operation);
473 }
474 if (var.empty()) {
476 "solve variable cannot be empty",
477 operation);
478 }
479 if (opts.max_roots < -1) {
481 "maximum root count must be -1 or non-negative",
482 operation);
483 }
484 if (opts.max_roots == 0) {
486 }
487
488 auto initial_step = context.consume_steps(1, operation);
489 if (!initial_step) {
490 return NumericRootsResult::failure(initial_step.error());
491 }
492
493 auto recognized_poly = recognize_rational_polynomial(*expr, var, context);
494 if (!recognized_poly) {
495 return NumericRootsResult::failure(recognized_poly.error());
496 }
497
498 if (recognized_poly.value() && !recognized_poly.value()->is_zero() &&
499 recognized_poly.value()->degree() >= 1) {
500 return solve_isolated_polynomial(
501 expr, var, *recognized_poly.value(), context, opts);
502 }
503 return solve_initial_guess(expr, var, context, opts);
504}
505
506}
一元多项式模板类
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
NumericRootResult run_bisection_backend(const std::shared_ptr< SymbolicExpr > &function, const std::string &variable, double lower, double upper, ComputationContext &context, const SolveOptions &options, const char *operation)
NumericRootsResult solve_initial_guess(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context, const SolveOptions &opts)
double root_derivative_callback(double x, void *user_data)
NumericRootsResult solve_isolated_polynomial(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const Polynomial< Rational > &polynomial, ComputationContext &context, const SolveOptions &opts)
std::optional< CasError > append_verified_root(std::vector< NumericRoot > &results, const NumericRootResult &candidate, const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context, lmmc_real_t tolerance)
Result< double > evaluate_root_function(const std::shared_ptr< SymbolicExpr > &expression, const std::string &variable, lmmc_real_t value, ComputationContext &context, const char *operation)
double root_function_callback(double x, void *user_data)
lmmc_nonlinear_config_t root_config(const SolveOptions &options, std::size_t max_iterations)
NumericRootResult backend_root_result(const lmmc_nonlinear_result_t &backend, double residual_limit)
NumericRootResult run_newton_backend(const std::shared_ptr< SymbolicExpr > &function, const std::shared_ptr< SymbolicExpr > &derivative, const std::string &variable, double initial, ComputationContext &context, const SolveOptions &options, bool enforce_bracket, double bracket_lower, double bracket_upper)
NumericRootResult invalid_root_options(const SolveOptions &options, const char *operation)
double evaluate_root_callback(const std::shared_ptr< SymbolicExpr > &expression, double x, RootCallbacks &callbacks)
SymbolicNodePtr differentiate_node(const SymbolicNodePtr &node, const std::string &variable, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
void ensure_lmmc_lifecycle() noexcept
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< std::vector< RationalInterval > > isolate_real_roots_exact(const Polynomial< Rational > &polynomial, ComputationContext &context, const std::string &operation)
lmmc_real_t finite_midpoint(lmmc_real_t lower, lmmc_real_t upper)
const std::string & variable
bool has_initial_guess
是否指定了初始猜测值
NumericRootResult newton_raphson_checked(const std::shared_ptr< SymbolicExpr > &function, const std::shared_ptr< SymbolicExpr > &derivative, const std::string &variable, lmmc_real_t initial, lmmc_real_t bracket_lower, lmmc_real_t bracket_upper, ComputationContext &context, const SolveOptions &options)
__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
const ExprPtr & lower
const std::string ComputationContext const SolveOptions & options
lmmc_real_t tolerance
收敛容差
Result< std::vector< std::pair< Rational, Rational > > > isolate_real_roots_checked(const Polynomial< Rational > &polynomial, ComputationContext &context)
static bool valid_newton_bracket(lmmc_real_t initial, lmmc_real_t lower, lmmc_real_t upper)
lmmc_real_t initial_guess
数值迭代初始猜测值
Result< std::vector< NumericRoot > > NumericRootsResult
Result< std::optional< NumericRoot > > NumericRootResult
Result< OptionalRationalPolynomial > recognize_rational_polynomial(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
NumericRootResult bisection_checked(const std::shared_ptr< SymbolicExpr > &function, const std::string &variable, lmmc_real_t lower, lmmc_real_t upper, ComputationContext &context, const SolveOptions &options)
const std::string const ExprPtr & value
int max_roots
数值候选数量预算;精确解集和整数解族保持完整。
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
NumericRootsResult solve_numeric_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SolveOptions &opts)
方程求解选项,控制数值求解行为和结果形式.
数值根结果,包含根值,残差和迭代次数.
数值求根:Newton-Raphson 迭代,二分法,Sturm 实根隔离.
多项式转换与精确符号多项式代数。