0

Following is the function I want to implement in python. I am getting Type Errors when defining a function. I tried defining using numpy.piecewise function object and also using just elif commands as a definition. I want to be able to then evaluate this function at different points as well as expressions like f(X-1) etc

This is my code:

from numpy import piecewise 
from scipy import *
from sympy.abc import x
from sympy.utilities.lambdify import lambdify, implemented_function
from sympy import Function
from sympy import *
h = 0.5 
a = -1
n = 2
x = Symbol('x')
expr = piecewise((0, x-a <=  -2*h), ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<=-h), (2*h**3/3-0.5*(x-a)**2*(2*h+(x-a)), -h<= x-a<= 0), (2*(h**3/3)-0.5*(x-a)**2*(2*h+(x-a)), 0<=x-a<=2*h), ((1/6)*(2*h-(x-a))**3, h<=x-a<=2*h), (0, x-a<=2*h))
p = lambdify((x, a,b,h), expr)

def basis(x,a,b, h):
    if x <= a-2*h:
        return 0;
    elif (x<=a-h) or (x >=2*h):
        return (1/6)*(2*h+(x-a))**3
    elif  (x-a<= 0) or (x-a >= -h):
        return (2*h**3/3-0.5*(x-a)**2*(2*h+(x-a)));
    elif (x<=2*h+a) or (x >= 0):
        return  (2*(h**3/3)-0.5*(x-a)**2*(2*h+(x-a)));
    elif (x<=a+2*h) or (x >= h):
        return (1/6)*(2*h-(x-a))**3; 
    elif x-a<=2*h:
        return 0

basis(x, -1,0.5,0)

Both ways I get this :

raise TypeError("cannot determine truth value of Relational")

TypeError: cannot determine truth value of Relational
2

2 Answers 2

1

You can use sympy's lambdify function to generate the numpy piecewise function. This is a simpler example but shows the general idea:

In [15]: from sympy import symbols, Piecewise                                                                                               

In [16]: x, a = symbols('x, a')                                                                                                   

In [17]: expr = Piecewise((x, x>a), (0, True))                                                                                    

In [18]: expr                                                                                                                     
Out[18]: 
⎧x  for a < x
⎨            
⎩0  otherwise

In [19]: from sympy import lambdify                                                                                               

In [20]: fun = lambdify((x, a), expr)                                                                                             

In [21]: fun([1, 3], [4, 2])                                                                                                      
Out[21]: array([0., 3.])

In [22]: import inspect                                                                                                           

In [23]: print(inspect.getsource(fun))                                                                                            
def _lambdifygenerated(x, a):
    return (select([less(a, x),True], [x,0], default=nan))
Sign up to request clarification or add additional context in comments.

6 Comments

What formatting wizardry lets you put a curly brace in a codeblock like that?
Thank you so much for this however when I add more symbols for my function I am still getting the relational error?
@peaapod, while this answer works, it doesn't help you identify the underlying problem with your code.
@DanielF that's sympy's pretty printer as shown from the isympy console. The curly brace is actually three separate unicode characters.
@OscarBenjamin Ahh, a different console. Didn't get anything like that in spyder so I was confused.
|
1

Sorry about the length of this answer, but I think you need to see the full debugging process. I had to look at the tracebacks and test small pieces of your code to identify the exact problem. I've seen a lot of the numpy ambiguity error, but not this sympy relational error.

===

Lets look at the whole traceback, not just one line of it. At the very least we need to identify which line of your code is producing the problem.

In [4]: expr = np.piecewise((0, x-a <=  -2*h), ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<
   ...: =-h), (2*h**3/3-0.5*(x-a)**2*(2*h+(x-a)), -h<= x-a<= 0), (2*(h**3/3)-0.5
   ...: *(x-a)**2*(2*h+(x-a)), 0<=x-a<=2*h), ((1/6)*(2*h-(x-a))**3, h<=x-a<=2*h)
   ...: , (0, x-a<=2*h))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-893bb4b36321> in <module>
----> 1 expr = np.piecewise((0, x-a <=  -2*h), ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<=-h), (2*h**3/3-0.5*(x-a)**2*(2*h+(x-a)), -h<= x-a<= 0), (2*(h**3/3)-0.5*(x-a)**2*(2*h+(x-a)), 0<=x-a<=2*h), ((1/6)*(2*h-(x-a))**3, h<=x-a<=2*h), (0, x-a<=2*h))

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

While np.piecewise is a numpy function, because x is a sympy.Symbol, the equations are sympy expressions. numpy and sympy are not well integrated. Somethings work, many others don't.

Did you try a small expression? Good programming practice is to start with small pieces, making sure those work first.

Let's try something smaller:

In [8]: expr = np.piecewise((0, x-a <=  -2*h),
   ...:  ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<=-h))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-37ff62e49efb> in <module>
      1 expr = np.piecewise((0, x-a <=  -2*h),
----> 2  ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<=-h))

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

and smaller pieces:

In [10]: (0, x-a <=  -2*h)
Out[10]: (0, x + 1 ≤ -1.0)

In [11]: ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<=-h)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-7bd9f95d077d> in <module>
----> 1 ((1/6)*(2*h+(x-a))**3, -2*h<=x-a<=-h)

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

In [12]: (1/6)*(2*h+(x-a))**3
Out[12]: 
                            3
1.33333333333333⋅(0.5⋅x + 1) 

But:

In [13]: -2*h<=x-a<=-h
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-5ffb419cd443> in <module>
----> 1 -2*h<=x-a<=-h

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

Simplify further:

In [14]: 0 < x < 3
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-59ba4ce00627> in <module>
----> 1 0 < x < 3

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

While a < b < c is allowed for regular Python variables and scalars, it does not work for numpy arrays, and evidently doesn't work for sympy variables either.

So the immediate problem has nothing to do with numpy. You are using invalid sympy expressions!

===

Your basis function reveals an aspect of the same problem. Again we need to look at the FULL traceback, and then test portions to identify the exact problem expression.

In [16]: basis(x, -1,0.5,0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-16-b328f95b3c79> in <module>
----> 1 basis(x, -1,0.5,0)

<ipython-input-15-c6436540e3f3> in basis(x, a, b, h)
      1 def basis(x,a,b, h):
----> 2     if x <= a-2*h:
      3         return 0;
      4     elif (x<=a-h) or (x >=2*h):
      5         return (1/6)*(2*h+(x-a))**3

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

This expression is a sympy relational:

In [17]: x <= -1
Out[17]: x ≤ -1

But we can't use such a relational in a Python if statement.

In [18]: if x <= -1: pass
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-b56148a48367> in <module>
----> 1 if x <= -1: pass

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __nonzero__(self)
    382 
    383     def __nonzero__(self):
--> 384         raise TypeError("cannot determine truth value of Relational")
    385 
    386     __bool__ = __nonzero__

TypeError: cannot determine truth value of Relational

Python if is simple True/False switch; its argument must evaluate to one or the other. The error is telling us that a sympy.Relational does not work. 0 < x < 1 is variation on that basic Python if (it tests 0<x and x<1 and performs a and).

A variation on this that we often see in numpy (and pandas) is:

In [20]: 0 < np.array([0,1,2])
Out[20]: array([False,  True,  True])

In [21]: 0 < np.array([0,1,2])<1
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-bc1039cec1fc> in <module>
----> 1 0 < np.array([0,1,2])<1

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

The numpy expression has multiple True/False values, and can't be used im a Python expression that requires a simple True/False.

edit

Correctly expanding the two sided tests:

In [23]: expr = np.piecewise((0, x-a <=  -2*h),
    ...:  ((1/6)*(2*h+(x-a))**3, (-2*h<=x-a)&(x-a<=-h)),
    ...:  (2*h**3/3-0.5*(x-a)**2*(2*h+(x-a)), (-h<= x-a)&(x-a<= 0)),
    ...:  (2*(h**3/3)-0.5*(x-a)**2*(2*h+(x-a)), (0<=x-a)&(x-a<=2*h)),
    ...:  ((1/6)*(2*h-(x-a))**3, (h<=x-a)&(x-a<=2*h)), (0, x-a<=2*h))

In [24]: expr
Out[24]: 
array([-0.5*(x + 1)**2*(x + 2.0) + 0.0833333333333333,
       -0.5*(x + 1)**2*(x + 2.0) + 0.0833333333333333], dtype=object)

In [26]: p = lambdify((x,), expr)

x is the only sympy symbol in expr.

Looking at the resulting function:

In [27]: print(p.__doc__)
Created with lambdify. Signature:

func(x)

Expression:

[-0.5*(x + 1)**2*(x + 2.0) + 0.0833333333333333  -0.5*(x + 1)**2*(x + 2.0)...

Source code:

def _lambdifygenerated(x):
    return ([-0.5*(x + 1)**2*(x + 2.0) + 0.0833333333333333, -0.5*(x + 1)**2*(x + 2.0) + 0.0833333333333333])

3 Comments

Standard way to do chained inequalities both for numpy and sympy is (a < b) & (b < c).
@hpaulj Thank you so much, this is extremely helpful! Really appreciate you taking it step by step and writing it all out in depth. Many thanks for your help, I learnt something new.
Is one able to plot the function generated?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.