I'm trying to maximize a function defined by sympy but cannot make it work. The basic idea can be summarized as follows:
import sympy
from scipy.optimize import minimize
from sympy.utilities.lambdify import lambdify
a,b,G = sympy.symbols('a b G')
func = (G - a)**2 + b
my_func = lambdify((G,a,b), -1*func)
results = minimize(my_func,[0.1,0.1,0.1])
The code works if I define a single-variable function, but as long as I have more than one variable, I receive the following error message.
TypeError: <lambda>() takes exactly 3 arguments (1 given)
Can someone help me to identify where went wrong?
As @Dair pointed out, sympy's lambdify in general requires more than one arguments, while scipy expects only one argument, a list (or an array) that contains all the values of each variable. Because my objective function is most conveniently defined using sympy, I need to find a way to get around this incompatibility of sympy and scipy.
@lhcgeneva pointed out the answer to a similar question. This answer does not conveniently handle a large number of independent variables, especially when the number of independent variables can change, requiring writing out the "vectorized" version of the objective function to be redefined. However, inspired by this post, I figured out the following solution using *tuple():
import sympy
from scipy.optimize import minimize
from sympy.utilities.lambdify import lambdify
a,b,G = sympy.symbols('a b G')
func = -1*((G - a)**2 + b)
my_func = lambdify((G,a,b), func)
def my_func_v(x):
return my_func(*tuple(x))
results = minimize(my_func_v,[0.1,0.1,0.1])
In the example I gave, it seems unnecessary to use *tuple(), but for the problem I want to solve, it saves a lot of hassle. Here's an example that is more similar to the question that I want to solve
NUM_VAR = 10
x = np.array(sympy.symbols('x0:%d'%NUM_VAR))
func = np.sum((x-1)**2)
my_func = lambdify(x, func)
def my_func_v(x):
return my_func(*tuple(x))
results = minimize(my_func_v,np.zeros(NUM_VAR))
This *tuple() thing can save me from writing out all the elements of x like the following (for the case of NUM_VAR=10):
def my_func_v(x):
return my_func(x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9])
Also, we don't need to change my_func_v when NUM_VAR changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With