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.