This section explains the basics of the algorithm 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. Suppose \(f(x)\) and \(g(x)\) are two
real-valued functions such that \(\lim_{x \to \infty} f(x) = \infty\) and
similarly \(\lim_{x \to \infty} g(x) = \infty\). We shall say that \(f(x)\)dominates\(g(x)\), written \(f(x) \succ g(x)\), if for all \(a, b \in \mathbb{R}_{>0}\) we have
\(\lim_{x \to \infty} \frac{f(x)^a}{g(x)^b} = \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)\).

Note that whenever \(a, b \in \mathbb{R}_{>0}\) then
\(a f(x)^b \asymp f(x)\), and we shall use this to extend the definition of
\(\succ\) to all functions which tend to \(0\) or \(\pm \infty\) as \(x \to \infty\).
Thus we declare that \(f(x) \asymp 1/f(x)\) and \(f(x) \asymp -f(x)\).

It is easy to show the following examples:

\(e^x \succ x^m\)

\(e^{x^2} \succ e^{mx}\)

\(e^{e^x} \succ e^{x^m}\)

\(x^m \asymp x^n\)

\(e^{x + \frac{1}{x}} \asymp e^{x + \log{x}} \asymp e^x\).

From the above definition, it is possible to prove the following property:

This exposition glossed over several details. Many are described in the file
gruntz.py, and all can be found in Gruntz’ very readable thesis. The most
important points that have not been explained are:

Given f(x) and g(x), how do we determine if \(f(x) \succ g(x)\),
\(g(x) \succ f(x)\) or \(g(x) \asymp f(x)\)?

For dir=”+” (default) it calculates the limit from the right
(z->z0+) and for dir=”-” the limit from the left (z->z0-). For infinite z0
(oo or -oo), the dir argument doesn’t matter.

This algorithm is fully described in the module docstring in the gruntz.py
file. It relies heavily on the series expansion. Most frequently, gruntz()
is only used if the faster limit() function (which uses heuristics) fails.

Computes the maximum of two sets of expressions f and g, which
are in the same comparability class, i.e. mrv_max1() compares (two elements of)
f and g and returns the set, which is in the higher comparability class
of the union of both, if they have the same order of variation.
Also returns exps, with the appropriate substitutions made.

Computes the maximum of two sets of expressions f and g, which
are in the same comparability class, i.e. max() compares (two elements of)
f and g and returns either (f, expsf) [if f is larger], (g, expsg)
[if g is larger] or (union, expsboth) [if f, g are of the same class].

This class first of all keeps track of the mapping expr->variable, i.e.
will at this stage be a dictionary:

{exp(x): d1, exp(-x): d2, exp(x - exp(-x)): d3}.

[It turns out to be more convenient this way round.]
But sometimes expressions in the mrv set have other expressions from the
mrv set as subexpressions, and we need to keep track of that as well. In
this case, d3 is really exp(x - d2), so rewrites at this stage is:

{d3: exp(x-d2)}.

The function rewrite uses all this information to correctly rewrite our
expression in terms of w. In this case w can be choosen to be exp(-x),
i.e. d2. The correct rewriting then is:

The order of a function characterizes the function based on the limiting
behavior of the function as it goes to some limit. Only taking all limit
points to be 0 or positive infinity is currently supported. This is
expressed in big O notation [R297].

The formal definition for the order of a function \(g(x)\) about a point \(a\)
is such that \(g(x) = O(f(x))\) as \(x \rightarrow a\) if and only if for any
\(\delta > 0\) there exists a \(M > 0\) such that \(|g(x)| \leq M|f(x)|\) for
\(|x-a| < \delta\). This is equivalent to \(\lim_{x \rightarrow a}
\sup |g(x)/f(x)| < \infty\).

Let’s illustrate it on the following example by taking the expansion of
\(\sin(x)\) about 0:

\[\sin(x) = x - x^3/3! + O(x^5)\]

where in this case \(O(x^5) = x^5/5! - x^7/7! + \cdots\). By the definition
of \(O\), for any \(\delta > 0\) there is an \(M\) such that:

As it is usually used, the order of a function can be intuitively thought
of representing all terms of powers greater than the one specified. For
example, \(O(x^3)\) corresponds to any terms proportional to \(x^3,
x^4,\ldots\) and any higher power. For a polynomial, this leaves terms
proportional to \(x^2\), \(x\) and constants.

Notes

In O(f(x),x) the expression f(x) is assumed to have a leading
term. O(f(x),x) is automatically transformed to
O(f(x).as_leading_term(x),x).

O(expr*f(x),x) is O(f(x),x)

O(expr,x) is O(1)

O(0,x) is 0.

Multivariate O is also supported:

O(f(x,y),x,y) is transformed to
O(f(x,y).as_leading_term(x,y).as_leading_term(y),x,y)

In the multivariate case, it is assumed the limits w.r.t. the various
symbols commute.

Return True if expr belongs to Order(self.expr, *self.variables).
Return False if self belongs to expr.
Return None if the inclusion relation cannot be determined
(e.g. when self and expr have different symbols).