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
added functionality to Q.real and Q.imaginary and fixed issue:2324 #1156
Conversation
@@ -192,4 +192,3 @@ M R Bharath <catchmrbharath@gmail.com> | |||
Matthias Toews <mat.toews@googlemail.com> | |||
Jorge E. Cardona <jorgeecardona@gmail.com> | |||
Sanket Agarwal <sanket@sanketagarwal.com> | |||
Manoj Babu K. <manoj.babu2378@gmail.com> |
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 remove Manoj from the authors list?
In general you're on the right track. You've found where you need to make changes, we'll just need to try again before we get it right. |
Ok , thanks for the suggestions, will work on that and make necessary changes |
@mrocklin made necessary changes to the code |
return True | ||
else: | ||
return False | ||
elif(ask(Q.imaginary(expr.args[1]) & ask(Q.imaginary(expr.args[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.
You're missing 2 closed parentheses.
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 should also use and
instead of &
. If you do something like ask(Q.real(x**y), Q.imaginary(x))
, this will raise an error.
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.
Also, if you want to do these ask's, you need to include the assumptions
argument.
You should implement new tests for all the new behavior you are implementing. |
@@ -238,6 +238,8 @@ def Pow(expr, assumptions): | |||
elif ask(Q.real(expr.exp), assumptions): | |||
if ask(Q.positive(expr.base), assumptions): | |||
return True | |||
elif ask(Q.imaginary(expr.base), assumptions): | |||
return not ask(Q.imaginary(expr)) |
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 don't think this works, in the case when this ask returns None, you should not return True, which happens if you do ask(Q.real(x**y), Q.imaginary(x))
(after you fix the other 2 comments I made on line 422).
You should be sure to run the tests on your machine before pushing. You need to fix the parentheses before the tests in assumptions will run, but when you do, there are a couple failures of current tests. There is also trailing whitespace in the file, causing a failure in test_code_quality. |
if integer%2 ==1 | ||
Imaginary**integer -> real | ||
if integer%2 ==0 | ||
Imaginary**Imaginary -> Real |
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 isn't true in general. I**I
is real, but you can change the base and get a complex number, e.g. (3*I)**I
.
So in addition to what I said in the code, there are still several cases that just aren't handled correctly. For example, This leads me to my next point that you must implement new tests whenever you implement new functionality. As I've noted there are many cases that are still wrong, but you have to have a test suite to see that. The tests should cover every corner case that you cover in your code, both numerically and symbolically. In addition to writing tests, you have to be sure you run the tests and they all pass. There are several test failures, not only the ones I mentioned w.r.t. white space, but also some of the current tests in the assumptions module fail. You have to be sure everything passes before we can commit it. I'm sorry if this is coming off as overly harsh. The assumption stuff has to be approached very carefully and the logic has to be thoroughly tested not to give a wrong answer for any corner case. If you want to do a GSoC, we also need to make sure you can run the tests and check the new features you are implementing. You are moving in the right direction, and you're doing great for a head first jump into the assumptions module, there are just some key subtleties to work out and a new workflow to adapt to. |
I'll echo the "overly harsh" thing. Your first pull request will be very difficult. There will be a lot of negative feedback. There are a lot of things every student needs to learn. This is normal. |
@@ -224,6 +224,7 @@ def Pow(expr, assumptions): | |||
Positive**Real -> Real | |||
Real**(Integer/Even) -> Real if base is nonnegative | |||
Real**(Integer/Odd) -> Real | |||
Real**Imaginary -> Imaginary if real is positive |
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 wrong. Just knowing a real number is raised to an imaginary number, you can't say anything about the result. That said, your code does not check for this case.
This might interest @mrocklin . |
@flacjacket @certik @mrocklin Sorry for the long delay, I fixed all the issues mentioned by flacjacket and some other, if any other issues let me know. |
I think this looks good now, @sachin004 thanks for finishing this up, regardless of the delay. If nobody else has any comments, this can be merged. |
After a brief glance it looks fine to me. |
@@ -191,7 +192,7 @@ class AskRealHandler(CommonHandler): | |||
|
|||
@staticmethod | |||
def _number(expr, assumptions): | |||
return not expr.as_real_imag()[1] | |||
return not expr.as_real_imag()[1].evalf() |
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 was this change made? Can you evalf to a lower precision, say 2, and still get the answer?
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 sorry for the late response, could you please explain what you mean by lower precision. Just to explain why I have added evalf is for the case of ask(Q.real((-1)**I))
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 you just seeing if the imaginary part is zero or not? might .evalf(2) work? evalf() will compute to 15 digits, or something like that. So if the answer involves a complicated imaginary part there is no need to calculate it to high precision just to learn that it is not zero.
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 I am checking whether its imaginary or not, sure I will reduce the precision to 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.
Something you have to be careful of is expressions that may not be zero but have no significance in their evaluation. It's been a while, but if you look at my work in round or int (and commits added around that time by me) you will see me returning None when the answer is calculated without precision. I think I put some notes in the commit messages about that. Have you used gitk to browse the commit log? It's really nice.
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 so. Commits dealing with equals, too.
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's a pertinent piece of code:
@property
def is_comparable(self):
is_real = self.is_real
if is_real is False:
return False
is_number = self.is_number
if is_number is False:
return False
if is_real and is_number:
return True
n, i = self.evalf(2).as_real_imag()
if not i.is_Number or not n.is_Number:
return False
if i:
if i._prec != 1:
return False
return n._prec != 1
A problem with this, however, is that for q=(-1)**I
q.is_real comes back as False. That's a bug. Otherwise, the idea from the n, i =
part that tries to be careful about reaching a conclusion. If the precision is 1 then you don't know what the number is and in the assumption system, you should return None in that case, not True or False as is done in the 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.
You asked whether it makes sense to return None...not if you can figure out what sort of number you are working with. But not all expressions that are zero can be, with a given piece of code, be proved to be zero. And we shouldn't use a numerical approximation to guess that it is zero. It might just be an expression with a very small value. In the code above, if we know something is a number, we try to evaluate it to 2 digits of precision. But if we only get 1 -- and it doesn't matter if we had attempted more, we would still get 1 -- it's because the evaluation system couldn't be sure that that value was or wasn't zero. For most non-pathological cases, the above is going to return True or False. It's going to have trouble when it gets an expression where the imaginary part is a zero that can't be proved to be so.
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 for the information, I will go through my code and merge the pull made by you, test it and will get back to you.
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.
btw, issue 3068 has some examples where difficulties in evalf'ing are encountered.
@sachin004, please ping when you get around to address @smichr's comments. |
3532: Pow.is_real fix for base = -1
Expressions may give a clear 0 for the imaginary part if they are allowed to simplify through as_real_imag but if evalf is done first then a _prec=1 result is obtained: >>> I**(I+2)).n(2) -0.21 + 0.e-8*I >>> _.as_real_imag()[1] 0.e-8 >>> _._prec 1 That "1" means there was no precision computed for the imaginary part. >>> (I**(I+2)).as_real_imag() (-I**I, 0) Now it's clear to see that the imaginary part is zero. In is_comparable, the answer was being based on n if there was an i part that had no precision. It seems better to just return False in that case. This particular method should return T or F so if it's not clear that the imaginary part is 0 we'll say that the expression is not comparable.
If there is a rational coefficient then it might make sense to split the power up, making b**(c + r) = b**c*b**r
You might want to edit the commit message, changing 'comparions' to 'camparison' or 'comparisons' |
Rebasing is showing many merge conflicts and I was not able to change the commit message. |
Were you able to just cherry-pick my latest changes? If so commit that and we're done (just leave the typo in the commit message). Otherwise, grab my branch and replace this one with that and, again, leave that typo commit alone:
|
@smichr sachin@SACHIN-PC ~/sympy (sachin1) sachin@SACHIN-PC ~/sympy (sachin1) |
Should we just use the version I put up and then if you have additions do them as a PR to that? |
That will be a good idea. |
OK, looks like the merge from my PR was successful. I don't have further comments and @flacjacket has already said this is ready, so...thanks, it's in. |
added functionality to Q.real and Q.imaginary and fixed issue:2324
Thanks to everyone who helped me in completing this. |
probably a fix to the issue:2324