EpiNow2 Stan Functions
primarycensored.stan
Go to the documentation of this file.
1// Stan functions from primarycensored version 1.5.1
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 -
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 log_window = log(pwindow);
22 // log E where E = k * theta = shape / rate is the mean of the delay
23 real log_E = log(shape) - log(rate);
24
25 // F_T(d; k) and the recursion to F_T(d; k+1):
26 // P(k+1, y) = P(k, y) - y^k e^{-y} / Gamma(k+1), with y = rate * d
27 real log_F_T_d_k = gamma_lcdf(d | shape, rate);
28 real gamma_kp1_pdf_log_d
29 = shape * log(rate * d) - rate * d - lgamma(shape + 1);
30 real log_F_T_d_kp1 = log_diff_exp(log_F_T_d_k, gamma_kp1_pdf_log_d);
31
32 // q-dependent terms. Final algebra is unified; only a guard to avoid
33 // log_diff_exp(-inf, -inf) and log(0) when q == 0 (q is data, so autodiff
34 // is unaffected by this branch).
35 real log_q_F_T_q; // log(q * F_T(q; k))
36 real log_E_tF_T_q; // log(E * F_T(q; k+1))
37 if (q > 0) {
38 real log_F_T_q_k = gamma_lcdf(q | shape, rate);
39 real gamma_kp1_pdf_log_q
40 = shape * log(rate * q) - rate * q - lgamma(shape + 1);
41 real log_F_T_q_kp1 = log_diff_exp(log_F_T_q_k, gamma_kp1_pdf_log_q);
42 log_q_F_T_q = log(q) + log_F_T_q_k;
43 log_E_tF_T_q = log_E + log_F_T_q_kp1;
44 } else {
45 log_q_F_T_q = negative_infinity();
46 log_E_tF_T_q = negative_infinity();
47 }
48
49 // Unified form: F_{S+}(d) = (A - B) / w_P with A, B sums of positives:
50 // A = d * F_T(d; k) + E * F_T(q; k+1)
51 // B = q * F_T(q; k) + E * F_T(d; k+1)
52 // Ordering A >= B is guaranteed by F_{S+}(d) >= 0.
53 real log_A = log_sum_exp(log(d) + log_F_T_d_k, log_E_tF_T_q);
54 real log_B = log_sum_exp(log_q_F_T_q, log_E + log_F_T_d_kp1);
55
56 return log_diff_exp(log_A, log_B) - log_window;
57}
58real primarycensored_lognormal_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) {
59 real mu = params[1];
60 real sigma = params[2];
61 real mu_sigma2 = mu + square(sigma);
62 real log_window = log(pwindow);
63 // log E where E = exp(mu + sigma^2/2) is the mean of the delay
64 real log_E = mu + 0.5 * square(sigma);
65
66 real log_F_T_d = lognormal_lcdf(d | mu, sigma);
67 real log_tF_T_d = lognormal_lcdf(d | mu_sigma2, sigma);
68
69 // q-dependent terms (guard only to avoid log(0); final algebra is unified).
70 real log_q_F_T_q; // log(q * F_T(q))
71 real log_E_tF_T_q; // log(E * tilde F_T(q))
72 if (q > 0) {
73 real log_F_T_q = lognormal_lcdf(q | mu, sigma);
74 real log_tF_T_q = lognormal_lcdf(q | mu_sigma2, sigma);
75 log_q_F_T_q = log(q) + log_F_T_q;
76 log_E_tF_T_q = log_E + log_tF_T_q;
77 } else {
78 log_q_F_T_q = negative_infinity();
79 log_E_tF_T_q = negative_infinity();
80 }
81
82 // Unified form: F_{S+}(d) = (A - B) / w_P with
83 // A = d * F_T(d) + E * tilde F_T(q)
84 // B = q * F_T(q) + E * tilde F_T(d)
85 // Ordering A >= B is guaranteed by F_{S+}(d) >= 0.
86 real log_A = log_sum_exp(log(d) + log_F_T_d, log_E_tF_T_q);
87 real log_B = log_sum_exp(log_q_F_T_q, log_E + log_tF_T_d);
88
89 return log_diff_exp(log_A, log_B) - log_window;
90}
91real log_weibull_g(real t, real shape, real scale) {
92 real x = pow(t * inv(scale), shape);
93 real a = 1 + inv(shape);
94 return log(gamma_p(a, x)) + lgamma(a);
95}
96real primarycensored_weibull_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) {
97 real shape = params[1];
98 real scale = params[2];
99 real log_window = log(pwindow);
100 real log_scale = log(scale);
101
102 // For Weibull: E = scale (lambda) and tilde F_T(t) = g(t; lambda, k), so
103 // log(E * tilde F_T(t)) = log(scale) + log_weibull_g(t, shape, scale).
104 real log_F_T_d = weibull_lcdf(d | shape, scale);
105 real log_E_tF_T_d = log_scale + log_weibull_g(d, shape, scale);
106
107 // q-dependent terms (guard only to avoid log(0); final algebra is unified).
108 real log_q_F_T_q; // log(q * F_T(q))
109 real log_E_tF_T_q; // log(E * tilde F_T(q)) = log(scale * g(q; lambda, k))
110 if (q > 0) {
111 log_q_F_T_q = log(q) + weibull_lcdf(q | shape, scale);
112 log_E_tF_T_q = log_scale + log_weibull_g(q, shape, scale);
113 } else {
114 log_q_F_T_q = negative_infinity();
115 log_E_tF_T_q = negative_infinity();
116 }
117
118 // Unified form: F_{S+}(d) = (A - B) / w_P with
119 // A = d * F_T(d) + scale * g(q; lambda, k)
120 // B = q * F_T(q) + scale * g(d; lambda, k)
121 // Ordering A >= B is guaranteed by F_{S+}(d) >= 0.
122 real log_A = log_sum_exp(log(d) + log_F_T_d, log_E_tF_T_q);
123 real log_B = log_sum_exp(log_q_F_T_q, log_E_tF_T_d);
124
125 return log_diff_exp(log_A, log_B) - log_window;
126}
127real primarycensored_analytical_lcdf_raw(data real d, int dist_id,
128 array[] real params,
129 data real pwindow,
130 int primary_id) {
131 real q = max({d - pwindow, 0});
132
133 if (dist_id == 2 && primary_id == 1) {
134 return primarycensored_gamma_uniform_lcdf(d | q, params, pwindow);
135 } else if (dist_id == 1 && primary_id == 1) {
136 return primarycensored_lognormal_uniform_lcdf(d | q, params, pwindow);
137 } else if (dist_id == 3 && primary_id == 1) {
138 return primarycensored_weibull_uniform_lcdf(d | q, params, pwindow);
139 }
140 return negative_infinity();
141}
142real primarycensored_analytical_lcdf(data real d, int dist_id,
143 array[] real params,
144 data real pwindow, data real L,
145 data real D, int primary_id,
146 array[] real primary_params) {
147 if (d <= L) return negative_infinity();
148 if (d >= D) return 0;
149
151 d, dist_id, params, pwindow, primary_id
152 );
153
154 // Apply truncation normalization
155 if (!is_inf(D) || L > 0) {
156 vector[2] bounds = primarycensored_truncation_bounds(
157 L, D, dist_id, params, pwindow, primary_id, primary_params
158 );
159 real log_cdf_L = bounds[1];
160 real log_cdf_D = bounds[2];
161
162 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
163 result = primarycensored_apply_truncation(result, log_cdf_L, log_normalizer, L);
164 }
165
166 return result;
167}
168real primarycensored_analytical_cdf(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 return exp(primarycensored_analytical_lcdf(d | dist_id, params, pwindow, L, D, primary_id, primary_params));
174}
175int dist_has_positive_support(data int dist_id) {
176 if (dist_id == 1) return 1; // Lognormal
177 if (dist_id == 2) return 1; // Gamma
178 if (dist_id == 3) return 1; // Weibull
179 if (dist_id == 4) return 1; // Exponential
180 if (dist_id == 9) return 1; // Beta (support on [0, 1])
181 if (dist_id == 13) return 1; // Chi-square
182 if (dist_id == 16) return 1; // Inverse Gamma
183 if (dist_id == 19) return 1; // Inverse Chi-square
184 if (dist_id == 21) return 1; // Pareto
185 if (dist_id == 22) return 1; // Scaled inverse Chi-square
186 return 0;
187}
188real dist_lcdf(real delay, array[] real params, int dist_id) {
189 if (dist_has_positive_support(dist_id) && delay <= 0) {
190 return negative_infinity();
191 }
192
193 // IDs match pcd_distributions$stan_id in R
194 if (dist_id == 1) return lognormal_lcdf(delay | params[1], params[2]);
195 else if (dist_id == 2) return gamma_lcdf(delay | params[1], params[2]);
196 else if (dist_id == 3) return weibull_lcdf(delay | params[1], params[2]);
197 else if (dist_id == 4) return exponential_lcdf(delay | params[1]);
198 else if (dist_id == 9) return beta_lcdf(delay | params[1], params[2]);
199 else if (dist_id == 12) return cauchy_lcdf(delay | params[1], params[2]);
200 else if (dist_id == 13) return chi_square_lcdf(delay | params[1]);
201 else if (dist_id == 15) return gumbel_lcdf(delay | params[1], params[2]);
202 else if (dist_id == 16) return inv_gamma_lcdf(delay | params[1], params[2]);
203 else if (dist_id == 17) return logistic_lcdf(delay | params[1], params[2]);
204 else if (dist_id == 18) return normal_lcdf(delay | params[1], params[2]);
205 else if (dist_id == 19) return inv_chi_square_lcdf(delay | params[1]);
206 else if (dist_id == 20) return double_exponential_lcdf(delay | params[1], params[2]);
207 else if (dist_id == 21) return pareto_lcdf(delay | params[1], params[2]);
208 else if (dist_id == 22) return scaled_inv_chi_square_lcdf(delay | params[1], params[2]);
209 else if (dist_id == 23) return student_t_lcdf(delay | params[1], params[2], params[3]);
210 else if (dist_id == 24) return uniform_lcdf(delay | params[1], params[2]);
211 else if (dist_id == 25) return von_mises_lcdf(delay | params[1], params[2]);
212 else reject("Invalid distribution identifier: ", dist_id);
213}
214real primary_lpdf(real x, int primary_id, array[] real params, real xmin, real xmax) {
215 // Implement switch for different primary distributions
216 if (primary_id == 1) return uniform_lpdf(x | xmin, xmax);
217 if (primary_id == 2) return expgrowth_lpdf(x | xmin, xmax, params[1]);
218 // Add more primary distributions as needed
219 reject("Invalid primary distribution identifier");
220}
221vector primarycensored_ode(real t, vector y, array[] real theta,
222 array[] real x_r, array[] int x_i) {
223 real d = x_r[1];
224 int dist_id = x_i[1];
225 int primary_id = x_i[2];
226 real pwindow = x_r[2];
227 int dist_params_len = x_i[3];
228 int primary_params_len = x_i[4];
229
230 // Extract distribution parameters
231 array[dist_params_len] real params;
232 if (dist_params_len) {
233 params = theta[1:dist_params_len];
234 }
235 array[primary_params_len] real primary_params;
236 if (primary_params_len) {
237 int primary_loc = num_elements(theta);
238 primary_params = theta[primary_loc - primary_params_len + 1:primary_loc];
239 }
240
241 real log_cdf = dist_lcdf(t | params, dist_id);
242 real log_primary_pdf = primary_lpdf(d - t | primary_id, primary_params, 0, pwindow);
243
244 return rep_vector(exp(log_cdf + log_primary_pdf), 1);
245}
246real primarycensored_log_normalizer(real log_cdf_D, real log_cdf_L, real L) {
247 if (!is_inf(L)) {
248 return log_diff_exp(log_cdf_D, log_cdf_L);
249 } else {
250 return log_cdf_D;
251 }
252}
253real primarycensored_apply_truncation(real log_cdf, real log_cdf_L,
254 real log_normalizer, real L) {
255 if (!is_inf(L)) {
256 return log_diff_exp(log_cdf, log_cdf_L) - log_normalizer;
257 } else {
258 return log_cdf - log_normalizer;
259 }
260}
262 data real L, data real D,
263 data int dist_id, array[] real params, data real pwindow,
264 data int primary_id, array[] real primary_params
265) {
266 vector[2] result;
267 // Internal lower bound for the un-truncated distribution: 0 lets the
268 // `d <= L` early-exit in primarycensored_lcdf return -inf for delays below
269 // the natural support of positive-support distributions; -inf disables that
270 // short-circuit so distributions with support on the reals are integrated.
271 // Expression is inlined (rather than bound to a local) so Stan's data-flow
272 // checker recognises it as data-only.
273
274 // Get CDF at lower truncation point L
275 if (is_inf(L)) {
276 result[1] = negative_infinity();
277 } else {
278 result[1] = primarycensored_lcdf(
279 L | dist_id, params, pwindow,
280 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
281 positive_infinity(), primary_id, primary_params
282 );
283 }
284
285 // Get CDF at upper truncation point D
286 if (is_inf(D)) {
287 result[2] = 0;
288 } else {
289 result[2] = primarycensored_lcdf(
290 D | dist_id, params, pwindow,
291 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
292 positive_infinity(), primary_id, primary_params
293 );
294 }
295
296 return result;
297}
298real primarycensored_cdf(data real d, data int dist_id, array[] real params,
299 data real pwindow, data real L, data real D,
300 data int primary_id,
301 array[] real primary_params) {
302 real result;
303 if (d <= L) {
304 return 0;
305 }
306
307 if (d >= D) {
308 return 1;
309 }
310
311 // Check if an analytical solution exists
312 if (check_for_analytical(dist_id, primary_id)) {
313 // Use analytical solution
315 d | dist_id, params, pwindow, L, D, primary_id, primary_params
316 );
317 } else {
318 // Use numerical integration for other cases. The integration variable
319 // ranges over the primary-event time, so the natural lower bound is
320 // d - pwindow. For positive-support delays the integrand `F_delay(t)` is
321 // 0 for t <= 0, so an unclipped lower bound just adds a flat zero region
322 // for negative t. Distributions with support on the reals also accept the
323 // unclipped lower bound directly.
324 real lower_bound = d - pwindow;
325 int n_params = num_elements(params);
326 int n_primary_params = num_elements(primary_params);
327 array[n_params + n_primary_params] real theta = append_array(params, primary_params);
328 array[4] int ids = {dist_id, primary_id, n_params, n_primary_params};
329
330 vector[1] y0 = rep_vector(0.0, 1);
331 result = ode_rk45(primarycensored_ode, y0, lower_bound, {d}, theta, {d, pwindow}, ids)[1, 1];
332
333 // Apply truncation normalization on log scale for numerical stability.
334 // Skip when F(L) = 0 makes it a no-op (positive support, L <= 0).
335 if (!is_inf(D) || L > 0 ||
336 (!is_inf(L) && !dist_has_positive_support(dist_id))) {
337 real log_result = log(result);
338 vector[2] bounds = primarycensored_truncation_bounds(
339 L, D, dist_id, params, pwindow, primary_id, primary_params
340 );
341 real log_cdf_L = bounds[1];
342 real log_cdf_D = bounds[2];
343
344 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
346 log_result, log_cdf_L, log_normalizer, L
347 );
348 result = exp(log_result);
349 }
350 }
351
352 return result;
353}
354real primarycensored_lcdf(data real d, data int dist_id, array[] real params,
355 data real pwindow, data real L, data real D,
356 data int primary_id,
357 array[] real primary_params) {
358 real result;
359
360 if (d <= L) {
361 return negative_infinity();
362 }
363
364 if (d >= D) {
365 return 0;
366 }
367
368 // Check if an analytical solution exists. The internal lower bound is 0 for
369 // positive-support delays (lets the d <= L early-exit return -inf for d <= 0)
370 // and -inf for distributions with support on the reals.
371 if (check_for_analytical(dist_id, primary_id)) {
373 d | dist_id, params, pwindow,
374 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
375 positive_infinity(), primary_id, primary_params
376 );
377 } else {
378 // Use numerical integration
379 result = log(primarycensored_cdf(
380 d | dist_id, params, pwindow,
381 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
382 positive_infinity(), primary_id, primary_params
383 ));
384 }
385
386 // Handle truncation normalization. Skip when F(L) = 0 makes it a no-op
387 // (positive support, L <= 0) to avoid the cancelling log_diff_exp.
388 if (!is_inf(D) || L > 0 ||
389 (!is_inf(L) && !dist_has_positive_support(dist_id))) {
390 vector[2] bounds = primarycensored_truncation_bounds(
391 L, D, dist_id, params, pwindow, primary_id, primary_params
392 );
393 real log_cdf_L = bounds[1];
394 real log_cdf_D = bounds[2];
395
396 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
397 result = primarycensored_apply_truncation(result, log_cdf_L, log_normalizer, L);
398 }
399
400 return result;
401}
402real primarycensored_lpmf(data int d, data int dist_id, array[] real params,
403 data real pwindow, data real d_upper,
404 data real L, data real D, data int primary_id,
405 array[] real primary_params) {
406 if (d_upper > D) {
407 reject("Upper truncation point is greater than D. It is ", d_upper,
408 " and D is ", D, ". Resolve this by increasing D to be greater or equal to d + swindow or decreasing swindow.");
409 }
410 if (d_upper <= d) {
411 reject("Upper truncation point is less than or equal to d. It is ", d_upper,
412 " and d is ", d, ". Resolve this by increasing d to be less than d_upper.");
413 }
414 if (d < L) {
415 return negative_infinity();
416 }
417 real log_cdf_upper = primarycensored_lcdf(
418 d_upper | dist_id, params, pwindow,
419 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
420 positive_infinity(), primary_id, primary_params
421 );
422 real log_cdf_lower = primarycensored_lcdf(
423 d | dist_id, params, pwindow,
424 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
425 positive_infinity(), primary_id, primary_params
426 );
427
428 // Apply truncation normalization: log((F(d_upper) - F(d)) / (F(D) - F(L))).
429 // Skip when F(L) = 0 makes it a no-op (positive support, L <= 0).
430 if (!is_inf(D) || L > 0 ||
431 (!is_inf(L) && !dist_has_positive_support(dist_id))) {
432 real log_cdf_D;
433 real log_cdf_L;
434
435 // Get CDF at lower truncation point L
436 if (is_inf(L)) {
437 // No left truncation (L = -inf sentinel)
438 log_cdf_L = negative_infinity();
439 } else if (d == L) {
440 // Reuse already computed CDF at d
441 log_cdf_L = log_cdf_lower;
442 } else {
443 // Compute CDF at L directly
444 log_cdf_L = primarycensored_lcdf(
445 L | dist_id, params, pwindow,
446 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
447 positive_infinity(), primary_id, primary_params
448 );
449 }
450
451 // Get CDF at upper truncation point D
452 if (d_upper == D) {
453 log_cdf_D = log_cdf_upper;
454 } else if (is_inf(D)) {
455 log_cdf_D = 0;
456 } else {
457 log_cdf_D = primarycensored_lcdf(
458 D | dist_id, params, pwindow,
459 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
460 positive_infinity(), primary_id, primary_params
461 );
462 }
463
464 real log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
465 return log_diff_exp(log_cdf_upper, log_cdf_lower) - log_normalizer;
466 } else {
467 return log_diff_exp(log_cdf_upper, log_cdf_lower);
468 }
469}
471 data int max_delay, data real L, data real D, data int dist_id,
472 array[] real params, data real pwindow,
473 data int primary_id, array[] real primary_params
474) {
475
476 int upper_interval = max_delay + 1;
477 vector[upper_interval] log_pmfs;
478 vector[upper_interval] log_cdfs;
479 real log_normalizer;
480
481 // Check if D is at least max_delay + 1
482 if (D < upper_interval) {
483 reject("D must be at least max_delay + 1");
484 }
485
486 // Compute log CDFs (without truncation normalization). The internal lower
487 // bound below is 0 for positive-support delays and -inf otherwise; it is
488 // inlined rather than bound to a local so Stan's type checker treats it as
489 // data-only.
490 // Start from max(1, floor(L)) to avoid computing unused CDFs when L > 0;
491 // for L <= 0 (including -inf) start at 1 since F(d) = 0 for d <= 0.
492 int start_idx = (!is_inf(L) && L > 0) ? max(1, to_int(floor(L))) : 1;
493 for (d in start_idx:upper_interval) {
494 log_cdfs[d] = primarycensored_lcdf(
495 d | dist_id, params, pwindow,
496 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
497 positive_infinity(), primary_id, primary_params
498 );
499 }
500
501 // Get CDF at lower truncation point L
502 real log_cdf_L;
503 if (is_inf(L)) {
504 // No left truncation (L = -inf sentinel)
505 log_cdf_L = negative_infinity();
506 } else if (L >= 1 && L <= upper_interval && floor(L) == L) {
507 // L is a positive integer within computed range, reuse cached value
508 log_cdf_L = log_cdfs[to_int(L)];
509 } else {
510 // L is outside computed range or non-integer, compute directly
511 log_cdf_L = primarycensored_lcdf(
512 L | dist_id, params, pwindow,
513 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
514 positive_infinity(), primary_id, primary_params
515 );
516 }
517
518 // Compute log normalizer: log(F(D) - F(L))
519 real log_cdf_D;
520 if (D > upper_interval) {
521 if (is_inf(D)) {
522 log_cdf_D = 0; // log(1) = 0 for infinite D
523 } else {
524 log_cdf_D = primarycensored_lcdf(
525 D | dist_id, params, pwindow,
526 dist_has_positive_support(dist_id) ? 0.0 : negative_infinity(),
527 positive_infinity(), primary_id, primary_params
528 );
529 }
530 } else {
531 log_cdf_D = log_cdfs[upper_interval];
532 }
533
534 log_normalizer = primarycensored_log_normalizer(log_cdf_D, log_cdf_L, L);
535
536 // Compute log PMFs: log((F(d) - F(d-1)) / (F(D) - F(L)))
537 for (d in 1:upper_interval) {
538 if (d <= L) {
539 // Delay interval [d-1, d) is entirely at or below L
540 log_pmfs[d] = negative_infinity();
541 } else if (d - 1 < L) {
542 // L falls within interval [d-1, d), so compute mass in [L, d)
543 log_pmfs[d] = log_diff_exp(log_cdfs[d], log_cdf_L) - log_normalizer;
544 } else if (d == 1 && dist_has_positive_support(dist_id)) {
545 // First interval [0, 1) with L <= 0 and positive-support delay:
546 // F(0) = 0, so PMF = F(1) / normalizer
547 log_pmfs[d] = log_cdfs[d] - log_normalizer;
548 } else if (d == 1) {
549 // First interval [0, 1) with L <= 0 and real-support delay: F(0) is
550 // non-zero in general, so compute it explicitly.
551 real log_cdf_0 = primarycensored_lcdf(
552 0.0 | dist_id, params, pwindow,
553 negative_infinity(), positive_infinity(),
554 primary_id, primary_params
555 );
556 log_pmfs[d] = log_diff_exp(log_cdfs[d], log_cdf_0) - log_normalizer;
557 } else {
558 // Standard case: PMF = (F(d) - F(d-1)) / normalizer
559 log_pmfs[d] = log_diff_exp(log_cdfs[d], log_cdfs[d-1]) - log_normalizer;
560 }
561 }
562
563 return log_pmfs;
564}
566 data int max_delay, data real L, data real D, data int dist_id,
567 array[] real params, data real pwindow,
568 data int primary_id,
569 array[] real primary_params
570) {
571 return exp(
573 max_delay, L, D, dist_id, params, pwindow, primary_id, primary_params
574 )
575 );
576}
real dist_lcdf(real delay, array[] real params, int dist_id)
vector primarycensored_sone_lpmf_vectorized(data int max_delay, data real L, data real D, data int dist_id, array[] real params, data real pwindow, data int primary_id, array[] real primary_params)
real primarycensored_apply_truncation(real log_cdf, real log_cdf_L, real log_normalizer, real L)
real primarycensored_cdf(data real d, data int dist_id, array[] real params, data real pwindow, data real L, data real D, data int primary_id, array[] real primary_params)
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(data int max_delay, data real L, data real D, data int dist_id, array[] real params, data real pwindow, data int primary_id, array[] real primary_params)
real primarycensored_lpmf(data int d, data int dist_id, array[] real params, data real pwindow, data real d_upper, data real L, data real D, data int primary_id, array[] real primary_params)
real primarycensored_lcdf(data real d, data int dist_id, array[] real params, data real pwindow, data real L, data real D, data 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)
int dist_has_positive_support(data int dist_id)
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)
vector primarycensored_truncation_bounds(data real L, data real D, data int dist_id, array[] real params, data real pwindow, data 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)
int check_for_analytical(int dist_id, int primary_id)