AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
"""A functions module, includes all the standard functions.
|
||||
|
||||
Combinatorial - factorial, fibonacci, harmonic, bernoulli...
|
||||
Elementary - hyperbolic, trigonometric, exponential, floor and ceiling, sqrt...
|
||||
Special - gamma, zeta,spherical harmonics...
|
||||
"""
|
||||
|
||||
from sympy.functions.combinatorial.factorials import (factorial, factorial2,
|
||||
rf, ff, binomial, RisingFactorial, FallingFactorial, subfactorial)
|
||||
from sympy.functions.combinatorial.numbers import (carmichael, fibonacci, lucas, tribonacci,
|
||||
harmonic, bernoulli, bell, euler, catalan, genocchi, andre, partition, divisor_sigma,
|
||||
udivisor_sigma, legendre_symbol, jacobi_symbol, kronecker_symbol, mobius,
|
||||
primenu, primeomega, totient, reduced_totient, primepi, motzkin)
|
||||
from sympy.functions.elementary.miscellaneous import (sqrt, root, Min, Max,
|
||||
Id, real_root, cbrt, Rem)
|
||||
from sympy.functions.elementary.complexes import (re, im, sign, Abs,
|
||||
conjugate, arg, polar_lift, periodic_argument, unbranched_argument,
|
||||
principal_branch, transpose, adjoint, polarify, unpolarify)
|
||||
from sympy.functions.elementary.trigonometric import (sin, cos, tan,
|
||||
sec, csc, cot, sinc, asin, acos, atan, asec, acsc, acot, atan2)
|
||||
from sympy.functions.elementary.exponential import (exp_polar, exp, log,
|
||||
LambertW)
|
||||
from sympy.functions.elementary.hyperbolic import (sinh, cosh, tanh, coth,
|
||||
sech, csch, asinh, acosh, atanh, acoth, asech, acsch)
|
||||
from sympy.functions.elementary.integers import floor, ceiling, frac
|
||||
from sympy.functions.elementary.piecewise import (Piecewise, piecewise_fold,
|
||||
piecewise_exclusive)
|
||||
from sympy.functions.special.error_functions import (erf, erfc, erfi, erf2,
|
||||
erfinv, erfcinv, erf2inv, Ei, expint, E1, li, Li, Si, Ci, Shi, Chi,
|
||||
fresnels, fresnelc)
|
||||
from sympy.functions.special.gamma_functions import (gamma, lowergamma,
|
||||
uppergamma, polygamma, loggamma, digamma, trigamma, multigamma)
|
||||
from sympy.functions.special.zeta_functions import (dirichlet_eta, zeta,
|
||||
lerchphi, polylog, stieltjes, riemann_xi)
|
||||
from sympy.functions.special.tensor_functions import (Eijk, LeviCivita,
|
||||
KroneckerDelta)
|
||||
from sympy.functions.special.singularity_functions import SingularityFunction
|
||||
from sympy.functions.special.delta_functions import DiracDelta, Heaviside
|
||||
from sympy.functions.special.bsplines import bspline_basis, bspline_basis_set, interpolating_spline
|
||||
from sympy.functions.special.bessel import (besselj, bessely, besseli, besselk,
|
||||
hankel1, hankel2, jn, yn, jn_zeros, hn1, hn2, airyai, airybi, airyaiprime, airybiprime, marcumq)
|
||||
from sympy.functions.special.hyper import hyper, meijerg, appellf1
|
||||
from sympy.functions.special.polynomials import (legendre, assoc_legendre,
|
||||
hermite, hermite_prob, chebyshevt, chebyshevu, chebyshevu_root,
|
||||
chebyshevt_root, laguerre, assoc_laguerre, gegenbauer, jacobi, jacobi_normalized)
|
||||
from sympy.functions.special.spherical_harmonics import Ynm, Ynm_c, Znm
|
||||
from sympy.functions.special.elliptic_integrals import (elliptic_k,
|
||||
elliptic_f, elliptic_e, elliptic_pi)
|
||||
from sympy.functions.special.beta_functions import beta, betainc, betainc_regularized
|
||||
from sympy.functions.special.mathieu_functions import (mathieus, mathieuc,
|
||||
mathieusprime, mathieucprime)
|
||||
ln = log
|
||||
|
||||
__all__ = [
|
||||
'factorial', 'factorial2', 'rf', 'ff', 'binomial', 'RisingFactorial',
|
||||
'FallingFactorial', 'subfactorial',
|
||||
|
||||
'carmichael', 'fibonacci', 'lucas', 'motzkin', 'tribonacci', 'harmonic',
|
||||
'bernoulli', 'bell', 'euler', 'catalan', 'genocchi', 'andre', 'partition',
|
||||
'divisor_sigma', 'udivisor_sigma', 'legendre_symbol', 'jacobi_symbol', 'kronecker_symbol',
|
||||
'mobius', 'primenu', 'primeomega', 'totient', 'reduced_totient', 'primepi',
|
||||
|
||||
'sqrt', 'root', 'Min', 'Max', 'Id', 'real_root', 'cbrt', 'Rem',
|
||||
|
||||
're', 'im', 'sign', 'Abs', 'conjugate', 'arg', 'polar_lift',
|
||||
'periodic_argument', 'unbranched_argument', 'principal_branch',
|
||||
'transpose', 'adjoint', 'polarify', 'unpolarify',
|
||||
|
||||
'sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'sinc', 'asin', 'acos', 'atan',
|
||||
'asec', 'acsc', 'acot', 'atan2',
|
||||
|
||||
'exp_polar', 'exp', 'ln', 'log', 'LambertW',
|
||||
|
||||
'sinh', 'cosh', 'tanh', 'coth', 'sech', 'csch', 'asinh', 'acosh', 'atanh',
|
||||
'acoth', 'asech', 'acsch',
|
||||
|
||||
'floor', 'ceiling', 'frac',
|
||||
|
||||
'Piecewise', 'piecewise_fold', 'piecewise_exclusive',
|
||||
|
||||
'erf', 'erfc', 'erfi', 'erf2', 'erfinv', 'erfcinv', 'erf2inv', 'Ei',
|
||||
'expint', 'E1', 'li', 'Li', 'Si', 'Ci', 'Shi', 'Chi', 'fresnels',
|
||||
'fresnelc',
|
||||
|
||||
'gamma', 'lowergamma', 'uppergamma', 'polygamma', 'loggamma', 'digamma',
|
||||
'trigamma', 'multigamma',
|
||||
|
||||
'dirichlet_eta', 'zeta', 'lerchphi', 'polylog', 'stieltjes', 'riemann_xi',
|
||||
|
||||
'Eijk', 'LeviCivita', 'KroneckerDelta',
|
||||
|
||||
'SingularityFunction',
|
||||
|
||||
'DiracDelta', 'Heaviside',
|
||||
|
||||
'bspline_basis', 'bspline_basis_set', 'interpolating_spline',
|
||||
|
||||
'besselj', 'bessely', 'besseli', 'besselk', 'hankel1', 'hankel2', 'jn',
|
||||
'yn', 'jn_zeros', 'hn1', 'hn2', 'airyai', 'airybi', 'airyaiprime',
|
||||
'airybiprime', 'marcumq',
|
||||
|
||||
'hyper', 'meijerg', 'appellf1',
|
||||
|
||||
'legendre', 'assoc_legendre', 'hermite', 'hermite_prob', 'chebyshevt',
|
||||
'chebyshevu', 'chebyshevu_root', 'chebyshevt_root', 'laguerre',
|
||||
'assoc_laguerre', 'gegenbauer', 'jacobi', 'jacobi_normalized',
|
||||
|
||||
'Ynm', 'Ynm_c', 'Znm',
|
||||
|
||||
'elliptic_k', 'elliptic_f', 'elliptic_e', 'elliptic_pi',
|
||||
|
||||
'beta', 'betainc', 'betainc_regularized',
|
||||
|
||||
'mathieus', 'mathieuc', 'mathieusprime', 'mathieucprime',
|
||||
]
|
||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
# Stub __init__.py for sympy.functions.combinatorial
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+1133
File diff suppressed because it is too large
Load Diff
+3196
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+653
@@ -0,0 +1,653 @@
|
||||
from sympy.concrete.products import Product
|
||||
from sympy.core.function import expand_func
|
||||
from sympy.core.mod import Mod
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core import EulerGamma
|
||||
from sympy.core.numbers import (Float, I, Rational, nan, oo, pi, zoo)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import (ff, rf, binomial, factorial, factorial2)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.gamma_functions import (gamma, polygamma)
|
||||
from sympy.polys.polytools import Poly
|
||||
from sympy.series.order import O
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.core.expr import unchanged
|
||||
from sympy.core.function import ArgumentIndexError
|
||||
from sympy.functions.combinatorial.factorials import subfactorial
|
||||
from sympy.functions.special.gamma_functions import uppergamma
|
||||
from sympy.testing.pytest import XFAIL, raises, slow
|
||||
|
||||
#Solves and Fixes Issue #10388 - This is the updated test for the same solved issue
|
||||
|
||||
def test_rf_eval_apply():
|
||||
x, y = symbols('x,y')
|
||||
n, k = symbols('n k', integer=True)
|
||||
m = Symbol('m', integer=True, nonnegative=True)
|
||||
|
||||
assert rf(nan, y) is nan
|
||||
assert rf(x, nan) is nan
|
||||
|
||||
assert unchanged(rf, x, y)
|
||||
|
||||
assert rf(oo, 0) == 1
|
||||
assert rf(-oo, 0) == 1
|
||||
|
||||
assert rf(oo, 6) is oo
|
||||
assert rf(-oo, 7) is -oo
|
||||
assert rf(-oo, 6) is oo
|
||||
|
||||
assert rf(oo, -6) is oo
|
||||
assert rf(-oo, -7) is oo
|
||||
|
||||
assert rf(-1, pi) == 0
|
||||
assert rf(-5, 1 + I) == 0
|
||||
|
||||
assert unchanged(rf, -3, k)
|
||||
assert unchanged(rf, x, Symbol('k', integer=False))
|
||||
assert rf(-3, Symbol('k', integer=False)) == 0
|
||||
assert rf(Symbol('x', negative=True, integer=True), Symbol('k', integer=False)) == 0
|
||||
|
||||
assert rf(x, 0) == 1
|
||||
assert rf(x, 1) == x
|
||||
assert rf(x, 2) == x*(x + 1)
|
||||
assert rf(x, 3) == x*(x + 1)*(x + 2)
|
||||
assert rf(x, 5) == x*(x + 1)*(x + 2)*(x + 3)*(x + 4)
|
||||
|
||||
assert rf(x, -1) == 1/(x - 1)
|
||||
assert rf(x, -2) == 1/((x - 1)*(x - 2))
|
||||
assert rf(x, -3) == 1/((x - 1)*(x - 2)*(x - 3))
|
||||
|
||||
assert rf(1, 100) == factorial(100)
|
||||
|
||||
assert rf(x**2 + 3*x, 2) == (x**2 + 3*x)*(x**2 + 3*x + 1)
|
||||
assert isinstance(rf(x**2 + 3*x, 2), Mul)
|
||||
assert rf(x**3 + x, -2) == 1/((x**3 + x - 1)*(x**3 + x - 2))
|
||||
|
||||
assert rf(Poly(x**2 + 3*x, x), 2) == Poly(x**4 + 8*x**3 + 19*x**2 + 12*x, x)
|
||||
assert isinstance(rf(Poly(x**2 + 3*x, x), 2), Poly)
|
||||
raises(ValueError, lambda: rf(Poly(x**2 + 3*x, x, y), 2))
|
||||
assert rf(Poly(x**3 + x, x), -2) == 1/(x**6 - 9*x**5 + 35*x**4 - 75*x**3 + 94*x**2 - 66*x + 20)
|
||||
raises(ValueError, lambda: rf(Poly(x**3 + x, x, y), -2))
|
||||
|
||||
assert rf(x, m).is_integer is None
|
||||
assert rf(n, k).is_integer is None
|
||||
assert rf(n, m).is_integer is True
|
||||
assert rf(n, k + pi).is_integer is False
|
||||
assert rf(n, m + pi).is_integer is False
|
||||
assert rf(pi, m).is_integer is False
|
||||
|
||||
def check(x, k, o, n):
|
||||
a, b = Dummy(), Dummy()
|
||||
r = lambda x, k: o(a, b).rewrite(n).subs({a:x,b:k})
|
||||
for i in range(-5,5):
|
||||
for j in range(-5,5):
|
||||
assert o(i, j) == r(i, j), (o, n, i, j)
|
||||
check(x, k, rf, ff)
|
||||
check(x, k, rf, binomial)
|
||||
check(n, k, rf, factorial)
|
||||
check(x, y, rf, factorial)
|
||||
check(x, y, rf, binomial)
|
||||
|
||||
assert rf(x, k).rewrite(ff) == ff(x + k - 1, k)
|
||||
assert rf(x, k).rewrite(gamma) == Piecewise(
|
||||
(gamma(k + x)/gamma(x), x > 0),
|
||||
((-1)**k*gamma(1 - x)/gamma(-k - x + 1), True))
|
||||
assert rf(5, k).rewrite(gamma) == gamma(k + 5)/24
|
||||
assert rf(x, k).rewrite(binomial) == factorial(k)*binomial(x + k - 1, k)
|
||||
assert rf(n, k).rewrite(factorial) == Piecewise(
|
||||
(factorial(k + n - 1)/factorial(n - 1), n > 0),
|
||||
((-1)**k*factorial(-n)/factorial(-k - n), True))
|
||||
assert rf(5, k).rewrite(factorial) == factorial(k + 4)/24
|
||||
assert rf(x, y).rewrite(factorial) == rf(x, y)
|
||||
assert rf(x, y).rewrite(binomial) == rf(x, y)
|
||||
|
||||
import random
|
||||
from mpmath import rf as mpmath_rf
|
||||
for i in range(100):
|
||||
x = -500 + 500 * random.random()
|
||||
k = -500 + 500 * random.random()
|
||||
assert (abs(mpmath_rf(x, k) - rf(x, k)) < 10**(-15))
|
||||
|
||||
|
||||
def test_ff_eval_apply():
|
||||
x, y = symbols('x,y')
|
||||
n, k = symbols('n k', integer=True)
|
||||
m = Symbol('m', integer=True, nonnegative=True)
|
||||
|
||||
assert ff(nan, y) is nan
|
||||
assert ff(x, nan) is nan
|
||||
|
||||
assert unchanged(ff, x, y)
|
||||
|
||||
assert ff(oo, 0) == 1
|
||||
assert ff(-oo, 0) == 1
|
||||
|
||||
assert ff(oo, 6) is oo
|
||||
assert ff(-oo, 7) is -oo
|
||||
assert ff(-oo, 6) is oo
|
||||
|
||||
assert ff(oo, -6) is oo
|
||||
assert ff(-oo, -7) is oo
|
||||
|
||||
assert ff(x, 0) == 1
|
||||
assert ff(x, 1) == x
|
||||
assert ff(x, 2) == x*(x - 1)
|
||||
assert ff(x, 3) == x*(x - 1)*(x - 2)
|
||||
assert ff(x, 5) == x*(x - 1)*(x - 2)*(x - 3)*(x - 4)
|
||||
|
||||
assert ff(x, -1) == 1/(x + 1)
|
||||
assert ff(x, -2) == 1/((x + 1)*(x + 2))
|
||||
assert ff(x, -3) == 1/((x + 1)*(x + 2)*(x + 3))
|
||||
|
||||
assert ff(100, 100) == factorial(100)
|
||||
|
||||
assert ff(2*x**2 - 5*x, 2) == (2*x**2 - 5*x)*(2*x**2 - 5*x - 1)
|
||||
assert isinstance(ff(2*x**2 - 5*x, 2), Mul)
|
||||
assert ff(x**2 + 3*x, -2) == 1/((x**2 + 3*x + 1)*(x**2 + 3*x + 2))
|
||||
|
||||
assert ff(Poly(2*x**2 - 5*x, x), 2) == Poly(4*x**4 - 28*x**3 + 59*x**2 - 35*x, x)
|
||||
assert isinstance(ff(Poly(2*x**2 - 5*x, x), 2), Poly)
|
||||
raises(ValueError, lambda: ff(Poly(2*x**2 - 5*x, x, y), 2))
|
||||
assert ff(Poly(x**2 + 3*x, x), -2) == 1/(x**4 + 12*x**3 + 49*x**2 + 78*x + 40)
|
||||
raises(ValueError, lambda: ff(Poly(x**2 + 3*x, x, y), -2))
|
||||
|
||||
|
||||
assert ff(x, m).is_integer is None
|
||||
assert ff(n, k).is_integer is None
|
||||
assert ff(n, m).is_integer is True
|
||||
assert ff(n, k + pi).is_integer is False
|
||||
assert ff(n, m + pi).is_integer is False
|
||||
assert ff(pi, m).is_integer is False
|
||||
|
||||
assert isinstance(ff(x, x), ff)
|
||||
assert ff(n, n) == factorial(n)
|
||||
|
||||
def check(x, k, o, n):
|
||||
a, b = Dummy(), Dummy()
|
||||
r = lambda x, k: o(a, b).rewrite(n).subs({a:x,b:k})
|
||||
for i in range(-5,5):
|
||||
for j in range(-5,5):
|
||||
assert o(i, j) == r(i, j), (o, n)
|
||||
check(x, k, ff, rf)
|
||||
check(x, k, ff, gamma)
|
||||
check(n, k, ff, factorial)
|
||||
check(x, k, ff, binomial)
|
||||
check(x, y, ff, factorial)
|
||||
check(x, y, ff, binomial)
|
||||
|
||||
assert ff(x, k).rewrite(rf) == rf(x - k + 1, k)
|
||||
assert ff(x, k).rewrite(gamma) == Piecewise(
|
||||
(gamma(x + 1)/gamma(-k + x + 1), x >= 0),
|
||||
((-1)**k*gamma(k - x)/gamma(-x), True))
|
||||
assert ff(5, k).rewrite(gamma) == 120/gamma(6 - k)
|
||||
assert ff(n, k).rewrite(factorial) == Piecewise(
|
||||
(factorial(n)/factorial(-k + n), n >= 0),
|
||||
((-1)**k*factorial(k - n - 1)/factorial(-n - 1), True))
|
||||
assert ff(5, k).rewrite(factorial) == 120/factorial(5 - k)
|
||||
assert ff(x, k).rewrite(binomial) == factorial(k) * binomial(x, k)
|
||||
assert ff(x, y).rewrite(factorial) == ff(x, y)
|
||||
assert ff(x, y).rewrite(binomial) == ff(x, y)
|
||||
|
||||
import random
|
||||
from mpmath import ff as mpmath_ff
|
||||
for i in range(100):
|
||||
x = -500 + 500 * random.random()
|
||||
k = -500 + 500 * random.random()
|
||||
a = mpmath_ff(x, k)
|
||||
b = ff(x, k)
|
||||
assert (abs(a - b) < abs(a) * 10**(-15))
|
||||
|
||||
|
||||
def test_rf_ff_eval_hiprec():
|
||||
maple = Float('6.9109401292234329956525265438452')
|
||||
us = ff(18, Rational(2, 3)).evalf(32)
|
||||
assert abs(us - maple)/us < 1e-31
|
||||
|
||||
maple = Float('6.8261540131125511557924466355367')
|
||||
us = rf(18, Rational(2, 3)).evalf(32)
|
||||
assert abs(us - maple)/us < 1e-31
|
||||
|
||||
maple = Float('34.007346127440197150854651814225')
|
||||
us = rf(Float('4.4', 32), Float('2.2', 32))
|
||||
assert abs(us - maple)/us < 1e-31
|
||||
|
||||
|
||||
def test_rf_lambdify_mpmath():
|
||||
from sympy.utilities.lambdify import lambdify
|
||||
x, y = symbols('x,y')
|
||||
f = lambdify((x,y), rf(x, y), 'mpmath')
|
||||
maple = Float('34.007346127440197')
|
||||
us = f(4.4, 2.2)
|
||||
assert abs(us - maple)/us < 1e-15
|
||||
|
||||
|
||||
def test_factorial():
|
||||
x = Symbol('x')
|
||||
n = Symbol('n', integer=True)
|
||||
k = Symbol('k', integer=True, nonnegative=True)
|
||||
r = Symbol('r', integer=False)
|
||||
s = Symbol('s', integer=False, negative=True)
|
||||
t = Symbol('t', nonnegative=True)
|
||||
u = Symbol('u', noninteger=True)
|
||||
|
||||
assert factorial(-2) is zoo
|
||||
assert factorial(0) == 1
|
||||
assert factorial(7) == 5040
|
||||
assert factorial(19) == 121645100408832000
|
||||
assert factorial(31) == 8222838654177922817725562880000000
|
||||
assert factorial(n).func == factorial
|
||||
assert factorial(2*n).func == factorial
|
||||
|
||||
assert factorial(x).is_integer is None
|
||||
assert factorial(n).is_integer is None
|
||||
assert factorial(k).is_integer
|
||||
assert factorial(r).is_integer is None
|
||||
|
||||
assert factorial(n).is_positive is None
|
||||
assert factorial(k).is_positive
|
||||
|
||||
assert factorial(x).is_real is None
|
||||
assert factorial(n).is_real is None
|
||||
assert factorial(k).is_real is True
|
||||
assert factorial(r).is_real is None
|
||||
assert factorial(s).is_real is True
|
||||
assert factorial(t).is_real is True
|
||||
assert factorial(u).is_real is True
|
||||
|
||||
assert factorial(x).is_composite is None
|
||||
assert factorial(n).is_composite is None
|
||||
assert factorial(k).is_composite is None
|
||||
assert factorial(k + 3).is_composite is True
|
||||
assert factorial(r).is_composite is None
|
||||
assert factorial(s).is_composite is None
|
||||
assert factorial(t).is_composite is None
|
||||
assert factorial(u).is_composite is None
|
||||
|
||||
assert factorial(oo) is oo
|
||||
|
||||
|
||||
def test_factorial_Mod():
|
||||
pr = Symbol('pr', prime=True)
|
||||
p, q = 10**9 + 9, 10**9 + 33 # prime modulo
|
||||
r, s = 10**7 + 5, 33333333 # composite modulo
|
||||
assert Mod(factorial(pr - 1), pr) == pr - 1
|
||||
assert Mod(factorial(pr - 1), -pr) == -1
|
||||
assert Mod(factorial(r - 1, evaluate=False), r) == 0
|
||||
assert Mod(factorial(s - 1, evaluate=False), s) == 0
|
||||
assert Mod(factorial(p - 1, evaluate=False), p) == p - 1
|
||||
assert Mod(factorial(q - 1, evaluate=False), q) == q - 1
|
||||
assert Mod(factorial(p - 50, evaluate=False), p) == 854928834
|
||||
assert Mod(factorial(q - 1800, evaluate=False), q) == 905504050
|
||||
assert Mod(factorial(153, evaluate=False), r) == Mod(factorial(153), r)
|
||||
assert Mod(factorial(255, evaluate=False), s) == Mod(factorial(255), s)
|
||||
assert Mod(factorial(4, evaluate=False), 3) == S.Zero
|
||||
assert Mod(factorial(5, evaluate=False), 6) == S.Zero
|
||||
|
||||
|
||||
def test_factorial_diff():
|
||||
n = Symbol('n', integer=True)
|
||||
|
||||
assert factorial(n).diff(n) == \
|
||||
gamma(1 + n)*polygamma(0, 1 + n)
|
||||
assert factorial(n**2).diff(n) == \
|
||||
2*n*gamma(1 + n**2)*polygamma(0, 1 + n**2)
|
||||
raises(ArgumentIndexError, lambda: factorial(n**2).fdiff(2))
|
||||
|
||||
|
||||
def test_factorial_series():
|
||||
n = Symbol('n', integer=True)
|
||||
|
||||
assert factorial(n).series(n, 0, 3) == \
|
||||
1 - n*EulerGamma + n**2*(EulerGamma**2/2 + pi**2/12) + O(n**3)
|
||||
|
||||
|
||||
def test_factorial_rewrite():
|
||||
n = Symbol('n', integer=True)
|
||||
k = Symbol('k', integer=True, nonnegative=True)
|
||||
|
||||
assert factorial(n).rewrite(gamma) == gamma(n + 1)
|
||||
_i = Dummy('i')
|
||||
assert factorial(k).rewrite(Product).dummy_eq(Product(_i, (_i, 1, k)))
|
||||
assert factorial(n).rewrite(Product) == factorial(n)
|
||||
|
||||
|
||||
def test_factorial2():
|
||||
n = Symbol('n', integer=True)
|
||||
|
||||
assert factorial2(-1) == 1
|
||||
assert factorial2(0) == 1
|
||||
assert factorial2(7) == 105
|
||||
assert factorial2(8) == 384
|
||||
|
||||
# The following is exhaustive
|
||||
tt = Symbol('tt', integer=True, nonnegative=True)
|
||||
tte = Symbol('tte', even=True, nonnegative=True)
|
||||
tpe = Symbol('tpe', even=True, positive=True)
|
||||
tto = Symbol('tto', odd=True, nonnegative=True)
|
||||
tf = Symbol('tf', integer=True, nonnegative=False)
|
||||
tfe = Symbol('tfe', even=True, nonnegative=False)
|
||||
tfo = Symbol('tfo', odd=True, nonnegative=False)
|
||||
ft = Symbol('ft', integer=False, nonnegative=True)
|
||||
ff = Symbol('ff', integer=False, nonnegative=False)
|
||||
fn = Symbol('fn', integer=False)
|
||||
nt = Symbol('nt', nonnegative=True)
|
||||
nf = Symbol('nf', nonnegative=False)
|
||||
nn = Symbol('nn')
|
||||
z = Symbol('z', zero=True)
|
||||
#Solves and Fixes Issue #10388 - This is the updated test for the same solved issue
|
||||
raises(ValueError, lambda: factorial2(oo))
|
||||
raises(ValueError, lambda: factorial2(Rational(5, 2)))
|
||||
raises(ValueError, lambda: factorial2(-4))
|
||||
assert factorial2(n).is_integer is None
|
||||
assert factorial2(tt - 1).is_integer
|
||||
assert factorial2(tte - 1).is_integer
|
||||
assert factorial2(tpe - 3).is_integer
|
||||
assert factorial2(tto - 4).is_integer
|
||||
assert factorial2(tto - 2).is_integer
|
||||
assert factorial2(tf).is_integer is None
|
||||
assert factorial2(tfe).is_integer is None
|
||||
assert factorial2(tfo).is_integer is None
|
||||
assert factorial2(ft).is_integer is None
|
||||
assert factorial2(ff).is_integer is None
|
||||
assert factorial2(fn).is_integer is None
|
||||
assert factorial2(nt).is_integer is None
|
||||
assert factorial2(nf).is_integer is None
|
||||
assert factorial2(nn).is_integer is None
|
||||
|
||||
assert factorial2(n).is_positive is None
|
||||
assert factorial2(tt - 1).is_positive is True
|
||||
assert factorial2(tte - 1).is_positive is True
|
||||
assert factorial2(tpe - 3).is_positive is True
|
||||
assert factorial2(tpe - 1).is_positive is True
|
||||
assert factorial2(tto - 2).is_positive is True
|
||||
assert factorial2(tto - 1).is_positive is True
|
||||
assert factorial2(tf).is_positive is None
|
||||
assert factorial2(tfe).is_positive is None
|
||||
assert factorial2(tfo).is_positive is None
|
||||
assert factorial2(ft).is_positive is None
|
||||
assert factorial2(ff).is_positive is None
|
||||
assert factorial2(fn).is_positive is None
|
||||
assert factorial2(nt).is_positive is None
|
||||
assert factorial2(nf).is_positive is None
|
||||
assert factorial2(nn).is_positive is None
|
||||
|
||||
assert factorial2(tt).is_even is None
|
||||
assert factorial2(tt).is_odd is None
|
||||
assert factorial2(tte).is_even is None
|
||||
assert factorial2(tte).is_odd is None
|
||||
assert factorial2(tte + 2).is_even is True
|
||||
assert factorial2(tpe).is_even is True
|
||||
assert factorial2(tpe).is_odd is False
|
||||
assert factorial2(tto).is_odd is True
|
||||
assert factorial2(tf).is_even is None
|
||||
assert factorial2(tf).is_odd is None
|
||||
assert factorial2(tfe).is_even is None
|
||||
assert factorial2(tfe).is_odd is None
|
||||
assert factorial2(tfo).is_even is False
|
||||
assert factorial2(tfo).is_odd is None
|
||||
assert factorial2(z).is_even is False
|
||||
assert factorial2(z).is_odd is True
|
||||
|
||||
|
||||
def test_factorial2_rewrite():
|
||||
n = Symbol('n', integer=True)
|
||||
assert factorial2(n).rewrite(gamma) == \
|
||||
2**(n/2)*Piecewise((1, Eq(Mod(n, 2), 0)), (sqrt(2)/sqrt(pi), Eq(Mod(n, 2), 1)))*gamma(n/2 + 1)
|
||||
assert factorial2(2*n).rewrite(gamma) == 2**n*gamma(n + 1)
|
||||
assert factorial2(2*n + 1).rewrite(gamma) == \
|
||||
sqrt(2)*2**(n + S.Half)*gamma(n + Rational(3, 2))/sqrt(pi)
|
||||
|
||||
|
||||
def test_binomial():
|
||||
x = Symbol('x')
|
||||
n = Symbol('n', integer=True)
|
||||
nz = Symbol('nz', integer=True, nonzero=True)
|
||||
k = Symbol('k', integer=True)
|
||||
kp = Symbol('kp', integer=True, positive=True)
|
||||
kn = Symbol('kn', integer=True, negative=True)
|
||||
u = Symbol('u', negative=True)
|
||||
v = Symbol('v', nonnegative=True)
|
||||
p = Symbol('p', positive=True)
|
||||
z = Symbol('z', zero=True)
|
||||
nt = Symbol('nt', integer=False)
|
||||
kt = Symbol('kt', integer=False)
|
||||
a = Symbol('a', integer=True, nonnegative=True)
|
||||
b = Symbol('b', integer=True, nonnegative=True)
|
||||
|
||||
assert binomial(0, 0) == 1
|
||||
assert binomial(1, 1) == 1
|
||||
assert binomial(10, 10) == 1
|
||||
assert binomial(n, z) == 1
|
||||
assert binomial(1, 2) == 0
|
||||
assert binomial(-1, 2) == 1
|
||||
assert binomial(1, -1) == 0
|
||||
assert binomial(-1, 1) == -1
|
||||
assert binomial(-1, -1) == 0
|
||||
assert binomial(S.Half, S.Half) == 1
|
||||
assert binomial(-10, 1) == -10
|
||||
assert binomial(-10, 7) == -11440
|
||||
assert binomial(n, -1) == 0 # holds for all integers (negative, zero, positive)
|
||||
assert binomial(kp, -1) == 0
|
||||
assert binomial(nz, 0) == 1
|
||||
assert expand_func(binomial(n, 1)) == n
|
||||
assert expand_func(binomial(n, 2)) == n*(n - 1)/2
|
||||
assert expand_func(binomial(n, n - 2)) == n*(n - 1)/2
|
||||
assert expand_func(binomial(n, n - 1)) == n
|
||||
assert binomial(n, 3).func == binomial
|
||||
assert binomial(n, 3).expand(func=True) == n**3/6 - n**2/2 + n/3
|
||||
assert expand_func(binomial(n, 3)) == n*(n - 2)*(n - 1)/6
|
||||
assert binomial(n, n).func == binomial # e.g. (-1, -1) == 0, (2, 2) == 1
|
||||
assert binomial(n, n + 1).func == binomial # e.g. (-1, 0) == 1
|
||||
assert binomial(kp, kp + 1) == 0
|
||||
assert binomial(kn, kn) == 0 # issue #14529
|
||||
assert binomial(n, u).func == binomial
|
||||
assert binomial(kp, u).func == binomial
|
||||
assert binomial(n, p).func == binomial
|
||||
assert binomial(n, k).func == binomial
|
||||
assert binomial(n, n + p).func == binomial
|
||||
assert binomial(kp, kp + p).func == binomial
|
||||
|
||||
assert expand_func(binomial(n, n - 3)) == n*(n - 2)*(n - 1)/6
|
||||
|
||||
assert binomial(n, k).is_integer
|
||||
assert binomial(nt, k).is_integer is None
|
||||
assert binomial(x, nt).is_integer is False
|
||||
|
||||
assert binomial(gamma(25), 6) == 79232165267303928292058750056084441948572511312165380965440075720159859792344339983120618959044048198214221915637090855535036339620413440000
|
||||
assert binomial(1324, 47) == 906266255662694632984994480774946083064699457235920708992926525848438478406790323869952
|
||||
assert binomial(1735, 43) == 190910140420204130794758005450919715396159959034348676124678207874195064798202216379800
|
||||
assert binomial(2512, 53) == 213894469313832631145798303740098720367984955243020898718979538096223399813295457822575338958939834177325304000
|
||||
assert binomial(3383, 52) == 27922807788818096863529701501764372757272890613101645521813434902890007725667814813832027795881839396839287659777235
|
||||
assert binomial(4321, 51) == 124595639629264868916081001263541480185227731958274383287107643816863897851139048158022599533438936036467601690983780576
|
||||
|
||||
assert binomial(a, b).is_nonnegative is True
|
||||
assert binomial(-1, 2, evaluate=False).is_nonnegative is True
|
||||
assert binomial(10, 5, evaluate=False).is_nonnegative is True
|
||||
assert binomial(10, -3, evaluate=False).is_nonnegative is True
|
||||
assert binomial(-10, -3, evaluate=False).is_nonnegative is True
|
||||
assert binomial(-10, 2, evaluate=False).is_nonnegative is True
|
||||
assert binomial(-10, 1, evaluate=False).is_nonnegative is False
|
||||
assert binomial(-10, 7, evaluate=False).is_nonnegative is False
|
||||
|
||||
# issue #14625
|
||||
for _ in (pi, -pi, nt, v, a):
|
||||
assert binomial(_, _) == 1
|
||||
assert binomial(_, _ - 1) == _
|
||||
assert isinstance(binomial(u, u), binomial)
|
||||
assert isinstance(binomial(u, u - 1), binomial)
|
||||
assert isinstance(binomial(x, x), binomial)
|
||||
assert isinstance(binomial(x, x - 1), binomial)
|
||||
|
||||
#issue #18802
|
||||
assert expand_func(binomial(x + 1, x)) == x + 1
|
||||
assert expand_func(binomial(x, x - 1)) == x
|
||||
assert expand_func(binomial(x + 1, x - 1)) == x*(x + 1)/2
|
||||
assert expand_func(binomial(x**2 + 1, x**2)) == x**2 + 1
|
||||
|
||||
# issue #13980 and #13981
|
||||
assert binomial(-7, -5) == 0
|
||||
assert binomial(-23, -12) == 0
|
||||
assert binomial(Rational(13, 2), -10) == 0
|
||||
assert binomial(-49, -51) == 0
|
||||
|
||||
assert binomial(19, Rational(-7, 2)) == S(-68719476736)/(911337863661225*pi)
|
||||
assert binomial(0, Rational(3, 2)) == S(-2)/(3*pi)
|
||||
assert binomial(-3, Rational(-7, 2)) is zoo
|
||||
assert binomial(kn, kt) is zoo
|
||||
|
||||
assert binomial(nt, kt).func == binomial
|
||||
assert binomial(nt, Rational(15, 6)) == 8*gamma(nt + 1)/(15*sqrt(pi)*gamma(nt - Rational(3, 2)))
|
||||
assert binomial(Rational(20, 3), Rational(-10, 8)) == gamma(Rational(23, 3))/(gamma(Rational(-1, 4))*gamma(Rational(107, 12)))
|
||||
assert binomial(Rational(19, 2), Rational(-7, 2)) == Rational(-1615, 8388608)
|
||||
assert binomial(Rational(-13, 5), Rational(-7, 8)) == gamma(Rational(-8, 5))/(gamma(Rational(-29, 40))*gamma(Rational(1, 8)))
|
||||
assert binomial(Rational(-19, 8), Rational(-13, 5)) == gamma(Rational(-11, 8))/(gamma(Rational(-8, 5))*gamma(Rational(49, 40)))
|
||||
|
||||
# binomial for complexes
|
||||
assert binomial(I, Rational(-89, 8)) == gamma(1 + I)/(gamma(Rational(-81, 8))*gamma(Rational(97, 8) + I))
|
||||
assert binomial(I, 2*I) == gamma(1 + I)/(gamma(1 - I)*gamma(1 + 2*I))
|
||||
assert binomial(-7, I) is zoo
|
||||
assert binomial(Rational(-7, 6), I) == gamma(Rational(-1, 6))/(gamma(Rational(-1, 6) - I)*gamma(1 + I))
|
||||
assert binomial((1+2*I), (1+3*I)) == gamma(2 + 2*I)/(gamma(1 - I)*gamma(2 + 3*I))
|
||||
assert binomial(I, 5) == Rational(1, 3) - I/S(12)
|
||||
assert binomial((2*I + 3), 7) == -13*I/S(63)
|
||||
assert isinstance(binomial(I, n), binomial)
|
||||
assert expand_func(binomial(3, 2, evaluate=False)) == 3
|
||||
assert expand_func(binomial(n, 0, evaluate=False)) == 1
|
||||
assert expand_func(binomial(n, -2, evaluate=False)) == 0
|
||||
assert expand_func(binomial(n, k)) == binomial(n, k)
|
||||
|
||||
|
||||
def test_binomial_Mod():
|
||||
p, q = 10**5 + 3, 10**9 + 33 # prime modulo
|
||||
r = 10**7 + 5 # composite modulo
|
||||
|
||||
# A few tests to get coverage
|
||||
# Lucas Theorem
|
||||
assert Mod(binomial(156675, 4433, evaluate=False), p) == Mod(binomial(156675, 4433), p)
|
||||
|
||||
# factorial Mod
|
||||
assert Mod(binomial(1234, 432, evaluate=False), q) == Mod(binomial(1234, 432), q)
|
||||
|
||||
# binomial factorize
|
||||
assert Mod(binomial(253, 113, evaluate=False), r) == Mod(binomial(253, 113), r)
|
||||
|
||||
# using Granville's generalisation of Lucas' Theorem
|
||||
assert Mod(binomial(10**18, 10**12, evaluate=False), p*p) == 3744312326
|
||||
|
||||
|
||||
@slow
|
||||
def test_binomial_Mod_slow():
|
||||
p, q = 10**5 + 3, 10**9 + 33 # prime modulo
|
||||
r, s = 10**7 + 5, 33333333 # composite modulo
|
||||
|
||||
n, k, m = symbols('n k m')
|
||||
assert (binomial(n, k) % q).subs({n: s, k: p}) == Mod(binomial(s, p), q)
|
||||
assert (binomial(n, k) % m).subs({n: 8, k: 5, m: 13}) == 4
|
||||
assert (binomial(9, k) % 7).subs(k, 2) == 1
|
||||
|
||||
# Lucas Theorem
|
||||
assert Mod(binomial(123456, 43253, evaluate=False), p) == Mod(binomial(123456, 43253), p)
|
||||
assert Mod(binomial(-178911, 237, evaluate=False), p) == Mod(-binomial(178911 + 237 - 1, 237), p)
|
||||
assert Mod(binomial(-178911, 238, evaluate=False), p) == Mod(binomial(178911 + 238 - 1, 238), p)
|
||||
|
||||
# factorial Mod
|
||||
assert Mod(binomial(9734, 451, evaluate=False), q) == Mod(binomial(9734, 451), q)
|
||||
assert Mod(binomial(-10733, 4459, evaluate=False), q) == Mod(binomial(-10733, 4459), q)
|
||||
assert Mod(binomial(-15733, 4458, evaluate=False), q) == Mod(binomial(-15733, 4458), q)
|
||||
assert Mod(binomial(23, -38, evaluate=False), q) is S.Zero
|
||||
assert Mod(binomial(23, 38, evaluate=False), q) is S.Zero
|
||||
|
||||
# binomial factorize
|
||||
assert Mod(binomial(753, 119, evaluate=False), r) == Mod(binomial(753, 119), r)
|
||||
assert Mod(binomial(3781, 948, evaluate=False), s) == Mod(binomial(3781, 948), s)
|
||||
assert Mod(binomial(25773, 1793, evaluate=False), s) == Mod(binomial(25773, 1793), s)
|
||||
assert Mod(binomial(-753, 118, evaluate=False), r) == Mod(binomial(-753, 118), r)
|
||||
assert Mod(binomial(-25773, 1793, evaluate=False), s) == Mod(binomial(-25773, 1793), s)
|
||||
|
||||
|
||||
def test_binomial_diff():
|
||||
n = Symbol('n', integer=True)
|
||||
k = Symbol('k', integer=True)
|
||||
|
||||
assert binomial(n, k).diff(n) == \
|
||||
(-polygamma(0, 1 + n - k) + polygamma(0, 1 + n))*binomial(n, k)
|
||||
assert binomial(n**2, k**3).diff(n) == \
|
||||
2*n*(-polygamma(
|
||||
0, 1 + n**2 - k**3) + polygamma(0, 1 + n**2))*binomial(n**2, k**3)
|
||||
|
||||
assert binomial(n, k).diff(k) == \
|
||||
(-polygamma(0, 1 + k) + polygamma(0, 1 + n - k))*binomial(n, k)
|
||||
assert binomial(n**2, k**3).diff(k) == \
|
||||
3*k**2*(-polygamma(
|
||||
0, 1 + k**3) + polygamma(0, 1 + n**2 - k**3))*binomial(n**2, k**3)
|
||||
raises(ArgumentIndexError, lambda: binomial(n, k).fdiff(3))
|
||||
|
||||
|
||||
def test_binomial_rewrite():
|
||||
n = Symbol('n', integer=True)
|
||||
k = Symbol('k', integer=True)
|
||||
x = Symbol('x')
|
||||
|
||||
assert binomial(n, k).rewrite(
|
||||
factorial) == factorial(n)/(factorial(k)*factorial(n - k))
|
||||
assert binomial(
|
||||
n, k).rewrite(gamma) == gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1))
|
||||
assert binomial(n, k).rewrite(ff) == ff(n, k) / factorial(k)
|
||||
assert binomial(n, x).rewrite(ff) == binomial(n, x)
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_factorial_simplify_fail():
|
||||
# simplify(factorial(x + 1).diff(x) - ((x + 1)*factorial(x)).diff(x))) == 0
|
||||
from sympy.abc import x
|
||||
assert simplify(x*polygamma(0, x + 1) - x*polygamma(0, x + 2) +
|
||||
polygamma(0, x + 1) - polygamma(0, x + 2) + 1) == 0
|
||||
|
||||
|
||||
def test_subfactorial():
|
||||
assert all(subfactorial(i) == ans for i, ans in enumerate(
|
||||
[1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496]))
|
||||
assert subfactorial(oo) is oo
|
||||
assert subfactorial(nan) is nan
|
||||
assert subfactorial(23) == 9510425471055777937262
|
||||
assert unchanged(subfactorial, 2.2)
|
||||
|
||||
x = Symbol('x')
|
||||
assert subfactorial(x).rewrite(uppergamma) == uppergamma(x + 1, -1)/S.Exp1
|
||||
|
||||
tt = Symbol('tt', integer=True, nonnegative=True)
|
||||
tf = Symbol('tf', integer=True, nonnegative=False)
|
||||
tn = Symbol('tf', integer=True)
|
||||
ft = Symbol('ft', integer=False, nonnegative=True)
|
||||
ff = Symbol('ff', integer=False, nonnegative=False)
|
||||
fn = Symbol('ff', integer=False)
|
||||
nt = Symbol('nt', nonnegative=True)
|
||||
nf = Symbol('nf', nonnegative=False)
|
||||
nn = Symbol('nf')
|
||||
te = Symbol('te', even=True, nonnegative=True)
|
||||
to = Symbol('to', odd=True, nonnegative=True)
|
||||
assert subfactorial(tt).is_integer
|
||||
assert subfactorial(tf).is_integer is None
|
||||
assert subfactorial(tn).is_integer is None
|
||||
assert subfactorial(ft).is_integer is None
|
||||
assert subfactorial(ff).is_integer is None
|
||||
assert subfactorial(fn).is_integer is None
|
||||
assert subfactorial(nt).is_integer is None
|
||||
assert subfactorial(nf).is_integer is None
|
||||
assert subfactorial(nn).is_integer is None
|
||||
assert subfactorial(tt).is_nonnegative
|
||||
assert subfactorial(tf).is_nonnegative is None
|
||||
assert subfactorial(tn).is_nonnegative is None
|
||||
assert subfactorial(ft).is_nonnegative is None
|
||||
assert subfactorial(ff).is_nonnegative is None
|
||||
assert subfactorial(fn).is_nonnegative is None
|
||||
assert subfactorial(nt).is_nonnegative is None
|
||||
assert subfactorial(nf).is_nonnegative is None
|
||||
assert subfactorial(nn).is_nonnegative is None
|
||||
assert subfactorial(tt).is_even is None
|
||||
assert subfactorial(tt).is_odd is None
|
||||
assert subfactorial(te).is_odd is True
|
||||
assert subfactorial(to).is_even is True
|
||||
+1250
File diff suppressed because it is too large
Load Diff
+1
@@ -0,0 +1 @@
|
||||
# Stub __init__.py for sympy.functions.elementary
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+261
@@ -0,0 +1,261 @@
|
||||
r"""A module for special angle formulas for trigonometric functions
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
This module should be developed in the future to contain direct square root
|
||||
representation of
|
||||
|
||||
.. math
|
||||
F(\frac{n}{m} \pi)
|
||||
|
||||
for every
|
||||
|
||||
- $m \in \{ 3, 5, 17, 257, 65537 \}$
|
||||
- $n \in \mathbb{N}$, $0 \le n < m$
|
||||
- $F \in \{\sin, \cos, \tan, \csc, \sec, \cot\}$
|
||||
|
||||
Without multi-step rewrites
|
||||
(e.g. $\tan \to \cos/\sin \to \cos/\sqrt \to \ sqrt$)
|
||||
or using chebyshev identities
|
||||
(e.g. $\cos \to \cos + \cos^2 + \cdots \to \sqrt{} + \sqrt{}^2 + \cdots $),
|
||||
which are trivial to implement in sympy,
|
||||
and had used to give overly complicated expressions.
|
||||
|
||||
The reference can be found below, if anyone may need help implementing them.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [*] Gottlieb, Christian. (1999). The Simple and straightforward construction
|
||||
of the regular 257-gon. The Mathematical Intelligencer. 21. 31-37.
|
||||
10.1007/BF03024829.
|
||||
.. [*] https://resources.wolframcloud.com/FunctionRepository/resources/Cos2PiOverFermatPrime
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Callable
|
||||
from functools import reduce
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.intfunc import igcdex
|
||||
from sympy.core.numbers import Integer
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.core.cache import cacheit
|
||||
|
||||
|
||||
def migcdex(*x: int) -> tuple[tuple[int, ...], int]:
|
||||
r"""Compute extended gcd for multiple integers.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Given the integers $x_1, \cdots, x_n$ and
|
||||
an extended gcd for multiple arguments are defined as a solution
|
||||
$(y_1, \cdots, y_n), g$ for the diophantine equation
|
||||
$x_1 y_1 + \cdots + x_n y_n = g$ such that
|
||||
$g = \gcd(x_1, \cdots, x_n)$.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.functions.elementary._trigonometric_special import migcdex
|
||||
>>> migcdex()
|
||||
((), 0)
|
||||
>>> migcdex(4)
|
||||
((1,), 4)
|
||||
>>> migcdex(4, 6)
|
||||
((-1, 1), 2)
|
||||
>>> migcdex(6, 10, 15)
|
||||
((1, 1, -1), 1)
|
||||
"""
|
||||
if not x:
|
||||
return (), 0
|
||||
|
||||
if len(x) == 1:
|
||||
return (1,), x[0]
|
||||
|
||||
if len(x) == 2:
|
||||
u, v, h = igcdex(x[0], x[1])
|
||||
return (u, v), h
|
||||
|
||||
y, g = migcdex(*x[1:])
|
||||
u, v, h = igcdex(x[0], g)
|
||||
return (u, *(v * i for i in y)), h
|
||||
|
||||
|
||||
def ipartfrac(*denoms: int) -> tuple[int, ...]:
|
||||
r"""Compute the partial fraction decomposition.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Given a rational number $\frac{1}{q_1 \cdots q_n}$ where all
|
||||
$q_1, \cdots, q_n$ are pairwise coprime,
|
||||
|
||||
A partial fraction decomposition is defined as
|
||||
|
||||
.. math::
|
||||
\frac{1}{q_1 \cdots q_n} = \frac{p_1}{q_1} + \cdots + \frac{p_n}{q_n}
|
||||
|
||||
And it can be derived from solving the following diophantine equation for
|
||||
the $p_1, \cdots, p_n$
|
||||
|
||||
.. math::
|
||||
1 = p_1 \prod_{i \ne 1}q_i + \cdots + p_n \prod_{i \ne n}q_i
|
||||
|
||||
Where $q_1, \cdots, q_n$ being pairwise coprime implies
|
||||
$\gcd(\prod_{i \ne 1}q_i, \cdots, \prod_{i \ne n}q_i) = 1$,
|
||||
which guarantees the existence of the solution.
|
||||
|
||||
It is sufficient to compute partial fraction decomposition only
|
||||
for numerator $1$ because partial fraction decomposition for any
|
||||
$\frac{n}{q_1 \cdots q_n}$ can be easily computed by multiplying
|
||||
the result by $n$ afterwards.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
denoms : int
|
||||
The pairwise coprime integer denominators $q_i$ which defines the
|
||||
rational number $\frac{1}{q_1 \cdots q_n}$
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
tuple[int, ...]
|
||||
The list of numerators which semantically corresponds to $p_i$ of the
|
||||
partial fraction decomposition
|
||||
$\frac{1}{q_1 \cdots q_n} = \frac{p_1}{q_1} + \cdots + \frac{p_n}{q_n}$
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Rational, Mul
|
||||
>>> from sympy.functions.elementary._trigonometric_special import ipartfrac
|
||||
|
||||
>>> denoms = 2, 3, 5
|
||||
>>> numers = ipartfrac(2, 3, 5)
|
||||
>>> numers
|
||||
(1, 7, -14)
|
||||
|
||||
>>> Rational(1, Mul(*denoms))
|
||||
1/30
|
||||
>>> out = 0
|
||||
>>> for n, d in zip(numers, denoms):
|
||||
... out += Rational(n, d)
|
||||
>>> out
|
||||
1/30
|
||||
"""
|
||||
if not denoms:
|
||||
return ()
|
||||
|
||||
def mul(x: int, y: int) -> int:
|
||||
return x * y
|
||||
|
||||
denom = reduce(mul, denoms)
|
||||
a = [denom // x for x in denoms]
|
||||
h, _ = migcdex(*a)
|
||||
return h
|
||||
|
||||
|
||||
def fermat_coords(n: int) -> list[int] | None:
|
||||
"""If n can be factored in terms of Fermat primes with
|
||||
multiplicity of each being 1, return those primes, else
|
||||
None
|
||||
"""
|
||||
primes = []
|
||||
for p in [3, 5, 17, 257, 65537]:
|
||||
quotient, remainder = divmod(n, p)
|
||||
if remainder == 0:
|
||||
n = quotient
|
||||
primes.append(p)
|
||||
if n == 1:
|
||||
return primes
|
||||
return None
|
||||
|
||||
|
||||
@cacheit
|
||||
def cos_3() -> Expr:
|
||||
r"""Computes $\cos \frac{\pi}{3}$ in square roots"""
|
||||
return S.Half
|
||||
|
||||
|
||||
@cacheit
|
||||
def cos_5() -> Expr:
|
||||
r"""Computes $\cos \frac{\pi}{5}$ in square roots"""
|
||||
return (sqrt(5) + 1) / 4
|
||||
|
||||
|
||||
@cacheit
|
||||
def cos_17() -> Expr:
|
||||
r"""Computes $\cos \frac{\pi}{17}$ in square roots"""
|
||||
return sqrt(
|
||||
(15 + sqrt(17)) / 32 + sqrt(2) * (sqrt(17 - sqrt(17)) +
|
||||
sqrt(sqrt(2) * (-8 * sqrt(17 + sqrt(17)) - (1 - sqrt(17))
|
||||
* sqrt(17 - sqrt(17))) + 6 * sqrt(17) + 34)) / 32)
|
||||
|
||||
|
||||
@cacheit
|
||||
def cos_257() -> Expr:
|
||||
r"""Computes $\cos \frac{\pi}{257}$ in square roots
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [*] https://math.stackexchange.com/questions/516142/how-does-cos2-pi-257-look-like-in-real-radicals
|
||||
.. [*] https://r-knott.surrey.ac.uk/Fibonacci/simpleTrig.html
|
||||
"""
|
||||
def f1(a: Expr, b: Expr) -> tuple[Expr, Expr]:
|
||||
return (a + sqrt(a**2 + b)) / 2, (a - sqrt(a**2 + b)) / 2
|
||||
|
||||
def f2(a: Expr, b: Expr) -> Expr:
|
||||
return (a - sqrt(a**2 + b))/2
|
||||
|
||||
t1, t2 = f1(S.NegativeOne, Integer(256))
|
||||
z1, z3 = f1(t1, Integer(64))
|
||||
z2, z4 = f1(t2, Integer(64))
|
||||
y1, y5 = f1(z1, 4*(5 + t1 + 2*z1))
|
||||
y6, y2 = f1(z2, 4*(5 + t2 + 2*z2))
|
||||
y3, y7 = f1(z3, 4*(5 + t1 + 2*z3))
|
||||
y8, y4 = f1(z4, 4*(5 + t2 + 2*z4))
|
||||
x1, x9 = f1(y1, -4*(t1 + y1 + y3 + 2*y6))
|
||||
x2, x10 = f1(y2, -4*(t2 + y2 + y4 + 2*y7))
|
||||
x3, x11 = f1(y3, -4*(t1 + y3 + y5 + 2*y8))
|
||||
x4, x12 = f1(y4, -4*(t2 + y4 + y6 + 2*y1))
|
||||
x5, x13 = f1(y5, -4*(t1 + y5 + y7 + 2*y2))
|
||||
x6, x14 = f1(y6, -4*(t2 + y6 + y8 + 2*y3))
|
||||
x15, x7 = f1(y7, -4*(t1 + y7 + y1 + 2*y4))
|
||||
x8, x16 = f1(y8, -4*(t2 + y8 + y2 + 2*y5))
|
||||
v1 = f2(x1, -4*(x1 + x2 + x3 + x6))
|
||||
v2 = f2(x2, -4*(x2 + x3 + x4 + x7))
|
||||
v3 = f2(x8, -4*(x8 + x9 + x10 + x13))
|
||||
v4 = f2(x9, -4*(x9 + x10 + x11 + x14))
|
||||
v5 = f2(x10, -4*(x10 + x11 + x12 + x15))
|
||||
v6 = f2(x16, -4*(x16 + x1 + x2 + x5))
|
||||
u1 = -f2(-v1, -4*(v2 + v3))
|
||||
u2 = -f2(-v4, -4*(v5 + v6))
|
||||
w1 = -2*f2(-u1, -4*u2)
|
||||
return sqrt(sqrt(2)*sqrt(w1 + 4)/8 + S.Half)
|
||||
|
||||
|
||||
def cos_table() -> dict[int, Callable[[], Expr]]:
|
||||
r"""Lazily evaluated table for $\cos \frac{\pi}{n}$ in square roots for
|
||||
$n \in \{3, 5, 17, 257, 65537\}$.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
65537 is the only other known Fermat prime and it is nearly impossible to
|
||||
build in the current SymPy due to performance issues.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
https://r-knott.surrey.ac.uk/Fibonacci/simpleTrig.html
|
||||
"""
|
||||
return {
|
||||
3: cos_3,
|
||||
5: cos_5,
|
||||
17: cos_17,
|
||||
257: cos_257
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+11
@@ -0,0 +1,11 @@
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
|
||||
x, y = symbols('x,y')
|
||||
|
||||
e = exp(2*x)
|
||||
q = exp(3*x)
|
||||
|
||||
|
||||
def timeit_exp_subs():
|
||||
e.subs(q, y)
|
||||
+1492
File diff suppressed because it is too large
Load Diff
+1286
File diff suppressed because it is too large
Load Diff
+2285
File diff suppressed because it is too large
Load Diff
+710
@@ -0,0 +1,710 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.expr import Expr
|
||||
|
||||
from sympy.core import Add, S
|
||||
from sympy.core.evalf import get_integer_part, PrecisionExhausted
|
||||
from sympy.core.function import DefinedFunction
|
||||
from sympy.core.logic import fuzzy_or, fuzzy_and
|
||||
from sympy.core.numbers import Integer, int_valued
|
||||
from sympy.core.relational import Gt, Lt, Ge, Le, Relational, is_eq, is_le, is_lt
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.functions.elementary.complexes import im, re
|
||||
from sympy.multipledispatch import dispatch
|
||||
|
||||
###############################################################################
|
||||
######################### FLOOR and CEILING FUNCTIONS #########################
|
||||
###############################################################################
|
||||
|
||||
|
||||
class RoundFunction(DefinedFunction):
|
||||
"""Abstract base class for rounding functions."""
|
||||
|
||||
args: tuple[Expr]
|
||||
|
||||
@classmethod
|
||||
def eval(cls, arg):
|
||||
if (v := cls._eval_number(arg)) is not None:
|
||||
return v
|
||||
if (v := cls._eval_const_number(arg)) is not None:
|
||||
return v
|
||||
|
||||
if arg.is_integer or arg.is_finite is False:
|
||||
return arg
|
||||
if arg.is_imaginary or (S.ImaginaryUnit*arg).is_real:
|
||||
i = im(arg)
|
||||
if not i.has(S.ImaginaryUnit):
|
||||
return cls(i)*S.ImaginaryUnit
|
||||
return cls(arg, evaluate=False)
|
||||
|
||||
# Integral, numerical, symbolic part
|
||||
ipart = npart = spart = S.Zero
|
||||
|
||||
# Extract integral (or complex integral) terms
|
||||
intof = lambda x: int(x) if int_valued(x) else (
|
||||
x if x.is_integer else None)
|
||||
for t in Add.make_args(arg):
|
||||
if t.is_imaginary and (i := intof(im(t))) is not None:
|
||||
ipart += i*S.ImaginaryUnit
|
||||
elif (i := intof(t)) is not None:
|
||||
ipart += i
|
||||
elif t.is_number:
|
||||
npart += t
|
||||
else:
|
||||
spart += t
|
||||
|
||||
if not (npart or spart):
|
||||
return ipart
|
||||
|
||||
# Evaluate npart numerically if independent of spart
|
||||
if npart and (
|
||||
not spart or
|
||||
npart.is_real and (spart.is_imaginary or (S.ImaginaryUnit*spart).is_real) or
|
||||
npart.is_imaginary and spart.is_real):
|
||||
try:
|
||||
r, i = get_integer_part(
|
||||
npart, cls._dir, {}, return_ints=True)
|
||||
ipart += Integer(r) + Integer(i)*S.ImaginaryUnit
|
||||
npart = S.Zero
|
||||
except (PrecisionExhausted, NotImplementedError):
|
||||
pass
|
||||
|
||||
spart += npart
|
||||
if not spart:
|
||||
return ipart
|
||||
elif spart.is_imaginary or (S.ImaginaryUnit*spart).is_real:
|
||||
return ipart + cls(im(spart), evaluate=False)*S.ImaginaryUnit
|
||||
elif isinstance(spart, (floor, ceiling)):
|
||||
return ipart + spart
|
||||
else:
|
||||
return ipart + cls(spart, evaluate=False)
|
||||
|
||||
@classmethod
|
||||
def _eval_number(cls, arg):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _eval_is_finite(self):
|
||||
return self.args[0].is_finite
|
||||
|
||||
def _eval_is_real(self):
|
||||
return self.args[0].is_real
|
||||
|
||||
def _eval_is_integer(self):
|
||||
return self.args[0].is_real
|
||||
|
||||
|
||||
class floor(RoundFunction):
|
||||
"""
|
||||
Floor is a univariate function which returns the largest integer
|
||||
value not greater than its argument. This implementation
|
||||
generalizes floor to complex numbers by taking the floor of the
|
||||
real and imaginary parts separately.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import floor, E, I, S, Float, Rational
|
||||
>>> floor(17)
|
||||
17
|
||||
>>> floor(Rational(23, 10))
|
||||
2
|
||||
>>> floor(2*E)
|
||||
5
|
||||
>>> floor(-Float(0.567))
|
||||
-1
|
||||
>>> floor(-I/2)
|
||||
-I
|
||||
>>> floor(S(5)/2 + 5*I/2)
|
||||
2 + 2*I
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.functions.elementary.integers.ceiling
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] "Concrete mathematics" by Graham, pp. 87
|
||||
.. [2] https://mathworld.wolfram.com/FloorFunction.html
|
||||
|
||||
"""
|
||||
_dir = -1
|
||||
|
||||
@classmethod
|
||||
def _eval_number(cls, arg):
|
||||
if arg.is_Number:
|
||||
return arg.floor()
|
||||
if any(isinstance(i, j)
|
||||
for i in (arg, -arg) for j in (floor, ceiling)):
|
||||
return arg
|
||||
if arg.is_NumberSymbol:
|
||||
return arg.approximation_interval(Integer)[0]
|
||||
|
||||
@classmethod
|
||||
def _eval_const_number(cls, arg):
|
||||
if arg.is_real:
|
||||
if arg.is_zero:
|
||||
return S.Zero
|
||||
if arg.is_positive:
|
||||
num, den = arg.as_numer_denom()
|
||||
s = den.is_negative
|
||||
if s is None:
|
||||
return None
|
||||
if s:
|
||||
num, den = -num, -den
|
||||
# 0 <= num/den < 1 -> 0
|
||||
if is_lt(num, den):
|
||||
return S.Zero
|
||||
# 1 <= num/den < 2 -> 1
|
||||
if fuzzy_and([is_le(den, num), is_lt(num, 2*den)]):
|
||||
return S.One
|
||||
if arg.is_negative:
|
||||
num, den = arg.as_numer_denom()
|
||||
s = den.is_negative
|
||||
if s is None:
|
||||
return None
|
||||
if s:
|
||||
num, den = -num, -den
|
||||
# -1 <= num/den < 0 -> -1
|
||||
if is_le(-den, num):
|
||||
return S.NegativeOne
|
||||
# -2 <= num/den < -1 -> -2
|
||||
if fuzzy_and([is_le(-2*den, num), is_lt(num, -den)]):
|
||||
return Integer(-2)
|
||||
|
||||
def _eval_as_leading_term(self, x, logx, cdir):
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
arg = self.args[0]
|
||||
arg0 = arg.subs(x, 0)
|
||||
r = self.subs(x, 0)
|
||||
if arg0 is S.NaN or isinstance(arg0, AccumBounds):
|
||||
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
||||
r = floor(arg0)
|
||||
if arg0.is_finite:
|
||||
if arg0 == r:
|
||||
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
||||
if ndir.is_negative:
|
||||
return r - 1
|
||||
elif ndir.is_positive:
|
||||
return r
|
||||
else:
|
||||
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
||||
else:
|
||||
return r
|
||||
return arg.as_leading_term(x, logx=logx, cdir=cdir)
|
||||
|
||||
def _eval_nseries(self, x, n, logx, cdir=0):
|
||||
arg = self.args[0]
|
||||
arg0 = arg.subs(x, 0)
|
||||
r = self.subs(x, 0)
|
||||
if arg0 is S.NaN:
|
||||
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
||||
r = floor(arg0)
|
||||
if arg0.is_infinite:
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
from sympy.series.order import Order
|
||||
s = arg._eval_nseries(x, n, logx, cdir)
|
||||
o = Order(1, (x, 0)) if n <= 0 else AccumBounds(-1, 0)
|
||||
return s + o
|
||||
if arg0 == r:
|
||||
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
||||
if ndir.is_negative:
|
||||
return r - 1
|
||||
elif ndir.is_positive:
|
||||
return r
|
||||
else:
|
||||
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
||||
else:
|
||||
return r
|
||||
|
||||
def _eval_is_negative(self):
|
||||
return self.args[0].is_negative
|
||||
|
||||
def _eval_is_nonnegative(self):
|
||||
return self.args[0].is_nonnegative
|
||||
|
||||
def _eval_rewrite_as_ceiling(self, arg, **kwargs):
|
||||
return -ceiling(-arg)
|
||||
|
||||
def _eval_rewrite_as_frac(self, arg, **kwargs):
|
||||
return arg - frac(arg)
|
||||
|
||||
def __le__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] < other + 1
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] < ceiling(other)
|
||||
if self.args[0] == other and other.is_real:
|
||||
return S.true
|
||||
if other is S.Infinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Le(self, other, evaluate=False)
|
||||
|
||||
def __ge__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] >= other
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] >= ceiling(other)
|
||||
if self.args[0] == other and other.is_real and other.is_noninteger:
|
||||
return S.false
|
||||
if other is S.NegativeInfinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Ge(self, other, evaluate=False)
|
||||
|
||||
def __gt__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] >= other + 1
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] >= ceiling(other)
|
||||
if self.args[0] == other and other.is_real:
|
||||
return S.false
|
||||
if other is S.NegativeInfinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Gt(self, other, evaluate=False)
|
||||
|
||||
def __lt__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] < other
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] < ceiling(other)
|
||||
if self.args[0] == other and other.is_real and other.is_noninteger:
|
||||
return S.true
|
||||
if other is S.Infinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Lt(self, other, evaluate=False)
|
||||
|
||||
|
||||
@dispatch(floor, Expr)
|
||||
def _eval_is_eq(lhs, rhs): # noqa:F811
|
||||
return is_eq(lhs.rewrite(ceiling), rhs) or \
|
||||
is_eq(lhs.rewrite(frac),rhs)
|
||||
|
||||
|
||||
class ceiling(RoundFunction):
|
||||
"""
|
||||
Ceiling is a univariate function which returns the smallest integer
|
||||
value not less than its argument. This implementation
|
||||
generalizes ceiling to complex numbers by taking the ceiling of the
|
||||
real and imaginary parts separately.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import ceiling, E, I, S, Float, Rational
|
||||
>>> ceiling(17)
|
||||
17
|
||||
>>> ceiling(Rational(23, 10))
|
||||
3
|
||||
>>> ceiling(2*E)
|
||||
6
|
||||
>>> ceiling(-Float(0.567))
|
||||
0
|
||||
>>> ceiling(I/2)
|
||||
I
|
||||
>>> ceiling(S(5)/2 + 5*I/2)
|
||||
3 + 3*I
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.functions.elementary.integers.floor
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] "Concrete mathematics" by Graham, pp. 87
|
||||
.. [2] https://mathworld.wolfram.com/CeilingFunction.html
|
||||
|
||||
"""
|
||||
_dir = 1
|
||||
|
||||
@classmethod
|
||||
def _eval_number(cls, arg):
|
||||
if arg.is_Number:
|
||||
return arg.ceiling()
|
||||
if any(isinstance(i, j)
|
||||
for i in (arg, -arg) for j in (floor, ceiling)):
|
||||
return arg
|
||||
if arg.is_NumberSymbol:
|
||||
return arg.approximation_interval(Integer)[1]
|
||||
|
||||
@classmethod
|
||||
def _eval_const_number(cls, arg):
|
||||
if arg.is_real:
|
||||
if arg.is_zero:
|
||||
return S.Zero
|
||||
if arg.is_positive:
|
||||
num, den = arg.as_numer_denom()
|
||||
s = den.is_negative
|
||||
if s is None:
|
||||
return None
|
||||
if s:
|
||||
num, den = -num, -den
|
||||
# 0 < num/den <= 1 -> 1
|
||||
if is_le(num, den):
|
||||
return S.One
|
||||
# 1 < num/den <= 2 -> 2
|
||||
if fuzzy_and([is_lt(den, num), is_le(num, 2*den)]):
|
||||
return Integer(2)
|
||||
if arg.is_negative:
|
||||
num, den = arg.as_numer_denom()
|
||||
s = den.is_negative
|
||||
if s is None:
|
||||
return None
|
||||
if s:
|
||||
num, den = -num, -den
|
||||
# -1 < num/den <= 0 -> 0
|
||||
if is_lt(-den, num):
|
||||
return S.Zero
|
||||
# -2 < num/den <= -1 -> -1
|
||||
if fuzzy_and([is_lt(-2*den, num), is_le(num, -den)]):
|
||||
return S.NegativeOne
|
||||
|
||||
def _eval_as_leading_term(self, x, logx, cdir):
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
arg = self.args[0]
|
||||
arg0 = arg.subs(x, 0)
|
||||
r = self.subs(x, 0)
|
||||
if arg0 is S.NaN or isinstance(arg0, AccumBounds):
|
||||
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
||||
r = ceiling(arg0)
|
||||
if arg0.is_finite:
|
||||
if arg0 == r:
|
||||
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
||||
if ndir.is_negative:
|
||||
return r
|
||||
elif ndir.is_positive:
|
||||
return r + 1
|
||||
else:
|
||||
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
||||
else:
|
||||
return r
|
||||
return arg.as_leading_term(x, logx=logx, cdir=cdir)
|
||||
|
||||
def _eval_nseries(self, x, n, logx, cdir=0):
|
||||
arg = self.args[0]
|
||||
arg0 = arg.subs(x, 0)
|
||||
r = self.subs(x, 0)
|
||||
if arg0 is S.NaN:
|
||||
arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
|
||||
r = ceiling(arg0)
|
||||
if arg0.is_infinite:
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
from sympy.series.order import Order
|
||||
s = arg._eval_nseries(x, n, logx, cdir)
|
||||
o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1)
|
||||
return s + o
|
||||
if arg0 == r:
|
||||
ndir = arg.dir(x, cdir=cdir if cdir != 0 else 1)
|
||||
if ndir.is_negative:
|
||||
return r
|
||||
elif ndir.is_positive:
|
||||
return r + 1
|
||||
else:
|
||||
raise NotImplementedError("Not sure of sign of %s" % ndir)
|
||||
else:
|
||||
return r
|
||||
|
||||
def _eval_rewrite_as_floor(self, arg, **kwargs):
|
||||
return -floor(-arg)
|
||||
|
||||
def _eval_rewrite_as_frac(self, arg, **kwargs):
|
||||
return arg + frac(-arg)
|
||||
|
||||
def _eval_is_positive(self):
|
||||
return self.args[0].is_positive
|
||||
|
||||
def _eval_is_nonpositive(self):
|
||||
return self.args[0].is_nonpositive
|
||||
|
||||
def __lt__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] <= other - 1
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] <= floor(other)
|
||||
if self.args[0] == other and other.is_real:
|
||||
return S.false
|
||||
if other is S.Infinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Lt(self, other, evaluate=False)
|
||||
|
||||
def __gt__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] > other
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] > floor(other)
|
||||
if self.args[0] == other and other.is_real and other.is_noninteger:
|
||||
return S.true
|
||||
if other is S.NegativeInfinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Gt(self, other, evaluate=False)
|
||||
|
||||
def __ge__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] > other - 1
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] > floor(other)
|
||||
if self.args[0] == other and other.is_real:
|
||||
return S.true
|
||||
if other is S.NegativeInfinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Ge(self, other, evaluate=False)
|
||||
|
||||
def __le__(self, other):
|
||||
other = S(other)
|
||||
if self.args[0].is_real:
|
||||
if other.is_integer:
|
||||
return self.args[0] <= other
|
||||
if other.is_number and other.is_real:
|
||||
return self.args[0] <= floor(other)
|
||||
if self.args[0] == other and other.is_real and other.is_noninteger:
|
||||
return S.false
|
||||
if other is S.Infinity and self.is_finite:
|
||||
return S.true
|
||||
|
||||
return Le(self, other, evaluate=False)
|
||||
|
||||
|
||||
@dispatch(ceiling, Basic) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa:F811
|
||||
return is_eq(lhs.rewrite(floor), rhs) or is_eq(lhs.rewrite(frac),rhs)
|
||||
|
||||
|
||||
class frac(DefinedFunction):
|
||||
r"""Represents the fractional part of x
|
||||
|
||||
For real numbers it is defined [1]_ as
|
||||
|
||||
.. math::
|
||||
x - \left\lfloor{x}\right\rfloor
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Symbol, frac, Rational, floor, I
|
||||
>>> frac(Rational(4, 3))
|
||||
1/3
|
||||
>>> frac(-Rational(4, 3))
|
||||
2/3
|
||||
|
||||
returns zero for integer arguments
|
||||
|
||||
>>> n = Symbol('n', integer=True)
|
||||
>>> frac(n)
|
||||
0
|
||||
|
||||
rewrite as floor
|
||||
|
||||
>>> x = Symbol('x')
|
||||
>>> frac(x).rewrite(floor)
|
||||
x - floor(x)
|
||||
|
||||
for complex arguments
|
||||
|
||||
>>> r = Symbol('r', real=True)
|
||||
>>> t = Symbol('t', real=True)
|
||||
>>> frac(t + I*r)
|
||||
I*frac(r) + frac(t)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.functions.elementary.integers.floor
|
||||
sympy.functions.elementary.integers.ceiling
|
||||
|
||||
References
|
||||
===========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Fractional_part
|
||||
.. [2] https://mathworld.wolfram.com/FractionalPart.html
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
def eval(cls, arg):
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
|
||||
def _eval(arg):
|
||||
if arg in (S.Infinity, S.NegativeInfinity):
|
||||
return AccumBounds(0, 1)
|
||||
if arg.is_integer:
|
||||
return S.Zero
|
||||
if arg.is_number:
|
||||
if arg is S.NaN:
|
||||
return S.NaN
|
||||
elif arg is S.ComplexInfinity:
|
||||
return S.NaN
|
||||
else:
|
||||
return arg - floor(arg)
|
||||
return cls(arg, evaluate=False)
|
||||
|
||||
real, imag = S.Zero, S.Zero
|
||||
for t in Add.make_args(arg):
|
||||
# Two checks are needed for complex arguments
|
||||
# see issue-7649 for details
|
||||
if t.is_imaginary or (S.ImaginaryUnit*t).is_real:
|
||||
i = im(t)
|
||||
if not i.has(S.ImaginaryUnit):
|
||||
imag += i
|
||||
else:
|
||||
real += t
|
||||
else:
|
||||
real += t
|
||||
|
||||
real = _eval(real)
|
||||
imag = _eval(imag)
|
||||
return real + S.ImaginaryUnit*imag
|
||||
|
||||
def _eval_rewrite_as_floor(self, arg, **kwargs):
|
||||
return arg - floor(arg)
|
||||
|
||||
def _eval_rewrite_as_ceiling(self, arg, **kwargs):
|
||||
return arg + ceiling(-arg)
|
||||
|
||||
def _eval_is_finite(self):
|
||||
return True
|
||||
|
||||
def _eval_is_real(self):
|
||||
return self.args[0].is_extended_real
|
||||
|
||||
def _eval_is_imaginary(self):
|
||||
return self.args[0].is_imaginary
|
||||
|
||||
def _eval_is_integer(self):
|
||||
return self.args[0].is_integer
|
||||
|
||||
def _eval_is_zero(self):
|
||||
return fuzzy_or([self.args[0].is_zero, self.args[0].is_integer])
|
||||
|
||||
def _eval_is_negative(self):
|
||||
return False
|
||||
|
||||
def __ge__(self, other):
|
||||
if self.is_extended_real:
|
||||
other = _sympify(other)
|
||||
# Check if other <= 0
|
||||
if other.is_extended_nonpositive:
|
||||
return S.true
|
||||
# Check if other >= 1
|
||||
res = self._value_one_or_more(other)
|
||||
if res is not None:
|
||||
return not(res)
|
||||
return Ge(self, other, evaluate=False)
|
||||
|
||||
def __gt__(self, other):
|
||||
if self.is_extended_real:
|
||||
other = _sympify(other)
|
||||
# Check if other < 0
|
||||
res = self._value_one_or_more(other)
|
||||
if res is not None:
|
||||
return not(res)
|
||||
# Check if other >= 1
|
||||
if other.is_extended_negative:
|
||||
return S.true
|
||||
return Gt(self, other, evaluate=False)
|
||||
|
||||
def __le__(self, other):
|
||||
if self.is_extended_real:
|
||||
other = _sympify(other)
|
||||
# Check if other < 0
|
||||
if other.is_extended_negative:
|
||||
return S.false
|
||||
# Check if other >= 1
|
||||
res = self._value_one_or_more(other)
|
||||
if res is not None:
|
||||
return res
|
||||
return Le(self, other, evaluate=False)
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.is_extended_real:
|
||||
other = _sympify(other)
|
||||
# Check if other <= 0
|
||||
if other.is_extended_nonpositive:
|
||||
return S.false
|
||||
# Check if other >= 1
|
||||
res = self._value_one_or_more(other)
|
||||
if res is not None:
|
||||
return res
|
||||
return Lt(self, other, evaluate=False)
|
||||
|
||||
def _value_one_or_more(self, other):
|
||||
if other.is_extended_real:
|
||||
if other.is_number:
|
||||
res = other >= 1
|
||||
if res and not isinstance(res, Relational):
|
||||
return S.true
|
||||
if other.is_integer and other.is_positive:
|
||||
return S.true
|
||||
|
||||
def _eval_as_leading_term(self, x, logx, cdir):
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
arg = self.args[0]
|
||||
arg0 = arg.subs(x, 0)
|
||||
r = self.subs(x, 0)
|
||||
|
||||
if arg0.is_finite:
|
||||
if r.is_zero:
|
||||
ndir = arg.dir(x, cdir=cdir)
|
||||
if ndir.is_negative:
|
||||
return S.One
|
||||
return (arg - arg0).as_leading_term(x, logx=logx, cdir=cdir)
|
||||
else:
|
||||
return r
|
||||
elif arg0 in (S.ComplexInfinity, S.Infinity, S.NegativeInfinity):
|
||||
return AccumBounds(0, 1)
|
||||
return arg.as_leading_term(x, logx=logx, cdir=cdir)
|
||||
|
||||
def _eval_nseries(self, x, n, logx, cdir=0):
|
||||
from sympy.series.order import Order
|
||||
arg = self.args[0]
|
||||
arg0 = arg.subs(x, 0)
|
||||
r = self.subs(x, 0)
|
||||
|
||||
if arg0.is_infinite:
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
o = Order(1, (x, 0)) if n <= 0 else AccumBounds(0, 1) + Order(x**n, (x, 0))
|
||||
return o
|
||||
else:
|
||||
res = (arg - arg0)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
||||
if r.is_zero:
|
||||
ndir = arg.dir(x, cdir=cdir)
|
||||
res += S.One if ndir.is_negative else S.Zero
|
||||
else:
|
||||
res += r
|
||||
return res
|
||||
|
||||
|
||||
@dispatch(frac, Basic) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa:F811
|
||||
if (lhs.rewrite(floor) == rhs) or \
|
||||
(lhs.rewrite(ceiling) == rhs):
|
||||
return True
|
||||
# Check if other < 0
|
||||
if rhs.is_extended_negative:
|
||||
return False
|
||||
# Check if other >= 1
|
||||
res = lhs._value_one_or_more(rhs)
|
||||
if res is not None:
|
||||
return False
|
||||
+915
@@ -0,0 +1,915 @@
|
||||
from sympy.core import S, sympify, NumberKind
|
||||
from sympy.utilities.iterables import sift
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.operations import LatticeOp, ShortCircuit
|
||||
from sympy.core.function import (Application, Lambda,
|
||||
ArgumentIndexError, DefinedFunction)
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.exprtools import factor_terms
|
||||
from sympy.core.mod import Mod
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import Rational
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.relational import Eq, Relational
|
||||
from sympy.core.singleton import Singleton
|
||||
from sympy.core.sorting import ordered
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.core.rules import Transform
|
||||
from sympy.core.logic import fuzzy_and, fuzzy_or, _torf
|
||||
from sympy.core.traversal import walk
|
||||
from sympy.core.numbers import Integer
|
||||
from sympy.logic.boolalg import And, Or
|
||||
|
||||
|
||||
def _minmax_as_Piecewise(op, *args):
|
||||
# helper for Min/Max rewrite as Piecewise
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
ec = []
|
||||
for i, a in enumerate(args):
|
||||
c = [Relational(a, args[j], op) for j in range(i + 1, len(args))]
|
||||
ec.append((a, And(*c)))
|
||||
return Piecewise(*ec)
|
||||
|
||||
|
||||
class IdentityFunction(Lambda, metaclass=Singleton):
|
||||
"""
|
||||
The identity function
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Id, Symbol
|
||||
>>> x = Symbol('x')
|
||||
>>> Id(x)
|
||||
x
|
||||
|
||||
"""
|
||||
|
||||
_symbol = Dummy('x')
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
return Tuple(self._symbol)
|
||||
|
||||
@property
|
||||
def expr(self):
|
||||
return self._symbol
|
||||
|
||||
|
||||
Id = S.IdentityFunction
|
||||
|
||||
###############################################################################
|
||||
############################# ROOT and SQUARE ROOT FUNCTION ###################
|
||||
###############################################################################
|
||||
|
||||
|
||||
def sqrt(arg, evaluate=None):
|
||||
"""Returns the principal square root.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
evaluate : bool, optional
|
||||
The parameter determines if the expression should be evaluated.
|
||||
If ``None``, its value is taken from
|
||||
``global_parameters.evaluate``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import sqrt, Symbol, S
|
||||
>>> x = Symbol('x')
|
||||
|
||||
>>> sqrt(x)
|
||||
sqrt(x)
|
||||
|
||||
>>> sqrt(x)**2
|
||||
x
|
||||
|
||||
Note that sqrt(x**2) does not simplify to x.
|
||||
|
||||
>>> sqrt(x**2)
|
||||
sqrt(x**2)
|
||||
|
||||
This is because the two are not equal to each other in general.
|
||||
For example, consider x == -1:
|
||||
|
||||
>>> from sympy import Eq
|
||||
>>> Eq(sqrt(x**2), x).subs(x, -1)
|
||||
False
|
||||
|
||||
This is because sqrt computes the principal square root, so the square may
|
||||
put the argument in a different branch. This identity does hold if x is
|
||||
positive:
|
||||
|
||||
>>> y = Symbol('y', positive=True)
|
||||
>>> sqrt(y**2)
|
||||
y
|
||||
|
||||
You can force this simplification by using the powdenest() function with
|
||||
the force option set to True:
|
||||
|
||||
>>> from sympy import powdenest
|
||||
>>> sqrt(x**2)
|
||||
sqrt(x**2)
|
||||
>>> powdenest(sqrt(x**2), force=True)
|
||||
x
|
||||
|
||||
To get both branches of the square root you can use the rootof function:
|
||||
|
||||
>>> from sympy import rootof
|
||||
|
||||
>>> [rootof(x**2-3,i) for i in (0,1)]
|
||||
[-sqrt(3), sqrt(3)]
|
||||
|
||||
Although ``sqrt`` is printed, there is no ``sqrt`` function so looking for
|
||||
``sqrt`` in an expression will fail:
|
||||
|
||||
>>> from sympy.utilities.misc import func_name
|
||||
>>> func_name(sqrt(x))
|
||||
'Pow'
|
||||
>>> sqrt(x).has(sqrt)
|
||||
False
|
||||
|
||||
To find ``sqrt`` look for ``Pow`` with an exponent of ``1/2``:
|
||||
|
||||
>>> (x + 1/sqrt(x)).find(lambda i: i.is_Pow and abs(i.exp) is S.Half)
|
||||
{1/sqrt(x)}
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.polys.rootoftools.rootof, root, real_root
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Square_root
|
||||
.. [2] https://en.wikipedia.org/wiki/Principal_value
|
||||
"""
|
||||
# arg = sympify(arg) is handled by Pow
|
||||
return Pow(arg, S.Half, evaluate=evaluate)
|
||||
|
||||
|
||||
def cbrt(arg, evaluate=None):
|
||||
"""Returns the principal cube root.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
evaluate : bool, optional
|
||||
The parameter determines if the expression should be evaluated.
|
||||
If ``None``, its value is taken from
|
||||
``global_parameters.evaluate``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import cbrt, Symbol
|
||||
>>> x = Symbol('x')
|
||||
|
||||
>>> cbrt(x)
|
||||
x**(1/3)
|
||||
|
||||
>>> cbrt(x)**3
|
||||
x
|
||||
|
||||
Note that cbrt(x**3) does not simplify to x.
|
||||
|
||||
>>> cbrt(x**3)
|
||||
(x**3)**(1/3)
|
||||
|
||||
This is because the two are not equal to each other in general.
|
||||
For example, consider `x == -1`:
|
||||
|
||||
>>> from sympy import Eq
|
||||
>>> Eq(cbrt(x**3), x).subs(x, -1)
|
||||
False
|
||||
|
||||
This is because cbrt computes the principal cube root, this
|
||||
identity does hold if `x` is positive:
|
||||
|
||||
>>> y = Symbol('y', positive=True)
|
||||
>>> cbrt(y**3)
|
||||
y
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.polys.rootoftools.rootof, root, real_root
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Cube_root
|
||||
.. [2] https://en.wikipedia.org/wiki/Principal_value
|
||||
|
||||
"""
|
||||
return Pow(arg, Rational(1, 3), evaluate=evaluate)
|
||||
|
||||
|
||||
def root(arg, n, k=0, evaluate=None):
|
||||
r"""Returns the *k*-th *n*-th root of ``arg``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
k : int, optional
|
||||
Should be an integer in $\{0, 1, ..., n-1\}$.
|
||||
Defaults to the principal root if $0$.
|
||||
|
||||
evaluate : bool, optional
|
||||
The parameter determines if the expression should be evaluated.
|
||||
If ``None``, its value is taken from
|
||||
``global_parameters.evaluate``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import root, Rational
|
||||
>>> from sympy.abc import x, n
|
||||
|
||||
>>> root(x, 2)
|
||||
sqrt(x)
|
||||
|
||||
>>> root(x, 3)
|
||||
x**(1/3)
|
||||
|
||||
>>> root(x, n)
|
||||
x**(1/n)
|
||||
|
||||
>>> root(x, -Rational(2, 3))
|
||||
x**(-3/2)
|
||||
|
||||
To get the k-th n-th root, specify k:
|
||||
|
||||
>>> root(-2, 3, 2)
|
||||
-(-1)**(2/3)*2**(1/3)
|
||||
|
||||
To get all n n-th roots you can use the rootof function.
|
||||
The following examples show the roots of unity for n
|
||||
equal 2, 3 and 4:
|
||||
|
||||
>>> from sympy import rootof
|
||||
|
||||
>>> [rootof(x**2 - 1, i) for i in range(2)]
|
||||
[-1, 1]
|
||||
|
||||
>>> [rootof(x**3 - 1,i) for i in range(3)]
|
||||
[1, -1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2]
|
||||
|
||||
>>> [rootof(x**4 - 1,i) for i in range(4)]
|
||||
[-1, 1, -I, I]
|
||||
|
||||
SymPy, like other symbolic algebra systems, returns the
|
||||
complex root of negative numbers. This is the principal
|
||||
root and differs from the text-book result that one might
|
||||
be expecting. For example, the cube root of -8 does not
|
||||
come back as -2:
|
||||
|
||||
>>> root(-8, 3)
|
||||
2*(-1)**(1/3)
|
||||
|
||||
The real_root function can be used to either make the principal
|
||||
result real (or simply to return the real root directly):
|
||||
|
||||
>>> from sympy import real_root
|
||||
>>> real_root(_)
|
||||
-2
|
||||
>>> real_root(-32, 5)
|
||||
-2
|
||||
|
||||
Alternatively, the n//2-th n-th root of a negative number can be
|
||||
computed with root:
|
||||
|
||||
>>> root(-32, 5, 5//2)
|
||||
-2
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.polys.rootoftools.rootof
|
||||
sympy.core.intfunc.integer_nthroot
|
||||
sqrt, real_root
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Square_root
|
||||
.. [2] https://en.wikipedia.org/wiki/Real_root
|
||||
.. [3] https://en.wikipedia.org/wiki/Root_of_unity
|
||||
.. [4] https://en.wikipedia.org/wiki/Principal_value
|
||||
.. [5] https://mathworld.wolfram.com/CubeRoot.html
|
||||
|
||||
"""
|
||||
n = sympify(n)
|
||||
if k:
|
||||
return Mul(Pow(arg, S.One/n, evaluate=evaluate), S.NegativeOne**(2*k/n), evaluate=evaluate)
|
||||
return Pow(arg, 1/n, evaluate=evaluate)
|
||||
|
||||
|
||||
def real_root(arg, n=None, evaluate=None):
|
||||
r"""Return the real *n*'th-root of *arg* if possible.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : int or None, optional
|
||||
If *n* is ``None``, then all instances of
|
||||
$(-n)^{1/\text{odd}}$ will be changed to $-n^{1/\text{odd}}$.
|
||||
This will only create a real root of a principal root.
|
||||
The presence of other factors may cause the result to not be
|
||||
real.
|
||||
|
||||
evaluate : bool, optional
|
||||
The parameter determines if the expression should be evaluated.
|
||||
If ``None``, its value is taken from
|
||||
``global_parameters.evaluate``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import root, real_root
|
||||
|
||||
>>> real_root(-8, 3)
|
||||
-2
|
||||
>>> root(-8, 3)
|
||||
2*(-1)**(1/3)
|
||||
>>> real_root(_)
|
||||
-2
|
||||
|
||||
If one creates a non-principal root and applies real_root, the
|
||||
result will not be real (so use with caution):
|
||||
|
||||
>>> root(-8, 3, 2)
|
||||
-2*(-1)**(2/3)
|
||||
>>> real_root(_)
|
||||
-2*(-1)**(2/3)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.polys.rootoftools.rootof
|
||||
sympy.core.intfunc.integer_nthroot
|
||||
root, sqrt
|
||||
"""
|
||||
from sympy.functions.elementary.complexes import Abs, im, sign
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
if n is not None:
|
||||
return Piecewise(
|
||||
(root(arg, n, evaluate=evaluate), Or(Eq(n, S.One), Eq(n, S.NegativeOne))),
|
||||
(Mul(sign(arg), root(Abs(arg), n, evaluate=evaluate), evaluate=evaluate),
|
||||
And(Eq(im(arg), S.Zero), Eq(Mod(n, 2), S.One))),
|
||||
(root(arg, n, evaluate=evaluate), True))
|
||||
rv = sympify(arg)
|
||||
n1pow = Transform(lambda x: -(-x.base)**x.exp,
|
||||
lambda x:
|
||||
x.is_Pow and
|
||||
x.base.is_negative and
|
||||
x.exp.is_Rational and
|
||||
x.exp.p == 1 and x.exp.q % 2)
|
||||
return rv.xreplace(n1pow)
|
||||
|
||||
###############################################################################
|
||||
############################# MINIMUM and MAXIMUM #############################
|
||||
###############################################################################
|
||||
|
||||
|
||||
class MinMaxBase(Expr, LatticeOp):
|
||||
def __new__(cls, *args, **assumptions):
|
||||
from sympy.core.parameters import global_parameters
|
||||
evaluate = assumptions.pop('evaluate', global_parameters.evaluate)
|
||||
args = (sympify(arg) for arg in args)
|
||||
|
||||
# first standard filter, for cls.zero and cls.identity
|
||||
# also reshape Max(a, Max(b, c)) to Max(a, b, c)
|
||||
|
||||
if evaluate:
|
||||
try:
|
||||
args = frozenset(cls._new_args_filter(args))
|
||||
except ShortCircuit:
|
||||
return cls.zero
|
||||
# remove redundant args that are easily identified
|
||||
args = cls._collapse_arguments(args, **assumptions)
|
||||
# find local zeros
|
||||
args = cls._find_localzeros(args, **assumptions)
|
||||
args = frozenset(args)
|
||||
|
||||
if not args:
|
||||
return cls.identity
|
||||
|
||||
if len(args) == 1:
|
||||
return list(args).pop()
|
||||
|
||||
# base creation
|
||||
obj = Expr.__new__(cls, *ordered(args), **assumptions)
|
||||
obj._argset = args
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def _collapse_arguments(cls, args, **assumptions):
|
||||
"""Remove redundant args.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Min, Max
|
||||
>>> from sympy.abc import a, b, c, d, e
|
||||
|
||||
Any arg in parent that appears in any
|
||||
parent-like function in any of the flat args
|
||||
of parent can be removed from that sub-arg:
|
||||
|
||||
>>> Min(a, Max(b, Min(a, c, d)))
|
||||
Min(a, Max(b, Min(c, d)))
|
||||
|
||||
If the arg of parent appears in an opposite-than parent
|
||||
function in any of the flat args of parent that function
|
||||
can be replaced with the arg:
|
||||
|
||||
>>> Min(a, Max(b, Min(c, d, Max(a, e))))
|
||||
Min(a, Max(b, Min(a, c, d)))
|
||||
"""
|
||||
if not args:
|
||||
return args
|
||||
args = list(ordered(args))
|
||||
if cls == Min:
|
||||
other = Max
|
||||
else:
|
||||
other = Min
|
||||
|
||||
# find global comparable max of Max and min of Min if a new
|
||||
# value is being introduced in these args at position 0 of
|
||||
# the ordered args
|
||||
if args[0].is_number:
|
||||
sifted = mins, maxs = [], []
|
||||
for i in args:
|
||||
for v in walk(i, Min, Max):
|
||||
if v.args[0].is_comparable:
|
||||
sifted[isinstance(v, Max)].append(v)
|
||||
small = Min.identity
|
||||
for i in mins:
|
||||
v = i.args[0]
|
||||
if v.is_number and (v < small) == True:
|
||||
small = v
|
||||
big = Max.identity
|
||||
for i in maxs:
|
||||
v = i.args[0]
|
||||
if v.is_number and (v > big) == True:
|
||||
big = v
|
||||
# at the point when this function is called from __new__,
|
||||
# there may be more than one numeric arg present since
|
||||
# local zeros have not been handled yet, so look through
|
||||
# more than the first arg
|
||||
if cls == Min:
|
||||
for arg in args:
|
||||
if not arg.is_number:
|
||||
break
|
||||
if (arg < small) == True:
|
||||
small = arg
|
||||
elif cls == Max:
|
||||
for arg in args:
|
||||
if not arg.is_number:
|
||||
break
|
||||
if (arg > big) == True:
|
||||
big = arg
|
||||
T = None
|
||||
if cls == Min:
|
||||
if small != Min.identity:
|
||||
other = Max
|
||||
T = small
|
||||
elif big != Max.identity:
|
||||
other = Min
|
||||
T = big
|
||||
if T is not None:
|
||||
# remove numerical redundancy
|
||||
for i in range(len(args)):
|
||||
a = args[i]
|
||||
if isinstance(a, other):
|
||||
a0 = a.args[0]
|
||||
if ((a0 > T) if other == Max else (a0 < T)) == True:
|
||||
args[i] = cls.identity
|
||||
|
||||
# remove redundant symbolic args
|
||||
def do(ai, a):
|
||||
if not isinstance(ai, (Min, Max)):
|
||||
return ai
|
||||
cond = a in ai.args
|
||||
if not cond:
|
||||
return ai.func(*[do(i, a) for i in ai.args],
|
||||
evaluate=False)
|
||||
if isinstance(ai, cls):
|
||||
return ai.func(*[do(i, a) for i in ai.args if i != a],
|
||||
evaluate=False)
|
||||
return a
|
||||
for i, a in enumerate(args):
|
||||
args[i + 1:] = [do(ai, a) for ai in args[i + 1:]]
|
||||
|
||||
# factor out common elements as for
|
||||
# Min(Max(x, y), Max(x, z)) -> Max(x, Min(y, z))
|
||||
# and vice versa when swapping Min/Max -- do this only for the
|
||||
# easy case where all functions contain something in common;
|
||||
# trying to find some optimal subset of args to modify takes
|
||||
# too long
|
||||
|
||||
def factor_minmax(args):
|
||||
is_other = lambda arg: isinstance(arg, other)
|
||||
other_args, remaining_args = sift(args, is_other, binary=True)
|
||||
if not other_args:
|
||||
return args
|
||||
|
||||
# Min(Max(x, y, z), Max(x, y, u, v)) -> {x,y}, ({z}, {u,v})
|
||||
arg_sets = [set(arg.args) for arg in other_args]
|
||||
common = set.intersection(*arg_sets)
|
||||
if not common:
|
||||
return args
|
||||
|
||||
new_other_args = list(common)
|
||||
arg_sets_diff = [arg_set - common for arg_set in arg_sets]
|
||||
|
||||
# If any set is empty after removing common then all can be
|
||||
# discarded e.g. Min(Max(a, b, c), Max(a, b)) -> Max(a, b)
|
||||
if all(arg_sets_diff):
|
||||
other_args_diff = [other(*s, evaluate=False) for s in arg_sets_diff]
|
||||
new_other_args.append(cls(*other_args_diff, evaluate=False))
|
||||
|
||||
other_args_factored = other(*new_other_args, evaluate=False)
|
||||
return remaining_args + [other_args_factored]
|
||||
|
||||
if len(args) > 1:
|
||||
args = factor_minmax(args)
|
||||
|
||||
return args
|
||||
|
||||
@classmethod
|
||||
def _new_args_filter(cls, arg_sequence):
|
||||
"""
|
||||
Generator filtering args.
|
||||
|
||||
first standard filter, for cls.zero and cls.identity.
|
||||
Also reshape ``Max(a, Max(b, c))`` to ``Max(a, b, c)``,
|
||||
and check arguments for comparability
|
||||
"""
|
||||
for arg in arg_sequence:
|
||||
# pre-filter, checking comparability of arguments
|
||||
if not isinstance(arg, Expr) or arg.is_extended_real is False or (
|
||||
arg.is_number and
|
||||
not arg.is_comparable):
|
||||
raise ValueError("The argument '%s' is not comparable." % arg)
|
||||
|
||||
if arg == cls.zero:
|
||||
raise ShortCircuit(arg)
|
||||
elif arg == cls.identity:
|
||||
continue
|
||||
elif arg.func == cls:
|
||||
yield from arg.args
|
||||
else:
|
||||
yield arg
|
||||
|
||||
@classmethod
|
||||
def _find_localzeros(cls, values, **options):
|
||||
"""
|
||||
Sequentially allocate values to localzeros.
|
||||
|
||||
When a value is identified as being more extreme than another member it
|
||||
replaces that member; if this is never true, then the value is simply
|
||||
appended to the localzeros.
|
||||
"""
|
||||
localzeros = set()
|
||||
for v in values:
|
||||
is_newzero = True
|
||||
localzeros_ = list(localzeros)
|
||||
for z in localzeros_:
|
||||
if id(v) == id(z):
|
||||
is_newzero = False
|
||||
else:
|
||||
con = cls._is_connected(v, z)
|
||||
if con:
|
||||
is_newzero = False
|
||||
if con is True or con == cls:
|
||||
localzeros.remove(z)
|
||||
localzeros.update([v])
|
||||
if is_newzero:
|
||||
localzeros.update([v])
|
||||
return localzeros
|
||||
|
||||
@classmethod
|
||||
def _is_connected(cls, x, y):
|
||||
"""
|
||||
Check if x and y are connected somehow.
|
||||
"""
|
||||
for i in range(2):
|
||||
if x == y:
|
||||
return True
|
||||
t, f = Max, Min
|
||||
for op in "><":
|
||||
for j in range(2):
|
||||
try:
|
||||
if op == ">":
|
||||
v = x >= y
|
||||
else:
|
||||
v = x <= y
|
||||
except TypeError:
|
||||
return False # non-real arg
|
||||
if not v.is_Relational:
|
||||
return t if v else f
|
||||
t, f = f, t
|
||||
x, y = y, x
|
||||
x, y = y, x # run next pass with reversed order relative to start
|
||||
# simplification can be expensive, so be conservative
|
||||
# in what is attempted
|
||||
x = factor_terms(x - y)
|
||||
y = S.Zero
|
||||
|
||||
return False
|
||||
|
||||
def _eval_derivative(self, s):
|
||||
# f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
|
||||
i = 0
|
||||
l = []
|
||||
for a in self.args:
|
||||
i += 1
|
||||
da = a.diff(s)
|
||||
if da.is_zero:
|
||||
continue
|
||||
try:
|
||||
df = self.fdiff(i)
|
||||
except ArgumentIndexError:
|
||||
df = super().fdiff(i)
|
||||
l.append(df * da)
|
||||
return Add(*l)
|
||||
|
||||
def _eval_rewrite_as_Abs(self, *args, **kwargs):
|
||||
from sympy.functions.elementary.complexes import Abs
|
||||
s = (args[0] + self.func(*args[1:]))/2
|
||||
d = abs(args[0] - self.func(*args[1:]))/2
|
||||
return (s + d if isinstance(self, Max) else s - d).rewrite(Abs)
|
||||
|
||||
def evalf(self, n=15, **options):
|
||||
return self.func(*[a.evalf(n, **options) for a in self.args])
|
||||
|
||||
def n(self, *args, **kwargs):
|
||||
return self.evalf(*args, **kwargs)
|
||||
|
||||
_eval_is_algebraic = lambda s: _torf(i.is_algebraic for i in s.args)
|
||||
_eval_is_antihermitian = lambda s: _torf(i.is_antihermitian for i in s.args)
|
||||
_eval_is_commutative = lambda s: _torf(i.is_commutative for i in s.args)
|
||||
_eval_is_complex = lambda s: _torf(i.is_complex for i in s.args)
|
||||
_eval_is_composite = lambda s: _torf(i.is_composite for i in s.args)
|
||||
_eval_is_even = lambda s: _torf(i.is_even for i in s.args)
|
||||
_eval_is_finite = lambda s: _torf(i.is_finite for i in s.args)
|
||||
_eval_is_hermitian = lambda s: _torf(i.is_hermitian for i in s.args)
|
||||
_eval_is_imaginary = lambda s: _torf(i.is_imaginary for i in s.args)
|
||||
_eval_is_infinite = lambda s: _torf(i.is_infinite for i in s.args)
|
||||
_eval_is_integer = lambda s: _torf(i.is_integer for i in s.args)
|
||||
_eval_is_irrational = lambda s: _torf(i.is_irrational for i in s.args)
|
||||
_eval_is_negative = lambda s: _torf(i.is_negative for i in s.args)
|
||||
_eval_is_noninteger = lambda s: _torf(i.is_noninteger for i in s.args)
|
||||
_eval_is_nonnegative = lambda s: _torf(i.is_nonnegative for i in s.args)
|
||||
_eval_is_nonpositive = lambda s: _torf(i.is_nonpositive for i in s.args)
|
||||
_eval_is_nonzero = lambda s: _torf(i.is_nonzero for i in s.args)
|
||||
_eval_is_odd = lambda s: _torf(i.is_odd for i in s.args)
|
||||
_eval_is_polar = lambda s: _torf(i.is_polar for i in s.args)
|
||||
_eval_is_positive = lambda s: _torf(i.is_positive for i in s.args)
|
||||
_eval_is_prime = lambda s: _torf(i.is_prime for i in s.args)
|
||||
_eval_is_rational = lambda s: _torf(i.is_rational for i in s.args)
|
||||
_eval_is_real = lambda s: _torf(i.is_real for i in s.args)
|
||||
_eval_is_extended_real = lambda s: _torf(i.is_extended_real for i in s.args)
|
||||
_eval_is_transcendental = lambda s: _torf(i.is_transcendental for i in s.args)
|
||||
_eval_is_zero = lambda s: _torf(i.is_zero for i in s.args)
|
||||
|
||||
|
||||
class Max(MinMaxBase, Application):
|
||||
r"""
|
||||
Return, if possible, the maximum value of the list.
|
||||
|
||||
When number of arguments is equal one, then
|
||||
return this argument.
|
||||
|
||||
When number of arguments is equal two, then
|
||||
return, if possible, the value from (a, b) that is $\ge$ the other.
|
||||
|
||||
In common case, when the length of list greater than 2, the task
|
||||
is more complicated. Return only the arguments, which are greater
|
||||
than others, if it is possible to determine directional relation.
|
||||
|
||||
If is not possible to determine such a relation, return a partially
|
||||
evaluated result.
|
||||
|
||||
Assumptions are used to make the decision too.
|
||||
|
||||
Also, only comparable arguments are permitted.
|
||||
|
||||
It is named ``Max`` and not ``max`` to avoid conflicts
|
||||
with the built-in function ``max``.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Max, Symbol, oo
|
||||
>>> from sympy.abc import x, y, z
|
||||
>>> p = Symbol('p', positive=True)
|
||||
>>> n = Symbol('n', negative=True)
|
||||
|
||||
>>> Max(x, -2)
|
||||
Max(-2, x)
|
||||
>>> Max(x, -2).subs(x, 3)
|
||||
3
|
||||
>>> Max(p, -2)
|
||||
p
|
||||
>>> Max(x, y)
|
||||
Max(x, y)
|
||||
>>> Max(x, y) == Max(y, x)
|
||||
True
|
||||
>>> Max(x, Max(y, z))
|
||||
Max(x, y, z)
|
||||
>>> Max(n, 8, p, 7, -oo)
|
||||
Max(8, p)
|
||||
>>> Max (1, x, oo)
|
||||
oo
|
||||
|
||||
* Algorithm
|
||||
|
||||
The task can be considered as searching of supremums in the
|
||||
directed complete partial orders [1]_.
|
||||
|
||||
The source values are sequentially allocated by the isolated subsets
|
||||
in which supremums are searched and result as Max arguments.
|
||||
|
||||
If the resulted supremum is single, then it is returned.
|
||||
|
||||
The isolated subsets are the sets of values which are only the comparable
|
||||
with each other in the current set. E.g. natural numbers are comparable with
|
||||
each other, but not comparable with the `x` symbol. Another example: the
|
||||
symbol `x` with negative assumption is comparable with a natural number.
|
||||
|
||||
Also there are "least" elements, which are comparable with all others,
|
||||
and have a zero property (maximum or minimum for all elements).
|
||||
For example, in case of $\infty$, the allocation operation is terminated
|
||||
and only this value is returned.
|
||||
|
||||
Assumption:
|
||||
- if $A > B > C$ then $A > C$
|
||||
- if $A = B$ then $B$ can be removed
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Directed_complete_partial_order
|
||||
.. [2] https://en.wikipedia.org/wiki/Lattice_%28order%29
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Min : find minimum values
|
||||
"""
|
||||
zero = S.Infinity
|
||||
identity = S.NegativeInfinity
|
||||
|
||||
def fdiff( self, argindex ):
|
||||
from sympy.functions.special.delta_functions import Heaviside
|
||||
n = len(self.args)
|
||||
if 0 < argindex and argindex <= n:
|
||||
argindex -= 1
|
||||
if n == 2:
|
||||
return Heaviside(self.args[argindex] - self.args[1 - argindex])
|
||||
newargs = tuple([self.args[i] for i in range(n) if i != argindex])
|
||||
return Heaviside(self.args[argindex] - Max(*newargs))
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
||||
from sympy.functions.special.delta_functions import Heaviside
|
||||
return Add(*[j*Mul(*[Heaviside(j - i) for i in args if i!=j]) \
|
||||
for j in args])
|
||||
|
||||
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
||||
return _minmax_as_Piecewise('>=', *args)
|
||||
|
||||
def _eval_is_positive(self):
|
||||
return fuzzy_or(a.is_positive for a in self.args)
|
||||
|
||||
def _eval_is_nonnegative(self):
|
||||
return fuzzy_or(a.is_nonnegative for a in self.args)
|
||||
|
||||
def _eval_is_negative(self):
|
||||
return fuzzy_and(a.is_negative for a in self.args)
|
||||
|
||||
|
||||
class Min(MinMaxBase, Application):
|
||||
"""
|
||||
Return, if possible, the minimum value of the list.
|
||||
It is named ``Min`` and not ``min`` to avoid conflicts
|
||||
with the built-in function ``min``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Min, Symbol, oo
|
||||
>>> from sympy.abc import x, y
|
||||
>>> p = Symbol('p', positive=True)
|
||||
>>> n = Symbol('n', negative=True)
|
||||
|
||||
>>> Min(x, -2)
|
||||
Min(-2, x)
|
||||
>>> Min(x, -2).subs(x, 3)
|
||||
-2
|
||||
>>> Min(p, -3)
|
||||
-3
|
||||
>>> Min(x, y)
|
||||
Min(x, y)
|
||||
>>> Min(n, 8, p, -7, p, oo)
|
||||
Min(-7, n)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Max : find maximum values
|
||||
"""
|
||||
zero = S.NegativeInfinity
|
||||
identity = S.Infinity
|
||||
|
||||
def fdiff( self, argindex ):
|
||||
from sympy.functions.special.delta_functions import Heaviside
|
||||
n = len(self.args)
|
||||
if 0 < argindex and argindex <= n:
|
||||
argindex -= 1
|
||||
if n == 2:
|
||||
return Heaviside( self.args[1-argindex] - self.args[argindex] )
|
||||
newargs = tuple([ self.args[i] for i in range(n) if i != argindex])
|
||||
return Heaviside( Min(*newargs) - self.args[argindex] )
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
||||
from sympy.functions.special.delta_functions import Heaviside
|
||||
return Add(*[j*Mul(*[Heaviside(i-j) for i in args if i!=j]) \
|
||||
for j in args])
|
||||
|
||||
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
||||
return _minmax_as_Piecewise('<=', *args)
|
||||
|
||||
def _eval_is_positive(self):
|
||||
return fuzzy_and(a.is_positive for a in self.args)
|
||||
|
||||
def _eval_is_nonnegative(self):
|
||||
return fuzzy_and(a.is_nonnegative for a in self.args)
|
||||
|
||||
def _eval_is_negative(self):
|
||||
return fuzzy_or(a.is_negative for a in self.args)
|
||||
|
||||
|
||||
class Rem(DefinedFunction):
|
||||
"""Returns the remainder when ``p`` is divided by ``q`` where ``p`` is finite
|
||||
and ``q`` is not equal to zero. The result, ``p - int(p/q)*q``, has the same sign
|
||||
as the divisor.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
p : Expr
|
||||
Dividend.
|
||||
|
||||
q : Expr
|
||||
Divisor.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
``Rem`` corresponds to the ``%`` operator in C.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y
|
||||
>>> from sympy import Rem
|
||||
>>> Rem(x**3, y)
|
||||
Rem(x**3, y)
|
||||
>>> Rem(x**3, y).subs({x: -5, y: 3})
|
||||
-2
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Mod
|
||||
"""
|
||||
kind = NumberKind
|
||||
|
||||
@classmethod
|
||||
def eval(cls, p, q):
|
||||
"""Return the function remainder if both p, q are numbers and q is not
|
||||
zero.
|
||||
"""
|
||||
|
||||
if q.is_zero:
|
||||
raise ZeroDivisionError("Division by zero")
|
||||
if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False:
|
||||
return S.NaN
|
||||
if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1):
|
||||
return S.Zero
|
||||
|
||||
if q.is_Number:
|
||||
if p.is_Number:
|
||||
return p - Integer(p/q)*q
|
||||
+1517
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+1030
File diff suppressed because it is too large
Load Diff
+810
@@ -0,0 +1,810 @@
|
||||
from sympy.assumptions.refine import refine
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
from sympy.concrete.products import Product
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.function import expand_log
|
||||
from sympy.core.numbers import (E, Float, I, Rational, nan, oo, pi, zoo)
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.elementary.complexes import (adjoint, conjugate, re, sign, transpose)
|
||||
from sympy.functions.elementary.exponential import (LambertW, exp, exp_polar, log)
|
||||
from sympy.functions.elementary.hyperbolic import (cosh, sinh, tanh)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin, tan)
|
||||
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
||||
from sympy.polys.polytools import gcd
|
||||
from sympy.series.order import O
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.core.parameters import global_parameters
|
||||
from sympy.functions.elementary.exponential import match_real_imag
|
||||
from sympy.abc import x, y, z
|
||||
from sympy.core.expr import unchanged
|
||||
from sympy.core.function import ArgumentIndexError
|
||||
from sympy.testing.pytest import raises, XFAIL, _both_exp_pow
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_values():
|
||||
if global_parameters.exp_is_pow:
|
||||
assert type(exp(x)) is Pow
|
||||
else:
|
||||
assert type(exp(x)) is exp
|
||||
|
||||
k = Symbol('k', integer=True)
|
||||
|
||||
assert exp(nan) is nan
|
||||
|
||||
assert exp(oo) is oo
|
||||
assert exp(-oo) == 0
|
||||
|
||||
assert exp(0) == 1
|
||||
assert exp(1) == E
|
||||
assert exp(-1 + x).as_base_exp() == (S.Exp1, x - 1)
|
||||
assert exp(1 + x).as_base_exp() == (S.Exp1, x + 1)
|
||||
|
||||
assert exp(pi*I/2) == I
|
||||
assert exp(pi*I) == -1
|
||||
assert exp(pi*I*Rational(3, 2)) == -I
|
||||
assert exp(2*pi*I) == 1
|
||||
|
||||
assert refine(exp(pi*I*2*k)) == 1
|
||||
assert refine(exp(pi*I*2*(k + S.Half))) == -1
|
||||
assert refine(exp(pi*I*2*(k + Rational(1, 4)))) == I
|
||||
assert refine(exp(pi*I*2*(k + Rational(3, 4)))) == -I
|
||||
|
||||
assert exp(log(x)) == x
|
||||
assert exp(2*log(x)) == x**2
|
||||
assert exp(pi*log(x)) == x**pi
|
||||
|
||||
assert exp(17*log(x) + E*log(y)) == x**17 * y**E
|
||||
|
||||
assert exp(x*log(x)) != x**x
|
||||
assert exp(sin(x)*log(x)) != x
|
||||
|
||||
assert exp(3*log(x) + oo*x) == exp(oo*x) * x**3
|
||||
assert exp(4*log(x)*log(y) + 3*log(x)) == x**3 * exp(4*log(x)*log(y))
|
||||
|
||||
assert exp(-oo, evaluate=False).is_finite is True
|
||||
assert exp(oo, evaluate=False).is_finite is False
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_period():
|
||||
assert exp(I*pi*Rational(9, 4)) == exp(I*pi/4)
|
||||
assert exp(I*pi*Rational(46, 18)) == exp(I*pi*Rational(5, 9))
|
||||
assert exp(I*pi*Rational(25, 7)) == exp(I*pi*Rational(-3, 7))
|
||||
assert exp(I*pi*Rational(-19, 3)) == exp(-I*pi/3)
|
||||
assert exp(I*pi*Rational(37, 8)) - exp(I*pi*Rational(-11, 8)) == 0
|
||||
assert exp(I*pi*Rational(-5, 3)) / exp(I*pi*Rational(11, 5)) * exp(I*pi*Rational(148, 15)) == 1
|
||||
|
||||
assert exp(2 - I*pi*Rational(17, 5)) == exp(2 + I*pi*Rational(3, 5))
|
||||
assert exp(log(3) + I*pi*Rational(29, 9)) == 3 * exp(I*pi*Rational(-7, 9))
|
||||
|
||||
n = Symbol('n', integer=True)
|
||||
e = Symbol('e', even=True)
|
||||
assert exp(e*I*pi) == 1
|
||||
assert exp((e + 1)*I*pi) == -1
|
||||
assert exp((1 + 4*n)*I*pi/2) == I
|
||||
assert exp((-1 + 4*n)*I*pi/2) == -I
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_log():
|
||||
x = Symbol("x", real=True)
|
||||
assert log(exp(x)) == x
|
||||
assert exp(log(x)) == x
|
||||
|
||||
if not global_parameters.exp_is_pow:
|
||||
assert log(x).inverse() == exp
|
||||
assert exp(x).inverse() == log
|
||||
|
||||
y = Symbol("y", polar=True)
|
||||
assert log(exp_polar(z)) == z
|
||||
assert exp(log(y)) == y
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_expand():
|
||||
e = exp(log(Rational(2))*(1 + x) - log(Rational(2))*x)
|
||||
assert e.expand() == 2
|
||||
assert exp(x + y) != exp(x)*exp(y)
|
||||
assert exp(x + y).expand() == exp(x)*exp(y)
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp__as_base_exp():
|
||||
assert exp(x).as_base_exp() == (E, x)
|
||||
assert exp(2*x).as_base_exp() == (E, 2*x)
|
||||
assert exp(x*y).as_base_exp() == (E, x*y)
|
||||
assert exp(-x).as_base_exp() == (E, -x)
|
||||
|
||||
# Pow( *expr.as_base_exp() ) == expr invariant should hold
|
||||
assert E**x == exp(x)
|
||||
assert E**(2*x) == exp(2*x)
|
||||
assert E**(x*y) == exp(x*y)
|
||||
|
||||
assert exp(x).base is S.Exp1
|
||||
assert exp(x).exp == x
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_infinity():
|
||||
assert exp(I*y) != nan
|
||||
assert refine(exp(I*oo)) is nan
|
||||
assert refine(exp(-I*oo)) is nan
|
||||
assert exp(y*I*oo) != nan
|
||||
assert exp(zoo) is nan
|
||||
x = Symbol('x', extended_real=True, finite=False)
|
||||
assert exp(x).is_complex is None
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_subs():
|
||||
x = Symbol('x')
|
||||
e = (exp(3*log(x), evaluate=False)) # evaluates to x**3
|
||||
assert e.subs(x**3, y**3) == e
|
||||
assert e.subs(x**2, 5) == e
|
||||
assert (x**3).subs(x**2, y) != y**Rational(3, 2)
|
||||
assert exp(exp(x) + exp(x**2)).subs(exp(exp(x)), y) == y * exp(exp(x**2))
|
||||
assert exp(x).subs(E, y) == y**x
|
||||
x = symbols('x', real=True)
|
||||
assert exp(5*x).subs(exp(7*x), y) == y**Rational(5, 7)
|
||||
assert exp(2*x + 7).subs(exp(3*x), y) == y**Rational(2, 3) * exp(7)
|
||||
x = symbols('x', positive=True)
|
||||
assert exp(3*log(x)).subs(x**2, y) == y**Rational(3, 2)
|
||||
# differentiate between E and exp
|
||||
assert exp(exp(x + E)).subs(exp, 3) == 3**(3**(x + E))
|
||||
assert exp(exp(x + E)).subs(exp, sin) == sin(sin(x + E))
|
||||
assert exp(exp(x + E)).subs(E, 3) == 3**(3**(x + 3))
|
||||
assert exp(3).subs(E, sin) == sin(3)
|
||||
|
||||
|
||||
def test_exp_adjoint():
|
||||
x = Symbol('x', commutative=False)
|
||||
assert adjoint(exp(x)) == exp(adjoint(x))
|
||||
|
||||
|
||||
def test_exp_conjugate():
|
||||
assert conjugate(exp(x)) == exp(conjugate(x))
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_transpose():
|
||||
assert transpose(exp(x)) == exp(transpose(x))
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_rewrite():
|
||||
assert exp(x).rewrite(sin) == sinh(x) + cosh(x)
|
||||
assert exp(x*I).rewrite(cos) == cos(x) + I*sin(x)
|
||||
assert exp(1).rewrite(cos) == sinh(1) + cosh(1)
|
||||
assert exp(1).rewrite(sin) == sinh(1) + cosh(1)
|
||||
assert exp(1).rewrite(sin) == sinh(1) + cosh(1)
|
||||
assert exp(x).rewrite(tanh) == (1 + tanh(x/2))/(1 - tanh(x/2))
|
||||
assert exp(pi*I/4).rewrite(sqrt) == sqrt(2)/2 + sqrt(2)*I/2
|
||||
assert exp(pi*I/3).rewrite(sqrt) == S.Half + sqrt(3)*I/2
|
||||
if not global_parameters.exp_is_pow:
|
||||
assert exp(x*log(y)).rewrite(Pow) == y**x
|
||||
assert exp(log(x)*log(y)).rewrite(Pow) in [x**log(y), y**log(x)]
|
||||
assert exp(log(log(x))*y).rewrite(Pow) == log(x)**y
|
||||
|
||||
n = Symbol('n', integer=True)
|
||||
|
||||
assert Sum((exp(pi*I/2)/2)**n, (n, 0, oo)).rewrite(sqrt).doit() == Rational(4, 5) + I*2/5
|
||||
assert Sum((exp(pi*I/4)/2)**n, (n, 0, oo)).rewrite(sqrt).doit() == 1/(1 - sqrt(2)*(1 + I)/4)
|
||||
assert (Sum((exp(pi*I/3)/2)**n, (n, 0, oo)).rewrite(sqrt).doit().cancel()
|
||||
== 4*I/(sqrt(3) + 3*I))
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_leading_term():
|
||||
assert exp(x).as_leading_term(x) == 1
|
||||
assert exp(2 + x).as_leading_term(x) == exp(2)
|
||||
assert exp((2*x + 3) / (x+1)).as_leading_term(x) == exp(3)
|
||||
|
||||
# The following tests are commented, since now SymPy returns the
|
||||
# original function when the leading term in the series expansion does
|
||||
# not exist.
|
||||
# raises(NotImplementedError, lambda: exp(1/x).as_leading_term(x))
|
||||
# raises(NotImplementedError, lambda: exp((x + 1) / x**2).as_leading_term(x))
|
||||
# raises(NotImplementedError, lambda: exp(x + 1/x).as_leading_term(x))
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_taylor_term():
|
||||
x = symbols('x')
|
||||
assert exp(x).taylor_term(1, x) == x
|
||||
assert exp(x).taylor_term(3, x) == x**3/6
|
||||
assert exp(x).taylor_term(4, x) == x**4/24
|
||||
assert exp(x).taylor_term(-1, x) is S.Zero
|
||||
|
||||
|
||||
def test_exp_MatrixSymbol():
|
||||
A = MatrixSymbol("A", 2, 2)
|
||||
assert exp(A).has(exp)
|
||||
|
||||
|
||||
def test_exp_fdiff():
|
||||
x = Symbol('x')
|
||||
raises(ArgumentIndexError, lambda: exp(x).fdiff(2))
|
||||
|
||||
|
||||
def test_log_values():
|
||||
assert log(nan) is nan
|
||||
|
||||
assert log(oo) is oo
|
||||
assert log(-oo) is oo
|
||||
|
||||
assert log(zoo) is zoo
|
||||
assert log(-zoo) is zoo
|
||||
|
||||
assert log(0) is zoo
|
||||
|
||||
assert log(1) == 0
|
||||
assert log(-1) == I*pi
|
||||
|
||||
assert log(E) == 1
|
||||
assert log(-E).expand() == 1 + I*pi
|
||||
|
||||
assert unchanged(log, pi)
|
||||
assert log(-pi).expand() == log(pi) + I*pi
|
||||
|
||||
assert unchanged(log, 17)
|
||||
assert log(-17) == log(17) + I*pi
|
||||
|
||||
assert log(I) == I*pi/2
|
||||
assert log(-I) == -I*pi/2
|
||||
|
||||
assert log(17*I) == I*pi/2 + log(17)
|
||||
assert log(-17*I).expand() == -I*pi/2 + log(17)
|
||||
|
||||
assert log(oo*I) is oo
|
||||
assert log(-oo*I) is oo
|
||||
assert log(0, 2) is zoo
|
||||
assert log(0, 5) is zoo
|
||||
|
||||
assert exp(-log(3))**(-1) == 3
|
||||
|
||||
assert log(S.Half) == -log(2)
|
||||
assert log(2*3).func is log
|
||||
assert log(2*3**2).func is log
|
||||
|
||||
|
||||
def test_match_real_imag():
|
||||
x, y = symbols('x,y', real=True)
|
||||
i = Symbol('i', imaginary=True)
|
||||
assert match_real_imag(S.One) == (1, 0)
|
||||
assert match_real_imag(I) == (0, 1)
|
||||
assert match_real_imag(3 - 5*I) == (3, -5)
|
||||
assert match_real_imag(-sqrt(3) + S.Half*I) == (-sqrt(3), S.Half)
|
||||
assert match_real_imag(x + y*I) == (x, y)
|
||||
assert match_real_imag(x*I + y*I) == (0, x + y)
|
||||
assert match_real_imag((x + y)*I) == (0, x + y)
|
||||
assert match_real_imag(Rational(-2, 3)*i*I) == (None, None)
|
||||
assert match_real_imag(1 - 2*i) == (None, None)
|
||||
assert match_real_imag(sqrt(2)*(3 - 5*I)) == (None, None)
|
||||
|
||||
|
||||
def test_log_exact():
|
||||
# check for pi/2, pi/3, pi/4, pi/6, pi/8, pi/12; pi/5, pi/10:
|
||||
for n in range(-23, 24):
|
||||
if gcd(n, 24) != 1:
|
||||
assert log(exp(n*I*pi/24).rewrite(sqrt)) == n*I*pi/24
|
||||
for n in range(-9, 10):
|
||||
assert log(exp(n*I*pi/10).rewrite(sqrt)) == n*I*pi/10
|
||||
|
||||
assert log(S.Half - I*sqrt(3)/2) == -I*pi/3
|
||||
assert log(Rational(-1, 2) + I*sqrt(3)/2) == I*pi*Rational(2, 3)
|
||||
assert log(-sqrt(2)/2 - I*sqrt(2)/2) == -I*pi*Rational(3, 4)
|
||||
assert log(-sqrt(3)/2 - I*S.Half) == -I*pi*Rational(5, 6)
|
||||
|
||||
assert log(Rational(-1, 4) + sqrt(5)/4 - I*sqrt(sqrt(5)/8 + Rational(5, 8))) == -I*pi*Rational(2, 5)
|
||||
assert log(sqrt(Rational(5, 8) - sqrt(5)/8) + I*(Rational(1, 4) + sqrt(5)/4)) == I*pi*Rational(3, 10)
|
||||
assert log(-sqrt(sqrt(2)/4 + S.Half) + I*sqrt(S.Half - sqrt(2)/4)) == I*pi*Rational(7, 8)
|
||||
assert log(-sqrt(6)/4 - sqrt(2)/4 + I*(-sqrt(6)/4 + sqrt(2)/4)) == -I*pi*Rational(11, 12)
|
||||
|
||||
assert log(-1 + I*sqrt(3)) == log(2) + I*pi*Rational(2, 3)
|
||||
assert log(5 + 5*I) == log(5*sqrt(2)) + I*pi/4
|
||||
assert log(sqrt(-12)) == log(2*sqrt(3)) + I*pi/2
|
||||
assert log(-sqrt(6) + sqrt(2) - I*sqrt(6) - I*sqrt(2)) == log(4) - I*pi*Rational(7, 12)
|
||||
assert log(-sqrt(6-3*sqrt(2)) - I*sqrt(6+3*sqrt(2))) == log(2*sqrt(3)) - I*pi*Rational(5, 8)
|
||||
assert log(1 + I*sqrt(2-sqrt(2))/sqrt(2+sqrt(2))) == log(2/sqrt(sqrt(2) + 2)) + I*pi/8
|
||||
assert log(cos(pi*Rational(7, 12)) + I*sin(pi*Rational(7, 12))) == I*pi*Rational(7, 12)
|
||||
assert log(cos(pi*Rational(6, 5)) + I*sin(pi*Rational(6, 5))) == I*pi*Rational(-4, 5)
|
||||
|
||||
assert log(5*(1 + I)/sqrt(2)) == log(5) + I*pi/4
|
||||
assert log(sqrt(2)*(-sqrt(3) + 1 - sqrt(3)*I - I)) == log(4) - I*pi*Rational(7, 12)
|
||||
assert log(-sqrt(2)*(1 - I*sqrt(3))) == log(2*sqrt(2)) + I*pi*Rational(2, 3)
|
||||
assert log(sqrt(3)*I*(-sqrt(6 - 3*sqrt(2)) - I*sqrt(3*sqrt(2) + 6))) == log(6) - I*pi/8
|
||||
|
||||
zero = (1 + sqrt(2))**2 - 3 - 2*sqrt(2)
|
||||
assert log(zero - I*sqrt(3)) == log(sqrt(3)) - I*pi/2
|
||||
assert unchanged(log, zero + I*zero) or log(zero + zero*I) is zoo
|
||||
|
||||
# bail quickly if no obvious simplification is possible:
|
||||
assert unchanged(log, (sqrt(2)-1/sqrt(sqrt(3)+I))**1000)
|
||||
# beware of non-real coefficients
|
||||
assert unchanged(log, sqrt(2-sqrt(5))*(1 + I))
|
||||
|
||||
|
||||
def test_log_base():
|
||||
assert log(1, 2) == 0
|
||||
assert log(2, 2) == 1
|
||||
assert log(3, 2) == log(3)/log(2)
|
||||
assert log(6, 2) == 1 + log(3)/log(2)
|
||||
assert log(6, 3) == 1 + log(2)/log(3)
|
||||
assert log(2**3, 2) == 3
|
||||
assert log(3**3, 3) == 3
|
||||
assert log(5, 1) is zoo
|
||||
assert log(1, 1) is nan
|
||||
assert log(Rational(2, 3), 10) == log(Rational(2, 3))/log(10)
|
||||
assert log(Rational(2, 3), Rational(1, 3)) == -log(2)/log(3) + 1
|
||||
assert log(Rational(2, 3), Rational(2, 5)) == \
|
||||
log(Rational(2, 3))/log(Rational(2, 5))
|
||||
# issue 17148
|
||||
assert log(Rational(8, 3), 2) == -log(3)/log(2) + 3
|
||||
|
||||
|
||||
def test_log_symbolic():
|
||||
assert log(x, exp(1)) == log(x)
|
||||
assert log(exp(x)) != x
|
||||
|
||||
assert log(x, exp(1)) == log(x)
|
||||
assert log(x*y) != log(x) + log(y)
|
||||
assert log(x/y).expand() != log(x) - log(y)
|
||||
assert log(x/y).expand(force=True) == log(x) - log(y)
|
||||
assert log(x**y).expand() != y*log(x)
|
||||
assert log(x**y).expand(force=True) == y*log(x)
|
||||
|
||||
assert log(x, 2) == log(x)/log(2)
|
||||
assert log(E, 2) == 1/log(2)
|
||||
|
||||
p, q = symbols('p,q', positive=True)
|
||||
r = Symbol('r', real=True)
|
||||
|
||||
assert log(p**2) != 2*log(p)
|
||||
assert log(p**2).expand() == 2*log(p)
|
||||
assert log(x**2).expand() != 2*log(x)
|
||||
assert log(p**q) != q*log(p)
|
||||
assert log(exp(p)) == p
|
||||
assert log(p*q) != log(p) + log(q)
|
||||
assert log(p*q).expand() == log(p) + log(q)
|
||||
|
||||
assert log(-sqrt(3)) == log(sqrt(3)) + I*pi
|
||||
assert log(-exp(p)) != p + I*pi
|
||||
assert log(-exp(x)).expand() != x + I*pi
|
||||
assert log(-exp(r)).expand() == r + I*pi
|
||||
|
||||
assert log(x**y) != y*log(x)
|
||||
|
||||
assert (log(x**-5)**-1).expand() != -1/log(x)/5
|
||||
assert (log(p**-5)**-1).expand() == -1/log(p)/5
|
||||
assert log(-x).func is log and log(-x).args[0] == -x
|
||||
assert log(-p).func is log and log(-p).args[0] == -p
|
||||
|
||||
|
||||
def test_log_exp():
|
||||
assert log(exp(4*I*pi)) == 0 # exp evaluates
|
||||
assert log(exp(-5*I*pi)) == I*pi # exp evaluates
|
||||
assert log(exp(I*pi*Rational(19, 4))) == I*pi*Rational(3, 4)
|
||||
assert log(exp(I*pi*Rational(25, 7))) == I*pi*Rational(-3, 7)
|
||||
assert log(exp(-5*I)) == -5*I + 2*I*pi
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_assumptions():
|
||||
r = Symbol('r', real=True)
|
||||
i = Symbol('i', imaginary=True)
|
||||
for e in exp, exp_polar:
|
||||
assert e(x).is_real is None
|
||||
assert e(x).is_imaginary is None
|
||||
assert e(i).is_real is None
|
||||
assert e(i).is_imaginary is None
|
||||
assert e(r).is_real is True
|
||||
assert e(r).is_imaginary is False
|
||||
assert e(re(x)).is_extended_real is True
|
||||
assert e(re(x)).is_imaginary is False
|
||||
|
||||
assert Pow(E, I*pi, evaluate=False).is_imaginary == False
|
||||
assert Pow(E, 2*I*pi, evaluate=False).is_imaginary == False
|
||||
assert Pow(E, I*pi/2, evaluate=False).is_imaginary == True
|
||||
assert Pow(E, I*pi/3, evaluate=False).is_imaginary is None
|
||||
|
||||
assert exp(0, evaluate=False).is_algebraic
|
||||
|
||||
a = Symbol('a', algebraic=True)
|
||||
an = Symbol('an', algebraic=True, nonzero=True)
|
||||
r = Symbol('r', rational=True)
|
||||
rn = Symbol('rn', rational=True, nonzero=True)
|
||||
assert exp(a).is_algebraic is None
|
||||
assert exp(an).is_algebraic is False
|
||||
assert exp(pi*r).is_algebraic is None
|
||||
assert exp(pi*rn).is_algebraic is False
|
||||
|
||||
assert exp(0, evaluate=False).is_algebraic is True
|
||||
assert exp(I*pi/3, evaluate=False).is_algebraic is True
|
||||
assert exp(I*pi*r, evaluate=False).is_algebraic is True
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_exp_AccumBounds():
|
||||
assert exp(AccumBounds(1, 2)) == AccumBounds(E, E**2)
|
||||
|
||||
|
||||
def test_log_assumptions():
|
||||
p = symbols('p', positive=True)
|
||||
n = symbols('n', negative=True)
|
||||
z = symbols('z', zero=True)
|
||||
x = symbols('x', infinite=True, extended_positive=True)
|
||||
|
||||
assert log(z).is_positive is False
|
||||
assert log(x).is_extended_positive is True
|
||||
assert log(2) > 0
|
||||
assert log(1, evaluate=False).is_zero
|
||||
assert log(1 + z).is_zero
|
||||
assert log(p).is_zero is None
|
||||
assert log(n).is_zero is False
|
||||
assert log(0.5).is_negative is True
|
||||
assert log(exp(p) + 1).is_positive
|
||||
|
||||
assert log(1, evaluate=False).is_algebraic
|
||||
assert log(42, evaluate=False).is_algebraic is False
|
||||
|
||||
assert log(1 + z).is_rational
|
||||
|
||||
|
||||
def test_log_hashing():
|
||||
assert x != log(log(x))
|
||||
assert hash(x) != hash(log(log(x)))
|
||||
assert log(x) != log(log(log(x)))
|
||||
|
||||
e = 1/log(log(x) + log(log(x)))
|
||||
assert e.base.func is log
|
||||
e = 1/log(log(x) + log(log(log(x))))
|
||||
assert e.base.func is log
|
||||
|
||||
e = log(log(x))
|
||||
assert e.func is log
|
||||
assert x.func is not log
|
||||
assert hash(log(log(x))) != hash(x)
|
||||
assert e != x
|
||||
|
||||
|
||||
def test_log_sign():
|
||||
assert sign(log(2)) == 1
|
||||
|
||||
|
||||
def test_log_expand_complex():
|
||||
assert log(1 + I).expand(complex=True) == log(2)/2 + I*pi/4
|
||||
assert log(1 - sqrt(2)).expand(complex=True) == log(sqrt(2) - 1) + I*pi
|
||||
|
||||
|
||||
def test_log_apply_evalf():
|
||||
value = (log(3)/log(2) - 1).evalf()
|
||||
assert value.epsilon_eq(Float("0.58496250072115618145373"))
|
||||
|
||||
|
||||
def test_log_leading_term():
|
||||
p = Symbol('p')
|
||||
|
||||
# Test for STEP 3
|
||||
assert log(1 + x + x**2).as_leading_term(x, cdir=1) == x
|
||||
# Test for STEP 4
|
||||
assert log(2*x).as_leading_term(x, cdir=1) == log(x) + log(2)
|
||||
assert log(2*x).as_leading_term(x, cdir=-1) == log(x) + log(2)
|
||||
assert log(-2*x).as_leading_term(x, cdir=1, logx=p) == p + log(2) + I*pi
|
||||
assert log(-2*x).as_leading_term(x, cdir=-1, logx=p) == p + log(2) - I*pi
|
||||
# Test for STEP 5
|
||||
assert log(-2*x + (3 - I)*x**2).as_leading_term(x, cdir=1) == log(x) + log(2) - I*pi
|
||||
assert log(-2*x + (3 - I)*x**2).as_leading_term(x, cdir=-1) == log(x) + log(2) - I*pi
|
||||
assert log(2*x + (3 - I)*x**2).as_leading_term(x, cdir=1) == log(x) + log(2)
|
||||
assert log(2*x + (3 - I)*x**2).as_leading_term(x, cdir=-1) == log(x) + log(2) - 2*I*pi
|
||||
assert log(-1 + x - I*x**2 + I*x**3).as_leading_term(x, cdir=1) == -I*pi
|
||||
assert log(-1 + x - I*x**2 + I*x**3).as_leading_term(x, cdir=-1) == -I*pi
|
||||
assert log(-1/(1 - x)).as_leading_term(x, cdir=1) == I*pi
|
||||
assert log(-1/(1 - x)).as_leading_term(x, cdir=-1) == I*pi
|
||||
|
||||
|
||||
def test_log_nseries():
|
||||
p = Symbol('p')
|
||||
assert log(1/x)._eval_nseries(x, 4, logx=-p, cdir=1) == p
|
||||
assert log(1/x)._eval_nseries(x, 4, logx=-p, cdir=-1) == p + 2*I*pi
|
||||
assert log(x - 1)._eval_nseries(x, 4, None, I) == I*pi - x - x**2/2 - x**3/3 + O(x**4)
|
||||
assert log(x - 1)._eval_nseries(x, 4, None, -I) == -I*pi - x - x**2/2 - x**3/3 + O(x**4)
|
||||
assert log(I*x + I*x**3 - 1)._eval_nseries(x, 3, None, 1) == I*pi - I*x + x**2/2 + O(x**3)
|
||||
assert log(I*x + I*x**3 - 1)._eval_nseries(x, 3, None, -1) == -I*pi - I*x + x**2/2 + O(x**3)
|
||||
assert log(I*x**2 + I*x**3 - 1)._eval_nseries(x, 3, None, 1) == I*pi - I*x**2 + O(x**3)
|
||||
assert log(I*x**2 + I*x**3 - 1)._eval_nseries(x, 3, None, -1) == I*pi - I*x**2 + O(x**3)
|
||||
assert log(2*x + (3 - I)*x**2)._eval_nseries(x, 3, None, 1) == log(2) + log(x) + \
|
||||
x*(S(3)/2 - I/2) + x**2*(-1 + 3*I/4) + O(x**3)
|
||||
assert log(2*x + (3 - I)*x**2)._eval_nseries(x, 3, None, -1) == -2*I*pi + log(2) + \
|
||||
log(x) - x*(-S(3)/2 + I/2) + x**2*(-1 + 3*I/4) + O(x**3)
|
||||
assert log(-2*x + (3 - I)*x**2)._eval_nseries(x, 3, None, 1) == -I*pi + log(2) + log(x) + \
|
||||
x*(-S(3)/2 + I/2) + x**2*(-1 + 3*I/4) + O(x**3)
|
||||
assert log(-2*x + (3 - I)*x**2)._eval_nseries(x, 3, None, -1) == -I*pi + log(2) + log(x) - \
|
||||
x*(S(3)/2 - I/2) + x**2*(-1 + 3*I/4) + O(x**3)
|
||||
assert log(sqrt(-I*x**2 - 3)*sqrt(-I*x**2 - 1) - 2)._eval_nseries(x, 3, None, 1) == -I*pi + \
|
||||
log(sqrt(3) + 2) + 2*sqrt(3)*I*x**2/(3*sqrt(3) + 6) + O(x**3)
|
||||
assert log(-1/(1 - x))._eval_nseries(x, 3, None, 1) == I*pi + x + x**2/2 + O(x**3)
|
||||
assert log(-1/(1 - x))._eval_nseries(x, 3, None, -1) == I*pi + x + x**2/2 + O(x**3)
|
||||
|
||||
|
||||
def test_log_series():
|
||||
# Note Series at infinities other than oo/-oo were introduced as a part of
|
||||
# pull request 23798. Refer https://github.com/sympy/sympy/pull/23798 for
|
||||
# more information.
|
||||
expr1 = log(1 + x)
|
||||
expr2 = log(x + sqrt(x**2 + 1))
|
||||
|
||||
assert expr1.series(x, x0=I*oo, n=4) == 1/(3*x**3) - 1/(2*x**2) + 1/x + \
|
||||
I*pi/2 - log(I/x) + O(x**(-4), (x, oo*I))
|
||||
assert expr1.series(x, x0=-I*oo, n=4) == 1/(3*x**3) - 1/(2*x**2) + 1/x - \
|
||||
I*pi/2 - log(-I/x) + O(x**(-4), (x, -oo*I))
|
||||
assert expr2.series(x, x0=I*oo, n=4) == 1/(4*x**2) + I*pi/2 + log(2) - \
|
||||
log(I/x) + O(x**(-4), (x, oo*I))
|
||||
assert expr2.series(x, x0=-I*oo, n=4) == -1/(4*x**2) - I*pi/2 - log(2) + \
|
||||
log(-I/x) + O(x**(-4), (x, -oo*I))
|
||||
|
||||
|
||||
def test_log_expand():
|
||||
w = Symbol("w", positive=True)
|
||||
e = log(w**(log(5)/log(3)))
|
||||
assert e.expand() == log(5)/log(3) * log(w)
|
||||
x, y, z = symbols('x,y,z', positive=True)
|
||||
assert log(x*(y + z)).expand(mul=False) == log(x) + log(y + z)
|
||||
assert log(log(x**2)*log(y*z)).expand() in [log(2*log(x)*log(y) +
|
||||
2*log(x)*log(z)), log(log(x)*log(z) + log(y)*log(x)) + log(2),
|
||||
log((log(y) + log(z))*log(x)) + log(2)]
|
||||
assert log(x**log(x**2)).expand(deep=False) == log(x)*log(x**2)
|
||||
assert log(x**log(x**2)).expand() == 2*log(x)**2
|
||||
x, y = symbols('x,y')
|
||||
assert log(x*y).expand(force=True) == log(x) + log(y)
|
||||
assert log(x**y).expand(force=True) == y*log(x)
|
||||
assert log(exp(x)).expand(force=True) == x
|
||||
|
||||
# there's generally no need to expand out logs since this requires
|
||||
# factoring and if simplification is sought, it's cheaper to put
|
||||
# logs together than it is to take them apart.
|
||||
assert log(2*3**2).expand() != 2*log(3) + log(2)
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_log_expand_fail():
|
||||
x, y, z = symbols('x,y,z', positive=True)
|
||||
assert (log(x*(y + z))*(x + y)).expand(mul=True, log=True) == y*log(
|
||||
x) + y*log(y + z) + z*log(x) + z*log(y + z)
|
||||
|
||||
|
||||
def test_log_simplify():
|
||||
x = Symbol("x", positive=True)
|
||||
assert log(x**2).expand() == 2*log(x)
|
||||
assert expand_log(log(x**(2 + log(2)))) == (2 + log(2))*log(x)
|
||||
|
||||
z = Symbol('z')
|
||||
assert log(sqrt(z)).expand() == log(z)/2
|
||||
assert expand_log(log(z**(log(2) - 1))) == (log(2) - 1)*log(z)
|
||||
assert log(z**(-1)).expand() != -log(z)
|
||||
assert log(z**(x/(x+1))).expand() == x*log(z)/(x + 1)
|
||||
|
||||
|
||||
def test_log_AccumBounds():
|
||||
assert log(AccumBounds(1, E)) == AccumBounds(0, 1)
|
||||
assert log(AccumBounds(0, E)) == AccumBounds(-oo, 1)
|
||||
assert log(AccumBounds(-1, E)) == S.NaN
|
||||
assert log(AccumBounds(0, oo)) == AccumBounds(-oo, oo)
|
||||
assert log(AccumBounds(-oo, 0)) == S.NaN
|
||||
assert log(AccumBounds(-oo, oo)) == S.NaN
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_lambertw():
|
||||
k = Symbol('k')
|
||||
|
||||
assert LambertW(x, 0) == LambertW(x)
|
||||
assert LambertW(x, 0, evaluate=False) != LambertW(x)
|
||||
assert LambertW(0) == 0
|
||||
assert LambertW(E) == 1
|
||||
assert LambertW(-1/E) == -1
|
||||
assert LambertW(-log(2)/2) == -log(2)
|
||||
assert LambertW(oo) is oo
|
||||
assert LambertW(0, 1) is -oo
|
||||
assert LambertW(0, 42) is -oo
|
||||
assert LambertW(-pi/2, -1) == -I*pi/2
|
||||
assert LambertW(-1/E, -1) == -1
|
||||
assert LambertW(-2*exp(-2), -1) == -2
|
||||
assert LambertW(2*log(2)) == log(2)
|
||||
assert LambertW(-pi/2) == I*pi/2
|
||||
assert LambertW(exp(1 + E)) == E
|
||||
|
||||
assert LambertW(x**2).diff(x) == 2*LambertW(x**2)/x/(1 + LambertW(x**2))
|
||||
assert LambertW(x, k).diff(x) == LambertW(x, k)/x/(1 + LambertW(x, k))
|
||||
|
||||
assert LambertW(sqrt(2)).evalf(30).epsilon_eq(
|
||||
Float("0.701338383413663009202120278965", 30), 1e-29)
|
||||
assert re(LambertW(2, -1)).evalf().epsilon_eq(Float("-0.834310366631110"))
|
||||
|
||||
assert LambertW(-1).is_real is False # issue 5215
|
||||
assert LambertW(2, evaluate=False).is_real
|
||||
p = Symbol('p', positive=True)
|
||||
assert LambertW(p, evaluate=False).is_real
|
||||
assert LambertW(p - 1, evaluate=False).is_real is None
|
||||
assert LambertW(-p - 2/S.Exp1, evaluate=False).is_real is False
|
||||
assert LambertW(S.Half, -1, evaluate=False).is_real is False
|
||||
assert LambertW(Rational(-1, 10), -1, evaluate=False).is_real
|
||||
assert LambertW(-10, -1, evaluate=False).is_real is False
|
||||
assert LambertW(-2, 2, evaluate=False).is_real is False
|
||||
|
||||
assert LambertW(0, evaluate=False).is_algebraic
|
||||
na = Symbol('na', nonzero=True, algebraic=True)
|
||||
assert LambertW(na).is_algebraic is False
|
||||
assert LambertW(p).is_zero is False
|
||||
n = Symbol('n', negative=True)
|
||||
assert LambertW(n).is_zero is False
|
||||
|
||||
|
||||
def test_issue_5673():
|
||||
e = LambertW(-1)
|
||||
assert e.is_comparable is False
|
||||
assert e.is_positive is not True
|
||||
e2 = 1 - 1/(1 - exp(-1000))
|
||||
assert e2.is_positive is not True
|
||||
e3 = -2 + exp(exp(LambertW(log(2)))*LambertW(log(2)))
|
||||
assert e3.is_nonzero is not True
|
||||
|
||||
|
||||
def test_log_fdiff():
|
||||
x = Symbol('x')
|
||||
raises(ArgumentIndexError, lambda: log(x).fdiff(2))
|
||||
|
||||
|
||||
def test_log_taylor_term():
|
||||
x = symbols('x')
|
||||
assert log(x).taylor_term(0, x) == x
|
||||
assert log(x).taylor_term(1, x) == -x**2/2
|
||||
assert log(x).taylor_term(4, x) == x**5/5
|
||||
assert log(x).taylor_term(-1, x) is S.Zero
|
||||
|
||||
|
||||
def test_exp_expand_NC():
|
||||
A, B, C = symbols('A,B,C', commutative=False)
|
||||
|
||||
assert exp(A + B).expand() == exp(A + B)
|
||||
assert exp(A + B + C).expand() == exp(A + B + C)
|
||||
assert exp(x + y).expand() == exp(x)*exp(y)
|
||||
assert exp(x + y + z).expand() == exp(x)*exp(y)*exp(z)
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_as_numer_denom():
|
||||
n = symbols('n', negative=True)
|
||||
assert exp(x).as_numer_denom() == (exp(x), 1)
|
||||
assert exp(-x).as_numer_denom() == (1, exp(x))
|
||||
assert exp(-2*x).as_numer_denom() == (1, exp(2*x))
|
||||
assert exp(-2).as_numer_denom() == (1, exp(2))
|
||||
assert exp(n).as_numer_denom() == (1, exp(-n))
|
||||
assert exp(-n).as_numer_denom() == (exp(-n), 1)
|
||||
assert exp(-I*x).as_numer_denom() == (1, exp(I*x))
|
||||
assert exp(-I*n).as_numer_denom() == (1, exp(I*n))
|
||||
assert exp(-n).as_numer_denom() == (exp(-n), 1)
|
||||
# Check noncommutativity
|
||||
a = symbols('a', commutative=False)
|
||||
assert exp(-a).as_numer_denom() == (exp(-a), 1)
|
||||
|
||||
|
||||
@_both_exp_pow
|
||||
def test_polar():
|
||||
x, y = symbols('x y', polar=True)
|
||||
|
||||
assert abs(exp_polar(I*4)) == 1
|
||||
assert abs(exp_polar(0)) == 1
|
||||
assert abs(exp_polar(2 + 3*I)) == exp(2)
|
||||
assert exp_polar(I*10).n() == exp_polar(I*10)
|
||||
|
||||
assert log(exp_polar(z)) == z
|
||||
assert log(x*y).expand() == log(x) + log(y)
|
||||
assert log(x**z).expand() == z*log(x)
|
||||
|
||||
assert exp_polar(3).exp == 3
|
||||
|
||||
# Compare exp(1.0*pi*I).
|
||||
assert (exp_polar(1.0*pi*I).n(n=5)).as_real_imag()[1] >= 0
|
||||
|
||||
assert exp_polar(0).is_rational is True # issue 8008
|
||||
|
||||
|
||||
def test_exp_summation():
|
||||
w = symbols("w")
|
||||
m, n, i, j = symbols("m n i j")
|
||||
expr = exp(Sum(w*i, (i, 0, n), (j, 0, m)))
|
||||
assert expr.expand() == Product(exp(w*i), (i, 0, n), (j, 0, m))
|
||||
|
||||
|
||||
def test_log_product():
|
||||
from sympy.abc import n, m
|
||||
|
||||
i, j = symbols('i,j', positive=True, integer=True)
|
||||
x, y = symbols('x,y', positive=True)
|
||||
z = symbols('z', real=True)
|
||||
w = symbols('w')
|
||||
|
||||
expr = log(Product(x**i, (i, 1, n)))
|
||||
assert simplify(expr) == expr
|
||||
assert expr.expand() == Sum(i*log(x), (i, 1, n))
|
||||
expr = log(Product(x**i*y**j, (i, 1, n), (j, 1, m)))
|
||||
assert simplify(expr) == expr
|
||||
assert expr.expand() == Sum(i*log(x) + j*log(y), (i, 1, n), (j, 1, m))
|
||||
|
||||
expr = log(Product(-2, (n, 0, 4)))
|
||||
assert simplify(expr) == expr
|
||||
assert expr.expand() == expr
|
||||
assert expr.expand(force=True) == Sum(log(-2), (n, 0, 4))
|
||||
|
||||
expr = log(Product(exp(z*i), (i, 0, n)))
|
||||
assert expr.expand() == Sum(z*i, (i, 0, n))
|
||||
|
||||
expr = log(Product(exp(w*i), (i, 0, n)))
|
||||
assert expr.expand() == expr
|
||||
assert expr.expand(force=True) == Sum(w*i, (i, 0, n))
|
||||
|
||||
expr = log(Product(i**2*abs(j), (i, 1, n), (j, 1, m)))
|
||||
assert expr.expand() == Sum(2*log(i) + log(j), (i, 1, n), (j, 1, m))
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_log_product_simplify_to_sum():
|
||||
from sympy.abc import n, m
|
||||
i, j = symbols('i,j', positive=True, integer=True)
|
||||
x, y = symbols('x,y', positive=True)
|
||||
assert simplify(log(Product(x**i, (i, 1, n)))) == Sum(i*log(x), (i, 1, n))
|
||||
assert simplify(log(Product(x**i*y**j, (i, 1, n), (j, 1, m)))) == \
|
||||
Sum(i*log(x) + j*log(y), (i, 1, n), (j, 1, m))
|
||||
|
||||
|
||||
def test_issue_8866():
|
||||
assert simplify(log(x, 10, evaluate=False)) == simplify(log(x, 10))
|
||||
assert expand_log(log(x, 10, evaluate=False)) == expand_log(log(x, 10))
|
||||
|
||||
y = Symbol('y', positive=True)
|
||||
l1 = log(exp(y), exp(10))
|
||||
b1 = log(exp(y), exp(5))
|
||||
l2 = log(exp(y), exp(10), evaluate=False)
|
||||
b2 = log(exp(y), exp(5), evaluate=False)
|
||||
assert simplify(log(l1, b1)) == simplify(log(l2, b2))
|
||||
assert expand_log(log(l1, b1)) == expand_log(log(l2, b2))
|
||||
|
||||
|
||||
def test_log_expand_factor():
|
||||
assert (log(18)/log(3) - 2).expand(factor=True) == log(2)/log(3)
|
||||
assert (log(12)/log(2)).expand(factor=True) == log(3)/log(2) + 2
|
||||
assert (log(15)/log(3)).expand(factor=True) == 1 + log(5)/log(3)
|
||||
assert (log(2)/(-log(12) + log(24))).expand(factor=True) == 1
|
||||
|
||||
assert expand_log(log(12), factor=True) == log(3) + 2*log(2)
|
||||
assert expand_log(log(21)/log(7), factor=False) == log(3)/log(7) + 1
|
||||
assert expand_log(log(45)/log(5) + log(20), factor=False) == \
|
||||
1 + 2*log(3)/log(5) + log(20)
|
||||
assert expand_log(log(45)/log(5) + log(26), factor=True) == \
|
||||
log(2) + log(13) + (log(5) + 2*log(3))/log(5)
|
||||
|
||||
|
||||
def test_issue_9116():
|
||||
n = Symbol('n', positive=True, integer=True)
|
||||
assert log(n).is_nonnegative is True
|
||||
|
||||
|
||||
def test_issue_18473():
|
||||
assert exp(x*log(cos(1/x))).as_leading_term(x) == S.NaN
|
||||
assert exp(x*log(tan(1/x))).as_leading_term(x) == S.NaN
|
||||
assert log(cos(1/x)).as_leading_term(x) == S.NaN
|
||||
assert log(tan(1/x)).as_leading_term(x) == S.NaN
|
||||
assert log(cos(1/x) + 2).as_leading_term(x) == AccumBounds(0, log(3))
|
||||
assert exp(x*log(cos(1/x) + 2)).as_leading_term(x) == 1
|
||||
assert log(cos(1/x) - 2).as_leading_term(x) == S.NaN
|
||||
assert exp(x*log(cos(1/x) - 2)).as_leading_term(x) == S.NaN
|
||||
assert log(cos(1/x) + 1).as_leading_term(x) == AccumBounds(-oo, log(2))
|
||||
assert exp(x*log(cos(1/x) + 1)).as_leading_term(x) == AccumBounds(0, 1)
|
||||
assert log(sin(1/x)**2).as_leading_term(x) == AccumBounds(-oo, 0)
|
||||
assert exp(x*log(sin(1/x)**2)).as_leading_term(x) == AccumBounds(0, 1)
|
||||
assert log(tan(1/x)**2).as_leading_term(x) == AccumBounds(-oo, oo)
|
||||
assert exp(2*x*(log(tan(1/x)**2))).as_leading_term(x) == AccumBounds(0, oo)
|
||||
+1553
File diff suppressed because it is too large
Load Diff
+688
@@ -0,0 +1,688 @@
|
||||
from sympy.calculus.accumulationbounds import AccumBounds
|
||||
from sympy.core.numbers import (E, Float, I, Rational, Integer, nan, oo, pi, zoo)
|
||||
from sympy.core.relational import (Eq, Ge, Gt, Le, Lt, Ne)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.integers import (ceiling, floor, frac)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import sin, cos, tan, asin
|
||||
from sympy.polys.rootoftools import RootOf, CRootOf
|
||||
from sympy import Integers
|
||||
from sympy.sets.sets import Interval
|
||||
from sympy.sets.fancysets import ImageSet
|
||||
from sympy.core.function import Lambda
|
||||
|
||||
from sympy.core.expr import unchanged
|
||||
from sympy.testing.pytest import XFAIL, raises
|
||||
|
||||
x = Symbol('x')
|
||||
i = Symbol('i', imaginary=True)
|
||||
y = Symbol('y', real=True)
|
||||
k, n = symbols('k,n', integer=True)
|
||||
b = Symbol('b', real=True, noninteger=True)
|
||||
m = Symbol('m', positive=True)
|
||||
|
||||
|
||||
def test_floor():
|
||||
|
||||
assert floor(nan) is nan
|
||||
|
||||
assert floor(oo) is oo
|
||||
assert floor(-oo) is -oo
|
||||
assert floor(zoo) is zoo
|
||||
|
||||
assert floor(0) == 0
|
||||
|
||||
assert floor(1) == 1
|
||||
assert floor(-1) == -1
|
||||
|
||||
assert floor(I*log(asin(5)/abs(asin(5)))) == 0
|
||||
assert floor(-I*log(asin(7)/abs(asin(7)))) == -2
|
||||
|
||||
assert floor(E) == 2
|
||||
assert floor(-E) == -3
|
||||
|
||||
assert floor(2*E) == 5
|
||||
assert floor(-2*E) == -6
|
||||
|
||||
assert floor(pi) == 3
|
||||
assert floor(-pi) == -4
|
||||
|
||||
assert floor(S.Half) == 0
|
||||
assert floor(Rational(-1, 2)) == -1
|
||||
|
||||
assert floor(Rational(7, 3)) == 2
|
||||
assert floor(Rational(-7, 3)) == -3
|
||||
assert floor(-Rational(7, 3)) == -3
|
||||
|
||||
assert floor(Float(17.0)) == 17
|
||||
assert floor(-Float(17.0)) == -17
|
||||
|
||||
assert floor(Float(7.69)) == 7
|
||||
assert floor(-Float(7.69)) == -8
|
||||
|
||||
assert floor(1/(m+1)) == S.Zero
|
||||
assert floor((m+2)/(m+1)) == S.One
|
||||
assert floor(-1/(m+1)) == S.NegativeOne
|
||||
assert floor((m+2)/(-m-1)) == Integer(-2)
|
||||
|
||||
assert floor(I) == I
|
||||
assert floor(-I) == -I
|
||||
e = floor(i)
|
||||
assert e.func is floor and e.args[0] == i
|
||||
|
||||
assert floor(oo*I) == oo*I
|
||||
assert floor(-oo*I) == -oo*I
|
||||
assert floor(exp(I*pi/4)*oo) == exp(I*pi/4)*oo
|
||||
|
||||
assert floor(2*I) == 2*I
|
||||
assert floor(-2*I) == -2*I
|
||||
|
||||
assert floor(I/2) == 0
|
||||
assert floor(-I/2) == -I
|
||||
|
||||
assert floor(E + 17) == 19
|
||||
assert floor(pi + 2) == 5
|
||||
|
||||
assert floor(E + pi) == 5
|
||||
assert floor(I + pi) == 3 + I
|
||||
|
||||
assert floor(floor(pi)) == 3
|
||||
assert floor(floor(y)) == floor(y)
|
||||
assert floor(floor(x)) == floor(x)
|
||||
|
||||
assert unchanged(floor, x)
|
||||
assert unchanged(floor, 2*x)
|
||||
assert unchanged(floor, k*x)
|
||||
|
||||
assert floor(k) == k
|
||||
assert floor(2*k) == 2*k
|
||||
assert floor(k*n) == k*n
|
||||
|
||||
assert unchanged(floor, k/2)
|
||||
|
||||
assert unchanged(floor, x + y)
|
||||
|
||||
assert floor(x + 3) == floor(x) + 3
|
||||
assert floor(x + k) == floor(x) + k
|
||||
|
||||
assert floor(y + 3) == floor(y) + 3
|
||||
assert floor(y + k) == floor(y) + k
|
||||
|
||||
assert floor(3 + I*y + pi) == 6 + floor(y)*I
|
||||
|
||||
assert floor(k + n) == k + n
|
||||
|
||||
assert unchanged(floor, x*I)
|
||||
assert floor(k*I) == k*I
|
||||
|
||||
assert floor(Rational(23, 10) - E*I) == 2 - 3*I
|
||||
|
||||
assert floor(sin(1)) == 0
|
||||
assert floor(sin(-1)) == -1
|
||||
|
||||
assert floor(exp(2)) == 7
|
||||
|
||||
assert floor(log(8)/log(2)) != 2
|
||||
assert int(floor(log(8)/log(2)).evalf(chop=True)) == 3
|
||||
|
||||
assert floor(factorial(50)/exp(1)) == \
|
||||
11188719610782480504630258070757734324011354208865721592720336800
|
||||
|
||||
assert (floor(y) < y).is_Relational
|
||||
assert (floor(y) <= y) == True
|
||||
assert (floor(y) > y) == False
|
||||
assert (floor(y) >= y).is_Relational
|
||||
assert (floor(x) <= x).is_Relational # x could be non-real
|
||||
assert (floor(x) > x).is_Relational
|
||||
assert (floor(x) <= y).is_Relational # arg is not same as rhs
|
||||
assert (floor(x) > y).is_Relational
|
||||
assert (floor(y) <= oo) == True
|
||||
assert (floor(y) < oo) == True
|
||||
assert (floor(y) >= -oo) == True
|
||||
assert (floor(y) > -oo) == True
|
||||
assert (floor(b) < b) == True
|
||||
assert (floor(b) <= b) == True
|
||||
assert (floor(b) > b) == False
|
||||
assert (floor(b) >= b) == False
|
||||
|
||||
assert floor(y).rewrite(frac) == y - frac(y)
|
||||
assert floor(y).rewrite(ceiling) == -ceiling(-y)
|
||||
assert floor(y).rewrite(frac).subs(y, -pi) == floor(-pi)
|
||||
assert floor(y).rewrite(frac).subs(y, E) == floor(E)
|
||||
assert floor(y).rewrite(ceiling).subs(y, E) == -ceiling(-E)
|
||||
assert floor(y).rewrite(ceiling).subs(y, -pi) == -ceiling(pi)
|
||||
|
||||
assert Eq(floor(y), y - frac(y))
|
||||
assert Eq(floor(y), -ceiling(-y))
|
||||
|
||||
neg = Symbol('neg', negative=True)
|
||||
nn = Symbol('nn', nonnegative=True)
|
||||
pos = Symbol('pos', positive=True)
|
||||
np = Symbol('np', nonpositive=True)
|
||||
|
||||
assert (floor(neg) < 0) == True
|
||||
assert (floor(neg) <= 0) == True
|
||||
assert (floor(neg) > 0) == False
|
||||
assert (floor(neg) >= 0) == False
|
||||
assert (floor(neg) <= -1) == True
|
||||
assert (floor(neg) >= -3) == (neg >= -3)
|
||||
assert (floor(neg) < 5) == (neg < 5)
|
||||
|
||||
assert (floor(nn) < 0) == False
|
||||
assert (floor(nn) >= 0) == True
|
||||
|
||||
assert (floor(pos) < 0) == False
|
||||
assert (floor(pos) <= 0) == (pos < 1)
|
||||
assert (floor(pos) > 0) == (pos >= 1)
|
||||
assert (floor(pos) >= 0) == True
|
||||
assert (floor(pos) >= 3) == (pos >= 3)
|
||||
|
||||
assert (floor(np) <= 0) == True
|
||||
assert (floor(np) > 0) == False
|
||||
|
||||
assert floor(neg).is_negative == True
|
||||
assert floor(neg).is_nonnegative == False
|
||||
assert floor(nn).is_negative == False
|
||||
assert floor(nn).is_nonnegative == True
|
||||
assert floor(pos).is_negative == False
|
||||
assert floor(pos).is_nonnegative == True
|
||||
assert floor(np).is_negative is None
|
||||
assert floor(np).is_nonnegative is None
|
||||
|
||||
assert (floor(7, evaluate=False) >= 7) == True
|
||||
assert (floor(7, evaluate=False) > 7) == False
|
||||
assert (floor(7, evaluate=False) <= 7) == True
|
||||
assert (floor(7, evaluate=False) < 7) == False
|
||||
|
||||
assert (floor(7, evaluate=False) >= 6) == True
|
||||
assert (floor(7, evaluate=False) > 6) == True
|
||||
assert (floor(7, evaluate=False) <= 6) == False
|
||||
assert (floor(7, evaluate=False) < 6) == False
|
||||
|
||||
assert (floor(7, evaluate=False) >= 8) == False
|
||||
assert (floor(7, evaluate=False) > 8) == False
|
||||
assert (floor(7, evaluate=False) <= 8) == True
|
||||
assert (floor(7, evaluate=False) < 8) == True
|
||||
|
||||
assert (floor(x) <= 5.5) == Le(floor(x), 5.5, evaluate=False)
|
||||
assert (floor(x) >= -3.2) == Ge(floor(x), -3.2, evaluate=False)
|
||||
assert (floor(x) < 2.9) == Lt(floor(x), 2.9, evaluate=False)
|
||||
assert (floor(x) > -1.7) == Gt(floor(x), -1.7, evaluate=False)
|
||||
|
||||
assert (floor(y) <= 5.5) == (y < 6)
|
||||
assert (floor(y) >= -3.2) == (y >= -3)
|
||||
assert (floor(y) < 2.9) == (y < 3)
|
||||
assert (floor(y) > -1.7) == (y >= -1)
|
||||
|
||||
assert (floor(y) <= n) == (y < n + 1)
|
||||
assert (floor(y) >= n) == (y >= n)
|
||||
assert (floor(y) < n) == (y < n)
|
||||
assert (floor(y) > n) == (y >= n + 1)
|
||||
|
||||
assert floor(RootOf(x**3 - 27*x, 2)) == 5
|
||||
|
||||
|
||||
def test_ceiling():
|
||||
|
||||
assert ceiling(nan) is nan
|
||||
|
||||
assert ceiling(oo) is oo
|
||||
assert ceiling(-oo) is -oo
|
||||
assert ceiling(zoo) is zoo
|
||||
|
||||
assert ceiling(0) == 0
|
||||
|
||||
assert ceiling(1) == 1
|
||||
assert ceiling(-1) == -1
|
||||
|
||||
assert ceiling(I*log(asin(5)/abs(asin(5)))) == 1
|
||||
assert ceiling(-I*log(asin(7)/abs(asin(7)))) == -1
|
||||
|
||||
assert ceiling(E) == 3
|
||||
assert ceiling(-E) == -2
|
||||
|
||||
assert ceiling(2*E) == 6
|
||||
assert ceiling(-2*E) == -5
|
||||
|
||||
assert ceiling(pi) == 4
|
||||
assert ceiling(-pi) == -3
|
||||
|
||||
assert ceiling(S.Half) == 1
|
||||
assert ceiling(Rational(-1, 2)) == 0
|
||||
|
||||
assert ceiling(Rational(7, 3)) == 3
|
||||
assert ceiling(-Rational(7, 3)) == -2
|
||||
|
||||
assert ceiling(Float(17.0)) == 17
|
||||
assert ceiling(-Float(17.0)) == -17
|
||||
|
||||
assert ceiling(Float(7.69)) == 8
|
||||
assert ceiling(-Float(7.69)) == -7
|
||||
|
||||
assert ceiling(1/(m+1)) == S.One
|
||||
assert ceiling((m+2)/(m+1)) == Integer(2)
|
||||
assert ceiling(-1/(m+1)) == S.Zero
|
||||
assert ceiling((m+2)/(-m-1)) == S.NegativeOne
|
||||
|
||||
assert ceiling(I) == I
|
||||
assert ceiling(-I) == -I
|
||||
e = ceiling(i)
|
||||
assert e.func is ceiling and e.args[0] == i
|
||||
|
||||
assert ceiling(oo*I) == oo*I
|
||||
assert ceiling(-oo*I) == -oo*I
|
||||
assert ceiling(exp(I*pi/4)*oo) == exp(I*pi/4)*oo
|
||||
|
||||
assert ceiling(2*I) == 2*I
|
||||
assert ceiling(-2*I) == -2*I
|
||||
|
||||
assert ceiling(I/2) == I
|
||||
assert ceiling(-I/2) == 0
|
||||
|
||||
assert ceiling(E + 17) == 20
|
||||
assert ceiling(pi + 2) == 6
|
||||
|
||||
assert ceiling(E + pi) == 6
|
||||
assert ceiling(I + pi) == I + 4
|
||||
|
||||
assert ceiling(ceiling(pi)) == 4
|
||||
assert ceiling(ceiling(y)) == ceiling(y)
|
||||
assert ceiling(ceiling(x)) == ceiling(x)
|
||||
|
||||
assert unchanged(ceiling, x)
|
||||
assert unchanged(ceiling, 2*x)
|
||||
assert unchanged(ceiling, k*x)
|
||||
|
||||
assert ceiling(k) == k
|
||||
assert ceiling(2*k) == 2*k
|
||||
assert ceiling(k*n) == k*n
|
||||
|
||||
assert unchanged(ceiling, k/2)
|
||||
|
||||
assert unchanged(ceiling, x + y)
|
||||
|
||||
assert ceiling(x + 3) == ceiling(x) + 3
|
||||
assert ceiling(x + 3.0) == ceiling(x) + 3
|
||||
assert ceiling(x + 3.0*I) == ceiling(x) + 3*I
|
||||
assert ceiling(x + k) == ceiling(x) + k
|
||||
|
||||
assert ceiling(y + 3) == ceiling(y) + 3
|
||||
assert ceiling(y + k) == ceiling(y) + k
|
||||
|
||||
assert ceiling(3 + pi + y*I) == 7 + ceiling(y)*I
|
||||
|
||||
assert ceiling(k + n) == k + n
|
||||
|
||||
assert unchanged(ceiling, x*I)
|
||||
assert ceiling(k*I) == k*I
|
||||
|
||||
assert ceiling(Rational(23, 10) - E*I) == 3 - 2*I
|
||||
|
||||
assert ceiling(sin(1)) == 1
|
||||
assert ceiling(sin(-1)) == 0
|
||||
|
||||
assert ceiling(exp(2)) == 8
|
||||
|
||||
assert ceiling(-log(8)/log(2)) != -2
|
||||
assert int(ceiling(-log(8)/log(2)).evalf(chop=True)) == -3
|
||||
|
||||
assert ceiling(factorial(50)/exp(1)) == \
|
||||
11188719610782480504630258070757734324011354208865721592720336801
|
||||
|
||||
assert (ceiling(y) >= y) == True
|
||||
assert (ceiling(y) > y).is_Relational
|
||||
assert (ceiling(y) < y) == False
|
||||
assert (ceiling(y) <= y).is_Relational
|
||||
assert (ceiling(x) >= x).is_Relational # x could be non-real
|
||||
assert (ceiling(x) < x).is_Relational
|
||||
assert (ceiling(x) >= y).is_Relational # arg is not same as rhs
|
||||
assert (ceiling(x) < y).is_Relational
|
||||
assert (ceiling(y) >= -oo) == True
|
||||
assert (ceiling(y) > -oo) == True
|
||||
assert (ceiling(y) <= oo) == True
|
||||
assert (ceiling(y) < oo) == True
|
||||
assert (ceiling(b) < b) == False
|
||||
assert (ceiling(b) <= b) == False
|
||||
assert (ceiling(b) > b) == True
|
||||
assert (ceiling(b) >= b) == True
|
||||
|
||||
assert ceiling(y).rewrite(floor) == -floor(-y)
|
||||
assert ceiling(y).rewrite(frac) == y + frac(-y)
|
||||
assert ceiling(y).rewrite(floor).subs(y, -pi) == -floor(pi)
|
||||
assert ceiling(y).rewrite(floor).subs(y, E) == -floor(-E)
|
||||
assert ceiling(y).rewrite(frac).subs(y, pi) == ceiling(pi)
|
||||
assert ceiling(y).rewrite(frac).subs(y, -E) == ceiling(-E)
|
||||
|
||||
assert Eq(ceiling(y), y + frac(-y))
|
||||
assert Eq(ceiling(y), -floor(-y))
|
||||
|
||||
neg = Symbol('neg', negative=True)
|
||||
nn = Symbol('nn', nonnegative=True)
|
||||
pos = Symbol('pos', positive=True)
|
||||
np = Symbol('np', nonpositive=True)
|
||||
|
||||
assert (ceiling(neg) <= 0) == True
|
||||
assert (ceiling(neg) < 0) == (neg <= -1)
|
||||
assert (ceiling(neg) > 0) == False
|
||||
assert (ceiling(neg) >= 0) == (neg > -1)
|
||||
assert (ceiling(neg) > -3) == (neg > -3)
|
||||
assert (ceiling(neg) <= 10) == (neg <= 10)
|
||||
|
||||
assert (ceiling(nn) < 0) == False
|
||||
assert (ceiling(nn) >= 0) == True
|
||||
|
||||
assert (ceiling(pos) < 0) == False
|
||||
assert (ceiling(pos) <= 0) == False
|
||||
assert (ceiling(pos) > 0) == True
|
||||
assert (ceiling(pos) >= 0) == True
|
||||
assert (ceiling(pos) >= 1) == True
|
||||
assert (ceiling(pos) > 5) == (pos > 5)
|
||||
|
||||
assert (ceiling(np) <= 0) == True
|
||||
assert (ceiling(np) > 0) == False
|
||||
|
||||
assert ceiling(neg).is_positive == False
|
||||
assert ceiling(neg).is_nonpositive == True
|
||||
assert ceiling(nn).is_positive is None
|
||||
assert ceiling(nn).is_nonpositive is None
|
||||
assert ceiling(pos).is_positive == True
|
||||
assert ceiling(pos).is_nonpositive == False
|
||||
assert ceiling(np).is_positive == False
|
||||
assert ceiling(np).is_nonpositive == True
|
||||
|
||||
assert (ceiling(7, evaluate=False) >= 7) == True
|
||||
assert (ceiling(7, evaluate=False) > 7) == False
|
||||
assert (ceiling(7, evaluate=False) <= 7) == True
|
||||
assert (ceiling(7, evaluate=False) < 7) == False
|
||||
|
||||
assert (ceiling(7, evaluate=False) >= 6) == True
|
||||
assert (ceiling(7, evaluate=False) > 6) == True
|
||||
assert (ceiling(7, evaluate=False) <= 6) == False
|
||||
assert (ceiling(7, evaluate=False) < 6) == False
|
||||
|
||||
assert (ceiling(7, evaluate=False) >= 8) == False
|
||||
assert (ceiling(7, evaluate=False) > 8) == False
|
||||
assert (ceiling(7, evaluate=False) <= 8) == True
|
||||
assert (ceiling(7, evaluate=False) < 8) == True
|
||||
|
||||
assert (ceiling(x) <= 5.5) == Le(ceiling(x), 5.5, evaluate=False)
|
||||
assert (ceiling(x) >= -3.2) == Ge(ceiling(x), -3.2, evaluate=False)
|
||||
assert (ceiling(x) < 2.9) == Lt(ceiling(x), 2.9, evaluate=False)
|
||||
assert (ceiling(x) > -1.7) == Gt(ceiling(x), -1.7, evaluate=False)
|
||||
|
||||
assert (ceiling(y) <= 5.5) == (y <= 5)
|
||||
assert (ceiling(y) >= -3.2) == (y > -4)
|
||||
assert (ceiling(y) < 2.9) == (y <= 2)
|
||||
assert (ceiling(y) > -1.7) == (y > -2)
|
||||
|
||||
assert (ceiling(y) <= n) == (y <= n)
|
||||
assert (ceiling(y) >= n) == (y > n - 1)
|
||||
assert (ceiling(y) < n) == (y <= n - 1)
|
||||
assert (ceiling(y) > n) == (y > n)
|
||||
|
||||
assert ceiling(RootOf(x**3 - 27*x, 2)) == 6
|
||||
s = ImageSet(Lambda(n, n + (CRootOf(x**5 - x**2 + 1, 0))), Integers)
|
||||
f = CRootOf(x**5 - x**2 + 1, 0)
|
||||
s = ImageSet(Lambda(n, n + f), Integers)
|
||||
assert s.intersect(Interval(-10, 10)) == {i + f for i in range(-9, 11)}
|
||||
|
||||
|
||||
def test_frac():
|
||||
assert isinstance(frac(x), frac)
|
||||
assert frac(oo) == AccumBounds(0, 1)
|
||||
assert frac(-oo) == AccumBounds(0, 1)
|
||||
assert frac(zoo) is nan
|
||||
|
||||
assert frac(n) == 0
|
||||
assert frac(nan) is nan
|
||||
assert frac(Rational(4, 3)) == Rational(1, 3)
|
||||
assert frac(-Rational(4, 3)) == Rational(2, 3)
|
||||
assert frac(Rational(-4, 3)) == Rational(2, 3)
|
||||
|
||||
r = Symbol('r', real=True)
|
||||
assert frac(I*r) == I*frac(r)
|
||||
assert frac(1 + I*r) == I*frac(r)
|
||||
assert frac(0.5 + I*r) == 0.5 + I*frac(r)
|
||||
assert frac(n + I*r) == I*frac(r)
|
||||
assert frac(n + I*k) == 0
|
||||
assert unchanged(frac, x + I*x)
|
||||
assert frac(x + I*n) == frac(x)
|
||||
|
||||
assert frac(x).rewrite(floor) == x - floor(x)
|
||||
assert frac(x).rewrite(ceiling) == x + ceiling(-x)
|
||||
assert frac(y).rewrite(floor).subs(y, pi) == frac(pi)
|
||||
assert frac(y).rewrite(floor).subs(y, -E) == frac(-E)
|
||||
assert frac(y).rewrite(ceiling).subs(y, -pi) == frac(-pi)
|
||||
assert frac(y).rewrite(ceiling).subs(y, E) == frac(E)
|
||||
|
||||
assert Eq(frac(y), y - floor(y))
|
||||
assert Eq(frac(y), y + ceiling(-y))
|
||||
|
||||
r = Symbol('r', real=True)
|
||||
p_i = Symbol('p_i', integer=True, positive=True)
|
||||
n_i = Symbol('p_i', integer=True, negative=True)
|
||||
np_i = Symbol('np_i', integer=True, nonpositive=True)
|
||||
nn_i = Symbol('nn_i', integer=True, nonnegative=True)
|
||||
p_r = Symbol('p_r', positive=True)
|
||||
n_r = Symbol('n_r', negative=True)
|
||||
np_r = Symbol('np_r', real=True, nonpositive=True)
|
||||
nn_r = Symbol('nn_r', real=True, nonnegative=True)
|
||||
|
||||
# Real frac argument, integer rhs
|
||||
assert frac(r) <= p_i
|
||||
assert not frac(r) <= n_i
|
||||
assert (frac(r) <= np_i).has(Le)
|
||||
assert (frac(r) <= nn_i).has(Le)
|
||||
assert frac(r) < p_i
|
||||
assert not frac(r) < n_i
|
||||
assert not frac(r) < np_i
|
||||
assert (frac(r) < nn_i).has(Lt)
|
||||
assert not frac(r) >= p_i
|
||||
assert frac(r) >= n_i
|
||||
assert frac(r) >= np_i
|
||||
assert (frac(r) >= nn_i).has(Ge)
|
||||
assert not frac(r) > p_i
|
||||
assert frac(r) > n_i
|
||||
assert (frac(r) > np_i).has(Gt)
|
||||
assert (frac(r) > nn_i).has(Gt)
|
||||
|
||||
assert not Eq(frac(r), p_i)
|
||||
assert not Eq(frac(r), n_i)
|
||||
assert Eq(frac(r), np_i).has(Eq)
|
||||
assert Eq(frac(r), nn_i).has(Eq)
|
||||
|
||||
assert Ne(frac(r), p_i)
|
||||
assert Ne(frac(r), n_i)
|
||||
assert Ne(frac(r), np_i).has(Ne)
|
||||
assert Ne(frac(r), nn_i).has(Ne)
|
||||
|
||||
|
||||
# Real frac argument, real rhs
|
||||
assert (frac(r) <= p_r).has(Le)
|
||||
assert not frac(r) <= n_r
|
||||
assert (frac(r) <= np_r).has(Le)
|
||||
assert (frac(r) <= nn_r).has(Le)
|
||||
assert (frac(r) < p_r).has(Lt)
|
||||
assert not frac(r) < n_r
|
||||
assert not frac(r) < np_r
|
||||
assert (frac(r) < nn_r).has(Lt)
|
||||
assert (frac(r) >= p_r).has(Ge)
|
||||
assert frac(r) >= n_r
|
||||
assert frac(r) >= np_r
|
||||
assert (frac(r) >= nn_r).has(Ge)
|
||||
assert (frac(r) > p_r).has(Gt)
|
||||
assert frac(r) > n_r
|
||||
assert (frac(r) > np_r).has(Gt)
|
||||
assert (frac(r) > nn_r).has(Gt)
|
||||
|
||||
assert not Eq(frac(r), n_r)
|
||||
assert Eq(frac(r), p_r).has(Eq)
|
||||
assert Eq(frac(r), np_r).has(Eq)
|
||||
assert Eq(frac(r), nn_r).has(Eq)
|
||||
|
||||
assert Ne(frac(r), p_r).has(Ne)
|
||||
assert Ne(frac(r), n_r)
|
||||
assert Ne(frac(r), np_r).has(Ne)
|
||||
assert Ne(frac(r), nn_r).has(Ne)
|
||||
|
||||
# Real frac argument, +/- oo rhs
|
||||
assert frac(r) < oo
|
||||
assert frac(r) <= oo
|
||||
assert not frac(r) > oo
|
||||
assert not frac(r) >= oo
|
||||
|
||||
assert not frac(r) < -oo
|
||||
assert not frac(r) <= -oo
|
||||
assert frac(r) > -oo
|
||||
assert frac(r) >= -oo
|
||||
|
||||
assert frac(r) < 1
|
||||
assert frac(r) <= 1
|
||||
assert not frac(r) > 1
|
||||
assert not frac(r) >= 1
|
||||
|
||||
assert not frac(r) < 0
|
||||
assert (frac(r) <= 0).has(Le)
|
||||
assert (frac(r) > 0).has(Gt)
|
||||
assert frac(r) >= 0
|
||||
|
||||
# Some test for numbers
|
||||
assert frac(r) <= sqrt(2)
|
||||
assert (frac(r) <= sqrt(3) - sqrt(2)).has(Le)
|
||||
assert not frac(r) <= sqrt(2) - sqrt(3)
|
||||
assert not frac(r) >= sqrt(2)
|
||||
assert (frac(r) >= sqrt(3) - sqrt(2)).has(Ge)
|
||||
assert frac(r) >= sqrt(2) - sqrt(3)
|
||||
|
||||
assert not Eq(frac(r), sqrt(2))
|
||||
assert Eq(frac(r), sqrt(3) - sqrt(2)).has(Eq)
|
||||
assert not Eq(frac(r), sqrt(2) - sqrt(3))
|
||||
assert Ne(frac(r), sqrt(2))
|
||||
assert Ne(frac(r), sqrt(3) - sqrt(2)).has(Ne)
|
||||
assert Ne(frac(r), sqrt(2) - sqrt(3))
|
||||
|
||||
assert frac(p_i, evaluate=False).is_zero
|
||||
assert frac(p_i, evaluate=False).is_finite
|
||||
assert frac(p_i, evaluate=False).is_integer
|
||||
assert frac(p_i, evaluate=False).is_real
|
||||
assert frac(r).is_finite
|
||||
assert frac(r).is_real
|
||||
assert frac(r).is_zero is None
|
||||
assert frac(r).is_integer is None
|
||||
|
||||
assert frac(oo).is_finite
|
||||
assert frac(oo).is_real
|
||||
|
||||
|
||||
def test_series():
|
||||
x, y = symbols('x,y')
|
||||
assert floor(x).nseries(x, y, 100) == floor(y)
|
||||
assert ceiling(x).nseries(x, y, 100) == ceiling(y)
|
||||
assert floor(x).nseries(x, pi, 100) == 3
|
||||
assert ceiling(x).nseries(x, pi, 100) == 4
|
||||
assert floor(x).nseries(x, 0, 100) == 0
|
||||
assert ceiling(x).nseries(x, 0, 100) == 1
|
||||
assert floor(-x).nseries(x, 0, 100) == -1
|
||||
assert ceiling(-x).nseries(x, 0, 100) == 0
|
||||
|
||||
|
||||
def test_issue_14355():
|
||||
# This test checks the leading term and series for the floor and ceil
|
||||
# function when arg0 evaluates to S.NaN.
|
||||
assert floor((x**3 + x)/(x**2 - x)).as_leading_term(x, cdir = 1) == -2
|
||||
assert floor((x**3 + x)/(x**2 - x)).as_leading_term(x, cdir = -1) == -1
|
||||
assert floor((cos(x) - 1)/x).as_leading_term(x, cdir = 1) == -1
|
||||
assert floor((cos(x) - 1)/x).as_leading_term(x, cdir = -1) == 0
|
||||
assert floor(sin(x)/x).as_leading_term(x, cdir = 1) == 0
|
||||
assert floor(sin(x)/x).as_leading_term(x, cdir = -1) == 0
|
||||
assert floor(-tan(x)/x).as_leading_term(x, cdir = 1) == -2
|
||||
assert floor(-tan(x)/x).as_leading_term(x, cdir = -1) == -2
|
||||
assert floor(sin(x)/x/3).as_leading_term(x, cdir = 1) == 0
|
||||
assert floor(sin(x)/x/3).as_leading_term(x, cdir = -1) == 0
|
||||
assert ceiling((x**3 + x)/(x**2 - x)).as_leading_term(x, cdir = 1) == -1
|
||||
assert ceiling((x**3 + x)/(x**2 - x)).as_leading_term(x, cdir = -1) == 0
|
||||
assert ceiling((cos(x) - 1)/x).as_leading_term(x, cdir = 1) == 0
|
||||
assert ceiling((cos(x) - 1)/x).as_leading_term(x, cdir = -1) == 1
|
||||
assert ceiling(sin(x)/x).as_leading_term(x, cdir = 1) == 1
|
||||
assert ceiling(sin(x)/x).as_leading_term(x, cdir = -1) == 1
|
||||
assert ceiling(-tan(x)/x).as_leading_term(x, cdir = 1) == -1
|
||||
assert ceiling(-tan(x)/x).as_leading_term(x, cdir = 1) == -1
|
||||
assert ceiling(sin(x)/x/3).as_leading_term(x, cdir = 1) == 1
|
||||
assert ceiling(sin(x)/x/3).as_leading_term(x, cdir = -1) == 1
|
||||
# test for series
|
||||
assert floor(sin(x)/x).series(x, 0, 100, cdir = 1) == 0
|
||||
assert floor(sin(x)/x).series(x, 0, 100, cdir = 1) == 0
|
||||
assert floor((x**3 + x)/(x**2 - x)).series(x, 0, 100, cdir = 1) == -2
|
||||
assert floor((x**3 + x)/(x**2 - x)).series(x, 0, 100, cdir = -1) == -1
|
||||
assert ceiling(sin(x)/x).series(x, 0, 100, cdir = 1) == 1
|
||||
assert ceiling(sin(x)/x).series(x, 0, 100, cdir = -1) == 1
|
||||
assert ceiling((x**3 + x)/(x**2 - x)).series(x, 0, 100, cdir = 1) == -1
|
||||
assert ceiling((x**3 + x)/(x**2 - x)).series(x, 0, 100, cdir = -1) == 0
|
||||
|
||||
|
||||
def test_frac_leading_term():
|
||||
assert frac(x).as_leading_term(x) == x
|
||||
assert frac(x).as_leading_term(x, cdir = 1) == x
|
||||
assert frac(x).as_leading_term(x, cdir = -1) == 1
|
||||
assert frac(x + S.Half).as_leading_term(x, cdir = 1) == S.Half
|
||||
assert frac(x + S.Half).as_leading_term(x, cdir = -1) == S.Half
|
||||
assert frac(-2*x + 1).as_leading_term(x, cdir = 1) == S.One
|
||||
assert frac(-2*x + 1).as_leading_term(x, cdir = -1) == -2*x
|
||||
assert frac(sin(x) + 5).as_leading_term(x, cdir = 1) == x
|
||||
assert frac(sin(x) + 5).as_leading_term(x, cdir = -1) == S.One
|
||||
assert frac(sin(x**2) + 5).as_leading_term(x, cdir = 1) == x**2
|
||||
assert frac(sin(x**2) + 5).as_leading_term(x, cdir = -1) == x**2
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_issue_4149():
|
||||
assert floor(3 + pi*I + y*I) == 3 + floor(pi + y)*I
|
||||
assert floor(3*I + pi*I + y*I) == floor(3 + pi + y)*I
|
||||
assert floor(3 + E + pi*I + y*I) == 5 + floor(pi + y)*I
|
||||
|
||||
|
||||
def test_issue_21651():
|
||||
k = Symbol('k', positive=True, integer=True)
|
||||
exp = 2*2**(-k)
|
||||
assert isinstance(floor(exp), floor)
|
||||
|
||||
|
||||
def test_issue_11207():
|
||||
assert floor(floor(x)) == floor(x)
|
||||
assert floor(ceiling(x)) == ceiling(x)
|
||||
assert ceiling(floor(x)) == floor(x)
|
||||
assert ceiling(ceiling(x)) == ceiling(x)
|
||||
|
||||
|
||||
def test_nested_floor_ceiling():
|
||||
assert floor(-floor(ceiling(x**3)/y)) == -floor(ceiling(x**3)/y)
|
||||
assert ceiling(-floor(ceiling(x**3)/y)) == -floor(ceiling(x**3)/y)
|
||||
assert floor(ceiling(-floor(x**Rational(7, 2)/y))) == -floor(x**Rational(7, 2)/y)
|
||||
assert -ceiling(-ceiling(floor(x)/y)) == ceiling(floor(x)/y)
|
||||
|
||||
def test_issue_18689():
|
||||
assert floor(floor(floor(x)) + 3) == floor(x) + 3
|
||||
assert ceiling(ceiling(ceiling(x)) + 1) == ceiling(x) + 1
|
||||
assert ceiling(ceiling(floor(x)) + 3) == floor(x) + 3
|
||||
|
||||
def test_issue_18421():
|
||||
assert floor(float(0)) is S.Zero
|
||||
assert ceiling(float(0)) is S.Zero
|
||||
|
||||
def test_issue_25230():
|
||||
a = Symbol('a', real = True)
|
||||
b = Symbol('b', positive = True)
|
||||
c = Symbol('c', negative = True)
|
||||
raises(NotImplementedError, lambda: floor(x/a).as_leading_term(x, cdir = 1))
|
||||
raises(NotImplementedError, lambda: ceiling(x/a).as_leading_term(x, cdir = 1))
|
||||
assert floor(x/b).as_leading_term(x, cdir = 1) == 0
|
||||
assert floor(x/b).as_leading_term(x, cdir = -1) == -1
|
||||
assert floor(x/c).as_leading_term(x, cdir = 1) == -1
|
||||
assert floor(x/c).as_leading_term(x, cdir = -1) == 0
|
||||
assert ceiling(x/b).as_leading_term(x, cdir = 1) == 1
|
||||
assert ceiling(x/b).as_leading_term(x, cdir = -1) == 0
|
||||
assert ceiling(x/c).as_leading_term(x, cdir = 1) == 0
|
||||
assert ceiling(x/c).as_leading_term(x, cdir = -1) == 1
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
# This test file tests the SymPy function interface, that people use to create
|
||||
# their own new functions. It should be as easy as possible.
|
||||
#
|
||||
# We test that it works with both Function and DefinedFunction. New code should
|
||||
# use DefinedFunction because it has better type inference. Old code still
|
||||
# using Function should continue to work though.
|
||||
from sympy.core.function import Function, DefinedFunction
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.hyperbolic import tanh
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.series.limits import limit
|
||||
from sympy.abc import x
|
||||
|
||||
|
||||
def test_function_series1():
|
||||
"""Create our new "sin" function."""
|
||||
|
||||
for F in [Function, DefinedFunction]:
|
||||
|
||||
class my_function(F):
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
return cos(self.args[0])
|
||||
|
||||
@classmethod
|
||||
def eval(cls, arg):
|
||||
arg = sympify(arg)
|
||||
if arg == 0:
|
||||
return sympify(0)
|
||||
|
||||
#Test that the taylor series is correct
|
||||
assert my_function(x).series(x, 0, 10) == sin(x).series(x, 0, 10)
|
||||
assert limit(my_function(x)/x, x, 0) == 1
|
||||
|
||||
|
||||
def test_function_series2():
|
||||
"""Create our new "cos" function."""
|
||||
|
||||
for F in [Function, DefinedFunction]:
|
||||
|
||||
class my_function2(F):
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
return -sin(self.args[0])
|
||||
|
||||
@classmethod
|
||||
def eval(cls, arg):
|
||||
arg = sympify(arg)
|
||||
if arg == 0:
|
||||
return sympify(1)
|
||||
|
||||
#Test that the taylor series is correct
|
||||
assert my_function2(x).series(x, 0, 10) == cos(x).series(x, 0, 10)
|
||||
|
||||
|
||||
def test_function_series3():
|
||||
"""
|
||||
Test our easy "tanh" function.
|
||||
|
||||
This test tests two things:
|
||||
* that the Function interface works as expected and it's easy to use
|
||||
* that the general algorithm for the series expansion works even when the
|
||||
derivative is defined recursively in terms of the original function,
|
||||
since tanh(x).diff(x) == 1-tanh(x)**2
|
||||
"""
|
||||
|
||||
for F in [Function, DefinedFunction]:
|
||||
|
||||
class mytanh(F):
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
return 1 - mytanh(self.args[0])**2
|
||||
|
||||
@classmethod
|
||||
def eval(cls, arg):
|
||||
arg = sympify(arg)
|
||||
if arg == 0:
|
||||
return sympify(0)
|
||||
|
||||
e = tanh(x)
|
||||
f = mytanh(x)
|
||||
assert e.series(x, 0, 6) == f.series(x, 0, 6)
|
||||
+504
@@ -0,0 +1,504 @@
|
||||
import itertools as it
|
||||
|
||||
from sympy.core.expr import unchanged
|
||||
from sympy.core.function import Function
|
||||
from sympy.core.numbers import I, oo, Rational
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.external import import_module
|
||||
from sympy.functions.elementary.exponential import log
|
||||
from sympy.functions.elementary.integers import floor, ceiling
|
||||
from sympy.functions.elementary.miscellaneous import (sqrt, cbrt, root, Min,
|
||||
Max, real_root, Rem)
|
||||
from sympy.functions.elementary.trigonometric import cos, sin
|
||||
from sympy.functions.special.delta_functions import Heaviside
|
||||
|
||||
from sympy.utilities.lambdify import lambdify
|
||||
from sympy.testing.pytest import raises, skip, ignore_warnings
|
||||
|
||||
def test_Min():
|
||||
from sympy.abc import x, y, z
|
||||
n = Symbol('n', negative=True)
|
||||
n_ = Symbol('n_', negative=True)
|
||||
nn = Symbol('nn', nonnegative=True)
|
||||
nn_ = Symbol('nn_', nonnegative=True)
|
||||
p = Symbol('p', positive=True)
|
||||
p_ = Symbol('p_', positive=True)
|
||||
np = Symbol('np', nonpositive=True)
|
||||
np_ = Symbol('np_', nonpositive=True)
|
||||
r = Symbol('r', real=True)
|
||||
|
||||
assert Min(5, 4) == 4
|
||||
assert Min(-oo, -oo) is -oo
|
||||
assert Min(-oo, n) is -oo
|
||||
assert Min(n, -oo) is -oo
|
||||
assert Min(-oo, np) is -oo
|
||||
assert Min(np, -oo) is -oo
|
||||
assert Min(-oo, 0) is -oo
|
||||
assert Min(0, -oo) is -oo
|
||||
assert Min(-oo, nn) is -oo
|
||||
assert Min(nn, -oo) is -oo
|
||||
assert Min(-oo, p) is -oo
|
||||
assert Min(p, -oo) is -oo
|
||||
assert Min(-oo, oo) is -oo
|
||||
assert Min(oo, -oo) is -oo
|
||||
assert Min(n, n) == n
|
||||
assert unchanged(Min, n, np)
|
||||
assert Min(np, n) == Min(n, np)
|
||||
assert Min(n, 0) == n
|
||||
assert Min(0, n) == n
|
||||
assert Min(n, nn) == n
|
||||
assert Min(nn, n) == n
|
||||
assert Min(n, p) == n
|
||||
assert Min(p, n) == n
|
||||
assert Min(n, oo) == n
|
||||
assert Min(oo, n) == n
|
||||
assert Min(np, np) == np
|
||||
assert Min(np, 0) == np
|
||||
assert Min(0, np) == np
|
||||
assert Min(np, nn) == np
|
||||
assert Min(nn, np) == np
|
||||
assert Min(np, p) == np
|
||||
assert Min(p, np) == np
|
||||
assert Min(np, oo) == np
|
||||
assert Min(oo, np) == np
|
||||
assert Min(0, 0) == 0
|
||||
assert Min(0, nn) == 0
|
||||
assert Min(nn, 0) == 0
|
||||
assert Min(0, p) == 0
|
||||
assert Min(p, 0) == 0
|
||||
assert Min(0, oo) == 0
|
||||
assert Min(oo, 0) == 0
|
||||
assert Min(nn, nn) == nn
|
||||
assert unchanged(Min, nn, p)
|
||||
assert Min(p, nn) == Min(nn, p)
|
||||
assert Min(nn, oo) == nn
|
||||
assert Min(oo, nn) == nn
|
||||
assert Min(p, p) == p
|
||||
assert Min(p, oo) == p
|
||||
assert Min(oo, p) == p
|
||||
assert Min(oo, oo) is oo
|
||||
|
||||
assert Min(n, n_).func is Min
|
||||
assert Min(nn, nn_).func is Min
|
||||
assert Min(np, np_).func is Min
|
||||
assert Min(p, p_).func is Min
|
||||
|
||||
# lists
|
||||
assert Min() is S.Infinity
|
||||
assert Min(x) == x
|
||||
assert Min(x, y) == Min(y, x)
|
||||
assert Min(x, y, z) == Min(z, y, x)
|
||||
assert Min(x, Min(y, z)) == Min(z, y, x)
|
||||
assert Min(x, Max(y, -oo)) == Min(x, y)
|
||||
assert Min(p, oo, n, p, p, p_) == n
|
||||
assert Min(p_, n_, p) == n_
|
||||
assert Min(n, oo, -7, p, p, 2) == Min(n, -7)
|
||||
assert Min(2, x, p, n, oo, n_, p, 2, -2, -2) == Min(-2, x, n, n_)
|
||||
assert Min(0, x, 1, y) == Min(0, x, y)
|
||||
assert Min(1000, 100, -100, x, p, n) == Min(n, x, -100)
|
||||
assert unchanged(Min, sin(x), cos(x))
|
||||
assert Min(sin(x), cos(x)) == Min(cos(x), sin(x))
|
||||
assert Min(cos(x), sin(x)).subs(x, 1) == cos(1)
|
||||
assert Min(cos(x), sin(x)).subs(x, S.Half) == sin(S.Half)
|
||||
raises(ValueError, lambda: Min(cos(x), sin(x)).subs(x, I))
|
||||
raises(ValueError, lambda: Min(I))
|
||||
raises(ValueError, lambda: Min(I, x))
|
||||
raises(ValueError, lambda: Min(S.ComplexInfinity, x))
|
||||
|
||||
assert Min(1, x).diff(x) == Heaviside(1 - x)
|
||||
assert Min(x, 1).diff(x) == Heaviside(1 - x)
|
||||
assert Min(0, -x, 1 - 2*x).diff(x) == -Heaviside(x + Min(0, -2*x + 1)) \
|
||||
- 2*Heaviside(2*x + Min(0, -x) - 1)
|
||||
|
||||
# issue 7619
|
||||
f = Function('f')
|
||||
assert Min(1, 2*Min(f(1), 2)) # doesn't fail
|
||||
|
||||
# issue 7233
|
||||
e = Min(0, x)
|
||||
assert e.n().args == (0, x)
|
||||
|
||||
# issue 8643
|
||||
m = Min(n, p_, n_, r)
|
||||
assert m.is_positive is False
|
||||
assert m.is_nonnegative is False
|
||||
assert m.is_negative is True
|
||||
|
||||
m = Min(p, p_)
|
||||
assert m.is_positive is True
|
||||
assert m.is_nonnegative is True
|
||||
assert m.is_negative is False
|
||||
|
||||
m = Min(p, nn_, p_)
|
||||
assert m.is_positive is None
|
||||
assert m.is_nonnegative is True
|
||||
assert m.is_negative is False
|
||||
|
||||
m = Min(nn, p, r)
|
||||
assert m.is_positive is None
|
||||
assert m.is_nonnegative is None
|
||||
assert m.is_negative is None
|
||||
|
||||
|
||||
def test_Max():
|
||||
from sympy.abc import x, y, z
|
||||
n = Symbol('n', negative=True)
|
||||
n_ = Symbol('n_', negative=True)
|
||||
nn = Symbol('nn', nonnegative=True)
|
||||
p = Symbol('p', positive=True)
|
||||
p_ = Symbol('p_', positive=True)
|
||||
r = Symbol('r', real=True)
|
||||
|
||||
assert Max(5, 4) == 5
|
||||
|
||||
# lists
|
||||
|
||||
assert Max() is S.NegativeInfinity
|
||||
assert Max(x) == x
|
||||
assert Max(x, y) == Max(y, x)
|
||||
assert Max(x, y, z) == Max(z, y, x)
|
||||
assert Max(x, Max(y, z)) == Max(z, y, x)
|
||||
assert Max(x, Min(y, oo)) == Max(x, y)
|
||||
assert Max(n, -oo, n_, p, 2) == Max(p, 2)
|
||||
assert Max(n, -oo, n_, p) == p
|
||||
assert Max(2, x, p, n, -oo, S.NegativeInfinity, n_, p, 2) == Max(2, x, p)
|
||||
assert Max(0, x, 1, y) == Max(1, x, y)
|
||||
assert Max(r, r + 1, r - 1) == 1 + r
|
||||
assert Max(1000, 100, -100, x, p, n) == Max(p, x, 1000)
|
||||
assert Max(cos(x), sin(x)) == Max(sin(x), cos(x))
|
||||
assert Max(cos(x), sin(x)).subs(x, 1) == sin(1)
|
||||
assert Max(cos(x), sin(x)).subs(x, S.Half) == cos(S.Half)
|
||||
raises(ValueError, lambda: Max(cos(x), sin(x)).subs(x, I))
|
||||
raises(ValueError, lambda: Max(I))
|
||||
raises(ValueError, lambda: Max(I, x))
|
||||
raises(ValueError, lambda: Max(S.ComplexInfinity, 1))
|
||||
assert Max(n, -oo, n_, p, 2) == Max(p, 2)
|
||||
assert Max(n, -oo, n_, p, 1000) == Max(p, 1000)
|
||||
|
||||
assert Max(1, x).diff(x) == Heaviside(x - 1)
|
||||
assert Max(x, 1).diff(x) == Heaviside(x - 1)
|
||||
assert Max(x**2, 1 + x, 1).diff(x) == \
|
||||
2*x*Heaviside(x**2 - Max(1, x + 1)) \
|
||||
+ Heaviside(x - Max(1, x**2) + 1)
|
||||
|
||||
e = Max(0, x)
|
||||
assert e.n().args == (0, x)
|
||||
|
||||
# issue 8643
|
||||
m = Max(p, p_, n, r)
|
||||
assert m.is_positive is True
|
||||
assert m.is_nonnegative is True
|
||||
assert m.is_negative is False
|
||||
|
||||
m = Max(n, n_)
|
||||
assert m.is_positive is False
|
||||
assert m.is_nonnegative is False
|
||||
assert m.is_negative is True
|
||||
|
||||
m = Max(n, n_, r)
|
||||
assert m.is_positive is None
|
||||
assert m.is_nonnegative is None
|
||||
assert m.is_negative is None
|
||||
|
||||
m = Max(n, nn, r)
|
||||
assert m.is_positive is None
|
||||
assert m.is_nonnegative is True
|
||||
assert m.is_negative is False
|
||||
|
||||
|
||||
def test_minmax_assumptions():
|
||||
r = Symbol('r', real=True)
|
||||
a = Symbol('a', real=True, algebraic=True)
|
||||
t = Symbol('t', real=True, transcendental=True)
|
||||
q = Symbol('q', rational=True)
|
||||
p = Symbol('p', irrational=True)
|
||||
n = Symbol('n', rational=True, integer=False)
|
||||
i = Symbol('i', integer=True)
|
||||
o = Symbol('o', odd=True)
|
||||
e = Symbol('e', even=True)
|
||||
k = Symbol('k', prime=True)
|
||||
reals = [r, a, t, q, p, n, i, o, e, k]
|
||||
|
||||
for ext in (Max, Min):
|
||||
for x, y in it.product(reals, repeat=2):
|
||||
|
||||
# Must be real
|
||||
assert ext(x, y).is_real
|
||||
|
||||
# Algebraic?
|
||||
if x.is_algebraic and y.is_algebraic:
|
||||
assert ext(x, y).is_algebraic
|
||||
elif x.is_transcendental and y.is_transcendental:
|
||||
assert ext(x, y).is_transcendental
|
||||
else:
|
||||
assert ext(x, y).is_algebraic is None
|
||||
|
||||
# Rational?
|
||||
if x.is_rational and y.is_rational:
|
||||
assert ext(x, y).is_rational
|
||||
elif x.is_irrational and y.is_irrational:
|
||||
assert ext(x, y).is_irrational
|
||||
else:
|
||||
assert ext(x, y).is_rational is None
|
||||
|
||||
# Integer?
|
||||
if x.is_integer and y.is_integer:
|
||||
assert ext(x, y).is_integer
|
||||
elif x.is_noninteger and y.is_noninteger:
|
||||
assert ext(x, y).is_noninteger
|
||||
else:
|
||||
assert ext(x, y).is_integer is None
|
||||
|
||||
# Odd?
|
||||
if x.is_odd and y.is_odd:
|
||||
assert ext(x, y).is_odd
|
||||
elif x.is_odd is False and y.is_odd is False:
|
||||
assert ext(x, y).is_odd is False
|
||||
else:
|
||||
assert ext(x, y).is_odd is None
|
||||
|
||||
# Even?
|
||||
if x.is_even and y.is_even:
|
||||
assert ext(x, y).is_even
|
||||
elif x.is_even is False and y.is_even is False:
|
||||
assert ext(x, y).is_even is False
|
||||
else:
|
||||
assert ext(x, y).is_even is None
|
||||
|
||||
# Prime?
|
||||
if x.is_prime and y.is_prime:
|
||||
assert ext(x, y).is_prime
|
||||
elif x.is_prime is False and y.is_prime is False:
|
||||
assert ext(x, y).is_prime is False
|
||||
else:
|
||||
assert ext(x, y).is_prime is None
|
||||
|
||||
|
||||
def test_issue_8413():
|
||||
x = Symbol('x', real=True)
|
||||
# we can't evaluate in general because non-reals are not
|
||||
# comparable: Min(floor(3.2 + I), 3.2 + I) -> ValueError
|
||||
assert Min(floor(x), x) == floor(x)
|
||||
assert Min(ceiling(x), x) == x
|
||||
assert Max(floor(x), x) == x
|
||||
assert Max(ceiling(x), x) == ceiling(x)
|
||||
|
||||
|
||||
def test_root():
|
||||
from sympy.abc import x
|
||||
n = Symbol('n', integer=True)
|
||||
k = Symbol('k', integer=True)
|
||||
|
||||
assert root(2, 2) == sqrt(2)
|
||||
assert root(2, 1) == 2
|
||||
assert root(2, 3) == 2**Rational(1, 3)
|
||||
assert root(2, 3) == cbrt(2)
|
||||
assert root(2, -5) == 2**Rational(4, 5)/2
|
||||
|
||||
assert root(-2, 1) == -2
|
||||
|
||||
assert root(-2, 2) == sqrt(2)*I
|
||||
assert root(-2, 1) == -2
|
||||
|
||||
assert root(x, 2) == sqrt(x)
|
||||
assert root(x, 1) == x
|
||||
assert root(x, 3) == x**Rational(1, 3)
|
||||
assert root(x, 3) == cbrt(x)
|
||||
assert root(x, -5) == x**Rational(-1, 5)
|
||||
|
||||
assert root(x, n) == x**(1/n)
|
||||
assert root(x, -n) == x**(-1/n)
|
||||
|
||||
assert root(x, n, k) == (-1)**(2*k/n)*x**(1/n)
|
||||
|
||||
|
||||
def test_real_root():
|
||||
assert real_root(-8, 3) == -2
|
||||
assert real_root(-16, 4) == root(-16, 4)
|
||||
r = root(-7, 4)
|
||||
assert real_root(r) == r
|
||||
r1 = root(-1, 3)
|
||||
r2 = r1**2
|
||||
r3 = root(-1, 4)
|
||||
assert real_root(r1 + r2 + r3) == -1 + r2 + r3
|
||||
assert real_root(root(-2, 3)) == -root(2, 3)
|
||||
assert real_root(-8., 3) == -2.0
|
||||
x = Symbol('x')
|
||||
n = Symbol('n')
|
||||
g = real_root(x, n)
|
||||
assert g.subs({"x": -8, "n": 3}) == -2
|
||||
assert g.subs({"x": 8, "n": 3}) == 2
|
||||
# give principle root if there is no real root -- if this is not desired
|
||||
# then maybe a Root class is needed to raise an error instead
|
||||
assert g.subs({"x": I, "n": 3}) == cbrt(I)
|
||||
assert g.subs({"x": -8, "n": 2}) == sqrt(-8)
|
||||
assert g.subs({"x": I, "n": 2}) == sqrt(I)
|
||||
|
||||
|
||||
def test_issue_11463():
|
||||
numpy = import_module('numpy')
|
||||
if not numpy:
|
||||
skip("numpy not installed.")
|
||||
x = Symbol('x')
|
||||
f = lambdify(x, real_root((log(x/(x-2))), 3), 'numpy')
|
||||
# numpy.select evaluates all options before considering conditions,
|
||||
# so it raises a warning about root of negative number which does
|
||||
# not affect the outcome. This warning is suppressed here
|
||||
with ignore_warnings(RuntimeWarning):
|
||||
assert f(numpy.array(-1)) < -1
|
||||
|
||||
|
||||
def test_rewrite_MaxMin_as_Heaviside():
|
||||
from sympy.abc import x
|
||||
assert Max(0, x).rewrite(Heaviside) == x*Heaviside(x)
|
||||
assert Max(3, x).rewrite(Heaviside) == x*Heaviside(x - 3) + \
|
||||
3*Heaviside(-x + 3)
|
||||
assert Max(0, x+2, 2*x).rewrite(Heaviside) == \
|
||||
2*x*Heaviside(2*x)*Heaviside(x - 2) + \
|
||||
(x + 2)*Heaviside(-x + 2)*Heaviside(x + 2)
|
||||
|
||||
assert Min(0, x).rewrite(Heaviside) == x*Heaviside(-x)
|
||||
assert Min(3, x).rewrite(Heaviside) == x*Heaviside(-x + 3) + \
|
||||
3*Heaviside(x - 3)
|
||||
assert Min(x, -x, -2).rewrite(Heaviside) == \
|
||||
x*Heaviside(-2*x)*Heaviside(-x - 2) - \
|
||||
x*Heaviside(2*x)*Heaviside(x - 2) \
|
||||
- 2*Heaviside(-x + 2)*Heaviside(x + 2)
|
||||
|
||||
|
||||
def test_rewrite_MaxMin_as_Piecewise():
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
x, y, z, a, b = symbols('x y z a b', real=True)
|
||||
vx, vy, va = symbols('vx vy va')
|
||||
assert Max(a, b).rewrite(Piecewise) == Piecewise((a, a >= b), (b, True))
|
||||
assert Max(x, y, z).rewrite(Piecewise) == Piecewise((x, (x >= y) & (x >= z)), (y, y >= z), (z, True))
|
||||
assert Max(x, y, a, b).rewrite(Piecewise) == Piecewise((a, (a >= b) & (a >= x) & (a >= y)),
|
||||
(b, (b >= x) & (b >= y)), (x, x >= y), (y, True))
|
||||
assert Min(a, b).rewrite(Piecewise) == Piecewise((a, a <= b), (b, True))
|
||||
assert Min(x, y, z).rewrite(Piecewise) == Piecewise((x, (x <= y) & (x <= z)), (y, y <= z), (z, True))
|
||||
assert Min(x, y, a, b).rewrite(Piecewise) == Piecewise((a, (a <= b) & (a <= x) & (a <= y)),
|
||||
(b, (b <= x) & (b <= y)), (x, x <= y), (y, True))
|
||||
|
||||
# Piecewise rewriting of Min/Max does also takes place for not explicitly real arguments
|
||||
assert Max(vx, vy).rewrite(Piecewise) == Piecewise((vx, vx >= vy), (vy, True))
|
||||
assert Min(va, vx, vy).rewrite(Piecewise) == Piecewise((va, (va <= vx) & (va <= vy)), (vx, vx <= vy), (vy, True))
|
||||
|
||||
|
||||
def test_issue_11099():
|
||||
from sympy.abc import x, y
|
||||
# some fixed value tests
|
||||
fixed_test_data = {x: -2, y: 3}
|
||||
assert Min(x, y).evalf(subs=fixed_test_data) == \
|
||||
Min(x, y).subs(fixed_test_data).evalf()
|
||||
assert Max(x, y).evalf(subs=fixed_test_data) == \
|
||||
Max(x, y).subs(fixed_test_data).evalf()
|
||||
# randomly generate some test data
|
||||
from sympy.core.random import randint
|
||||
for i in range(20):
|
||||
random_test_data = {x: randint(-100, 100), y: randint(-100, 100)}
|
||||
assert Min(x, y).evalf(subs=random_test_data) == \
|
||||
Min(x, y).subs(random_test_data).evalf()
|
||||
assert Max(x, y).evalf(subs=random_test_data) == \
|
||||
Max(x, y).subs(random_test_data).evalf()
|
||||
|
||||
|
||||
def test_issue_12638():
|
||||
from sympy.abc import a, b, c
|
||||
assert Min(a, b, c, Max(a, b)) == Min(a, b, c)
|
||||
assert Min(a, b, Max(a, b, c)) == Min(a, b)
|
||||
assert Min(a, b, Max(a, c)) == Min(a, b)
|
||||
|
||||
def test_issue_21399():
|
||||
from sympy.abc import a, b, c
|
||||
assert Max(Min(a, b), Min(a, b, c)) == Min(a, b)
|
||||
|
||||
|
||||
def test_instantiation_evaluation():
|
||||
from sympy.abc import v, w, x, y, z
|
||||
assert Min(1, Max(2, x)) == 1
|
||||
assert Max(3, Min(2, x)) == 3
|
||||
assert Min(Max(x, y), Max(x, z)) == Max(x, Min(y, z))
|
||||
assert set(Min(Max(w, x), Max(y, z)).args) == {
|
||||
Max(w, x), Max(y, z)}
|
||||
assert Min(Max(x, y), Max(x, z), w) == Min(
|
||||
w, Max(x, Min(y, z)))
|
||||
A, B = Min, Max
|
||||
for i in range(2):
|
||||
assert A(x, B(x, y)) == x
|
||||
assert A(x, B(y, A(x, w, z))) == A(x, B(y, A(w, z)))
|
||||
A, B = B, A
|
||||
assert Min(w, Max(x, y), Max(v, x, z)) == Min(
|
||||
w, Max(x, Min(y, Max(v, z))))
|
||||
|
||||
def test_rewrite_as_Abs():
|
||||
from itertools import permutations
|
||||
from sympy.functions.elementary.complexes import Abs
|
||||
from sympy.abc import x, y, z, w
|
||||
def test(e):
|
||||
free = e.free_symbols
|
||||
a = e.rewrite(Abs)
|
||||
assert not a.has(Min, Max)
|
||||
for i in permutations(range(len(free))):
|
||||
reps = dict(zip(free, i))
|
||||
assert a.xreplace(reps) == e.xreplace(reps)
|
||||
test(Min(x, y))
|
||||
test(Max(x, y))
|
||||
test(Min(x, y, z))
|
||||
test(Min(Max(w, x), Max(y, z)))
|
||||
|
||||
def test_issue_14000():
|
||||
assert isinstance(sqrt(4, evaluate=False), Pow) == True
|
||||
assert isinstance(cbrt(3.5, evaluate=False), Pow) == True
|
||||
assert isinstance(root(16, 4, evaluate=False), Pow) == True
|
||||
|
||||
assert sqrt(4, evaluate=False) == Pow(4, S.Half, evaluate=False)
|
||||
assert cbrt(3.5, evaluate=False) == Pow(3.5, Rational(1, 3), evaluate=False)
|
||||
assert root(4, 2, evaluate=False) == Pow(4, S.Half, evaluate=False)
|
||||
|
||||
assert root(16, 4, 2, evaluate=False).has(Pow) == True
|
||||
assert real_root(-8, 3, evaluate=False).has(Pow) == True
|
||||
|
||||
def test_issue_6899():
|
||||
from sympy.core.function import Lambda
|
||||
x = Symbol('x')
|
||||
eqn = Lambda(x, x)
|
||||
assert eqn.func(*eqn.args) == eqn
|
||||
|
||||
def test_Rem():
|
||||
from sympy.abc import x, y
|
||||
assert Rem(5, 3) == 2
|
||||
assert Rem(-5, 3) == -2
|
||||
assert Rem(5, -3) == 2
|
||||
assert Rem(-5, -3) == -2
|
||||
assert Rem(x**3, y) == Rem(x**3, y)
|
||||
assert Rem(Rem(-5, 3) + 3, 3) == 1
|
||||
|
||||
|
||||
def test_minmax_no_evaluate():
|
||||
from sympy import evaluate
|
||||
p = Symbol('p', positive=True)
|
||||
|
||||
assert Max(1, 3) == 3
|
||||
assert Max(1, 3).args == ()
|
||||
assert Max(0, p) == p
|
||||
assert Max(0, p).args == ()
|
||||
assert Min(0, p) == 0
|
||||
assert Min(0, p).args == ()
|
||||
|
||||
assert Max(1, 3, evaluate=False) != 3
|
||||
assert Max(1, 3, evaluate=False).args == (1, 3)
|
||||
assert Max(0, p, evaluate=False) != p
|
||||
assert Max(0, p, evaluate=False).args == (0, p)
|
||||
assert Min(0, p, evaluate=False) != 0
|
||||
assert Min(0, p, evaluate=False).args == (0, p)
|
||||
|
||||
with evaluate(False):
|
||||
assert Max(1, 3) != 3
|
||||
assert Max(1, 3).args == (1, 3)
|
||||
assert Max(0, p) != p
|
||||
assert Max(0, p).args == (0, p)
|
||||
assert Min(0, p) != 0
|
||||
assert Min(0, p).args == (0, p)
|
||||
+1639
File diff suppressed because it is too large
Load Diff
+2236
File diff suppressed because it is too large
Load Diff
+3627
File diff suppressed because it is too large
Load Diff
+1
@@ -0,0 +1 @@
|
||||
# Stub __init__.py for the sympy.functions.special package
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+8
@@ -0,0 +1,8 @@
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.special.spherical_harmonics import Ynm
|
||||
|
||||
x, y = symbols('x,y')
|
||||
|
||||
|
||||
def timeit_Ynm_xy():
|
||||
Ynm(1, 1, x, y)
|
||||
+2208
File diff suppressed because it is too large
Load Diff
+389
@@ -0,0 +1,389 @@
|
||||
from sympy.core import S
|
||||
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
||||
from sympy.core.symbol import Dummy, uniquely_named_symbol
|
||||
from sympy.functions.special.gamma_functions import gamma, digamma
|
||||
from sympy.functions.combinatorial.numbers import catalan
|
||||
from sympy.functions.elementary.complexes import conjugate
|
||||
|
||||
# See mpmath #569 and SymPy #20569
|
||||
def betainc_mpmath_fix(a, b, x1, x2, reg=0):
|
||||
from mpmath import betainc, mpf
|
||||
if x1 == x2:
|
||||
return mpf(0)
|
||||
else:
|
||||
return betainc(a, b, x1, x2, reg)
|
||||
|
||||
###############################################################################
|
||||
############################ COMPLETE BETA FUNCTION ##########################
|
||||
###############################################################################
|
||||
|
||||
class beta(DefinedFunction):
|
||||
r"""
|
||||
The beta integral is called the Eulerian integral of the first kind by
|
||||
Legendre:
|
||||
|
||||
.. math::
|
||||
\mathrm{B}(x,y) \int^{1}_{0} t^{x-1} (1-t)^{y-1} \mathrm{d}t.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The Beta function or Euler's first integral is closely associated
|
||||
with the gamma function. The Beta function is often used in probability
|
||||
theory and mathematical statistics. It satisfies properties like:
|
||||
|
||||
.. math::
|
||||
\mathrm{B}(a,1) = \frac{1}{a} \\
|
||||
\mathrm{B}(a,b) = \mathrm{B}(b,a) \\
|
||||
\mathrm{B}(a,b) = \frac{\Gamma(a) \Gamma(b)}{\Gamma(a+b)}
|
||||
|
||||
Therefore for integral values of $a$ and $b$:
|
||||
|
||||
.. math::
|
||||
\mathrm{B} = \frac{(a-1)! (b-1)!}{(a+b-1)!}
|
||||
|
||||
A special case of the Beta function when `x = y` is the
|
||||
Central Beta function. It satisfies properties like:
|
||||
|
||||
.. math::
|
||||
\mathrm{B}(x) = 2^{1 - 2x}\mathrm{B}(x, \frac{1}{2})
|
||||
\mathrm{B}(x) = 2^{1 - 2x} cos(\pi x) \mathrm{B}(\frac{1}{2} - x, x)
|
||||
\mathrm{B}(x) = \int_{0}^{1} \frac{t^x}{(1 + t)^{2x}} dt
|
||||
\mathrm{B}(x) = \frac{2}{x} \prod_{n = 1}^{\infty} \frac{n(n + 2x)}{(n + x)^2}
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import I, pi
|
||||
>>> from sympy.abc import x, y
|
||||
|
||||
The Beta function obeys the mirror symmetry:
|
||||
|
||||
>>> from sympy import beta, conjugate
|
||||
>>> conjugate(beta(x, y))
|
||||
beta(conjugate(x), conjugate(y))
|
||||
|
||||
Differentiation with respect to both $x$ and $y$ is supported:
|
||||
|
||||
>>> from sympy import beta, diff
|
||||
>>> diff(beta(x, y), x)
|
||||
(polygamma(0, x) - polygamma(0, x + y))*beta(x, y)
|
||||
|
||||
>>> diff(beta(x, y), y)
|
||||
(polygamma(0, y) - polygamma(0, x + y))*beta(x, y)
|
||||
|
||||
>>> diff(beta(x), x)
|
||||
2*(polygamma(0, x) - polygamma(0, 2*x))*beta(x, x)
|
||||
|
||||
We can numerically evaluate the Beta function to
|
||||
arbitrary precision for any complex numbers x and y:
|
||||
|
||||
>>> from sympy import beta
|
||||
>>> beta(pi).evalf(40)
|
||||
0.02671848900111377452242355235388489324562
|
||||
|
||||
>>> beta(1 + I).evalf(20)
|
||||
-0.2112723729365330143 - 0.7655283165378005676*I
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
gamma: Gamma function.
|
||||
uppergamma: Upper incomplete gamma function.
|
||||
lowergamma: Lower incomplete gamma function.
|
||||
polygamma: Polygamma function.
|
||||
loggamma: Log Gamma function.
|
||||
digamma: Digamma function.
|
||||
trigamma: Trigamma function.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Beta_function
|
||||
.. [2] https://mathworld.wolfram.com/BetaFunction.html
|
||||
.. [3] https://dlmf.nist.gov/5.12
|
||||
|
||||
"""
|
||||
unbranched = True
|
||||
|
||||
def fdiff(self, argindex):
|
||||
x, y = self.args
|
||||
if argindex == 1:
|
||||
# Diff wrt x
|
||||
return beta(x, y)*(digamma(x) - digamma(x + y))
|
||||
elif argindex == 2:
|
||||
# Diff wrt y
|
||||
return beta(x, y)*(digamma(y) - digamma(x + y))
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, x, y=None):
|
||||
if y is None:
|
||||
return beta(x, x)
|
||||
if x.is_Number and y.is_Number:
|
||||
return beta(x, y, evaluate=False).doit()
|
||||
|
||||
def doit(self, **hints):
|
||||
x = xold = self.args[0]
|
||||
# Deal with unevaluated single argument beta
|
||||
single_argument = len(self.args) == 1
|
||||
y = yold = self.args[0] if single_argument else self.args[1]
|
||||
if hints.get('deep', True):
|
||||
x = x.doit(**hints)
|
||||
y = y.doit(**hints)
|
||||
if y.is_zero or x.is_zero:
|
||||
return S.ComplexInfinity
|
||||
if y is S.One:
|
||||
return 1/x
|
||||
if x is S.One:
|
||||
return 1/y
|
||||
if y == x + 1:
|
||||
return 1/(x*y*catalan(x))
|
||||
s = x + y
|
||||
if (s.is_integer and s.is_negative and x.is_integer is False and
|
||||
y.is_integer is False):
|
||||
return S.Zero
|
||||
if x == xold and y == yold and not single_argument:
|
||||
return self
|
||||
return beta(x, y)
|
||||
|
||||
def _eval_expand_func(self, **hints):
|
||||
x, y = self.args
|
||||
return gamma(x)*gamma(y) / gamma(x + y)
|
||||
|
||||
def _eval_is_real(self):
|
||||
return self.args[0].is_real and self.args[1].is_real
|
||||
|
||||
def _eval_conjugate(self):
|
||||
return self.func(self.args[0].conjugate(), self.args[1].conjugate())
|
||||
|
||||
def _eval_rewrite_as_gamma(self, x, y, piecewise=True, **kwargs):
|
||||
return self._eval_expand_func(**kwargs)
|
||||
|
||||
def _eval_rewrite_as_Integral(self, x, y, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
t = Dummy(uniquely_named_symbol('t', [x, y]).name)
|
||||
return Integral(t**(x - 1)*(1 - t)**(y - 1), (t, 0, 1))
|
||||
|
||||
###############################################################################
|
||||
########################## INCOMPLETE BETA FUNCTION ###########################
|
||||
###############################################################################
|
||||
|
||||
class betainc(DefinedFunction):
|
||||
r"""
|
||||
The Generalized Incomplete Beta function is defined as
|
||||
|
||||
.. math::
|
||||
\mathrm{B}_{(x_1, x_2)}(a, b) = \int_{x_1}^{x_2} t^{a - 1} (1 - t)^{b - 1} dt
|
||||
|
||||
The Incomplete Beta function is a special case
|
||||
of the Generalized Incomplete Beta function :
|
||||
|
||||
.. math:: \mathrm{B}_z (a, b) = \mathrm{B}_{(0, z)}(a, b)
|
||||
|
||||
The Incomplete Beta function satisfies :
|
||||
|
||||
.. math:: \mathrm{B}_z (a, b) = (-1)^a \mathrm{B}_{\frac{z}{z - 1}} (a, 1 - a - b)
|
||||
|
||||
The Beta function is a special case of the Incomplete Beta function :
|
||||
|
||||
.. math:: \mathrm{B}(a, b) = \mathrm{B}_{1}(a, b)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import betainc, symbols, conjugate
|
||||
>>> a, b, x, x1, x2 = symbols('a b x x1 x2')
|
||||
|
||||
The Generalized Incomplete Beta function is given by:
|
||||
|
||||
>>> betainc(a, b, x1, x2)
|
||||
betainc(a, b, x1, x2)
|
||||
|
||||
The Incomplete Beta function can be obtained as follows:
|
||||
|
||||
>>> betainc(a, b, 0, x)
|
||||
betainc(a, b, 0, x)
|
||||
|
||||
The Incomplete Beta function obeys the mirror symmetry:
|
||||
|
||||
>>> conjugate(betainc(a, b, x1, x2))
|
||||
betainc(conjugate(a), conjugate(b), conjugate(x1), conjugate(x2))
|
||||
|
||||
We can numerically evaluate the Incomplete Beta function to
|
||||
arbitrary precision for any complex numbers a, b, x1 and x2:
|
||||
|
||||
>>> from sympy import betainc, I
|
||||
>>> betainc(2, 3, 4, 5).evalf(10)
|
||||
56.08333333
|
||||
>>> betainc(0.75, 1 - 4*I, 0, 2 + 3*I).evalf(25)
|
||||
0.2241657956955709603655887 + 0.3619619242700451992411724*I
|
||||
|
||||
The Generalized Incomplete Beta function can be expressed
|
||||
in terms of the Generalized Hypergeometric function.
|
||||
|
||||
>>> from sympy import hyper
|
||||
>>> betainc(a, b, x1, x2).rewrite(hyper)
|
||||
(-x1**a*hyper((a, 1 - b), (a + 1,), x1) + x2**a*hyper((a, 1 - b), (a + 1,), x2))/a
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
beta: Beta function
|
||||
hyper: Generalized Hypergeometric function
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function
|
||||
.. [2] https://dlmf.nist.gov/8.17
|
||||
.. [3] https://functions.wolfram.com/GammaBetaErf/Beta4/
|
||||
.. [4] https://functions.wolfram.com/GammaBetaErf/BetaRegularized4/02/
|
||||
|
||||
"""
|
||||
nargs = 4
|
||||
unbranched = True
|
||||
|
||||
def fdiff(self, argindex):
|
||||
a, b, x1, x2 = self.args
|
||||
if argindex == 3:
|
||||
# Diff wrt x1
|
||||
return -(1 - x1)**(b - 1)*x1**(a - 1)
|
||||
elif argindex == 4:
|
||||
# Diff wrt x2
|
||||
return (1 - x2)**(b - 1)*x2**(a - 1)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_mpmath(self):
|
||||
return betainc_mpmath_fix, self.args
|
||||
|
||||
def _eval_is_real(self):
|
||||
if all(arg.is_real for arg in self.args):
|
||||
return True
|
||||
|
||||
def _eval_conjugate(self):
|
||||
return self.func(*map(conjugate, self.args))
|
||||
|
||||
def _eval_rewrite_as_Integral(self, a, b, x1, x2, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
t = Dummy(uniquely_named_symbol('t', [a, b, x1, x2]).name)
|
||||
return Integral(t**(a - 1)*(1 - t)**(b - 1), (t, x1, x2))
|
||||
|
||||
def _eval_rewrite_as_hyper(self, a, b, x1, x2, **kwargs):
|
||||
from sympy.functions.special.hyper import hyper
|
||||
return (x2**a * hyper((a, 1 - b), (a + 1,), x2) - x1**a * hyper((a, 1 - b), (a + 1,), x1)) / a
|
||||
|
||||
###############################################################################
|
||||
#################### REGULARIZED INCOMPLETE BETA FUNCTION #####################
|
||||
###############################################################################
|
||||
|
||||
class betainc_regularized(DefinedFunction):
|
||||
r"""
|
||||
The Generalized Regularized Incomplete Beta function is given by
|
||||
|
||||
.. math::
|
||||
\mathrm{I}_{(x_1, x_2)}(a, b) = \frac{\mathrm{B}_{(x_1, x_2)}(a, b)}{\mathrm{B}(a, b)}
|
||||
|
||||
The Regularized Incomplete Beta function is a special case
|
||||
of the Generalized Regularized Incomplete Beta function :
|
||||
|
||||
.. math:: \mathrm{I}_z (a, b) = \mathrm{I}_{(0, z)}(a, b)
|
||||
|
||||
The Regularized Incomplete Beta function is the cumulative distribution
|
||||
function of the beta distribution.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import betainc_regularized, symbols, conjugate
|
||||
>>> a, b, x, x1, x2 = symbols('a b x x1 x2')
|
||||
|
||||
The Generalized Regularized Incomplete Beta
|
||||
function is given by:
|
||||
|
||||
>>> betainc_regularized(a, b, x1, x2)
|
||||
betainc_regularized(a, b, x1, x2)
|
||||
|
||||
The Regularized Incomplete Beta function
|
||||
can be obtained as follows:
|
||||
|
||||
>>> betainc_regularized(a, b, 0, x)
|
||||
betainc_regularized(a, b, 0, x)
|
||||
|
||||
The Regularized Incomplete Beta function
|
||||
obeys the mirror symmetry:
|
||||
|
||||
>>> conjugate(betainc_regularized(a, b, x1, x2))
|
||||
betainc_regularized(conjugate(a), conjugate(b), conjugate(x1), conjugate(x2))
|
||||
|
||||
We can numerically evaluate the Regularized Incomplete Beta function
|
||||
to arbitrary precision for any complex numbers a, b, x1 and x2:
|
||||
|
||||
>>> from sympy import betainc_regularized, pi, E
|
||||
>>> betainc_regularized(1, 2, 0, 0.25).evalf(10)
|
||||
0.4375000000
|
||||
>>> betainc_regularized(pi, E, 0, 1).evalf(5)
|
||||
1.00000
|
||||
|
||||
The Generalized Regularized Incomplete Beta function can be
|
||||
expressed in terms of the Generalized Hypergeometric function.
|
||||
|
||||
>>> from sympy import hyper
|
||||
>>> betainc_regularized(a, b, x1, x2).rewrite(hyper)
|
||||
(-x1**a*hyper((a, 1 - b), (a + 1,), x1) + x2**a*hyper((a, 1 - b), (a + 1,), x2))/(a*beta(a, b))
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
beta: Beta function
|
||||
hyper: Generalized Hypergeometric function
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function
|
||||
.. [2] https://dlmf.nist.gov/8.17
|
||||
.. [3] https://functions.wolfram.com/GammaBetaErf/Beta4/
|
||||
.. [4] https://functions.wolfram.com/GammaBetaErf/BetaRegularized4/02/
|
||||
|
||||
"""
|
||||
nargs = 4
|
||||
unbranched = True
|
||||
|
||||
def __new__(cls, a, b, x1, x2):
|
||||
return super().__new__(cls, a, b, x1, x2)
|
||||
|
||||
def _eval_mpmath(self):
|
||||
return betainc_mpmath_fix, (*self.args, S(1))
|
||||
|
||||
def fdiff(self, argindex):
|
||||
a, b, x1, x2 = self.args
|
||||
if argindex == 3:
|
||||
# Diff wrt x1
|
||||
return -(1 - x1)**(b - 1)*x1**(a - 1) / beta(a, b)
|
||||
elif argindex == 4:
|
||||
# Diff wrt x2
|
||||
return (1 - x2)**(b - 1)*x2**(a - 1) / beta(a, b)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_is_real(self):
|
||||
if all(arg.is_real for arg in self.args):
|
||||
return True
|
||||
|
||||
def _eval_conjugate(self):
|
||||
return self.func(*map(conjugate, self.args))
|
||||
|
||||
def _eval_rewrite_as_Integral(self, a, b, x1, x2, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
t = Dummy(uniquely_named_symbol('t', [a, b, x1, x2]).name)
|
||||
integrand = t**(a - 1)*(1 - t)**(b - 1)
|
||||
expr = Integral(integrand, (t, x1, x2))
|
||||
return expr / Integral(integrand, (t, 0, 1))
|
||||
|
||||
def _eval_rewrite_as_hyper(self, a, b, x1, x2, **kwargs):
|
||||
from sympy.functions.special.hyper import hyper
|
||||
expr = (x2**a * hyper((a, 1 - b), (a + 1,), x2) - x1**a * hyper((a, 1 - b), (a + 1,), x1)) / a
|
||||
return expr / beta(a, b)
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
from sympy.core import S, sympify
|
||||
from sympy.core.symbol import (Dummy, symbols)
|
||||
from sympy.functions import Piecewise, piecewise_fold
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.sets.sets import Interval
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
def _ivl(cond, x):
|
||||
"""return the interval corresponding to the condition
|
||||
|
||||
Conditions in spline's Piecewise give the range over
|
||||
which an expression is valid like (lo <= x) & (x <= hi).
|
||||
This function returns (lo, hi).
|
||||
"""
|
||||
if isinstance(cond, And) and len(cond.args) == 2:
|
||||
a, b = cond.args
|
||||
if a.lts == x:
|
||||
a, b = b, a
|
||||
return a.lts, b.gts
|
||||
raise TypeError('unexpected cond type: %s' % cond)
|
||||
|
||||
|
||||
def _add_splines(c, b1, d, b2, x):
|
||||
"""Construct c*b1 + d*b2."""
|
||||
|
||||
if S.Zero in (b1, c):
|
||||
rv = piecewise_fold(d * b2)
|
||||
elif S.Zero in (b2, d):
|
||||
rv = piecewise_fold(c * b1)
|
||||
else:
|
||||
new_args = []
|
||||
# Just combining the Piecewise without any fancy optimization
|
||||
p1 = piecewise_fold(c * b1)
|
||||
p2 = piecewise_fold(d * b2)
|
||||
|
||||
# Search all Piecewise arguments except (0, True)
|
||||
p2args = list(p2.args[:-1])
|
||||
|
||||
# This merging algorithm assumes the conditions in
|
||||
# p1 and p2 are sorted
|
||||
for arg in p1.args[:-1]:
|
||||
expr = arg.expr
|
||||
cond = arg.cond
|
||||
|
||||
lower = _ivl(cond, x)[0]
|
||||
|
||||
# Check p2 for matching conditions that can be merged
|
||||
for i, arg2 in enumerate(p2args):
|
||||
expr2 = arg2.expr
|
||||
cond2 = arg2.cond
|
||||
|
||||
lower_2, upper_2 = _ivl(cond2, x)
|
||||
if cond2 == cond:
|
||||
# Conditions match, join expressions
|
||||
expr += expr2
|
||||
# Remove matching element
|
||||
del p2args[i]
|
||||
# No need to check the rest
|
||||
break
|
||||
elif lower_2 < lower and upper_2 <= lower:
|
||||
# Check if arg2 condition smaller than arg1,
|
||||
# add to new_args by itself (no match expected
|
||||
# in p1)
|
||||
new_args.append(arg2)
|
||||
del p2args[i]
|
||||
break
|
||||
|
||||
# Checked all, add expr and cond
|
||||
new_args.append((expr, cond))
|
||||
|
||||
# Add remaining items from p2args
|
||||
new_args.extend(p2args)
|
||||
|
||||
# Add final (0, True)
|
||||
new_args.append((0, True))
|
||||
|
||||
rv = Piecewise(*new_args, evaluate=False)
|
||||
|
||||
return rv.expand()
|
||||
|
||||
|
||||
@lru_cache(maxsize=128)
|
||||
def bspline_basis(d, knots, n, x):
|
||||
"""
|
||||
The $n$-th B-spline at $x$ of degree $d$ with knots.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
B-Splines are piecewise polynomials of degree $d$. They are defined on a
|
||||
set of knots, which is a sequence of integers or floats.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
The 0th degree splines have a value of 1 on a single interval:
|
||||
|
||||
>>> from sympy import bspline_basis
|
||||
>>> from sympy.abc import x
|
||||
>>> d = 0
|
||||
>>> knots = tuple(range(5))
|
||||
>>> bspline_basis(d, knots, 0, x)
|
||||
Piecewise((1, (x >= 0) & (x <= 1)), (0, True))
|
||||
|
||||
For a given ``(d, knots)`` there are ``len(knots)-d-1`` B-splines
|
||||
defined, that are indexed by ``n`` (starting at 0).
|
||||
|
||||
Here is an example of a cubic B-spline:
|
||||
|
||||
>>> bspline_basis(3, tuple(range(5)), 0, x)
|
||||
Piecewise((x**3/6, (x >= 0) & (x <= 1)),
|
||||
(-x**3/2 + 2*x**2 - 2*x + 2/3,
|
||||
(x >= 1) & (x <= 2)),
|
||||
(x**3/2 - 4*x**2 + 10*x - 22/3,
|
||||
(x >= 2) & (x <= 3)),
|
||||
(-x**3/6 + 2*x**2 - 8*x + 32/3,
|
||||
(x >= 3) & (x <= 4)),
|
||||
(0, True))
|
||||
|
||||
By repeating knot points, you can introduce discontinuities in the
|
||||
B-splines and their derivatives:
|
||||
|
||||
>>> d = 1
|
||||
>>> knots = (0, 0, 2, 3, 4)
|
||||
>>> bspline_basis(d, knots, 0, x)
|
||||
Piecewise((1 - x/2, (x >= 0) & (x <= 2)), (0, True))
|
||||
|
||||
It is quite time consuming to construct and evaluate B-splines. If
|
||||
you need to evaluate a B-spline many times, it is best to lambdify them
|
||||
first:
|
||||
|
||||
>>> from sympy import lambdify
|
||||
>>> d = 3
|
||||
>>> knots = tuple(range(10))
|
||||
>>> b0 = bspline_basis(d, knots, 0, x)
|
||||
>>> f = lambdify(x, b0)
|
||||
>>> y = f(0.5)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
d : integer
|
||||
degree of bspline
|
||||
|
||||
knots : list of integer values
|
||||
list of knots points of bspline
|
||||
|
||||
n : integer
|
||||
$n$-th B-spline
|
||||
|
||||
x : symbol
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
bspline_basis_set
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/B-spline
|
||||
|
||||
"""
|
||||
# make sure x has no assumptions so conditions don't evaluate
|
||||
xvar = x
|
||||
x = Dummy()
|
||||
|
||||
knots = tuple(sympify(k) for k in knots)
|
||||
d = int(d)
|
||||
n = int(n)
|
||||
n_knots = len(knots)
|
||||
n_intervals = n_knots - 1
|
||||
if n + d + 1 > n_intervals:
|
||||
raise ValueError("n + d + 1 must not exceed len(knots) - 1")
|
||||
if d == 0:
|
||||
result = Piecewise(
|
||||
(S.One, Interval(knots[n], knots[n + 1]).contains(x)), (0, True)
|
||||
)
|
||||
elif d > 0:
|
||||
denom = knots[n + d + 1] - knots[n + 1]
|
||||
if denom != S.Zero:
|
||||
B = (knots[n + d + 1] - x) / denom
|
||||
b2 = bspline_basis(d - 1, knots, n + 1, x)
|
||||
else:
|
||||
b2 = B = S.Zero
|
||||
|
||||
denom = knots[n + d] - knots[n]
|
||||
if denom != S.Zero:
|
||||
A = (x - knots[n]) / denom
|
||||
b1 = bspline_basis(d - 1, knots, n, x)
|
||||
else:
|
||||
b1 = A = S.Zero
|
||||
|
||||
result = _add_splines(A, b1, B, b2, x)
|
||||
else:
|
||||
raise ValueError("degree must be non-negative: %r" % n)
|
||||
|
||||
# return result with user-given x
|
||||
return result.xreplace({x: xvar})
|
||||
|
||||
|
||||
def bspline_basis_set(d, knots, x):
|
||||
"""
|
||||
Return the ``len(knots)-d-1`` B-splines at *x* of degree *d*
|
||||
with *knots*.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns a list of piecewise polynomials that are the
|
||||
``len(knots)-d-1`` B-splines of degree *d* for the given knots.
|
||||
This function calls ``bspline_basis(d, knots, n, x)`` for different
|
||||
values of *n*.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import bspline_basis_set
|
||||
>>> from sympy.abc import x
|
||||
>>> d = 2
|
||||
>>> knots = range(5)
|
||||
>>> splines = bspline_basis_set(d, knots, x)
|
||||
>>> splines
|
||||
[Piecewise((x**2/2, (x >= 0) & (x <= 1)),
|
||||
(-x**2 + 3*x - 3/2, (x >= 1) & (x <= 2)),
|
||||
(x**2/2 - 3*x + 9/2, (x >= 2) & (x <= 3)),
|
||||
(0, True)),
|
||||
Piecewise((x**2/2 - x + 1/2, (x >= 1) & (x <= 2)),
|
||||
(-x**2 + 5*x - 11/2, (x >= 2) & (x <= 3)),
|
||||
(x**2/2 - 4*x + 8, (x >= 3) & (x <= 4)),
|
||||
(0, True))]
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
d : integer
|
||||
degree of bspline
|
||||
|
||||
knots : list of integers
|
||||
list of knots points of bspline
|
||||
|
||||
x : symbol
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
bspline_basis
|
||||
|
||||
"""
|
||||
n_splines = len(knots) - d - 1
|
||||
return [bspline_basis(d, tuple(knots), i, x) for i in range(n_splines)]
|
||||
|
||||
|
||||
def interpolating_spline(d, x, X, Y):
|
||||
"""
|
||||
Return spline of degree *d*, passing through the given *X*
|
||||
and *Y* values.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns a piecewise function such that each part is
|
||||
a polynomial of degree not greater than *d*. The value of *d*
|
||||
must be 1 or greater and the values of *X* must be strictly
|
||||
increasing.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import interpolating_spline
|
||||
>>> from sympy.abc import x
|
||||
>>> interpolating_spline(1, x, [1, 2, 4, 7], [3, 6, 5, 7])
|
||||
Piecewise((3*x, (x >= 1) & (x <= 2)),
|
||||
(7 - x/2, (x >= 2) & (x <= 4)),
|
||||
(2*x/3 + 7/3, (x >= 4) & (x <= 7)))
|
||||
>>> interpolating_spline(3, x, [-2, 0, 1, 3, 4], [4, 2, 1, 1, 3])
|
||||
Piecewise((7*x**3/117 + 7*x**2/117 - 131*x/117 + 2, (x >= -2) & (x <= 1)),
|
||||
(10*x**3/117 - 2*x**2/117 - 122*x/117 + 77/39, (x >= 1) & (x <= 4)))
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
d : integer
|
||||
Degree of Bspline strictly greater than equal to one
|
||||
|
||||
x : symbol
|
||||
|
||||
X : list of strictly increasing real values
|
||||
list of X coordinates through which the spline passes
|
||||
|
||||
Y : list of real values
|
||||
list of corresponding Y coordinates through which the spline passes
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
bspline_basis_set, interpolating_poly
|
||||
|
||||
"""
|
||||
from sympy.solvers.solveset import linsolve
|
||||
from sympy.matrices.dense import Matrix
|
||||
|
||||
# Input sanitization
|
||||
d = sympify(d)
|
||||
if not (d.is_Integer and d.is_positive):
|
||||
raise ValueError("Spline degree must be a positive integer, not %s." % d)
|
||||
if len(X) != len(Y):
|
||||
raise ValueError("Number of X and Y coordinates must be the same.")
|
||||
if len(X) < d + 1:
|
||||
raise ValueError("Degree must be less than the number of control points.")
|
||||
if not all(a < b for a, b in zip(X, X[1:])):
|
||||
raise ValueError("The x-coordinates must be strictly increasing.")
|
||||
X = [sympify(i) for i in X]
|
||||
|
||||
# Evaluating knots value
|
||||
if d.is_odd:
|
||||
j = (d + 1) // 2
|
||||
interior_knots = X[j:-j]
|
||||
else:
|
||||
j = d // 2
|
||||
interior_knots = [
|
||||
(a + b)/2 for a, b in zip(X[j : -j - 1], X[j + 1 : -j])
|
||||
]
|
||||
|
||||
knots = [X[0]] * (d + 1) + list(interior_knots) + [X[-1]] * (d + 1)
|
||||
|
||||
basis = bspline_basis_set(d, knots, x)
|
||||
|
||||
A = [[b.subs(x, v) for b in basis] for v in X]
|
||||
|
||||
coeff = linsolve((Matrix(A), Matrix(Y)), symbols("c0:{}".format(len(X)), cls=Dummy))
|
||||
coeff = list(coeff)[0]
|
||||
intervals = {c for b in basis for (e, c) in b.args if c != True}
|
||||
|
||||
# Sorting the intervals
|
||||
# ival contains the end-points of each interval
|
||||
intervals = sorted(intervals, key=lambda c: _ivl(c, x))
|
||||
|
||||
basis_dicts = [{c: e for (e, c) in b.args} for b in basis]
|
||||
spline = []
|
||||
for i in intervals:
|
||||
piece = sum(
|
||||
[c * d.get(i, S.Zero) for (c, d) in zip(coeff, basis_dicts)], S.Zero
|
||||
)
|
||||
spline.append((piece, i))
|
||||
return Piecewise(*spline)
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
from sympy.core import S, diff
|
||||
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
||||
from sympy.core.logic import fuzzy_not
|
||||
from sympy.core.relational import Eq, Ne
|
||||
from sympy.functions.elementary.complexes import im, sign
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.polys.polyerrors import PolynomialError
|
||||
from sympy.polys.polyroots import roots
|
||||
from sympy.utilities.misc import filldedent
|
||||
|
||||
|
||||
###############################################################################
|
||||
################################ DELTA FUNCTION ###############################
|
||||
###############################################################################
|
||||
|
||||
|
||||
class DiracDelta(DefinedFunction):
|
||||
r"""
|
||||
The DiracDelta function and its derivatives.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
DiracDelta is not an ordinary function. It can be rigorously defined either
|
||||
as a distribution or as a measure.
|
||||
|
||||
DiracDelta only makes sense in definite integrals, and in particular,
|
||||
integrals of the form ``Integral(f(x)*DiracDelta(x - x0), (x, a, b))``,
|
||||
where it equals ``f(x0)`` if ``a <= x0 <= b`` and ``0`` otherwise. Formally,
|
||||
DiracDelta acts in some ways like a function that is ``0`` everywhere except
|
||||
at ``0``, but in many ways it also does not. It can often be useful to treat
|
||||
DiracDelta in formal ways, building up and manipulating expressions with
|
||||
delta functions (which may eventually be integrated), but care must be taken
|
||||
to not treat it as a real function. SymPy's ``oo`` is similar. It only
|
||||
truly makes sense formally in certain contexts (such as integration limits),
|
||||
but SymPy allows its use everywhere, and it tries to be consistent with
|
||||
operations on it (like ``1/oo``), but it is easy to get into trouble and get
|
||||
wrong results if ``oo`` is treated too much like a number. Similarly, if
|
||||
DiracDelta is treated too much like a function, it is easy to get wrong or
|
||||
nonsensical results.
|
||||
|
||||
DiracDelta function has the following properties:
|
||||
|
||||
1) $\frac{d}{d x} \theta(x) = \delta(x)$
|
||||
2) $\int_{-\infty}^\infty \delta(x - a)f(x)\, dx = f(a)$ and $\int_{a-
|
||||
\epsilon}^{a+\epsilon} \delta(x - a)f(x)\, dx = f(a)$
|
||||
3) $\delta(x) = 0$ for all $x \neq 0$
|
||||
4) $\delta(g(x)) = \sum_i \frac{\delta(x - x_i)}{\|g'(x_i)\|}$ where $x_i$
|
||||
are the roots of $g$
|
||||
5) $\delta(-x) = \delta(x)$
|
||||
|
||||
Derivatives of ``k``-th order of DiracDelta have the following properties:
|
||||
|
||||
6) $\delta(x, k) = 0$ for all $x \neq 0$
|
||||
7) $\delta(-x, k) = -\delta(x, k)$ for odd $k$
|
||||
8) $\delta(-x, k) = \delta(x, k)$ for even $k$
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import DiracDelta, diff, pi
|
||||
>>> from sympy.abc import x, y
|
||||
|
||||
>>> DiracDelta(x)
|
||||
DiracDelta(x)
|
||||
>>> DiracDelta(1)
|
||||
0
|
||||
>>> DiracDelta(-1)
|
||||
0
|
||||
>>> DiracDelta(pi)
|
||||
0
|
||||
>>> DiracDelta(x - 4).subs(x, 4)
|
||||
DiracDelta(0)
|
||||
>>> diff(DiracDelta(x))
|
||||
DiracDelta(x, 1)
|
||||
>>> diff(DiracDelta(x - 1), x, 2)
|
||||
DiracDelta(x - 1, 2)
|
||||
>>> diff(DiracDelta(x**2 - 1), x, 2)
|
||||
2*(2*x**2*DiracDelta(x**2 - 1, 2) + DiracDelta(x**2 - 1, 1))
|
||||
>>> DiracDelta(3*x).is_simple(x)
|
||||
True
|
||||
>>> DiracDelta(x**2).is_simple(x)
|
||||
False
|
||||
>>> DiracDelta((x**2 - 1)*y).expand(diracdelta=True, wrt=x)
|
||||
DiracDelta(x - 1)/(2*Abs(y)) + DiracDelta(x + 1)/(2*Abs(y))
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Heaviside
|
||||
sympy.simplify.simplify.simplify, is_simple
|
||||
sympy.functions.special.tensor_functions.KroneckerDelta
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://mathworld.wolfram.com/DeltaFunction.html
|
||||
|
||||
"""
|
||||
|
||||
is_real = True
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
"""
|
||||
Returns the first derivative of a DiracDelta Function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the
|
||||
user-level function and ``fdiff()`` is an object method. ``fdiff()`` is
|
||||
a convenience method available in the ``Function`` class. It returns
|
||||
the derivative of the function without considering the chain rule.
|
||||
``diff(function, x)`` calls ``Function._eval_derivative`` which in turn
|
||||
calls ``fdiff()`` internally to compute the derivative of the function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import DiracDelta, diff
|
||||
>>> from sympy.abc import x
|
||||
|
||||
>>> DiracDelta(x).fdiff()
|
||||
DiracDelta(x, 1)
|
||||
|
||||
>>> DiracDelta(x, 1).fdiff()
|
||||
DiracDelta(x, 2)
|
||||
|
||||
>>> DiracDelta(x**2 - 1).fdiff()
|
||||
DiracDelta(x**2 - 1, 1)
|
||||
|
||||
>>> diff(DiracDelta(x, 1)).fdiff()
|
||||
DiracDelta(x, 3)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
argindex : integer
|
||||
degree of derivative
|
||||
|
||||
"""
|
||||
if argindex == 1:
|
||||
#I didn't know if there is a better way to handle default arguments
|
||||
k = 0
|
||||
if len(self.args) > 1:
|
||||
k = self.args[1]
|
||||
return self.func(self.args[0], k + 1)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, arg, k=S.Zero):
|
||||
"""
|
||||
Returns a simplified form or a value of DiracDelta depending on the
|
||||
argument passed by the DiracDelta object.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The ``eval()`` method is automatically called when the ``DiracDelta``
|
||||
class is about to be instantiated and it returns either some simplified
|
||||
instance or the unevaluated instance depending on the argument passed.
|
||||
In other words, ``eval()`` method is not needed to be called explicitly,
|
||||
it is being called and evaluated once the object is called.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import DiracDelta, S
|
||||
>>> from sympy.abc import x
|
||||
|
||||
>>> DiracDelta(x)
|
||||
DiracDelta(x)
|
||||
|
||||
>>> DiracDelta(-x, 1)
|
||||
-DiracDelta(x, 1)
|
||||
|
||||
>>> DiracDelta(1)
|
||||
0
|
||||
|
||||
>>> DiracDelta(5, 1)
|
||||
0
|
||||
|
||||
>>> DiracDelta(0)
|
||||
DiracDelta(0)
|
||||
|
||||
>>> DiracDelta(-1)
|
||||
0
|
||||
|
||||
>>> DiracDelta(S.NaN)
|
||||
nan
|
||||
|
||||
>>> DiracDelta(x - 100).subs(x, 5)
|
||||
0
|
||||
|
||||
>>> DiracDelta(x - 100).subs(x, 100)
|
||||
DiracDelta(0)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
k : integer
|
||||
order of derivative
|
||||
|
||||
arg : argument passed to DiracDelta
|
||||
|
||||
"""
|
||||
if not k.is_Integer or k.is_negative:
|
||||
raise ValueError("Error: the second argument of DiracDelta must be \
|
||||
a non-negative integer, %s given instead." % (k,))
|
||||
if arg is S.NaN:
|
||||
return S.NaN
|
||||
if arg.is_nonzero:
|
||||
return S.Zero
|
||||
if fuzzy_not(im(arg).is_zero):
|
||||
raise ValueError(filldedent('''
|
||||
Function defined only for Real Values.
|
||||
Complex part: %s found in %s .''' % (
|
||||
repr(im(arg)), repr(arg))))
|
||||
c, nc = arg.args_cnc()
|
||||
if c and c[0] is S.NegativeOne:
|
||||
# keep this fast and simple instead of using
|
||||
# could_extract_minus_sign
|
||||
if k.is_odd:
|
||||
return -cls(-arg, k)
|
||||
elif k.is_even:
|
||||
return cls(-arg, k) if k else cls(-arg)
|
||||
elif k.is_zero:
|
||||
return cls(arg, evaluate=False)
|
||||
|
||||
def _eval_expand_diracdelta(self, **hints):
|
||||
"""
|
||||
Compute a simplified representation of the function using
|
||||
property number 4. Pass ``wrt`` as a hint to expand the expression
|
||||
with respect to a particular variable.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
``wrt`` is:
|
||||
|
||||
- a variable with respect to which a DiracDelta expression will
|
||||
get expanded.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import DiracDelta
|
||||
>>> from sympy.abc import x, y
|
||||
|
||||
>>> DiracDelta(x*y).expand(diracdelta=True, wrt=x)
|
||||
DiracDelta(x)/Abs(y)
|
||||
>>> DiracDelta(x*y).expand(diracdelta=True, wrt=y)
|
||||
DiracDelta(y)/Abs(x)
|
||||
|
||||
>>> DiracDelta(x**2 + x - 2).expand(diracdelta=True, wrt=x)
|
||||
DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
is_simple, Diracdelta
|
||||
|
||||
"""
|
||||
wrt = hints.get('wrt', None)
|
||||
if wrt is None:
|
||||
free = self.free_symbols
|
||||
if len(free) == 1:
|
||||
wrt = free.pop()
|
||||
else:
|
||||
raise TypeError(filldedent('''
|
||||
When there is more than 1 free symbol or variable in the expression,
|
||||
the 'wrt' keyword is required as a hint to expand when using the
|
||||
DiracDelta hint.'''))
|
||||
|
||||
if not self.args[0].has(wrt) or (len(self.args) > 1 and self.args[1] != 0 ):
|
||||
return self
|
||||
try:
|
||||
argroots = roots(self.args[0], wrt)
|
||||
result = 0
|
||||
valid = True
|
||||
darg = abs(diff(self.args[0], wrt))
|
||||
for r, m in argroots.items():
|
||||
if r.is_real is not False and m == 1:
|
||||
result += self.func(wrt - r)/darg.subs(wrt, r)
|
||||
else:
|
||||
# don't handle non-real and if m != 1 then
|
||||
# a polynomial will have a zero in the derivative (darg)
|
||||
# at r
|
||||
valid = False
|
||||
break
|
||||
if valid:
|
||||
return result
|
||||
except PolynomialError:
|
||||
pass
|
||||
return self
|
||||
|
||||
def is_simple(self, x):
|
||||
"""
|
||||
Tells whether the argument(args[0]) of DiracDelta is a linear
|
||||
expression in *x*.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import DiracDelta, cos
|
||||
>>> from sympy.abc import x, y
|
||||
|
||||
>>> DiracDelta(x*y).is_simple(x)
|
||||
True
|
||||
>>> DiracDelta(x*y).is_simple(y)
|
||||
True
|
||||
|
||||
>>> DiracDelta(x**2 + x - 2).is_simple(x)
|
||||
False
|
||||
|
||||
>>> DiracDelta(cos(x)).is_simple(x)
|
||||
False
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
x : can be a symbol
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.simplify.simplify.simplify, DiracDelta
|
||||
|
||||
"""
|
||||
p = self.args[0].as_poly(x)
|
||||
if p:
|
||||
return p.degree() == 1
|
||||
return False
|
||||
|
||||
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
||||
"""
|
||||
Represents DiracDelta in a piecewise form.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import DiracDelta, Piecewise, Symbol
|
||||
>>> x = Symbol('x')
|
||||
|
||||
>>> DiracDelta(x).rewrite(Piecewise)
|
||||
Piecewise((DiracDelta(0), Eq(x, 0)), (0, True))
|
||||
|
||||
>>> DiracDelta(x - 5).rewrite(Piecewise)
|
||||
Piecewise((DiracDelta(0), Eq(x, 5)), (0, True))
|
||||
|
||||
>>> DiracDelta(x**2 - 5).rewrite(Piecewise)
|
||||
Piecewise((DiracDelta(0), Eq(x**2, 5)), (0, True))
|
||||
|
||||
>>> DiracDelta(x - 5, 4).rewrite(Piecewise)
|
||||
DiracDelta(x - 5, 4)
|
||||
|
||||
"""
|
||||
if len(args) == 1:
|
||||
return Piecewise((DiracDelta(0), Eq(args[0], 0)), (0, True))
|
||||
|
||||
def _eval_rewrite_as_SingularityFunction(self, *args, **kwargs):
|
||||
"""
|
||||
Returns the DiracDelta expression written in the form of Singularity
|
||||
Functions.
|
||||
|
||||
"""
|
||||
from sympy.solvers import solve
|
||||
from sympy.functions.special.singularity_functions import SingularityFunction
|
||||
if self == DiracDelta(0):
|
||||
return SingularityFunction(0, 0, -1)
|
||||
if self == DiracDelta(0, 1):
|
||||
return SingularityFunction(0, 0, -2)
|
||||
free = self.free_symbols
|
||||
if len(free) == 1:
|
||||
x = (free.pop())
|
||||
if len(args) == 1:
|
||||
return SingularityFunction(x, solve(args[0], x)[0], -1)
|
||||
return SingularityFunction(x, solve(args[0], x)[0], -args[1] - 1)
|
||||
else:
|
||||
# I don't know how to handle the case for DiracDelta expressions
|
||||
# having arguments with more than one variable.
|
||||
raise TypeError(filldedent('''
|
||||
rewrite(SingularityFunction) does not support
|
||||
arguments with more that one variable.'''))
|
||||
|
||||
|
||||
###############################################################################
|
||||
############################## HEAVISIDE FUNCTION #############################
|
||||
###############################################################################
|
||||
|
||||
|
||||
class Heaviside(DefinedFunction):
|
||||
r"""
|
||||
Heaviside step function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The Heaviside step function has the following properties:
|
||||
|
||||
1) $\frac{d}{d x} \theta(x) = \delta(x)$
|
||||
2) $\theta(x) = \begin{cases} 0 & \text{for}\: x < 0 \\ \frac{1}{2} &
|
||||
\text{for}\: x = 0 \\1 & \text{for}\: x > 0 \end{cases}$
|
||||
3) $\frac{d}{d x} \max(x, 0) = \theta(x)$
|
||||
|
||||
Heaviside(x) is printed as $\theta(x)$ with the SymPy LaTeX printer.
|
||||
|
||||
The value at 0 is set differently in different fields. SymPy uses 1/2,
|
||||
which is a convention from electronics and signal processing, and is
|
||||
consistent with solving improper integrals by Fourier transform and
|
||||
convolution.
|
||||
|
||||
To specify a different value of Heaviside at ``x=0``, a second argument
|
||||
can be given. Using ``Heaviside(x, nan)`` gives an expression that will
|
||||
evaluate to nan for x=0.
|
||||
|
||||
.. versionchanged:: 1.9 ``Heaviside(0)`` now returns 1/2 (before: undefined)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Heaviside, nan
|
||||
>>> from sympy.abc import x
|
||||
>>> Heaviside(9)
|
||||
1
|
||||
>>> Heaviside(-9)
|
||||
0
|
||||
>>> Heaviside(0)
|
||||
1/2
|
||||
>>> Heaviside(0, nan)
|
||||
nan
|
||||
>>> (Heaviside(x) + 1).replace(Heaviside(x), Heaviside(x, 1))
|
||||
Heaviside(x, 1) + 1
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
DiracDelta
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://mathworld.wolfram.com/HeavisideStepFunction.html
|
||||
.. [2] https://dlmf.nist.gov/1.16#iv
|
||||
|
||||
"""
|
||||
|
||||
is_real = True
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
"""
|
||||
Returns the first derivative of a Heaviside Function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Heaviside, diff
|
||||
>>> from sympy.abc import x
|
||||
|
||||
>>> Heaviside(x).fdiff()
|
||||
DiracDelta(x)
|
||||
|
||||
>>> Heaviside(x**2 - 1).fdiff()
|
||||
DiracDelta(x**2 - 1)
|
||||
|
||||
>>> diff(Heaviside(x)).fdiff()
|
||||
DiracDelta(x, 1)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
argindex : integer
|
||||
order of derivative
|
||||
|
||||
"""
|
||||
if argindex == 1:
|
||||
return DiracDelta(self.args[0])
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def __new__(cls, arg, H0=S.Half, **options):
|
||||
if isinstance(H0, Heaviside) and len(H0.args) == 1:
|
||||
H0 = S.Half
|
||||
return super(cls, cls).__new__(cls, arg, H0, **options)
|
||||
|
||||
@property
|
||||
def pargs(self):
|
||||
"""Args without default S.Half"""
|
||||
args = self.args
|
||||
if args[1] is S.Half:
|
||||
args = args[:1]
|
||||
return args
|
||||
|
||||
@classmethod
|
||||
def eval(cls, arg, H0=S.Half):
|
||||
"""
|
||||
Returns a simplified form or a value of Heaviside depending on the
|
||||
argument passed by the Heaviside object.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The ``eval()`` method is automatically called when the ``Heaviside``
|
||||
class is about to be instantiated and it returns either some simplified
|
||||
instance or the unevaluated instance depending on the argument passed.
|
||||
In other words, ``eval()`` method is not needed to be called explicitly,
|
||||
it is being called and evaluated once the object is called.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Heaviside, S
|
||||
>>> from sympy.abc import x
|
||||
|
||||
>>> Heaviside(x)
|
||||
Heaviside(x)
|
||||
|
||||
>>> Heaviside(19)
|
||||
1
|
||||
|
||||
>>> Heaviside(0)
|
||||
1/2
|
||||
|
||||
>>> Heaviside(0, 1)
|
||||
1
|
||||
|
||||
>>> Heaviside(-5)
|
||||
0
|
||||
|
||||
>>> Heaviside(S.NaN)
|
||||
nan
|
||||
|
||||
>>> Heaviside(x - 100).subs(x, 5)
|
||||
0
|
||||
|
||||
>>> Heaviside(x - 100).subs(x, 105)
|
||||
1
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
arg : argument passed by Heaviside object
|
||||
|
||||
H0 : value of Heaviside(0)
|
||||
|
||||
"""
|
||||
if arg.is_extended_negative:
|
||||
return S.Zero
|
||||
elif arg.is_extended_positive:
|
||||
return S.One
|
||||
elif arg.is_zero:
|
||||
return H0
|
||||
elif arg is S.NaN:
|
||||
return S.NaN
|
||||
elif fuzzy_not(im(arg).is_zero):
|
||||
raise ValueError("Function defined only for Real Values. Complex part: %s found in %s ." % (repr(im(arg)), repr(arg)) )
|
||||
|
||||
def _eval_rewrite_as_Piecewise(self, arg, H0=None, **kwargs):
|
||||
"""
|
||||
Represents Heaviside in a Piecewise form.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Heaviside, Piecewise, Symbol, nan
|
||||
>>> x = Symbol('x')
|
||||
|
||||
>>> Heaviside(x).rewrite(Piecewise)
|
||||
Piecewise((0, x < 0), (1/2, Eq(x, 0)), (1, True))
|
||||
|
||||
>>> Heaviside(x,nan).rewrite(Piecewise)
|
||||
Piecewise((0, x < 0), (nan, Eq(x, 0)), (1, True))
|
||||
|
||||
>>> Heaviside(x - 5).rewrite(Piecewise)
|
||||
Piecewise((0, x < 5), (1/2, Eq(x, 5)), (1, True))
|
||||
|
||||
>>> Heaviside(x**2 - 1).rewrite(Piecewise)
|
||||
Piecewise((0, x**2 < 1), (1/2, Eq(x**2, 1)), (1, True))
|
||||
|
||||
"""
|
||||
if H0 == 0:
|
||||
return Piecewise((0, arg <= 0), (1, True))
|
||||
if H0 == 1:
|
||||
return Piecewise((0, arg < 0), (1, True))
|
||||
return Piecewise((0, arg < 0), (H0, Eq(arg, 0)), (1, True))
|
||||
|
||||
def _eval_rewrite_as_sign(self, arg, H0=S.Half, **kwargs):
|
||||
"""
|
||||
Represents the Heaviside function in the form of sign function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The value of Heaviside(0) must be 1/2 for rewriting as sign to be
|
||||
strictly equivalent. For easier usage, we also allow this rewriting
|
||||
when Heaviside(0) is undefined.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Heaviside, Symbol, sign, nan
|
||||
>>> x = Symbol('x', real=True)
|
||||
>>> y = Symbol('y')
|
||||
|
||||
>>> Heaviside(x).rewrite(sign)
|
||||
sign(x)/2 + 1/2
|
||||
|
||||
>>> Heaviside(x, 0).rewrite(sign)
|
||||
Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (0, True))
|
||||
|
||||
>>> Heaviside(x, nan).rewrite(sign)
|
||||
Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (nan, True))
|
||||
|
||||
>>> Heaviside(x - 2).rewrite(sign)
|
||||
sign(x - 2)/2 + 1/2
|
||||
|
||||
>>> Heaviside(x**2 - 2*x + 1).rewrite(sign)
|
||||
sign(x**2 - 2*x + 1)/2 + 1/2
|
||||
|
||||
>>> Heaviside(y).rewrite(sign)
|
||||
Heaviside(y)
|
||||
|
||||
>>> Heaviside(y**2 - 2*y + 1).rewrite(sign)
|
||||
Heaviside(y**2 - 2*y + 1)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sign
|
||||
|
||||
"""
|
||||
if arg.is_extended_real:
|
||||
pw1 = Piecewise(
|
||||
((sign(arg) + 1)/2, Ne(arg, 0)),
|
||||
(Heaviside(0, H0=H0), True))
|
||||
pw2 = Piecewise(
|
||||
((sign(arg) + 1)/2, Eq(Heaviside(0, H0=H0), S.Half)),
|
||||
(pw1, True))
|
||||
return pw2
|
||||
|
||||
def _eval_rewrite_as_SingularityFunction(self, args, H0=S.Half, **kwargs):
|
||||
"""
|
||||
Returns the Heaviside expression written in the form of Singularity
|
||||
Functions.
|
||||
|
||||
"""
|
||||
from sympy.solvers import solve
|
||||
from sympy.functions.special.singularity_functions import SingularityFunction
|
||||
if self == Heaviside(0):
|
||||
return SingularityFunction(0, 0, 0)
|
||||
free = self.free_symbols
|
||||
if len(free) == 1:
|
||||
x = (free.pop())
|
||||
return SingularityFunction(x, solve(args, x)[0], 0)
|
||||
# TODO
|
||||
# ((x - 5)**3*Heaviside(x - 5)).rewrite(SingularityFunction) should output
|
||||
# SingularityFunction(x, 5, 0) instead of (x - 5)**3*SingularityFunction(x, 5, 0)
|
||||
else:
|
||||
# I don't know how to handle the case for Heaviside expressions
|
||||
# having arguments with more than one variable.
|
||||
raise TypeError(filldedent('''
|
||||
rewrite(SingularityFunction) does not
|
||||
support arguments with more that one variable.'''))
|
||||
+445
@@ -0,0 +1,445 @@
|
||||
""" Elliptic Integrals. """
|
||||
|
||||
from sympy.core import S, pi, I, Rational
|
||||
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
||||
from sympy.core.symbol import Dummy,uniquely_named_symbol
|
||||
from sympy.functions.elementary.complexes import sign
|
||||
from sympy.functions.elementary.hyperbolic import atanh
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import sin, tan
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.functions.special.hyper import hyper, meijerg
|
||||
|
||||
class elliptic_k(DefinedFunction):
|
||||
r"""
|
||||
The complete elliptic integral of the first kind, defined by
|
||||
|
||||
.. math:: K(m) = F\left(\tfrac{\pi}{2}\middle| m\right)
|
||||
|
||||
where $F\left(z\middle| m\right)$ is the Legendre incomplete
|
||||
elliptic integral of the first kind.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The function $K(m)$ is a single-valued function on the complex
|
||||
plane with branch cut along the interval $(1, \infty)$.
|
||||
|
||||
Note that our notation defines the incomplete elliptic integral
|
||||
in terms of the parameter $m$ instead of the elliptic modulus
|
||||
(eccentricity) $k$.
|
||||
In this case, the parameter $m$ is defined as $m=k^2$.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import elliptic_k, I
|
||||
>>> from sympy.abc import m
|
||||
>>> elliptic_k(0)
|
||||
pi/2
|
||||
>>> elliptic_k(1.0 + I)
|
||||
1.50923695405127 + 0.625146415202697*I
|
||||
>>> elliptic_k(m).series(n=3)
|
||||
pi/2 + pi*m/8 + 9*pi*m**2/128 + O(m**3)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
elliptic_f
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
||||
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticK
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def eval(cls, m):
|
||||
if m.is_zero:
|
||||
return pi*S.Half
|
||||
elif m is S.Half:
|
||||
return 8*pi**Rational(3, 2)/gamma(Rational(-1, 4))**2
|
||||
elif m is S.One:
|
||||
return S.ComplexInfinity
|
||||
elif m is S.NegativeOne:
|
||||
return gamma(Rational(1, 4))**2/(4*sqrt(2*pi))
|
||||
elif m in (S.Infinity, S.NegativeInfinity, I*S.Infinity,
|
||||
I*S.NegativeInfinity, S.ComplexInfinity):
|
||||
return S.Zero
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
m = self.args[0]
|
||||
return (elliptic_e(m) - (1 - m)*elliptic_k(m))/(2*m*(1 - m))
|
||||
|
||||
def _eval_conjugate(self):
|
||||
m = self.args[0]
|
||||
if (m.is_real and (m - 1).is_positive) is False:
|
||||
return self.func(m.conjugate())
|
||||
|
||||
def _eval_nseries(self, x, n, logx, cdir=0):
|
||||
from sympy.simplify import hyperexpand
|
||||
return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx))
|
||||
|
||||
def _eval_rewrite_as_hyper(self, m, **kwargs):
|
||||
return pi*S.Half*hyper((S.Half, S.Half), (S.One,), m)
|
||||
|
||||
def _eval_rewrite_as_meijerg(self, m, **kwargs):
|
||||
return meijerg(((S.Half, S.Half), []), ((S.Zero,), (S.Zero,)), -m)/2
|
||||
|
||||
def _eval_is_zero(self):
|
||||
m = self.args[0]
|
||||
if m.is_infinite:
|
||||
return True
|
||||
|
||||
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
t = Dummy(uniquely_named_symbol('t', args).name)
|
||||
m = self.args[0]
|
||||
return Integral(1/sqrt(1 - m*sin(t)**2), (t, 0, pi/2))
|
||||
|
||||
|
||||
class elliptic_f(DefinedFunction):
|
||||
r"""
|
||||
The Legendre incomplete elliptic integral of the first
|
||||
kind, defined by
|
||||
|
||||
.. math:: F\left(z\middle| m\right) =
|
||||
\int_0^z \frac{dt}{\sqrt{1 - m \sin^2 t}}
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function reduces to a complete elliptic integral of
|
||||
the first kind, $K(m)$, when $z = \pi/2$.
|
||||
|
||||
Note that our notation defines the incomplete elliptic integral
|
||||
in terms of the parameter $m$ instead of the elliptic modulus
|
||||
(eccentricity) $k$.
|
||||
In this case, the parameter $m$ is defined as $m=k^2$.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import elliptic_f, I
|
||||
>>> from sympy.abc import z, m
|
||||
>>> elliptic_f(z, m).series(z)
|
||||
z + z**5*(3*m**2/40 - m/30) + m*z**3/6 + O(z**6)
|
||||
>>> elliptic_f(3.0 + I/2, 1.0 + I)
|
||||
2.909449841483 + 1.74720545502474*I
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
elliptic_k
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
||||
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticF
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def eval(cls, z, m):
|
||||
if z.is_zero:
|
||||
return S.Zero
|
||||
if m.is_zero:
|
||||
return z
|
||||
k = 2*z/pi
|
||||
if k.is_integer:
|
||||
return k*elliptic_k(m)
|
||||
elif m in (S.Infinity, S.NegativeInfinity):
|
||||
return S.Zero
|
||||
elif z.could_extract_minus_sign():
|
||||
return -elliptic_f(-z, m)
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
z, m = self.args
|
||||
fm = sqrt(1 - m*sin(z)**2)
|
||||
if argindex == 1:
|
||||
return 1/fm
|
||||
elif argindex == 2:
|
||||
return (elliptic_e(z, m)/(2*m*(1 - m)) - elliptic_f(z, m)/(2*m) -
|
||||
sin(2*z)/(4*(1 - m)*fm))
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_conjugate(self):
|
||||
z, m = self.args
|
||||
if (m.is_real and (m - 1).is_positive) is False:
|
||||
return self.func(z.conjugate(), m.conjugate())
|
||||
|
||||
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
t = Dummy(uniquely_named_symbol('t', args).name)
|
||||
z, m = self.args[0], self.args[1]
|
||||
return Integral(1/(sqrt(1 - m*sin(t)**2)), (t, 0, z))
|
||||
|
||||
def _eval_is_zero(self):
|
||||
z, m = self.args
|
||||
if z.is_zero:
|
||||
return True
|
||||
if m.is_extended_real and m.is_infinite:
|
||||
return True
|
||||
|
||||
|
||||
class elliptic_e(DefinedFunction):
|
||||
r"""
|
||||
Called with two arguments $z$ and $m$, evaluates the
|
||||
incomplete elliptic integral of the second kind, defined by
|
||||
|
||||
.. math:: E\left(z\middle| m\right) = \int_0^z \sqrt{1 - m \sin^2 t} dt
|
||||
|
||||
Called with a single argument $m$, evaluates the Legendre complete
|
||||
elliptic integral of the second kind
|
||||
|
||||
.. math:: E(m) = E\left(\tfrac{\pi}{2}\middle| m\right)
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The function $E(m)$ is a single-valued function on the complex
|
||||
plane with branch cut along the interval $(1, \infty)$.
|
||||
|
||||
Note that our notation defines the incomplete elliptic integral
|
||||
in terms of the parameter $m$ instead of the elliptic modulus
|
||||
(eccentricity) $k$.
|
||||
In this case, the parameter $m$ is defined as $m=k^2$.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import elliptic_e, I
|
||||
>>> from sympy.abc import z, m
|
||||
>>> elliptic_e(z, m).series(z)
|
||||
z + z**5*(-m**2/40 + m/30) - m*z**3/6 + O(z**6)
|
||||
>>> elliptic_e(m).series(n=4)
|
||||
pi/2 - pi*m/8 - 3*pi*m**2/128 - 5*pi*m**3/512 + O(m**4)
|
||||
>>> elliptic_e(1 + I, 2 - I/2).n()
|
||||
1.55203744279187 + 0.290764986058437*I
|
||||
>>> elliptic_e(0)
|
||||
pi/2
|
||||
>>> elliptic_e(2.0 - I)
|
||||
0.991052601328069 + 0.81879421395609*I
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
||||
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticE2
|
||||
.. [3] https://functions.wolfram.com/EllipticIntegrals/EllipticE
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def eval(cls, m, z=None):
|
||||
if z is not None:
|
||||
z, m = m, z
|
||||
k = 2*z/pi
|
||||
if m.is_zero:
|
||||
return z
|
||||
if z.is_zero:
|
||||
return S.Zero
|
||||
elif k.is_integer:
|
||||
return k*elliptic_e(m)
|
||||
elif m in (S.Infinity, S.NegativeInfinity):
|
||||
return S.ComplexInfinity
|
||||
elif z.could_extract_minus_sign():
|
||||
return -elliptic_e(-z, m)
|
||||
else:
|
||||
if m.is_zero:
|
||||
return pi/2
|
||||
elif m is S.One:
|
||||
return S.One
|
||||
elif m is S.Infinity:
|
||||
return I*S.Infinity
|
||||
elif m is S.NegativeInfinity:
|
||||
return S.Infinity
|
||||
elif m is S.ComplexInfinity:
|
||||
return S.ComplexInfinity
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
if len(self.args) == 2:
|
||||
z, m = self.args
|
||||
if argindex == 1:
|
||||
return sqrt(1 - m*sin(z)**2)
|
||||
elif argindex == 2:
|
||||
return (elliptic_e(z, m) - elliptic_f(z, m))/(2*m)
|
||||
else:
|
||||
m = self.args[0]
|
||||
if argindex == 1:
|
||||
return (elliptic_e(m) - elliptic_k(m))/(2*m)
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_conjugate(self):
|
||||
if len(self.args) == 2:
|
||||
z, m = self.args
|
||||
if (m.is_real and (m - 1).is_positive) is False:
|
||||
return self.func(z.conjugate(), m.conjugate())
|
||||
else:
|
||||
m = self.args[0]
|
||||
if (m.is_real and (m - 1).is_positive) is False:
|
||||
return self.func(m.conjugate())
|
||||
|
||||
def _eval_nseries(self, x, n, logx, cdir=0):
|
||||
from sympy.simplify import hyperexpand
|
||||
if len(self.args) == 1:
|
||||
return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx))
|
||||
return super()._eval_nseries(x, n=n, logx=logx)
|
||||
|
||||
def _eval_rewrite_as_hyper(self, *args, **kwargs):
|
||||
if len(args) == 1:
|
||||
m = args[0]
|
||||
return (pi/2)*hyper((Rational(-1, 2), S.Half), (S.One,), m)
|
||||
|
||||
def _eval_rewrite_as_meijerg(self, *args, **kwargs):
|
||||
if len(args) == 1:
|
||||
m = args[0]
|
||||
return -meijerg(((S.Half, Rational(3, 2)), []), \
|
||||
((S.Zero,), (S.Zero,)), -m)/4
|
||||
|
||||
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
z, m = (pi/2, self.args[0]) if len(self.args) == 1 else self.args
|
||||
t = Dummy(uniquely_named_symbol('t', args).name)
|
||||
return Integral(sqrt(1 - m*sin(t)**2), (t, 0, z))
|
||||
|
||||
|
||||
class elliptic_pi(DefinedFunction):
|
||||
r"""
|
||||
Called with three arguments $n$, $z$ and $m$, evaluates the
|
||||
Legendre incomplete elliptic integral of the third kind, defined by
|
||||
|
||||
.. math:: \Pi\left(n; z\middle| m\right) = \int_0^z \frac{dt}
|
||||
{\left(1 - n \sin^2 t\right) \sqrt{1 - m \sin^2 t}}
|
||||
|
||||
Called with two arguments $n$ and $m$, evaluates the complete
|
||||
elliptic integral of the third kind:
|
||||
|
||||
.. math:: \Pi\left(n\middle| m\right) =
|
||||
\Pi\left(n; \tfrac{\pi}{2}\middle| m\right)
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Note that our notation defines the incomplete elliptic integral
|
||||
in terms of the parameter $m$ instead of the elliptic modulus
|
||||
(eccentricity) $k$.
|
||||
In this case, the parameter $m$ is defined as $m=k^2$.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import elliptic_pi, I
|
||||
>>> from sympy.abc import z, n, m
|
||||
>>> elliptic_pi(n, z, m).series(z, n=4)
|
||||
z + z**3*(m/6 + n/3) + O(z**4)
|
||||
>>> elliptic_pi(0.5 + I, 1.0 - I, 1.2)
|
||||
2.50232379629182 - 0.760939574180767*I
|
||||
>>> elliptic_pi(0, 0)
|
||||
pi/2
|
||||
>>> elliptic_pi(1.0 - I/3, 2.0 + I)
|
||||
3.29136443417283 + 0.32555634906645*I
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
|
||||
.. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticPi3
|
||||
.. [3] https://functions.wolfram.com/EllipticIntegrals/EllipticPi
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def eval(cls, n, m, z=None):
|
||||
if z is not None:
|
||||
z, m = m, z
|
||||
if n.is_zero:
|
||||
return elliptic_f(z, m)
|
||||
elif n is S.One:
|
||||
return (elliptic_f(z, m) +
|
||||
(sqrt(1 - m*sin(z)**2)*tan(z) -
|
||||
elliptic_e(z, m))/(1 - m))
|
||||
k = 2*z/pi
|
||||
if k.is_integer:
|
||||
return k*elliptic_pi(n, m)
|
||||
elif m.is_zero:
|
||||
return atanh(sqrt(n - 1)*tan(z))/sqrt(n - 1)
|
||||
elif n == m:
|
||||
return (elliptic_f(z, n) - elliptic_pi(1, z, n) +
|
||||
tan(z)/sqrt(1 - n*sin(z)**2))
|
||||
elif n in (S.Infinity, S.NegativeInfinity):
|
||||
return S.Zero
|
||||
elif m in (S.Infinity, S.NegativeInfinity):
|
||||
return S.Zero
|
||||
elif z.could_extract_minus_sign():
|
||||
return -elliptic_pi(n, -z, m)
|
||||
if n.is_zero:
|
||||
return elliptic_f(z, m)
|
||||
if m.is_extended_real and m.is_infinite or \
|
||||
n.is_extended_real and n.is_infinite:
|
||||
return S.Zero
|
||||
else:
|
||||
if n.is_zero:
|
||||
return elliptic_k(m)
|
||||
elif n is S.One:
|
||||
return S.ComplexInfinity
|
||||
elif m.is_zero:
|
||||
return pi/(2*sqrt(1 - n))
|
||||
elif m == S.One:
|
||||
return S.NegativeInfinity/sign(n - 1)
|
||||
elif n == m:
|
||||
return elliptic_e(n)/(1 - n)
|
||||
elif n in (S.Infinity, S.NegativeInfinity):
|
||||
return S.Zero
|
||||
elif m in (S.Infinity, S.NegativeInfinity):
|
||||
return S.Zero
|
||||
if n.is_zero:
|
||||
return elliptic_k(m)
|
||||
if m.is_extended_real and m.is_infinite or \
|
||||
n.is_extended_real and n.is_infinite:
|
||||
return S.Zero
|
||||
|
||||
def _eval_conjugate(self):
|
||||
if len(self.args) == 3:
|
||||
n, z, m = self.args
|
||||
if (n.is_real and (n - 1).is_positive) is False and \
|
||||
(m.is_real and (m - 1).is_positive) is False:
|
||||
return self.func(n.conjugate(), z.conjugate(), m.conjugate())
|
||||
else:
|
||||
n, m = self.args
|
||||
return self.func(n.conjugate(), m.conjugate())
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
if len(self.args) == 3:
|
||||
n, z, m = self.args
|
||||
fm, fn = sqrt(1 - m*sin(z)**2), 1 - n*sin(z)**2
|
||||
if argindex == 1:
|
||||
return (elliptic_e(z, m) + (m - n)*elliptic_f(z, m)/n +
|
||||
(n**2 - m)*elliptic_pi(n, z, m)/n -
|
||||
n*fm*sin(2*z)/(2*fn))/(2*(m - n)*(n - 1))
|
||||
elif argindex == 2:
|
||||
return 1/(fm*fn)
|
||||
elif argindex == 3:
|
||||
return (elliptic_e(z, m)/(m - 1) +
|
||||
elliptic_pi(n, z, m) -
|
||||
m*sin(2*z)/(2*(m - 1)*fm))/(2*(n - m))
|
||||
else:
|
||||
n, m = self.args
|
||||
if argindex == 1:
|
||||
return (elliptic_e(m) + (m - n)*elliptic_k(m)/n +
|
||||
(n**2 - m)*elliptic_pi(n, m)/n)/(2*(m - n)*(n - 1))
|
||||
elif argindex == 2:
|
||||
return (elliptic_e(m)/(m - 1) + elliptic_pi(n, m))/(2*(n - m))
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_rewrite_as_Integral(self, *args, **kwargs):
|
||||
from sympy.integrals.integrals import Integral
|
||||
if len(self.args) == 2:
|
||||
n, m, z = self.args[0], self.args[1], pi/2
|
||||
else:
|
||||
n, z, m = self.args
|
||||
t = Dummy(uniquely_named_symbol('t', args).name)
|
||||
return Integral(1/((1 - n*sin(t)**2)*sqrt(1 - m*sin(t)**2)), (t, 0, z))
|
||||
+2801
File diff suppressed because it is too large
Load Diff
+1344
File diff suppressed because it is too large
Load Diff
+1185
File diff suppressed because it is too large
Load Diff
+269
@@ -0,0 +1,269 @@
|
||||
""" This module contains the Mathieu functions.
|
||||
"""
|
||||
|
||||
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import sin, cos
|
||||
|
||||
|
||||
class MathieuBase(DefinedFunction):
|
||||
"""
|
||||
Abstract base class for Mathieu functions.
|
||||
|
||||
This class is meant to reduce code duplication.
|
||||
|
||||
"""
|
||||
|
||||
unbranched = True
|
||||
|
||||
def _eval_conjugate(self):
|
||||
a, q, z = self.args
|
||||
return self.func(a.conjugate(), q.conjugate(), z.conjugate())
|
||||
|
||||
|
||||
class mathieus(MathieuBase):
|
||||
r"""
|
||||
The Mathieu Sine function $S(a,q,z)$.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function is one solution of the Mathieu differential equation:
|
||||
|
||||
.. math ::
|
||||
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
||||
|
||||
The other solution is the Mathieu Cosine function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import diff, mathieus
|
||||
>>> from sympy.abc import a, q, z
|
||||
|
||||
>>> mathieus(a, q, z)
|
||||
mathieus(a, q, z)
|
||||
|
||||
>>> mathieus(a, 0, z)
|
||||
sin(sqrt(a)*z)
|
||||
|
||||
>>> diff(mathieus(a, q, z), z)
|
||||
mathieusprime(a, q, z)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
mathieuc: Mathieu cosine function.
|
||||
mathieusprime: Derivative of Mathieu sine function.
|
||||
mathieucprime: Derivative of Mathieu cosine function.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
||||
.. [2] https://dlmf.nist.gov/28
|
||||
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
||||
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuS/
|
||||
|
||||
"""
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
if argindex == 3:
|
||||
a, q, z = self.args
|
||||
return mathieusprime(a, q, z)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, a, q, z):
|
||||
if q.is_Number and q.is_zero:
|
||||
return sin(sqrt(a)*z)
|
||||
# Try to pull out factors of -1
|
||||
if z.could_extract_minus_sign():
|
||||
return -cls(a, q, -z)
|
||||
|
||||
|
||||
class mathieuc(MathieuBase):
|
||||
r"""
|
||||
The Mathieu Cosine function $C(a,q,z)$.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function is one solution of the Mathieu differential equation:
|
||||
|
||||
.. math ::
|
||||
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
||||
|
||||
The other solution is the Mathieu Sine function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import diff, mathieuc
|
||||
>>> from sympy.abc import a, q, z
|
||||
|
||||
>>> mathieuc(a, q, z)
|
||||
mathieuc(a, q, z)
|
||||
|
||||
>>> mathieuc(a, 0, z)
|
||||
cos(sqrt(a)*z)
|
||||
|
||||
>>> diff(mathieuc(a, q, z), z)
|
||||
mathieucprime(a, q, z)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
mathieus: Mathieu sine function
|
||||
mathieusprime: Derivative of Mathieu sine function
|
||||
mathieucprime: Derivative of Mathieu cosine function
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
||||
.. [2] https://dlmf.nist.gov/28
|
||||
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
||||
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuC/
|
||||
|
||||
"""
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
if argindex == 3:
|
||||
a, q, z = self.args
|
||||
return mathieucprime(a, q, z)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, a, q, z):
|
||||
if q.is_Number and q.is_zero:
|
||||
return cos(sqrt(a)*z)
|
||||
# Try to pull out factors of -1
|
||||
if z.could_extract_minus_sign():
|
||||
return cls(a, q, -z)
|
||||
|
||||
|
||||
class mathieusprime(MathieuBase):
|
||||
r"""
|
||||
The derivative $S^{\prime}(a,q,z)$ of the Mathieu Sine function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function is one solution of the Mathieu differential equation:
|
||||
|
||||
.. math ::
|
||||
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
||||
|
||||
The other solution is the Mathieu Cosine function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import diff, mathieusprime
|
||||
>>> from sympy.abc import a, q, z
|
||||
|
||||
>>> mathieusprime(a, q, z)
|
||||
mathieusprime(a, q, z)
|
||||
|
||||
>>> mathieusprime(a, 0, z)
|
||||
sqrt(a)*cos(sqrt(a)*z)
|
||||
|
||||
>>> diff(mathieusprime(a, q, z), z)
|
||||
(-a + 2*q*cos(2*z))*mathieus(a, q, z)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
mathieus: Mathieu sine function
|
||||
mathieuc: Mathieu cosine function
|
||||
mathieucprime: Derivative of Mathieu cosine function
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
||||
.. [2] https://dlmf.nist.gov/28
|
||||
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
||||
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuSPrime/
|
||||
|
||||
"""
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
if argindex == 3:
|
||||
a, q, z = self.args
|
||||
return (2*q*cos(2*z) - a)*mathieus(a, q, z)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, a, q, z):
|
||||
if q.is_Number and q.is_zero:
|
||||
return sqrt(a)*cos(sqrt(a)*z)
|
||||
# Try to pull out factors of -1
|
||||
if z.could_extract_minus_sign():
|
||||
return cls(a, q, -z)
|
||||
|
||||
|
||||
class mathieucprime(MathieuBase):
|
||||
r"""
|
||||
The derivative $C^{\prime}(a,q,z)$ of the Mathieu Cosine function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function is one solution of the Mathieu differential equation:
|
||||
|
||||
.. math ::
|
||||
y(x)^{\prime\prime} + (a - 2 q \cos(2 x)) y(x) = 0
|
||||
|
||||
The other solution is the Mathieu Sine function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import diff, mathieucprime
|
||||
>>> from sympy.abc import a, q, z
|
||||
|
||||
>>> mathieucprime(a, q, z)
|
||||
mathieucprime(a, q, z)
|
||||
|
||||
>>> mathieucprime(a, 0, z)
|
||||
-sqrt(a)*sin(sqrt(a)*z)
|
||||
|
||||
>>> diff(mathieucprime(a, q, z), z)
|
||||
(-a + 2*q*cos(2*z))*mathieuc(a, q, z)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
mathieus: Mathieu sine function
|
||||
mathieuc: Mathieu cosine function
|
||||
mathieusprime: Derivative of Mathieu sine function
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Mathieu_function
|
||||
.. [2] https://dlmf.nist.gov/28
|
||||
.. [3] https://mathworld.wolfram.com/MathieuFunction.html
|
||||
.. [4] https://functions.wolfram.com/MathieuandSpheroidalFunctions/MathieuCPrime/
|
||||
|
||||
"""
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
if argindex == 3:
|
||||
a, q, z = self.args
|
||||
return (2*q*cos(2*z) - a)*mathieuc(a, q, z)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, a, q, z):
|
||||
if q.is_Number and q.is_zero:
|
||||
return -sqrt(a)*sin(sqrt(a)*z)
|
||||
# Try to pull out factors of -1
|
||||
if z.could_extract_minus_sign():
|
||||
return -cls(a, q, -z)
|
||||
+1447
File diff suppressed because it is too large
Load Diff
+235
@@ -0,0 +1,235 @@
|
||||
from sympy.core import S, oo, diff
|
||||
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
||||
from sympy.core.logic import fuzzy_not
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.functions.elementary.complexes import im
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.delta_functions import Heaviside
|
||||
|
||||
###############################################################################
|
||||
############################# SINGULARITY FUNCTION ############################
|
||||
###############################################################################
|
||||
|
||||
|
||||
class SingularityFunction(DefinedFunction):
|
||||
r"""
|
||||
Singularity functions are a class of discontinuous functions.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Singularity functions take a variable, an offset, and an exponent as
|
||||
arguments. These functions are represented using Macaulay brackets as:
|
||||
|
||||
SingularityFunction(x, a, n) := <x - a>^n
|
||||
|
||||
The singularity function will automatically evaluate to
|
||||
``Derivative(DiracDelta(x - a), x, -n - 1)`` if ``n < 0``
|
||||
and ``(x - a)**n*Heaviside(x - a, 1)`` if ``n >= 0``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import SingularityFunction, diff, Piecewise, DiracDelta, Heaviside, Symbol
|
||||
>>> from sympy.abc import x, a, n
|
||||
>>> SingularityFunction(x, a, n)
|
||||
SingularityFunction(x, a, n)
|
||||
>>> y = Symbol('y', positive=True)
|
||||
>>> n = Symbol('n', nonnegative=True)
|
||||
>>> SingularityFunction(y, -10, n)
|
||||
(y + 10)**n
|
||||
>>> y = Symbol('y', negative=True)
|
||||
>>> SingularityFunction(y, 10, n)
|
||||
0
|
||||
>>> SingularityFunction(x, 4, -1).subs(x, 4)
|
||||
oo
|
||||
>>> SingularityFunction(x, 10, -2).subs(x, 10)
|
||||
oo
|
||||
>>> SingularityFunction(4, 1, 5)
|
||||
243
|
||||
>>> diff(SingularityFunction(x, 1, 5) + SingularityFunction(x, 1, 4), x)
|
||||
4*SingularityFunction(x, 1, 3) + 5*SingularityFunction(x, 1, 4)
|
||||
>>> diff(SingularityFunction(x, 4, 0), x, 2)
|
||||
SingularityFunction(x, 4, -2)
|
||||
>>> SingularityFunction(x, 4, 5).rewrite(Piecewise)
|
||||
Piecewise(((x - 4)**5, x >= 4), (0, True))
|
||||
>>> expr = SingularityFunction(x, a, n)
|
||||
>>> y = Symbol('y', positive=True)
|
||||
>>> n = Symbol('n', nonnegative=True)
|
||||
>>> expr.subs({x: y, a: -10, n: n})
|
||||
(y + 10)**n
|
||||
|
||||
The methods ``rewrite(DiracDelta)``, ``rewrite(Heaviside)``, and
|
||||
``rewrite('HeavisideDiracDelta')`` returns the same output. One can use any
|
||||
of these methods according to their choice.
|
||||
|
||||
>>> expr = SingularityFunction(x, 4, 5) + SingularityFunction(x, -3, -1) - SingularityFunction(x, 0, -2)
|
||||
>>> expr.rewrite(Heaviside)
|
||||
(x - 4)**5*Heaviside(x - 4, 1) + DiracDelta(x + 3) - DiracDelta(x, 1)
|
||||
>>> expr.rewrite(DiracDelta)
|
||||
(x - 4)**5*Heaviside(x - 4, 1) + DiracDelta(x + 3) - DiracDelta(x, 1)
|
||||
>>> expr.rewrite('HeavisideDiracDelta')
|
||||
(x - 4)**5*Heaviside(x - 4, 1) + DiracDelta(x + 3) - DiracDelta(x, 1)
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
DiracDelta, Heaviside
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Singularity_function
|
||||
|
||||
"""
|
||||
|
||||
is_real = True
|
||||
|
||||
def fdiff(self, argindex=1):
|
||||
"""
|
||||
Returns the first derivative of a DiracDelta Function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the
|
||||
user-level function and ``fdiff()`` is an object method. ``fdiff()`` is
|
||||
a convenience method available in the ``Function`` class. It returns
|
||||
the derivative of the function without considering the chain rule.
|
||||
``diff(function, x)`` calls ``Function._eval_derivative`` which in turn
|
||||
calls ``fdiff()`` internally to compute the derivative of the function.
|
||||
|
||||
"""
|
||||
|
||||
if argindex == 1:
|
||||
x, a, n = self.args
|
||||
if n in (S.Zero, S.NegativeOne, S(-2), S(-3)):
|
||||
return self.func(x, a, n-1)
|
||||
elif n.is_positive:
|
||||
return n*self.func(x, a, n-1)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
@classmethod
|
||||
def eval(cls, variable, offset, exponent):
|
||||
"""
|
||||
Returns a simplified form or a value of Singularity Function depending
|
||||
on the argument passed by the object.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The ``eval()`` method is automatically called when the
|
||||
``SingularityFunction`` class is about to be instantiated and it
|
||||
returns either some simplified instance or the unevaluated instance
|
||||
depending on the argument passed. In other words, ``eval()`` method is
|
||||
not needed to be called explicitly, it is being called and evaluated
|
||||
once the object is called.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import SingularityFunction, Symbol, nan
|
||||
>>> from sympy.abc import x, a, n
|
||||
>>> SingularityFunction(x, a, n)
|
||||
SingularityFunction(x, a, n)
|
||||
>>> SingularityFunction(5, 3, 2)
|
||||
4
|
||||
>>> SingularityFunction(x, a, nan)
|
||||
nan
|
||||
>>> SingularityFunction(x, 3, 0).subs(x, 3)
|
||||
1
|
||||
>>> SingularityFunction(4, 1, 5)
|
||||
243
|
||||
>>> x = Symbol('x', positive = True)
|
||||
>>> a = Symbol('a', negative = True)
|
||||
>>> n = Symbol('n', nonnegative = True)
|
||||
>>> SingularityFunction(x, a, n)
|
||||
(-a + x)**n
|
||||
>>> x = Symbol('x', negative = True)
|
||||
>>> a = Symbol('a', positive = True)
|
||||
>>> SingularityFunction(x, a, n)
|
||||
0
|
||||
|
||||
"""
|
||||
|
||||
x = variable
|
||||
a = offset
|
||||
n = exponent
|
||||
shift = (x - a)
|
||||
|
||||
if fuzzy_not(im(shift).is_zero):
|
||||
raise ValueError("Singularity Functions are defined only for Real Numbers.")
|
||||
if fuzzy_not(im(n).is_zero):
|
||||
raise ValueError("Singularity Functions are not defined for imaginary exponents.")
|
||||
if shift is S.NaN or n is S.NaN:
|
||||
return S.NaN
|
||||
if (n + 4).is_negative:
|
||||
raise ValueError("Singularity Functions are not defined for exponents less than -4.")
|
||||
if shift.is_extended_negative:
|
||||
return S.Zero
|
||||
if n.is_nonnegative:
|
||||
if shift.is_zero: # use literal 0 in case of Symbol('z', zero=True)
|
||||
return S.Zero**n
|
||||
if shift.is_extended_nonnegative:
|
||||
return shift**n
|
||||
if n in (S.NegativeOne, -2, -3, -4):
|
||||
if shift.is_negative or shift.is_extended_positive:
|
||||
return S.Zero
|
||||
if shift.is_zero:
|
||||
return oo
|
||||
|
||||
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
||||
'''
|
||||
Converts a Singularity Function expression into its Piecewise form.
|
||||
|
||||
'''
|
||||
x, a, n = self.args
|
||||
|
||||
if n in (S.NegativeOne, S(-2), S(-3), S(-4)):
|
||||
return Piecewise((oo, Eq(x - a, 0)), (0, True))
|
||||
elif n.is_nonnegative:
|
||||
return Piecewise(((x - a)**n, x - a >= 0), (0, True))
|
||||
|
||||
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
||||
'''
|
||||
Rewrites a Singularity Function expression using Heavisides and DiracDeltas.
|
||||
|
||||
'''
|
||||
x, a, n = self.args
|
||||
|
||||
if n == -4:
|
||||
return diff(Heaviside(x - a), x.free_symbols.pop(), 4)
|
||||
if n == -3:
|
||||
return diff(Heaviside(x - a), x.free_symbols.pop(), 3)
|
||||
if n == -2:
|
||||
return diff(Heaviside(x - a), x.free_symbols.pop(), 2)
|
||||
if n == -1:
|
||||
return diff(Heaviside(x - a), x.free_symbols.pop(), 1)
|
||||
if n.is_nonnegative:
|
||||
return (x - a)**n*Heaviside(x - a, 1)
|
||||
|
||||
def _eval_as_leading_term(self, x, logx, cdir):
|
||||
z, a, n = self.args
|
||||
shift = (z - a).subs(x, 0)
|
||||
if n < 0:
|
||||
return S.Zero
|
||||
elif n.is_zero and shift.is_zero:
|
||||
return S.Zero if cdir == -1 else S.One
|
||||
elif shift.is_positive:
|
||||
return shift**n
|
||||
return S.Zero
|
||||
|
||||
def _eval_nseries(self, x, n, logx=None, cdir=0):
|
||||
z, a, n = self.args
|
||||
shift = (z - a).subs(x, 0)
|
||||
if n < 0:
|
||||
return S.Zero
|
||||
elif n.is_zero and shift.is_zero:
|
||||
return S.Zero if cdir == -1 else S.One
|
||||
elif shift.is_positive:
|
||||
return ((z - a)**n)._eval_nseries(x, n, logx=logx, cdir=cdir)
|
||||
return S.Zero
|
||||
|
||||
_eval_rewrite_as_DiracDelta = _eval_rewrite_as_Heaviside
|
||||
_eval_rewrite_as_HeavisideDiracDelta = _eval_rewrite_as_Heaviside
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.function import DefinedFunction, ArgumentIndexError
|
||||
from sympy.core.numbers import I, pi
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.functions import assoc_legendre
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.complexes import Abs, conjugate
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import sin, cos, cot
|
||||
|
||||
_x = Dummy("x")
|
||||
|
||||
class Ynm(DefinedFunction):
|
||||
r"""
|
||||
Spherical harmonics defined as
|
||||
|
||||
.. math::
|
||||
Y_n^m(\theta, \varphi) := \sqrt{\frac{(2n+1)(n-m)!}{4\pi(n+m)!}}
|
||||
\exp(i m \varphi)
|
||||
\mathrm{P}_n^m\left(\cos(\theta)\right)
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
``Ynm()`` gives the spherical harmonic function of order $n$ and $m$
|
||||
in $\theta$ and $\varphi$, $Y_n^m(\theta, \varphi)$. The four
|
||||
parameters are as follows: $n \geq 0$ an integer and $m$ an integer
|
||||
such that $-n \leq m \leq n$ holds. The two angles are real-valued
|
||||
with $\theta \in [0, \pi]$ and $\varphi \in [0, 2\pi]$.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Ynm, Symbol, simplify
|
||||
>>> from sympy.abc import n,m
|
||||
>>> theta = Symbol("theta")
|
||||
>>> phi = Symbol("phi")
|
||||
|
||||
>>> Ynm(n, m, theta, phi)
|
||||
Ynm(n, m, theta, phi)
|
||||
|
||||
Several symmetries are known, for the order:
|
||||
|
||||
>>> Ynm(n, -m, theta, phi)
|
||||
(-1)**m*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
||||
|
||||
As well as for the angles:
|
||||
|
||||
>>> Ynm(n, m, -theta, phi)
|
||||
Ynm(n, m, theta, phi)
|
||||
|
||||
>>> Ynm(n, m, theta, -phi)
|
||||
exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
||||
|
||||
For specific integers $n$ and $m$ we can evaluate the harmonics
|
||||
to more useful expressions:
|
||||
|
||||
>>> simplify(Ynm(0, 0, theta, phi).expand(func=True))
|
||||
1/(2*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(1, -1, theta, phi).expand(func=True))
|
||||
sqrt(6)*exp(-I*phi)*sin(theta)/(4*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(1, 0, theta, phi).expand(func=True))
|
||||
sqrt(3)*cos(theta)/(2*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(1, 1, theta, phi).expand(func=True))
|
||||
-sqrt(6)*exp(I*phi)*sin(theta)/(4*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(2, -2, theta, phi).expand(func=True))
|
||||
sqrt(30)*exp(-2*I*phi)*sin(theta)**2/(8*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(2, -1, theta, phi).expand(func=True))
|
||||
sqrt(30)*exp(-I*phi)*sin(2*theta)/(8*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(2, 0, theta, phi).expand(func=True))
|
||||
sqrt(5)*(3*cos(theta)**2 - 1)/(4*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(2, 1, theta, phi).expand(func=True))
|
||||
-sqrt(30)*exp(I*phi)*sin(2*theta)/(8*sqrt(pi))
|
||||
|
||||
>>> simplify(Ynm(2, 2, theta, phi).expand(func=True))
|
||||
sqrt(30)*exp(2*I*phi)*sin(theta)**2/(8*sqrt(pi))
|
||||
|
||||
We can differentiate the functions with respect
|
||||
to both angles:
|
||||
|
||||
>>> from sympy import Ynm, Symbol, diff
|
||||
>>> from sympy.abc import n,m
|
||||
>>> theta = Symbol("theta")
|
||||
>>> phi = Symbol("phi")
|
||||
|
||||
>>> diff(Ynm(n, m, theta, phi), theta)
|
||||
m*cot(theta)*Ynm(n, m, theta, phi) + sqrt((-m + n)*(m + n + 1))*exp(-I*phi)*Ynm(n, m + 1, theta, phi)
|
||||
|
||||
>>> diff(Ynm(n, m, theta, phi), phi)
|
||||
I*m*Ynm(n, m, theta, phi)
|
||||
|
||||
Further we can compute the complex conjugation:
|
||||
|
||||
>>> from sympy import Ynm, Symbol, conjugate
|
||||
>>> from sympy.abc import n,m
|
||||
>>> theta = Symbol("theta")
|
||||
>>> phi = Symbol("phi")
|
||||
|
||||
>>> conjugate(Ynm(n, m, theta, phi))
|
||||
(-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
||||
|
||||
To get back the well known expressions in spherical
|
||||
coordinates, we use full expansion:
|
||||
|
||||
>>> from sympy import Ynm, Symbol, expand_func
|
||||
>>> from sympy.abc import n,m
|
||||
>>> theta = Symbol("theta")
|
||||
>>> phi = Symbol("phi")
|
||||
|
||||
>>> expand_func(Ynm(n, m, theta, phi))
|
||||
sqrt((2*n + 1)*factorial(-m + n)/factorial(m + n))*exp(I*m*phi)*assoc_legendre(n, m, cos(theta))/(2*sqrt(pi))
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Ynm_c, Znm
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
|
||||
.. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
|
||||
.. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
|
||||
.. [4] https://dlmf.nist.gov/14.30
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def eval(cls, n, m, theta, phi):
|
||||
# Handle negative index m and arguments theta, phi
|
||||
if m.could_extract_minus_sign():
|
||||
m = -m
|
||||
return S.NegativeOne**m * exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
|
||||
if theta.could_extract_minus_sign():
|
||||
theta = -theta
|
||||
return Ynm(n, m, theta, phi)
|
||||
if phi.could_extract_minus_sign():
|
||||
phi = -phi
|
||||
return exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
|
||||
|
||||
# TODO Add more simplififcation here
|
||||
|
||||
def _eval_expand_func(self, **hints):
|
||||
n, m, theta, phi = self.args
|
||||
rv = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
|
||||
exp(I*m*phi) * assoc_legendre(n, m, cos(theta)))
|
||||
# We can do this because of the range of theta
|
||||
return rv.subs(sqrt(-cos(theta)**2 + 1), sin(theta))
|
||||
|
||||
def fdiff(self, argindex=4):
|
||||
if argindex == 1:
|
||||
# Diff wrt n
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
elif argindex == 2:
|
||||
# Diff wrt m
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
elif argindex == 3:
|
||||
# Diff wrt theta
|
||||
n, m, theta, phi = self.args
|
||||
return (m * cot(theta) * Ynm(n, m, theta, phi) +
|
||||
sqrt((n - m)*(n + m + 1)) * exp(-I*phi) * Ynm(n, m + 1, theta, phi))
|
||||
elif argindex == 4:
|
||||
# Diff wrt phi
|
||||
n, m, theta, phi = self.args
|
||||
return I * m * Ynm(n, m, theta, phi)
|
||||
else:
|
||||
raise ArgumentIndexError(self, argindex)
|
||||
|
||||
def _eval_rewrite_as_polynomial(self, n, m, theta, phi, **kwargs):
|
||||
# TODO: Make sure n \in N
|
||||
# TODO: Assert |m| <= n ortherwise we should return 0
|
||||
return self.expand(func=True)
|
||||
|
||||
def _eval_rewrite_as_sin(self, n, m, theta, phi, **kwargs):
|
||||
return self.rewrite(cos)
|
||||
|
||||
def _eval_rewrite_as_cos(self, n, m, theta, phi, **kwargs):
|
||||
# This method can be expensive due to extensive use of simplification!
|
||||
from sympy.simplify import simplify, trigsimp
|
||||
# TODO: Make sure n \in N
|
||||
# TODO: Assert |m| <= n ortherwise we should return 0
|
||||
term = simplify(self.expand(func=True))
|
||||
# We can do this because of the range of theta
|
||||
term = term.xreplace({Abs(sin(theta)):sin(theta)})
|
||||
return simplify(trigsimp(term))
|
||||
|
||||
def _eval_conjugate(self):
|
||||
# TODO: Make sure theta \in R and phi \in R
|
||||
n, m, theta, phi = self.args
|
||||
return S.NegativeOne**m * self.func(n, -m, theta, phi)
|
||||
|
||||
def as_real_imag(self, deep=True, **hints):
|
||||
# TODO: Handle deep and hints
|
||||
n, m, theta, phi = self.args
|
||||
re = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
|
||||
cos(m*phi) * assoc_legendre(n, m, cos(theta)))
|
||||
im = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
|
||||
sin(m*phi) * assoc_legendre(n, m, cos(theta)))
|
||||
return (re, im)
|
||||
|
||||
def _eval_evalf(self, prec):
|
||||
# Note: works without this function by just calling
|
||||
# mpmath for Legendre polynomials. But using
|
||||
# the dedicated function directly is cleaner.
|
||||
from mpmath import mp, workprec
|
||||
n = self.args[0]._to_mpmath(prec)
|
||||
m = self.args[1]._to_mpmath(prec)
|
||||
theta = self.args[2]._to_mpmath(prec)
|
||||
phi = self.args[3]._to_mpmath(prec)
|
||||
with workprec(prec):
|
||||
res = mp.spherharm(n, m, theta, phi)
|
||||
return Expr._from_mpmath(res, prec)
|
||||
|
||||
|
||||
def Ynm_c(n, m, theta, phi):
|
||||
r"""
|
||||
Conjugate spherical harmonics defined as
|
||||
|
||||
.. math::
|
||||
\overline{Y_n^m(\theta, \varphi)} := (-1)^m Y_n^{-m}(\theta, \varphi).
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Ynm_c, Symbol, simplify
|
||||
>>> from sympy.abc import n,m
|
||||
>>> theta = Symbol("theta")
|
||||
>>> phi = Symbol("phi")
|
||||
>>> Ynm_c(n, m, theta, phi)
|
||||
(-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
||||
>>> Ynm_c(n, m, -theta, phi)
|
||||
(-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
|
||||
|
||||
For specific integers $n$ and $m$ we can evaluate the harmonics
|
||||
to more useful expressions:
|
||||
|
||||
>>> simplify(Ynm_c(0, 0, theta, phi).expand(func=True))
|
||||
1/(2*sqrt(pi))
|
||||
>>> simplify(Ynm_c(1, -1, theta, phi).expand(func=True))
|
||||
sqrt(6)*exp(I*(-phi + 2*conjugate(phi)))*sin(theta)/(4*sqrt(pi))
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Ynm, Znm
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
|
||||
.. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
|
||||
.. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
|
||||
|
||||
"""
|
||||
return conjugate(Ynm(n, m, theta, phi))
|
||||
|
||||
|
||||
class Znm(DefinedFunction):
|
||||
r"""
|
||||
Real spherical harmonics defined as
|
||||
|
||||
.. math::
|
||||
|
||||
Z_n^m(\theta, \varphi) :=
|
||||
\begin{cases}
|
||||
\frac{Y_n^m(\theta, \varphi) + \overline{Y_n^m(\theta, \varphi)}}{\sqrt{2}} &\quad m > 0 \\
|
||||
Y_n^m(\theta, \varphi) &\quad m = 0 \\
|
||||
\frac{Y_n^m(\theta, \varphi) - \overline{Y_n^m(\theta, \varphi)}}{i \sqrt{2}} &\quad m < 0 \\
|
||||
\end{cases}
|
||||
|
||||
which gives in simplified form
|
||||
|
||||
.. math::
|
||||
|
||||
Z_n^m(\theta, \varphi) =
|
||||
\begin{cases}
|
||||
\frac{Y_n^m(\theta, \varphi) + (-1)^m Y_n^{-m}(\theta, \varphi)}{\sqrt{2}} &\quad m > 0 \\
|
||||
Y_n^m(\theta, \varphi) &\quad m = 0 \\
|
||||
\frac{Y_n^m(\theta, \varphi) - (-1)^m Y_n^{-m}(\theta, \varphi)}{i \sqrt{2}} &\quad m < 0 \\
|
||||
\end{cases}
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Znm, Symbol, simplify
|
||||
>>> from sympy.abc import n, m
|
||||
>>> theta = Symbol("theta")
|
||||
>>> phi = Symbol("phi")
|
||||
>>> Znm(n, m, theta, phi)
|
||||
Znm(n, m, theta, phi)
|
||||
|
||||
For specific integers n and m we can evaluate the harmonics
|
||||
to more useful expressions:
|
||||
|
||||
>>> simplify(Znm(0, 0, theta, phi).expand(func=True))
|
||||
1/(2*sqrt(pi))
|
||||
>>> simplify(Znm(1, 1, theta, phi).expand(func=True))
|
||||
-sqrt(3)*sin(theta)*cos(phi)/(2*sqrt(pi))
|
||||
>>> simplify(Znm(2, 1, theta, phi).expand(func=True))
|
||||
-sqrt(15)*sin(2*theta)*cos(phi)/(4*sqrt(pi))
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Ynm, Ynm_c
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
|
||||
.. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
|
||||
.. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def eval(cls, n, m, theta, phi):
|
||||
if m.is_positive:
|
||||
zz = (Ynm(n, m, theta, phi) + Ynm_c(n, m, theta, phi)) / sqrt(2)
|
||||
return zz
|
||||
elif m.is_zero:
|
||||
return Ynm(n, m, theta, phi)
|
||||
elif m.is_negative:
|
||||
zz = (Ynm(n, m, theta, phi) - Ynm_c(n, m, theta, phi)) / (sqrt(2)*I)
|
||||
return zz
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
from math import prod
|
||||
|
||||
from sympy.core import S, Integer
|
||||
from sympy.core.function import DefinedFunction
|
||||
from sympy.core.logic import fuzzy_not
|
||||
from sympy.core.relational import Ne
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.external.gmpy import SYMPY_INTS
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.utilities.iterables import has_dups
|
||||
|
||||
###############################################################################
|
||||
###################### Kronecker Delta, Levi-Civita etc. ######################
|
||||
###############################################################################
|
||||
|
||||
|
||||
def Eijk(*args, **kwargs):
|
||||
"""
|
||||
Represent the Levi-Civita symbol.
|
||||
|
||||
This is a compatibility wrapper to ``LeviCivita()``.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
LeviCivita
|
||||
|
||||
"""
|
||||
return LeviCivita(*args, **kwargs)
|
||||
|
||||
|
||||
def eval_levicivita(*args):
|
||||
"""Evaluate Levi-Civita symbol."""
|
||||
n = len(args)
|
||||
return prod(
|
||||
prod(args[j] - args[i] for j in range(i + 1, n))
|
||||
/ factorial(i) for i in range(n))
|
||||
# converting factorial(i) to int is slightly faster
|
||||
|
||||
|
||||
class LeviCivita(DefinedFunction):
|
||||
"""
|
||||
Represent the Levi-Civita symbol.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
For even permutations of indices it returns 1, for odd permutations -1, and
|
||||
for everything else (a repeated index) it returns 0.
|
||||
|
||||
Thus it represents an alternating pseudotensor.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import LeviCivita
|
||||
>>> from sympy.abc import i, j, k
|
||||
>>> LeviCivita(1, 2, 3)
|
||||
1
|
||||
>>> LeviCivita(1, 3, 2)
|
||||
-1
|
||||
>>> LeviCivita(1, 2, 2)
|
||||
0
|
||||
>>> LeviCivita(i, j, k)
|
||||
LeviCivita(i, j, k)
|
||||
>>> LeviCivita(i, j, i)
|
||||
0
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
Eijk
|
||||
|
||||
"""
|
||||
|
||||
is_integer = True
|
||||
|
||||
@classmethod
|
||||
def eval(cls, *args):
|
||||
if all(isinstance(a, (SYMPY_INTS, Integer)) for a in args):
|
||||
return eval_levicivita(*args)
|
||||
if has_dups(args):
|
||||
return S.Zero
|
||||
|
||||
def doit(self, **hints):
|
||||
return eval_levicivita(*self.args)
|
||||
|
||||
|
||||
class KroneckerDelta(DefinedFunction):
|
||||
"""
|
||||
The discrete, or Kronecker, delta function.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
A function that takes in two integers $i$ and $j$. It returns $0$ if $i$
|
||||
and $j$ are not equal, or it returns $1$ if $i$ and $j$ are equal.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
An example with integer indices:
|
||||
|
||||
>>> from sympy import KroneckerDelta
|
||||
>>> KroneckerDelta(1, 2)
|
||||
0
|
||||
>>> KroneckerDelta(3, 3)
|
||||
1
|
||||
|
||||
Symbolic indices:
|
||||
|
||||
>>> from sympy.abc import i, j, k
|
||||
>>> KroneckerDelta(i, j)
|
||||
KroneckerDelta(i, j)
|
||||
>>> KroneckerDelta(i, i)
|
||||
1
|
||||
>>> KroneckerDelta(i, i + 1)
|
||||
0
|
||||
>>> KroneckerDelta(i, i + 1 + k)
|
||||
KroneckerDelta(i, i + k + 1)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
i : Number, Symbol
|
||||
The first index of the delta function.
|
||||
j : Number, Symbol
|
||||
The second index of the delta function.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
eval
|
||||
DiracDelta
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Kronecker_delta
|
||||
|
||||
"""
|
||||
|
||||
is_integer = True
|
||||
|
||||
@classmethod
|
||||
def eval(cls, i, j, delta_range=None):
|
||||
"""
|
||||
Evaluates the discrete delta function.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta
|
||||
>>> from sympy.abc import i, j, k
|
||||
|
||||
>>> KroneckerDelta(i, j)
|
||||
KroneckerDelta(i, j)
|
||||
>>> KroneckerDelta(i, i)
|
||||
1
|
||||
>>> KroneckerDelta(i, i + 1)
|
||||
0
|
||||
>>> KroneckerDelta(i, i + 1 + k)
|
||||
KroneckerDelta(i, i + k + 1)
|
||||
|
||||
# indirect doctest
|
||||
|
||||
"""
|
||||
|
||||
if delta_range is not None:
|
||||
dinf, dsup = delta_range
|
||||
if (dinf - i > 0) == True:
|
||||
return S.Zero
|
||||
if (dinf - j > 0) == True:
|
||||
return S.Zero
|
||||
if (dsup - i < 0) == True:
|
||||
return S.Zero
|
||||
if (dsup - j < 0) == True:
|
||||
return S.Zero
|
||||
|
||||
diff = i - j
|
||||
if diff.is_zero:
|
||||
return S.One
|
||||
elif fuzzy_not(diff.is_zero):
|
||||
return S.Zero
|
||||
|
||||
if i.assumptions0.get("below_fermi") and \
|
||||
j.assumptions0.get("above_fermi"):
|
||||
return S.Zero
|
||||
if j.assumptions0.get("below_fermi") and \
|
||||
i.assumptions0.get("above_fermi"):
|
||||
return S.Zero
|
||||
# to make KroneckerDelta canonical
|
||||
# following lines will check if inputs are in order
|
||||
# if not, will return KroneckerDelta with correct order
|
||||
if default_sort_key(j) < default_sort_key(i):
|
||||
if delta_range:
|
||||
return cls(j, i, delta_range)
|
||||
else:
|
||||
return cls(j, i)
|
||||
|
||||
@property
|
||||
def delta_range(self):
|
||||
if len(self.args) > 2:
|
||||
return self.args[2]
|
||||
|
||||
def _eval_power(self, expt):
|
||||
if expt.is_positive:
|
||||
return self
|
||||
if expt.is_negative and expt is not S.NegativeOne:
|
||||
return 1/self
|
||||
|
||||
@property
|
||||
def is_above_fermi(self):
|
||||
"""
|
||||
True if Delta can be non-zero above fermi.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> q = Symbol('q')
|
||||
>>> KroneckerDelta(p, a).is_above_fermi
|
||||
True
|
||||
>>> KroneckerDelta(p, i).is_above_fermi
|
||||
False
|
||||
>>> KroneckerDelta(p, q).is_above_fermi
|
||||
True
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
is_below_fermi, is_only_below_fermi, is_only_above_fermi
|
||||
|
||||
"""
|
||||
if self.args[0].assumptions0.get("below_fermi"):
|
||||
return False
|
||||
if self.args[1].assumptions0.get("below_fermi"):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_below_fermi(self):
|
||||
"""
|
||||
True if Delta can be non-zero below fermi.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> q = Symbol('q')
|
||||
>>> KroneckerDelta(p, a).is_below_fermi
|
||||
False
|
||||
>>> KroneckerDelta(p, i).is_below_fermi
|
||||
True
|
||||
>>> KroneckerDelta(p, q).is_below_fermi
|
||||
True
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
is_above_fermi, is_only_above_fermi, is_only_below_fermi
|
||||
|
||||
"""
|
||||
if self.args[0].assumptions0.get("above_fermi"):
|
||||
return False
|
||||
if self.args[1].assumptions0.get("above_fermi"):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_only_above_fermi(self):
|
||||
"""
|
||||
True if Delta is restricted to above fermi.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> q = Symbol('q')
|
||||
>>> KroneckerDelta(p, a).is_only_above_fermi
|
||||
True
|
||||
>>> KroneckerDelta(p, q).is_only_above_fermi
|
||||
False
|
||||
>>> KroneckerDelta(p, i).is_only_above_fermi
|
||||
False
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
is_above_fermi, is_below_fermi, is_only_below_fermi
|
||||
|
||||
"""
|
||||
return ( self.args[0].assumptions0.get("above_fermi")
|
||||
or
|
||||
self.args[1].assumptions0.get("above_fermi")
|
||||
) or False
|
||||
|
||||
@property
|
||||
def is_only_below_fermi(self):
|
||||
"""
|
||||
True if Delta is restricted to below fermi.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> q = Symbol('q')
|
||||
>>> KroneckerDelta(p, i).is_only_below_fermi
|
||||
True
|
||||
>>> KroneckerDelta(p, q).is_only_below_fermi
|
||||
False
|
||||
>>> KroneckerDelta(p, a).is_only_below_fermi
|
||||
False
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
is_above_fermi, is_below_fermi, is_only_above_fermi
|
||||
|
||||
"""
|
||||
return ( self.args[0].assumptions0.get("below_fermi")
|
||||
or
|
||||
self.args[1].assumptions0.get("below_fermi")
|
||||
) or False
|
||||
|
||||
@property
|
||||
def indices_contain_equal_information(self):
|
||||
"""
|
||||
Returns True if indices are either both above or below fermi.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> q = Symbol('q')
|
||||
>>> KroneckerDelta(p, q).indices_contain_equal_information
|
||||
True
|
||||
>>> KroneckerDelta(p, q+1).indices_contain_equal_information
|
||||
True
|
||||
>>> KroneckerDelta(i, p).indices_contain_equal_information
|
||||
False
|
||||
|
||||
"""
|
||||
if (self.args[0].assumptions0.get("below_fermi") and
|
||||
self.args[1].assumptions0.get("below_fermi")):
|
||||
return True
|
||||
if (self.args[0].assumptions0.get("above_fermi")
|
||||
and self.args[1].assumptions0.get("above_fermi")):
|
||||
return True
|
||||
|
||||
# if both indices are general we are True, else false
|
||||
return self.is_below_fermi and self.is_above_fermi
|
||||
|
||||
@property
|
||||
def preferred_index(self):
|
||||
"""
|
||||
Returns the index which is preferred to keep in the final expression.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The preferred index is the index with more information regarding fermi
|
||||
level. If indices contain the same information, 'a' is preferred before
|
||||
'b'.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> j = Symbol('j', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> KroneckerDelta(p, i).preferred_index
|
||||
i
|
||||
>>> KroneckerDelta(p, a).preferred_index
|
||||
a
|
||||
>>> KroneckerDelta(i, j).preferred_index
|
||||
i
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
killable_index
|
||||
|
||||
"""
|
||||
if self._get_preferred_index():
|
||||
return self.args[1]
|
||||
else:
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def killable_index(self):
|
||||
"""
|
||||
Returns the index which is preferred to substitute in the final
|
||||
expression.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The index to substitute is the index with less information regarding
|
||||
fermi level. If indices contain the same information, 'a' is preferred
|
||||
before 'b'.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import KroneckerDelta, Symbol
|
||||
>>> a = Symbol('a', above_fermi=True)
|
||||
>>> i = Symbol('i', below_fermi=True)
|
||||
>>> j = Symbol('j', below_fermi=True)
|
||||
>>> p = Symbol('p')
|
||||
>>> KroneckerDelta(p, i).killable_index
|
||||
p
|
||||
>>> KroneckerDelta(p, a).killable_index
|
||||
p
|
||||
>>> KroneckerDelta(i, j).killable_index
|
||||
j
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
preferred_index
|
||||
|
||||
"""
|
||||
if self._get_preferred_index():
|
||||
return self.args[0]
|
||||
else:
|
||||
return self.args[1]
|
||||
|
||||
def _get_preferred_index(self):
|
||||
"""
|
||||
Returns the index which is preferred to keep in the final expression.
|
||||
|
||||
The preferred index is the index with more information regarding fermi
|
||||
level. If indices contain the same information, index 0 is returned.
|
||||
|
||||
"""
|
||||
if not self.is_above_fermi:
|
||||
if self.args[0].assumptions0.get("below_fermi"):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
elif not self.is_below_fermi:
|
||||
if self.args[0].assumptions0.get("above_fermi"):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def indices(self):
|
||||
return self.args[0:2]
|
||||
|
||||
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
||||
i, j = args
|
||||
return Piecewise((0, Ne(i, j)), (1, True))
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user