sympy lambdify with function arguments in tuple? - python

Suppose you have computed fu as a result of a sympy calculation:
fu= sy.cos(x)+sy.sin(y)+1
where
x,y = sy.symbols("x y")
are symbols. Now you want to turn fu to a numpy function of (obviously) two variables.
You can do this by:
fun= sy.lambdify((x,y), fu, "numpy")
and you produce fun(x,y). Is there a way that lambdify can produce fun(z) with x,y=z, i.e produce the following function:
def fun(z):
x,y=z
return np.cos(x)+np.sin(y)+1

According to the documentation of lambdify you can nest the symbols in the first argument to denote unpacking in the signature:
import sympy as sym
x,y = sym.symbols('x y')
fu = sym.cos(x) + sym.sin(y) + 1
# original: signature f1(x, y)
f1 = sym.lambdify((x,y), fu)
f1(1, 2) # returns 2.4495997326938213
# nested: signature f2(z) where x,y = z
f2 = sym.lambdify([(x,y)], fu)
f2((1, 2)) # returns 2.4495997326938213
Even if this weren't possible to do within lambdify, we could define a thin wrapper that unpacks the arguments to the lambdified function (although this would be one function call slower on each call, so for fast functions that get called a lot of times this might lead to measurable impact on the runtime):
f = sym.lambdify((x,y), fu) # signature f(x,y)
def unpacking_f(z): # signature f(z) where x,y = z
return f(*z)
Of course if the function is not for a single, throw-away use in a numerical solver (such as curve fitting or minimization), it's good practice to use functools.wraps for wrappers. This would preserve the docstring automatically generated by lambdify.

Related

How to save a function as string in Python?

Given the following math function in form of a Python function:
import math
def f(x):
a = x - math.log(x)
b = x + math.log(x)
return a / x + b / math.log(x)
Is there any way that I can convert this function into a string like
expr = '(x - math.log(x)) / x + (x + math.log(x)) / math.log(x)'
so that when I want to call the function, I can simply use it by
func = lambda x: eval(expr)
print(func(3))
# 4.364513583657809
Note that I want to keep a and b in the original function. In reality, I have a lot more intermediate variables. Also, I am aware sympy could do similar tasks, but I would like to know if it is possible to convert the function to string, as it would be much more efficient to store.
Any suggestions?
Your function is already a string the moment you write it to a file!
If the function is valid Python, you can then just import it
from myfile import expr
print(expr(3)) # 4.364513583657809
WARNING Do not ever do this
If you want some incredibly evil logic for some reason, you can save your function directly with inspect.getsource(f) and then do something like this
>>> fn_body = """def f(x):
... a = x - math.log(x)
... b = x + math.log(x)
... return a / x + b / math.log(x)
... """
>>> eval(f'lambda {fn_body.split("(")[1].split(")")[0]}, _={exec(fn_body)}: {fn_body.split(" ", 1)[-1].split(")")[0]})')(3)
4.364513583657809
This works by finding the parts needed to call the function, evaluating the source as one of the args (to smuggle it into your namespace), and then building an anonymous function to call it
Further Caveats
not remotely maintainable
extremely fragile
will clobber or conflict with an existing function with the same name depending on use
you will still need to import math or whatever other libraries
won't work with default args without more pain
calling eval() first (before creating the lambda) will allow you to use inspect to get the signature (.signature()) and you can combine it with re and/or ast for a much robust parser, but a 1-liner seemed more exciting
manages to use both eval() and exec() for an extra helping of evil
You're probably looking for a symbolic equation solver!
Sympy's lambdify feature can do this for you!
>>> fn = sympy.lambdify("x", '(x - log(x)) / x + (x + log(x)) / log(x)')
>>> fn(x=3)
4.364513583657809
Caution: this also uses eval internally as #Joshua Voskamp warns about in a comment

How to solve the 'mul objective has no attribute 'cos' '

How to solve the multi object problem?
import sympy as sym
from sympy import lambdify
x = sym.Symbol('x')
n = sym.Symbol('n')
f = sym.sin(n*x)
derivative_f = f.diff(x)
derivative_f = lambdify(x, derivative_f)
x = float(input('x:'))
print(derivative_f(x))
print(derivative_f)
If I input 2, the expected result should be 2*cos(2*x).
Your code contains a few misconceptions. One problem is an important general programming rule: try to use different variable names for variables with different meanings. So, x shouldn't be assigned a float, as it was a symbolic variable before. And derivative_f being a symbolic expression, shouldn't be assigned the result of lambdify.
Note that sympy's symbolic world doesn't mix well with the numeric world of non-sympy functions. lambdify forms a bridge between these worlds, from completely symbolic to completely numeric. E.g. the function created with lambdify doesn't have access to the symbolic n anymore.
The code lambdify(x, derivative_f) contains an error. derivative_f is a symbolic expression containing two symbolic variables (x and n), so it needs to be called as derivative_f_x_n = lambdify((x, n), derivative_f) (also giving the result a different name). Afterwards, you can use numeric expressions as derivative_f_x_n(7, 8), but you can't use symbolic parameters anymore.
For what you seem to be trying to do, lambdify isn't adequate. To get the derivative with x substituted, you call .subs(x, new_value) directly on the symbolic version of derivative_f:
import sympy as sym
from sympy import lambdify
x = sym.Symbol('x')
n = sym.Symbol('n')
f = sym.sin(n * x)
derivative_f_x = f.diff(x)
x_float = 2.0
print(derivative_f_x.subs(x, x_float))
Output: n*cos(2.0*n)
Also note that sympy strongly prefers to work with exact symbolic expressions, and using floats inevitably brings in approximations. Whenever possible, integers, sympy fractions (sym.S(1)/2) or symbolic expressions (sym.sqrt(5)) are recommended.
You call also use the derivative with respect to x and then substitute n:
print(f.diff(x).subs(n, 2))
Output: 2*cos(2*x)
To use that function later on in numeric calculations, after substitution you only have one symbolic variable left(x):
g = lambdify(x, f.diff(x).subs(n, 2))
You can type help(g) to see its generated source code:
Source code:
def _lambdifygenerated(x):
return (2*cos(2*x))
Then you can use g e.g. to create matplotlib plot. After lambdify nothing is symbolic anymore.
import matplotlib.pyplot as plt
import numpy as np
xs = np.linspace(0, 10)
plt.plot(xs, g(xs))
The other answer hit the key point, that you needed to account for the symbolic n when lambdifying.
Look at the help of your function:
In [2]: help(derivative_f)
Help on function _lambdifygenerated:
_lambdifygenerated(x)
Created with lambdify. Signature:
....
Source code:
def _lambdifygenerated(x):
return (n*cos(n*x))
It's a function of x, drawing n from the global environment, here a symbol.
The full error message when given an array argument:
In [5]: derivative_f(np.array([.1,.2,.3]))
AttributeError: 'Mul' object has no attribute 'cos'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<ipython-input-5-e04e9f3c0a8e>", line 1, in <module>
derivative_f(np.array([.1,.2,.3]))
File "<lambdifygenerated-1>", line 2, in _lambdifygenerated
return (n*cos(n*x))
TypeError: loop of ufunc does not support argument 0 of type Mul which has no callable cos method
See the return line in the traceback. That's the 'source code' in the help, and tells you what numpy is trying to execute.
test the inner expression of that line:
In [6]: n*np.array([.1,.2,.3])
Out[6]: array([0.1*n, 0.2*n, 0.3*n], dtype=object)
This is an object dtype array. Math on such an array delegates the calculation to each of the elements. Specifically it tries
(0.1*n).cos()
0.1*n is a sympy mul expression, hence the error. symbols often don't work in numpy expressions.
lambdify is a handy way of creating numpy functions from sympy expressions, but it needs to be used with care, and full awareness of what it does, and does not, do.

How to define a function that returns the derivative to be later passed in a lambda function

Consider the following Python code
def Hubble_a(a):
...
return
def t_of_a(a):
res = np.zeros_like(a)
for i,ai in enumerate(a):
t,err = quad(lambda ap : 1.0/(ap*Hubble_a(ap)),0,ai)
res[i] = t
return res
a = np.logspace(-8,1,100)
What I want to do is to define a function Hubble_a(a) that gives the derivative of a divided by a, in order to integrate over it with quad. I tried to define it in this way:
def Hubble_a(a):
da = diff(a,1)
da_over_a = da/a
return da_over_a
where diff is the FFT derivative imported from scipy.fftpack. Then, if I execute t_of_a(a) I get a object of type 'float' has no len() error, presumably because quad doesn't take arrays? However I don't think this definition makes any sense in the first place because I want to pass a function such that lambda maps to 1.0/(ap*Hubble_a(ap) and know I'm passing the derivative of an array instead of a function that can then by integrated over. So I'm looking for help on how to implement a function that maps to something like (da/dt)/a.

Python: passing arguments to a function

I am using dlib's find_min_global function, an optimization algorithm which helps to find values which minimize the output of a function. For example
import dlib
def holder_table(x0,x1):
return -abs(sin(x0)*cos(x1)*exp(abs(1-sqrt(x0*x0+x1*x1)/pi)))
x,y = dlib.find_min_global(holder_table,
[-10,-10], # Lower bound constraints on x0 and x1 respectively
[10,10], # Upper bound constraints on x0 and x1 respectively
80) # The number of times find_min_global() will call holder_table()
Here the holder_table function returns the value that needs to be minimized for different values of x0 and x1.
Here the holder_table function takes in only the values that need to be optimized that is x0 and x1. But the function that I want to use with the dlib function takes more than x0 and x1. The function definiton looks like so
def holder_table(a,b,x0,x1):
return -abs(sin(b*x0/a)*cos(x1)*exp(abs(1-sqrt(x0*x0+x1*x1)/pi)))
The values a, b are not fixed and are the outputs of another function. Now, I can directly call the function the returns a, b inside the holder_table but I dont want to end up re-calculating them because each time holder_table is called a, b gets re-calculated and the process is time consuming.
How do I pass a, b to the holder_table function?
Your question is not 100% clear but it looks like you want a partial application. In Python this can be done using the dedicated functools.partial object, or quite simply with a closure (using either an inner function or lambda)
def holder_table(a,b,x0,x1):
return -abs(sin(b*x0/a)*cos(x1)*exp(abs(1-sqrt(x0*x0+x1*x1)/pi)))
def main():
a, b = some_heavy_function(...)
holder_table_partial = lambda ax, ay: holder_table(a, b, ax, ay)
x, y = dlib.find_min_global(
holder_table_partial, [-10,-10], [10,10], 80
)
Going only by your presentation of the specification, holder_table is a function that takes two arguments and returns the final result that can be used to help guide the optimization step. Also, if I understand correctly, a and b are components of the objective formula, but might take a while to compute and you don't want the computation of their logic to be called more frequently than necessary -- so including their derivation inside the holder_table seems inefficient.
What about something like:
def build_objective_function(a,b):
def holder_table(x0,x1):
return -abs(sin(b*x0/a)*cos(x1)*exp(abs(1-sqrt(x0*x0+x1*x1)/pi)))
return holder_table
And you'd call it like:
a = <compute a>
b = <compute b>
holder_table = build_objective_function(a,b) # holder_table will be a function
x,y = dlib.find_min_global(holder_table,
[-10,-10], # Lower bound constraints on x0 and x1 respectively
[10,10], # Upper bound constraints on x0 and x1 respectively
80) # The number of times find_min_global() will call holder_table()

sympy parser doesn't identify expm1 as a symbolic function

The function expm1 is not parsed properly in the following example:
from sympy.parsing.sympy_parser import parse_expr
print parse_expr('expm1(x)').diff('x')
gives
Derivative(expm1(x), x)
How can I get sympy identifying expm1 as symbolic function, so that I get the same result as
print parse_expr('exp(x) - 1').diff('x')
which gives exp(x)?
Since there is no built-in expm1 in SymPy, the parser does not know anything about this notation. The parameter local_dict of parse_expr can be used to explain the meaning of unfamiliar functions and symbols to SymPy.
expm1 = lambda x: exp(x)-1
parse_expr('expm1(x)', local_dict={"expm1": expm1})
This returns exp(x) - 1.
For expm1 to remain a single function with known derivative, rather than exp(x)-1, define it as a SymPy function (see tutorial for more such examples).
class expm1(Function):
def fdiff(self, argindex=1):
return exp(self.args[0])
A confirmation that this works:
e = parse_expr('expm1(x)', local_dict={"expm1": expm1})
print(e) # expm1(x)
print(e.diff(x)) # exp(x)
f = lambdify(x, e)
print(f(1)) # 1.718281828459045
print(f(1e-20)) # 1e-20, unlike exp(x)-1 which would evaluate to 0

Categories