-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Fix integrate 1/sqrt(x**2-1) for heurisch #23573
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
Conversation
✅ Hi, I am the SymPy bot (v167). 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.11. Click here to see the pull request description that was parsed.
Update The release notes on the wiki have been updated. |
With checking |
I'll repeat what I said earlier: Unless there is a bug in the derivative of log I would rather just XFAIL the integrals test than make any changes to the derivative of log regardless of speed. While the speed does concern me the more important point is that the code for differentiation of logs should not be complicated by adding checks for random special cases. Just think for a moment how many other possible expression patterns we could check for here: this is clearly not a scalable way to implement differentiation. Ideally we would use much better algorithms for differentiation but that's impossible if those algorithms are expected to produce arbitrarily defined results in random special cases. |
It’s not a random special case. It helps fixing a long standing bug elsewhere, meanwhile provides a nicer result for derivative itself. SymPy is a rule-based library so there will never be too many rules to add (think about the model that SymPy tries to compete: mathematica, its size is perhaps 100x of SymPy, it even hard-codes solution for some special quintic equations). There are only awkward rules that should be replaced by smarter rules. |
That bug can be fixed without the change here. If necessary the integral test can be XFAILed. It previously succeeded only by depending on buggy behaviour in the derivative of Fixing Making this particular integral work is not a reason to change In [8]: a, b, c = symbols('a, b, c')
In [9]: F = log(b + 2*sqrt(c)*sqrt(a + b*x + c*x**2) + 2*c*x)
In [10]: F.diff(x)
Out[10]:
⎛b ⎞
2⋅√c⋅⎜─ + c⋅x⎟
⎝2 ⎠
─────────────────── + 2⋅c
________________
╱ 2
╲╱ a + b⋅x + c⋅x
────────────────────────────────────
________________
╱ 2
b + 2⋅√c⋅╲╱ a + b⋅x + c⋅x + 2⋅c⋅x That's a nice clear application of the chain rule for the In [7]: F.diff(x).factor()
Out[7]:
________________
3/2 ╱ 2
b⋅√c + 2⋅c ⋅x + 2⋅c⋅╲╱ a + b⋅x + c⋅x
──────────────────────────────────────────────────────────
________________ ⎛ ________________ ⎞
╱ 2 ⎜ ╱ 2 ⎟
╲╱ a + b⋅x + c⋅x ⋅⎝b + 2⋅√c⋅╲╱ a + b⋅x + c⋅x + 2⋅c⋅x⎠ The reason is that most simplification routines depend in some way on the polynomial code but the polynomial code doesn't understand how to treat In [3]: Poly(sqrt(c) + c**2)
Out[3]: Poly(c**2 + (sqrt(c)), c, sqrt(c), domain='ZZ')
In [4]: Poly(sqrt(c) + c**2).gens
Out[4]: (c, √c)
In [5]: Poly(sqrt(c) + c**2) ** 2
Out[5]: Poly(c**4 + 2*c**2*(sqrt(c)) + (sqrt(c))**2, c, sqrt(c), domain='ZZ') Notice that Something like this can make the polys code smarter about square-rooted symbols (although it needs to be applied in more places): diff --git a/sympy/polys/polyutils.py b/sympy/polys/polyutils.py
index 82c8c83..1798171 100644
--- a/sympy/polys/polyutils.py
+++ b/sympy/polys/polyutils.py
@@ -273,6 +273,28 @@ def _is_coeff(factor):
reprs.append(terms)
+ #
+ # See if e.g. sqrt(c) and c are both in gens
+ # If they are then rewrite c as sqrt(c)**2
+ #
+ to_replace = {}
+ for g1 in gens:
+ if g1.is_Pow and g1.base in gens and g1.exp.is_Rational and g1.exp.p == 1:
+ to_replace[g1.base] = (g1, g1.exp.q)
+
+ if to_replace:
+
+ to_replace_set = to_replace.keys()
+
+ for terms in reprs:
+ for coeff, term in terms:
+ for g2 in to_replace_set & term:
+ exp2 = term.pop(g2)
+ g1, exp1 = to_replace[g2]
+ term[g1] = term.get(g1, 0) + exp1*exp2
+
+ gens -= to_replace_set
+
gens = _sort_gens(gens, opt=opt)
k, indices = len(gens), {}
With that you can have: In [13]: F.diff(x).factor()
Out[13]:
√c
───────────────────
________________
╱ 2
╲╱ a + b⋅x + c⋅x |
Yes. I have a local fix for polynomial but it breaks some solver tests and I haven't fixed them. |
How to XFAIL this test? I'm not familiar with ODE. sympy/sympy/solvers/ode/tests/test_single.py Lines 2860 to 2863 in 78bbfa8
|
You can do it like sympy/sympy/solvers/ode/tests/test_single.py Line 673 in 78bbfa8
The list is the list of dsolve hints that fails (check classify_ode ).
|
Why does it fail? |
I've mentioned it before, but it would really help to have a separate method for computing derivatives just for heurisch. See #4300 and the cross-linked issues. |
5af5a34
to
7f10d3b
Compare
The 2 new solutions are valid only on a given range related to from sympy import *
x,C1 = symbols('x C1')
def test(f):
equ = f**2 + (x*sqrt(f**2 - x**2) - x*f)*f.diff(x)
print(equ.subs({x: -1, C1: -1}).n())
print(equ.subs({x: 1, C1: -1}).n())
print(equ.subs({x: -1, C1: 1}).n())
print(equ.subs({x: 1, C1: 1}).n())
test(-sqrt(-x*exp(2*C1)/(x - 2*exp(C1))))
test(sqrt(-x*exp(2*C1)/(x - 2*exp(C1)))) output:
I XFAIL it for checksol. |
I saw PRs aimed for it. They are not finished as the problem is not as trivial as previously thought. To fix #23566 that seems over complicated. |
Benchmark results from GitHub Actions Lower numbers are good, higher numbers are bad. A ratio less than 1 Significantly changed benchmark results (PR vs master) Significantly changed benchmark results (master vs previous release) before after ratio
[77f1d79c] [d142bcfd]
<sympy-1.10.1^0>
+ 129±3ms 225±10ms 1.75 sum.TimeSum.time_doit
Full benchmark results can be found as artifacts in GitHub Actions |
Would the previous solution also fail if the derivative of |
No, to my surprise it still passes. sympy/sympy/solvers/ode/subscheck.py Line 276 in 6a04cdb
So whether x,y=symbols('x y', positive=True)
simplify(sqrt(x+y)*sqrt(x-y)) # == sqrt(x**2-y**2) |
Yeah, that shouldn't be there... |
terms.add(acosh(sqrt(M[a]/M[b])*x)) | ||
dF = 1/sqrt(M[a]*x**2 - M[b]) | ||
F = log(2*sqrt(M[a])*sqrt(M[a]*x**2 - M[b]) + 2*M[a]*x)/sqrt(M[a]) | ||
dcache.cache[F] = dF # hack: F.diff(x) doesn't automatically simplify to f |
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.
We should open a separate issue about this not simplifying.
I think this looks good. |
7f10d3b
to
e22bc53
Compare
References to other Issues or PRs
#23566 heurisch part. It shouldn't be closed.
Brief description of what is fixed or changed
Use
log
instead ofacosh
.Implement a special case for evaluating derivative of log.Patch heurisch to simplify a special case of derivative.Other comments
Release Notes