LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
series_expansions.cpp
浏览该文件的文档.
1#include <stdexcept>
3#include "limit_result.hpp"
4#include "series_engine.hpp"
5#include "symbolic.hpp"
12#include <algorithm>
13#include <cmath>
14#include <limits>
15#include <memory>
16#include <optional>
17#include <string>
18#include <variant>
19#include <vector>
20
21namespace LMCAS {
22
23static int detect_parity(const std::shared_ptr<SymbolicExpr>& f, const std::string& var, ComputationContext& context) {
24
25 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
26 (context).limits().max_ast_nodes, "transform");
27 if (!f) {
28 return 0;
29 }
30 auto neg_x = SymbolicExpr::multiply(SymbolicExpr::number(-1), SymbolicExpr::variable(var));
31 auto f_neg = detail::make_expression_ptr(detail::substitute_node(detail::node(f), var, detail::node(neg_x), transform_budget));
32 if (!f_neg) {
33 return 0;
34 }
35 f_neg = detail::make_expression_ptr(detail::normalize_node(detail::node(f_neg), transform_budget));
36 auto f_s = detail::make_expression_ptr(detail::normalize_node(detail::node(f), transform_budget));
37 auto diff_even = SymbolicExpr::add(f_neg, SymbolicExpr::multiply(SymbolicExpr::number(-1), f_s));
38 if (diff_even) { diff_even = detail::make_expression_ptr(detail::normalize_node(detail::node(diff_even), transform_budget)); if (diff_even->is_zero()) {
39 return 1;
40 } }
41 auto diff_odd = SymbolicExpr::add(f_neg, f_s);
42 if (diff_odd) { diff_odd = detail::make_expression_ptr(detail::normalize_node(detail::node(diff_odd), transform_budget)); if (diff_odd->is_zero()) {
43 return -1;
44 } }
45 return 0;
46}
47
48
49
52
54 const std::shared_ptr<SymbolicExpr>& expr,
55 const std::shared_ptr<SymbolicExpr>& center,
56 const std::string& var,
58 const std::string& operation)
59{
60 auto var_check = validate_series_variable(var, context, operation);
61 if (!var_check) {
62 return var_check;
63 }
64 if (!expr || !LMCAS::detail::node(expr) || !center || !LMCAS::detail::node(center)) {
66 "series expression and center cannot be null",
67 operation);
68 }
69 return Result<void>::success();
70}
71
72
74 int order_pos,
75 const std::string& operation)
76{
77 if (order_neg < 0 || order_pos < 0 || order_neg > 64 || order_pos > 64) {
79 "Laurent truncation orders must be between 0 and 64",
80 operation);
81 }
82 return Result<void>::success();
83}
84
85static std::optional<int> essential_exponent(
86 const std::shared_ptr<SymbolicExpr>& f,
87 const std::shared_ptr<SymbolicExpr>& shift, ComputationContext& context) {
88
89 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
90 (context).limits().max_ast_nodes, "transform");
91 auto fn = std::dynamic_pointer_cast<const FunctionNode>(detail::node(f));
92 if (!fn || fn->type() != FunctionNode::FuncType::Exp ||
93 fn->arguments().size() != 1) {
94 return std::nullopt;
95 }
96 auto argument = detail::make_expression_ptr(detail::normalize_node(fn->arguments()[0], transform_budget));
97 auto power = std::dynamic_pointer_cast<const PowerNode>(detail::node(argument));
98 if (!power) {
99 return std::nullopt;
100 }
102 power->exponent(), -std::numeric_limits<int>::max(), std::numeric_limits<int>::max());
103 if (!exponent) {
104 return std::nullopt;
105 }
106 auto base = detail::make_expression_ptr(detail::normalize_node(power->base(), transform_budget));
107 auto delta = detail::make_expression_ptr(detail::normalize_node(detail::node(shift), transform_budget));
108 if (base->compare(delta) == 0 && *exponent < 0) {
109 return -*exponent;
110 }
111 auto nested = std::dynamic_pointer_cast<const PowerNode>(detail::node(base));
112 if (nested && *exponent == -1) {
114 nested->exponent(), 1, std::numeric_limits<int>::max());
115 auto nested_base = detail::make_expression_ptr(detail::normalize_node(nested->base(), transform_budget));
116 if (m && nested_base->compare(delta) == 0) {
117 return m;
118 }
119 }
120 return std::nullopt;
121}
122
124 const std::shared_ptr<SymbolicExpr>& shift, int m, int order_neg,
126 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
127 (context).limits().max_ast_nodes, "transform");
128 constexpr const char* operation = "laurent_series_full";
129 auto series = SymbolicExpr::number(1);
130 BigInt factorial(1);
131 for (int k = 1; k <= order_neg / m; ++k) {
132 auto step = context.consume_steps(1, operation);
133 if (!step) {
134 return LaurentSeriesResult::failure(step.error());
135 }
136 factorial *= BigInt(k);
137 auto term = SymbolicExpr::divide(
138 SymbolicExpr::number(1),
139 SymbolicExpr::multiply(
140 SymbolicExpr::number(factorial),
141 SymbolicExpr::power(shift, SymbolicExpr::number(m * k))));
142 series = SymbolicExpr::add(series, term);
143 }
144 return LaurentResult{
146 SymbolicExpr::number(m == 1 ? 1 : 0)};
147} catch (const CasError& error) {
148 return LaurentSeriesResult::failure(error);
149} catch (const std::bad_alloc&) {
150 return LaurentSeriesResult::failure(CasErrc::ResourceLimit, "allocation failed", "laurent_series_full");
151} catch (const std::length_error& error) {
152 return LaurentSeriesResult::failure(CasErrc::ResourceLimit, error.what(), "laurent_series_full");
153} catch (const std::exception& error) {
154 return LaurentSeriesResult::failure(CasErrc::InternalInvariant, error.what(), "laurent_series_full");
155}
157 const std::shared_ptr<SymbolicExpr>&,
158 const std::string&,
159 const std::shared_ptr<SymbolicExpr>&,
160 int, int, ComputationContext&);
162 Integrator& integrator, const std::shared_ptr<SymbolicExpr>& f,
163 const std::string& var, const std::shared_ptr<SymbolicExpr>& half_lo,
164 const std::shared_ptr<SymbolicExpr>& half_hi,
165 const std::shared_ptr<SymbolicExpr>& L,
166 const std::shared_ptr<SymbolicExpr>& arg, bool cosine,
167 std::shared_ptr<SymbolicExpr>& result, ComputationContext& context) try {
168 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
169 (context).limits().max_ast_nodes, "transform");
170 auto integrand = SymbolicExpr::multiply(
171 f, cosine ? SymbolicExpr::cos(arg) : SymbolicExpr::sin(arg));
172 auto integrated = integrator.integrate_def_checked(
173 *integrand, var, *half_lo, *half_hi, context);
174 if (!integrated) {
175 return Result<void>::failure(integrated.error());
176 }
177 auto coefficient = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(
178 detail::make_expression_ptr(integrated.value()), L)), transform_budget));
179 if (!(detail::node(coefficient) && detail::node(coefficient)->is_zero())) {
180 result = SymbolicExpr::add(
181 result, SymbolicExpr::multiply(
182 coefficient, cosine ? SymbolicExpr::cos(arg) : SymbolicExpr::sin(arg)));
183 }
184 return Result<void>::success();
185} catch (const CasError& error) {
186 return Result<void>::failure(error);
187} catch (const std::bad_alloc&) {
188 return Result<void>::failure(CasErrc::ResourceLimit, "allocation failed", "add_fourier_harmonic");
189} catch (const std::length_error& error) {
190 return Result<void>::failure(CasErrc::ResourceLimit, error.what(), "add_fourier_harmonic");
191} catch (const std::exception& error) {
192 return Result<void>::failure(CasErrc::InternalInvariant, error.what(), "add_fourier_harmonic");
193}
194
196 std::shared_ptr<SymbolicExpr> two;
197 std::shared_ptr<SymbolicExpr> half_length;
198 std::shared_ptr<SymbolicExpr> lower;
199 std::shared_ptr<SymbolicExpr> upper;
200 std::shared_ptr<SymbolicExpr> frequency;
201};
202
204 const std::shared_ptr<SymbolicExpr>& period, const std::string& var,
206 auto T = period;
207 std::optional<detail::AssumptionFacts> assumed;
208 if (context.assumptions()) { assumed.emplace(*context.assumptions()); }
209 const auto& facts = assumed ? static_cast<const FactsQuery&>(*assumed) : detail::no_facts();
210 auto positive_period = detail::query_positive_value(detail::node(T), facts, context);
211 if (!positive_period) { return Result<FourierPeriod>::failure(positive_period.error()); }
213 positive_period.value() != Tribool::True) {
216 "Fourier period must be a proved positive constant", "fourier_series");
217 }
220 auto two = SymbolicExpr::number(2);
221 auto L = SymbolicExpr::divide(T, two);
222 auto half_lo = SymbolicExpr::multiply(SymbolicExpr::number(-1), L);
223 auto half_hi = L;
224 auto w = SymbolicExpr::divide(SymbolicExpr::multiply(two, pi), T);
225 return FourierPeriod{std::move(two), std::move(L), std::move(half_lo),
226 std::move(half_hi), std::move(w)};
227}
228
230 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
231 const FourierPeriod& period, ComputationContext& context) try {
232 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
233 (context).limits().max_ast_nodes, "transform");
234 auto a0_integral = integrator.integrate_def_checked(
235 *f, var, *period.lower, *period.upper, context);
236 if (!a0_integral) { return ExpressionResult::failure(a0_integral.error()); }
238 LMCAS::detail::make_expression_ptr(a0_integral.value()), period.half_length)), transform_budget));
239 return SymbolicExpr::divide(a0, period.two);
240} catch (const CasError& error) {
241 return ExpressionResult::failure(error);
242} catch (const std::bad_alloc&) {
243 return ExpressionResult::failure(CasErrc::ResourceLimit, "allocation failed", "fourier_constant_coefficient");
244} catch (const std::length_error& error) {
245 return ExpressionResult::failure(CasErrc::ResourceLimit, error.what(), "fourier_constant_coefficient");
246} catch (const std::exception& error) {
247 return ExpressionResult::failure(CasErrc::InternalInvariant, error.what(), "fourier_constant_coefficient");
248}
249
251 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
252 const FourierPeriod& period, const std::shared_ptr<SymbolicExpr>& x,
253 int n_terms, int parity, std::shared_ptr<SymbolicExpr>& result, ComputationContext& context) {
254 for (int k = 1; k <= n_terms; ++k) {
255 auto kw = SymbolicExpr::multiply(SymbolicExpr::number(k), period.frequency);
256 auto arg = SymbolicExpr::multiply(kw, x);
257 if (parity != -1) {
258 auto added = add_fourier_harmonic(integrator, f, var, period.lower,
259 period.upper, period.half_length, arg, true, result, context);
260 if (!added) { return added; }
261 }
262 if (parity != 1) {
263 auto added = add_fourier_harmonic(integrator, f, var, period.lower,
264 period.upper, period.half_length, arg, false, result, context);
265 if (!added) { return added; }
266 }
267 }
268 return Result<void>::success();
269}
270
272 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
273 const std::shared_ptr<SymbolicExpr>& period, int n_terms,
275 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
276 (context).limits().max_ast_nodes, "transform");
277 constexpr const char* operation = "fourier_series";
278 if (!f || !period || var.empty() || n_terms < 0) {
281 "Fourier expansion requires an expression, variable, positive period, and non-negative term count",
282 operation);
283 }
284 const std::size_t expansion_terms =
285 1 + 2 * static_cast<std::size_t>(n_terms);
286 if (expansion_terms > context.limits().max_expansion_terms) {
289 "Fourier term count exceeds the expansion budget", operation);
290 }
291
292 try {
293 auto access = context.consume_steps(1, operation);
294 if (!access) {
295 return ExpressionResult::failure(access.error());
296 }
297
298 auto prepared = prepare_fourier_period(period, var, context);
299 if (!prepared) { return ExpressionResult::failure(prepared.error()); }
300
301 int parity = detect_parity(f, var, context);
302 Integrator integrator;
303 auto x = SymbolicExpr::variable(var);
304
305 auto constant = fourier_constant_coefficient(integrator, f, var, prepared.value(), context);
306 if (!constant) { return constant; }
307 auto result = std::move(constant.value());
308 auto harmonics = fourier_harmonics(
309 integrator, f, var, prepared.value(), x, n_terms, parity, result, context);
310 if (!harmonics) { return ExpressionResult::failure(harmonics.error()); }
312 } catch (const std::bad_alloc&) {
315 "Fourier expansion allocation failed", operation);
316 } catch (const std::length_error& error) { return ExpressionResult::failure(CasErrc::ResourceLimit, error.what(), "fourier_series"); } catch (const std::exception& error) {
318 CasErrc::InternalInvariant, error.what(), operation);
319 }
320} catch (const CasError& error) {
321 return ExpressionResult::failure(error);
322} catch (const std::bad_alloc&) {
323 return ExpressionResult::failure(CasErrc::ResourceLimit, "allocation failed", "fourier_series");
324} catch (const std::length_error& error) {
325 return ExpressionResult::failure(CasErrc::ResourceLimit, error.what(), "fourier_series");
326} catch (const std::exception& error) {
327 return ExpressionResult::failure(CasErrc::InternalInvariant, error.what(), "fourier_series");
328}
329
331 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
332 const std::shared_ptr<SymbolicExpr>& period, int n_terms) {
334 return fourier_series_checked(f, var, period, n_terms, context);
335}
336
337
339 const std::shared_ptr<SymbolicExpr>& f,
340 const std::string& var,
341 const std::shared_ptr<SymbolicExpr>& center,
342 int order_neg,
343 int order_pos,
345try {
346 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
347 (context).limits().max_ast_nodes, "transform");
348 auto full = laurent_series_full_checked(
349 f, var, center, order_neg, order_pos, context);
350 if (!full) {
351 return ExpressionResult::failure(full.error());
352 }
353 auto simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(f), transform_budget));
354 auto supported_power = simplified
356 LMCAS::detail::node(simplified), var)
357 : std::nullopt;
358 if (center->is_zero() && supported_power && *supported_power < 0) {
359 return ExpressionResult::success(std::move(simplified));
360 }
361 return ExpressionResult::success(std::move(full.value().series));
362} catch (const CasError& error) {
363 return ExpressionResult::failure(error);
364} catch (const std::bad_alloc&) {
365 return ExpressionResult::failure(CasErrc::ResourceLimit, "allocation failed", "laurent_series_checked");
366} catch (const std::length_error& error) {
367 return ExpressionResult::failure(CasErrc::ResourceLimit, error.what(), "laurent_series_checked");
368} catch (const std::exception& error) {
369 return ExpressionResult::failure(CasErrc::InternalInvariant, error.what(), "laurent_series_checked");
370}
371
373 const std::shared_ptr<SymbolicExpr>& f,
374 const std::string& var,
375 const std::shared_ptr<SymbolicExpr>& center,
376 int order_neg,
377 int order_pos)
378{
380 return laurent_series_checked(f, var, center, order_neg, order_pos, context);
381}
382
384 const LaurentResult& result, const std::optional<int>& supported_power,
385 const std::shared_ptr<SymbolicExpr>& center, const std::string& operation) {
386 if (!result.series || !LMCAS::detail::node(result.series)) {
389 "Laurent series could not be constructed in the supported domain",
390 operation);
391 }
392 if (!result.residue || !LMCAS::detail::node(result.residue)) {
395 "Laurent series produced a null residue expression",
396 operation);
397 }
398 if (supported_power && center->is_zero()) {
399 BigInt expected_pole_order =
400 *supported_power < 0 ? -BigInt(*supported_power) : BigInt(0);
401 if (BigInt(result.pole_order) != expected_pole_order) {
404 "Laurent pole order could not be verified",
405 operation);
406 }
407 }
408 return Result<void>::success();
409}
410
412 const std::shared_ptr<SymbolicExpr>& f,
413 const std::string& var,
414 const std::shared_ptr<SymbolicExpr>& center,
415 int order_neg,
416 int order_pos,
418try {
419 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
420 (context).limits().max_ast_nodes, "transform");
421 const std::string operation = "laurent_series_full";
422 auto input = validate_series_expr_point(f, center, var, context, operation);
423 if (!input) {
424 return LaurentSeriesResult::failure(input.error());
425 }
426 auto order_check = validate_laurent_orders(order_neg, order_pos, operation);
427 if (!order_check) {
428 return LaurentSeriesResult::failure(order_check.error());
429 }
430 auto budget = context.consume_steps(static_cast<std::size_t>(order_neg + order_pos + 1) * 16 + 16,
431 operation);
432 if (!budget) {
433 return LaurentSeriesResult::failure(budget.error());
434 }
435 try {
436 auto simplified_input = detail::make_expression_ptr(detail::normalize_node(detail::node(f), transform_budget));
437 if (!simplified_input || !LMCAS::detail::node(simplified_input)) {
440 "checked Laurent input could not be simplified in the supported domain",
441 operation);
442 }
443 auto supported_power = supported_laurent_integer_power(
444 LMCAS::detail::node(simplified_input), var);
445 auto built = laurent_series_full_impl(
446 f, var, center, order_neg, order_pos, context);
447 if (!built) {
448 return built;
449 }
450 auto result = std::move(built.value());
451 auto valid = validate_laurent_result(result, supported_power, center, operation);
452 if (!valid) {
453 return LaurentSeriesResult::failure(valid.error());
454 }
455 return LaurentSeriesResult::success(std::move(result));
456 } catch (const CasError& error) {
457 return LaurentSeriesResult::failure(error);
458 } catch (const std::bad_alloc&) {
460 "allocation failed while calculating Laurent series",
461 operation);
462 } catch (const std::length_error& error) { return LaurentSeriesResult::failure(CasErrc::ResourceLimit, error.what(), "laurent_series_full"); } catch (const std::exception& ex) {
464 ex.what(),
465 operation);
466 }
467} catch (const CasError& error) {
468 return LaurentSeriesResult::failure(error);
469} catch (const std::bad_alloc&) {
470 return LaurentSeriesResult::failure(CasErrc::ResourceLimit, "allocation failed", "laurent_series_full");
471} catch (const std::length_error& error) {
472 return LaurentSeriesResult::failure(CasErrc::ResourceLimit, error.what(), "laurent_series_full");
473} catch (const std::exception& error) {
474 return LaurentSeriesResult::failure(CasErrc::InternalInvariant, error.what(), "laurent_series_full");
475}
476
478 const std::shared_ptr<SymbolicExpr>& f,
479 const std::string& var,
480 const std::shared_ptr<SymbolicExpr>& center,
481 int order_neg,
482 int order_pos)
483{
485 return laurent_series_full_checked(f, var, center, order_neg, order_pos, context);
486}
487
493
495 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
497 constexpr const char* operation = "laurent_series_full";
498 auto fn = std::dynamic_pointer_cast<const FunctionNode>(detail::node(f));
499 if (fn && fn->arguments().size() == 1 && regular_laurent_type(fn->type())) {
500 auto argument = detail::make_expression_ptr(fn->arguments()[0]);
501 auto polynomial = recognize_rational_polynomial(*argument, var, context);
502 if (!polynomial) {
503 return Result<int>::failure(polynomial.error());
504 }
505 if (polynomial.value()) {
506 return 0;
507 }
508 }
510 "Laurent singularity could not be proved", operation);
511}
512
514 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
515 const std::shared_ptr<SymbolicExpr>& center,
516 const std::shared_ptr<SymbolicExpr>& shift,
517 std::shared_ptr<SymbolicExpr>& regular, ComputationContext& context) try {
518 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
519 (context).limits().max_ast_nodes, "transform");
520 constexpr const char* operation = "laurent_series_full";
521 auto center_number = std::dynamic_pointer_cast<const NumberNode>(detail::node(center));
522 if (!center_number) {
524 "Laurent center must be an exact number", operation);
525 }
526 Rational point = std::holds_alternative<BigInt>(center_number->value())
527 ? Rational(std::get<BigInt>(center_number->value()))
528 : std::get<Rational>(center_number->value());
529 Polynomial<Rational> numerator(var), denominator(var);
530 auto recognized = detail::extract_rational(*f, var, numerator, denominator);
531 if (!recognized) {
532 return Result<int>::failure(recognized.error());
533 }
534 if (!recognized.value()) {
535 return recognize_regular_laurent(f, var, context);
536 }
537 if (numerator.is_zero()) {
538 return 0;
539 }
540 const Polynomial<Rational> factor({Rational(0) - point, Rational(1)}, var);
541 int pole_order = 0;
542 while (denominator.eval(point) == Rational(0)) {
543 auto step = context.consume_steps(1, operation);
544 if (!step) {
545 return Result<int>::failure(step.error());
546 }
547 if (pole_order >= 64) {
549 "Laurent pole order exceeds supported range", operation);
550 }
551 denominator = denominator.div_mod(factor).first;
552 ++pole_order;
553 }
554 if (pole_order > 0) {
555 regular = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
556 SymbolicExpr::power(shift, SymbolicExpr::number(pole_order)), f)->cancel()), transform_budget));
557 }
558 return pole_order;
559} catch (const CasError& error) {
560 return Result<int>::failure(error);
561} catch (const std::bad_alloc&) {
562 return Result<int>::failure(CasErrc::ResourceLimit, "allocation failed", "laurent_series_full");
563} catch (const std::length_error& error) {
564 return Result<int>::failure(CasErrc::ResourceLimit, error.what(), "laurent_series_full");
565} catch (const std::exception& error) {
566 return Result<int>::failure(CasErrc::InternalInvariant, error.what(), "laurent_series_full");
567}
568
570 const std::shared_ptr<SymbolicExpr>& f, const std::string& var,
571 const std::shared_ptr<SymbolicExpr>& center, int order_neg, int order_pos,
573 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
574 (context).limits().max_ast_nodes, "transform");
575 LaurentResult res{nullptr, SingularityType::Removable, 0, SymbolicExpr::number(0)};
576 if (!f || !center) {
577 return res;
578 }
579
580 auto x = SymbolicExpr::variable(var);
581 auto shift = SymbolicExpr::add(x, SymbolicExpr::multiply(SymbolicExpr::number(-1), center));
582 if (!expression_depends_on_variable(detail::node(center), var)) {
583 if (auto m = essential_exponent(f, shift, context)) {
584 return essential_laurent(shift, *m, order_neg, context);
585 }
586 }
587
588 std::shared_ptr<SymbolicExpr> regular = f;
589 auto extracted = extract_laurent_regular_part(
590 f, var, center, shift, regular, context);
591 if (!extracted) {
592 return LaurentSeriesResult::failure(extracted.error());
593 }
594 const int pole_order = extracted.value();
595
596 /// 对 regular(解析部分)做 Taylor 展开
597 int taylor_order = order_pos + pole_order + 1;
598 if (taylor_order < 1) {
599 taylor_order = 1;
600 }
601 auto taylor = regular->series(var, center, taylor_order);
602 if (!taylor) {
603 return res;
604 }
605
606 /// Laurent = taylor / (x-c)^pole_order
607 std::shared_ptr<SymbolicExpr> laurent = taylor;
608 if (pole_order > 0) {
609 auto powm = SymbolicExpr::power(shift, SymbolicExpr::number(pole_order));
610 laurent = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(taylor, powm)), transform_budget));
611 }
612
614 res.pole_order = pole_order;
615 if (pole_order == 0) {
617 } else {
619 }
620
621 /// 留数 = Taylor 展开中 (x-c)^(pole_order-1) 的系数
622 if (pole_order >= 1) {
623 /// a_{m-1} = (1/(m-1)!) lim_{x->c} d^{m-1}/dx^{m-1} [regular]
624 auto deriv = regular;
625 for (int i = 0; i < pole_order - 1; ++i) deriv = detail::make_expression_ptr(detail::differentiate_node(detail::node(deriv), var, transform_budget));
626 /// 用极限求值,稳健处理 0/0 形式(如 z/(z(z+1)) 在 z=0).
627 auto limited = limit_expression_checked(
628 deriv, var, center, LimitDirection::Both, context);
629 if (!limited) {
630 return LaurentSeriesResult::failure(limited.error());
631 }
632 auto val = std::move(limited.value());
633 if (!val) {
634 val = detail::make_expression_ptr(detail::substitute_node(detail::node(deriv), var, detail::node(center), transform_budget));
635 }
636 auto fact = BigInt::factorial_checked(BigInt(pole_order - 1), context);
637 if (!fact) {
638 return LaurentSeriesResult::failure(fact.error());
639 }
640 res.residue = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(val, SymbolicExpr::number(fact.value()))), transform_budget));
641 }
642
643 return res;
644} catch (const CasError& error) {
645 return LaurentSeriesResult::failure(error);
646} catch (const std::bad_alloc&) {
647 return LaurentSeriesResult::failure(CasErrc::ResourceLimit, "allocation failed", "laurent_series_full_impl");
648} catch (const std::length_error& error) {
649 return LaurentSeriesResult::failure(CasErrc::ResourceLimit, error.what(), "laurent_series_full_impl");
650} catch (const std::exception& error) {
651 return LaurentSeriesResult::failure(CasErrc::InternalInvariant, error.what(), "laurent_series_full_impl");
652}
653
654std::shared_ptr<SymbolicExpr> asymptotic_expand(
655 const std::shared_ptr<SymbolicExpr>& f, const std::string& var, int order) {
657 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
658 (context).limits().max_ast_nodes, "transform");
659 if (!f || order < 0) {
660 return nullptr;
661 }
662 /// 通过 x = 1/t 替换,在 t=0 处做 Taylor 展开,再回代 t = 1/x,
663 /// 得到按 x 递减幂次的渐近展开.
664 auto x = SymbolicExpr::variable(var);
665 std::string tname = var + "__asym_t";
666 auto t = SymbolicExpr::variable(tname);
667 auto inv_t = SymbolicExpr::divide(SymbolicExpr::number(1), t);
668 auto g = detail::make_expression_ptr(detail::substitute_node(detail::node(f), var, detail::node(inv_t), transform_budget));
669 if (!g) {
670 return nullptr;
671 }
673 auto taylor_t = g->series(tname, SymbolicExpr::number(0), order + 1);
674 if (!taylor_t) {
675 return nullptr;
676 }
677 /// 回代 t = 1/x
678 auto inv_x = SymbolicExpr::divide(SymbolicExpr::number(1), x);
679 auto back = detail::make_expression_ptr(detail::substitute_node(detail::node(taylor_t), tname, detail::node(inv_x), transform_budget));
680 if (!back) {
681 return nullptr;
682 }
683 return detail::make_expression_ptr(detail::normalize_node(detail::node(back), transform_budget));
684}
685}
一元多项式模板类
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
std::optional< int > supported_laurent_integer_power(const std::shared_ptr< const SymbolicNode > &node, const std::string &variable)
Result< void > validate_series_variable(const std::string &variable, ComputationContext &context, const std::string &operation)
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
const FactsQuery & no_facts() noexcept
std::shared_ptr< const Node > make_node(Args &&... args)
Result< bool > extract_rational(const SymbolicExpr &expr, const std::string &var, Polynomial< Rational > &numerator, Polynomial< Rational > &denominator)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
Result< Tribool > query_positive_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
SymbolicNodePtr substitute_node(const SymbolicNodePtr &node, const std::string &variable, const SymbolicNodePtr &replacement, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
const std::string const LimitExprPtr & point
ExpressionResult fourier_series_checked(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const std::shared_ptr< SymbolicExpr > &period, int n_terms, ComputationContext &context)
static int detect_parity(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, ComputationContext &context)
std::shared_ptr< SymbolicExpr > half_length
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
static LaurentSeriesResult essential_laurent(const std::shared_ptr< SymbolicExpr > &shift, int m, int order_neg, ComputationContext &context)
SingularityType singularity
奇点类型
const std::vector< FGLMPoly > const MonomialOrder & order
static bool regular_laurent_type(FunctionNode::FuncType type)
ExpressionResult laurent_series_checked(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const std::shared_ptr< SymbolicExpr > &center, int order_neg, int order_pos, ComputationContext &context)
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
@ True
Property definitely holds
@ False
Property definitely does not hold
int pole_order
极点阶数(非极点时为 0)
LimitExpressionResult limit_expression_checked(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, Domain domain)
static Result< void > validate_laurent_result(const LaurentResult &result, const std::optional< int > &supported_power, const std::shared_ptr< SymbolicExpr > &center, const std::string &operation)
Result< std::shared_ptr< SymbolicExpr > > ExpressionResult
Result of an operation that produces a symbolic expression.
std::shared_ptr< SymbolicExpr > upper
static LaurentSeriesResult laurent_series_full_impl(const std::shared_ptr< SymbolicExpr > &, const std::string &, const std::shared_ptr< SymbolicExpr > &, int, int, ComputationContext &)
static Result< FourierPeriod > prepare_fourier_period(const std::shared_ptr< SymbolicExpr > &period, const std::string &var, ComputationContext &context)
Result< void > validate_series_expr_point(const std::shared_ptr< SymbolicExpr > &expr, const std::shared_ptr< SymbolicExpr > &center, const std::string &var, ComputationContext &context, const std::string &operation)
static Result< int > recognize_regular_laurent(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, ComputationContext &context)
std::shared_ptr< SymbolicExpr > two
const ExprPtr & exponent
std::optional< int > exact_small_integer_node(const detail::SymbolicNodePtr &node, int min_value, int max_value)
static Result< void > fourier_harmonics(Integrator &integrator, const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const FourierPeriod &period, const std::shared_ptr< SymbolicExpr > &x, int n_terms, int parity, std::shared_ptr< SymbolicExpr > &result, ComputationContext &context)
LaurentSeriesResult laurent_series_full_checked(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const std::shared_ptr< SymbolicExpr > &center, int order_neg, int order_pos, ComputationContext &context)
std::shared_ptr< SymbolicExpr > frequency
static std::optional< int > essential_exponent(const std::shared_ptr< SymbolicExpr > &f, const std::shared_ptr< SymbolicExpr > &shift, ComputationContext &context)
static ExpressionResult fourier_constant_coefficient(Integrator &integrator, const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const FourierPeriod &period, ComputationContext &context)
Result< void > validate_laurent_orders(int order_neg, int order_pos, const std::string &operation)
Result< OptionalRationalPolynomial > recognize_rational_polynomial(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
Result< LaurentResult > LaurentSeriesResult
std::shared_ptr< SymbolicExpr > residue
留数((z-z_0)-¹ 的系数)
ExprResult pi()
std::shared_ptr< SymbolicExpr > asymptotic_expand(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, int order)
std::shared_ptr< SymbolicExpr > series
洛朗级数表达式
std::optional< int > supported_laurent_integer_power(const std::shared_ptr< const SymbolicNode > &node, const std::string &variable)
static Result< int > extract_laurent_regular_part(const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const std::shared_ptr< SymbolicExpr > &center, const std::shared_ptr< SymbolicExpr > &shift, std::shared_ptr< SymbolicExpr > &regular, ComputationContext &context)
static Result< void > add_fourier_harmonic(Integrator &integrator, const std::shared_ptr< SymbolicExpr > &f, const std::string &var, const std::shared_ptr< SymbolicExpr > &half_lo, const std::shared_ptr< SymbolicExpr > &half_hi, const std::shared_ptr< SymbolicExpr > &L, const std::shared_ptr< SymbolicExpr > &arg, bool cosine, std::shared_ptr< SymbolicExpr > &result, ComputationContext &context)
const ExprPtr & denominator
__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 & t
std::shared_ptr< SymbolicExpr > lower
const std::vector< IntegrationStep > Integrator & integrator
Result< void > validate_series_variable(const std::string &variable, ComputationContext &context, const std::string &operation)
洛朗级数展开结果.
符号表达式与一元多项式的转换。
级数引擎模块:收敛性判定,幂级数运算,傅里叶级数,洛朗级数,渐近展开,符号求和与乘积.
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
内部 AST 定义。