chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
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'
|
||||
]
|
||||
@@ -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)
|
||||
@@ -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)]
|
||||
@@ -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