How to embed boolean expressions in a sympy expressions?

162 views
Skip to first unread message

Gaetan de Menten

unread,
Jul 28, 2010, 5:47:19 AM7/28/10
to sy...@googlegroups.com
Hi all,

I have expressions which look like this:
"0.56 * ((i1 == 5) & (i2 == 2)) + 0.88 * ((i1 == 4) & (i2 == 3)) +
0.33 * ((i1 == 2) & (i2 ==4)) + 0.25 * (~b1 & b2)"
where i* are integer variables, b* are booleans.

At some point in my program I know the values for half the variables
(i1, b1, ...), and I would like to simplify the expression by
partially evaluating it.
eg if i1 is 5 and b1 is False, I would like to get:
"0.56 * (i2 == 2) + 0.25 * b2"

I was hoping sympy would help my in this, however I stumbled on two problems:

- the "==" is not supported by Sympy. From what I read, it seems to be
on purpose and I should use "Eq(expr1, expr2)" instead... Ok, fine,
but given that the expressions I get are written using ==, is there an
easy way to transform "==" to Eq()? I was hoping to avoid writing a
parser for those expressions... "sympify" does not help.

- while the boolean simplification work as I was hoping (in the
example above, sympy gets the "0.25 * b2" part right), the equalities
are not:

In [31]: x = sympy.Symbol('x')

In [32]: (0.5 * sympy.Eq(x, 5)).subs({'x': 5})
Out[32]: 0.5*(5 == 5)

I was hoping for just "0.5".

What's worse for me is that I was unable to find a way to evaluate
that expression. evalf() and the others I tried don't help. Is there a
way to do this?

Thanks in advance,
--
Gaëtan de Menten

Aaron S. Meurer

unread,
Jul 28, 2010, 1:41:29 PM7/28/10
to sy...@googlegroups.com

On Jul 28, 2010, at 3:47 AM, Gaetan de Menten wrote:

> Hi all,
>
> I have expressions which look like this:
> "0.56 * ((i1 == 5) & (i2 == 2)) + 0.88 * ((i1 == 4) & (i2 == 3)) +
> 0.33 * ((i1 == 2) & (i2 ==4)) + 0.25 * (~b1 & b2)"
> where i* are integer variables, b* are booleans.
>
> At some point in my program I know the values for half the variables
> (i1, b1, ...), and I would like to simplify the expression by
> partially evaluating it.
> eg if i1 is 5 and b1 is False, I would like to get:
> "0.56 * (i2 == 2) + 0.25 * b2"
>
> I was hoping sympy would help my in this, however I stumbled on two problems:
>
> - the "==" is not supported by Sympy. From what I read, it seems to be
> on purpose and I should use "Eq(expr1, expr2)" instead... Ok, fine,
> but given that the expressions I get are written using ==, is there an
> easy way to transform "==" to Eq()? I was hoping to avoid writing a
> parser for those expressions... "sympify" does not help.

If you have something that can parse a find and replace regular expression, like sed or some text editors, you could just do find: \((.+) == (.+)\) replace: Eq\(\1, \2\) (you will need to use extended regular expressions, or else swap the '(' and '\(' ). That's how I would do it anyway.

>
> - while the boolean simplification work as I was hoping (in the
> example above, sympy gets the "0.25 * b2" part right), the equalities
> are not:
>
> In [31]: x = sympy.Symbol('x')
>
> In [32]: (0.5 * sympy.Eq(x, 5)).subs({'x': 5})
> Out[32]: 0.5*(5 == 5)
>
> I was hoping for just "0.5".
>

It works in SymPy 0.6.7:

In [3]: (0.5 * sympy.Eq(x, 5)).subs({'x': 5})
Out[3]: 0.500000000000000

but not in older versions. So maybe you just need to upgrade.

If things still do not work, you might try the bleeding edge, which is the git master. Just install git and type

git clone git://github.com/sympy/sympy.git

in a terminal in some directory where you want sympy (I am assuming you are using Linux or Mac OS X, for Windows, use msysgit). Then do

cd sympy
./bin/isympy

and try from there. You can do

git pull

to update the bleeding edge (see git tutorials online for more information).

Aaron Meurer

Gaëtan de Menten

unread,
Jul 29, 2010, 4:31:55 AM7/29/10
to sympy
On Jul 28, 7:41 pm, "Aaron S. Meurer" <asmeu...@gmail.com> wrote:
> On Jul 28, 2010, at 3:47 AM, Gaetan de Menten wrote:
>
> > I have expressions which look like this:
> > "0.56 * ((i1 == 5) & (i2 == 2)) + 0.88 * ((i1 == 4) & (i2 == 3)) +
> > 0.33 * ((i1 == 2) & (i2 ==4)) + 0.25 * (~b1 & b2)"
> > where i* are integer variables, b* are booleans.
>
> > At some point in my program I know the values for half the variables
> > (i1, b1, ...), and I would like to simplify the expression by
> > partially evaluating it.
> > eg if i1 is 5 and b1 is False, I would like to get:
> > "0.56 * (i2 == 2) + 0.25 * b2"
>
> > I was hoping sympy would help my in this, however I stumbled on two problems:
>
> > - the "==" is not supported by Sympy. From what I read, it seems to be
> > on purpose and I should use "Eq(expr1, expr2)" instead... Ok, fine,
> > but given that the expressions I get are written using ==, is there an
> > easy way to transform "==" to Eq()? I was hoping to avoid writing a
> > parser for those expressions... "sympify" does not help.
>
> If you have something that can parse a find and replace regular expression, like sed or some text editors, you could just do find: \((.+) == (.+)\)  replace: Eq\(\1, \2\) (you will need to use extended regular expressions, or else swap the '(' and '\(' ).  That's how I would do it anyway.

Well, that's what I thought at first too, but this is not reliable
enough, expressions like ((x * 2 + 1) == 5) are not handled properly.
Oh well, I guess I'll need to code that parser (or at least inspect
Python's AST).

> > - while the boolean simplification work as I was hoping (in the
> > example above, sympy gets the "0.25 * b2" part right), the equalities
> > are not:
>
> > In [31]: x = sympy.Symbol('x')
>
> > In [32]: (0.5 * sympy.Eq(x, 5)).subs({'x': 5})
> > Out[32]: 0.5*(5 == 5)
>
> > I was hoping for just "0.5".
>
> It works in SymPy 0.6.7:
>
> In [3]: (0.5 * sympy.Eq(x, 5)).subs({'x': 5})
> Out[3]: 0.500000000000000

For some reason, it does not here since my tests were already using
0.6.7.

> but not in older versions.  So maybe you just need to upgrade.
>
> If things still do not work, you might try the bleeding edge, which is the git master.  Just install git and type
>
> git clone git://github.com/sympy/sympy.git

Python 2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit
(Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sympy
>>> sympy.__version__
'0.6.7-git'
>>> x = sympy.Symbol('x')
>>> (0.5 * sympy.Eq(x, 5)).subs({'x': 5})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sympy\core\basic.py", line 694, in subs
return self._subs_dict(sequence)
File "sympy\core\basic.py", line 784, in _subs_dict
return self._subs_list(subst)
File "sympy\core\basic.py", line 737, in _subs_list
result = result.subs(old, new)
File "sympy\core\basic.py", line 701, in subs
return self._subs_old_new(old, new)
File "sympy\core\cache.py", line 85, in wrapper
func_cache_it_cache[k] = r = func(*args, **kw_args)
File "sympy\core\basic.py", line 710, in _subs_old_new
return self._eval_subs(old, new)
File "sympy\core\mul.py", line 941, in _eval_subs
return self.__class__(*[s._eval_subs(old, new) for s in
self.args])
File "sympy\core\cache.py", line 85, in wrapper
func_cache_it_cache[k] = r = func(*args, **kw_args)
File "sympy\core\operations.py", line 35, in __new__
c_part, nc_part, order_symbols = cls.flatten(map(_sympify, args))
File "sympy\core\sympify.py", line 152, in _sympify
return sympify(a, strict=True)
File "sympy\core\sympify.py", line 75, in sympify
raise SympifyError(a)
sympy.core.sympify.SympifyError: SympifyError: True


Any idea?

> in a terminal in some directory where you want sympy (I am assuming you are using Linux or Mac OS X, for Windows, use msysgit).

Windows at work unfortunately...

Thanks for your help,
Gaetan de Menten

smichr

unread,
Jul 29, 2010, 1:33:19 PM7/29/10
to sympy
> Gaetan de Menten

I get the same error here under windows. I believe this has to do with
strict option in sympify which causes it to raise an error when an
unexpected object (like True or False is encountered).

My first thought at a solution to your problem was Piecewise:

>>> p=Piecewise((.5, Eq(x,5)),(0, not Eq(x,5)))
>>> p.subs(x,4)
0
>>> p.subs(x,5)
0.500000000000000

However, you have multiple conditions that you want to place on values
so I try to extend that in the most natural way:

>>> q=Piecewise((.5, And(Eq(i,1),Eq(j,2))))
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "C:\Documents and Settings\chris\sympy\sympy\functions
\elementary\piecewise.py", line 79, in __new__
" Relational, Number or Set" % (pair.cond, cond_type)
TypeError: Cond And(i == 1, j == 2) is of type And, but must be a
bool, Relational, Number or Set

** It would be nice if Piecewise would take and And or Or **

If you wrap the two parts in an Eq then you are back to the
sympification error:

>>> p = Piecewise((.5, Eq(Eq(i, 1), Eq(j, 2))))
>>> p.subs(i,1)
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "C:\Documents and Settings\chris\sympy\sympy\core\basic.py",
line 701, in subs
return self._subs_old_new(old, new)
File "C:\Documents and Settings\chris\sympy\sympy\core\cache.py",
line 85, in wrapper
func_cache_it_cache[k] = r = func(*args, **kw_args)
File "C:\Documents and Settings\chris\sympy\sympy\core\basic.py",
line 710, in _subs_old_new
return self._eval_subs(old, new)
File "C:\Documents and Settings\chris\sympy\sympy\functions
\elementary\piecewise.py", line 243, in _eval_subs
new_args.append((e._eval_subs(old, new), c._eval_subs(old, new)))
File "C:\Documents and Settings\chris\sympy\sympy\core
\relational.py", line 139, in _eval_subs
return self.__class__(self.lhs._eval_subs(old, new),
self.rhs._eval_subs(old, new))
File "C:\Documents and Settings\chris\sympy\sympy\core
\relational.py", line 117, in __new__
lhs = _sympify(lhs)
File "C:\Documents and Settings\chris\sympy\sympy\core\sympify.py",
line 233, in _sympify
return sympify(a, strict=True)
File "C:\Documents and Settings\chris\sympy\sympy\core\sympify.py",
line 77, in sympify
raise SympifyError(a)
SympifyError: SympifyError: True

So the other idea is to just create a string of your input expression
and (with variables defined) let python handle its evaluation:

>>> var('i j')
(i, j)
>>> s='.5*(i==1 and j==2)'
>>> eval(s)
0.0
>>> i=1;j=2
>>> eval(s)
0.5

OK, let's try this on your expression:

>>> s="0.56 * ((i1 == 5) & (i2 == 2)) + 0.88 * ((i1 == 4) & (i2 == 3)) +0.33 * ((i1 == 2) & (i2 ==4)) + 0.25 * (~b1 & b2)"
>>> i1=5;i2=2;b1=True
>>> eval(s)
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "<string>", line 1, in <module>
TypeError: unsupported operand type(s) for &: 'int' and 'Symbol'

OK, so it wants 'and' instead of '&'

>>> s=s.replace('&','and')
>>> eval(s)
0.56 + 0.25*b2

Will that work?
Reply all
Reply to author
Forward
0 new messages