LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
limit_visitor_taylor.cpp
浏览该文件的文档.
2/**
3 * @file limit_visitor_taylor.cpp
4 * @brief LimitVisitor 的 Taylor 展开回退实现.
5 *
6 * L'Hôpital 规则达到最大迭代深度后,
7 * Taylor 级数展开分子和分母,并以首项系数比继续求极限.
8 *
9 * 算法来源:标准 CAS Taylor 级数极限技术
10 */
11
12#include "symbolic.hpp"
16
17namespace LMCAS {
18
19namespace {
21 const std::shared_ptr<const SymbolicNode>& num,
22 const std::shared_ptr<const SymbolicNode>& den, const std::string& var,
23 std::shared_ptr<SymbolicExpr>& num_expr, std::shared_ptr<SymbolicExpr>& den_expr, ComputationContext& context) {
24
25 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
26 (context).limits().max_ast_nodes, "transform");
27 /// x -> infinity: 代换 x = 1/t,在 t -> 0 处展开
28 std::string t_var = "__lim_t__";
29 auto names = detail::all_variable_names(num);
30 auto denominator_names = detail::all_variable_names(den);
31 names.insert(denominator_names.begin(), denominator_names.end());
32 while (names.count(t_var)) { t_var += "_"; }
33 auto t_expr = SymbolicExpr::variable(t_var);
34 auto one_over_t = SymbolicExpr::power(t_expr, SymbolicExpr::number(-1));
35
36 num_expr = detail::make_expression_ptr(detail::substitute_node(detail::node(num_expr), var, detail::node(one_over_t), transform_budget));
37 den_expr = detail::make_expression_ptr(detail::substitute_node(detail::node(den_expr), var, detail::node(one_over_t), transform_budget));
38
39 if (num_expr) { num_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(num_expr), transform_budget)); }
40 if (den_expr) { den_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(den_expr), transform_budget)); }
41
42 return t_var;
43}
44} // namespace
45
46std::shared_ptr<const SymbolicNode> LimitVisitor::divergent_leading_ratio(
47 const std::shared_ptr<SymbolicExpr>& ratio, int difference) {
48 auto sign = ratio ? get_node_sign(LMCAS::detail::node(ratio)) : std::nullopt;
49 if (!sign || !*sign) { return nullptr; }
50 if (difference % 2 && direction.empty() && !is_inf(point)) { return nullptr; }
51 if (difference % 2 && direction == "-" && !is_inf(point)) { *sign = -*sign; }
52 return signed_infinity(*sign);
53}
54
55
56
57/**
58 * @brief 对 L'Hôpital 法则产生的导数比进行代数化简后求极限.
59 *
60 * 当 dN/dD 含有公因子(如 x^-2)时,直接分别求极限会导致
61 * 无限 0/0 循环.此方法先通过 simplify() 约去公因子,
62 * 再对化简后的表达式求极限.
63 *
64 * @param[in] ratio_node 导数比 dN * dD^(-1) 的 AST 节点
65 * @return 极限结果,化简无效时返回 nullptr
66 */
67std::shared_ptr<const SymbolicNode> LimitVisitor::simplify_and_eval_ratio(
68 const std::shared_ptr<const SymbolicNode>& ratio_node) {
69
70 if (!ratio_node) {
71 return nullptr;
72 }
73
74 auto ratio_expr = LMCAS::detail::make_expression_ptr(ratio_node->clone());
75 auto simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(ratio_expr), *rewrite_budget()));
76 if (!simplified || !LMCAS::detail::node(simplified)) {
77 return nullptr;
78 }
79
80 if (detail::node(ratio_expr)->compare(*detail::node(simplified)) == 0) return nullptr;
81
82 /// Reject simplified forms that are AddNodes (sums). When simplify() expands
83 /// a fraction like (1-cos(x))/(sin(x)+x*cos(x)) into a sum of terms with
84 /// negative powers, evaluating the limit of that sum can trigger infinity-infinity detection,
85 /// which calls resolve_inf_minus_inf -> apply_lhopital -> simplify_and_eval_ratio
86 /// again, creating an infinite loop.
87 if (std::dynamic_pointer_cast<const AddNode>(LMCAS::detail::node(simplified))) {
88 return nullptr;
89 }
90
91 return derivative_limit(LMCAS::detail::node(simplified), true);
92}
93
94/**
95 * @brief Taylor 展开回退策略实现.
96 *
97 * 当极限点为有限值时,直接在该点展开 Taylor 级数.
98 * 当极限点为无穷时,先做 x = 1/t 代换,再在 t = 0 处展开.
99 * 从 order=4 开始逐步增加到 max_order,直到找到非零首项.
100 *
101 * @param[in] num 分子 AST 节点
102 * @param[in] den 分母 AST 节点
103 * @param[in] max_order 最大展开阶数
104 * @return 极限结果节点;nullptr 表示当前规则保持结果未知
105 */
106std::shared_ptr<const SymbolicNode> LimitVisitor::taylor_fallback(
107 const std::shared_ptr<const SymbolicNode>& num,
108 const std::shared_ptr<const SymbolicNode>& den,
109 int max_order) {
110
111 if (!num || !den) {
112 return nullptr;
113 }
114 if (!is_inf(point) && (!analytic_at_point(num) || !analytic_at_point(den))) { return nullptr; }
115
116 auto num_expr = LMCAS::detail::make_expression_ptr(num->clone());
117 auto den_expr = LMCAS::detail::make_expression_ptr(den->clone());
118 auto point_expr = LMCAS::detail::make_expression_ptr(point->clone());
119
120 bool at_infinity = is_inf(point);
121
122 std::string expand_var = var;
123 std::shared_ptr<SymbolicExpr> expand_point;
124
125 if (at_infinity) {
126 expand_var = substitute_infinity_series(num, den, var, num_expr, den_expr, rewrite_budget()->context());
127 expand_point = SymbolicExpr::number(0);
128 } else {
129 expand_point = point_expr;
130 }
131
132 if (!num_expr || !den_expr || !expand_point) {
133 return nullptr;
134 }
135 if (at_infinity) {
136 LimitVisitor analytic(expand_var, detail::node(expand_point), "+", assumption_ctx_, context_);
137 if (!analytic.analytic_at_point(detail::node(num_expr)) ||
138 !analytic.analytic_at_point(detail::node(den_expr))) { return nullptr; }
139 }
140
141 /// 从 order=4 开始,逐步增加到 max_order
142 for (int order = 4; order <= max_order; order += 2) {
143 auto value = taylor_series_limit(num_expr, den_expr, expand_var, expand_point, order);
144 if (value) {
145 return value;
146 }
147 }
148
149 return nullptr;
150}
151
152std::shared_ptr<const SymbolicNode> LimitVisitor::taylor_series_limit(
153 const std::shared_ptr<SymbolicExpr>& numerator,
154 const std::shared_ptr<SymbolicExpr>& denominator,
155 const std::string& expand_var,
156 const std::shared_ptr<SymbolicExpr>& expand_point, int order) {
157 auto numerator_series = numerator->series(expand_var, expand_point, order);
158 auto denominator_series = denominator->series(expand_var, expand_point, order);
159 if (!numerator_series || !denominator_series) {
160 return nullptr;
161 }
162 numerator_series = detail::make_expression_ptr(detail::normalize_node(detail::node(numerator_series), *rewrite_budget()));
163 denominator_series = detail::make_expression_ptr(detail::normalize_node(detail::node(denominator_series), *rewrite_budget()));
164 auto numerator_leading = find_leading_term(numerator_series, expand_var, expand_point, order);
165 auto denominator_leading = find_leading_term(denominator_series, expand_var, expand_point, order);
166 return leading_terms_limit(numerator_leading, denominator_leading);
167}
168
169std::shared_ptr<const SymbolicNode> LimitVisitor::leading_terms_limit(
170 const std::pair<std::shared_ptr<const SymbolicNode>, int>& numerator,
171 const std::pair<std::shared_ptr<const SymbolicNode>, int>& denominator) {
172 if (!numerator.first || !denominator.first) {
173 return nullptr;
174 }
175 if (denominator.first->is_zero()) {
176 return nullptr;
177 }
178 if (numerator.first->is_zero() || numerator.second > denominator.second) {
179 return LMCAS::detail::make_node<NumberNode>(BigInt(0));
180 }
181 auto ratio = SymbolicExpr::multiply(
182 LMCAS::detail::make_expression_ptr(numerator.first),
183 SymbolicExpr::power(LMCAS::detail::make_expression_ptr(denominator.first),
184 SymbolicExpr::number(-1)));
185 ratio = detail::make_expression_ptr(detail::normalize_node(detail::node(ratio), *rewrite_budget()));
186 if (numerator.second < denominator.second) {
187 return divergent_leading_ratio(ratio, denominator.second - numerator.second);
188 }
189 return ratio ? LMCAS::detail::node(ratio) : nullptr;
190}
191
192/**
193 * @internal
194 * @brief 从 Taylor 级数中提取关于 (x - a) 的首个非零项.
195 *
196 * 通过逐阶求导并在展开点求值来确定首个非零系数及其阶数.
197 *
198 * @param[in] series_expr 级数表达式
199 * @param[in] expand_var 展开变量名
200 * @param[in] expand_point 展开中心点
201 * @param[in] max_order 最大检查阶数
202 * @return (首项系数节点, 首项阶数) 对
203 */
204std::pair<std::shared_ptr<const SymbolicNode>, int> LimitVisitor::find_leading_term(
205 const std::shared_ptr<SymbolicExpr>& series_expr,
206 const std::string& expand_var,
207 const std::shared_ptr<SymbolicExpr>& expand_point,
208 int max_order) {
209
210 if (!series_expr) {
211 return {nullptr, 0};
212 }
213
214 auto current = series_expr;
215
216 for (int n = 0; n <= max_order; ++n) {
217 /// 在展开点求值得到第 n 阶系数(乘以 n!)
218 auto val = detail::make_expression_ptr(detail::substitute_node(detail::node(current), expand_var, detail::node(expand_point), *rewrite_budget()));
219 if (!val) {
220 return {nullptr, 0};
221 }
223
224 auto sign = val ? get_node_sign(LMCAS::detail::node(val)) : std::nullopt;
225 if (!sign) return {nullptr, 0};
226 if (*sign) return {LMCAS::detail::node(val), n};
227
228 /// 对当前表达式求导以获取下一阶系数
229 current = detail::make_expression_ptr(detail::differentiate_node(detail::node(current), expand_var, *rewrite_budget()));
230 if (!current) {
231 return {nullptr, 0};
232 }
233 current = detail::make_expression_ptr(detail::normalize_node(detail::node(current), *rewrite_budget()));
234 }
235
236 return {nullptr, max_order + 1};
237}
238
239/**
240 * @internal
241 * @brief 判断数值节点的符号.
242 * @param[in] node AST 节点
243 * @return 正数返回 1,负数返回 -1,零或未知符号返回 0
244 */
245int LimitVisitor::get_sign(const std::shared_ptr<const SymbolicNode>& node) {
246 if (!node) {
247 return 0;
248 }
249 if (auto num = std::dynamic_pointer_cast<const NumberNode>(node)) {
250 if (std::holds_alternative<BigInt>(num->value())) {
251 BigInt v = std::get<BigInt>(num->value());
252 if (v > BigInt(0)) {
253 return 1;
254 }
255 if (v < BigInt(0)) {
256 return -1;
257 }
258 return 0;
259 }
260 if (std::holds_alternative<Rational>(num->value())) {
261 Rational v = std::get<Rational>(num->value());
262 if (v > Rational(0)) {
263 return 1;
264 }
265 if (v < Rational(0)) {
266 return -1;
267 }
268 return 0;
269 }
270 }
271 /// 对于乘法节点,符号为各因子符号之积
272 if (auto mul = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
273 int sign = 1;
274 for (auto& op : mul->operands()) {
275 int s = get_sign(op);
276 if (s == 0) {
277 return 0;
278 }
279 sign *= s;
280 }
281 return sign;
282 }
283 return 0;
284}
285
286
287} // namespace LMCAS
极限访问器,通过代入和 L'Hôpital 法则计算极限。
std::string substitute_infinity_series(const std::shared_ptr< const SymbolicNode > &num, const std::shared_ptr< const SymbolicNode > &den, const std::string &var, std::shared_ptr< SymbolicExpr > &num_expr, std::shared_ptr< SymbolicExpr > &den_expr, ComputationContext &context)
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
std::shared_ptr< const Node > make_node(Args &&... args)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::set< std::string > all_variable_names(const SymbolicNodePtr &expression, RewriteBudget *budget=nullptr)
SymbolicNodePtr substitute_node(const SymbolicNodePtr &node, const std::string &variable, const SymbolicNodePtr &replacement, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr< SymbolicExpr > num(int value)
const std::string const LimitExprPtr & point
__attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::string & v
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
const std::vector< FGLMPoly > const MonomialOrder & order
@ Rational
Rational numbers
ExprResult mul(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const std::string const LimitExprPtr LimitDirection direction
const ExprPtr RelationOp op
const std::string const ExprPtr & value
const ExprPtr & denominator
符号规范化访问器接口。
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.