LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
ode_classification.cpp
浏览该文件的文档.
2/**
3 * @file ode_classification.cpp
4 * @brief ODE 方程类型检测与分类。
5 */
8#include "symbolic.hpp"
9#include "poly_utils.hpp"
12#include "lmmc/config.h"
13#include "lmmc/numeric.h"
15#include <cmath>
16#include <cstdint>
17#include <memory>
18#include <string>
19#include <limits>
20#include <map>
21#include <optional>
22#include <vector>
23
24namespace LMCAS {
25
27 const std::string& x,
28 const std::string& y)
29{
30 return !x.empty() && !y.empty() && x != y;
31}
32
33
35 const MultiplyNode& product, const std::string& x, const std::string& y) {
36 for (const auto& factor : product.operands()) {
37 bool fx = expression_depends_on_variable(factor, x);
38 bool fy = expression_depends_on_variable(factor, y);
39 if (fx && fy) {
40 return false;
41 }
42 }
43 return true;
44}
45
47 const PowerNode& power, const std::string& x, const std::string& y) {
48 auto exponent = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
49 if (!exponent) {
50 return false;
51 }
53 int equal;
54 lmmc_double_nearly_equal_tol(value, -1.0, 1e-12, 1e-12, &equal);
55 if (!equal) {
56 return false;
57 }
58 bool base_x = expression_depends_on_variable(power.base(), x);
59 bool base_y = expression_depends_on_variable(power.base(), y);
60 return (base_x && !base_y) || (!base_x && base_y);
61}
62
64 const std::shared_ptr<SymbolicExpr>& rhs,
65 const std::string& x,
66 const std::string& y)
67{
68 if (!rhs || !LMCAS::detail::node(rhs)) {
69 return false;
70 }
71 if (!valid_classifier_variables(x, y)) {
72 return false;
73 }
74
77
78 /// 若只依赖一个变量或都不依赖,则可分离
79 if (!has_x || !has_y) {
80 return true;
81 }
82
83 /// 检查乘法形式 f(x)*g(y)
84 auto mul = std::dynamic_pointer_cast<const MultiplyNode>(LMCAS::detail::node(rhs));
85 if (mul && separable_product(*mul, x, y)) {
86 return true;
87 }
88 auto power = std::dynamic_pointer_cast<const PowerNode>(LMCAS::detail::node(rhs));
89 return power && separable_reciprocal(*power, x, y);
90}
91
93 const std::shared_ptr<SymbolicExpr>& rhs,
94 const std::string& x,
95 const std::string& y,
96 std::shared_ptr<SymbolicExpr>& P,
97 std::shared_ptr<SymbolicExpr>& Q)
98{
100 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
101 (context).limits().max_ast_nodes, "transform");
102 P.reset();
103 Q.reset();
104 if (!rhs || !LMCAS::detail::node(rhs) ||
106 return false;
107 }
108
109 /// 方程形式: y' = rhs(x, y)
110 /// 线性形式: y' + P(x)*y = Q(x) → y' = Q(x) - P(x)*y → rhs = Q(x) - P(x)*y
111 /// 即 rhs 关于 y 是线性的: rhs = A(x) + B(x)*y,其中 Q = A, P = -B
112
113 /// 若 rhs 不依赖 y,则 P=0, Q=rhs
115 P = SymbolicExpr::number(0);
116 Q = rhs;
117 return true;
118 }
119
120 /// 对 rhs 关于 y 求导,若结果不依赖 y,则 rhs 关于 y 是线性的
121 auto drhs_dy = detail::make_expression_ptr(detail::differentiate_node(detail::node(rhs), y, transform_budget));
122 if (!drhs_dy || expression_depends_on_variable(LMCAS::detail::node(drhs_dy), y)) {
123 return false;
124 }
125
126 /// rhs = A(x) + B(x)*y,其中 B(x) = ∂rhs/∂y
127 /// 计算 A(x) = rhs|_{y=0}
128 auto A = detail::make_expression_ptr(detail::substitute_node(detail::node(rhs), y, detail::node(SymbolicExpr::number(0)), transform_budget));
129 if (!A) {
130 return false;
131 }
132
133 /// 验证 A 不依赖 y
135 return false;
136 }
137
138 /// B(x) = drhs_dy(已验证不依赖 y)
139 /// 线性形式: y' = A(x) + B(x)*y → y' - B(x)*y = A(x) → y' + (-B(x))*y = A(x)
140 /// 所以 P = -B(x), Q = A(x)
141 P = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(SymbolicExpr::number(-1), drhs_dy)), transform_budget));
143 return true;
144}
145
146
148 const std::shared_ptr<SymbolicExpr>& rhs,
149 const std::string& x,
150 const std::string& y)
151{
153 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
154 (context).limits().max_ast_nodes, "transform");
155 if (!rhs || !LMCAS::detail::node(rhs)) {
156 return false;
157 }
158 if (!valid_classifier_variables(x, y)) {
159 return false;
160 }
161
162 /// 齐次方程: f(tx, ty) = f(x, y) 对所有 t 成立
163 /// 用 t=2 进行数值测试:f(2x, 2y) 应等于 f(x, y)
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));
167
168 auto f_scaled = detail::make_expression_ptr(detail::substitute_node(detail::node(rhs), x, detail::node(tx), transform_budget));
169 f_scaled = detail::make_expression_ptr(detail::substitute_node(detail::node(f_scaled), y, detail::node(ty), transform_budget));
170 f_scaled = detail::make_expression_ptr(detail::normalize_node(detail::node(f_scaled), transform_budget));
171
172 /// 计算 f_scaled - rhs,若化简为零则齐次
173 auto diff = SymbolicExpr::add(f_scaled,
174 SymbolicExpr::multiply(SymbolicExpr::number(-1), rhs));
175 diff = detail::make_expression_ptr(detail::normalize_node(detail::node(diff), transform_budget));
176
177 if (diff->is_zero()) {
178 return true;
179 }
180
181
182 auto verified = check_zero_residual(diff, context);
183 return verified &&
184 std::holds_alternative<ProvedZeroResidual>(verified.value());
185}
186
187
188static std::optional<int> bernoulli_integer_exponent(const NumberNode& number)
189{
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())) {
194 const auto& rational = std::get<Rational>(number.value());
195 if (!rational.is_integer()) {
196 return std::nullopt;
197 }
198 value = rational.to_bigint().try_to_int64();
199 }
200 if (!value || *value < std::numeric_limits<int>::min() ||
201 *value > std::numeric_limits<int>::max()) {
202 return std::nullopt;
203 }
204 return static_cast<int>(*value);
205}
206
208 if (value > 0 && exponent > std::numeric_limits<int>::max() - value) {
209 return false;
210 }
211 if (value < 0 && exponent < std::numeric_limits<int>::min() - value) {
212 return false;
213 }
214 exponent += value;
215 return true;
216}
217
219 const std::shared_ptr<const SymbolicNode>& factor, const std::string& y,
220 int& exponent, std::shared_ptr<SymbolicExpr>& coefficient, ComputationContext& context) {
221
222 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
223 (context).limits().max_ast_nodes, "transform");
224 if (auto variable =
225 std::dynamic_pointer_cast<const VariableNode>(factor);
226 variable && !variable->is_constant() && variable->name() == y) {
227 if (exponent == std::numeric_limits<int>::max()) {
228 return false;
229 }
230 ++exponent;
231 return true;
232 }
233
234 if (auto power = std::dynamic_pointer_cast<const PowerNode>(factor)) {
235 auto base =
236 std::dynamic_pointer_cast<const VariableNode>(power->base());
237 auto power_value =
238 std::dynamic_pointer_cast<const NumberNode>(power->exponent());
239 if (base && !base->is_constant() && base->name() == y && power_value) {
240 auto value = bernoulli_integer_exponent(*power_value);
241 if (!value) {
242 return false;
243 }
245 }
246 }
247 if (expression_depends_on_variable(factor, y)) {
248 return false;
249 }
250 coefficient = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
251 coefficient, LMCAS::detail::make_expression_ptr(factor))), transform_budget));
252 return true;
253}
254
256 const std::shared_ptr<const SymbolicNode>& term,
257 const std::string& y,
258 int& exponent,
259 std::shared_ptr<SymbolicExpr>& coefficient, ComputationContext& context)
260{
261 exponent = 0;
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();
266 } else {
267 factors.push_back(term);
268 }
269
270 for (const auto& factor : factors) {
271 if (!extract_bernoulli_factor(factor, y, exponent, coefficient, context)) {
272 return false;
273 }
274 }
275 return true;
276}
277
279 const std::vector<std::shared_ptr<const SymbolicNode>>& terms,
280 const std::string& y,
281 std::map<int, std::shared_ptr<SymbolicExpr>>& coefficients, ComputationContext& context) {
282
283 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
284 (context).limits().max_ast_nodes, "transform");
285 for (const auto& term : terms) {
286 int exponent = 0;
287 std::shared_ptr<SymbolicExpr> coefficient;
289 term, y, exponent, coefficient, context)) {
290 return false;
291 }
292 auto& combined = coefficients[exponent];
293 combined = combined
294 ? detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(combined, coefficient)), transform_budget))
295 : detail::make_expression_ptr(detail::normalize_node(detail::node(coefficient), transform_budget));
296 }
297 return true;
298}
299
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,
306 int& n)
307{
309 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
310 (context).limits().max_ast_nodes, "transform");
311 P.reset();
312 Q.reset();
313 n = 0;
314 if (!rhs || !LMCAS::detail::node(rhs) ||
316 return false;
317 }
319 return false;
320 }
321
322 std::vector<std::shared_ptr<const SymbolicNode>> terms;
323 if (auto sum =
324 std::dynamic_pointer_cast<const AddNode>(LMCAS::detail::node(rhs))) {
325 terms = sum->operands();
326 } else {
327 terms.push_back(LMCAS::detail::node(rhs));
328 }
329
330 std::map<int, std::shared_ptr<SymbolicExpr>> coefficients;
331 if (!collect_bernoulli_coefficients(terms, y, coefficients, context)) {
332 return false;
333 }
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) {
338 coefficient = detail::make_expression_ptr(detail::normalize_node(detail::node(coefficient), transform_budget));
339 if (coefficient->is_zero()) {
340 continue;
341 }
342 if (exponent == 1) {
343 linear_coefficient = coefficient;
344 continue;
345 }
346 if (exponent == 0 || nonlinear_coefficient) {
347 return false;
348 }
349 nonlinear_exponent = exponent;
350 nonlinear_coefficient = coefficient;
351 }
352
353 if (!nonlinear_coefficient ||
354 nonlinear_exponent == 0 || nonlinear_exponent == 1) {
355 return false;
356 }
357
359 SymbolicExpr::number(-1), linear_coefficient)), transform_budget));
360 Q = detail::make_expression_ptr(detail::normalize_node(detail::node(nonlinear_coefficient), transform_budget));
361 n = nonlinear_exponent;
362 return true;
363}
364
365
367 const std::shared_ptr<SymbolicExpr>& M,
368 const std::shared_ptr<SymbolicExpr>& N,
369 const std::string& x,
370 const std::string& y)
371{
373 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
374 (context).limits().max_ast_nodes, "transform");
375 if (!M || !LMCAS::detail::node(M) ||
376 !N || !LMCAS::detail::node(N) ||
378 return false;
379 }
380
381 /// 恰当条件: ∂M/∂y = ∂N/∂x
382 auto dM_dy = detail::make_expression_ptr(detail::differentiate_node(detail::node(M), y, transform_budget));
383 auto dN_dx = detail::make_expression_ptr(detail::differentiate_node(detail::node(N), x, transform_budget));
384
385 if (!dM_dy || !dN_dx) {
386 return false;
387 }
388
389 dM_dy = detail::make_expression_ptr(detail::normalize_node(detail::node(dM_dy), transform_budget));
390 dN_dx = detail::make_expression_ptr(detail::normalize_node(detail::node(dN_dx), transform_budget));
391 if (dM_dy->compare(dN_dx) == 0) {
392 return true;
393 }
394
395 /// 计算差值并化简
396 auto diff = SymbolicExpr::add(dM_dy,
397 SymbolicExpr::multiply(SymbolicExpr::number(-1), dN_dx));
398 diff = detail::make_expression_ptr(detail::normalize_node(detail::node(diff), transform_budget));
399
400 if (diff->is_zero()) {
401 return true;
402 }
403
404
405 auto verified = check_zero_residual(diff, context);
406 return verified &&
407 std::holds_alternative<ProvedZeroResidual>(verified.value());
408
409}
410
411
413 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
414 const std::string& x)
415{
416 if (coeffs.empty() || x.empty()) {
417 return false;
418 }
419 for (const auto& c : coeffs) {
420 if (!c || !LMCAS::detail::node(c)) {
421 return false;
422 }
424 return false;
425 }
426 }
427 return true;
428}
429
430
432 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
433 const std::string& x,
434 std::vector<double>& euler_consts)
435{
437 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
438 (context).limits().max_ast_nodes, "transform");
439 euler_consts.clear();
440 if (coeffs.empty() || x.empty()) {
441 return false;
442 }
443 if (!coeffs.front() || !LMCAS::detail::node(coeffs.front()) ||
444 coeffs.front()->is_zero()) {
445 return false;
446 }
447
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;
452 if (!coeffs[index] || !LMCAS::detail::node(coeffs[index])) {
453 return false;
454 }
455 if (coeffs[index]->is_zero()) {
456 continue;
457 }
458
459 std::shared_ptr<SymbolicExpr> ratio;
460 if (derivative_order == 0) {
461 ratio = detail::make_expression_ptr(detail::normalize_node(detail::node(coeffs[index]), transform_budget));
462 } else {
463 auto x_power = SymbolicExpr::power(
464 SymbolicExpr::variable(x),
465 SymbolicExpr::number(derivative_order));
467 coeffs[index], x_power)), transform_budget));
468 }
469 if (!ratio || !LMCAS::detail::node(ratio)) {
470 return false;
471 }
472
474 LMCAS::detail::node(ratio), x)) {
475 return false;
476 }
477
478 const double value = try_eval_double(ratio);
479 if (!std::isfinite(value)) {
480 return false;
481 }
482 extracted[index] = value;
483 }
484
485 euler_consts = std::move(extracted);
486 return true;
487}
488
489
491 const std::shared_ptr<SymbolicExpr>& rhs,
492 const std::string& x,
493 const std::string& y)
494{
496 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
497 (context).limits().max_ast_nodes, "transform");
498 ODEClassification result;
499 result.order = 1;
500
501 if (!rhs || !LMCAS::detail::node(rhs)) {
502 result.type = ODEType::Unknown;
503 return result;
504 }
505
506 /// 1. 检测可分离变量
507 if (is_separable(rhs, x, y)) {
508 result.type = ODEType::Separable;
509 return result;
510 }
511
512 /// 2. 检测一阶线性
513 std::shared_ptr<SymbolicExpr> P, Q;
514 if (is_linear_first_order(rhs, x, y, P, Q)) {
515 result.type = ODEType::Linear1;
516 result.P_coeff = P;
517 result.Q_coeff = Q;
518 return result;
519 }
520
521 /// 3. 检测齐次方程
522 if (is_homogeneous_ode(rhs, x, y)) {
523 result.type = ODEType::Homogeneous;
524 return result;
525 }
526
527 /// 4. 检测 Bernoulli
528 std::shared_ptr<SymbolicExpr> bP, bQ;
529 int bn;
530 if (is_bernoulli_ode(rhs, x, y, bP, bQ, bn)) {
531 result.type = ODEType::Bernoulli;
532 result.bernoulli_P = bP;
533 result.bernoulli_Q = bQ;
534 result.bernoulli_n = bn;
535 return result;
536 }
537
538 /// 5. 检测恰当方程
539 /// 将 y' = rhs 改写为 M + N*y' = 0 形式: -rhs + y' = 0
540 /// 即 M = -rhs, N = 1(标准形式 M*dx + N*dy = 0 → M + N*dy/dx = 0)
541 /// 更一般地: 若 rhs = f(x,y),则 -f(x,y)*dx + dy = 0
542 /// M = -f(x,y), N = 1
543 auto neg_rhs = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(SymbolicExpr::number(-1), rhs)), transform_budget));
544 auto one = SymbolicExpr::number(1);
545 if (is_exact_ode(neg_rhs, one, x, y)) {
546 result.type = ODEType::Exact;
547 result.exact_M = neg_rhs;
548 result.exact_N = one;
549 return result;
550 }
551
552 result.type = ODEType::Unknown;
553 return result;
554}
555
556
558 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
559 const std::shared_ptr<SymbolicExpr>& forcing,
560 const std::string& x,
561 const std::string&)
562{
563 ODEClassification result;
564 result.order = static_cast<int>(coeffs.size()) - 1;
565
566 if (coeffs.empty()) {
567 result.type = ODEType::Unknown;
568 return result;
569 }
570
571 /// 1. 检测当前数值分类结果能够表示的常系数
572 if (is_constant_coefficient(coeffs, x)) {
573 std::vector<double> numeric_coeffs;
574 numeric_coeffs.reserve(coeffs.size());
575 for (const auto& c : coeffs) {
576 double val = try_eval_double(c);
577 if (!std::isfinite(val)) {
578 result.type = ODEType::Unknown;
579 return result;
580 }
581 numeric_coeffs.push_back(val);
582 }
583
584 result.type = result.order == 2
587 result.const_coeffs = std::move(numeric_coeffs);
588 result.forcing_func = forcing;
589 return result;
590 }
591
592 /// 2. 检测 Euler 方程
593 std::vector<double> euler_consts;
594 if (is_euler_equation(coeffs, x, euler_consts)) {
595 result.type = ODEType::Euler;
596 result.euler_coeffs = euler_consts;
597 result.euler_forcing = forcing;
598 return result;
599 }
600
601 result.type = ODEType::Unknown;
602 return result;
603}
604
605} // namespace LMCAS
乘法节点,表示多个操作数的乘积。
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
const std::variant< BigInt, Rational > & value() const noexcept
幂运算节点,表示 base^exponent。
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
ExprResult e()
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
高阶常系数线性
@ Unknown
无法识别的类型
@ 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)
const ExprPtr & exponent
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
const ExprPtr & rhs
static std::optional< int > bernoulli_integer_exponent(const NumberNode &number)
ODEType type
识别出的 ODE 类型
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)
ODE 分类结果,包含类型及提取的结构信息.
多项式转换与精确符号多项式代数。
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
内部 AST 定义。
统一 ODE 求解引擎:分类,分派,多种求解方法.