LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
ode_frobenius.cpp
浏览该文件的文档.
1#include <stdexcept>
3#include "limit_result.hpp"
4/**
5 * @file ode_frobenius.cpp
6 * @brief 奇点分类与 Frobenius 系数递推。
7 */
10#include "symbolic.hpp"
11#include "poly_utils.hpp"
13#include "lmmc/config.h"
14#include "lmmc/numeric.h"
16#include <cmath>
17#include <limits>
18#include <memory>
19#include <string>
20#include <utility>
21#include <vector>
22
23namespace LMCAS {
24
26 const std::shared_ptr<SymbolicExpr>&,
27 const std::shared_ptr<SymbolicExpr>&,
28 const std::shared_ptr<SymbolicExpr>&,
29 const std::string&, int, ODESingularityType, ComputationContext&);
30
33 "Frobenius series coefficients are outside the checked numeric support domain",
34 "solve_frobenius"};
35}
36
39 "Frobenius coefficient recurrence produced a non-finite value",
40 "solve_frobenius"};
41}
42
44 std::vector<double> p;
45 std::vector<double> q;
46};
47
49 const std::shared_ptr<SymbolicExpr>& p,
50 const std::shared_ptr<SymbolicExpr>& q,
51 const std::shared_ptr<SymbolicExpr>& x0,
52 const std::string& x, int order, ComputationContext& context) try {
53
54 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
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);
58
59 auto p_current = p;
60 auto q_current = q;
61 double factorial = 1.0;
62 for (int k = 0; k <= order + 1; ++k) {
63 if (k > 0) {
64 factorial *= k;
65 }
66 auto p_val_expr = detail::make_expression_ptr(detail::substitute_node(detail::node(p_current), x, detail::node(x0), transform_budget));
67 auto q_val_expr = detail::make_expression_ptr(detail::substitute_node(detail::node(q_current), x, detail::node(x0), transform_budget));
68 double pv = try_eval_double(p_val_expr);
69 double qv = try_eval_double(q_val_expr);
70 if (!std::isfinite(pv) || !std::isfinite(qv)) {
72 }
73 p_coeffs[k] = pv / factorial;
74 q_coeffs[k] = qv / factorial;
75 p_current = detail::make_expression_ptr(detail::differentiate_node(detail::node(p_current), x, transform_budget));
76 q_current = detail::make_expression_ptr(detail::differentiate_node(detail::node(q_current), x, transform_budget));
77 if (!p_current) {
78 p_current = SymbolicExpr::number(0);
79 }
80 if (!q_current) {
81 q_current = SymbolicExpr::number(0);
82 }
83 }
84
85 return OdeTaylorCoefficients{std::move(p_coeffs), std::move(q_coeffs)};
86} catch (const CasError& error) {
88} catch (const std::bad_alloc&) {
89 return Result<OdeTaylorCoefficients>::failure(CasErrc::ResourceLimit, "allocation failed", "ordinary_taylor_coefficients");
90} catch (const std::length_error& error) {
91 return Result<OdeTaylorCoefficients>::failure(CasErrc::ResourceLimit, error.what(), "ordinary_taylor_coefficients");
92} catch (const std::exception& error) {
93 return Result<OdeTaylorCoefficients>::failure(CasErrc::InternalInvariant, error.what(), "ordinary_taylor_coefficients");
94}
95
97 const OdeTaylorCoefficients& coefficients, int order) {
98 const auto& p_coeffs = coefficients.p;
99 const auto& q_coeffs = coefficients.q;
100 std::vector<double> a(order + 1, 0.0);
101 a[0] = 1.0;
102 for (int n_idx = 0; n_idx + 2 <= order; ++n_idx) {
103 double sum = 0.0;
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];
107 }
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])) {
112 }
113 }
114
115 return a;
116}
117
118static std::shared_ptr<SymbolicExpr> assemble_frobenius_polynomial(
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]) < 1e-15) {
125 continue;
126 }
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));
132 }
133
134 return series_sol;
135}
136
138 double P0, double Q0) {
139 double ind_b = P0 - 1.0;
140 double ind_c = Q0;
141 double ind_D = ind_b * ind_b - 4.0 * ind_c;
142 if (!std::isfinite(ind_D)) {
144 }
145
146 double r1, r2;
147 int eq;
148 lmmc_double_nearly_equal_tol(ind_D, 0.0, 1e-12, 1e-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;
152 } else if (eq) {
153 r1 = -ind_b / 2.0;
154 r2 = r1;
155 } else {
156 r1 = -ind_b / 2.0;
157 r2 = r1;
158 }
159 if (r1 < r2) {
160 std::swap(r1, r2);
161 }
162
163 return std::make_pair(r1, r2);
164}
165
167 const std::shared_ptr<SymbolicExpr>& coefficient,
168 const std::string& x, const std::shared_ptr<SymbolicExpr>& x0,
170 if (std::isfinite(value)) {
171 return value;
172 }
173 auto limited = limit_expression_checked(
174 coefficient, x, x0, LimitDirection::Both, context);
175 if (!limited) {
176 return Result<double>::failure(limited.error());
177 }
178 const auto& lim = limited.value();
179 return lim ? try_eval_double(lim)
180 : std::numeric_limits<double>::quiet_NaN();
181}
182
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,
189 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
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);
193
194 auto xp_current = xp_expr;
195 auto x2q_current = x2q_expr;
196 double fact = 1.0;
197 for (int k = 0; k <= order; ++k) {
198 if (k > 0) {
199 fact *= k;
200 }
201 double pv, qv;
202 if (k == 0) {
203 pv = P0;
204 qv = Q0;
205 } else {
206 auto pv_expr = detail::make_expression_ptr(detail::substitute_node(detail::node(xp_current), x, detail::node(x0), transform_budget));
207 auto qv_expr = detail::make_expression_ptr(detail::substitute_node(detail::node(x2q_current), x, detail::node(x0), transform_budget));
208 pv = try_eval_double(pv_expr);
209 qv = try_eval_double(qv_expr);
210
211 auto p_limit = frobenius_coefficient_limit(xp_current, x, x0, pv, context);
212 if (!p_limit) {
213 return Result<OdeTaylorCoefficients>::failure(p_limit.error());
214 }
215 pv = p_limit.value();
216 auto q_limit = frobenius_coefficient_limit(x2q_current, x, x0, qv, context);
217 if (!q_limit) {
218 return Result<OdeTaylorCoefficients>::failure(q_limit.error());
219 }
220 qv = q_limit.value();
221 }
222 if (!std::isfinite(pv) || !std::isfinite(qv)) {
224 }
225 pn_coeffs[k] = pv / fact;
226 qn_coeffs[k] = qv / fact;
227
228 xp_current = detail::make_expression_ptr(detail::differentiate_node(detail::node(xp_current), x, transform_budget));
229 x2q_current = detail::make_expression_ptr(detail::differentiate_node(detail::node(x2q_current), x, transform_budget));
230 if (!xp_current) {
231 xp_current = SymbolicExpr::number(0);
232 }
233 if (!x2q_current) {
234 x2q_current = SymbolicExpr::number(0);
235 }
236 }
237
238 return OdeTaylorCoefficients{std::move(pn_coeffs), std::move(qn_coeffs)};
239} catch (const CasError& error) {
241} catch (const std::bad_alloc&) {
242 return Result<OdeTaylorCoefficients>::failure(CasErrc::ResourceLimit, "allocation failed", "singular_taylor_coefficients");
243} catch (const std::length_error& error) {
244 return Result<OdeTaylorCoefficients>::failure(CasErrc::ResourceLimit, error.what(), "singular_taylor_coefficients");
245} catch (const std::exception& error) {
246 return Result<OdeTaylorCoefficients>::failure(CasErrc::InternalInvariant, error.what(), "singular_taylor_coefficients");
247}
248
250 const OdeTaylorCoefficients& coefficients,
251 double P0, double Q0, double r1, int order) {
252 const auto& pn_coeffs = coefficients.p;
253 const auto& qn_coeffs = coefficients.q;
254 /**
255 * @brief 递推 a_n = -1/F(r1+n) * ∑_{k=0}^{n-1} [(r1+k)·p_{n-k} + q_{n-k}]·a_k。
256 * @note F(s) = s(s-1) + P₀·s + Q₀。
257 */
258 auto indicial_poly = [&](double s) -> double {
259 return s * (s - 1.0) + P0 * s + Q0;
260 };
261
262 std::vector<double> a(order + 1, 0.0);
263 a[0] = 1.0;
264
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)) {
269 }
270 if (std::abs(F_val) < 1e-15) {
271 a[n_idx] = 0.0;
272 continue;
273 }
274 double sum = 0.0;
275 for (int k = 0; k < n_idx; ++k) {
276 int idx = n_idx - k;
277 if (idx > order) {
278 continue;
279 }
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];
283 }
284 a[n_idx] = -sum / F_val;
285 if (!std::isfinite(a[n_idx])) {
287 }
288 }
289
290 return a;
291}
292
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,
299
300 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
301 (context).limits().max_ast_nodes, "transform");
302 auto coefficients = ordinary_taylor_coefficients(p, q, x0, x, result.truncation_order, context);
303 if (!coefficients) {
304 return FrobeniusSolutionResult::failure(coefficients.error());
305 }
306 auto recurrence = ordinary_series_recurrence(coefficients.value(), result.truncation_order);
307 if (!recurrence) {
308 return FrobeniusSolutionResult::failure(recurrence.error());
309 }
311 recurrence.value(), x_minus_x0)), transform_budget));
312 result.indicial_roots = {0.0};
313 return result;
314} catch (const CasError& error) {
316} catch (const std::bad_alloc&) {
317 return FrobeniusSolutionResult::failure(CasErrc::ResourceLimit, "allocation failed", "ordinary_frobenius_solution");
318} catch (const std::length_error& error) {
319 return FrobeniusSolutionResult::failure(CasErrc::ResourceLimit, error.what(), "ordinary_frobenius_solution");
320} catch (const std::exception& error) {
321 return FrobeniusSolutionResult::failure(CasErrc::InternalInvariant, error.what(), "ordinary_frobenius_solution");
322}
323
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,
330 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
331 (context).limits().max_ast_nodes, "transform");
332 auto xp_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(x_minus_x0, p)), transform_budget));
333 auto x2q_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
334 SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(2)), q)), transform_budget));
335
336 auto P0_result = limit_expression_checked(
337 xp_expr, x, x0, LimitDirection::Both, context);
338 if (!P0_result) {
339 return FrobeniusSolutionResult::failure(P0_result.error());
340 }
341 auto Q0_result = limit_expression_checked(
342 x2q_expr, x, x0, LimitDirection::Both, context);
343 if (!Q0_result) {
344 return FrobeniusSolutionResult::failure(Q0_result.error());
345 }
346 auto P0_expr = std::move(P0_result.value());
347 auto Q0_expr = std::move(Q0_result.value());
348
349 double P0 = P0_expr
350 ? try_eval_double(P0_expr)
351 : std::numeric_limits<double>::quiet_NaN();
352 double Q0 = Q0_expr
353 ? try_eval_double(Q0_expr)
354 : std::numeric_limits<double>::quiet_NaN();
355 if (!std::isfinite(P0) || !std::isfinite(Q0)) {
357 }
358 /**
359 * @brief 在原定义域内确认去心极限有限后约分,提取可去奇点解析延拓的 Taylor 系数。
360 * @note 原乘积未经约分的导数仍含缺失点,不能直接用于系数计算。
361 */
362 xp_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(xp_expr->cancel()), transform_budget));
363 x2q_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(x2q_expr->cancel()), transform_budget));
364
365 auto roots = frobenius_indicial_roots(P0, Q0);
366 if (!roots) {
368 }
369 const double r1 = roots.value().first;
370 result.indicial_roots = {r1, roots.value().second};
371 auto coefficients = singular_taylor_coefficients(
372 xp_expr, x2q_expr, x0, x, result.truncation_order, P0, Q0, context);
373 if (!coefficients) {
374 return FrobeniusSolutionResult::failure(coefficients.error());
375 }
376 auto recurrence = singular_series_recurrence(
377 coefficients.value(), P0, Q0, r1, result.truncation_order);
378 if (!recurrence) {
379 return FrobeniusSolutionResult::failure(recurrence.error());
380 }
381 auto power_prefix = SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(r1));
382 auto series_part = assemble_frobenius_polynomial(recurrence.value(), x_minus_x0);
383 result.series_solution = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(power_prefix, series_part)), transform_budget));
384 return result;
385} catch (const CasError& error) {
387} catch (const std::bad_alloc&) {
388 return FrobeniusSolutionResult::failure(CasErrc::ResourceLimit, "allocation failed", "singular_frobenius_solution");
389} catch (const std::length_error& error) {
390 return FrobeniusSolutionResult::failure(CasErrc::ResourceLimit, error.what(), "singular_frobenius_solution");
391} catch (const std::exception& error) {
392 return FrobeniusSolutionResult::failure(CasErrc::InternalInvariant, error.what(), "singular_frobenius_solution");
393}
394
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,
401try {
402 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
403 (context).limits().max_ast_nodes, "transform");
404 const std::string operation = "classify_singular_point";
405 auto valid = validate_ode_two_expr_point(p, q, x0, x, context, operation);
406 if (!valid) {
407 return ODESingularityResult::failure(valid.error());
408 }
409 try {
410 auto p_at_x0 = detail::make_expression_ptr(detail::substitute_node(detail::node(p), x, detail::node(x0), transform_budget));
411 auto q_at_x0 = detail::make_expression_ptr(detail::substitute_node(detail::node(q), x, detail::node(x0), transform_budget));
412 const double p_val = try_eval_double(p_at_x0);
413 const double q_val = try_eval_double(q_at_x0);
414 if (std::isfinite(p_val) && std::isfinite(q_val)) {
417 }
418 if ((!std::isfinite(p_val) &&
419 !free_variables(detail::node(p_at_x0)).empty()) ||
420 (!std::isfinite(q_val) &&
421 !free_variables(detail::node(q_at_x0)).empty())) {
424 "singularity classification requires numeric coefficient values",
425 operation);
426 }
427
428 auto x_var = SymbolicExpr::variable(x);
429 auto x_minus_x0 = SymbolicExpr::add(
430 x_var, SymbolicExpr::multiply(SymbolicExpr::number(-1), x0));
431 auto xp = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(x_minus_x0, p)), transform_budget));
432 auto x2q = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
433 SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(2)),
434 q)), transform_budget));
435 auto xp_limit = limit_checked(xp, x, x0, LimitDirection::Both, context);
436 if (!xp_limit) return ODESingularityResult::failure(xp_limit.error());
437 auto x2q_limit = limit_checked(x2q, x, x0, LimitDirection::Both, context);
438 if (!x2q_limit) return ODESingularityResult::failure(x2q_limit.error());
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",
448 operation);
449 } catch (const std::length_error& error) { return ODESingularityResult::failure(CasErrc::ResourceLimit, error.what(), "classify_singular_point"); } catch (const std::exception& ex) {
451 CasErrc::InternalInvariant, ex.what(), operation);
452 }
453} catch (const CasError& error) {
454 return ODESingularityResult::failure(error);
455} catch (const std::bad_alloc&) {
456 return ODESingularityResult::failure(CasErrc::ResourceLimit, "allocation failed", "classify_singular_point");
457} catch (const std::length_error& error) {
458 return ODESingularityResult::failure(CasErrc::ResourceLimit, error.what(), "classify_singular_point");
459} catch (const std::exception& error) {
460 return ODESingularityResult::failure(CasErrc::InternalInvariant, error.what(), "classify_singular_point");
461}
462
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)
468{
470 return classify_singular_point_checked(p, q, x0, x, context);
471}
472
473
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)
481try {
482 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
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));
487 auto xp_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(x_minus_x0, p)), transform_budget));
488 auto x2q_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
489 SymbolicExpr::power(x_minus_x0, SymbolicExpr::number(2)), q)), transform_budget));
490
491 auto P0_result = limit_expression_checked(
492 xp_expr, x, x0, LimitDirection::Both, context);
493 if (!P0_result) {
494 return Result<void>::failure(P0_result.error());
495 }
496 auto Q0_result = limit_expression_checked(
497 x2q_expr, x, x0, LimitDirection::Both, context);
498 if (!Q0_result) {
499 return Result<void>::failure(Q0_result.error());
500 }
501 const auto& P0_expr = P0_result.value();
502 const auto& Q0_expr = Q0_result.value();
503 double P0 = P0_expr
504 ? try_eval_double(P0_expr)
505 : std::numeric_limits<double>::quiet_NaN();
506 double Q0 = Q0_expr
507 ? try_eval_double(Q0_expr)
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",
513 operation);
514 }
515
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, 1e-12, 1e-12,
519 &zero_discriminant);
520 if (discriminant < 0.0 && !zero_discriminant) {
523 "Frobenius checked API currently supports real indicial roots only",
524 operation);
525 }
526
527 return Result<void>::success();
528} catch (const CasError& error) {
529 return Result<void>::failure(error);
530} catch (const std::bad_alloc&) {
531 return Result<void>::failure(CasErrc::ResourceLimit, "allocation failed", operation);
532} catch (const std::length_error& error) {
533 return Result<void>::failure(CasErrc::ResourceLimit, error.what(), operation);
534} catch (const std::exception& error) {
535 return Result<void>::failure(CasErrc::InternalInvariant, error.what(), operation);
536}
537
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,
543 int order,
545{
546 const std::string operation = "solve_frobenius";
547 auto valid = validate_ode_two_expr_point(p, q, x0, x, context, operation);
548 if (!valid) {
549 return FrobeniusSolutionResult::failure(valid.error());
550 }
551 if (order < 0 || order > 64) {
554 "Frobenius truncation order must be between 0 and 64",
555 operation);
556 }
557 auto budget = context.consume_steps(static_cast<std::size_t>(order + 1) * 24 + 24,
558 operation);
559 if (!budget) {
560 return FrobeniusSolutionResult::failure(budget.error());
561 }
562
563 try {
564 auto classified =
566 if (!classified) {
567 return FrobeniusSolutionResult::failure(classified.error());
568 }
569 const auto point_type = classified.value();
570 if (point_type == ODESingularityType::IrregularSingular) {
573 "Frobenius checked API does not support irregular singular points",
574 operation);
575 }
576 if (point_type == ODESingularityType::RegularSingular) {
577 auto regular_domain = validate_frobenius_regular_singular_domain(
578 p, q, x0, x, context, operation);
579 if (!regular_domain) {
580 return FrobeniusSolutionResult::failure(regular_domain.error());
581 }
582 }
583
584 auto solved =
586 p, q, x0, x, order, point_type, context);
587 if (!solved) {
588 return solved;
589 }
590 auto solution = std::move(solved.value());
591 if (!solution.series_solution ||
592 !LMCAS::detail::node(solution.series_solution)) {
595 "Frobenius solver produced no series in the supported domain",
596 operation);
597 }
598 if (solution.point_type != point_type) {
601 "Frobenius solver reported an unexpected singularity type",
602 operation);
603 }
604 return FrobeniusSolutionResult::success(std::move(solution));
605 } catch (const std::bad_alloc&) {
608 "allocation failed while solving Frobenius series",
609 operation);
610 } catch (const std::exception& ex) {
613 ex.what(),
614 operation);
615 }
616}
617
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,
623 int order)
624{
626 return solve_frobenius_checked(p, q, x0, x, order, context);
627}
628
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,
634 int order,
635 ODESingularityType point_type,
637{
638 FrobeniusSolution result;
639 result.truncation_order = order;
640 result.point_type = point_type;
641 auto x_var = SymbolicExpr::variable(x);
642 auto x_minus_x0 = SymbolicExpr::add(x_var,
643 SymbolicExpr::multiply(SymbolicExpr::number(-1), x0));
644
646 result.series_solution = nullptr;
647 return result;
648 }
649
651 return ordinary_frobenius_solution(p, q, x0, x, x_minus_x0, std::move(result), context);
652 }
654 p, q, x0, x, x_minus_x0, std::move(result), context);
655}
656
657}
CasError & error()
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)
ExprResult e()
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 及其工厂方法,运算接口.
内部 AST 定义。
统一 ODE 求解引擎:分类,分派,多种求解方法.