rde.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. """
  2. Algorithms for solving the Risch differential equation.
  3. Given a differential field K of characteristic 0 that is a simple
  4. monomial extension of a base field k and f, g in K, the Risch
  5. Differential Equation problem is to decide if there exist y in K such
  6. that Dy + f*y == g and to find one if there are some. If t is a
  7. monomial over k and the coefficients of f and g are in k(t), then y is
  8. in k(t), and the outline of the algorithm here is given as:
  9. 1. Compute the normal part n of the denominator of y. The problem is
  10. then reduced to finding y' in k<t>, where y == y'/n.
  11. 2. Compute the special part s of the denominator of y. The problem is
  12. then reduced to finding y'' in k[t], where y == y''/(n*s)
  13. 3. Bound the degree of y''.
  14. 4. Reduce the equation Dy + f*y == g to a similar equation with f, g in
  15. k[t].
  16. 5. Find the solutions in k[t] of bounded degree of the reduced equation.
  17. See Chapter 6 of "Symbolic Integration I: Transcendental Functions" by
  18. Manuel Bronstein. See also the docstring of risch.py.
  19. """
  20. from operator import mul
  21. from functools import reduce
  22. from sympy.core import oo
  23. from sympy.core.symbol import Dummy
  24. from sympy.polys import Poly, gcd, ZZ, cancel
  25. from sympy.functions.elementary.complexes import (im, re)
  26. from sympy.functions.elementary.miscellaneous import sqrt
  27. from sympy.integrals.risch import (gcdex_diophantine, frac_in, derivation,
  28. splitfactor, NonElementaryIntegralException, DecrementLevel, recognize_log_derivative)
  29. # TODO: Add messages to NonElementaryIntegralException errors
  30. def order_at(a, p, t):
  31. """
  32. Computes the order of a at p, with respect to t.
  33. Explanation
  34. ===========
  35. For a, p in k[t], the order of a at p is defined as nu_p(a) = max({n
  36. in Z+ such that p**n|a}), where a != 0. If a == 0, nu_p(a) = +oo.
  37. To compute the order at a rational function, a/b, use the fact that
  38. nu_p(a/b) == nu_p(a) - nu_p(b).
  39. """
  40. if a.is_zero:
  41. return oo
  42. if p == Poly(t, t):
  43. return a.as_poly(t).ET()[0][0]
  44. # Uses binary search for calculating the power. power_list collects the tuples
  45. # (p^k,k) where each k is some power of 2. After deciding the largest k
  46. # such that k is power of 2 and p^k|a the loop iteratively calculates
  47. # the actual power.
  48. power_list = []
  49. p1 = p
  50. r = a.rem(p1)
  51. tracks_power = 1
  52. while r.is_zero:
  53. power_list.append((p1,tracks_power))
  54. p1 = p1*p1
  55. tracks_power *= 2
  56. r = a.rem(p1)
  57. n = 0
  58. product = Poly(1, t)
  59. while len(power_list) != 0:
  60. final = power_list.pop()
  61. productf = product*final[0]
  62. r = a.rem(productf)
  63. if r.is_zero:
  64. n += final[1]
  65. product = productf
  66. return n
  67. def order_at_oo(a, d, t):
  68. """
  69. Computes the order of a/d at oo (infinity), with respect to t.
  70. For f in k(t), the order or f at oo is defined as deg(d) - deg(a), where
  71. f == a/d.
  72. """
  73. if a.is_zero:
  74. return oo
  75. return d.degree(t) - a.degree(t)
  76. def weak_normalizer(a, d, DE, z=None):
  77. """
  78. Weak normalization.
  79. Explanation
  80. ===========
  81. Given a derivation D on k[t] and f == a/d in k(t), return q in k[t]
  82. such that f - Dq/q is weakly normalized with respect to t.
  83. f in k(t) is said to be "weakly normalized" with respect to t if
  84. residue_p(f) is not a positive integer for any normal irreducible p
  85. in k[t] such that f is in R_p (Definition 6.1.1). If f has an
  86. elementary integral, this is equivalent to no logarithm of
  87. integral(f) whose argument depends on t has a positive integer
  88. coefficient, where the arguments of the logarithms not in k(t) are
  89. in k[t].
  90. Returns (q, f - Dq/q)
  91. """
  92. z = z or Dummy('z')
  93. dn, ds = splitfactor(d, DE)
  94. # Compute d1, where dn == d1*d2**2*...*dn**n is a square-free
  95. # factorization of d.
  96. g = gcd(dn, dn.diff(DE.t))
  97. d_sqf_part = dn.quo(g)
  98. d1 = d_sqf_part.quo(gcd(d_sqf_part, g))
  99. a1, b = gcdex_diophantine(d.quo(d1).as_poly(DE.t), d1.as_poly(DE.t),
  100. a.as_poly(DE.t))
  101. r = (a - Poly(z, DE.t)*derivation(d1, DE)).as_poly(DE.t).resultant(
  102. d1.as_poly(DE.t))
  103. r = Poly(r, z)
  104. if not r.expr.has(z):
  105. return (Poly(1, DE.t), (a, d))
  106. N = [i for i in r.real_roots() if i in ZZ and i > 0]
  107. q = reduce(mul, [gcd(a - Poly(n, DE.t)*derivation(d1, DE), d1) for n in N],
  108. Poly(1, DE.t))
  109. dq = derivation(q, DE)
  110. sn = q*a - d*dq
  111. sd = q*d
  112. sn, sd = sn.cancel(sd, include=True)
  113. return (q, (sn, sd))
  114. def normal_denom(fa, fd, ga, gd, DE):
  115. """
  116. Normal part of the denominator.
  117. Explanation
  118. ===========
  119. Given a derivation D on k[t] and f, g in k(t) with f weakly
  120. normalized with respect to t, either raise NonElementaryIntegralException,
  121. in which case the equation Dy + f*y == g has no solution in k(t), or the
  122. quadruplet (a, b, c, h) such that a, h in k[t], b, c in k<t>, and for any
  123. solution y in k(t) of Dy + f*y == g, q = y*h in k<t> satisfies
  124. a*Dq + b*q == c.
  125. This constitutes step 1 in the outline given in the rde.py docstring.
  126. """
  127. dn, ds = splitfactor(fd, DE)
  128. en, es = splitfactor(gd, DE)
  129. p = dn.gcd(en)
  130. h = en.gcd(en.diff(DE.t)).quo(p.gcd(p.diff(DE.t)))
  131. a = dn*h
  132. c = a*h
  133. if c.div(en)[1]:
  134. # en does not divide dn*h**2
  135. raise NonElementaryIntegralException
  136. ca = c*ga
  137. ca, cd = ca.cancel(gd, include=True)
  138. ba = a*fa - dn*derivation(h, DE)*fd
  139. ba, bd = ba.cancel(fd, include=True)
  140. # (dn*h, dn*h*f - dn*Dh, dn*h**2*g, h)
  141. return (a, (ba, bd), (ca, cd), h)
  142. def special_denom(a, ba, bd, ca, cd, DE, case='auto'):
  143. """
  144. Special part of the denominator.
  145. Explanation
  146. ===========
  147. case is one of {'exp', 'tan', 'primitive'} for the hyperexponential,
  148. hypertangent, and primitive cases, respectively. For the
  149. hyperexponential (resp. hypertangent) case, given a derivation D on
  150. k[t] and a in k[t], b, c, in k<t> with Dt/t in k (resp. Dt/(t**2 + 1) in
  151. k, sqrt(-1) not in k), a != 0, and gcd(a, t) == 1 (resp.
  152. gcd(a, t**2 + 1) == 1), return the quadruplet (A, B, C, 1/h) such that
  153. A, B, C, h in k[t] and for any solution q in k<t> of a*Dq + b*q == c,
  154. r = qh in k[t] satisfies A*Dr + B*r == C.
  155. For ``case == 'primitive'``, k<t> == k[t], so it returns (a, b, c, 1) in
  156. this case.
  157. This constitutes step 2 of the outline given in the rde.py docstring.
  158. """
  159. # TODO: finish writing this and write tests
  160. if case == 'auto':
  161. case = DE.case
  162. if case == 'exp':
  163. p = Poly(DE.t, DE.t)
  164. elif case == 'tan':
  165. p = Poly(DE.t**2 + 1, DE.t)
  166. elif case in ('primitive', 'base'):
  167. B = ba.to_field().quo(bd)
  168. C = ca.to_field().quo(cd)
  169. return (a, B, C, Poly(1, DE.t))
  170. else:
  171. raise ValueError("case must be one of {'exp', 'tan', 'primitive', "
  172. "'base'}, not %s." % case)
  173. nb = order_at(ba, p, DE.t) - order_at(bd, p, DE.t)
  174. nc = order_at(ca, p, DE.t) - order_at(cd, p, DE.t)
  175. n = min(0, nc - min(0, nb))
  176. if not nb:
  177. # Possible cancellation.
  178. from .prde import parametric_log_deriv
  179. if case == 'exp':
  180. dcoeff = DE.d.quo(Poly(DE.t, DE.t))
  181. with DecrementLevel(DE): # We are guaranteed to not have problems,
  182. # because case != 'base'.
  183. alphaa, alphad = frac_in(-ba.eval(0)/bd.eval(0)/a.eval(0), DE.t)
  184. etaa, etad = frac_in(dcoeff, DE.t)
  185. A = parametric_log_deriv(alphaa, alphad, etaa, etad, DE)
  186. if A is not None:
  187. Q, m, z = A
  188. if Q == 1:
  189. n = min(n, m)
  190. elif case == 'tan':
  191. dcoeff = DE.d.quo(Poly(DE.t**2+1, DE.t))
  192. with DecrementLevel(DE): # We are guaranteed to not have problems,
  193. # because case != 'base'.
  194. alphaa, alphad = frac_in(im(-ba.eval(sqrt(-1))/bd.eval(sqrt(-1))/a.eval(sqrt(-1))), DE.t)
  195. betaa, betad = frac_in(re(-ba.eval(sqrt(-1))/bd.eval(sqrt(-1))/a.eval(sqrt(-1))), DE.t)
  196. etaa, etad = frac_in(dcoeff, DE.t)
  197. if recognize_log_derivative(Poly(2, DE.t)*betaa, betad, DE):
  198. A = parametric_log_deriv(alphaa*Poly(sqrt(-1), DE.t)*betad+alphad*betaa, alphad*betad, etaa, etad, DE)
  199. if A is not None:
  200. Q, m, z = A
  201. if Q == 1:
  202. n = min(n, m)
  203. N = max(0, -nb, n - nc)
  204. pN = p**N
  205. pn = p**-n
  206. A = a*pN
  207. B = ba*pN.quo(bd) + Poly(n, DE.t)*a*derivation(p, DE).quo(p)*pN
  208. C = (ca*pN*pn).quo(cd)
  209. h = pn
  210. # (a*p**N, (b + n*a*Dp/p)*p**N, c*p**(N - n), p**-n)
  211. return (A, B, C, h)
  212. def bound_degree(a, b, cQ, DE, case='auto', parametric=False):
  213. """
  214. Bound on polynomial solutions.
  215. Explanation
  216. ===========
  217. Given a derivation D on k[t] and ``a``, ``b``, ``c`` in k[t] with ``a != 0``, return
  218. n in ZZ such that deg(q) <= n for any solution q in k[t] of
  219. a*Dq + b*q == c, when parametric=False, or deg(q) <= n for any solution
  220. c1, ..., cm in Const(k) and q in k[t] of a*Dq + b*q == Sum(ci*gi, (i, 1, m))
  221. when parametric=True.
  222. For ``parametric=False``, ``cQ`` is ``c``, a ``Poly``; for ``parametric=True``, ``cQ`` is Q ==
  223. [q1, ..., qm], a list of Polys.
  224. This constitutes step 3 of the outline given in the rde.py docstring.
  225. """
  226. # TODO: finish writing this and write tests
  227. if case == 'auto':
  228. case = DE.case
  229. da = a.degree(DE.t)
  230. db = b.degree(DE.t)
  231. # The parametric and regular cases are identical, except for this part
  232. if parametric:
  233. dc = max([i.degree(DE.t) for i in cQ])
  234. else:
  235. dc = cQ.degree(DE.t)
  236. alpha = cancel(-b.as_poly(DE.t).LC().as_expr()/
  237. a.as_poly(DE.t).LC().as_expr())
  238. if case == 'base':
  239. n = max(0, dc - max(db, da - 1))
  240. if db == da - 1 and alpha.is_Integer:
  241. n = max(0, alpha, dc - db)
  242. elif case == 'primitive':
  243. if db > da:
  244. n = max(0, dc - db)
  245. else:
  246. n = max(0, dc - da + 1)
  247. etaa, etad = frac_in(DE.d, DE.T[DE.level - 1])
  248. t1 = DE.t
  249. with DecrementLevel(DE):
  250. alphaa, alphad = frac_in(alpha, DE.t)
  251. if db == da - 1:
  252. from .prde import limited_integrate
  253. # if alpha == m*Dt + Dz for z in k and m in ZZ:
  254. try:
  255. (za, zd), m = limited_integrate(alphaa, alphad, [(etaa, etad)],
  256. DE)
  257. except NonElementaryIntegralException:
  258. pass
  259. else:
  260. if len(m) != 1:
  261. raise ValueError("Length of m should be 1")
  262. n = max(n, m[0])
  263. elif db == da:
  264. # if alpha == Dz/z for z in k*:
  265. # beta = -lc(a*Dz + b*z)/(z*lc(a))
  266. # if beta == m*Dt + Dw for w in k and m in ZZ:
  267. # n = max(n, m)
  268. from .prde import is_log_deriv_k_t_radical_in_field
  269. A = is_log_deriv_k_t_radical_in_field(alphaa, alphad, DE)
  270. if A is not None:
  271. aa, z = A
  272. if aa == 1:
  273. beta = -(a*derivation(z, DE).as_poly(t1) +
  274. b*z.as_poly(t1)).LC()/(z.as_expr()*a.LC())
  275. betaa, betad = frac_in(beta, DE.t)
  276. from .prde import limited_integrate
  277. try:
  278. (za, zd), m = limited_integrate(betaa, betad,
  279. [(etaa, etad)], DE)
  280. except NonElementaryIntegralException:
  281. pass
  282. else:
  283. if len(m) != 1:
  284. raise ValueError("Length of m should be 1")
  285. n = max(n, m[0].as_expr())
  286. elif case == 'exp':
  287. from .prde import parametric_log_deriv
  288. n = max(0, dc - max(db, da))
  289. if da == db:
  290. etaa, etad = frac_in(DE.d.quo(Poly(DE.t, DE.t)), DE.T[DE.level - 1])
  291. with DecrementLevel(DE):
  292. alphaa, alphad = frac_in(alpha, DE.t)
  293. A = parametric_log_deriv(alphaa, alphad, etaa, etad, DE)
  294. if A is not None:
  295. # if alpha == m*Dt/t + Dz/z for z in k* and m in ZZ:
  296. # n = max(n, m)
  297. a, m, z = A
  298. if a == 1:
  299. n = max(n, m)
  300. elif case in ('tan', 'other_nonlinear'):
  301. delta = DE.d.degree(DE.t)
  302. lam = DE.d.LC()
  303. alpha = cancel(alpha/lam)
  304. n = max(0, dc - max(da + delta - 1, db))
  305. if db == da + delta - 1 and alpha.is_Integer:
  306. n = max(0, alpha, dc - db)
  307. else:
  308. raise ValueError("case must be one of {'exp', 'tan', 'primitive', "
  309. "'other_nonlinear', 'base'}, not %s." % case)
  310. return n
  311. def spde(a, b, c, n, DE):
  312. """
  313. Rothstein's Special Polynomial Differential Equation algorithm.
  314. Explanation
  315. ===========
  316. Given a derivation D on k[t], an integer n and ``a``,``b``,``c`` in k[t] with
  317. ``a != 0``, either raise NonElementaryIntegralException, in which case the
  318. equation a*Dq + b*q == c has no solution of degree at most ``n`` in
  319. k[t], or return the tuple (B, C, m, alpha, beta) such that B, C,
  320. alpha, beta in k[t], m in ZZ, and any solution q in k[t] of degree
  321. at most n of a*Dq + b*q == c must be of the form
  322. q == alpha*h + beta, where h in k[t], deg(h) <= m, and Dh + B*h == C.
  323. This constitutes step 4 of the outline given in the rde.py docstring.
  324. """
  325. zero = Poly(0, DE.t)
  326. alpha = Poly(1, DE.t)
  327. beta = Poly(0, DE.t)
  328. while True:
  329. if c.is_zero:
  330. return (zero, zero, 0, zero, beta) # -1 is more to the point
  331. if (n < 0) is True:
  332. raise NonElementaryIntegralException
  333. g = a.gcd(b)
  334. if not c.rem(g).is_zero: # g does not divide c
  335. raise NonElementaryIntegralException
  336. a, b, c = a.quo(g), b.quo(g), c.quo(g)
  337. if a.degree(DE.t) == 0:
  338. b = b.to_field().quo(a)
  339. c = c.to_field().quo(a)
  340. return (b, c, n, alpha, beta)
  341. r, z = gcdex_diophantine(b, a, c)
  342. b += derivation(a, DE)
  343. c = z - derivation(r, DE)
  344. n -= a.degree(DE.t)
  345. beta += alpha * r
  346. alpha *= a
  347. def no_cancel_b_large(b, c, n, DE):
  348. """
  349. Poly Risch Differential Equation - No cancellation: deg(b) large enough.
  350. Explanation
  351. ===========
  352. Given a derivation D on k[t], ``n`` either an integer or +oo, and ``b``,``c``
  353. in k[t] with ``b != 0`` and either D == d/dt or
  354. deg(b) > max(0, deg(D) - 1), either raise NonElementaryIntegralException, in
  355. which case the equation ``Dq + b*q == c`` has no solution of degree at
  356. most n in k[t], or a solution q in k[t] of this equation with
  357. ``deg(q) < n``.
  358. """
  359. q = Poly(0, DE.t)
  360. while not c.is_zero:
  361. m = c.degree(DE.t) - b.degree(DE.t)
  362. if not 0 <= m <= n: # n < 0 or m < 0 or m > n
  363. raise NonElementaryIntegralException
  364. p = Poly(c.as_poly(DE.t).LC()/b.as_poly(DE.t).LC()*DE.t**m, DE.t,
  365. expand=False)
  366. q = q + p
  367. n = m - 1
  368. c = c - derivation(p, DE) - b*p
  369. return q
  370. def no_cancel_b_small(b, c, n, DE):
  371. """
  372. Poly Risch Differential Equation - No cancellation: deg(b) small enough.
  373. Explanation
  374. ===========
  375. Given a derivation D on k[t], ``n`` either an integer or +oo, and ``b``,``c``
  376. in k[t] with deg(b) < deg(D) - 1 and either D == d/dt or
  377. deg(D) >= 2, either raise NonElementaryIntegralException, in which case the
  378. equation Dq + b*q == c has no solution of degree at most n in k[t],
  379. or a solution q in k[t] of this equation with deg(q) <= n, or the
  380. tuple (h, b0, c0) such that h in k[t], b0, c0, in k, and for any
  381. solution q in k[t] of degree at most n of Dq + bq == c, y == q - h
  382. is a solution in k of Dy + b0*y == c0.
  383. """
  384. q = Poly(0, DE.t)
  385. while not c.is_zero:
  386. if n == 0:
  387. m = 0
  388. else:
  389. m = c.degree(DE.t) - DE.d.degree(DE.t) + 1
  390. if not 0 <= m <= n: # n < 0 or m < 0 or m > n
  391. raise NonElementaryIntegralException
  392. if m > 0:
  393. p = Poly(c.as_poly(DE.t).LC()/(m*DE.d.as_poly(DE.t).LC())*DE.t**m,
  394. DE.t, expand=False)
  395. else:
  396. if b.degree(DE.t) != c.degree(DE.t):
  397. raise NonElementaryIntegralException
  398. if b.degree(DE.t) == 0:
  399. return (q, b.as_poly(DE.T[DE.level - 1]),
  400. c.as_poly(DE.T[DE.level - 1]))
  401. p = Poly(c.as_poly(DE.t).LC()/b.as_poly(DE.t).LC(), DE.t,
  402. expand=False)
  403. q = q + p
  404. n = m - 1
  405. c = c - derivation(p, DE) - b*p
  406. return q
  407. # TODO: better name for this function
  408. def no_cancel_equal(b, c, n, DE):
  409. """
  410. Poly Risch Differential Equation - No cancellation: deg(b) == deg(D) - 1
  411. Explanation
  412. ===========
  413. Given a derivation D on k[t] with deg(D) >= 2, n either an integer
  414. or +oo, and b, c in k[t] with deg(b) == deg(D) - 1, either raise
  415. NonElementaryIntegralException, in which case the equation Dq + b*q == c has
  416. no solution of degree at most n in k[t], or a solution q in k[t] of
  417. this equation with deg(q) <= n, or the tuple (h, m, C) such that h
  418. in k[t], m in ZZ, and C in k[t], and for any solution q in k[t] of
  419. degree at most n of Dq + b*q == c, y == q - h is a solution in k[t]
  420. of degree at most m of Dy + b*y == C.
  421. """
  422. q = Poly(0, DE.t)
  423. lc = cancel(-b.as_poly(DE.t).LC()/DE.d.as_poly(DE.t).LC())
  424. if lc.is_Integer and lc.is_positive:
  425. M = lc
  426. else:
  427. M = -1
  428. while not c.is_zero:
  429. m = max(M, c.degree(DE.t) - DE.d.degree(DE.t) + 1)
  430. if not 0 <= m <= n: # n < 0 or m < 0 or m > n
  431. raise NonElementaryIntegralException
  432. u = cancel(m*DE.d.as_poly(DE.t).LC() + b.as_poly(DE.t).LC())
  433. if u.is_zero:
  434. return (q, m, c)
  435. if m > 0:
  436. p = Poly(c.as_poly(DE.t).LC()/u*DE.t**m, DE.t, expand=False)
  437. else:
  438. if c.degree(DE.t) != DE.d.degree(DE.t) - 1:
  439. raise NonElementaryIntegralException
  440. else:
  441. p = c.as_poly(DE.t).LC()/b.as_poly(DE.t).LC()
  442. q = q + p
  443. n = m - 1
  444. c = c - derivation(p, DE) - b*p
  445. return q
  446. def cancel_primitive(b, c, n, DE):
  447. """
  448. Poly Risch Differential Equation - Cancellation: Primitive case.
  449. Explanation
  450. ===========
  451. Given a derivation D on k[t], n either an integer or +oo, ``b`` in k, and
  452. ``c`` in k[t] with Dt in k and ``b != 0``, either raise
  453. NonElementaryIntegralException, in which case the equation Dq + b*q == c
  454. has no solution of degree at most n in k[t], or a solution q in k[t] of
  455. this equation with deg(q) <= n.
  456. """
  457. # Delayed imports
  458. from .prde import is_log_deriv_k_t_radical_in_field
  459. with DecrementLevel(DE):
  460. ba, bd = frac_in(b, DE.t)
  461. A = is_log_deriv_k_t_radical_in_field(ba, bd, DE)
  462. if A is not None:
  463. n, z = A
  464. if n == 1: # b == Dz/z
  465. raise NotImplementedError("is_deriv_in_field() is required to "
  466. " solve this problem.")
  467. # if z*c == Dp for p in k[t] and deg(p) <= n:
  468. # return p/z
  469. # else:
  470. # raise NonElementaryIntegralException
  471. if c.is_zero:
  472. return c # return 0
  473. if n < c.degree(DE.t):
  474. raise NonElementaryIntegralException
  475. q = Poly(0, DE.t)
  476. while not c.is_zero:
  477. m = c.degree(DE.t)
  478. if n < m:
  479. raise NonElementaryIntegralException
  480. with DecrementLevel(DE):
  481. a2a, a2d = frac_in(c.LC(), DE.t)
  482. sa, sd = rischDE(ba, bd, a2a, a2d, DE)
  483. stm = Poly(sa.as_expr()/sd.as_expr()*DE.t**m, DE.t, expand=False)
  484. q += stm
  485. n = m - 1
  486. c -= b*stm + derivation(stm, DE)
  487. return q
  488. def cancel_exp(b, c, n, DE):
  489. """
  490. Poly Risch Differential Equation - Cancellation: Hyperexponential case.
  491. Explanation
  492. ===========
  493. Given a derivation D on k[t], n either an integer or +oo, ``b`` in k, and
  494. ``c`` in k[t] with Dt/t in k and ``b != 0``, either raise
  495. NonElementaryIntegralException, in which case the equation Dq + b*q == c
  496. has no solution of degree at most n in k[t], or a solution q in k[t] of
  497. this equation with deg(q) <= n.
  498. """
  499. from .prde import parametric_log_deriv
  500. eta = DE.d.quo(Poly(DE.t, DE.t)).as_expr()
  501. with DecrementLevel(DE):
  502. etaa, etad = frac_in(eta, DE.t)
  503. ba, bd = frac_in(b, DE.t)
  504. A = parametric_log_deriv(ba, bd, etaa, etad, DE)
  505. if A is not None:
  506. a, m, z = A
  507. if a == 1:
  508. raise NotImplementedError("is_deriv_in_field() is required to "
  509. "solve this problem.")
  510. # if c*z*t**m == Dp for p in k<t> and q = p/(z*t**m) in k[t] and
  511. # deg(q) <= n:
  512. # return q
  513. # else:
  514. # raise NonElementaryIntegralException
  515. if c.is_zero:
  516. return c # return 0
  517. if n < c.degree(DE.t):
  518. raise NonElementaryIntegralException
  519. q = Poly(0, DE.t)
  520. while not c.is_zero:
  521. m = c.degree(DE.t)
  522. if n < m:
  523. raise NonElementaryIntegralException
  524. # a1 = b + m*Dt/t
  525. a1 = b.as_expr()
  526. with DecrementLevel(DE):
  527. # TODO: Write a dummy function that does this idiom
  528. a1a, a1d = frac_in(a1, DE.t)
  529. a1a = a1a*etad + etaa*a1d*Poly(m, DE.t)
  530. a1d = a1d*etad
  531. a2a, a2d = frac_in(c.LC(), DE.t)
  532. sa, sd = rischDE(a1a, a1d, a2a, a2d, DE)
  533. stm = Poly(sa.as_expr()/sd.as_expr()*DE.t**m, DE.t, expand=False)
  534. q += stm
  535. n = m - 1
  536. c -= b*stm + derivation(stm, DE) # deg(c) becomes smaller
  537. return q
  538. def solve_poly_rde(b, cQ, n, DE, parametric=False):
  539. """
  540. Solve a Polynomial Risch Differential Equation with degree bound ``n``.
  541. This constitutes step 4 of the outline given in the rde.py docstring.
  542. For parametric=False, cQ is c, a Poly; for parametric=True, cQ is Q ==
  543. [q1, ..., qm], a list of Polys.
  544. """
  545. # No cancellation
  546. if not b.is_zero and (DE.case == 'base' or
  547. b.degree(DE.t) > max(0, DE.d.degree(DE.t) - 1)):
  548. if parametric:
  549. # Delayed imports
  550. from .prde import prde_no_cancel_b_large
  551. return prde_no_cancel_b_large(b, cQ, n, DE)
  552. return no_cancel_b_large(b, cQ, n, DE)
  553. elif (b.is_zero or b.degree(DE.t) < DE.d.degree(DE.t) - 1) and \
  554. (DE.case == 'base' or DE.d.degree(DE.t) >= 2):
  555. if parametric:
  556. from .prde import prde_no_cancel_b_small
  557. return prde_no_cancel_b_small(b, cQ, n, DE)
  558. R = no_cancel_b_small(b, cQ, n, DE)
  559. if isinstance(R, Poly):
  560. return R
  561. else:
  562. # XXX: Might k be a field? (pg. 209)
  563. h, b0, c0 = R
  564. with DecrementLevel(DE):
  565. b0, c0 = b0.as_poly(DE.t), c0.as_poly(DE.t)
  566. if b0 is None: # See above comment
  567. raise ValueError("b0 should be a non-Null value")
  568. if c0 is None:
  569. raise ValueError("c0 should be a non-Null value")
  570. y = solve_poly_rde(b0, c0, n, DE).as_poly(DE.t)
  571. return h + y
  572. elif DE.d.degree(DE.t) >= 2 and b.degree(DE.t) == DE.d.degree(DE.t) - 1 and \
  573. n > -b.as_poly(DE.t).LC()/DE.d.as_poly(DE.t).LC():
  574. # TODO: Is this check necessary, and if so, what should it do if it fails?
  575. # b comes from the first element returned from spde()
  576. if not b.as_poly(DE.t).LC().is_number:
  577. raise TypeError("Result should be a number")
  578. if parametric:
  579. raise NotImplementedError("prde_no_cancel_b_equal() is not yet "
  580. "implemented.")
  581. R = no_cancel_equal(b, cQ, n, DE)
  582. if isinstance(R, Poly):
  583. return R
  584. else:
  585. h, m, C = R
  586. # XXX: Or should it be rischDE()?
  587. y = solve_poly_rde(b, C, m, DE)
  588. return h + y
  589. else:
  590. # Cancellation
  591. if b.is_zero:
  592. raise NotImplementedError("Remaining cases for Poly (P)RDE are "
  593. "not yet implemented (is_deriv_in_field() required).")
  594. else:
  595. if DE.case == 'exp':
  596. if parametric:
  597. raise NotImplementedError("Parametric RDE cancellation "
  598. "hyperexponential case is not yet implemented.")
  599. return cancel_exp(b, cQ, n, DE)
  600. elif DE.case == 'primitive':
  601. if parametric:
  602. raise NotImplementedError("Parametric RDE cancellation "
  603. "primitive case is not yet implemented.")
  604. return cancel_primitive(b, cQ, n, DE)
  605. else:
  606. raise NotImplementedError("Other Poly (P)RDE cancellation "
  607. "cases are not yet implemented (%s)." % DE.case)
  608. if parametric:
  609. raise NotImplementedError("Remaining cases for Poly PRDE not yet "
  610. "implemented.")
  611. raise NotImplementedError("Remaining cases for Poly RDE not yet "
  612. "implemented.")
  613. def rischDE(fa, fd, ga, gd, DE):
  614. """
  615. Solve a Risch Differential Equation: Dy + f*y == g.
  616. Explanation
  617. ===========
  618. See the outline in the docstring of rde.py for more information
  619. about the procedure used. Either raise NonElementaryIntegralException, in
  620. which case there is no solution y in the given differential field,
  621. or return y in k(t) satisfying Dy + f*y == g, or raise
  622. NotImplementedError, in which case, the algorithms necessary to
  623. solve the given Risch Differential Equation have not yet been
  624. implemented.
  625. """
  626. _, (fa, fd) = weak_normalizer(fa, fd, DE)
  627. a, (ba, bd), (ca, cd), hn = normal_denom(fa, fd, ga, gd, DE)
  628. A, B, C, hs = special_denom(a, ba, bd, ca, cd, DE)
  629. try:
  630. # Until this is fully implemented, use oo. Note that this will almost
  631. # certainly cause non-termination in spde() (unless A == 1), and
  632. # *might* lead to non-termination in the next step for a nonelementary
  633. # integral (I don't know for certain yet). Fortunately, spde() is
  634. # currently written recursively, so this will just give
  635. # RuntimeError: maximum recursion depth exceeded.
  636. n = bound_degree(A, B, C, DE)
  637. except NotImplementedError:
  638. # Useful for debugging:
  639. # import warnings
  640. # warnings.warn("rischDE: Proceeding with n = oo; may cause "
  641. # "non-termination.")
  642. n = oo
  643. B, C, m, alpha, beta = spde(A, B, C, n, DE)
  644. if C.is_zero:
  645. y = C
  646. else:
  647. y = solve_poly_rde(B, C, m, DE)
  648. return (alpha*y + beta, hn*hs)