This page was generated from examples/Lotka-Volterra.ipynb. Interactive online version: (Warning: The interactions will be much laggier on Binder than on your computer.) Binder badge.

Visualizing the Lotka-Volterra Model

The Lotka-Volterra equations are a set of coupled ordinary differential equations(ODEs) that can be used to model predator prey relationships.

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_interactionsplot function to plot the results of the integrated ODE and have the plot update automatically as we update the parameters.

Define the function

[ ]:
%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np

from mpl_interactions import ipyplot as iplt
[ ]:
# this cell is based on https://scipy-cookbook.readthedocs.io/items/LoktaVolterraTutorial.html
from scipy import integrate

t = np.linspace(0, 15, 1000)  # time
X0 = np.array([10, 5])  # initials conditions: 10 rabbits and 5 foxes


def f(a, b, c, d):
    def dX_dt(X, t=0):
        """ Return the growth rate of fox and rabbit populations. """
        rabbits, foxes = X
        dRabbit_dt = a * rabbits - b * foxes * rabbits
        dFox_dt = -c * foxes + d * b * rabbits * foxes
        return [dRabbit_dt, dFox_dt]

    X, _ = integrate.odeint(dX_dt, X0, t, full_output=True)
    return X  # expects shape (N, 2)

Make the plots

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.

[ ]:
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4.8))
controls = iplt.plot(
    f, ax=ax1, a=(0.5, 2), b=(0.1, 3), c=(1, 3), d=(0.1, 2), parametric=True
)
ax1.set_xlabel("rabbits")
ax1.set_ylabel("foxes")

iplt.plot(f, ax=ax2, controls=controls, label=["rabbits", "foxes"])
ax2.set_xlabel("time")
ax2.set_ylabel("population")
_ = ax2.legend()

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. Forutunately the control object implements a cache and will avoid call a function more than necessary when we move the sliders.

If for some reason you want to disable this you can disable it by setting the use_cache attribute to False:

controls.use_cache = False
[ ]: