3

I want to solve my other question here so I need sympy to return an error whenever there is no analytical/symbolic solution for and integral.

For example if I try :

from sympy import *
init_printing(use_unicode=False, wrap_line=False, no_global=True)
x = Symbol('x')
integrate(1/cos(x**2), x)

It just [pretty] prints the integral itself

enter image description here

without solving and/or giving an error about not being able to solve it!

P.S. I have also asked this question here on Reddit.

Foad S. Farimani
  • 12,396
  • 15
  • 78
  • 193
  • 1
    Could you please paste the code as [text instead of screenshots](https://meta.stackoverflow.com/a/285557/3005167)? (The resulting expressions in form of images is fine.) – MB-F Apr 26 '18 at 09:41
  • @kazemakase sure. Actually the upper half of the image is not that relevant. that's why I just put an image. – Foad S. Farimani Apr 26 '18 at 09:43
  • 1
    Thank you for the update. It's a convenience thing - if people can easily copy and paste your code they are more likely to write an answer. For me that is the most important reason :) – MB-F Apr 26 '18 at 10:34
  • @kazemakase thanks for your support man. very appreciated. ممنونم :) – Foad S. Farimani Apr 26 '18 at 10:35
  • @kazemakase would you please undlete your post. I don't have a back up yet. – Foad S. Farimani Apr 26 '18 at 15:59
  • 1
    I can do so if you really want... but @bro has pointed out that the approach does not work as soon the expression gets more complicated. It does not even work for `integrate(2/cos(x**3), x)` because it factors the multiplication out so the outermost operation is a multiplication and not an integral. His/her answer is correct I think. Still want me to (temporarilly) undelete my wrong answer? :) – MB-F Apr 27 '18 at 06:19
  • would you please put the main command here in the comments? – Foad S. Farimani Apr 27 '18 at 07:46
  • 1
    `result = sp.integrate(1 / sp.cos(x**3))` ; `isinstance(result, sp.Integral)` ... that one? – MB-F Apr 27 '18 at 08:13
  • @kazemakase see my comment under [bro post](https://stackoverflow.com/a/50047206/4999991) or see [this Jupyter notebook on GitHub](https://gist.github.com/Foadsf/00185794133b22628f87a4a0f1f18933) – Foad S. Farimani Apr 27 '18 at 15:44

1 Answers1

6

A "symbolic" solution always exists: I just invented a new function intcos(x), which by definition is the antiderivative of 1/cos(x**2). Now this integral has a symbolic solution!

For the question to be rigorously answerable, one has to restrict the class of functions allowed in the answer. Typically one considers elementary functions. As SymPy integral reference explains, the Risch algorithm it employs can prove that some functions do not have elementary antiderivatives. Use the option risch=True and check whether the return value is an instance of sympy.integrals.risch.NonElementaryIntegral

from sympy.integrals.risch import NonElementaryIntegral
isinstance(integrate(1/exp(x**2), x, risch=True), NonElementaryIntegral)  # True

However, since Risch algorithm implementation is incomplete, in many cases like 1/cos(x**2) it returns an ordinary Integral object. This means it was not able to either find an elementary antiderivative or prove that one does not exist.

For this example, it helps to rewrite the trigonometric function in terms of exponential, with rewrite(cos, exp):

isinstance(integrate((1/cos(x**2)).rewrite(cos, exp), x, risch=True), NonElementaryIntegral)  

returns True, so we know the integral is nonelementary.

Non-elementary antiderivatives

But often we don't really need an elementary function; something like Gamma or erf or Bessel functions may be okay; as long as it's some "known" function (which of course is a fuzzy term). The question becomes: how to tell if SymPy was able to integrate a specific expression or not? Use .has(Integral) check for that:

integrate(2/cos(x**2), x).has(Integral)   # True

(not isinstance(Integral) because the return value can be, like here, 2*Integral(1/cos(x**2), x).) This does not prove anything other than SymPy's failure to find the antiderivative. The antiderivative may well be a known function, even an elementary one.

  • Ok guys @bro and @kazemakase today finally I got the time to do some experiments and test the different methods you suggested. You may see them all here in [this notebook on GitHub](https://gist.github.com/Foadsf/00185794133b22628f87a4a0f1f18933). It seems the `.has(Integral)` is the correct answer, although it is very slow, defeating [the whole purpose](https://stackoverflow.com/questions/50037208/python-integration-using-both-scipy-and-sympy)! @kazemakase method is most of the times ok except when it is not. For example for `2/cos(x**2)` it fails. – Foad S. Farimani Apr 27 '18 at 15:42
  • 1
    @Foad If you need integration to exit quickly instead of trying various methods for a long time, use `risch=True` option. –  Apr 27 '18 at 16:08
  • then will it be as accurate as when there are no `risch=True` options? Is that the reason your first method is fast but fails on a majority of cases? – Foad S. Farimani Apr 27 '18 at 16:10
  • 1
    Using only Risch algorithm means two things: (1) fewer integrals will be evaluated; (2) `integrate` will return quicker. Whether this is the tradeoff you should make depends on your specific task. –  Apr 27 '18 at 16:47
  • running the experiment again without the `risch=True` options shows that both your methods work perfectly fine. Thanks a lot. I had no clue what it does the first time. – Foad S. Farimani Apr 27 '18 at 17:09