Source code for pints.toy._logistic_model
#
# Logistic toy model.
#
# 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 ToyModel
[docs]
class LogisticModel(pints.ForwardModelS1, ToyModel):
r"""
Logistic model of population growth [1]_.
.. math::
f(t) &= \frac{k}{1+(k/p_0 - 1) \exp(-r t)} \\
\frac{\partial f(t)}{\partial r} &=
\frac{k t (k / p_0 - 1) \exp(-r t)}
{((k/p_0-1) \exp(-r t) + 1)^2} \\
\frac{\partial f(t)}{ \partial k} &= -\frac{k \exp(-r t)}
{p_0 ((k/p_0-1)\exp(-r t) + 1)^2}
+ \frac{1}{(k/p_0 - 1)\exp(-r t) + 1}
Has two model parameters: A growth rate :math:`r` and a carrying capacity
:math:`k`. The initial population size :math:`p_0 = f(0)` is a fixed
(known) parameter in the model.
Extends :class:`pints.ForwardModel`, :class:`pints.toy.ToyModel`.
Parameters
----------
initial_population_size : float
Sets the initial population size :math:`p_0`.
References
----------
.. [1] https://en.wikipedia.org/wiki/Population_growth
"""
def __init__(self, initial_population_size=2):
super(LogisticModel, self).__init__()
self._p0 = float(initial_population_size)
if self._p0 < 0:
raise ValueError('Population size cannot be negative.')
[docs]
def n_parameters(self):
""" See :meth:`pints.ForwardModel.n_parameters()`. """
return 2
[docs]
def simulate(self, parameters, times):
""" See :meth:`pints.ForwardModel.simulate()`. """
return self._simulate(parameters, times, False)
[docs]
def simulateS1(self, parameters, times):
""" See :meth:`pints.ForwardModelS1.simulateS1()`. """
return self._simulate(parameters, times, True)
def _simulate(self, parameters, times, sensitivities):
r, k = [float(x) for x in parameters]
times = np.asarray(times)
if np.any(times < 0):
raise ValueError('Negative times are not allowed.')
if self._p0 == 0 or k < 0:
if sensitivities:
return np.zeros(times.shape), \
np.zeros((len(times), len(parameters)))
else:
return np.zeros(times.shape)
exp = np.exp(-r * times)
c = (k / self._p0 - 1)
values = k / (1 + c * exp)
if sensitivities:
dvalues_dp = np.empty((len(times), len(parameters)))
dvalues_dp[:, 0] = k * times * c * exp / (c * exp + 1)**2
dvalues_dp[:, 1] = -k * exp / \
(self._p0 * (c * exp + 1)**2) + 1 / (c * exp + 1)
return values, dvalues_dp
else:
return values
[docs]
def suggested_parameters(self):
""" See :meth:`pints.toy.ToyModel.suggested_parameters()`. """
return np.array([0.1, 50])
[docs]
def suggested_times(self):
""" See :meth:`pints.toy.ToyModel.suggested_times()`. """
return np.linspace(0, 100, 100)