calibration

calibration

HPVsim calibration — ss.Calibration subclass with a weighted-gof eval.

Provides

  • hpv.Calibration: ss.Calibration subclass that takes data (a standardized wide DataFrame, a dict of pre-scoped DataFrames, or a list of CSV paths / long-format DataFrames — all normalized via hpv.data.loaders.load_calib_data) and computes a single weighted mismatch using compute_gof.
  • compute_gof: goodness-of-fit between actual and predicted arrays.
  • build_sim: default build_fn that routes flat dotted-key calib_pars to sim.pars, sim.diseases[].pars, or the CrossImmunity connector.

Standardized data DataFrame: index=‘t’ (float years), columns dot-scoped: - all_hpv.<name> scalar-per-year pooled target; looked up on sim.results.all_hpv (HPVTotal) at eval time (e.g. all_hpv.asr_cancer_incidence). - all_hpv.<name>.<bin> age-stratified pooled target; looked up on the auto-attached all_hpv_by_age by_age analyzer (e.g. all_hpv.cancers.0-15). - by_genotype.<name>.<g> per-genotype distribution target; computed at eval time via hpv.results_by_genotype.

Classes

Name Description
Calibration HPVsim calibration. Delegates to ss.Calibration with HPV-aware defaults.

Calibration

calibration.Calibration(
    sim,
    calib_pars,
    *,
    data=None,
    weights=None,
    gof_kwargs=None,
    build_fn=None,
    eval_fn=None,
    eval_kw=None,
    **kwargs,
)

HPVsim calibration. Delegates to ss.Calibration with HPV-aware defaults.

Three entry points to specify the fit target:

  • datafiles=['data/foo_cancer_cases.csv', ...]: list of long-format CSV paths (columns year,name,[age,sex,genotype,]value). The loader dispatches on stratification columns: files with an age column go to a shared by_age analyzer named 'calib_by_age' (attached to sim.pars.analyzers if not already there); by_age result keys then live under the scoped data key 'calib_by_age.<name>'. Ages and years are derived from the data itself. Genotype-stratified files (no age column) are reserved for 'calib_by_genotype.<name>' but not yet wired up (NotImplementedError).
  • data={'calib_by_age.cancers': df, ...}: pre-built dict of ‘t’-indexed DataFrames. Keys are '<analyzer_name>.<result_key>' (scoped) or a bare result key that unambiguously matches a single by_age analyzer on the sim. The default eval_fn extracts each, aligns on (index, columns), and sums compute_gof across all cells, scaled by per-key weights.
  • components=[...] or a custom eval_fn: standard ss.Calibration paths, unchanged.

calib_pars is a nested dict grouped by scope, with list leaves [best, low, high, step] (step optional). sc.flattendict collapses the scope tree to ss.Calibration’s flat dotted-key form::

calib_pars = dict(
    beta=[0.2, 0.1, 0.34, 0.02],
    network=dict(m_partners_casual=[0.5, 0.1, 0.9, 0.05]),
    hi5=dict(cin_fn=dict(k=[0.15, 0.1, 0.25, 0.02])),
)

Default build_fn is hpv.calibration.build_sim (route_pars); it accepts the flattened dotted keys and routes them.

Methods

Name Description
remove_db Skip cleanup when self.run_args.storage isn’t a string URL
shrink Return a lightweight sc.objdict with the top-n_results
worker Run a single worker.
remove_db
calibration.Calibration.remove_db()

Skip cleanup when self.run_args.storage isn’t a string URL (parent’s 'sqlite' in storage check errors on a Storage object). Each hpv.Calibration gets its own tempdir per instance, so there’s no cross-run pollution to worry about.

shrink
calibration.Calibration.shrink(n_results=100)

Return a lightweight sc.objdict with the top-n_results trials (by mismatch) + the metadata needed to replot / rebuild sims.

Use for committing calibration results to source control: a full 1000-5000-trial calib.obj is many MB (Optuna study, tempdir storage refs, per-trial state); the shrunken version drops all of that while remaining a drop-in for hpv.plot_calibration and utils.run_top_n.

Preserved attributes (accessed by plotting/rebuild code paths): - df : top-N rows of the trial DataFrame, sorted by mismatch ascending. - best_pars : the best-fit par dict. - eval_kw : the eval kwargs (holds data for plotting). - calib_pars : the Optuna spec dict. - build_fn : how to reconstruct a sim from a par set. - build_kw : build_fn kwargs (defaults to {}). - sim : base sim template.

worker
calibration.Calibration.worker()

Run a single worker.

Mirrors stisim.Calibration.worker: wraps study.optimize in a try/except so a single worker’s transient Optuna storage failure does not propagate through Optuna’s own error handler and trip its assert False, 'Should not reach.', taking the whole run down. Upstream ss.Calibration.worker calls study.optimize bare.

Functions

Name Description
compute_gof Goodness-of-fit between two arrays — normalized absolute error by default.
default_eval_fn Weighted sum of compute_gof across each column of data.
make_calib_sims Rerun the top-n trials from a hpv.Calibration in parallel.

compute_gof

calibration.compute_gof(
    actual,
    predicted,
    normalize=True,
    use_frac=False,
    use_squared=False,
    as_scalar='none',
    eps=1e-09,
    skestimator=None,
    estimator=None,
    **kwargs,
)

Goodness-of-fit between two arrays — normalized absolute error by default.

Mean squared error is normalize=False, use_squared=True, as_scalar='mean'.

Parameters

Name Type Description Default
actual array of observed (data) points. required
predicted array of model points; same shape as actual. required
normalize if True, divide errors by max(\|actual\|). True
use_frac if True, divide each error by max(actual, predicted) + eps instead of normalizing by the global max. False
use_squared if True, square the per-point errors. False
as_scalar collapse to a scalar via 'sum' / 'mean' / 'median'; 'none' returns the per-point array. 'none'
eps small constant guarding the use_frac denominator. 1e-09
skestimator scikit-learn metric name (e.g. 'mean_squared_error'). None
estimator user-supplied callable (actual, predicted, **kwargs). None
kwargs forwarded to the scikit-learn / custom estimator. {}

default_eval_fn

calibration.default_eval_fn(sim, data, weights=None, gof_kwargs=None)

Weighted sum of compute_gof across each column of data.

data is a wide DataFrame (index=‘t’, dot-scoped columns). For each column, sim-side values are extracted via _extract_columns, NaN cells in data are skipped, and per-column mismatches are weighted by weights.get(column_name, 1.0) before summing.

make_calib_sims

calibration.make_calib_sims(
    calib,
    n=50,
    sim_kwargs=None,
    analyzers=None,
    extract_fn=None,
    n_workers=None,
)

Rerun the top-n trials from a hpv.Calibration in parallel.

A hpv.Calibration stores per-trial mismatch + eval-column values, not per-trial sim results. To inspect any other result (e.g. the asr_cancer_incidence trajectory, or a custom by_age analyzer output), rerun the top-n best-fit trials with this helper and read sim.results / sim.analyzers from the returned sims.

Parameters

Name Type Description Default
calib hpv.Calibration (or the shrunk sc.objdict from calib.shrink() — both expose df, calib_pars, build_fn, build_kw, sim). required
n int number of best-mismatch trials to rerun; clamped to len(calib.df). 50
sim_kwargs dict overrides applied to sim.pars before build_fn (e.g. dict(stop=2045) to project past the calibration window). None
analyzers zero-arg callable returning a fresh list of Analyzer instances (recommended so each subprocess gets its own state), OR a list of instances (deep-copied per worker). Appended to any analyzers the Calibration already attached to the base sim. None
extract_fn callable if provided, worker returns extract_fn(sim) instead of the full sim. Strongly recommended — see the note below. None
n_workers int defaults to min(n, cpu_count()). None

Returns

Name Type Description
list of length min(n, len(calib.df)): run sims if extract_fn
is None, else the extract_fn outputs.

Note

Full sims are memory-heavy (each holds people arrays, edge tables, results, analyzers). For n=50 this can be many GB in the parent process after sc.parallelize pickles them back. Standard practice: pass an extract_fn that returns a small dict / DataFrame / array (use ss.Result.to_df or ss.Result.annualize for time-series results), then commit that extracted output rather than the raw sims.