1

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?

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
ccc
  • 574
  • 2
  • 9
  • 22

1 Answers1

2

Use the *-argument unpacking operator:

def func_XY_to_X_Y(f):
    """ Wrapper for f(X) -> f(X[0], X[1], ..., X[n])"""
    return lambda X: np.array(f(*X)) 
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Thanks, Unutbu... But how can I ask to change the count (n) automatically? – ccc Oct 03 '17 at 12:11
  • 1
    `f(*X)` is equivalent to `f(X[0], X[1], ..., X[n])` where `n` is `len(X)-1`. In that sense, `n` is handled automatically by the `*` argument unpacking operator. – unutbu Oct 03 '17 at 12:13