Sympy: turn python function into sympy expression - python

For example, I have the function, that accepts only floats:
def do_smth(a: float, b: float) -> float:
return a + b
This is not necessarily a summation, it can be any function (for example, a function after autowrap) - what matters is that it does not accept Symbol as arguments
How can I turn it into sympy expression, which only evaluated when the numbers are substituted?
Something like this:
from sympy.abc import x,y
wf = wrap2sympy(do_smth, args=(x,y))
wf.subs(x, 5).subs(y, 0.5).evalf() # returns 5.5

Related

Sympy. How to check input is numeric, sympy expression and if so if a symbol included in the expression?

I want to code some function that takes 1 input. The input could be numeric (integer, float, Sympy constant like sympy.pi or expression evaluated as real number like sympy.sin(5)), or could be a sympy expression (like x**2-2*y+5.
What I need is to determine:
Is the input is numeric (has no symbols)?
If the input is expression, does a specific variable exist?
I have tried .free_symbols and it fails if the input is integer and throws exception.
Also str(input).isnumeric fails if the input is sympy.sin(5).
There is a function in SymPy called sympify whose purpose is to turn non-SymPy objects into SymPy expressions e.g. an int becomes a SymPy Integer etc. If you sympify the input then you can use free_symbols:
In [6]: def inspect(expr):
...: expr = sympify(expr)
...: symbols = expr.free_symbols
...: if not symbols:
...: print(expr, 'has no symbols')
...: else:
...: print(expr, 'has these symbols:', ', '.join(map(str, symbols)))
...:
In [7]: inspect(1)
1 has no symbols
In [8]: inspect(pi)
pi has no symbols
In [9]: inspect(x+y)
x + y has these symbols: y, x

Unexpected behaviour of sympy.lambdify with trigonometric functions

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)

String in list, into a function

myList = ['100', 'sin(x)', '0', '1']
I read these strings from a text file. I now want to execute the function call sin(x) from that string -- I want this to be a general interpretation of the string for any function expression.
I have tried the following with no success.
myList[1].replace("'", "")
I guess what I am asking is how to pull a string from a list and use it's 'raw text' so to speak.
The end goal is to get this function, where myList[1] should turn to sin(x)
from math import sin
def f(x):
return myList[1]
Thus f(x) will give the computed value of sin(x) from this list.
use dict and you archive it
from math import sin
myList = ['100', 'sin(x)', '0', '1']
options = { 'sin(x)':sin }
options[myList[1]](1)
0.8414709848078965
You're confusing a string value with executable code. Two basic points:
You can do this with the eval function, which evaluates a string as Python code.
Don't. Really; eval is a loaded gun pointed at your foot, and usually indicates a poor system design.
What is it that you're trying to do overall? If you want to trigger the trig functions with text input, you're better heeled to do it with enumerated checks, such as:
choice = myList[1]
if choice == "sin":
return sin(x)
elif choice == "cos":
return cos(x)
...
The safest way I can see to do this is to store the expected functions in a dictionary like this:
math_dict = {'sin(x)':sin, 'cos(x)':cos}
then you can call sin from the list like this:
def f(x):
return math_dict[myList[1]](x)
I just added cos(x) to show you can do this with any arbitrary function. You could then do something like this to generalize it:
def f(x):
return math_dict[x] if x in math_dict else None
Sin function is part of math library, you can get it like:
import math
getattr(math, 'sin')
That is same for all non-builtin libraries. Just split the string to get function name:
function_name = myList[1].split('(')[0]
function = getattr(math, function_name)
function(value for x)
For builtin functions like round:
getattr(globals()['__builtins__'], 'round')
With sympy.sympify and highlighting the warning
Warning : sympify uses eval. Don’t use it on unsanitized input.
you can solve this as
myList = ['100', 'sin(x)', '0', '1']
from sympy.abc import x
from sympy import sympify
N, expr, a,b = myList
# convert strings to objects
N=int(N); a=float(a); b=float(b);
expr = sympify(expr)
# define user function
def f(xval): return expr.subs(x,xval)
or for multi-point evaluation replace the last line with
from sympy import lambdify
f = lambdify(x, expr, "numpy")
# integrate as left Riemann sum (order 1)
from numpy import linspace
xlist = linspace(a,b,N)
integral = sum(f(xlist))*(b-a)/N

add two numpy arrays using lamdify - pass expression as function argument

I have this piece of code:
import numpy as np
import sympy
from sympy import symbols
from sympy.utilities.autowrap import ufuncify
from sympy.utilities.lambdify import lambdify
def test(expr,a,b):
a_var, b_var = symbols("a b")
#f = ufuncify((a_var, b_var), expr, backend='numpy')
f = lambdify( (a_var, b_var), expr, 'numpy')
return f(a_var, b_var)
a = np.array([2,3])
b = np.array([1,2])
expr = a + b
print(test(expr, a, b))
which give me:
../anaconda3/envs/python3/lib/python3.6/site-packages/sympy/core/sympify.py:282: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future
rational=rational) for x in a])
File "<string>", line 1
lambda _Dummy_52,_Dummy_53: ([3 5])
SyntaxError: invalid syntax
If I use the ufuncify :
...
TypeError: unhashable type: 'numpy.ndarray'
During handling of the above exception, another exception occurred:
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
====== UPDATE ================
One solution I found is to use expr like a string and then inside function use sympify:
data_a = np.array([2,3])
data_b = np.array([1,2])
expr = "data_a + data_b"
def test(expr,data_a, data_b):
a, b = symbols("data_a data_b")
expr = sympify(expr)
f = lambdify( (a, b), expr, 'numpy')
return f(data_a, data_b)
and I am taking:
[3 5]
But how can I avoid using the expression as a string?
lambdify converts SymPy expressions into NumPy functions. You are trying to convert a NumPy array into a NumPy function. The arguments to lambdify need to be SymPy objects.
You want something like
a_var, b_var = symbols("a b")
expr = a_var + b_var
f = lambdify((a_var, b_var), expr, 'numpy')
You'll then get
>>> a = np.array([2,3])
>>> b = np.array([1,2])
>>> f(a, b)
array([3, 5])
The basic code flow for lambdify is SymPy expression => NumPy function. To keep things clear in your head and in your code, you should start with just SymPy, and manipulate the expressions until you have a lambdified function. Then use it with your NumPy data. Start with defining symbol names. Then you can define an expression in terms of those symbols, without using a string (for instance, as I have done above). Once you have an expression and the symbols, you create a lambdified function. At that point, you pass NumPy arrays into the function. I recommend using different variable names for SymPy symbols/expressions and NumPy arrays, so that they don't get mixed up. I also recommend using the same variable name for symbols as the symbol names themselves, so that when you print the expression it will appear exactly as you would write it (e.g., below if you print(expr), you will get a + b, which is exactly what you would write to get expr).
In your updated example, you can use
a, b = symbols("a b")
expr = a + b
f = lambdify((a, b), expr, 'numpy')
data_a = np.array([2,3])
data_b = np.array([1,2])
f(data_a, data_b)
Note how I start with creating SymPy symbols and a SymPy expression from those symbols. Then I lambdify it. Once it's lambdified, I have the lambdified function (f). At this point, I'm not using SymPy at all anymore, just NumPy arrays (the data) and the lambdified function f.

Python: How to create a function? e.g. f(x) = ax^2

I want to have some sort of reference to a function but I do not know if I need to use a def f(x) or a lambda of some kind.
For instance I'd like to print f(3) and have it output 9a, or is this not how python works?
Second question: Assuming I have a working function, how do I return the degree of it?
To create a function, you define it. Functions can do anything, but their primary use pattern is taking parameters and returning values. You have to decide how exactly it transforms parameters into the return value.
For instance, if you want f(x) to return a number, then a should also be a numeric variable defined globally or inside the function:
In [1]: def f(x):
...: a = 2.5
...: return a * x**2
...:
In [2]: f(3)
Out[2]: 22.5
Or maybe you want it to return a string like this:
In [3]: def f(x):
...: return str(x**2) + 'a'
...:
In [4]: f(3)
Out[4]: '9a'
You have to specify your needs if you need more help.
EDIT: As it turns out, you want to work with polynomials or algebraic functions as objects and do some algebraic stuff with them. Python will allow doing that, but not using standard data types. You can define a class for a polynomial and then define any methods or functions to get the highest power or anything else. But Polynomial is not a built-in data type. There may be some good libraries defining such classes, though.
Python (and most other computer languages) don't do algebra, which is what you'll need if you want symbolic output like this. But you could have a function f(a,x) which returns the result for particular (numerical) values of a:
def f(a, x):
return a*x*x
But if you want a program or language which actually does algebra for you, check out sympy or commercial programs like Mathematica.
If you are just working with polynomials, and you just need a data structure which deals well with them, check out numpy and its polynomial class.
I normally use lambda for short and simple functions:
f = lambda a, x: a * x**2
here a and x are parameters of my function. You need to enter a and x
f(2,4)
If you want a as a constant parameter eg. a=2:
f = lambda x: 2 * x**2
f(5)
if you have a list of input values of x, you can combine map with lambda.
it is straighforward and easily readable.
(*map(lambda x: 3 * x**2, [1,2,3,4]),)
or
list(map(lambda x: 3 * x**2, [1,2,3,4])
cheers!
def func():
print "F(x) = 2x + 3"
x = int(raw_input('Enter an integer value for x: '))
Fx = 2 * x + 3
return Fx
print func()
have fun :)
Cheese,
you can use the def function in Python to create a math function, you could type this:
def f(x):
return(2x + (3 + 3) * 11 + 88) # <- you could make your own function.
print(f(3))
Log:
220
Like THAT
or in this:
def f(a, x):
return((a + x) ** (a * x))
then...
print(f(1, 2))
Log...
6

Categories