> Hi,
>
> Here is my summary of some bugs i have found in Relational. I would fix them, but I think there are larger issues that need discussing.
>
> What is the truth value of a symbolic expression?
> =================================================
>
> The current general pattern is that symbolic expressions evaluate to True:
>
> In [61]: bool(x)
> Out[61]: True
>
> In [62]: bool(x+1)
> Out[62]: True
>
> In [63]: bool(x*2)
> Out[63]: True
>
> In [64]: bool(2*x)
> Out[64]: True
>
> In [65]: bool(x>0)
> Out[65]: True
>
> But:
>
> In [66]: bool(x<0)
> Out[66]: False
>
> This is obviously not consistent and it a bug in Relational.__nonzero__
Yes, let's make any SymPy expression return True if and only if it is not the object S.Zero (or maybe also some empty containers, like Matrix([])).
I'm not sure what to do about the relational (see below).
>
> Relational classes don't properly evaluate to booleans
> ======================================================
>
> Consider these examples:
>
> In [67]: Eq(0,0)
> Out[67]: 0 == 0
>
> In [68]: Ge(1,0)
> Out[68]: 0 <= 1
>
> Obviously, these should resolve to booleans. You may think, why wouldn't
> you just do 0==0 or 1<0? But consider this:
Just to be clear we overload __lt__, etc. to return the Le() class, but == is just the simple equality testing (not the Eq class). This is fine, but like you say, there needs to still be a way to do actual comparison. The problem I have seen is that it likes to turn things into booleans that don't make sense that way, other than being non-empty. For example, the x<0 above.
Another thing, automatic evaluation should proceed on a SymPy object only if three conditions are met:
1. The evaluation will always make the object simpler.
2. The evaluation is very cheap in every case.
3. The evaluation is so trivial that no one will ever not want it done.
For example, we auto-simplify exp(x)*exp(x) to exp(2*x), but we leave exp(x)*exp(y) alone, because if we auto-combined it, it would be impossible to actually get exp(x)*exp(y) instead of exp(x + y) (things used to be this way back in SymPy 0.6.4 until I spent a very headacheful few weeks changing it last summer).
(1) is clearly the case for turning inequalities into booleans. (2) will only be true for the simple case of number < number. Otherwise, even if the assumption for Ask(x - y, Q.positive) is True for x > y, we should leave it alone in Gt.__new__ and leave the work to refine(). Actually, even if this were cheap, I would still want to leave it alone because of (3).
Also, should we prevent bool(relational) from working otherwise, much like the TypeError you get when you do 1j < 1? I would rather have bool(x<y) raise an error than mislead me by returning True (misleading in the sense that bool(x>y) would also be True). Semantically speaking, relational.__nonzero__ should be True unless the relational is explicitly False, and relational.__bool__ should be either the explicit truth value of the relational or raise TypeError if there is none, but I do not know the actual implementation difference between these two in Python.
>
> In [69]: e = Ge(x,0)
>
> In [70]: e.subs(x,1)
> Out[70]: 0 <= 1 # Just Ge(1,0)!
>
> Thus. it is common and easy to get Relational classes that should evaluate to
> a boolean but don't. This behavior also affects boolean logic classes
> like And/Or, etc. as well as Interval:
>
> In [71]: e = And(x>0,x<1)
>
> In [72]: e.subs(x,0.5)
> Out[72]: And(0 < 0.5, 0.5 < 1)
>
> The problem is that the __new__ method of Relational does NOT actually
> try to compare the lhs and rhs. That is, Ge.__new__(lhs, rhs) doesn't actually
> try lhs >= rhs. But, we can't have it try that or it will generate an
> infinite loop! Resolving this will be quite subtle.
This should at least work with refine:
In [17]: refine(e.subs(x,0.5))
Out[17]: And(0 < 0.5, 0.5 < 1)
I wonder if internally, x > y should be represented as Assume(x - y, Q.positive) and x >= y as Assume(x - y, Q.nonnegative) (except there doesn't seem to be a Q.nonnegative yet for some reason), except that it would also have to keep track of what the original kind of inequality it was and what terms were on what side. Anyway, Ask(x - y, Q.positive) is the proper way to evaluate x < y, not checking x>=y, etc.
Aaron Meurer
>
> Cheers,
>
> Brian
>
>
>
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sy...@googlegroups.com.
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
> In [66]: bool(x<0)
> Out[66]: False
>
> This is obviously not consistent and it a bug in Relational.__nonzero__
Yes, let's make any SymPy expression return True if and only if it is not the object S.Zero (or maybe also some empty containers, like Matrix([])).
I'm not sure what to do about the relational (see below).
> Relational classes don't properly evaluate to booleansJust to be clear we overload __lt__, etc. to return the Le() class, but == is just the simple equality testing (not the Eq class). This is fine, but like you say, there needs to still be a way to do actual comparison. The problem I have seen is that it likes to turn things into booleans that don't make sense that way, other than being non-empty. For example, the x<0 above.
> ======================================================
>
> Consider these examples:
>
> In [67]: Eq(0,0)
> Out[67]: 0 == 0
>
> In [68]: Ge(1,0)
> Out[68]: 0 <= 1
>
> Obviously, these should resolve to booleans. You may think, why wouldn't
> you just do 0==0 or 1<0? But consider this:
Another thing, automatic evaluation should proceed on a SymPy object only if three conditions are met:
1. The evaluation will always make the object simpler.
2. The evaluation is very cheap in every case.
3. The evaluation is so trivial that no one will ever not want it done.
For example, we auto-simplify exp(x)*exp(x) to exp(2*x), but we leave exp(x)*exp(y) alone, because if we auto-combined it, it would be impossible to actually get exp(x)*exp(y) instead of exp(x + y) (things used to be this way back in SymPy 0.6.4 until I spent a very headacheful few weeks changing it last summer).
(1) is clearly the case for turning inequalities into booleans. (2) will only be true for the simple case of number < number.
Otherwise, even if the assumption for Ask(x - y, Q.positive) is True for x > y, we should leave it alone in Gt.__new__ and leave the work to refine(). Actually, even if this were cheap, I would still want to leave it alone because of (3).
Also, should we prevent bool(relational) from working otherwise, much like the TypeError you get when you do 1j < 1?
I would rather have bool(x<y) raise an error than mislead me by returning True (misleading in the sense that bool(x>y) would also be True).
Semantically speaking, relational.__nonzero__ should be True unless the relational is explicitly False, and relational.__bool__ should be either the explicit truth value of the relational or raise TypeError if there is none, but I do not know the actual implementation difference between these two in Python.
> In [69]: e = Ge(x,0)This should at least work with refine:
>
> In [70]: e.subs(x,1)
> Out[70]: 0 <= 1 # Just Ge(1,0)!
>
> Thus. it is common and easy to get Relational classes that should evaluate to
> a boolean but don't. This behavior also affects boolean logic classes
> like And/Or, etc. as well as Interval:
>
> In [71]: e = And(x>0,x<1)
>
> In [72]: e.subs(x,0.5)
> Out[72]: And(0 < 0.5, 0.5 < 1)
>
> The problem is that the __new__ method of Relational does NOT actually
> try to compare the lhs and rhs. That is, Ge.__new__(lhs, rhs) doesn't actually
> try lhs >= rhs. But, we can't have it try that or it will generate an
> infinite loop! Resolving this will be quite subtle.
In [17]: refine(e.subs(x,0.5))
Out[17]: And(0 < 0.5, 0.5 < 1)
I wonder if internally, x > y should be represented as Assume(x - y, Q.positive) and x >= y as Assume(x - y, Q.nonnegative) (except there doesn't seem to be a Q.nonnegative yet for some reason), except that it would also have to keep track of what the original kind of inequality it was and what terms were on what side. Anyway, Ask(x - y, Q.positive) is the proper way to evaluate x < y, not checking x>=y, etc.
Otherwise, even if the assumption for Ask(x - y, Q.positive) is True for x > y, we should leave it alone in Gt.__new__ and leave the work to refine(). Actually, even if this were cheap, I would still want to leave it alone because of (3).
Yes, I agree that refine should be used in this cases.
Also, should we prevent bool(relational) from working otherwise, much like the TypeError you get when you do 1j < 1?
The problem with this is that there is little different between bool(x) and bool(x<0).
I would rather have bool(x<y) raise an error than mislead me by returning True (misleading in the sense that bool(x>y) would also be True).
But it would also be misleading that bool(2*x) is True, but bool(x<0) raises an exception.
Semantically speaking, relational.__nonzero__ should be True unless the relational is explicitly False, and relational.__bool__ should be either the explicit truth value of the relational or raise TypeError if there is none, but I do not know the actual implementation difference between these two in Python.
Yes, there is this issue as well.> In [69]: e = Ge(x,0)This should at least work with refine:
>
> In [70]: e.subs(x,1)
> Out[70]: 0 <= 1 # Just Ge(1,0)!
>
> Thus. it is common and easy to get Relational classes that should evaluate to
> a boolean but don't. This behavior also affects boolean logic classes
> like And/Or, etc. as well as Interval:
>
> In [71]: e = And(x>0,x<1)
>
> In [72]: e.subs(x,0.5)
> Out[72]: And(0 < 0.5, 0.5 < 1)
>
> The problem is that the __new__ method of Relational does NOT actually
> try to compare the lhs and rhs. That is, Ge.__new__(lhs, rhs) doesn't actually
> try lhs >= rhs. But, we can't have it try that or it will generate an
> infinite loop! Resolving this will be quite subtle.
In [17]: refine(e.subs(x,0.5))
Out[17]: And(0 < 0.5, 0.5 < 1)
Not sure what you mean by this. Are you saying you *should* get this:
In [17]: refine(e.subs(x,0.5))
Out[17]: True
I wonder if internally, x > y should be represented as Assume(x - y, Q.positive) and x >= y as Assume(x - y, Q.nonnegative) (except there doesn't seem to be a Q.nonnegative yet for some reason), except that it would also have to keep track of what the original kind of inequality it was and what terms were on what side. Anyway, Ask(x - y, Q.positive) is the proper way to evaluate x < y, not checking x>=y, etc.
An inequality is not different from assumptions. x < y is the same as saying x - y is positive. It's similar to how we usually use x - y in SymPy instead of x == y. Expressions are assumed by default to be equal to 0. This is why having an assumptions system that allows assumptions on expressions instead of just variables is essential for inequality manipulation. Any SymPy algorithm when presented with an Equality class will first subtract one side from the other and proceed with it as an expression (c.f. the top of the sources of solve and dsolve()). I would imagine that a similar thing would be true for an inequality, only also carrying the Assume(…, Q.positive) or Assume(…, Q.nonnegative) object around with it, where … is the smaller side subtracted from the larger.
Not sure about using Assume for all of this. Inequalities seem different than assumptions to me…
I'm still working out how the new assumptions work myself. They are not fully merged in yet, so I don't know how much will actually work. I guess you would just have to try it. We may need to finish merging the assumptions in order to full sort out inequalities (ironically, we need assumptions to do inequalities, but we also need to ability to solve inequalities to have some kinds of assumptions).
Yes, Ask(whatever, assumptions) will return True if whatever is true under assumptions, False if it is false, and None if it is undetermined. Or at least it is supposed to, again, the system is not fully merged in yet, so there could be problems. This is to become the standard interface. I believe your concerns were one of the reasons for creating the new assumptions system in the first place.
I agree that that __nonzero__ is a mess. Based on your discussion below, I think the inequalities should automatically return True or False if it is number < number, and if it stays as an inequality class, then the bool should just be false (i.e., replace __nonzero__ with simply "return True").
I think I figured out most of your problems. See the attached patch for a demonstration (it needs cleaning up, and I only did StrictInequality). See also the commit message.
--
You received this message because you are subscribed to the Google Groups "sympy" group.
To post to this group, send email to sy...@googlegroups.com.
To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
Someone else will need to answer your question on how far we are with the assumptions system. I haven't been working on it.Aaron Meurer
In [1]: if x < y and x > y:
...: print "I would not expect to see this printed"
Today I get no output from this and it would surprise me to get
anything else.
Instead I propose that we just agree that bool(x<y) has nothing to do
with the mathematical relation, but will completely hand over the
relation to python for evaluation. (Isn't this the present
behavior?) If we accept this rule, Out[65] and Out[66] are not
inconsistent, but shows rather how python works internally. The
language reference states that "objects of different types always
compare unequal, and are ordered consistently but arbitrarily.".
If on the other hand you want to test the mathematical expression, you
should use ask() as you have discussed, or maybe a method like
Relational.is_valid().
Øyvind
> > sympy+un...@googlegroups.com <sympy%2Bunsu...@googlegroups.com>.
> ...
>
> les mer »
> Am I right that you have concluded that bool() of a relational should
> always return True?
> That would allow some rather weird things:
>
> In [1]: if x < y and x > y:
> ...: print "I would not expect to see this printed"
>
But why would you want to do this? "if x < y" doesn't make sense if x and y are symbols.
> Today I get no output from this and it would surprise me to get
> anything else.
>
> Instead I propose that we just agree that bool(x<y) has nothing to do
> with the mathematical relation, but will completely hand over the
> relation to python for evaluation.
But then we loose the shortcut x < y => Lt(x, y).
> (Isn't this the present
> behavior?) If we accept this rule, Out[65] and Out[66] are not
> inconsistent, but shows rather how python works internally. The
> language reference states that "objects of different types always
> compare unequal, and are ordered consistently but arbitrarily.
>
This will change in Python 3. There, objects have to be the same type to be compared.
Aaron Meurer
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
Vinzent
Because it could make sense to compare python objects that way. (but
see below)
> But then we loose the shortcut x < y => Lt(x, y).
Actually no, x < y can return Lt(x, y). The comparison is only handed
over to python when you try to evaluate it with bool(Lt(x, y)). (I
think I am only describing how things work today, but from a different
perspective.)
> This will change in Python 3. There, objects have to be the same type to be compared.
Oh, that changes the situation. The future python programmer will not
expect the comparison operators to apply everywhere, so then I can
understand your point that "x < y doesn't make sense if x and y are
symbols."
But still, it would be easy to work with a rule that says that
bool(Relational(...)) hands over the
evaluation to python. This would mean that "if x < y:" acts like the
comparison python would normally do, and "if ask(x < y, Assume(x-
y,Q.whatever):" tries to determine the mathematical question.
I think the central point in my objection is that if you write a piece
of python code using the sympy libraries, it would be very confusing
if some comparisons turn out to be always True. Because of this, I
suggest that relational objects should not simply return True to
indicate that they are ``nonzero''. Since the mathematical problem
can be non-trivial or impossible to determine, that is not an option,
so instead the comparisons should be handed over to python, completely
transparent to the user.
All that is needed if we go for this solution is a section in the
"gotchas and pitfalls" explaining what the user should do to evaluate
a mathematical inequality, and what "if x<y:" really means.
Øyvind
I should clarify the current state of things because I am the one who
last touched the code in this respect.
* There is now logic in Relational that tries to actually evaluate the
truth value of a Relational when it is constructed. Thus you get the
following:
In [20]: Lt(0,2)
Out[20]: True
In [21]: Lt(2,0)
Out[21]: False
This uses the new _eval_relation to and is a mathematical test.
* When Relationals are symbolic, if you do .subs on them, you can get
to a bool. Again, this result has a mathematical meaning:
In [22]: Lt(x,0).subs(x,-1)
Out[22]: True
In [23]: Lt(x,0).subs(x,1)
Out[23]: False
* The only time you access the other meaning (the more primitive
Python one) is if you *cast* a Relational to a bool:
In [24]: bool(Lt(x,y))
Out[24]: True
But currently, you don't always get true:
In [25]: bool(Lt(x,0))
Out[25]: False
The reason is that the __nonzero__ methods of different Relational
subclasses use the .compare method in different ways. I didn't write
this code, BUT I seem to remember that many tests fail if you simply
have __nonzero__ return True. I didn't investigate why, but my guess
is that this is used to order different Relationals in expressions.
* Doing the following is dangerous:
if x < y:
...
The reason is that depending on what x and y are, you may be getting
back the primitive or Mathematical version of less than.
* There is another inconsistency in that == and != don't return
Relational subclasses, but <, >, <= and >= do.
* Some of this changes with python 3, so whatever we do, we need to
move towards a solutions that won't change the user interface of sympy
when we transition to python 3.
Here is what I propose:
Someone (unfortunately I don't have time right now, I may have time
later in the quarter) should go through and write a summary
(preferably in the dev section of the Sphinx docs) that summarizes:
* How Python 2 and 3 handle object equality, comparisons, etc. This
includes cmp, bool, all the __lt__ methods, __nonzero__, etc.
* How we currently handle mathematical truth in sympy. This is mostly
focused on the math of the Relational classes and how they are
constructed (x<y shorthand for Lt(x,y)).
* How we currently handle the more primitive type of truth in sympy.
This is relevant in both casting sympy objects to bool AND how we
order sympy objects in expressions.
* A proposal of how to handle all of this in a uniform and consistent
manner in both python 2 and 3.
I think we should do this before making any major changes to how all
this is handled. Otherwise, we are going chase our tails around and
go crazy.
Cheers,
Brian
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
The problem with them always returning True, is that this behavior is
implemented in the __nonzero__ method of Relational. This method is
also used to order Relationals in expressions. Thus, it needs to
return 1, 0, -1 to allow that ordering.
> That would allow some rather weird things:
>
> In [1]: if x < y and x > y:
> ...: print "I would not expect to see this printed"
With our current code base, I would not ever do this. The reason (see
my other email) is that x<y can either be a bool that represents
mathematical < OR bool(Lt()) which is is the more primitive type of
truth.
For now I would do:
e = x<y
if isinstance(e, Relational):
...
elif isinstance(e, bool):
...
A pain, but until we decide what to do with all of this, this is the
safe thing to do.
Cheers,
Brian
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
I strongly discourage the usage of bool() for such things, you should
rather use a method, like .doit() or something. It confuses the math's
and python's concept of truth.
You could have code like
if x:
do something
which tests for example if x was defined, so it should just return
True (python's concept). But mathematically, rather None should be
returned, because we do not know, whether the symbol is True or not.
And it is not even technically possible, because ask() would quite
often return None, which can not be returned by bool().
> I think the central point in my objection is that if you write a piece
> of python code using the sympy libraries, it would be very confusing
> if some comparisons turn out to be always True. Because of this, I
> suggest that relational objects should not simply return True to
> indicate that they are ``nonzero''. Since the mathematical problem
> can be non-trivial or impossible to determine, that is not an option,
> so instead the comparisons should be handed over to python, completely
> transparent to the user.
In my opinion they should just return True, and the comparison should
not be abused. Currently it's usage is not very transparent I think.
Maybe it should be better completely disabled.
>
> All that is needed if we go for this solution is a section in the
> "gotchas and pitfalls" explaining what the user should do to evaluate
> a mathematical inequality, and what "if x<y:" really means.
The operators '==' and '!=' are strictly necessary in a non-
mathematical way. Currently the relational operators are used
mathematically. If want to be strictly consistent, we have to remove
them. Also, the only work for real symbols, not for general symbols.
This can be a nice shortcut, but it is dangerous if you confuse them
with the non-mathematical operators.
Also, should (x < y) raise an exception when x and y are not real or
automatically create such assumptions?
Vinzent
This is imho a bug that should be fixed. Such things should not be
used for ordering, this is not a clean approach and this should also
be fixed.
Vinzent
If I understand you correctly, the decision is that a>b should only be
used in the mathematical meaning. I have also learned (from Vincent I
think) that cmp(a,b) will be removed from python 3. What I don't
understand then is how Sympy objects can be sorted? If __lt__,
__gt___, etc. are overloaded to construct objects, and __cmp__ is
deprecated, what is the plan for canonical ordering?
Øyvind
On 31 Mar, 23:02, Vinzent Steinberg <vinzent.steinb...@googlemail.com>
wrote:
I personally don't see any need to do this, but if you *really* need it, you could always do:
sorted(list_of_sympy_objects, key=hash)
and it will be canonical, at least on the same machine.
Aaron Meurer