The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
This vignette fits a time-stratified case-crossover model to daily mortality and PM10 in London (2002–2006), using the example dataset from Tobias et al. (2024).
Strata are defined by day-of-week within month and year via
dirichlet_multinom(..., by = "year_month_dow"). PM10 enters
through an integrated Wiener process smooth
(iwp(pm10, ...)), with relative humidity as a linear fixed
effect. Fitting is done with adlaplace().
data("london", package = "adlaplace")
if (!vignette_full) {
london <- london[london$year == 2003, ]
}
head(london)## city date year month day dow all tmean rh pm10
## 1 London 2002-01-01 2002 Jan 1 Tue 199 -0.22500001 75.6783 71.70000
## 2 London 2002-01-02 2002 Jan 2 Wed 231 0.08749995 77.5250 40.20000
## 3 London 2002-01-03 2002 Jan 3 Thu 210 0.85000002 81.3250 41.80000
## 4 London 2002-01-04 2002 Jan 4 Fri 203 0.53750002 85.4500 50.40000
## 5 London 2002-01-05 2002 Jan 5 Sat 224 4.25000000 93.5250 49.36429
## 6 London 2002-01-06 2002 Jan 6 Sun 198 7.06874990 96.4000 31.10000
## 0% 25% 50% 75% 100%
## 8.10000 30.70000 39.72857 50.50000 99.60000
## 0% 25% 50% 75% 100%
## -1.400507 7.506250 11.472748 16.198438 28.174999
Case-crossover strata absorb the intercept, so the formula uses
0 + on the right-hand side.
fit <- adlaplace(
formula = cc_formula,
data = london,
config = list(
num_threads = vignette_num_threads,
num_shards = fit_num_shards
),
control = list(
maxit = fit_maxit,
trace = 3, REPORT = 5
),
verbose = FALSE, hessian = vignette_full
)## N = 4, M = 5 machine precision = 2.22045e-16
## At X0, 0 variables are exactly at the bounds
## At iterate 0 f= 5352.3 |proj g|= 10604
## At iterate 5 f = 5349.2 |proj g|= 121.32
## At iterate 10 f = 5349.1 |proj g|= 1659
## Warning: inner_opt status is Radius of trust region is less than
## stop.trust.radius; the Laplace approximation used this inner solution anyway
## At iterate 15 f = 5346.5 |proj g|= 438.17
## At iterate 20 f = 5341.5 |proj g|= 8.1316
##
## iterations 21
## function evaluations 30
## segments explored during Cauchy searches 21
## BFGS updates skipped 0
## active bounds at final generalized Cauchy point 0
## norm of the final projected gradient 1.00993
## final function value 5341.48
##
## F = 5341.48
## final value 5341.481082
## converged
## label mle se mle_internal
## 1 rh_linear 0.0006178345 0.0002083658 0.0006178345
## 2 all_dirichlet_multinom_sd 0.0023596821 NA -6.0492283849
## 3 pm10_iwp 0.0010411985 NA -6.8673827804
## 4 tmean_iwp 0.0090517336 NA -4.7047989815
## se_internal init lower upper parscale log
## 1 0.0002083658 0.000000 -Inf Inf 1 FALSE
## 2 0.0000000000 -3.912023 -Inf Inf 1 TRUE
## 3 0.0000000000 -6.907755 -20.72327 Inf 1 TRUE
## 4 0.3720226591 -6.907755 -20.72327 Inf 1 TRUE
The same model with overdispersion optimized on the natural scale
(log = FALSE) should recover the same MLE and
log-likelihood.
cc_formula_nat <- dirichlet_multinom(
all,
by = c("year", "month", "dow"),
init = 0.015,
lower = 0,
upper = 0.5,
parscale = 1e-3,
log = FALSE
) ~ 0 + rh + iwp(
pm10,
p = 2,
knots = seq(0, 100, by = 10),
ref_value = 40,
init = 0.0001
) + iwp(
tmean,
p = 3, knots = seq(-2, 30, by = 2),
init = 0.002,
ref_value = 20
)
fit_nat <- adlaplace(
formula = cc_formula_nat,
data = london,
config = list(
num_threads = vignette_num_threads,
num_shards = 100L
),
control = list(
maxit = fit_maxit,
trace = 3, REPORT = 5,
factr = 1e3,
pgtol = 1e-8
),
verbose = FALSE, hessian = vignette_full
)## N = 4, M = 5 machine precision = 2.22045e-16
## At X0, 0 variables are exactly at the bounds
## At iterate 0 f= 5345.1 |proj g|= 12225
## At iterate 5 f = 5341.4 |proj g|= 105.58
## At iterate 10 f = 5341.3 |proj g|= 1492.9
## At iterate 15 f = 5335.5 |proj g|= 11033
## At iterate 20 f = 5330.5 |proj g|= 2.0286
## At iterate 25 f = 5330.5 |proj g|= 4.3351
## At iterate 30 f = 5330.5 |proj g|= 35.953
## At iterate 35 f = 5330.4 |proj g|= 3.2288
## At iterate 40 f = 5330.1 |proj g|= 0.026001
##
## iterations 43
## function evaluations 71
## segments explored during Cauchy searches 43
## BFGS updates skipped 0
## active bounds at final generalized Cauchy point 0
## norm of the final projected gradient 0.000377107
## final function value 5330.09
##
## F = 5330.09
## final value 5330.091906
## converged
## [1] "Mean relative difference: 1.293903"
## [1] "Mean relative difference: 0.002132213"
num_sim <- 200L
num_grid <- 101L
gamma_sims <- rmvnldl(n = num_sim, fit = fit)
smooth_vars <- unique(fit$model_data$term_data$info$gamma$term)
oldpar <- par(no.readonly = TRUE)
par(mfrow = c(1, 2), mar = c(2.25, 2.25, 0, 0), bty = "l", mgp = c(1, 0.5, 0))
for (D in smooth_vars) {
sim_here <- sim_random(
D,
fit = fit,
gamma_sims = gamma_sims,
num_grid = num_grid,
transform = exp
)
q <- if (!is.null(sim_here$global)) sim_here$global else sim_here$pointwise
matplot(
sim_here$x, sim_here$sim,
type = "l", lty = 1, col = "#00000010",
xlab = D, ylab = "RR", xaxs = "i", yaxs = "i",
ylim = quantile(sim_here$sim, prob = c(0.005, 0.95))
)
matlines(sim_here$x, q, lty = c(2, 1, 2), col = "red")
}Build model_data, shard map, and ad_pack
without running outer optimization, then maximize the Laplace profile
likelihood with optim directly.
md <- model_data(cc_formula, data = london, verbose = FALSE)
config_dev <- list(
transform_theta = TRUE,
num_threads = vignette_num_threads,
verbose = FALSE
)
config_dev$obs_groups <- obs_groups(
A = md$term_data$A,
elgm_matrix = md$term_data$elgm_matrix,
num_shards = 50L,
min_shards = min(50L, 2L * 4L)
)
par_info <- md$term_data$info$parameters
cache <- new.env(parent = emptyenv())
cache$gamma <- rep(0, nrow(md$term_data$info$gamma))
control_dev <- list(
maxit = 10, trace = 3, REPORT = 5,
parscale = par_info$parscale
)
control_inner <- list(report.level = 0)
af <- ad_pack(md, config_dev, num_threads = vignette_num_threads)
slotNames(af)## [1] "ptr" "group_sparsity" "outer" "inner"
## [5] "map_outer" "map_inner" "parallel_map" "chol_inner"
## [9] "chol_inner_list" "sizes" "info"
## [1] 420 50
## term model label init lower upper
## 1 rh linear rh_linear 0.000000 -Inf Inf
## 2 all dirichlet_multinom all_dirichlet_multinom_sd -3.912023 -Inf Inf
## 3 pm10 iwp pm10_iwp -6.907755 -20.72327 Inf
## 4 tmean iwp tmean_iwp -6.907755 -20.72327 Inf
## parscale log
## 1 1 FALSE
## 2 1 TRUE
## 3 1 TRUE
## 4 1 TRUE
bob <- stats::optim(
par = par_info$init,
fn = outer_fn,
gr = outer_gr,
method = "L-BFGS-B",
control = control_dev,
lower = par_info$lower,
upper = par_info$upper,
config = config_dev,
ad_pack = af,
cache = cache,
control_inner = control_inner,
hessian = TRUE
)## N = 4, M = 5 machine precision = 2.22045e-16
## At X0, 0 variables are exactly at the bounds
## At iterate 0 f= 5352.3 |proj g|= 10604
## At iterate 5 f = 5349.2 |proj g|= 121.32
## At iterate 10 f = 5349.1 |proj g|= 1659
## final value 5348.990265
## stopped after 11 iterations
At starting values, evaluate the full log-density and its gradient
over all parameters (beta, inner gamma, and
theta).
sz <- af@sizes
gamma0 <- rep(0, nrow(md$term_data$info$gamma))
x_full <- c(
par_info$init[seq_len(sz["beta"])],
gamma0,
par_info$init[seq(sz["beta"] + 1L, length.out = sz["theta"])]
)
# Serial dens/grad/hess on a dens-safe clone (multi-thread affinity cleared).
dens_ptr <- clone_ad_pack_ptr(af@ptr)
shards <- seq.int(from = 0L, length.out = adlaplace:::n_groups(dens_ptr))
by_shard <- vapply(
shards,
function(s) joint_log_dens(dens_ptr, x_full, ad_shards = s),
numeric(1)
)
by_shard[1:5]## [1] 4386.024 2790.146 4277.597 6248.223 3486.712
## [1] 9445.66083 8740.04252 -59.88817 -95.82107 -363562.89841
## [1] 5271.516
## [1] 5271.516
## [1] 6.734860e+02 6.169219e+02 1.701712e+02 1.065871e+01 3.620924e-02
## [6] -2.058877e+03 -1.421416e+03 -8.792575e+02 -4.298762e+02 -1.374054e+02
## [11] -7.191833e+00 -3.433567e+03 -1.181943e+03 -1.960381e+03 -2.089588e+03
## [16] -1.849754e+03 -1.501943e+03 -1.151819e+03 -7.451234e+02 -3.623431e+02
## [21] -1.257438e+02 -1.793431e+01 -2.509312e-01 -1.504503e+03 -5.623891e+02
## [26] -1.529105e+02 -1.805906e+01 -8.759916e-03 -3.446074e+03 -5.494638e+03
## [31] -3.552390e+01 1.000000e+01 1.600000e+01
With outer parameters fixed at their starting values,
log_lik_laplace() optimizes random effects and returns the
log-likelihood.
x_outer <- par_info$init
lik <- log_lik_laplace(
x = x_outer,
ad_pack = af,
config = modifyList(config_dev, list(verbose = TRUE)),
gamma = gamma0,
control = list(report.level = 2, report.freq = 1),
deriv = TRUE
)## inner_opt: threads = 2, shards = 53, params = 33 (beta = 1, gamma = 29, theta = 3)
## inner_opt: constructing AD_Func_Opt (hess maps)...
## inner_opt: AD_Func_Opt done nnz=755
## inner_opt: entering CppadParallelScope...
## CppadParallelScope: drain shard eval buffers (entry)...
## CppadParallelScope: setup num_threads=2
## CppadParallelScope: setup done
## inner_opt get_fdf: before OpenMP threads=2 Nparams=33 shards=53
## inner_opt get_fdf: after OpenMP
## inner_opt get_fdfh: before OpenMP threads=2 Nparams=33 hess_size=755 shards=53
## inner_opt get_fdfh parallel_team=2
## inner_opt get_fdfh: after OpenMP api_err_shard=-1
## inner_opt get_fdfh: scatter Hessian...
## inner_opt get_fdfh: scatter done
## inner_opt: opt.run() starting (first get_fdfh)...
## Beginning optimization
## inner_opt get_f: before OpenMP threads=2 shards=53
## inner_opt get_f: after OpenMP
## inner_opt get_fdf: before OpenMP threads=2 Nparams=33 shards=53
## inner_opt get_fdf: after OpenMP
##
## iter f nrm_gr status
## 1 5178.319260 254.593072 Continuing
## inner_opt get_fdfh: before OpenMP threads=2 Nparams=33 hess_size=755 shards=53
## inner_opt get_fdfh parallel_team=2
## inner_opt get_fdfh: after OpenMP api_err_shard=-1
## inner_opt get_fdfh: scatter Hessian...
## inner_opt get_fdfh: scatter done
## inner_opt get_f: before OpenMP threads=2 shards=53
## inner_opt get_f: after OpenMP
## inner_opt get_fdf: before OpenMP threads=2 Nparams=33 shards=53
## inner_opt get_fdf: after OpenMP
## 2 5178.248523 0.158533 Continuing
## inner_opt get_fdfh: before OpenMP threads=2 Nparams=33 hess_size=755 shards=53
## inner_opt get_fdfh parallel_team=2
## inner_opt get_fdfh: after OpenMP api_err_shard=-1
## inner_opt get_fdfh: scatter Hessian...
## inner_opt get_fdfh: scatter done
## inner_opt get_f: before OpenMP threads=2 shards=53
## inner_opt get_f: after OpenMP
## inner_opt get_fdf: before OpenMP threads=2 Nparams=33 shards=53
## inner_opt get_fdf: after OpenMP
## 3 5178.248523 0.000001 Continuing
##
## Iteration has terminated
## 3 5178.248523 0.000001 Success
##
## inner_opt get_fdf: before OpenMP threads=2 Nparams=33 shards=53
## inner_opt get_fdf: after OpenMP
## inner_opt get_fdfh: before OpenMP threads=2 Nparams=33 hess_size=755 shards=53
## inner_opt get_fdfh parallel_team=2
## inner_opt get_fdfh: after OpenMP api_err_shard=-1
## inner_opt get_fdfh: scatter Hessian...
## inner_opt get_fdfh: scatter done
## inner_opt: opt.run() done iters=3 fval=5178.248523
## inner_opt: chol_update starting...
## inner_opt: chol_update done log_det=401.365487
## inner_opt: trust region done; outer get_fdfh next (2 threads)
## outer get_fdfh: before OpenMP threads=2 Nparams=33 hess_size=929 shards=53
## outer get_fdfh parallel_team=2
## outer get_fdfh: after OpenMP api_err_shard=-1
## outer get_fdfh: scatter Hessian...
## outer get_fdfh: scatter done
## inner_opt: outer get_fdfh done
## inner_opt: trace_hinv_t next (2 threads)
## trace_hinv_t: threads = 2, shards = 53, params = 33
## trace_hinv_t: finished
## inner_opt: leaving CppadParallelScope (teardown next)...
## CppadParallelScope: drain shard eval buffers (exit)...
## CppadParallelScope: teardown (free_available + free_all)...
## CppadParallelScope: teardown done
## inner_opt: parallel block ended
## inner_opt: finished
## [1] 5352.282
## [1] -1.060442e+04 8.159608e+00 -1.261264e-01 -9.724369e+00
## [1] 6.175304e-05 -1.200242e-05 -2.041847e-07 -4.323359e-08 1.271120e-04
## [6] 2.751351e-04
neg_log_lik is the negative profile log-likelihood after
inner optimization over random effects.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.