Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python optimization using sympy lambdify and scipy

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?

like image 739
user3821012 Avatar asked Dec 06 '15 07:12

user3821012


1 Answers

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.

like image 184
user3821012 Avatar answered Oct 31 '22 02:10

user3821012