rationaltools.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. """This module implements tools for integrating rational functions. """
  2. from sympy.core.function import Lambda
  3. from sympy.core.numbers import I
  4. from sympy.core.singleton import S
  5. from sympy.core.symbol import (Dummy, Symbol, symbols)
  6. from sympy.functions.elementary.exponential import log
  7. from sympy.functions.elementary.trigonometric import atan
  8. from sympy.polys.polyroots import roots
  9. from sympy.polys.polytools import cancel
  10. from sympy.polys.rootoftools import RootSum
  11. from sympy.polys import Poly, resultant, ZZ
  12. from sympy.solvers.solvers import solve
  13. def ratint(f, x, **flags):
  14. """
  15. Performs indefinite integration of rational functions.
  16. Explanation
  17. ===========
  18. Given a field :math:`K` and a rational function :math:`f = p/q`,
  19. where :math:`p` and :math:`q` are polynomials in :math:`K[x]`,
  20. returns a function :math:`g` such that :math:`f = g'`.
  21. Examples
  22. ========
  23. >>> from sympy.integrals.rationaltools import ratint
  24. >>> from sympy.abc import x
  25. >>> ratint(36/(x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2), x)
  26. (12*x + 6)/(x**2 - 1) + 4*log(x - 2) - 4*log(x + 1)
  27. References
  28. ==========
  29. .. [1] M. Bronstein, Symbolic Integration I: Transcendental
  30. Functions, Second Edition, Springer-Verlag, 2005, pp. 35-70
  31. See Also
  32. ========
  33. sympy.integrals.integrals.Integral.doit
  34. sympy.integrals.rationaltools.ratint_logpart
  35. sympy.integrals.rationaltools.ratint_ratpart
  36. """
  37. if isinstance(f, tuple):
  38. p, q = f
  39. else:
  40. p, q = f.as_numer_denom()
  41. p, q = Poly(p, x, composite=False, field=True), Poly(q, x, composite=False, field=True)
  42. coeff, p, q = p.cancel(q)
  43. poly, p = p.div(q)
  44. result = poly.integrate(x).as_expr()
  45. if p.is_zero:
  46. return coeff*result
  47. g, h = ratint_ratpart(p, q, x)
  48. P, Q = h.as_numer_denom()
  49. P = Poly(P, x)
  50. Q = Poly(Q, x)
  51. q, r = P.div(Q)
  52. result += g + q.integrate(x).as_expr()
  53. if not r.is_zero:
  54. symbol = flags.get('symbol', 't')
  55. if not isinstance(symbol, Symbol):
  56. t = Dummy(symbol)
  57. else:
  58. t = symbol.as_dummy()
  59. L = ratint_logpart(r, Q, x, t)
  60. real = flags.get('real')
  61. if real is None:
  62. if isinstance(f, tuple):
  63. p, q = f
  64. atoms = p.atoms() | q.atoms()
  65. else:
  66. atoms = f.atoms()
  67. for elt in atoms - {x}:
  68. if not elt.is_extended_real:
  69. real = False
  70. break
  71. else:
  72. real = True
  73. eps = S.Zero
  74. if not real:
  75. for h, q in L:
  76. _, h = h.primitive()
  77. eps += RootSum(
  78. q, Lambda(t, t*log(h.as_expr())), quadratic=True)
  79. else:
  80. for h, q in L:
  81. _, h = h.primitive()
  82. R = log_to_real(h, q, x, t)
  83. if R is not None:
  84. eps += R
  85. else:
  86. eps += RootSum(
  87. q, Lambda(t, t*log(h.as_expr())), quadratic=True)
  88. result += eps
  89. return coeff*result
  90. def ratint_ratpart(f, g, x):
  91. """
  92. Horowitz-Ostrogradsky algorithm.
  93. Explanation
  94. ===========
  95. Given a field K and polynomials f and g in K[x], such that f and g
  96. are coprime and deg(f) < deg(g), returns fractions A and B in K(x),
  97. such that f/g = A' + B and B has square-free denominator.
  98. Examples
  99. ========
  100. >>> from sympy.integrals.rationaltools import ratint_ratpart
  101. >>> from sympy.abc import x, y
  102. >>> from sympy import Poly
  103. >>> ratint_ratpart(Poly(1, x, domain='ZZ'),
  104. ... Poly(x + 1, x, domain='ZZ'), x)
  105. (0, 1/(x + 1))
  106. >>> ratint_ratpart(Poly(1, x, domain='EX'),
  107. ... Poly(x**2 + y**2, x, domain='EX'), x)
  108. (0, 1/(x**2 + y**2))
  109. >>> ratint_ratpart(Poly(36, x, domain='ZZ'),
  110. ... Poly(x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2, x, domain='ZZ'), x)
  111. ((12*x + 6)/(x**2 - 1), 12/(x**2 - x - 2))
  112. See Also
  113. ========
  114. ratint, ratint_logpart
  115. """
  116. f = Poly(f, x)
  117. g = Poly(g, x)
  118. u, v, _ = g.cofactors(g.diff())
  119. n = u.degree()
  120. m = v.degree()
  121. A_coeffs = [ Dummy('a' + str(n - i)) for i in range(0, n) ]
  122. B_coeffs = [ Dummy('b' + str(m - i)) for i in range(0, m) ]
  123. C_coeffs = A_coeffs + B_coeffs
  124. A = Poly(A_coeffs, x, domain=ZZ[C_coeffs])
  125. B = Poly(B_coeffs, x, domain=ZZ[C_coeffs])
  126. H = f - A.diff()*v + A*(u.diff()*v).quo(u) - B*u
  127. result = solve(H.coeffs(), C_coeffs)
  128. A = A.as_expr().subs(result)
  129. B = B.as_expr().subs(result)
  130. rat_part = cancel(A/u.as_expr(), x)
  131. log_part = cancel(B/v.as_expr(), x)
  132. return rat_part, log_part
  133. def ratint_logpart(f, g, x, t=None):
  134. r"""
  135. Lazard-Rioboo-Trager algorithm.
  136. Explanation
  137. ===========
  138. Given a field K and polynomials f and g in K[x], such that f and g
  139. are coprime, deg(f) < deg(g) and g is square-free, returns a list
  140. of tuples (s_i, q_i) of polynomials, for i = 1..n, such that s_i
  141. in K[t, x] and q_i in K[t], and::
  142. ___ ___
  143. d f d \ ` \ `
  144. -- - = -- ) ) a log(s_i(a, x))
  145. dx g dx /__, /__,
  146. i=1..n a | q_i(a) = 0
  147. Examples
  148. ========
  149. >>> from sympy.integrals.rationaltools import ratint_logpart
  150. >>> from sympy.abc import x
  151. >>> from sympy import Poly
  152. >>> ratint_logpart(Poly(1, x, domain='ZZ'),
  153. ... Poly(x**2 + x + 1, x, domain='ZZ'), x)
  154. [(Poly(x + 3*_t/2 + 1/2, x, domain='QQ[_t]'),
  155. ...Poly(3*_t**2 + 1, _t, domain='ZZ'))]
  156. >>> ratint_logpart(Poly(12, x, domain='ZZ'),
  157. ... Poly(x**2 - x - 2, x, domain='ZZ'), x)
  158. [(Poly(x - 3*_t/8 - 1/2, x, domain='QQ[_t]'),
  159. ...Poly(-_t**2 + 16, _t, domain='ZZ'))]
  160. See Also
  161. ========
  162. ratint, ratint_ratpart
  163. """
  164. f, g = Poly(f, x), Poly(g, x)
  165. t = t or Dummy('t')
  166. a, b = g, f - g.diff()*Poly(t, x)
  167. res, R = resultant(a, b, includePRS=True)
  168. res = Poly(res, t, composite=False)
  169. assert res, "BUG: resultant(%s, %s) cannot be zero" % (a, b)
  170. R_map, H = {}, []
  171. for r in R:
  172. R_map[r.degree()] = r
  173. def _include_sign(c, sqf):
  174. if c.is_extended_real and (c < 0) == True:
  175. h, k = sqf[0]
  176. c_poly = c.as_poly(h.gens)
  177. sqf[0] = h*c_poly, k
  178. C, res_sqf = res.sqf_list()
  179. _include_sign(C, res_sqf)
  180. for q, i in res_sqf:
  181. _, q = q.primitive()
  182. if g.degree() == i:
  183. H.append((g, q))
  184. else:
  185. h = R_map[i]
  186. h_lc = Poly(h.LC(), t, field=True)
  187. c, h_lc_sqf = h_lc.sqf_list(all=True)
  188. _include_sign(c, h_lc_sqf)
  189. for a, j in h_lc_sqf:
  190. h = h.quo(Poly(a.gcd(q)**j, x))
  191. inv, coeffs = h_lc.invert(q), [S.One]
  192. for coeff in h.coeffs()[1:]:
  193. coeff = coeff.as_poly(inv.gens)
  194. T = (inv*coeff).rem(q)
  195. coeffs.append(T.as_expr())
  196. h = Poly(dict(list(zip(h.monoms(), coeffs))), x)
  197. H.append((h, q))
  198. return H
  199. def log_to_atan(f, g):
  200. """
  201. Convert complex logarithms to real arctangents.
  202. Explanation
  203. ===========
  204. Given a real field K and polynomials f and g in K[x], with g != 0,
  205. returns a sum h of arctangents of polynomials in K[x], such that:
  206. dh d f + I g
  207. -- = -- I log( ------- )
  208. dx dx f - I g
  209. Examples
  210. ========
  211. >>> from sympy.integrals.rationaltools import log_to_atan
  212. >>> from sympy.abc import x
  213. >>> from sympy import Poly, sqrt, S
  214. >>> log_to_atan(Poly(x, x, domain='ZZ'), Poly(1, x, domain='ZZ'))
  215. 2*atan(x)
  216. >>> log_to_atan(Poly(x + S(1)/2, x, domain='QQ'),
  217. ... Poly(sqrt(3)/2, x, domain='EX'))
  218. 2*atan(2*sqrt(3)*x/3 + sqrt(3)/3)
  219. See Also
  220. ========
  221. log_to_real
  222. """
  223. if f.degree() < g.degree():
  224. f, g = -g, f
  225. f = f.to_field()
  226. g = g.to_field()
  227. p, q = f.div(g)
  228. if q.is_zero:
  229. return 2*atan(p.as_expr())
  230. else:
  231. s, t, h = g.gcdex(-f)
  232. u = (f*s + g*t).quo(h)
  233. A = 2*atan(u.as_expr())
  234. return A + log_to_atan(s, t)
  235. def log_to_real(h, q, x, t):
  236. r"""
  237. Convert complex logarithms to real functions.
  238. Explanation
  239. ===========
  240. Given real field K and polynomials h in K[t,x] and q in K[t],
  241. returns real function f such that:
  242. ___
  243. df d \ `
  244. -- = -- ) a log(h(a, x))
  245. dx dx /__,
  246. a | q(a) = 0
  247. Examples
  248. ========
  249. >>> from sympy.integrals.rationaltools import log_to_real
  250. >>> from sympy.abc import x, y
  251. >>> from sympy import Poly, S
  252. >>> log_to_real(Poly(x + 3*y/2 + S(1)/2, x, domain='QQ[y]'),
  253. ... Poly(3*y**2 + 1, y, domain='ZZ'), x, y)
  254. 2*sqrt(3)*atan(2*sqrt(3)*x/3 + sqrt(3)/3)/3
  255. >>> log_to_real(Poly(x**2 - 1, x, domain='ZZ'),
  256. ... Poly(-2*y + 1, y, domain='ZZ'), x, y)
  257. log(x**2 - 1)/2
  258. See Also
  259. ========
  260. log_to_atan
  261. """
  262. from sympy.simplify.radsimp import collect
  263. u, v = symbols('u,v', cls=Dummy)
  264. H = h.as_expr().subs({t: u + I*v}).expand()
  265. Q = q.as_expr().subs({t: u + I*v}).expand()
  266. H_map = collect(H, I, evaluate=False)
  267. Q_map = collect(Q, I, evaluate=False)
  268. a, b = H_map.get(S.One, S.Zero), H_map.get(I, S.Zero)
  269. c, d = Q_map.get(S.One, S.Zero), Q_map.get(I, S.Zero)
  270. R = Poly(resultant(c, d, v), u)
  271. R_u = roots(R, filter='R')
  272. if len(R_u) != R.count_roots():
  273. return None
  274. result = S.Zero
  275. for r_u in R_u.keys():
  276. C = Poly(c.subs({u: r_u}), v)
  277. R_v = roots(C, filter='R')
  278. if len(R_v) != C.count_roots():
  279. return None
  280. R_v_paired = [] # take one from each pair of conjugate roots
  281. for r_v in R_v:
  282. if r_v not in R_v_paired and -r_v not in R_v_paired:
  283. if r_v.is_negative or r_v.could_extract_minus_sign():
  284. R_v_paired.append(-r_v)
  285. elif not r_v.is_zero:
  286. R_v_paired.append(r_v)
  287. for r_v in R_v_paired:
  288. D = d.subs({u: r_u, v: r_v})
  289. if D.evalf(chop=True) != 0:
  290. continue
  291. A = Poly(a.subs({u: r_u, v: r_v}), x)
  292. B = Poly(b.subs({u: r_u, v: r_v}), x)
  293. AB = (A**2 + B**2).as_expr()
  294. result += r_u*log(AB) + r_v*log_to_atan(A, B)
  295. R_q = roots(q, filter='R')
  296. if len(R_q) != q.count_roots():
  297. return None
  298. for r in R_q.keys():
  299. result += r*log(h.as_expr().subs(t, r))
  300. return result