Eli
Eli

Reputation: 43

python-sympy: lambdify returns the wrong answer when function defined piecewise

I am baffled at why the answer to the following code is 10 and not 1. Can someone help me understand what is going on with lambdify or what is creating the wrong answer?

import sympy
from sympy.utilities.lambdify import lambdify
from sympy import Function
from sympy.abc import x, y

def kFct(xIndex,loc,k1,k2):
...   if xIndex <= loc:
...     return k1
...   else:
...     return k2
... 
loc = 0.5
k1 = 1
k2 = 10

kfun = lambdify( (x,y), kFct(x,loc,k1,k2) )
print kfun(0,0)
>>> 10

Why isn't the answer k1 or 1, since x = 0, which is less than loc = 0.5?

However, it return the correct answer if I do

print kfct(0,loc,k1,k2)
>>> 1

I need to have kfun as a function of x and y, because later on, I use it as part of an argument for an integral. It will also eventually depend on y.

I'm using python 2.6.8 on a Mac 10.6.x.

Upvotes: 2

Views: 636

Answers (2)

asmeurer
asmeurer

Reputation: 91620

You want to use Piecewise, which represents if branches in a symbolic way that SymPy can work with.

Here's your example with the values of loc, k1, and k2 put in explicitly. You can of course use them symbolically and replace them later with subs if your code dictates that.

>>> kFct = Piecewise((1, x < 0.5), (10, True))
>>> kfun = lambdify(x, kFct)
>>> kfun(0)
1
>>> kfun(1)
10

Upvotes: 2

DSM
DSM

Reputation: 353419

The arguments to lambdify are evaluated before they're passed, and so you're not actually passing your function to lambdify, you're passing the number 10:

>>> kFct(x, loc, k1, k2)
10

You get 10 here because

>>> x <= loc
x <= 0.5
>>> bool(x <= loc)
False

and so the second branch is taken. Because of the way Python works, I don't think you'll be able to get this to work -- you can't suppress only one branch being taken. (In principle, a program could do some crazy bytecode introspection, but I'm pretty sure sympy doesn't.)

You could use implemented_function, though:

>>> f = implemented_function(Function('kFct'), lambda x,y: kFct(x, loc, k1, k2))
>>> kfun = lambdify((x,y), f(x,y))
>>> kfun(0,0)
1
>>> kfun(0.5,0)
1
>>> kfun(0.51,0)
10
>>> kfun(1, 0.0)
10

I'm not sure how much this really benefits you, though, given the extra indirection: I'd probably just work with the function itself (assuming you're ultimately looking for a numerical evaluation of the integral.)

Upvotes: 3

Related Questions