{
    "nbformat": 4,
    "nbformat_minor": 5,
    "metadata": {
        "kernelspec": {
            "display_name": "Python",
            "language": "python",
            "name": "python"
        },
        "language_info": {
            "name": "python"
        }
    },
    "cells": [
        {
            "id": "783e9093",
            "cell_type": "markdown",
            "source": "<div class='alert alert-warning'>\n\nSciPy's interactive examples with Jupyterlite are experimental and may not always work as expected. Execution of cells containing imports may result in large downloads (up to 60MB of content for the first import from SciPy). Load times when importing from SciPy may take roughly 10-20 seconds. If you notice any problems, feel free to open an [issue](https://github.com/scipy/scipy/issues/new/choose).\n\n</div>",
            "metadata": {}
        },
        {
            "id": "a696c5ab",
            "cell_type": "markdown",
            "source": "The following example is a 1-D minimization problem, with many\nlocal minima superimposed on a parabola.\n",
            "metadata": {}
        },
        {
            "id": "1a0febcc",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "import numpy as np\nfrom scipy.optimize import basinhopping\nfunc = lambda x: np.cos(14.5 * x - 0.3) + (x + 0.2) * x\nx0 = [1.]",
            "outputs": []
        },
        {
            "id": "f5eac3f1",
            "cell_type": "markdown",
            "source": "Basinhopping, internally, uses a local minimization algorithm. We will use\nthe parameter `minimizer_kwargs` to tell basinhopping which algorithm to\nuse and how to set up that minimizer. This parameter will be passed to\n`scipy.optimize.minimize`.\n",
            "metadata": {}
        },
        {
            "id": "f4952abf",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "minimizer_kwargs = {\"method\": \"BFGS\"}\nret = basinhopping(func, x0, minimizer_kwargs=minimizer_kwargs,\n                   niter=200)\n# the global minimum is:\nret.x, ret.fun",
            "outputs": [
                {
                    "output_type": "execute_result",
                    "metadata": {},
                    "data": {
                        "text/plain": "-0.1951, -1.0009"
                    },
                    "execution_count": null
                }
            ]
        },
        {
            "id": "53a179f5",
            "cell_type": "markdown",
            "source": "Next consider a 2-D minimization problem. Also, this time, we\nwill use gradient information to significantly speed up the search.\n",
            "metadata": {}
        },
        {
            "id": "8cef0c13",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "def func2d(x):\n    f = np.cos(14.5 * x[0] - 0.3) + (x[1] + 0.2) * x[1] + (x[0] +\n                                                           0.2) * x[0]\n    df = np.zeros(2)\n    df[0] = -14.5 * np.sin(14.5 * x[0] - 0.3) + 2. * x[0] + 0.2\n    df[1] = 2. * x[1] + 0.2\n    return f, df",
            "outputs": []
        },
        {
            "id": "13f1b568",
            "cell_type": "markdown",
            "source": "We'll also use a different local minimization algorithm. Also, we must tell\nthe minimizer that our function returns both energy and gradient (Jacobian).\n",
            "metadata": {}
        },
        {
            "id": "0e437a9c",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "minimizer_kwargs = {\"method\":\"L-BFGS-B\", \"jac\":True}\nx0 = [1.0, 1.0]\nret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,\n                   niter=200)\nprint(\"global minimum: x = [%.4f, %.4f], f(x) = %.4f\" % (ret.x[0],\n                                                          ret.x[1],\n                                                          ret.fun))",
            "outputs": [
                {
                    "output_type": "execute_result",
                    "metadata": {},
                    "data": {
                        "text/plain": "global minimum: x = [-0.1951, -0.1000], f(x) = -1.0109"
                    },
                    "execution_count": null
                }
            ]
        },
        {
            "id": "3d76f9e9",
            "cell_type": "markdown",
            "source": "Here is an example using a custom step-taking routine. Imagine you want\nthe first coordinate to take larger steps than the rest of the coordinates.\nThis can be implemented like so:\n",
            "metadata": {}
        },
        {
            "id": "77a72981",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "class MyTakeStep:\n   def __init__(self, stepsize=0.5):\n       self.stepsize = stepsize\n       self.rng = np.random.default_rng()\n   def __call__(self, x):\n       s = self.stepsize\n       x[0] += self.rng.uniform(-2.*s, 2.*s)\n       x[1:] += self.rng.uniform(-s, s, x[1:].shape)\n       return x",
            "outputs": []
        },
        {
            "id": "2b0fa2a7",
            "cell_type": "markdown",
            "source": "Since ``MyTakeStep.stepsize`` exists basinhopping will adjust the magnitude\nof `stepsize` to optimize the search. We'll use the same 2-D function as\nbefore\n",
            "metadata": {}
        },
        {
            "id": "4392d13e",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "mytakestep = MyTakeStep()\nret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,\n                   niter=200, take_step=mytakestep)\nprint(\"global minimum: x = [%.4f, %.4f], f(x) = %.4f\" % (ret.x[0],\n                                                          ret.x[1],\n                                                          ret.fun))",
            "outputs": [
                {
                    "output_type": "execute_result",
                    "metadata": {},
                    "data": {
                        "text/plain": "global minimum: x = [-0.1951, -0.1000], f(x) = -1.0109"
                    },
                    "execution_count": null
                }
            ]
        },
        {
            "id": "cd65934b",
            "cell_type": "markdown",
            "source": "Now, let's do an example using a custom callback function which prints the\nvalue of every minimum found\n",
            "metadata": {}
        },
        {
            "id": "6dcdb5e6",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "def print_fun(x, f, accepted):\n        print(\"at minimum %.4f accepted %d\" % (f, int(accepted)))",
            "outputs": []
        },
        {
            "id": "c88a137c",
            "cell_type": "markdown",
            "source": "We'll run it for only 10 basinhopping steps this time.\n",
            "metadata": {}
        },
        {
            "id": "551c1903",
            "cell_type": "code",
            "metadata": {},
            "execution_count": null,
            "source": "rng = np.random.default_rng()\nret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,\n                   niter=10, callback=print_fun, rng=rng)",
            "outputs": [
                {
                    "output_type": "execute_result",
                    "metadata": {},
                    "data": {
                        "text/plain": "at minimum 0.4159 accepted 1\nat minimum -0.4317 accepted 1\nat minimum -1.0109 accepted 1\nat minimum -0.9073 accepted 1\nat minimum -0.4317 accepted 0\nat minimum -0.1021 accepted 1\nat minimum -0.7425 accepted 1\nat minimum -0.9073 accepted 1\nat minimum -0.4317 accepted 0\nat minimum -0.7425 accepted 1\nat minimum -0.9073 accepted 1"
                    },
                    "execution_count": null
                }
            ]
        },
        {
            "id": "203a562b",
            "cell_type": "markdown",
            "source": "The minimum at -1.0109 is actually the global minimum, found already on the\n8th iteration.",
            "metadata": {}
        }
    ]
}