-1

I'm trying to set a custom method for the local minimization. From my understanding of basinhopping, you start with an initial starting condition, minimize using a local method, then use basinhopping change the value of the parameters, and minimize locally again. I know basinhopping has a few options for local minimization methods, but I'm trying to play around with using a custom one and am having a few problems. So let's start with a MVE:

import scipy.optimize as so
import numpy as np
from scipy.optimize import basinhopping
from scipy.optimize import OptimizeResult

data_set=[np.array([0.01252837, 0.00994032, 0.02607758, 0.02938639, 0.03470389,
       0.0393117 , 0.05045751, 0.05288866]), np.array([0.01096586, 0.0093723 , 0.02996665, 0.0490254 , 0.06359686,
       0.07470107, 0.07533133, 0.10770218]), np.array([0.0108    , 0.01922004, 0.0290243 , 0.03236109, 0.00761577,
       0.05216742, 0.05526853, 0.06572701]), np.array([0.01744162, 0.02563377, 0.03473111, 0.04372516, 0.05533209,
       0.06429533, 0.06852919, 0.08112336]), np.array([0.01664812, 0.03377632, 0.04334155, 0.05260618, 0.06893069,
       0.07831481, 0.08656102, 0.0999732 ]), np.array([0.01933805, 0.02861486, 0.04197618, 0.05017609, 0.06353904,
       0.07471151, 0.08393098, 0.09447883])]
prot,lig=np.array([0.28, 0.26, 0.25, 0.23, 0.21, 0.19, 0.18, 0.15]), np.array([0.14, 0.26, 0.37, 0.47, 0.63, 0.77, 0.88, 1.1 ])

def global_fun(par,protein,ligand,csp_list):
    kd,wmax=par
    chi2=0
    for csp in csp_list:
        model=wmax*((protein+ligand+kd-np.sqrt(((protein+ligand+kd)**2)-(4*protein*ligand)))/(2*protein))
        chi2+=np.sum((csp-model)**2)
    return chi2
sol=basinhopping(global_fun,minimizer_kwargs={'args':(prot,lig,data_set),'options':{'maxiter':100000},'bounds':((0,np.inf),)*2,'method':'Nelder-Mead'},x0=[1,0.01])
>>>
                        fun: 0.00879004731452548
 lowest_optimization_result:  final_simplex: (array([[3.20231857, 0.33548486],
       [3.20238274, 0.33549024],
       [3.20240774, 0.33549202]]), array([0.00879005, 0.00879005, 0.00879005]))
           fun: 0.00879004731452548
       message: 'Optimization terminated successfully.'
          nfev: 66
           nit: 34
        status: 0
       success: True
             x: array([3.20231857, 0.33548486])
                    message: ['requested number of basinhopping iterations completed successfully']
      minimization_failures: 0
                       nfev: 8794
                        nit: 100
                          x: array([3.20231857, 0.33548486])

It works well, providing a good solution with low chi2.

Basinhopping takes the inputs in minimizer_kwargs, and uses those for the local solver. So the bounds, arguments, and options are all defined in minimizer_kwargs and passed on. So I tried my hand at a local solver (I'm only posting the bottom half of the code, the top half is the same).

def custom_method(args=(args,bounds,options)):
    local_so=so.minimize(args=args,bounds=bounds,options=options,method='Nelder-Mead')
    return OptimizeResult(x=local_so.x,fun=local_so.fun,success=local_so.success)

sol=basinhopping(global_fun,minimizer_kwargs={'args':(prot,lig,data_set),'options':{'maxiter':100000},'bounds':((0,np.inf),)*2,'method':custom_method},x0=[1,0.01])

This is incorrectly set up, but I don't quite know how to fix it. I don't quite know how to transfer the argument from minimizer_kwargs, over to my custom method so that they can be used as inputs for the local solver. Furthermore, I don't quite know how x0 is transferred over from basinhopping to the local solver. X0 will be the same for the first iteration for both basinhopping and the local solver, but after the first iteration of basinhopping, x0 for basinhopping will change, and that change will need to be transferred over to the local solver.

Nick ODell
  • 15,465
  • 3
  • 32
  • 66
samman
  • 559
  • 10
  • 29
  • In the documentation for scipy.optimize.minimize, it explains how the custom minimizer is called. See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html Ctrl-F "Custom minimizers" – Nick ODell Aug 07 '23 at 17:33
  • @NickODell yes, my example setup I have above was actually from scipys example. But I'm having difficulty in passing the arguments from minimizer_kwargs to the local solver. I was hoping to get some assistance in how that might be done. – samman Aug 07 '23 at 17:40
  • It seems like you could change the arguments of `custom_method` to `def custom_method(fun, x0, args, **kwargs):` and you'd get fun, x0, and args inside your custom solver. – Nick ODell Aug 07 '23 at 17:56
  • Oh wow, now I feel dumb. It was literally that simple (I just thought that was an example/placeholder). Thank you! If you'd like you can post it as an answer, or I can just delete the question. – samman Aug 07 '23 at 18:14

1 Answers1

2

Furthermore, I don't quite know how x0 is transferred over from basinhopping to the local solver. X0 will be the same for the first iteration for both basinhopping and the local solver, but after the first iteration of basinhopping, x0 for basinhopping will change, and that change will need to be transferred over to the local solver.

You can get this argument by writing a function with the following arguments, and using it as your custom method:

def custom_method(fun, x0, args, **kwargs):
    # implement your custom method here

More info here: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html (Ctrl-F "Custom minimizers")

Nick ODell
  • 15,465
  • 3
  • 32
  • 66