A Laplace library of parametric survival models for Stan — Gompertz, log-logistic, Gompertz–Makeham, and piecewise-exponential — with right-censored likelihoods, pointwise log-likelihoods for LOO, survival curves, and event-time simulators. Import it into any .laplace model and call it with namespaced calls (survival::function_name(...)).
Like all Laplace libraries, survival compiles down to plain, readable Stan functions. Nothing about how you use it hides what actually ends up in your .stan file.
Every model is described by two functions: the log hazard
where
| Model | Hazard |
Cumulative hazard |
Parameters |
|---|---|---|---|
| Gompertz |
gamma log-hazard slope, eta log hazard at |
||
| Log-logistic |
alpha scale (median), beta shape |
||
| Gompertz–Makeham |
a background hazard, gamma, eta as Gompertz |
||
| Piecewise exponential |
|
log_lambda baseline log hazard per interval, eta linear predictor |
In the Gompertz, Makeham, and PWE models, eta is where your covariates go (e.g. eta0 + X * beta), giving a proportional-hazards model. The Gompertz and Makeham cumulative hazards are computed in a form that stays numerically stable as
All functions live in survival_hazard.laplacelib and follow the naming pattern srv_<model>_<quantity>, with <model> one of gompertz, log_logistics, makeham, pwe.
| Function | Returns | Gompertz | Log-logistic | Makeham | PWE |
|---|---|---|---|---|---|
srv_<model>_log_hazard |
✓ | ✓ | ✓ | ✓ | |
srv_<model>_cumulative_hazard |
✓ | ✓ | ✓ | ✓ | |
srv_<model>_lpdf |
Summed right-censored log likelihood | ✓ | ✓ | ✓ | srv_pwe_loglik_sum |
srv_<model>_lccdf |
Summed log survival, |
✓ | ✓ | ✓ | — |
srv_<model>_loglik |
Per-observation log likelihood (for LOO / WAIC) | ✓ | ✓ | ✓ | ✓ |
srv_<model>_survival_curve |
✓ | ✓ | ✓ | ✓ | |
srv_<model>_rng |
One simulated event time | ✓ | ✓ | ✓ | — |
The piecewise-exponential model adds two data-preparation helpers, meant to be called once in transformed data:
| Function | Returns |
|---|---|
srv_pwe_interval(t, starts) |
Interval index of each observation: the largest |
srv_pwe_exposure(t, starts) |
|
Most functions are overloaded so shared parameters can be passed as real and per-observation parameters as vector. Every function carries @brief, @param, @return, @example, and @math documentation, so you can read it from the terminal without leaving your model:
laplace doc survival::srv_gompertz_lpdf
Argument order is not identical across models — in particular, where the event indicator d goes — so here it is in one place:
| Model | Hazards / survival curve | lpdf |
lccdf |
loglik |
rng |
|---|---|---|---|---|---|
| Gompertz | (t, gamma, eta) |
(t | gamma, eta, d) |
(t | gamma, eta) |
(t, d, gamma, eta) |
(gamma, eta) |
| Log-logistic | (t, alpha, beta) |
(t | alpha, beta, d) |
(t | alpha, beta) |
(t, d, alpha, beta) |
(alpha, beta) |
| Makeham | (t, a, gamma, eta) |
(t | a, gamma, eta, d) |
(t | a, gamma, eta) |
(t, a, gamma, eta, d) |
(log_c, gamma, eta) |
| PWE | (interval, log_lambda, eta) / (E, log_lambda, eta) |
loglik_sum(interval, E, d, log_lambda, eta) |
— | (interval, E, d, log_lambda, eta) |
— |
survival is distributed as a git-hosted Laplace library — there's no published registry entry yet, so it's added by pointing laplace (or cmdlaplacer, if you're working from R) directly at the repository. The package lives in the repository's laplace/ subdirectory, so pass it as the subdir.
From inside a Laplace project (a directory with its own laplace.toml):
laplace add survival --git https://github.com/mlatinov/laplace-survival --tag 0.1.0 --subdir laplace
library(cmdlaplacer)
laplace_install_git(
"survival",
"https://github.com/mlatinov/laplace-survival",
tag = "0.1.0",
subdir = "laplace"
)Either way, this pins the dependency in your project's laplace.toml/laplace.lock at tag 0.1.0. Check the releases for newer tags as they become available.
Import the library in a library { } block and call its functions with the survival:: namespace prefix.
A Gompertz model with covariates, right censoring, pointwise log likelihoods for loo, a baseline survival curve, and a simulated event time:
library {
import survival
}
data {
int<lower=1> N; // subjects
int<lower=1> K; // covariates
vector<lower=0>[N] t; // follow-up time
vector<lower=0, upper=1>[N] d; // 1 = event, 0 = censored
matrix[N, K] X;
int<lower=1> G;
vector<lower=0>[G] t_grid; // times for the survival curve
}
parameters {
real eta0; // baseline log hazard at t = 0
vector[K] beta; // log hazard ratios
real gamma; // change in log hazard per unit time
}
model {
vector[N] eta = eta0 + X * beta;
eta0 ~ normal(-3, 2);
beta ~ normal(0, 1);
gamma ~ normal(0, 0.5);
target += survival::srv_gompertz_lpdf(t | gamma, eta, d);
}
generated quantities {
vector[N] log_lik = survival::srv_gompertz_loglik(t, d, gamma, eta0 + X * beta);
vector[G] S_baseline = survival::srv_gompertz_survival_curve(t_grid, gamma, rep_vector(eta0, G));
real t_new = survival::srv_gompertz_rng(gamma, eta0);
}Swapping in another parametric model is a matter of changing the function family and its parameters, e.g. survival::srv_log_logistics_lpdf(t | alpha, beta, d) or survival::srv_makeham_lpdf(t | a, gamma, eta, d).
The interval lookup and exposure matrix depend only on data, so they are computed once in transformed data:
library {
import survival
}
data {
int<lower=1> N;
int<lower=1> K;
int<lower=1> J; // number of intervals
vector<lower=0>[N] t;
vector<lower=0, upper=1>[N] d;
matrix[N, K] X;
vector<lower=0>[J] starts; // interval start points, starts[1] = 0
}
transformed data {
array[N] int interval = survival::srv_pwe_interval(t, starts);
matrix[N, J] E = survival::srv_pwe_exposure(t, starts);
}
parameters {
vector[J] log_lambda; // baseline log hazard per interval
vector[K] beta;
}
model {
log_lambda ~ normal(-3, 1);
beta ~ normal(0, 1);
target += survival::srv_pwe_loglik_sum(interval, E, d, log_lambda, X * beta);
}
generated quantities {
vector[N] log_lik = survival::srv_pwe_loglik(interval, E, d, log_lambda, X * beta);
}With cmdlaplacer, the .laplace file compiles straight to a cmdstanr model, and the generated .stan file stays on disk next to it:
library(cmdlaplacer)
mod <- laplace_model("gompertz.laplace")
fit <- mod$sample(data = stan_data)
fit$loo() # uses the log_lik vector from generated quantities-
Use
target +=, not~. Laplace rewritessurvival::func(calls, so writetarget += survival::srv_gompertz_lpdf(t | ...). Thet ~ survival::srv_gompertz(...)form won't resolve. -
The
lpdffunctions already handle censoring throughd, so pass every observation — events and censored — in one call. Thelccdffunctions are for the alternative pattern where censored rows are passed separately; never use both for the same rows, or censored observations are counted twice. -
dis avector, not an integer array. If your indicator isarray[N] int, convert it once intransformed datawithto_vector(...). - Right censoring only. Left censoring, interval censoring, and delayed entry (left truncation) are not supported yet.
-
Log-logistic times must be strictly positive, since the hazard involves
$\log t$ . -
Gompertz with
$\gamma < 0$ implies a cure fraction: some subjects never experience the event, andsrv_gompertz_rngreturnspositive_infinity()for them. Account for this when summarising simulated times. -
srv_makeham_rngtakes the background hazard on the log scale (log_c), unlike the other Makeham functions, which takeaon the natural scale. Passlog(a). -
PWE intervals should start at 0 (
starts[1] = 0). A time exactly on a boundary belongs to the earlier interval, and the last interval is open-ended. -
Survival curves take one
etaper time point. For a single covariate profile over a time grid, userep_vector(eta0, G).
See LICENSE.