Skip to content

Instantly share code, notes, and snippets.

@gngdb
Last active September 24, 2022 12:03
Show Gist options
  • Select an option

  • Save gngdb/a94776788d6c82d69e96fe7db5d7acd4 to your computer and use it in GitHub Desktop.

Select an option

Save gngdb/a94776788d6c82d69e96fe7db5d7acd4 to your computer and use it in GitHub Desktop.
An example using scipy.optimize's basin hopping with torch calculating gradients
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "hungry-scanning",
"metadata": {},
"source": [
"An example using the global optimization algorithm [Basin Hopping][basin] to optimize a scalar function, with gradients on the inner loop conjugate gradient optimizer provided by pytorch. This could be adapted to use the [other global optimizers][global] implemented in `scipy.optimize`.\n",
"\n",
"Toy Function\n",
"===========\n",
"\n",
"I need a toy function to maximize in 1D. How about this:\n",
"\n",
"[global]: https://docs.scipy.org/doc/scipy/reference/optimize.html#global-optimization\n",
"[basin]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.basinhopping.html#scipy.optimize.basinhopping"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "competitive-sheep",
"metadata": {},
"outputs": [],
"source": [
"import torch"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "seeing-steering",
"metadata": {},
"outputs": [],
"source": [
"def toy(x):\n",
" x = x - .5 # bias to place the max in the range (0,1)\n",
" return torch.exp(-2.*x**2)*torch.cos(16.*x)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "western-ethernet",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"plt.style.use(\"ggplot\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "central-authorization",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7fe66f23fc70>]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x = torch.linspace(0,1,100)\n",
"plt.plot(x, toy(x))"
]
},
{
"cell_type": "markdown",
"id": "overall-worth",
"metadata": {},
"source": [
"Torch Function\n",
"============\n",
"\n",
"I need a wrapper function:\n",
"\n",
"1. Takes numpy array (with one value in it)\n",
"2. Turns it into a torch tensor that requires grad\n",
"3. Passes it through the above function\n",
"4. Calculates the gradient\n",
"5. Returns the function value as float and the gradient as numpy array"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "distinguished-index",
"metadata": {},
"outputs": [],
"source": [
"def torch_wrapper(x):\n",
" x = torch.tensor(x, requires_grad=True) # numpy array -> torch tensor\n",
" # DON'T NEED TO ZERO GRADIENT BECAUSE GRADIENT WILL BE NONE AT INIT\n",
" # if x.grad is not None:\n",
" # x.grad.zero_() # zero the gradient, same as optimizer.zero_grads()\n",
" f = -toy(x) # negated because we're minimizing to find an optimum\n",
" f.backward()\n",
" return f.item(), x.grad.numpy()"
]
},
{
"cell_type": "markdown",
"id": "overall-competition",
"metadata": {},
"source": [
"Run Basin Hopping\n",
"================\n",
"\n",
"Basin hopping runs an internal optimizer, I'm going to choose [conjugate gradients][conjugate] and the arguments must be passed in [minimizer_kwargs][kwargs].\n",
"\n",
"* `method=\"CG\"` selects the conjugate gradient algorithm\n",
"* `disp=True` displays status messages, just for this example\n",
"* `jac=True` tells the inner optimizer to expect `func` (in this case, `torch_wrapper`) to return a tuple of `(func_val, grad_array)`; as the docs say:\n",
"\n",
"> If jac is a Boolean and is True, fun is assumed to return and objective and gradient as an (f, g) tuple. Methods ‘Newton-CG’, ‘trust-ncg’, ‘dogleg’, ‘trust-exact’, and ‘trust-krylov’ require that either a callable be supplied, or that fun return the objective and gradient.\n",
"\n",
"[conjugate]: https://docs.scipy.org/doc/scipy/reference/optimize.minimize-cg.html#optimize-minimize-cg\n",
"[kwargs]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.basinhopping.html#scipy.optimize.basinhopping"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "adjusted-singles",
"metadata": {},
"outputs": [],
"source": [
"import scipy.optimize"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "organized-stake",
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"basinhopping step 0: f -0.738092\n",
"basinhopping step 1: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 2: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 3: f -0.0649789 trial_f -0.0649789 accepted 1 lowest_f -0.738092\n",
"basinhopping step 4: f -0.000502366 trial_f -0.000502366 accepted 1 lowest_f -0.738092\n",
"basinhopping step 5: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 6: f -0.0649789 trial_f -0.0649789 accepted 1 lowest_f -0.738092\n",
"basinhopping step 7: f -0.0649789 trial_f -0.00774549 accepted 0 lowest_f -0.738092\n",
"basinhopping step 8: f -0.0649789 trial_f -0.0649789 accepted 1 lowest_f -0.738092\n",
"basinhopping step 9: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 10: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 11: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 12: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 13: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 14: f -0.000502366 trial_f -0.000502366 accepted 1 lowest_f -0.738092\n",
"basinhopping step 15: f -0.000502366 trial_f -0.000502366 accepted 1 lowest_f -0.738092\n",
"basinhopping step 16: f -0.00774549 trial_f -0.00774549 accepted 1 lowest_f -0.738092\n",
"basinhopping step 17: f -0.296759 trial_f -0.296759 accepted 1 lowest_f -0.738092\n",
"basinhopping step 18: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"found new global minimum on step 18 with function value -1\n",
"basinhopping step 19: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 20: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 21: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 22: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 23: f -0.738092 trial_f -0.00774549 accepted 0 lowest_f -1\n",
"basinhopping step 24: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 25: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 26: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 27: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 28: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 29: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 30: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 31: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 32: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 33: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 34: f -8.57577e-10 trial_f -8.57577e-10 accepted 1 lowest_f -1\n",
"basinhopping step 35: f -9.83625e-12 trial_f -9.83625e-12 accepted 1 lowest_f -1\n",
"basinhopping step 36: f -6.92756e-10 trial_f -6.92756e-10 accepted 1 lowest_f -1\n",
"basinhopping step 37: f 2.56061e-10 trial_f 2.56061e-10 accepted 1 lowest_f -1\n",
"basinhopping step 38: f -1.89463e-11 trial_f -1.89463e-11 accepted 1 lowest_f -1\n",
"basinhopping step 39: f -8.41884e-11 trial_f -8.41884e-11 accepted 1 lowest_f -1\n",
"basinhopping step 40: f -2.38918e-07 trial_f -2.38918e-07 accepted 1 lowest_f -1\n",
"basinhopping step 41: f -3.20204e-07 trial_f -3.20204e-07 accepted 1 lowest_f -1\n",
"basinhopping step 42: f -1.77182e-05 trial_f -1.77182e-05 accepted 1 lowest_f -1\n",
"basinhopping step 43: f -0.000502366 trial_f -0.000502366 accepted 1 lowest_f -1\n",
"basinhopping step 44: f -1.77192e-05 trial_f -1.77192e-05 accepted 1 lowest_f -1\n",
"basinhopping step 45: f -3.38004e-07 trial_f -3.38004e-07 accepted 1 lowest_f -1\n",
"basinhopping step 46: f -3.53261e-09 trial_f -3.53261e-09 accepted 1 lowest_f -1\n",
"basinhopping step 47: f 3.71179e-09 trial_f 3.71179e-09 accepted 1 lowest_f -1\n",
"basinhopping step 48: f 3.06148e-08 trial_f 3.06148e-08 accepted 1 lowest_f -1\n",
"basinhopping step 49: f -7.20242e-10 trial_f -7.20242e-10 accepted 1 lowest_f -1\n",
"adaptive stepsize: acceptance rate 0.920000 target 0.500000 new stepsize 0.555556 old stepsize 0.5\n",
"basinhopping step 50: f -1.8799e-11 trial_f -1.8799e-11 accepted 1 lowest_f -1\n",
"basinhopping step 51: f 4.79491e-12 trial_f 4.79491e-12 accepted 1 lowest_f -1\n",
"basinhopping step 52: f -1.69689e-11 trial_f -1.69689e-11 accepted 1 lowest_f -1\n",
"basinhopping step 53: f -1.4664e-11 trial_f -1.4664e-11 accepted 1 lowest_f -1\n",
"basinhopping step 54: f -1.85035e-15 trial_f -1.85035e-15 accepted 1 lowest_f -1\n",
"basinhopping step 55: f -1.00309e-16 trial_f -1.00309e-16 accepted 1 lowest_f -1\n",
"basinhopping step 56: f 8.76664e-19 trial_f 8.76664e-19 accepted 1 lowest_f -1\n",
"basinhopping step 57: f -3.74651e-17 trial_f -3.74651e-17 accepted 1 lowest_f -1\n",
"basinhopping step 58: f 1.26588e-15 trial_f 1.26588e-15 accepted 1 lowest_f -1\n",
"basinhopping step 59: f 3.21604e-18 trial_f 3.21604e-18 accepted 1 lowest_f -1\n",
"basinhopping step 60: f -2.81502e-20 trial_f -2.81502e-20 accepted 1 lowest_f -1\n",
"basinhopping step 61: f -2.42157e-23 trial_f -2.42157e-23 accepted 1 lowest_f -1\n",
"basinhopping step 62: f 2.19138e-21 trial_f 2.19138e-21 accepted 1 lowest_f -1\n",
"basinhopping step 63: f 6.45057e-25 trial_f 6.45057e-25 accepted 1 lowest_f -1\n",
"basinhopping step 64: f 8.3955e-29 trial_f 8.3955e-29 accepted 1 lowest_f -1\n",
"basinhopping step 65: f 4.99976e-29 trial_f 4.99976e-29 accepted 1 lowest_f -1\n",
"basinhopping step 66: f 1.08694e-26 trial_f 1.08694e-26 accepted 1 lowest_f -1\n",
"basinhopping step 67: f 6.77215e-25 trial_f 6.77215e-25 accepted 1 lowest_f -1\n",
"basinhopping step 68: f 1.65313e-21 trial_f 1.65313e-21 accepted 1 lowest_f -1\n",
"basinhopping step 69: f 3.01779e-18 trial_f 3.01779e-18 accepted 1 lowest_f -1\n",
"basinhopping step 70: f -5.08264e-20 trial_f -5.08264e-20 accepted 1 lowest_f -1\n",
"basinhopping step 71: f 5.43143e-22 trial_f 5.43143e-22 accepted 1 lowest_f -1\n",
"basinhopping step 72: f -9.18275e-20 trial_f -9.18275e-20 accepted 1 lowest_f -1\n",
"basinhopping step 73: f 3.43676e-22 trial_f 3.43676e-22 accepted 1 lowest_f -1\n",
"basinhopping step 74: f 1.09878e-25 trial_f 1.09878e-25 accepted 1 lowest_f -1\n",
"basinhopping step 75: f -2.1628e-23 trial_f -2.1628e-23 accepted 1 lowest_f -1\n",
"basinhopping step 76: f 1.0914e-25 trial_f 1.0914e-25 accepted 1 lowest_f -1\n",
"basinhopping step 77: f 1.5329e-28 trial_f 1.5329e-28 accepted 1 lowest_f -1\n",
"basinhopping step 78: f 1.06108e-28 trial_f 1.06108e-28 accepted 1 lowest_f -1\n",
"basinhopping step 79: f 8.7774e-33 trial_f 8.7774e-33 accepted 1 lowest_f -1\n",
"basinhopping step 80: f -4.55858e-39 trial_f -4.55858e-39 accepted 1 lowest_f -1\n",
"basinhopping step 81: f -2.66178e-40 trial_f -2.66178e-40 accepted 1 lowest_f -1\n",
"basinhopping step 82: f -1.24256e-43 trial_f -1.24256e-43 accepted 1 lowest_f -1\n",
"basinhopping step 83: f -1.18363e-43 trial_f -1.18363e-43 accepted 1 lowest_f -1\n",
"basinhopping step 84: f 2.40247e-38 trial_f 2.40247e-38 accepted 1 lowest_f -1\n",
"basinhopping step 85: f -1.29757e-34 trial_f -1.29757e-34 accepted 1 lowest_f -1\n",
"basinhopping step 86: f 5.52329e-30 trial_f 5.52329e-30 accepted 1 lowest_f -1\n",
"basinhopping step 87: f 5.48749e-30 trial_f 5.48749e-30 accepted 1 lowest_f -1\n",
"basinhopping step 88: f -3.53814e-35 trial_f -3.53814e-35 accepted 1 lowest_f -1\n",
"basinhopping step 89: f -2.4334e-35 trial_f -2.4334e-35 accepted 1 lowest_f -1\n",
"basinhopping step 90: f -6.95159e-31 trial_f -6.95159e-31 accepted 1 lowest_f -1\n",
"basinhopping step 91: f 7.90719e-33 trial_f 7.90719e-33 accepted 1 lowest_f -1\n",
"basinhopping step 92: f -1.90114e-35 trial_f -1.90114e-35 accepted 1 lowest_f -1\n",
"basinhopping step 93: f 1.0968e-32 trial_f 1.0968e-32 accepted 1 lowest_f -1\n",
"basinhopping step 94: f -4.36463e-32 trial_f -4.36463e-32 accepted 1 lowest_f -1\n",
"basinhopping step 95: f -3.95555e-32 trial_f -3.95555e-32 accepted 1 lowest_f -1\n",
"basinhopping step 96: f -1.19614e-34 trial_f -1.19614e-34 accepted 1 lowest_f -1\n",
"basinhopping step 97: f -1.27009e-36 trial_f -1.27009e-36 accepted 1 lowest_f -1\n",
"basinhopping step 98: f -5.54331e-39 trial_f -5.54331e-39 accepted 1 lowest_f -1\n",
"basinhopping step 99: f -1.60483e-44 trial_f -1.60483e-44 accepted 1 lowest_f -1\n",
"adaptive stepsize: acceptance rate 0.960000 target 0.500000 new stepsize 0.617284 old stepsize 0.555556\n",
"basinhopping step 100: f 1.09229e-37 trial_f 1.09229e-37 accepted 1 lowest_f -1\n"
]
}
],
"source": [
"x0 = 0.1\n",
"res = scipy.optimize.basinhopping(torch_wrapper, x0, disp=True,\n",
" minimizer_kwargs=dict(method=\"CG\", jac=True)\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "antique-associate",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
" fun: -1.0\n",
" lowest_optimization_result: fun: -1.0\n",
" jac: array([-9.46579248e-08])\n",
" message: 'Optimization terminated successfully.'\n",
" nfev: 14\n",
" nit: 5\n",
" njev: 14\n",
" status: 0\n",
" success: True\n",
" x: array([0.5])\n",
" message: ['requested number of basinhopping iterations completed successfully']\n",
" minimization_failures: 0\n",
" nfev: 486\n",
" nit: 100\n",
" njev: 466\n",
" x: array([0.5])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"res"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "transparent-grade",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimum found at [0.5] with value 1.0\n"
]
}
],
"source": [
"xstar = res.x\n",
"f = -res.fun\n",
"print(f\"Optimum found at {xstar} with value {f}\")"
]
},
{
"cell_type": "markdown",
"id": "cleared-poison",
"metadata": {},
"source": [
"Bounds\n",
"======\n",
"\n",
"Adding bounds to this algorithm is slightly different than the standard `scipy.optimize`, where we just enter `bounds=(xmin, xmax)`. There's an example way to implement bounds at the bottom of the [basin hopping page][bounds]. It works by rejecting steps into domains outside the bounds:\n",
"\n",
"[bounds]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.basinhopping.html#scipy.optimize.basinhopping"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "integrated-paraguay",
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"basinhopping step 0: f -0.738092\n",
"basinhopping step 1: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"found new global minimum on step 1 with function value -1\n",
"basinhopping step 2: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 3: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 4: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 5: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 6: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 7: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 8: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 9: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 10: f -0.738092 trial_f -0.00774549 accepted 0 lowest_f -1\n",
"basinhopping step 11: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 12: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 13: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 14: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 15: f -0.738092 trial_f -0.296759 accepted 0 lowest_f -1\n",
"basinhopping step 16: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 17: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 18: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 19: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 20: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 21: f -0.738092 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 22: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 23: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 24: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 25: f -0.738092 trial_f -0.00774549 accepted 0 lowest_f -1\n",
"basinhopping step 26: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 27: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 28: f -0.738092 trial_f -0.00774549 accepted 0 lowest_f -1\n",
"basinhopping step 29: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 30: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 31: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 32: f -0.738092 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 33: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 34: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 35: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 36: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 37: f -0.738092 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 38: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 39: f -0.738092 trial_f -0.296759 accepted 0 lowest_f -1\n",
"basinhopping step 40: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 41: f -1 trial_f -1.77193e-05 accepted 0 lowest_f -1\n",
"basinhopping step 42: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 43: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 44: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 45: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 46: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 47: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 48: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 49: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"adaptive stepsize: acceptance rate 0.640000 target 0.500000 new stepsize 0.555556 old stepsize 0.5\n",
"basinhopping step 50: f -0.738092 trial_f -0.00774549 accepted 0 lowest_f -1\n",
"basinhopping step 51: f -0.738092 trial_f -0.296759 accepted 0 lowest_f -1\n",
"basinhopping step 52: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 53: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 54: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 55: f -1 trial_f -0.296759 accepted 0 lowest_f -1\n",
"basinhopping step 56: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 57: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 58: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 59: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 60: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 61: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 62: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 63: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 64: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 65: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 66: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 67: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 68: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 69: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 70: f -1 trial_f -0.738092 accepted 0 lowest_f -1\n",
"basinhopping step 71: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 72: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 73: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 74: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 75: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 76: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 77: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 78: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 79: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 80: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 81: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 82: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 83: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 84: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 85: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 86: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 87: f -0.738092 trial_f -0.296759 accepted 0 lowest_f -1\n",
"basinhopping step 88: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 89: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 90: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 91: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 92: f -0.738092 trial_f -0.00774549 accepted 0 lowest_f -1\n",
"basinhopping step 93: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 94: f -1 trial_f -0.0649789 accepted 0 lowest_f -1\n",
"basinhopping step 95: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 96: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 97: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"basinhopping step 98: f -1 trial_f -1 accepted 1 lowest_f -1\n",
"basinhopping step 99: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -1\n",
"adaptive stepsize: acceptance rate 0.700000 target 0.500000 new stepsize 0.617284 old stepsize 0.555556\n",
"basinhopping step 100: f -0.738092 trial_f -0.0649789 accepted 0 lowest_f -1\n"
]
}
],
"source": [
"class MyBounds(object):\n",
" def __init__(self, xmax=1.0, xmin=0.):\n",
" self.xmax = xmax\n",
" self.xmin = xmin\n",
" def __call__(self, **kwargs):\n",
" x = kwargs[\"x_new\"][0]\n",
" tmax = x <= self.xmax\n",
" tmin = x >= self.xmin\n",
" return tmax and tmin\n",
"\n",
"mybounds = MyBounds()\n",
"\n",
"x0 = 0.1\n",
"res = scipy.optimize.basinhopping(torch_wrapper, x0, disp=True,\n",
" minimizer_kwargs=dict(method=\"CG\", jac=True),\n",
" accept_test=mybounds)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "explicit-kinase",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
" fun: -1.0\n",
" lowest_optimization_result: fun: -1.0\n",
" jac: array([-2.77111667e-12])\n",
" message: 'Optimization terminated successfully.'\n",
" nfev: 14\n",
" nit: 5\n",
" njev: 14\n",
" status: 0\n",
" success: True\n",
" x: array([0.5])\n",
" message: ['requested number of basinhopping iterations completed successfully']\n",
" minimization_failures: 0\n",
" nfev: 1180\n",
" nit: 100\n",
" njev: 1109\n",
" x: array([0.5])"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"res"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "other-cleaning",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimum found at [0.5] with value 1.0\n"
]
}
],
"source": [
"xstar = res.x\n",
"f = -res.fun\n",
"print(f\"Optimum found at {xstar} with value {f}\")"
]
},
{
"cell_type": "markdown",
"id": "female-pharmacology",
"metadata": {},
"source": [
"Reparameterizing\n",
"==============\n",
"\n",
"I would normally reparameterize when optimizing a bounded value, but this seems to fail with basin hopping."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "honest-beauty",
"metadata": {},
"outputs": [],
"source": [
"def torch_wrapper(x):\n",
" x = torch.tensor(x, requires_grad=True) # numpy array -> torch tensor\n",
" reparam_x = torch.sigmoid(x)\n",
" f = -toy(reparam_x) # negated because we're minimizing to find an optimum\n",
" f.backward()\n",
" return f.item(), x.grad.numpy()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "taken-skirt",
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"basinhopping step 0: f -0.738092\n",
"basinhopping step 1: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 2: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"found new global minimum on step 2 with function value -0.738092\n",
"basinhopping step 3: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 4: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 5: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 6: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 7: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 8: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 9: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 10: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 11: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 12: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 13: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 14: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 15: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 16: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 17: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 18: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 19: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 20: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 21: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 22: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 23: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 24: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 25: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 26: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 27: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 28: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 29: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 30: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"found new global minimum on step 30 with function value -0.738092\n",
"basinhopping step 31: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 32: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 33: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 34: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 35: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 36: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 37: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 38: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 39: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 40: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 41: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 42: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 43: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 44: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 45: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 46: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 47: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 48: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 49: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"adaptive stepsize: acceptance rate 0.980000 target 0.500000 new stepsize 0.555556 old stepsize 0.5\n",
"basinhopping step 50: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 51: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 52: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 53: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 54: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 55: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 56: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 57: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 58: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 59: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 60: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 61: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 62: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 63: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 64: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 65: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 66: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 67: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 68: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 69: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 70: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 71: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 72: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 73: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 74: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 75: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 76: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 77: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 78: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 79: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 80: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 81: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 82: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 83: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 84: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 85: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 86: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 87: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 88: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 89: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 90: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 91: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 92: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 93: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 94: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 95: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 96: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 97: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 98: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"basinhopping step 99: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n",
"adaptive stepsize: acceptance rate 0.990000 target 0.500000 new stepsize 0.617284 old stepsize 0.555556\n",
"basinhopping step 100: f -0.738092 trial_f -0.738092 accepted 1 lowest_f -0.738092\n"
]
}
],
"source": [
"x0 = -1.\n",
"res = scipy.optimize.basinhopping(torch_wrapper, x0, disp=True,\n",
" minimizer_kwargs=dict(method=\"CG\", jac=True)\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "obvious-mount",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimum found at tensor([0.1133], dtype=torch.float64) with value 0.7380915689455229\n"
]
}
],
"source": [
"xstar = torch.sigmoid(torch.tensor(res.x))\n",
"f = -res.fun\n",
"print(f\"Optimum found at {xstar} with value {f}\")"
]
},
{
"cell_type": "markdown",
"id": "exterior-custom",
"metadata": {},
"source": [
"The minimum should be easy to find, here's the reparameterized function it's trying to optimize:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "answering-magic",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7fe664e18550>]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x = torch.linspace(-4., 4., 100)\n",
"plt.plot(x, [torch_wrapper(a.item())[0] for a in x]) # list comprehension because torch_wrapper expects scalar"
]
},
{
"cell_type": "markdown",
"id": "conceptual-belgium",
"metadata": {},
"source": [
"OK, maybe reparameterizing doesn't work well for basin hopping?"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment