Spaces:
Running
Running
| 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 | |