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
raise value error for Eq(RootOf(), Symbol) #15923
Conversation
✅ Hi, I am the SymPy bot (v142). 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.4. Note: This comment will be updated with the latest check if you edit the pull request. You need to reload the page to see it. Click here to see the pull request description that was parsed.
Update The release notes on the wiki have been updated. |
An |
Your test assert Eq(r, x) is not S.false is not very informative. Virtually any object created will pass this test. What is actually the result here? You should test for that. Probably a better test would check the lhs and rhs attributes of the Eq. |
Okay this patch looks good. Can you combine it into one commit? I would do that with: $ git reset HEAD~4 # Assuming 4 is the number of commits to "undo"
$ git commit
$ git push -f Make sure to save your work before doing this in case it goes wrong. Also can you write a more informative release note. The release note should make sense to end users so describe the effect of the change not the code that was changed. You can see previous release notes here: https://github.com/sympy/sympy/wiki/Release-Notes-for-1.3 |
@asmeurer can you check it? |
Actually I'm not sure about this. It seems like you're only checking for the case when one side of the equation is a pure symbol. It could be more complicated like In [1]: (x*y).is_Symbol
Out[1]: False Rather than inserting this check can you work out which case is causing the Eq to become False? There must be some incorrect logic in there somewhere and we need to know what it is. |
sympy/polys/rootoftools.py
Outdated
@@ -970,6 +970,8 @@ def _eval_Eq(self, other): | |||
# is_real value of the CRootOf instance. | |||
if type(self) == type(other): | |||
return sympify(self == other) | |||
if other.has(Symbol): |
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.
The case below would return False for Eq(root, Integral(x, (x, 1, 10)))
but yours would return None. As was suggested, which if
is sending back the False? It is the one making the mistake and should be fixed.
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.
The case below would return False for
Eq(root, Integral(x, (x, 1, 10)))
but yours would return None
In my case also it is returning false -
In [1]: r = RootOf(x**5 - x + 1, 0)
In [2]: Eq(r, Integral(x, (x, 1, 10)))
Out[2]: False
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 need to figure out the problem before deciding how to fix it
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.
In my case also it is returning false
Then it is not doing so from this function because the first check is false and your check is true and would return None.
I am returning the same |
But we are burying a problem with a patch. This needs more investigation to see from where the wrong value is coming from. |
This check is incorrectly returning False in this case: sympy/sympy/polys/rootoftools.py Line 964 in 946be88
I checked the blame but it's just been there since the method was added so not informative. |
So that's saying 'if it's not a number or if it's a number with undefined functions, return False'. That should probably just return None if there are undefined functions because the expression would have to be simplified to see if the unknown values disappeared. So the logic should be if other.is_number and other.has(AppliedUndef) or not other.is_number:
# it's a number that can't be evaluated or it's not a number in its current form
# though simplification might identify it as such (but that is the user's choice)
return None But in the mods that were made, he said that |
I can confirm that with the PR as it stands (hasn't changed since) In [2]: r = rootof(x**5-x+1,0)
In [3]: Eq(r, Integral(x, (x, 1, y)))
Out[3]:
y
⎛ 5 ⎞ ⌠
CRootOf⎝x - x + 1, 0⎠ = ⎮ x dx
⌡
1 @RituRajSingh878 these examples are good candidates for tests that you could add. Also can you change the PR to be as suggested by @smichr above? |
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.
Looks good. I left one comment.
@@ -12,7 +12,7 @@ | |||
|
|||
from sympy import ( | |||
S, sqrt, I, Rational, Float, Lambda, log, exp, tan, Function, Eq, | |||
solve, legendre_poly | |||
solve, legendre_poly, Symbol, Integral |
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.
Are these extra imports needed?
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.
Yes and No. They are both used in test 15920, but x is already imported at the top from abc so needs not to be recreated.
@@ -566,3 +565,9 @@ def test_eval_approx_relative(): | |||
assert [str(i) for i in a] == [ | |||
'-0.10', '0.05 - 3.2*I', '0.05 + 3.2*I'] | |||
assert all(abs(((a[i] - t[i])/t[i]).n()) < 1e-2 for i in range(len(a))) | |||
|
|||
def test_issue_15920(): | |||
x = Symbol("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.
not needed
please put space around -+
operators (below) when fixing
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 patched it up. This should be ready to go.
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.
thanks
sympy/polys/rootoftools.py
Outdated
@@ -970,6 +970,10 @@ def _eval_Eq(self, other): | |||
# is_real value of the CRootOf instance. | |||
if type(self) == type(other): | |||
return sympify(self == other) | |||
if other.is_number and other.has(AppliedUndef) or not other.is_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.
other
will never have AppliedUndef if other.is_number
is True:
>>> f(1).is_number
False
So I think this should be
if not other.is_number:
return # simplification by user might make it a number
sympy/polys/rootoftools.py
Outdated
if other.is_number and other.has(AppliedUndef) or not other.is_number: | ||
# it's a number that can't be evaluated or it's not a number in its current form | ||
# though simplification might identify it as such (but that is the user's choice) | ||
return None | ||
if not (other.is_number and not other.has(AppliedUndef)): |
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.
These are mutually exclusive so if it is a number it doesn't have AppliedUndef so this simplifies to if not other.is_number
...but that is what the previous if-block is testing so this is redundant.
It might make more sense to do
if not other.is_number:
# it might be a number after simplification but it isn't right now
# but if it is known that it is not constant, then it can't be a
# RootOf value (hence False)
return other.is_constant
But I would tend to just return the None and let the user decide if they want to attempt simplification; I know equals
is expensive, but I forget how expensive is_constant
is.
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 didn't change this online because you might want to test it locally before making this change.
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 I understand this. Surely we don't want to return True just because other.is_constant
is True? That doesn't imply that these two are equal.
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.
right: return None if other.is_constant else False
if not (other.is_number and not other.has(AppliedUndef)): | ||
return S.false | ||
if not other.is_number: | ||
return 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.
Change this to return None if other.is_constant else False
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.
Actually sorry I see that this might work okay. Have you tested it on your computer? Is it better than the other suggestions?
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.
Have you tested it on your computer? Is it better than the other suggestions?
I have tested it and I think it is a better suggestion.
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.
Those were not really yes/no questions. Can you explain what the difference is? Why is this version better? What examples are handled differently?
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.
return None
is a fast, conservative (safe) result; using is_constant()
method by default will inolve simplification but simplify=False
can be passed. In that case, two random numerical evaluations will be attempted to see if the results are the different (hence not constant) or not (return None since it could be a fluke). We could think of Eq as a "low hanging fruit" method which tests for really obvious cases and equals/simplification/expression manipulation
as being the much more expensive 'help me out, here' methods.
I am happy with this as it is. It would be interesting to see if foo.is_constant(simplify=False)
can be used with any profit here or in other +eval_Eq
methods.
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 also happy with this as it is. The current version makes sense to me even if further optimisations might be possible.
assert Eq(r, 0) is S.false | ||
assert Eq(r, S.Infinity) is S.false | ||
assert Eq(r, I) is S.false | ||
assert Eq(r, f(0)) is S.false | ||
assert Eq(r, f(0)) is S.false | ||
assert Eq(r, f(0)).lhs is r and Eq(r, f(0)).rhs is f(0) |
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.
Is it guaranteed that is
will do the right thing here? Is f(0)
guaranteed to be a singleton?
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 is the type of test that I wrote this function for; it would be used like:
unchanged(Eq, r, f(0))
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.
That would be useful but wasn't include...
Maybe this test for now should just be:
eq = Eq(r, f(0))
assert isinstance(eq, Eq) and eq.args == (r, f(0))
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 think using is
is ok here since is
tells if the two operands are the same object...which they are. In this case they are not something like S.One and Python 1. If this is an issue it can be fixed (and should maybe be part of code quality testing).
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.
@smichr this only works because of caching otherwise f(0) is not f(0)
:
$ cat t.py
from sympy import *
f = Function('f')
print(f(0) is f(0))
$ python t.py
True
$ SYMPY_USE_CACHE=no python t.py
False
References to other Issues or PRs
Brief description of what is fixed or changed
Fixes #15920
Other comments
As y is symbol so we do not know the exact answer of Eq(r, y) so we can't say it's answer will be false .
SO I am returning same equation as it is.
Release Notes