I'm trying to minimise a function using BFGS method. Initially, I'm creating my functions as expressions. In the first iteration, my first expression is
f_sym = ((x0 - x6)**2 + (y0 - y6)**2)**0.5 + ((x0 - x9)**2 + (y0 - y9)**2)**0.5 + ((-x1 + 36)**2 + (-y1 + 9)**2)**0.5 + ((x1 - x7)**2 + (y1 - y7)**2)**0.5 + ((-x10 + x3)**2 + (-y10 + y3)**2)**0.5 + ((-x10 + x7)**2 + (-y10 + y7)**2)**0.5 + ((-x12 + x3)**2 + (-y12 + y3)**2)**0.5 + ((-x12 + x6)**2 + (-y12 + y6)**2)**0.5 + ((-x9 + 48)**2 + (-y9 + 97)**2)**0.5
variables = [x1, y1, x9, y9, x0, y0, x6, y6, x7, y7, x3, y3, x10, y10, x12, y12] #variables of the function expression f_sym
fprime_sym = [f_sym.diff(x_) for x_ in variables] # derivative of f_sym
To create vectorised functions for the above symbolic expressions, I use sympy.lamdify
as follows:
f_lmbda = sympy.lambdify(symvar, f_sym, 'numpy')
fprime_lmbda = sympy.lambdify(symvar, fprime_sym, 'numpy')
The functions produced by sympy.lambdify
take one argument for each variable in the corresponding expression. Also the SciPy optimization functions expect a vectorized function where all coordinates are packed into one array. To obtain functions that are compatible with the SciPy optimization routines, we need to wrap each of the functions generated by sympy.lambdify
with a Python function that reshuffles the arguments.I tried it as follows:
def func_XY_to_X_Y(f):
""" Wrapper for f(X) -> f(X[0], X[1])"""
return lambda X: np.array(f(X[0],X[1],X[2],X[3],X[4],X[5],X[6],X[7],X[8],X[9],X[10],X[11],X[12],X[13],X[14])) #since f_sym has 14 parameters we need 14 X[i]s
f = func_XY_to_X_Y(f_lmbda)
fprime = func_XY_to_X_Y(fprime_lmbda)
Now the functions f and fprime are vectorized Python functions. Then,
x_opt = optimize.fmin_ncg(f, x_0, fprime=fprime, fhess=fhess) #x_0 is the initial condition for all the variables
This solves the function and returns the new array of values for variables.
If there's only one function expression, this process can be done manually.
But if this happens inside a loop, the number of variables in each function expression will be different. Hence I need to make my def func_XY_to_X_Y(f):
a dynamic one.
Can someone please help me to make this a dynamic one?