AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
"""A module that handles series: find a limit, order the series etc.
|
||||
"""
|
||||
from .order import Order
|
||||
from .limits import limit, Limit
|
||||
from .gruntz import gruntz
|
||||
from .series import series
|
||||
from .approximants import approximants
|
||||
from .residues import residue
|
||||
from .sequences import SeqPer, SeqFormula, sequence, SeqAdd, SeqMul
|
||||
from .fourier import fourier_series
|
||||
from .formal import fps
|
||||
from .limitseq import difference_delta, limit_seq
|
||||
|
||||
from sympy.core.singleton import S
|
||||
EmptySequence = S.EmptySequence
|
||||
|
||||
O = Order
|
||||
|
||||
__all__ = ['Order', 'O', 'limit', 'Limit', 'gruntz', 'series', 'approximants',
|
||||
'residue', 'EmptySequence', 'SeqPer', 'SeqFormula', 'sequence',
|
||||
'SeqAdd', 'SeqMul', 'fourier_series', 'fps', 'difference_delta',
|
||||
'limit_seq'
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Convergence acceleration / extrapolation methods for series and
|
||||
sequences.
|
||||
|
||||
References:
|
||||
Carl M. Bender & Steven A. Orszag, "Advanced Mathematical Methods for
|
||||
Scientists and Engineers: Asymptotic Methods and Perturbation Theory",
|
||||
Springer 1999. (Shanks transformation: pp. 368-375, Richardson
|
||||
extrapolation: pp. 375-377.)
|
||||
"""
|
||||
|
||||
from sympy.core.numbers import Integer
|
||||
from sympy.core.singleton import S
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
|
||||
|
||||
def richardson(A, k, n, N):
|
||||
"""
|
||||
Calculate an approximation for lim k->oo A(k) using Richardson
|
||||
extrapolation with the terms A(n), A(n+1), ..., A(n+N+1).
|
||||
Choosing N ~= 2*n often gives good results.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A simple example is to calculate exp(1) using the limit definition.
|
||||
This limit converges slowly; n = 100 only produces two accurate
|
||||
digits:
|
||||
|
||||
>>> from sympy.abc import n
|
||||
>>> e = (1 + 1/n)**n
|
||||
>>> print(round(e.subs(n, 100).evalf(), 10))
|
||||
2.7048138294
|
||||
|
||||
Richardson extrapolation with 11 appropriately chosen terms gives
|
||||
a value that is accurate to the indicated precision:
|
||||
|
||||
>>> from sympy import E
|
||||
>>> from sympy.series.acceleration import richardson
|
||||
>>> print(round(richardson(e, n, 10, 20).evalf(), 10))
|
||||
2.7182818285
|
||||
>>> print(round(E.evalf(), 10))
|
||||
2.7182818285
|
||||
|
||||
Another useful application is to speed up convergence of series.
|
||||
Computing 100 terms of the zeta(2) series 1/k**2 yields only
|
||||
two accurate digits:
|
||||
|
||||
>>> from sympy.abc import k, n
|
||||
>>> from sympy import Sum
|
||||
>>> A = Sum(k**-2, (k, 1, n))
|
||||
>>> print(round(A.subs(n, 100).evalf(), 10))
|
||||
1.6349839002
|
||||
|
||||
Richardson extrapolation performs much better:
|
||||
|
||||
>>> from sympy import pi
|
||||
>>> print(round(richardson(A, n, 10, 20).evalf(), 10))
|
||||
1.6449340668
|
||||
>>> print(round(((pi**2)/6).evalf(), 10)) # Exact value
|
||||
1.6449340668
|
||||
|
||||
"""
|
||||
s = S.Zero
|
||||
for j in range(0, N + 1):
|
||||
s += (A.subs(k, Integer(n + j)).doit() * (n + j)**N *
|
||||
S.NegativeOne**(j + N) / (factorial(j) * factorial(N - j)))
|
||||
return s
|
||||
|
||||
|
||||
def shanks(A, k, n, m=1):
|
||||
"""
|
||||
Calculate an approximation for lim k->oo A(k) using the n-term Shanks
|
||||
transformation S(A)(n). With m > 1, calculate the m-fold recursive
|
||||
Shanks transformation S(S(...S(A)...))(n).
|
||||
|
||||
The Shanks transformation is useful for summing Taylor series that
|
||||
converge slowly near a pole or singularity, e.g. for log(2):
|
||||
|
||||
>>> from sympy.abc import k, n
|
||||
>>> from sympy import Sum, Integer
|
||||
>>> from sympy.series.acceleration import shanks
|
||||
>>> A = Sum(Integer(-1)**(k+1) / k, (k, 1, n))
|
||||
>>> print(round(A.subs(n, 100).doit().evalf(), 10))
|
||||
0.6881721793
|
||||
>>> print(round(shanks(A, n, 25).evalf(), 10))
|
||||
0.6931396564
|
||||
>>> print(round(shanks(A, n, 25, 5).evalf(), 10))
|
||||
0.6931471806
|
||||
|
||||
The correct value is 0.6931471805599453094172321215.
|
||||
"""
|
||||
table = [A.subs(k, Integer(j)).doit() for j in range(n + m + 2)]
|
||||
table2 = table.copy()
|
||||
|
||||
for i in range(1, m + 1):
|
||||
for j in range(i, n + m + 1):
|
||||
x, y, z = table[j - 1], table[j], table[j + 1]
|
||||
table2[j] = (z*x - y**2) / (z + x - 2*y)
|
||||
table = table2.copy()
|
||||
return table[n]
|
||||
@@ -0,0 +1,103 @@
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.polys.polytools import lcm
|
||||
from sympy.utilities import public
|
||||
|
||||
@public
|
||||
def approximants(l, X=Symbol('x'), simplify=False):
|
||||
"""
|
||||
Return a generator for consecutive Pade approximants for a series.
|
||||
It can also be used for computing the rational generating function of a
|
||||
series when possible, since the last approximant returned by the generator
|
||||
will be the generating function (if any).
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The input list can contain more complex expressions than integer or rational
|
||||
numbers; symbols may also be involved in the computation. An example below
|
||||
show how to compute the generating function of the whole Pascal triangle.
|
||||
|
||||
The generator can be asked to apply the sympy.simplify function on each
|
||||
generated term, which will make the computation slower; however it may be
|
||||
useful when symbols are involved in the expressions.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.series import approximants
|
||||
>>> from sympy import lucas, fibonacci, symbols, binomial
|
||||
>>> g = [lucas(k) for k in range(16)]
|
||||
>>> [e for e in approximants(g)]
|
||||
[2, -4/(x - 2), (5*x - 2)/(3*x - 1), (x - 2)/(x**2 + x - 1)]
|
||||
|
||||
>>> h = [fibonacci(k) for k in range(16)]
|
||||
>>> [e for e in approximants(h)]
|
||||
[x, -x/(x - 1), (x**2 - x)/(2*x - 1), -x/(x**2 + x - 1)]
|
||||
|
||||
>>> x, t = symbols("x,t")
|
||||
>>> p=[sum(binomial(k,i)*x**i for i in range(k+1)) for k in range(16)]
|
||||
>>> y = approximants(p, t)
|
||||
>>> for k in range(3): print(next(y))
|
||||
1
|
||||
(x + 1)/((-x - 1)*(t*(x + 1) + (x + 1)/(-x - 1)))
|
||||
nan
|
||||
|
||||
>>> y = approximants(p, t, simplify=True)
|
||||
>>> for k in range(3): print(next(y))
|
||||
1
|
||||
-1/(t*(x + 1) - 1)
|
||||
nan
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.concrete.guess.guess_generating_function_rational
|
||||
mpmath.pade
|
||||
"""
|
||||
from sympy.simplify import simplify as simp
|
||||
from sympy.simplify.radsimp import denom
|
||||
p1, q1 = [S.One], [S.Zero]
|
||||
p2, q2 = [S.Zero], [S.One]
|
||||
while len(l):
|
||||
b = 0
|
||||
while l[b]==0:
|
||||
b += 1
|
||||
if b == len(l):
|
||||
return
|
||||
m = [S.One/l[b]]
|
||||
for k in range(b+1, len(l)):
|
||||
s = 0
|
||||
for j in range(b, k):
|
||||
s -= l[j+1] * m[b-j-1]
|
||||
m.append(s/l[b])
|
||||
l = m
|
||||
a, l[0] = l[0], 0
|
||||
p = [0] * max(len(p2), b+len(p1))
|
||||
q = [0] * max(len(q2), b+len(q1))
|
||||
for k in range(len(p2)):
|
||||
p[k] = a*p2[k]
|
||||
for k in range(b, b+len(p1)):
|
||||
p[k] += p1[k-b]
|
||||
for k in range(len(q2)):
|
||||
q[k] = a*q2[k]
|
||||
for k in range(b, b+len(q1)):
|
||||
q[k] += q1[k-b]
|
||||
while p[-1]==0: p.pop()
|
||||
while q[-1]==0: q.pop()
|
||||
p1, p2 = p2, p
|
||||
q1, q2 = q2, q
|
||||
|
||||
# yield result
|
||||
c = 1
|
||||
for x in p:
|
||||
c = lcm(c, denom(x))
|
||||
for x in q:
|
||||
c = lcm(c, denom(x))
|
||||
out = ( sum(c*e*X**k for k, e in enumerate(p))
|
||||
/ sum(c*e*X**k for k, e in enumerate(q)) )
|
||||
if simplify:
|
||||
yield(simp(out))
|
||||
else:
|
||||
yield out
|
||||
return
|
||||
@@ -0,0 +1,10 @@
|
||||
from sympy.core.sympify import sympify
|
||||
|
||||
|
||||
def aseries(expr, x=None, n=6, bound=0, hir=False):
|
||||
"""
|
||||
See the docstring of Expr.aseries() for complete details of this wrapper.
|
||||
|
||||
"""
|
||||
expr = sympify(expr)
|
||||
return expr.aseries(x, n, bound, hir)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
from sympy.core.numbers import oo
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.series.limits import limit
|
||||
|
||||
x = Symbol('x')
|
||||
|
||||
|
||||
def timeit_limit_1x():
|
||||
limit(1/x, x, oo)
|
||||
@@ -0,0 +1,10 @@
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.series.order import O
|
||||
|
||||
x = Symbol('x')
|
||||
l = [x**i for i in range(1000)]
|
||||
l.append(O(x**1001))
|
||||
|
||||
def timeit_order_1x():
|
||||
Add(*l)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,811 @@
|
||||
"""Fourier Series"""
|
||||
|
||||
from sympy.core.numbers import (oo, pi)
|
||||
from sympy.core.symbol import Wild
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Dummy, Symbol
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.trigonometric import sin, cos, sinc
|
||||
from sympy.series.series_class import SeriesBase
|
||||
from sympy.series.sequences import SeqFormula
|
||||
from sympy.sets.sets import Interval
|
||||
from sympy.utilities.iterables import is_sequence
|
||||
|
||||
|
||||
__doctest_requires__ = {('fourier_series',): ['matplotlib']}
|
||||
|
||||
|
||||
def fourier_cos_seq(func, limits, n):
|
||||
"""Returns the cos sequence in a Fourier series"""
|
||||
from sympy.integrals import integrate
|
||||
x, L = limits[0], limits[2] - limits[1]
|
||||
cos_term = cos(2*n*pi*x / L)
|
||||
formula = 2 * cos_term * integrate(func * cos_term, limits) / L
|
||||
a0 = formula.subs(n, S.Zero) / 2
|
||||
return a0, SeqFormula(2 * cos_term * integrate(func * cos_term, limits)
|
||||
/ L, (n, 1, oo))
|
||||
|
||||
|
||||
def fourier_sin_seq(func, limits, n):
|
||||
"""Returns the sin sequence in a Fourier series"""
|
||||
from sympy.integrals import integrate
|
||||
x, L = limits[0], limits[2] - limits[1]
|
||||
sin_term = sin(2*n*pi*x / L)
|
||||
return SeqFormula(2 * sin_term * integrate(func * sin_term, limits)
|
||||
/ L, (n, 1, oo))
|
||||
|
||||
|
||||
def _process_limits(func, limits):
|
||||
"""
|
||||
Limits should be of the form (x, start, stop).
|
||||
x should be a symbol. Both start and stop should be bounded.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
* If x is not given, x is determined from func.
|
||||
* If limits is None. Limit of the form (x, -pi, pi) is returned.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.series.fourier import _process_limits as pari
|
||||
>>> from sympy.abc import x
|
||||
>>> pari(x**2, (x, -2, 2))
|
||||
(x, -2, 2)
|
||||
>>> pari(x**2, (-2, 2))
|
||||
(x, -2, 2)
|
||||
>>> pari(x**2, None)
|
||||
(x, -pi, pi)
|
||||
"""
|
||||
def _find_x(func):
|
||||
free = func.free_symbols
|
||||
if len(free) == 1:
|
||||
return free.pop()
|
||||
elif not free:
|
||||
return Dummy('k')
|
||||
else:
|
||||
raise ValueError(
|
||||
" specify dummy variables for %s. If the function contains"
|
||||
" more than one free symbol, a dummy variable should be"
|
||||
" supplied explicitly e.g. FourierSeries(m*n**2, (n, -pi, pi))"
|
||||
% func)
|
||||
|
||||
x, start, stop = None, None, None
|
||||
if limits is None:
|
||||
x, start, stop = _find_x(func), -pi, pi
|
||||
if is_sequence(limits, Tuple):
|
||||
if len(limits) == 3:
|
||||
x, start, stop = limits
|
||||
elif len(limits) == 2:
|
||||
x = _find_x(func)
|
||||
start, stop = limits
|
||||
|
||||
if not isinstance(x, Symbol) or start is None or stop is None:
|
||||
raise ValueError('Invalid limits given: %s' % str(limits))
|
||||
|
||||
unbounded = [S.NegativeInfinity, S.Infinity]
|
||||
if start in unbounded or stop in unbounded:
|
||||
raise ValueError("Both the start and end value should be bounded")
|
||||
|
||||
return sympify((x, start, stop))
|
||||
|
||||
|
||||
def finite_check(f, x, L):
|
||||
|
||||
def check_fx(exprs, x):
|
||||
return x not in exprs.free_symbols
|
||||
|
||||
def check_sincos(_expr, x, L):
|
||||
if isinstance(_expr, (sin, cos)):
|
||||
sincos_args = _expr.args[0]
|
||||
|
||||
if sincos_args.match(a*(pi/L)*x + b) is not None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
from sympy.simplify.fu import TR2, TR1, sincos_to_sum
|
||||
_expr = sincos_to_sum(TR2(TR1(f)))
|
||||
add_coeff = _expr.as_coeff_add()
|
||||
|
||||
a = Wild('a', properties=[lambda k: k.is_Integer, lambda k: k != S.Zero, ])
|
||||
b = Wild('b', properties=[lambda k: x not in k.free_symbols, ])
|
||||
|
||||
for s in add_coeff[1]:
|
||||
mul_coeffs = s.as_coeff_mul()[1]
|
||||
for t in mul_coeffs:
|
||||
if not (check_fx(t, x) or check_sincos(t, x, L)):
|
||||
return False, f
|
||||
|
||||
return True, _expr
|
||||
|
||||
|
||||
class FourierSeries(SeriesBase):
|
||||
r"""Represents Fourier sine/cosine series.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This class only represents a fourier series.
|
||||
No computation is performed.
|
||||
|
||||
For how to compute Fourier series, see the :func:`fourier_series`
|
||||
docstring.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.fourier.fourier_series
|
||||
"""
|
||||
def __new__(cls, *args):
|
||||
args = map(sympify, args)
|
||||
return Expr.__new__(cls, *args)
|
||||
|
||||
@property
|
||||
def function(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self.args[1][0]
|
||||
|
||||
@property
|
||||
def period(self):
|
||||
return (self.args[1][1], self.args[1][2])
|
||||
|
||||
@property
|
||||
def a0(self):
|
||||
return self.args[2][0]
|
||||
|
||||
@property
|
||||
def an(self):
|
||||
return self.args[2][1]
|
||||
|
||||
@property
|
||||
def bn(self):
|
||||
return self.args[2][2]
|
||||
|
||||
@property
|
||||
def interval(self):
|
||||
return Interval(0, oo)
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
return self.interval.inf
|
||||
|
||||
@property
|
||||
def stop(self):
|
||||
return self.interval.sup
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return oo
|
||||
|
||||
@property
|
||||
def L(self):
|
||||
return abs(self.period[1] - self.period[0]) / 2
|
||||
|
||||
def _eval_subs(self, old, new):
|
||||
x = self.x
|
||||
if old.has(x):
|
||||
return self
|
||||
|
||||
def truncate(self, n=3):
|
||||
"""
|
||||
Return the first n nonzero terms of the series.
|
||||
|
||||
If ``n`` is None return an iterator.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : int or None
|
||||
Amount of non-zero terms in approximation or None.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Expr or iterator :
|
||||
Approximation of function expanded into Fourier series.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> s = fourier_series(x, (x, -pi, pi))
|
||||
>>> s.truncate(4)
|
||||
2*sin(x) - sin(2*x) + 2*sin(3*x)/3 - sin(4*x)/2
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.fourier.FourierSeries.sigma_approximation
|
||||
"""
|
||||
if n is None:
|
||||
return iter(self)
|
||||
|
||||
terms = []
|
||||
for t in self:
|
||||
if len(terms) == n:
|
||||
break
|
||||
if t is not S.Zero:
|
||||
terms.append(t)
|
||||
|
||||
return Add(*terms)
|
||||
|
||||
def sigma_approximation(self, n=3):
|
||||
r"""
|
||||
Return :math:`\sigma`-approximation of Fourier series with respect
|
||||
to order n.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Sigma approximation adjusts a Fourier summation to eliminate the Gibbs
|
||||
phenomenon which would otherwise occur at discontinuities.
|
||||
A sigma-approximated summation for a Fourier series of a T-periodical
|
||||
function can be written as
|
||||
|
||||
.. math::
|
||||
s(\theta) = \frac{1}{2} a_0 + \sum _{k=1}^{m-1}
|
||||
\operatorname{sinc} \Bigl( \frac{k}{m} \Bigr) \cdot
|
||||
\left[ a_k \cos \Bigl( \frac{2\pi k}{T} \theta \Bigr)
|
||||
+ b_k \sin \Bigl( \frac{2\pi k}{T} \theta \Bigr) \right],
|
||||
|
||||
where :math:`a_0, a_k, b_k, k=1,\ldots,{m-1}` are standard Fourier
|
||||
series coefficients and
|
||||
:math:`\operatorname{sinc} \Bigl( \frac{k}{m} \Bigr)` is a Lanczos
|
||||
:math:`\sigma` factor (expressed in terms of normalized
|
||||
:math:`\operatorname{sinc}` function).
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : int
|
||||
Highest order of the terms taken into account in approximation.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Expr :
|
||||
Sigma approximation of function expanded into Fourier series.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> s = fourier_series(x, (x, -pi, pi))
|
||||
>>> s.sigma_approximation(4)
|
||||
2*sin(x)*sinc(pi/4) - 2*sin(2*x)/pi + 2*sin(3*x)*sinc(3*pi/4)/3
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.fourier.FourierSeries.truncate
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
The behaviour of
|
||||
:meth:`~sympy.series.fourier.FourierSeries.sigma_approximation`
|
||||
is different from :meth:`~sympy.series.fourier.FourierSeries.truncate`
|
||||
- it takes all nonzero terms of degree smaller than n, rather than
|
||||
first n nonzero ones.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Gibbs_phenomenon
|
||||
.. [2] https://en.wikipedia.org/wiki/Sigma_approximation
|
||||
"""
|
||||
terms = [sinc(pi * i / n) * t for i, t in enumerate(self[:n])
|
||||
if t is not S.Zero]
|
||||
return Add(*terms)
|
||||
|
||||
def shift(self, s):
|
||||
"""
|
||||
Shift the function by a term independent of x.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
f(x) -> f(x) + s
|
||||
|
||||
This is fast, if Fourier series of f(x) is already
|
||||
computed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> s = fourier_series(x**2, (x, -pi, pi))
|
||||
>>> s.shift(1).truncate()
|
||||
-4*cos(x) + cos(2*x) + 1 + pi**2/3
|
||||
"""
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
a0 = self.a0 + s
|
||||
sfunc = self.function + s
|
||||
|
||||
return self.func(sfunc, self.args[1], (a0, self.an, self.bn))
|
||||
|
||||
def shiftx(self, s):
|
||||
"""
|
||||
Shift x by a term independent of x.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
f(x) -> f(x + s)
|
||||
|
||||
This is fast, if Fourier series of f(x) is already
|
||||
computed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> s = fourier_series(x**2, (x, -pi, pi))
|
||||
>>> s.shiftx(1).truncate()
|
||||
-4*cos(x + 1) + cos(2*x + 2) + pi**2/3
|
||||
"""
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
an = self.an.subs(x, x + s)
|
||||
bn = self.bn.subs(x, x + s)
|
||||
sfunc = self.function.subs(x, x + s)
|
||||
|
||||
return self.func(sfunc, self.args[1], (self.a0, an, bn))
|
||||
|
||||
def scale(self, s):
|
||||
"""
|
||||
Scale the function by a term independent of x.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
f(x) -> s * f(x)
|
||||
|
||||
This is fast, if Fourier series of f(x) is already
|
||||
computed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> s = fourier_series(x**2, (x, -pi, pi))
|
||||
>>> s.scale(2).truncate()
|
||||
-8*cos(x) + 2*cos(2*x) + 2*pi**2/3
|
||||
"""
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
an = self.an.coeff_mul(s)
|
||||
bn = self.bn.coeff_mul(s)
|
||||
a0 = self.a0 * s
|
||||
sfunc = self.args[0] * s
|
||||
|
||||
return self.func(sfunc, self.args[1], (a0, an, bn))
|
||||
|
||||
def scalex(self, s):
|
||||
"""
|
||||
Scale x by a term independent of x.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
f(x) -> f(s*x)
|
||||
|
||||
This is fast, if Fourier series of f(x) is already
|
||||
computed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> s = fourier_series(x**2, (x, -pi, pi))
|
||||
>>> s.scalex(2).truncate()
|
||||
-4*cos(2*x) + cos(4*x) + pi**2/3
|
||||
"""
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
an = self.an.subs(x, x * s)
|
||||
bn = self.bn.subs(x, x * s)
|
||||
sfunc = self.function.subs(x, x * s)
|
||||
|
||||
return self.func(sfunc, self.args[1], (self.a0, an, bn))
|
||||
|
||||
def _eval_as_leading_term(self, x, logx, cdir):
|
||||
for t in self:
|
||||
if t is not S.Zero:
|
||||
return t
|
||||
|
||||
def _eval_term(self, pt):
|
||||
if pt == 0:
|
||||
return self.a0
|
||||
return self.an.coeff(pt) + self.bn.coeff(pt)
|
||||
|
||||
def __neg__(self):
|
||||
return self.scale(-1)
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, FourierSeries):
|
||||
if self.period != other.period:
|
||||
raise ValueError("Both the series should have same periods")
|
||||
|
||||
x, y = self.x, other.x
|
||||
function = self.function + other.function.subs(y, x)
|
||||
|
||||
if self.x not in function.free_symbols:
|
||||
return function
|
||||
|
||||
an = self.an + other.an
|
||||
bn = self.bn + other.bn
|
||||
a0 = self.a0 + other.a0
|
||||
|
||||
return self.func(function, self.args[1], (a0, an, bn))
|
||||
|
||||
return Add(self, other)
|
||||
|
||||
def __sub__(self, other):
|
||||
return self.__add__(-other)
|
||||
|
||||
|
||||
class FiniteFourierSeries(FourierSeries):
|
||||
r"""Represents Finite Fourier sine/cosine series.
|
||||
|
||||
For how to compute Fourier series, see the :func:`fourier_series`
|
||||
docstring.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
f : Expr
|
||||
Expression for finding fourier_series
|
||||
|
||||
limits : ( x, start, stop)
|
||||
x is the independent variable for the expression f
|
||||
(start, stop) is the period of the fourier series
|
||||
|
||||
exprs: (a0, an, bn) or Expr
|
||||
a0 is the constant term a0 of the fourier series
|
||||
an is a dictionary of coefficients of cos terms
|
||||
an[k] = coefficient of cos(pi*(k/L)*x)
|
||||
bn is a dictionary of coefficients of sin terms
|
||||
bn[k] = coefficient of sin(pi*(k/L)*x)
|
||||
|
||||
or exprs can be an expression to be converted to fourier form
|
||||
|
||||
Methods
|
||||
=======
|
||||
|
||||
This class is an extension of FourierSeries class.
|
||||
Please refer to sympy.series.fourier.FourierSeries for
|
||||
further information.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.fourier.FourierSeries
|
||||
sympy.series.fourier.fourier_series
|
||||
"""
|
||||
|
||||
def __new__(cls, f, limits, exprs):
|
||||
f = sympify(f)
|
||||
limits = sympify(limits)
|
||||
exprs = sympify(exprs)
|
||||
|
||||
if not (isinstance(exprs, Tuple) and len(exprs) == 3): # exprs is not of form (a0, an, bn)
|
||||
# Converts the expression to fourier form
|
||||
c, e = exprs.as_coeff_add()
|
||||
from sympy.simplify.fu import TR10
|
||||
rexpr = c + Add(*[TR10(i) for i in e])
|
||||
a0, exp_ls = rexpr.expand(trig=False, power_base=False, power_exp=False, log=False).as_coeff_add()
|
||||
|
||||
x = limits[0]
|
||||
L = abs(limits[2] - limits[1]) / 2
|
||||
|
||||
a = Wild('a', properties=[lambda k: k.is_Integer, lambda k: k is not S.Zero, ])
|
||||
b = Wild('b', properties=[lambda k: x not in k.free_symbols, ])
|
||||
|
||||
an = {}
|
||||
bn = {}
|
||||
|
||||
# separates the coefficients of sin and cos terms in dictionaries an, and bn
|
||||
for p in exp_ls:
|
||||
t = p.match(b * cos(a * (pi / L) * x))
|
||||
q = p.match(b * sin(a * (pi / L) * x))
|
||||
if t:
|
||||
an[t[a]] = t[b] + an.get(t[a], S.Zero)
|
||||
elif q:
|
||||
bn[q[a]] = q[b] + bn.get(q[a], S.Zero)
|
||||
else:
|
||||
a0 += p
|
||||
|
||||
exprs = Tuple(a0, an, bn)
|
||||
|
||||
return Expr.__new__(cls, f, limits, exprs)
|
||||
|
||||
@property
|
||||
def interval(self):
|
||||
_length = 1 if self.a0 else 0
|
||||
_length += max(set(self.an.keys()).union(set(self.bn.keys()))) + 1
|
||||
return Interval(0, _length)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return self.stop - self.start
|
||||
|
||||
def shiftx(self, s):
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
_expr = self.truncate().subs(x, x + s)
|
||||
sfunc = self.function.subs(x, x + s)
|
||||
|
||||
return self.func(sfunc, self.args[1], _expr)
|
||||
|
||||
def scale(self, s):
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
_expr = self.truncate() * s
|
||||
sfunc = self.function * s
|
||||
|
||||
return self.func(sfunc, self.args[1], _expr)
|
||||
|
||||
def scalex(self, s):
|
||||
s, x = sympify(s), self.x
|
||||
|
||||
if x in s.free_symbols:
|
||||
raise ValueError("'%s' should be independent of %s" % (s, x))
|
||||
|
||||
_expr = self.truncate().subs(x, x * s)
|
||||
sfunc = self.function.subs(x, x * s)
|
||||
|
||||
return self.func(sfunc, self.args[1], _expr)
|
||||
|
||||
def _eval_term(self, pt):
|
||||
if pt == 0:
|
||||
return self.a0
|
||||
|
||||
_term = self.an.get(pt, S.Zero) * cos(pt * (pi / self.L) * self.x) \
|
||||
+ self.bn.get(pt, S.Zero) * sin(pt * (pi / self.L) * self.x)
|
||||
return _term
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, FourierSeries):
|
||||
return other.__add__(fourier_series(self.function, self.args[1],\
|
||||
finite=False))
|
||||
elif isinstance(other, FiniteFourierSeries):
|
||||
if self.period != other.period:
|
||||
raise ValueError("Both the series should have same periods")
|
||||
|
||||
x, y = self.x, other.x
|
||||
function = self.function + other.function.subs(y, x)
|
||||
|
||||
if self.x not in function.free_symbols:
|
||||
return function
|
||||
|
||||
return fourier_series(function, limits=self.args[1])
|
||||
|
||||
|
||||
def fourier_series(f, limits=None, finite=True):
|
||||
r"""Computes the Fourier trigonometric series expansion.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Fourier trigonometric series of $f(x)$ over the interval $(a, b)$
|
||||
is defined as:
|
||||
|
||||
.. math::
|
||||
\frac{a_0}{2} + \sum_{n=1}^{\infty}
|
||||
(a_n \cos(\frac{2n \pi x}{L}) + b_n \sin(\frac{2n \pi x}{L}))
|
||||
|
||||
where the coefficients are:
|
||||
|
||||
.. math::
|
||||
L = b - a
|
||||
|
||||
.. math::
|
||||
a_0 = \frac{2}{L} \int_{a}^{b}{f(x) dx}
|
||||
|
||||
.. math::
|
||||
a_n = \frac{2}{L} \int_{a}^{b}{f(x) \cos(\frac{2n \pi x}{L}) dx}
|
||||
|
||||
.. math::
|
||||
b_n = \frac{2}{L} \int_{a}^{b}{f(x) \sin(\frac{2n \pi x}{L}) dx}
|
||||
|
||||
The condition whether the function $f(x)$ given should be periodic
|
||||
or not is more than necessary, because it is sufficient to consider
|
||||
the series to be converging to $f(x)$ only in the given interval,
|
||||
not throughout the whole real line.
|
||||
|
||||
This also brings a lot of ease for the computation because
|
||||
you do not have to make $f(x)$ artificially periodic by
|
||||
wrapping it with piecewise, modulo operations,
|
||||
but you can shape the function to look like the desired periodic
|
||||
function only in the interval $(a, b)$, and the computed series will
|
||||
automatically become the series of the periodic version of $f(x)$.
|
||||
|
||||
This property is illustrated in the examples section below.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
limits : (sym, start, end), optional
|
||||
*sym* denotes the symbol the series is computed with respect to.
|
||||
|
||||
*start* and *end* denotes the start and the end of the interval
|
||||
where the fourier series converges to the given function.
|
||||
|
||||
Default range is specified as $-\pi$ and $\pi$.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
FourierSeries
|
||||
A symbolic object representing the Fourier trigonometric series.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Computing the Fourier series of $f(x) = x^2$:
|
||||
|
||||
>>> from sympy import fourier_series, pi
|
||||
>>> from sympy.abc import x
|
||||
>>> f = x**2
|
||||
>>> s = fourier_series(f, (x, -pi, pi))
|
||||
>>> s1 = s.truncate(n=3)
|
||||
>>> s1
|
||||
-4*cos(x) + cos(2*x) + pi**2/3
|
||||
|
||||
Shifting of the Fourier series:
|
||||
|
||||
>>> s.shift(1).truncate()
|
||||
-4*cos(x) + cos(2*x) + 1 + pi**2/3
|
||||
>>> s.shiftx(1).truncate()
|
||||
-4*cos(x + 1) + cos(2*x + 2) + pi**2/3
|
||||
|
||||
Scaling of the Fourier series:
|
||||
|
||||
>>> s.scale(2).truncate()
|
||||
-8*cos(x) + 2*cos(2*x) + 2*pi**2/3
|
||||
>>> s.scalex(2).truncate()
|
||||
-4*cos(2*x) + cos(4*x) + pi**2/3
|
||||
|
||||
Computing the Fourier series of $f(x) = x$:
|
||||
|
||||
This illustrates how truncating to the higher order gives better
|
||||
convergence.
|
||||
|
||||
.. plot::
|
||||
:context: reset
|
||||
:format: doctest
|
||||
:include-source: True
|
||||
|
||||
>>> from sympy import fourier_series, pi, plot
|
||||
>>> from sympy.abc import x
|
||||
>>> f = x
|
||||
>>> s = fourier_series(f, (x, -pi, pi))
|
||||
>>> s1 = s.truncate(n = 3)
|
||||
>>> s2 = s.truncate(n = 5)
|
||||
>>> s3 = s.truncate(n = 7)
|
||||
>>> p = plot(f, s1, s2, s3, (x, -pi, pi), show=False, legend=True)
|
||||
|
||||
>>> p[0].line_color = (0, 0, 0)
|
||||
>>> p[0].label = 'x'
|
||||
>>> p[1].line_color = (0.7, 0.7, 0.7)
|
||||
>>> p[1].label = 'n=3'
|
||||
>>> p[2].line_color = (0.5, 0.5, 0.5)
|
||||
>>> p[2].label = 'n=5'
|
||||
>>> p[3].line_color = (0.3, 0.3, 0.3)
|
||||
>>> p[3].label = 'n=7'
|
||||
|
||||
>>> p.show()
|
||||
|
||||
This illustrates how the series converges to different sawtooth
|
||||
waves if the different ranges are specified.
|
||||
|
||||
.. plot::
|
||||
:context: close-figs
|
||||
:format: doctest
|
||||
:include-source: True
|
||||
|
||||
>>> s1 = fourier_series(x, (x, -1, 1)).truncate(10)
|
||||
>>> s2 = fourier_series(x, (x, -pi, pi)).truncate(10)
|
||||
>>> s3 = fourier_series(x, (x, 0, 1)).truncate(10)
|
||||
>>> p = plot(x, s1, s2, s3, (x, -5, 5), show=False, legend=True)
|
||||
|
||||
>>> p[0].line_color = (0, 0, 0)
|
||||
>>> p[0].label = 'x'
|
||||
>>> p[1].line_color = (0.7, 0.7, 0.7)
|
||||
>>> p[1].label = '[-1, 1]'
|
||||
>>> p[2].line_color = (0.5, 0.5, 0.5)
|
||||
>>> p[2].label = '[-pi, pi]'
|
||||
>>> p[3].line_color = (0.3, 0.3, 0.3)
|
||||
>>> p[3].label = '[0, 1]'
|
||||
|
||||
>>> p.show()
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Computing Fourier series can be slow
|
||||
due to the integration required in computing
|
||||
an, bn.
|
||||
|
||||
It is faster to compute Fourier series of a function
|
||||
by using shifting and scaling on an already
|
||||
computed Fourier series rather than computing
|
||||
again.
|
||||
|
||||
e.g. If the Fourier series of ``x**2`` is known
|
||||
the Fourier series of ``x**2 - 1`` can be found by shifting by ``-1``.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.fourier.FourierSeries
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://mathworld.wolfram.com/FourierSeries.html
|
||||
"""
|
||||
f = sympify(f)
|
||||
|
||||
limits = _process_limits(f, limits)
|
||||
x = limits[0]
|
||||
|
||||
if x not in f.free_symbols:
|
||||
return f
|
||||
|
||||
if finite:
|
||||
L = abs(limits[2] - limits[1]) / 2
|
||||
is_finite, res_f = finite_check(f, x, L)
|
||||
if is_finite:
|
||||
return FiniteFourierSeries(f, limits, res_f)
|
||||
|
||||
n = Dummy('n')
|
||||
center = (limits[1] + limits[2]) / 2
|
||||
if center.is_zero:
|
||||
neg_f = f.subs(x, -x)
|
||||
if f == neg_f:
|
||||
a0, an = fourier_cos_seq(f, limits, n)
|
||||
bn = SeqFormula(0, (1, oo))
|
||||
return FourierSeries(f, limits, (a0, an, bn))
|
||||
elif f == -neg_f:
|
||||
a0 = S.Zero
|
||||
an = SeqFormula(0, (1, oo))
|
||||
bn = fourier_sin_seq(f, limits, n)
|
||||
return FourierSeries(f, limits, (a0, an, bn))
|
||||
a0, an = fourier_cos_seq(f, limits, n)
|
||||
bn = fourier_sin_seq(f, limits, n)
|
||||
return FourierSeries(f, limits, (a0, an, bn))
|
||||
@@ -0,0 +1,701 @@
|
||||
"""
|
||||
Limits
|
||||
======
|
||||
|
||||
Implemented according to the PhD thesis
|
||||
https://www.cybertester.com/data/gruntz.pdf, which contains very thorough
|
||||
descriptions of the algorithm including many examples. We summarize here
|
||||
the gist of it.
|
||||
|
||||
All functions are sorted according to how rapidly varying they are at
|
||||
infinity using the following rules. Any two functions f and g can be
|
||||
compared using the properties of L:
|
||||
|
||||
L=lim log|f(x)| / log|g(x)| (for x -> oo)
|
||||
|
||||
We define >, < ~ according to::
|
||||
|
||||
1. f > g .... L=+-oo
|
||||
|
||||
we say that:
|
||||
- f is greater than any power of g
|
||||
- f is more rapidly varying than g
|
||||
- f goes to infinity/zero faster than g
|
||||
|
||||
2. f < g .... L=0
|
||||
|
||||
we say that:
|
||||
- f is lower than any power of g
|
||||
|
||||
3. f ~ g .... L!=0, +-oo
|
||||
|
||||
we say that:
|
||||
- both f and g are bounded from above and below by suitable integral
|
||||
powers of the other
|
||||
|
||||
Examples
|
||||
========
|
||||
::
|
||||
2 < x < exp(x) < exp(x**2) < exp(exp(x))
|
||||
2 ~ 3 ~ -5
|
||||
x ~ x**2 ~ x**3 ~ 1/x ~ x**m ~ -x
|
||||
exp(x) ~ exp(-x) ~ exp(2x) ~ exp(x)**2 ~ exp(x+exp(-x))
|
||||
f ~ 1/f
|
||||
|
||||
So we can divide all the functions into comparability classes (x and x^2
|
||||
belong to one class, exp(x) and exp(-x) belong to some other class). In
|
||||
principle, we could compare any two functions, but in our algorithm, we
|
||||
do not compare anything below the class 2~3~-5 (for example log(x) is
|
||||
below this), so we set 2~3~-5 as the lowest comparability class.
|
||||
|
||||
Given the function f, we find the list of most rapidly varying (mrv set)
|
||||
subexpressions of it. This list belongs to the same comparability class.
|
||||
Let's say it is {exp(x), exp(2x)}. Using the rule f ~ 1/f we find an
|
||||
element "w" (either from the list or a new one) from the same
|
||||
comparability class which goes to zero at infinity. In our example we
|
||||
set w=exp(-x) (but we could also set w=exp(-2x) or w=exp(-3x) ...). We
|
||||
rewrite the mrv set using w, in our case {1/w, 1/w^2}, and substitute it
|
||||
into f. Then we expand f into a series in w::
|
||||
|
||||
f = c0*w^e0 + c1*w^e1 + ... + O(w^en), where e0<e1<...<en, c0!=0
|
||||
|
||||
but for x->oo, lim f = lim c0*w^e0, because all the other terms go to zero,
|
||||
because w goes to zero faster than the ci and ei. So::
|
||||
|
||||
for e0>0, lim f = 0
|
||||
for e0<0, lim f = +-oo (the sign depends on the sign of c0)
|
||||
for e0=0, lim f = lim c0
|
||||
|
||||
We need to recursively compute limits at several places of the algorithm, but
|
||||
as is shown in the PhD thesis, it always finishes.
|
||||
|
||||
Important functions from the implementation:
|
||||
|
||||
compare(a, b, x) compares "a" and "b" by computing the limit L.
|
||||
mrv(e, x) returns list of most rapidly varying (mrv) subexpressions of "e"
|
||||
rewrite(e, Omega, x, wsym) rewrites "e" in terms of w
|
||||
leadterm(f, x) returns the lowest power term in the series of f
|
||||
mrv_leadterm(e, x) returns the lead term (c0, e0) for e
|
||||
limitinf(e, x) computes lim e (for x->oo)
|
||||
limit(e, z, z0) computes any limit by converting it to the case x->oo
|
||||
|
||||
All the functions are really simple and straightforward except
|
||||
rewrite(), which is the most difficult/complex part of the algorithm.
|
||||
When the algorithm fails, the bugs are usually in the series expansion
|
||||
(i.e. in SymPy) or in rewrite.
|
||||
|
||||
This code is almost exact rewrite of the Maple code inside the Gruntz
|
||||
thesis.
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Because the gruntz algorithm is highly recursive, it's difficult to
|
||||
figure out what went wrong inside a debugger. Instead, turn on nice
|
||||
debug prints by defining the environment variable SYMPY_DEBUG. For
|
||||
example:
|
||||
|
||||
[user@localhost]: SYMPY_DEBUG=True ./bin/isympy
|
||||
|
||||
In [1]: limit(sin(x)/x, x, 0)
|
||||
limitinf(_x*sin(1/_x), _x) = 1
|
||||
+-mrv_leadterm(_x*sin(1/_x), _x) = (1, 0)
|
||||
| +-mrv(_x*sin(1/_x), _x) = set([_x])
|
||||
| | +-mrv(_x, _x) = set([_x])
|
||||
| | +-mrv(sin(1/_x), _x) = set([_x])
|
||||
| | +-mrv(1/_x, _x) = set([_x])
|
||||
| | +-mrv(_x, _x) = set([_x])
|
||||
| +-mrv_leadterm(exp(_x)*sin(exp(-_x)), _x, set([exp(_x)])) = (1, 0)
|
||||
| +-rewrite(exp(_x)*sin(exp(-_x)), set([exp(_x)]), _x, _w) = (1/_w*sin(_w), -_x)
|
||||
| +-sign(_x, _x) = 1
|
||||
| +-mrv_leadterm(1, _x) = (1, 0)
|
||||
+-sign(0, _x) = 0
|
||||
+-limitinf(1, _x) = 1
|
||||
|
||||
And check manually which line is wrong. Then go to the source code and
|
||||
debug this function to figure out the exact problem.
|
||||
|
||||
"""
|
||||
from functools import reduce
|
||||
|
||||
from sympy.core import Basic, S, Mul, PoleError
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.function import AppliedUndef
|
||||
from sympy.core.intfunc import ilcm
|
||||
from sympy.core.numbers import I, oo
|
||||
from sympy.core.symbol import Dummy, Wild
|
||||
from sympy.core.traversal import bottom_up
|
||||
|
||||
from sympy.functions import log, exp, sign as _sign
|
||||
from sympy.series.order import Order
|
||||
from sympy.utilities.misc import debug_decorator as debug
|
||||
from sympy.utilities.timeutils import timethis
|
||||
|
||||
timeit = timethis('gruntz')
|
||||
|
||||
|
||||
def compare(a, b, x):
|
||||
"""Returns "<" if a<b, "=" for a == b, ">" for a>b"""
|
||||
# log(exp(...)) must always be simplified here for termination
|
||||
la, lb = log(a), log(b)
|
||||
if isinstance(a, Basic) and (isinstance(a, exp) or (a.is_Pow and a.base == S.Exp1)):
|
||||
la = a.exp
|
||||
if isinstance(b, Basic) and (isinstance(b, exp) or (b.is_Pow and b.base == S.Exp1)):
|
||||
lb = b.exp
|
||||
|
||||
c = limitinf(la/lb, x)
|
||||
if c == 0:
|
||||
return "<"
|
||||
elif c.is_infinite:
|
||||
return ">"
|
||||
else:
|
||||
return "="
|
||||
|
||||
|
||||
class SubsSet(dict):
|
||||
"""
|
||||
Stores (expr, dummy) pairs, and how to rewrite expr-s.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The gruntz algorithm needs to rewrite certain expressions in term of a new
|
||||
variable w. We cannot use subs, because it is just too smart for us. For
|
||||
example::
|
||||
|
||||
> Omega=[exp(exp(_p - exp(-_p))/(1 - 1/_p)), exp(exp(_p))]
|
||||
> O2=[exp(-exp(_p) + exp(-exp(-_p))*exp(_p)/(1 - 1/_p))/_w, 1/_w]
|
||||
> e = exp(exp(_p - exp(-_p))/(1 - 1/_p)) - exp(exp(_p))
|
||||
> e.subs(Omega[0],O2[0]).subs(Omega[1],O2[1])
|
||||
-1/w + exp(exp(p)*exp(-exp(-p))/(1 - 1/p))
|
||||
|
||||
is really not what we want!
|
||||
|
||||
So we do it the hard way and keep track of all the things we potentially
|
||||
want to substitute by dummy variables. Consider the expression::
|
||||
|
||||
exp(x - exp(-x)) + exp(x) + x.
|
||||
|
||||
The mrv set is {exp(x), exp(-x), exp(x - exp(-x))}.
|
||||
We introduce corresponding dummy variables d1, d2, d3 and rewrite::
|
||||
|
||||
d3 + d1 + x.
|
||||
|
||||
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 chosen to be exp(-x),
|
||||
i.e. d2. The correct rewriting then is::
|
||||
|
||||
exp(-w)/w + 1/w + x.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.rewrites = {}
|
||||
|
||||
def __repr__(self):
|
||||
return super().__repr__() + ', ' + self.rewrites.__repr__()
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self:
|
||||
self[key] = Dummy()
|
||||
return dict.__getitem__(self, key)
|
||||
|
||||
def do_subs(self, e):
|
||||
"""Substitute the variables with expressions"""
|
||||
for expr, var in self.items():
|
||||
e = e.xreplace({var: expr})
|
||||
return e
|
||||
|
||||
def meets(self, s2):
|
||||
"""Tell whether or not self and s2 have non-empty intersection"""
|
||||
return set(self.keys()).intersection(list(s2.keys())) != set()
|
||||
|
||||
def union(self, s2, exps=None):
|
||||
"""Compute the union of self and s2, adjusting exps"""
|
||||
res = self.copy()
|
||||
tr = {}
|
||||
for expr, var in s2.items():
|
||||
if expr in self:
|
||||
if exps:
|
||||
exps = exps.xreplace({var: res[expr]})
|
||||
tr[var] = res[expr]
|
||||
else:
|
||||
res[expr] = var
|
||||
for var, rewr in s2.rewrites.items():
|
||||
res.rewrites[var] = rewr.xreplace(tr)
|
||||
return res, exps
|
||||
|
||||
def copy(self):
|
||||
"""Create a shallow copy of SubsSet"""
|
||||
r = SubsSet()
|
||||
r.rewrites = self.rewrites.copy()
|
||||
for expr, var in self.items():
|
||||
r[expr] = var
|
||||
return r
|
||||
|
||||
|
||||
@debug
|
||||
def mrv(e, x):
|
||||
"""Returns a SubsSet of most rapidly varying (mrv) subexpressions of 'e',
|
||||
and e rewritten in terms of these"""
|
||||
from sympy.simplify.powsimp import powsimp
|
||||
e = powsimp(e, deep=True, combine='exp')
|
||||
if not isinstance(e, Basic):
|
||||
raise TypeError("e should be an instance of Basic")
|
||||
if not e.has(x):
|
||||
return SubsSet(), e
|
||||
elif e == x:
|
||||
s = SubsSet()
|
||||
return s, s[x]
|
||||
elif e.is_Mul or e.is_Add:
|
||||
i, d = e.as_independent(x) # throw away x-independent terms
|
||||
if d.func != e.func:
|
||||
s, expr = mrv(d, x)
|
||||
return s, e.func(i, expr)
|
||||
a, b = d.as_two_terms()
|
||||
s1, e1 = mrv(a, x)
|
||||
s2, e2 = mrv(b, x)
|
||||
return mrv_max1(s1, s2, e.func(i, e1, e2), x)
|
||||
elif e.is_Pow and e.base != S.Exp1:
|
||||
e1 = S.One
|
||||
while e.is_Pow:
|
||||
b1 = e.base
|
||||
e1 *= e.exp
|
||||
e = b1
|
||||
if b1 == 1:
|
||||
return SubsSet(), b1
|
||||
if e1.has(x):
|
||||
return mrv(exp(e1*log(b1)), x)
|
||||
else:
|
||||
s, expr = mrv(b1, x)
|
||||
return s, expr**e1
|
||||
elif isinstance(e, log):
|
||||
s, expr = mrv(e.args[0], x)
|
||||
return s, log(expr)
|
||||
elif isinstance(e, exp) or (e.is_Pow and e.base == S.Exp1):
|
||||
# We know from the theory of this algorithm that exp(log(...)) may always
|
||||
# be simplified here, and doing so is vital for termination.
|
||||
if isinstance(e.exp, log):
|
||||
return mrv(e.exp.args[0], x)
|
||||
# if a product has an infinite factor the result will be
|
||||
# infinite if there is no zero, otherwise NaN; here, we
|
||||
# consider the result infinite if any factor is infinite
|
||||
li = limitinf(e.exp, x)
|
||||
if any(_.is_infinite for _ in Mul.make_args(li)):
|
||||
s1 = SubsSet()
|
||||
e1 = s1[e]
|
||||
s2, e2 = mrv(e.exp, x)
|
||||
su = s1.union(s2)[0]
|
||||
su.rewrites[e1] = exp(e2)
|
||||
return mrv_max3(s1, e1, s2, exp(e2), su, e1, x)
|
||||
else:
|
||||
s, expr = mrv(e.exp, x)
|
||||
return s, exp(expr)
|
||||
elif isinstance(e, AppliedUndef):
|
||||
raise ValueError("MRV set computation for UndefinedFunction is not allowed")
|
||||
elif e.is_Function:
|
||||
l = [mrv(a, x) for a in e.args]
|
||||
l2 = [s for (s, _) in l if s != SubsSet()]
|
||||
if len(l2) != 1:
|
||||
# e.g. something like BesselJ(x, x)
|
||||
raise NotImplementedError("MRV set computation for functions in"
|
||||
" several variables not implemented.")
|
||||
s, ss = l2[0], SubsSet()
|
||||
args = [ss.do_subs(x[1]) for x in l]
|
||||
return s, e.func(*args)
|
||||
elif e.is_Derivative:
|
||||
raise NotImplementedError("MRV set computation for derivatives"
|
||||
" not implemented yet.")
|
||||
raise NotImplementedError(
|
||||
"Don't know how to calculate the mrv of '%s'" % e)
|
||||
|
||||
|
||||
def mrv_max3(f, expsf, g, expsg, union, expsboth, x):
|
||||
"""
|
||||
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].
|
||||
"""
|
||||
if not isinstance(f, SubsSet):
|
||||
raise TypeError("f should be an instance of SubsSet")
|
||||
if not isinstance(g, SubsSet):
|
||||
raise TypeError("g should be an instance of SubsSet")
|
||||
if f == SubsSet():
|
||||
return g, expsg
|
||||
elif g == SubsSet():
|
||||
return f, expsf
|
||||
elif f.meets(g):
|
||||
return union, expsboth
|
||||
|
||||
c = compare(list(f.keys())[0], list(g.keys())[0], x)
|
||||
if c == ">":
|
||||
return f, expsf
|
||||
elif c == "<":
|
||||
return g, expsg
|
||||
else:
|
||||
if c != "=":
|
||||
raise ValueError("c should be =")
|
||||
return union, expsboth
|
||||
|
||||
|
||||
def mrv_max1(f, g, exps, x):
|
||||
"""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.
|
||||
"""
|
||||
u, b = f.union(g, exps)
|
||||
return mrv_max3(f, g.do_subs(exps), g, f.do_subs(exps),
|
||||
u, b, x)
|
||||
|
||||
|
||||
@debug
|
||||
@cacheit
|
||||
@timeit
|
||||
def sign(e, x):
|
||||
"""
|
||||
Returns a sign of an expression e(x) for x->oo.
|
||||
|
||||
::
|
||||
|
||||
e > 0 for x sufficiently large ... 1
|
||||
e == 0 for x sufficiently large ... 0
|
||||
e < 0 for x sufficiently large ... -1
|
||||
|
||||
The result of this function is currently undefined if e changes sign
|
||||
arbitrarily often for arbitrarily large x (e.g. sin(x)).
|
||||
|
||||
Note that this returns zero only if e is *constantly* zero
|
||||
for x sufficiently large. [If e is constant, of course, this is just
|
||||
the same thing as the sign of e.]
|
||||
"""
|
||||
if not isinstance(e, Basic):
|
||||
raise TypeError("e should be an instance of Basic")
|
||||
|
||||
if e.is_positive:
|
||||
return 1
|
||||
elif e.is_negative:
|
||||
return -1
|
||||
elif e.is_zero:
|
||||
return 0
|
||||
|
||||
elif not e.has(x):
|
||||
from sympy.simplify import logcombine
|
||||
e = logcombine(e)
|
||||
return _sign(e)
|
||||
elif e == x:
|
||||
return 1
|
||||
elif e.is_Mul:
|
||||
a, b = e.as_two_terms()
|
||||
sa = sign(a, x)
|
||||
if not sa:
|
||||
return 0
|
||||
return sa * sign(b, x)
|
||||
elif isinstance(e, exp):
|
||||
return 1
|
||||
elif e.is_Pow:
|
||||
if e.base == S.Exp1:
|
||||
return 1
|
||||
s = sign(e.base, x)
|
||||
if s == 1:
|
||||
return 1
|
||||
if e.exp.is_Integer:
|
||||
return s**e.exp
|
||||
elif isinstance(e, log) and e.args[0].is_positive:
|
||||
return sign(e.args[0] - 1, x)
|
||||
|
||||
# if all else fails, do it the hard way
|
||||
c0, e0 = mrv_leadterm(e, x)
|
||||
return sign(c0, x)
|
||||
|
||||
|
||||
@debug
|
||||
@timeit
|
||||
@cacheit
|
||||
def limitinf(e, x):
|
||||
"""Limit e(x) for x-> oo."""
|
||||
# rewrite e in terms of tractable functions only
|
||||
|
||||
old = e
|
||||
if not e.has(x):
|
||||
return e # e is a constant
|
||||
from sympy.simplify.powsimp import powdenest
|
||||
from sympy.calculus.util import AccumBounds
|
||||
if e.has(Order):
|
||||
e = e.expand().removeO()
|
||||
if not x.is_positive or x.is_integer:
|
||||
# We make sure that x.is_positive is True and x.is_integer is None
|
||||
# so we get all the correct mathematical behavior from the expression.
|
||||
# We need a fresh variable.
|
||||
p = Dummy('p', positive=True)
|
||||
e = e.subs(x, p)
|
||||
x = p
|
||||
e = e.rewrite('tractable', deep=True, limitvar=x)
|
||||
e = powdenest(e)
|
||||
if isinstance(e, AccumBounds):
|
||||
if mrv_leadterm(e.min, x) != mrv_leadterm(e.max, x):
|
||||
raise NotImplementedError
|
||||
c0, e0 = mrv_leadterm(e.min, x)
|
||||
else:
|
||||
c0, e0 = mrv_leadterm(e, x)
|
||||
sig = sign(e0, x)
|
||||
if sig == 1:
|
||||
return S.Zero # e0>0: lim f = 0
|
||||
elif sig == -1: # e0<0: lim f = +-oo (the sign depends on the sign of c0)
|
||||
if c0.match(I*Wild("a", exclude=[I])):
|
||||
return c0*oo
|
||||
s = sign(c0, x)
|
||||
# the leading term shouldn't be 0:
|
||||
if s == 0:
|
||||
raise ValueError("Leading term should not be 0")
|
||||
return s*oo
|
||||
elif sig == 0:
|
||||
if c0 == old:
|
||||
c0 = c0.cancel()
|
||||
return limitinf(c0, x) # e0=0: lim f = lim c0
|
||||
else:
|
||||
raise ValueError("{} could not be evaluated".format(sig))
|
||||
|
||||
|
||||
def moveup2(s, x):
|
||||
r = SubsSet()
|
||||
for expr, var in s.items():
|
||||
r[expr.xreplace({x: exp(x)})] = var
|
||||
for var, expr in s.rewrites.items():
|
||||
r.rewrites[var] = s.rewrites[var].xreplace({x: exp(x)})
|
||||
return r
|
||||
|
||||
|
||||
def moveup(l, x):
|
||||
return [e.xreplace({x: exp(x)}) for e in l]
|
||||
|
||||
|
||||
@debug
|
||||
@timeit
|
||||
@cacheit
|
||||
def mrv_leadterm(e, x):
|
||||
"""Returns (c0, e0) for e."""
|
||||
Omega = SubsSet()
|
||||
if not e.has(x):
|
||||
return (e, S.Zero)
|
||||
if Omega == SubsSet():
|
||||
Omega, exps = mrv(e, x)
|
||||
if not Omega:
|
||||
# e really does not depend on x after simplification
|
||||
return exps, S.Zero
|
||||
if x in Omega:
|
||||
# move the whole omega up (exponentiate each term):
|
||||
Omega_up = moveup2(Omega, x)
|
||||
exps_up = moveup([exps], x)[0]
|
||||
# NOTE: there is no need to move this down!
|
||||
Omega = Omega_up
|
||||
exps = exps_up
|
||||
#
|
||||
# The positive dummy, w, is used here so log(w*2) etc. will expand;
|
||||
# a unique dummy is needed in this algorithm
|
||||
#
|
||||
# For limits of complex functions, the algorithm would have to be
|
||||
# improved, or just find limits of Re and Im components separately.
|
||||
#
|
||||
w = Dummy("w", positive=True)
|
||||
f, logw = rewrite(exps, Omega, x, w)
|
||||
|
||||
# Ensure expressions of the form exp(log(...)) don't get simplified automatically in the previous steps.
|
||||
# see: https://github.com/sympy/sympy/issues/15323#issuecomment-478639399
|
||||
f = f.replace(lambda f: f.is_Pow and f.has(x), lambda f: exp(log(f.base)*f.exp))
|
||||
|
||||
try:
|
||||
lt = f.leadterm(w, logx=logw)
|
||||
except (NotImplementedError, PoleError, ValueError):
|
||||
n0 = 1
|
||||
_series = Order(1)
|
||||
incr = S.One
|
||||
while _series.is_Order:
|
||||
_series = f._eval_nseries(w, n=n0+incr, logx=logw)
|
||||
incr *= 2
|
||||
series = _series.expand().removeO()
|
||||
try:
|
||||
lt = series.leadterm(w, logx=logw)
|
||||
except (NotImplementedError, PoleError, ValueError):
|
||||
lt = f.as_coeff_exponent(w)
|
||||
if lt[0].has(w):
|
||||
base = f.as_base_exp()[0].as_coeff_exponent(w)
|
||||
ex = f.as_base_exp()[1]
|
||||
lt = (base[0]**ex, base[1]*ex)
|
||||
return (lt[0].subs(log(w), logw), lt[1])
|
||||
|
||||
|
||||
def build_expression_tree(Omega, rewrites):
|
||||
r""" Helper function for rewrite.
|
||||
|
||||
We need to sort Omega (mrv set) so that we replace an expression before
|
||||
we replace any expression in terms of which it has to be rewritten::
|
||||
|
||||
e1 ---> e2 ---> e3
|
||||
\
|
||||
-> e4
|
||||
|
||||
Here we can do e1, e2, e3, e4 or e1, e2, e4, e3.
|
||||
To do this we assemble the nodes into a tree, and sort them by height.
|
||||
|
||||
This function builds the tree, rewrites then sorts the nodes.
|
||||
"""
|
||||
class Node:
|
||||
def __init__(self):
|
||||
self.before = []
|
||||
self.expr = None
|
||||
self.var = None
|
||||
def ht(self):
|
||||
return reduce(lambda x, y: x + y,
|
||||
[x.ht() for x in self.before], 1)
|
||||
nodes = {}
|
||||
for expr, v in Omega:
|
||||
n = Node()
|
||||
n.var = v
|
||||
n.expr = expr
|
||||
nodes[v] = n
|
||||
for _, v in Omega:
|
||||
if v in rewrites:
|
||||
n = nodes[v]
|
||||
r = rewrites[v]
|
||||
for _, v2 in Omega:
|
||||
if r.has(v2):
|
||||
n.before.append(nodes[v2])
|
||||
|
||||
return nodes
|
||||
|
||||
|
||||
@debug
|
||||
@timeit
|
||||
def rewrite(e, Omega, x, wsym):
|
||||
"""e(x) ... the function
|
||||
Omega ... the mrv set
|
||||
wsym ... the symbol which is going to be used for w
|
||||
|
||||
Returns the rewritten e in terms of w and log(w). See test_rewrite1()
|
||||
for examples and correct results.
|
||||
"""
|
||||
|
||||
from sympy import AccumBounds
|
||||
if not isinstance(Omega, SubsSet):
|
||||
raise TypeError("Omega should be an instance of SubsSet")
|
||||
if len(Omega) == 0:
|
||||
raise ValueError("Length cannot be 0")
|
||||
# all items in Omega must be exponentials
|
||||
for t in Omega.keys():
|
||||
if not isinstance(t, exp):
|
||||
raise ValueError("Value should be exp")
|
||||
rewrites = Omega.rewrites
|
||||
Omega = list(Omega.items())
|
||||
|
||||
nodes = build_expression_tree(Omega, rewrites)
|
||||
Omega.sort(key=lambda x: nodes[x[1]].ht(), reverse=True)
|
||||
|
||||
# make sure we know the sign of each exp() term; after the loop,
|
||||
# g is going to be the "w" - the simplest one in the mrv set
|
||||
for g, _ in Omega:
|
||||
sig = sign(g.exp, x)
|
||||
if sig != 1 and sig != -1 and not sig.has(AccumBounds):
|
||||
raise NotImplementedError('Result depends on the sign of %s' % sig)
|
||||
if sig == 1:
|
||||
wsym = 1/wsym # if g goes to oo, substitute 1/w
|
||||
# O2 is a list, which results by rewriting each item in Omega using "w"
|
||||
O2 = []
|
||||
denominators = []
|
||||
for f, var in Omega:
|
||||
c = limitinf(f.exp/g.exp, x)
|
||||
if c.is_Rational:
|
||||
denominators.append(c.q)
|
||||
arg = f.exp
|
||||
if var in rewrites:
|
||||
if not isinstance(rewrites[var], exp):
|
||||
raise ValueError("Value should be exp")
|
||||
arg = rewrites[var].args[0]
|
||||
O2.append((var, exp((arg - c*g.exp))*wsym**c))
|
||||
|
||||
# Remember that Omega contains subexpressions of "e". So now we find
|
||||
# them in "e" and substitute them for our rewriting, stored in O2
|
||||
|
||||
# the following powsimp is necessary to automatically combine exponentials,
|
||||
# so that the .xreplace() below succeeds:
|
||||
# TODO this should not be necessary
|
||||
from sympy.simplify.powsimp import powsimp
|
||||
f = powsimp(e, deep=True, combine='exp')
|
||||
for a, b in O2:
|
||||
f = f.xreplace({a: b})
|
||||
|
||||
for _, var in Omega:
|
||||
assert not f.has(var)
|
||||
|
||||
# finally compute the logarithm of w (logw).
|
||||
logw = g.exp
|
||||
if sig == 1:
|
||||
logw = -logw # log(w)->log(1/w)=-log(w)
|
||||
|
||||
# Some parts of SymPy have difficulty computing series expansions with
|
||||
# non-integral exponents. The following heuristic improves the situation:
|
||||
exponent = reduce(ilcm, denominators, 1)
|
||||
f = f.subs({wsym: wsym**exponent})
|
||||
logw /= exponent
|
||||
|
||||
# bottom_up function is required for a specific case - when f is
|
||||
# -exp(p/(p + 1)) + exp(-p**2/(p + 1) + p). No current simplification
|
||||
# methods reduce this to 0 while not expanding polynomials.
|
||||
f = bottom_up(f, lambda w: getattr(w, 'normal', lambda: w)())
|
||||
|
||||
return f, logw
|
||||
|
||||
|
||||
def gruntz(e, z, z0, dir="+"):
|
||||
"""
|
||||
Compute the limit of e(z) at the point z0 using the Gruntz algorithm.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
``z0`` can be any expression, including oo and -oo.
|
||||
|
||||
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 does not 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.
|
||||
"""
|
||||
if not z.is_symbol:
|
||||
raise NotImplementedError("Second argument must be a Symbol")
|
||||
|
||||
# convert all limits to the limit z->oo; sign of z is handled in limitinf
|
||||
r = None
|
||||
if z0 in (oo, I*oo):
|
||||
e0 = e
|
||||
elif z0 in (-oo, -I*oo):
|
||||
e0 = e.subs(z, -z)
|
||||
else:
|
||||
if str(dir) == "-":
|
||||
e0 = e.subs(z, z0 - 1/z)
|
||||
elif str(dir) == "+":
|
||||
e0 = e.subs(z, z0 + 1/z)
|
||||
else:
|
||||
raise NotImplementedError("dir must be '+' or '-'")
|
||||
|
||||
r = limitinf(e0, z)
|
||||
|
||||
# This is a bit of a heuristic for nice results... we always rewrite
|
||||
# tractable functions in terms of familiar intractable ones.
|
||||
# It might be nicer to rewrite the exactly to what they were initially,
|
||||
# but that would take some work to implement.
|
||||
return r.rewrite('intractable', deep=True)
|
||||
@@ -0,0 +1,51 @@
|
||||
def finite_diff(expression, variable, increment=1):
|
||||
"""
|
||||
Takes as input a polynomial expression and the variable used to construct
|
||||
it and returns the difference between function's value when the input is
|
||||
incremented to 1 and the original function value. If you want an increment
|
||||
other than one supply it as a third argument.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y, z
|
||||
>>> from sympy.series.kauers import finite_diff
|
||||
>>> finite_diff(x**2, x)
|
||||
2*x + 1
|
||||
>>> finite_diff(y**3 + 2*y**2 + 3*y + 4, y)
|
||||
3*y**2 + 7*y + 6
|
||||
>>> finite_diff(x**2 + 3*x + 8, x, 2)
|
||||
4*x + 10
|
||||
>>> finite_diff(z**3 + 8*z, z, 3)
|
||||
9*z**2 + 27*z + 51
|
||||
"""
|
||||
expression = expression.expand()
|
||||
expression2 = expression.subs(variable, variable + increment)
|
||||
expression2 = expression2.expand()
|
||||
return expression2 - expression
|
||||
|
||||
def finite_diff_kauers(sum):
|
||||
"""
|
||||
Takes as input a Sum instance and returns the difference between the sum
|
||||
with the upper index incremented by 1 and the original sum. For example,
|
||||
if S(n) is a sum, then finite_diff_kauers will return S(n + 1) - S(n).
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.series.kauers import finite_diff_kauers
|
||||
>>> from sympy import Sum
|
||||
>>> from sympy.abc import x, y, m, n, k
|
||||
>>> finite_diff_kauers(Sum(k, (k, 1, n)))
|
||||
n + 1
|
||||
>>> finite_diff_kauers(Sum(1/k, (k, 1, n)))
|
||||
1/(n + 1)
|
||||
>>> finite_diff_kauers(Sum((x*y**2), (x, 1, n), (y, 1, m)))
|
||||
(m + 1)**2*(n + 1)
|
||||
>>> finite_diff_kauers(Sum((x*y), (x, 1, m), (y, 1, n)))
|
||||
(m + 1)*(n + 1)
|
||||
"""
|
||||
function = sum.function
|
||||
for l in sum.limits:
|
||||
function = function.subs(l[0], l[- 1] + 1)
|
||||
return function
|
||||
@@ -0,0 +1,394 @@
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
from sympy.core import S, Symbol, Add, sympify, Expr, PoleError, Mul
|
||||
from sympy.core.exprtools import factor_terms
|
||||
from sympy.core.numbers import Float, _illegal
|
||||
from sympy.core.function import AppliedUndef
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.complexes import (Abs, sign, arg, re)
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.polys import PolynomialError, factor
|
||||
from sympy.series.order import Order
|
||||
from .gruntz import gruntz
|
||||
|
||||
def limit(e, z, z0, dir="+"):
|
||||
"""Computes the limit of ``e(z)`` at the point ``z0``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
e : expression, the limit of which is to be taken
|
||||
|
||||
z : symbol representing the variable in the limit.
|
||||
Other symbols are treated as constants. Multivariate limits
|
||||
are not supported.
|
||||
|
||||
z0 : the value toward which ``z`` tends. Can be any expression,
|
||||
including ``oo`` and ``-oo``.
|
||||
|
||||
dir : string, optional (default: "+")
|
||||
The limit is bi-directional if ``dir="+-"``, from the right
|
||||
(z->z0+) if ``dir="+"``, and from the left (z->z0-) if
|
||||
``dir="-"``. For infinite ``z0`` (``oo`` or ``-oo``), the ``dir``
|
||||
argument is determined from the direction of the infinity
|
||||
(i.e., ``dir="-"`` for ``oo``).
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import limit, sin, oo
|
||||
>>> from sympy.abc import x
|
||||
>>> limit(sin(x)/x, x, 0)
|
||||
1
|
||||
>>> limit(1/x, x, 0) # default dir='+'
|
||||
oo
|
||||
>>> limit(1/x, x, 0, dir="-")
|
||||
-oo
|
||||
>>> limit(1/x, x, 0, dir='+-')
|
||||
zoo
|
||||
>>> limit(1/x, x, oo)
|
||||
0
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
First we try some heuristics for easy and frequent cases like "x", "1/x",
|
||||
"x**2" and similar, so that it's fast. For all other cases, we use the
|
||||
Gruntz algorithm (see the gruntz() function).
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
limit_seq : returns the limit of a sequence.
|
||||
"""
|
||||
|
||||
return Limit(e, z, z0, dir).doit(deep=False)
|
||||
|
||||
|
||||
def heuristics(e, z, z0, dir):
|
||||
"""Computes the limit of an expression term-wise.
|
||||
Parameters are the same as for the ``limit`` function.
|
||||
Works with the arguments of expression ``e`` one by one, computing
|
||||
the limit of each and then combining the results. This approach
|
||||
works only for simple limits, but it is fast.
|
||||
"""
|
||||
|
||||
rv = None
|
||||
if z0 is S.Infinity:
|
||||
rv = limit(e.subs(z, 1/z), z, S.Zero, "+")
|
||||
if isinstance(rv, Limit):
|
||||
return
|
||||
elif (e.is_Mul or e.is_Add or e.is_Pow or (e.is_Function and not isinstance(e, AppliedUndef))):
|
||||
r = []
|
||||
from sympy.simplify.simplify import together
|
||||
for a in e.args:
|
||||
l = limit(a, z, z0, dir)
|
||||
if l.has(S.Infinity) and l.is_finite is None:
|
||||
if isinstance(e, Add):
|
||||
m = factor_terms(e)
|
||||
if not isinstance(m, Mul): # try together
|
||||
m = together(m)
|
||||
if not isinstance(m, Mul): # try factor if the previous methods failed
|
||||
m = factor(e)
|
||||
if isinstance(m, Mul):
|
||||
return heuristics(m, z, z0, dir)
|
||||
return
|
||||
return
|
||||
elif isinstance(l, Limit):
|
||||
return
|
||||
elif l is S.NaN:
|
||||
return
|
||||
else:
|
||||
r.append(l)
|
||||
if r:
|
||||
rv = e.func(*r)
|
||||
if rv is S.NaN and e.is_Mul and any(isinstance(rr, AccumBounds) for rr in r):
|
||||
r2 = []
|
||||
e2 = []
|
||||
for ii, rval in enumerate(r):
|
||||
if isinstance(rval, AccumBounds):
|
||||
r2.append(rval)
|
||||
else:
|
||||
e2.append(e.args[ii])
|
||||
|
||||
if len(e2) > 0:
|
||||
e3 = Mul(*e2).simplify()
|
||||
l = limit(e3, z, z0, dir)
|
||||
rv = l * Mul(*r2)
|
||||
|
||||
if rv is S.NaN:
|
||||
try:
|
||||
from sympy.simplify.ratsimp import ratsimp
|
||||
rat_e = ratsimp(e)
|
||||
except PolynomialError:
|
||||
return
|
||||
if rat_e is S.NaN or rat_e == e:
|
||||
return
|
||||
return limit(rat_e, z, z0, dir)
|
||||
return rv
|
||||
|
||||
|
||||
class Limit(Expr):
|
||||
"""Represents an unevaluated limit.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Limit, sin
|
||||
>>> from sympy.abc import x
|
||||
>>> Limit(sin(x)/x, x, 0)
|
||||
Limit(sin(x)/x, x, 0, dir='+')
|
||||
>>> Limit(1/x, x, 0, dir="-")
|
||||
Limit(1/x, x, 0, dir='-')
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, e, z, z0, dir="+"):
|
||||
e = sympify(e)
|
||||
z = sympify(z)
|
||||
z0 = sympify(z0)
|
||||
|
||||
if z0 in (S.Infinity, S.ImaginaryUnit*S.Infinity):
|
||||
dir = "-"
|
||||
elif z0 in (S.NegativeInfinity, S.ImaginaryUnit*S.NegativeInfinity):
|
||||
dir = "+"
|
||||
|
||||
if(z0.has(z)):
|
||||
raise NotImplementedError("Limits approaching a variable point are"
|
||||
" not supported (%s -> %s)" % (z, z0))
|
||||
if isinstance(dir, str):
|
||||
dir = Symbol(dir)
|
||||
elif not isinstance(dir, Symbol):
|
||||
raise TypeError("direction must be of type basestring or "
|
||||
"Symbol, not %s" % type(dir))
|
||||
if str(dir) not in ('+', '-', '+-'):
|
||||
raise ValueError("direction must be one of '+', '-' "
|
||||
"or '+-', not %s" % dir)
|
||||
|
||||
obj = Expr.__new__(cls)
|
||||
obj._args = (e, z, z0, dir)
|
||||
return obj
|
||||
|
||||
|
||||
@property
|
||||
def free_symbols(self):
|
||||
e = self.args[0]
|
||||
isyms = e.free_symbols
|
||||
isyms.difference_update(self.args[1].free_symbols)
|
||||
isyms.update(self.args[2].free_symbols)
|
||||
return isyms
|
||||
|
||||
|
||||
def pow_heuristics(self, e):
|
||||
_, z, z0, _ = self.args
|
||||
b1, e1 = e.base, e.exp
|
||||
if not b1.has(z):
|
||||
res = limit(e1*log(b1), z, z0)
|
||||
return exp(res)
|
||||
|
||||
ex_lim = limit(e1, z, z0)
|
||||
base_lim = limit(b1, z, z0)
|
||||
|
||||
if base_lim is S.One:
|
||||
if ex_lim in (S.Infinity, S.NegativeInfinity):
|
||||
res = limit(e1*(b1 - 1), z, z0)
|
||||
return exp(res)
|
||||
if base_lim is S.NegativeInfinity and ex_lim is S.Infinity:
|
||||
return S.ComplexInfinity
|
||||
|
||||
|
||||
def doit(self, **hints):
|
||||
"""Evaluates the limit.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
deep : bool, optional (default: True)
|
||||
Invoke the ``doit`` method of the expressions involved before
|
||||
taking the limit.
|
||||
|
||||
hints : optional keyword arguments
|
||||
To be passed to ``doit`` methods; only used if deep is True.
|
||||
"""
|
||||
|
||||
e, z, z0, dir = self.args
|
||||
|
||||
if str(dir) == '+-':
|
||||
r = limit(e, z, z0, dir='+')
|
||||
l = limit(e, z, z0, dir='-')
|
||||
if isinstance(r, Limit) and isinstance(l, Limit):
|
||||
if r.args[0] == l.args[0]:
|
||||
return self
|
||||
if r == l:
|
||||
return l
|
||||
if r.is_infinite and l.is_infinite:
|
||||
return S.ComplexInfinity
|
||||
raise ValueError("The limit does not exist since "
|
||||
"left hand limit = %s and right hand limit = %s"
|
||||
% (l, r))
|
||||
|
||||
if z0 is S.ComplexInfinity:
|
||||
raise NotImplementedError("Limits at complex "
|
||||
"infinity are not implemented")
|
||||
|
||||
if z0.is_infinite:
|
||||
cdir = sign(z0)
|
||||
cdir = cdir/abs(cdir)
|
||||
e = e.subs(z, cdir*z)
|
||||
dir = "-"
|
||||
z0 = S.Infinity
|
||||
|
||||
if hints.get('deep', True):
|
||||
e = e.doit(**hints)
|
||||
z = z.doit(**hints)
|
||||
z0 = z0.doit(**hints)
|
||||
|
||||
if e == z:
|
||||
return z0
|
||||
|
||||
if not e.has(z):
|
||||
return e
|
||||
|
||||
if z0 is S.NaN:
|
||||
return S.NaN
|
||||
|
||||
if e.has(*_illegal):
|
||||
return self
|
||||
|
||||
if e.is_Order:
|
||||
return Order(limit(e.expr, z, z0), *e.args[1:])
|
||||
|
||||
cdir = S.Zero
|
||||
if str(dir) == "+":
|
||||
cdir = S.One
|
||||
elif str(dir) == "-":
|
||||
cdir = S.NegativeOne
|
||||
|
||||
def set_signs(expr):
|
||||
if not expr.args:
|
||||
return expr
|
||||
newargs = tuple(set_signs(arg) for arg in expr.args)
|
||||
if newargs != expr.args:
|
||||
expr = expr.func(*newargs)
|
||||
abs_flag = isinstance(expr, Abs)
|
||||
arg_flag = isinstance(expr, arg)
|
||||
sign_flag = isinstance(expr, sign)
|
||||
if abs_flag or sign_flag or arg_flag:
|
||||
try:
|
||||
sig = limit(expr.args[0], z, z0, dir)
|
||||
if sig.is_zero:
|
||||
sig = limit(1/expr.args[0], z, z0, dir)
|
||||
except NotImplementedError:
|
||||
return expr
|
||||
else:
|
||||
if sig.is_extended_real:
|
||||
if (sig < 0) == True:
|
||||
return (-expr.args[0] if abs_flag else
|
||||
S.NegativeOne if sign_flag else S.Pi)
|
||||
elif (sig > 0) == True:
|
||||
return (expr.args[0] if abs_flag else
|
||||
S.One if sign_flag else S.Zero)
|
||||
return expr
|
||||
|
||||
if e.has(Float):
|
||||
# Convert floats like 0.5 to exact SymPy numbers like S.Half, to
|
||||
# prevent rounding errors which can lead to unexpected execution
|
||||
# of conditional blocks that work on comparisons
|
||||
# Also see comments in https://github.com/sympy/sympy/issues/19453
|
||||
from sympy.simplify.simplify import nsimplify
|
||||
e = nsimplify(e)
|
||||
e = set_signs(e)
|
||||
|
||||
|
||||
if e.is_meromorphic(z, z0):
|
||||
if z0 is S.Infinity:
|
||||
newe = e.subs(z, 1/z)
|
||||
# cdir changes sign as oo- should become 0+
|
||||
cdir = -cdir
|
||||
else:
|
||||
newe = e.subs(z, z + z0)
|
||||
try:
|
||||
coeff, ex = newe.leadterm(z, cdir=cdir)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
if ex > 0:
|
||||
return S.Zero
|
||||
elif ex == 0:
|
||||
return coeff
|
||||
if cdir == 1 or not(int(ex) & 1):
|
||||
return S.Infinity*sign(coeff)
|
||||
elif cdir == -1:
|
||||
return S.NegativeInfinity*sign(coeff)
|
||||
else:
|
||||
return S.ComplexInfinity
|
||||
|
||||
if z0 is S.Infinity:
|
||||
if e.is_Mul:
|
||||
e = factor_terms(e)
|
||||
dummy = Dummy('z', positive=z.is_positive, negative=z.is_negative, real=z.is_real)
|
||||
newe = e.subs(z, 1/dummy)
|
||||
# cdir changes sign as oo- should become 0+
|
||||
cdir = -cdir
|
||||
newz = dummy
|
||||
else:
|
||||
newe = e.subs(z, z + z0)
|
||||
newz = z
|
||||
try:
|
||||
coeff, ex = newe.leadterm(newz, cdir=cdir)
|
||||
except (ValueError, NotImplementedError, PoleError):
|
||||
# The NotImplementedError catching is for custom functions
|
||||
from sympy.simplify.powsimp import powsimp
|
||||
e = powsimp(e)
|
||||
if e.is_Pow:
|
||||
r = self.pow_heuristics(e)
|
||||
if r is not None:
|
||||
return r
|
||||
try:
|
||||
coeff = newe.as_leading_term(newz, cdir=cdir)
|
||||
if coeff != newe and (coeff.has(exp) or coeff.has(S.Exp1)):
|
||||
return gruntz(coeff, newz, 0, "-" if re(cdir).is_negative else "+")
|
||||
except (ValueError, NotImplementedError, PoleError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(coeff, AccumBounds) and ex == S.Zero:
|
||||
return coeff
|
||||
if coeff.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN):
|
||||
return self
|
||||
if not coeff.has(newz):
|
||||
if ex.is_positive:
|
||||
return S.Zero
|
||||
elif ex == 0:
|
||||
return coeff
|
||||
elif ex.is_negative:
|
||||
if cdir == 1:
|
||||
return S.Infinity*sign(coeff)
|
||||
elif cdir == -1:
|
||||
return S.NegativeInfinity*sign(coeff)*S.NegativeOne**(S.One + ex)
|
||||
else:
|
||||
return S.ComplexInfinity
|
||||
else:
|
||||
raise NotImplementedError("Not sure of sign of %s" % ex)
|
||||
|
||||
# gruntz fails on factorials but works with the gamma function
|
||||
# If no factorial term is present, e should remain unchanged.
|
||||
# factorial is defined to be zero for negative inputs (which
|
||||
# differs from gamma) so only rewrite for non-negative z0.
|
||||
if z0.is_extended_nonnegative:
|
||||
e = e.rewrite(factorial, gamma)
|
||||
|
||||
l = None
|
||||
|
||||
try:
|
||||
r = gruntz(e, z, z0, dir)
|
||||
if r is S.NaN or l is S.NaN:
|
||||
raise PoleError()
|
||||
except (PoleError, ValueError):
|
||||
if l is not None:
|
||||
raise
|
||||
r = heuristics(e, z, z0, dir)
|
||||
if r is None:
|
||||
return self
|
||||
|
||||
return r
|
||||
@@ -0,0 +1,257 @@
|
||||
"""Limits of sequences"""
|
||||
|
||||
from sympy.calculus.accumulationbounds import AccumulationBounds
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.function import PoleError
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.combinatorial.numbers import fibonacci
|
||||
from sympy.functions.combinatorial.factorials import factorial, subfactorial
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.functions.elementary.complexes import Abs
|
||||
from sympy.functions.elementary.miscellaneous import Max, Min
|
||||
from sympy.functions.elementary.trigonometric import cos, sin
|
||||
from sympy.series.limits import Limit
|
||||
|
||||
|
||||
def difference_delta(expr, n=None, step=1):
|
||||
"""Difference Operator.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Discrete analog of differential operator. Given a sequence x[n],
|
||||
returns the sequence x[n + step] - x[n].
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import difference_delta as dd
|
||||
>>> from sympy.abc import n
|
||||
>>> dd(n*(n + 1), n)
|
||||
2*n + 2
|
||||
>>> dd(n*(n + 1), n, 2)
|
||||
4*n + 6
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://reference.wolfram.com/language/ref/DifferenceDelta.html
|
||||
"""
|
||||
expr = sympify(expr)
|
||||
|
||||
if n is None:
|
||||
f = expr.free_symbols
|
||||
if len(f) == 1:
|
||||
n = f.pop()
|
||||
elif len(f) == 0:
|
||||
return S.Zero
|
||||
else:
|
||||
raise ValueError("Since there is more than one variable in the"
|
||||
" expression, a variable must be supplied to"
|
||||
" take the difference of %s" % expr)
|
||||
step = sympify(step)
|
||||
if step.is_number is False or step.is_finite is False:
|
||||
raise ValueError("Step should be a finite number.")
|
||||
|
||||
if hasattr(expr, '_eval_difference_delta'):
|
||||
result = expr._eval_difference_delta(n, step)
|
||||
if result:
|
||||
return result
|
||||
|
||||
return expr.subs(n, n + step) - expr
|
||||
|
||||
|
||||
def dominant(expr, n):
|
||||
"""Finds the dominant term in a sum, that is a term that dominates
|
||||
every other term.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
If limit(a/b, n, oo) is oo then a dominates b.
|
||||
If limit(a/b, n, oo) is 0 then b dominates a.
|
||||
Otherwise, a and b are comparable.
|
||||
|
||||
If there is no unique dominant term, then returns ``None``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Sum
|
||||
>>> from sympy.series.limitseq import dominant
|
||||
>>> from sympy.abc import n, k
|
||||
>>> dominant(5*n**3 + 4*n**2 + n + 1, n)
|
||||
5*n**3
|
||||
>>> dominant(2**n + Sum(k, (k, 0, n)), n)
|
||||
2**n
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.limitseq.dominant
|
||||
"""
|
||||
terms = Add.make_args(expr.expand(func=True))
|
||||
term0 = terms[-1]
|
||||
comp = [term0] # comparable terms
|
||||
for t in terms[:-1]:
|
||||
r = term0/t
|
||||
e = r.gammasimp()
|
||||
if e == r:
|
||||
e = r.factor()
|
||||
l = limit_seq(e, n)
|
||||
if l is None:
|
||||
return None
|
||||
elif l.is_zero:
|
||||
term0 = t
|
||||
comp = [term0]
|
||||
elif l not in [S.Infinity, S.NegativeInfinity]:
|
||||
comp.append(t)
|
||||
if len(comp) > 1:
|
||||
return None
|
||||
return term0
|
||||
|
||||
|
||||
def _limit_inf(expr, n):
|
||||
try:
|
||||
return Limit(expr, n, S.Infinity).doit(deep=False)
|
||||
except (NotImplementedError, PoleError):
|
||||
return None
|
||||
|
||||
|
||||
def _limit_seq(expr, n, trials):
|
||||
from sympy.concrete.summations import Sum
|
||||
|
||||
for i in range(trials):
|
||||
if not expr.has(Sum):
|
||||
result = _limit_inf(expr, n)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
num, den = expr.as_numer_denom()
|
||||
if not den.has(n) or not num.has(n):
|
||||
result = _limit_inf(expr.doit(), n)
|
||||
if result is not None:
|
||||
return result
|
||||
return None
|
||||
|
||||
num, den = (difference_delta(t.expand(), n) for t in [num, den])
|
||||
expr = (num / den).gammasimp()
|
||||
|
||||
if not expr.has(Sum):
|
||||
result = _limit_inf(expr, n)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
num, den = expr.as_numer_denom()
|
||||
|
||||
num = dominant(num, n)
|
||||
if num is None:
|
||||
return None
|
||||
|
||||
den = dominant(den, n)
|
||||
if den is None:
|
||||
return None
|
||||
|
||||
expr = (num / den).gammasimp()
|
||||
|
||||
|
||||
def limit_seq(expr, n=None, trials=5):
|
||||
"""Finds the limit of a sequence as index ``n`` tends to infinity.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Expr
|
||||
SymPy expression for the ``n-th`` term of the sequence
|
||||
n : Symbol, optional
|
||||
The index of the sequence, an integer that tends to positive
|
||||
infinity. If None, inferred from the expression unless it has
|
||||
multiple symbols.
|
||||
trials: int, optional
|
||||
The algorithm is highly recursive. ``trials`` is a safeguard from
|
||||
infinite recursion in case the limit is not easily computed by the
|
||||
algorithm. Try increasing ``trials`` if the algorithm returns ``None``.
|
||||
|
||||
Admissible Terms
|
||||
================
|
||||
|
||||
The algorithm is designed for sequences built from rational functions,
|
||||
indefinite sums, and indefinite products over an indeterminate n. Terms of
|
||||
alternating sign are also allowed, but more complex oscillatory behavior is
|
||||
not supported.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import limit_seq, Sum, binomial
|
||||
>>> from sympy.abc import n, k, m
|
||||
>>> limit_seq((5*n**3 + 3*n**2 + 4) / (3*n**3 + 4*n - 5), n)
|
||||
5/3
|
||||
>>> limit_seq(binomial(2*n, n) / Sum(binomial(2*k, k), (k, 1, n)), n)
|
||||
3/4
|
||||
>>> limit_seq(Sum(k**2 * Sum(2**m/m, (m, 1, k)), (k, 1, n)) / (2**n*n), n)
|
||||
4
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.series.limitseq.dominant
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] Computing Limits of Sequences - Manuel Kauers
|
||||
"""
|
||||
|
||||
from sympy.concrete.summations import Sum
|
||||
if n is None:
|
||||
free = expr.free_symbols
|
||||
if len(free) == 1:
|
||||
n = free.pop()
|
||||
elif not free:
|
||||
return expr
|
||||
else:
|
||||
raise ValueError("Expression has more than one variable. "
|
||||
"Please specify a variable.")
|
||||
elif n not in expr.free_symbols:
|
||||
return expr
|
||||
|
||||
expr = expr.rewrite(fibonacci, S.GoldenRatio)
|
||||
expr = expr.rewrite(factorial, subfactorial, gamma)
|
||||
n_ = Dummy("n", integer=True, positive=True)
|
||||
n1 = Dummy("n", odd=True, positive=True)
|
||||
n2 = Dummy("n", even=True, positive=True)
|
||||
|
||||
# If there is a negative term raised to a power involving n, or a
|
||||
# trigonometric function, then consider even and odd n separately.
|
||||
powers = (p.as_base_exp() for p in expr.atoms(Pow))
|
||||
if (any(b.is_negative and e.has(n) for b, e in powers) or
|
||||
expr.has(cos, sin)):
|
||||
L1 = _limit_seq(expr.xreplace({n: n1}), n1, trials)
|
||||
if L1 is not None:
|
||||
L2 = _limit_seq(expr.xreplace({n: n2}), n2, trials)
|
||||
if L1 != L2:
|
||||
if L1.is_comparable and L2.is_comparable:
|
||||
return AccumulationBounds(Min(L1, L2), Max(L1, L2))
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
L1 = _limit_seq(expr.xreplace({n: n_}), n_, trials)
|
||||
if L1 is not None:
|
||||
return L1
|
||||
else:
|
||||
if expr.is_Add:
|
||||
limits = [limit_seq(term, n, trials) for term in expr.args]
|
||||
if any(result is None for result in limits):
|
||||
return None
|
||||
else:
|
||||
return Add(*limits)
|
||||
# Maybe the absolute value is easier to deal with (though not if
|
||||
# it has a Sum). If it tends to 0, the limit is 0.
|
||||
elif not expr.has(Sum):
|
||||
lim = _limit_seq(Abs(expr.xreplace({n: n_})), n_, trials)
|
||||
if lim is not None and lim.is_zero:
|
||||
return S.Zero
|
||||
@@ -0,0 +1,522 @@
|
||||
from sympy.core import S, sympify, Expr, Dummy, Add, Mul
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.function import Function, PoleError, expand_power_base, expand_log
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.functions.elementary.exponential import exp, log
|
||||
from sympy.sets.sets import Complement
|
||||
from sympy.utilities.iterables import uniq, is_sequence
|
||||
|
||||
|
||||
class Order(Expr):
|
||||
r""" Represents the limiting behavior of some function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The order of a function characterizes the function based on the limiting
|
||||
behavior of the function as it goes to some limit. Only taking the limit
|
||||
point to be a number is currently supported. This is expressed in
|
||||
big O notation [1]_.
|
||||
|
||||
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 there
|
||||
exists a `\delta > 0` and an `M > 0` such that `|g(x)| \leq M|f(x)|` for
|
||||
`|x-a| < \delta`. This is equivalent to `\limsup_{x \rightarrow a}
|
||||
|g(x)/f(x)| < \infty`.
|
||||
|
||||
Let's illustrate it on the following example by taking the expansion of
|
||||
`\sin(x)` about 0:
|
||||
|
||||
.. math ::
|
||||
\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`, there is a `\delta > 0` and an `M` such that:
|
||||
|
||||
.. math ::
|
||||
|x^5/5! - x^7/7! + ....| <= M|x^5| \text{ for } |x| < \delta
|
||||
|
||||
or by the alternate definition:
|
||||
|
||||
.. math ::
|
||||
\lim_{x \rightarrow 0} | (x^5/5! - x^7/7! + ....) / x^5| < \infty
|
||||
|
||||
which surely is true, because
|
||||
|
||||
.. math ::
|
||||
\lim_{x \rightarrow 0} | (x^5/5! - x^7/7! + ....) / x^5| = 1/5!
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import O, oo, cos, pi
|
||||
>>> from sympy.abc import x, y
|
||||
|
||||
>>> O(x + x**2)
|
||||
O(x)
|
||||
>>> O(x + x**2, (x, 0))
|
||||
O(x)
|
||||
>>> O(x + x**2, (x, oo))
|
||||
O(x**2, (x, oo))
|
||||
|
||||
>>> O(1 + x*y)
|
||||
O(1, x, y)
|
||||
>>> O(1 + x*y, (x, 0), (y, 0))
|
||||
O(1, x, y)
|
||||
>>> O(1 + x*y, (x, oo), (y, oo))
|
||||
O(x*y, (x, oo), (y, oo))
|
||||
|
||||
>>> O(1) in O(1, x)
|
||||
True
|
||||
>>> O(1, x) in O(1)
|
||||
False
|
||||
>>> O(x) in O(1, x)
|
||||
True
|
||||
>>> O(x**2) in O(x)
|
||||
True
|
||||
|
||||
>>> O(x)*x
|
||||
O(x**2)
|
||||
>>> O(x) - O(x)
|
||||
O(x)
|
||||
>>> O(cos(x))
|
||||
O(1)
|
||||
>>> O(cos(x), (x, pi/2))
|
||||
O(x - pi/2, (x, pi/2))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] `Big O notation <https://en.wikipedia.org/wiki/Big_O_notation>`_
|
||||
|
||||
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.
|
||||
|
||||
If no symbols are passed then all symbols in the expression are used
|
||||
and the limit point is assumed to be zero.
|
||||
|
||||
"""
|
||||
|
||||
is_Order = True
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
@cacheit
|
||||
def __new__(cls, expr, *args, **kwargs):
|
||||
expr = sympify(expr)
|
||||
|
||||
if not args:
|
||||
if expr.is_Order:
|
||||
variables = expr.variables
|
||||
point = expr.point
|
||||
else:
|
||||
variables = list(expr.free_symbols)
|
||||
point = [S.Zero]*len(variables)
|
||||
else:
|
||||
args = list(args if is_sequence(args) else [args])
|
||||
variables, point = [], []
|
||||
if is_sequence(args[0]):
|
||||
for a in args:
|
||||
v, p = list(map(sympify, a))
|
||||
variables.append(v)
|
||||
point.append(p)
|
||||
else:
|
||||
variables = list(map(sympify, args))
|
||||
point = [S.Zero]*len(variables)
|
||||
|
||||
if not all(v.is_symbol for v in variables):
|
||||
raise TypeError('Variables are not symbols, got %s' % variables)
|
||||
|
||||
if len(list(uniq(variables))) != len(variables):
|
||||
raise ValueError('Variables are supposed to be unique symbols, got %s' % variables)
|
||||
|
||||
if expr.is_Order:
|
||||
expr_vp = dict(expr.args[1:])
|
||||
new_vp = dict(expr_vp)
|
||||
vp = dict(zip(variables, point))
|
||||
for v, p in vp.items():
|
||||
if v in new_vp.keys():
|
||||
if p != new_vp[v]:
|
||||
raise NotImplementedError(
|
||||
"Mixing Order at different points is not supported.")
|
||||
else:
|
||||
new_vp[v] = p
|
||||
if set(expr_vp.keys()) == set(new_vp.keys()):
|
||||
return expr
|
||||
else:
|
||||
variables = list(new_vp.keys())
|
||||
point = [new_vp[v] for v in variables]
|
||||
|
||||
if expr is S.NaN:
|
||||
return S.NaN
|
||||
|
||||
if any(x in p.free_symbols for x in variables for p in point):
|
||||
raise ValueError('Got %s as a point.' % point)
|
||||
|
||||
if variables:
|
||||
if any(p != point[0] for p in point):
|
||||
raise NotImplementedError(
|
||||
"Multivariable orders at different points are not supported.")
|
||||
if point[0] in (S.Infinity, S.Infinity*S.ImaginaryUnit):
|
||||
s = {k: 1/Dummy() for k in variables}
|
||||
rs = {1/v: 1/k for k, v in s.items()}
|
||||
ps = [S.Zero for p in point]
|
||||
elif point[0] in (S.NegativeInfinity, S.NegativeInfinity*S.ImaginaryUnit):
|
||||
s = {k: -1/Dummy() for k in variables}
|
||||
rs = {-1/v: -1/k for k, v in s.items()}
|
||||
ps = [S.Zero for p in point]
|
||||
elif point[0] is not S.Zero:
|
||||
s = {k: Dummy() + point[0] for k in variables}
|
||||
rs = {(v - point[0]).together(): k - point[0] for k, v in s.items()}
|
||||
ps = [S.Zero for p in point]
|
||||
else:
|
||||
s = ()
|
||||
rs = ()
|
||||
ps = list(point)
|
||||
|
||||
expr = expr.subs(s)
|
||||
|
||||
if expr.is_Add:
|
||||
expr = expr.factor()
|
||||
|
||||
if s:
|
||||
args = tuple([r[0] for r in rs.items()])
|
||||
else:
|
||||
args = tuple(variables)
|
||||
|
||||
if len(variables) > 1:
|
||||
# XXX: better way? We need this expand() to
|
||||
# workaround e.g: expr = x*(x + y).
|
||||
# (x*(x + y)).as_leading_term(x, y) currently returns
|
||||
# x*y (wrong order term!). That's why we want to deal with
|
||||
# expand()'ed expr (handled in "if expr.is_Add" branch below).
|
||||
expr = expr.expand()
|
||||
|
||||
old_expr = None
|
||||
while old_expr != expr:
|
||||
old_expr = expr
|
||||
if expr.is_Add:
|
||||
lst = expr.extract_leading_order(args)
|
||||
expr = Add(*[f.expr for (e, f) in lst])
|
||||
|
||||
elif expr:
|
||||
try:
|
||||
expr = expr.as_leading_term(*args)
|
||||
except PoleError:
|
||||
if isinstance(expr, Function) or\
|
||||
all(isinstance(arg, Function) for arg in expr.args):
|
||||
# It is not possible to simplify an expression
|
||||
# containing only functions (which raise error on
|
||||
# call to leading term) further
|
||||
pass
|
||||
else:
|
||||
orders = []
|
||||
pts = tuple(zip(args, ps))
|
||||
for arg in expr.args:
|
||||
try:
|
||||
lt = arg.as_leading_term(*args)
|
||||
except PoleError:
|
||||
lt = arg
|
||||
if lt not in args:
|
||||
order = Order(lt)
|
||||
else:
|
||||
order = Order(lt, *pts)
|
||||
orders.append(order)
|
||||
if expr.is_Add:
|
||||
new_expr = Order(Add(*orders), *pts)
|
||||
if new_expr.is_Add:
|
||||
new_expr = Order(Add(*[a.expr for a in new_expr.args]), *pts)
|
||||
expr = new_expr.expr
|
||||
elif expr.is_Mul:
|
||||
expr = Mul(*[a.expr for a in orders])
|
||||
elif expr.is_Pow:
|
||||
e = expr.exp
|
||||
b = expr.base
|
||||
expr = exp(e * log(b))
|
||||
|
||||
# It would probably be better to handle this somewhere
|
||||
# else. This is needed for a testcase in which there is a
|
||||
# symbol with the assumptions zero=True.
|
||||
if expr.is_zero:
|
||||
expr = S.Zero
|
||||
else:
|
||||
expr = expr.as_independent(*args, as_Add=False)[1]
|
||||
|
||||
expr = expand_power_base(expr)
|
||||
expr = expand_log(expr)
|
||||
|
||||
if len(args) == 1:
|
||||
# The definition of O(f(x)) symbol explicitly stated that
|
||||
# the argument of f(x) is irrelevant. That's why we can
|
||||
# combine some power exponents (only "on top" of the
|
||||
# expression tree for f(x)), e.g.:
|
||||
# x**p * (-x)**q -> x**(p+q) for real p, q.
|
||||
x = args[0]
|
||||
margs = list(Mul.make_args(
|
||||
expr.as_independent(x, as_Add=False)[1]))
|
||||
|
||||
for i, t in enumerate(margs):
|
||||
if t.is_Pow:
|
||||
b, q = t.args
|
||||
if b in (x, -x) and q.is_real and not q.has(x):
|
||||
margs[i] = x**q
|
||||
elif b.is_Pow and not b.exp.has(x):
|
||||
b, r = b.args
|
||||
if b in (x, -x) and r.is_real:
|
||||
margs[i] = x**(r*q)
|
||||
elif b.is_Mul and b.args[0] is S.NegativeOne:
|
||||
b = -b
|
||||
if b.is_Pow and not b.exp.has(x):
|
||||
b, r = b.args
|
||||
if b in (x, -x) and r.is_real:
|
||||
margs[i] = x**(r*q)
|
||||
|
||||
expr = Mul(*margs)
|
||||
|
||||
expr = expr.subs(rs)
|
||||
|
||||
if expr.is_Order:
|
||||
expr = expr.expr
|
||||
|
||||
if not expr.has(*variables) and not expr.is_zero:
|
||||
expr = S.One
|
||||
|
||||
# create Order instance:
|
||||
vp = dict(zip(variables, point))
|
||||
variables.sort(key=default_sort_key)
|
||||
point = [vp[v] for v in variables]
|
||||
args = (expr,) + Tuple(*zip(variables, point))
|
||||
obj = Expr.__new__(cls, *args)
|
||||
return obj
|
||||
|
||||
def _eval_nseries(self, x, n, logx, cdir=0):
|
||||
return self
|
||||
|
||||
@property
|
||||
def expr(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
if self.args[1:]:
|
||||
return tuple(x[0] for x in self.args[1:])
|
||||
else:
|
||||
return ()
|
||||
|
||||
@property
|
||||
def point(self):
|
||||
if self.args[1:]:
|
||||
return tuple(x[1] for x in self.args[1:])
|
||||
else:
|
||||
return ()
|
||||
|
||||
@property
|
||||
def free_symbols(self):
|
||||
return self.expr.free_symbols | set(self.variables)
|
||||
|
||||
def _eval_power(b, e):
|
||||
if e.is_Number and e.is_nonnegative:
|
||||
return b.func(b.expr ** e, *b.args[1:])
|
||||
if e == O(1):
|
||||
return b
|
||||
return
|
||||
|
||||
def as_expr_variables(self, order_symbols):
|
||||
if order_symbols is None:
|
||||
order_symbols = self.args[1:]
|
||||
else:
|
||||
if (not all(o[1] == order_symbols[0][1] for o in order_symbols) and
|
||||
not all(p == self.point[0] for p in self.point)): # pragma: no cover
|
||||
raise NotImplementedError('Order at points other than 0 '
|
||||
'or oo not supported, got %s as a point.' % self.point)
|
||||
if order_symbols and order_symbols[0][1] != self.point[0]:
|
||||
raise NotImplementedError(
|
||||
"Multiplying Order at different points is not supported.")
|
||||
order_symbols = dict(order_symbols)
|
||||
for s, p in dict(self.args[1:]).items():
|
||||
if s not in order_symbols.keys():
|
||||
order_symbols[s] = p
|
||||
order_symbols = sorted(order_symbols.items(), key=lambda x: default_sort_key(x[0]))
|
||||
return self.expr, tuple(order_symbols)
|
||||
|
||||
def removeO(self):
|
||||
return S.Zero
|
||||
|
||||
def getO(self):
|
||||
return self
|
||||
|
||||
@cacheit
|
||||
def contains(self, expr):
|
||||
r"""
|
||||
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).
|
||||
"""
|
||||
expr = sympify(expr)
|
||||
if expr.is_zero:
|
||||
return True
|
||||
if expr is S.NaN:
|
||||
return False
|
||||
point = self.point[0] if self.point else S.Zero
|
||||
if expr.is_Order:
|
||||
if (any(p != point for p in expr.point) or
|
||||
any(p != point for p in self.point)):
|
||||
return None
|
||||
if expr.expr == self.expr:
|
||||
# O(1) + O(1), O(1) + O(1, x), etc.
|
||||
return all(x in self.args[1:] for x in expr.args[1:])
|
||||
if expr.expr.is_Add:
|
||||
return all(self.contains(x) for x in expr.expr.args)
|
||||
if self.expr.is_Add and point.is_zero:
|
||||
return any(self.func(x, *self.args[1:]).contains(expr)
|
||||
for x in self.expr.args)
|
||||
if self.variables and expr.variables:
|
||||
common_symbols = tuple(
|
||||
[s for s in self.variables if s in expr.variables])
|
||||
elif self.variables:
|
||||
common_symbols = self.variables
|
||||
else:
|
||||
common_symbols = expr.variables
|
||||
if not common_symbols:
|
||||
return None
|
||||
if (self.expr.is_Pow and len(self.variables) == 1
|
||||
and self.variables == expr.variables):
|
||||
symbol = self.variables[0]
|
||||
other = expr.expr.as_independent(symbol, as_Add=False)[1]
|
||||
if (other.is_Pow and other.base == symbol and
|
||||
self.expr.base == symbol):
|
||||
if point.is_zero:
|
||||
rv = (self.expr.exp - other.exp).is_nonpositive
|
||||
if point.is_infinite:
|
||||
rv = (self.expr.exp - other.exp).is_nonnegative
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
||||
from sympy.simplify.powsimp import powsimp
|
||||
r = None
|
||||
ratio = self.expr/expr.expr
|
||||
ratio = powsimp(ratio, deep=True, combine='exp')
|
||||
for s in common_symbols:
|
||||
from sympy.series.limits import Limit
|
||||
l = Limit(ratio, s, point).doit(heuristics=False)
|
||||
if not isinstance(l, Limit):
|
||||
l = l != 0
|
||||
else:
|
||||
l = None
|
||||
if r is None:
|
||||
r = l
|
||||
else:
|
||||
if r != l:
|
||||
return
|
||||
return r
|
||||
|
||||
if self.expr.is_Pow and len(self.variables) == 1:
|
||||
symbol = self.variables[0]
|
||||
other = expr.as_independent(symbol, as_Add=False)[1]
|
||||
if (other.is_Pow and other.base == symbol and
|
||||
self.expr.base == symbol):
|
||||
if point.is_zero:
|
||||
rv = (self.expr.exp - other.exp).is_nonpositive
|
||||
if point.is_infinite:
|
||||
rv = (self.expr.exp - other.exp).is_nonnegative
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
||||
obj = self.func(expr, *self.args[1:])
|
||||
return self.contains(obj)
|
||||
|
||||
def __contains__(self, other):
|
||||
result = self.contains(other)
|
||||
if result is None:
|
||||
raise TypeError('contains did not evaluate to a bool')
|
||||
return result
|
||||
|
||||
def _eval_subs(self, old, new):
|
||||
if old in self.variables:
|
||||
newexpr = self.expr.subs(old, new)
|
||||
i = self.variables.index(old)
|
||||
newvars = list(self.variables)
|
||||
newpt = list(self.point)
|
||||
if new.is_symbol:
|
||||
newvars[i] = new
|
||||
else:
|
||||
syms = new.free_symbols
|
||||
if len(syms) == 1 or old in syms:
|
||||
if old in syms:
|
||||
var = self.variables[i]
|
||||
else:
|
||||
var = syms.pop()
|
||||
# First, try to substitute self.point in the "new"
|
||||
# expr to see if this is a fixed point.
|
||||
# E.g. O(y).subs(y, sin(x))
|
||||
from sympy import limit
|
||||
if new.has(Order) and limit(new.getO().expr, var, new.getO().point[0]) == self.point[i]:
|
||||
point = new.getO().point[0]
|
||||
return Order(newexpr, *zip([var], [point]))
|
||||
else:
|
||||
point = new.subs(var, self.point[i])
|
||||
if point != self.point[i]:
|
||||
from sympy.solvers.solveset import solveset
|
||||
d = Dummy()
|
||||
sol = solveset(old - new.subs(var, d), d)
|
||||
if isinstance(sol, Complement):
|
||||
e1 = sol.args[0]
|
||||
e2 = sol.args[1]
|
||||
sol = set(e1) - set(e2)
|
||||
res = [dict(zip((d, ), sol))]
|
||||
point = d.subs(res[0]).limit(old, self.point[i])
|
||||
newvars[i] = var
|
||||
newpt[i] = point
|
||||
elif old not in syms:
|
||||
del newvars[i], newpt[i]
|
||||
if not syms and new == self.point[i]:
|
||||
newvars.extend(syms)
|
||||
newpt.extend([S.Zero]*len(syms))
|
||||
else:
|
||||
return
|
||||
return Order(newexpr, *zip(newvars, newpt))
|
||||
|
||||
def _eval_conjugate(self):
|
||||
expr = self.expr._eval_conjugate()
|
||||
if expr is not None:
|
||||
return self.func(expr, *self.args[1:])
|
||||
|
||||
def _eval_derivative(self, x):
|
||||
return self.func(self.expr.diff(x), *self.args[1:]) or self
|
||||
|
||||
def _eval_transpose(self):
|
||||
expr = self.expr._eval_transpose()
|
||||
if expr is not None:
|
||||
return self.func(expr, *self.args[1:])
|
||||
|
||||
def __neg__(self):
|
||||
return self
|
||||
|
||||
O = Order
|
||||
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
This module implements the Residue function and related tools for working
|
||||
with residues.
|
||||
"""
|
||||
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.utilities.timeutils import timethis
|
||||
|
||||
|
||||
@timethis('residue')
|
||||
def residue(expr, x, x0):
|
||||
"""
|
||||
Finds the residue of ``expr`` at the point x=x0.
|
||||
|
||||
The residue is defined as the coefficient of ``1/(x-x0)`` in the power series
|
||||
expansion about ``x=x0``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Symbol, residue, sin
|
||||
>>> x = Symbol("x")
|
||||
>>> residue(1/x, x, 0)
|
||||
1
|
||||
>>> residue(1/x**2, x, 0)
|
||||
0
|
||||
>>> residue(2/sin(x), x, 0)
|
||||
2
|
||||
|
||||
This function is essential for the Residue Theorem [1].
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Residue_theorem
|
||||
"""
|
||||
# The current implementation uses series expansion to
|
||||
# calculate it. A more general implementation is explained in
|
||||
# the section 5.6 of the Bronstein's book {M. Bronstein:
|
||||
# Symbolic Integration I, Springer Verlag (2005)}. For purely
|
||||
# rational functions, the algorithm is much easier. See
|
||||
# sections 2.4, 2.5, and 2.7 (this section actually gives an
|
||||
# algorithm for computing any Laurent series coefficient for
|
||||
# a rational function). The theory in section 2.4 will help to
|
||||
# understand why the resultant works in the general algorithm.
|
||||
# For the definition of a resultant, see section 1.4 (and any
|
||||
# previous sections for more review).
|
||||
|
||||
from sympy.series.order import Order
|
||||
from sympy.simplify.radsimp import collect
|
||||
expr = sympify(expr)
|
||||
if x0 != 0:
|
||||
expr = expr.subs(x, x + x0)
|
||||
for n in (0, 1, 2, 4, 8, 16, 32):
|
||||
s = expr.nseries(x, n=n)
|
||||
if not s.has(Order) or s.getn() >= 0:
|
||||
break
|
||||
s = collect(s.removeO(), x)
|
||||
if s.is_Add:
|
||||
args = s.args
|
||||
else:
|
||||
args = [s]
|
||||
res = S.Zero
|
||||
for arg in args:
|
||||
c, m = arg.as_coeff_mul(x)
|
||||
m = Mul(*m)
|
||||
if not (m in (S.One, x) or (m.is_Pow and m.exp.is_Integer)):
|
||||
raise NotImplementedError('term of unexpected form: %s' % m)
|
||||
if m == 1/x:
|
||||
res += c
|
||||
return res
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
from sympy.core.sympify import sympify
|
||||
|
||||
|
||||
def series(expr, x=None, x0=0, n=6, dir="+"):
|
||||
"""Series expansion of expr around point `x = x0`.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Expression
|
||||
The expression whose series is to be expanded.
|
||||
|
||||
x : Symbol
|
||||
It is the variable of the expression to be calculated.
|
||||
|
||||
x0 : Value
|
||||
The value around which ``x`` is calculated. Can be any value
|
||||
from ``-oo`` to ``oo``.
|
||||
|
||||
n : Value
|
||||
The number of terms upto which the series is to be expanded.
|
||||
|
||||
dir : String, optional
|
||||
The series-expansion can be bi-directional. If ``dir="+"``,
|
||||
then (x->x0+). If ``dir="-"``, then (x->x0-). For infinite
|
||||
``x0`` (``oo`` or ``-oo``), the ``dir`` argument is determined
|
||||
from the direction of the infinity (i.e., ``dir="-"`` for
|
||||
``oo``).
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import series, tan, oo
|
||||
>>> from sympy.abc import x
|
||||
>>> f = tan(x)
|
||||
>>> series(f, x, 2, 6, "+")
|
||||
tan(2) + (1 + tan(2)**2)*(x - 2) + (x - 2)**2*(tan(2)**3 + tan(2)) +
|
||||
(x - 2)**3*(1/3 + 4*tan(2)**2/3 + tan(2)**4) + (x - 2)**4*(tan(2)**5 +
|
||||
5*tan(2)**3/3 + 2*tan(2)/3) + (x - 2)**5*(2/15 + 17*tan(2)**2/15 +
|
||||
2*tan(2)**4 + tan(2)**6) + O((x - 2)**6, (x, 2))
|
||||
|
||||
>>> series(f, x, 2, 3, "-")
|
||||
tan(2) + (2 - x)*(-tan(2)**2 - 1) + (2 - x)**2*(tan(2)**3 + tan(2))
|
||||
+ O((x - 2)**3, (x, 2))
|
||||
|
||||
>>> series(f, x, 2, oo, "+")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: 'Infinity' object cannot be interpreted as an integer
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Expr
|
||||
Series expansion of the expression about x0
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.core.expr.Expr.series: See the docstring of Expr.series() for complete details of this wrapper.
|
||||
"""
|
||||
expr = sympify(expr)
|
||||
return expr.series(x, x0, n, dir)
|
||||
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
Contains the base class for series
|
||||
Made using sequences in mind
|
||||
"""
|
||||
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.cache import cacheit
|
||||
|
||||
|
||||
class SeriesBase(Expr):
|
||||
"""Base Class for series"""
|
||||
|
||||
@property
|
||||
def interval(self):
|
||||
"""The interval on which the series is defined"""
|
||||
raise NotImplementedError("(%s).interval" % self)
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
"""The starting point of the series. This point is included"""
|
||||
raise NotImplementedError("(%s).start" % self)
|
||||
|
||||
@property
|
||||
def stop(self):
|
||||
"""The ending point of the series. This point is included"""
|
||||
raise NotImplementedError("(%s).stop" % self)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
"""Length of the series expansion"""
|
||||
raise NotImplementedError("(%s).length" % self)
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
"""Returns a tuple of variables that are bounded"""
|
||||
return ()
|
||||
|
||||
@property
|
||||
def free_symbols(self):
|
||||
"""
|
||||
This method returns the symbols in the object, excluding those
|
||||
that take on a specific value (i.e. the dummy symbols).
|
||||
"""
|
||||
return ({j for i in self.args for j in i.free_symbols}
|
||||
.difference(self.variables))
|
||||
|
||||
@cacheit
|
||||
def term(self, pt):
|
||||
"""Term at point pt of a series"""
|
||||
if pt < self.start or pt > self.stop:
|
||||
raise IndexError("Index %s out of bounds %s" % (pt, self.interval))
|
||||
return self._eval_term(pt)
|
||||
|
||||
def _eval_term(self, pt):
|
||||
raise NotImplementedError("The _eval_term method should be added to"
|
||||
"%s to return series term so it is available"
|
||||
"when 'term' calls it."
|
||||
% self.func)
|
||||
|
||||
def _ith_point(self, i):
|
||||
"""
|
||||
Returns the i'th point of a series
|
||||
If start point is negative infinity, point is returned from the end.
|
||||
Assumes the first point to be indexed zero.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
TODO
|
||||
"""
|
||||
if self.start is S.NegativeInfinity:
|
||||
initial = self.stop
|
||||
step = -1
|
||||
else:
|
||||
initial = self.start
|
||||
step = 1
|
||||
|
||||
return initial + i*step
|
||||
|
||||
def __iter__(self):
|
||||
i = 0
|
||||
while i < self.length:
|
||||
pt = self._ith_point(i)
|
||||
yield self.term(pt)
|
||||
i += 1
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
index = self._ith_point(index)
|
||||
return self.term(index)
|
||||
elif isinstance(index, slice):
|
||||
start, stop = index.start, index.stop
|
||||
if start is None:
|
||||
start = 0
|
||||
if stop is None:
|
||||
stop = self.length
|
||||
return [self.term(self._ith_point(i)) for i in
|
||||
range(start, stop, index.step or 1)]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
from sympy.series import approximants
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.combinatorial.factorials import binomial
|
||||
from sympy.functions.combinatorial.numbers import (fibonacci, lucas)
|
||||
|
||||
|
||||
def test_approximants():
|
||||
x, t = symbols("x,t")
|
||||
g = [lucas(k) for k in range(16)]
|
||||
assert list(approximants(g)) == (
|
||||
[2, -4/(x - 2), (5*x - 2)/(3*x - 1), (x - 2)/(x**2 + x - 1)] )
|
||||
g = [lucas(k)+fibonacci(k+2) for k in range(16)]
|
||||
assert list(approximants(g)) == (
|
||||
[3, -3/(x - 1), (3*x - 3)/(2*x - 1), -3/(x**2 + x - 1)] )
|
||||
g = [lucas(k)**2 for k in range(16)]
|
||||
assert list(approximants(g)) == (
|
||||
[4, -16/(x - 4), (35*x - 4)/(9*x - 1), (37*x - 28)/(13*x**2 + 11*x - 7),
|
||||
(50*x**2 + 63*x - 52)/(37*x**2 + 19*x - 13),
|
||||
(-x**2 - 7*x + 4)/(x**3 - 2*x**2 - 2*x + 1)] )
|
||||
p = [sum(binomial(k,i)*x**i for i in range(k+1)) for k in range(16)]
|
||||
y = approximants(p, t, simplify=True)
|
||||
assert next(y) == 1
|
||||
assert next(y) == -1/(t*(x + 1) - 1)
|
||||
@@ -0,0 +1,55 @@
|
||||
from sympy.core.function import PoleError
|
||||
from sympy.core.numbers import oo
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.series.order import O
|
||||
from sympy.abc import x
|
||||
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
def test_simple():
|
||||
# Gruntz' theses pp. 91 to 96
|
||||
# 6.6
|
||||
e = sin(1/x + exp(-x)) - sin(1/x)
|
||||
assert e.aseries(x) == (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x)
|
||||
|
||||
e = exp(x) * (exp(1/x + exp(-x)) - exp(1/x))
|
||||
assert e.aseries(x, n=4) == 1/(6*x**3) + 1/(2*x**2) + 1/x + 1 + O(x**(-4), (x, oo))
|
||||
|
||||
e = exp(exp(x) / (1 - 1/x))
|
||||
assert e.aseries(x) == exp(exp(x) / (1 - 1/x))
|
||||
|
||||
# The implementation of bound in aseries is incorrect currently. This test
|
||||
# should be commented out when that is fixed.
|
||||
# assert e.aseries(x, bound=3) == exp(exp(x) / x**2)*exp(exp(x) / x)*exp(-exp(x) + exp(x)/(1 - 1/x) - \
|
||||
# exp(x) / x - exp(x) / x**2) * exp(exp(x))
|
||||
|
||||
e = exp(sin(1/x + exp(-exp(x)))) - exp(sin(1/x))
|
||||
assert e.aseries(x, n=4) == (-1/(2*x**3) + 1/x + 1 + O(x**(-4), (x, oo)))*exp(-exp(x))
|
||||
|
||||
e3 = lambda x:exp(exp(exp(x)))
|
||||
e = e3(x)/e3(x - 1/e3(x))
|
||||
assert e.aseries(x, n=3) == 1 + exp(2*x + 2*exp(x))*exp(-2*exp(exp(x)))/2\
|
||||
- exp(2*x + exp(x))*exp(-2*exp(exp(x)))/2 - exp(x + exp(x))*exp(-2*exp(exp(x)))/2\
|
||||
+ exp(x + exp(x))*exp(-exp(exp(x))) + O(exp(-3*exp(exp(x))), (x, oo))
|
||||
|
||||
e = exp(exp(x)) * (exp(sin(1/x + 1/exp(exp(x)))) - exp(sin(1/x)))
|
||||
assert e.aseries(x, n=4) == -1/(2*x**3) + 1/x + 1 + O(x**(-4), (x, oo))
|
||||
|
||||
n = Symbol('n', integer=True)
|
||||
e = (sqrt(n)*log(n)**2*exp(sqrt(log(n))*log(log(n))**2*exp(sqrt(log(log(n)))*log(log(log(n)))**3)))/n
|
||||
assert e.aseries(n) == \
|
||||
exp(exp(sqrt(log(log(n)))*log(log(log(n)))**3)*sqrt(log(n))*log(log(n))**2)*log(n)**2/sqrt(n)
|
||||
|
||||
|
||||
def test_hierarchical():
|
||||
e = sin(1/x + exp(-x))
|
||||
assert e.aseries(x, n=3, hir=True) == -exp(-2*x)*sin(1/x)/2 + \
|
||||
exp(-x)*cos(1/x) + sin(1/x) + O(exp(-3*x), (x, oo))
|
||||
|
||||
e = sin(x) * cos(exp(-x))
|
||||
assert e.aseries(x, hir=True) == exp(-4*x)*sin(x)/24 - \
|
||||
exp(-2*x)*sin(x)/2 + sin(x) + O(exp(-6*x), (x, oo))
|
||||
raises(PoleError, lambda: e.aseries(x))
|
||||
@@ -0,0 +1,143 @@
|
||||
from sympy.core.numbers import (Rational, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import (root, sqrt)
|
||||
from sympy.functions.elementary.trigonometric import (asin, cos, sin, tan)
|
||||
from sympy.polys.rationaltools import together
|
||||
from sympy.series.limits import limit
|
||||
|
||||
# Numbers listed with the tests refer to problem numbers in the book
|
||||
# "Anti-demidovich, problemas resueltos, Ed. URSS"
|
||||
|
||||
x = Symbol("x")
|
||||
|
||||
|
||||
def test_leadterm():
|
||||
assert (3 + 2*x**(log(3)/log(2) - 1)).leadterm(x) == (3, 0)
|
||||
|
||||
|
||||
def root3(x):
|
||||
return root(x, 3)
|
||||
|
||||
|
||||
def root4(x):
|
||||
return root(x, 4)
|
||||
|
||||
|
||||
def test_Limits_simple_0():
|
||||
assert limit((2**(x + 1) + 3**(x + 1))/(2**x + 3**x), x, oo) == 3 # 175
|
||||
|
||||
|
||||
def test_Limits_simple_1():
|
||||
assert limit((x + 1)*(x + 2)*(x + 3)/x**3, x, oo) == 1 # 172
|
||||
assert limit(sqrt(x + 1) - sqrt(x), x, oo) == 0 # 179
|
||||
assert limit((2*x - 3)*(3*x + 5)*(4*x - 6)/(3*x**3 + x - 1), x, oo) == 8 # Primjer 1
|
||||
assert limit(x/root3(x**3 + 10), x, oo) == 1 # Primjer 2
|
||||
assert limit((x + 1)**2/(x**2 + 1), x, oo) == 1 # 181
|
||||
|
||||
|
||||
def test_Limits_simple_2():
|
||||
assert limit(1000*x/(x**2 - 1), x, oo) == 0 # 182
|
||||
assert limit((x**2 - 5*x + 1)/(3*x + 7), x, oo) is oo # 183
|
||||
assert limit((2*x**2 - x + 3)/(x**3 - 8*x + 5), x, oo) == 0 # 184
|
||||
assert limit((2*x**2 - 3*x - 4)/sqrt(x**4 + 1), x, oo) == 2 # 186
|
||||
assert limit((2*x + 3)/(x + root3(x)), x, oo) == 2 # 187
|
||||
assert limit(x**2/(10 + x*sqrt(x)), x, oo) is oo # 188
|
||||
assert limit(root3(x**2 + 1)/(x + 1), x, oo) == 0 # 189
|
||||
assert limit(sqrt(x)/sqrt(x + sqrt(x + sqrt(x))), x, oo) == 1 # 190
|
||||
|
||||
|
||||
def test_Limits_simple_3a():
|
||||
a = Symbol('a')
|
||||
#issue 3513
|
||||
assert together(limit((x**2 - (a + 1)*x + a)/(x**3 - a**3), x, a)) == \
|
||||
(a - 1)/(3*a**2) # 196
|
||||
|
||||
|
||||
def test_Limits_simple_3b():
|
||||
h = Symbol("h")
|
||||
assert limit(((x + h)**3 - x**3)/h, h, 0) == 3*x**2 # 197
|
||||
assert limit((1/(1 - x) - 3/(1 - x**3)), x, 1) == -1 # 198
|
||||
assert limit((sqrt(1 + x) - 1)/(root3(1 + x) - 1), x, 0) == Rational(3)/2 # Primer 4
|
||||
assert limit((sqrt(x) - 1)/(x - 1), x, 1) == Rational(1)/2 # 199
|
||||
assert limit((sqrt(x) - 8)/(root3(x) - 4), x, 64) == 3 # 200
|
||||
assert limit((root3(x) - 1)/(root4(x) - 1), x, 1) == Rational(4)/3 # 201
|
||||
assert limit(
|
||||
(root3(x**2) - 2*root3(x) + 1)/(x - 1)**2, x, 1) == Rational(1)/9 # 202
|
||||
|
||||
|
||||
def test_Limits_simple_4a():
|
||||
a = Symbol('a')
|
||||
assert limit((sqrt(x) - sqrt(a))/(x - a), x, a) == 1/(2*sqrt(a)) # Primer 5
|
||||
assert limit((sqrt(x) - 1)/(root3(x) - 1), x, 1) == Rational(3, 2) # 205
|
||||
assert limit((sqrt(1 + x) - sqrt(1 - x))/x, x, 0) == 1 # 207
|
||||
assert limit(sqrt(x**2 - 5*x + 6) - x, x, oo) == Rational(-5, 2) # 213
|
||||
|
||||
|
||||
def test_limits_simple_4aa():
|
||||
assert limit(x*(sqrt(x**2 + 1) - x), x, oo) == Rational(1)/2 # 214
|
||||
|
||||
|
||||
def test_Limits_simple_4b():
|
||||
#issue 3511
|
||||
assert limit(x - root3(x**3 - 1), x, oo) == 0 # 215
|
||||
|
||||
|
||||
def test_Limits_simple_4c():
|
||||
assert limit(log(1 + exp(x))/x, x, -oo) == 0 # 267a
|
||||
assert limit(log(1 + exp(x))/x, x, oo) == 1 # 267b
|
||||
|
||||
|
||||
def test_bounded():
|
||||
assert limit(sin(x)/x, x, oo) == 0 # 216b
|
||||
assert limit(x*sin(1/x), x, 0) == 0 # 227a
|
||||
|
||||
|
||||
def test_f1a():
|
||||
#issue 3508:
|
||||
assert limit((sin(2*x)/x)**(1 + x), x, 0) == 2 # Primer 7
|
||||
|
||||
|
||||
def test_f1a2():
|
||||
#issue 3509:
|
||||
assert limit(((x - 1)/(x + 1))**x, x, oo) == exp(-2) # Primer 9
|
||||
|
||||
|
||||
def test_f1b():
|
||||
m = Symbol("m")
|
||||
n = Symbol("n")
|
||||
h = Symbol("h")
|
||||
a = Symbol("a")
|
||||
assert limit(sin(x)/x, x, 2) == sin(2)/2 # 216a
|
||||
assert limit(sin(3*x)/x, x, 0) == 3 # 217
|
||||
assert limit(sin(5*x)/sin(2*x), x, 0) == Rational(5, 2) # 218
|
||||
assert limit(sin(pi*x)/sin(3*pi*x), x, 0) == Rational(1, 3) # 219
|
||||
assert limit(x*sin(pi/x), x, oo) == pi # 220
|
||||
assert limit((1 - cos(x))/x**2, x, 0) == S.Half # 221
|
||||
assert limit(x*sin(1/x), x, oo) == 1 # 227b
|
||||
assert limit((cos(m*x) - cos(n*x))/x**2, x, 0) == -m**2/2 + n**2/2 # 232
|
||||
assert limit((tan(x) - sin(x))/x**3, x, 0) == S.Half # 233
|
||||
assert limit((x - sin(2*x))/(x + sin(3*x)), x, 0) == -Rational(1, 4) # 237
|
||||
assert limit((1 - sqrt(cos(x)))/x**2, x, 0) == Rational(1, 4) # 239
|
||||
assert limit((sqrt(1 + sin(x)) - sqrt(1 - sin(x)))/x, x, 0) == 1 # 240
|
||||
|
||||
assert limit((1 + h/x)**x, x, oo) == exp(h) # Primer 9
|
||||
assert limit((sin(x) - sin(a))/(x - a), x, a) == cos(a) # 222, *176
|
||||
assert limit((cos(x) - cos(a))/(x - a), x, a) == -sin(a) # 223
|
||||
assert limit((sin(x + h) - sin(x))/h, h, 0) == cos(x) # 225
|
||||
|
||||
|
||||
def test_f2a():
|
||||
assert limit(((x + 1)/(2*x + 1))**(x**2), x, oo) == 0 # Primer 8
|
||||
|
||||
|
||||
def test_f2():
|
||||
assert limit((sqrt(
|
||||
cos(x)) - root3(cos(x)))/(sin(x)**2), x, 0) == -Rational(1, 12) # *184
|
||||
|
||||
|
||||
def test_f3():
|
||||
a = Symbol('a')
|
||||
#issue 3504
|
||||
assert limit(asin(a*x)/x, x, 0) == a
|
||||
@@ -0,0 +1,618 @@
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.function import (Derivative, Function)
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import (I, Rational, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.hyperbolic import (acosh, asech)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (acos, asin, atan, cos, sin)
|
||||
from sympy.functions.special.bessel import airyai
|
||||
from sympy.functions.special.error_functions import erf
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.series.formal import fps
|
||||
from sympy.series.order import O
|
||||
from sympy.series.formal import (rational_algorithm, FormalPowerSeries,
|
||||
FormalPowerSeriesProduct, FormalPowerSeriesCompose,
|
||||
FormalPowerSeriesInverse, simpleDE,
|
||||
rational_independent, exp_re, hyper_re)
|
||||
from sympy.testing.pytest import raises, XFAIL, slow
|
||||
|
||||
x, y, z = symbols('x y z')
|
||||
n, m, k = symbols('n m k', integer=True)
|
||||
f, r = Function('f'), Function('r')
|
||||
|
||||
|
||||
def test_rational_algorithm():
|
||||
f = 1 / ((x - 1)**2 * (x - 2))
|
||||
assert rational_algorithm(f, x, k) == \
|
||||
(-2**(-k - 1) + 1 - (factorial(k + 1) / factorial(k)), 0, 0)
|
||||
|
||||
f = (1 + x + x**2 + x**3) / ((x - 1) * (x - 2))
|
||||
assert rational_algorithm(f, x, k) == \
|
||||
(-15*2**(-k - 1) + 4, x + 4, 0)
|
||||
|
||||
f = z / (y*m - m*x - y*x + x**2)
|
||||
assert rational_algorithm(f, x, k) == \
|
||||
(((-y**(-k - 1)*z) / (y - m)) + ((m**(-k - 1)*z) / (y - m)), 0, 0)
|
||||
|
||||
f = x / (1 - x - x**2)
|
||||
assert rational_algorithm(f, x, k) is None
|
||||
assert rational_algorithm(f, x, k, full=True) == \
|
||||
(((Rational(-1, 2) + sqrt(5)/2)**(-k - 1) *
|
||||
(-sqrt(5)/10 + S.Half)) +
|
||||
((-sqrt(5)/2 - S.Half)**(-k - 1) *
|
||||
(sqrt(5)/10 + S.Half)), 0, 0)
|
||||
|
||||
f = 1 / (x**2 + 2*x + 2)
|
||||
assert rational_algorithm(f, x, k) is None
|
||||
assert rational_algorithm(f, x, k, full=True) == \
|
||||
((I*(-1 + I)**(-k - 1)) / 2 - (I*(-1 - I)**(-k - 1)) / 2, 0, 0)
|
||||
|
||||
f = log(1 + x)
|
||||
assert rational_algorithm(f, x, k) == \
|
||||
(-(-1)**(-k) / k, 0, 1)
|
||||
|
||||
f = atan(x)
|
||||
assert rational_algorithm(f, x, k) is None
|
||||
assert rational_algorithm(f, x, k, full=True) == \
|
||||
(((I*I**(-k)) / 2 - (I*(-I)**(-k)) / 2) / k, 0, 1)
|
||||
|
||||
f = x*atan(x) - log(1 + x**2) / 2
|
||||
assert rational_algorithm(f, x, k) is None
|
||||
assert rational_algorithm(f, x, k, full=True) == \
|
||||
(((I*I**(-k + 1)) / 2 - (I*(-I)**(-k + 1)) / 2) /
|
||||
(k*(k - 1)), 0, 2)
|
||||
|
||||
f = log((1 + x) / (1 - x)) / 2 - atan(x)
|
||||
assert rational_algorithm(f, x, k) is None
|
||||
assert rational_algorithm(f, x, k, full=True) == \
|
||||
((-(-1)**(-k) / 2 - (I*I**(-k)) / 2 + (I*(-I)**(-k)) / 2 +
|
||||
S.Half) / k, 0, 1)
|
||||
|
||||
assert rational_algorithm(cos(x), x, k) is None
|
||||
|
||||
|
||||
def test_rational_independent():
|
||||
ri = rational_independent
|
||||
assert ri([], x) == []
|
||||
assert ri([cos(x), sin(x)], x) == [cos(x), sin(x)]
|
||||
assert ri([x**2, sin(x), x*sin(x), x**3], x) == \
|
||||
[x**3 + x**2, x*sin(x) + sin(x)]
|
||||
assert ri([S.One, x*log(x), log(x), sin(x)/x, cos(x), sin(x), x], x) == \
|
||||
[x + 1, x*log(x) + log(x), sin(x)/x + sin(x), cos(x)]
|
||||
|
||||
|
||||
def test_simpleDE():
|
||||
# Tests just the first valid DE
|
||||
for DE in simpleDE(exp(x), x, f):
|
||||
assert DE == (-f(x) + Derivative(f(x), x), 1)
|
||||
break
|
||||
for DE in simpleDE(sin(x), x, f):
|
||||
assert DE == (f(x) + Derivative(f(x), x, x), 2)
|
||||
break
|
||||
for DE in simpleDE(log(1 + x), x, f):
|
||||
assert DE == ((x + 1)*Derivative(f(x), x, 2) + Derivative(f(x), x), 2)
|
||||
break
|
||||
for DE in simpleDE(asin(x), x, f):
|
||||
assert DE == (x*Derivative(f(x), x) + (x**2 - 1)*Derivative(f(x), x, x),
|
||||
2)
|
||||
break
|
||||
for DE in simpleDE(exp(x)*sin(x), x, f):
|
||||
assert DE == (2*f(x) - 2*Derivative(f(x)) + Derivative(f(x), x, x), 2)
|
||||
break
|
||||
for DE in simpleDE(((1 + x)/(1 - x))**n, x, f):
|
||||
assert DE == (2*n*f(x) + (x**2 - 1)*Derivative(f(x), x), 1)
|
||||
break
|
||||
for DE in simpleDE(airyai(x), x, f):
|
||||
assert DE == (-x*f(x) + Derivative(f(x), x, x), 2)
|
||||
break
|
||||
|
||||
|
||||
def test_exp_re():
|
||||
d = -f(x) + Derivative(f(x), x)
|
||||
assert exp_re(d, r, k) == -r(k) + r(k + 1)
|
||||
|
||||
d = f(x) + Derivative(f(x), x, x)
|
||||
assert exp_re(d, r, k) == r(k) + r(k + 2)
|
||||
|
||||
d = f(x) + Derivative(f(x), x) + Derivative(f(x), x, x)
|
||||
assert exp_re(d, r, k) == r(k) + r(k + 1) + r(k + 2)
|
||||
|
||||
d = Derivative(f(x), x) + Derivative(f(x), x, x)
|
||||
assert exp_re(d, r, k) == r(k) + r(k + 1)
|
||||
|
||||
d = Derivative(f(x), x, 3) + Derivative(f(x), x, 4) + Derivative(f(x))
|
||||
assert exp_re(d, r, k) == r(k) + r(k + 2) + r(k + 3)
|
||||
|
||||
|
||||
def test_hyper_re():
|
||||
d = f(x) + Derivative(f(x), x, x)
|
||||
assert hyper_re(d, r, k) == r(k) + (k+1)*(k+2)*r(k + 2)
|
||||
|
||||
d = -x*f(x) + Derivative(f(x), x, x)
|
||||
assert hyper_re(d, r, k) == (k + 2)*(k + 3)*r(k + 3) - r(k)
|
||||
|
||||
d = 2*f(x) - 2*Derivative(f(x), x) + Derivative(f(x), x, x)
|
||||
assert hyper_re(d, r, k) == \
|
||||
(-2*k - 2)*r(k + 1) + (k + 1)*(k + 2)*r(k + 2) + 2*r(k)
|
||||
|
||||
d = 2*n*f(x) + (x**2 - 1)*Derivative(f(x), x)
|
||||
assert hyper_re(d, r, k) == \
|
||||
k*r(k) + 2*n*r(k + 1) + (-k - 2)*r(k + 2)
|
||||
|
||||
d = (x**10 + 4)*Derivative(f(x), x) + x*(x**10 - 1)*Derivative(f(x), x, x)
|
||||
assert hyper_re(d, r, k) == \
|
||||
(k*(k - 1) + k)*r(k) + (4*k - (k + 9)*(k + 10) + 40)*r(k + 10)
|
||||
|
||||
d = ((x**2 - 1)*Derivative(f(x), x, 3) + 3*x*Derivative(f(x), x, x) +
|
||||
Derivative(f(x), x))
|
||||
assert hyper_re(d, r, k) == \
|
||||
((k*(k - 2)*(k - 1) + 3*k*(k - 1) + k)*r(k) +
|
||||
(-k*(k + 1)*(k + 2))*r(k + 2))
|
||||
|
||||
|
||||
def test_fps():
|
||||
assert fps(1) == 1
|
||||
assert fps(2, x) == 2
|
||||
assert fps(2, x, dir='+') == 2
|
||||
assert fps(2, x, dir='-') == 2
|
||||
assert fps(1/x + 1/x**2) == 1/x + 1/x**2
|
||||
assert fps(log(1 + x), hyper=False, rational=False) == log(1 + x)
|
||||
|
||||
f = fps(x**2 + x + 1)
|
||||
assert isinstance(f, FormalPowerSeries)
|
||||
assert f.function == x**2 + x + 1
|
||||
assert f[0] == 1
|
||||
assert f[2] == x**2
|
||||
assert f.truncate(4) == x**2 + x + 1 + O(x**4)
|
||||
assert f.polynomial() == x**2 + x + 1
|
||||
|
||||
f = fps(log(1 + x))
|
||||
assert isinstance(f, FormalPowerSeries)
|
||||
assert f.function == log(1 + x)
|
||||
assert f.subs(x, y) == f
|
||||
assert f[:5] == [0, x, -x**2/2, x**3/3, -x**4/4]
|
||||
assert f.as_leading_term(x) == x
|
||||
assert f.polynomial(6) == x - x**2/2 + x**3/3 - x**4/4 + x**5/5
|
||||
|
||||
k = f.ak.variables[0]
|
||||
assert f.infinite == Sum((-(-1)**(-k)*x**k)/k, (k, 1, oo))
|
||||
|
||||
ft, s = f.truncate(n=None), f[:5]
|
||||
for i, t in enumerate(ft):
|
||||
if i == 5:
|
||||
break
|
||||
assert s[i] == t
|
||||
|
||||
f = sin(x).fps(x)
|
||||
assert isinstance(f, FormalPowerSeries)
|
||||
assert f.truncate() == x - x**3/6 + x**5/120 + O(x**6)
|
||||
|
||||
raises(NotImplementedError, lambda: fps(y*x))
|
||||
raises(ValueError, lambda: fps(x, dir=0))
|
||||
|
||||
|
||||
@slow
|
||||
def test_fps__rational():
|
||||
assert fps(1/x) == (1/x)
|
||||
assert fps((x**2 + x + 1) / x**3, dir=-1) == (x**2 + x + 1) / x**3
|
||||
|
||||
f = 1 / ((x - 1)**2 * (x - 2))
|
||||
assert fps(f, x).truncate() == \
|
||||
(Rational(-1, 2) - x*Rational(5, 4) - 17*x**2/8 - 49*x**3/16 - 129*x**4/32 -
|
||||
321*x**5/64 + O(x**6))
|
||||
|
||||
f = (1 + x + x**2 + x**3) / ((x - 1) * (x - 2))
|
||||
assert fps(f, x).truncate() == \
|
||||
(S.Half + x*Rational(5, 4) + 17*x**2/8 + 49*x**3/16 + 113*x**4/32 +
|
||||
241*x**5/64 + O(x**6))
|
||||
|
||||
f = x / (1 - x - x**2)
|
||||
assert fps(f, x, full=True).truncate() == \
|
||||
x + x**2 + 2*x**3 + 3*x**4 + 5*x**5 + O(x**6)
|
||||
|
||||
f = 1 / (x**2 + 2*x + 2)
|
||||
assert fps(f, x, full=True).truncate() == \
|
||||
S.Half - x/2 + x**2/4 - x**4/8 + x**5/8 + O(x**6)
|
||||
|
||||
f = log(1 + x)
|
||||
assert fps(f, x).truncate() == \
|
||||
x - x**2/2 + x**3/3 - x**4/4 + x**5/5 + O(x**6)
|
||||
assert fps(f, x, dir=1).truncate() == fps(f, x, dir=-1).truncate()
|
||||
assert fps(f, x, 2).truncate() == \
|
||||
(log(3) - Rational(2, 3) - (x - 2)**2/18 + (x - 2)**3/81 -
|
||||
(x - 2)**4/324 + (x - 2)**5/1215 + x/3 + O((x - 2)**6, (x, 2)))
|
||||
assert fps(f, x, 2, dir=-1).truncate() == \
|
||||
(log(3) - Rational(2, 3) - (-x + 2)**2/18 - (-x + 2)**3/81 -
|
||||
(-x + 2)**4/324 - (-x + 2)**5/1215 + x/3 + O((x - 2)**6, (x, 2)))
|
||||
|
||||
f = atan(x)
|
||||
assert fps(f, x, full=True).truncate() == x - x**3/3 + x**5/5 + O(x**6)
|
||||
assert fps(f, x, full=True, dir=1).truncate() == \
|
||||
fps(f, x, full=True, dir=-1).truncate()
|
||||
assert fps(f, x, 2, full=True).truncate() == \
|
||||
(atan(2) - Rational(2, 5) - 2*(x - 2)**2/25 + 11*(x - 2)**3/375 -
|
||||
6*(x - 2)**4/625 + 41*(x - 2)**5/15625 + x/5 + O((x - 2)**6, (x, 2)))
|
||||
assert fps(f, x, 2, full=True, dir=-1).truncate() == \
|
||||
(atan(2) - Rational(2, 5) - 2*(-x + 2)**2/25 - 11*(-x + 2)**3/375 -
|
||||
6*(-x + 2)**4/625 - 41*(-x + 2)**5/15625 + x/5 + O((x - 2)**6, (x, 2)))
|
||||
|
||||
f = x*atan(x) - log(1 + x**2) / 2
|
||||
assert fps(f, x, full=True).truncate() == x**2/2 - x**4/12 + O(x**6)
|
||||
|
||||
f = log((1 + x) / (1 - x)) / 2 - atan(x)
|
||||
assert fps(f, x, full=True).truncate(n=10) == 2*x**3/3 + 2*x**7/7 + O(x**10)
|
||||
|
||||
|
||||
@slow
|
||||
def test_fps__hyper():
|
||||
f = sin(x)
|
||||
assert fps(f, x).truncate() == x - x**3/6 + x**5/120 + O(x**6)
|
||||
|
||||
f = cos(x)
|
||||
assert fps(f, x).truncate() == 1 - x**2/2 + x**4/24 + O(x**6)
|
||||
|
||||
f = exp(x)
|
||||
assert fps(f, x).truncate() == \
|
||||
1 + x + x**2/2 + x**3/6 + x**4/24 + x**5/120 + O(x**6)
|
||||
|
||||
f = atan(x)
|
||||
assert fps(f, x).truncate() == x - x**3/3 + x**5/5 + O(x**6)
|
||||
|
||||
f = exp(acos(x))
|
||||
assert fps(f, x).truncate() == \
|
||||
(exp(pi/2) - x*exp(pi/2) + x**2*exp(pi/2)/2 - x**3*exp(pi/2)/3 +
|
||||
5*x**4*exp(pi/2)/24 - x**5*exp(pi/2)/6 + O(x**6))
|
||||
|
||||
f = exp(acosh(x))
|
||||
assert fps(f, x).truncate() == I + x - I*x**2/2 - I*x**4/8 + O(x**6)
|
||||
|
||||
f = atan(1/x)
|
||||
assert fps(f, x).truncate() == pi/2 - x + x**3/3 - x**5/5 + O(x**6)
|
||||
|
||||
f = x*atan(x) - log(1 + x**2) / 2
|
||||
assert fps(f, x, rational=False).truncate() == x**2/2 - x**4/12 + O(x**6)
|
||||
|
||||
f = log(1 + x)
|
||||
assert fps(f, x, rational=False).truncate() == \
|
||||
x - x**2/2 + x**3/3 - x**4/4 + x**5/5 + O(x**6)
|
||||
|
||||
f = airyai(x**2)
|
||||
assert fps(f, x).truncate() == \
|
||||
(3**Rational(5, 6)*gamma(Rational(1, 3))/(6*pi) -
|
||||
3**Rational(2, 3)*x**2/(3*gamma(Rational(1, 3))) + O(x**6))
|
||||
|
||||
f = exp(x)*sin(x)
|
||||
assert fps(f, x).truncate() == x + x**2 + x**3/3 - x**5/30 + O(x**6)
|
||||
|
||||
f = exp(x)*sin(x)/x
|
||||
assert fps(f, x).truncate() == 1 + x + x**2/3 - x**4/30 - x**5/90 + O(x**6)
|
||||
|
||||
f = sin(x) * cos(x)
|
||||
assert fps(f, x).truncate() == x - 2*x**3/3 + 2*x**5/15 + O(x**6)
|
||||
|
||||
|
||||
def test_fps_shift():
|
||||
f = x**-5*sin(x)
|
||||
assert fps(f, x).truncate() == \
|
||||
1/x**4 - 1/(6*x**2) + Rational(1, 120) - x**2/5040 + x**4/362880 + O(x**6)
|
||||
|
||||
f = x**2*atan(x)
|
||||
assert fps(f, x, rational=False).truncate() == \
|
||||
x**3 - x**5/3 + O(x**6)
|
||||
|
||||
f = cos(sqrt(x))*x
|
||||
assert fps(f, x).truncate() == \
|
||||
x - x**2/2 + x**3/24 - x**4/720 + x**5/40320 + O(x**6)
|
||||
|
||||
f = x**2*cos(sqrt(x))
|
||||
assert fps(f, x).truncate() == \
|
||||
x**2 - x**3/2 + x**4/24 - x**5/720 + O(x**6)
|
||||
|
||||
|
||||
def test_fps__Add_expr():
|
||||
f = x*atan(x) - log(1 + x**2) / 2
|
||||
assert fps(f, x).truncate() == x**2/2 - x**4/12 + O(x**6)
|
||||
|
||||
f = sin(x) + cos(x) - exp(x) + log(1 + x)
|
||||
assert fps(f, x).truncate() == x - 3*x**2/2 - x**4/4 + x**5/5 + O(x**6)
|
||||
|
||||
f = 1/x + sin(x)
|
||||
assert fps(f, x).truncate() == 1/x + x - x**3/6 + x**5/120 + O(x**6)
|
||||
|
||||
f = sin(x) - cos(x) + 1/(x - 1)
|
||||
assert fps(f, x).truncate() == \
|
||||
-2 - x**2/2 - 7*x**3/6 - 25*x**4/24 - 119*x**5/120 + O(x**6)
|
||||
|
||||
|
||||
def test_fps__asymptotic():
|
||||
f = exp(x)
|
||||
assert fps(f, x, oo) == f
|
||||
assert fps(f, x, -oo).truncate() == O(1/x**6, (x, oo))
|
||||
|
||||
f = erf(x)
|
||||
assert fps(f, x, oo).truncate() == 1 + O(1/x**6, (x, oo))
|
||||
assert fps(f, x, -oo).truncate() == -1 + O(1/x**6, (x, oo))
|
||||
|
||||
f = atan(x)
|
||||
assert fps(f, x, oo, full=True).truncate() == \
|
||||
-1/(5*x**5) + 1/(3*x**3) - 1/x + pi/2 + O(1/x**6, (x, oo))
|
||||
assert fps(f, x, -oo, full=True).truncate() == \
|
||||
-1/(5*x**5) + 1/(3*x**3) - 1/x - pi/2 + O(1/x**6, (x, oo))
|
||||
|
||||
f = log(1 + x)
|
||||
assert fps(f, x, oo) != \
|
||||
(-1/(5*x**5) - 1/(4*x**4) + 1/(3*x**3) - 1/(2*x**2) + 1/x - log(1/x) +
|
||||
O(1/x**6, (x, oo)))
|
||||
assert fps(f, x, -oo) != \
|
||||
(-1/(5*x**5) - 1/(4*x**4) + 1/(3*x**3) - 1/(2*x**2) + 1/x + I*pi -
|
||||
log(-1/x) + O(1/x**6, (x, oo)))
|
||||
|
||||
|
||||
def test_fps__fractional():
|
||||
f = sin(sqrt(x)) / x
|
||||
assert fps(f, x).truncate() == \
|
||||
(1/sqrt(x) - sqrt(x)/6 + x**Rational(3, 2)/120 -
|
||||
x**Rational(5, 2)/5040 + x**Rational(7, 2)/362880 -
|
||||
x**Rational(9, 2)/39916800 + x**Rational(11, 2)/6227020800 + O(x**6))
|
||||
|
||||
f = sin(sqrt(x)) * x
|
||||
assert fps(f, x).truncate() == \
|
||||
(x**Rational(3, 2) - x**Rational(5, 2)/6 + x**Rational(7, 2)/120 -
|
||||
x**Rational(9, 2)/5040 + x**Rational(11, 2)/362880 + O(x**6))
|
||||
|
||||
f = atan(sqrt(x)) / x**2
|
||||
assert fps(f, x).truncate() == \
|
||||
(x**Rational(-3, 2) - x**Rational(-1, 2)/3 + x**S.Half/5 -
|
||||
x**Rational(3, 2)/7 + x**Rational(5, 2)/9 - x**Rational(7, 2)/11 +
|
||||
x**Rational(9, 2)/13 - x**Rational(11, 2)/15 + O(x**6))
|
||||
|
||||
f = exp(sqrt(x))
|
||||
assert fps(f, x).truncate().expand() == \
|
||||
(1 + x/2 + x**2/24 + x**3/720 + x**4/40320 + x**5/3628800 + sqrt(x) +
|
||||
x**Rational(3, 2)/6 + x**Rational(5, 2)/120 + x**Rational(7, 2)/5040 +
|
||||
x**Rational(9, 2)/362880 + x**Rational(11, 2)/39916800 + O(x**6))
|
||||
|
||||
f = exp(sqrt(x))*x
|
||||
assert fps(f, x).truncate().expand() == \
|
||||
(x + x**2/2 + x**3/24 + x**4/720 + x**5/40320 + x**Rational(3, 2) +
|
||||
x**Rational(5, 2)/6 + x**Rational(7, 2)/120 + x**Rational(9, 2)/5040 +
|
||||
x**Rational(11, 2)/362880 + O(x**6))
|
||||
|
||||
|
||||
def test_fps__logarithmic_singularity():
|
||||
f = log(1 + 1/x)
|
||||
assert fps(f, x) != \
|
||||
-log(x) + x - x**2/2 + x**3/3 - x**4/4 + x**5/5 + O(x**6)
|
||||
assert fps(f, x, rational=False) != \
|
||||
-log(x) + x - x**2/2 + x**3/3 - x**4/4 + x**5/5 + O(x**6)
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_fps__logarithmic_singularity_fail():
|
||||
f = asech(x) # Algorithms for computing limits probably needs improvements
|
||||
assert fps(f, x) == log(2) - log(x) - x**2/4 - 3*x**4/64 + O(x**6)
|
||||
|
||||
|
||||
def test_fps_symbolic():
|
||||
f = x**n*sin(x**2)
|
||||
assert fps(f, x).truncate(8) == x**(n + 2) - x**(n + 6)/6 + O(x**(n + 8), x)
|
||||
|
||||
f = x**n*log(1 + x)
|
||||
fp = fps(f, x)
|
||||
k = fp.ak.variables[0]
|
||||
assert fp.infinite == \
|
||||
Sum((-(-1)**(-k)*x**(k + n))/k, (k, 1, oo))
|
||||
|
||||
f = (x - 2)**n*log(1 + x)
|
||||
assert fps(f, x, 2).truncate() == \
|
||||
((x - 2)**n*log(3) + (x - 2)**(n + 1)/3 - (x - 2)**(n + 2)/18 + (x - 2)**(n + 3)/81 -
|
||||
(x - 2)**(n + 4)/324 + (x - 2)**(n + 5)/1215 + O((x - 2)**(n + 6), (x, 2)))
|
||||
|
||||
f = x**(n - 2)*cos(x)
|
||||
assert fps(f, x).truncate() == \
|
||||
(x**(n - 2) - x**n/2 + x**(n + 2)/24 + O(x**(n + 4), x))
|
||||
|
||||
f = x**(n - 2)*sin(x) + x**n*exp(x)
|
||||
assert fps(f, x).truncate() == \
|
||||
(x**(n - 1) + x**(n + 1) + x**(n + 2)/2 + x**n +
|
||||
x**(n + 4)/24 + x**(n + 5)/60 + O(x**(n + 6), x))
|
||||
|
||||
f = x**n*atan(x)
|
||||
assert fps(f, x, oo).truncate() == \
|
||||
(-x**(n - 5)/5 + x**(n - 3)/3 + x**n*(pi/2 - 1/x) +
|
||||
O((1/x)**(-n)/x**6, (x, oo)))
|
||||
|
||||
f = x**(n/2)*cos(x)
|
||||
assert fps(f, x).truncate() == \
|
||||
x**(n/2) - x**(n/2 + 2)/2 + x**(n/2 + 4)/24 + O(x**(n/2 + 6), x)
|
||||
|
||||
f = x**(n + m)*sin(x)
|
||||
assert fps(f, x).truncate() == \
|
||||
x**(m + n + 1) - x**(m + n + 3)/6 + x**(m + n + 5)/120 + O(x**(m + n + 6), x)
|
||||
|
||||
|
||||
def test_fps__slow():
|
||||
f = x*exp(x)*sin(2*x) # TODO: rsolve needs improvement
|
||||
assert fps(f, x).truncate() == 2*x**2 + 2*x**3 - x**4/3 - x**5 + O(x**6)
|
||||
|
||||
|
||||
def test_fps__operations():
|
||||
f1, f2 = fps(sin(x)), fps(cos(x))
|
||||
|
||||
fsum = f1 + f2
|
||||
assert fsum.function == sin(x) + cos(x)
|
||||
assert fsum.truncate() == \
|
||||
1 + x - x**2/2 - x**3/6 + x**4/24 + x**5/120 + O(x**6)
|
||||
|
||||
fsum = f1 + 1
|
||||
assert fsum.function == sin(x) + 1
|
||||
assert fsum.truncate() == 1 + x - x**3/6 + x**5/120 + O(x**6)
|
||||
|
||||
fsum = 1 + f2
|
||||
assert fsum.function == cos(x) + 1
|
||||
assert fsum.truncate() == 2 - x**2/2 + x**4/24 + O(x**6)
|
||||
|
||||
assert (f1 + x) == Add(f1, x)
|
||||
|
||||
assert -f2.truncate() == -1 + x**2/2 - x**4/24 + O(x**6)
|
||||
assert (f1 - f1) is S.Zero
|
||||
|
||||
fsub = f1 - f2
|
||||
assert fsub.function == sin(x) - cos(x)
|
||||
assert fsub.truncate() == \
|
||||
-1 + x + x**2/2 - x**3/6 - x**4/24 + x**5/120 + O(x**6)
|
||||
|
||||
fsub = f1 - 1
|
||||
assert fsub.function == sin(x) - 1
|
||||
assert fsub.truncate() == -1 + x - x**3/6 + x**5/120 + O(x**6)
|
||||
|
||||
fsub = 1 - f2
|
||||
assert fsub.function == -cos(x) + 1
|
||||
assert fsub.truncate() == x**2/2 - x**4/24 + O(x**6)
|
||||
|
||||
raises(ValueError, lambda: f1 + fps(exp(x), dir=-1))
|
||||
raises(ValueError, lambda: f1 + fps(exp(x), x0=1))
|
||||
|
||||
fm = f1 * 3
|
||||
|
||||
assert fm.function == 3*sin(x)
|
||||
assert fm.truncate() == 3*x - x**3/2 + x**5/40 + O(x**6)
|
||||
|
||||
fm = 3 * f2
|
||||
|
||||
assert fm.function == 3*cos(x)
|
||||
assert fm.truncate() == 3 - 3*x**2/2 + x**4/8 + O(x**6)
|
||||
|
||||
assert (f1 * f2) == Mul(f1, f2)
|
||||
assert (f1 * x) == Mul(f1, x)
|
||||
|
||||
fd = f1.diff()
|
||||
assert fd.function == cos(x)
|
||||
assert fd.truncate() == 1 - x**2/2 + x**4/24 + O(x**6)
|
||||
|
||||
fd = f2.diff()
|
||||
assert fd.function == -sin(x)
|
||||
assert fd.truncate() == -x + x**3/6 - x**5/120 + O(x**6)
|
||||
|
||||
fd = f2.diff().diff()
|
||||
assert fd.function == -cos(x)
|
||||
assert fd.truncate() == -1 + x**2/2 - x**4/24 + O(x**6)
|
||||
|
||||
f3 = fps(exp(sqrt(x)))
|
||||
fd = f3.diff()
|
||||
assert fd.truncate().expand() == \
|
||||
(1/(2*sqrt(x)) + S.Half + x/12 + x**2/240 + x**3/10080 + x**4/725760 +
|
||||
x**5/79833600 + sqrt(x)/4 + x**Rational(3, 2)/48 + x**Rational(5, 2)/1440 +
|
||||
x**Rational(7, 2)/80640 + x**Rational(9, 2)/7257600 + x**Rational(11, 2)/958003200 +
|
||||
O(x**6))
|
||||
|
||||
assert f1.integrate((x, 0, 1)) == -cos(1) + 1
|
||||
assert integrate(f1, (x, 0, 1)) == -cos(1) + 1
|
||||
|
||||
fi = integrate(f1, x)
|
||||
assert fi.function == -cos(x)
|
||||
assert fi.truncate() == -1 + x**2/2 - x**4/24 + O(x**6)
|
||||
|
||||
fi = f2.integrate(x)
|
||||
assert fi.function == sin(x)
|
||||
assert fi.truncate() == x - x**3/6 + x**5/120 + O(x**6)
|
||||
|
||||
def test_fps__product():
|
||||
f1, f2, f3 = fps(sin(x)), fps(exp(x)), fps(cos(x))
|
||||
|
||||
raises(ValueError, lambda: f1.product(exp(x), x))
|
||||
raises(ValueError, lambda: f1.product(fps(exp(x), dir=-1), x, 4))
|
||||
raises(ValueError, lambda: f1.product(fps(exp(x), x0=1), x, 4))
|
||||
raises(ValueError, lambda: f1.product(fps(exp(y)), x, 4))
|
||||
|
||||
fprod = f1.product(f2, x)
|
||||
assert isinstance(fprod, FormalPowerSeriesProduct)
|
||||
assert isinstance(fprod.ffps, FormalPowerSeries)
|
||||
assert isinstance(fprod.gfps, FormalPowerSeries)
|
||||
assert fprod.f == sin(x)
|
||||
assert fprod.g == exp(x)
|
||||
assert fprod.function == sin(x) * exp(x)
|
||||
assert fprod._eval_terms(4) == x + x**2 + x**3/3
|
||||
assert fprod.truncate(4) == x + x**2 + x**3/3 + O(x**4)
|
||||
assert fprod.polynomial(4) == x + x**2 + x**3/3
|
||||
|
||||
raises(NotImplementedError, lambda: fprod._eval_term(5))
|
||||
raises(NotImplementedError, lambda: fprod.infinite)
|
||||
raises(NotImplementedError, lambda: fprod._eval_derivative(x))
|
||||
raises(NotImplementedError, lambda: fprod.integrate(x))
|
||||
|
||||
assert f1.product(f3, x)._eval_terms(4) == x - 2*x**3/3
|
||||
assert f1.product(f3, x).truncate(4) == x - 2*x**3/3 + O(x**4)
|
||||
|
||||
|
||||
def test_fps__compose():
|
||||
f1, f2, f3 = fps(exp(x)), fps(sin(x)), fps(cos(x))
|
||||
|
||||
raises(ValueError, lambda: f1.compose(sin(x), x))
|
||||
raises(ValueError, lambda: f1.compose(fps(sin(x), dir=-1), x, 4))
|
||||
raises(ValueError, lambda: f1.compose(fps(sin(x), x0=1), x, 4))
|
||||
raises(ValueError, lambda: f1.compose(fps(sin(y)), x, 4))
|
||||
|
||||
raises(ValueError, lambda: f1.compose(f3, x))
|
||||
raises(ValueError, lambda: f2.compose(f3, x))
|
||||
|
||||
fcomp = f1.compose(f2, x)
|
||||
assert isinstance(fcomp, FormalPowerSeriesCompose)
|
||||
assert isinstance(fcomp.ffps, FormalPowerSeries)
|
||||
assert isinstance(fcomp.gfps, FormalPowerSeries)
|
||||
assert fcomp.f == exp(x)
|
||||
assert fcomp.g == sin(x)
|
||||
assert fcomp.function == exp(sin(x))
|
||||
assert fcomp._eval_terms(6) == 1 + x + x**2/2 - x**4/8 - x**5/15
|
||||
assert fcomp.truncate() == 1 + x + x**2/2 - x**4/8 - x**5/15 + O(x**6)
|
||||
assert fcomp.truncate(5) == 1 + x + x**2/2 - x**4/8 + O(x**5)
|
||||
|
||||
raises(NotImplementedError, lambda: fcomp._eval_term(5))
|
||||
raises(NotImplementedError, lambda: fcomp.infinite)
|
||||
raises(NotImplementedError, lambda: fcomp._eval_derivative(x))
|
||||
raises(NotImplementedError, lambda: fcomp.integrate(x))
|
||||
|
||||
assert f1.compose(f2, x).truncate(4) == 1 + x + x**2/2 + O(x**4)
|
||||
assert f1.compose(f2, x).truncate(8) == \
|
||||
1 + x + x**2/2 - x**4/8 - x**5/15 - x**6/240 + x**7/90 + O(x**8)
|
||||
assert f1.compose(f2, x).truncate(6) == \
|
||||
1 + x + x**2/2 - x**4/8 - x**5/15 + O(x**6)
|
||||
|
||||
assert f2.compose(f2, x).truncate(4) == x - x**3/3 + O(x**4)
|
||||
assert f2.compose(f2, x).truncate(8) == x - x**3/3 + x**5/10 - 8*x**7/315 + O(x**8)
|
||||
assert f2.compose(f2, x).truncate(6) == x - x**3/3 + x**5/10 + O(x**6)
|
||||
|
||||
|
||||
def test_fps__inverse():
|
||||
f1, f2, f3 = fps(sin(x)), fps(exp(x)), fps(cos(x))
|
||||
|
||||
raises(ValueError, lambda: f1.inverse(x))
|
||||
|
||||
finv = f2.inverse(x)
|
||||
assert isinstance(finv, FormalPowerSeriesInverse)
|
||||
assert isinstance(finv.ffps, FormalPowerSeries)
|
||||
raises(ValueError, lambda: finv.gfps)
|
||||
|
||||
assert finv.f == exp(x)
|
||||
assert finv.function == exp(-x)
|
||||
assert finv._eval_terms(5) == 1 - x + x**2/2 - x**3/6 + x**4/24
|
||||
assert finv.truncate() == 1 - x + x**2/2 - x**3/6 + x**4/24 - x**5/120 + O(x**6)
|
||||
assert finv.truncate(5) == 1 - x + x**2/2 - x**3/6 + x**4/24 + O(x**5)
|
||||
|
||||
raises(NotImplementedError, lambda: finv._eval_term(5))
|
||||
raises(ValueError, lambda: finv.g)
|
||||
raises(NotImplementedError, lambda: finv.infinite)
|
||||
raises(NotImplementedError, lambda: finv._eval_derivative(x))
|
||||
raises(NotImplementedError, lambda: finv.integrate(x))
|
||||
|
||||
assert f2.inverse(x).truncate(8) == \
|
||||
1 - x + x**2/2 - x**3/6 + x**4/24 - x**5/120 + x**6/720 - x**7/5040 + O(x**8)
|
||||
|
||||
assert f3.inverse(x).truncate() == 1 + x**2/2 + 5*x**4/24 + O(x**6)
|
||||
assert f3.inverse(x).truncate(8) == 1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + O(x**8)
|
||||
@@ -0,0 +1,165 @@
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.numbers import (Rational, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin, sinc, tan)
|
||||
from sympy.series.fourier import fourier_series
|
||||
from sympy.series.fourier import FourierSeries
|
||||
from sympy.testing.pytest import raises
|
||||
from functools import lru_cache
|
||||
|
||||
x, y, z = symbols('x y z')
|
||||
|
||||
# Don't declare these during import because they are slow
|
||||
@lru_cache()
|
||||
def _get_examples():
|
||||
fo = fourier_series(x, (x, -pi, pi))
|
||||
fe = fourier_series(x**2, (-pi, pi))
|
||||
fp = fourier_series(Piecewise((0, x < 0), (pi, True)), (x, -pi, pi))
|
||||
return fo, fe, fp
|
||||
|
||||
|
||||
def test_FourierSeries():
|
||||
fo, fe, fp = _get_examples()
|
||||
|
||||
assert fourier_series(1, (-pi, pi)) == 1
|
||||
assert (Piecewise((0, x < 0), (pi, True)).
|
||||
fourier_series((x, -pi, pi)).truncate()) == fp.truncate()
|
||||
assert isinstance(fo, FourierSeries)
|
||||
assert fo.function == x
|
||||
assert fo.x == x
|
||||
assert fo.period == (-pi, pi)
|
||||
|
||||
assert fo.term(3) == 2*sin(3*x) / 3
|
||||
assert fe.term(3) == -4*cos(3*x) / 9
|
||||
assert fp.term(3) == 2*sin(3*x) / 3
|
||||
|
||||
assert fo.as_leading_term(x) == 2*sin(x)
|
||||
assert fe.as_leading_term(x) == pi**2 / 3
|
||||
assert fp.as_leading_term(x) == pi / 2
|
||||
|
||||
assert fo.truncate() == 2*sin(x) - sin(2*x) + (2*sin(3*x) / 3)
|
||||
assert fe.truncate() == -4*cos(x) + cos(2*x) + pi**2 / 3
|
||||
assert fp.truncate() == 2*sin(x) + (2*sin(3*x) / 3) + pi / 2
|
||||
|
||||
fot = fo.truncate(n=None)
|
||||
s = [0, 2*sin(x), -sin(2*x)]
|
||||
for i, t in enumerate(fot):
|
||||
if i == 3:
|
||||
break
|
||||
assert s[i] == t
|
||||
|
||||
def _check_iter(f, i):
|
||||
for ind, t in enumerate(f):
|
||||
assert t == f[ind] # noqa: PLR1736
|
||||
if ind == i:
|
||||
break
|
||||
|
||||
_check_iter(fo, 3)
|
||||
_check_iter(fe, 3)
|
||||
_check_iter(fp, 3)
|
||||
|
||||
assert fo.subs(x, x**2) == fo
|
||||
|
||||
raises(ValueError, lambda: fourier_series(x, (0, 1, 2)))
|
||||
raises(ValueError, lambda: fourier_series(x, (x, 0, oo)))
|
||||
raises(ValueError, lambda: fourier_series(x*y, (0, oo)))
|
||||
|
||||
|
||||
def test_FourierSeries_2():
|
||||
p = Piecewise((0, x < 0), (x, True))
|
||||
f = fourier_series(p, (x, -2, 2))
|
||||
|
||||
assert f.term(3) == (2*sin(3*pi*x / 2) / (3*pi) -
|
||||
4*cos(3*pi*x / 2) / (9*pi**2))
|
||||
assert f.truncate() == (2*sin(pi*x / 2) / pi - sin(pi*x) / pi -
|
||||
4*cos(pi*x / 2) / pi**2 + S.Half)
|
||||
|
||||
|
||||
def test_square_wave():
|
||||
"""Test if fourier_series approximates discontinuous function correctly."""
|
||||
square_wave = Piecewise((1, x < pi), (-1, True))
|
||||
s = fourier_series(square_wave, (x, 0, 2*pi))
|
||||
|
||||
assert s.truncate(3) == 4 / pi * sin(x) + 4 / (3 * pi) * sin(3 * x) + \
|
||||
4 / (5 * pi) * sin(5 * x)
|
||||
assert s.sigma_approximation(4) == 4 / pi * sin(x) * sinc(pi / 4) + \
|
||||
4 / (3 * pi) * sin(3 * x) * sinc(3 * pi / 4)
|
||||
|
||||
|
||||
def test_sawtooth_wave():
|
||||
s = fourier_series(x, (x, 0, pi))
|
||||
assert s.truncate(4) == \
|
||||
pi/2 - sin(2*x) - sin(4*x)/2 - sin(6*x)/3
|
||||
s = fourier_series(x, (x, 0, 1))
|
||||
assert s.truncate(4) == \
|
||||
S.Half - sin(2*pi*x)/pi - sin(4*pi*x)/(2*pi) - sin(6*pi*x)/(3*pi)
|
||||
|
||||
|
||||
def test_FourierSeries__operations():
|
||||
fo, fe, fp = _get_examples()
|
||||
|
||||
fes = fe.scale(-1).shift(pi**2)
|
||||
assert fes.truncate() == 4*cos(x) - cos(2*x) + 2*pi**2 / 3
|
||||
|
||||
assert fp.shift(-pi/2).truncate() == (2*sin(x) + (2*sin(3*x) / 3) +
|
||||
(2*sin(5*x) / 5))
|
||||
|
||||
fos = fo.scale(3)
|
||||
assert fos.truncate() == 6*sin(x) - 3*sin(2*x) + 2*sin(3*x)
|
||||
|
||||
fx = fe.scalex(2).shiftx(1)
|
||||
assert fx.truncate() == -4*cos(2*x + 2) + cos(4*x + 4) + pi**2 / 3
|
||||
|
||||
fl = fe.scalex(3).shift(-pi).scalex(2).shiftx(1).scale(4)
|
||||
assert fl.truncate() == (-16*cos(6*x + 6) + 4*cos(12*x + 12) -
|
||||
4*pi + 4*pi**2 / 3)
|
||||
|
||||
raises(ValueError, lambda: fo.shift(x))
|
||||
raises(ValueError, lambda: fo.shiftx(sin(x)))
|
||||
raises(ValueError, lambda: fo.scale(x*y))
|
||||
raises(ValueError, lambda: fo.scalex(x**2))
|
||||
|
||||
|
||||
def test_FourierSeries__neg():
|
||||
fo, fe, fp = _get_examples()
|
||||
|
||||
assert (-fo).truncate() == -2*sin(x) + sin(2*x) - (2*sin(3*x) / 3)
|
||||
assert (-fe).truncate() == +4*cos(x) - cos(2*x) - pi**2 / 3
|
||||
|
||||
|
||||
def test_FourierSeries__add__sub():
|
||||
fo, fe, fp = _get_examples()
|
||||
|
||||
assert fo + fo == fo.scale(2)
|
||||
assert fo - fo == 0
|
||||
assert -fe - fe == fe.scale(-2)
|
||||
|
||||
assert (fo + fe).truncate() == 2*sin(x) - sin(2*x) - 4*cos(x) + cos(2*x) \
|
||||
+ pi**2 / 3
|
||||
assert (fo - fe).truncate() == 2*sin(x) - sin(2*x) + 4*cos(x) - cos(2*x) \
|
||||
- pi**2 / 3
|
||||
|
||||
assert isinstance(fo + 1, Add)
|
||||
|
||||
raises(ValueError, lambda: fo + fourier_series(x, (x, 0, 2)))
|
||||
|
||||
|
||||
def test_FourierSeries_finite():
|
||||
|
||||
assert fourier_series(sin(x)).truncate(1) == sin(x)
|
||||
# assert type(fourier_series(sin(x)*log(x))).truncate() == FourierSeries
|
||||
# assert type(fourier_series(sin(x**2+6))).truncate() == FourierSeries
|
||||
assert fourier_series(sin(x)*log(y)*exp(z),(x,pi,-pi)).truncate() == sin(x)*log(y)*exp(z)
|
||||
assert fourier_series(sin(x)**6).truncate(oo) == -15*cos(2*x)/32 + 3*cos(4*x)/16 - cos(6*x)/32 \
|
||||
+ Rational(5, 16)
|
||||
assert fourier_series(sin(x) ** 6).truncate() == -15 * cos(2 * x) / 32 + 3 * cos(4 * x) / 16 \
|
||||
+ Rational(5, 16)
|
||||
assert fourier_series(sin(4*x+3) + cos(3*x+4)).truncate(oo) == -sin(4)*sin(3*x) + sin(4*x)*cos(3) \
|
||||
+ cos(4)*cos(3*x) + sin(3)*cos(4*x)
|
||||
assert fourier_series(sin(x)+cos(x)*tan(x)).truncate(oo) == 2*sin(x)
|
||||
assert fourier_series(cos(pi*x), (x, -1, 1)).truncate(oo) == cos(pi*x)
|
||||
assert fourier_series(cos(3*pi*x + 4) - sin(4*pi*x)*log(pi*y), (x, -1, 1)).truncate(oo) == -log(pi*y)*sin(4*pi*x)\
|
||||
- sin(4)*sin(3*pi*x) + cos(4)*cos(3*pi*x)
|
||||
@@ -0,0 +1,490 @@
|
||||
from sympy.core import EulerGamma
|
||||
from sympy.core.function import Function
|
||||
from sympy.core.numbers import (E, I, Integer, Rational, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (acot, atan, cos, sin)
|
||||
from sympy.functions.special.error_functions import (Ei, erf)
|
||||
from sympy.functions.special.gamma_functions import (digamma, gamma, loggamma)
|
||||
from sympy.functions.special.zeta_functions import zeta
|
||||
from sympy.polys.polytools import cancel
|
||||
from sympy.functions.elementary.hyperbolic import cosh, coth, sinh, tanh
|
||||
from sympy.series.gruntz import compare, mrv, rewrite, mrv_leadterm, gruntz, \
|
||||
sign
|
||||
from sympy.testing.pytest import XFAIL, raises, skip, slow
|
||||
|
||||
"""
|
||||
This test suite is testing the limit algorithm using the bottom up approach.
|
||||
See the documentation in limits2.py. The algorithm itself is highly recursive
|
||||
by nature, so "compare" is logically the lowest part of the algorithm, yet in
|
||||
some sense it's the most complex part, because it needs to calculate a limit
|
||||
to return the result.
|
||||
|
||||
Nevertheless, the rest of the algorithm depends on compare working correctly.
|
||||
"""
|
||||
|
||||
x = Symbol('x', real=True)
|
||||
m = Symbol('m', real=True)
|
||||
|
||||
|
||||
runslow = False
|
||||
|
||||
|
||||
def _sskip():
|
||||
if not runslow:
|
||||
skip("slow")
|
||||
|
||||
|
||||
@slow
|
||||
def test_gruntz_evaluation():
|
||||
# Gruntz' thesis pp. 122 to 123
|
||||
# 8.1
|
||||
assert gruntz(exp(x)*(exp(1/x - exp(-x)) - exp(1/x)), x, oo) == -1
|
||||
# 8.2
|
||||
assert gruntz(exp(x)*(exp(1/x + exp(-x) + exp(-x**2))
|
||||
- exp(1/x - exp(-exp(x)))), x, oo) == 1
|
||||
# 8.3
|
||||
assert gruntz(exp(exp(x - exp(-x))/(1 - 1/x)) - exp(exp(x)), x, oo) is oo
|
||||
# 8.5
|
||||
assert gruntz(exp(exp(exp(x + exp(-x)))) / exp(exp(exp(x))), x, oo) is oo
|
||||
# 8.6
|
||||
assert gruntz(exp(exp(exp(x))) / exp(exp(exp(x - exp(-exp(x))))),
|
||||
x, oo) is oo
|
||||
# 8.7
|
||||
assert gruntz(exp(exp(exp(x))) / exp(exp(exp(x - exp(-exp(exp(x)))))),
|
||||
x, oo) == 1
|
||||
# 8.8
|
||||
assert gruntz(exp(exp(x)) / exp(exp(x - exp(-exp(exp(x))))), x, oo) == 1
|
||||
# 8.9
|
||||
assert gruntz(log(x)**2 * exp(sqrt(log(x))*(log(log(x)))**2
|
||||
* exp(sqrt(log(log(x))) * (log(log(log(x))))**3)) / sqrt(x),
|
||||
x, oo) == 0
|
||||
# 8.10
|
||||
assert gruntz((x*log(x)*(log(x*exp(x) - x**2))**2)
|
||||
/ (log(log(x**2 + 2*exp(exp(3*x**3*log(x)))))), x, oo) == Rational(1, 3)
|
||||
# 8.11
|
||||
assert gruntz((exp(x*exp(-x)/(exp(-x) + exp(-2*x**2/(x + 1)))) - exp(x))/x,
|
||||
x, oo) == -exp(2)
|
||||
# 8.12
|
||||
assert gruntz((3**x + 5**x)**(1/x), x, oo) == 5
|
||||
# 8.13
|
||||
assert gruntz(x/log(x**(log(x**(log(2)/log(x))))), x, oo) is oo
|
||||
# 8.14
|
||||
assert gruntz(exp(exp(2*log(x**5 + x)*log(log(x))))
|
||||
/ exp(exp(10*log(x)*log(log(x)))), x, oo) is oo
|
||||
# 8.15
|
||||
assert gruntz(exp(exp(Rational(5, 2)*x**Rational(-5, 7) + Rational(21, 8)*x**Rational(6, 11)
|
||||
+ 2*x**(-8) + Rational(54, 17)*x**Rational(49, 45)))**8
|
||||
/ log(log(-log(Rational(4, 3)*x**Rational(-5, 14))))**Rational(7, 6), x, oo) is oo
|
||||
# 8.16
|
||||
assert gruntz((exp(4*x*exp(-x)/(1/exp(x) + 1/exp(2*x**2/(x + 1)))) - exp(x))
|
||||
/ exp(x)**4, x, oo) == 1
|
||||
# 8.17
|
||||
assert gruntz(exp(x*exp(-x)/(exp(-x) + exp(-2*x**2/(x + 1))))/exp(x), x, oo) \
|
||||
== 1
|
||||
# 8.19
|
||||
assert gruntz(log(x)*(log(log(x) + log(log(x))) - log(log(x)))
|
||||
/ (log(log(x) + log(log(log(x))))), x, oo) == 1
|
||||
# 8.20
|
||||
assert gruntz(exp((log(log(x + exp(log(x)*log(log(x))))))
|
||||
/ (log(log(log(exp(x) + x + log(x)))))), x, oo) == E
|
||||
# Another
|
||||
assert gruntz(exp(exp(exp(x + exp(-x)))) / exp(exp(x)), x, oo) is oo
|
||||
|
||||
|
||||
def test_gruntz_evaluation_slow():
|
||||
_sskip()
|
||||
# 8.4
|
||||
assert gruntz(exp(exp(exp(x)/(1 - 1/x)))
|
||||
- exp(exp(exp(x)/(1 - 1/x - log(x)**(-log(x))))), x, oo) is -oo
|
||||
# 8.18
|
||||
assert gruntz((exp(exp(-x/(1 + exp(-x))))*exp(-x/(1 + exp(-x/(1 + exp(-x)))))
|
||||
*exp(exp(-x + exp(-x/(1 + exp(-x))))))
|
||||
/ (exp(-x/(1 + exp(-x))))**2 - exp(x) + x, x, oo) == 2
|
||||
|
||||
|
||||
@slow
|
||||
def test_gruntz_eval_special():
|
||||
# Gruntz, p. 126
|
||||
assert gruntz(exp(x)*(sin(1/x + exp(-x)) - sin(1/x + exp(-x**2))), x, oo) == 1
|
||||
assert gruntz((erf(x - exp(-exp(x))) - erf(x)) * exp(exp(x)) * exp(x**2),
|
||||
x, oo) == -2/sqrt(pi)
|
||||
assert gruntz(exp(exp(x)) * (exp(sin(1/x + exp(-exp(x)))) - exp(sin(1/x))),
|
||||
x, oo) == 1
|
||||
assert gruntz(exp(x)*(gamma(x + exp(-x)) - gamma(x)), x, oo) is oo
|
||||
assert gruntz(exp(exp(digamma(digamma(x))))/x, x, oo) == exp(Rational(-1, 2))
|
||||
assert gruntz(exp(exp(digamma(log(x))))/x, x, oo) == exp(Rational(-1, 2))
|
||||
assert gruntz(digamma(digamma(digamma(x))), x, oo) is oo
|
||||
assert gruntz(loggamma(loggamma(x)), x, oo) is oo
|
||||
assert gruntz(((gamma(x + 1/gamma(x)) - gamma(x))/log(x) - cos(1/x))
|
||||
* x*log(x), x, oo) == Rational(-1, 2)
|
||||
assert gruntz(x * (gamma(x - 1/gamma(x)) - gamma(x) + log(x)), x, oo) \
|
||||
== S.Half
|
||||
assert gruntz((gamma(x + 1/gamma(x)) - gamma(x)) / log(x), x, oo) == 1
|
||||
|
||||
|
||||
def test_gruntz_eval_special_slow():
|
||||
_sskip()
|
||||
assert gruntz(gamma(x + 1)/sqrt(2*pi)
|
||||
- exp(-x)*(x**(x + S.Half) + x**(x - S.Half)/12), x, oo) is oo
|
||||
assert gruntz(exp(exp(exp(digamma(digamma(digamma(x))))))/x, x, oo) == 0
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_grunts_eval_special_slow_sometimes_fail():
|
||||
_sskip()
|
||||
# XXX This sometimes fails!!!
|
||||
assert gruntz(exp(gamma(x - exp(-x))*exp(1/x)) - exp(gamma(x)), x, oo) is oo
|
||||
|
||||
|
||||
def test_gruntz_Ei():
|
||||
assert gruntz((Ei(x - exp(-exp(x))) - Ei(x)) *exp(-x)*exp(exp(x))*x, x, oo) == -1
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_gruntz_eval_special_fail():
|
||||
# TODO zeta function series
|
||||
assert gruntz(
|
||||
exp((log(2) + 1)*x) * (zeta(x + exp(-x)) - zeta(x)), x, oo) == -log(2)
|
||||
|
||||
# TODO 8.35 - 8.37 (bessel, max-min)
|
||||
|
||||
|
||||
def test_gruntz_hyperbolic():
|
||||
assert gruntz(cosh(x), x, oo) is oo
|
||||
assert gruntz(cosh(x), x, -oo) is oo
|
||||
assert gruntz(sinh(x), x, oo) is oo
|
||||
assert gruntz(sinh(x), x, -oo) is -oo
|
||||
assert gruntz(2*cosh(x)*exp(x), x, oo) is oo
|
||||
assert gruntz(2*cosh(x)*exp(x), x, -oo) == 1
|
||||
assert gruntz(2*sinh(x)*exp(x), x, oo) is oo
|
||||
assert gruntz(2*sinh(x)*exp(x), x, -oo) == -1
|
||||
assert gruntz(tanh(x), x, oo) == 1
|
||||
assert gruntz(tanh(x), x, -oo) == -1
|
||||
assert gruntz(coth(x), x, oo) == 1
|
||||
assert gruntz(coth(x), x, -oo) == -1
|
||||
|
||||
|
||||
def test_compare1():
|
||||
assert compare(2, x, x) == "<"
|
||||
assert compare(x, exp(x), x) == "<"
|
||||
assert compare(exp(x), exp(x**2), x) == "<"
|
||||
assert compare(exp(x**2), exp(exp(x)), x) == "<"
|
||||
assert compare(1, exp(exp(x)), x) == "<"
|
||||
|
||||
assert compare(x, 2, x) == ">"
|
||||
assert compare(exp(x), x, x) == ">"
|
||||
assert compare(exp(x**2), exp(x), x) == ">"
|
||||
assert compare(exp(exp(x)), exp(x**2), x) == ">"
|
||||
assert compare(exp(exp(x)), 1, x) == ">"
|
||||
|
||||
assert compare(2, 3, x) == "="
|
||||
assert compare(3, -5, x) == "="
|
||||
assert compare(2, -5, x) == "="
|
||||
|
||||
assert compare(x, x**2, x) == "="
|
||||
assert compare(x**2, x**3, x) == "="
|
||||
assert compare(x**3, 1/x, x) == "="
|
||||
assert compare(1/x, x**m, x) == "="
|
||||
assert compare(x**m, -x, x) == "="
|
||||
|
||||
assert compare(exp(x), exp(-x), x) == "="
|
||||
assert compare(exp(-x), exp(2*x), x) == "="
|
||||
assert compare(exp(2*x), exp(x)**2, x) == "="
|
||||
assert compare(exp(x)**2, exp(x + exp(-x)), x) == "="
|
||||
assert compare(exp(x), exp(x + exp(-x)), x) == "="
|
||||
|
||||
assert compare(exp(x**2), 1/exp(x**2), x) == "="
|
||||
|
||||
|
||||
def test_compare2():
|
||||
assert compare(exp(x), x**5, x) == ">"
|
||||
assert compare(exp(x**2), exp(x)**2, x) == ">"
|
||||
assert compare(exp(x), exp(x + exp(-x)), x) == "="
|
||||
assert compare(exp(x + exp(-x)), exp(x), x) == "="
|
||||
assert compare(exp(x + exp(-x)), exp(-x), x) == "="
|
||||
assert compare(exp(-x), x, x) == ">"
|
||||
assert compare(x, exp(-x), x) == "<"
|
||||
assert compare(exp(x + 1/x), x, x) == ">"
|
||||
assert compare(exp(-exp(x)), exp(x), x) == ">"
|
||||
assert compare(exp(exp(-exp(x)) + x), exp(-exp(x)), x) == "<"
|
||||
|
||||
|
||||
def test_compare3():
|
||||
assert compare(exp(exp(x)), exp(x + exp(-exp(x))), x) == ">"
|
||||
|
||||
|
||||
def test_sign1():
|
||||
assert sign(Rational(0), x) == 0
|
||||
assert sign(Rational(3), x) == 1
|
||||
assert sign(Rational(-5), x) == -1
|
||||
assert sign(log(x), x) == 1
|
||||
assert sign(exp(-x), x) == 1
|
||||
assert sign(exp(x), x) == 1
|
||||
assert sign(-exp(x), x) == -1
|
||||
assert sign(3 - 1/x, x) == 1
|
||||
assert sign(-3 - 1/x, x) == -1
|
||||
assert sign(sin(1/x), x) == 1
|
||||
assert sign((x**Integer(2)), x) == 1
|
||||
assert sign(x**2, x) == 1
|
||||
assert sign(x**5, x) == 1
|
||||
|
||||
|
||||
def test_sign2():
|
||||
assert sign(x, x) == 1
|
||||
assert sign(-x, x) == -1
|
||||
y = Symbol("y", positive=True)
|
||||
assert sign(y, x) == 1
|
||||
assert sign(-y, x) == -1
|
||||
assert sign(y*x, x) == 1
|
||||
assert sign(-y*x, x) == -1
|
||||
|
||||
|
||||
def mmrv(a, b):
|
||||
return set(mrv(a, b)[0].keys())
|
||||
|
||||
|
||||
def test_mrv1():
|
||||
assert mmrv(x, x) == {x}
|
||||
assert mmrv(x + 1/x, x) == {x}
|
||||
assert mmrv(x**2, x) == {x}
|
||||
assert mmrv(log(x), x) == {x}
|
||||
assert mmrv(exp(x), x) == {exp(x)}
|
||||
assert mmrv(exp(-x), x) == {exp(-x)}
|
||||
assert mmrv(exp(x**2), x) == {exp(x**2)}
|
||||
assert mmrv(-exp(1/x), x) == {x}
|
||||
assert mmrv(exp(x + 1/x), x) == {exp(x + 1/x)}
|
||||
|
||||
|
||||
def test_mrv2a():
|
||||
assert mmrv(exp(x + exp(-exp(x))), x) == {exp(-exp(x))}
|
||||
assert mmrv(exp(x + exp(-x)), x) == {exp(x + exp(-x)), exp(-x)}
|
||||
assert mmrv(exp(1/x + exp(-x)), x) == {exp(-x)}
|
||||
|
||||
#sometimes infinite recursion due to log(exp(x**2)) not simplifying
|
||||
|
||||
|
||||
def test_mrv2b():
|
||||
assert mmrv(exp(x + exp(-x**2)), x) == {exp(-x**2)}
|
||||
|
||||
#sometimes infinite recursion due to log(exp(x**2)) not simplifying
|
||||
|
||||
|
||||
def test_mrv2c():
|
||||
assert mmrv(
|
||||
exp(-x + 1/x**2) - exp(x + 1/x), x) == {exp(x + 1/x), exp(1/x**2 - x)}
|
||||
|
||||
#sometimes infinite recursion due to log(exp(x**2)) not simplifying
|
||||
|
||||
|
||||
def test_mrv3():
|
||||
assert mmrv(exp(x**2) + x*exp(x) + log(x)**x/x, x) == {exp(x**2)}
|
||||
assert mmrv(
|
||||
exp(x)*(exp(1/x + exp(-x)) - exp(1/x)), x) == {exp(x), exp(-x)}
|
||||
assert mmrv(log(
|
||||
x**2 + 2*exp(exp(3*x**3*log(x)))), x) == {exp(exp(3*x**3*log(x)))}
|
||||
assert mmrv(log(x - log(x))/log(x), x) == {x}
|
||||
assert mmrv(
|
||||
(exp(1/x - exp(-x)) - exp(1/x))*exp(x), x) == {exp(x), exp(-x)}
|
||||
assert mmrv(
|
||||
1/exp(-x + exp(-x)) - exp(x), x) == {exp(x), exp(-x), exp(x - exp(-x))}
|
||||
assert mmrv(log(log(x*exp(x*exp(x)) + 1)), x) == {exp(x*exp(x))}
|
||||
assert mmrv(exp(exp(log(log(x) + 1/x))), x) == {x}
|
||||
|
||||
|
||||
def test_mrv4():
|
||||
ln = log
|
||||
assert mmrv((ln(ln(x) + ln(ln(x))) - ln(ln(x)))/ln(ln(x) + ln(ln(ln(x))))*ln(x),
|
||||
x) == {x}
|
||||
assert mmrv(log(log(x*exp(x*exp(x)) + 1)) - exp(exp(log(log(x) + 1/x))), x) == \
|
||||
{exp(x*exp(x))}
|
||||
|
||||
|
||||
def mrewrite(a, b, c):
|
||||
return rewrite(a[1], a[0], b, c)
|
||||
|
||||
|
||||
def test_rewrite1():
|
||||
e = exp(x)
|
||||
assert mrewrite(mrv(e, x), x, m) == (1/m, -x)
|
||||
e = exp(x**2)
|
||||
assert mrewrite(mrv(e, x), x, m) == (1/m, -x**2)
|
||||
e = exp(x + 1/x)
|
||||
assert mrewrite(mrv(e, x), x, m) == (1/m, -x - 1/x)
|
||||
e = 1/exp(-x + exp(-x)) - exp(x)
|
||||
assert mrewrite(mrv(e, x), x, m) == ((-m*exp(m) + m)*exp(-m)/m**2, -x)
|
||||
|
||||
|
||||
def test_rewrite2():
|
||||
e = exp(x)*log(log(exp(x)))
|
||||
assert mmrv(e, x) == {exp(x)}
|
||||
assert mrewrite(mrv(e, x), x, m) == (1/m*log(x), -x)
|
||||
|
||||
#sometimes infinite recursion due to log(exp(x**2)) not simplifying
|
||||
|
||||
|
||||
def test_rewrite3():
|
||||
e = exp(-x + 1/x**2) - exp(x + 1/x)
|
||||
#both of these are correct and should be equivalent:
|
||||
assert mrewrite(mrv(e, x), x, m) in [(-1/m + m*exp(
|
||||
(x**2 + x)/x**3), -x - 1/x), ((m**2 - exp((x**2 + x)/x**3))/m, x**(-2) - x)]
|
||||
|
||||
|
||||
def test_mrv_leadterm1():
|
||||
assert mrv_leadterm(-exp(1/x), x) == (-1, 0)
|
||||
assert mrv_leadterm(1/exp(-x + exp(-x)) - exp(x), x) == (-1, 0)
|
||||
assert mrv_leadterm(
|
||||
(exp(1/x - exp(-x)) - exp(1/x))*exp(x), x) == (-exp(1/x), 0)
|
||||
|
||||
|
||||
def test_mrv_leadterm2():
|
||||
#Gruntz: p51, 3.25
|
||||
assert mrv_leadterm((log(exp(x) + x) - x)/log(exp(x) + log(x))*exp(x), x) == \
|
||||
(1, 0)
|
||||
|
||||
|
||||
def test_mrv_leadterm3():
|
||||
#Gruntz: p56, 3.27
|
||||
assert mmrv(exp(-x + exp(-x)*exp(-x*log(x))), x) == {exp(-x - x*log(x))}
|
||||
assert mrv_leadterm(exp(-x + exp(-x)*exp(-x*log(x))), x) == (exp(-x), 0)
|
||||
|
||||
|
||||
def test_limit1():
|
||||
assert gruntz(x, x, oo) is oo
|
||||
assert gruntz(x, x, -oo) is -oo
|
||||
assert gruntz(-x, x, oo) is -oo
|
||||
assert gruntz(x**2, x, -oo) is oo
|
||||
assert gruntz(-x**2, x, oo) is -oo
|
||||
assert gruntz(x*log(x), x, 0, dir="+") == 0
|
||||
assert gruntz(1/x, x, oo) == 0
|
||||
assert gruntz(exp(x), x, oo) is oo
|
||||
assert gruntz(-exp(x), x, oo) is -oo
|
||||
assert gruntz(exp(x)/x, x, oo) is oo
|
||||
assert gruntz(1/x - exp(-x), x, oo) == 0
|
||||
assert gruntz(x + 1/x, x, oo) is oo
|
||||
|
||||
|
||||
def test_limit2():
|
||||
assert gruntz(x**x, x, 0, dir="+") == 1
|
||||
assert gruntz((exp(x) - 1)/x, x, 0) == 1
|
||||
assert gruntz(1 + 1/x, x, oo) == 1
|
||||
assert gruntz(-exp(1/x), x, oo) == -1
|
||||
assert gruntz(x + exp(-x), x, oo) is oo
|
||||
assert gruntz(x + exp(-x**2), x, oo) is oo
|
||||
assert gruntz(x + exp(-exp(x)), x, oo) is oo
|
||||
assert gruntz(13 + 1/x - exp(-x), x, oo) == 13
|
||||
|
||||
|
||||
def test_limit3():
|
||||
a = Symbol('a')
|
||||
assert gruntz(x - log(1 + exp(x)), x, oo) == 0
|
||||
assert gruntz(x - log(a + exp(x)), x, oo) == 0
|
||||
assert gruntz(exp(x)/(1 + exp(x)), x, oo) == 1
|
||||
assert gruntz(exp(x)/(a + exp(x)), x, oo) == 1
|
||||
|
||||
|
||||
def test_limit4():
|
||||
#issue 3463
|
||||
assert gruntz((3**x + 5**x)**(1/x), x, oo) == 5
|
||||
#issue 3463
|
||||
assert gruntz((3**(1/x) + 5**(1/x))**x, x, 0) == 5
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_MrvTestCase_page47_ex3_21():
|
||||
h = exp(-x/(1 + exp(-x)))
|
||||
expr = exp(h)*exp(-x/(1 + h))*exp(exp(-x + h))/h**2 - exp(x) + x
|
||||
assert mmrv(expr, x) == {1/h, exp(-x), exp(x), exp(x - h), exp(x/(1 + h))}
|
||||
|
||||
|
||||
def test_gruntz_I():
|
||||
y = Symbol("y")
|
||||
assert gruntz(I*x, x, oo) == I*oo
|
||||
assert gruntz(y*I*x, x, oo) == y*I*oo
|
||||
assert gruntz(y*3*I*x, x, oo) == y*I*oo
|
||||
assert gruntz(y*3*sin(I)*x, x, oo) == y*I*oo
|
||||
|
||||
|
||||
def test_issue_4814():
|
||||
assert gruntz((x + 1)**(1/log(x + 1)), x, oo) == E
|
||||
|
||||
|
||||
def test_intractable():
|
||||
assert gruntz(1/gamma(x), x, oo) == 0
|
||||
assert gruntz(1/loggamma(x), x, oo) == 0
|
||||
assert gruntz(gamma(x)/loggamma(x), x, oo) is oo
|
||||
assert gruntz(exp(gamma(x))/gamma(x), x, oo) is oo
|
||||
assert gruntz(gamma(x), x, 3) == 2
|
||||
assert gruntz(gamma(Rational(1, 7) + 1/x), x, oo) == gamma(Rational(1, 7))
|
||||
assert gruntz(log(x**x)/log(gamma(x)), x, oo) == 1
|
||||
assert gruntz(log(gamma(gamma(x)))/exp(x), x, oo) is oo
|
||||
|
||||
|
||||
def test_aseries_trig():
|
||||
assert cancel(gruntz(1/log(atan(x)), x, oo)
|
||||
- 1/(log(pi) + log(S.Half))) == 0
|
||||
assert gruntz(1/acot(x), x, -oo) is -oo
|
||||
|
||||
|
||||
def test_exp_log_series():
|
||||
assert gruntz(x/log(log(x*exp(x))), x, oo) is oo
|
||||
|
||||
|
||||
def test_issue_3644():
|
||||
assert gruntz(((x**7 + x + 1)/(2**x + x**2))**(-1/x), x, oo) == 2
|
||||
|
||||
|
||||
def test_issue_6843():
|
||||
n = Symbol('n', integer=True, positive=True)
|
||||
r = (n + 1)*x**(n + 1)/(x**(n + 1) - 1) - x/(x - 1)
|
||||
assert gruntz(r, x, 1).simplify() == n/2
|
||||
|
||||
|
||||
def test_issue_4190():
|
||||
assert gruntz(x - gamma(1/x), x, oo) == S.EulerGamma
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_issue_5172():
|
||||
n = Symbol('n')
|
||||
r = Symbol('r', positive=True)
|
||||
c = Symbol('c')
|
||||
p = Symbol('p', positive=True)
|
||||
m = Symbol('m', negative=True)
|
||||
expr = ((2*n*(n - r + 1)/(n + r*(n - r + 1)))**c + \
|
||||
(r - 1)*(n*(n - r + 2)/(n + r*(n - r + 1)))**c - n)/(n**c - n)
|
||||
expr = expr.subs(c, c + 1)
|
||||
assert gruntz(expr.subs(c, m), n, oo) == 1
|
||||
# fail:
|
||||
assert gruntz(expr.subs(c, p), n, oo).simplify() == \
|
||||
(2**(p + 1) + r - 1)/(r + 1)**(p + 1)
|
||||
|
||||
|
||||
def test_issue_4109():
|
||||
assert gruntz(1/gamma(x), x, 0) == 0
|
||||
assert gruntz(x*gamma(x), x, 0) == 1
|
||||
|
||||
|
||||
def test_issue_6682():
|
||||
assert gruntz(exp(2*Ei(-x))/x**2, x, 0) == exp(2*EulerGamma)
|
||||
|
||||
|
||||
def test_issue_7096():
|
||||
from sympy.functions import sign
|
||||
assert gruntz(x**-pi, x, 0, dir='-') == oo*sign((-1)**(-pi))
|
||||
|
||||
|
||||
def test_issue_7391_8166():
|
||||
f = Function('f')
|
||||
# limit should depend on the continuity of the expression at the point passed
|
||||
raises(ValueError, lambda: gruntz(f(x), x, 4))
|
||||
raises(ValueError, lambda: gruntz(x*f(x)**2/(x**2 + f(x)**4), x, 0))
|
||||
|
||||
|
||||
def test_issue_24210_25885():
|
||||
eq = exp(x)/(1+1/x)**x**2
|
||||
ans = sqrt(E)
|
||||
assert gruntz(eq, x, oo) == ans
|
||||
assert gruntz(1/eq, x, oo) == 1/ans
|
||||
@@ -0,0 +1,23 @@
|
||||
from sympy.series.kauers import finite_diff
|
||||
from sympy.series.kauers import finite_diff_kauers
|
||||
from sympy.abc import x, y, z, m, n, w
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.concrete.summations import Sum
|
||||
|
||||
|
||||
def test_finite_diff():
|
||||
assert finite_diff(x**2 + 2*x + 1, x) == 2*x + 3
|
||||
assert finite_diff(y**3 + 2*y**2 + 3*y + 5, y) == 3*y**2 + 7*y + 6
|
||||
assert finite_diff(z**2 - 2*z + 3, z) == 2*z - 1
|
||||
assert finite_diff(w**2 + 3*w - 2, w) == 2*w + 4
|
||||
assert finite_diff(sin(x), x, pi/6) == -sin(x) + sin(x + pi/6)
|
||||
assert finite_diff(cos(y), y, pi/3) == -cos(y) + cos(y + pi/3)
|
||||
assert finite_diff(x**2 - 2*x + 3, x, 2) == 4*x
|
||||
assert finite_diff(n**2 - 2*n + 3, n, 3) == 6*n + 3
|
||||
|
||||
def test_finite_diff_kauers():
|
||||
assert finite_diff_kauers(Sum(x**2, (x, 1, n))) == (n + 1)**2
|
||||
assert finite_diff_kauers(Sum(y, (y, 1, m))) == (m + 1)
|
||||
assert finite_diff_kauers(Sum((x*y), (x, 1, m), (y, 1, n))) == (m + 1)*(n + 1)
|
||||
assert finite_diff_kauers(Sum((x*y**2), (x, 1, m), (y, 1, n))) == (n + 1)**2*(m + 1)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.numbers import (I, Rational, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import (binomial, factorial, subfactorial)
|
||||
from sympy.functions.combinatorial.numbers import (fibonacci, harmonic)
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.series.limitseq import limit_seq
|
||||
from sympy.series.limitseq import difference_delta as dd
|
||||
from sympy.testing.pytest import raises, XFAIL
|
||||
from sympy.calculus.accumulationbounds import AccumulationBounds
|
||||
|
||||
n, m, k = symbols('n m k', integer=True)
|
||||
|
||||
|
||||
def test_difference_delta():
|
||||
e = n*(n + 1)
|
||||
e2 = e * k
|
||||
|
||||
assert dd(e) == 2*n + 2
|
||||
assert dd(e2, n, 2) == k*(4*n + 6)
|
||||
|
||||
raises(ValueError, lambda: dd(e2))
|
||||
raises(ValueError, lambda: dd(e2, n, oo))
|
||||
|
||||
|
||||
def test_difference_delta__Sum():
|
||||
e = Sum(1/k, (k, 1, n))
|
||||
assert dd(e, n) == 1/(n + 1)
|
||||
assert dd(e, n, 5) == Add(*[1/(i + n + 1) for i in range(5)])
|
||||
|
||||
e = Sum(1/k, (k, 1, 3*n))
|
||||
assert dd(e, n) == Add(*[1/(i + 3*n + 1) for i in range(3)])
|
||||
|
||||
e = n * Sum(1/k, (k, 1, n))
|
||||
assert dd(e, n) == 1 + Sum(1/k, (k, 1, n))
|
||||
|
||||
e = Sum(1/k, (k, 1, n), (m, 1, n))
|
||||
assert dd(e, n) == harmonic(n)
|
||||
|
||||
|
||||
def test_difference_delta__Add():
|
||||
e = n + n*(n + 1)
|
||||
assert dd(e, n) == 2*n + 3
|
||||
assert dd(e, n, 2) == 4*n + 8
|
||||
|
||||
e = n + Sum(1/k, (k, 1, n))
|
||||
assert dd(e, n) == 1 + 1/(n + 1)
|
||||
assert dd(e, n, 5) == 5 + Add(*[1/(i + n + 1) for i in range(5)])
|
||||
|
||||
|
||||
def test_difference_delta__Pow():
|
||||
e = 4**n
|
||||
assert dd(e, n) == 3*4**n
|
||||
assert dd(e, n, 2) == 15*4**n
|
||||
|
||||
e = 4**(2*n)
|
||||
assert dd(e, n) == 15*4**(2*n)
|
||||
assert dd(e, n, 2) == 255*4**(2*n)
|
||||
|
||||
e = n**4
|
||||
assert dd(e, n) == (n + 1)**4 - n**4
|
||||
|
||||
e = n**n
|
||||
assert dd(e, n) == (n + 1)**(n + 1) - n**n
|
||||
|
||||
|
||||
def test_limit_seq():
|
||||
e = binomial(2*n, n) / Sum(binomial(2*k, k), (k, 1, n))
|
||||
assert limit_seq(e) == S(3) / 4
|
||||
assert limit_seq(e, m) == e
|
||||
|
||||
e = (5*n**3 + 3*n**2 + 4) / (3*n**3 + 4*n - 5)
|
||||
assert limit_seq(e, n) == S(5) / 3
|
||||
|
||||
e = (harmonic(n) * Sum(harmonic(k), (k, 1, n))) / (n * harmonic(2*n)**2)
|
||||
assert limit_seq(e, n) == 1
|
||||
|
||||
e = Sum(k**2 * Sum(2**m/m, (m, 1, k)), (k, 1, n)) / (2**n*n)
|
||||
assert limit_seq(e, n) == 4
|
||||
|
||||
e = (Sum(binomial(3*k, k) * binomial(5*k, k), (k, 1, n)) /
|
||||
(binomial(3*n, n) * binomial(5*n, n)))
|
||||
assert limit_seq(e, n) == S(84375) / 83351
|
||||
|
||||
e = Sum(harmonic(k)**2/k, (k, 1, 2*n)) / harmonic(n)**3
|
||||
assert limit_seq(e, n) == S.One / 3
|
||||
|
||||
raises(ValueError, lambda: limit_seq(e * m))
|
||||
|
||||
|
||||
def test_alternating_sign():
|
||||
assert limit_seq((-1)**n/n**2, n) == 0
|
||||
assert limit_seq((-2)**(n+1)/(n + 3**n), n) == 0
|
||||
assert limit_seq((2*n + (-1)**n)/(n + 1), n) == 2
|
||||
assert limit_seq(sin(pi*n), n) == 0
|
||||
assert limit_seq(cos(2*pi*n), n) == 1
|
||||
assert limit_seq((S.NegativeOne/5)**n, n) == 0
|
||||
assert limit_seq((Rational(-1, 5))**n, n) == 0
|
||||
assert limit_seq((I/3)**n, n) == 0
|
||||
assert limit_seq(sqrt(n)*(I/2)**n, n) == 0
|
||||
assert limit_seq(n**7*(I/3)**n, n) == 0
|
||||
assert limit_seq(n/(n + 1) + (I/2)**n, n) == 1
|
||||
|
||||
|
||||
def test_accum_bounds():
|
||||
assert limit_seq((-1)**n, n) == AccumulationBounds(-1, 1)
|
||||
assert limit_seq(cos(pi*n), n) == AccumulationBounds(-1, 1)
|
||||
assert limit_seq(sin(pi*n/2)**2, n) == AccumulationBounds(0, 1)
|
||||
assert limit_seq(2*(-3)**n/(n + 3**n), n) == AccumulationBounds(-2, 2)
|
||||
assert limit_seq(3*n/(n + 1) + 2*(-1)**n, n) == AccumulationBounds(1, 5)
|
||||
|
||||
|
||||
def test_limitseq_sum():
|
||||
from sympy.abc import x, y, z
|
||||
assert limit_seq(Sum(1/x, (x, 1, y)) - log(y), y) == S.EulerGamma
|
||||
assert limit_seq(Sum(1/x, (x, 1, y)) - 1/y, y) is S.Infinity
|
||||
assert (limit_seq(binomial(2*x, x) / Sum(binomial(2*y, y), (y, 1, x)), x) ==
|
||||
S(3) / 4)
|
||||
assert (limit_seq(Sum(y**2 * Sum(2**z/z, (z, 1, y)), (y, 1, x)) /
|
||||
(2**x*x), x) == 4)
|
||||
|
||||
|
||||
def test_issue_9308():
|
||||
assert limit_seq(subfactorial(n)/factorial(n), n) == exp(-1)
|
||||
|
||||
|
||||
def test_issue_10382():
|
||||
n = Symbol('n', integer=True)
|
||||
assert limit_seq(fibonacci(n+1)/fibonacci(n), n).together() == S.GoldenRatio
|
||||
|
||||
|
||||
def test_issue_11672():
|
||||
assert limit_seq(Rational(-1, 2)**n, n) == 0
|
||||
|
||||
|
||||
def test_issue_14196():
|
||||
k, n = symbols('k, n', positive=True)
|
||||
m = Symbol('m')
|
||||
assert limit_seq(Sum(m**k, (m, 1, n)).doit()/(n**(k + 1)), n) == 1/(k + 1)
|
||||
|
||||
|
||||
def test_issue_16735():
|
||||
assert limit_seq(5**n/factorial(n), n) == 0
|
||||
|
||||
|
||||
def test_issue_19868():
|
||||
assert limit_seq(1/gamma(n + S.One/2), n) == 0
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_limit_seq_fail():
|
||||
# improve Summation algorithm or add ad-hoc criteria
|
||||
e = (harmonic(n)**3 * Sum(1/harmonic(k), (k, 1, n)) /
|
||||
(n * Sum(harmonic(k)/k, (k, 1, n))))
|
||||
assert limit_seq(e, n) == 2
|
||||
|
||||
# No unique dominant term
|
||||
e = (Sum(2**k * binomial(2*k, k) / k**2, (k, 1, n)) /
|
||||
(Sum(2**k/k*2, (k, 1, n)) * Sum(binomial(2*k, k), (k, 1, n))))
|
||||
assert limit_seq(e, n) == S(3) / 7
|
||||
|
||||
# Simplifications of summations needs to be improved.
|
||||
e = n**3*Sum(2**k/k**2, (k, 1, n))**2 / (2**n * Sum(2**k/k, (k, 1, n)))
|
||||
assert limit_seq(e, n) == 2
|
||||
|
||||
e = (harmonic(n) * Sum(2**k/k, (k, 1, n)) /
|
||||
(n * Sum(2**k*harmonic(k)/k**2, (k, 1, n))))
|
||||
assert limit_seq(e, n) == 1
|
||||
|
||||
e = (Sum(2**k*factorial(k) / k**2, (k, 1, 2*n)) /
|
||||
(Sum(4**k/k**2, (k, 1, n)) * Sum(factorial(k), (k, 1, 2*n))))
|
||||
assert limit_seq(e, n) == S(3) / 16
|
||||
@@ -0,0 +1,65 @@
|
||||
from sympy.core.numbers import E
|
||||
from sympy.core.singleton import S
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.hyperbolic import tanh
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.series.order import Order
|
||||
from sympy.abc import x, y
|
||||
|
||||
|
||||
def test_sin():
|
||||
e = sin(x).lseries(x)
|
||||
assert next(e) == x
|
||||
assert next(e) == -x**3/6
|
||||
assert next(e) == x**5/120
|
||||
|
||||
|
||||
def test_cos():
|
||||
e = cos(x).lseries(x)
|
||||
assert next(e) == 1
|
||||
assert next(e) == -x**2/2
|
||||
assert next(e) == x**4/24
|
||||
|
||||
|
||||
def test_exp():
|
||||
e = exp(x).lseries(x)
|
||||
assert next(e) == 1
|
||||
assert next(e) == x
|
||||
assert next(e) == x**2/2
|
||||
assert next(e) == x**3/6
|
||||
|
||||
|
||||
def test_exp2():
|
||||
e = exp(cos(x)).lseries(x)
|
||||
assert next(e) == E
|
||||
assert next(e) == -E*x**2/2
|
||||
assert next(e) == E*x**4/6
|
||||
assert next(e) == -31*E*x**6/720
|
||||
|
||||
|
||||
def test_simple():
|
||||
assert list(x.lseries()) == [x]
|
||||
assert list(S.One.lseries(x)) == [1]
|
||||
assert not next((x/(x + y)).lseries(y)).has(Order)
|
||||
|
||||
|
||||
def test_issue_5183():
|
||||
s = (x + 1/x).lseries()
|
||||
assert list(s) == [1/x, x]
|
||||
assert next((x + x**2).lseries()) == x
|
||||
assert next(((1 + x)**7).lseries(x)) == 1
|
||||
assert next((sin(x + y)).series(x, n=3).lseries(y)) == x
|
||||
# it would be nice if all terms were grouped, but in the
|
||||
# following case that would mean that all the terms would have
|
||||
# to be known since, for example, every term has a constant in it.
|
||||
s = ((1 + x)**7).series(x, 1, n=None)
|
||||
assert [next(s) for i in range(2)] == [128, -448 + 448*x]
|
||||
|
||||
|
||||
def test_issue_6999():
|
||||
s = tanh(x).lseries(x, 1)
|
||||
assert next(s) == tanh(1)
|
||||
assert next(s) == x - (x - 1)*tanh(1)**2 - 1
|
||||
assert next(s) == -(x - 1)**2*tanh(1) + (x - 1)**2*tanh(1)**3
|
||||
assert next(s) == -(x - 1)**3*tanh(1)**4 - (x - 1)**3/3 + \
|
||||
4*(x - 1)**3*tanh(1)**2/3
|
||||
@@ -0,0 +1,557 @@
|
||||
from sympy.calculus.util import AccumBounds
|
||||
from sympy.core.function import (Derivative, PoleError)
|
||||
from sympy.core.numbers import (E, I, Integer, Rational, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.elementary.complexes import sign
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.hyperbolic import (acosh, acoth, asinh, atanh, cosh, coth, sinh, tanh)
|
||||
from sympy.functions.elementary.integers import (ceiling, floor, frac)
|
||||
from sympy.functions.elementary.miscellaneous import (cbrt, sqrt)
|
||||
from sympy.functions.elementary.trigonometric import (asin, cos, cot, sin, tan)
|
||||
from sympy.series.limits import limit
|
||||
from sympy.series.order import O
|
||||
from sympy.abc import x, y, z
|
||||
|
||||
from sympy.testing.pytest import raises, XFAIL
|
||||
|
||||
|
||||
def test_simple_1():
|
||||
assert x.nseries(x, n=5) == x
|
||||
assert y.nseries(x, n=5) == y
|
||||
assert (1/(x*y)).nseries(y, n=5) == 1/(x*y)
|
||||
assert Rational(3, 4).nseries(x, n=5) == Rational(3, 4)
|
||||
assert x.nseries() == x
|
||||
|
||||
|
||||
def test_mul_0():
|
||||
assert (x*log(x)).nseries(x, n=5) == x*log(x)
|
||||
|
||||
|
||||
def test_mul_1():
|
||||
assert (x*log(2 + x)).nseries(x, n=5) == x*log(2) + x**2/2 - x**3/8 + \
|
||||
x**4/24 + O(x**5)
|
||||
assert (x*log(1 + x)).nseries(
|
||||
x, n=5) == x**2 - x**3/2 + x**4/3 + O(x**5)
|
||||
|
||||
|
||||
def test_pow_0():
|
||||
assert (x**2).nseries(x, n=5) == x**2
|
||||
assert (1/x).nseries(x, n=5) == 1/x
|
||||
assert (1/x**2).nseries(x, n=5) == 1/x**2
|
||||
assert (x**Rational(2, 3)).nseries(x, n=5) == (x**Rational(2, 3))
|
||||
assert (sqrt(x)**3).nseries(x, n=5) == (sqrt(x)**3)
|
||||
|
||||
|
||||
def test_pow_1():
|
||||
assert ((1 + x)**2).nseries(x, n=5) == x**2 + 2*x + 1
|
||||
|
||||
# https://github.com/sympy/sympy/issues/21075
|
||||
assert ((sqrt(x) + 1)**2).nseries(x) == 2*sqrt(x) + x + 1
|
||||
assert ((sqrt(x) + cbrt(x))**2).nseries(x) == 2*x**Rational(5, 6)\
|
||||
+ x**Rational(2, 3) + x
|
||||
|
||||
|
||||
def test_geometric_1():
|
||||
assert (1/(1 - x)).nseries(x, n=5) == 1 + x + x**2 + x**3 + x**4 + O(x**5)
|
||||
assert (x/(1 - x)).nseries(x, n=6) == x + x**2 + x**3 + x**4 + x**5 + O(x**6)
|
||||
assert (x**3/(1 - x)).nseries(x, n=8) == x**3 + x**4 + x**5 + x**6 + \
|
||||
x**7 + O(x**8)
|
||||
|
||||
|
||||
def test_sqrt_1():
|
||||
assert sqrt(1 + x).nseries(x, n=5) == 1 + x/2 - x**2/8 + x**3/16 - 5*x**4/128 + O(x**5)
|
||||
|
||||
|
||||
def test_exp_1():
|
||||
assert exp(x).nseries(x, n=5) == 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
|
||||
assert exp(x).nseries(x, n=12) == 1 + x + x**2/2 + x**3/6 + x**4/24 + x**5/120 + \
|
||||
x**6/720 + x**7/5040 + x**8/40320 + x**9/362880 + x**10/3628800 + \
|
||||
x**11/39916800 + O(x**12)
|
||||
assert exp(1/x).nseries(x, n=5) == exp(1/x)
|
||||
assert exp(1/(1 + x)).nseries(x, n=4) == \
|
||||
(E*(1 - x - 13*x**3/6 + 3*x**2/2)).expand() + O(x**4)
|
||||
assert exp(2 + x).nseries(x, n=5) == \
|
||||
(exp(2)*(1 + x + x**2/2 + x**3/6 + x**4/24)).expand() + O(x**5)
|
||||
|
||||
|
||||
def test_exp_sqrt_1():
|
||||
assert exp(1 + sqrt(x)).nseries(x, n=3) == \
|
||||
(exp(1)*(1 + sqrt(x) + x/2 + sqrt(x)*x/6)).expand() + O(sqrt(x)**3)
|
||||
|
||||
|
||||
def test_power_x_x1():
|
||||
assert (exp(x*log(x))).nseries(x, n=4) == \
|
||||
1 + x*log(x) + x**2*log(x)**2/2 + x**3*log(x)**3/6 + O(x**4*log(x)**4)
|
||||
|
||||
|
||||
def test_power_x_x2():
|
||||
assert (x**x).nseries(x, n=4) == \
|
||||
1 + x*log(x) + x**2*log(x)**2/2 + x**3*log(x)**3/6 + O(x**4*log(x)**4)
|
||||
|
||||
|
||||
def test_log_singular1():
|
||||
assert log(1 + 1/x).nseries(x, n=5) == x - log(x) - x**2/2 + x**3/3 - \
|
||||
x**4/4 + O(x**5)
|
||||
|
||||
|
||||
def test_log_power1():
|
||||
e = 1 / (1/x + x ** (log(3)/log(2)))
|
||||
assert e.nseries(x, n=5) == -x**(log(3)/log(2) + 2) + x + O(x**5)
|
||||
|
||||
|
||||
def test_log_series():
|
||||
l = Symbol('l')
|
||||
e = 1/(1 - log(x))
|
||||
assert e.nseries(x, n=5, logx=l) == 1/(1 - l)
|
||||
|
||||
|
||||
def test_log2():
|
||||
e = log(-1/x)
|
||||
assert e.nseries(x, n=5) == -log(x) + log(-1)
|
||||
|
||||
|
||||
def test_log3():
|
||||
l = Symbol('l')
|
||||
e = 1/log(-1/x)
|
||||
assert e.nseries(x, n=4, logx=l) == 1/(-l + log(-1))
|
||||
|
||||
|
||||
def test_series1():
|
||||
e = sin(x)
|
||||
assert e.nseries(x, 0, 0) != 0
|
||||
assert e.nseries(x, 0, 0) == O(1, x)
|
||||
assert e.nseries(x, 0, 1) == O(x, x)
|
||||
assert e.nseries(x, 0, 2) == x + O(x**2, x)
|
||||
assert e.nseries(x, 0, 3) == x + O(x**3, x)
|
||||
assert e.nseries(x, 0, 4) == x - x**3/6 + O(x**4, x)
|
||||
|
||||
e = (exp(x) - 1)/x
|
||||
assert e.nseries(x, 0, 3) == 1 + x/2 + x**2/6 + O(x**3)
|
||||
|
||||
assert x.nseries(x, 0, 2) == x
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_series1_failing():
|
||||
assert x.nseries(x, 0, 0) == O(1, x)
|
||||
assert x.nseries(x, 0, 1) == O(x, x)
|
||||
|
||||
|
||||
def test_seriesbug1():
|
||||
assert (1/x).nseries(x, 0, 3) == 1/x
|
||||
assert (x + 1/x).nseries(x, 0, 3) == x + 1/x
|
||||
|
||||
|
||||
def test_series2x():
|
||||
assert ((x + 1)**(-2)).nseries(x, 0, 4) == 1 - 2*x + 3*x**2 - 4*x**3 + O(x**4, x)
|
||||
assert ((x + 1)**(-1)).nseries(x, 0, 4) == 1 - x + x**2 - x**3 + O(x**4, x)
|
||||
assert ((x + 1)**0).nseries(x, 0, 3) == 1
|
||||
assert ((x + 1)**1).nseries(x, 0, 3) == 1 + x
|
||||
assert ((x + 1)**2).nseries(x, 0, 3) == x**2 + 2*x + 1
|
||||
assert ((x + 1)**3).nseries(x, 0, 3) == 1 + 3*x + 3*x**2 + O(x**3)
|
||||
|
||||
assert (1/(1 + x)).nseries(x, 0, 4) == 1 - x + x**2 - x**3 + O(x**4, x)
|
||||
assert (x + 3/(1 + 2*x)).nseries(x, 0, 4) == 3 - 5*x + 12*x**2 - 24*x**3 + O(x**4, x)
|
||||
|
||||
assert ((1/x + 1)**3).nseries(x, 0, 3) == 1 + 3/x + 3/x**2 + x**(-3)
|
||||
assert (1/(1 + 1/x)).nseries(x, 0, 4) == x - x**2 + x**3 - O(x**4, x)
|
||||
assert (1/(1 + 1/x**2)).nseries(x, 0, 6) == x**2 - x**4 + O(x**6, x)
|
||||
|
||||
|
||||
def test_bug2(): # 1/log(0)*log(0) problem
|
||||
w = Symbol("w")
|
||||
e = (w**(-1) + w**(
|
||||
-log(3)*log(2)**(-1)))**(-1)*(3*w**(-log(3)*log(2)**(-1)) + 2*w**(-1))
|
||||
e = e.expand()
|
||||
assert e.nseries(w, 0, 4).subs(w, 0) == 3
|
||||
|
||||
|
||||
def test_exp():
|
||||
e = (1 + x)**(1/x)
|
||||
assert e.nseries(x, n=3) == exp(1) - x*exp(1)/2 + 11*exp(1)*x**2/24 + O(x**3)
|
||||
|
||||
|
||||
def test_exp2():
|
||||
w = Symbol("w")
|
||||
e = w**(1 - log(x)/(log(2) + log(x)))
|
||||
logw = Symbol("logw")
|
||||
assert e.nseries(
|
||||
w, 0, 1, logx=logw) == exp(logw*log(2)/(log(x) + log(2)))
|
||||
|
||||
|
||||
def test_bug3():
|
||||
e = (2/x + 3/x**2)/(1/x + 1/x**2)
|
||||
assert e.nseries(x, n=3) == 3 - x + x**2 + O(x**3)
|
||||
|
||||
|
||||
def test_generalexponent():
|
||||
p = 2
|
||||
e = (2/x + 3/x**p)/(1/x + 1/x**p)
|
||||
assert e.nseries(x, 0, 3) == 3 - x + x**2 + O(x**3)
|
||||
p = S.Half
|
||||
e = (2/x + 3/x**p)/(1/x + 1/x**p)
|
||||
assert e.nseries(x, 0, 2) == 2 - x + sqrt(x) + x**(S(3)/2) + O(x**2)
|
||||
|
||||
e = 1 + sqrt(x)
|
||||
assert e.nseries(x, 0, 4) == 1 + sqrt(x)
|
||||
|
||||
# more complicated example
|
||||
|
||||
|
||||
def test_genexp_x():
|
||||
e = 1/(1 + sqrt(x))
|
||||
assert e.nseries(x, 0, 2) == \
|
||||
1 + x - sqrt(x) - sqrt(x)**3 + O(x**2, x)
|
||||
|
||||
# more complicated example
|
||||
|
||||
|
||||
def test_genexp_x2():
|
||||
p = Rational(3, 2)
|
||||
e = (2/x + 3/x**p)/(1/x + 1/x**p)
|
||||
assert e.nseries(x, 0, 3) == 3 + x + x**2 - sqrt(x) - x**(S(3)/2) - x**(S(5)/2) + O(x**3)
|
||||
|
||||
|
||||
def test_seriesbug2():
|
||||
w = Symbol("w")
|
||||
#simple case (1):
|
||||
e = ((2*w)/w)**(1 + w)
|
||||
assert e.nseries(w, 0, 1) == 2 + O(w, w)
|
||||
assert e.nseries(w, 0, 1).subs(w, 0) == 2
|
||||
|
||||
|
||||
def test_seriesbug2b():
|
||||
w = Symbol("w")
|
||||
#test sin
|
||||
e = sin(2*w)/w
|
||||
assert e.nseries(w, 0, 3) == 2 - 4*w**2/3 + O(w**3)
|
||||
|
||||
|
||||
def test_seriesbug2d():
|
||||
w = Symbol("w", real=True)
|
||||
e = log(sin(2*w)/w)
|
||||
assert e.series(w, n=5) == log(2) - 2*w**2/3 - 4*w**4/45 + O(w**5)
|
||||
|
||||
|
||||
def test_seriesbug2c():
|
||||
w = Symbol("w", real=True)
|
||||
#more complicated case, but sin(x)~x, so the result is the same as in (1)
|
||||
e = (sin(2*w)/w)**(1 + w)
|
||||
assert e.series(w, 0, 1) == 2 + O(w)
|
||||
assert e.series(w, 0, 3) == 2 + 2*w*log(2) + \
|
||||
w**2*(Rational(-4, 3) + log(2)**2) + O(w**3)
|
||||
assert e.series(w, 0, 2).subs(w, 0) == 2
|
||||
|
||||
|
||||
def test_expbug4():
|
||||
x = Symbol("x", real=True)
|
||||
assert (log(
|
||||
sin(2*x)/x)*(1 + x)).series(x, 0, 2) == log(2) + x*log(2) + O(x**2, x)
|
||||
assert exp(
|
||||
log(sin(2*x)/x)*(1 + x)).series(x, 0, 2) == 2 + 2*x*log(2) + O(x**2)
|
||||
|
||||
assert exp(log(2) + O(x)).nseries(x, 0, 2) == 2 + O(x)
|
||||
assert ((2 + O(x))**(1 + x)).nseries(x, 0, 2) == 2 + O(x)
|
||||
|
||||
|
||||
def test_logbug4():
|
||||
assert log(2 + O(x)).nseries(x, 0, 2) == log(2) + O(x, x)
|
||||
|
||||
|
||||
def test_expbug5():
|
||||
assert exp(log(1 + x)/x).nseries(x, n=3) == exp(1) + -exp(1)*x/2 + 11*exp(1)*x**2/24 + O(x**3)
|
||||
|
||||
assert exp(O(x)).nseries(x, 0, 2) == 1 + O(x)
|
||||
|
||||
|
||||
def test_sinsinbug():
|
||||
assert sin(sin(x)).nseries(x, 0, 8) == x - x**3/3 + x**5/10 - 8*x**7/315 + O(x**8)
|
||||
|
||||
|
||||
def test_issue_3258():
|
||||
a = x/(exp(x) - 1)
|
||||
assert a.nseries(x, 0, 5) == 1 - x/2 - x**4/720 + x**2/12 + O(x**5)
|
||||
|
||||
|
||||
def test_issue_3204():
|
||||
x = Symbol("x", nonnegative=True)
|
||||
f = sin(x**3)**Rational(1, 3)
|
||||
assert f.nseries(x, 0, 17) == x - x**7/18 - x**13/3240 + O(x**17)
|
||||
|
||||
|
||||
def test_issue_3224():
|
||||
f = sqrt(1 - sqrt(y))
|
||||
assert f.nseries(y, 0, 2) == 1 - sqrt(y)/2 - y/8 - sqrt(y)**3/16 + O(y**2)
|
||||
|
||||
|
||||
def test_issue_3463():
|
||||
w, i = symbols('w,i')
|
||||
r = log(5)/log(3)
|
||||
p = w**(-1 + r)
|
||||
e = 1/x*(-log(w**(1 + r)) + log(w + w**r))
|
||||
e_ser = -r*log(w)/x + p/x - p**2/(2*x) + O(w)
|
||||
assert e.nseries(w, n=1) == e_ser
|
||||
|
||||
|
||||
def test_sin():
|
||||
assert sin(8*x).nseries(x, n=4) == 8*x - 256*x**3/3 + O(x**4)
|
||||
assert sin(x + y).nseries(x, n=1) == sin(y) + O(x)
|
||||
assert sin(x + y).nseries(x, n=2) == sin(y) + cos(y)*x + O(x**2)
|
||||
assert sin(x + y).nseries(x, n=5) == sin(y) + cos(y)*x - sin(y)*x**2/2 - \
|
||||
cos(y)*x**3/6 + sin(y)*x**4/24 + O(x**5)
|
||||
|
||||
|
||||
def test_issue_3515():
|
||||
e = sin(8*x)/x
|
||||
assert e.nseries(x, n=6) == 8 - 256*x**2/3 + 4096*x**4/15 + O(x**6)
|
||||
|
||||
|
||||
def test_issue_3505():
|
||||
e = sin(x)**(-4)*(sqrt(cos(x))*sin(x)**2 -
|
||||
cos(x)**Rational(1, 3)*sin(x)**2)
|
||||
assert e.nseries(x, n=9) == Rational(-1, 12) - 7*x**2/288 - \
|
||||
43*x**4/10368 - 1123*x**6/2488320 + 377*x**8/29859840 + O(x**9)
|
||||
|
||||
|
||||
def test_issue_3501():
|
||||
a = Symbol("a")
|
||||
e = x**(-2)*(x*sin(a + x) - x*sin(a))
|
||||
assert e.nseries(x, n=6) == cos(a) - sin(a)*x/2 - cos(a)*x**2/6 + \
|
||||
x**3*sin(a)/24 + x**4*cos(a)/120 - x**5*sin(a)/720 + O(x**6)
|
||||
e = x**(-2)*(x*cos(a + x) - x*cos(a))
|
||||
assert e.nseries(x, n=6) == -sin(a) - cos(a)*x/2 + sin(a)*x**2/6 + \
|
||||
cos(a)*x**3/24 - x**4*sin(a)/120 - x**5*cos(a)/720 + O(x**6)
|
||||
|
||||
|
||||
def test_issue_3502():
|
||||
e = sin(5*x)/sin(2*x)
|
||||
assert e.nseries(x, n=2) == Rational(5, 2) + O(x**2)
|
||||
assert e.nseries(x, n=6) == \
|
||||
Rational(5, 2) - 35*x**2/4 + 329*x**4/48 + O(x**6)
|
||||
|
||||
|
||||
def test_issue_3503():
|
||||
e = sin(2 + x)/(2 + x)
|
||||
assert e.nseries(x, n=2) == sin(2)/2 + x*cos(2)/2 - x*sin(2)/4 + O(x**2)
|
||||
|
||||
|
||||
def test_issue_3506():
|
||||
e = (x + sin(3*x))**(-2)*(x*(x + sin(3*x)) - (x + sin(3*x))*sin(2*x))
|
||||
assert e.nseries(x, n=7) == \
|
||||
Rational(-1, 4) + 5*x**2/96 + 91*x**4/768 + 11117*x**6/129024 + O(x**7)
|
||||
|
||||
|
||||
def test_issue_3508():
|
||||
x = Symbol("x", real=True)
|
||||
assert log(sin(x)).series(x, n=5) == log(x) - x**2/6 - x**4/180 + O(x**5)
|
||||
e = -log(x) + x*(-log(x) + log(sin(2*x))) + log(sin(2*x))
|
||||
assert e.series(x, n=5) == \
|
||||
log(2) + log(2)*x - 2*x**2/3 - 2*x**3/3 - 4*x**4/45 + O(x**5)
|
||||
|
||||
|
||||
def test_issue_3507():
|
||||
e = x**(-4)*(x**2 - x**2*sqrt(cos(x)))
|
||||
assert e.nseries(x, n=9) == \
|
||||
Rational(1, 4) + x**2/96 + 19*x**4/5760 + 559*x**6/645120 + 29161*x**8/116121600 + O(x**9)
|
||||
|
||||
|
||||
def test_issue_3639():
|
||||
assert sin(cos(x)).nseries(x, n=5) == \
|
||||
sin(1) - x**2*cos(1)/2 - x**4*sin(1)/8 + x**4*cos(1)/24 + O(x**5)
|
||||
|
||||
|
||||
def test_hyperbolic():
|
||||
assert sinh(x).nseries(x, n=6) == x + x**3/6 + x**5/120 + O(x**6)
|
||||
assert cosh(x).nseries(x, n=5) == 1 + x**2/2 + x**4/24 + O(x**5)
|
||||
assert tanh(x).nseries(x, n=6) == x - x**3/3 + 2*x**5/15 + O(x**6)
|
||||
assert coth(x).nseries(x, n=6) == \
|
||||
1/x - x**3/45 + x/3 + 2*x**5/945 + O(x**6)
|
||||
assert asinh(x).nseries(x, n=6) == x - x**3/6 + 3*x**5/40 + O(x**6)
|
||||
assert acosh(x).nseries(x, n=6) == \
|
||||
pi*I/2 - I*x - 3*I*x**5/40 - I*x**3/6 + O(x**6)
|
||||
assert atanh(x).nseries(x, n=6) == x + x**3/3 + x**5/5 + O(x**6)
|
||||
assert acoth(x).nseries(x, n=6) == -I*pi/2 + x + x**3/3 + x**5/5 + O(x**6)
|
||||
|
||||
|
||||
def test_series2():
|
||||
w = Symbol("w", real=True)
|
||||
x = Symbol("x", real=True)
|
||||
e = w**(-2)*(w*exp(1/x - w) - w*exp(1/x))
|
||||
assert e.nseries(w, n=4) == -exp(1/x) + w*exp(1/x)/2 - w**2*exp(1/x)/6 + w**3*exp(1/x)/24 + O(w**4)
|
||||
|
||||
|
||||
def test_series3():
|
||||
w = Symbol("w", real=True)
|
||||
e = w**(-6)*(w**3*tan(w) - w**3*sin(w))
|
||||
assert e.nseries(w, n=8) == Integer(1)/2 + w**2/8 + 13*w**4/240 + 529*w**6/24192 + O(w**8)
|
||||
|
||||
|
||||
def test_bug4():
|
||||
w = Symbol("w")
|
||||
e = x/(w**4 + x**2*w**4 + 2*x*w**4)*w**4
|
||||
assert e.nseries(w, n=2).removeO().expand() in [x/(1 + 2*x + x**2),
|
||||
1/(1 + x/2 + 1/x/2)/2, 1/x/(1 + 2/x + x**(-2))]
|
||||
|
||||
|
||||
def test_bug5():
|
||||
w = Symbol("w")
|
||||
l = Symbol('l')
|
||||
e = (-log(w) + log(1 + w*log(x)))**(-2)*w**(-2)*((-log(w) +
|
||||
log(1 + x*w))*(-log(w) + log(1 + w*log(x)))*w - x*(-log(w) +
|
||||
log(1 + w*log(x)))*w)
|
||||
assert e.nseries(w, n=0, logx=l) == x/w/l + 1/w + O(1, w)
|
||||
assert e.nseries(w, n=1, logx=l) == x/w/l + 1/w - x/l + 1/l*log(x) \
|
||||
+ x*log(x)/l**2 + O(w)
|
||||
|
||||
|
||||
def test_issue_4115():
|
||||
assert (sin(x)/(1 - cos(x))).nseries(x, n=1) == 2/x + O(x)
|
||||
assert (sin(x)**2/(1 - cos(x))).nseries(x, n=1) == 2 + O(x)
|
||||
|
||||
|
||||
def test_pole():
|
||||
raises(PoleError, lambda: sin(1/x).series(x, 0, 5))
|
||||
raises(PoleError, lambda: sin(1 + 1/x).series(x, 0, 5))
|
||||
raises(PoleError, lambda: (x*sin(1/x)).series(x, 0, 5))
|
||||
|
||||
|
||||
def test_expsinbug():
|
||||
assert exp(sin(x)).series(x, 0, 0) == O(1, x)
|
||||
assert exp(sin(x)).series(x, 0, 1) == 1 + O(x)
|
||||
assert exp(sin(x)).series(x, 0, 2) == 1 + x + O(x**2)
|
||||
assert exp(sin(x)).series(x, 0, 3) == 1 + x + x**2/2 + O(x**3)
|
||||
assert exp(sin(x)).series(x, 0, 4) == 1 + x + x**2/2 + O(x**4)
|
||||
assert exp(sin(x)).series(x, 0, 5) == 1 + x + x**2/2 - x**4/8 + O(x**5)
|
||||
|
||||
|
||||
def test_floor():
|
||||
x = Symbol('x')
|
||||
assert floor(x).series(x) == 0
|
||||
assert floor(-x).series(x) == -1
|
||||
assert floor(sin(x)).series(x) == 0
|
||||
assert floor(sin(-x)).series(x) == -1
|
||||
assert floor(x**3).series(x) == 0
|
||||
assert floor(-x**3).series(x) == -1
|
||||
assert floor(cos(x)).series(x) == 0
|
||||
assert floor(cos(-x)).series(x) == 0
|
||||
assert floor(5 + sin(x)).series(x) == 5
|
||||
assert floor(5 + sin(-x)).series(x) == 4
|
||||
|
||||
assert floor(x).series(x, 2) == 2
|
||||
assert floor(-x).series(x, 2) == -3
|
||||
|
||||
x = Symbol('x', negative=True)
|
||||
assert floor(x + 1.5).series(x) == 1
|
||||
|
||||
|
||||
def test_frac():
|
||||
assert frac(x).series(x, cdir=1) == x
|
||||
assert frac(x).series(x, cdir=-1) == 1 + x
|
||||
assert frac(2*x + 1).series(x, cdir=1) == 2*x
|
||||
assert frac(2*x + 1).series(x, cdir=-1) == 1 + 2*x
|
||||
assert frac(x**2).series(x, cdir=1) == x**2
|
||||
assert frac(x**2).series(x, cdir=-1) == x**2
|
||||
assert frac(sin(x) + 5).series(x, cdir=1) == x - x**3/6 + x**5/120 + O(x**6)
|
||||
assert frac(sin(x) + 5).series(x, cdir=-1) == 1 + x - x**3/6 + x**5/120 + O(x**6)
|
||||
assert frac(sin(x) + S.Half).series(x) == S.Half + x - x**3/6 + x**5/120 + O(x**6)
|
||||
assert frac(x**8).series(x, cdir=1) == O(x**6)
|
||||
assert frac(1/x).series(x) == AccumBounds(0, 1) + O(x**6)
|
||||
|
||||
|
||||
def test_ceiling():
|
||||
assert ceiling(x).series(x) == 1
|
||||
assert ceiling(-x).series(x) == 0
|
||||
assert ceiling(sin(x)).series(x) == 1
|
||||
assert ceiling(sin(-x)).series(x) == 0
|
||||
assert ceiling(1 - cos(x)).series(x) == 1
|
||||
assert ceiling(1 - cos(-x)).series(x) == 1
|
||||
assert ceiling(x).series(x, 2) == 3
|
||||
assert ceiling(-x).series(x, 2) == -2
|
||||
|
||||
|
||||
def test_abs():
|
||||
a = Symbol('a')
|
||||
assert abs(x).nseries(x, n=4) == x
|
||||
assert abs(-x).nseries(x, n=4) == x
|
||||
assert abs(x + 1).nseries(x, n=4) == x + 1
|
||||
assert abs(sin(x)).nseries(x, n=4) == x - Rational(1, 6)*x**3 + O(x**4)
|
||||
assert abs(sin(-x)).nseries(x, n=4) == x - Rational(1, 6)*x**3 + O(x**4)
|
||||
assert abs(x - a).nseries(x, 1) == -a*sign(1 - a) + (x - 1)*sign(1 - a) + sign(1 - a)
|
||||
|
||||
|
||||
def test_dir():
|
||||
assert abs(x).series(x, 0, dir="+") == x
|
||||
assert abs(x).series(x, 0, dir="-") == -x
|
||||
assert floor(x + 2).series(x, 0, dir='+') == 2
|
||||
assert floor(x + 2).series(x, 0, dir='-') == 1
|
||||
assert floor(x + 2.2).series(x, 0, dir='-') == 2
|
||||
assert ceiling(x + 2.2).series(x, 0, dir='-') == 3
|
||||
assert sin(x + y).series(x, 0, dir='-') == sin(x + y).series(x, 0, dir='+')
|
||||
|
||||
|
||||
def test_cdir():
|
||||
assert abs(x).series(x, 0, cdir=1) == x
|
||||
assert abs(x).series(x, 0, cdir=-1) == -x
|
||||
assert floor(x + 2).series(x, 0, cdir=1) == 2
|
||||
assert floor(x + 2).series(x, 0, cdir=-1) == 1
|
||||
assert floor(x + 2.2).series(x, 0, cdir=1) == 2
|
||||
assert ceiling(x + 2.2).series(x, 0, cdir=-1) == 3
|
||||
assert sin(x + y).series(x, 0, cdir=-1) == sin(x + y).series(x, 0, cdir=1)
|
||||
|
||||
|
||||
def test_issue_3504():
|
||||
a = Symbol("a")
|
||||
e = asin(a*x)/x
|
||||
assert e.series(x, 4, n=2).removeO() == \
|
||||
(x - 4)*(a/(4*sqrt(-16*a**2 + 1)) - asin(4*a)/16) + asin(4*a)/4
|
||||
|
||||
|
||||
def test_issue_4441():
|
||||
a, b = symbols('a,b')
|
||||
f = 1/(1 + a*x)
|
||||
assert f.series(x, 0, 5) == 1 - a*x + a**2*x**2 - a**3*x**3 + \
|
||||
a**4*x**4 + O(x**5)
|
||||
f = 1/(1 + (a + b)*x)
|
||||
assert f.series(x, 0, 3) == 1 + x*(-a - b)\
|
||||
+ x**2*(a + b)**2 + O(x**3)
|
||||
|
||||
|
||||
def test_issue_4329():
|
||||
assert tan(x).series(x, pi/2, n=3).removeO() == \
|
||||
-pi/6 + x/3 - 1/(x - pi/2)
|
||||
assert cot(x).series(x, pi, n=3).removeO() == \
|
||||
-x/3 + pi/3 + 1/(x - pi)
|
||||
assert limit(tan(x)**tan(2*x), x, pi/4) == exp(-1)
|
||||
|
||||
|
||||
def test_issue_5183():
|
||||
assert abs(x + x**2).series(n=1) == O(x)
|
||||
assert abs(x + x**2).series(n=2) == x + O(x**2)
|
||||
assert ((1 + x)**2).series(x, n=6) == x**2 + 2*x + 1
|
||||
assert (1 + 1/x).series() == 1 + 1/x
|
||||
assert Derivative(exp(x).series(), x).doit() == \
|
||||
1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5)
|
||||
|
||||
|
||||
def test_issue_5654():
|
||||
a = Symbol('a')
|
||||
assert (1/(x**2+a**2)**2).nseries(x, x0=I*a, n=0) == \
|
||||
-I/(4*a**3*(-I*a + x)) - 1/(4*a**2*(-I*a + x)**2) + O(1, (x, I*a))
|
||||
assert (1/(x**2+a**2)**2).nseries(x, x0=I*a, n=1) == 3/(16*a**4) \
|
||||
-I/(4*a**3*(-I*a + x)) - 1/(4*a**2*(-I*a + x)**2) + O(-I*a + x, (x, I*a))
|
||||
|
||||
|
||||
def test_issue_5925():
|
||||
sx = sqrt(x + z).series(z, 0, 1)
|
||||
sxy = sqrt(x + y + z).series(z, 0, 1)
|
||||
s1, s2 = sx.subs(x, x + y), sxy
|
||||
assert (s1 - s2).expand().removeO().simplify() == 0
|
||||
|
||||
sx = sqrt(x + z).series(z, 0, 1)
|
||||
sxy = sqrt(x + y + z).series(z, 0, 1)
|
||||
assert sxy.subs({x:1, y:2}) == sx.subs(x, 3)
|
||||
|
||||
|
||||
def test_exp_2():
|
||||
assert exp(x**3).nseries(x, 0, 14) == 1 + x**3 + x**6/2 + x**9/6 + x**12/24 + O(x**14)
|
||||
@@ -0,0 +1,503 @@
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.function import (Function, expand)
|
||||
from sympy.core.numbers import (I, Rational, nan, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.complexes import (conjugate, transpose)
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.series.order import O, Order
|
||||
from sympy.core.expr import unchanged
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.abc import w, x, y, z
|
||||
from sympy.testing.pytest import XFAIL
|
||||
|
||||
|
||||
def test_caching_bug():
|
||||
#needs to be a first test, so that all caches are clean
|
||||
#cache it
|
||||
O(w)
|
||||
#and test that this won't raise an exception
|
||||
O(w**(-1/x/log(3)*log(5)), w)
|
||||
|
||||
|
||||
def test_free_symbols():
|
||||
assert Order(1).free_symbols == set()
|
||||
assert Order(x).free_symbols == {x}
|
||||
assert Order(1, x).free_symbols == {x}
|
||||
assert Order(x*y).free_symbols == {x, y}
|
||||
assert Order(x, x, y).free_symbols == {x, y}
|
||||
|
||||
|
||||
def test_simple_1():
|
||||
o = Rational(0)
|
||||
assert Order(2*x) == Order(x)
|
||||
assert Order(x)*3 == Order(x)
|
||||
assert -28*Order(x) == Order(x)
|
||||
assert Order(Order(x)) == Order(x)
|
||||
assert Order(Order(x), y) == Order(Order(x), x, y)
|
||||
assert Order(-23) == Order(1)
|
||||
assert Order(exp(x)) == Order(1, x)
|
||||
assert Order(exp(1/x)).expr == exp(1/x)
|
||||
assert Order(x*exp(1/x)).expr == x*exp(1/x)
|
||||
assert Order(x**(o/3)).expr == x**(o/3)
|
||||
assert Order(x**(o*Rational(5, 3))).expr == x**(o*Rational(5, 3))
|
||||
assert Order(x**2 + x + y, x) == O(1, x)
|
||||
assert Order(x**2 + x + y, y) == O(1, y)
|
||||
raises(ValueError, lambda: Order(exp(x), x, x))
|
||||
raises(TypeError, lambda: Order(x, 2 - x))
|
||||
|
||||
|
||||
def test_simple_2():
|
||||
assert Order(2*x)*x == Order(x**2)
|
||||
assert Order(2*x)/x == Order(1, x)
|
||||
assert Order(2*x)*x*exp(1/x) == Order(x**2*exp(1/x))
|
||||
assert (Order(2*x)*x*exp(1/x)/log(x)**3).expr == x**2*exp(1/x)*log(x)**-3
|
||||
|
||||
|
||||
def test_simple_3():
|
||||
assert Order(x) + x == Order(x)
|
||||
assert Order(x) + 2 == 2 + Order(x)
|
||||
assert Order(x) + x**2 == Order(x)
|
||||
assert Order(x) + 1/x == 1/x + Order(x)
|
||||
assert Order(1/x) + 1/x**2 == 1/x**2 + Order(1/x)
|
||||
assert Order(x) + exp(1/x) == Order(x) + exp(1/x)
|
||||
|
||||
|
||||
def test_simple_4():
|
||||
assert Order(x)**2 == Order(x**2)
|
||||
|
||||
|
||||
def test_simple_5():
|
||||
assert Order(x) + Order(x**2) == Order(x)
|
||||
assert Order(x) + Order(x**-2) == Order(x**-2)
|
||||
assert Order(x) + Order(1/x) == Order(1/x)
|
||||
|
||||
|
||||
def test_simple_6():
|
||||
assert Order(x) - Order(x) == Order(x)
|
||||
assert Order(x) + Order(1) == Order(1)
|
||||
assert Order(x) + Order(x**2) == Order(x)
|
||||
assert Order(1/x) + Order(1) == Order(1/x)
|
||||
assert Order(x) + Order(exp(1/x)) == Order(exp(1/x))
|
||||
assert Order(x**3) + Order(exp(2/x)) == Order(exp(2/x))
|
||||
assert Order(x**-3) + Order(exp(2/x)) == Order(exp(2/x))
|
||||
|
||||
|
||||
def test_simple_7():
|
||||
assert 1 + O(1) == O(1)
|
||||
assert 2 + O(1) == O(1)
|
||||
assert x + O(1) == O(1)
|
||||
assert 1/x + O(1) == 1/x + O(1)
|
||||
|
||||
|
||||
def test_simple_8():
|
||||
assert O(sqrt(-x)) == O(sqrt(x))
|
||||
assert O(x**2*sqrt(x)) == O(x**Rational(5, 2))
|
||||
assert O(x**3*sqrt(-(-x)**3)) == O(x**Rational(9, 2))
|
||||
assert O(x**Rational(3, 2)*sqrt((-x)**3)) == O(x**3)
|
||||
assert O(x*(-2*x)**(I/2)) == O(x*(-x)**(I/2))
|
||||
|
||||
|
||||
def test_as_expr_variables():
|
||||
assert Order(x).as_expr_variables(None) == (x, ((x, 0),))
|
||||
assert Order(x).as_expr_variables(((x, 0),)) == (x, ((x, 0),))
|
||||
assert Order(y).as_expr_variables(((x, 0),)) == (y, ((x, 0), (y, 0)))
|
||||
assert Order(y).as_expr_variables(((x, 0), (y, 0))) == (y, ((x, 0), (y, 0)))
|
||||
|
||||
|
||||
def test_contains_0():
|
||||
assert Order(1, x).contains(Order(1, x))
|
||||
assert Order(1, x).contains(Order(1))
|
||||
assert Order(1).contains(Order(1, x)) is False
|
||||
|
||||
|
||||
def test_contains_1():
|
||||
assert Order(x).contains(Order(x))
|
||||
assert Order(x).contains(Order(x**2))
|
||||
assert not Order(x**2).contains(Order(x))
|
||||
assert not Order(x).contains(Order(1/x))
|
||||
assert not Order(1/x).contains(Order(exp(1/x)))
|
||||
assert not Order(x).contains(Order(exp(1/x)))
|
||||
assert Order(1/x).contains(Order(x))
|
||||
assert Order(exp(1/x)).contains(Order(x))
|
||||
assert Order(exp(1/x)).contains(Order(1/x))
|
||||
assert Order(exp(1/x)).contains(Order(exp(1/x)))
|
||||
assert Order(exp(2/x)).contains(Order(exp(1/x)))
|
||||
assert not Order(exp(1/x)).contains(Order(exp(2/x)))
|
||||
|
||||
|
||||
def test_contains_2():
|
||||
assert Order(x).contains(Order(y)) is None
|
||||
assert Order(x).contains(Order(y*x))
|
||||
assert Order(y*x).contains(Order(x))
|
||||
assert Order(y).contains(Order(x*y))
|
||||
assert Order(x).contains(Order(y**2*x))
|
||||
|
||||
|
||||
def test_contains_3():
|
||||
assert Order(x*y**2).contains(Order(x**2*y)) is None
|
||||
assert Order(x**2*y).contains(Order(x*y**2)) is None
|
||||
|
||||
|
||||
def test_contains_4():
|
||||
assert Order(sin(1/x**2)).contains(Order(cos(1/x**2))) is True
|
||||
assert Order(cos(1/x**2)).contains(Order(sin(1/x**2))) is True
|
||||
|
||||
|
||||
def test_contains():
|
||||
assert Order(1, x) not in Order(1)
|
||||
assert Order(1) in Order(1, x)
|
||||
raises(TypeError, lambda: Order(x*y**2) in Order(x**2*y))
|
||||
|
||||
|
||||
def test_add_1():
|
||||
assert Order(x + x) == Order(x)
|
||||
assert Order(3*x - 2*x**2) == Order(x)
|
||||
assert Order(1 + x) == Order(1, x)
|
||||
assert Order(1 + 1/x) == Order(1/x)
|
||||
# TODO : A better output for Order(log(x) + 1/log(x))
|
||||
# could be Order(log(x)). Currently Order for expressions
|
||||
# where all arguments would involve a log term would fall
|
||||
# in this category and outputs for these should be improved.
|
||||
assert Order(log(x) + 1/log(x)) == Order((log(x)**2 + 1)/log(x))
|
||||
assert Order(exp(1/x) + x) == Order(exp(1/x))
|
||||
assert Order(exp(1/x) + 1/x**20) == Order(exp(1/x))
|
||||
|
||||
|
||||
def test_ln_args():
|
||||
assert O(log(x)) + O(log(2*x)) == O(log(x))
|
||||
assert O(log(x)) + O(log(x**3)) == O(log(x))
|
||||
assert O(log(x*y)) + O(log(x) + log(y)) == O(log(x) + log(y), x, y)
|
||||
|
||||
|
||||
def test_multivar_0():
|
||||
assert Order(x*y).expr == x*y
|
||||
assert Order(x*y**2).expr == x*y**2
|
||||
assert Order(x*y, x).expr == x
|
||||
assert Order(x*y**2, y).expr == y**2
|
||||
assert Order(x*y*z).expr == x*y*z
|
||||
assert Order(x/y).expr == x/y
|
||||
assert Order(x*exp(1/y)).expr == x*exp(1/y)
|
||||
assert Order(exp(x)*exp(1/y)).expr == exp(x)*exp(1/y)
|
||||
|
||||
|
||||
def test_multivar_0a():
|
||||
assert Order(exp(1/x)*exp(1/y)).expr == exp(1/x)*exp(1/y)
|
||||
|
||||
|
||||
def test_multivar_1():
|
||||
assert Order(x + y).expr == x + y
|
||||
assert Order(x + 2*y).expr == x + y
|
||||
assert (Order(x + y) + x).expr == (x + y)
|
||||
assert (Order(x + y) + x**2) == Order(x + y)
|
||||
assert (Order(x + y) + 1/x) == 1/x + Order(x + y)
|
||||
assert Order(x**2 + y*x).expr == x**2 + y*x
|
||||
|
||||
|
||||
def test_multivar_2():
|
||||
assert Order(x**2*y + y**2*x, x, y).expr == x**2*y + y**2*x
|
||||
|
||||
|
||||
def test_multivar_mul_1():
|
||||
assert Order(x + y)*x == Order(x**2 + y*x, x, y)
|
||||
|
||||
|
||||
def test_multivar_3():
|
||||
assert (Order(x) + Order(y)).args in [
|
||||
(Order(x), Order(y)),
|
||||
(Order(y), Order(x))]
|
||||
assert Order(x) + Order(y) + Order(x + y) == Order(x + y)
|
||||
assert (Order(x**2*y) + Order(y**2*x)).args in [
|
||||
(Order(x*y**2), Order(y*x**2)),
|
||||
(Order(y*x**2), Order(x*y**2))]
|
||||
assert (Order(x**2*y) + Order(y*x)) == Order(x*y)
|
||||
|
||||
|
||||
def test_issue_3468():
|
||||
y = Symbol('y', negative=True)
|
||||
z = Symbol('z', complex=True)
|
||||
|
||||
# check that Order does not modify assumptions about symbols
|
||||
Order(x)
|
||||
Order(y)
|
||||
Order(z)
|
||||
|
||||
assert x.is_positive is None
|
||||
assert y.is_positive is False
|
||||
assert z.is_positive is None
|
||||
|
||||
|
||||
def test_leading_order():
|
||||
assert (x + 1 + 1/x**5).extract_leading_order(x) == ((1/x**5, O(1/x**5)),)
|
||||
assert (1 + 1/x).extract_leading_order(x) == ((1/x, O(1/x)),)
|
||||
assert (1 + x).extract_leading_order(x) == ((1, O(1, x)),)
|
||||
assert (1 + x**2).extract_leading_order(x) == ((1, O(1, x)),)
|
||||
assert (2 + x**2).extract_leading_order(x) == ((2, O(1, x)),)
|
||||
assert (x + x**2).extract_leading_order(x) == ((x, O(x)),)
|
||||
|
||||
|
||||
def test_leading_order2():
|
||||
assert set((2 + pi + x**2).extract_leading_order(x)) == {(pi, O(1, x)),
|
||||
(S(2), O(1, x))}
|
||||
assert set((2*x + pi*x + x**2).extract_leading_order(x)) == {(2*x, O(x)),
|
||||
(x*pi, O(x))}
|
||||
|
||||
|
||||
def test_order_leadterm():
|
||||
assert O(x**2)._eval_as_leading_term(x, None, 1) == O(x**2)
|
||||
|
||||
|
||||
def test_order_symbols():
|
||||
e = x*y*sin(x)*Integral(x, (x, 1, 2))
|
||||
assert O(e) == O(x**2*y, x, y)
|
||||
assert O(e, x) == O(x**2)
|
||||
|
||||
|
||||
def test_nan():
|
||||
assert O(nan) is nan
|
||||
assert not O(x).contains(nan)
|
||||
|
||||
|
||||
def test_O1():
|
||||
assert O(1, x) * x == O(x)
|
||||
assert O(1, y) * x == O(1, y)
|
||||
|
||||
|
||||
def test_getn():
|
||||
# other lines are tested incidentally by the suite
|
||||
assert O(x).getn() == 1
|
||||
assert O(x/log(x)).getn() == 1
|
||||
assert O(x**2/log(x)**2).getn() == 2
|
||||
assert O(x*log(x)).getn() == 1
|
||||
raises(NotImplementedError, lambda: (O(x) + O(y)).getn())
|
||||
|
||||
|
||||
def test_diff():
|
||||
assert O(x**2).diff(x) == O(x)
|
||||
|
||||
|
||||
def test_getO():
|
||||
assert (x).getO() is None
|
||||
assert (x).removeO() == x
|
||||
assert (O(x)).getO() == O(x)
|
||||
assert (O(x)).removeO() == 0
|
||||
assert (z + O(x) + O(y)).getO() == O(x) + O(y)
|
||||
assert (z + O(x) + O(y)).removeO() == z
|
||||
raises(NotImplementedError, lambda: (O(x) + O(y)).getn())
|
||||
|
||||
|
||||
def test_leading_term():
|
||||
from sympy.functions.special.gamma_functions import digamma
|
||||
assert O(1/digamma(1/x)) == O(1/log(x))
|
||||
|
||||
|
||||
def test_eval():
|
||||
assert Order(x).subs(Order(x), 1) == 1
|
||||
assert Order(x).subs(x, y) == Order(y)
|
||||
assert Order(x).subs(y, x) == Order(x)
|
||||
assert Order(x).subs(x, x + y) == Order(x + y, (x, -y))
|
||||
assert (O(1)**x).is_Pow
|
||||
|
||||
|
||||
def test_issue_4279():
|
||||
a, b = symbols('a b')
|
||||
assert O(a, a, b) + O(1, a, b) == O(1, a, b)
|
||||
assert O(b, a, b) + O(1, a, b) == O(1, a, b)
|
||||
assert O(a + b, a, b) + O(1, a, b) == O(1, a, b)
|
||||
assert O(1, a, b) + O(a, a, b) == O(1, a, b)
|
||||
assert O(1, a, b) + O(b, a, b) == O(1, a, b)
|
||||
assert O(1, a, b) + O(a + b, a, b) == O(1, a, b)
|
||||
|
||||
|
||||
def test_issue_4855():
|
||||
assert 1/O(1) != O(1)
|
||||
assert 1/O(x) != O(1/x)
|
||||
assert 1/O(x, (x, oo)) != O(1/x, (x, oo))
|
||||
|
||||
f = Function('f')
|
||||
assert 1/O(f(x)) != O(1/x)
|
||||
|
||||
|
||||
def test_order_conjugate_transpose():
|
||||
x = Symbol('x', real=True)
|
||||
y = Symbol('y', imaginary=True)
|
||||
assert conjugate(Order(x)) == Order(conjugate(x))
|
||||
assert conjugate(Order(y)) == Order(conjugate(y))
|
||||
assert conjugate(Order(x**2)) == Order(conjugate(x)**2)
|
||||
assert conjugate(Order(y**2)) == Order(conjugate(y)**2)
|
||||
assert transpose(Order(x)) == Order(transpose(x))
|
||||
assert transpose(Order(y)) == Order(transpose(y))
|
||||
assert transpose(Order(x**2)) == Order(transpose(x)**2)
|
||||
assert transpose(Order(y**2)) == Order(transpose(y)**2)
|
||||
|
||||
|
||||
def test_order_noncommutative():
|
||||
A = Symbol('A', commutative=False)
|
||||
assert Order(A + A*x, x) == Order(1, x)
|
||||
assert (A + A*x)*Order(x) == Order(x)
|
||||
assert (A*x)*Order(x) == Order(x**2, x)
|
||||
assert expand((1 + Order(x))*A*A*x) == A*A*x + Order(x**2, x)
|
||||
assert expand((A*A + Order(x))*x) == A*A*x + Order(x**2, x)
|
||||
assert expand((A + Order(x))*A*x) == A*A*x + Order(x**2, x)
|
||||
|
||||
|
||||
def test_issue_6753():
|
||||
assert (1 + x**2)**10000*O(x) == O(x)
|
||||
|
||||
|
||||
def test_order_at_infinity():
|
||||
assert Order(1 + x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(3*x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo))*3 == Order(x, (x, oo))
|
||||
assert -28*Order(x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(Order(x, (x, oo)), (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(Order(x, (x, oo)), (y, oo)) == Order(x, (x, oo), (y, oo))
|
||||
assert Order(3, (x, oo)) == Order(1, (x, oo))
|
||||
assert Order(x**2 + x + y, (x, oo)) == O(x**2, (x, oo))
|
||||
assert Order(x**2 + x + y, (y, oo)) == O(y, (y, oo))
|
||||
|
||||
assert Order(2*x, (x, oo))*x == Order(x**2, (x, oo))
|
||||
assert Order(2*x, (x, oo))/x == Order(1, (x, oo))
|
||||
assert Order(2*x, (x, oo))*x*exp(1/x) == Order(x**2*exp(1/x), (x, oo))
|
||||
assert Order(2*x, (x, oo))*x*exp(1/x)/log(x)**3 == Order(x**2*exp(1/x)*log(x)**-3, (x, oo))
|
||||
|
||||
assert Order(x, (x, oo)) + 1/x == 1/x + Order(x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo)) + 1 == 1 + Order(x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo)) + x == x + Order(x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo)) + x**2 == x**2 + Order(x, (x, oo))
|
||||
assert Order(1/x, (x, oo)) + 1/x**2 == 1/x**2 + Order(1/x, (x, oo)) == Order(1/x, (x, oo))
|
||||
assert Order(x, (x, oo)) + exp(1/x) == exp(1/x) + Order(x, (x, oo))
|
||||
|
||||
assert Order(x, (x, oo))**2 == Order(x**2, (x, oo))
|
||||
|
||||
assert Order(x, (x, oo)) + Order(x**2, (x, oo)) == Order(x**2, (x, oo))
|
||||
assert Order(x, (x, oo)) + Order(x**-2, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo)) + Order(1/x, (x, oo)) == Order(x, (x, oo))
|
||||
|
||||
assert Order(x, (x, oo)) - Order(x, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo)) + Order(1, (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x, (x, oo)) + Order(x**2, (x, oo)) == Order(x**2, (x, oo))
|
||||
assert Order(1/x, (x, oo)) + Order(1, (x, oo)) == Order(1, (x, oo))
|
||||
assert Order(x, (x, oo)) + Order(exp(1/x), (x, oo)) == Order(x, (x, oo))
|
||||
assert Order(x**3, (x, oo)) + Order(exp(2/x), (x, oo)) == Order(x**3, (x, oo))
|
||||
assert Order(x**-3, (x, oo)) + Order(exp(2/x), (x, oo)) == Order(exp(2/x), (x, oo))
|
||||
|
||||
# issue 7207
|
||||
assert Order(exp(x), (x, oo)).expr == Order(2*exp(x), (x, oo)).expr == exp(x)
|
||||
assert Order(y**x, (x, oo)).expr == Order(2*y**x, (x, oo)).expr == exp(x*log(y))
|
||||
|
||||
# issue 19545
|
||||
assert Order(1/x - 3/(3*x + 2), (x, oo)).expr == x**(-2)
|
||||
|
||||
def test_mixing_order_at_zero_and_infinity():
|
||||
assert (Order(x, (x, 0)) + Order(x, (x, oo))).is_Add
|
||||
assert Order(x, (x, 0)) + Order(x, (x, oo)) == Order(x, (x, oo)) + Order(x, (x, 0))
|
||||
assert Order(Order(x, (x, oo))) == Order(x, (x, oo))
|
||||
|
||||
# not supported (yet)
|
||||
raises(NotImplementedError, lambda: Order(x, (x, 0))*Order(x, (x, oo)))
|
||||
raises(NotImplementedError, lambda: Order(x, (x, oo))*Order(x, (x, 0)))
|
||||
raises(NotImplementedError, lambda: Order(Order(x, (x, oo)), y))
|
||||
raises(NotImplementedError, lambda: Order(Order(x), (x, oo)))
|
||||
|
||||
|
||||
def test_order_at_some_point():
|
||||
assert Order(x, (x, 1)) == Order(1, (x, 1))
|
||||
assert Order(2*x - 2, (x, 1)) == Order(x - 1, (x, 1))
|
||||
assert Order(-x + 1, (x, 1)) == Order(x - 1, (x, 1))
|
||||
assert Order(x - 1, (x, 1))**2 == Order((x - 1)**2, (x, 1))
|
||||
assert Order(x - 2, (x, 2)) - O(x - 2, (x, 2)) == Order(x - 2, (x, 2))
|
||||
|
||||
|
||||
def test_order_subs_limits():
|
||||
# issue 3333
|
||||
assert (1 + Order(x)).subs(x, 1/x) == 1 + Order(1/x, (x, oo))
|
||||
assert (1 + Order(x)).limit(x, 0) == 1
|
||||
# issue 5769
|
||||
assert ((x + Order(x**2))/x).limit(x, 0) == 1
|
||||
|
||||
assert Order(x**2).subs(x, y - 1) == Order((y - 1)**2, (y, 1))
|
||||
assert Order(10*x**2, (x, 2)).subs(x, y - 1) == Order(1, (y, 3))
|
||||
|
||||
#issue 19120
|
||||
assert O(x).subs(x, O(x)) == O(x)
|
||||
assert O(x**2).subs(x, x + O(x)) == O(x**2)
|
||||
assert O(x, (x, oo)).subs(x, O(x, (x, oo))) == O(x, (x, oo))
|
||||
assert O(x**2, (x, oo)).subs(x, x + O(x, (x, oo))) == O(x**2, (x, oo))
|
||||
assert (x + O(x**2)).subs(x, x + O(x**2)) == x + O(x**2)
|
||||
assert (x**2 + O(x**2) + 1/x**2).subs(x, x + O(x**2)) == (x + O(x**2))**(-2) + O(x**2)
|
||||
assert (x**2 + O(x**2) + 1).subs(x, x + O(x**2)) == 1 + O(x**2)
|
||||
assert O(x, (x, oo)).subs(x, x + O(x**2, (x, oo))) == O(x**2, (x, oo))
|
||||
assert sin(x).series(n=8).subs(x,sin(x).series(n=8)).expand() == x - x**3/3 + x**5/10 - 8*x**7/315 + O(x**8)
|
||||
assert cos(x).series(n=8).subs(x,sin(x).series(n=8)).expand() == 1 - x**2/2 + 5*x**4/24 - 37*x**6/720 + O(x**8)
|
||||
assert O(x).subs(x, O(1/x, (x, oo))) == O(1/x, (x, oo))
|
||||
|
||||
@XFAIL
|
||||
def test_order_failing_due_to_solveset():
|
||||
assert O(x**3).subs(x, exp(-x**2)) == O(exp(-3*x**2), (x, -oo))
|
||||
raises(NotImplementedError, lambda: O(x).subs(x, O(1/x))) # mixing of order at different points
|
||||
|
||||
|
||||
def test_issue_9351():
|
||||
assert exp(x).series(x, 10, 1) == exp(10) + Order(x - 10, (x, 10))
|
||||
|
||||
|
||||
def test_issue_9192():
|
||||
assert O(1)*O(1) == O(1)
|
||||
assert O(1)**O(1) == O(1)
|
||||
|
||||
|
||||
def test_issue_9910():
|
||||
assert O(x*log(x) + sin(x), (x, oo)) == O(x*log(x), (x, oo))
|
||||
|
||||
|
||||
def test_performance_of_adding_order():
|
||||
l = [x**i for i in range(1000)]
|
||||
l.append(O(x**1001))
|
||||
assert Add(*l).subs(x,1) == O(1)
|
||||
|
||||
def test_issue_14622():
|
||||
assert (x**(-4) + x**(-3) + x**(-1) + O(x**(-6), (x, oo))).as_numer_denom() == (
|
||||
x**4 + x**5 + x**7 + O(x**2, (x, oo)), x**8)
|
||||
assert (x**3 + O(x**2, (x, oo))).is_Add
|
||||
assert O(x**2, (x, oo)).contains(x**3) is False
|
||||
assert O(x, (x, oo)).contains(O(x, (x, 0))) is None
|
||||
assert O(x, (x, 0)).contains(O(x, (x, oo))) is None
|
||||
raises(NotImplementedError, lambda: O(x**3).contains(x**w))
|
||||
|
||||
|
||||
def test_issue_15539():
|
||||
assert O(1/x**2 + 1/x**4, (x, -oo)) == O(1/x**2, (x, -oo))
|
||||
assert O(1/x**4 + exp(x), (x, -oo)) == O(1/x**4, (x, -oo))
|
||||
assert O(1/x**4 + exp(-x), (x, -oo)) == O(exp(-x), (x, -oo))
|
||||
assert O(1/x, (x, oo)).subs(x, -x) == O(-1/x, (x, -oo))
|
||||
|
||||
def test_issue_18606():
|
||||
assert unchanged(Order, 0)
|
||||
|
||||
|
||||
def test_issue_22165():
|
||||
assert O(log(x)).contains(2)
|
||||
|
||||
|
||||
def test_issue_23231():
|
||||
# This test checks Order for expressions having
|
||||
# arguments containing variables in exponents/powers.
|
||||
assert O(x**x + 2**x, (x, oo)) == O(exp(x*log(x)), (x, oo))
|
||||
assert O(x**x + x**2, (x, oo)) == O(exp(x*log(x)), (x, oo))
|
||||
assert O(x**x + 1/x**2, (x, oo)) == O(exp(x*log(x)), (x, oo))
|
||||
assert O(2**x + 3**x , (x, oo)) == O(exp(x*log(3)), (x, oo))
|
||||
|
||||
|
||||
def test_issue_9917():
|
||||
assert O(x*sin(x) + 1, (x, oo)) == O(x, (x, oo))
|
||||
|
||||
|
||||
def test_issue_22836():
|
||||
assert O(2**x + factorial(x), (x, oo)) == O(factorial(x), (x, oo))
|
||||
assert O(2**x + factorial(x) + x**x, (x, oo)) == O(exp(x*log(x)), (x, oo))
|
||||
assert O(x + factorial(x), (x, oo)) == O(factorial(x), (x, oo))
|
||||
@@ -0,0 +1,101 @@
|
||||
from sympy.core.function import Function
|
||||
from sympy.core.numbers import (I, Rational, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.hyperbolic import tanh
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cot, sin, tan)
|
||||
from sympy.series.residues import residue
|
||||
from sympy.testing.pytest import XFAIL, raises
|
||||
from sympy.abc import x, z, a, s, k
|
||||
|
||||
|
||||
def test_basic1():
|
||||
assert residue(1/x, x, 0) == 1
|
||||
assert residue(-2/x, x, 0) == -2
|
||||
assert residue(81/x, x, 0) == 81
|
||||
assert residue(1/x**2, x, 0) == 0
|
||||
assert residue(0, x, 0) == 0
|
||||
assert residue(5, x, 0) == 0
|
||||
assert residue(x, x, 0) == 0
|
||||
assert residue(x**2, x, 0) == 0
|
||||
|
||||
|
||||
def test_basic2():
|
||||
assert residue(1/x, x, 1) == 0
|
||||
assert residue(-2/x, x, 1) == 0
|
||||
assert residue(81/x, x, -1) == 0
|
||||
assert residue(1/x**2, x, 1) == 0
|
||||
assert residue(0, x, 1) == 0
|
||||
assert residue(5, x, 1) == 0
|
||||
assert residue(x, x, 1) == 0
|
||||
assert residue(x**2, x, 5) == 0
|
||||
|
||||
|
||||
def test_f():
|
||||
f = Function("f")
|
||||
assert residue(f(x)/x**5, x, 0) == f(x).diff(x, 4).subs(x, 0)/24
|
||||
|
||||
|
||||
def test_functions():
|
||||
assert residue(1/sin(x), x, 0) == 1
|
||||
assert residue(2/sin(x), x, 0) == 2
|
||||
assert residue(1/sin(x)**2, x, 0) == 0
|
||||
assert residue(1/sin(x)**5, x, 0) == Rational(3, 8)
|
||||
|
||||
|
||||
def test_expressions():
|
||||
assert residue(1/(x + 1), x, 0) == 0
|
||||
assert residue(1/(x + 1), x, -1) == 1
|
||||
assert residue(1/(x**2 + 1), x, -1) == 0
|
||||
assert residue(1/(x**2 + 1), x, I) == -I/2
|
||||
assert residue(1/(x**2 + 1), x, -I) == I/2
|
||||
assert residue(1/(x**4 + 1), x, 0) == 0
|
||||
assert residue(1/(x**4 + 1), x, exp(I*pi/4)).equals(-(Rational(1, 4) + I/4)/sqrt(2))
|
||||
assert residue(1/(x**2 + a**2)**2, x, a*I) == -I/4/a**3
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_expressions_failing():
|
||||
n = Symbol('n', integer=True, positive=True)
|
||||
assert residue(exp(z)/(z - pi*I/4*a)**n, z, I*pi*a) == \
|
||||
exp(I*pi*a/4)/factorial(n - 1)
|
||||
|
||||
|
||||
def test_NotImplemented():
|
||||
raises(NotImplementedError, lambda: residue(exp(1/z), z, 0))
|
||||
|
||||
|
||||
def test_bug():
|
||||
assert residue(2**(z)*(s + z)*(1 - s - z)/z**2, z, 0) == \
|
||||
1 + s*log(2) - s**2*log(2) - 2*s
|
||||
|
||||
|
||||
def test_issue_5654():
|
||||
assert residue(1/(x**2 + a**2)**2, x, a*I) == -I/(4*a**3)
|
||||
assert residue(1/s*1/(z - exp(s)), s, 0) == 1/(z - 1)
|
||||
assert residue((1 + k)/s*1/(z - exp(s)), s, 0) == k/(z - 1) + 1/(z - 1)
|
||||
|
||||
|
||||
def test_issue_6499():
|
||||
assert residue(1/(exp(z) - 1), z, 0) == 1
|
||||
|
||||
|
||||
def test_issue_14037():
|
||||
assert residue(sin(x**50)/x**51, x, 0) == 1
|
||||
|
||||
|
||||
def test_issue_21176():
|
||||
f = x**2*cot(pi*x)/(x**4 + 1)
|
||||
assert residue(f, x, -sqrt(2)/2 - sqrt(2)*I/2).cancel().together(deep=True)\
|
||||
== sqrt(2)*(1 - I)/(8*tan(sqrt(2)*pi*(1 + I)/2))
|
||||
|
||||
|
||||
def test_issue_21177():
|
||||
r = -sqrt(3)*tanh(sqrt(3)*pi/2)/3
|
||||
a = residue(cot(pi*x)/((x - 1)*(x - 2) + 1), x, S(3)/2 - sqrt(3)*I/2)
|
||||
b = residue(cot(pi*x)/(x**2 - 3*x + 3), x, S(3)/2 - sqrt(3)*I/2)
|
||||
assert a == r
|
||||
assert (b - a).cancel() == 0
|
||||
@@ -0,0 +1,312 @@
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.function import Function
|
||||
from sympy.core.numbers import oo, Rational
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols, Symbol
|
||||
from sympy.functions.combinatorial.numbers import tribonacci, fibonacci
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import cos, sin
|
||||
from sympy.series import EmptySequence
|
||||
from sympy.series.sequences import (SeqMul, SeqAdd, SeqPer, SeqFormula,
|
||||
sequence)
|
||||
from sympy.sets.sets import Interval
|
||||
from sympy.tensor.indexed import Indexed, Idx
|
||||
from sympy.series.sequences import SeqExpr, SeqExprOp, RecursiveSeq
|
||||
from sympy.testing.pytest import raises, slow
|
||||
|
||||
x, y, z = symbols('x y z')
|
||||
n, m = symbols('n m')
|
||||
|
||||
|
||||
def test_EmptySequence():
|
||||
assert S.EmptySequence is EmptySequence
|
||||
|
||||
assert S.EmptySequence.interval is S.EmptySet
|
||||
assert S.EmptySequence.length is S.Zero
|
||||
|
||||
assert list(S.EmptySequence) == []
|
||||
|
||||
|
||||
def test_SeqExpr():
|
||||
#SeqExpr is a baseclass and does not take care of
|
||||
#ensuring all arguments are Basics hence the use of
|
||||
#Tuple(...) here.
|
||||
s = SeqExpr(Tuple(1, n, y), Tuple(x, 0, 10))
|
||||
|
||||
assert isinstance(s, SeqExpr)
|
||||
assert s.gen == (1, n, y)
|
||||
assert s.interval == Interval(0, 10)
|
||||
assert s.start == 0
|
||||
assert s.stop == 10
|
||||
assert s.length == 11
|
||||
assert s.variables == (x,)
|
||||
|
||||
assert SeqExpr(Tuple(1, 2, 3), Tuple(x, 0, oo)).length is oo
|
||||
|
||||
|
||||
def test_SeqPer():
|
||||
s = SeqPer((1, n, 3), (x, 0, 5))
|
||||
|
||||
assert isinstance(s, SeqPer)
|
||||
assert s.periodical == Tuple(1, n, 3)
|
||||
assert s.period == 3
|
||||
assert s.coeff(3) == 1
|
||||
assert s.free_symbols == {n}
|
||||
|
||||
assert list(s) == [1, n, 3, 1, n, 3]
|
||||
assert s[:] == [1, n, 3, 1, n, 3]
|
||||
assert SeqPer((1, n, 3), (x, -oo, 0))[0:6] == [1, n, 3, 1, n, 3]
|
||||
|
||||
raises(ValueError, lambda: SeqPer((1, 2, 3), (0, 1, 2)))
|
||||
raises(ValueError, lambda: SeqPer((1, 2, 3), (x, -oo, oo)))
|
||||
raises(ValueError, lambda: SeqPer(n**2, (0, oo)))
|
||||
|
||||
assert SeqPer((n, n**2, n**3), (m, 0, oo))[:6] == \
|
||||
[n, n**2, n**3, n, n**2, n**3]
|
||||
assert SeqPer((n, n**2, n**3), (n, 0, oo))[:6] == [0, 1, 8, 3, 16, 125]
|
||||
assert SeqPer((n, m), (n, 0, oo))[:6] == [0, m, 2, m, 4, m]
|
||||
|
||||
|
||||
def test_SeqFormula():
|
||||
s = SeqFormula(n**2, (n, 0, 5))
|
||||
|
||||
assert isinstance(s, SeqFormula)
|
||||
assert s.formula == n**2
|
||||
assert s.coeff(3) == 9
|
||||
|
||||
assert list(s) == [i**2 for i in range(6)]
|
||||
assert s[:] == [i**2 for i in range(6)]
|
||||
assert SeqFormula(n**2, (n, -oo, 0))[0:6] == [i**2 for i in range(6)]
|
||||
|
||||
assert SeqFormula(n**2, (0, oo)) == SeqFormula(n**2, (n, 0, oo))
|
||||
|
||||
assert SeqFormula(n**2, (0, m)).subs(m, x) == SeqFormula(n**2, (0, x))
|
||||
assert SeqFormula(m*n**2, (n, 0, oo)).subs(m, x) == \
|
||||
SeqFormula(x*n**2, (n, 0, oo))
|
||||
|
||||
raises(ValueError, lambda: SeqFormula(n**2, (0, 1, 2)))
|
||||
raises(ValueError, lambda: SeqFormula(n**2, (n, -oo, oo)))
|
||||
raises(ValueError, lambda: SeqFormula(m*n**2, (0, oo)))
|
||||
|
||||
seq = SeqFormula(x*(y**2 + z), (z, 1, 100))
|
||||
assert seq.expand() == SeqFormula(x*y**2 + x*z, (z, 1, 100))
|
||||
seq = SeqFormula(sin(x*(y**2 + z)),(z, 1, 100))
|
||||
assert seq.expand(trig=True) == SeqFormula(sin(x*y**2)*cos(x*z) + sin(x*z)*cos(x*y**2), (z, 1, 100))
|
||||
assert seq.expand() == SeqFormula(sin(x*y**2 + x*z), (z, 1, 100))
|
||||
assert seq.expand(trig=False) == SeqFormula(sin(x*y**2 + x*z), (z, 1, 100))
|
||||
seq = SeqFormula(exp(x*(y**2 + z)), (z, 1, 100))
|
||||
assert seq.expand() == SeqFormula(exp(x*y**2)*exp(x*z), (z, 1, 100))
|
||||
assert seq.expand(power_exp=False) == SeqFormula(exp(x*y**2 + x*z), (z, 1, 100))
|
||||
assert seq.expand(mul=False, power_exp=False) == SeqFormula(exp(x*(y**2 + z)), (z, 1, 100))
|
||||
|
||||
def test_sequence():
|
||||
form = SeqFormula(n**2, (n, 0, 5))
|
||||
per = SeqPer((1, 2, 3), (n, 0, 5))
|
||||
inter = SeqFormula(n**2)
|
||||
|
||||
assert sequence(n**2, (n, 0, 5)) == form
|
||||
assert sequence((1, 2, 3), (n, 0, 5)) == per
|
||||
assert sequence(n**2) == inter
|
||||
|
||||
|
||||
def test_SeqExprOp():
|
||||
form = SeqFormula(n**2, (n, 0, 10))
|
||||
per = SeqPer((1, 2, 3), (m, 5, 10))
|
||||
|
||||
s = SeqExprOp(form, per)
|
||||
assert s.gen == (n**2, (1, 2, 3))
|
||||
assert s.interval == Interval(5, 10)
|
||||
assert s.start == 5
|
||||
assert s.stop == 10
|
||||
assert s.length == 6
|
||||
assert s.variables == (n, m)
|
||||
|
||||
|
||||
def test_SeqAdd():
|
||||
per = SeqPer((1, 2, 3), (n, 0, oo))
|
||||
form = SeqFormula(n**2)
|
||||
|
||||
per_bou = SeqPer((1, 2), (n, 1, 5))
|
||||
form_bou = SeqFormula(n**2, (6, 10))
|
||||
form_bou2 = SeqFormula(n**2, (1, 5))
|
||||
|
||||
assert SeqAdd() == S.EmptySequence
|
||||
assert SeqAdd(S.EmptySequence) == S.EmptySequence
|
||||
assert SeqAdd(per) == per
|
||||
assert SeqAdd(per, S.EmptySequence) == per
|
||||
assert SeqAdd(per_bou, form_bou) == S.EmptySequence
|
||||
|
||||
s = SeqAdd(per_bou, form_bou2, evaluate=False)
|
||||
assert s.args == (form_bou2, per_bou)
|
||||
assert s[:] == [2, 6, 10, 18, 26]
|
||||
assert list(s) == [2, 6, 10, 18, 26]
|
||||
|
||||
assert isinstance(SeqAdd(per, per_bou, evaluate=False), SeqAdd)
|
||||
|
||||
s1 = SeqAdd(per, per_bou)
|
||||
assert isinstance(s1, SeqPer)
|
||||
assert s1 == SeqPer((2, 4, 4, 3, 3, 5), (n, 1, 5))
|
||||
s2 = SeqAdd(form, form_bou)
|
||||
assert isinstance(s2, SeqFormula)
|
||||
assert s2 == SeqFormula(2*n**2, (6, 10))
|
||||
|
||||
assert SeqAdd(form, form_bou, per) == \
|
||||
SeqAdd(per, SeqFormula(2*n**2, (6, 10)))
|
||||
assert SeqAdd(form, SeqAdd(form_bou, per)) == \
|
||||
SeqAdd(per, SeqFormula(2*n**2, (6, 10)))
|
||||
assert SeqAdd(per, SeqAdd(form, form_bou), evaluate=False) == \
|
||||
SeqAdd(per, SeqFormula(2*n**2, (6, 10)))
|
||||
|
||||
assert SeqAdd(SeqPer((1, 2), (n, 0, oo)), SeqPer((1, 2), (m, 0, oo))) == \
|
||||
SeqPer((2, 4), (n, 0, oo))
|
||||
|
||||
|
||||
def test_SeqMul():
|
||||
per = SeqPer((1, 2, 3), (n, 0, oo))
|
||||
form = SeqFormula(n**2)
|
||||
|
||||
per_bou = SeqPer((1, 2), (n, 1, 5))
|
||||
form_bou = SeqFormula(n**2, (n, 6, 10))
|
||||
form_bou2 = SeqFormula(n**2, (1, 5))
|
||||
|
||||
assert SeqMul() == S.EmptySequence
|
||||
assert SeqMul(S.EmptySequence) == S.EmptySequence
|
||||
assert SeqMul(per) == per
|
||||
assert SeqMul(per, S.EmptySequence) == S.EmptySequence
|
||||
assert SeqMul(per_bou, form_bou) == S.EmptySequence
|
||||
|
||||
s = SeqMul(per_bou, form_bou2, evaluate=False)
|
||||
assert s.args == (form_bou2, per_bou)
|
||||
assert s[:] == [1, 8, 9, 32, 25]
|
||||
assert list(s) == [1, 8, 9, 32, 25]
|
||||
|
||||
assert isinstance(SeqMul(per, per_bou, evaluate=False), SeqMul)
|
||||
|
||||
s1 = SeqMul(per, per_bou)
|
||||
assert isinstance(s1, SeqPer)
|
||||
assert s1 == SeqPer((1, 4, 3, 2, 2, 6), (n, 1, 5))
|
||||
s2 = SeqMul(form, form_bou)
|
||||
assert isinstance(s2, SeqFormula)
|
||||
assert s2 == SeqFormula(n**4, (6, 10))
|
||||
|
||||
assert SeqMul(form, form_bou, per) == \
|
||||
SeqMul(per, SeqFormula(n**4, (6, 10)))
|
||||
assert SeqMul(form, SeqMul(form_bou, per)) == \
|
||||
SeqMul(per, SeqFormula(n**4, (6, 10)))
|
||||
assert SeqMul(per, SeqMul(form, form_bou2,
|
||||
evaluate=False), evaluate=False) == \
|
||||
SeqMul(form, per, form_bou2, evaluate=False)
|
||||
|
||||
assert SeqMul(SeqPer((1, 2), (n, 0, oo)), SeqPer((1, 2), (n, 0, oo))) == \
|
||||
SeqPer((1, 4), (n, 0, oo))
|
||||
|
||||
|
||||
def test_add():
|
||||
per = SeqPer((1, 2), (n, 0, oo))
|
||||
form = SeqFormula(n**2)
|
||||
|
||||
assert per + (SeqPer((2, 3))) == SeqPer((3, 5), (n, 0, oo))
|
||||
assert form + SeqFormula(n**3) == SeqFormula(n**2 + n**3)
|
||||
|
||||
assert per + form == SeqAdd(per, form)
|
||||
|
||||
raises(TypeError, lambda: per + n)
|
||||
raises(TypeError, lambda: n + per)
|
||||
|
||||
|
||||
def test_sub():
|
||||
per = SeqPer((1, 2), (n, 0, oo))
|
||||
form = SeqFormula(n**2)
|
||||
|
||||
assert per - (SeqPer((2, 3))) == SeqPer((-1, -1), (n, 0, oo))
|
||||
assert form - (SeqFormula(n**3)) == SeqFormula(n**2 - n**3)
|
||||
|
||||
assert per - form == SeqAdd(per, -form)
|
||||
|
||||
raises(TypeError, lambda: per - n)
|
||||
raises(TypeError, lambda: n - per)
|
||||
|
||||
|
||||
def test_mul__coeff_mul():
|
||||
assert SeqPer((1, 2), (n, 0, oo)).coeff_mul(2) == SeqPer((2, 4), (n, 0, oo))
|
||||
assert SeqFormula(n**2).coeff_mul(2) == SeqFormula(2*n**2)
|
||||
assert S.EmptySequence.coeff_mul(100) == S.EmptySequence
|
||||
|
||||
assert SeqPer((1, 2), (n, 0, oo)) * (SeqPer((2, 3))) == \
|
||||
SeqPer((2, 6), (n, 0, oo))
|
||||
assert SeqFormula(n**2) * SeqFormula(n**3) == SeqFormula(n**5)
|
||||
|
||||
assert S.EmptySequence * SeqFormula(n**2) == S.EmptySequence
|
||||
assert SeqFormula(n**2) * S.EmptySequence == S.EmptySequence
|
||||
|
||||
raises(TypeError, lambda: sequence(n**2) * n)
|
||||
raises(TypeError, lambda: n * sequence(n**2))
|
||||
|
||||
|
||||
def test_neg():
|
||||
assert -SeqPer((1, -2), (n, 0, oo)) == SeqPer((-1, 2), (n, 0, oo))
|
||||
assert -SeqFormula(n**2) == SeqFormula(-n**2)
|
||||
|
||||
|
||||
def test_operations():
|
||||
per = SeqPer((1, 2), (n, 0, oo))
|
||||
per2 = SeqPer((2, 4), (n, 0, oo))
|
||||
form = SeqFormula(n**2)
|
||||
form2 = SeqFormula(n**3)
|
||||
|
||||
assert per + form + form2 == SeqAdd(per, form, form2)
|
||||
assert per + form - form2 == SeqAdd(per, form, -form2)
|
||||
assert per + form - S.EmptySequence == SeqAdd(per, form)
|
||||
assert per + per2 + form == SeqAdd(SeqPer((3, 6), (n, 0, oo)), form)
|
||||
assert S.EmptySequence - per == -per
|
||||
assert form + form == SeqFormula(2*n**2)
|
||||
|
||||
assert per * form * form2 == SeqMul(per, form, form2)
|
||||
assert form * form == SeqFormula(n**4)
|
||||
assert form * -form == SeqFormula(-n**4)
|
||||
|
||||
assert form * (per + form2) == SeqMul(form, SeqAdd(per, form2))
|
||||
assert form * (per + per) == SeqMul(form, per2)
|
||||
|
||||
assert form.coeff_mul(m) == SeqFormula(m*n**2, (n, 0, oo))
|
||||
assert per.coeff_mul(m) == SeqPer((m, 2*m), (n, 0, oo))
|
||||
|
||||
|
||||
def test_Idx_limits():
|
||||
i = symbols('i', cls=Idx)
|
||||
r = Indexed('r', i)
|
||||
|
||||
assert SeqFormula(r, (i, 0, 5))[:] == [r.subs(i, j) for j in range(6)]
|
||||
assert SeqPer((1, 2), (i, 0, 5))[:] == [1, 2, 1, 2, 1, 2]
|
||||
|
||||
|
||||
@slow
|
||||
def test_find_linear_recurrence():
|
||||
assert sequence((0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55), \
|
||||
(n, 0, 10)).find_linear_recurrence(11) == [1, 1]
|
||||
assert sequence((1, 2, 4, 7, 28, 128, 582, 2745, 13021, 61699, 292521, \
|
||||
1387138), (n, 0, 11)).find_linear_recurrence(12) == [5, -2, 6, -11]
|
||||
assert sequence(x*n**3+y*n, (n, 0, oo)).find_linear_recurrence(10) \
|
||||
== [4, -6, 4, -1]
|
||||
assert sequence(x**n, (n,0,20)).find_linear_recurrence(21) == [x]
|
||||
assert sequence((1,2,3)).find_linear_recurrence(10, 5) == [0, 0, 1]
|
||||
assert sequence(((1 + sqrt(5))/2)**n + \
|
||||
(-(1 + sqrt(5))/2)**(-n)).find_linear_recurrence(10) == [1, 1]
|
||||
assert sequence(x*((1 + sqrt(5))/2)**n + y*(-(1 + sqrt(5))/2)**(-n), \
|
||||
(n,0,oo)).find_linear_recurrence(10) == [1, 1]
|
||||
assert sequence((1,2,3,4,6),(n, 0, 4)).find_linear_recurrence(5) == []
|
||||
assert sequence((2,3,4,5,6,79),(n, 0, 5)).find_linear_recurrence(6,gfvar=x) \
|
||||
== ([], None)
|
||||
assert sequence((2,3,4,5,8,30),(n, 0, 5)).find_linear_recurrence(6,gfvar=x) \
|
||||
== ([Rational(19, 2), -20, Rational(27, 2)], (-31*x**2 + 32*x - 4)/(27*x**3 - 40*x**2 + 19*x -2))
|
||||
assert sequence(fibonacci(n)).find_linear_recurrence(30,gfvar=x) \
|
||||
== ([1, 1], -x/(x**2 + x - 1))
|
||||
assert sequence(tribonacci(n)).find_linear_recurrence(30,gfvar=x) \
|
||||
== ([1, 1, 1], -x/(x**3 + x**2 + x - 1))
|
||||
|
||||
def test_RecursiveSeq():
|
||||
y = Function('y')
|
||||
n = Symbol('n')
|
||||
fib = RecursiveSeq(y(n - 1) + y(n - 2), y(n), n, [0, 1])
|
||||
assert fib.coeff(3) == 2
|
||||
@@ -0,0 +1,421 @@
|
||||
from sympy.core.evalf import N
|
||||
from sympy.core.function import (Derivative, Function, PoleError, Subs)
|
||||
from sympy.core.numbers import (E, Float, Rational, oo, pi, I)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.elementary.exponential import (LambertW, exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (atan, cos, sin)
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.integrals.integrals import Integral, integrate
|
||||
from sympy.series.order import O
|
||||
from sympy.series.series import series
|
||||
from sympy.abc import x, y, n, k
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.core import EulerGamma
|
||||
|
||||
|
||||
def test_sin():
|
||||
e1 = sin(x).series(x, 0)
|
||||
e2 = series(sin(x), x, 0)
|
||||
assert e1 == e2
|
||||
|
||||
|
||||
def test_cos():
|
||||
e1 = cos(x).series(x, 0)
|
||||
e2 = series(cos(x), x, 0)
|
||||
assert e1 == e2
|
||||
|
||||
|
||||
def test_exp():
|
||||
e1 = exp(x).series(x, 0)
|
||||
e2 = series(exp(x), x, 0)
|
||||
assert e1 == e2
|
||||
|
||||
|
||||
def test_exp2():
|
||||
e1 = exp(cos(x)).series(x, 0)
|
||||
e2 = series(exp(cos(x)), x, 0)
|
||||
assert e1 == e2
|
||||
|
||||
|
||||
def test_issue_5223():
|
||||
assert series(1, x) == 1
|
||||
assert next(S.Zero.lseries(x)) == 0
|
||||
assert cos(x).series() == cos(x).series(x)
|
||||
raises(ValueError, lambda: cos(x + y).series())
|
||||
raises(ValueError, lambda: x.series(dir=""))
|
||||
|
||||
assert (cos(x).series(x, 1) -
|
||||
cos(x + 1).series(x).subs(x, x - 1)).removeO() == 0
|
||||
e = cos(x).series(x, 1, n=None)
|
||||
assert [next(e) for i in range(2)] == [cos(1), -((x - 1)*sin(1))]
|
||||
e = cos(x).series(x, 1, n=None, dir='-')
|
||||
assert [next(e) for i in range(2)] == [cos(1), (1 - x)*sin(1)]
|
||||
# the following test is exact so no need for x -> x - 1 replacement
|
||||
assert abs(x).series(x, 1, dir='-') == x
|
||||
assert exp(x).series(x, 1, dir='-', n=3).removeO() == \
|
||||
E - E*(-x + 1) + E*(-x + 1)**2/2
|
||||
|
||||
D = Derivative
|
||||
assert D(x**2 + x**3*y**2, x, 2, y, 1).series(x).doit() == 12*x*y
|
||||
assert next(D(cos(x), x).lseries()) == D(1, x)
|
||||
assert D(
|
||||
exp(x), x).series(n=3) == D(1, x) + D(x, x) + D(x**2/2, x) + D(x**3/6, x) + O(x**3)
|
||||
|
||||
assert Integral(x, (x, 1, 3), (y, 1, x)).series(x) == -4 + 4*x
|
||||
|
||||
assert (1 + x + O(x**2)).getn() == 2
|
||||
assert (1 + x).getn() is None
|
||||
|
||||
raises(PoleError, lambda: ((1/sin(x))**oo).series())
|
||||
logx = Symbol('logx')
|
||||
assert ((sin(x))**y).nseries(x, n=1, logx=logx) == \
|
||||
exp(y*logx) + O(x*exp(y*logx), x)
|
||||
|
||||
assert sin(1/x).series(x, oo, n=5) == 1/x - 1/(6*x**3) + O(x**(-5), (x, oo))
|
||||
assert abs(x).series(x, oo, n=5, dir='+') == x
|
||||
assert abs(x).series(x, -oo, n=5, dir='-') == -x
|
||||
assert abs(-x).series(x, oo, n=5, dir='+') == x
|
||||
assert abs(-x).series(x, -oo, n=5, dir='-') == -x
|
||||
|
||||
assert exp(x*log(x)).series(n=3) == \
|
||||
1 + x*log(x) + x**2*log(x)**2/2 + O(x**3*log(x)**3)
|
||||
# XXX is this right? If not, fix "ngot > n" handling in expr.
|
||||
p = Symbol('p', positive=True)
|
||||
assert exp(sqrt(p)**3*log(p)).series(n=3) == \
|
||||
1 + p**S('3/2')*log(p) + O(p**3*log(p)**3)
|
||||
|
||||
assert exp(sin(x)*log(x)).series(n=2) == 1 + x*log(x) + O(x**2*log(x)**2)
|
||||
|
||||
|
||||
def test_issue_6350():
|
||||
expr = integrate(exp(k*(y**3 - 3*y)), (y, 0, oo), conds='none')
|
||||
assert expr.series(k, 0, 3) == -(-1)**(S(2)/3)*sqrt(3)*gamma(S(1)/3)**2*gamma(S(2)/3)/(6*pi*k**(S(1)/3)) - \
|
||||
sqrt(3)*k*gamma(-S(2)/3)*gamma(-S(1)/3)/(6*pi) - \
|
||||
(-1)**(S(1)/3)*sqrt(3)*k**(S(1)/3)*gamma(-S(1)/3)*gamma(S(1)/3)*gamma(S(2)/3)/(6*pi) - \
|
||||
(-1)**(S(2)/3)*sqrt(3)*k**(S(5)/3)*gamma(S(1)/3)**2*gamma(S(2)/3)/(4*pi) - \
|
||||
(-1)**(S(1)/3)*sqrt(3)*k**(S(7)/3)*gamma(-S(1)/3)*gamma(S(1)/3)*gamma(S(2)/3)/(8*pi) + O(k**3)
|
||||
|
||||
|
||||
def test_issue_11313():
|
||||
assert Integral(cos(x), x).series(x) == sin(x).series(x)
|
||||
assert Derivative(sin(x), x).series(x, n=3).doit() == cos(x).series(x, n=3)
|
||||
|
||||
assert Derivative(x**3, x).as_leading_term(x) == 3*x**2
|
||||
assert Derivative(x**3, y).as_leading_term(x) == 0
|
||||
assert Derivative(sin(x), x).as_leading_term(x) == 1
|
||||
assert Derivative(cos(x), x).as_leading_term(x) == -x
|
||||
|
||||
# This result is equivalent to zero, zero is not return because
|
||||
# `Expr.series` doesn't currently detect an `x` in its `free_symbol`s.
|
||||
assert Derivative(1, x).as_leading_term(x) == Derivative(1, x)
|
||||
|
||||
assert Derivative(exp(x), x).series(x).doit() == exp(x).series(x)
|
||||
assert 1 + Integral(exp(x), x).series(x) == exp(x).series(x)
|
||||
|
||||
assert Derivative(log(x), x).series(x).doit() == (1/x).series(x)
|
||||
assert Integral(log(x), x).series(x) == Integral(log(x), x).doit().series(x).removeO()
|
||||
|
||||
|
||||
def test_series_of_Subs():
|
||||
from sympy.abc import z
|
||||
|
||||
subs1 = Subs(sin(x), x, y)
|
||||
subs2 = Subs(sin(x) * cos(z), x, y)
|
||||
subs3 = Subs(sin(x * z), (x, z), (y, x))
|
||||
|
||||
assert subs1.series(x) == subs1
|
||||
subs1_series = (Subs(x, x, y) + Subs(-x**3/6, x, y) +
|
||||
Subs(x**5/120, x, y) + O(y**6))
|
||||
assert subs1.series() == subs1_series
|
||||
assert subs1.series(y) == subs1_series
|
||||
assert subs1.series(z) == subs1
|
||||
assert subs2.series(z) == (Subs(z**4*sin(x)/24, x, y) +
|
||||
Subs(-z**2*sin(x)/2, x, y) + Subs(sin(x), x, y) + O(z**6))
|
||||
assert subs3.series(x).doit() == subs3.doit().series(x)
|
||||
assert subs3.series(z).doit() == sin(x*y)
|
||||
|
||||
raises(ValueError, lambda: Subs(x + 2*y, y, z).series())
|
||||
assert Subs(x + y, y, z).series(x).doit() == x + z
|
||||
|
||||
|
||||
def test_issue_3978():
|
||||
f = Function('f')
|
||||
assert f(x).series(x, 0, 3, dir='-') == \
|
||||
f(0) + x*Subs(Derivative(f(x), x), x, 0) + \
|
||||
x**2*Subs(Derivative(f(x), x, x), x, 0)/2 + O(x**3)
|
||||
assert f(x).series(x, 0, 3) == \
|
||||
f(0) + x*Subs(Derivative(f(x), x), x, 0) + \
|
||||
x**2*Subs(Derivative(f(x), x, x), x, 0)/2 + O(x**3)
|
||||
assert f(x**2).series(x, 0, 3) == \
|
||||
f(0) + x**2*Subs(Derivative(f(x), x), x, 0) + O(x**3)
|
||||
assert f(x**2+1).series(x, 0, 3) == \
|
||||
f(1) + x**2*Subs(Derivative(f(x), x), x, 1) + O(x**3)
|
||||
|
||||
class TestF(Function):
|
||||
pass
|
||||
|
||||
assert TestF(x).series(x, 0, 3) == TestF(0) + \
|
||||
x*Subs(Derivative(TestF(x), x), x, 0) + \
|
||||
x**2*Subs(Derivative(TestF(x), x, x), x, 0)/2 + O(x**3)
|
||||
|
||||
from sympy.series.acceleration import richardson, shanks
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.numbers import Integer
|
||||
|
||||
|
||||
def test_acceleration():
|
||||
e = (1 + 1/n)**n
|
||||
assert round(richardson(e, n, 10, 20).evalf(), 10) == round(E.evalf(), 10)
|
||||
|
||||
A = Sum(Integer(-1)**(k + 1) / k, (k, 1, n))
|
||||
assert round(shanks(A, n, 25).evalf(), 4) == round(log(2).evalf(), 4)
|
||||
assert round(shanks(A, n, 25, 5).evalf(), 10) == round(log(2).evalf(), 10)
|
||||
|
||||
|
||||
def test_issue_5852():
|
||||
assert series(1/cos(x/log(x)), x, 0) == 1 + x**2/(2*log(x)**2) + \
|
||||
5*x**4/(24*log(x)**4) + O(x**6)
|
||||
|
||||
|
||||
def test_issue_4583():
|
||||
assert cos(1 + x + x**2).series(x, 0, 5) == cos(1) - x*sin(1) + \
|
||||
x**2*(-sin(1) - cos(1)/2) + x**3*(-cos(1) + sin(1)/6) + \
|
||||
x**4*(-11*cos(1)/24 + sin(1)/2) + O(x**5)
|
||||
|
||||
|
||||
def test_issue_6318():
|
||||
eq = (1/x)**Rational(2, 3)
|
||||
assert (eq + 1).as_leading_term(x) == eq
|
||||
|
||||
|
||||
def test_x_is_base_detection():
|
||||
eq = (x**2)**Rational(2, 3)
|
||||
assert eq.series() == x**Rational(4, 3)
|
||||
|
||||
|
||||
def test_issue_7203():
|
||||
assert series(cos(x), x, pi, 3) == \
|
||||
-1 + (x - pi)**2/2 + O((x - pi)**3, (x, pi))
|
||||
|
||||
|
||||
def test_exp_product_positive_factors():
|
||||
a, b = symbols('a, b', positive=True)
|
||||
x = a * b
|
||||
assert series(exp(x), x, n=8) == 1 + a*b + a**2*b**2/2 + \
|
||||
a**3*b**3/6 + a**4*b**4/24 + a**5*b**5/120 + a**6*b**6/720 + \
|
||||
a**7*b**7/5040 + O(a**8*b**8, a, b)
|
||||
|
||||
|
||||
def test_issue_8805():
|
||||
assert series(1, n=8) == 1
|
||||
|
||||
|
||||
def test_issue_9173():
|
||||
p0,p1,p2,p3,b0,b1,b2=symbols('p0 p1 p2 p3 b0 b1 b2')
|
||||
Q=(p0+(p1+(p2+p3/y)/y)/y)/(1+((p3/(b0*y)+(b0*p2-b1*p3)/b0**2)/y+\
|
||||
(b0**2*p1-b0*b1*p2-p3*(b0*b2-b1**2))/b0**3)/y)
|
||||
|
||||
series = Q.series(y,n=3)
|
||||
|
||||
assert series == y*(b0*p2/p3+b0*(-p2/p3+b1/b0))+y**2*(b0*p1/p3+b0*p2*\
|
||||
(-p2/p3+b1/b0)/p3+b0*(-p1/p3+(p2/p3-b1/b0)**2+b1*p2/(b0*p3)+\
|
||||
b2/b0-b1**2/b0**2))+b0+O(y**3)
|
||||
assert series.simplify() == b2*y**2 + b1*y + b0 + O(y**3)
|
||||
|
||||
|
||||
def test_issue_9549():
|
||||
y = (x**2 + x + 1) / (x**3 + x**2)
|
||||
assert series(y, x, oo) == x**(-5) - 1/x**4 + x**(-3) + 1/x + O(x**(-6), (x, oo))
|
||||
|
||||
|
||||
def test_issue_10761():
|
||||
assert series(1/(x**-2 + x**-3), x, 0) == x**3 - x**4 + x**5 + O(x**6)
|
||||
|
||||
|
||||
def test_issue_12578():
|
||||
y = (1 - 1/(x/2 - 1/(2*x))**4)**(S(1)/8)
|
||||
assert y.series(x, 0, n=17) == 1 - 2*x**4 - 8*x**6 - 34*x**8 - 152*x**10 - 714*x**12 - \
|
||||
3472*x**14 - 17318*x**16 + O(x**17)
|
||||
|
||||
|
||||
def test_issue_12791():
|
||||
beta = symbols('beta', positive=True)
|
||||
theta, varphi = symbols('theta varphi', real=True)
|
||||
|
||||
expr = (-beta**2*varphi*sin(theta) + beta**2*cos(theta) + \
|
||||
beta*varphi*sin(theta) - beta*cos(theta) - beta + 1)/(beta*cos(theta) - 1)**2
|
||||
|
||||
sol = (0.5/(0.5*cos(theta) - 1.0)**2 - 0.25*cos(theta)/(0.5*cos(theta) - 1.0)**2
|
||||
+ (beta - 0.5)*(-0.25*varphi*sin(2*theta) - 1.5*cos(theta)
|
||||
+ 0.25*cos(2*theta) + 1.25)/((0.5*cos(theta) - 1.0)**2*(0.5*cos(theta) - 1.0))
|
||||
+ 0.25*varphi*sin(theta)/(0.5*cos(theta) - 1.0)**2
|
||||
+ O((beta - S.Half)**2, (beta, S.Half)))
|
||||
|
||||
assert expr.series(beta, 0.5, 2).trigsimp() == sol
|
||||
|
||||
|
||||
def test_issue_14384():
|
||||
x, a = symbols('x a')
|
||||
assert series(x**a, x) == x**a
|
||||
assert series(x**(-2*a), x) == x**(-2*a)
|
||||
assert series(exp(a*log(x)), x) == exp(a*log(x))
|
||||
raises(PoleError, lambda: series(x**I, x))
|
||||
raises(PoleError, lambda: series(x**(I + 1), x))
|
||||
raises(PoleError, lambda: series(exp(I*log(x)), x))
|
||||
|
||||
|
||||
def test_issue_14885():
|
||||
assert series(x**Rational(-3, 2)*exp(x), x, 0) == (x**Rational(-3, 2) + 1/sqrt(x) +
|
||||
sqrt(x)/2 + x**Rational(3, 2)/6 + x**Rational(5, 2)/24 + x**Rational(7, 2)/120 +
|
||||
x**Rational(9, 2)/720 + x**Rational(11, 2)/5040 + O(x**6))
|
||||
|
||||
|
||||
def test_issue_15539():
|
||||
assert series(atan(x), x, -oo) == (-1/(5*x**5) + 1/(3*x**3) - 1/x - pi/2
|
||||
+ O(x**(-6), (x, -oo)))
|
||||
assert series(atan(x), x, oo) == (-1/(5*x**5) + 1/(3*x**3) - 1/x + pi/2
|
||||
+ O(x**(-6), (x, oo)))
|
||||
|
||||
|
||||
def test_issue_7259():
|
||||
assert series(LambertW(x), x) == x - x**2 + 3*x**3/2 - 8*x**4/3 + 125*x**5/24 + O(x**6)
|
||||
assert series(LambertW(x**2), x, n=8) == x**2 - x**4 + 3*x**6/2 + O(x**8)
|
||||
assert series(LambertW(sin(x)), x, n=4) == x - x**2 + 4*x**3/3 + O(x**4)
|
||||
|
||||
def test_issue_11884():
|
||||
assert cos(x).series(x, 1, n=1) == cos(1) + O(x - 1, (x, 1))
|
||||
|
||||
|
||||
def test_issue_18008():
|
||||
y = x*(1 + x*(1 - x))/((1 + x*(1 - x)) - (1 - x)*(1 - x))
|
||||
assert y.series(x, oo, n=4) == -9/(32*x**3) - 3/(16*x**2) - 1/(8*x) + S(1)/4 + x/2 + \
|
||||
O(x**(-4), (x, oo))
|
||||
|
||||
|
||||
def test_issue_18842():
|
||||
f = log(x/(1 - x))
|
||||
assert f.series(x, 0.491, n=1).removeO().nsimplify() == \
|
||||
-S(180019443780011)/5000000000000000
|
||||
|
||||
|
||||
def test_issue_19534():
|
||||
dt = symbols('dt', real=True)
|
||||
expr = 16*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0)/45 + \
|
||||
49*dt*(-0.049335189898860408029*dt*(2.0*dt + 1.0) + \
|
||||
0.29601113939316244817*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) - \
|
||||
0.12564355335492979587*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) + 0.051640768506639183825*dt + \
|
||||
dt*(1/2 - sqrt(21)/14) + 1.0)/180 + 49*dt*(-0.23637909581542530626*dt*(2.0*dt + 1.0) - \
|
||||
0.74817562366625959291*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.88085458023927036857*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) + \
|
||||
2.1165151389911680013*dt*(-0.049335189898860408029*dt*(2.0*dt + 1.0) + \
|
||||
0.29601113939316244817*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) - \
|
||||
0.12564355335492979587*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) + 0.22431393315265061193*dt + 1.0) - \
|
||||
1.1854881643947648988*dt + dt*(sqrt(21)/14 + 1/2) + 1.0)/180 + \
|
||||
dt*(0.66666666666666666667*dt*(2.0*dt + 1.0) + \
|
||||
6.0173399699313066769*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) - \
|
||||
4.1117044797036320069*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) - \
|
||||
7.0189140975801991157*dt*(-0.049335189898860408029*dt*(2.0*dt + 1.0) + \
|
||||
0.29601113939316244817*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) - \
|
||||
0.12564355335492979587*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) + 0.22431393315265061193*dt + 1.0) + \
|
||||
0.94010945196161777522*dt*(-0.23637909581542530626*dt*(2.0*dt + 1.0) - \
|
||||
0.74817562366625959291*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.88085458023927036857*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) + \
|
||||
2.1165151389911680013*dt*(-0.049335189898860408029*dt*(2.0*dt + 1.0) + \
|
||||
0.29601113939316244817*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) - \
|
||||
0.12564355335492979587*dt*(0.074074074074074074074*dt*(2.0*dt + 1.0) + \
|
||||
0.2962962962962962963*dt*(0.125*dt*(2.0*dt + 1.0) + 0.875*dt + 1.0) + \
|
||||
0.96296296296296296296*dt + 1.0) + 0.22431393315265061193*dt + 1.0) - \
|
||||
0.35816132904077632692*dt + 1.0) + 5.5065024887242400038*dt + 1.0)/20 + dt/20 + 1
|
||||
|
||||
assert N(expr.series(dt, 0, 8), 20) == (
|
||||
- Float('0.00092592592592592596126289', precision=70) * dt**7
|
||||
+ Float('0.0027777777777777783174695', precision=70) * dt**6
|
||||
+ Float('0.016666666666666656027029', precision=70) * dt**5
|
||||
+ Float('0.083333333333333300951828', precision=70) * dt**4
|
||||
+ Float('0.33333333333333337034077', precision=70) * dt**3
|
||||
+ Float('1.0', precision=70) * dt**2
|
||||
+ Float('1.0', precision=70) * dt
|
||||
+ Float('1.0', precision=70)
|
||||
)
|
||||
|
||||
|
||||
def test_issue_11407():
|
||||
a, b, c, x = symbols('a b c x')
|
||||
assert series(sqrt(a + b + c*x), x, 0, 1) == sqrt(a + b) + O(x)
|
||||
assert series(sqrt(a + b + c + c*x), x, 0, 1) == sqrt(a + b + c) + O(x)
|
||||
|
||||
|
||||
def test_issue_14037():
|
||||
assert (sin(x**50)/x**51).series(x, n=0) == 1/x + O(1, x)
|
||||
|
||||
|
||||
def test_issue_20551():
|
||||
expr = (exp(x)/x).series(x, n=None)
|
||||
terms = [ next(expr) for i in range(3) ]
|
||||
assert terms == [1/x, 1, x/2]
|
||||
|
||||
|
||||
def test_issue_20697():
|
||||
p_0, p_1, p_2, p_3, b_0, b_1, b_2 = symbols('p_0 p_1 p_2 p_3 b_0 b_1 b_2')
|
||||
Q = (p_0 + (p_1 + (p_2 + p_3/y)/y)/y)/(1 + ((p_3/(b_0*y) + (b_0*p_2\
|
||||
- b_1*p_3)/b_0**2)/y + (b_0**2*p_1 - b_0*b_1*p_2 - p_3*(b_0*b_2\
|
||||
- b_1**2))/b_0**3)/y)
|
||||
assert Q.series(y, n=3).ratsimp() == b_2*y**2 + b_1*y + b_0 + O(y**3)
|
||||
|
||||
|
||||
def test_issue_21245():
|
||||
fi = (1 + sqrt(5))/2
|
||||
assert (1/(1 - x - x**2)).series(x, 1/fi, 1).factor() == \
|
||||
(-37*sqrt(5) - 83 + 13*sqrt(5)*x + 29*x + O((x - 2/(1 + sqrt(5)))**2, (x\
|
||||
, 2/(1 + sqrt(5)))))/((2*sqrt(5) + 5)**2*(x + sqrt(5)*x - 2))
|
||||
|
||||
|
||||
|
||||
def test_issue_21938():
|
||||
expr = sin(1/x + exp(-x)) - sin(1/x)
|
||||
assert expr.series(x, oo) == (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x)
|
||||
|
||||
|
||||
def test_issue_23432():
|
||||
expr = 1/sqrt(1 - x**2)
|
||||
result = expr.series(x, 0.5)
|
||||
assert result.is_Add and len(result.args) == 7
|
||||
|
||||
|
||||
def test_issue_23727():
|
||||
res = series(sqrt(1 - x**2), x, 0.1)
|
||||
assert res.is_Add == True
|
||||
|
||||
|
||||
def test_issue_24266():
|
||||
#type1: exp(f(x))
|
||||
assert (exp(-I*pi*(2*x+1))).series(x, 0, 3) == -1 + 2*I*pi*x + 2*pi**2*x**2 + O(x**3)
|
||||
assert (exp(-I*pi*(2*x+1))*gamma(1+x)).series(x, 0, 3) == -1 + x*(EulerGamma + 2*I*pi) + \
|
||||
x**2*(-EulerGamma**2/2 + 23*pi**2/12 - 2*EulerGamma*I*pi) + O(x**3)
|
||||
|
||||
#type2: c**f(x)
|
||||
assert ((2*I)**(-I*pi*(2*x+1))).series(x, 0, 2) == exp(pi**2/2 - I*pi*log(2)) + \
|
||||
x*(pi**2*exp(pi**2/2 - I*pi*log(2)) - 2*I*pi*exp(pi**2/2 - I*pi*log(2))*log(2)) + O(x**2)
|
||||
assert ((2)**(-I*pi*(2*x+1))).series(x, 0, 2) == exp(-I*pi*log(2)) - 2*I*pi*x*exp(-I*pi*log(2))*log(2) + O(x**2)
|
||||
|
||||
#type3: f(y)**g(x)
|
||||
assert ((y)**(I*pi*(2*x+1))).series(x, 0, 2) == exp(I*pi*log(y)) + 2*I*pi*x*exp(I*pi*log(y))*log(y) + O(x**2)
|
||||
assert ((I*y)**(I*pi*(2*x+1))).series(x, 0, 2) == exp(I*pi*log(I*y)) + 2*I*pi*x*exp(I*pi*log(I*y))*log(I*y) + O(x**2)
|
||||
|
||||
|
||||
def test_issue_26856():
|
||||
raises(ValueError, lambda: (2**x).series(x, oo, -1))
|
||||
Reference in New Issue
Block a user