EpiNow2 Stan Functions
rt.stan
Go to the documentation of this file.
1/**
2 * Reproduction Number (Rt) Functions
3 *
4 * This group of functions handles the calculation, updating, and conversion of
5 * reproduction numbers in the model. The reproduction number represents the average
6 * number of secondary infections caused by a single infected individual.
7 *
8 * @ingroup rt_estimation
9 */
10
11/**
12 * @ingroup rt_estimation
13 * @brief Extend a vector to length t by repeating its last value.
14 *
15 * @param x Vector to extend
16 * @param t Target length
17 * @return x followed by `t - num_elements(x)` copies of its last value, or x
18 * unchanged if it already has at least t elements
19 */
20vector hold_forward(vector x, int t) {
21 int n = num_elements(x);
22 if (n >= t) {
23 return x;
24 }
25 return append_row(x, rep_vector(x[n], t - n));
26}
27
28/**
29 * @ingroup rt_estimation
30 * @brief Uncentred Gaussian process contribution to log Rt.
31 *
32 * A stationary GP enters log Rt directly. A non-stationary GP enters as the
33 * cumulative sum of daily increments starting from 0. Either path is held at
34 * its last value up to length t.
35 *
36 * @param noise Vector of Gaussian process noise values
37 * @param t Length of the time series
38 * @param stationary Whether the Gaussian process is stationary (1) or
39 * non-stationary (0)
40 * @return A vector of length t, all zeros if noise is empty
41 */
42vector gp_log_path(vector noise, int t, int stationary) {
43 if (num_elements(noise) == 0) {
44 return rep_vector(0, t);
45 }
46 if (stationary) {
47 return hold_forward(noise, t);
48 }
49 return hold_forward(append_row(0, cumulative_sum(noise)), t);
50}
51
52/**
53 * @ingroup rt_estimation
54 * @brief Uncentred log Rt level of each breakpoint segment.
55 *
56 * @param bp_effects Vector of breakpoint effects
57 * @return A vector with one more element than bp_effects: 0 for the first
58 * level, then the cumulative sum of the effects
59 */
60vector bp_log_levels(vector bp_effects) {
61 return append_row(0, cumulative_sum(bp_effects));
62}
63
64/**
65 * @ingroup rt_estimation
66 * @brief Log Rt intercept that centres the paths over the centring window.
67 *
68 * Subtracting the means of the uncentred paths over the first `n_centre`
69 * days from `log(R0)` makes the mean of log Rt over those days equal
70 * `log(R0)`. The stationary GP is not centred.
71 *
72 * @param R0 Initial reproduction number
73 * @param gp Uncentred GP path from `gp_log_path()`
74 * @param bp Uncentred breakpoint levels from `bp_log_levels()`
75 * @param bps Array of breakpoint indices
76 * @param stationary Whether the Gaussian process is stationary (1) or
77 * non-stationary (0)
78 * @param n_centre Number of leading days in the centring window
79 * @return The centred intercept on the log scale
80 */
81real centred_log_intercept(real R0, vector gp, vector bp, array[] int bps,
82 int stationary, int n_centre) {
83 // sum() / n rather than mean() as sum() is a single autodiff node
84 real c = log(R0);
85 if (!stationary) {
86 c -= sum(gp[1:n_centre]) / n_centre;
87 }
88 if (num_elements(bp) > 1) {
89 c -= sum(bp[bps[1:n_centre]]) / n_centre;
90 }
91 return c;
92}
93
94/**
95 * @ingroup rt_estimation
96 * @brief Update a vector of effective reproduction numbers (Rt) based on
97 * an intercept, breakpoints (i.e. a random walk), and a Gaussian
98 * process.
99 *
100 * @param t Length of the time series
101 * @param R0 Initial reproduction number
102 * @param noise Vector of Gaussian process noise values
103 * @param bps Array of breakpoint indices
104 * @param bp_effects Vector of breakpoint effects
105 * @param stationary Whether the Gaussian process is stationary (1) or non-stationary (0)
106 * @param n_centre Number of leading positions over which to centre the
107 * non-stationary GP trajectory and the breakpoint random walk. Set to the
108 * observation window length so the centring is invariant to the forecast
109 * horizon. Ignored for the GP branch when `stationary` is 1; the breakpoint
110 * path is centred whenever breakpoints are present.
111 * @return A vector of length t containing the updated Rt values
112 */
113vector update_Rt(int t, real R0, vector noise, array[] int bps,
114 vector bp_effects, int stationary, int n_centre) {
115 int bp_n = num_elements(bp_effects);
116 vector[t] gp = gp_log_path(noise, t, stationary);
117 vector[bp_n + 1] bp = bp_log_levels(bp_effects);
118 real c = centred_log_intercept(R0, gp, bp, bps, stationary, n_centre);
119 if (bp_n == 0) {
120 return exp(c + gp);
121 }
122 vector[bp_n + 1] log_R_bp = c + bp;
123 if (num_elements(noise) == 0) {
124 // One exp per breakpoint level rather than per day
125 vector[bp_n + 1] R_bp = exp(log_R_bp);
126 return R_bp[bps];
127 }
128 return exp(log_R_bp[bps] + gp);
129}
130
131/**
132 * Calculate the log-probability of the reproduction number (Rt) priors
133 *
134 * This function adds the log density contributions from priors on initial infections
135 * and breakpoint effects to the target.
136 *
137 * @param initial_infections_scale Array of initial infection values
138 * @param bp_effects Vector of breakpoint effects
139 * @param bp_sd Array of breakpoint standard deviations
140 * @param bp_n Number of breakpoints
141 * @param cases Array of observed case counts
142 * @param initial_infections_guess Initial guess for infections based on cases
143 *
144 * @ingroup rt_estimation
145 */
146void rt_lp(array[] real initial_infections_scale, vector bp_effects,
147 array[] real bp_sd, int bp_n, array[] int cases,
148 real initial_infections_guess) {
149 //breakpoint effects on Rt
150 if (bp_n > 0) {
151 bp_sd[1] ~ normal(0, 0.1);
152 bp_effects ~ normal(0, bp_sd[1]);
153 }
154 initial_infections_scale ~ normal(initial_infections_guess, 2);
155}
156
157/**
158 * Estimate the growth rate r from reproduction number R
159 *
160 * This function uses the Newton method to solve for the growth rate r
161 * that corresponds to a given reproduction number R, using the generation
162 * time distribution. It stops after 100 steps if the tolerance has not been
163 * reached. It is implemented in C++, with a hand-written gradient, in
164 * `inst/include/epinow2/R_to_r.hpp`, which gives the maths.
165 *
166 * @param R Reproduction number
167 * @param gt_rev_pmf Reversed probability mass function of the generation time
168 * @param abs_tol Absolute tolerance for the Newton solver
169 * @return The estimated growth rate r
170 *
171 * @par Example
172 * For a generation time with probability `{0.1, 0.3, 0.4, 0.2}` of taking 0,
173 * 1, 2 or 3 days (in practice the 0-day probability would be at or near
174 * zero, as a non-zero one is rejected when the generation time is given as
175 * a nonparametric PMF), the reversed PMF is
176 * `gt_rev_pmf = {0.2, 0.4, 0.3, 0.1}`.
177 * `R_to_r(1.5, gt_rev_pmf, 1e-6)` then returns a growth rate of
178 * approximately `0.254` per day.
179 *
180 * @ingroup rt_estimation
181 */
182real R_to_r(real R, vector gt_rev_pmf, data real abs_tol);
vector bp_log_levels(vector bp_effects)
Uncentred log Rt level of each breakpoint segment.
Definition rt.stan:60
vector gp_log_path(vector noise, int t, int stationary)
Uncentred Gaussian process contribution to log Rt.
Definition rt.stan:42
vector hold_forward(vector x, int t)
Extend a vector to length t by repeating its last value.
Definition rt.stan:20
vector update_Rt(int t, real R0, vector noise, array[] int bps, vector bp_effects, int stationary, int n_centre)
Update a vector of effective reproduction numbers (Rt) based on an intercept, breakpoints (i....
Definition rt.stan:113
void rt_lp(array[] real initial_infections_scale, vector bp_effects, array[] real bp_sd, int bp_n, array[] int cases, real initial_infections_guess)
Definition rt.stan:146
real R_to_r(real R, vector gt_rev_pmf, data real abs_tol)
real centred_log_intercept(real R0, vector gp, vector bp, array[] int bps, int stationary, int n_centre)
Log Rt intercept that centres the paths over the centring window.
Definition rt.stan:81