SymbolicLimits.jl
SymbolicLimits.jl is a Julia package for computing symbolic limits using the Gruntz algorithm. It provides robust limit computation for complex symbolic expressions involving exponentials, logarithms, and algebraic operations.
Overview
Computing symbolic limits is a fundamental operation in symbolic mathematics, but it's also computationally challenging. Zero equivalence of log-exp functions is undecidable and reducible to computing symbolic limits. SymbolicLimits.jl implements the Gruntz algorithm to handle this complexity by using a heuristic zero-equivalence detector and tracking all assumptions made during computation.
Key Features
- Robust Limit Computation: Handles complex expressions involving exponentials and logarithms
- Assumption Tracking: Returns both the limit result and the assumptions required for correctness
- Gruntz Algorithm: Implements the state-of-the-art algorithm for symbolic limits
- Direction Support: Supports left-sided, right-sided, and two-sided limits
Quick Start
using SymbolicLimits, SymbolicUtils
@syms x::Real
# Basic limits
limit(exp(x+exp(-x))-exp(x), x, Inf)
limit(-1/x, x, Inf)
limit(-x / log(x), x, Inf)Limitations and Assumptions
Since zero equivalence is undecidable, SymbolicLimits uses heuristics and tracks assumptions. The returned result is correct if all tracked assumptions hold. In practice, the heuristics are reliable for most mathematical expressions encountered in typical use cases.
Algorithm Background
The package implements the Gruntz algorithm (1996), which handles limits by:
- Finding most rapidly varying subexpressions (MRV sets)
- Rewriting expressions in terms of distinguished subexpressions
- Computing series expansions around the most varying terms
- Extracting leading coefficients and exponents
API Reference
SymbolicLimits.compare_variance_rapidity — Method
compare_variance_rapidity(expr1, expr2, x, assumptions)Compare the rapidity of growth between two expressions as x approaches infinity.
Returns:
-1ifexpr1grows slower thanexpr2(i.e.,expr1 ≺ expr2)0ifexpr1andexpr2grow at the same rate (i.e.,expr1 ≍ expr2)1ifexpr1grows faster thanexpr2(i.e.,expr1 ≻ expr2)
The comparison is based on the limit of log(expr1)/log(expr2) as x approaches infinity:
f ≺ gifflog(f)/log(g) → 0f ≍ gifflog(f)/log(g) → cfor some finite nonzero constantcf ≻ gifflog(f)/log(g) → ∞
Arguments
expr1,expr2: Expressions to compare (must be of the formxorexp(...)).x: The variable approaching infinityassumptions: A set to track assumptions made during computation
Returns
An integer (-1, 0, or 1) indicating the relative growth rates.
SymbolicLimits.get_leading_exponent — Method
get_leading_exponent(expr, ω, h, assumptions)Find the leading (smallest) exponent in the series expansion of expr in powers of ω.
This function determines the power of the leading term in the series expansion: expr = c_{e}⋅ω^e + c_{e+1}⋅ω^{e+1} + ... where e is the leading exponent (the smallest exponent with a non-zero coefficient).
Returns Inf if expr is equivalent to zero.
Arguments
expr: The expression to analyzeω: A symbol representing the distinguished exponentialexp(h)h: The argument of the distinguished exponentialassumptions: A set to track assumptions made during computation
Returns
The leading exponent e, or Inf if expr is equivalent to zero.
SymbolicLimits.get_series_term — Method
get_series_term(expr, ω, h, i, assumptions)Get the coefficient of ω^i in the series expansion of expr around the most rapidly varying term.
This function computes the i-th coefficient in the series expansion of expr in powers of ω, where ω represents the expression exp(h). The expansion takes the form: expr = c₀ + c₁⋅ω + c₂⋅ω² + ...
This is a core component of the Gruntz algorithm for computing limits.
Arguments
expr: The expression to expandω: A symbol representing the distinguished exponentialexp(h)h: The argument of the distinguished exponentiali: The power ofωfor which to compute the coefficientassumptions: A set to track assumptions made during computation
Returns
The coefficient of ω^i in the series expansion of expr.
SymbolicLimits.is_exp — Method
is_exp(expr)Check if an expression is of the form exp(...).
Returns true if expr is a symbolic expression with the exponential function as its operation, false otherwise.
SymbolicLimits.limit — Function
limit(expr, var, h[, side::Symbol])Compute the limit of expr as var approaches h and return (limit, assumptions). If all the assumptions are true, then the returned limit is correct.
side indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.
SymbolicLimits.limit_inf — Method
limit_inf(expr, x)Compute the limit of expr as x approaches infinity and return (limit, assumptions).
This is the internal API boundary between the internal limits.jl file and the public SymbolicLimits.jl file
SymbolicLimits.log_exp_simplify — Method
log_exp_simplify(expr)Simplify expressions by canceling log(exp(x)) → x transformations.
This function performs basic log-exp simplifications that are always valid, specifically canceling log(exp(x)) to x. This is a conservative simplification that doesn't extend domains.
Arguments
expr: The expression to simplify
Returns
The simplified expression with log-exp cancellations applied.
SymbolicLimits.most_rapidly_varying_subexpressions — Method
most_rapidly_varying_subexpressions(expr, x, assumptions)Find the most rapidly varying subexpressions (MRV set) in expr with respect to x.
The MRV set consists of subexpressions that grow most rapidly as x approaches infinity. This is a key component of the Gruntz algorithm for computing symbolic limits.
For scalar expressions, returns an empty list.
Arguments
expr: The expression to analyzex: The variable with respect to which we find rapidly varying subexpressionsassumptions: A set to track assumptions made during computation
Returns
A list of the most rapidly varying subexpressions in expr.
SymbolicLimits.recursive — Method
recursive(f, args...)Apply function f recursively to its arguments, where f takes the recursive function as its first argument.
This utility function enables recursive operations on nested expressions by passing the recursive function itself as the first argument to f.
Example
recursive(expr) do f, ex
# f is the recursive function, ex is the current expression
# Apply f to subexpressions and build the result
endSymbolicLimits.rewrite — Method
rewrite(expr, ω, h, x, assumptions)Rewrite expr in the form A⋅ω^c where A is less rapidly varying than ω and c is a real number.
This function takes an exponential expression and rewrites it in terms of a distinguished exponential subexpression ω. The symbol ω represents exp(h) where h is the argument of the most rapidly varying exponential.
The rewriting follows the formula: if expr = exp(s) and ω = exp(h), then we compute c = lim(s/h, x, ∞) and rewrite as exp(s-c⋅h) ⋅ ω^c.
Arguments
expr: An exponential expression to rewrite (must be of the formexp(...))ω: A symbol representing the distinguished exponentialexp(h)h: The argument of the distinguished exponentialx: The variable approaching infinityassumptions: A set to track assumptions made during computation
Returns
An expression of the form A⋅ω^c where A is less rapidly varying than ω.
SymbolicLimits.signed_limit_inf — Method
signed_limit_inf(expr, x, assumptions)Compute the limit of expr as x approaches infinity and return (limit, sign). This function handles both symbolic expressions and scalar values, tracking assumptions made during the computation.
For scalar expressions, returns (expr, sign(expr)). For symbolic expressions, applies the Gruntz algorithm to compute the limit.
Arguments
expr: The expression to take the limit ofx: The variable approaching infinityassumptions: A set to track assumptions made during computation
Returns
A tuple (limit, sign) where:
limit: The computed limit valuesign: The sign of the limit
SymbolicLimits.strong_log_exp_simplify — Method
cancels log(exp(x)) and exp(log(x)), the latter may extend the domain
SymbolicLimits.zero_equivalence — Method
zero_equivalence(expr, assumptions)Determine if expr is equivalent to zero on its domain using heuristic methods.
This function uses symbolic simplification to determine if an expression is zero. Since zero equivalence of general expressions is undecidable, this function uses heuristics and records the result as an assumption that must hold for the computed limit to be correct.
The function applies strong log-exp simplification and algebraic expansion before testing for zero equivalence.
Arguments
expr: The expression to test for zero equivalenceassumptions: A set to record assumptions made (updated in-place)
Returns
true if the expression is determined to be zero, false otherwise.
Side Effects
Adds an assumption about the zero-equivalence of expr to the assumptions set.