def test_operations(): F = QQ.old_poly_ring(x).free_module(2) G = QQ.old_poly_ring(x).free_module(3) f = F.identity_hom() g = homomorphism(F, F, [0, [1, x]]) h = homomorphism(F, F, [[1, 0], 0]) i = homomorphism(F, G, [[1, 0, 0], [0, 1, 0]]) assert f == f assert f != g assert f != i assert (f != F.identity_hom()) is False assert 2 * f == f * 2 == homomorphism(F, F, [[2, 0], [0, 2]]) assert f / 2 == homomorphism(F, F, [[S.Half, 0], [0, S.Half]]) assert f + g == homomorphism(F, F, [[1, 0], [1, x + 1]]) assert f - g == homomorphism(F, F, [[1, 0], [-1, 1 - x]]) assert f * g == g == g * f assert h * g == homomorphism(F, F, [0, [1, 0]]) assert g * h == homomorphism(F, F, [0, 0]) assert i * f == i assert f([1, 2]) == [1, 2] assert g([1, 2]) == [2, 2 * x] assert i.restrict_domain(F.submodule([x, x]))([x, x]) == i([x, x]) h1 = h.quotient_domain(F.submodule([0, 1])) assert h1([1, 0]) == h([1, 0]) assert h1.restrict_domain(h1.domain.submodule([x, 0]))([x, 0]) == h([x, 0]) raises(TypeError, lambda: f / g) raises(TypeError, lambda: f + 1) raises(TypeError, lambda: f + i) raises(TypeError, lambda: f - 1) raises(TypeError, lambda: f * i)
def test_to_Sequence_Initial_Coniditons(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') n = symbols('n', integer=True) _, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') p = HolonomicFunction(Dx - 1, x, 0, [1]).to_sequence() q = [(HolonomicSequence(-1 + (n + 1) * Sn, 1), 0)] assert p == q p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).to_sequence() q = [(HolonomicSequence(1 + (n**2 + 3 * n + 2) * Sn**2, [0, 1]), 0)] assert p == q p = HolonomicFunction(Dx**2 + 1 + x**3 * Dx, x, 0, [2, 3]).to_sequence() q = [(HolonomicSequence( n + Sn**2 + (n**2 + 7 * n + 12) * Sn**4, [2, 3, -1, Rational(-1, 2), Rational(1, 12)]), 1)] assert p == q p = HolonomicFunction(x**3 * Dx**5 + 1 + Dx, x).to_sequence() q = [(HolonomicSequence(1 + (n + 1) * Sn + (n**5 - 5 * n**3 + 4 * n) * Sn**2), 0, 3)] assert p == q C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3') p = expr_to_holonomic(log(1 + x**2)) q = [(HolonomicSequence(n**2 + (n**2 + 2 * n) * Sn**2, [0, 0, C_2]), 0, 1)] assert p.to_sequence() == q p = p.diff() q = [(HolonomicSequence((n + 2) + (n + 2) * Sn**2, [C_0, 0]), 1, 0)] assert p.to_sequence() == q p = expr_to_holonomic(erf(x) + x).to_sequence() q = [(HolonomicSequence( (2 * n**2 - 2 * n) + (n**3 + 2 * n**2 - n - 2) * Sn**2, [0, 1 + 2 / sqrt(pi), 0, C_3]), 0, 2)] assert p == q
def test_QuotientRing(): I = QQ.old_poly_ring(x).ideal(x**2 + 1) R = QQ.old_poly_ring(x) / I assert R == QQ.old_poly_ring(x) / [x**2 + 1] assert R == QQ.old_poly_ring(x) / QQ.old_poly_ring(x).ideal(x**2 + 1) assert R != QQ.old_poly_ring(x) assert R.convert(1) / x == -x + I assert -1 + I == x**2 + I assert R.convert(ZZ(1), ZZ) == 1 + I assert R.convert(R.convert(x), R) == R.convert(x) X = R.convert(x) Y = QQ.old_poly_ring(x).convert(x) assert -1 + I == X**2 + I assert -1 + I == Y**2 + I assert R.to_sympy(X) == x raises(ValueError, lambda: QQ.old_poly_ring(x) / QQ.old_poly_ring(x, y).ideal(x)) R = QQ.old_poly_ring(x, order="ilex") I = R.ideal(x) assert R.convert(1) + I == (R / I).convert(1)
def denom(self, a): """Get the denominator of ``a``.""" ZZ = self.dom.get_ring() QQ = self.dom ZZ_I = self.get_ring() denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y)) return ZZ_I(denom_ZZ, ZZ.zero)
def test_pickling_polys_polyclasses(): from sympy.polys.polyclasses import DMP, DMF, ANP for c in (DMP, DMP([[ZZ(1)], [ZZ(2)], [ZZ(3)]], ZZ)): check(c) for c in (DMF, DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(3)]), ZZ)): check(c) for c in (ANP, ANP([QQ(1), QQ(2)], [QQ(1), QQ(2), QQ(3)], QQ)): check(c)
def test_prde_no_cancel(): # b large DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert prde_no_cancel_b_large(Poly(1, x), [Poly(x**2, x), Poly(1, x)], 2, DE) == \ ([Poly(x**2 - 2*x + 2, x), Poly(1, x)], Matrix([[1, 0, -1, 0], [0, 1, 0, -1]], x)) assert prde_no_cancel_b_large(Poly(1, x), [Poly(x**3, x), Poly(1, x)], 3, DE) == \ ([Poly(x**3 - 3*x**2 + 6*x - 6, x), Poly(1, x)], Matrix([[1, 0, -1, 0], [0, 1, 0, -1]], x)) assert prde_no_cancel_b_large(Poly(x, x), [Poly(x**2, x), Poly(1, x)], 1, DE) == \ ([Poly(x, x, domain='ZZ'), Poly(0, x, domain='ZZ')], Matrix([[1, -1, 0, 0], [1, 0, -1, 0], [0, 1, 0, -1]], x)) # b small # XXX: Is there a better example of a monomial with D.degree() > 2? DE = DifferentialExtension( extension={'D': [Poly(1, x), Poly(t**3 + 1, t)]}) # My original q was t**4 + t + 1, but this solution implies q == t**4 # (c1 = 4), with some of the ci for the original q equal to 0. G = [ Poly(t**6, t), Poly(x * t**5, t), Poly(t**3, t), Poly(x * t**2, t), Poly(1 + x, t) ] R = QQ.frac_field(x)[t] assert prde_no_cancel_b_small(Poly(x*t, t), G, 4, DE) == \ ([Poly(t**4/4 - x/12*t**3 + x**2/24*t**2 + (Rational(-11, 12) - x**3/24)*t + x/24, t), Poly(x/3*t**3 - x**2/6*t**2 + (Rational(-1, 3) + x**3/6)*t - x/6, t), Poly(t, t), Poly(0, t), Poly(0, t)], Matrix([[1, 0, -1, 0, 0, 0, 0, 0, 0, 0], [0, 1, Rational(-1, 4), 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, -1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, -1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, -1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, -1]], ring=R)) # TODO: Add test for deg(b) <= 0 with b small DE = DifferentialExtension( extension={'D': [Poly(1, x), Poly(1 + t**2, t)]}) b = Poly(-1 / x**2, t, field=True) # deg(b) == 0 q = [Poly(x**i * t**j, t, field=True) for i in range(2) for j in range(3)] h, A = prde_no_cancel_b_small(b, q, 3, DE) V = A.nullspace() R = QQ.frac_field(x)[t] assert len(V) == 1 assert V[0] == Matrix([Rational(-1, 2), 0, 0, 1, 0, 0] * 3, ring=R) assert (Matrix([h]) * V[0][6:, :])[0] == Poly(x**2 / 2, t, domain='QQ(x)') assert (Matrix([q]) * V[0][:6, :])[0] == Poly(x - S.Half, t, domain='QQ(x)')
def test_negative_power(): x = symbols("x") _, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') h1 = HolonomicFunction((-1) + (x) * Dx, x)**-2 h2 = HolonomicFunction((2) + (x) * Dx, x) assert h1 == h2
def test_to_hyper(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') p = HolonomicFunction(Dx - 2, x, 0, [3]).to_hyper() q = 3 * hyper([], [], 2 * x) assert p == q p = hyperexpand(HolonomicFunction((1 + x) * Dx - 3, x, 0, [2]).to_hyper()).expand() q = 2 * x**3 + 6 * x**2 + 6 * x + 2 assert p == q p = HolonomicFunction((1 + x) * Dx**2 + Dx, x, 0, [0, 1]).to_hyper() q = -x**2 * hyper((2, 2, 1), (3, 2), -x) / 2 + x assert p == q p = HolonomicFunction(2 * x * Dx + Dx**2, x, 0, [0, 2 / sqrt(pi)]).to_hyper() q = 2 * x * hyper((S.Half, ), (Rational(3, 2), ), -x**2) / sqrt(pi) assert p == q p = hyperexpand( HolonomicFunction(2 * x * Dx + Dx**2, x, 0, [1, -2 / sqrt(pi)]).to_hyper()) q = erfc(x) assert p.rewrite(erfc) == q p = hyperexpand( HolonomicFunction((x**2 - 1) + x * Dx + x**2 * Dx**2, x, 0, [0, S.Half]).to_hyper()) q = besselj(1, x) assert p == q p = hyperexpand( HolonomicFunction(x * Dx**2 + Dx + x, x, 0, [1, 0]).to_hyper()) q = besselj(0, x) assert p == q
def test_addition_initial_condition(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') p = HolonomicFunction(Dx - 1, x, 0, [3]) q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) r = HolonomicFunction(-1 + Dx - Dx**2 + Dx**3, x, 0, [4, 3, 2]) assert p + q == r p = HolonomicFunction(Dx - x + Dx**2, x, 0, [1, 2]) q = HolonomicFunction(Dx**2 + x, x, 0, [1, 0]) r = HolonomicFunction((-x**4 - x**3/4 - x**2 + Rational(1, 4)) + (x**3 + x**2/4 + x*Rational(3, 4) + 1)*Dx + \ (x*Rational(-3, 2) + Rational(7, 4))*Dx**2 + (x**2 - x*Rational(7, 4) + Rational(1, 4))*Dx**3 + (x**2 + x/4 + S.Half)*Dx**4, x, 0, [2, 2, -2, 2]) assert p + q == r p = HolonomicFunction(Dx**2 + 4 * x * Dx + x**2, x, 0, [3, 4]) q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1]) r = HolonomicFunction((x**6 + 2*x**4 - 5*x**2 - 6) + (4*x**5 + 36*x**3 - 32*x)*Dx + \ (x**6 + 3*x**4 + 5*x**2 - 9)*Dx**2 + (4*x**5 + 36*x**3 - 32*x)*Dx**3 + (x**4 + \ 10*x**2 - 3)*Dx**4, x, 0, [4, 5, -1, -17]) assert p + q == r q = HolonomicFunction(Dx**3 + x, x, 2, [3, 0, 1]) p = HolonomicFunction(Dx - 1, x, 2, [1]) r = HolonomicFunction((-x**2 - x + 1) + (x**2 + x)*Dx + (-x - 2)*Dx**3 + \ (x + 1)*Dx**4, x, 2, [4, 1, 2, -5 ]) assert p + q == r p = expr_to_holonomic(sin(x)) q = expr_to_holonomic(1 / x, x0=1) r = HolonomicFunction((x**2 + 6) + (x**3 + 2*x)*Dx + (x**2 + 6)*Dx**2 + (x**3 + 2*x)*Dx**3, \ x, 1, [sin(1) + 1, -1 + cos(1), -sin(1) + 2]) assert p + q == r C_1 = symbols('C_1') p = expr_to_holonomic(sqrt(x)) q = expr_to_holonomic(sqrt(x**2 - x)) r = (p + q).to_expr().subs(C_1, -I / 2).expand() assert r == I * sqrt(x) * sqrt(-x + 1) + sqrt(x)
def test_properties(): R = QQ.old_poly_ring(x, y) F = R.free_module(2) h = homomorphism(F, F, [[x, 0], [y, 0]]) assert h.kernel() == F.submodule([-y, x]) assert h.image() == F.submodule([x, 0], [y, 0]) assert not h.is_injective() assert not h.is_surjective() assert h.restrict_codomain(h.image()).is_surjective() assert h.restrict_domain(F.submodule([1, 0])).is_injective() assert h.quotient_domain(h.kernel()).restrict_codomain( h.image()).is_isomorphism() R2 = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y))) / [x**2 + 1] F = R2.free_module(2) h = homomorphism(F, F, [[x, 0], [y, y + 1]]) assert h.is_isomorphism()
def test_RecurrenceOperator(): n = symbols('n', integer=True) R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') assert Sn * n == (n + 1) * Sn assert Sn * n**2 == (n**2 + 1 + 2 * n) * Sn assert Sn**2 * n**2 == (n**2 + 4 * n + 4) * Sn**2 p = (Sn**3 * n**2 + Sn * n)**2 q = (n**2 + 3*n + 2)*Sn**2 + (2*n**3 + 19*n**2 + 57*n + 52)*Sn**4 + (n**4 + 18*n**3 + \ 117*n**2 + 324*n + 324)*Sn**6 assert p == q
def test_printing(): R = QQ.old_poly_ring(x) assert str(homomorphism(R.free_module(1), R.free_module(1), [0])) == \ 'Matrix([[0]]) : QQ[x]**1 -> QQ[x]**1' assert str(homomorphism(R.free_module(2), R.free_module(2), [0, 0])) == \ 'Matrix([ \n[0, 0], : QQ[x]**2 -> QQ[x]**2\n[0, 0]]) ' assert str(homomorphism(R.free_module(1), R.free_module(1) / [[x]], [0])) == \ 'Matrix([[0]]) : QQ[x]**1 -> QQ[x]**1/<[x]>' assert str(R.free_module( 0).identity_hom()) == 'Matrix(0, 0, []) : QQ[x]**0 -> QQ[x]**0'
def test_constant_system(): A = Matrix([[-(x + 3) / (x - 1), (x + 1) / (x - 1), 1], [-x - 3, x + 1, x - 1], [2 * (x + 3) / (x - 1), 0, 0]], t) u = Matrix([[(x + 1) / (x - 1)], [x + 1], [0]], t) DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) R = QQ.frac_field(x)[t] assert constant_system(A, u, DE) == \ (Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 0], [0, 0, 1]], ring=R), Matrix([0, 1, 0, 0], ring=R))
def test_creation(): F = QQ.old_poly_ring(x).free_module(3) G = QQ.old_poly_ring(x).free_module(2) SM = F.submodule([1, 1, 1]) Q = F / SM SQ = Q.submodule([1, 0, 0]) matrix = [[1, 0], [0, 1], [-1, -1]] h = homomorphism(F, G, matrix) h2 = homomorphism(Q, G, matrix) assert h.quotient_domain(SM) == h2 raises(ValueError, lambda: h.quotient_domain(F.submodule([1, 0, 0]))) assert h2.restrict_domain(SQ) == homomorphism(SQ, G, matrix) raises(ValueError, lambda: h.restrict_domain(G)) raises(ValueError, lambda: h.restrict_codomain(G.submodule([1, 0]))) raises(ValueError, lambda: h.quotient_codomain(F)) im = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] for M in [F, SM, Q, SQ]: assert M.identity_hom() == homomorphism(M, M, im) assert SM.inclusion_hom() == homomorphism(SM, F, im) assert SQ.inclusion_hom() == homomorphism(SQ, Q, im) assert Q.quotient_hom() == homomorphism(F, Q, im) assert SQ.quotient_hom() == homomorphism(SQ.base, SQ, im) class conv: def convert(x, y=None): return x class dummy: container = conv() def submodule(*args): return None raises(TypeError, lambda: homomorphism(dummy(), G, matrix)) raises(TypeError, lambda: homomorphism(F, dummy(), matrix)) raises( ValueError, lambda: homomorphism(QQ.old_poly_ring(x, y).free_module(3), G, matrix)) raises(ValueError, lambda: homomorphism(F, G, [0, 0]))
def get_num_denom(c): r""" Given any argument on which :py:func:`~.is_rat` is ``True``, return the numerator and denominator of this number. See Also ======== is_rat """ r = QQ(c) return r.numerator, r.denominator
def test_solve_triangulated(): f_1 = x**2 + y + z - 1 f_2 = x + y**2 + z - 1 f_3 = x + y + z**2 - 1 a, b = sqrt(2) - 1, -sqrt(2) - 1 assert solve_triangulated([f_1, f_2, f_3], x, y, z) == \ [(0, 0, 1), (0, 1, 0), (1, 0, 0)] dom = QQ.algebraic_field(sqrt(2)) assert solve_triangulated([f_1, f_2, f_3], x, y, z, domain=dom) == \ [(0, 0, 1), (0, 1, 0), (1, 0, 0), (a, a, a), (b, b, b)]
def test_RecurrenceOperatorEqPoly(): n = symbols('n', integer=True) R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') rr = RecurrenceOperator([n**2, 0, 0], R) rr2 = RecurrenceOperator([n**2, 1, n], R) assert not rr == rr2 # polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799 # should work once that is solved # d = rr.listofpoly[0] # assert rr == d d2 = rr2.listofpoly[0] assert not rr2 == d2
def test_DifferentialOperatorEqPoly(): x = symbols('x', integer=True) R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R) do2 = DifferentialOperator([x**2, 1, x], R) assert not do == do2 # polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799 # should work once that is solved # p = do.listofpoly[0] # assert do == p p2 = do2.listofpoly[0] assert not do2 == p2
def test_DifferentialOperator(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') assert Dx == R.derivative_operator assert Dx == DifferentialOperator([R.base.zero, R.base.one], R) assert x * Dx + x**2 * Dx**2 == DifferentialOperator([0, x, x**2], R) assert (x**2 + 1) + Dx + x * \ Dx**5 == DifferentialOperator([x**2 + 1, 1, 0, 0, 0, x], R) assert (x * Dx + x**2 + 1 - Dx * (x**3 + x))**3 == (-48 * x**6) + \ (-57 * x**7) * Dx + (-15 * x**8) * Dx**2 + (-x**9) * Dx**3 p = (x * Dx**2 + (x**2 + 3) * Dx**5) * (Dx + x**2) q = (2 * x) + (4 * x**2) * Dx + (x**3) * Dx**2 + \ (20 * x**2 + x + 60) * Dx**3 + (10 * x**3 + 30 * x) * Dx**4 + \ (x**4 + 3 * x**2) * Dx**5 + (x**2 + 3) * Dx**6 assert p == q
def test_from_hyper(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') p = hyper([1, 1], [Rational(3, 2)], x**2 / 4) q = HolonomicFunction( (4 * x) + (5 * x**2 - 8) * Dx + (x**3 - 4 * x) * Dx**2, x, 1, [2 * sqrt(3) * pi / 9, -4 * sqrt(3) * pi / 27 + Rational(4, 3)]) r = from_hyper(p) assert r == q p = from_hyper(hyper([1], [Rational(3, 2)], x**2 / 4)) q = HolonomicFunction(-x + (-x**2 / 2 + 2) * Dx + x * Dx**2, x) # x0 = 1 y0 = '[sqrt(pi)*exp(1/4)*erf(1/2), -sqrt(pi)*exp(1/4)*erf(1/2)/2 + 1]' assert sstr(p.y0) == y0 assert q.annihilator == p.annihilator
def test_QuotientRingElement(): R = QQ.old_poly_ring(x) / [x**10] X = R.convert(x) assert X * (X + 1) == R.convert(x**2 + x) assert X * x == R.convert(x**2) assert x * X == R.convert(x**2) assert X + x == R.convert(2 * x) assert x + X == 2 * X assert X**2 == R.convert(x**2) assert 1 / (1 - X) == R.convert(sum(x**i for i in range(10))) assert X**10 == R.zero assert X != x raises(NotReversible, lambda: 1 / X)
def test_from_meijerg(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') p = from_meijerg( meijerg(([], [Rational(3, 2)]), ([S.Half], [S.Half, 1]), x)) q = HolonomicFunction(x/2 - Rational(1, 4) + (-x**2 + x/4)*Dx + x**2*Dx**2 + x**3*Dx**3, x, 1, \ [1/sqrt(pi), 1/(2*sqrt(pi)), -1/(4*sqrt(pi))]) assert p == q p = from_meijerg(meijerg(([], []), ([0], []), x)) q = HolonomicFunction(1 + Dx, x, 0, [1]) assert p == q p = from_meijerg(meijerg(([1], []), ([S.Half], [0]), x)) q = HolonomicFunction((x + S.Half) * Dx + x * Dx**2, x, 1, [sqrt(pi) * erf(1), exp(-1)]) assert p == q p = from_meijerg(meijerg(([0], [1]), ([0], []), 2 * x**2)) q = HolonomicFunction((3 * x**2 - 1) * Dx + x**3 * Dx**2, x, 1, [-exp(Rational(-1, 2)) + 1, -exp(Rational(-1, 2))]) assert p == q
def test_multiplication_initial_condition(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') p = HolonomicFunction(Dx**2 + x * Dx - 1, x, 0, [3, 1]) q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1]) r = HolonomicFunction((x**4 + 14*x**2 + 60) + 4*x*Dx + (x**4 + 9*x**2 + 20)*Dx**2 + \ (2*x**3 + 18*x)*Dx**3 + (x**2 + 10)*Dx**4, x, 0, [3, 4, 2, 3]) assert p * q == r p = HolonomicFunction(Dx**2 + x, x, 0, [1, 0]) q = HolonomicFunction(Dx**3 - x**2, x, 0, [3, 3, 3]) r = HolonomicFunction((x**8 - 37*x**7/27 - 10*x**6/27 - 164*x**5/9 - 184*x**4/9 + \ 160*x**3/27 + 404*x**2/9 + 8*x + Rational(40, 3)) + (6*x**7 - 128*x**6/9 - 98*x**5/9 - 28*x**4/9 + \ 8*x**3/9 + 28*x**2 + x*Rational(40, 9) - 40)*Dx + (3*x**6 - 82*x**5/9 + 76*x**4/9 + 4*x**3/3 + \ 220*x**2/9 - x*Rational(80, 3))*Dx**2 + (-2*x**6 + 128*x**5/27 - 2*x**4/3 -80*x**2/9 + Rational(200, 9))*Dx**3 + \ (3*x**5 - 64*x**4/9 - 28*x**3/9 + 6*x**2 - x*Rational(20, 9) - Rational(20, 3))*Dx**4 + (-4*x**3 + 64*x**2/9 + \ x*Rational(8, 3))*Dx**5 + (x**4 - 64*x**3/27 - 4*x**2/3 + Rational(20, 9))*Dx**6, x, 0, [3, 3, 3, -3, -12, -24]) assert p * q == r p = HolonomicFunction(Dx - 1, x, 0, [2]) q = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) r = HolonomicFunction(2 - 2 * Dx + Dx**2, x, 0, [0, 2]) assert p * q == r q = HolonomicFunction(x * Dx**2 + 1 + 2 * Dx, x, 0, [0, 1]) r = HolonomicFunction((x - 1) + (-2 * x + 2) * Dx + x * Dx**2, x, 0, [0, 2]) assert p * q == r p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 3]) q = HolonomicFunction(Dx**3 + 1, x, 0, [1, 2, 1]) r = HolonomicFunction(6 * Dx + 3 * Dx**2 + 2 * Dx**3 - 3 * Dx**4 + Dx**6, x, 0, [1, 5, 14, 17, 17, 2]) assert p * q == r p = expr_to_holonomic(sin(x)) q = expr_to_holonomic(1 / x, x0=1) r = HolonomicFunction(x + 2 * Dx + x * Dx**2, x, 1, [sin(1), -sin(1) + cos(1)]) assert p * q == r p = expr_to_holonomic(sqrt(x)) q = expr_to_holonomic(sqrt(x**2 - x)) r = (p * q).to_expr() assert r == I * x * sqrt(-x + 1)
def is_rat(c): r""" Test whether an argument is of an acceptable type to be used as a rational number. Explanation =========== Returns ``True`` on any argument of type ``int``, :ref:`ZZ`, or :ref:`QQ`. See Also ======== is_int """ # ``c in QQ`` is too accepting (e.g. ``3.14 in QQ`` is ``True``), # ``QQ.of_type(c)`` is too demanding (e.g. ``QQ.of_type(3)`` is ``False``). # # Meanwhile, if gmpy2 is installed then ``ZZ.of_type()`` accepts only # ``mpz``, not ``int``, so we need another clause to ensure ``int`` is # accepted. return isinstance(c, int) or ZZ.of_type(c) or QQ.of_type(c)
def test_evalf_rk4(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') # log(1+x) p = HolonomicFunction((1 + x) * Dx**2 + Dx, x, 0, [0, 1]) # path taken is a straight line from 0 to 1, on the real axis r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] s = '0.693146363174626' # approx. equal to log(2) i.e. 0.693147180559945 assert sstr(p.evalf(r)[-1]) == s # path taken is a traingle 0-->1+i-->2 r = [0.1 + 0.1 * I] for i in range(9): r.append(r[-1] + 0.1 + 0.1 * I) for i in range(10): r.append(r[-1] + 0.1 - 0.1 * I) # close to the exact solution 1.09861228866811 # imaginary part also close to zero s = '1.098616 + 1.36083e-7*I' assert sstr(p.evalf(r)[-1].n(7)) == s # sin(x) p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) s = '0.90929463522785 + 1.52655665885959e-16*I' assert sstr(p.evalf(r)[-1]) == s # computing sin(pi/2) using this method # using a linear path from 0 to pi/2 r = [0.1] for i in range(14): r.append(r[-1] + 0.1) r.append(pi / 2) s = '0.999999895088917' # close to 1.0 (exact solution) assert sstr(p.evalf(r)[-1]) == s # trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2) # computing the same value sin(pi/2) using different path r = [0.1 * I] for i in range(9): r.append(r[-1] + 0.1 * I) for i in range(15): r.append(r[-1] + 0.1) r.append(pi / 2 + I) for i in range(10): r.append(r[-1] - 0.1 * I) # close to 1.0 s = '1.00000003415141 + 6.11940487991086e-16*I' assert sstr(p.evalf(r)[-1]) == s # cos(x) p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) # compute cos(pi) along 0-->pi r = [0.05] for i in range(61): r.append(r[-1] + 0.05) r.append(pi) # close to -1 (exact answer) s = '-0.999999993238714' assert sstr(p.evalf(r)[-1]) == s # a rectangular path (0 -> i -> 2+i -> 2) r = [0.1 * I] for i in range(9): r.append(r[-1] + 0.1 * I) for i in range(20): r.append(r[-1] + 0.1) for i in range(10): r.append(r[-1] - 0.1 * I) p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1]).evalf(r) s = '0.493152791638442 - 1.41553435639707e-15*I' assert sstr(p[-1]) == s
def test_rref_solve(): x, y, z = Dummy('x'), Dummy('y'), Dummy('z') assert rref_solve( [[QQ(25), QQ(15), QQ(-5)], [QQ(15), QQ(18), QQ(0)], [QQ(-5), QQ(0), QQ(11)]], [[x], [y], [z]], [[QQ(2)], [QQ(3)], [QQ(1)]], QQ) == [[QQ(-1, 225)], [QQ(23, 135)], [QQ(4, 45)]]
def test_LU_solve(): x, y, z = Dummy('x'), Dummy('y'), Dummy('z') assert LU_solve( [[QQ(2), QQ(-1), QQ(-2)], [QQ(-4), QQ(6), QQ(3)], [QQ(-4), QQ(-2), QQ(8)]], [[x], [y], [z]], [[QQ(-1)], [QQ(13)], [QQ(-6)]], QQ) == [[QQ(2, 1)], [QQ(3, 1)], [QQ(1, 1)]]
def test_expr_to_holonomic(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') p = expr_to_holonomic((sin(x) / x)**2) q = HolonomicFunction(8*x + (4*x**2 + 6)*Dx + 6*x*Dx**2 + x**2*Dx**3, x, 0, \ [1, 0, Rational(-2, 3)]) assert p == q p = expr_to_holonomic(1 / (1 + x**2)**2) q = HolonomicFunction(4 * x + (x**2 + 1) * Dx, x, 0, [1]) assert p == q p = expr_to_holonomic(exp(x) * sin(x) + x * log(1 + x)) q = HolonomicFunction((2*x**3 + 10*x**2 + 20*x + 18) + (-2*x**4 - 10*x**3 - 20*x**2 \ - 18*x)*Dx + (2*x**5 + 6*x**4 + 7*x**3 + 8*x**2 + 10*x - 4)*Dx**2 + \ (-2*x**5 - 5*x**4 - 2*x**3 + 2*x**2 - x + 4)*Dx**3 + (x**5 + 2*x**4 - x**3 - \ 7*x**2/2 + x + Rational(5, 2))*Dx**4, x, 0, [0, 1, 4, -1]) assert p == q p = expr_to_holonomic(x * exp(x) + cos(x) + 1) q = HolonomicFunction((-x - 3)*Dx + (x + 2)*Dx**2 + (-x - 3)*Dx**3 + (x + 2)*Dx**4, x, \ 0, [2, 1, 1, 3]) assert p == q assert (x * exp(x) + cos(x) + 1).series(n=10) == p.series(n=10) p = expr_to_holonomic(log(1 + x)**2 + 1) q = HolonomicFunction( Dx + (3 * x + 3) * Dx**2 + (x**2 + 2 * x + 1) * Dx**3, x, 0, [1, 0, 2]) assert p == q p = expr_to_holonomic(erf(x)**2 + x) q = HolonomicFunction((8*x**4 - 2*x**2 + 2)*Dx**2 + (6*x**3 - x/2)*Dx**3 + \ (x**2+ Rational(1, 4))*Dx**4, x, 0, [0, 1, 8/pi, 0]) assert p == q p = expr_to_holonomic(cosh(x) * x) q = HolonomicFunction((-x**2 + 2) - 2 * x * Dx + x**2 * Dx**2, x, 0, [0, 1]) assert p == q p = expr_to_holonomic(besselj(2, x)) q = HolonomicFunction((x**2 - 4) + x * Dx + x**2 * Dx**2, x, 0, [0, 0]) assert p == q p = expr_to_holonomic(besselj(0, x) + exp(x)) q = HolonomicFunction((-x**2 - x/2 + S.Half) + (x**2 - x/2 - Rational(3, 2))*Dx + (-x**2 + x/2 + 1)*Dx**2 +\ (x**2 + x/2)*Dx**3, x, 0, [2, 1, S.Half]) assert p == q p = expr_to_holonomic(sin(x)**2 / x) q = HolonomicFunction(4 + 4 * x * Dx + 3 * Dx**2 + x * Dx**3, x, 0, [0, 1, 0]) assert p == q p = expr_to_holonomic(sin(x)**2 / x, x0=2) q = HolonomicFunction((4) + (4 * x) * Dx + (3) * Dx**2 + (x) * Dx**3, x, 2, [ sin(2)**2 / 2, sin(2) * cos(2) - sin(2)**2 / 4, -3 * sin(2)**2 / 4 + cos(2)**2 - sin(2) * cos(2) ]) assert p == q p = expr_to_holonomic(log(x) / 2 - Ci(2 * x) / 2 + Ci(2) / 2) q = HolonomicFunction(4*Dx + 4*x*Dx**2 + 3*Dx**3 + x*Dx**4, x, 0, \ [-log(2)/2 - EulerGamma/2 + Ci(2)/2, 0, 1, 0]) assert p == q p = p.to_expr() q = log(x) / 2 - Ci(2 * x) / 2 + Ci(2) / 2 assert p == q p = expr_to_holonomic(x**S.Half, x0=1) q = HolonomicFunction(x * Dx - S.Half, x, 1, [1]) assert p == q p = expr_to_holonomic(sqrt(1 + x**2)) q = HolonomicFunction((-x) + (x**2 + 1) * Dx, x, 0, [1]) assert p == q assert (expr_to_holonomic(sqrt(x) + sqrt(2*x)).to_expr()-\ (sqrt(x) + sqrt(2*x))).simplify() == 0 assert expr_to_holonomic(3 * x + 2 * sqrt(x)).to_expr() == 3 * x + 2 * sqrt(x) p = expr_to_holonomic((x**4 + x**3 + 5 * x**2 + 3 * x + 2) / x**2, lenics=3) q = HolonomicFunction((-2*x**4 - x**3 + 3*x + 4) + (x**5 + x**4 + 5*x**3 + 3*x**2 + \ 2*x)*Dx, x, 0, {-2: [2, 3, 5]}) assert p == q p = expr_to_holonomic(1 / (x - 1)**2, lenics=3, x0=1) q = HolonomicFunction((2) + (x - 1) * Dx, x, 1, {-2: [1, 0, 0]}) assert p == q a = symbols("a") p = expr_to_holonomic(sqrt(a * x), x=x) assert p.to_expr() == sqrt(a) * sqrt(x)
class GaussianRationalField(GaussianDomain, Field): r"""Field of Gaussian rationals ``QQ_I`` The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)` as a :py:class:`~.Domain` in the domain system (see :ref:`polys-domainsintro`). By default a :py:class:`~.Poly` created from an expression with coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`) will have the domain :ref:`QQ_I`. >>> from sympy import Poly, Symbol, I >>> x = Symbol('x') >>> p = Poly(x**2 + I/2) >>> p Poly(x**2 + I/2, x, domain='QQ_I') >>> p.domain QQ_I The polys option ``gaussian=True`` can be used to specify that the domain should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are all integers. >>> Poly(x**2) Poly(x**2, x, domain='ZZ') >>> Poly(x**2 + I) Poly(x**2 + I, x, domain='ZZ_I') >>> Poly(x**2/2) Poly(1/2*x**2, x, domain='QQ') >>> Poly(x**2, gaussian=True) Poly(x**2, x, domain='QQ_I') >>> Poly(x**2 + I, gaussian=True) Poly(x**2 + I, x, domain='QQ_I') >>> Poly(x**2/2, gaussian=True) Poly(1/2*x**2, x, domain='QQ_I') The :ref:`QQ_I` domain can be used to factorise polynomials that are reducible over the Gaussian rationals. >>> from sympy import factor, QQ_I >>> factor(x**2/4 + 1) (x**2 + 4)/4 >>> factor(x**2/4 + 1, domain='QQ_I') (x - 2*I)*(x + 2*I)/4 >>> factor(x**2/4 + 1, domain=QQ_I) (x - 2*I)*(x + 2*I)/4 It is also possible to specify the :ref:`QQ_I` domain explicitly with polys functions like :py:func:`~.apart`. >>> from sympy import apart >>> apart(1/(1 + x**2)) 1/(x**2 + 1) >>> apart(1/(1 + x**2), domain=QQ_I) I/(2*(x + I)) - I/(2*(x - I)) The corresponding `ring of integers`_ is the domain of the Gaussian integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_ of :ref:`ZZ_I`. >>> from sympy import ZZ_I, QQ_I, QQ >>> ZZ_I.get_field() QQ_I >>> QQ_I.get_ring() ZZ_I When using the domain directly :ref:`QQ_I` can be used as a constructor. >>> QQ_I(3, 4) (3 + 4*I) >>> QQ_I(5) (5 + 0*I) >>> QQ_I(QQ(2, 3), QQ(4, 5)) (2/3 + 4/5*I) The domain elements of :ref:`QQ_I` are instances of :py:class:`~.GaussianRational` which support the field operations ``+,-,*,**,/``. >>> z1 = QQ_I(5, 1) >>> z2 = QQ_I(2, QQ(1, 2)) >>> z1 (5 + 1*I) >>> z2 (2 + 1/2*I) >>> z1 + z2 (7 + 3/2*I) >>> z1 * z2 (19/2 + 9/2*I) >>> z2 ** 2 (15/4 + 2*I) True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and is always exact. >>> z1 / z2 (42/17 + -2/17*I) >>> QQ_I.exquo(z1, z2) (42/17 + -2/17*I) >>> z1 == (z1/z2)*z2 True Both floor (``//``) and modulo (``%``) division can be used with :py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`) but division is always exact so there is no remainder. >>> z1 // z2 (42/17 + -2/17*I) >>> z1 % z2 (0 + 0*I) >>> QQ_I.div(z1, z2) ((42/17 + -2/17*I), (0 + 0*I)) >>> (z1//z2)*z2 + z1%z2 == z1 True .. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational """ dom = QQ dtype = GaussianRational zero = dtype(QQ(0), QQ(0)) one = dtype(QQ(1), QQ(0)) imag_unit = dtype(QQ(0), QQ(1)) units = (one, imag_unit, -one, -imag_unit) # powers of i rep = 'QQ_I' is_GaussianField = True is_QQ_I = True def __init__(self): # override Domain.__init__ """For constructing QQ_I.""" def get_ring(self): """Returns a ring associated with ``self``. """ return ZZ_I def get_field(self): """Returns a field associated with ``self``. """ return self def as_AlgebraicField(self): """Get equivalent domain as an ``AlgebraicField``. """ return AlgebraicField(self.dom, I) def numer(self, a): """Get the numerator of ``a``.""" ZZ_I = self.get_ring() return ZZ_I.convert(a * self.denom(a)) def denom(self, a): """Get the denominator of ``a``.""" ZZ = self.dom.get_ring() QQ = self.dom ZZ_I = self.get_ring() denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y)) return ZZ_I(denom_ZZ, ZZ.zero) def from_GaussianIntegerRing(K1, a, K0): """Convert a ZZ_I element to QQ_I.""" return K1.new(a.x, a.y) def from_GaussianRationalField(K1, a, K0): """Convert a QQ_I element to QQ_I.""" return a
def test_evalf_euler(): x = symbols('x') R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') # log(1+x) p = HolonomicFunction((1 + x) * Dx**2 + Dx, x, 0, [0, 1]) # path taken is a straight line from 0 to 1, on the real axis r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] s = '0.699525841805253' # approx. equal to log(2) i.e. 0.693147180559945 assert sstr(p.evalf(r, method='Euler')[-1]) == s # path taken is a traingle 0-->1+i-->2 r = [0.1 + 0.1 * I] for i in range(9): r.append(r[-1] + 0.1 + 0.1 * I) for i in range(10): r.append(r[-1] + 0.1 - 0.1 * I) # close to the exact solution 1.09861228866811 # imaginary part also close to zero s = '1.07530466271334 - 0.0251200594793912*I' assert sstr(p.evalf(r, method='Euler')[-1]) == s # sin(x) p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) s = '0.905546532085401 - 6.93889390390723e-18*I' assert sstr(p.evalf(r, method='Euler')[-1]) == s # computing sin(pi/2) using this method # using a linear path from 0 to pi/2 r = [0.1] for i in range(14): r.append(r[-1] + 0.1) r.append(pi / 2) s = '1.08016557252834' # close to 1.0 (exact solution) assert sstr(p.evalf(r, method='Euler')[-1]) == s # trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2) # computing the same value sin(pi/2) using different path r = [0.1 * I] for i in range(9): r.append(r[-1] + 0.1 * I) for i in range(15): r.append(r[-1] + 0.1) r.append(pi / 2 + I) for i in range(10): r.append(r[-1] - 0.1 * I) # close to 1.0 s = '0.976882381836257 - 1.65557671738537e-16*I' assert sstr(p.evalf(r, method='Euler')[-1]) == s # cos(x) p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) # compute cos(pi) along 0-->pi r = [0.05] for i in range(61): r.append(r[-1] + 0.05) r.append(pi) # close to -1 (exact answer) s = '-1.08140824719196' assert sstr(p.evalf(r, method='Euler')[-1]) == s # a rectangular path (0 -> i -> 2+i -> 2) r = [0.1 * I] for i in range(9): r.append(r[-1] + 0.1 * I) for i in range(20): r.append(r[-1] + 0.1) for i in range(10): r.append(r[-1] - 0.1 * I) p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1]).evalf(r, method='Euler') s = '0.501421652861245 - 3.88578058618805e-16*I' assert sstr(p[-1]) == s