-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
updates to unrad
, continuous_domain
, roots_(cubic, quartic, quintic)
#21276
Conversation
✅ 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.9. Click here to see the pull request description that was parsed.
Update The release notes on the wiki have been updated. |
unrad
, fix quinticsunrad
, continuous_domain
, and quintic solving
a01256b
to
23731c9
Compare
unrad
, continuous_domain
, and quintic solvingunrad
, continuous_domain
, cubic and quintic solving
The case from issue #21263 now gives the simpler result: >>> roots_cubic(Poly(x**3+3*x**2 + 3*x + a*b*c + 1, x))
[(-a*b*c)**(1/3) - 1, (-a*b*c)**(1/3)*(-1/2 + sqrt(3)*I/2) - 1, (-a*b*c)**(1/3)*(-1/2 - sqrt(3)*I/2) - 1]
>>> [(x**3+3*x**2 + 3*x + a*b*c + 1).subs(x,i).simplify() for i in _]
[0, 0, 0] And it can be demonstrated that the alternate forms calculated internally are correct: >>> from sympy.polys.polyroots import roots_cubic
>>> var('y',positive=True)
>>> s=roots_cubic(Poly(x**3 +y, x))
>>> [(x**3 +y).subs(x,i).simplify() for i in s]
[0, 0, 0]
>>> s=roots_cubic(Poly(x**3 -y, x))
>>> [(x**3 -y).subs(x,i).simplify() for i in s]
[0, 0, 0]
>>> |
23731c9
to
7cec88f
Compare
Also reject polys that have fewer than 3 args in _try_rescale.
7cec88f
to
783d380
Compare
@oscarbenjamin , |
assert quantile(Y)(x) == Intersection(S.Reals, FiniteSet(sqrt(2)*sigma*(sqrt(2)*mu/(2*sigma) + erfinv(2*x - 1)))) | ||
ans = quantile(Y)(x) | ||
eq = ans.atoms(Eq).pop() | ||
ans = factor_terms(ans.xreplace({eq: Eq(eq.lhs.simplify().factor(), 0)} | ||
).xreplace({eq.atoms(Dummy).pop(): y})) | ||
assert ans == Complement(ConditionSet(y, Eq((-mu + | ||
y)*(2*x + erf(sqrt(2)*(mu - y)/(2*sigma)) - 1), | ||
0), S.Reals), FiniteSet(mu)) |
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 has the answer here become more complicated?
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.
Because solveset
bails out if it can't solve the expression as written:
>>> e
-_x*erf(sqrt(2)*(_x - mu)/(2*sigma)) - _x + mu*erf(sqrt(2)*(_x - mu)/(2*sigma))
+ mu + 2*x*(_x - mu)
>>> solveset(_, _x)
ConditionSet(_x, Eq(-_x*erf(sqrt(2)*(_x - mu)/(2*sigma)) - _x + mu*erf(sqrt(2)*(
_x - mu)/(2*sigma)) + mu + 2*x*(_x - mu), 0), Complexes)
>>> solveset(e.factor(), _x)
FiniteSet(mu, sqrt(2)*sigma*(sqrt(2)*mu/sigma + 2*erfinv(2*x - 1))/2)
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 has been fixed. The equation represents an interesting case where signsimp
helps a lot by doing a simple thing. And then factor
tidies up the simplified expression and gives a product of two factors which can be easily solved:
>>> fprint(e)
-w*exp(-w**2 + w*y - y**2)*exp(w**2 - w*y + y**2)*erf(w - y) +
w*exp(-w**2 + w*y - y**2)*exp(w**2 - w*y + y**2) + 2*x*(-w + y) +
y*exp(-w**2 + w*y - y**2)*exp(w**2 - w*y + y**2)*erf(w - y) -
y*exp(-w**2 + w*y - y**2)*exp(w**2 - w*y + y**2)
>>> signsimp(e)
-w*erf(w - y) + w - 2*x*(w - y) + y*erf(w - y) - y
>>> factor(_)
(-w + y)*(2*x + erf(w - y) - 1)
sympy/polys/polyroots.py
Outdated
@@ -343,6 +341,10 @@ def _ans(y): | |||
ans.append((s*w - t*root)/2 - aon4) | |||
return ans | |||
|
|||
# whether a Piecewise is returned or not | |||
# depends on knowing p, so try to simplify | |||
p = p.simplify() # XXX only if number? |
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.
Here p
comes from p = -e**2/12 - g
above. The reason this simplify is added is because if e
is an Add
like 1 + sqrt(2)
then we need to expand the power to see whether it is zero. I think it is better to use _mexpand
here since it is clear why that might be needed in some cases.
There might be some cases that get left unresolved where simplify
could handle them but in those cases we return a Piecewise which is still correct. The caller can use simplify if they want. Ideally refine
would be better at handling this sort of thing or there should be a simplify_piecewise
function whose job is precisely to simplify the conditional parts of a Piecewise.
I think this looks good although I had a couple of questions |
thanks for taking the time to look this over; I'll make a few mods and look it over again and commit. |
unrad
, continuous_domain
, cubic and quintic solvingunrad
, continuous_domain
, roots_(cubic, quartic, quintic)
d33b98b
to
4dbbc8f
Compare
4dbbc8f
to
ec9397e
Compare
@oscarbenjamin , what do you think about using |
I'm not sure... When is it needed? |
see the test added in the last commit; also, the different solve path used by the changes here apparently put the expression in a state that wasn't recognized without factoring; you will see that the original stats tests (except for the additional exclusion of |
Another way to think about such equations is to think about them in terms of equations that can be easily solved when written in terms of "separation of generators'. Such equations will have solutions for generators that are each independent of the others:
there are two generators involving y
This solution does not depend on y so eq has a factor of
And now we can solve that easily for the second generator
So we know how eq can be factored as a result but did not need to factor to find the roots:
An equation that can be solved like this will have these properties:
Interesting to note that if you replace erf(y - z) with sqrt(y) then you end up with an expression that SymPy does not recognize as being factorable...
So currently,
To do the separation we would have to carefully separate the generators symbolically (e.g. getting
Then solve for each as
and
I suspect this gets a little trickier to identify as factors increase? But maybe not -- if we had 3 generators,
I'm not sure how you would identify that the last expression can be factored into 3 terms instead of 2. Looking at the highest power and thinking how it can be written as the sum of the generator powers? This is not a trivial exercise for me; it may be so for others (@jksuom ?). So my preference is to leave this at the present use of |
Poly doesn't handle square roots of symbols very well: In [53]: Poly(sqrt(x) + x)
Out[53]: Poly(x + (sqrt(x)), x, sqrt(x), domain='ZZ')
In [54]: Poly(sqrt(x) + x, sqrt(x))
Out[54]: Poly((sqrt(x)) + x, sqrt(x), domain='ZZ[x]') There are two possible approaches to handling that in
Your last example can be factored by manually implementing 1.: In [49]: a, b, c, x = symbols('a, b, c, x')
In [50]: eq = expand((sqrt(x)-a)*(sqrt(x)-b)*(x-c))
In [51]: eq
Out[51]:
3/2 3/2 2
-a⋅b⋅c + a⋅b⋅x + a⋅c⋅√x - a⋅x + b⋅c⋅√x - b⋅x - c⋅x + x
In [52]: eq.subs(sqrt(x), t).factor().subs(t, sqrt(x))
Out[52]: (-a + √x)⋅(-b + √x)⋅(-c + x) The extension approach is needed for more complicated examples where it isn't possible to just substitute out one symbol of interest e.g. if you have |
OK, I'm going to commit what is here. I will open an issue about Poly identifying |
References to other Issues or PRs
fixes #19869
fixes #21263
fixes #21268
fixes #21287
closes #21049 and closes #21002 as an alternate
closes #21295
Brief description of what is fixed or changed
unrad
was changed in #18324 and #21032 to return an expression even when the original expression didn't have persisting radicals; it should only return an expression when the expression is going to give a superset of the solutions to an expression, not when the expression can be simply expanded to remove radicals. Those changes have been reverted and the function modified to handle the case that prompted that change.Other comments
Release Notes
roots_quintic
better detects non-rational coefficientsroots_cubic
results are simpler for cubics that can be written (or rewritten) asx**3 + y = 0
to_rational_coeffs
continuous_domain
returns results for any power with even denominator, not just 2unrad
better detects radicals to be removed even when they are hidden as bases of rational powersunrad
no longer makes the highest order term positive when making sign canonical_has_rational_power
(a private function) is no longer used and is removed_solve_as_rational
now fully expands the expression to be solved_solve_radical
was changed to accept the unrad form of f_solveset
will now useunrad
to see if an equation needs to use_solve_radical
before doing sosolveset
now usesfactor
as a last-resort