epinowcast
Loading...
Searching...
No Matches
arima_kernel.stan
Go to the documentation of this file.
1
36vector pacf_to_phi(vector r) {
37 int p = num_elements(r);
38 if (p == 0) return r;
39 vector[p] phi = r;
40 vector[p] work = r;
41 for (k in 2:p) {
42 for (j in 1:(k - 1)) {
43 work[j] = phi[j] - r[k] * phi[k - j];
44 }
45 for (j in 1:(k - 1)) {
46 phi[j] = work[j];
47 }
48 phi[k] = r[k];
49 }
50 return phi;
51}
52
59vector arma_impulse(vector phi, vector theta, int T) {
60 int p = num_elements(phi);
61 int q = num_elements(theta);
62 vector[T] psi = rep_vector(0.0, T);
63 psi[1] = 1.0;
64 for (t in 2:T) {
65 real s = 0.0;
66 int kmax = min(p, t - 1);
67 for (k in 1:kmax) s += phi[k] * psi[t - k];
68 if (t - 1 <= q) s += theta[t - 1];
69 psi[t] = s;
70 }
71 return psi;
72}
73
82matrix lower_toeplitz(vector psi) {
83 int T = num_elements(psi);
84 matrix[T, T] K = rep_matrix(0.0, T, T);
85 for (s in 1:T) {
86 K[s:T, s] = head(psi, T - s + 1);
87 }
88 return K;
89}
90
99matrix cumulative_op(int T, int d) {
100 if (d == 0) return diag_matrix(rep_vector(1.0, T));
101 matrix[T, T] D1 = rep_matrix(0.0, T, T);
102 for (s in 1:T) {
103 D1[s:T, s] = rep_vector(1.0, T - s + 1);
104 }
105 matrix[T, T] D = D1;
106 for (i in 2:d) D = D * D1;
107 return D;
108}
109
121matrix arima_kernel_matrix(vector phi, vector theta, int d, int T) {
122 vector[T] psi = arma_impulse(phi, theta, T);
123 matrix[T, T] K = lower_toeplitz(psi);
124 for (i in 1:d) {
125 for (col in 1:T) {
126 K[, col] = cumulative_sum(K[, col]);
127 }
128 }
129 return K;
130}
131
145matrix arima_filter(matrix Z, vector phi, vector theta, int d) {
146 int T = rows(Z);
147 int G = cols(Z);
148 if (num_elements(phi) == 0 && num_elements(theta) == 0) {
149 if (d == 0) return Z;
150 matrix[T, G] result = Z;
151 for (i in 1:d) {
152 for (g in 1:G) {
153 result[, g] = cumulative_sum(result[, g]);
154 }
155 }
156 return result;
157 }
158 return arima_kernel_matrix(phi, theta, d, T) * Z;
159}
160
204real arima_latent_mean_offset(int present, int centre, int T, int G,
205 int p, int d, int q,
206 matrix z, vector pacf, vector theta,
207 array[] real sigma) {
208 if (!present || !centre || d < 1) return 0.0;
209 vector[p] phi = pacf_to_phi(pacf);
210 matrix[T, G] eps = sigma[1] * arima_filter(z, phi, theta, d);
211 return mean(to_vector(eps));
212}
213
214vector apply_arima_residual(vector base, int n_obs,
215 int present, int centre, int T, int G,
216 int p, int d, int q,
217 matrix z, vector pacf, vector theta,
218 array[] real sigma,
219 array[] int flat_idx) {
220 if (!present) return base;
221 vector[p] phi = pacf_to_phi(pacf);
222 matrix[T, G] eps = sigma[1] * arima_filter(z, phi, theta, d);
223 // Integration (d >= 1) gives the residual a free overall level: the
224 // first shock propagates through the cumulative sum and shifts the
225 // whole path, so the level of the residual trades off against the
226 // intercept (a ridge in the joint posterior that slows HMC). When an
227 // intercept is present to carry the level, remove the grand mean (over
228 // time and groups) so the intercept owns it. This mirrors brms's
229 // design-matrix centring and EpiNow2's `gp -= mean(gp)`.
230 //
231 // Only the grand mean is removed -- the single level direction that
232 // competes with the shared intercept. Each group keeps its own level
233 // relative to that grand mean, so this is exact for any number of groups
234 // and a grouped latent still gives each group its own level and drift.
235 if (centre && d >= 1) {
236 eps = eps - mean(to_vector(eps));
237 }
238 // Vectorised gather: flat_idx is precomputed in transformed data
239 // as (group_idx - 1) * T + time_idx, so to_vector(eps) (column-
240 // major flatten) [flat_idx] returns the per-observation residuals
241 // in one shot, replacing the previous element-by-element loop.
242 return base + to_vector(eps)[flat_idx];
243}
vector apply_arima_residual(vector base, int n_obs, int present, int centre, int T, int G, int p, int d, int q, matrix z, vector pacf, vector theta, array[] real sigma, array[] int flat_idx)
matrix lower_toeplitz(vector psi)
vector arma_impulse(vector phi, vector theta, int T)
vector pacf_to_phi(vector r)
real arima_latent_mean_offset(int present, int centre, int T, int G, int p, int d, int q, matrix z, vector pacf, vector theta, array[] real sigma)
matrix cumulative_op(int T, int d)
matrix arima_filter(matrix Z, vector phi, vector theta, int d)
matrix arima_kernel_matrix(vector phi, vector theta, int d, int T)
vector vector matrix int int centre