#
# Lotka-Volterra model of Predatory-Prey relationships.
#
# This file is part of PINTS (https://github.com/pints-team/pints/) which is
# released under the BSD 3-clause license. See accompanying LICENSE.md for
# copyright notice and full license details.
#
import numpy as np
import pints
from . import ToyODEModel
[docs]
class LotkaVolterraModel(ToyODEModel, pints.ForwardModelS1):
"""
Lotka-Volterra model of Predatory-Prey relationships [1]_.
This model describes cyclical fluctuations in the populations of two
interacting species.
.. math::
\\frac{dx}{dt} = ax - bxy \\\\
\\frac{dy}{dt} = -cy + dxy
where ``x`` is the number of prey, and ``y`` is the number of predators.
Real data is included via :meth:`suggested_values`, which was taken from
[2]_, and includes hare and lynx pelt count data collected by the Hudson's
Bay Company, in Canada in the early twentieth century.
Extends :class:`pints.ForwardModelS1`, :class:`pints.toy.ToyODEModel`.
Parameters
----------
y0
The initial population, given as a vector ``[a, b]`` such that
``a >= 0`` and ``b >= 0``.
References
----------
.. [1] https://en.wikipedia.org/wiki/Lotka-Volterra_equations
.. [2] Howard, P. (2009). Modeling basics. Lecture Notes for Math 442,
Texas A&M University
"""
def __init__(self, y0=None):
if y0 is None:
self.set_initial_conditions(np.log([30, 4]))
else:
self.set_initial_conditions(y0)
def _dfdp(self, z, t, p):
""" See :meth:`pints.ToyModel.jacobian()`. """
x, y = z
a, b, c, d = [float(param) for param in p]
ret = np.empty((2, 4))
ret[0, 0] = x
ret[0, 1] = -x * y
ret[0, 2] = 0
ret[0, 3] = 0
ret[1, 0] = 0
ret[1, 1] = 0
ret[1, 2] = -y
ret[1, 3] = x * y
return ret
[docs]
def jacobian(self, z, t, p):
""" See :meth:`pints.ToyModel.jacobian()`. """
x, y = z
a, b, c, d = [float(param) for param in p]
ret = np.empty((2, 2))
ret[0, 0] = a - b * y
ret[0, 1] = -b * x
ret[1, 0] = d * y
ret[1, 1] = -c + d * x
return ret
[docs]
def initial_conditions(self):
"""
Returns the current initial conditions.
"""
return np.array(self._y0, copy=True)
[docs]
def n_outputs(self):
""" See :meth:`pints.ForwardModel.n_outputs()`. """
return 2
[docs]
def n_parameters(self):
""" See :meth:`pints.ForwardModel.n_parameters()`. """
return 4
def _rhs(self, state, time, parameters):
"""
Right-hand side equation of the ode to solve.
"""
x, y = state
a, b, c, d = parameters
return np.array([a * x - b * x * y, -c * y + d * x * y])
[docs]
def set_initial_conditions(self, y0):
"""
Changes the initial conditions for this model.
"""
a, b = y0
if a < 0 or b < 0:
raise ValueError('Initial populations cannot be negative.')
self._y0 = [a, b]
[docs]
def suggested_parameters(self):
""" See :meth:`pints.toy.ToyModel.suggested_parameters()`. """
return np.array([0.5, 0.15, 1.0, 0.3])
[docs]
def suggested_times(self):
""" See :meth:`pints.toy.ToyModel.suggested_times()`. """
return np.linspace(0, 20, 21)
[docs]
def suggested_values(self):
"""
Returns hare-lynx pelt count data collected by the Hudson's Bay Company
in Canada in the early twentieth century, which is taken from [2]_.
The data given here corresponds to annual observations taken from
1900-1920 (inclusive).
"""
return np.array([
[30.0, 4.0], # 1900
[47.2, 6.1],
[70.2, 9.8],
[77.4, 35.2],
[36.3, 59.4], # 1904
[20.6, 41.7],
[18.1, 19.0],
[21.4, 13.0],
[22.0, 8.3],
[25.4, 9.1], # 1909
[27.1, 7.4],
[40.3, 8.0],
[57.0, 12.3],
[76.6, 19.5],
[52.3, 45.7], # 1914
[19.5, 51.1],
[11.2, 29.7],
[7.6, 15.8],
[14.6, 9.7],
[16.2, 10.1],
[24.7, 8.1], # 1920
])