The Gruntz Algorithm

This section explains the basics of the algorithm [R1] used for computing limits. Most of the time the limit() function should just work. However it is still useful to keep in mind how it is implemented in case something does not work as expected.

First we define an ordering on functions of single variable \(x\) according to how rapidly varying they at infinity. Any two functions \(f(x)\) and \(g(x)\) can be compared using the properties of:

\[L = \lim\limits_{x\to\infty}\frac{\log|f(x)|}{\log|g(x)|}\]

We shall say that \(f(x)\) dominates \(g(x)\), written \(f(x) \succ g(x)\), iff \(L=\pm\infty\). We also say that \(f(x)\) and \(g(x)\) are of the same comparability class if neither \(f(x) \succ g(x)\) nor \(g(x) \succ f(x)\) and shall denote it as \(f(x) \asymp g(x)\).

It is easy to show the following examples:

  • \(e^{e^x} \succ e^{x^2} \succ e^x \succ x \succ 42\)
  • \(2 \asymp 3 \asymp -5\)
  • \(x \asymp x^2 \asymp x^3 \asymp -x\)
  • \(e^x \asymp e^{-x} \asymp e^{2x} \asymp e^{x + e^{-x}}\)
  • \(f(x) \asymp 1/f(x)\)

Using these definitions yields the following strategy for computing \(\lim_{x \to \infty} f(x)\):

  1. Given the function \(f(x)\), we find the set of most rapidly varying subexpressions (MRV set) of it. All items of this set belongs to the same comparability class. Let’s say it is \(\{e^x, e^{2x}\}\).

  2. Choose an expression \(\omega\) which is positive and tends to zero and which is in the same comparability class as any element of the MRV set. Such element always exists. Then we rewrite the MRV set using \(\omega\), in our case \(\{\omega^{-1}, \omega^{-2}\}\), and substitute it into \(f(x)\).

  3. Let \(f(\omega)\) be the function which is obtained from \(f(x)\) after the rewrite step above. Consider all expressions independent of \(\omega\) as constants and compute the leading term of the power series of \(f(\omega)\) around \(\omega = 0^+\):

    \[f(\omega) = c_0 \omega^{e_0} + c_1 \omega^{e_1} + \ldots\]

    where \(e_0 < e_1 < e_2 \ldots\)

  4. If the leading exponent \(e_0 > 0\) then the limit is \(0\). If \(e_0 < 0\), then the answer is \(\pm\infty\) (depends on sign of \(c_0\)). Finally, if \(e_0 = 0\), the limit is the limit of the leading coefficient \(c_0\).


This exposition glossed over several details. For example, limits could be computed recursively (steps 1 and 4). Please address to the Gruntz thesis [R1] for proof of the termination (pp. 52-60).


[R1](1, 2) Gruntz Thesis, b, x)[source]

Determine order relation between two functons.


{1, 0, -1}

Respectively, if \(a(x) \succ b(x)\), \(a(x) \asymp b(x)\) or \(b(x) \succ a(x)\).


>>> x = Symbol('x', real=True, positive=True)
>>> m = Symbol('m', real=True, positive=True)
>>> compare(x, x**2, x)
>>> compare(1/x, x**m, x)
>>> compare(exp(x), exp(x**2), x)
>>> compare(exp(x), x**5, x)
diofant.series.gruntz.limitinf(e, x)[source]

Compute limit of the expression at the infinity.


>>> x = Symbol('x', real=True, positive=True)
>>> limitinf(exp(x)*(exp(1/x - exp(-x)) - exp(1/x)), x)
>>> limitinf(x/log(x**(log(x**(log(2)/log(x))))), x)
diofant.series.gruntz.mrv(e, x)[source]

Calculate the MRV set of expression.


>>> x = Symbol('x', real=True, positive=True)
>>> mrv(log(x - log(x))/log(x), x)
>>> mrv(exp(x + exp(-x)), x)
{E**(-x), E**(x + E**(-x))}
diofant.series.gruntz.mrv_leadterm(e, x)[source]

Compute the leading term of the series.



The leading term \(c_0 w^{e_0}\) of the series of \(e\) in terms of the most rapidly varying subexpression \(w\) in form of the pair (c0, e0) of Expr.


>>> x = Symbol('x', real=True, positive=True)
>>> mrv_leadterm(1/exp(-x + exp(-x)) - exp(x), x)
(-1, 0)
diofant.series.gruntz.mrv_max(f, g, x)[source]

Computes the maximum of two MRV sets.

diofant.series.gruntz.rewrite(e, x, w)[source]

Rewrites expression in terms of the most rapidly varying subexpression.


e : Expr

an expression

x : Symbol

variable of the \(e\)

w : Symbol

The symbol which is going to be used for substitution in place of the most rapidly varying in \(x\) subexpression.



A pair: rewritten (in \(w\)) expression and \(\log(w)\).


>>> x = Symbol('x', real=True, positive=True)
>>> m = Symbol('m', real=True, positive=True)
>>> rewrite(exp(x), x, m)
(1/m, -x)
>>> rewrite(exp(x)*log(log(exp(x))), x, m)
(log(x)/m, -x)
diofant.series.gruntz.sign(e, x)[source]

Determine a sign of an expression at infinity.


{1, 0, -1}

One or minus one, if \(e > 0\) or \(e < 0\) for \(x\) sufficiently large and zero if \(e\) is constantly zero for \(x\to\infty\).

The result of this function is currently undefined if \(e\) changes sign arbitrarily often at infinity (e.g. \(\sin(x)\)).