Spaces:
Sleeping
Sleeping
from src.utility_functions import log_pi_lambda, log_pi_t | |
import numpy as np | |
def update_lambda(lam, t, s, i, P, sigma, alpha, beta, phi): | |
# This function updates the parameter vector lambda. | |
# INPUT: | |
# - lam: array of the values of lambda; | |
# - t: array of the breakpoints; | |
# - s: array of susceptible individuals during time; | |
# - i: array of infected individuals during time; | |
# - P: total number of individuals; | |
# - sigma: algorithm parameter for the proposal of a new candidate lambda; | |
# - alpha, beta: hyperparameters of the prior of lambda; | |
# - phi parameter phi of the model. | |
# OUTPUT: | |
# - lam: update array; | |
# - accept: number of acceted candidates. | |
# NOTES: The update is done component-wise in a sequential manner. | |
current = np.copy(lam) # Get the current state of the chain. | |
candidate = np.copy(current) # Initialize the new candidate. | |
# For every component of the parameter vector, we tweak such component | |
# according to the chosen proposal and then update the chain according | |
# to the computed acceptance rate. | |
accepted = 0 # Initialize the count of accepted candidates. | |
for j in range(current.shape[0]): | |
# Tweak the j-th component. | |
candidate[j] = candidate[j] + sigma * np.random.normal() | |
# Compute the acceptance rate. | |
log_alpha = (log_pi_lambda(candidate, t, s, i, P, alpha, beta, phi) | |
- log_pi_lambda(current, t, s, i, P, alpha, beta, phi)) | |
# If the candidate is accepted, we move the chain (current = candidate) | |
# and increase the count of accepted candidates. Otherwise, we reject | |
# the candidate and the chain does not move from the current state | |
# (candidate = current). | |
if log_alpha > np.log(np.random.uniform()): | |
current = np.copy(candidate) | |
accepted = accepted + 1 | |
else: | |
candidate = np.copy(current) | |
return current.reshape(-1, 1), accepted | |
def update_t(lam, t, s, i, P, M, phi): | |
# This function updates the parameter vector lambda. | |
# INPUT: | |
# - lam: array of the values of lambda; | |
# - t: array of the breakpoints; | |
# - s: array of susceptible individuals during time; | |
# - i: array of infected individuals during time; | |
# - P: total number of individuals; | |
# - M: algorithm parameter for the proposal of a new candidate t; | |
# - phi parameter phi of the model. | |
# OUTPUT: | |
# - lam: update array; | |
# - accept: number of acceted candidates. | |
# NOTES: The update is done component-wise in a sequential manner. | |
current = np.copy(t) # Get the current state of the chain. | |
candidate = np.copy(current) # Initialize the new candidate. | |
# For every component of the parameter vector, we tweak such component | |
# according to the chosen proposal and then update the chain according | |
# to the computed acceptance rate. | |
accepted = 0 # Initialize the count of accepted candidates. | |
for j in range(current.shape[0]): | |
# Tweak the j-th component. | |
candidate[j] = candidate[j] + np.random.choice(np.arange(-M, M + 1)) | |
# Compute the acceptance rate. | |
log_alpha = (log_pi_t(lam, candidate, s, i, P, phi) | |
- log_pi_t(lam, current, s, i, P, phi)) | |
# If the candidate is accepted, we move the chain (current = candidate) | |
# and increase the count of accepted candidates. Otherwise, we reject | |
# the candidate and the chain does not move from the current state | |
# (candidate = current). | |
if log_alpha > np.log(np.random.uniform()): | |
current = np.copy(candidate) | |
accepted = accepted + 1 | |
else: | |
candidate = np.copy(current) | |
return current.reshape(-1, 1), accepted | |
def mcmc_sampler(s, i, d, P, n_iterations, burnin, M, sigma, | |
alpha, beta, a, b, phi): | |
# This function implement the hybrid MCMC sampler. | |
# INPUT: | |
# - s: array of susceptible individuals during time; | |
# - i: array of infected individuals during time; | |
# - d: number of breakpoints; | |
# - P: total number of individuals; | |
# - n_iterations: number of iterations for the algorithm; | |
# - burnin: number of burnin iterations to discard; | |
# - M: algorithm parameter for the proposal of a new candidate t; | |
# - sigma: algorithm parameter for the proposal of a new candidate lambda; | |
# - alpha, beta: hyperparameters of the prior of lambda; | |
# - a, b: hyperparameters of the prior of p; | |
# - phi: parameter phi of the model. | |
# OUTPUT: | |
# - p: simulated chain for the probability of removal from | |
# infected population; | |
# - lam: simulated chain for lambda; | |
# - t: simulated chain for the breakpoints. | |
T = s.shape[0] - 1 # Index of the final time instant. | |
# Initialize the parameters. | |
# The initial value of p is drawn from the prior distribution. | |
p = np.random.beta(a, b, size=(1, 1)) | |
# Each of the d breakpoints (t_i) is drawn randomly (without replacement) | |
# between 1 and T-1. The obtained vector is then sorted to make sure | |
# that t_1 < t_2 < ... < t_d. | |
t = np.sort(np.random.choice(np.arange(1, T), size=d-1, replace=False)) | |
t = t.reshape(-1, 1) | |
# Each of the lambda_i's is drawn independently from | |
# its prior distribution. | |
lam = np.random.gamma(alpha, beta) | |
lam = lam.reshape(-1, 1) | |
# Compute the hyperparameters of the posterior of p. | |
a_new = a + i[0] - i[-1] + s[0] - s[-1] | |
b_new = b + np.sum(i[1:]) + s[-1] - s[0] | |
# Initialize the count of accepted candidates for lambda and t. | |
a_lam = 0 | |
a_t = 0 | |
# Run the chain | |
for _ in range(n_iterations): | |
# Update p by sampling from its posterior. | |
p = np.hstack((p, np.random.beta(a_new, b_new, size=(1, 1)))) | |
# Update lam via Metropolis-Hastings step. | |
new_lam, accepted_lam = update_lambda(lam[:, -1], t[:, -1], s, i, P, | |
sigma, alpha, beta, phi) | |
# Update t via Metropolis-Hastings step. | |
new_t, accepted_t = update_t(lam[:, -1], t[:, -1], s, i, P, M, phi) | |
lam = np.hstack((lam, new_lam)) | |
t = np.hstack((t, new_t)) | |
# Update the counts of accepted candidates for lambda and t. | |
a_lam = a_lam + accepted_lam | |
a_t = a_t + accepted_t | |
# Compute the acceptance rates for lambda and t. | |
lam_ar = a_lam / n_iterations / d | |
t_ar = a_t / n_iterations / (d-1) | |
# Discard burn-in iterations. | |
p = p[:, burnin:] | |
lam = lam[:, burnin:] | |
t = t[:, burnin:] | |
return p, lam, t, lam_ar, t_ar | |