Aaron,

Thanks for the thoughtful replies.  As I have been looking at this further,
I think the core question is this:  how are Sympy objects converted to
bools?

Is seem that there are two notions:

* The primitive notion of truth built into python itself.  This is handled
by Python in the __nonzero__ method.  The pattern followed in 90% of Sympy
is that __nonzero__ returns True unless the object is Zero.  In this concept
of truth is makes sense to ask the truth value of single objects:

bool(x)
bool(1)
etc.

* The mathematical idea related to whether or not a relational equation is
satisfied:

1 < 0
x < y
2*x + y == 0

To me it seems that these two types of truth concepts should be *completely*
orthogonal.  What are the consequences of this?

1. Converting a relational equation to a boolean using bool [bool(x<0)] is
not asking the mathematical question "is x less than 0".  It is asking the
the more primitive question of "is this a nonzero python object".

2. There must be a different API for testing if a relational equation is
satisfied.  I don't see this separate interface.  Maybe it is the assumption
system, but assumptions seem to be different conceptually than equations.
But I don't really understand how assumptions work in sympy.  Whatever this
other API is, it needs to be capable of giving the following answers:

* It is satisfied
* It is not satisfied
* It is undetermined.

The current situation is lacking in two respects:

* The classes in relational (Equality, Inequality, StrictInequality, etc.)
violate this clean separation of truth types by having __nonzero__ method
that combines and confuses the two types.  You see things like this:

    def __nonzero__(self):
        if self.lhs.is_comparable and self.rhs.is_comparable:
            if self.lhs.is_Number and self.rhs.is_Number:
                return self.lhs < self.rhs
            return self.lhs.evalf()<self.rhs.evalf()
        return self.lhs.compare(self.rhs)==-1

* There is no standard interface for asking about the second (relational
equation) type of truth.  This is probably why Relational classes are
confusing the two.

I tried commenting out the __nonzero__ method in Relational, but a good
number of tests fail - some with a RuntimeError saying the recursion limit
has been reached.

I am not sure how to proceed.  What is the most up to date description of
the assumption system in sympy?  Is the new assumption system in place and
the recommended approach.

Cheers,

Brian



On Fri, Feb 26, 2010 at 7:11 PM, Aaron S. Meurer <[email protected]> wrote:

>
> 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] <sympy%[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] <sympy%[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] <sympy%[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.

Reply via email to