def prde_no_cancel_b_large(b, Q, n, DE): """ Parametric Poly Risch Differential Equation - No cancellation: deg(b) large enough. Given a derivation D on k[t], n in ZZ, and b, q1, ..., qm in k[t] with b != 0 and either D == d/dt or deg(b) > max(0, deg(D) - 1), returns h1, ..., hr in k[r] and a matrix A with coefficients in Const(k) such that if c1, ..., cm in Const(k) and q in k[t] satisfy deg(q) <= n and Dq + b*Q == Sum(ci*qi, (i, 1, m)), then q = Sum(dj*hj, (j, 1, r)), where d1, ..., dr in Const(k) and A*Matrix([[c1, ..., cm, d1, ..., dr]]).T == 0. """ db = b.degree(DE.t) m = len(Q) H = [Poly(0, DE.t)]*m for N in range(n, -1, -1): # [n, ..., 0] for i in range(m): si = Q[i].nth(N + db)/b.LC() sitn = Poly(si*DE.t**N, DE.t) H[i] = H[i] + sitn Q[i] = Q[i] - derivation(sitn, DE) - b*sitn if all(qi.is_zero for qi in Q): dc = -1 M = zeros(0, 2) else: dc = max([qi.degree(t) for qi in Q]) M = Matrix(dc + 1, m, lambda i, j: Q[j].nth(i)) A, u = constant_system(M, zeros(dc + 1, 1), DE) c = eye(m) A = A.row_join(zeros(A.rows, m)).col_join(c.row_join(-c)) return H, A
def limited_integrate(fa, fd, G, DE): """ Solves the limited integration problem: f = Dv + Sum(ci*wi, (i, 1, n)) """ fa, fd = fa*Poly(1/fd.LC(), DE.t), fd.monic() A, B, h, N, g, V = limited_integrate_reduce(fa, fd, G, DE) V = [g] + V g = A.gcd(B) A, B, V = A.quo(g), B.quo(g), [via.cancel(vid*g, include=True) for via, vid in V] Q, M = prde_linear_constraints(A, B, V, DE) M, _ = constant_system(M, zeros(M.rows, 1), DE) l = M.nullspace() if M == Matrix() or len(l) > 1: # Continue with param_rischDE() raise NotImplementedError("param_rischDE() is required to solve this " "integral.") elif len(l) == 0: raise NonElementaryIntegralException elif len(l) == 1: # The c1 == 1. In this case, we can assume a normal Risch DE if l[0][0].is_zero: raise NonElementaryIntegralException else: l[0] *= 1/l[0][0] C = sum(Poly(i, DE.t)*q for (i, q) in zip(l[0], Q)) # Custom version of rischDE() that uses the already computed # denominator and degree bound from above. B, C, m, alpha, beta = spde(A, B, C, N, DE) y = solve_poly_rde(B, C, m, DE) return (alpha*y + beta, h), list(l[0][1:]) else: raise NotImplementedError
def no_cancel_b_large(b, c, n, DE): """ Poly Risch Differential Equation - No cancellation: deg(b) large enough. Given a derivation D on k[t], n either an integer or +oo, and b, c in k[t] with b != 0 and either D == d/dt or deg(b) > max(0, deg(D) - 1), either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) < n. """ q = Poly(0, DE.t) while not c.is_zero: m = c.degree(DE.t) - b.degree(DE.t) if not 0 <= m <= n: # n < 0 or m < 0 or m > n raise NonElementaryIntegralException p = Poly(c.as_poly(DE.t).LC() / b.as_poly(DE.t).LC() * DE.t**m, DE.t, expand=False) q = q + p n = m - 1 c = c - derivation(p, DE) - b * p return q
def _sqrt_symbolic_denest(a, b, r): """Given an expression, sqrt(a + b*sqrt(b)), return the denested expression or None. Algorithm: If r = ra + rb*sqrt(rr), try replacing sqrt(rr) in ``a`` with (y**2 - ra)/rb, and if the result is a quadratic, ca*y**2 + cb*y + cc, and (cb + b)**2 - 4*ca*cc is 0, then sqrt(a + b*sqrt(r)) can be rewritten as sqrt(ca*(sqrt(r) + (cb + b)/(2*ca))**2). Examples ======== >>> from diofant.simplify.sqrtdenest import _sqrt_symbolic_denest, sqrtdenest >>> from diofant import sqrt, Symbol >>> from diofant.abc import x >>> a, b, r = 16 - 2*sqrt(29), 2, -10*sqrt(29) + 55 >>> _sqrt_symbolic_denest(a, b, r) sqrt(-2*sqrt(29) + 11) + sqrt(5) If the expression is numeric, it will be simplified: >>> w = sqrt(sqrt(sqrt(3) + 1) + 1) + 1 + sqrt(2) >>> sqrtdenest(sqrt((w**2).expand())) 1 + sqrt(2) + sqrt(1 + sqrt(1 + sqrt(3))) Otherwise, it will only be simplified if assumptions allow: >>> w = w.subs(sqrt(3), sqrt(x + 3)) >>> sqrtdenest(sqrt((w**2).expand())) sqrt((sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2))**2) Notice that the argument of the sqrt is a square. If x is made positive then the sqrt of the square is resolved: >>> _.subs(x, Symbol('x', positive=True)) sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2) """ a, b, r = map(sympify, (a, b, r)) rval = _sqrt_match(r) if not rval: return ra, rb, rr = rval if rb: y = Dummy('y', positive=True) try: newa = Poly(a.subs(sqrt(rr), (y**2 - ra) / rb), y) except PolynomialError: return if newa.degree() == 2: ca, cb, cc = newa.all_coeffs() cb += b if _mexpand(cb**2 - 4 * ca * cc).equals(0): z = sqrt(ca * (sqrt(r) + cb / (2 * ca))**2) if z.is_number: z = _mexpand(Mul._from_args(z.as_content_primitive())) return z
def test_sympyissue_8438(): p = Poly([1, y, -2, -3], x).as_expr() roots = roots_cubic(Poly(p, x), x) z = -Rational(3, 2) - 7*I/2 # this will fail in code given in commit msg post = [r.subs({y: z}) for r in roots] assert set(post) == set(roots_cubic(Poly(p.subs({y: z}), x))) # /!\ if p is not made an expression, this is *very* slow assert all(p.subs({y: z, x: i}).evalf(2, chop=True) == 0 for i in post)
def cancel_exp(b, c, n, DE): """ Poly Risch Differential Equation - Cancellation: Hyperexponential case. Given a derivation D on k[t], n either an integer or +oo, b in k, and c in k[t] with Dt/t in k and b != 0, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n. """ from diofant.integrals.prde import parametric_log_deriv eta = DE.d.quo(Poly(DE.t, DE.t)).as_expr() with DecrementLevel(DE): etaa, etad = frac_in(eta, DE.t) ba, bd = frac_in(b, DE.t) A = parametric_log_deriv(ba, bd, etaa, etad, DE) if A is not None: a, m, z = A if a == 1: raise NotImplementedError("is_deriv_in_field() is required to " "solve this problem.") # if c*z*t**m == Dp for p in k<t> and q = p/(z*t**m) in k[t] and # deg(q) <= n: # return q # else: # raise NonElementaryIntegralException if c.is_zero: return c # return 0 if n < c.degree(DE.t): raise NonElementaryIntegralException q = Poly(0, DE.t) while not c.is_zero: m = c.degree(DE.t) if n < m: raise NonElementaryIntegralException # a1 = b + m*Dt/t a1 = b.as_expr() with DecrementLevel(DE): # TODO: Write a dummy function that does this idiom a1a, a1d = frac_in(a1, DE.t) a1a = a1a * etad + etaa * a1d * Poly(m, DE.t) a1d = a1d * etad a2a, a2d = frac_in(c.LC(), DE.t) sa, sd = rischDE(a1a, a1d, a2a, a2d, DE) stm = Poly(sa.as_expr() / sd.as_expr() * DE.t**m, DE.t, expand=False) q += stm n = m - 1 c -= b * stm + derivation(stm, DE) # deg(c) becomes smaller return q
def test_sympyissue_8289(): roots = (Poly(x**2 + 2) * Poly(x**4 + 2)).all_roots() assert roots == _nsort(roots) roots = Poly(x**6 + 3 * x**3 + 2, x).all_roots() assert roots == _nsort(roots) roots = Poly(x**6 - x + 1).all_roots() assert roots == _nsort(roots) # all imaginary roots roots = Poly(x**4 + 4 * x**2 + 4, x).all_roots() assert roots == _nsort(roots)
def test_roots_quartic(): assert roots_quartic(Poly(x**4, x)) == [0, 0, 0, 0] assert roots_quartic(Poly(x**4 + x**3, x)) in [ [-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1] ] assert roots_quartic(Poly(x**4 - x**3, x)) in [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ] lhs = roots_quartic(Poly(x**4 + x, x)) rhs = [Rational(1, 2) + I*sqrt(3)/2, Rational(1, 2) - I*sqrt(3)/2, 0, -1] assert sorted(lhs, key=hash) == sorted(rhs, key=hash) # test of all branches of roots quartic for i, (a, b, c, d) in enumerate([(1, 2, 3, 0), (3, -7, -9, 9), (1, 2, 3, 4), (1, 2, 3, 4), (-7, -3, 3, -6), (-3, 5, -6, -4), (6, -5, -10, -3)]): if i == 2: c = -a*(a**2/Integer(8) - b/Integer(2)) elif i == 3: d = a*(a*(3*a**2/Integer(256) - b/Integer(16)) + c/Integer(4)) eq = x**4 + a*x**3 + b*x**2 + c*x + d ans = roots_quartic(Poly(eq, x)) assert all(eq.subs({x: ai}).evalf(chop=True) == 0 for ai in ans) # not all symbolic quartics are unresolvable eq = Poly(q*x + q/4 + x**4 + x**3 + 2*x**2 - Rational(1, 3), x) sol = roots_quartic(eq) assert all(verify_numerically(eq.subs({x: i}), 0) for i in sol) z = symbols('z', negative=True) eq = x**4 + 2*x**3 + 3*x**2 + x*(z + 11) + 5 zans = roots_quartic(Poly(eq, x)) assert all(verify_numerically(eq.subs({x: i, z: -1}), 0) for i in zans) # but some are (see also issue sympy/sympy#4989) # it's ok if the solution is not Piecewise, but the tests below should pass eq = Poly(y*x**4 + x**3 - x + z, x) ans = roots_quartic(eq) assert all(type(i) == Piecewise for i in ans) reps = ({y: -Rational(1, 3), z: -Rational(1, 4)}, # 4 real {y: -Rational(1, 3), z: -Rational(1, 2)}, # 2 real {y: -Rational(1, 3), z: -2}) # 0 real for rep in reps: sol = roots_quartic(Poly(eq.subs(rep), x)) assert all(verify_numerically(w.subs(rep) - s, 0) for w, s in zip(ans, sol))
def test_roots_binomial(): assert roots_binomial(Poly(5 * x, x)) == [0] assert roots_binomial(Poly(5 * x**4, x)) == [0, 0, 0, 0] assert roots_binomial(Poly(5 * x + 2, x)) == [-Rational(2, 5)] A = 10**Rational(3, 4) / 10 assert roots_binomial(Poly(5*x**4 + 2, x)) == \ [-A - A*I, -A + A*I, A - A*I, A + A*I] a1 = Symbol('a1', nonnegative=True) b1 = Symbol('b1', nonnegative=True) r0 = roots_quadratic(Poly(a1 * x**2 + b1, x)) r1 = roots_binomial(Poly(a1 * x**2 + b1, x)) assert powsimp(r0[0]) == powsimp(r1[0]) assert powsimp(r0[1]) == powsimp(r1[1]) for a, b, s, n in itertools.product((1, 2), (1, 2), (-1, 1), (2, 3, 4, 5)): if a == b and a != 1: # a == b == 1 is sufficient continue p = Poly(a * x**n + s * b) ans = roots_binomial(p) assert ans == _nsort(ans) # issue sympy/sympy#8813 assert roots(Poly(2 * x**3 - 16 * y**3, x)) == { 2 * y * (-Rational(1, 2) - sqrt(3) * I / 2): 1, 2 * y: 1, 2 * y * (-Rational(1, 2) + sqrt(3) * I / 2): 1 }
def test_nroots2(): p = Poly(x**5 + 3 * x + 1, x) roots = p.nroots(n=3) # The order of roots matters. The roots are ordered by their real # components (if they agree, then by their imaginary components), # with real roots appearing first. assert [str(r) for r in roots] == \ ['-0.332', '-0.839 - 0.944*I', '-0.839 + 0.944*I', '1.01 - 0.937*I', '1.01 + 0.937*I'] roots = p.nroots(n=5) assert [str(r) for r in roots] == \ ['-0.33199', '-0.83907 - 0.94385*I', '-0.83907 + 0.94385*I', '1.0051 - 0.93726*I', '1.0051 + 0.93726*I']
def test_nroots2(): p = Poly(x**5 + 3*x + 1, x) roots = p.nroots(n=3) # The order of roots matters. The roots are ordered by their real # components (if they agree, then by their imaginary components), # with real roots appearing first. assert [str(r) for r in roots] == \ ['-0.332', '-0.839 - 0.944*I', '-0.839 + 0.944*I', '1.01 - 0.937*I', '1.01 + 0.937*I'] roots = p.nroots(n=5) assert [str(r) for r in roots] == \ ['-0.33199', '-0.83907 - 0.94385*I', '-0.83907 + 0.94385*I', '1.0051 - 0.93726*I', '1.0051 + 0.93726*I']
def test_roots_cubic(): assert roots_cubic(Poly(2 * x**3, x)) == [0, 0, 0] assert roots_cubic(Poly(x**3 - 3 * x**2 + 3 * x - 1, x)) == [1, 1, 1] assert roots_cubic(Poly(x**3 + 1, x)) == \ [-1, Rational(1, 2) - I*sqrt(3)/2, Rational(1, 2) + I*sqrt(3)/2] assert roots_cubic(Poly(2*x**3 - 3*x**2 - 3*x - 1, x))[0] == \ Rational(1, 2) + cbrt(3)/2 + 3**Rational(2, 3)/2 eq = -x**3 + 2 * x**2 + 3 * x - 2 assert roots(eq, trig=True, multiple=True) == \ roots_cubic(Poly(eq, x), trig=True) == [ Rational(2, 3) + 2*sqrt(13)*cos(acos(8*sqrt(13)/169)/3)/3, -2*sqrt(13)*sin(-acos(8*sqrt(13)/169)/3 + pi/6)/3 + Rational(2, 3), -2*sqrt(13)*cos(-acos(8*sqrt(13)/169)/3 + pi/3)/3 + Rational(2, 3), ]
def prde_normal_denom(fa, fd, G, DE): """ Parametric Risch Differential Equation - Normal part of the denominator. Given a derivation D on k[t] and f, g1, ..., gm in k(t) with f weakly normalized with respect to t, return the tuple (a, b, G, h) such that a, h in k[t], b in k<t>, G = [g1, ..., gm] in k(t)^m, and for any solution c1, ..., cm in Const(k) and y in k(t) of Dy + f*y == Sum(ci*gi, (i, 1, m)), q == y*h in k<t> satisfies a*Dq + b*q == Sum(ci*Gi, (i, 1, m)). """ dn, ds = splitfactor(fd, DE) Gas, Gds = list(zip(*G)) gd = reduce(lambda i, j: i.lcm(j), Gds, Poly(1, DE.t)) en, es = splitfactor(gd, DE) p = dn.gcd(en) h = en.gcd(en.diff(DE.t)).quo(p.gcd(p.diff(DE.t))) a = dn*h c = a*h ba = a*fa - dn*derivation(h, DE)*fd ba, bd = ba.cancel(fd, include=True) G = [(c*A).cancel(D, include=True) for A, D in G] return a, (ba, bd), G, h
def prde_linear_constraints(a, b, G, DE): """ Parametric Risch Differential Equation - Generate linear constraints on the constants. Given a derivation D on k[t], a, b, in k[t] with gcd(a, b) == 1, and G = [g1, ..., gm] in k(t)^m, return Q = [q1, ..., qm] in k[t]^m and a matrix M with entries in k(t) such that for any solution c1, ..., cm in Const(k) and p in k[t] of a*Dp + b*p == Sum(ci*gi, (i, 1, m)), (c1, ..., cm) is a solution of Mx == 0, and p and the ci satisfy a*Dp + b*p == Sum(ci*qi, (i, 1, m)). Because M has entries in k(t), and because Matrix doesn't play well with Poly, M will be a Matrix of Basic expressions. """ m = len(G) Gns, Gds = list(zip(*G)) d = reduce(lambda i, j: i.lcm(j), Gds) d = Poly(d, field=True) Q = [(ga*(d).quo(gd)).div(d) for ga, gd in G] if not all([ri.is_zero for _, ri in Q]): N = max([ri.degree(DE.t) for _, ri in Q]) M = Matrix(N + 1, m, lambda i, j: Q[j][1].nth(i)) else: M = Matrix() # No constraints, return the empty matrix. qs, _ = list(zip(*Q)) return qs, M
def cancel_primitive(b, c, n, DE): """ Poly Risch Differential Equation - Cancellation: Primitive case. Given a derivation D on k[t], n either an integer or +oo, b in k, and c in k[t] with Dt in k and b != 0, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n. """ from diofant.integrals.prde import is_log_deriv_k_t_radical_in_field with DecrementLevel(DE): ba, bd = frac_in(b, DE.t) A = is_log_deriv_k_t_radical_in_field(ba, bd, DE) if A is not None: n, z = A if n == 1: # b == Dz/z raise NotImplementedError("is_deriv_in_field() is required to " " solve this problem.") # if z*c == Dp for p in k[t] and deg(p) <= n: # return p/z # else: # raise NonElementaryIntegralException if c.is_zero: return c # return 0 if n < c.degree(DE.t): raise NonElementaryIntegralException q = Poly(0, DE.t) while not c.is_zero: m = c.degree(DE.t) if n < m: raise NonElementaryIntegralException with DecrementLevel(DE): a2a, a2d = frac_in(c.LC(), DE.t) sa, sd = rischDE(ba, bd, a2a, a2d, DE) stm = Poly(sa.as_expr() / sd.as_expr() * DE.t**m, DE.t, expand=False) q += stm n = m - 1 c -= b * stm + derivation(stm, DE) return q
def test_roots_quadratic(): assert roots_quadratic(Poly(2*x**2, x)) == [0, 0] assert roots_quadratic(Poly(2*x**2 + 3*x, x)) == [-Rational(3, 2), 0] assert roots_quadratic(Poly(2*x**2 + 3, x)) == [-I*sqrt(6)/2, I*sqrt(6)/2] assert roots_quadratic(Poly(2*x**2 + 4*x + 3, x)) == [-1 - I*sqrt(2)/2, -1 + I*sqrt(2)/2] f = x**2 + (2*a*e + 2*c*e)/(a - c)*x + (d - b + a*e**2 - c*e**2)/(a - c) assert (roots_quadratic(Poly(f, x)) == [-e*(a + c)/(a - c) - sqrt((a*b + 4*a*c*e**2 - a*d - b*c + c*d)/(a - c)**2), -e*(a + c)/(a - c) + sqrt((a*b + 4*a*c*e**2 - a*d - b*c + c*d)/(a - c)**2)]) # check for simplification f = Poly(y*x**2 - 2*x - 2*y, x) assert roots_quadratic(f) == [-sqrt((2*y**2 + 1)/y**2) + 1/y, sqrt((2*y**2 + 1)/y**2) + 1/y] f = Poly(x**2 + (-y**2 - 2)*x + y**2 + 1, x) assert roots_quadratic(f) == [y**2/2 - sqrt(y**4)/2 + 1, y**2/2 + sqrt(y**4)/2 + 1] f = Poly(sqrt(2)*x**2 - 1, x) r = roots_quadratic(f) assert r == _nsort(r) # issue sympy/sympy#8255 f = Poly(-24*x**2 - 180*x + 264) assert [w.evalf(2) for w in f.all_roots(radicals=True)] == \ [w.evalf(2) for w in f.all_roots(radicals=False)] for _a, _b, _c in itertools.product((-2, 2), (-2, 2), (0, -1)): f = Poly(_a*x**2 + _b*x + _c) roots = roots_quadratic(f) assert roots == _nsort(roots)
def no_cancel_b_small(b, c, n, DE): """ Poly Risch Differential Equation - No cancellation: deg(b) small enough. Given a derivation D on k[t], n either an integer or +oo, and b, c in k[t] with deg(b) < deg(D) - 1 and either D == d/dt or deg(D) >= 2, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n, or the tuple (h, b0, c0) such that h in k[t], b0, c0, in k, and for any solution q in k[t] of degree at most n of Dq + bq == c, y == q - h is a solution in k of Dy + b0*y == c0. """ q = Poly(0, DE.t) while not c.is_zero: if n == 0: m = 0 else: m = c.degree(DE.t) - DE.d.degree(DE.t) + 1 if not 0 <= m <= n: # n < 0 or m < 0 or m > n raise NonElementaryIntegralException if m > 0: p = Poly(c.as_poly(DE.t).LC() / (m * DE.d.as_poly(DE.t).LC()) * DE.t**m, DE.t, expand=False) else: if b.degree(DE.t) != c.degree(DE.t): raise NonElementaryIntegralException if b.degree(DE.t) == 0: return (q, b.as_poly(DE.T[DE.level - 1]), c.as_poly(DE.T[DE.level - 1])) p = Poly(c.as_poly(DE.t).LC() / b.as_poly(DE.t).LC(), DE.t, expand=False) q = q + p n = m - 1 c = c - derivation(p, DE) - b * p return q
def spde(a, b, c, n, DE): """ Rothstein's Special Polynomial Differential Equation algorithm. Given a derivation D on k[t], an integer n and a, b, c in k[t] with a != 0, either raise NonElementaryIntegralException, in which case the equation a*Dq + b*q == c has no solution of degree at most n in k[t], or return the tuple (B, C, m, alpha, beta) such that B, C, alpha, beta in k[t], m in ZZ, and any solution q in k[t] of degree at most n of a*Dq + b*q == c must be of the form q == alpha*h + beta, where h in k[t], deg(h) <= m, and Dh + B*h == C. This constitutes step 4 of the outline given in the rde.py docstring. """ zero = Poly(0, DE.t) alpha = Poly(1, DE.t) beta = Poly(0, DE.t) while True: if c.is_zero: return zero, zero, 0, zero, beta # -1 is more to the point if (n < 0) is True: raise NonElementaryIntegralException g = a.gcd(b) if not c.rem(g).is_zero: # g does not divide c raise NonElementaryIntegralException a, b, c = a.quo(g), b.quo(g), c.quo(g) if a.degree(DE.t) == 0: b = b.to_field().quo(a) c = c.to_field().quo(a) return b, c, n, alpha, beta r, z = gcdex_diophantine(b, a, c) b += derivation(a, DE) c = z - derivation(r, DE) n -= a.degree(DE.t) beta += alpha * r alpha *= a
def test_root_factors(): assert root_factors(Poly(1, x)) == [Poly(1, x)] assert root_factors(Poly(x, x)) == [Poly(x, x)] assert root_factors(x**2 - 1, x) == [x + 1, x - 1] assert root_factors(x**2 - y, x) == [x - sqrt(y), x + sqrt(y)] assert root_factors((x**4 - 1)**2) == \ [x + 1, x + 1, x - 1, x - 1, x - I, x - I, x + I, x + I] assert root_factors(Poly(x**4 - 1, x), filter='Z') == \ [Poly(x + 1, x), Poly(x - 1, x), Poly(x**2 + 1, x)] assert root_factors(8*x**2 + 12*x**4 + 6*x**6 + x**8, x, filter='Q') == \ [x, x, x**6 + 6*x**4 + 12*x**2 + 8] pytest.raises(ValueError, lambda: root_factors(Poly(x * y)))
def no_cancel_equal(b, c, n, DE): """ Poly Risch Differential Equation - No cancellation: deg(b) == deg(D) - 1 Given a derivation D on k[t] with deg(D) >= 2, n either an integer or +oo, and b, c in k[t] with deg(b) == deg(D) - 1, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n, or the tuple (h, m, C) such that h in k[t], m in ZZ, and C in k[t], and for any solution q in k[t] of degree at most n of Dq + b*q == c, y == q - h is a solution in k[t] of degree at most m of Dy + b*y == C. """ q = Poly(0, DE.t) lc = cancel(-b.as_poly(DE.t).LC() / DE.d.as_poly(DE.t).LC()) if lc.is_Integer and lc.is_positive: M = lc else: M = -1 while not c.is_zero: m = max(M, c.degree(DE.t) - DE.d.degree(DE.t) + 1) if not 0 <= m <= n: # n < 0 or m < 0 or m > n raise NonElementaryIntegralException u = cancel(m * DE.d.as_poly(DE.t).LC() + b.as_poly(DE.t).LC()) if u.is_zero: return q, m, c if m > 0: p = Poly(c.as_poly(DE.t).LC() / u * DE.t**m, DE.t, expand=False) else: if c.degree(DE.t) != DE.d.degree(DE.t) - 1: raise NonElementaryIntegralException else: p = c.as_poly(DE.t).LC() / b.as_poly(DE.t).LC() q = q + p n = m - 1 c = c - derivation(p, DE) - b * p return q
def test_roots_cubic(): assert roots_cubic(Poly(2 * x**3, x)) == [0, 0, 0] assert roots_cubic(Poly(x**3 - 3 * x**2 + 3 * x - 1, x)) == [1, 1, 1] assert roots_cubic(Poly(x**3 + 1, x)) == \ [-1, Rational(1, 2) - I*sqrt(3)/2, Rational(1, 2) + I*sqrt(3)/2] assert roots_cubic(Poly(2*x**3 - 3*x**2 - 3*x - 1, x))[0] == \ Rational(1, 2) + cbrt(3)/2 + 3**Rational(2, 3)/2 eq = -x**3 + 2 * x**2 + 3 * x - 2 assert roots(eq, trig=True, multiple=True) == \ roots_cubic(Poly(eq, x), trig=True) == [ Rational(2, 3) + 2*sqrt(13)*cos(acos(8*sqrt(13)/169)/3)/3, -2*sqrt(13)*sin(-acos(8*sqrt(13)/169)/3 + pi/6)/3 + Rational(2, 3), -2*sqrt(13)*cos(-acos(8*sqrt(13)/169)/3 + pi/3)/3 + Rational(2, 3), ] res = roots_cubic(Poly(x**3 + 2 * a / 27, x)) assert res == [ -root(a + sqrt(a**2), 3) / 3, Mul(Rational(-1, 3), Rational(-1, 2) + sqrt(3) * I / 2, root(a + sqrt(a**2), 3), evaluate=False), Mul(Rational(-1, 3), Rational(-1, 2) - sqrt(3) * I / 2, root(a + sqrt(a**2), 3), evaluate=False) ]
def prde_no_cancel_b_small(b, Q, n, DE): """ Parametric Poly Risch Differential Equation - No cancellation: deg(b) small enough. Given a derivation D on k[t], n in ZZ, and b, q1, ..., qm in k[t] with deg(b) < deg(D) - 1 and either D == d/dt or deg(D) >= 2, returns h1, ..., hr in k[t] and a matrix A with coefficients in Const(k) such that if c1, ..., cm in Const(k) and q in k[t] satisfy deg(q) <= n and Dq + b*q == Sum(ci*qi, (i, 1, m)) then q = Sum(dj*hj, (j, 1, r)) where d1, ..., dr in Const(k) and A*Matrix([[c1, ..., cm, d1, ..., dr]]).T == 0. """ m = len(Q) H = [Poly(0, DE.t)]*m for N in range(n, 0, -1): # [n, ..., 1] for i in range(m): si = Q[i].nth(N + DE.d.degree(DE.t) - 1)/(N*DE.d.LC()) sitn = Poly(si*DE.t**N, DE.t) H[i] = H[i] + sitn Q[i] = Q[i] - derivation(sitn, DE) - b*sitn if b.degree(DE.t) > 0: for i in range(m): si = Poly(Q[i].nth(b.degree(DE.t))/b.LC(), DE.t) H[i] = H[i] + si Q[i] = Q[i] - derivation(si, DE) - b*si if all(qi.is_zero for qi in Q): dc = -1 M = Matrix() else: dc = max([qi.degree(DE.t) for qi in Q]) M = Matrix(dc + 1, m, lambda i, j: Q[j].nth(i)) A, u = constant_system(M, zeros(dc + 1, 1), DE) c = eye(m) A = A.row_join(zeros(A.rows, m)).col_join(c.row_join(-c)) return H, A else: # TODO: implement this (requires recursive param_rischDE() call) raise NotImplementedError
def order_at(a, p, t): """ Computes the order of a at p, with respect to t. For a, p in k[t], the order of a at p is defined as nu_p(a) = max({n in Z+ such that p**n|a}), where a != 0. If a == 0, nu_p(a) = +oo. To compute the order at a rational function, a/b, use the fact that nu_p(a/b) == nu_p(a) - nu_p(b). """ if a.is_zero: return oo if p == Poly(t, t): return a.as_poly(t).ET()[0][0] # Uses binary search for calculating the power. power_list collects the tuples # (p^k,k) where each k is some power of 2. After deciding the largest k # such that k is power of 2 and p^k|a the loop iteratively calculates # the actual power. power_list = [] p1 = p r = a.rem(p1) tracks_power = 1 while r.is_zero: power_list.append((p1, tracks_power)) p1 = p1 * p1 tracks_power *= 2 r = a.rem(p1) n = 0 product = Poly(1, t) while len(power_list) != 0: final = power_list.pop() productf = product * final[0] r = a.rem(productf) if r.is_zero: n += final[1] product = productf return n
def test_printing(): f, g = [dmp_normal([], 0, EX)] * 2 e = PolynomialDivisionFailed(f, g, EX) assert str(e)[:57] == ("couldn't reduce degree in a polynomial " "division algorithm") assert str(e)[-140:][:57] == ("You may want to use a different " "simplification algorithm.") f, g = [dmp_normal([], 0, RR)] * 2 e = PolynomialDivisionFailed(f, g, RR) assert str(e)[-139:][:74] == ("Your working precision or tolerance of " "computations may be set improperly.") f, g = [dmp_normal([], 0, ZZ)] * 2 e = PolynomialDivisionFailed(f, g, ZZ) assert str(e)[-168:][:80] == ( "Zero detection is guaranteed in this " "coefficient domain. This may indicate a bug") e = OperationNotSupported(Poly(x), 'spam') assert str(e).find('spam') >= 0 assert str(e).find('operation not supported') >= 0 exc = PolificationFailed(1, x, x**2) assert str(exc).find("can't construct a polynomial from x") >= 0 exc = PolificationFailed(1, [x], [x**2], True) assert str(exc).find("can't construct polynomials from x") >= 0 e = ComputationFailed('LT', 1, exc) assert str(e).find('failed without generators') >= 0 assert str(e).find('x**2') >= 0 e = ExactQuotientFailed(Poly(x), Poly(x**2)) assert str(e).find('does not divide') >= 0 assert str(e).find('x**2') >= 0 assert str(e).find('in ZZ') < 0 e = ExactQuotientFailed(Poly(x), Poly(x**2), ZZ) assert str(e).find('in ZZ') >= 0
def test_sympyissue_8438(): p = Poly([1, y, -2, -3], x).as_expr() roots = roots_cubic(Poly(p, x), x) z = -Rational(3, 2) - 7 * I / 2 # this will fail in code given in commit msg post = [r.subs({y: z}) for r in roots] assert set(post) == set(roots_cubic(Poly(p.subs({y: z}), x))) # /!\ if p is not made an expression, this is *very* slow assert all(p.subs({y: z, x: i}).evalf(2, chop=True) == 0 for i in post)
def weak_normalizer(a, d, DE, z=None): """ Weak normalization. Given a derivation D on k[t] and f == a/d in k(t), return q in k[t] such that f - Dq/q is weakly normalized with respect to t. f in k(t) is said to be "weakly normalized" with respect to t if residue_p(f) is not a positive integer for any normal irreducible p in k[t] such that f is in R_p (Definition 6.1.1). If f has an elementary integral, this is equivalent to no logarithm of integral(f) whose argument depends on t has a positive integer coefficient, where the arguments of the logarithms not in k(t) are in k[t]. Returns (q, f - Dq/q) """ z = z or Dummy('z') dn, ds = splitfactor(d, DE) # Compute d1, where dn == d1*d2**2*...*dn**n is a square-free # factorization of d. g = gcd(dn, dn.diff(DE.t)) d_sqf_part = dn.quo(g) d1 = d_sqf_part.quo(gcd(d_sqf_part, g)) a1, b = gcdex_diophantine( d.quo(d1).as_poly(DE.t), d1.as_poly(DE.t), a.as_poly(DE.t)) r = (a - Poly(z, DE.t) * derivation(d1, DE)).as_poly(DE.t).resultant( d1.as_poly(DE.t)) r = Poly(r, z) if not r.has(z): return Poly(1, DE.t), (a, d) N = [i for i in r.real_roots() if i in ZZ and i > 0] q = reduce(mul, [gcd(a - Poly(n, DE.t) * derivation(d1, DE), d1) for n in N], Poly(1, DE.t)) dq = derivation(q, DE) sn = q * a - d * dq sd = q * d sn, sd = sn.cancel(sd, include=True) return q, (sn, sd)
def solve_biquadratic(f, g, opt): """Solve a system of two bivariate quadratic polynomial equations. Examples ======== >>> from diofant.polys import Options, Poly >>> from diofant.abc import x, y >>> from diofant.solvers.polysys import solve_biquadratic >>> NewOption = Options((x, y), {'domain': 'ZZ'}) >>> a = Poly(y**2 - 4 + x, y, x, domain='ZZ') >>> b = Poly(y*2 + 3*x - 7, y, x, domain='ZZ') >>> solve_biquadratic(a, b, NewOption) [(1/3, 3), (41/27, 11/9)] >>> a = Poly(y + x**2 - 3, y, x, domain='ZZ') >>> b = Poly(-y + x - 4, y, x, domain='ZZ') >>> solve_biquadratic(a, b, NewOption) [(-sqrt(29)/2 + 7/2, -sqrt(29)/2 - 1/2), (sqrt(29)/2 + 7/2, -1/2 + \ sqrt(29)/2)] """ G = groebner([f, g]) if len(G) == 1 and G[0].is_ground: return if len(G) != 2: raise SolveFailed p, q = G x, y = opt.gens p = Poly(p, x, expand=False) q = q.ltrim(-1) p_roots = [ rcollect(expr, y) for expr in roots(p).keys() ] q_roots = list(roots(q).keys()) solutions = [] for q_root in q_roots: for p_root in p_roots: solution = (p_root.subs(y, q_root), q_root) solutions.append(solution) return sorted(solutions, key=default_sort_key)
def test_sympyissue_8285(): roots = (Poly(4 * x**8 - 1, x) * Poly(x**2 + 1)).all_roots() assert roots == _nsort(roots) f = Poly(x**4 + 5 * x**2 + 6, x) ro = [RootOf(f, i) for i in range(4)] roots = Poly(x**4 + 5 * x**2 + 6, x).all_roots() assert roots == ro assert roots == _nsort(roots) # more than 2 complex roots from which to identify the # imaginary ones roots = Poly(2 * x**8 - 1).all_roots() assert roots == _nsort(roots) assert len(Poly(2 * x**10 - 1).all_roots()) == 10 # doesn't fail
def test_roots1(): assert roots(1) == {} assert roots(1, multiple=True) == [] q = Symbol('q', real=True) assert roots(x**3 - q, x) == { cbrt(q): 1, -cbrt(q) / 2 - sqrt(3) * I * cbrt(q) / 2: 1, -cbrt(q) / 2 + sqrt(3) * I * cbrt(q) / 2: 1 } assert roots_cubic(Poly(x**3 - 1)) == [ 1, Rational(-1, 2) + sqrt(3) * I / 2, Rational(-1, 2) - sqrt(3) * I / 2 ] assert roots([1, x, y]) == { -x / 2 - sqrt(x**2 - 4 * y) / 2: 1, -x / 2 + sqrt(x**2 - 4 * y) / 2: 1 } pytest.raises(ValueError, lambda: roots([1, x, y], z))
def apart_undetermined_coeffs(P, Q): """Partial fractions via method of undetermined coefficients. """ X = numbered_symbols(cls=Dummy) partial, symbols = [], [] _, factors = Q.factor_list() for f, k in factors: n, q = f.degree(), Q for i in range(1, k + 1): coeffs, q = take(X, n), q.quo(f) partial.append((coeffs, q, f, i)) symbols.extend(coeffs) dom = Q.get_domain().inject(*symbols) F = Poly(0, Q.gen, domain=dom) for i, (coeffs, q, f, k) in enumerate(partial): h = Poly(coeffs, Q.gen, domain=dom) partial[i] = (h, f, k) q = q.set_domain(dom) F += h * q system, result = [], Integer(0) for (k, ), coeff in F.terms(): system.append(coeff - P.nth(k)) from diofant.solvers import solve solution = solve(system, symbols) for h, f, k in partial: h = h.as_expr().subs(solution) result += h / f.as_expr()**k return result
def dispersionset(p, q=None, *gens, **args): r"""Compute the *dispersion set* of two polynomials. For two polynomials `f(x)` and `g(x)` with `\deg f > 0` and `\deg g > 0` the dispersion set `\operatorname{J}(f, g)` is defined as: .. math:: \operatorname{J}(f, g) & := \{a \in \mathbb{N}_0 | \gcd(f(x), g(x+a)) \neq 1\} \\ & = \{a \in \mathbb{N}_0 | \deg \gcd(f(x), g(x+a)) \geq 1\} For a single polynomial one defines `\operatorname{J}(f) := \operatorname{J}(f, f)`. Examples ======== >>> from diofant import poly >>> from diofant.abc import x Dispersion set and dispersion of a simple polynomial: >>> fp = poly((x - 3)*(x + 3), x) >>> sorted(dispersionset(fp)) [0, 6] >>> dispersion(fp) 6 Note that the definition of the dispersion is not symmetric: >>> fp = poly(x**4 - 3*x**2 + 1, x) >>> gp = fp.shift(-3) >>> sorted(dispersionset(fp, gp)) [2, 3, 4] >>> dispersion(fp, gp) 4 >>> sorted(dispersionset(gp, fp)) [] >>> dispersion(gp, fp) -oo Computing the dispersion also works over field extensions: >>> from diofant import sqrt >>> fp = poly(x**2 + sqrt(5)*x - 1, x, domain='QQ<sqrt(5)>') >>> gp = poly(x**2 + (2 + sqrt(5))*x + sqrt(5), x, domain='QQ<sqrt(5)>') >>> sorted(dispersionset(fp, gp)) [2] >>> sorted(dispersionset(gp, fp)) [1, 4] We can even perform the computations for polynomials having symbolic coefficients: >>> from diofant.abc import a >>> fp = poly(4*x**4 + (4*a + 8)*x**3 + (a**2 + 6*a + 4)*x**2 + (a**2 + 2*a)*x, x) >>> sorted(dispersionset(fp)) [0, 1] See Also ======== diofant.polys.dispersion.dispersion References ========== .. [1] [ManWright94]_ .. [2] [Koepf98]_ .. [3] [Abramov71]_ .. [4] [Man93]_ """ # Check for valid input same = False if q is not None else True if same: q = p p = Poly(p, *gens, **args) q = Poly(q, *gens, **args) if not p.is_univariate or not q.is_univariate: raise ValueError("Polynomials need to be univariate") # The generator if not p.gen == q.gen: raise ValueError("Polynomials must have the same generator") gen = p.gen # We define the dispersion of constant polynomials to be zero if p.degree() < 1 or q.degree() < 1: return {0} # Factor p and q over the rationals fp = p.factor_list() fq = q.factor_list() if not same else fp # Iterate over all pairs of factors J = set() for s, unused in fp[1]: for t, unused in fq[1]: m = s.degree() n = t.degree() if n != m: continue an = s.LC() bn = t.LC() if not (an - bn).is_zero: continue # Note that the roles of `s` and `t` below are switched # w.r.t. the original paper. This is for consistency # with the description in the book of W. Koepf. anm1 = s.coeff_monomial(gen**(m - 1)) bnm1 = t.coeff_monomial(gen**(n - 1)) alpha = (anm1 - bnm1) / (n * bn) if not alpha.is_Integer: continue if alpha < 0 or alpha in J: continue if n > 1 and not (s - t.shift(alpha)).is_zero: continue J.add(alpha) return J
def test_RootOf(): p = Poly(x**3 + y*x + 1, x) assert mcode(RootOf(p, 0)) == 'Root[#^3 + #*y + 1 &, 1]'
def test_sympyissue_14291(): p = Poly(((x - 1)**2 + 1) * ((x - 1)**2 + 2) * (x - 1)) assert set( p.all_roots()) == {1, 1 - I, 1 + I, 1 - I * sqrt(2), 1 + sqrt(2) * I}
def test_sympyissue_7724(): e = x**4 * I + x**2 + I r1, r2 = roots(e, x), Poly(e, x).all_roots() assert len(r1) == 4 assert {_.evalf() for _ in r1} == {_.evalf() for _ in r2}
def test_sympyissue_14291(): p = Poly(((x - 1)**2 + 1)*((x - 1)**2 + 2)*(x - 1)) assert set(p.all_roots()) == {1, 1 - I, 1 + I, 1 - I*sqrt(2), 1 + sqrt(2)*I}