Post-ensemble optimization that evaluates top-N subsets (ranked by
likelihood) and selects the N that maximizes prediction quality. Produces a
separate mosaic_ensemble object at the optimal subset size, leaving the
original ensemble untouched.
For each candidate size N (from min_n to the full ensemble), the
function re-computes the best-subset weights within the top-N subset,
re-computes weighted median predictions from the 4D arrays, and scores with
the selected objective function.
Selection scores the ENGINE-level member trajectories
(ensemble$cases_engine_array/deaths_engine_array; the
cases_array/deaths_array of an ensemble saved before v0.101.0,
which were engine-level), the series the ensemble's central lines are built
from. The optimized ensemble is then rebuilt exactly as
calc_model_ensemble builds one: central lines from the engine
draws and the interval envelope and predictive_median from the
observation-level draws of the selected members, when the ensemble carries
them. The "wis" objective therefore scores the engine-level spread.
The per-N weights use the same scheme as the weight_best posterior
that run_MOSAIC() builds the candidate ensemble from (by default
\(w \propto \exp(-0.5 \min(\Delta, 4))\) with
\(\Delta = -2(\ell - \max \ell)\) within the top N).
So at N = the full ensemble the weights, and therefore
diagnostics_score, reproduce the candidate ensemble, and any score
change reported for a smaller N is due to the subset, not to a change of
weighting scheme.
Arguments
- ensemble
A
mosaic_ensembleobject returned bycalc_model_ensemble.- likelihoods
Numeric vector of log-likelihoods, one per parameter set in the ensemble. Must have length
ensemble$n_param_sets.- seeds
Optional numeric/integer vector of simulation seeds aligned with
likelihoods. When supplied, the function returnsoptimal_seedsso callers can map the optimized subset back to the originalsamples.parquetrows without re-deriving the internal sort. Separately, ifensemble$seedsis present (the per-member seeds carried bycalc_model_ensemble, aligned withcases_arrayby construction), it is carried through the same sort/slice and exposed asensemble_optimized$seedsfor cases_array-aligned consumers such as medoid selection. This argument andensemble$seedsserve different roles and are kept independent.- min_n
Minimum subset size to evaluate. Default
30Lto guard against small-subset KDE degeneracy when the optimized subset drives posterior artifacts. Fox et al. (2024) report 4 as a statistical minimum for accuracy, but posterior density estimation needs larger N.- objective
Scoring function:
"mae"(default, normalized MAE),"r2_bias"(R-squared plus bias penalty), or"wis"(normalized Weighted Interval Score).- central_method
Central tendency used to summarize each per-cell ensemble distribution for the
"mae"and"r2_bias"objectives and for the recorded R^2/bias/MAE diagnostics:"median"(default, reproduces historical selection) or"mean"(weighted mean, unbiased for expected counts). Scalar or per-channelc(cases=, deaths=). The"wis"objective is quantile-based and unaffected by this setting (its point forecast remains the weighted median). The default"median"here is deliberate – it preserves historical direct-call selection and the Tier-2 bit-for-bit parity guarantee;run_MOSAIC()passes the resolvedcontrol$predictions$central_method(package default since v0.101.0:"median"for cases,"mean"for deaths) explicitly.- stride
Integer >= 1.
1L(default) evaluates every N inmin_n:max_n(exhaustive, bit-identical parity path).> 1Lenables a coarse-then-refine two-stage search (see Details). Opt-in; changes which N's are evaluated and may select a slightly differentoptimal_nthan the exhaustive search.- cl
Optional parallel cluster (from
make_mosaic_clusterorparallel::makeCluster) used to split the per-cell evaluation kernel across workers.NULL(default) runs serially. When supplied, the result is bit-for-bit identical to the serial path. Must be a"PSOCK"cluster (FORK is unsafe at this point in the pipeline). For trivially small problems the function falls back to serial regardless ofcl.- weighting
Best-subset weighting scheme for the per-N weights, matching
control$targets$best_subset_weighting(the scheme ofweight_best):"saturated"(default) or"tempered".- ess_method
ESS formula for
evaluation_table$ess:"kish"(default) or"perplexity".- verbose
Logical; if
TRUE, emit progress messages.
Value
An S3 object of class mosaic_subset_optimization containing:
- evaluation_table
Data frame with one row per N evaluated. With
stride > 1Lthis contains only the coarse + refine N's, sorted by N, not every N inmin_n:max_n.- optimal_n
Selected subset size.
- optimal_score
Score at optimal N.
- optimal_weights
Re-computed best-subset weights for the optimal subset.
- optimal_indices
Integer indices into the original ensemble arrays.
- optimal_seeds
Simulation seeds of the optimal subset in likelihood-sorted order, aligned with
optimal_weights(whenseedssupplied), elseNULL.- ensemble_optimized
Complete
mosaic_ensembleobject at optimal N, carrying the selected members' observation-level and engine-level arrays. Its$seedsfield is the per-member seed aligned with its arrays (member-to-seed aligned) whenensemble$seedswas present, else it falls back tooptimal_seeds.- stability_flag
TRUE if score profile was flat.
- diagnostics_n
Original diagnostics-selected N.
- diagnostics_score
Score at the diagnostics-selected N.
- objective
Which objective was used.
- central_method
Resolved per-channel central tendency used for
"mae"/"r2_bias"scoring and the recorded diagnostics.
Details
The evaluation loop is organized cell-outer: each
[location, time] cell's finite predictions are gathered once and then
scored across every candidate N (the gather does not depend on N), filling a
per-cell vector of central tendencies. The default call path (stride = 1L,
cl = NULL) is exhaustive and serial and is guaranteed bit-for-bit
identical to the historical implementation (Tier-2 parity).
Two opt-in speed levers are available:
PSOCK parallelism (
cl): then_locations * n_time_pointscells are split across the supplied cluster's workers. The result is bit-for-bit identical to the serial path (each worker runs the same kernel on a disjoint block of cells; the master scatters the per-cell central/quantile values back into the same[i, j]positions). FORK clusters are unsafe here (native reticulate/numba threads are loaded by this post-calibration point); pass a"PSOCK"cluster.Stride-then-refine (
stride): withstride > 1L, a coarse gridseq(min_n, max_n, by = stride)is evaluated first to locate the best N*, then a refinement bracket(N*-stride):(N*+stride)is evaluated, and the selection logic runs over the combined (coarse + refine) rows. This is not bit-identical to the exhaustive path:evaluation_tablecontains only the evaluated N's (fewer rows), and the selectedoptimal_nmay differ slightly from the exhaustive optimum if the score profile is non-monotone between coarse points. The trade is roughly anO(stride)reduction in the number of N's evaluated.
References
Bracher J et al. (2021). Evaluating epidemic forecasts in an interval format. PLOS Computational Biology, 17(2), e1008618.
Gneiting T & Raftery AE (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. JASA, 102(477), 359–378.
See also
make_mosaic_cluster for building a PSOCK cluster to
pass to cl.
