def test_gcdex_diophantine(): assert gcdex_diophantine( Poly(x ** 4 - 2 * x ** 3 - 6 * x ** 2 + 12 * x + 15), Poly(x ** 3 + x ** 2 - 4 * x - 4), Poly(x ** 2 - 1), ) == ( Poly((-(x ** 2) + 4 * x - 3) / 5), Poly((x ** 3 - 7 * x ** 2 + 16 * x - 10) / 5), ) assert gcdex_diophantine( Poly(x ** 3 + 6 * x + 7), Poly(x ** 2 + 3 * x + 2), Poly(x + 1) ) == (Poly(1 / 13, x, domain="QQ"), Poly(-1 / 13 * x + 3 / 13, x, domain="QQ"))
def weak_normalizer(a, d, DE, z=None): """ Weak normalization. Explanation =========== 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.expr.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 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 spde(a, b, c, n, DE): """ Rothstein's Special Polynomial Differential Equation algorithm. Explanation =========== 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 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) pow_a = 0 while True: if (n < 0) is True: if c.is_zero: return (zero, zero, 0, zero, beta) 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) alpha *= a beta += (a**pow_a)*r pow_a += 1
def prde_spde(a, b, Q, n, DE): """ Special Polynomial Differential Equation algorithm: Parametric Version. Given a derivation D on k[t], an integer n, and a, b, q1, ..., qm in k[t] with deg(a) > 0 and gcd(a, b) == 1, return (A, B, Q, R, n1), with Qq = [q1, ..., qm] and R = [r1, ..., rm], such that for any solution c1, ..., cm in Const(k) and q in k[t] of degree at most n of a*Dq + b*q == Sum(ci*gi, (i, 1, m)), p = (q - Sum(ci*ri, (i, 1, m)))/a has degree at most n1 and satisfies A*Dp + B*p == Sum(ci*qi, (i, 1, m)) """ R, Z = list(zip(*[gcdex_diophantine(b, a, qi) for qi in Q])) A = a B = b + derivation(a, DE) Qq = [zi - derivation(ri, DE) for ri, zi in zip(R, Z)] R = list(R) n1 = n - a.degree(DE.t) return (A, B, Qq, R, n1)
def prde_spde(a, b, Q, n, DE): """ Special Polynomial Differential Equation algorithm: Parametric Version. Given a derivation D on k[t], an integer n, and a, b, q1, ..., qm in k[t] with deg(a) > 0 and gcd(a, b) == 1, return (A, B, Q, R, n1), with Qq = [q1, ..., qm] and R = [r1, ..., rm], such that for any solution c1, ..., cm in Const(k) and q in k[t] of degree at most n of a*Dq + b*q == Sum(ci*gi, (i, 1, m)), p = (q - Sum(ci*ri, (i, 1, m)))/a has degree at most n1 and satisfies A*Dp + B*p == Sum(ci*qi, (i, 1, m)) """ R, Z = zip(*[gcdex_diophantine(b, a, qi) for qi in Q]) A = a B = b + derivation(a, DE) Qq = [zi - derivation(ri, DE) for ri, zi in zip(R, Z)] R = list(R) n1 = n - a.degree(DE.t) return (A, B, Qq, R, n1)
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. """ # TODO: Rewrite this non-recursively zero = Poly(0, DE.t) if n < 0: if c.is_zero: return (zero, zero, 0, zero, zero) 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, Poly(1, DE.t), zero) r, z = gcdex_diophantine(b, a, c) u = (a, b + derivation(a, DE), z - derivation(r, DE), n - a.degree(DE.t), DE) B, C, m, alpha, beta = spde(*u) return (B, C, m, a*alpha, a*beta + r)
def test_gcdex_diophantine(): assert gcdex_diophantine(Poly(x**4 - 2*x**3 - 6*x**2 + 12*x + 15), Poly(x**3 + x**2 - 4*x - 4), Poly(x**2 - 1)) == \ (Poly((-x**2 + 4*x - 3)/5), Poly((x**3 - 7*x**2 + 16*x - 10)/5))