Source code for pyEpiabm.utility.rate_multiplier

#
# Class to produce rate multipliers to be used for waning individuals in the
# StateTransitionMatrix
#

import math
import typing


[docs] class RateMultiplier: """Class which produces a decaying exponential curve parameterised by time given two data points (default of 90 and 180 days). Note that p(t) = 0 represents immunity having a maximal effect on the StateTransitionMatrix probabilities, and p(t) = 1 represents immunity having no effect. So p -> 1 as t -> inf. p will take the form p(t) = 1 - be^(-at) for some constants a and b (determined by the two data points). """ def __init__(self, p_1: float, p_2: float, t_1: float = 90.0, t_2: float = 180.0): """Constructor Method Parameters ---------- p_1 : float Rate multiplier at t = t_1 p_2 : float Rate multiplier at t = t_2 t_1 : float First time data point (represents a Person's time since last recovery in days, default = 90.0) t_2 : float Second time data point (represents a Person's time since last recovery in days, default = 180.0) """ # Parameter checks # We assume t_1 is the first value if not 0.0 <= t_1 < t_2: raise ValueError("t_1 must be smaller than t_2 and both must" "be non-negative") # This means that p_1 < p_2 (as this curve will be increasing in t) if not 0.0 <= p_1 < p_2 <= 1.0: raise ValueError("p_1 must be smaller than p_2 and both must" "lie between 0 and 1 inclusive") self.p_1 = p_1 self.p_2 = p_2 self.t_1 = t_1 self.t_2 = t_2 self.a, self.b = self._calculate_parameters() def _calculate_parameters(self) -> typing.Tuple: """Given the initial data points p(t_1) = p_1 and p(t_2) = p_2, this method determines the parameters a and b of the function. Note that p(t) = 1 - be^(-at). Returns ------- tuple[float, float] The parameters (a, b) """ # If either data point is 1, this means that this curve can only be # fit to the flat line p(t) = 1 (representing immunity having no # effect on the transition probabilities) if self.p_1 == 1.0 or self.p_2 == 1.0: return 0.0, 0.0 a = - math.log((1.0 - self.p_1) / (1.0 - self.p_2)) / (self.t_1 - self.t_2) b = (1.0 - self.p_1) * math.exp(a * self.t_1) return a, b
[docs] def __call__(self, t: float) -> float: """Given a value t (representing days since a Person last recovered from the disease), calculates the rate multiplier for a waning individual moving to a specific compartment, using a decaying exponential Parameters ---------- t : float Days since a Person last recovered from the disease Returns ------- float The rate multiplier for the probability of them moving to a certain compartment in the StateTransitionMatrix """ # Check for erroneous values if t < 0.0: raise ValueError("t must be non-negative") # If b = 0 (representing no effect from waning immunity) we return a # rate multiplier of 1. We check for a as well to avoid division by # zero (a should never be zero unless b is zero) if self.a == 0.0 or self.b == 0.0: return 1.0 # If b > 1, then a section of the curve p(t) = 1 - be^(-at) will be # negative for some positive values of t. Hence, we will simply set # p = 0 for these values of t if t < math.log(self.b) / self.a: return 0.0 return 1.0 - self.b * math.exp(-self.a * t)