I want to integrate a max function by sympy in python. However, it seems sympy cannot deal with such a function with relational comparison.
import sympy
def func(x):
return max(x,0)
x = sympy.symbols(x)
sympy.integrate(func(x),(x,-1,1))
Run the above code, it gives the error info:
File "<ipython-input-11-2630b8af4afe>", line 2, in func
return max(x,0)
File "/Applications/anaconda/lib/python3.6/site-packages/sympy/core/relational.py", line 304, in __nonzero__
raise TypeError("cannot determine truth value of Relational")
TypeError: cannot determine truth value of Relational
It seems sympy cannot deal with functions with comparison. It gives the same error when I try a piecewise function, which also includes a process of comparison.
SymPy uses uppercase and lowercase names to refer (often) to functions and classes. When you get a "truth value" error it means that something that could give a True or False answer didn't (like if x < 1: print('less than 1')). If x is a Symbol then x < 1 remains a Lt(x, 1) object.
In your case, the function max tried to compare x and 0 and it couldn't get True or False comparison. Use the Max object instead to see your integral evaluate:
>>> from sympy import Max
>>> from sympy.abc import x
>>> integrate(Max(x,0),(x,-1,1))
1/2
There are two problems:
The first parameter to sympy.symbols should be a string to represent the variable. For example: x = sympy.symbols('x').
max is a Python function. A sympy function should be used instead: sympy.Max
import sympy
def func(x):
return sympy.Max(0, x)
x = sympy.symbols('x')
sympy.integrate(func(x), (x, -1, 1))
Also piecewise functions can be integrated:
def func2(x):
return sympy.Piecewise((-x, x < 0), (x * x, x < 0.5), (x * x * x, True))
sympy.integrate(func2(x), (x, -1, 1)) # 0.776041666666667
Related
Given an expression, we can convert it into a function using sympy.lambdify. Similarly, given a function, we can convert it into an expression by evaluating it at symbol x. We would naturally expect that these two operations are inverses of each other. And, this expected behaviour is displayed when I use polynomial expressions. For example,
import sympy as sym
x = sym.symbols('x')
expr = 5*x**2 + 2*x + 3
f = sym.lambdify([x],expr)
f_expr = f(x)
print(expr == f_expr)
gives True as its output.
On the other hand, the following code does not run
import sympy as sym
x = sym.symbols('x')
expr = sym.sin(x)
f = sym.lambdify([x],expr)
f_expr = f(x)
print(expr == f_expr)
and throws the error "TypeError: loop of ufunc does not support argument 0 of type Symbol which has no callable sin method". Could you please explain why this is happening? My guess would be that sym.sin(x) does not return an "expression" analogous to 5x**2 + 2x + 3. But, I would like to understand it a bit better. Thanks in advance.
For a non-numeric object the lambdify code tries to do x.sin()
with making sure the sin function is from library sympy not numpy to avoid confusions.
you can try :
import sympy as sym
from sympy import sin
x = sym.symbols('x')
expr = sin(x)
# f = sym.lambdify(x,expr)
f = lambda x:sin(x)
f_expr = f(x)
print(expr == f_expr)
I'm obtaining a strange result when I vectorise a function with numpy.
import numpy as np
def scalar_function(x, y):
""" A function that returns x*y if x<y and x/y otherwise
"""
if x < y :
out = x * y
else:
out = x/y
return out
def vector_function(x, y):
"""
Make it possible to accept vectors as input
"""
v_scalar_function = np.vectorize(scalar_function)
return v_scalar_function(x, y)
we do have
scalar_function(4,3)
# 1.3333333333333333
Why is the vectorized version giving this strange output?
vector_function(np.array([3,4]), np.array([4,3]))
[12 1]
While this call to the vectorized version works fine:
vector_function(np.array([4,4]), np.array([4,3]))
[1. 1.33333333]
Reading numpy.divide:
Notes
The floor division operator // was added in Python 2.2 making // and / equivalent operators. The default floor division operation of / can be replaced by true division with from __future__ import division.
In Python 3.0, // is the floor division operator and / the true division operator. The true_divide(x1, x2) function is equivalent to true division in Python.
Makes me think this might be a remaining issue related to python2?
But I'm using python 3!
The docs for numpy.vectorize state:
The output type is determined by evaluating the first element of the
input, unless it is specified
Since you did not specify a return data type, and the first example is integer multiplication, the first array is also of integer type and rounds the values. Conversely, when the first operation is division, the datatype is automatically upcasted to float. You can fix your code by specifying a dtype in vector_function (which doesn't necessarily have to be as big as 64-bit for this problem):
def vector_function(x, y):
"""
Make it possible to accept vectors as input
"""
v_scalar_function = np.vectorize(scalar_function, otypes=[np.float64])
return v_scalar_function(x, y)
Separately, you should also make note from that very same documentation that numpy.vectorize is a convenience function and basically just wraps a Python for loop so is not vectorized in the sense that it provides any real performance gains.
For a binary choice like this, a better overall approach would be:
def vectorized_scalar_function(arr_1, arr_2):
return np.where(arr_1 < arr_2, arr_1 * arr_2, arr_1 / arr_2)
print(vectorized_scalar_function(np.array([4,4]), np.array([4,3])))
print(vectorized_scalar_function(np.array([3,4]), np.array([4,3])))
The above should be orders of magnitude faster and (possibly coincidentally rather than a hard-and-fast rule to rely on) doesn't suffer the type casting issue for the result.
Checking which statemets are triggered:
import numpy as np
def scalar_function(x, y):
""" A function that returns x*y if x<y and x/y otherwise
"""
if x < y :
print('if x: ',x)
print('if y: ',y)
out = x * y
print('if out', out)
else:
print('else x: ',x)
print('else y: ',y)
out = x/y
print('else out', out)
return out
def vector_function(x, y):
"""
Make it possible to accept vectors as input
"""
v_scalar_function = np.vectorize(scalar_function)
return v_scalar_function(x, y)
vector_function(np.array([3,4]), np.array([4,3]))
if x: 3
if y: 4
if out 12
if x: 3
if y: 4
if out 12
else x: 4
else y: 3
else out 1.3333333333333333 # <-- seems that the value is calculated correctly, but the wrong dtype is returned
So, you can rewrite the scalar function:
def scalar_function(x, y):
""" A function that returns x*y if x<y and x/y otherwise
"""
if x < y :
out = x * y
else:
out = x/y
return float(out)
vector_function(np.array([3,4]), np.array([4,3]))
array([12. , 1.33333333])
I read such a script:
add_numbers = lambda x, y: x+y
add_five = lambda y: add_numbers(5,y)
It derive a new function of one variable, add_five, that adds 5 to its argument:
from this point, introduced functools
In [9]: from functools import partial
In [10]: add_five = partial(add_numbers, 5)
In [11]: add_five(7)
Out[11]: 12
As a novice, I guess it can be easily achieved by
add_five = lambda y: 5+y
add_six = lambda y: 6+y
I am confused what's the benefit if not define add_five in a straighforward method?
The utility of partial is to easily create specialised versions of functions from a general definition.
The case of adding numbers can be illustrating here add_numbers is the general case.
from functools import partial
def add_numbers(x, y):
return x + y
add5 = partial(add_nums, 5)
Here add5 is a specialised case of add_numbers roughly equivalent to
def add5(x):
return add_numbers(x, 5)
Adding numbers is a very trivial example and does not show the utility of partial
The following is a simple example that may better show the utility of partial.
Consider writing a procedure to compute the square root of a number using the Babylonian method.
def square_root(x, tolerance, convergence_test):
y = 1
while not convergence_test(x, y, tolerance):
y = (y + x/y)/2
return y
For most numbers, the convergence test can simply check the difference between y squared and x is 0. Let's call this the absolute error of the estimate
def absolute_error(x, y, tolerance):
return abs(x - y**2) <= tolerance
For very large and small numbers, using absolute error of the estimate can lead to wrong answers for various reasons. In those cases, it is better to use the relative error:
def relative_error(x, y, tolerance):
return abs(x/(y**2) - 1) <= tolerance
With partial, we can easily create specialised functions for using the either absolute and relative error.
sqrt_rel_err = partial(square_root, convergence_test=relative_error)
sqrt_abs_err = partial(square_root, convergence_test=absolute_error)
Now using either is trivial
>>> sqrt_rel_err(2, 0.00001)
1.4142156862745097
>>> sqrt_abs_err(2, 0.00001)
1.4142156862745097
And for small numbers: we see using absolute error gives the wrong answer (especially when the tolerance is greater than the number we are trying to get the square root of)
>>> x = sqrt_abs_err(1e-6, 0.00001)
>>> x**2
4.4981362843183905e-06
Whilst the relative error method yields a more accurate answer.
>>> x = sqrt_rel_err(1e-6, 0.00001)
>>> x**2
1.0000003066033492e-06
This code runs correctly:
import sympy as sp
def xon (ton, t):
return (t-ton)/5
xonInt = sp.integrate (xon(ton, t),t)
print xonInt
But when the function becomes piecewise, e.g.:
import sympy as sp
def xon (ton, t):
if ton <= t:
return (t-ton)/5
else:
return 0
xonInt = sp.integrate (xon(ton, t),t)
print xonInt
I get the following error:
File "//anaconda/lib/python2.7/site-packages/sympy/core/relational.py", line > 103, in nonzero
raise TypeError("cannot determine truth value of\n%s" % self)
TypeError: cannot determine truth value of
ton <= t
As far as I understand, the error is due to the fact that both ton and t can be positive and negative. Is it correct? If I set positive integration limits for t the error doesn't disappear. How can I calculate the integral for the given piecewise function?
UPDATE: The updated version o the function, which works:
import sympy as sp
t = sp.symbols('t')
ton = sp.symbols('ton')
xon = sp.Piecewise((((t-ton)/5), t <= ton), (0, True))
xonInt = sp.integrate (xon,t)
print xonInt
Piecewise Class
You need to use the sympy Piecewise class.
As suggested in the comments:
Piecewise(((t - ton)/5, ton <= t), (0, True))
I have a complicated equation which is function of several variables and I want to manipulate like this example:
y = (x + a) / z
x = y*z - a
Is it possible to do this kind of manipulation matlab or python?
If there is possibility then please point out method or function to do this operation.
I tried following code in Sympy Shell:
x,y,z,a = symbols ('x y z a')
solve ( y = (x-a)/z, x)
I am getting following error:
Traceback (most recent call last):
File "<string>", line 1
SyntaxError: non-keyword arg after keyword arg
In Matlab you'd need the symbolic math toolbox (which I don't have so I can't test) and then you should be able to do use the solve function:
syms y x a z
solve(y == (x+a)/z, x)
I have NO experince with sympy but pretty sure based on the docs this is how you do it:
from sympy import solve, Poly, Eq, Function, exp
from sympy.abc import x, y, z, a
solve(y - (x+a)/z, x)
SymPy is a Python library, so your SymPy code needs to be valid Python. In Python, = is the assignment operator, which is why solve ( y = (x-a)/z, x) gives a SyntaxError. See http://docs.sympy.org/latest/gotchas.html#equals-signs.
To create an equality in SymPy use Eq, like solve(Eq(y, (x - a)/z, x), or use the fact that expressions in SymPy are assumed to be equal to zero, like solve(y - (x - a)/z, x).