19using NodePtr = std::shared_ptr<const SymbolicNode>;
25 "limit could not be proved in the supported domain",
"limit");
31 if (std::holds_alternative<PositiveInfinityLimit>(
value))
return 1;
32 if (std::holds_alternative<NegativeInfinityLimit>(
value))
return -1;
44 if (node) node->
accept(detector);
45 return detector.found;
57 auto negative = facts.is_negative(node,
context);
68 (
context).limits().max_ast_nodes,
"transform");
70 std::vector<NodePtr> finite_terms;
71 for (
const auto& term : sum.
operands()) {
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)); }
84}
catch (
const std::bad_alloc&) {
86}
catch (
const std::length_error& error) {
88}
catch (
const std::exception& error) {
95 (
context).limits().max_ast_nodes,
"transform");
97 bool infinite =
false;
98 std::vector<NodePtr> finite_factors;
99 for (
const auto& factor : product.
operands()) {
103 if (next) { infinite =
true; sign *= next; }
104 else { finite_factors.push_back(
detail::node(std::get<FiniteLimit>(
value.value().value).value)); }
107 for (
const auto& factor : finite_factors) {
110 if (!s.value()) {
return unknown(); }
119}
catch (
const std::bad_alloc&) {
121}
catch (
const std::length_error& error) {
123}
catch (
const std::exception& error) {
130 if (!base) {
return base; }
133 auto number = std::dynamic_pointer_cast<const NumberNode>(power.
exponent());
136 if (!sign || !
exponent || (sign <= 0 && !integral)) {
return unknown(); }
138 if (
exponent.value() > Rational(0)) {
return sign < 0 &&
integer.is_odd()
147 if (!argument) {
return argument; }
151 detail::node(std::get<FiniteLimit>(argument.value().value).value)});
163 return outcome(
FiniteLimit{SymbolicExpr::multiply(SymbolicExpr::number(Rational(sign,2)),
173 auto step =
context.consume_steps(1,
"limit.classify");
177 if (
auto function = std::dynamic_pointer_cast<const FunctionNode>(node)) {
181 if (
auto sum = std::dynamic_pointer_cast<const AddNode>(node)) {
184 if (
auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
187 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
190 if (
auto function = std::dynamic_pointer_cast<const FunctionNode>(node)) {
215bool elementary_continuous_structure(
216 const NodePtr& node,
const ComplexContinuityPoint* complex_point =
nullptr);
221 auto exponent = std::dynamic_pointer_cast<const NumberNode>(power.
exponent());
223 !
point->positive(power.
base())) {
return false; }
242 default: {
return false; }
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)) {
251 if (
auto complex = std::dynamic_pointer_cast<const ComplexNode>(node)) {
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();
262 for (
const auto& operand : *operands) {
267 auto function = std::dynamic_pointer_cast<const FunctionNode>(node);
278 auto selected =
visitor_.select_piecewise_near_point(node);
282 bool changed =
false;
285 const auto square = std::dynamic_pointer_cast<const PowerNode>(base);
289 if (outer && inner && outer.value() == Rational(1, 2) &&
295 bool changed =
false;
297 if (args.size() == 1) {
305 auto square = std::dynamic_pointer_cast<const PowerNode>(argument);
306 if (!square) {
return false; }
314 if (!sign)
return false;
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; }
336 if (nonzero.value() !=
Tribool::True) { invertible_scale =
false;
break; }
339 if (invertible_scale && varying) {
342 if (inner && std::holds_alternative<LimitDoesNotExist>(inner.value().value)) {
return inner; }
350 auto f = std::dynamic_pointer_cast<const FunctionNode>(node);
351 if (!f || f->arguments().size() != 1) {
return std::nullopt; }
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; }
372 const AssumptionContext& local,
const FactsQuery& facts, LimitVisitor& visitor)
try {
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)) {
382 if (scaled) {
return std::move(*scaled); }
385 if (oscillatory) {
return std::move(*oscillatory); }
387 context.limits().max_ast_nodes,
"limit");
390 prepared->accept(visitor);
394}
catch (
const std::bad_alloc&) {
396}
catch (
const std::length_error& error) {
398}
catch (
const std::exception& error) {
405 AssumptionContext local =
context.assumptions() ? *
context.assumptions() : AssumptionContext{};
409 const int sign = std::dynamic_pointer_cast<const FunctionNode>(
detail::node(
point)) ? 1 : -1;
418 if (sign && *sign != 0) {
430 if (std::holds_alternative<LimitDoesNotExist>(left) || std::holds_alternative<LimitDoesNotExist>(
right))
434 if (ls == rs)
return outcome(left);
437 const auto& lv = std::get<FiniteLimit>(left).value;
438 const auto& rv = std::get<FiniteLimit>(
right).value;
455 auto access =
context.consume_steps(0,
"facts.complex_punctured");
457 for (
const auto& proven :
nonzero_) {
458 auto step =
context.consume_steps(1,
"facts.complex_punctured");
472 (
context).limits().max_ast_nodes,
"transform");
475 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(difference,
variable);
480 bool nonzero =
false;
481 for (
const auto& coefficient : polynomial.value().coeffs) {
482 auto step =
context.consume_steps(1,
"limit.complex_domain");
491 if (!nonzero) {
return false; }
493 if (!normalized) {
return false; }
498}
catch (
const std::bad_alloc&) {
500}
catch (
const std::length_error& error) {
502}
catch (
const std::exception& error) {
508 auto step =
context.consume_steps(1,
"limit.complex_condition");
510 auto logical = std::dynamic_pointer_cast<const LogicalNode>(condition);
513 if (!left || left.value()) {
return left; }
516 auto relation = std::dynamic_pointer_cast<const RelationalNode>(condition);
517 if (!
relation ||
relation->op() != RelationalNode::Op::NEQ) {
return false; }
525 if (!
domain.value()) {
return false; }
526 for (
const auto& condition : *
domain.value()) {
528 if (!proven || !proven.value()) {
return proven; }
537 "limit requires an expression, named variable, and point",
"limit");
541 "limit point depends on the approaching variable",
"limit");
555 (
context).limits().max_ast_nodes,
"transform");
562 std::vector<NodePtr> nonzero_bases;
565 if (!domain_proof.value()) {
return unknown(); }
570 if (!prepared) {
return unknown(); }
572 if (!substituted) {
return unknown(); }
576 substituted->accept(normalizer);
581}
catch (
const std::bad_alloc&) {
583}
catch (
const std::length_error& error) {
585}
catch (
const std::exception& error) {
598 if ((left && std::holds_alternative<LimitDoesNotExist>(left.value().value)) ||
599 (
right && std::holds_alternative<LimitDoesNotExist>(
right.value().value))) {
610 (
context).limits().max_ast_nodes,
"transform");
612 if (!endpoint) {
return endpoint; }
613 const int endpoint_sign =
infinity_sign(endpoint.value().value);
615 auto canonical_point = endpoint_sign ? SymbolicExpr::infinity(endpoint_sign)
617 if (!canonical_point) {
return unknown(); }
624}
catch (
const std::bad_alloc&) {
626}
catch (
const std::length_error& error) {
628}
catch (
const std::exception& error) {
637 auto step =
context.consume_steps(1,
"limit");
640 std::optional<detail::AssumptionFacts> assumed;
641 if (
context.assumptions()) { assumed.emplace(*
context.assumptions()); }
646 }
catch (
const std::bad_alloc&) {
648 }
catch (
const std::exception& error) {
661 const auto&
value = result.value().value;
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
FuncType type() const noexcept
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
统一规范化数值幂。 精确操作数保留整数和有理数运算;近似求根要求指数精确等于 1/2。 有限浮点幂直接折叠,省去倒数中间值;非有限结果保留幂表达式。
std::shared_ptr< const SymbolicNode > get_result() const
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
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)
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
bool rewrite_absolute(const NodePtr &argument)
SideBranchRewriter(LimitVisitor &visitor, const FactsQuery &facts, ComputationContext &context)
ComputationContext & context_
void visit(const PiecewiseNode &node) override
const FactsQuery & facts_
void visit(const FunctionNode &node) override
void visit(const PowerNode &node) override
bool rewrite_square_absolute(const NodePtr &argument)
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 法则计算极限。
bool has_infinity(const NodePtr &node)
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)
int infinity_sign(const LimitOutcome &value)
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)
LimitResult outcome(LimitOutcome value)
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)
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 NumberDomainSet & domain
Result< Verified< LimitOutcome > > LimitResult
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)
已证在请求的实数或复数域内有定义的有限值。 表达式树中所有极限均已求定,扩展实数运算均有确定结果。
const std::string & variable
bool positive(const NodePtr &argument) const
ComputationContext & context
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.