GruntzLimit on four term types (Julia Expr, SymEngine, SymbolicUtils and SymJ) and SciML's SymbolicLimits.jl on SymbolicUtils terms, with Giac's own limit (through Giac.jl) as a reference from a long-established computer algebra system, on a set of limits chosen to be hard: the worked examples of Gruntz's thesis, cancellations that defeat numerical evaluation, high-order series at finite points, one-sided limits, and limits with the parameters a and b. Each answer is compared with a known value.
Select a problem number or a cell to see the limit and each method's answer.
GruntzLimit solves all 66 on every term type (Expr, SymEngine, SymbolicUtils and SymJ), in 4–5 s in all from a cold start, most of it compiling for the first problem (0.6 s on SymJ terms, for which SymJ's GruntzLimit extension precompiles the limit code): all of Gruntz's §8 examples, the nested-radical, high-order-series (−1/30 for (sin∘tan − tan∘sin)/x⁷) and parameter problems, the limit points π/4 and π/2, and every one-sided limit.
Changes since the first run (same day, same problems): GruntzLimit now evaluates sin and cos at rational multiples of π with denominators 1, 2, 3, 4 and 6, so cos(π/2) is zero. Before, it returned 0 for (π/2 − x)·tan x at π/2 (problem 57, limit 1) and an expression dividing by cos(π/2) for tan(x)tan 2x at π/4 (56). A leading coefficient free of x that evaluates to zero is now passed over instead of trusted. Comparable exponentials whose exponents have an irrational ratio, such as 3ˣ and 5ˣ, are now handled, which solves Gruntz 8.5 and (3ˣ + 5ˣ)1/x (5, 12). And Expr input now accepts rational literals such as 5//7 (Gruntz 8.15, problem 15). Last, an infinite limit whose sign depends on a parameter, such as a·log x, is no longer an error where only its being infinite matters, which solves xᵃ/eˣ (38); where the sign matters, as in xa−2, it still is.
SymbolicLimits supports only +, *, integer powers, exp and log, so on the same terms it does 19: 46 problems stop with “Not implemented” (sqrt, sin, tan, a power with a non-integer exponent) or with a MethodError for a symbolic limit point (π/4, π/2, a). With powers and radicals rewritten as exp and log first it does 37, among them Gruntz 8.1–8.4, 8.6–8.8, 8.10, 8.14, 8.19, 8.20 and the parameter problems at ∞. Some still stop on a non-integer power that its own rewriting creates (e.g. Gruntz 8.9, 8.11, 8.16, 8.17), and the 20 problems with trigonometric functions remain out of reach.
SymbolicLimits gives one wrong answer, ∞ for Gruntz 8.18, whose limit is 2, in both rows. Several of its correct answers (problems 3, 4, 23, 50) come with the assumption false in the set it returns; by its own contract the answer is guaranteed only when every assumption holds.
Giac's limit solves 65 of the 66, in under 1 s in all, and its answers agree with GruntzLimit's. The one it misses is xᵃ/eˣ (38): with the sign of a unknown it cannot make the series expansion, where GruntzLimit needs only that a·log x is small next to x.
Every problem either SymbolicLimits row solves, GruntzLimit solves too. SymbolicLimits takes 30–46 s in all, mostly on the nested exponentials of Gruntz 8.6, 8.7 and 8.14 (4–8 s each); GruntzLimit takes 5 s or less on any term type.
A method has an answer when it returned a value free of x and threw nothing; GruntzLimit throws a GruntzError when it cannot compute a limit, SymbolicLimits an ErrorException or ArgumentError. Giac returns undef, an unsigned infinity or bounded_function(…) when it finds no limit, and these do not count as answers; its +infinity and −infinity count as ±∞. An answer is correct when its value, with a = 1.7 and b = 2.3, matches the known limit to a relative 1e-8 (infinite limits must match exactly). SymbolicLimits also returns the assumptions its zero test made; any other than true are shown with its answer.
Every method gets the same expression. For SymbolicUtils, π is the exact term identity(π) and ℯ is exp(1) with a constant 1, as in GruntzLimit's tests; SymbolicLimits gets exactly the terms GruntzLimit does. SymJ terms are built from Sym(:x), Sym(π) and Sym(ℯ); SymJ is not registered yet, so it is taken from the latest commit of its main branch, shown with its version below. Giac gets the same expressions built from Giac.jl variables, with π as pi and ℯ as exp(1); its two-sided limits are asked without a direction, its one-sided ones with direction 1 or −1, and its answers are evaluated with evalf after substituting values rounded to 12 digits (Giac misreads longer floats). The row “powers as exp/log” first rewrites sqrt(u), cbrt(u) and u^v (v not an integer) as exp(log(u)/2), exp(log(u)/3) and exp(v·log(u)), since SymbolicLimits supports only +, *, integer powers, exp and log.
Each method ran in its own Julia process, one after another, with a limit of s per problem; after a timeout the process was restarted at the next problem. Solving time is the sum of the per-problem times from a cold start, so it includes compiling. Load time is the time to load the packages.