rationaltools.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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.polyerrors import DomainError
  9. from sympy.polys.polyroots import roots
  10. from sympy.polys.polytools import cancel
  11. from sympy.polys.rootoftools import RootSum
  12. from sympy.polys import Poly, resultant, ZZ
  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. from sympy.solvers.solvers import solve
  117. f = Poly(f, x)
  118. g = Poly(g, x)
  119. u, v, _ = g.cofactors(g.diff())
  120. n = u.degree()
  121. m = v.degree()
  122. A_coeffs = [ Dummy('a' + str(n - i)) for i in range(0, n) ]
  123. B_coeffs = [ Dummy('b' + str(m - i)) for i in range(0, m) ]
  124. C_coeffs = A_coeffs + B_coeffs
  125. A = Poly(A_coeffs, x, domain=ZZ[C_coeffs])
  126. B = Poly(B_coeffs, x, domain=ZZ[C_coeffs])
  127. H = f - A.diff()*v + A*(u.diff()*v).quo(u) - B*u
  128. result = solve(H.coeffs(), C_coeffs)
  129. A = A.as_expr().subs(result)
  130. B = B.as_expr().subs(result)
  131. rat_part = cancel(A/u.as_expr(), x)
  132. log_part = cancel(B/v.as_expr(), x)
  133. return rat_part, log_part
  134. def ratint_logpart(f, g, x, t=None):
  135. r"""
  136. Lazard-Rioboo-Trager algorithm.
  137. Explanation
  138. ===========
  139. Given a field K and polynomials f and g in K[x], such that f and g
  140. are coprime, deg(f) < deg(g) and g is square-free, returns a list
  141. of tuples (s_i, q_i) of polynomials, for i = 1..n, such that s_i
  142. in K[t, x] and q_i in K[t], and::
  143. ___ ___
  144. d f d \ ` \ `
  145. -- - = -- ) ) a log(s_i(a, x))
  146. dx g dx /__, /__,
  147. i=1..n a | q_i(a) = 0
  148. Examples
  149. ========
  150. >>> from sympy.integrals.rationaltools import ratint_logpart
  151. >>> from sympy.abc import x
  152. >>> from sympy import Poly
  153. >>> ratint_logpart(Poly(1, x, domain='ZZ'),
  154. ... Poly(x**2 + x + 1, x, domain='ZZ'), x)
  155. [(Poly(x + 3*_t/2 + 1/2, x, domain='QQ[_t]'),
  156. ...Poly(3*_t**2 + 1, _t, domain='ZZ'))]
  157. >>> ratint_logpart(Poly(12, x, domain='ZZ'),
  158. ... Poly(x**2 - x - 2, x, domain='ZZ'), x)
  159. [(Poly(x - 3*_t/8 - 1/2, x, domain='QQ[_t]'),
  160. ...Poly(-_t**2 + 16, _t, domain='ZZ'))]
  161. See Also
  162. ========
  163. ratint, ratint_ratpart
  164. """
  165. f, g = Poly(f, x), Poly(g, x)
  166. t = t or Dummy('t')
  167. a, b = g, f - g.diff()*Poly(t, x)
  168. res, R = resultant(a, b, includePRS=True)
  169. res = Poly(res, t, composite=False)
  170. assert res, "BUG: resultant(%s, %s) cannot be zero" % (a, b)
  171. R_map, H = {}, []
  172. for r in R:
  173. R_map[r.degree()] = r
  174. def _include_sign(c, sqf):
  175. if c.is_extended_real and (c < 0) == True:
  176. h, k = sqf[0]
  177. c_poly = c.as_poly(h.gens)
  178. sqf[0] = h*c_poly, k
  179. C, res_sqf = res.sqf_list()
  180. _include_sign(C, res_sqf)
  181. for q, i in res_sqf:
  182. _, q = q.primitive()
  183. if g.degree() == i:
  184. H.append((g, q))
  185. else:
  186. h = R_map[i]
  187. h_lc = Poly(h.LC(), t, field=True)
  188. c, h_lc_sqf = h_lc.sqf_list(all=True)
  189. _include_sign(c, h_lc_sqf)
  190. for a, j in h_lc_sqf:
  191. h = h.quo(Poly(a.gcd(q)**j, x))
  192. inv, coeffs = h_lc.invert(q), [S.One]
  193. for coeff in h.coeffs()[1:]:
  194. coeff = coeff.as_poly(inv.gens)
  195. T = (inv*coeff).rem(q)
  196. coeffs.append(T.as_expr())
  197. h = Poly(dict(list(zip(h.monoms(), coeffs))), x)
  198. H.append((h, q))
  199. return H
  200. def log_to_atan(f, g):
  201. """
  202. Convert complex logarithms to real arctangents.
  203. Explanation
  204. ===========
  205. Given a real field K and polynomials f and g in K[x], with g != 0,
  206. returns a sum h of arctangents of polynomials in K[x], such that:
  207. dh d f + I g
  208. -- = -- I log( ------- )
  209. dx dx f - I g
  210. Examples
  211. ========
  212. >>> from sympy.integrals.rationaltools import log_to_atan
  213. >>> from sympy.abc import x
  214. >>> from sympy import Poly, sqrt, S
  215. >>> log_to_atan(Poly(x, x, domain='ZZ'), Poly(1, x, domain='ZZ'))
  216. 2*atan(x)
  217. >>> log_to_atan(Poly(x + S(1)/2, x, domain='QQ'),
  218. ... Poly(sqrt(3)/2, x, domain='EX'))
  219. 2*atan(2*sqrt(3)*x/3 + sqrt(3)/3)
  220. See Also
  221. ========
  222. log_to_real
  223. """
  224. if f.degree() < g.degree():
  225. f, g = -g, f
  226. f = f.to_field()
  227. g = g.to_field()
  228. p, q = f.div(g)
  229. if q.is_zero:
  230. return 2*atan(p.as_expr())
  231. else:
  232. s, t, h = g.gcdex(-f)
  233. u = (f*s + g*t).quo(h)
  234. A = 2*atan(u.as_expr())
  235. return A + log_to_atan(s, t)
  236. def _get_real_roots(f, x):
  237. """get real roots of f if possible"""
  238. rs = roots(f, filter='R')
  239. try:
  240. num_roots = f.count_roots()
  241. except DomainError:
  242. return rs
  243. else:
  244. if len(rs) == num_roots:
  245. return rs
  246. else:
  247. return None
  248. def log_to_real(h, q, x, t):
  249. r"""
  250. Convert complex logarithms to real functions.
  251. Explanation
  252. ===========
  253. Given real field K and polynomials h in K[t,x] and q in K[t],
  254. returns real function f such that:
  255. ___
  256. df d \ `
  257. -- = -- ) a log(h(a, x))
  258. dx dx /__,
  259. a | q(a) = 0
  260. Examples
  261. ========
  262. >>> from sympy.integrals.rationaltools import log_to_real
  263. >>> from sympy.abc import x, y
  264. >>> from sympy import Poly, S
  265. >>> log_to_real(Poly(x + 3*y/2 + S(1)/2, x, domain='QQ[y]'),
  266. ... Poly(3*y**2 + 1, y, domain='ZZ'), x, y)
  267. 2*sqrt(3)*atan(2*sqrt(3)*x/3 + sqrt(3)/3)/3
  268. >>> log_to_real(Poly(x**2 - 1, x, domain='ZZ'),
  269. ... Poly(-2*y + 1, y, domain='ZZ'), x, y)
  270. log(x**2 - 1)/2
  271. See Also
  272. ========
  273. log_to_atan
  274. """
  275. from sympy.simplify.radsimp import collect
  276. u, v = symbols('u,v', cls=Dummy)
  277. H = h.as_expr().xreplace({t: u + I*v}).expand()
  278. Q = q.as_expr().xreplace({t: u + I*v}).expand()
  279. H_map = collect(H, I, evaluate=False)
  280. Q_map = collect(Q, I, evaluate=False)
  281. a, b = H_map.get(S.One, S.Zero), H_map.get(I, S.Zero)
  282. c, d = Q_map.get(S.One, S.Zero), Q_map.get(I, S.Zero)
  283. R = Poly(resultant(c, d, v), u)
  284. R_u = _get_real_roots(R, u)
  285. if R_u is None:
  286. return None
  287. result = S.Zero
  288. for r_u in R_u.keys():
  289. C = Poly(c.xreplace({u: r_u}), v)
  290. if not C:
  291. # t was split into real and imaginary parts
  292. # and denom Q(u, v) = c + I*d. We just found
  293. # that c(r_u) is 0 so the roots are in d
  294. C = Poly(d.xreplace({u: r_u}), v)
  295. # we were going to reject roots from C that
  296. # did not set d to zero, but since we are now
  297. # using C = d and c is already 0, there is
  298. # nothing to check
  299. d = S.Zero
  300. R_v = _get_real_roots(C, v)
  301. if R_v is None:
  302. return None
  303. R_v_paired = [] # take one from each pair of conjugate roots
  304. for r_v in R_v:
  305. if r_v not in R_v_paired and -r_v not in R_v_paired:
  306. if r_v.is_negative or r_v.could_extract_minus_sign():
  307. R_v_paired.append(-r_v)
  308. elif not r_v.is_zero:
  309. R_v_paired.append(r_v)
  310. for r_v in R_v_paired:
  311. D = d.xreplace({u: r_u, v: r_v})
  312. if D.evalf(chop=True) != 0:
  313. continue
  314. A = Poly(a.xreplace({u: r_u, v: r_v}), x)
  315. B = Poly(b.xreplace({u: r_u, v: r_v}), x)
  316. AB = (A**2 + B**2).as_expr()
  317. result += r_u*log(AB) + r_v*log_to_atan(A, B)
  318. R_q = _get_real_roots(q, t)
  319. if R_q is None:
  320. return None
  321. for r in R_q.keys():
  322. result += r*log(h.as_expr().subs(t, r))
  323. return result