-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reuse pole detection/reintegration logic from heurisch_wrapper in meijerint #20270
base: master
Are you sure you want to change the base?
Conversation
…ntegrate (since we're already checking for poles in the denominator); this avoids unnecessary calls to denoms/solve.
…tion that can be shared by heurisch and meijerg
…of a nan by solve when handling a Piecewise (see also sympygh-20269). Because we're calling solve on an equation derived from the integration result anyway, it should be safe to ignore any special cases arrising from poles in the denominator of this integration result.
✅ Hi, I am the SymPy bot (v161). I'm here to help you write a release notes entry. Please read the guide on how to write release notes. Your release notes are in good order. Here is what the release notes will look like:
This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.8. Click here to see the pull request description that was parsed.
|
Regarding the change to When solving this: sympy/sympy/solvers/ode/tests/test_ode.py Line 241 in 87a885b
This integral comes up:
which after these changes results in
Then calling solve on an equation derived from this:
results in the solution to the ode involving
I considered just changing the definition of the constant to |
I don't think it's a good idea to put assumptions on the constants introduced by the ode solver. That would be very opaque to end-users. |
Should we be using this wrapper in manualintegrate too? |
How about implementing a wrapper like this in the main code? Different integrators could be used in special cases. |
>>> from sympy import sin | ||
>>> from sympy.abc import x, n, k | ||
>>> meijerint_indefinite_wrapper(sin(x**(n*k + 1)), x) | ||
Piecewise((x*x**(k*n)*x**(k*n/(k*n + 1))*x**(1/(k*n + 1))*gamma(1/2 + 1/(2*(k*n + 1)))*hyper((1/2 + 1/(2*(k*n + 1)),), (3/2, 3/2 + 1/(2*(k*n + 1))), -x**2*x**(2*k*n)/4)/(2*k*n*gamma(3/2 + 1/(2*(k*n + 1))) + 2*gamma(3/2 + 1/(2*(k*n + 1)))), Ne(k, -1/n)), (x*sin(1), True)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can line wrap this and the doctest will still pass.
|
||
def reintegrator(f, x): | ||
expr = heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, | ||
unnecessary_permutations, _try_heurisch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why any of the integration algorithms should reuse only that algorithm for the recursive integration, rather than integrate(). Does anything break for heurisch if we change this? Heurisch in particular is the slowest algorithm, which is always tried last, so it's better to avoid using it if we can. It's also unclear to me that all these arguments to heurisch should be the same for the new substituted integrand.
The only place where it might make sense to keep the same algorithm would be Risch, if we can be sure the "simplified" integral is also of the form that Risch can handle (most likely this is the case, though I'd have to look at the specific places where the divisions happen to be sure). Even then, though, Risch has many NotImplementedError holes, so it still might not make sense there.
|
||
|
||
def pole_reintegrate(f, x, antideriv, | ||
reintegrator=lambda f, x: integrate(f, x, conds="none")): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another concern is that if someone passes a specific algorithm to integrate, like integrate(meijerg=True), should the reintegrator also only use that algorithm? I'm unsure. It does seem to make sense for the flag to be passed through, but it could lead to the confusing situation where integrate(f, x)
is computed with, say, meijerg, but integrate(f, x, meijerg=True)
doesn't produce the same result, because meijerg isn't what is actually used by the default reintegrator.
Takes an integrand f with integration variable x with an initially | ||
computed antiderivative and checks for poles in the denominator. For each | ||
of these poles, the integral is reevaluated, and the final integration | ||
result is given in terms of a Piecewise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the arguments here, or give an example?
I'm not sure if you already explained this, but what is the advantage of having a function that checks for denominators rather than hooking into the specific place(s) in the integration code where a division happens? It seems to me that the latter would be preferred if it is possible, as it would avoid unnecessarily splitting out cases where a constant also appears in the denominator of the integrand. I see that the helper function here tries to handle those cases, but it still requires extra work to detect them (if I am reading the code correctly, there is a solve() call for every denominator). |
slns0 = [] | ||
for d in denoms(f): | ||
try: | ||
slns0 += solve(d, dict=True, exclude=(x,)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have to solve here too? Can't we filter out the common denominators from f
without calling solve
? That would save a lot of unnecessary work if f
has a lot of complicated symbolic constant expressions in denominators, which are unrelated to the integration variable. It's also possible for such expressions to not be solvable, in which case they would be filtered if I am reading this code correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comes from #2112 but we can just check if d.subs(already_computed_solution) == 0
but still looking at optimizations. Have a few changes coming regarding the comments.
I experimented with wrapping the entire _eval_integral but found this is unnecessary with manualintegrate. I seem to have a fix for meijerg that follows similar logic to manualintegrate and doesn't require solve or denoms etc. Still have a few lingering test cases to fix but will send an update. |
@ehren Some tests seem to fail. Would you like to debug these? Please let us know if you are still working on it. Thanks for your contributions. |
References to other Issues or PRs
Fixes #5949
Brief description of what is fixed or changed
Moves the code in
heurisch_wrapper
to a shared function,pole_reintegrate
that can also be used bymeijerint
(via a newmeijerint_indefinite_wrapper
.Other comments
reused heurisch_wrapper logic originally from #1622
Release Notes
meijerg=True
option ofintegrate
now returns aPiecewise
handling cases where the indefinite integral returned bymeijerint_indefinite
could have a zero denominator. This affects any uses of theintegrals
module that rely onmeijerint
. This behavior can be disabled using theintegrate
optionconds='none'
.