13#include "lmmc/config.h"
14#include "lmmc/numeric.h"
26 const std::shared_ptr<SymbolicExpr>&,
27 const std::shared_ptr<SymbolicExpr>&,
28 const std::shared_ptr<SymbolicExpr>&,
33 "Frobenius series coefficients are outside the checked numeric support domain",
39 "Frobenius coefficient recurrence produced a non-finite value",
44 std::vector<double>
p;
45 std::vector<double>
q;
49 const std::shared_ptr<SymbolicExpr>& p,
50 const std::shared_ptr<SymbolicExpr>& q,
51 const std::shared_ptr<SymbolicExpr>& x0,
55 (
context).limits().max_ast_nodes,
"transform");
56 std::vector<double> p_coeffs(
order + 2, 0.0);
57 std::vector<double> q_coeffs(
order + 2, 0.0);
61 double factorial = 1.0;
62 for (
int k = 0; k <=
order + 1; ++k) {
70 if (!std::isfinite(pv) || !std::isfinite(qv)) {
73 p_coeffs[k] = pv / factorial;
74 q_coeffs[k] = qv / factorial;
78 p_current = SymbolicExpr::number(0);
81 q_current = SymbolicExpr::number(0);
86}
catch (
const CasError& error) {
88}
catch (
const std::bad_alloc&) {
90}
catch (
const std::length_error& error) {
92}
catch (
const std::exception& error) {
98 const auto& p_coeffs = coefficients.
p;
99 const auto& q_coeffs = coefficients.
q;
100 std::vector<double>
a(
order + 1, 0.0);
102 for (
int n_idx = 0; n_idx + 2 <=
order; ++n_idx) {
104 for (
int k = 0; k <= n_idx; ++k) {
105 sum += (k + 1) *
a[k + 1] * p_coeffs[n_idx - k];
106 sum +=
a[k] * q_coeffs[n_idx - k];
108 double denom =
static_cast<double>((n_idx + 2) * (n_idx + 1));
109 a[n_idx + 2] = -sum / denom;
110 if (!std::isfinite(
a[n_idx + 2])) {
119 const std::vector<double>&
a,
120 const std::shared_ptr<SymbolicExpr>& x_minus_x0) {
121 const int order =
static_cast<int>(
a.size()) - 1;
122 auto series_sol = SymbolicExpr::number(0);
123 for (
int k = 0; k <=
order; ++k) {
124 if (std::abs(
a[k]) < 1
e-15) {
127 auto coeff = SymbolicExpr::number(
a[k]);
128 auto power_term = (k == 0) ? SymbolicExpr::number(1)
129 : SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(k));
130 series_sol = SymbolicExpr::add(series_sol,
131 SymbolicExpr::multiply(coeff, power_term));
138 double P0,
double Q0) {
139 double ind_b = P0 - 1.0;
141 double ind_D = ind_b * ind_b - 4.0 * ind_c;
142 if (!std::isfinite(ind_D)) {
148 lmmc_double_nearly_equal_tol(ind_D, 0.0, 1
e-12, 1
e-12, &
eq);
149 if (!
eq && ind_D > 0) {
150 r1 = (-ind_b + std::sqrt(ind_D)) / 2.0;
151 r2 = (-ind_b - std::sqrt(ind_D)) / 2.0;
163 return std::make_pair(r1, r2);
167 const std::shared_ptr<SymbolicExpr>& coefficient,
168 const std::string& x,
const std::shared_ptr<SymbolicExpr>& x0,
170 if (std::isfinite(
value)) {
178 const auto& lim = limited.value();
180 : std::numeric_limits<double>::quiet_NaN();
184 const std::shared_ptr<SymbolicExpr>& xp_expr,
185 const std::shared_ptr<SymbolicExpr>& x2q_expr,
186 const std::shared_ptr<SymbolicExpr>& x0,
187 const std::string& x,
int order,
double P0,
double Q0,
190 (
context).limits().max_ast_nodes,
"transform");
191 std::vector<double> pn_coeffs(
order + 1, 0.0);
192 std::vector<double> qn_coeffs(
order + 1, 0.0);
194 auto xp_current = xp_expr;
195 auto x2q_current = x2q_expr;
197 for (
int k = 0; k <=
order; ++k) {
215 pv = p_limit.value();
220 qv = q_limit.value();
222 if (!std::isfinite(pv) || !std::isfinite(qv)) {
225 pn_coeffs[k] = pv / fact;
226 qn_coeffs[k] = qv / fact;
231 xp_current = SymbolicExpr::number(0);
234 x2q_current = SymbolicExpr::number(0);
239}
catch (
const CasError& error) {
241}
catch (
const std::bad_alloc&) {
243}
catch (
const std::length_error& error) {
245}
catch (
const std::exception& error) {
251 double P0,
double Q0,
double r1,
int order) {
252 const auto& pn_coeffs = coefficients.
p;
253 const auto& qn_coeffs = coefficients.
q;
258 auto indicial_poly = [&](
double s) ->
double {
259 return s * (s - 1.0) + P0 * s + Q0;
262 std::vector<double>
a(
order + 1, 0.0);
265 for (
int n_idx = 1; n_idx <=
order; ++n_idx) {
266 double F_val = indicial_poly(r1 + n_idx);
267 if (!std::isfinite(F_val)) {
270 if (std::abs(F_val) < 1
e-15) {
275 for (
int k = 0; k < n_idx; ++k) {
280 double p_term = (idx < static_cast<int>(pn_coeffs.size())) ? pn_coeffs[idx] : 0.0;
281 double q_term = (idx < static_cast<int>(qn_coeffs.size())) ? qn_coeffs[idx] : 0.0;
282 sum += ((r1 + k) * p_term + q_term) *
a[k];
284 a[n_idx] = -sum / F_val;
285 if (!std::isfinite(
a[n_idx])) {
294 const std::shared_ptr<SymbolicExpr>& p,
295 const std::shared_ptr<SymbolicExpr>& q,
296 const std::shared_ptr<SymbolicExpr>& x0,
297 const std::string& x,
const std::shared_ptr<SymbolicExpr>& x_minus_x0,
301 (
context).limits().max_ast_nodes,
"transform");
311 recurrence.value(), x_minus_x0)), transform_budget));
314}
catch (
const CasError& error) {
316}
catch (
const std::bad_alloc&) {
318}
catch (
const std::length_error& error) {
320}
catch (
const std::exception& error) {
325 const std::shared_ptr<SymbolicExpr>& p,
326 const std::shared_ptr<SymbolicExpr>& q,
327 const std::shared_ptr<SymbolicExpr>& x0,
328 const std::string& x,
const std::shared_ptr<SymbolicExpr>& x_minus_x0,
331 (
context).limits().max_ast_nodes,
"transform");
334 SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(2)), q)), transform_budget));
346 auto P0_expr = std::move(P0_result.value());
347 auto Q0_expr = std::move(Q0_result.value());
351 : std::numeric_limits<double>::quiet_NaN();
354 : std::numeric_limits<double>::quiet_NaN();
355 if (!std::isfinite(P0) || !std::isfinite(Q0)) {
381 auto power_prefix = SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(r1));
385}
catch (
const CasError& error) {
387}
catch (
const std::bad_alloc&) {
389}
catch (
const std::length_error& error) {
391}
catch (
const std::exception& error) {
396 const std::shared_ptr<SymbolicExpr>& p,
397 const std::shared_ptr<SymbolicExpr>& q,
398 const std::shared_ptr<SymbolicExpr>& x0,
399 const std::string& x,
403 (
context).limits().max_ast_nodes,
"transform");
404 const std::string operation =
"classify_singular_point";
414 if (std::isfinite(p_val) && std::isfinite(q_val)) {
418 if ((!std::isfinite(p_val) &&
420 (!std::isfinite(q_val) &&
424 "singularity classification requires numeric coefficient values",
428 auto x_var = SymbolicExpr::variable(x);
429 auto x_minus_x0 = SymbolicExpr::add(
430 x_var, SymbolicExpr::multiply(SymbolicExpr::number(-1), x0));
433 SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(2)),
434 q)), transform_budget));
440 std::holds_alternative<FiniteLimit>(xp_limit.value().value) &&
441 std::holds_alternative<FiniteLimit>(x2q_limit.value().value)
444 }
catch (
const std::bad_alloc&) {
447 "allocation failed while classifying ODE singularity",
453}
catch (
const CasError& error) {
455}
catch (
const std::bad_alloc&) {
457}
catch (
const std::length_error& error) {
459}
catch (
const std::exception& error) {
464 const std::shared_ptr<SymbolicExpr>& p,
465 const std::shared_ptr<SymbolicExpr>& q,
466 const std::shared_ptr<SymbolicExpr>& x0,
467 const std::string& x)
475 const std::shared_ptr<SymbolicExpr>& p,
476 const std::shared_ptr<SymbolicExpr>& q,
477 const std::shared_ptr<SymbolicExpr>& x0,
478 const std::string& x,
480 const std::string& operation)
483 (
context).limits().max_ast_nodes,
"transform");
484 auto x_var = SymbolicExpr::variable(x);
485 auto x_minus_x0 = SymbolicExpr::add(x_var,
486 SymbolicExpr::multiply(SymbolicExpr::number(-1), x0));
489 SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(2)), q)), transform_budget));
501 const auto& P0_expr = P0_result.value();
502 const auto& Q0_expr = Q0_result.value();
505 : std::numeric_limits<double>::quiet_NaN();
508 : std::numeric_limits<double>::quiet_NaN();
509 if (!std::isfinite(P0) || !std::isfinite(Q0)) {
512 "Frobenius regular-singular coefficients are outside the checked numeric support domain",
516 double discriminant = (P0 - 1.0) * (P0 - 1.0) - 4.0 * Q0;
517 int zero_discriminant;
518 lmmc_double_nearly_equal_tol(discriminant, 0.0, 1
e-12, 1
e-12,
520 if (discriminant < 0.0 && !zero_discriminant) {
523 "Frobenius checked API currently supports real indicial roots only",
528}
catch (
const CasError& error) {
530}
catch (
const std::bad_alloc&) {
532}
catch (
const std::length_error& error) {
534}
catch (
const std::exception& error) {
539 const std::shared_ptr<SymbolicExpr>& p,
540 const std::shared_ptr<SymbolicExpr>& q,
541 const std::shared_ptr<SymbolicExpr>& x0,
542 const std::string& x,
546 const std::string operation =
"solve_frobenius";
554 "Frobenius truncation order must be between 0 and 64",
557 auto budget =
context.consume_steps(
static_cast<std::size_t
>(
order + 1) * 24 + 24,
569 const auto point_type = classified.value();
573 "Frobenius checked API does not support irregular singular points",
578 p, q, x0, x,
context, operation);
579 if (!regular_domain) {
590 auto solution = std::move(solved.value());
591 if (!solution.series_solution ||
595 "Frobenius solver produced no series in the supported domain",
598 if (solution.point_type != point_type) {
601 "Frobenius solver reported an unexpected singularity type",
605 }
catch (
const std::bad_alloc&) {
608 "allocation failed while solving Frobenius series",
610 }
catch (
const std::exception& ex) {
619 const std::shared_ptr<SymbolicExpr>& p,
620 const std::shared_ptr<SymbolicExpr>& q,
621 const std::shared_ptr<SymbolicExpr>& x0,
622 const std::string& x,
630 const std::shared_ptr<SymbolicExpr>& p,
631 const std::shared_ptr<SymbolicExpr>& q,
632 const std::shared_ptr<SymbolicExpr>& x0,
633 const std::string& x,
641 auto x_var = SymbolicExpr::variable(x);
642 auto x_minus_x0 = SymbolicExpr::add(x_var,
643 SymbolicExpr::multiply(SymbolicExpr::number(-1), x0));
654 p, q, x0, x, x_minus_x0, std::move(result),
context);
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(ODESingularityType value)
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)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
SymbolicNodePtr substitute_node(const SymbolicNodePtr &node, const std::string &variable, const SymbolicNodePtr &replacement, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
static Result< std::vector< double > > singular_series_recurrence(const OdeTaylorCoefficients &coefficients, double P0, double Q0, double r1, int order)
ExprResult eq(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
LimitResult limit_checked(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, Domain domain)
static Result< double > frobenius_coefficient_limit(const std::shared_ptr< SymbolicExpr > &coefficient, const std::string &x, const std::shared_ptr< SymbolicExpr > &x0, double value, ComputationContext &context)
Result< ODESingularityType > ODESingularityResult
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
double try_eval_double(const std::shared_ptr< SymbolicExpr > &expression)
尝试将表达式求值为 double。
const std::vector< FGLMPoly > const MonomialOrder & order
FrobeniusSolutionResult solve_frobenius_checked(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, int order, ComputationContext &context)
static Result< std::pair< double, double > > frobenius_indicial_roots(double P0, double Q0)
LimitExpressionResult limit_expression_checked(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, Domain domain)
static std::shared_ptr< SymbolicExpr > assemble_frobenius_polynomial(const std::vector< double > &a, const std::shared_ptr< SymbolicExpr > &x_minus_x0)
static Result< OdeTaylorCoefficients > singular_taylor_coefficients(const std::shared_ptr< SymbolicExpr > &xp_expr, const std::shared_ptr< SymbolicExpr > &x2q_expr, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, int order, double P0, double Q0, ComputationContext &context)
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
Result< FrobeniusSolution > FrobeniusSolutionResult
static FrobeniusSolutionResult solve_frobenius_impl(const std::shared_ptr< SymbolicExpr > &, const std::shared_ptr< SymbolicExpr > &, const std::shared_ptr< SymbolicExpr > &, const std::string &, int, ODESingularityType, ComputationContext &)
std::set< std::string > free_variables(const detail::SymbolicNodePtr &expression, detail::RewriteBudget *budget)
ODESingularityResult classify_singular_point_checked(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, ComputationContext &context)
static Result< OdeTaylorCoefficients > ordinary_taylor_coefficients(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, int order, ComputationContext &context)
ODESingularityType
对一阶 ODE 进行类型分类.
std::vector< double > indicial_roots
指标方程的根(正则奇点时)
static FrobeniusSolutionResult ordinary_frobenius_solution(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, const std::shared_ptr< SymbolicExpr > &x_minus_x0, FrobeniusSolution result, ComputationContext &context)
std::shared_ptr< SymbolicExpr > series_solution
截断级数解
ExprSetResult roots(const ExprPtr &expression, const std::string &variable, ComputationContext &context, const SolveOptions &options)
ODESingularityType point_type
展开点的奇点类型
const std::string const ExprPtr & value
static FrobeniusSolutionResult singular_frobenius_solution(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, const std::shared_ptr< SymbolicExpr > &x_minus_x0, FrobeniusSolution result, ComputationContext &context)
static Result< void > validate_frobenius_regular_singular_domain(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, ComputationContext &context, const std::string &operation)
static CasError frobenius_arithmetic_failure()
Result< void > validate_ode_two_expr_point(const std::shared_ptr< SymbolicExpr > &p, const std::shared_ptr< SymbolicExpr > &q, const std::shared_ptr< SymbolicExpr > &x0, const std::string &x, ComputationContext &context, const std::string &operation)
static Result< std::vector< double > > ordinary_series_recurrence(const OdeTaylorCoefficients &coefficients, int order)
static CasError frobenius_unsupported_numeric()
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
统一 ODE 求解引擎:分类,分派,多种求解方法.