Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Relational: return BooleanAtoms instead of bools. #2844

Merged
merged 1 commit into from

2 participants

@Tempel

Also fixed tests in several locations to check for the new results, and corrected several areas where Python bools were expected; these areas should now handle Python bools and SymPy BooleanAtoms equally.

I realize this may be a contentious change, as True and S.true each have their place; see this comment block in sympy.logic.boolalg for reference. I feel that, since Relationals are "arbitrary symbolic Boolean[s]", it's more appropriate to have them simplify to SymPy objects instead of native Python bools. As an example, I have a current application where I substitute a series of symbols in an Equality; currently, I have to check whether it's reduced to True or False before substituting. My code would be simpler if I could just call the subs method on S.true and not worry about how it simplifies until the end. But I am open to arguments to leave the current behaviour too.

Note that, when using inequality operators, Python bools are still returned in some cases, since they do not always pass off to the Relational subclasses. For example, (x < 2).subs(x, 1) will return S.true, because it starts out as a StrictLessThan object, but S.One < 2 will return True because that logic is all within the Number class. Presumably consistency would be good here; I can try to change the behaviour of the second case if it's deemed worthwhile.

@Tempel Tempel Relational: return BooleanAtoms instead of bools.
Fixed tests in several locations to check for the new results.  Corrected
several areas where Python bools were expected; these areas should now handle
Python bools and SymPy BooleanAtoms equally.
f5e0a15
@asmeurer
Owner

I think this change is OK. Your argument seems valid, and your use-case seems motivating.

Probably you should change the number class ones too.

@asmeurer
Owner

This passes tests, though, and I don't think we need to block on the S.One < 2, so I am going to merge it.

@asmeurer asmeurer merged commit 4c29e99 into sympy:master

1 check failed

Details default The Travis CI build could not complete due to an error
@Tempel Tempel deleted the Tempel:relational_STrue branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 27, 2014
  1. @Tempel

    Relational: return BooleanAtoms instead of bools.

    Tempel authored
    Fixed tests in several locations to check for the new results.  Corrected
    several areas where Python bools were expected; these areas should now handle
    Python bools and SymPy BooleanAtoms equally.
This page is out of date. Refresh to see the latest.
View
18 sympy/core/relational.py
@@ -181,11 +181,11 @@ def __new__(cls, lhs, rhs=0, **assumptions):
rhs = _sympify(rhs)
# If expressions have the same structure, they must be equal.
if lhs == rhs:
- return True
+ return S.true
# If one side is real and the other complex, they must be unequal.
elif (lhs.is_real != rhs.is_real and
None not in (lhs.is_real, rhs.is_real)):
- return False
+ return S.false
# Otherwise, see if the difference can be evaluated.
r = cls._eval_sides(lhs, rhs)
if r is not None:
@@ -195,7 +195,7 @@ def __new__(cls, lhs, rhs=0, **assumptions):
@classmethod
def _eval_relation(cls, lhs, rhs):
- return lhs == rhs
+ return _sympify(lhs == rhs)
Eq = Equality
@@ -231,12 +231,12 @@ def __new__(cls, lhs, rhs, **assumptions):
rhs = _sympify(rhs)
is_equal = Equality(lhs, rhs)
if is_equal == True or is_equal == False:
- return not is_equal
+ return ~is_equal
return Relational.__new__(cls, lhs, rhs, **assumptions)
@classmethod
def _eval_relation(cls, lhs, rhs):
- return lhs != rhs
+ return _sympify(lhs != rhs)
Ne = Unequality
@@ -546,7 +546,7 @@ class GreaterThan(_Greater):
@classmethod
def _eval_relation(cls, lhs, rhs):
- return lhs >= rhs
+ return _sympify(lhs >= rhs)
Ge = GreaterThan
@@ -559,7 +559,7 @@ class LessThan(_Less):
@classmethod
def _eval_relation(cls, lhs, rhs):
- return lhs <= rhs
+ return _sympify(lhs <= rhs)
Le = LessThan
@@ -572,7 +572,7 @@ class StrictGreaterThan(_Greater):
@classmethod
def _eval_relation(cls, lhs, rhs):
- return lhs > rhs
+ return _sympify(lhs > rhs)
Gt = StrictGreaterThan
@@ -585,7 +585,7 @@ class StrictLessThan(_Less):
@classmethod
def _eval_relation(cls, lhs, rhs):
- return lhs < rhs
+ return _sympify(lhs < rhs)
Lt = StrictLessThan
View
4 sympy/core/tests/test_assumptions.py
@@ -694,8 +694,8 @@ def test_special_assumptions():
assert (z2*z).is_zero is True
e = -3 - sqrt(5) + (-sqrt(10)/2 - sqrt(2)/2)**2
- assert (e < 0) is False
- assert (e > 0) is False
+ assert (e < 0) is S.false
+ assert (e > 0) is S.false
assert (e == 0) is False # it's not a literal 0
assert e.equals(0) is True
View
64 sympy/core/tests/test_relational.py
@@ -1,5 +1,6 @@
from sympy.utilities.pytest import XFAIL, raises
-from sympy import Symbol, symbols, oo, I, pi, Float, And, Or, Not, Implies, Xor
+from sympy import (S, Symbol, symbols, oo, I, pi, Float, And, Or, Not, Implies,
+ Xor)
from sympy.core.relational import ( Relational, Equality, Unequality,
GreaterThan, LessThan, StrictGreaterThan, StrictLessThan, Rel, Eq, Lt, Le,
Gt, Ge, Ne )
@@ -49,8 +50,8 @@ def test_rel_subs():
assert e.rhs == y
e = Eq(x, 0)
- assert e.subs(x, 0) is True
- assert e.subs(x, 1) is False
+ assert e.subs(x, 0) is S.true
+ assert e.subs(x, 1) is S.false
def test_wrappers():
@@ -83,6 +84,9 @@ def test_Eq():
def test_rel_Infinity():
+ # NOTE: All of these are actually handled by sympy.core.Number, and do
+ # not create Relational objects. Therefore, they still return True and
+ # False instead of S.true and S.false.
assert (oo > oo) is False
assert (oo > -oo) is True
assert (oo > 1) is True
@@ -110,29 +114,29 @@ def test_rel_Infinity():
def test_bool():
- assert Eq(0, 0) is True
- assert Eq(1, 0) is False
- assert Ne(0, 0) is False
- assert Ne(1, 0) is True
- assert Lt(0, 1) is True
- assert Lt(1, 0) is False
- assert Le(0, 1) is True
- assert Le(1, 0) is False
- assert Le(0, 0) is True
- assert Gt(1, 0) is True
- assert Gt(0, 1) is False
- assert Ge(1, 0) is True
- assert Ge(0, 1) is False
- assert Ge(1, 1) is True
- assert Eq(I, 2) is False
- assert Ne(I, 2) is True
- assert Gt(I, 2) not in [True, False]
- assert Ge(I, 2) not in [True, False]
- assert Lt(I, 2) not in [True, False]
- assert Le(I, 2) not in [True, False]
+ assert Eq(0, 0) is S.true
+ assert Eq(1, 0) is S.false
+ assert Ne(0, 0) is S.false
+ assert Ne(1, 0) is S.true
+ assert Lt(0, 1) is S.true
+ assert Lt(1, 0) is S.false
+ assert Le(0, 1) is S.true
+ assert Le(1, 0) is S.false
+ assert Le(0, 0) is S.true
+ assert Gt(1, 0) is S.true
+ assert Gt(0, 1) is S.false
+ assert Ge(1, 0) is S.true
+ assert Ge(0, 1) is S.false
+ assert Ge(1, 1) is S.true
+ assert Eq(I, 2) is S.false
+ assert Ne(I, 2) is S.true
+ assert Gt(I, 2) not in [S.true, S.false]
+ assert Ge(I, 2) not in [S.true, S.false]
+ assert Lt(I, 2) not in [S.true, S.false]
+ assert Le(I, 2) not in [S.true, S.false]
a = Float('.000000000000000000001', '')
b = Float('.0000000000000000000001', '')
- assert Eq(pi + a, pi + b) is False
+ assert Eq(pi + a, pi + b) is S.false
def test_rich_cmp():
@@ -149,14 +153,14 @@ def test_doit():
np = Symbol('np', nonpositive=True)
nn = Symbol('nn', nonnegative=True)
- assert Gt(p, 0).doit() is True
+ assert Gt(p, 0).doit() is S.true
assert Gt(p, 1).doit() == Gt(p, 1)
- assert Ge(p, 0).doit() is True
- assert Le(p, 0).doit() is False
- assert Lt(n, 0).doit() is True
- assert Le(np, 0).doit() is True
+ assert Ge(p, 0).doit() is S.true
+ assert Le(p, 0).doit() is S.false
+ assert Lt(n, 0).doit() is S.true
+ assert Le(np, 0).doit() is S.true
assert Gt(nn, 0).doit() == Gt(nn, 0)
- assert Lt(nn, 0).doit() is False
+ assert Lt(nn, 0).doit() is S.false
assert Eq(x, 0).doit() == Eq(x, 0)
View
2  sympy/simplify/tests/test_simplify.py
@@ -1702,7 +1702,7 @@ def test_issue_2998():
def test_signsimp():
e = x*(-x + 1) + x*(x - 1)
- assert signsimp(Eq(e, 0)) is True
+ assert signsimp(Eq(e, 0)) is S.true
def test_besselsimp():
View
12 sympy/solvers/solvers.py
@@ -683,7 +683,7 @@ def _sympified_list(w):
f[i] = fi.lhs - fi.rhs
elif isinstance(fi, Poly):
f[i] = fi.as_expr()
- elif isinstance(fi, bool) or fi.is_Relational:
+ elif isinstance(fi, (bool, C.BooleanAtom)) or fi.is_Relational:
return reduce_inequalities(f, assume=flags.get('assume'),
symbols=symbols)
@@ -1162,22 +1162,22 @@ def _solve(f, *symbols, **flags):
for candidate in candidates:
if candidate in result:
continue
- cond = cond is True or cond.subs(symbol, candidate)
- if cond is not False:
+ cond = (cond == True) or cond.subs(symbol, candidate)
+ if cond != False:
# Only include solutions that do not match the condition
# of any previous pieces.
matches_other_piece = False
for other_n, (other_expr, other_cond) in enumerate(f.args):
if other_n == n:
break
- if other_cond is False:
+ if other_cond == False:
continue
- if other_cond.subs(symbol, candidate) is True:
+ if other_cond.subs(symbol, candidate) == True:
matches_other_piece = True
break
if not matches_other_piece:
result.add(Piecewise(
- (candidate, cond is True or cond.doit()),
+ (candidate, cond == True or cond.doit()),
(S.NaN, True)
))
check = False
View
14 sympy/stats/rv.py
@@ -832,10 +832,10 @@ def return_generator():
if condition: # Check that these values satisfy the condition
gd = given_fn(*args)
- if not isinstance(gd, bool):
+ if gd != True and gd != False:
raise ValueError(
"Conditions must not contain free symbols")
- if gd is False: # If the values don't satisfy then try again
+ if not gd: # If the values don't satisfy then try again
continue
yield fn(*args)
@@ -861,9 +861,9 @@ def sample_iter_subs(expr, condition=None, numsamples=S.Infinity, **kwargs):
if condition is not None: # Check that these values satisfy the condition
gd = condition.xreplace(d)
- if not isinstance(gd, bool):
+ if gd != True and gd != False:
raise ValueError("Conditions must not contain free symbols")
- if gd is False: # If the values don't satisfy then try again
+ if not gd: # If the values don't satisfy then try again
continue
yield expr.xreplace(d)
@@ -889,10 +889,10 @@ def sampling_P(condition, given_condition=None, numsamples=1,
numsamples=numsamples, **kwargs)
for x in samples:
- if not isinstance(x, bool):
+ if x != True and x != False:
raise ValueError("Conditions must not contain free symbols")
- if x is True:
+ if x:
count_true += 1
else:
count_false += 1
@@ -1035,5 +1035,5 @@ def _value_check(condition, message):
Raises ValueError with message if condition is not True
"""
- if condition is not True:
+ if condition != True:
raise ValueError(message)
Something went wrong with that request. Please try again.