heurisch.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. from __future__ import annotations
  2. from itertools import permutations
  3. from functools import reduce
  4. from sympy.core.add import Add
  5. from sympy.core.basic import Basic
  6. from sympy.core.mul import Mul
  7. from sympy.core.symbol import Wild, Dummy, Symbol
  8. from sympy.core.basic import sympify
  9. from sympy.core.numbers import Rational, pi, I
  10. from sympy.core.relational import Eq, Ne
  11. from sympy.core.singleton import S
  12. from sympy.core.sorting import ordered
  13. from sympy.core.traversal import iterfreeargs
  14. from sympy.functions import exp, sin, cos, tan, cot, asin, atan
  15. from sympy.functions import log, sinh, cosh, tanh, coth, asinh
  16. from sympy.functions import sqrt, erf, erfi, li, Ei
  17. from sympy.functions import besselj, bessely, besseli, besselk
  18. from sympy.functions import hankel1, hankel2, jn, yn
  19. from sympy.functions.elementary.complexes import Abs, re, im, sign, arg
  20. from sympy.functions.elementary.exponential import LambertW
  21. from sympy.functions.elementary.integers import floor, ceiling
  22. from sympy.functions.elementary.piecewise import Piecewise
  23. from sympy.functions.special.delta_functions import Heaviside, DiracDelta
  24. from sympy.simplify.radsimp import collect
  25. from sympy.logic.boolalg import And, Or
  26. from sympy.utilities.iterables import uniq
  27. from sympy.polys import quo, gcd, lcm, factor_list, cancel, PolynomialError
  28. from sympy.polys.monomials import itermonomials
  29. from sympy.polys.polyroots import root_factors
  30. from sympy.polys.rings import PolyRing
  31. from sympy.polys.solvers import solve_lin_sys
  32. from sympy.polys.constructor import construct_domain
  33. from sympy.integrals.integrals import integrate
  34. def components(f, x):
  35. """
  36. Returns a set of all functional components of the given expression
  37. which includes symbols, function applications and compositions and
  38. non-integer powers. Fractional powers are collected with
  39. minimal, positive exponents.
  40. Examples
  41. ========
  42. >>> from sympy import cos, sin
  43. >>> from sympy.abc import x
  44. >>> from sympy.integrals.heurisch import components
  45. >>> components(sin(x)*cos(x)**2, x)
  46. {x, sin(x), cos(x)}
  47. See Also
  48. ========
  49. heurisch
  50. """
  51. result = set()
  52. if f.has_free(x):
  53. if f.is_symbol and f.is_commutative:
  54. result.add(f)
  55. elif f.is_Function or f.is_Derivative:
  56. for g in f.args:
  57. result |= components(g, x)
  58. result.add(f)
  59. elif f.is_Pow:
  60. result |= components(f.base, x)
  61. if not f.exp.is_Integer:
  62. if f.exp.is_Rational:
  63. result.add(f.base**Rational(1, f.exp.q))
  64. else:
  65. result |= components(f.exp, x) | {f}
  66. else:
  67. for g in f.args:
  68. result |= components(g, x)
  69. return result
  70. # name -> [] of symbols
  71. _symbols_cache: dict[str, list[Dummy]] = {}
  72. # NB @cacheit is not convenient here
  73. def _symbols(name, n):
  74. """get vector of symbols local to this module"""
  75. try:
  76. lsyms = _symbols_cache[name]
  77. except KeyError:
  78. lsyms = []
  79. _symbols_cache[name] = lsyms
  80. while len(lsyms) < n:
  81. lsyms.append( Dummy('%s%i' % (name, len(lsyms))) )
  82. return lsyms[:n]
  83. def heurisch_wrapper(f, x, rewrite=False, hints=None, mappings=None, retries=3,
  84. degree_offset=0, unnecessary_permutations=None,
  85. _try_heurisch=None):
  86. """
  87. A wrapper around the heurisch integration algorithm.
  88. Explanation
  89. ===========
  90. This method takes the result from heurisch and checks for poles in the
  91. denominator. For each of these poles, the integral is reevaluated, and
  92. the final integration result is given in terms of a Piecewise.
  93. Examples
  94. ========
  95. >>> from sympy import cos, symbols
  96. >>> from sympy.integrals.heurisch import heurisch, heurisch_wrapper
  97. >>> n, x = symbols('n x')
  98. >>> heurisch(cos(n*x), x)
  99. sin(n*x)/n
  100. >>> heurisch_wrapper(cos(n*x), x)
  101. Piecewise((sin(n*x)/n, Ne(n, 0)), (x, True))
  102. See Also
  103. ========
  104. heurisch
  105. """
  106. from sympy.solvers.solvers import solve, denoms
  107. f = sympify(f)
  108. if not f.has_free(x):
  109. return f*x
  110. res = heurisch(f, x, rewrite, hints, mappings, retries, degree_offset,
  111. unnecessary_permutations, _try_heurisch)
  112. if not isinstance(res, Basic):
  113. return res
  114. # We consider each denominator in the expression, and try to find
  115. # cases where one or more symbolic denominator might be zero. The
  116. # conditions for these cases are stored in the list slns.
  117. #
  118. # Since denoms returns a set we use ordered. This is important because the
  119. # ordering of slns determines the order of the resulting Piecewise so we
  120. # need a deterministic order here to make the output deterministic.
  121. slns = []
  122. for d in ordered(denoms(res)):
  123. try:
  124. slns += solve([d], dict=True, exclude=(x,))
  125. except NotImplementedError:
  126. pass
  127. if not slns:
  128. return res
  129. slns = list(uniq(slns))
  130. # Remove the solutions corresponding to poles in the original expression.
  131. slns0 = []
  132. for d in denoms(f):
  133. try:
  134. slns0 += solve([d], dict=True, exclude=(x,))
  135. except NotImplementedError:
  136. pass
  137. slns = [s for s in slns if s not in slns0]
  138. if not slns:
  139. return res
  140. if len(slns) > 1:
  141. eqs = []
  142. for sub_dict in slns:
  143. eqs.extend([Eq(key, value) for key, value in sub_dict.items()])
  144. slns = solve(eqs, dict=True, exclude=(x,)) + slns
  145. # For each case listed in the list slns, we reevaluate the integral.
  146. pairs = []
  147. for sub_dict in slns:
  148. expr = heurisch(f.subs(sub_dict), x, rewrite, hints, mappings, retries,
  149. degree_offset, unnecessary_permutations,
  150. _try_heurisch)
  151. cond = And(*[Eq(key, value) for key, value in sub_dict.items()])
  152. generic = Or(*[Ne(key, value) for key, value in sub_dict.items()])
  153. if expr is None:
  154. expr = integrate(f.subs(sub_dict),x)
  155. pairs.append((expr, cond))
  156. # If there is one condition, put the generic case first. Otherwise,
  157. # doing so may lead to longer Piecewise formulas
  158. if len(pairs) == 1:
  159. pairs = [(heurisch(f, x, rewrite, hints, mappings, retries,
  160. degree_offset, unnecessary_permutations,
  161. _try_heurisch),
  162. generic),
  163. (pairs[0][0], True)]
  164. else:
  165. pairs.append((heurisch(f, x, rewrite, hints, mappings, retries,
  166. degree_offset, unnecessary_permutations,
  167. _try_heurisch),
  168. True))
  169. return Piecewise(*pairs)
  170. class BesselTable:
  171. """
  172. Derivatives of Bessel functions of orders n and n-1
  173. in terms of each other.
  174. See the docstring of DiffCache.
  175. """
  176. def __init__(self):
  177. self.table = {}
  178. self.n = Dummy('n')
  179. self.z = Dummy('z')
  180. self._create_table()
  181. def _create_table(t):
  182. table, n, z = t.table, t.n, t.z
  183. for f in (besselj, bessely, hankel1, hankel2):
  184. table[f] = (f(n-1, z) - n*f(n, z)/z,
  185. (n-1)*f(n-1, z)/z - f(n, z))
  186. f = besseli
  187. table[f] = (f(n-1, z) - n*f(n, z)/z,
  188. (n-1)*f(n-1, z)/z + f(n, z))
  189. f = besselk
  190. table[f] = (-f(n-1, z) - n*f(n, z)/z,
  191. (n-1)*f(n-1, z)/z - f(n, z))
  192. for f in (jn, yn):
  193. table[f] = (f(n-1, z) - (n+1)*f(n, z)/z,
  194. (n-1)*f(n-1, z)/z - f(n, z))
  195. def diffs(t, f, n, z):
  196. if f in t.table:
  197. diff0, diff1 = t.table[f]
  198. repl = [(t.n, n), (t.z, z)]
  199. return (diff0.subs(repl), diff1.subs(repl))
  200. def has(t, f):
  201. return f in t.table
  202. _bessel_table = None
  203. class DiffCache:
  204. """
  205. Store for derivatives of expressions.
  206. Explanation
  207. ===========
  208. The standard form of the derivative of a Bessel function of order n
  209. contains two Bessel functions of orders n-1 and n+1, respectively.
  210. Such forms cannot be used in parallel Risch algorithm, because
  211. there is a linear recurrence relation between the three functions
  212. while the algorithm expects that functions and derivatives are
  213. represented in terms of algebraically independent transcendentals.
  214. The solution is to take two of the functions, e.g., those of orders
  215. n and n-1, and to express the derivatives in terms of the pair.
  216. To guarantee that the proper form is used the two derivatives are
  217. cached as soon as one is encountered.
  218. Derivatives of other functions are also cached at no extra cost.
  219. All derivatives are with respect to the same variable `x`.
  220. """
  221. def __init__(self, x):
  222. self.cache = {}
  223. self.x = x
  224. global _bessel_table
  225. if not _bessel_table:
  226. _bessel_table = BesselTable()
  227. def get_diff(self, f):
  228. cache = self.cache
  229. if f in cache:
  230. pass
  231. elif (not hasattr(f, 'func') or
  232. not _bessel_table.has(f.func)):
  233. cache[f] = cancel(f.diff(self.x))
  234. else:
  235. n, z = f.args
  236. d0, d1 = _bessel_table.diffs(f.func, n, z)
  237. dz = self.get_diff(z)
  238. cache[f] = d0*dz
  239. cache[f.func(n-1, z)] = d1*dz
  240. return cache[f]
  241. def heurisch(f, x, rewrite=False, hints=None, mappings=None, retries=3,
  242. degree_offset=0, unnecessary_permutations=None,
  243. _try_heurisch=None):
  244. """
  245. Compute indefinite integral using heuristic Risch algorithm.
  246. Explanation
  247. ===========
  248. This is a heuristic approach to indefinite integration in finite
  249. terms using the extended heuristic (parallel) Risch algorithm, based
  250. on Manuel Bronstein's "Poor Man's Integrator".
  251. The algorithm supports various classes of functions including
  252. transcendental elementary or special functions like Airy,
  253. Bessel, Whittaker and Lambert.
  254. Note that this algorithm is not a decision procedure. If it isn't
  255. able to compute the antiderivative for a given function, then this is
  256. not a proof that such a functions does not exist. One should use
  257. recursive Risch algorithm in such case. It's an open question if
  258. this algorithm can be made a full decision procedure.
  259. This is an internal integrator procedure. You should use top level
  260. 'integrate' function in most cases, as this procedure needs some
  261. preprocessing steps and otherwise may fail.
  262. Specification
  263. =============
  264. heurisch(f, x, rewrite=False, hints=None)
  265. where
  266. f : expression
  267. x : symbol
  268. rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh'
  269. hints -> a list of functions that may appear in anti-derivate
  270. - hints = None --> no suggestions at all
  271. - hints = [ ] --> try to figure out
  272. - hints = [f1, ..., fn] --> we know better
  273. Examples
  274. ========
  275. >>> from sympy import tan
  276. >>> from sympy.integrals.heurisch import heurisch
  277. >>> from sympy.abc import x, y
  278. >>> heurisch(y*tan(x), x)
  279. y*log(tan(x)**2 + 1)/2
  280. See Manuel Bronstein's "Poor Man's Integrator":
  281. References
  282. ==========
  283. .. [1] https://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html
  284. For more information on the implemented algorithm refer to:
  285. .. [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration
  286. Method and its Implementation in Maple, Proceedings of
  287. ISSAC'89, ACM Press, 212-217.
  288. .. [3] J. H. Davenport, On the Parallel Risch Algorithm (I),
  289. Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157.
  290. .. [4] J. H. Davenport, On the Parallel Risch Algorithm (III):
  291. Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6.
  292. .. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch
  293. Algorithm (II), ACM Transactions on Mathematical
  294. Software 11 (1985), 356-362.
  295. See Also
  296. ========
  297. sympy.integrals.integrals.Integral.doit
  298. sympy.integrals.integrals.Integral
  299. sympy.integrals.heurisch.components
  300. """
  301. f = sympify(f)
  302. # There are some functions that Heurisch cannot currently handle,
  303. # so do not even try.
  304. # Set _try_heurisch=True to skip this check
  305. if _try_heurisch is not True:
  306. if f.has(Abs, re, im, sign, Heaviside, DiracDelta, floor, ceiling, arg):
  307. return
  308. if not f.has_free(x):
  309. return f*x
  310. if not f.is_Add:
  311. indep, f = f.as_independent(x)
  312. else:
  313. indep = S.One
  314. rewritables = {
  315. (sin, cos, cot): tan,
  316. (sinh, cosh, coth): tanh,
  317. }
  318. if rewrite:
  319. for candidates, rule in rewritables.items():
  320. f = f.rewrite(candidates, rule)
  321. else:
  322. for candidates in rewritables.keys():
  323. if f.has(*candidates):
  324. break
  325. else:
  326. rewrite = True
  327. terms = components(f, x)
  328. dcache = DiffCache(x)
  329. if hints is not None:
  330. if not hints:
  331. a = Wild('a', exclude=[x])
  332. b = Wild('b', exclude=[x])
  333. c = Wild('c', exclude=[x])
  334. for g in set(terms): # using copy of terms
  335. if g.is_Function:
  336. if isinstance(g, li):
  337. M = g.args[0].match(a*x**b)
  338. if M is not None:
  339. terms.add( x*(li(M[a]*x**M[b]) - (M[a]*x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) )
  340. #terms.add( x*(li(M[a]*x**M[b]) - (x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) )
  341. #terms.add( x*(li(M[a]*x**M[b]) - x*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) )
  342. #terms.add( li(M[a]*x**M[b]) - Ei((M[b]+1)*log(M[a]*x**M[b])/M[b]) )
  343. elif isinstance(g, exp):
  344. M = g.args[0].match(a*x**2)
  345. if M is not None:
  346. if M[a].is_positive:
  347. terms.add(erfi(sqrt(M[a])*x))
  348. else: # M[a].is_negative or unknown
  349. terms.add(erf(sqrt(-M[a])*x))
  350. M = g.args[0].match(a*x**2 + b*x + c)
  351. if M is not None:
  352. if M[a].is_positive:
  353. terms.add(sqrt(pi/4*(-M[a]))*exp(M[c] - M[b]**2/(4*M[a]))*
  354. erfi(sqrt(M[a])*x + M[b]/(2*sqrt(M[a]))))
  355. elif M[a].is_negative:
  356. terms.add(sqrt(pi/4*(-M[a]))*exp(M[c] - M[b]**2/(4*M[a]))*
  357. erf(sqrt(-M[a])*x - M[b]/(2*sqrt(-M[a]))))
  358. M = g.args[0].match(a*log(x)**2)
  359. if M is not None:
  360. if M[a].is_positive:
  361. terms.add(erfi(sqrt(M[a])*log(x) + 1/(2*sqrt(M[a]))))
  362. if M[a].is_negative:
  363. terms.add(erf(sqrt(-M[a])*log(x) - 1/(2*sqrt(-M[a]))))
  364. elif g.is_Pow:
  365. if g.exp.is_Rational and g.exp.q == 2:
  366. M = g.base.match(a*x**2 + b)
  367. if M is not None and M[b].is_positive:
  368. if M[a].is_positive:
  369. terms.add(asinh(sqrt(M[a]/M[b])*x))
  370. elif M[a].is_negative:
  371. terms.add(asin(sqrt(-M[a]/M[b])*x))
  372. M = g.base.match(a*x**2 - b)
  373. if M is not None and M[b].is_positive:
  374. if M[a].is_positive:
  375. dF = 1/sqrt(M[a]*x**2 - M[b])
  376. F = log(2*sqrt(M[a])*sqrt(M[a]*x**2 - M[b]) + 2*M[a]*x)/sqrt(M[a])
  377. dcache.cache[F] = dF # hack: F.diff(x) doesn't automatically simplify to f
  378. terms.add(F)
  379. elif M[a].is_negative:
  380. terms.add(-M[b]/2*sqrt(-M[a])*
  381. atan(sqrt(-M[a])*x/sqrt(M[a]*x**2 - M[b])))
  382. else:
  383. terms |= set(hints)
  384. for g in set(terms): # using copy of terms
  385. terms |= components(dcache.get_diff(g), x)
  386. # XXX: The commented line below makes heurisch more deterministic wrt
  387. # PYTHONHASHSEED and the iteration order of sets. There are other places
  388. # where sets are iterated over but this one is possibly the most important.
  389. # Theoretically the order here should not matter but different orderings
  390. # can expose potential bugs in the different code paths so potentially it
  391. # is better to keep the non-determinism.
  392. #
  393. # terms = list(ordered(terms))
  394. # TODO: caching is significant factor for why permutations work at all. Change this.
  395. V = _symbols('x', len(terms))
  396. # sort mapping expressions from largest to smallest (last is always x).
  397. mapping = list(reversed(list(zip(*ordered( #
  398. [(a[0].as_independent(x)[1], a) for a in zip(terms, V)])))[1])) #
  399. rev_mapping = {v: k for k, v in mapping} #
  400. if mappings is None: #
  401. # optimizing the number of permutations of mapping #
  402. assert mapping[-1][0] == x # if not, find it and correct this comment
  403. unnecessary_permutations = [mapping.pop(-1)]
  404. mappings = permutations(mapping)
  405. else:
  406. unnecessary_permutations = unnecessary_permutations or []
  407. def _substitute(expr):
  408. return expr.subs(mapping)
  409. for mapping in mappings:
  410. mapping = list(mapping)
  411. mapping = mapping + unnecessary_permutations
  412. diffs = [ _substitute(dcache.get_diff(g)) for g in terms ]
  413. denoms = [ g.as_numer_denom()[1] for g in diffs ]
  414. if all(h.is_polynomial(*V) for h in denoms) and _substitute(f).is_rational_function(*V):
  415. denom = reduce(lambda p, q: lcm(p, q, *V), denoms)
  416. break
  417. else:
  418. if not rewrite:
  419. result = heurisch(f, x, rewrite=True, hints=hints,
  420. unnecessary_permutations=unnecessary_permutations)
  421. if result is not None:
  422. return indep*result
  423. return None
  424. numers = [ cancel(denom*g) for g in diffs ]
  425. def _derivation(h):
  426. return Add(*[ d * h.diff(v) for d, v in zip(numers, V) ])
  427. def _deflation(p):
  428. for y in V:
  429. if not p.has(y):
  430. continue
  431. if _derivation(p) is not S.Zero:
  432. c, q = p.as_poly(y).primitive()
  433. return _deflation(c)*gcd(q, q.diff(y)).as_expr()
  434. return p
  435. def _splitter(p):
  436. for y in V:
  437. if not p.has(y):
  438. continue
  439. if _derivation(y) is not S.Zero:
  440. c, q = p.as_poly(y).primitive()
  441. q = q.as_expr()
  442. h = gcd(q, _derivation(q), y)
  443. s = quo(h, gcd(q, q.diff(y), y), y)
  444. c_split = _splitter(c)
  445. if s.as_poly(y).degree() == 0:
  446. return (c_split[0], q * c_split[1])
  447. q_split = _splitter(cancel(q / s))
  448. return (c_split[0]*q_split[0]*s, c_split[1]*q_split[1])
  449. return (S.One, p)
  450. special = {}
  451. for term in terms:
  452. if term.is_Function:
  453. if isinstance(term, tan):
  454. special[1 + _substitute(term)**2] = False
  455. elif isinstance(term, tanh):
  456. special[1 + _substitute(term)] = False
  457. special[1 - _substitute(term)] = False
  458. elif isinstance(term, LambertW):
  459. special[_substitute(term)] = True
  460. F = _substitute(f)
  461. P, Q = F.as_numer_denom()
  462. u_split = _splitter(denom)
  463. v_split = _splitter(Q)
  464. polys = set(list(v_split) + [ u_split[0] ] + list(special.keys()))
  465. s = u_split[0] * Mul(*[ k for k, v in special.items() if v ])
  466. polified = [ p.as_poly(*V) for p in [s, P, Q] ]
  467. if None in polified:
  468. return None
  469. #--- definitions for _integrate
  470. a, b, c = [ p.total_degree() for p in polified ]
  471. poly_denom = (s * v_split[0] * _deflation(v_split[1])).as_expr()
  472. def _exponent(g):
  473. if g.is_Pow:
  474. if g.exp.is_Rational and g.exp.q != 1:
  475. if g.exp.p > 0:
  476. return g.exp.p + g.exp.q - 1
  477. else:
  478. return abs(g.exp.p + g.exp.q)
  479. else:
  480. return 1
  481. elif not g.is_Atom and g.args:
  482. return max([ _exponent(h) for h in g.args ])
  483. else:
  484. return 1
  485. A, B = _exponent(f), a + max(b, c)
  486. if A > 1 and B > 1:
  487. monoms = tuple(ordered(itermonomials(V, A + B - 1 + degree_offset)))
  488. else:
  489. monoms = tuple(ordered(itermonomials(V, A + B + degree_offset)))
  490. poly_coeffs = _symbols('A', len(monoms))
  491. poly_part = Add(*[ poly_coeffs[i]*monomial
  492. for i, monomial in enumerate(monoms) ])
  493. reducibles = set()
  494. for poly in ordered(polys):
  495. coeff, factors = factor_list(poly, *V)
  496. reducibles.add(coeff)
  497. for fact, mul in factors:
  498. reducibles.add(fact)
  499. def _integrate(field=None):
  500. atans = set()
  501. pairs = set()
  502. if field == 'Q':
  503. irreducibles = set(reducibles)
  504. else:
  505. setV = set(V)
  506. irreducibles = set()
  507. for poly in ordered(reducibles):
  508. zV = setV & set(iterfreeargs(poly))
  509. for z in ordered(zV):
  510. s = set(root_factors(poly, z, filter=field))
  511. irreducibles |= s
  512. break
  513. log_part, atan_part = [], []
  514. for poly in ordered(irreducibles):
  515. m = collect(poly, I, evaluate=False)
  516. y = m.get(I, S.Zero)
  517. if y:
  518. x = m.get(S.One, S.Zero)
  519. if x.has(I) or y.has(I):
  520. continue # nontrivial x + I*y
  521. pairs.add((x, y))
  522. irreducibles.remove(poly)
  523. while pairs:
  524. x, y = pairs.pop()
  525. if (x, -y) in pairs:
  526. pairs.remove((x, -y))
  527. # Choosing b with no minus sign
  528. if y.could_extract_minus_sign():
  529. y = -y
  530. irreducibles.add(x*x + y*y)
  531. atans.add(atan(x/y))
  532. else:
  533. irreducibles.add(x + I*y)
  534. B = _symbols('B', len(irreducibles))
  535. C = _symbols('C', len(atans))
  536. # Note: the ordering matters here
  537. for poly, b in reversed(list(zip(ordered(irreducibles), B))):
  538. if poly.has(*V):
  539. poly_coeffs.append(b)
  540. log_part.append(b * log(poly))
  541. for poly, c in reversed(list(zip(ordered(atans), C))):
  542. if poly.has(*V):
  543. poly_coeffs.append(c)
  544. atan_part.append(c * poly)
  545. # TODO: Currently it's better to use symbolic expressions here instead
  546. # of rational functions, because it's simpler and FracElement doesn't
  547. # give big speed improvement yet. This is because cancellation is slow
  548. # due to slow polynomial GCD algorithms. If this gets improved then
  549. # revise this code.
  550. candidate = poly_part/poly_denom + Add(*log_part) + Add(*atan_part)
  551. h = F - _derivation(candidate) / denom
  552. raw_numer = h.as_numer_denom()[0]
  553. # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field
  554. # that we have to determine. We can't use simply atoms() because log(3),
  555. # sqrt(y) and similar expressions can appear, leading to non-trivial
  556. # domains.
  557. syms = set(poly_coeffs) | set(V)
  558. non_syms = set()
  559. def find_non_syms(expr):
  560. if expr.is_Integer or expr.is_Rational:
  561. pass # ignore trivial numbers
  562. elif expr in syms:
  563. pass # ignore variables
  564. elif not expr.has_free(*syms):
  565. non_syms.add(expr)
  566. elif expr.is_Add or expr.is_Mul or expr.is_Pow:
  567. list(map(find_non_syms, expr.args))
  568. else:
  569. # TODO: Non-polynomial expression. This should have been
  570. # filtered out at an earlier stage.
  571. raise PolynomialError
  572. try:
  573. find_non_syms(raw_numer)
  574. except PolynomialError:
  575. return None
  576. else:
  577. ground, _ = construct_domain(non_syms, field=True)
  578. coeff_ring = PolyRing(poly_coeffs, ground)
  579. ring = PolyRing(V, coeff_ring)
  580. try:
  581. numer = ring.from_expr(raw_numer)
  582. except ValueError:
  583. raise PolynomialError
  584. solution = solve_lin_sys(numer.coeffs(), coeff_ring, _raw=False)
  585. if solution is None:
  586. return None
  587. else:
  588. return candidate.xreplace(solution).xreplace(
  589. dict(zip(poly_coeffs, [S.Zero]*len(poly_coeffs))))
  590. if all(isinstance(_, Symbol) for _ in V):
  591. more_free = F.free_symbols - set(V)
  592. else:
  593. Fd = F.as_dummy()
  594. more_free = Fd.xreplace(dict(zip(V, (Dummy() for _ in V)))
  595. ).free_symbols & Fd.free_symbols
  596. if not more_free:
  597. # all free generators are identified in V
  598. solution = _integrate('Q')
  599. if solution is None:
  600. solution = _integrate()
  601. else:
  602. solution = _integrate()
  603. if solution is not None:
  604. antideriv = solution.subs(rev_mapping)
  605. antideriv = cancel(antideriv).expand()
  606. if antideriv.is_Add:
  607. antideriv = antideriv.as_independent(x)[1]
  608. return indep*antideriv
  609. else:
  610. if retries >= 0:
  611. result = heurisch(f, x, mappings=mappings, rewrite=rewrite, hints=hints, retries=retries - 1, unnecessary_permutations=unnecessary_permutations)
  612. if result is not None:
  613. return indep*result
  614. return None