Toht
Toht

Reputation: 63

Error with sympy.lambdify for piecewise functions and numpy module

In sympy 0.7.6, I had no troubles with the following code for both the modules='sympy' and the modules='numpy' options. Now with sympy v0.1, the evaluation with modules='numpy' raise a ZeroDivisionError:

import sympy

x, y = sympy.symbols(['x', 'y'])
expr = sympy.Piecewise((1/x, y < -1), (x, y <= 1), (1/x, True))

f_sympy = sympy.lambdify([x, y], expr, modules='sympy')
f_numpy = sympy.lambdify([x, y], expr, modules='numpy')

print f_sympy(0, 1)  # performs well

print f_numpy(0, 1) # issue: ZeroDivisionError

Seems like the piecewise functions evaluate before the condition with modules='numpy'.

My questions are:

Is this behavior normal?

If so, why, and how to define a piecewise expression and evaluate it as fast as with numpy module without the sympy.lambdify procedure?

EDIT:

Found that in my case the solution is theano:

import sympy

x, y = sympy.symbols(['x', 'y'])
f = sympy.Piecewise((1/x, y < -1), (x, y <= 1), (1/x, True))

from sympy.printing.theanocode import theano_function
f_theano = theano_function([x, y], [f])

print f_theano(0, 1)  # OK, return 0

Upvotes: 5

Views: 1317

Answers (1)

asmeurer
asmeurer

Reputation: 91620

I deleted my other answer (in case you already saw it). There is a much simpler solution.

The ZeroDivisionError comes because the lambdified expression produces, roughly, lambda x, y: select([less(y, -1),less_equal(y, 1),True], [1/x,x,1/x], default=nan). The problem is that passing in x = 0 results in 1/0 being evaluated by Python, which raises the error.

But NumPy is just fine with dividing by zero. It will issue a warning, but otherwise works fine (it gives inf), and in this example there is no problem, because the inf is not actually used.

So the solution is to wrap the input to lambdify as numpy arrays, that is, instead of

f_numpy(0, 1)

use

f_numpy(array(0), array(1))

There is a SymPy issue discussing this if you are interested.

Upvotes: 4

Related Questions