heurisch.py 25 KB

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