LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
limit_result.cpp
浏览该文件的文档.
1#include <stdexcept>
4#include "limit_result.hpp"
14#include "symbolic.hpp"
15#include <exception>
16
17namespace LMCAS {
18namespace {
19using NodePtr = std::shared_ptr<const SymbolicNode>;
25 "limit could not be proved in the supported domain", "limit");
26}
31 if (std::holds_alternative<PositiveInfinityLimit>(value)) return 1;
32 if (std::holds_alternative<NegativeInfinityLimit>(value)) return -1;
33 return 0;
34}
35bool has_infinity(const NodePtr& node) {
36 class Detector final : public detail::RecursiveSymbolicVisitor {
37 public:
38 bool found = false;
39 void visit(const FunctionNode& f) override {
40 if (f.type() == FunctionNode::FuncType::Infinity) found = true;
42 }
43 } detector;
44 if (node) node->accept(detector);
45 return detector.found;
46}
48 auto nz = detail::query_nonzero_value(node, facts, Domain::Real, context);
49 if (!nz) { return Result<int>::failure(nz.error()); }
50 if (nz.value() == Tribool::False) { return 0; }
51 auto positive = detail::query_positive_value(node, facts, context);
52 if (!positive) { return Result<int>::failure(positive.error()); }
53 if (positive.value() == Tribool::True) { return 1; }
54 auto nonnegative = detail::query_nonnegative_value(node, facts, context);
55 if (!nonnegative) { return Result<int>::failure(nonnegative.error()); }
56 if (nonnegative.value() == Tribool::False) { return -1; }
57 auto negative = facts.is_negative(node, context);
58 if (!negative) { return Result<int>::failure(negative.error()); }
59 if (negative.value() == Tribool::True) { return -1; }
60 return Result<int>::failure(CasErrc::Inconclusive, "finite sign is unknown", "limit");
61}
62LimitResult classify_limit_result(const NodePtr& node, const std::string& variable,
64
65LimitResult classify_infinite_sum(const AddNode& sum, const std::string& variable,
67 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
68 (context).limits().max_ast_nodes, "transform");
69 int sign = 0;
70 std::vector<NodePtr> finite_terms;
71 for (const auto& term : sum.operands()) {
72 auto value = classify_limit_result(term, variable, context, facts, domain);
73 if (!value) { return value; }
74 int next = infinity_sign(value.value().value);
75 if (sign && next && sign != next) { return unknown(); }
76 if (next) { sign = next; }
77 else { finite_terms.push_back(detail::node(std::get<FiniteLimit>(value.value().value).value)); }
78 }
79 if (sign) { return sign > 0 ? outcome(PositiveInfinityLimit{}) : outcome(NegativeInfinityLimit{}); }
80 auto finite = detail::make_expression_ptr(detail::normalize_node(detail::make_node<AddNode>(std::move(finite_terms)), transform_budget));
81 return finite ? classify_limit_result(detail::node(finite), variable, context, facts, domain) : unknown();
82} catch (const CasError& error) {
83 return LimitResult::failure(error);
84} catch (const std::bad_alloc&) {
85 return LimitResult::failure(CasErrc::ResourceLimit, "allocation failed", "classify_infinite_sum");
86} catch (const std::length_error& error) {
87 return LimitResult::failure(CasErrc::ResourceLimit, error.what(), "classify_infinite_sum");
88} catch (const std::exception& error) {
89 return LimitResult::failure(CasErrc::InternalInvariant, error.what(), "classify_infinite_sum");
90}
91
94 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
95 (context).limits().max_ast_nodes, "transform");
96 int sign = 1;
97 bool infinite = false;
98 std::vector<NodePtr> finite_factors;
99 for (const auto& factor : product.operands()) {
100 auto value = classify_limit_result(factor, variable, context, facts, domain);
101 if (!value) { return value; }
102 int next = infinity_sign(value.value().value);
103 if (next) { infinite = true; sign *= next; }
104 else { finite_factors.push_back(detail::node(std::get<FiniteLimit>(value.value().value).value)); }
105 }
106 if (infinite) {
107 for (const auto& factor : finite_factors) {
108 auto s = finite_sign(factor, facts, context);
109 if (!s) { return LimitResult::failure(s.error()); }
110 if (!s.value()) { return unknown(); }
111 sign *= s.value();
112 }
114 }
115 auto finite = detail::make_expression_ptr(detail::normalize_node(detail::make_node<MultiplyNode>(std::move(finite_factors)), transform_budget));
116 return finite ? classify_limit_result(detail::node(finite), variable, context, facts, domain) : unknown();
117} catch (const CasError& error) {
118 return LimitResult::failure(error);
119} catch (const std::bad_alloc&) {
120 return LimitResult::failure(CasErrc::ResourceLimit, "allocation failed", "classify_infinite_product");
121} catch (const std::length_error& error) {
122 return LimitResult::failure(CasErrc::ResourceLimit, error.what(), "classify_infinite_product");
123} catch (const std::exception& error) {
124 return LimitResult::failure(CasErrc::InternalInvariant, error.what(), "classify_infinite_product");
125}
126
127LimitResult classify_infinite_power(const PowerNode& power, const std::string& variable,
129 auto base = classify_limit_result(power.base(), variable, context, facts, domain);
130 if (!base) { return base; }
131 const int sign = infinity_sign(base.value().value);
132 BigInt integer;
133 auto number = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
135 bool integral = number && try_get_integer_value(number, integer);
136 if (!sign || !exponent || (sign <= 0 && !integral)) { return unknown(); }
137 if (exponent.value() < Rational(0)) { return outcome(FiniteLimit{SymbolicExpr::number(0)}); }
138 if (exponent.value() > Rational(0)) { return sign < 0 && integer.is_odd()
140 return unknown();
141}
142
145 if (function.arguments().size() != 1) { return unknown(); }
146 auto argument = classify_limit_result(function.arguments()[0], variable, context, facts, domain);
147 if (!argument) { return argument; }
148 int sign = infinity_sign(argument.value().value);
149 if (!sign) {
150 auto applied = detail::make_node<FunctionNode>(function.type(), std::vector<NodePtr>{
151 detail::node(std::get<FiniteLimit>(argument.value().value).value)});
152 return classify_limit_result(applied, variable, context, facts, domain);
153 }
154 switch (function.type()) {
156 return sign > 0 ? outcome(PositiveInfinityLimit{}) : outcome(FiniteLimit{SymbolicExpr::number(0)});
157 }
160 return sign > 0 ? outcome(PositiveInfinityLimit{}) : unknown();
161 }
163 return outcome(FiniteLimit{SymbolicExpr::multiply(SymbolicExpr::number(Rational(sign,2)),
166 }
167 default: { return unknown(); }
168 }
169}
170
171LimitResult classify_limit_result(const NodePtr& node, const std::string& variable,
173 auto step = context.consume_steps(1, "limit.classify");
174 if (!step) { return LimitResult::failure(step.error()); }
176 { return unknown(); }
177 if (auto function = std::dynamic_pointer_cast<const FunctionNode>(node)) {
179 }
180 if (has_infinity(node)) {
181 if (auto sum = std::dynamic_pointer_cast<const AddNode>(node)) {
182 return classify_infinite_sum(*sum, variable, context, facts, domain);
183 }
184 if (auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
185 return classify_infinite_product(*product, variable, context, facts, domain);
186 }
187 if (auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
188 return classify_infinite_power(*power, variable, context, facts, domain);
189 }
190 if (auto function = std::dynamic_pointer_cast<const FunctionNode>(node)) {
192 }
193 return unknown();
194 }
195 auto defined = detail::query_definedness(node, facts, domain, context);
196 if (!defined) { return LimitResult::failure(defined.error()); }
197 if (defined.value() != Tribool::True) { return unknown(); }
199}
200
202 const std::string& variable;
206
207 bool positive(const NodePtr& argument) const {
208 auto substituted = substitute_free(argument, variable, point);
209 auto value = detail::query_positive_value(substituted, facts, context);
210 if (!value) throw value.error();
211 return value.value() == Tribool::True;
212 }
213};
214
215bool elementary_continuous_structure(
216 const NodePtr& node, const ComplexContinuityPoint* complex_point = nullptr);
217
219 if (point) {
220 BigInt integer;
221 auto exponent = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
223 !point->positive(power.base())) { return false; }
224 }
225 return elementary_continuous_structure(power.base(), point) &&
227}
228
231 switch (type) {
232 case FunctionNode::FuncType::Sin: { return true; }
233 case FunctionNode::FuncType::Cos: { return true; }
234 case FunctionNode::FuncType::Tan: { return true; }
235 case FunctionNode::FuncType::Exp: { return true; }
236 case FunctionNode::FuncType::Abs: { return true; }
237 case FunctionNode::FuncType::Ln: { return !point || point->positive(argument); }
238 case FunctionNode::FuncType::Sqrt: { return !point || point->positive(argument); }
239 case FunctionNode::FuncType::ArcTan: { return point == nullptr; }
240 case FunctionNode::FuncType::ArcSin: { return point == nullptr; }
241 case FunctionNode::FuncType::ArcCos: { return point == nullptr; }
242 default: { return false; }
243 }
244}
246 if (std::dynamic_pointer_cast<const NumberNode>(node) ||
247 std::dynamic_pointer_cast<const VariableNode>(node)) { return true; }
248 if (auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
249 return power_continuous_structure(*power, point);
250 }
251 if (auto complex = std::dynamic_pointer_cast<const ComplexNode>(node)) {
254 }
255 const std::vector<NodePtr>* operands = nullptr;
256 if (auto sum = std::dynamic_pointer_cast<const AddNode>(node)) {
257 operands = &sum->operands();
258 } else if (auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
259 operands = &product->operands();
260 }
261 if (operands) {
262 for (const auto& operand : *operands) {
263 if (!elementary_continuous_structure(operand, point)) { return false; }
264 }
265 return true;
266 }
267 auto function = std::dynamic_pointer_cast<const FunctionNode>(node);
268 return function && function->arguments().size() == 1 &&
269 continuous_function_branch(function->type(), function->arguments()[0], point) &&
271}
272
274public:
275 SideBranchRewriter(LimitVisitor& visitor, const FactsQuery& facts, ComputationContext& context)
276 : visitor_(visitor), facts_(facts), context_(context) {}
277 void visit(const PiecewiseNode& node) override {
278 auto selected = visitor_.select_piecewise_near_point(node);
279 set_result(selected ? rewrite(selected) : current());
280 }
281 void visit(const PowerNode& node) override {
282 bool changed = false;
283 auto base = rewrite_child(node.base(), changed);
284 auto exponent = rewrite_child(node.exponent(), changed);
285 const auto square = std::dynamic_pointer_cast<const PowerNode>(base);
286 if (square) {
288 auto inner = extract_coeff_value<Rational>(detail::make_expression_ptr(square->exponent()));
289 if (outer && inner && outer.value() == Rational(1, 2) &&
290 inner.value() == Rational(2) && rewrite_absolute(square->base())) return;
291 }
293 }
294 void visit(const FunctionNode& node) override {
295 bool changed = false;
296 auto args = rewrite_children(node.arguments(), changed);
297 if (args.size() == 1) {
298 if (node.type() == FunctionNode::FuncType::Abs && rewrite_absolute(args[0])) { return; }
299 if (node.type() == FunctionNode::FuncType::Sqrt && rewrite_square_absolute(args[0])) { return; }
300 }
301 set_result(changed ? detail::make_node<FunctionNode>(node.type(), std::move(args)) : current());
302 }
303private:
304 bool rewrite_square_absolute(const NodePtr& argument) {
305 auto square = std::dynamic_pointer_cast<const PowerNode>(argument);
306 if (!square) { return false; }
308 return exponent && exponent.value() == Rational(2) && rewrite_absolute(square->base());
309 }
310 bool rewrite_absolute(const NodePtr& argument) {
311 auto real = detail::query_real_value(argument, facts_, context_);
312 if (!real) throw real.error();
313 auto sign = real.value() == Tribool::True ? visitor_.sign_near_point(argument) : std::nullopt;
314 if (!sign) return false;
315 set_result(*sign < 0 ? detail::node(SymbolicExpr::multiply(
316 SymbolicExpr::number(-1), detail::make_expression_ptr(argument))) : argument);
317 return true;
318 }
319 LimitVisitor& visitor_;
322};
323
324std::optional<LimitResult> scaled_one_sided_limit(const MultiplyNode& product,
325 const std::string& variable, const LimitExprPtr& point, LimitDirection direction,
326 ComputationContext& context, const FactsQuery& facts) {
327 std::shared_ptr<const SymbolicNode> varying;
328 bool invertible_scale = true;
329 for (const auto& factor : product.operands()) {
331 if (varying) { invertible_scale = false; break; }
332 varying = factor;
333 } else {
334 auto nonzero = detail::query_nonzero_value(factor, facts, Domain::Real, context);
335 if (!nonzero) { return LimitResult::failure(nonzero.error()); }
336 if (nonzero.value() != Tribool::True) { invertible_scale = false; break; }
337 }
338 }
339 if (invertible_scale && varying) {
341 if (!inner && inner.error().code != CasErrc::Inconclusive) { return inner; }
342 if (inner && std::holds_alternative<LimitDoesNotExist>(inner.value().value)) { return inner; }
343 }
344 return std::nullopt;
345}
346
347std::optional<LimitResult> oscillatory_one_sided_limit(const NodePtr& node,
348 const std::string& variable, const LimitExprPtr& point, LimitDirection direction,
349 ComputationContext& context, const FactsQuery& facts, LimitVisitor& visitor) {
350 auto f = std::dynamic_pointer_cast<const FunctionNode>(node);
351 if (!f || f->arguments().size() != 1) { return std::nullopt; }
352 if (f->type() != FunctionNode::FuncType::Sin && f->type() != FunctionNode::FuncType::Cos) {
353 return std::nullopt;
354 }
355 auto argument = detail::make_expression_ptr(f->arguments()[0]);
356 auto inner = limit_checked(argument, variable, point, direction, context);
357 if (!inner && inner.error().code != CasErrc::Inconclusive) { return inner; }
358 if (!inner || !infinity_sign(inner.value().value) || !elementary_continuous_structure(f->arguments()[0])) {
359 return std::nullopt;
360 }
361 auto domain = detail::domain_constraints(f->arguments()[0], facts, Domain::Real, context);
362 if (!domain) { return LimitResult::failure(domain.error()); }
363 if (!domain.value()) { return std::nullopt; }
364 for (const auto& condition : *domain.value()) {
365 if (!visitor.condition_holds_near_point(detail::node(condition))) { return std::nullopt; }
366 }
367 return outcome(LimitDoesNotExist{});
368}
369
370LimitResult evaluate_one_sided_node(const NodePtr& node, const std::string& variable,
372 const AssumptionContext& local, const FactsQuery& facts, LimitVisitor& visitor) try {
374 { return classify_limit_result(node, variable, context, facts); }
375 auto original_domain = detail::domain_constraints(node, facts, Domain::Real, context);
376 if (!original_domain) { return LimitResult::failure(original_domain.error()); }
377 if (!original_domain.value()) { return unknown(); }
378 for (const auto& condition : *original_domain.value())
379 { if (!visitor.condition_holds_near_point(detail::node(condition))) { return unknown(); } }
380 if (auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
381 auto scaled = scaled_one_sided_limit(*product, variable, point, direction, context, facts);
382 if (scaled) { return std::move(*scaled); }
383 }
384 auto oscillatory = oscillatory_one_sided_limit(node, variable, point, direction, context, facts, visitor);
385 if (oscillatory) { return std::move(*oscillatory); }
386 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
387 context.limits().max_ast_nodes, "limit");
388 detail::AssumptionFacts local_facts(local);
389 auto prepared = detail::normalize_node(node, budget, local_facts);
390 prepared->accept(visitor);
391 return classify_limit_result(visitor.get_result(), variable, context, facts);
392} catch (const CasError& error) {
393 return LimitResult::failure(error);
394} catch (const std::bad_alloc&) {
395 return LimitResult::failure(CasErrc::ResourceLimit, "allocation failed", "evaluate_one_sided_node");
396} catch (const std::length_error& error) {
397 return LimitResult::failure(CasErrc::ResourceLimit, error.what(), "evaluate_one_sided_node");
398} catch (const std::exception& error) {
399 return LimitResult::failure(CasErrc::InternalInvariant, error.what(), "evaluate_one_sided_node");
400}
401
402
403LimitResult limit_one_sided_checked(const LimitExprPtr& expression, const std::string& variable,
405 AssumptionContext local = context.assumptions() ? *context.assumptions() : AssumptionContext{};
406 auto real_variable = local.assume_domain_checked(variable, Domain::Real);
407 if (!real_variable) { return assumption_limit_failure(real_variable.error()); }
409 const int sign = std::dynamic_pointer_cast<const FunctionNode>(detail::node(point)) ? 1 : -1;
410 auto tail = local.assume_sign_checked(variable, sign < 0 ? Sign::Negative : Sign::Positive);
411 if (!tail) { return assumption_limit_failure(tail.error()); }
412 }
413 detail::AssumptionFacts facts(local);
414 const char* token = direction == LimitDirection::FromBelow ? "-" : "+";
415 LimitVisitor visitor(variable, detail::node(point), token, &local, &context);
417 auto sign = visitor.sign_near_point(detail::make_node<VariableNode>(variable));
418 if (sign && *sign != 0) {
419 auto side = local.assume_sign_checked(variable, *sign < 0 ? Sign::Negative : Sign::Positive);
420 if (!side) { return assumption_limit_failure(side.error()); }
421 }
422 }
423 SideBranchRewriter branches(visitor, facts, context);
424 auto node = branches.rewrite(detail::node(expression));
425 return evaluate_one_sided_node(node, variable, point, direction, context, local, facts, visitor);
426}
427
430 if (std::holds_alternative<LimitDoesNotExist>(left) || std::holds_alternative<LimitDoesNotExist>(right))
431 return outcome(LimitDoesNotExist{});
432 int ls = infinity_sign(left), rs = infinity_sign(right);
433 if (ls || rs) {
434 if (ls == rs) return outcome(left);
435 return outcome(LimitDoesNotExist{});
436 }
437 const auto& lv = std::get<FiniteLimit>(left).value;
438 const auto& rv = std::get<FiniteLimit>(right).value;
440 if (!equal) return LimitResult::failure(equal.error());
441 if (equal.value() == Tribool::True) return outcome(left);
442 if (equal.value() == Tribool::False) return outcome(LimitDoesNotExist{});
443 return unknown();
444}
445
446class ComplexPuncturedFacts final : public FactsQuery {
447public:
448 ComplexPuncturedFacts(const FactsQuery& base, const std::vector<NodePtr>& nonzero)
449 : base_(base), nonzero_(nonzero) {}
450 Result<Tribool> is_positive(const NodePtr& node, ComputationContext& context) const override { return base_.is_positive(node, context); }
451 Result<Tribool> is_negative(const NodePtr& node, ComputationContext& context) const override { return base_.is_negative(node, context); }
452 Result<Tribool> is_nonnegative(const NodePtr& node, ComputationContext& context) const override { return base_.is_nonnegative(node, context); }
453 Result<Tribool> is_real(const NodePtr& node, ComputationContext& context) const override { return base_.is_real(node, context); }
455 auto access = context.consume_steps(0, "facts.complex_punctured");
456 if (!access) { return Result<Tribool>::failure(access.error()); }
457 for (const auto& proven : nonzero_) {
458 auto step = context.consume_steps(1, "facts.complex_punctured");
459 if (!step) { return Result<Tribool>::failure(step.error()); }
460 if (node->equals(*proven)) { return Tribool::True; }
461 }
462 return base_.is_nonzero(node, context);
463 }
464private:
466 const std::vector<NodePtr>& nonzero_;
467};
468
470 const FactsQuery& facts, ComputationContext& context, std::vector<NodePtr>& nonzero_bases) try {
471 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
472 (context).limits().max_ast_nodes, "transform");
473 auto difference = SymbolicExpr::add(detail::make_expression_ptr(relation.left()),
474 SymbolicExpr::multiply(SymbolicExpr::number(-1), detail::make_expression_ptr(relation.right())));
475 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(difference, variable);
476 if (!polynomial) {
477 if (polynomial.error().code == CasErrc::UnsupportedExpression) { return false; }
478 return Result<bool>::failure(polynomial.error());
479 }
480 bool nonzero = false;
481 for (const auto& coefficient : polynomial.value().coeffs) {
482 auto step = context.consume_steps(1, "limit.complex_domain");
483 if (!step) { return Result<bool>::failure(step.error()); }
484 auto defined = detail::query_definedness(detail::node(coefficient.val), facts, Domain::Complex, context);
485 if (!defined) { return Result<bool>::failure(defined.error()); }
486 if (defined.value() != Tribool::True) { return false; }
487 auto value = detail::query_nonzero_value(detail::node(coefficient.val), facts, Domain::Complex, context);
488 if (!value) { return Result<bool>::failure(value.error()); }
489 nonzero |= value.value() == Tribool::True;
490 }
491 if (!nonzero) { return false; }
492 auto normalized = detail::make_expression_ptr(detail::normalize_node(detail::node(difference), transform_budget));
493 if (!normalized) { return false; }
494 nonzero_bases.push_back(detail::node(normalized));
495 return true;
496} catch (const CasError& error) {
497 return Result<bool>::failure(error);
498} catch (const std::bad_alloc&) {
499 return Result<bool>::failure(CasErrc::ResourceLimit, "allocation failed", "complex_polynomial_nonzero");
500} catch (const std::length_error& error) {
501 return Result<bool>::failure(CasErrc::ResourceLimit, error.what(), "complex_polynomial_nonzero");
502} catch (const std::exception& error) {
503 return Result<bool>::failure(CasErrc::InternalInvariant, error.what(), "complex_polynomial_nonzero");
504}
505
506Result<bool> complex_punctured_condition(const NodePtr& condition, const std::string& variable,
507 const FactsQuery& facts, ComputationContext& context, std::vector<NodePtr>& nonzero_bases) {
508 auto step = context.consume_steps(1, "limit.complex_condition");
509 if (!step) { return Result<bool>::failure(step.error()); }
510 auto logical = std::dynamic_pointer_cast<const LogicalNode>(condition);
511 if (logical && logical->op() == LogicalNode::Op::Or) {
512 auto left = complex_punctured_condition(logical->left(), variable, facts, context, nonzero_bases);
513 if (!left || left.value()) { return left; }
514 return complex_punctured_condition(logical->right(), variable, facts, context, nonzero_bases);
515 }
516 auto relation = std::dynamic_pointer_cast<const RelationalNode>(condition);
517 if (!relation || relation->op() != RelationalNode::Op::NEQ) { return false; }
518 return complex_polynomial_nonzero(*relation, variable, facts, context, nonzero_bases);
519}
520
522 const FactsQuery& facts, ComputationContext& context, std::vector<NodePtr>& nonzero_bases) {
524 if (!domain) { return Result<bool>::failure(domain.error()); }
525 if (!domain.value()) { return false; }
526 for (const auto& condition : *domain.value()) {
527 auto proven = complex_punctured_condition(detail::node(condition), variable, facts, context, nonzero_bases);
528 if (!proven || !proven.value()) { return proven; }
529 }
530 return true;
531}
532
533Result<void> validate_limit_input(const LimitExprPtr& expression, const std::string& variable,
535 if (!expression || !point || !detail::node(expression) || !detail::node(point) || variable.empty()) {
537 "limit requires an expression, named variable, and point", "limit");
538 }
541 "limit point depends on the approaching variable", "limit");
542 }
544 return Result<void>::failure(CasErrc::InvalidArgument, "limit domain must be Real or Complex", "limit");
545 }
547 return Result<void>::failure(CasErrc::InvalidArgument, "complex limits have no ordered sides", "limit");
548 }
549 return Result<void>::success();
550}
551
552LimitResult limit_complex_at_point(const LimitExprPtr& expression, const std::string& variable,
553 const LimitExprPtr& point, ComputationContext& context, const FactsQuery& facts) try {
554 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
555 (context).limits().max_ast_nodes, "transform");
556 if (has_infinity(detail::node(expression))) { return unknown(); }
559 }
560 ComplexContinuityPoint continuity_point{variable, detail::node(point), facts, context};
561 if (!elementary_continuous_structure(detail::node(expression), &continuity_point)) { return unknown(); }
562 std::vector<NodePtr> nonzero_bases;
563 auto domain_proof = complex_punctured_domain(detail::node(expression), variable, facts, context, nonzero_bases);
564 if (!domain_proof) { return LimitResult::failure(domain_proof.error()); }
565 if (!domain_proof.value()) { return unknown(); }
566 ComplexPuncturedFacts punctured(facts, nonzero_bases);
567 NormalizationVisitor normalizer(transform_budget, punctured, Domain::Complex);
568 detail::node(expression)->accept(normalizer);
569 auto prepared = normalizer.get_result();
570 if (!prepared) { return unknown(); }
571 auto substituted = substitute_free(prepared, variable, detail::node(point), &transform_budget);
572 if (!substituted) { return unknown(); }
573 auto defined = detail::query_definedness(substituted, facts, Domain::Complex, context);
574 if (!defined) { return LimitResult::failure(defined.error()); }
575 if (defined.value() != Tribool::True) { return unknown(); }
576 substituted->accept(normalizer);
577 auto value = normalizer.get_result();
579} catch (const CasError& error) {
580 return LimitResult::failure(error);
581} catch (const std::bad_alloc&) {
582 return LimitResult::failure(CasErrc::ResourceLimit, "allocation failed", "limit_complex_at_point");
583} catch (const std::length_error& error) {
584 return LimitResult::failure(CasErrc::ResourceLimit, error.what(), "limit_complex_at_point");
585} catch (const std::exception& error) {
586 return LimitResult::failure(CasErrc::InternalInvariant, error.what(), "limit_complex_at_point");
587}
588
589LimitResult limit_real_at_point(const LimitExprPtr& expression, const std::string& variable,
591 if (direction != LimitDirection::Both || endpoint_sign) {
593 }
595 if (!left && left.error().code != CasErrc::Inconclusive) { return left; }
597 if (!right && right.error().code != CasErrc::Inconclusive) { return right; }
598 if ((left && std::holds_alternative<LimitDoesNotExist>(left.value().value)) ||
599 (right && std::holds_alternative<LimitDoesNotExist>(right.value().value))) {
600 return outcome(LimitDoesNotExist{});
601 }
602 if (!left || !right) { return unknown(); }
603 return combine_one_sided_limits(left.value().value, right.value().value, context);
604}
605
606LimitResult limit_at_checked_point(const LimitExprPtr& expression, const std::string& variable,
608 const FactsQuery& facts, Domain domain) try {
609 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
610 (context).limits().max_ast_nodes, "transform");
612 if (!endpoint) { return endpoint; }
613 const int endpoint_sign = infinity_sign(endpoint.value().value);
614 if (domain == Domain::Complex && endpoint_sign) { return unknown(); }
615 auto canonical_point = endpoint_sign ? SymbolicExpr::infinity(endpoint_sign)
616 : detail::make_expression_ptr(detail::normalize_node(detail::node(std::get<FiniteLimit>(endpoint.value().value).value), transform_budget));
617 if (!canonical_point) { return unknown(); }
618 if (domain == Domain::Complex) {
619 return limit_complex_at_point(expression, variable, canonical_point, context, facts);
620 }
621 return limit_real_at_point(expression, variable, canonical_point, direction, endpoint_sign, context);
622} catch (const CasError& error) {
623 return LimitResult::failure(error);
624} catch (const std::bad_alloc&) {
625 return LimitResult::failure(CasErrc::ResourceLimit, "allocation failed", "limit_at_checked_point");
626} catch (const std::length_error& error) {
627 return LimitResult::failure(CasErrc::ResourceLimit, error.what(), "limit_at_checked_point");
628} catch (const std::exception& error) {
629 return LimitResult::failure(CasErrc::InternalInvariant, error.what(), "limit_at_checked_point");
630}
631}
632
633LimitResult limit_checked(const LimitExprPtr& expression, const std::string& variable,
635 auto valid = validate_limit_input(expression, variable, point, direction, domain);
636 if (!valid) { return LimitResult::failure(valid.error()); }
637 auto step = context.consume_steps(1, "limit");
638 if (!step) { return LimitResult::failure(step.error()); }
639 try {
640 std::optional<detail::AssumptionFacts> assumed;
641 if (context.assumptions()) { assumed.emplace(*context.assumptions()); }
642 const auto& facts = assumed ? static_cast<const FactsQuery&>(*assumed) : detail::no_facts();
643 return limit_at_checked_point(expression, variable, point, direction, context, facts, domain);
644 } catch (const CasError& error) {
645 return LimitResult::failure(error);
646 } catch (const std::bad_alloc&) {
647 return LimitResult::failure(CasErrc::ResourceLimit, "limit allocation failed", "limit");
648 } catch (const std::exception& error) {
649 return LimitResult::failure(CasErrc::InternalInvariant, error.what(), "limit");
650 }
651}
659 auto result = limit_checked(expression, variable, point, direction, context, domain);
660 if (!result) return LimitExpressionResult::failure(result.error());
661 const auto& value = result.value().value;
662 if (auto finite = std::get_if<FiniteLimit>(&value)) return LimitExpressionResult::success(finite->value);
663 if (int sign = infinity_sign(value)) return LimitExpressionResult::success(SymbolicExpr::infinity(sign));
664 return LimitExpressionResult::failure(CasErrc::Inconclusive, "limit does not exist", "limit");
665}
671}
加法节点,表示多个操作数的求和。
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
FuncType type() const noexcept
@ Sqrt
对数、绝对值、平方根。
乘法节点,表示多个操作数的乘积。
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
统一规范化数值幂。 精确操作数保留整数和有理数运算;近似求根要求指数精确等于 1/2。 有限浮点幂直接折叠,省去倒数中间值;非有限结果保留幂表达式。
std::shared_ptr< const SymbolicNode > get_result() const
分段函数节点,表示条件分支表达式.
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(Verified< LimitOutcome > value)
virtual void accept(LMCAS::detail::SymbolicVisitor &visitor) const =0
Result< Tribool > is_nonzero(const NodePtr &node, ComputationContext &context) const override
Result< Tribool > is_positive(const NodePtr &node, ComputationContext &context) const override
ComplexPuncturedFacts(const FactsQuery &base, const std::vector< NodePtr > &nonzero)
Result< Tribool > is_real(const NodePtr &node, ComputationContext &context) const override
Result< Tribool > is_negative(const NodePtr &node, ComputationContext &context) const override
Result< Tribool > is_nonnegative(const NodePtr &node, ComputationContext &context) const override
SideBranchRewriter(LimitVisitor &visitor, const FactsQuery &facts, ComputationContext &context)
void visit(const NumberNode &) override
void set_result(SymbolicNodePtr result)
SymbolicNodePtr rewrite(const SymbolicNodePtr &node)
SymbolicNodePtr rewrite_child(const SymbolicNodePtr &child, bool &changed)
std::vector< SymbolicNodePtr > rewrite_children(const Range &children, bool &changed)
SymbolicNodePtr current() const noexcept
极限访问器,通过代入和 L'Hôpital 法则计算极限。
Result< void > validate_limit_input(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, Domain domain)
LimitResult classify_infinite_product(const MultiplyNode &product, const std::string &variable, ComputationContext &context, const FactsQuery &facts, Domain domain)
LimitResult classify_limit_result(const NodePtr &node, const std::string &variable, ComputationContext &context, const FactsQuery &facts, Domain domain=Domain::Real)
bool elementary_continuous_structure(const NodePtr &node, const ComplexContinuityPoint *complex_point=nullptr)
LimitResult classify_infinite_power(const PowerNode &power, const std::string &variable, ComputationContext &context, const FactsQuery &facts, Domain domain)
LimitResult limit_real_at_point(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, int endpoint_sign, ComputationContext &context)
bool continuous_function_branch(FunctionNode::FuncType type, const NodePtr &argument, const ComplexContinuityPoint *point)
LimitResult evaluate_one_sided_node(const NodePtr &node, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, const AssumptionContext &local, const FactsQuery &facts, LimitVisitor &visitor)
Result< int > finite_sign(const NodePtr &node, const FactsQuery &facts, ComputationContext &context)
Result< bool > complex_punctured_condition(const NodePtr &condition, const std::string &variable, const FactsQuery &facts, ComputationContext &context, std::vector< NodePtr > &nonzero_bases)
LimitResult assumption_limit_failure(const CasError &error)
LimitResult limit_complex_at_point(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, ComputationContext &context, const FactsQuery &facts)
LimitResult classify_infinite_function(const FunctionNode &function, const std::string &variable, ComputationContext &context, const FactsQuery &facts, Domain domain)
bool power_continuous_structure(const PowerNode &power, const ComplexContinuityPoint *point)
Result< bool > complex_polynomial_nonzero(const RelationalNode &relation, const std::string &variable, const FactsQuery &facts, ComputationContext &context, std::vector< NodePtr > &nonzero_bases)
LimitResult limit_one_sided_checked(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context)
std::optional< LimitResult > scaled_one_sided_limit(const MultiplyNode &product, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, const FactsQuery &facts)
LimitResult limit_at_checked_point(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, const FactsQuery &facts, Domain domain)
Result< bool > complex_punctured_domain(const NodePtr &node, const std::string &variable, const FactsQuery &facts, ComputationContext &context, std::vector< NodePtr > &nonzero_bases)
std::shared_ptr< const SymbolicNode > NodePtr
LimitResult combine_one_sided_limits(const LimitOutcome &left, const LimitOutcome &right, ComputationContext &context)
LimitResult classify_infinite_sum(const AddNode &sum, const std::string &variable, ComputationContext &context, const FactsQuery &facts, Domain domain)
std::optional< LimitResult > oscillatory_one_sided_limit(const NodePtr &node, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, const FactsQuery &facts, LimitVisitor &visitor)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< Tribool > query_nonzero_value(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
bool contains_node_type(const SymbolicNodePtr &root)
const FactsQuery & no_facts() noexcept
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
Result< Tribool > compare_pointwise_values(const ExprPtr &left, const ExprPtr &right, ComputationContext &context, Domain domain)
在给定假设的 Real 或 Complex 域内比较逐点值,并保留错误。 True/False 分别证明值处处有定义且相等/不等;定义性未证实也归为 Unknown, 仅有非恒等残差不足以判定 Fals...
std::shared_ptr< const Node > make_node(Args &&... args)
Result< std::optional< std::vector< std::shared_ptr< SymbolicExpr > > > > domain_constraints(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
Result< Tribool > query_real_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
Result< Tribool > query_nonnegative_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
Result< Tribool > query_positive_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
const std::string const LimitExprPtr & point
const std::string & variable
LimitResult limit_checked(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, Domain domain)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult integer(Integer)=delete
bool try_get_integer_value(const std::shared_ptr< const NumberNode > &node, BigInt &value)
ExprResult real(const ExprPtr &expression, ComputationContext &context)
Domain
Mathematical domain hierarchy (least specific → most specific). Complex ⊃ Real ⊃ Algebraic ⊃ Rational...
@ Complex
Complex numbers (default, least specific)
@ Real
Real numbers
const std::string const LimitExprPtr LimitDirection direction
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
Result< LimitExprPtr > LimitExpressionResult
@ True
Property definitely holds
@ False
Property definitely does not hold
Result< T > extract_coeff_value(const std::shared_ptr< SymbolicExpr > &coefficient)
精确提取系数;不支持的值返回错误,保持数值完整。
LimitExpressionResult limit_expression_checked(const LimitExprPtr &expression, const std::string &variable, const LimitExprPtr &point, LimitDirection direction, ComputationContext &context, Domain domain)
std::variant< FiniteLimit, PositiveInfinityLimit, NegativeInfinityLimit, LimitDoesNotExist > LimitOutcome
ExprResult complex(ExprPtr real, ExprPtr imag)
const ExprPtr & exponent
const NumberDomainSet & domain
Result< Verified< LimitOutcome > > LimitResult
const ExprPtr & right
ExprResult relation(const ExprPtr &lhs, const ExprPtr &rhs, RelationOp op)
const std::string const ExprPtr & value
int infinity_sign(const SymbolicExpr &expression, ComputationContext &context)
LimitDirection
Direction from which a limit approaches its point.
std::shared_ptr< SymbolicExpr > LimitExprPtr
ExprResult function(const std::string &name, std::vector< ExprPtr > arguments)
detail::SymbolicNodePtr substitute_free(const detail::SymbolicNodePtr &expression, const std::string &variable, const detail::SymbolicNodePtr &replacement, detail::RewriteBudget *budget)
已证在请求的实数或复数域内有定义的有限值。 表达式树中所有极限均已求定,扩展实数运算均有确定结果。
符号规范化访问器接口。
符号表达式与一元多项式的转换。
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
内部 AST 定义。