14std::optional<std::pair<Rational, int>> LimitVisitor::exact_polynomial_leading(
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) {
22 for (
auto it = coefficients.rbegin(); it != coefficients.rend(); ++it)
23 {
value = arithmetic.add(arithmetic.mul(
value, point_value), *it); }
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();
32bool LimitVisitor::analytic_defined_at_point(
33 const std::shared_ptr<const SymbolicNode>& expr,
34 const std::shared_ptr<const SymbolicNode>& positive_base)
const {
36 if (!substituted) {
return false; }
37 std::optional<detail::AssumptionFacts> assumed;
38 if (assumption_ctx_) { assumed.emplace(*assumption_ctx_); }
42 if (!at_point) {
return false; }
44 if (!positive) {
throw positive.error(); }
48 if (!defined) {
throw defined.error(); }
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; }
57 auto number = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
59 if (integral &&
integer > BigInt(0)) {
return true; }
60 if (!integral) { positive_base = power.base(); }
61 return analytic_defined_at_point(expr, positive_base);
65 const std::shared_ptr<const SymbolicNode>& expr)
const {
66 if (
function.arguments().size() != 1 || !analytic_at_point(
function.arguments()[0])) {
return false; }
74 default: {
return false; }
76 return analytic_defined_at_point(expr,
nullptr);
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; }
87std::optional<int> LimitVisitor::rational_sign_at_infinity(
88 const std::shared_ptr<const SymbolicNode>& expr) {
89 auto fraction = extract_num_den(expr);
95 if (numerator.value().is_zero()) {
return 0; }
96 auto coefficient = numerator.value().lead_coeff() /
denominator.value().lead_coeff();
98 if (is_limit_at_neg_infinity() && (numerator.value().degree() -
denominator.value().degree()) % 2) {
sign = -
sign; }
104std::optional<int> LimitVisitor::product_sign_near_point(
107 for (
const auto&
op : product.operands()) {
108 auto value = determine_sign_near_point(
op, dir);
109 if (!
value) {
return std::nullopt; }
115std::optional<int> LimitVisitor::power_sign_near_point(
116 const PowerNode& power,
const std::string& dir) {
118 auto number = std::dynamic_pointer_cast<const NumberNode>(power.exponent());
119 auto sign = determine_sign_near_point(power.base(), dir);
122 if (number && sign && *sign > 0) {
124 if (real_exponent) {
return 1; }
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; }
136 if (!substituted) {
return std::nullopt; }
139 if (!sign) {
return std::nullopt; }
141 if (
order % 2 && dir.empty()) {
return std::nullopt; }
144 current->accept(differentiation);
145 current = differentiation.get_result();
146 if (!current) {
return std::nullopt; }
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; }
161 std::optional<bool> left, std::optional<bool>
right) {
162 if ((left && !*left) || (
right && !*
right)) {
return false; }
163 if (left &&
right) {
return *left && *
right; }
168 std::optional<bool> left, std::optional<bool>
right) {
169 if ((left && *left) || (
right && *
right)) {
return true; }
170 if (left &&
right) {
return false; }
175std::optional<bool> LimitVisitor::relation_truth_near_point(
const RelationalNode&
relation) {
180 if (!normalized) {
throw normalized.error(); }
182 if (!sign) {
return std::nullopt; }
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; }
194std::optional<bool> LimitVisitor::logical_truth_near_point(
const LogicalNode& logical) {
195 auto left = condition_truth_near_point(logical.left());
197 return left ? std::optional<bool>(!*left) : std::nullopt;
199 auto right = condition_truth_near_point(logical.right());
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);
214 auto relation = std::dynamic_pointer_cast<const RelationalNode>(node);
215 if (!
relation) {
return std::nullopt; }
216 return relation_truth_near_point(*
relation);
229 LimitVisitor left(var,
point,
"-", assumption_ctx_, context_);
230 LimitVisitor
right(var,
point,
"+", assumption_ctx_, context_);
233 auto lv = left.get_result(), rv =
right.get_result();
234 result = lv && rv && lv->compare(*rv) == 0 ? lv :
nullptr;
237 auto branch = select_branch_by_direction(node,
direction);
238 result = branch ? eval_limit(branch) : nullptr;
244std::shared_ptr<const SymbolicNode> LimitVisitor::select_branch_by_direction(
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;
250 if (!witness.condition_holds_near_point(inverse))
return nullptr;
252 return node.default_expr();
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_);
265 auto al = sv.get_result();
271 al = norm.get_result();
272 if (!al->is_zero()) {
273 auto s = get_node_sign(al);
280 if (!sign)
return std::nullopt;
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_);
291 auto al = sv.get_result();
297 al = norm.get_result();
299 std::vector<std::shared_ptr<const SymbolicNode>> inf_args;
302 auto s = get_node_sign(al);
309 neg_result->accept(norm);
310 return norm.get_result();
316std::optional<std::pair<Rational, int>> LimitVisitor::exact_leading_at_point(
317 const std::shared_ptr<const SymbolicNode>& expr)
const {
324 if (!
a && !is_inf(
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 +=
"_"; }
332 if (!shifted) {
return std::nullopt; }
333 p = symbolic_to_poly<Rational>(shifted, shifted_variable);
337 if (!p || !
a) {
return std::nullopt; }
338 return exact_polynomial_leading(p.value(),
a.
value(), arithmetic);
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());
348 if (
auto product = std::dynamic_pointer_cast<const MultiplyNode>(expr)) {
349 return analytic_operands_at_point(product->operands());
351 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(expr)) {
352 return analytic_power_at_point(*power, expr);
354 if (
auto function = std::dynamic_pointer_cast<const FunctionNode>(expr)) {
355 return analytic_function_at_point(*
function, expr);
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; }
367 if (
auto leading = exact_leading_at_point(expr)) {
370 if (
auto product = std::dynamic_pointer_cast<const MultiplyNode>(expr)) {
371 return product_sign_near_point(*product, dir);
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; }
377 if (is_limit_at_infinity() || is_limit_at_neg_infinity()) {
378 auto sign = get_node_sign(eval_limit(expr));
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);
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;
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
@ Not
一元逻辑非,right 为 nullptr。
统一规范化数值幂。 精确操作数保留整数和有理数运算;近似求根要求指数精确等于 1/2。 有限浮点幂直接折叠,省去倒数中间值;非有限结果保留幂表达式。
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)
int sign(const Rational &value)
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
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
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 及其工厂方法,运算接口.