On Feb 26, 2010, at 5:40 PM, Brian Granger wrote: > Aaron, > > Thanks for the response...replies inline > > > 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([])). > > Yep and that is what Basic.__nonzero__ does. > > I'm not sure what to do about the relational (see below). > > Right. > > > 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. > > Right. > > 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. > > I could imagine other mathematical objects that have a clear concept of < and > > and that are cheap to find. Yes. I think we are simplifying things like x < x + 1 right now too. But there is also (3) to consider for that. > > 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. I guess the problem is what if you want to do an actual comparison. We override <, >, etc., but that is what you would normally use in an if statement, though I guess you should really be making a call to Ask(). So as long as this is clear, it shouldn't be a problem either way. > > 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) > > > > 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) > > 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 > Yes, that is what I mean. Refine should recursively check if an inequality will evaluate to True or False, even in the case where it is only because of assumptions on variables. So this should return True also:
In [6]: refine(x > y, Assume(x - y, Q.positive)) Out[6]: y < x But like you say, 0 < 0.5 should automatically return True even without refine(), unless some evaluate=False flag is sent to Lt(). > > > 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. > > > Not sure about using Assume for all of this. Inequalities seem different > than assumptions to me… 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. By the way, another thing that I have noticed about inequalities, though it is only moderately annoying rather than show stopping, is that they do not always retain the order that they are initialized in: In [9]: x > y Out[9]: y < x Internally, there should be little distinction between x > y and y < x, but for humans, reading inequalities can be much easier if they are in a certain order, which will be the order that the user enters them in. Aaron Meurer > > Brian > > 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 [email protected]. > > To unsubscribe from this group, send email to > > [email protected]. > > For more options, visit this group at > > http://groups.google.com/group/sympy?hl=en. > > -- > You received this message because you are subscribed to the Google Groups > "sympy" group. > To post to this group, send email to [email protected]. > To unsubscribe from this group, send email to > [email protected]. > For more options, visit this group at > http://groups.google.com/group/sympy?hl=en. > > > > > -- > You received this message because you are subscribed to the Google Groups > "sympy" group. > To post to this group, send email to [email protected]. > To unsubscribe from this group, send email to > [email protected]. > For more options, visit this group at > http://groups.google.com/group/sympy?hl=en. -- You received this message because you are subscribed to the Google Groups "sympy" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
