LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
limit_visitor_direction.cpp
浏览该文件的文档.
5#include "symbolic.hpp"
11
12namespace LMCAS {
13
14std::optional<std::pair<Rational, int>> LimitVisitor::exact_polynomial_leading(
15 const Polynomial<Rational>& polynomial, const Rational& point_value,
16 detail::ExactBoundArithmetic& arithmetic) const {
17 if (polynomial.is_zero()) { return std::make_pair(Rational(0), 0); }
18 auto coefficients = polynomial.coeffs;
19 for (int order = 0; !coefficients.empty(); ++order) {
20 charge();
21 Rational value(0);
22 for (auto it = coefficients.rbegin(); it != coefficients.rend(); ++it)
23 { value = arithmetic.add(arithmetic.mul(value, point_value), *it); }
24 if (value != Rational(0)) { return std::make_pair(value, order); }
25 for (std::size_t i = 1; i < coefficients.size(); ++i)
26 { coefficients[i - 1] = arithmetic.mul(coefficients[i], Rational(BigInt(i))); }
27 coefficients.pop_back();
28 }
29 return std::nullopt;
30}
31
32bool LimitVisitor::analytic_defined_at_point(
33 const std::shared_ptr<const SymbolicNode>& expr,
34 const std::shared_ptr<const SymbolicNode>& positive_base) const {
35 auto substituted = detail::make_expression_ptr(detail::substitute_node(expr, var, point, *rewrite_budget()));
36 if (!substituted) { return false; }
37 std::optional<detail::AssumptionFacts> assumed;
38 if (assumption_ctx_) { assumed.emplace(*assumption_ctx_); }
39 const auto& facts = assumed ? static_cast<const FactsQuery&>(*assumed) : detail::no_facts();
40 if (positive_base) {
41 auto at_point = detail::make_expression_ptr(detail::substitute_node(positive_base, var, point, *rewrite_budget()));
42 if (!at_point) { return false; }
43 auto positive = detail::query_positive_value(detail::node(at_point), facts, *context_);
44 if (!positive) { throw positive.error(); }
45 if (positive.value() != Tribool::True) { return false; }
46 }
47 auto defined = detail::query_definedness(detail::node(substituted), facts, Domain::Real, *context_);
48 if (!defined) { throw defined.error(); }
49 return defined && defined.value() == Tribool::True;
50}
51
52bool LimitVisitor::analytic_power_at_point(const PowerNode& power,
53 const std::shared_ptr<const SymbolicNode>& expr) const {
54 std::shared_ptr<const SymbolicNode> positive_base;
55 if (!analytic_at_point(power.base()) || !analytic_at_point(power.exponent())) { return false; }
56 BigInt integer;
57 auto number = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
58 const bool integral = number && try_get_integer_value(number, integer);
59 if (integral && integer > BigInt(0)) { return true; }
60 if (!integral) { positive_base = power.base(); }
61 return analytic_defined_at_point(expr, positive_base);
62}
63
64bool LimitVisitor::analytic_function_at_point(const FunctionNode& function,
65 const std::shared_ptr<const SymbolicNode>& expr) const {
66 if (function.arguments().size() != 1 || !analytic_at_point(function.arguments()[0])) { return false; }
67 switch (function.type()) {
68 case FunctionNode::FuncType::Sin: { break; }
69 case FunctionNode::FuncType::Cos: { break; }
70 case FunctionNode::FuncType::Exp: { break; }
71 case FunctionNode::FuncType::Ln: { break; }
72 case FunctionNode::FuncType::Tan: { break; }
73 case FunctionNode::FuncType::ArcTan: { break; }
74 default: { return false; }
75 }
76 return analytic_defined_at_point(expr, nullptr);
77}
78
79bool LimitVisitor::analytic_operands_at_point(
80 const std::vector<std::shared_ptr<const SymbolicNode>>& operands) const {
81 for (const auto& op : operands) {
82 if (!analytic_at_point(op)) { return false; }
83 }
84 return true;
85}
86
87std::optional<int> LimitVisitor::rational_sign_at_infinity(
88 const std::shared_ptr<const SymbolicNode>& expr) {
89 auto fraction = extract_num_den(expr);
90 auto numerator = symbolic_to_poly<Rational>(detail::make_expression_ptr(fraction.first), var);
91 auto denominator = symbolic_to_poly<Rational>(detail::make_expression_ptr(fraction.second), var);
92 if (!numerator && numerator.error().code != CasErrc::UnsupportedExpression) { throw numerator.error(); }
93 if (!denominator && denominator.error().code != CasErrc::UnsupportedExpression) { throw denominator.error(); }
94 if (numerator && denominator && !denominator.value().is_zero()) {
95 if (numerator.value().is_zero()) { return 0; }
96 auto coefficient = numerator.value().lead_coeff() / denominator.value().lead_coeff();
97 int sign = coefficient > Rational(0) ? 1 : -1;
98 if (is_limit_at_neg_infinity() && (numerator.value().degree() - denominator.value().degree()) % 2) { sign = -sign; }
99 return sign;
100 }
101 return std::nullopt;
102}
103
104std::optional<int> LimitVisitor::product_sign_near_point(
105 const MultiplyNode& product, const std::string& dir) {
106 int sign = 1;
107 for (const auto& op : product.operands()) {
108 auto value = determine_sign_near_point(op, dir);
109 if (!value) { return std::nullopt; }
110 sign *= *value;
111 }
112 return sign;
113}
114
115std::optional<int> LimitVisitor::power_sign_near_point(
116 const PowerNode& power, const std::string& dir) {
117 BigInt integer;
118 auto number = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
119 auto sign = determine_sign_near_point(power.base(), dir);
120 if (number && sign && *sign && try_get_integer_value(number, integer))
121 { return integer.is_odd() ? *sign : 1; }
122 if (number && sign && *sign > 0) {
123 auto real_exponent = extract_coeff_value<Rational>(detail::make_expression_ptr(power.exponent()));
124 if (real_exponent) { return 1; }
125 }
126 return std::nullopt;
127}
128
129std::optional<int> LimitVisitor::analytic_sign_near_point(
130 const std::shared_ptr<const SymbolicNode>& expr, const std::string& dir) {
131 if (!analytic_at_point(expr)) { return std::nullopt; }
132 DifferentiationVisitor differentiation(var, *rewrite_budget());
133 auto current = expr;
134 for (int order = 0; order <= 8; ++order) {
135 auto substituted = detail::make_expression_ptr(detail::substitute_node(current, var, point, *rewrite_budget()));
136 if (!substituted) { return std::nullopt; }
137 auto value = detail::make_expression_ptr(detail::normalize_node(detail::node(substituted), *rewrite_budget()));
138 auto sign = value ? get_node_sign(detail::node(value)) : std::nullopt;
139 if (!sign) { return std::nullopt; }
140 if (*sign) {
141 if (order % 2 && dir.empty()) { return std::nullopt; }
142 return order % 2 && dir == "-" ? -*sign : *sign;
143 }
144 current->accept(differentiation);
145 current = differentiation.get_result();
146 if (!current) { return std::nullopt; }
147 }
148 return std::nullopt;
149}
150
151namespace {
152std::optional<int> directional_leading_sign(
153 const std::pair<Rational, int>& leading, const std::string& dir) {
154 int sign = leading.first > Rational(0) ? 1 : leading.first < Rational(0) ? -1 : 0;
155 if (leading.second % 2 && dir == "-") { sign = -sign; }
156 if (leading.second % 2 && dir.empty()) { return std::nullopt; }
157 return sign;
158}
159
161 std::optional<bool> left, std::optional<bool> right) {
162 if ((left && !*left) || (right && !*right)) { return false; }
163 if (left && right) { return *left && *right; }
164 return std::nullopt;
165}
166
168 std::optional<bool> left, std::optional<bool> right) {
169 if ((left && *left) || (right && *right)) { return true; }
170 if (left && right) { return false; }
171 return std::nullopt;
172}
173}
174
175std::optional<bool> LimitVisitor::relation_truth_near_point(const RelationalNode& relation) {
176 auto difference = detail::make_node<AddNode>(std::vector<std::shared_ptr<const SymbolicNode>>{
177 relation.left(), detail::make_node<MultiplyNode>(std::vector<std::shared_ptr<const SymbolicNode>>{
178 detail::make_node<NumberNode>(BigInt(-1)), relation.right()})});
179 auto normalized = detail::simplify_expression(detail::make_expression_ptr(difference), *context_);
180 if (!normalized) { throw normalized.error(); }
181 auto sign = determine_sign_near_point(detail::node(normalized.value()), direction);
182 if (!sign) { return std::nullopt; }
183 switch (relation.op()) {
184 case RelationalNode::Op::GT: { return *sign > 0; }
185 case RelationalNode::Op::GEQ: { return *sign >= 0; }
186 case RelationalNode::Op::LT: { return *sign < 0; }
187 case RelationalNode::Op::LEQ: { return *sign <= 0; }
188 case RelationalNode::Op::EQ: { return *sign == 0; }
189 case RelationalNode::Op::NEQ: { return *sign != 0; }
190 }
191 return std::nullopt;
192}
193
194std::optional<bool> LimitVisitor::logical_truth_near_point(const LogicalNode& logical) {
195 auto left = condition_truth_near_point(logical.left());
196 if (logical.op() == LogicalNode::Op::Not) {
197 return left ? std::optional<bool>(!*left) : std::nullopt;
198 }
199 auto right = condition_truth_near_point(logical.right());
200 if (logical.op() == LogicalNode::Op::And) {
202 }
203 if (logical.op() == LogicalNode::Op::Or) {
205 }
206 return std::nullopt;
207}
208
209std::optional<bool> LimitVisitor::condition_truth_near_point(
210 const std::shared_ptr<const SymbolicNode>& node) {
211 if (auto logical = std::dynamic_pointer_cast<const LogicalNode>(node)) {
212 return logical_truth_near_point(*logical);
213 }
214 auto relation = std::dynamic_pointer_cast<const RelationalNode>(node);
215 if (!relation) { return std::nullopt; }
216 return relation_truth_near_point(*relation);
217}
218
219
220
221/**
222 * @brief 处理分段函数节点的极限.
223 *
224 * 根据趋近方向选择满足条件的分支并计算极限;
225 * 双侧结果相等时返回该值,差异时以 nullptr 表示极限未定义.
226 */
227void LimitVisitor::visit(const PiecewiseNode& node) {
228 if (direction.empty()) {
229 LimitVisitor left(var, point, "-", assumption_ctx_, context_);
230 LimitVisitor right(var, point, "+", assumption_ctx_, context_);
231 node.accept(left);
232 node.accept(right);
233 auto lv = left.get_result(), rv = right.get_result();
234 result = lv && rv && lv->compare(*rv) == 0 ? lv : nullptr;
235 return;
236 }
237 auto branch = select_branch_by_direction(node, direction);
238 result = branch ? eval_limit(branch) : nullptr;
239}
240
241/**
242 * @brief 根据趋近方向选择分段函数中满足条件的分支表达式.
243 */
244std::shared_ptr<const SymbolicNode> LimitVisitor::select_branch_by_direction(
245 const PiecewiseNode& node, const std::string& dir) {
246 LimitVisitor witness(var, point, dir, assumption_ctx_, context_);
247 for (const auto& branch : node.branches()) {
248 if (witness.condition_holds_near_point(branch.condition)) return branch.expression;
249 auto inverse = detail::make_node<LogicalNode>(branch.condition, nullptr, LogicalNode::Op::Not);
250 if (!witness.condition_holds_near_point(inverse)) return nullptr;
251 }
252 return node.default_expr();
253}
254
255
256/**
257 * @brief 方向感知的 sgn 函数极限计算.
258 *
259 * 当 sgn 的参数在趋近点为零时,根据趋近方向确定符号.
260 */
261std::optional<std::shared_ptr<const SymbolicNode>> LimitVisitor::evaluate_sgn_limit(
262 const std::shared_ptr<const SymbolicNode>& arg) {
263 LimitVisitor sv(var, point, direction, assumption_ctx_, context_);
264 arg->accept(sv);
265 auto al = sv.get_result();
266 if (!al) {
267 return std::nullopt;
268 }
269 NormalizationVisitor norm(*rewrite_budget(), detail::no_facts(), Domain::Real);
270 al->accept(norm);
271 al = norm.get_result();
272 if (!al->is_zero()) {
273 auto s = get_node_sign(al);
274 if (s) {
275 return LMCAS::detail::make_node<NumberNode>(BigInt(*s));
276 }
277 return std::nullopt;
278 }
279 auto sign = determine_sign_near_point(arg, direction);
280 if (!sign) return std::nullopt;
281 return LMCAS::detail::make_node<NumberNode>(BigInt(*sign));
282}
283
284/**
285 * @brief 方向感知的绝对值函数极限计算.
286 */
287std::optional<std::shared_ptr<const SymbolicNode>> LimitVisitor::evaluate_abs_limit(
288 const std::shared_ptr<const SymbolicNode>& arg) {
289 LimitVisitor sv(var, point, direction, assumption_ctx_, context_);
290 arg->accept(sv);
291 auto al = sv.get_result();
292 if (!al) {
293 return std::nullopt;
294 }
295 NormalizationVisitor norm(*rewrite_budget(), detail::no_facts(), Domain::Real);
296 al->accept(norm);
297 al = norm.get_result();
298 if (is_inf(al)) {
299 std::vector<std::shared_ptr<const SymbolicNode>> inf_args;
301 }
302 auto s = get_node_sign(al);
303 if (s) {
304 if (*s >= 0) {
305 return al;
306 }
307 std::vector<std::shared_ptr<const SymbolicNode>> neg_ops = {LMCAS::detail::make_node<NumberNode>(BigInt(-1)), al};
308 auto neg_result = LMCAS::detail::make_node<MultiplyNode>(neg_ops);
309 neg_result->accept(norm);
310 return norm.get_result();
311 }
312 return std::nullopt;
313}
314
315
316std::optional<std::pair<Rational, int>> LimitVisitor::exact_leading_at_point(
317 const std::shared_ptr<const SymbolicNode>& expr) const {
318 charge();
319 detail::ExactBoundArithmetic arithmetic(*context_, "limit.leading_term");
320 auto p = symbolic_to_poly<Rational>(detail::make_expression_ptr(expr), var);
322 if (!p && p.error().code != CasErrc::UnsupportedExpression) { throw p.error(); }
323 if (!a && a.error().code != CasErrc::UnsupportedExpression) { throw a.error(); }
324 if (!a && !is_inf(point)) {
325 auto names = detail::all_variable_names(expr);
326 auto point_names = detail::all_variable_names(point);
327 names.insert(point_names.begin(), point_names.end());
328 std::string shifted_variable = "__limit_shift";
329 while (names.count(shifted_variable)) { shifted_variable += "_"; }
330 auto shifted = detail::make_expression_ptr(detail::substitute_node(expr, var, detail::node(SymbolicExpr::add(detail::make_expression_ptr(point), SymbolicExpr::variable(shifted_variable))), *rewrite_budget()));
331 if (shifted) { shifted = detail::make_expression_ptr(detail::expand_node(detail::node(shifted), *rewrite_budget())); }
332 if (!shifted) { return std::nullopt; }
333 p = symbolic_to_poly<Rational>(shifted, shifted_variable);
334 if (!p && p.error().code != CasErrc::UnsupportedExpression) { throw p.error(); }
336 }
337 if (!p || !a) { return std::nullopt; }
338 return exact_polynomial_leading(p.value(), a.value(), arithmetic);
339}
340
341bool LimitVisitor::analytic_at_point(const std::shared_ptr<const SymbolicNode>& expr) const {
342 if (!expr) { return false; }
343 if (std::dynamic_pointer_cast<const NumberNode>(expr) ||
344 std::dynamic_pointer_cast<const VariableNode>(expr)) { return true; }
345 if (auto sum = std::dynamic_pointer_cast<const AddNode>(expr)) {
346 return analytic_operands_at_point(sum->operands());
347 }
348 if (auto product = std::dynamic_pointer_cast<const MultiplyNode>(expr)) {
349 return analytic_operands_at_point(product->operands());
350 }
351 if (auto power = std::dynamic_pointer_cast<const PowerNode>(expr)) {
352 return analytic_power_at_point(*power, expr);
353 }
354 if (auto function = std::dynamic_pointer_cast<const FunctionNode>(expr)) {
355 return analytic_function_at_point(*function, expr);
356 }
357 return false;
358}
359
360std::optional<int> LimitVisitor::determine_sign_near_point(
361 const std::shared_ptr<const SymbolicNode>& expr, const std::string& dir) {
362 if (!expr) { return std::nullopt; }
363 if (is_limit_at_infinity() || is_limit_at_neg_infinity()) {
364 auto sign = rational_sign_at_infinity(expr);
365 if (sign) { return sign; }
366 }
367 if (auto leading = exact_leading_at_point(expr)) {
368 return directional_leading_sign(*leading, dir);
369 }
370 if (auto product = std::dynamic_pointer_cast<const MultiplyNode>(expr)) {
371 return product_sign_near_point(*product, dir);
372 }
373 if (auto power = std::dynamic_pointer_cast<const PowerNode>(expr)) {
374 auto sign = power_sign_near_point(*power, dir);
375 if (sign) { return sign; }
376 }
377 if (is_limit_at_infinity() || is_limit_at_neg_infinity()) {
378 auto sign = get_node_sign(eval_limit(expr));
379 return sign && *sign != 0 ? sign : std::nullopt;
380 }
381 auto limiting_sign = get_node_sign(eval_limit(expr));
382 if (limiting_sign && *limiting_sign != 0) { return limiting_sign; }
383 return analytic_sign_near_point(expr, dir);
384}
385bool LimitVisitor::condition_holds_near_point(
386 const std::shared_ptr<const SymbolicNode>& condition) {
387 auto result = condition_truth_near_point(condition);
388 return result && *result;
389}
390
391
392}
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
@ Not
一元逻辑非,right 为 nullptr。
乘法节点,表示多个操作数的乘积。
统一规范化数值幂。 精确操作数保留整数和有理数运算;近似求根要求指数精确等于 1/2。 有限浮点幂直接折叠,省去倒数中间值;非有限结果保留幂表达式。
分段函数节点,表示条件分支表达式.
一元多项式模板类
幂运算节点,表示 base^exponent。
CasError & error()
static Result success(T value)
常数及根式包围区间共用的内部算术策略。 保守界覆盖未约分 Rational 的交叉乘积及结果;受检入口统一捕获 CasError,仅返回完整结果。
极限访问器,通过代入和 L'Hôpital 法则计算极限。
std::optional< int > directional_leading_sign(const std::pair< Rational, int > &leading, const std::string &dir)
std::optional< bool > disjunctive_near_point_truth(std::optional< bool > left, std::optional< bool > right)
std::optional< bool > conjunctive_near_point_truth(std::optional< bool > left, std::optional< bool > right)
SymbolicNodePtr expand_node(const SymbolicNodePtr &node, 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
ExpressionResult simplify_expression(const ExprPtr &expression, ComputationContext &context)
const FactsQuery & no_facts() noexcept
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
RewriteBudget const FactsQuery & facts
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)
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
ExprResult integer(Integer)=delete
bool try_get_integer_value(const std::shared_ptr< const NumberNode > &node, BigInt &value)
const std::vector< FGLMPoly > const MonomialOrder & order
@ Rational
Rational numbers
@ Real
Real numbers
const std::string const LimitExprPtr LimitDirection direction
@ True
Property definitely holds
Result< T > extract_coeff_value(const std::shared_ptr< SymbolicExpr > &coefficient)
精确提取系数;不支持的值返回错误,保持数值完整。
const ExprPtr RelationOp op
__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
const ExprPtr & right
ExprResult relation(const ExprPtr &lhs, const ExprPtr &rhs, RelationOp op)
const std::string const ExprPtr & value
const ExprPtr & denominator
ExprResult function(const std::string &name, std::vector< ExprPtr > arguments)
符号表达式与一元多项式的转换。
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.