-
-
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
Expr: _eval_interval checks both limits(left and right) #11889
Conversation
Expr._eval_interval only evaluates right limit and left limit, checks if they are equal, gives error if theyare not equal, else behaves normaly.
It does not matter if left and right limits differ at an end point, since only one of them is used by _eval_interval (and therefore no error message should be returned). It will suffice to compute the limit from the side where the interval lies. So, for example, the limit at |
@jksuom makes sense ill fix that. |
This reverts commit ebc2cae.
@jksuom also could you please hint at why the tests are failing? |
They are failing because some evaluations now incorrectly return an error. That should not happen after the lines raising an error are removed. |
Expr._eval_limit now compute the limit from the side where the interval lies. This is crutial since left and right limit need not be same.
@jksuom i have made changes, please review them :) |
@@ -781,7 +781,11 @@ def _eval_interval(self, x, a, b): | |||
else: | |||
A = self.subs(x, a) | |||
if A.has(S.NaN, S.Infinity, S.NegativeInfinity, S.ComplexInfinity): | |||
A = limit(self, x, a) | |||
if a.__lt__(b): |
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.
if a < b:
should be preferred. It would work even if a.__lt__()
is not defined, but b.__gt__()
is defined, in which case the interpreter will automatically use b.__gt__(a)
.
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.
One could also write:
dir = '+' if a < b else '-'
A = limit(self, x, a, dir=dir)
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.
You need to guard against symbolic a < b. Not sure what to do in that case. Maybe just assume a < b by default?
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.
Maybe just assume a < b by default?
I guess that is what should be done. So maybe if (a < b) is not 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.
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, the typical way is if (a < b) == True
(be careful, don't use is
, since it gives sympy.true
, not True
). The issue is that if you just use if a < b
then if the inequality is symbolic (can't be evaluated, like if a and b are symbols), then it will raise a TypeError
.
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 fact, you can see the TypeError
in the failing tests.
If you use
because To avoid the error you can write:
One can also think that there are three truth values: true, false, and undecided. |
@jksuom thanks for explanation, i think it's ready for merge |
I wanted to point out that |
There could also be a line testing the case Also tests should be added to show that |
Humm, i may be wrong but here is what i think:
If you are sure |
Okay one correction, if |
I think we should have the undecided case treated in the same way as when a < b is true. That will apparently not happen if we use The tests at the two endpoints should be compatible in the undecided case. (So we should have
It is true in general that integrals over a set of measure zero have value zero (even if the integrand is not finite). Therefore I think we should return zero. |
Is it true even when the function(or it's limit) is not defined at that point? Right, ill make chages |
If the function is not defined on a set of measure zero, then its value can be set arbitrarily. That will not affect the integral. In general, if two functions differ only on a set of measure zero, their integrals have the same value. (This is how the Lebesgue integral behaves, the Riemann integral is only defined under fairly restrictive conditions.) |
all test seem to pass here but |
Sorry that got resolved. The case |
If the order of the limits cannot be decided, for example, if we integrate from a = 0 to b = L, where L is a symbol, then neither It seems to me that the algorithm should be as follows: If it can be decided that This means that exactly the same condition should be used at both endpoints. It could be The case |
@jksuom Yup, thats the algorithm we are using right now. Altho the exact same condition is not used, the logic remains the same because if and else blocks are swapped. Currently things work the same way you described in your algorithm. Aldo i added a test, please review. |
@@ -792,7 +800,11 @@ def _eval_interval(self, x, a, b): | |||
else: | |||
B = self.subs(x, b) | |||
if B.has(S.NaN, S.Infinity, S.NegativeInfinity, S.ComplexInfinity): | |||
B = limit(self, x, b) | |||
if (b < a) != 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.
This should also be a < b
(and then "-" in the limit call) to make sure that the limits are always taken on opposite sides.
|
||
def test_issue_11877(): | ||
x = symbols('x') | ||
assert (long(integrate(log(0.5-x), (x, 0, 0.5))) == long(-0.846573590279973)) |
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 test probably shows that there is no imaginary part, but otherwise it does not say much:
In [3]: long(-0.846573590279973)
Out[3]: 0
It is usually best to use rational numbers in tests, since floating point comparisons may be problematic. In this case, integrate(log(S(1)/2 - x), (x, 0, S(1)/2))
could be used.
This looks good otherwise, but the test should be improved. |
humm, i am thinking of a simpler function to use as a test case, how about something like |
I think this would be the proper test for the issue:
|
@@ -1768,4 +1768,5 @@ def test_issue_10755(): | |||
|
|||
def test_issue_11877(): | |||
x = symbols('x') | |||
assert (long(integrate(log(0.5-x), (x, 0, 0.5))) == long(-0.846573590279973)) | |||
y = integrate(x**2/(-x*x), (x, 0, 1)) |
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.
@jksuom i changed the test to one you suggest, but this one is a little shorter and easy to understand should we rather use this test?
Not that it really matters but just saying.
I think this looks good. The error must be unrelated to the changes. I'll try to investigate. |
#11980 should fix the error. I'll restart the failed doctests. |
Awesome 🎉 , Thanks for your patience @jksuom :) |
Ref. #11877
Expr._eval_interval now evaluates right limit and left limit both
Before
>>> integrate(log(0.5-x), (x, 0, 0.5)) −0.846573590279973+1.0iπ
After:
>>> integrate(log(0.5-x), (x, 0, 0.5)) 799, in _eval_interval raise MathError("Right limit and left limit do not match")