12#include "lmmc/config.h"
13#include "lmmc/numeric.h"
30 return !x.empty() && !y.empty() && x != y;
35 const MultiplyNode& product,
const std::string& x,
const std::string& y) {
36 for (
const auto& factor : product.
operands()) {
47 const PowerNode& power,
const std::string& x,
const std::string& y) {
48 auto exponent = std::dynamic_pointer_cast<const NumberNode>(power.
exponent());
54 lmmc_double_nearly_equal_tol(
value, -1.0, 1
e-12, 1
e-12, &equal);
60 return (base_x && !base_y) || (!base_x && base_y);
64 const std::shared_ptr<SymbolicExpr>&
rhs,
79 if (!has_x || !has_y) {
93 const std::shared_ptr<SymbolicExpr>&
rhs,
96 std::shared_ptr<SymbolicExpr>& P,
97 std::shared_ptr<SymbolicExpr>& Q)
101 (
context).limits().max_ast_nodes,
"transform");
115 P = SymbolicExpr::number(0);
148 const std::shared_ptr<SymbolicExpr>&
rhs,
149 const std::string& x,
150 const std::string& y)
154 (
context).limits().max_ast_nodes,
"transform");
164 auto t_val = SymbolicExpr::number(2);
165 auto tx = SymbolicExpr::multiply(t_val, SymbolicExpr::variable(x));
166 auto ty = SymbolicExpr::multiply(t_val, SymbolicExpr::variable(y));
173 auto diff = SymbolicExpr::add(f_scaled,
174 SymbolicExpr::multiply(SymbolicExpr::number(-1),
rhs));
177 if (diff->is_zero()) {
184 std::holds_alternative<ProvedZeroResidual>(verified.value());
190 std::optional<std::int64_t>
value;
191 if (std::holds_alternative<BigInt>(number.
value())) {
192 value = std::get<BigInt>(number.
value()).try_to_int64();
193 }
else if (std::holds_alternative<Rational>(number.
value())) {
200 if (!
value || *
value < std::numeric_limits<int>::min() ||
201 *
value > std::numeric_limits<int>::max()) {
204 return static_cast<int>(*value);
219 const std::shared_ptr<const SymbolicNode>& factor,
const std::string& y,
223 (
context).limits().max_ast_nodes,
"transform");
225 std::dynamic_pointer_cast<const VariableNode>(factor);
227 if (
exponent == std::numeric_limits<int>::max()) {
234 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(factor)) {
236 std::dynamic_pointer_cast<const VariableNode>(power->base());
238 std::dynamic_pointer_cast<const NumberNode>(power->exponent());
239 if (base && !base->is_constant() && base->name() == y && power_value) {
256 const std::shared_ptr<const SymbolicNode>& term,
257 const std::string& y,
262 coefficient = SymbolicExpr::number(1);
263 std::vector<std::shared_ptr<const SymbolicNode>> factors;
264 if (
auto product = std::dynamic_pointer_cast<const MultiplyNode>(term)) {
265 factors = product->operands();
267 factors.push_back(term);
270 for (
const auto& factor : factors) {
279 const std::vector<std::shared_ptr<const SymbolicNode>>& terms,
280 const std::string& y,
284 (
context).limits().max_ast_nodes,
"transform");
285 for (
const auto& term : terms) {
287 std::shared_ptr<SymbolicExpr> coefficient;
292 auto& combined = coefficients[
exponent];
301 const std::shared_ptr<SymbolicExpr>&
rhs,
302 const std::string& x,
303 const std::string& y,
304 std::shared_ptr<SymbolicExpr>& P,
305 std::shared_ptr<SymbolicExpr>& Q,
310 (
context).limits().max_ast_nodes,
"transform");
322 std::vector<std::shared_ptr<const SymbolicNode>> terms;
325 terms = sum->operands();
330 std::map<int, std::shared_ptr<SymbolicExpr>> coefficients;
334 auto linear_coefficient = SymbolicExpr::number(0);
335 std::shared_ptr<SymbolicExpr> nonlinear_coefficient;
336 int nonlinear_exponent = 0;
337 for (
auto& [
exponent, coefficient] : coefficients) {
339 if (coefficient->is_zero()) {
343 linear_coefficient = coefficient;
346 if (
exponent == 0 || nonlinear_coefficient) {
350 nonlinear_coefficient = coefficient;
353 if (!nonlinear_coefficient ||
354 nonlinear_exponent == 0 || nonlinear_exponent == 1) {
359 SymbolicExpr::number(-1), linear_coefficient)), transform_budget));
361 n = nonlinear_exponent;
367 const std::shared_ptr<SymbolicExpr>& M,
368 const std::shared_ptr<SymbolicExpr>& N,
369 const std::string& x,
370 const std::string& y)
374 (
context).limits().max_ast_nodes,
"transform");
385 if (!dM_dy || !dN_dx) {
391 if (dM_dy->compare(dN_dx) == 0) {
396 auto diff = SymbolicExpr::add(dM_dy,
397 SymbolicExpr::multiply(SymbolicExpr::number(-1), dN_dx));
400 if (diff->is_zero()) {
407 std::holds_alternative<ProvedZeroResidual>(verified.value());
413 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
414 const std::string& x)
416 if (coeffs.empty() || x.empty()) {
419 for (
const auto&
c : coeffs) {
432 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
433 const std::string& x,
434 std::vector<double>& euler_consts)
438 (
context).limits().max_ast_nodes,
"transform");
439 euler_consts.clear();
440 if (coeffs.empty() || x.empty()) {
448 const int highest_order =
static_cast<int>(coeffs.size()) - 1;
449 std::vector<double> extracted(coeffs.size(), 0.0);
450 for (
int index = 0; index <= highest_order; ++index) {
451 const int derivative_order = highest_order - index;
455 if (coeffs[index]->is_zero()) {
459 std::shared_ptr<SymbolicExpr> ratio;
460 if (derivative_order == 0) {
463 auto x_power = SymbolicExpr::power(
464 SymbolicExpr::variable(x),
465 SymbolicExpr::number(derivative_order));
467 coeffs[index], x_power)), transform_budget));
479 if (!std::isfinite(
value)) {
482 extracted[index] =
value;
485 euler_consts = std::move(extracted);
491 const std::shared_ptr<SymbolicExpr>&
rhs,
492 const std::string& x,
493 const std::string& y)
497 (
context).limits().max_ast_nodes,
"transform");
513 std::shared_ptr<SymbolicExpr> P, Q;
528 std::shared_ptr<SymbolicExpr> bP, bQ;
544 auto one = SymbolicExpr::number(1);
558 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
559 const std::shared_ptr<SymbolicExpr>& forcing,
560 const std::string& x,
564 result.
order =
static_cast<int>(coeffs.size()) - 1;
566 if (coeffs.empty()) {
573 std::vector<double> numeric_coeffs;
574 numeric_coeffs.reserve(coeffs.size());
575 for (
const auto&
c : coeffs) {
577 if (!std::isfinite(val)) {
581 numeric_coeffs.push_back(val);
593 std::vector<double> euler_consts;
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
const std::variant< BigInt, Rational > & value() const noexcept
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
virtual bool is_zero() const
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)
std::vector< double > const_coeffs
常系数: 系数列表 [a_n, a_{n-1}, ..., a_1, a_0] 和非齐次项 f(x)
bool is_homogeneous_ode(const std::shared_ptr< SymbolicExpr > &rhs, const std::string &x, const std::string &y)
const std::string & variable
std::shared_ptr< SymbolicExpr > bernoulli_P
Bernoulli: P(x), Q(x), n 使得 y' + P(x)y = Q(x)y^n
ODEClassification classify_higher_order_ode(const std::vector< std::shared_ptr< SymbolicExpr > > &coeffs, const std::shared_ptr< SymbolicExpr > &forcing, const std::string &x, const std::string &)
std::shared_ptr< SymbolicExpr > forcing_func
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
@ Euler
Euler (Cauchy-Euler) 方程
@ Bernoulli
Bernoulli 方程 y' + P(x)y = Q(x)y^n
@ Exact
恰当方程 M(x,y)dx + N(x,y)dy = 0
@ Separable
可分离变量型 dy/dx = f(x)g(y)
@ Linear1
一阶线性 dy/dx + P(x)y = Q(x)
@ Homogeneous
齐次方程 y' = f(y/x)
@ HigherOrder_ConstCoeff
高阶常系数线性
@ Linear2_ConstCoeff
二阶常系数线性
double try_eval_double(const std::shared_ptr< SymbolicExpr > &expression)
尝试将表达式求值为 double。
ODEClassification classify_first_order_ode(const std::shared_ptr< SymbolicExpr > &rhs, const std::string &x, const std::string &y)
static bool extract_bernoulli_monomial(const std::shared_ptr< const SymbolicNode > &term, const std::string &y, int &exponent, std::shared_ptr< SymbolicExpr > &coefficient, ComputationContext &context)
ExprResult mul(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const VectorField const VectorField & c
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
static bool separable_product(const MultiplyNode &product, const std::string &x, const std::string &y)
std::shared_ptr< SymbolicExpr > Q_coeff
bool is_linear_first_order(const std::shared_ptr< SymbolicExpr > &rhs, const std::string &x, const std::string &y, std::shared_ptr< SymbolicExpr > &P, std::shared_ptr< SymbolicExpr > &Q)
static bool valid_classifier_variables(const std::string &x, const std::string &y)
static bool separable_reciprocal(const PowerNode &power, const std::string &x, const std::string &y)
static bool accumulate_bernoulli_exponent(int value, int &exponent)
std::shared_ptr< SymbolicExpr > euler_forcing
std::shared_ptr< SymbolicExpr > P_coeff
一阶线性: P(x), Q(x) 使得 y' + P(x)y = Q(x)
bool is_bernoulli_ode(const std::shared_ptr< SymbolicExpr > &rhs, const std::string &x, const std::string &y, std::shared_ptr< SymbolicExpr > &P, std::shared_ptr< SymbolicExpr > &Q, int &n)
std::shared_ptr< SymbolicExpr > exact_M
恰当方程: M(x,y), N(x,y)
bool is_constant_coefficient(const std::vector< std::shared_ptr< SymbolicExpr > > &coeffs, const std::string &x)
ResidualCheckResult check_zero_residual(const ExprPtr &residual, ComputationContext &context, const LMCAS::EqvOptions &options)
const std::string const ExprPtr & value
static std::optional< int > bernoulli_integer_exponent(const NumberNode &number)
std::shared_ptr< SymbolicExpr > bernoulli_Q
static bool collect_bernoulli_coefficients(const std::vector< std::shared_ptr< const SymbolicNode > > &terms, const std::string &y, std::map< int, std::shared_ptr< SymbolicExpr > > &coefficients, ComputationContext &context)
static bool extract_bernoulli_factor(const std::shared_ptr< const SymbolicNode > &factor, const std::string &y, int &exponent, std::shared_ptr< SymbolicExpr > &coefficient, ComputationContext &context)
ExprResult rational(const Rational &value)
std::shared_ptr< SymbolicExpr > exact_N
bool is_exact_ode(const std::shared_ptr< SymbolicExpr > &M, const std::shared_ptr< SymbolicExpr > &N, const std::string &x, const std::string &y)
bool is_euler_equation(const std::vector< std::shared_ptr< SymbolicExpr > > &coeffs, const std::string &x, std::vector< double > &euler_consts)
std::vector< double > euler_coeffs
Euler 方程: 系数列表 [a_n, ..., a_0] 对应 x^n*y^(n) + ... + a_0*y = f(x)
bool is_separable(const std::shared_ptr< SymbolicExpr > &rhs, const std::string &x, const std::string &y)
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
统一 ODE 求解引擎:分类,分派,多种求解方法.