def test_harmonic_rewrite(): n = Symbol("n") m = Symbol("m") assert harmonic(n).rewrite(digamma) == polygamma(0, n + 1) + EulerGamma assert harmonic(n).rewrite(trigamma) == polygamma(0, n + 1) + EulerGamma assert harmonic(n).rewrite(polygamma) == polygamma(0, n + 1) + EulerGamma assert harmonic( n, 3).rewrite(polygamma) == polygamma(2, n + 1) / 2 - polygamma(2, 1) / 2 assert harmonic(n, m).rewrite(polygamma) == (-1)**m * ( polygamma(m - 1, 1) - polygamma(m - 1, n + 1)) / factorial(m - 1) assert expand_func( harmonic(n + 4) ) == harmonic(n) + 1 / (n + 4) + 1 / (n + 3) + 1 / (n + 2) + 1 / (n + 1) assert expand_func(harmonic( n - 4)) == harmonic(n) - 1 / (n - 1) - 1 / (n - 2) - 1 / (n - 3) - 1 / n assert harmonic(n, m).rewrite("tractable") == harmonic(n, m).rewrite(polygamma) _k = Dummy("k") assert harmonic(n).rewrite(Sum).dummy_eq(Sum(1 / _k, (_k, 1, n))) assert harmonic(n, m).rewrite(Sum).dummy_eq(Sum(_k**(-m), (_k, 1, n)))
def test_slow_expand(): def check(eq, ans): return tn(eq, ans) and eq == ans rn = randcplx(a=1, b=0, d=0, c=2) for besselx in [besselj, bessely, besseli, besselk]: ri = S(2*randint(-11, 10) + 1) / 2 # half integer in [-21/2, 21/2] assert tn(besselsimp(besselx(ri, z)), besselx(ri, z)) assert check(expand_func(besseli(rn, x)), besseli(rn - 2, x) - 2*(rn - 1)*besseli(rn - 1, x)/x) assert check(expand_func(besseli(-rn, x)), besseli(-rn + 2, x) + 2*(-rn + 1)*besseli(-rn + 1, x)/x) assert check(expand_func(besselj(rn, x)), -besselj(rn - 2, x) + 2*(rn - 1)*besselj(rn - 1, x)/x) assert check(expand_func(besselj(-rn, x)), -besselj(-rn + 2, x) + 2*(-rn + 1)*besselj(-rn + 1, x)/x) assert check(expand_func(besselk(rn, x)), besselk(rn - 2, x) + 2*(rn - 1)*besselk(rn - 1, x)/x) assert check(expand_func(besselk(-rn, x)), besselk(-rn + 2, x) - 2*(-rn + 1)*besselk(-rn + 1, x)/x) assert check(expand_func(bessely(rn, x)), -bessely(rn - 2, x) + 2*(rn - 1)*bessely(rn - 1, x)/x) assert check(expand_func(bessely(-rn, x)), -bessely(-rn + 2, x) + 2*(-rn + 1)*bessely(-rn + 1, x)/x)
def test_airyai(): z = Symbol('z', real=False) t = Symbol('t', negative=True) p = Symbol('p', positive=True) assert isinstance(airyai(z), airyai) assert airyai(0) == 3**Rational(1, 3)/(3*gamma(Rational(2, 3))) assert airyai(oo) == 0 assert airyai(-oo) == 0 assert diff(airyai(z), z) == airyaiprime(z) assert series(airyai(z), z, 0, 3) == ( 3**Rational(5, 6)*gamma(Rational(1, 3))/(6*pi) - 3**Rational(1, 6)*z*gamma(Rational(2, 3))/(2*pi) + O(z**3)) assert airyai(z).rewrite(hyper) == ( -3**Rational(2, 3)*z*hyper((), (Rational(4, 3),), z**3/9)/(3*gamma(Rational(1, 3))) + 3**Rational(1, 3)*hyper((), (Rational(2, 3),), z**3/9)/(3*gamma(Rational(2, 3)))) assert isinstance(airyai(z).rewrite(besselj), airyai) assert airyai(t).rewrite(besselj) == ( sqrt(-t)*(besselj(Rational(-1, 3), 2*(-t)**Rational(3, 2)/3) + besselj(Rational(1, 3), 2*(-t)**Rational(3, 2)/3))/3) assert airyai(z).rewrite(besseli) == ( -z*besseli(Rational(1, 3), 2*z**Rational(3, 2)/3)/(3*(z**Rational(3, 2))**Rational(1, 3)) + (z**Rational(3, 2))**Rational(1, 3)*besseli(Rational(-1, 3), 2*z**Rational(3, 2)/3)/3) assert airyai(p).rewrite(besseli) == ( sqrt(p)*(besseli(Rational(-1, 3), 2*p**Rational(3, 2)/3) - besseli(Rational(1, 3), 2*p**Rational(3, 2)/3))/3) assert expand_func(airyai(2*(3*z**5)**Rational(1, 3))) == ( -sqrt(3)*(-1 + (z**5)**Rational(1, 3)/z**Rational(5, 3))*airybi(2*3**Rational(1, 3)*z**Rational(5, 3))/6 + (1 + (z**5)**Rational(1, 3)/z**Rational(5, 3))*airyai(2*3**Rational(1, 3)*z**Rational(5, 3))/2)
def test_airybiprime(): z = Symbol('z', real=False) t = Symbol('t', negative=True) p = Symbol('p', positive=True) assert isinstance(airybiprime(z), airybiprime) assert airybiprime(0) == 3**Rational(1, 6)/gamma(Rational(1, 3)) assert airybiprime(oo) is oo assert airybiprime(-oo) == 0 assert diff(airybiprime(z), z) == z*airybi(z) assert series(airybiprime(z), z, 0, 3) == ( 3**Rational(1, 6)/gamma(Rational(1, 3)) + 3**Rational(5, 6)*z**2/(6*gamma(Rational(2, 3))) + O(z**3)) assert airybiprime(z).rewrite(hyper) == ( 3**Rational(5, 6)*z**2*hyper((), (Rational(5, 3),), z**3/9)/(6*gamma(Rational(2, 3))) + 3**Rational(1, 6)*hyper((), (Rational(1, 3),), z**3/9)/gamma(Rational(1, 3))) assert isinstance(airybiprime(z).rewrite(besselj), airybiprime) assert airyai(t).rewrite(besselj) == ( sqrt(-t)*(besselj(Rational(-1, 3), 2*(-t)**Rational(3, 2)/3) + besselj(Rational(1, 3), 2*(-t)**Rational(3, 2)/3))/3) assert airybiprime(z).rewrite(besseli) == ( sqrt(3)*(z**2*besseli(Rational(2, 3), 2*z**Rational(3, 2)/3)/(z**Rational(3, 2))**Rational(2, 3) + (z**Rational(3, 2))**Rational(2, 3)*besseli(Rational(-2, 3), 2*z**Rational(3, 2)/3))/3) assert airybiprime(p).rewrite(besseli) == ( sqrt(3)*p*(besseli(Rational(-2, 3), 2*p**Rational(3, 2)/3) + besseli(Rational(2, 3), 2*p**Rational(3, 2)/3))/3) assert expand_func(airybiprime(2*(3*z**5)**Rational(1, 3))) == ( sqrt(3)*(z**Rational(5, 3)/(z**5)**Rational(1, 3) - 1)*airyaiprime(2*3**Rational(1, 3)*z**Rational(5, 3))/2 + (z**Rational(5, 3)/(z**5)**Rational(1, 3) + 1)*airybiprime(2*3**Rational(1, 3)*z**Rational(5, 3))/2)
def test_jn(): z = symbols("z") assert jn(0, 0) == 1 assert jn(1, 0) == 0 assert jn(-1, 0) == S.ComplexInfinity assert jn(z, 0) == jn(z, 0, evaluate=False) assert jn(0, oo) == 0 assert jn(0, -oo) == 0 assert mjn(0, z) == sin(z)/z assert mjn(1, z) == sin(z)/z**2 - cos(z)/z assert mjn(2, z) == (3/z**3 - 1/z)*sin(z) - (3/z**2) * cos(z) assert mjn(3, z) == (15/z**4 - 6/z**2)*sin(z) + (1/z - 15/z**3)*cos(z) assert mjn(4, z) == (1/z + 105/z**5 - 45/z**3)*sin(z) + \ (-105/z**4 + 10/z**2)*cos(z) assert mjn(5, z) == (945/z**6 - 420/z**4 + 15/z**2)*sin(z) + \ (-1/z - 945/z**5 + 105/z**3)*cos(z) assert mjn(6, z) == (-1/z + 10395/z**7 - 4725/z**5 + 210/z**3)*sin(z) + \ (-10395/z**6 + 1260/z**4 - 21/z**2)*cos(z) assert expand_func(jn(n, z)) == jn(n, z) # SBFs not defined for complex-valued orders assert jn(2+3j, 5.2+0.3j).evalf() == jn(2+3j, 5.2+0.3j) assert eq([jn(2, 5.2+0.3j).evalf(10)], [0.09941975672 - 0.05452508024*I])
def test_erfi(): assert erfi(nan) is nan assert erfi(oo) is S.Infinity assert erfi(-oo) is S.NegativeInfinity assert erfi(0) is S.Zero assert erfi(I*oo) == I assert erfi(-I*oo) == -I assert erfi(-x) == -erfi(x) assert erfi(I*erfinv(x)) == I*x assert erfi(I*erfcinv(x)) == I*(1 - x) assert erfi(I*erf2inv(0, x)) == I*x assert erfi(I*erf2inv(0, x, evaluate=False)) == I*x # To cover code in erfi assert erfi(I).is_real is False assert erfi(0, evaluate=False).is_real assert erfi(0, evaluate=False).is_zero assert conjugate(erfi(z)) == erfi(conjugate(z)) assert erfi(x).as_leading_term(x) == 2*x/sqrt(pi) assert erfi(x*y).as_leading_term(y) == 2*x*y/sqrt(pi) assert (erfi(x*y)/erfi(y)).as_leading_term(y) == x assert erfi(1/x).as_leading_term(x) == erfi(1/x) assert erfi(z).rewrite('erf') == -I*erf(I*z) assert erfi(z).rewrite('erfc') == I*erfc(I*z) - I assert erfi(z).rewrite('fresnels') == (1 - I)*(fresnelc(z*(1 + I)/sqrt(pi)) - I*fresnels(z*(1 + I)/sqrt(pi))) assert erfi(z).rewrite('fresnelc') == (1 - I)*(fresnelc(z*(1 + I)/sqrt(pi)) - I*fresnels(z*(1 + I)/sqrt(pi))) assert erfi(z).rewrite('hyper') == 2*z*hyper([S.Half], [3*S.Half], z**2)/sqrt(pi) assert erfi(z).rewrite('meijerg') == z*meijerg([S.Half], [], [0], [Rational(-1, 2)], -z**2)/sqrt(pi) assert erfi(z).rewrite('uppergamma') == (sqrt(-z**2)/z*(uppergamma(S.Half, -z**2)/sqrt(S.Pi) - S.One)) assert erfi(z).rewrite('expint') == sqrt(-z**2)/z - z*expint(S.Half, -z**2)/sqrt(S.Pi) assert erfi(z).rewrite('tractable') == -I*(-_erfs(I*z)*exp(z**2) + 1) assert expand_func(erfi(I*z)) == I*erf(z) assert erfi(x).as_real_imag() == \ (erfi(re(x) - I*im(x))/2 + erfi(re(x) + I*im(x))/2, -I*(-erfi(re(x) - I*im(x)) + erfi(re(x) + I*im(x)))/2) assert erfi(x).as_real_imag(deep=False) == \ (erfi(re(x) - I*im(x))/2 + erfi(re(x) + I*im(x))/2, -I*(-erfi(re(x) - I*im(x)) + erfi(re(x) + I*im(x)))/2) assert erfi(w).as_real_imag() == (erfi(w), 0) assert erfi(w).as_real_imag(deep=False) == (erfi(w), 0) raises(ArgumentIndexError, lambda: erfi(x).fdiff(2))
def test_polylog_expansion(): assert polylog(s, 0) == 0 assert polylog(s, 1) == zeta(s) assert polylog(s, -1) == -dirichlet_eta(s) assert polylog(s, exp_polar(I*pi*Rational(4, 3))) == polylog(s, exp(I*pi*Rational(4, 3))) assert polylog(s, exp_polar(I*pi)/3) == polylog(s, exp(I*pi)/3) assert myexpand(polylog(1, z), -log(1 - z)) assert myexpand(polylog(0, z), z/(1 - z)) assert myexpand(polylog(-1, z), z/(1 - z)**2) assert ((1-z)**3 * expand_func(polylog(-2, z))).simplify() == z*(1 + z) assert myexpand(polylog(-5, z), None)
def test_yn(): z = symbols("z") assert myn(0, z) == -cos(z)/z assert myn(1, z) == -cos(z)/z**2 - sin(z)/z assert myn(2, z) == -((3/z**3 - 1/z)*cos(z) + (3/z**2)*sin(z)) assert expand_func(yn(n, z)) == yn(n, z) # SBFs not defined for complex-valued orders assert yn(2+3j, 5.2+0.3j).evalf() == yn(2+3j, 5.2+0.3j) assert eq([yn(2, 5.2+0.3j).evalf(10)], [0.185250342 + 0.01489557397*I])
def myexpand(func, target): expanded = expand_func(func) if target is not None: return expanded == target if expanded == func: # it didn't expand return False # check to see that the expanded and original evaluate to the same value subs = {} for a in func.free_symbols: subs[a] = randcplx() return abs(func.subs(subs).n() - expanded.replace(exp_polar, exp).subs(subs).n()) < 1e-10
def test_erfc(): assert erfc(nan) is nan assert erfc(oo) is S.Zero assert erfc(-oo) == 2 assert erfc(0) == 1 assert erfc(I*oo) == -oo*I assert erfc(-I*oo) == oo*I assert erfc(-x) == S(2) - erfc(x) assert erfc(erfcinv(x)) == x assert erfc(I).is_real is False assert erfc(0, evaluate=False).is_real assert erfc(0, evaluate=False).is_zero is False assert erfc(erfinv(x)) == 1 - x assert conjugate(erfc(z)) == erfc(conjugate(z)) assert erfc(x).as_leading_term(x) is S.One assert erfc(1/x).as_leading_term(x) == S.Zero assert erfc(z).rewrite('erf') == 1 - erf(z) assert erfc(z).rewrite('erfi') == 1 + I*erfi(I*z) assert erfc(z).rewrite('fresnels') == 1 - (1 + I)*(fresnelc(z*(1 - I)/sqrt(pi)) - I*fresnels(z*(1 - I)/sqrt(pi))) assert erfc(z).rewrite('fresnelc') == 1 - (1 + I)*(fresnelc(z*(1 - I)/sqrt(pi)) - I*fresnels(z*(1 - I)/sqrt(pi))) assert erfc(z).rewrite('hyper') == 1 - 2*z*hyper([S.Half], [3*S.Half], -z**2)/sqrt(pi) assert erfc(z).rewrite('meijerg') == 1 - z*meijerg([S.Half], [], [0], [Rational(-1, 2)], z**2)/sqrt(pi) assert erfc(z).rewrite('uppergamma') == 1 - sqrt(z**2)*(1 - erfc(sqrt(z**2)))/z assert erfc(z).rewrite('expint') == S.One - sqrt(z**2)/z + z*expint(S.Half, z**2)/sqrt(S.Pi) assert erfc(z).rewrite('tractable') == _erfs(z)*exp(-z**2) assert expand_func(erf(x) + erfc(x)) is S.One assert erfc(x).as_real_imag() == \ (erfc(re(x) - I*im(x))/2 + erfc(re(x) + I*im(x))/2, -I*(-erfc(re(x) - I*im(x)) + erfc(re(x) + I*im(x)))/2) assert erfc(x).as_real_imag(deep=False) == \ (erfc(re(x) - I*im(x))/2 + erfc(re(x) + I*im(x))/2, -I*(-erfc(re(x) - I*im(x)) + erfc(re(x) + I*im(x)))/2) assert erfc(w).as_real_imag() == (erfc(w), 0) assert erfc(w).as_real_imag(deep=False) == (erfc(w), 0) raises(ArgumentIndexError, lambda: erfc(x).fdiff(2)) assert erfc(x).inverse() == erfcinv
def test_beta(): x, y = symbols('x y') t = Dummy('t') assert unchanged(beta, x, y) assert beta(5, -3).is_real == True assert beta(3, y).is_real is None assert expand_func(beta(x, y)) == gamma(x)*gamma(y)/gamma(x + y) assert expand_func(beta(x, y) - beta(y, x)) == 0 # Symmetric assert expand_func(beta(x, y)) == expand_func(beta(x, y + 1) + beta(x + 1, y)).simplify() assert diff(beta(x, y), x) == beta(x, y)*(polygamma(0, x) - polygamma(0, x + y)) assert diff(beta(x, y), y) == beta(x, y)*(polygamma(0, y) - polygamma(0, x + y)) assert conjugate(beta(x, y)) == beta(conjugate(x), conjugate(y)) raises(ArgumentIndexError, lambda: beta(x, y).fdiff(3)) assert beta(x, y).rewrite(gamma) == gamma(x)*gamma(y)/gamma(x + y) assert beta(x).rewrite(gamma) == gamma(x)**2/gamma(2*x) assert beta(x, y).rewrite(Integral).dummy_eq(Integral(t**(x - 1) * (1 - t)**(y - 1), (t, 0, 1)))
def test_erf2(): assert erf2(0, 0) is S.Zero assert erf2(x, x) is S.Zero assert erf2(nan, 0) is nan assert erf2(-oo, y) == erf(y) + 1 assert erf2( oo, y) == erf(y) - 1 assert erf2( x, oo) == 1 - erf(x) assert erf2( x,-oo) == -1 - erf(x) assert erf2(x, erf2inv(x, y)) == y assert erf2(-x, -y) == -erf2(x,y) assert erf2(-x, y) == erf(y) + erf(x) assert erf2( x, -y) == -erf(y) - erf(x) assert erf2(x, y).rewrite('fresnels') == erf(y).rewrite(fresnels)-erf(x).rewrite(fresnels) assert erf2(x, y).rewrite('fresnelc') == erf(y).rewrite(fresnelc)-erf(x).rewrite(fresnelc) assert erf2(x, y).rewrite('hyper') == erf(y).rewrite(hyper)-erf(x).rewrite(hyper) assert erf2(x, y).rewrite('meijerg') == erf(y).rewrite(meijerg)-erf(x).rewrite(meijerg) assert erf2(x, y).rewrite('uppergamma') == erf(y).rewrite(uppergamma) - erf(x).rewrite(uppergamma) assert erf2(x, y).rewrite('expint') == erf(y).rewrite(expint)-erf(x).rewrite(expint) assert erf2(I, 0).is_real is False assert erf2(0, 0, evaluate=False).is_real assert erf2(0, 0, evaluate=False).is_zero assert erf2(x, x, evaluate=False).is_zero assert erf2(x, y).is_zero is None assert expand_func(erf(x) + erf2(x, y)) == erf(y) assert conjugate(erf2(x, y)) == erf2(conjugate(x), conjugate(y)) assert erf2(x, y).rewrite('erf') == erf(y) - erf(x) assert erf2(x, y).rewrite('erfc') == erfc(x) - erfc(y) assert erf2(x, y).rewrite('erfi') == I*(erfi(I*x) - erfi(I*y)) assert erf2(x, y).diff(x) == erf2(x, y).fdiff(1) assert erf2(x, y).diff(y) == erf2(x, y).fdiff(2) assert erf2(x, y).diff(x) == -2*exp(-x**2)/sqrt(pi) assert erf2(x, y).diff(y) == 2*exp(-y**2)/sqrt(pi) raises(ArgumentIndexError, lambda: erf2(x, y).fdiff(3)) assert erf2(x, y).is_extended_real is None xr, yr = symbols('xr yr', extended_real=True) assert erf2(xr, yr).is_extended_real is True
def test_expand_func(): # evaluation at 1 of Gauss' hypergeometric function: from sympy.abc import a, b, c from sympy.core.function import expand_func a1, b1, c1 = randcplx(), randcplx(), randcplx() + 5 assert expand_func(hyper([a, b], [c], 1)) == \ gamma(c)*gamma(-a - b + c)/(gamma(-a + c)*gamma(-b + c)) assert abs(expand_func(hyper([a1, b1], [c1], 1)).n() - hyper([a1, b1], [c1], 1).n()) < 1e-10 # hyperexpand wrapper for hyper: assert expand_func(hyper([], [], z)) == exp(z) assert expand_func(hyper([1, 2, 3], [], z)) == hyper([1, 2, 3], [], z) assert expand_func(meijerg([[1, 1], []], [[1], [0]], z)) == log(z + 1) assert expand_func(meijerg([[1, 1], []], [[], []], z)) == \ meijerg([[1, 1], []], [[], []], z)
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_fresnel(): from sympy.functions.special.error_functions import (fresnelc, fresnels) assert expand_func(integrate(sin(pi * x**2 / 2), x)) == fresnels(x) assert expand_func(integrate(cos(pi * x**2 / 2), x)) == fresnelc(x)
def test_loggamma(): raises(TypeError, lambda: loggamma(2, 3)) raises(ArgumentIndexError, lambda: loggamma(x).fdiff(2)) assert loggamma(-1) is oo assert loggamma(-2) is oo assert loggamma(0) is oo assert loggamma(1) == 0 assert loggamma(2) == 0 assert loggamma(3) == log(2) assert loggamma(4) == log(6) n = Symbol("n", integer=True, positive=True) assert loggamma(n) == log(gamma(n)) assert loggamma(-n) is oo assert loggamma(n/2) == log(2**(-n + 1)*sqrt(pi)*gamma(n)/gamma(n/2 + S.Half)) assert loggamma(oo) is oo assert loggamma(-oo) is zoo assert loggamma(I*oo) is zoo assert loggamma(-I*oo) is zoo assert loggamma(zoo) is zoo assert loggamma(nan) is nan L = loggamma(Rational(16, 3)) E = -5*log(3) + loggamma(Rational(1, 3)) + log(4) + log(7) + log(10) + log(13) assert expand_func(L).doit() == E assert L.n() == E.n() L = loggamma(Rational(19, 4)) E = -4*log(4) + loggamma(Rational(3, 4)) + log(3) + log(7) + log(11) + log(15) assert expand_func(L).doit() == E assert L.n() == E.n() L = loggamma(Rational(23, 7)) E = -3*log(7) + log(2) + loggamma(Rational(2, 7)) + log(9) + log(16) assert expand_func(L).doit() == E assert L.n() == E.n() L = loggamma(Rational(19, 4) - 7) E = -log(9) - log(5) + loggamma(Rational(3, 4)) + 3*log(4) - 3*I*pi assert expand_func(L).doit() == E assert L.n() == E.n() L = loggamma(Rational(23, 7) - 6) E = -log(19) - log(12) - log(5) + loggamma(Rational(2, 7)) + 3*log(7) - 3*I*pi assert expand_func(L).doit() == E assert L.n() == E.n() assert loggamma(x).diff(x) == polygamma(0, x) s1 = loggamma(1/(x + sin(x)) + cos(x)).nseries(x, n=4) s2 = (-log(2*x) - 1)/(2*x) - log(x/pi)/2 + (4 - log(2*x))*x/24 + O(x**2) + \ log(x)*x**2/2 assert (s1 - s2).expand(force=True).removeO() == 0 s1 = loggamma(1/x).series(x) s2 = (1/x - S.Half)*log(1/x) - 1/x + log(2*pi)/2 + \ x/12 - x**3/360 + x**5/1260 + O(x**7) assert ((s1 - s2).expand(force=True)).removeO() == 0 assert loggamma(x).rewrite('intractable') == log(gamma(x)) s1 = loggamma(x).series(x).cancel() assert s1 == -log(x) - EulerGamma*x + pi**2*x**2/12 + x**3*polygamma(2, 1)/6 + \ pi**4*x**4/360 + x**5*polygamma(4, 1)/120 + O(x**6) assert s1 == loggamma(x).rewrite('intractable').series(x).cancel() assert conjugate(loggamma(x)) == loggamma(conjugate(x)) assert conjugate(loggamma(0)) is oo assert conjugate(loggamma(1)) == loggamma(conjugate(1)) assert conjugate(loggamma(-oo)) == conjugate(zoo) assert loggamma(Symbol('v', positive=True)).is_real is True assert loggamma(Symbol('v', zero=True)).is_real is False assert loggamma(Symbol('v', negative=True)).is_real is False assert loggamma(Symbol('v', nonpositive=True)).is_real is False assert loggamma(Symbol('v', nonnegative=True)).is_real is None assert loggamma(Symbol('v', imaginary=True)).is_real is None assert loggamma(Symbol('v', real=True)).is_real is None assert loggamma(Symbol('v')).is_real is None assert loggamma(S.Half).is_real is True assert loggamma(0).is_real is False assert loggamma(Rational(-1, 2)).is_real is False assert loggamma(I).is_real is None assert loggamma(2 + 3*I).is_real is None def tN(N, M): assert loggamma(1/x)._eval_nseries(x, n=N).getn() == M tN(0, 0) tN(1, 1) tN(2, 2) tN(3, 3) tN(4, 4) tN(5, 5)
def _gammasimp(expr, as_comb): """ Helper function for gammasimp and combsimp. Explanation =========== Simplifies expressions written in terms of gamma function. If as_comb is True, it tries to preserve integer arguments. See docstring of gammasimp for more information. This was part of combsimp() in combsimp.py. """ expr = expr.replace(gamma, lambda n: _rf(1, (n - 1).expand())) if as_comb: expr = expr.replace(_rf, lambda a, b: gamma(b + 1)) else: expr = expr.replace(_rf, lambda a, b: gamma(a + b) / gamma(a)) def rule_gamma(expr, level=0): """ Simplify products of gamma functions further. """ if expr.is_Atom: return expr def gamma_rat(x): # helper to simplify ratios of gammas was = x.count(gamma) xx = x.replace( gamma, lambda n: _rf(1, (n - 1).expand()).replace( _rf, lambda a, b: gamma(a + b) / gamma(a))) if xx.count(gamma) < was: x = xx return x def gamma_factor(x): # return True if there is a gamma factor in shallow args if isinstance(x, gamma): return True if x.is_Add or x.is_Mul: return any(gamma_factor(xi) for xi in x.args) if x.is_Pow and (x.exp.is_integer or x.base.is_positive): return gamma_factor(x.base) return False # recursion step if level == 0: expr = expr.func(*[rule_gamma(x, level + 1) for x in expr.args]) level += 1 if not expr.is_Mul: return expr # non-commutative step if level == 1: args, nc = expr.args_cnc() if not args: return expr if nc: return rule_gamma(Mul._from_args(args), level + 1) * Mul._from_args(nc) level += 1 # pure gamma handling, not factor absorption if level == 2: T, F = sift(expr.args, gamma_factor, binary=True) gamma_ind = Mul(*F) d = Mul(*T) nd, dd = d.as_numer_denom() for ipass in range(2): args = list(ordered(Mul.make_args(nd))) for i, ni in enumerate(args): if ni.is_Add: ni, dd = Add(*[ rule_gamma(gamma_rat(a / dd), level + 1) for a in ni.args ]).as_numer_denom() args[i] = ni if not dd.has(gamma): break nd = Mul(*args) if ipass == 0 and not gamma_factor(nd): break nd, dd = dd, nd # now process in reversed order expr = gamma_ind * nd / dd if not (expr.is_Mul and (gamma_factor(dd) or gamma_factor(nd))): return expr level += 1 # iteration until constant if level == 3: while True: was = expr expr = rule_gamma(expr, 4) if expr == was: return expr numer_gammas = [] denom_gammas = [] numer_others = [] denom_others = [] def explicate(p): if p is S.One: return None, [] b, e = p.as_base_exp() if e.is_Integer: if isinstance(b, gamma): return True, [b.args[0]] * e else: return False, [b] * e else: return False, [p] newargs = list(ordered(expr.args)) while newargs: n, d = newargs.pop().as_numer_denom() isg, l = explicate(n) if isg: numer_gammas.extend(l) elif isg is False: numer_others.extend(l) isg, l = explicate(d) if isg: denom_gammas.extend(l) elif isg is False: denom_others.extend(l) # =========== level 2 work: pure gamma manipulation ========= if not as_comb: # Try to reduce the number of gamma factors by applying the # reflection formula gamma(x)*gamma(1-x) = pi/sin(pi*x) for gammas, numer, denom in [ (numer_gammas, numer_others, denom_others), (denom_gammas, denom_others, numer_others) ]: new = [] while gammas: g1 = gammas.pop() if g1.is_integer: new.append(g1) continue for i, g2 in enumerate(gammas): n = g1 + g2 - 1 if not n.is_Integer: continue numer.append(S.Pi) denom.append(sin(S.Pi * g1)) gammas.pop(i) if n > 0: for k in range(n): numer.append(1 - g1 + k) elif n < 0: for k in range(-n): denom.append(-g1 - k) break else: new.append(g1) # /!\ updating IN PLACE gammas[:] = new # Try to reduce the number of gammas by using the duplication # theorem to cancel an upper and lower: gamma(2*s)/gamma(s) = # 2**(2*s + 1)/(4*sqrt(pi))*gamma(s + 1/2). Although this could # be done with higher argument ratios like gamma(3*x)/gamma(x), # this would not reduce the number of gammas as in this case. for ng, dg, no, do in [ (numer_gammas, denom_gammas, numer_others, denom_others), (denom_gammas, numer_gammas, denom_others, numer_others) ]: while True: for x in ng: for y in dg: n = x - 2 * y if n.is_Integer: break else: continue break else: break ng.remove(x) dg.remove(y) if n > 0: for k in range(n): no.append(2 * y + k) elif n < 0: for k in range(-n): do.append(2 * y - 1 - k) ng.append(y + S.Half) no.append(2**(2 * y - 1)) do.append(sqrt(S.Pi)) # Try to reduce the number of gamma factors by applying the # multiplication theorem (used when n gammas with args differing # by 1/n mod 1 are encountered). # # run of 2 with args differing by 1/2 # # >>> gammasimp(gamma(x)*gamma(x+S.Half)) # 2*sqrt(2)*2**(-2*x - 1/2)*sqrt(pi)*gamma(2*x) # # run of 3 args differing by 1/3 (mod 1) # # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(2)/3)) # 6*3**(-3*x - 1/2)*pi*gamma(3*x) # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(5)/3)) # 2*3**(-3*x - 1/2)*pi*(3*x + 2)*gamma(3*x) # def _run(coeffs): # find runs in coeffs such that the difference in terms (mod 1) # of t1, t2, ..., tn is 1/n u = list(uniq(coeffs)) for i in range(len(u)): dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))]) for one, j in dj: if one.p == 1 and one.q != 1: n = one.q got = [i] get = list(range(1, n)) for d, j in dj: m = n * d if m.is_Integer and m in get: get.remove(m) got.append(j) if not get: break else: continue for i, j in enumerate(got): c = u[j] coeffs.remove(c) got[i] = c return one.q, got[0], got[1:] def _mult_thm(gammas, numer, denom): # pull off and analyze the leading coefficient from each gamma arg # looking for runs in those Rationals # expr -> coeff + resid -> rats[resid] = coeff rats = {} for g in gammas: c, resid = g.as_coeff_Add() rats.setdefault(resid, []).append(c) # look for runs in Rationals for each resid keys = sorted(rats, key=default_sort_key) for resid in keys: coeffs = list(sorted(rats[resid])) new = [] while True: run = _run(coeffs) if run is None: break # process the sequence that was found: # 1) convert all the gamma functions to have the right # argument (could be off by an integer) # 2) append the factors corresponding to the theorem # 3) append the new gamma function n, ui, other = run # (1) for u in other: con = resid + u - 1 for k in range(int(u - ui)): numer.append(con - k) con = n * (resid + ui) # for (2) and (3) # (2) numer.append( (2 * S.Pi)**(S(n - 1) / 2) * n**(S.Half - con)) # (3) new.append(con) # restore resid to coeffs rats[resid] = [resid + c for c in coeffs] + new # rebuild the gamma arguments g = [] for resid in keys: g += rats[resid] # /!\ updating IN PLACE gammas[:] = g for l, numer, denom in [(numer_gammas, numer_others, denom_others), (denom_gammas, denom_others, numer_others) ]: _mult_thm(l, numer, denom) # =========== level >= 2 work: factor absorption ========= if level >= 2: # Try to absorb factors into the gammas: x*gamma(x) -> gamma(x + 1) # and gamma(x)/(x - 1) -> gamma(x - 1) # This code (in particular repeated calls to find_fuzzy) can be very # slow. def find_fuzzy(l, x): if not l: return S1, T1 = compute_ST(x) for y in l: S2, T2 = inv[y] if T1 != T2 or (not S1.intersection(S2) and (S1 != set() or S2 != set())): continue # XXX we want some simplification (e.g. cancel or # simplify) but no matter what it's slow. a = len(cancel(x / y).free_symbols) b = len(x.free_symbols) c = len(y.free_symbols) # TODO is there a better heuristic? if a == 0 and (b > 0 or c > 0): return y # We thus try to avoid expensive calls by building the following # "invariants": For every factor or gamma function argument # - the set of free symbols S # - the set of functional components T # We will only try to absorb if T1==T2 and (S1 intersect S2 != emptyset # or S1 == S2 == emptyset) inv = {} def compute_ST(expr): if expr in inv: return inv[expr] return (expr.free_symbols, expr.atoms(Function).union( {e.exp for e in expr.atoms(Pow)})) def update_ST(expr): inv[expr] = compute_ST(expr) for expr in numer_gammas + denom_gammas + numer_others + denom_others: update_ST(expr) for gammas, numer, denom in [ (numer_gammas, numer_others, denom_others), (denom_gammas, denom_others, numer_others) ]: new = [] while gammas: g = gammas.pop() cont = True while cont: cont = False y = find_fuzzy(numer, g) if y is not None: numer.remove(y) if y != g: numer.append(y / g) update_ST(y / g) g += 1 cont = True y = find_fuzzy(denom, g - 1) if y is not None: denom.remove(y) if y != g - 1: numer.append((g - 1) / y) update_ST((g - 1) / y) g -= 1 cont = True new.append(g) # /!\ updating IN PLACE gammas[:] = new # =========== rebuild expr ================================== return Mul(*[gamma(g) for g in numer_gammas]) \ / Mul(*[gamma(g) for g in denom_gammas]) \ * Mul(*numer_others) / Mul(*denom_others) was = factor(expr) # (for some reason we cannot use Basic.replace in this case) expr = rule_gamma(was) if expr != was: expr = factor(expr) expr = expr.replace( gamma, lambda n: expand_func(gamma(n)) if n.is_Rational else gamma(n)) return expr
def test_harmonic_rational(): ne = S(6) no = S(5) pe = S(8) po = S(9) qe = S(10) qo = S(13) Heee = harmonic(ne + pe / qe) Aeee = (-log(10) + 2 * (Rational(-1, 4) + sqrt(5) / 4) * log(sqrt(-sqrt(5) / 8 + Rational(5, 8))) + 2 * (-sqrt(5) / 4 - Rational(1, 4)) * log(sqrt(sqrt(5) / 8 + Rational(5, 8))) + pi * sqrt(2 * sqrt(5) / 5 + 1) / 2 + Rational(13944145, 4720968)) Heeo = harmonic(ne + pe / qo) Aeeo = (-log(26) + 2 * log(sin(pi * Rational(3, 13))) * cos(pi * Rational(4, 13)) + 2 * log(sin(pi * Rational(2, 13))) * cos(pi * Rational(32, 13)) + 2 * log(sin(pi * Rational(5, 13))) * cos(pi * Rational(80, 13)) - 2 * log(sin(pi * Rational(6, 13))) * cos(pi * Rational(5, 13)) - 2 * log(sin(pi * Rational(4, 13))) * cos(pi / 13) + pi * cot(pi * Rational(5, 13)) / 2 - 2 * log(sin(pi / 13)) * cos(pi * Rational(3, 13)) + Rational(2422020029, 702257080)) Heoe = harmonic(ne + po / qe) Aeoe = ( -log(20) + 2 * (Rational(1, 4) + sqrt(5) / 4) * log(Rational(-1, 4) + sqrt(5) / 4) + 2 * (Rational(-1, 4) + sqrt(5) / 4) * log(sqrt(-sqrt(5) / 8 + Rational(5, 8))) + 2 * (-sqrt(5) / 4 - Rational(1, 4)) * log(sqrt(sqrt(5) / 8 + Rational(5, 8))) + 2 * (-sqrt(5) / 4 + Rational(1, 4)) * log(Rational(1, 4) + sqrt(5) / 4) + Rational(11818877030, 4286604231) + pi * sqrt(2 * sqrt(5) + 5) / 2) Heoo = harmonic(ne + po / qo) Aeoo = (-log(26) + 2 * log(sin(pi * Rational(3, 13))) * cos(pi * Rational(54, 13)) + 2 * log(sin(pi * Rational(4, 13))) * cos(pi * Rational(6, 13)) + 2 * log(sin(pi * Rational(6, 13))) * cos(pi * Rational(108, 13)) - 2 * log(sin(pi * Rational(5, 13))) * cos(pi / 13) - 2 * log(sin(pi / 13)) * cos(pi * Rational(5, 13)) + pi * cot(pi * Rational(4, 13)) / 2 - 2 * log(sin(pi * Rational(2, 13))) * cos(pi * Rational(3, 13)) + Rational(11669332571, 3628714320)) Hoee = harmonic(no + pe / qe) Aoee = (-log(10) + 2 * (Rational(-1, 4) + sqrt(5) / 4) * log(sqrt(-sqrt(5) / 8 + Rational(5, 8))) + 2 * (-sqrt(5) / 4 - Rational(1, 4)) * log(sqrt(sqrt(5) / 8 + Rational(5, 8))) + pi * sqrt(2 * sqrt(5) / 5 + 1) / 2 + Rational(779405, 277704)) Hoeo = harmonic(no + pe / qo) Aoeo = (-log(26) + 2 * log(sin(pi * Rational(3, 13))) * cos(pi * Rational(4, 13)) + 2 * log(sin(pi * Rational(2, 13))) * cos(pi * Rational(32, 13)) + 2 * log(sin(pi * Rational(5, 13))) * cos(pi * Rational(80, 13)) - 2 * log(sin(pi * Rational(6, 13))) * cos(pi * Rational(5, 13)) - 2 * log(sin(pi * Rational(4, 13))) * cos(pi / 13) + pi * cot(pi * Rational(5, 13)) / 2 - 2 * log(sin(pi / 13)) * cos(pi * Rational(3, 13)) + Rational(53857323, 16331560)) Hooe = harmonic(no + po / qe) Aooe = ( -log(20) + 2 * (Rational(1, 4) + sqrt(5) / 4) * log(Rational(-1, 4) + sqrt(5) / 4) + 2 * (Rational(-1, 4) + sqrt(5) / 4) * log(sqrt(-sqrt(5) / 8 + Rational(5, 8))) + 2 * (-sqrt(5) / 4 - Rational(1, 4)) * log(sqrt(sqrt(5) / 8 + Rational(5, 8))) + 2 * (-sqrt(5) / 4 + Rational(1, 4)) * log(Rational(1, 4) + sqrt(5) / 4) + Rational(486853480, 186374097) + pi * sqrt(2 * sqrt(5) + 5) / 2) Hooo = harmonic(no + po / qo) Aooo = (-log(26) + 2 * log(sin(pi * Rational(3, 13))) * cos(pi * Rational(54, 13)) + 2 * log(sin(pi * Rational(4, 13))) * cos(pi * Rational(6, 13)) + 2 * log(sin(pi * Rational(6, 13))) * cos(pi * Rational(108, 13)) - 2 * log(sin(pi * Rational(5, 13))) * cos(pi / 13) - 2 * log(sin(pi / 13)) * cos(pi * Rational(5, 13)) + pi * cot(pi * Rational(4, 13)) / 2 - 2 * log(sin(pi * Rational(2, 13))) * cos(3 * pi / 13) + Rational(383693479, 125128080)) H = [Heee, Heeo, Heoe, Heoo, Hoee, Hoeo, Hooe, Hooo] A = [Aeee, Aeeo, Aeoe, Aeoo, Aoee, Aoeo, Aooe, Aooo] for h, a in zip(H, A): e = expand_func(h).doit() assert cancel(e / a) == 1 assert abs(h.n() - a.n()) < 1e-12
def test_fresnel(): assert fresnels(0) is S.Zero assert fresnels(oo) is S.Half assert fresnels(-oo) == Rational(-1, 2) assert fresnels(I*oo) == -I*S.Half assert unchanged(fresnels, z) assert fresnels(-z) == -fresnels(z) assert fresnels(I*z) == -I*fresnels(z) assert fresnels(-I*z) == I*fresnels(z) assert conjugate(fresnels(z)) == fresnels(conjugate(z)) assert fresnels(z).diff(z) == sin(pi*z**2/2) assert fresnels(z).rewrite(erf) == (S.One + I)/4 * ( erf((S.One + I)/2*sqrt(pi)*z) - I*erf((S.One - I)/2*sqrt(pi)*z)) assert fresnels(z).rewrite(hyper) == \ pi*z**3/6 * hyper([Rational(3, 4)], [Rational(3, 2), Rational(7, 4)], -pi**2*z**4/16) assert fresnels(z).series(z, n=15) == \ pi*z**3/6 - pi**3*z**7/336 + pi**5*z**11/42240 + O(z**15) assert fresnels(w).is_extended_real is True assert fresnels(w).is_finite is True assert fresnels(z).is_extended_real is None assert fresnels(z).is_finite is None assert fresnels(z).as_real_imag() == (fresnels(re(z) - I*im(z))/2 + fresnels(re(z) + I*im(z))/2, -I*(-fresnels(re(z) - I*im(z)) + fresnels(re(z) + I*im(z)))/2) assert fresnels(z).as_real_imag(deep=False) == (fresnels(re(z) - I*im(z))/2 + fresnels(re(z) + I*im(z))/2, -I*(-fresnels(re(z) - I*im(z)) + fresnels(re(z) + I*im(z)))/2) assert fresnels(w).as_real_imag() == (fresnels(w), 0) assert fresnels(w).as_real_imag(deep=True) == (fresnels(w), 0) assert fresnels(2 + 3*I).as_real_imag() == ( fresnels(2 + 3*I)/2 + fresnels(2 - 3*I)/2, -I*(fresnels(2 + 3*I) - fresnels(2 - 3*I))/2 ) assert expand_func(integrate(fresnels(z), z)) == \ z*fresnels(z) + cos(pi*z**2/2)/pi assert fresnels(z).rewrite(meijerg) == sqrt(2)*pi*z**Rational(9, 4) * \ meijerg(((), (1,)), ((Rational(3, 4),), (Rational(1, 4), 0)), -pi**2*z**4/16)/(2*(-z)**Rational(3, 4)*(z**2)**Rational(3, 4)) assert fresnelc(0) is S.Zero assert fresnelc(oo) == S.Half assert fresnelc(-oo) == Rational(-1, 2) assert fresnelc(I*oo) == I*S.Half assert unchanged(fresnelc, z) assert fresnelc(-z) == -fresnelc(z) assert fresnelc(I*z) == I*fresnelc(z) assert fresnelc(-I*z) == -I*fresnelc(z) assert conjugate(fresnelc(z)) == fresnelc(conjugate(z)) assert fresnelc(z).diff(z) == cos(pi*z**2/2) assert fresnelc(z).rewrite(erf) == (S.One - I)/4 * ( erf((S.One + I)/2*sqrt(pi)*z) + I*erf((S.One - I)/2*sqrt(pi)*z)) assert fresnelc(z).rewrite(hyper) == \ z * hyper([Rational(1, 4)], [S.Half, Rational(5, 4)], -pi**2*z**4/16) assert fresnelc(w).is_extended_real is True assert fresnelc(z).as_real_imag() == \ (fresnelc(re(z) - I*im(z))/2 + fresnelc(re(z) + I*im(z))/2, -I*(-fresnelc(re(z) - I*im(z)) + fresnelc(re(z) + I*im(z)))/2) assert fresnelc(z).as_real_imag(deep=False) == \ (fresnelc(re(z) - I*im(z))/2 + fresnelc(re(z) + I*im(z))/2, -I*(-fresnelc(re(z) - I*im(z)) + fresnelc(re(z) + I*im(z)))/2) assert fresnelc(2 + 3*I).as_real_imag() == ( fresnelc(2 - 3*I)/2 + fresnelc(2 + 3*I)/2, -I*(fresnelc(2 + 3*I) - fresnelc(2 - 3*I))/2 ) assert expand_func(integrate(fresnelc(z), z)) == \ z*fresnelc(z) - sin(pi*z**2/2)/pi assert fresnelc(z).rewrite(meijerg) == sqrt(2)*pi*z**Rational(3, 4) * \ meijerg(((), (1,)), ((Rational(1, 4),), (Rational(3, 4), 0)), -pi**2*z**4/16)/(2*(-z)**Rational(1, 4)*(z**2)**Rational(1, 4)) from sympy.core.random import verify_numerically verify_numerically(re(fresnels(z)), fresnels(z).as_real_imag()[0], z) verify_numerically(im(fresnels(z)), fresnels(z).as_real_imag()[1], z) verify_numerically(fresnels(z), fresnels(z).rewrite(hyper), z) verify_numerically(fresnels(z), fresnels(z).rewrite(meijerg), z) verify_numerically(re(fresnelc(z)), fresnelc(z).as_real_imag()[0], z) verify_numerically(im(fresnelc(z)), fresnelc(z).as_real_imag()[1], z) verify_numerically(fresnelc(z), fresnelc(z).rewrite(hyper), z) verify_numerically(fresnelc(z), fresnelc(z).rewrite(meijerg), z) raises(ArgumentIndexError, lambda: fresnels(z).fdiff(2)) raises(ArgumentIndexError, lambda: fresnelc(z).fdiff(2)) assert fresnels(x).taylor_term(-1, x) is S.Zero assert fresnelc(x).taylor_term(-1, x) is S.Zero assert fresnelc(x).taylor_term(1, x) == -pi**2*x**5/40
def _gammasimp(expr, as_comb): """ Helper function for gammasimp and combsimp. Simplifies expressions written in terms of gamma function. If as_comb is True, it tries to preserve integer arguments. See docstring of gammasimp for more information. This was part of combsimp() in combsimp.py. """ expr = expr.replace(gamma, lambda n: _rf(1, (n - 1).expand())) if as_comb: expr = expr.replace(_rf, lambda a, b: gamma(b + 1)) else: expr = expr.replace(_rf, lambda a, b: gamma(a + b)/gamma(a)) def rule(n, k): coeff, rewrite = S.One, False cn, _n = n.as_coeff_Add() if _n and cn.is_Integer and cn: coeff *= _rf(_n + 1, cn)/_rf(_n - k + 1, cn) rewrite = True n = _n # this sort of binomial has already been removed by # rising factorials but is left here in case the order # of rule application is changed if k.is_Add: ck, _k = k.as_coeff_Add() if _k and ck.is_Integer and ck: coeff *= _rf(n - ck - _k + 1, ck)/_rf(_k + 1, ck) rewrite = True k = _k if count_ops(k) > count_ops(n - k): rewrite = True k = n - k if rewrite: return coeff*binomial(n, k) expr = expr.replace(binomial, rule) def rule_gamma(expr, level=0): """ Simplify products of gamma functions further. """ if expr.is_Atom: return expr def gamma_rat(x): # helper to simplify ratios of gammas was = x.count(gamma) xx = x.replace(gamma, lambda n: _rf(1, (n - 1).expand() ).replace(_rf, lambda a, b: gamma(a + b)/gamma(a))) if xx.count(gamma) < was: x = xx return x def gamma_factor(x): # return True if there is a gamma factor in shallow args if isinstance(x, gamma): return True if x.is_Add or x.is_Mul: return any(gamma_factor(xi) for xi in x.args) if x.is_Pow and (x.exp.is_integer or x.base.is_positive): return gamma_factor(x.base) return False # recursion step if level == 0: expr = expr.func(*[rule_gamma(x, level + 1) for x in expr.args]) level += 1 if not expr.is_Mul: return expr # non-commutative step if level == 1: args, nc = expr.args_cnc() if not args: return expr if nc: return rule_gamma(Mul._from_args(args), level + 1)*Mul._from_args(nc) level += 1 # pure gamma handling, not factor absorption if level == 2: T, F = sift(expr.args, gamma_factor, binary=True) gamma_ind = Mul(*F) d = Mul(*T) nd, dd = d.as_numer_denom() for ipass in range(2): args = list(ordered(Mul.make_args(nd))) for i, ni in enumerate(args): if ni.is_Add: ni, dd = Add(*[ rule_gamma(gamma_rat(a/dd), level + 1) for a in ni.args] ).as_numer_denom() args[i] = ni if not dd.has(gamma): break nd = Mul(*args) if ipass == 0 and not gamma_factor(nd): break nd, dd = dd, nd # now process in reversed order expr = gamma_ind*nd/dd if not (expr.is_Mul and (gamma_factor(dd) or gamma_factor(nd))): return expr level += 1 # iteration until constant if level == 3: while True: was = expr expr = rule_gamma(expr, 4) if expr == was: return expr numer_gammas = [] denom_gammas = [] numer_others = [] denom_others = [] def explicate(p): if p is S.One: return None, [] b, e = p.as_base_exp() if e.is_Integer: if isinstance(b, gamma): return True, [b.args[0]]*e else: return False, [b]*e else: return False, [p] newargs = list(ordered(expr.args)) while newargs: n, d = newargs.pop().as_numer_denom() isg, l = explicate(n) if isg: numer_gammas.extend(l) elif isg is False: numer_others.extend(l) isg, l = explicate(d) if isg: denom_gammas.extend(l) elif isg is False: denom_others.extend(l) # =========== level 2 work: pure gamma manipulation ========= if not as_comb: # Try to reduce the number of gamma factors by applying the # reflection formula gamma(x)*gamma(1-x) = pi/sin(pi*x) for gammas, numer, denom in [( numer_gammas, numer_others, denom_others), (denom_gammas, denom_others, numer_others)]: new = [] while gammas: g1 = gammas.pop() if g1.is_integer: new.append(g1) continue for i, g2 in enumerate(gammas): n = g1 + g2 - 1 if not n.is_Integer: continue numer.append(S.Pi) denom.append(sin(S.Pi*g1)) gammas.pop(i) if n > 0: for k in range(n): numer.append(1 - g1 + k) elif n < 0: for k in range(-n): denom.append(-g1 - k) break else: new.append(g1) # /!\ updating IN PLACE gammas[:] = new # Try to reduce the number of gammas by using the duplication # theorem to cancel an upper and lower: gamma(2*s)/gamma(s) = # 2**(2*s + 1)/(4*sqrt(pi))*gamma(s + 1/2). Although this could # be done with higher argument ratios like gamma(3*x)/gamma(x), # this would not reduce the number of gammas as in this case. for ng, dg, no, do in [(numer_gammas, denom_gammas, numer_others, denom_others), (denom_gammas, numer_gammas, denom_others, numer_others)]: while True: for x in ng: for y in dg: n = x - 2*y if n.is_Integer: break else: continue break else: break ng.remove(x) dg.remove(y) if n > 0: for k in range(n): no.append(2*y + k) elif n < 0: for k in range(-n): do.append(2*y - 1 - k) ng.append(y + S(1)/2) no.append(2**(2*y - 1)) do.append(sqrt(S.Pi)) # Try to reduce the number of gamma factors by applying the # multiplication theorem (used when n gammas with args differing # by 1/n mod 1 are encountered). # # run of 2 with args differing by 1/2 # # >>> gammasimp(gamma(x)*gamma(x+S.Half)) # 2*sqrt(2)*2**(-2*x - 1/2)*sqrt(pi)*gamma(2*x) # # run of 3 args differing by 1/3 (mod 1) # # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(2)/3)) # 6*3**(-3*x - 1/2)*pi*gamma(3*x) # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(5)/3)) # 2*3**(-3*x - 1/2)*pi*(3*x + 2)*gamma(3*x) # def _run(coeffs): # find runs in coeffs such that the difference in terms (mod 1) # of t1, t2, ..., tn is 1/n u = list(uniq(coeffs)) for i in range(len(u)): dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))]) for one, j in dj: if one.p == 1 and one.q != 1: n = one.q got = [i] get = list(range(1, n)) for d, j in dj: m = n*d if m.is_Integer and m in get: get.remove(m) got.append(j) if not get: break else: continue for i, j in enumerate(got): c = u[j] coeffs.remove(c) got[i] = c return one.q, got[0], got[1:] def _mult_thm(gammas, numer, denom): # pull off and analyze the leading coefficient from each gamma arg # looking for runs in those Rationals # expr -> coeff + resid -> rats[resid] = coeff rats = {} for g in gammas: c, resid = g.as_coeff_Add() rats.setdefault(resid, []).append(c) # look for runs in Rationals for each resid keys = sorted(rats, key=default_sort_key) for resid in keys: coeffs = list(sorted(rats[resid])) new = [] while True: run = _run(coeffs) if run is None: break # process the sequence that was found: # 1) convert all the gamma functions to have the right # argument (could be off by an integer) # 2) append the factors corresponding to the theorem # 3) append the new gamma function n, ui, other = run # (1) for u in other: con = resid + u - 1 for k in range(int(u - ui)): numer.append(con - k) con = n*(resid + ui) # for (2) and (3) # (2) numer.append((2*S.Pi)**(S(n - 1)/2)* n**(S(1)/2 - con)) # (3) new.append(con) # restore resid to coeffs rats[resid] = [resid + c for c in coeffs] + new # rebuild the gamma arguments g = [] for resid in keys: g += rats[resid] # /!\ updating IN PLACE gammas[:] = g for l, numer, denom in [(numer_gammas, numer_others, denom_others), (denom_gammas, denom_others, numer_others)]: _mult_thm(l, numer, denom) # =========== level >= 2 work: factor absorption ========= if level >= 2: # Try to absorb factors into the gammas: x*gamma(x) -> gamma(x + 1) # and gamma(x)/(x - 1) -> gamma(x - 1) # This code (in particular repeated calls to find_fuzzy) can be very # slow. def find_fuzzy(l, x): if not l: return S1, T1 = compute_ST(x) for y in l: S2, T2 = inv[y] if T1 != T2 or (not S1.intersection(S2) and (S1 != set() or S2 != set())): continue # XXX we want some simplification (e.g. cancel or # simplify) but no matter what it's slow. a = len(cancel(x/y).free_symbols) b = len(x.free_symbols) c = len(y.free_symbols) # TODO is there a better heuristic? if a == 0 and (b > 0 or c > 0): return y # We thus try to avoid expensive calls by building the following # "invariants": For every factor or gamma function argument # - the set of free symbols S # - the set of functional components T # We will only try to absorb if T1==T2 and (S1 intersect S2 != emptyset # or S1 == S2 == emptyset) inv = {} def compute_ST(expr): if expr in inv: return inv[expr] return (expr.free_symbols, expr.atoms(Function).union( set(e.exp for e in expr.atoms(Pow)))) def update_ST(expr): inv[expr] = compute_ST(expr) for expr in numer_gammas + denom_gammas + numer_others + denom_others: update_ST(expr) for gammas, numer, denom in [( numer_gammas, numer_others, denom_others), (denom_gammas, denom_others, numer_others)]: new = [] while gammas: g = gammas.pop() cont = True while cont: cont = False y = find_fuzzy(numer, g) if y is not None: numer.remove(y) if y != g: numer.append(y/g) update_ST(y/g) g += 1 cont = True y = find_fuzzy(denom, g - 1) if y is not None: denom.remove(y) if y != g - 1: numer.append((g - 1)/y) update_ST((g - 1)/y) g -= 1 cont = True new.append(g) # /!\ updating IN PLACE gammas[:] = new # =========== rebuild expr ================================== return Mul(*[gamma(g) for g in numer_gammas]) \ / Mul(*[gamma(g) for g in denom_gammas]) \ * Mul(*numer_others) / Mul(*denom_others) # (for some reason we cannot use Basic.replace in this case) was = factor(expr) expr = rule_gamma(was) if expr != was: expr = factor(expr) expr = expr.replace(gamma, lambda n: expand_func(gamma(n)) if n.is_Rational else gamma(n)) return expr
def test_expand(): assert expand_func(besselj(S.Half, z).rewrite(jn)) == \ sqrt(2)*sin(z)/(sqrt(pi)*sqrt(z)) assert expand_func(bessely(S.Half, z).rewrite(yn)) == \ -sqrt(2)*cos(z)/(sqrt(pi)*sqrt(z)) # XXX: teach sin/cos to work around arguments like # x*exp_polar(I*pi*n/2). Then change besselsimp -> expand_func assert besselsimp(besselj(S.Half, z)) == sqrt(2)*sin(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besselj(Rational(-1, 2), z)) == sqrt(2)*cos(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besselj(Rational(5, 2), z)) == \ -sqrt(2)*(z**2*sin(z) + 3*z*cos(z) - 3*sin(z))/(sqrt(pi)*z**Rational(5, 2)) assert besselsimp(besselj(Rational(-5, 2), z)) == \ -sqrt(2)*(z**2*cos(z) - 3*z*sin(z) - 3*cos(z))/(sqrt(pi)*z**Rational(5, 2)) assert besselsimp(bessely(S.Half, z)) == \ -(sqrt(2)*cos(z))/(sqrt(pi)*sqrt(z)) assert besselsimp(bessely(Rational(-1, 2), z)) == sqrt(2)*sin(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(bessely(Rational(5, 2), z)) == \ sqrt(2)*(z**2*cos(z) - 3*z*sin(z) - 3*cos(z))/(sqrt(pi)*z**Rational(5, 2)) assert besselsimp(bessely(Rational(-5, 2), z)) == \ -sqrt(2)*(z**2*sin(z) + 3*z*cos(z) - 3*sin(z))/(sqrt(pi)*z**Rational(5, 2)) assert besselsimp(besseli(S.Half, z)) == sqrt(2)*sinh(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besseli(Rational(-1, 2), z)) == \ sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besseli(Rational(5, 2), z)) == \ sqrt(2)*(z**2*sinh(z) - 3*z*cosh(z) + 3*sinh(z))/(sqrt(pi)*z**Rational(5, 2)) assert besselsimp(besseli(Rational(-5, 2), z)) == \ sqrt(2)*(z**2*cosh(z) - 3*z*sinh(z) + 3*cosh(z))/(sqrt(pi)*z**Rational(5, 2)) assert besselsimp(besselk(S.Half, z)) == \ besselsimp(besselk(Rational(-1, 2), z)) == sqrt(pi)*exp(-z)/(sqrt(2)*sqrt(z)) assert besselsimp(besselk(Rational(5, 2), z)) == \ besselsimp(besselk(Rational(-5, 2), z)) == \ sqrt(2)*sqrt(pi)*(z**2 + 3*z + 3)*exp(-z)/(2*z**Rational(5, 2)) n = Symbol('n', integer=True, positive=True) assert expand_func(besseli(n + 2, z)) == \ besseli(n, z) + (-2*n - 2)*(-2*n*besseli(n, z)/z + besseli(n - 1, z))/z assert expand_func(besselj(n + 2, z)) == \ -besselj(n, z) + (2*n + 2)*(2*n*besselj(n, z)/z - besselj(n - 1, z))/z assert expand_func(besselk(n + 2, z)) == \ besselk(n, z) + (2*n + 2)*(2*n*besselk(n, z)/z + besselk(n - 1, z))/z assert expand_func(bessely(n + 2, z)) == \ -bessely(n, z) + (2*n + 2)*(2*n*bessely(n, z)/z - bessely(n - 1, z))/z assert expand_func(besseli(n + S.Half, z).rewrite(jn)) == \ (sqrt(2)*sqrt(z)*exp(-I*pi*(n + S.Half)/2) * exp_polar(I*pi/4)*jn(n, z*exp_polar(I*pi/2))/sqrt(pi)) assert expand_func(besselj(n + S.Half, z).rewrite(jn)) == \ sqrt(2)*sqrt(z)*jn(n, z)/sqrt(pi) r = Symbol('r', real=True) p = Symbol('p', positive=True) i = Symbol('i', integer=True) for besselx in [besselj, bessely, besseli, besselk]: assert besselx(i, p).is_extended_real is True assert besselx(i, x).is_extended_real is None assert besselx(x, z).is_extended_real is None for besselx in [besselj, besseli]: assert besselx(i, r).is_extended_real is True for besselx in [bessely, besselk]: assert besselx(i, r).is_extended_real is None for besselx in [besselj, bessely, besseli, besselk]: assert expand_func(besselx(oo, x)) == besselx(oo, x, evaluate=False) assert expand_func(besselx(-oo, x)) == besselx(-oo, x, evaluate=False)
def mjn(n, z): return expand_func(jn(n, z))
def myn(n, z): return expand_func(yn(n, z))
def test_gamma(): assert gamma(nan) is nan assert gamma(oo) is oo assert gamma(-100) is zoo assert gamma(0) is zoo assert gamma(-100.0) is zoo assert gamma(1) == 1 assert gamma(2) == 1 assert gamma(3) == 2 assert gamma(102) == factorial(101) assert gamma(S.Half) == sqrt(pi) assert gamma(Rational(3, 2)) == sqrt(pi)*S.Half assert gamma(Rational(5, 2)) == sqrt(pi)*Rational(3, 4) assert gamma(Rational(7, 2)) == sqrt(pi)*Rational(15, 8) assert gamma(Rational(-1, 2)) == -2*sqrt(pi) assert gamma(Rational(-3, 2)) == sqrt(pi)*Rational(4, 3) assert gamma(Rational(-5, 2)) == sqrt(pi)*Rational(-8, 15) assert gamma(Rational(-15, 2)) == sqrt(pi)*Rational(256, 2027025) assert gamma(Rational( -11, 8)).expand(func=True) == Rational(64, 33)*gamma(Rational(5, 8)) assert gamma(Rational( -10, 3)).expand(func=True) == Rational(81, 280)*gamma(Rational(2, 3)) assert gamma(Rational( 14, 3)).expand(func=True) == Rational(880, 81)*gamma(Rational(2, 3)) assert gamma(Rational( 17, 7)).expand(func=True) == Rational(30, 49)*gamma(Rational(3, 7)) assert gamma(Rational( 19, 8)).expand(func=True) == Rational(33, 64)*gamma(Rational(3, 8)) assert gamma(x).diff(x) == gamma(x)*polygamma(0, x) assert gamma(x - 1).expand(func=True) == gamma(x)/(x - 1) assert gamma(x + 2).expand(func=True, mul=False) == x*(x + 1)*gamma(x) assert conjugate(gamma(x)) == gamma(conjugate(x)) assert expand_func(gamma(x + Rational(3, 2))) == \ (x + S.Half)*gamma(x + S.Half) assert expand_func(gamma(x - S.Half)) == \ gamma(S.Half + x)/(x - S.Half) # Test a bug: assert expand_func(gamma(x + Rational(3, 4))) == gamma(x + Rational(3, 4)) # XXX: Not sure about these tests. I can fix them by defining e.g. # exp_polar.is_integer but I'm not sure if that makes sense. assert gamma(3*exp_polar(I*pi)/4).is_nonnegative is False assert gamma(3*exp_polar(I*pi)/4).is_extended_nonpositive is True y = Symbol('y', nonpositive=True, integer=True) assert gamma(y).is_real == False y = Symbol('y', positive=True, noninteger=True) assert gamma(y).is_real == True assert gamma(-1.0, evaluate=False).is_real == False assert gamma(0, evaluate=False).is_real == False assert gamma(-2, evaluate=False).is_real == False