EpiNow2 Stan Functions
primarycensored.stan
Go to the documentation of this file.
1// Stan functions from primarycensored version 1.4.0
2real expgrowth_lpdf(real x, real xmin, real xmax, real r) {
3 if (x < xmin || x > xmax) {
4 return negative_infinity();
5 }
6 if (abs(r) < 1e-10) {
7 return -log(xmax - xmin);
8 }
9 return log(abs(r)) + r * (x - xmin) -
10 log(abs(exp(r * xmax) - exp(r * xmin)));
11}
12int check_for_analytical(int dist_id, int primary_id) {
13 if (dist_id == 2 && primary_id == 1) return 1; // Gamma delay with Uniform primary
14 if (dist_id == 1 && primary_id == 1) return 1; // Lognormal delay with Uniform primary
15 if (dist_id == 3 && primary_id == 1) return 1; // Weibull delay with Uniform primary
16 return 0; // No analytical solution for other combinations
17}
18real primarycensored_gamma_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) {
19 real shape = params[1];
20 real rate = params[2];
21 real shape_1 = shape + 1;
22 real log_window = log(pwindow);
23
24 real log_F_T = gamma_lcdf(d | shape, rate);
25 real log_F_T_kp1 = gamma_lcdf(d | shape_1, rate);
26
27 real log_delta_F_T_kp1;
28 real log_delta_F_T_k;
29 real log_F_Splus;
30
31 if (q != 0) {
32 real log_F_T_q = gamma_lcdf(q | shape, rate);
33 real log_F_T_q_kp1 = gamma_lcdf(q | shape_1, rate);
34
35 // Ensure that the first argument is greater than the second
36 log_delta_F_T_kp1 = log_diff_exp(log_F_T_kp1, log_F_T_q_kp1);
37 log_delta_F_T_k = log_diff_exp(log_F_T, log_F_T_q);
38
39 log_F_Splus = log_diff_exp(
40 log_F_T,
41 log_diff_exp(
42 log(shape * inv(rate)) + log_delta_F_T_kp1,
43 log(d - pwindow) + log_delta_F_T_k
44 ) - log_window
45 );
46 } else {
47 log_delta_F_T_kp1 = log_F_T_kp1;
48 log_delta_F_T_k = log_F_T;
49
50 log_F_Splus = log_diff_exp(
51 log_F_T,
52 log_sum_exp(
53 log(shape * inv(rate)) + log_delta_F_T_kp1,
54 log(pwindow - d) + log_delta_F_T_k
55 ) - log_window
56 );
57 }
58
59 return log_F_Splus;
60}
61real primarycensored_lognormal_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) {
62 real mu = params[1];
63 real sigma = params[2];
64 real mu_sigma2 = mu + square(sigma);
65 real log_window = log(pwindow);
66
67 real log_F_T = lognormal_lcdf(d | mu, sigma);
68 real log_F_T_mu_sigma2 = lognormal_lcdf(d | mu_sigma2, sigma);
69
70 real log_delta_F_T_mu_sigma;
71 real log_delta_F_T;
72 real log_F_Splus;
73
74 if (q != 0) {
75 real log_F_T_q = lognormal_lcdf(q | mu, sigma);
76 real log_F_T_q_mu_sigma2 = lognormal_lcdf(q | mu_sigma2, sigma);
77
78 // Ensure that the first argument is greater than the second
79 log_delta_F_T_mu_sigma = log_diff_exp(
80 log_F_T_mu_sigma2, log_F_T_q_mu_sigma2
81 );
82 log_delta_F_T = log_diff_exp(log_F_T, log_F_T_q);
83
84 log_F_Splus = log_diff_exp(
85 log_F_T,
86 log_diff_exp(
87 (mu + 0.5 * square(sigma)) + log_delta_F_T_mu_sigma,
88 log(d - pwindow) + log_delta_F_T
89 ) - log_window
90 );
91 } else {
92 log_delta_F_T_mu_sigma = log_F_T_mu_sigma2;
93 log_delta_F_T = log_F_T;
94
95 log_F_Splus = log_diff_exp(
96 log_F_T,
97 log_sum_exp(
98 (mu + 0.5 * square(sigma)) + log_delta_F_T_mu_sigma,
99 log(pwindow - d) + log_delta_F_T
100 ) - log_window
101 );
102 }
103
104 return log_F_Splus;
105}
106real log_weibull_g(real t, real shape, real scale) {
107 real x = pow(t * inv(scale), shape);
108 real a = 1 + inv(shape);
109 return log(gamma_p(a, x)) + lgamma(a);
110}
111real primarycensored_weibull_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) {
112 real shape = params[1];
113 real scale = params[2];
114 real log_window = log(pwindow);
115
116 real log_F_T = weibull_lcdf(d | shape, scale);
117
118 real log_delta_g;
119 real log_delta_F_T;
120 real log_F_Splus;
121
122 if (q != 0) {
123 real log_F_T_q = weibull_lcdf(q | shape, scale);
124
125 log_delta_g = log_diff_exp(
126 log_weibull_g(d, shape, scale),
127 log_weibull_g(q, shape, scale)
128 );
129 log_delta_F_T = log_diff_exp(log_F_T, log_F_T_q);
130
131 log_F_Splus = log_diff_exp(
132 log_F_T,
133 log_diff_exp(
134 log(scale) + log_delta_g,
135 log(d - pwindow) + log_delta_F_T
136 ) - log_window
137 );
138 } else {
139 log_delta_g = log_weibull_g(d, shape, scale);
140 log_delta_F_T = log_F_T;
141
142 log_F_Splus = log_diff_exp(
143 log_F_T,
144 log_sum_exp(
145 log(scale) + log_delta_g,
146 log(pwindow - d) + log_delta_F_T
147 ) - log_window
148 );
149 }
150
151 return log_F_Splus;
152}
153real primarycensored_analytical_lcdf_raw(data real d, int dist_id,
154 array[] real params,
155 data real pwindow,
156 int primary_id) {
157 real q = max({d - pwindow, 0});
158
159 if (dist_id == 2 && primary_id == 1) {
160 return primarycensored_gamma_uniform_lcdf(d | q, params, pwindow);
161 } else if (dist_id == 1 && primary_id == 1) {
162 return primarycensored_lognormal_uniform_lcdf(d | q, params, pwindow);
163 } else if (dist_id == 3 && primary_id == 1) {
164 return primarycensored_weibull_uniform_lcdf(d | q, params, pwindow);
165 }
166 return negative_infinity();
167}
168real primarycensored_analytical_lcdf(data real d, int dist_id,
169 array[] real params,
170 data real pwindow, data real L,
171 data real D, int primary_id,
172 array[] real primary_params) {
173 if (d <= L) return negative_infinity();
174 if (d >= D) return 0;
175
177 d, dist_id, params, pwindow, primary_id
178 );
179
180 // Apply truncation normalization
181 if (!is_inf(D) || L > 0) {
182 vector[2] bounds = primarycensored_truncation_bounds(
183 L, D, dist_id, params, pwindow, primary_id, primary_params
184 );
185 real log_cdf_L = bounds[1];
186 real log_cdf_D = bounds[2];
187
188 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
189 result = primarycensored_apply_truncation(result, log_cdf_L, log_normalizer, L);
190 }
191
192 return result;
193}
194real primarycensored_analytical_cdf(data real d, int dist_id,
195 array[] real params,
196 data real pwindow, data real L,
197 data real D, int primary_id,
198 array[] real primary_params) {
199 return exp(primarycensored_analytical_lcdf(d | dist_id, params, pwindow, L, D, primary_id, primary_params));
200}
201real dist_lcdf(real delay, array[] real params, int dist_id) {
202 if (delay <= 0) return negative_infinity();
203
204 // IDs match pcd_distributions$stan_id in R
205 if (dist_id == 1) return lognormal_lcdf(delay | params[1], params[2]);
206 else if (dist_id == 2) return gamma_lcdf(delay | params[1], params[2]);
207 else if (dist_id == 3) return weibull_lcdf(delay | params[1], params[2]);
208 else if (dist_id == 4) return exponential_lcdf(delay | params[1]);
209 else if (dist_id == 9) return beta_lcdf(delay | params[1], params[2]);
210 else if (dist_id == 12) return cauchy_lcdf(delay | params[1], params[2]);
211 else if (dist_id == 13) return chi_square_lcdf(delay | params[1]);
212 else if (dist_id == 15) return gumbel_lcdf(delay | params[1], params[2]);
213 else if (dist_id == 16) return inv_gamma_lcdf(delay | params[1], params[2]);
214 else if (dist_id == 17) return logistic_lcdf(delay | params[1], params[2]);
215 else if (dist_id == 18) return normal_lcdf(delay | params[1], params[2]);
216 else if (dist_id == 19) return inv_chi_square_lcdf(delay | params[1]);
217 else if (dist_id == 20) return double_exponential_lcdf(delay | params[1], params[2]);
218 else if (dist_id == 21) return pareto_lcdf(delay | params[1], params[2]);
219 else if (dist_id == 22) return scaled_inv_chi_square_lcdf(delay | params[1], params[2]);
220 else if (dist_id == 23) return student_t_lcdf(delay | params[1], params[2], params[3]);
221 else if (dist_id == 24) return uniform_lcdf(delay | params[1], params[2]);
222 else if (dist_id == 25) return von_mises_lcdf(delay | params[1], params[2]);
223 else reject("Invalid distribution identifier: ", dist_id);
224}
225real primary_lpdf(real x, int primary_id, array[] real params, real xmin, real xmax) {
226 // Implement switch for different primary distributions
227 if (primary_id == 1) return uniform_lpdf(x | xmin, xmax);
228 if (primary_id == 2) return expgrowth_lpdf(x | xmin, xmax, params[1]);
229 // Add more primary distributions as needed
230 reject("Invalid primary distribution identifier");
231}
232vector primarycensored_ode(real t, vector y, array[] real theta,
233 array[] real x_r, array[] int x_i) {
234 real d = x_r[1];
235 int dist_id = x_i[1];
236 int primary_id = x_i[2];
237 real pwindow = x_r[2];
238 int dist_params_len = x_i[3];
239 int primary_params_len = x_i[4];
240
241 // Extract distribution parameters
242 array[dist_params_len] real params;
243 if (dist_params_len) {
244 params = theta[1:dist_params_len];
245 }
246 array[primary_params_len] real primary_params;
247 if (primary_params_len) {
248 int primary_loc = num_elements(theta);
249 primary_params = theta[primary_loc - primary_params_len + 1:primary_loc];
250 }
251
252 real log_cdf = dist_lcdf(t | params, dist_id);
253 real log_primary_pdf = primary_lpdf(d - t | primary_id, primary_params, 0, pwindow);
254
255 return rep_vector(exp(log_cdf + log_primary_pdf), 1);
256}
257real primarycensored_log_normalizer(real log_cdf_D, real log_cdf_L, real L) {
258 if (L > 0) {
259 return log_diff_exp(log_cdf_D, log_cdf_L);
260 } else {
261 return log_cdf_D;
262 }
263}
264real primarycensored_apply_truncation(real log_cdf, real log_cdf_L,
265 real log_normalizer, real L) {
266 if (L > 0) {
267 return log_diff_exp(log_cdf, log_cdf_L) - log_normalizer;
268 } else {
269 return log_cdf - log_normalizer;
270 }
271}
273 data real L, data real D,
274 int dist_id, array[] real params, data real pwindow,
275 int primary_id, array[] real primary_params
276) {
277 vector[2] result;
278
279 // Get CDF at lower truncation point L
280 if (L <= 0) {
281 result[1] = negative_infinity();
282 } else {
283 result[1] = primarycensored_lcdf(
284 L | dist_id, params, pwindow, 0, positive_infinity(),
285 primary_id, primary_params
286 );
287 }
288
289 // Get CDF at upper truncation point D
290 if (is_inf(D)) {
291 result[2] = 0;
292 } else {
293 result[2] = primarycensored_lcdf(
294 D | dist_id, params, pwindow, 0, positive_infinity(),
295 primary_id, primary_params
296 );
297 }
298
299 return result;
300}
301real primarycensored_cdf(data real d, int dist_id, array[] real params,
302 data real pwindow, data real L, data real D,
303 int primary_id,
304 array[] real primary_params) {
305 real result;
306 if (d <= L) {
307 return 0;
308 }
309
310 if (d >= D) {
311 return 1;
312 }
313
314 // Check if an analytical solution exists
315 if (check_for_analytical(dist_id, primary_id)) {
316 // Use analytical solution
318 d | dist_id, params, pwindow, L, D, primary_id, primary_params
319 );
320 } else {
321 // Use numerical integration for other cases
322 real lower_bound = max({d - pwindow, 1e-6});
323 int n_params = num_elements(params);
324 int n_primary_params = num_elements(primary_params);
325 array[n_params + n_primary_params] real theta = append_array(params, primary_params);
326 array[4] int ids = {dist_id, primary_id, n_params, n_primary_params};
327
328 vector[1] y0 = rep_vector(0.0, 1);
329 result = ode_rk45(primarycensored_ode, y0, lower_bound, {d}, theta, {d, pwindow}, ids)[1, 1];
330
331 // Apply truncation normalization on log scale for numerical stability
332 if (!is_inf(D) || L > 0) {
333 real log_result = log(result);
334 vector[2] bounds = primarycensored_truncation_bounds(
335 L, D, dist_id, params, pwindow, primary_id, primary_params
336 );
337 real log_cdf_L = bounds[1];
338 real log_cdf_D = bounds[2];
339
340 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
342 log_result, log_cdf_L, log_normalizer, L
343 );
344 result = exp(log_result);
345 }
346 }
347
348 return result;
349}
350real primarycensored_lcdf(data real d, int dist_id, array[] real params,
351 data real pwindow, data real L, data real D,
352 int primary_id,
353 array[] real primary_params) {
354 real result;
355
356 if (d <= L) {
357 return negative_infinity();
358 }
359
360 if (d >= D) {
361 return 0;
362 }
363
364 // Check if an analytical solution exists
365 if (check_for_analytical(dist_id, primary_id)) {
367 d | dist_id, params, pwindow, 0, positive_infinity(), primary_id, primary_params
368 );
369 } else {
370 // Use numerical integration
371 result = log(primarycensored_cdf(
372 d | dist_id, params, pwindow, 0, positive_infinity(), primary_id, primary_params
373 ));
374 }
375
376 // Handle truncation normalization
377 if (!is_inf(D) || L > 0) {
378 vector[2] bounds = primarycensored_truncation_bounds(
379 L, D, dist_id, params, pwindow, primary_id, primary_params
380 );
381 real log_cdf_L = bounds[1];
382 real log_cdf_D = bounds[2];
383
384 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
385 result = primarycensored_apply_truncation(result, log_cdf_L, log_normalizer, L);
386 }
387
388 return result;
389}
390real primarycensored_lpmf(data int d, int dist_id, array[] real params,
391 data real pwindow, data real d_upper,
392 data real L, data real D, int primary_id,
393 array[] real primary_params) {
394 if (d_upper > D) {
395 reject("Upper truncation point is greater than D. It is ", d_upper,
396 " and D is ", D, ". Resolve this by increasing D to be greater or equal to d + swindow or decreasing swindow.");
397 }
398 if (d_upper <= d) {
399 reject("Upper truncation point is less than or equal to d. It is ", d_upper,
400 " and d is ", d, ". Resolve this by increasing d to be less than d_upper.");
401 }
402 if (d < L) {
403 return negative_infinity();
404 }
405 real log_cdf_upper = primarycensored_lcdf(
406 d_upper | dist_id, params, pwindow, 0, positive_infinity(), primary_id, primary_params
407 );
408 real log_cdf_lower = primarycensored_lcdf(
409 d | dist_id, params, pwindow, 0, positive_infinity(), primary_id, primary_params
410 );
411
412 // Apply truncation normalization: log((F(d_upper) - F(d)) / (F(D) - F(L)))
413 if (!is_inf(D) || L > 0) {
414 real log_cdf_D;
415 real log_cdf_L;
416
417 // Get CDF at lower truncation point L
418 if (L <= 0) {
419 // No left truncation
420 log_cdf_L = negative_infinity();
421 } else if (d == L) {
422 // Reuse already computed CDF at d
423 log_cdf_L = log_cdf_lower;
424 } else {
425 // Compute CDF at L directly
426 log_cdf_L = primarycensored_lcdf(
427 L | dist_id, params, pwindow, 0, positive_infinity(),
428 primary_id, primary_params
429 );
430 }
431
432 // Get CDF at upper truncation point D
433 if (d_upper == D) {
434 log_cdf_D = log_cdf_upper;
435 } else if (is_inf(D)) {
436 log_cdf_D = 0;
437 } else {
438 log_cdf_D = primarycensored_lcdf(
439 D | dist_id, params, pwindow, 0, positive_infinity(),
440 primary_id, primary_params
441 );
442 }
443
444 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
445 return log_diff_exp(log_cdf_upper, log_cdf_lower) - log_normalizer;
446 } else {
447 return log_diff_exp(log_cdf_upper, log_cdf_lower);
448 }
449}
451 int max_delay, data real L, data real D, int dist_id,
452 array[] real params, data real pwindow,
453 int primary_id, array[] real primary_params
454) {
455
456 int upper_interval = max_delay + 1;
457 vector[upper_interval] log_pmfs;
458 vector[upper_interval] log_cdfs;
459 real log_normalizer;
460
461 // Check if D is at least max_delay + 1
462 if (D < upper_interval) {
463 reject("D must be at least max_delay + 1");
464 }
465
466 // Compute log CDFs (without truncation normalization)
467 // Start from max(1, floor(L)) to avoid computing unused CDFs when L > 0
468 int start_idx = L > 0 ? max(1, to_int(floor(L))) : 1;
469 for (d in start_idx:upper_interval) {
470 log_cdfs[d] = primarycensored_lcdf(
471 d | dist_id, params, pwindow, 0, positive_infinity(), primary_id,
472 primary_params
473 );
474 }
475
476 // Get CDF at lower truncation point L
477 real log_cdf_L;
478 if (L <= 0) {
479 // No left truncation
480 log_cdf_L = negative_infinity();
481 } else if (L <= upper_interval && floor(L) == L) {
482 // L is an integer within computed range, reuse cached value
483 log_cdf_L = log_cdfs[to_int(L)];
484 } else {
485 // L is outside computed range or non-integer, compute directly
486 log_cdf_L = primarycensored_lcdf(
487 L | dist_id, params, pwindow, 0, positive_infinity(),
488 primary_id, primary_params
489 );
490 }
491
492 // Compute log normalizer: log(F(D) - F(L))
493 real log_cdf_D;
494 if (D > upper_interval) {
495 if (is_inf(D)) {
496 log_cdf_D = 0; // log(1) = 0 for infinite D
497 } else {
498 log_cdf_D = primarycensored_lcdf(
499 D | dist_id, params, pwindow, 0, positive_infinity(),
500 primary_id, primary_params
501 );
502 }
503 } else {
504 log_cdf_D = log_cdfs[upper_interval];
505 }
506
507 log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
508
509 // Compute log PMFs: log((F(d) - F(d-1)) / (F(D) - F(L)))
510 for (d in 1:upper_interval) {
511 if (d <= L) {
512 // Delay interval [d-1, d) is entirely at or below L
513 log_pmfs[d] = negative_infinity();
514 } else if (d - 1 < L) {
515 // L falls within interval [d-1, d), so compute mass in [L, d)
516 log_pmfs[d] = log_diff_exp(log_cdfs[d], log_cdf_L) - log_normalizer;
517 } else if (d == 1) {
518 // First interval [0, 1) with L <= 0: F(0) = 0, so PMF = F(1) / normalizer
519 log_pmfs[d] = log_cdfs[d] - log_normalizer;
520 } else {
521 // Standard case: PMF = (F(d) - F(d-1)) / normalizer
522 log_pmfs[d] = log_diff_exp(log_cdfs[d], log_cdfs[d-1]) - log_normalizer;
523 }
524 }
525
526 return log_pmfs;
527}
529 int max_delay, data real L, data real D, int dist_id,
530 array[] real params, data real pwindow,
531 int primary_id,
532 array[] real primary_params
533) {
534 return exp(
536 max_delay, L, D, dist_id, params, pwindow, primary_id, primary_params
537 )
538 );
539}
real dist_lcdf(real delay, array[] real params, int dist_id)
real primarycensored_apply_truncation(real log_cdf, real log_cdf_L, real log_normalizer, real L)
real primarycensored_log_normalizer(real log_cdf_D, real log_cdf_L, real L)
real log_weibull_g(real t, real shape, real scale)
vector primarycensored_ode(real t, vector y, array[] real theta, array[] real x_r, array[] int x_i)
real primarycensored_analytical_lcdf_raw(data real d, int dist_id, array[] real params, data real pwindow, int primary_id)
real primarycensored_weibull_uniform_lcdf(data real d, real q, array[] real params, data real pwindow)
real expgrowth_lpdf(real x, real xmin, real xmax, real r)
vector primarycensored_sone_pmf_vectorized(int max_delay, data real L, data real D, int dist_id, array[] real params, data real pwindow, int primary_id, array[] real primary_params)
vector primarycensored_sone_lpmf_vectorized(int max_delay, data real L, data real D, int dist_id, array[] real params, data real pwindow, int primary_id, array[] real primary_params)
vector primarycensored_truncation_bounds(data real L, data real D, int dist_id, array[] real params, data real pwindow, int primary_id, array[] real primary_params)
real primarycensored_lognormal_uniform_lcdf(data real d, real q, array[] real params, data real pwindow)
real primarycensored_analytical_lcdf(data real d, int dist_id, array[] real params, data real pwindow, data real L, data real D, int primary_id, array[] real primary_params)
real primarycensored_lpmf(data int d, int dist_id, array[] real params, data real pwindow, data real d_upper, data real L, data real D, int primary_id, array[] real primary_params)
real primarycensored_gamma_uniform_lcdf(data real d, real q, array[] real params, data real pwindow)
real primary_lpdf(real x, int primary_id, array[] real params, real xmin, real xmax)
real primarycensored_cdf(data real d, int dist_id, array[] real params, data real pwindow, data real L, data real D, int primary_id, array[] real primary_params)
real primarycensored_analytical_cdf(data real d, int dist_id, array[] real params, data real pwindow, data real L, data real D, int primary_id, array[] real primary_params)
real primarycensored_lcdf(data real d, int dist_id, array[] real params, data real pwindow, data real L, data real D, int primary_id, array[] real primary_params)
int check_for_analytical(int dist_id, int primary_id)