8#include "lmmc/nonlinear.h"
26 const std::shared_ptr<SymbolicExpr>& expression,
30 const char* operation) {
34 "root function expression cannot be null",
40 "root variable cannot be empty",
49 if (!evaluated.value().is_finite() ||
50 !std::isfinite(evaluated.value().value)) {
53 "root function evaluation is not finite",
61 const char* operation) {
62 if (!std::isfinite(
options.tolerance) ||
options.tolerance <= 0.0) {
65 "root tolerance must be finite and positive",
68 if (
options.max_newton_iterations <= 0) {
71 "maximum iteration count must be positive",
78 const std::shared_ptr<SymbolicExpr>*
function =
nullptr;
79 const std::shared_ptr<SymbolicExpr>*
derivative =
nullptr;
91 const std::shared_ptr<SymbolicExpr>& expression,
94 if (callbacks.
error) {
95 return std::numeric_limits<double>::quiet_NaN();
98 (x < callbacks.bracket_lower || x > callbacks.
bracket_upper)) {
100 return std::numeric_limits<double>::quiet_NaN();
107 return std::numeric_limits<double>::quiet_NaN();
109 return value.value();
124 std::size_t max_iterations) {
125 lmmc_nonlinear_config_t config{};
126 if (lmmc_nonlinear_default_config(&config) != LMMC_STATUS_OK) {
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;
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) {
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});
154 const std::shared_ptr<SymbolicExpr>&
function,
160 const char* operation) {
178 const std::size_t max_iterations =
179 static_cast<std::size_t
>(
options.max_newton_iterations) * 3;
181 lmmc_nonlinear_result_t backend{};
182 const auto status = lmmc_bisection_solve(
184 if (callbacks.
error) {
187 if (status == LMMC_STATUS_INVALID_ARGUMENT &&
188 backend.failure_reason == LMMC_NONLINEAR_FAILURE_INVALID_BRACKET) {
195 const std::shared_ptr<SymbolicExpr>&
function,
196 const std::shared_ptr<SymbolicExpr>& derivative,
201 bool enforce_bracket,
202 double bracket_lower,
203 double bracket_upper) {
207 enforce_bracket, bracket_lower, bracket_upper,
false, std::nullopt};
209 options,
static_cast<std::size_t
>(
options.max_newton_iterations));
210 lmmc_nonlinear_result_t backend{};
211 (void)lmmc_newton_solve(
214 if (callbacks.
error) {
222 const std::shared_ptr<SymbolicExpr>& expr,
const std::string& var,
225 return candidate.
error();
227 if (!candidate.
value()) {
230 auto root = *candidate.
value();
233 return std::move(verified.error());
235 root.residual = std::abs(verified.value());
236 if (root.residual <= tolerance) {
237 results.push_back(root);
243 const std::shared_ptr<SymbolicExpr>& expr,
const std::string& var,
247 (
context).limits().max_ast_nodes,
"transform");
248 std::vector<NumericRoot> results;
254 for (
const auto& [
lower,
upper] : isolated.value()) {
256 static_cast<int>(results.size()) >= opts.
max_roots) {
259 auto step =
context.consume_steps(1,
"solve_numeric");
263 const lmmc_real_t lo =
lower.to_double();
264 const lmmc_real_t hi =
upper.to_double();
267 expr, derivative, var, initial, lo, hi,
context, opts);
277}
catch (
const std::bad_alloc&) {
279}
catch (
const std::length_error& error) {
281}
catch (
const std::exception& error) {
286 const std::shared_ptr<SymbolicExpr>& expr,
const std::string& var,
289 (
context).limits().max_ast_nodes,
"transform");
290 std::vector<NumericRoot> results;
302}
catch (
const std::bad_alloc&) {
304}
catch (
const std::length_error& error) {
306}
catch (
const std::exception& error) {
317 polynomial,
context,
"isolate_real_roots");
320Result<std::vector<std::pair<Rational, Rational>>>
327 const std::shared_ptr<SymbolicExpr>&
function,
333 auto valid = invalid_root_options(
options, kBisectionOperation);
341 "bisection function, variable, and ordered finite interval are required",
342 kBisectionOperation);
344 return run_bisection_backend(
346 kBisectionOperation);
350 const std::shared_ptr<SymbolicExpr>&
function,
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)) {
370 const std::shared_ptr<SymbolicExpr>&
function,
371 const std::shared_ptr<SymbolicExpr>& derivative,
374 lmmc_real_t bracket_lower,
375 lmmc_real_t bracket_upper,
378 auto valid = invalid_root_options(
options, kNewtonOperation);
386 "Newton function, derivative, variable, bracket, and initial value are invalid",
389 auto newton = run_newton_backend(
391 bracket_lower, bracket_upper);
392 if (!newton || newton.value()) {
395 return run_bisection_backend(
397 kBisectionOperation);
401 const std::shared_ptr<SymbolicExpr>&
function,
402 const std::shared_ptr<SymbolicExpr>& derivative,
405 lmmc_real_t bracket_lower,
406 lmmc_real_t bracket_upper,
415 const std::shared_ptr<SymbolicExpr>&
function,
416 const std::shared_ptr<SymbolicExpr>& derivative,
421 auto valid = invalid_root_options(
options, kNewtonOperation);
426 !std::isfinite(initial)) {
429 "Newton function, derivative, variable, and finite initial value are required",
432 return run_newton_backend(
438 const std::shared_ptr<SymbolicExpr>&
function,
439 const std::shared_ptr<SymbolicExpr>& derivative,
450 const std::shared_ptr<SymbolicExpr>& expr,
451 const std::string& var,
459 const std::shared_ptr<SymbolicExpr>& expr,
460 const std::string& var,
464 constexpr const char* operation =
"solve_numeric";
465 auto options = invalid_root_options(opts, operation);
471 "expression cannot be null",
476 "solve variable cannot be empty",
481 "maximum root count must be -1 or non-negative",
488 auto initial_step =
context.consume_steps(1, operation);
494 if (!recognized_poly) {
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);
503 return solve_initial_guess(expr, var,
context, opts);
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
std::optional< CasError > error
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)
const std::shared_ptr< SymbolicExpr > * function
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)
constexpr const char * kBisectionOperation
double root_function_callback(double x, void *user_data)
lmmc_nonlinear_config_t root_config(const SolveOptions &options, std::size_t max_iterations)
ComputationContext * context
NumericRootResult backend_root_result(const lmmc_nonlinear_result_t &backend, double residual_limit)
constexpr const char * kNewtonOperation
const std::string * variable
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)
const std::shared_ptr< SymbolicExpr > * derivative
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 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 实根隔离.