{
"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",
"\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": {
"tags": [
"remove-output"
]
},
"outputs": [],
"source": [
"fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4.8))\n",
"controls = iplt.plot(f, ax=ax1, a=(0.5, 2), b=(0.1, 3), c_=(1, 3), d=(0.1, 2), parametric=True)\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": [
"![](../_static/images/lotka-volterra1.gif)"
]
},
{
"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",
"```"
]
}
],
"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
}