{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualizing the Lotka-Volterra Model\n",
    "\n",
    "The [Lotka-Volterra](https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations) equations are a set of coupled [ordinary differential equations](https://en.wikipedia.org/wiki/Ordinary_differential_equation)(ODEs) that can be used to model predator prey relationships.\n",
    "\n",
    "They have 4 parameters that can each be tuned individually which will affect how the flucuations in population behave. In order to explore this 4D parameter space we can use `mpl_interactions`' `plot` function to plot the results of the integrated ODE and have the plot update automatically as we update the parameters. \n",
    "\n",
    "## Define the function\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib ipympl\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "from mpl_interactions import ipyplot as iplt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# this cell is based on https://scipy-cookbook.readthedocs.io/items/LoktaVolterraTutorial.html\n",
    "from scipy import integrate\n",
    "\n",
    "t = np.linspace(0, 15, 1000)  # time\n",
    "X0 = np.array([10, 5])  # initials conditions: 10 rabbits and 5 foxes\n",
    "\n",
    "# use `c_` instead of `c` because `c` is an argument to plt.scatter\n",
    "\n",
    "def f(a, b, c_, d):\n",
    "    def dX_dt(X, t=0):\n",
    "        \"\"\" Return the growth rate of fox and rabbit populations. \"\"\"\n",
    "        rabbits, foxes = X\n",
    "        dRabbit_dt = a * rabbits - b * foxes * rabbits\n",
    "        dFox_dt = -c_ * foxes + d * b * rabbits * foxes\n",
    "        return [dRabbit_dt, dFox_dt]\n",
    "\n",
    "    X, _ = integrate.odeint(dX_dt, X0, t, full_output=True)\n",
    "    return X  # expects shape (N, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Make the plots\n",
    "\n",
    "Here we make two plots. On the left is a parametric plot that shows all the possible combinations of rabbits and foxes that we can have. The plot on the right has time on the X axis and shows how the fox and rabbit populations evolve in time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "gif": "lotka-volterra1.gif"
   },
   "outputs": [],
   "source": [
    "fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4.8))\n",
    "controls = iplt.plot(\n",
    "    f, ax=ax1, a=(0.5, 2), b=(0.1, 3), c_=(1, 3), d=(0.1, 2), parametric=True\n",
    ")\n",
    "ax1.set_xlabel(\"rabbits\")\n",
    "ax1.set_ylabel(\"foxes\")\n",
    "\n",
    "iplt.plot(f, ax=ax2, controls=controls, label=[\"rabbits\", \"foxes\"])\n",
    "ax2.set_xlabel(\"time\")\n",
    "ax2.set_ylabel(\"population\")\n",
    "_ = ax2.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You may have noticed that it looks as though we will end up calling our function `f` twice every time we update the parameters. This would be a bummer because then our computer would be doing twice as much work as it needs to. Fortunately the `control` object implements a cache and will avoid call a function more than necessary when we move the sliders.\n",
    "\n",
    "\n",
    "If for some reason you want to disable this you can disable it by setting the `use_cache` attribute to `False`:\n",
    "```python\n",
    "controls.use_cache = False\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.9.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}