What’s new
All notable changes to the codebase are documented in this file.
Version 1.7.0 (2026-09-29)
stisim.ai — new AI plugin
The stisim.ai plugin ships with an initial skill library for Claude Code, in three functional clusters. Enable via python -m stisim.ai install — see stisim/ai/README.md for the full inventory.
Analysis workflow — scaffold and structure a research project: - analysis-intake — grilling-style design-tree intake that turns a vague research idea into a provisional analysis specification. Adapts Matt Pocock’s grilling with a facts-vs-decisions split and blocked-on-evidence as a third branch state alongside settled and open. - analysis-selector — runs very early in intake, before any tool is assumed. Classifies the research question into one of six analytical objectives (description / association / structure / causal / forecasting / dynamic-mechanistic) and only routes to HIVsim if dynamic mechanistic transmission is actually required. Redirection away from HIVsim is a successful outcome. - project-memory — establishes a durable memory strategy at project intake (repo-based, session-indexing, agent-memory provider, or hybrid) so a multi-month project can survive session ends, machine switches, and collaborator handoffs. A chat session is working memory, not project memory. - session-close — handoff summariser at session end, feeding into whichever memory mechanism project-memory established.
Model authoring and calibration — invoked once the analysis is scoped: - model-primer — reference-layer architectural index of stisim, including a calibration-knobs.md reference for what stisim actually exposes as a knob vs. what should come from data vs. what belongs in an upstream PR. - model-writer — composes a new Sim from a scoped research question. - hiv-interventions — design-interview for the ART / testing / VMMC / PrEP set, with per-intervention data-source references. - network-data — DHS-focused sexual-network calibration input. - calibration-strategy — HIVsim / STIsim-specific what/why/whether-to-calibrate: parameter classification (data-informed / literature-fixed / country-specific-uncertain / behavioural / tuning / intervention-assumption), target-vs-knob distinction, ART/testing structural dependency, failure diagnosis. Delegates algorithm / sampler / likelihood / diagnostics to the calib:* plugin.
Software quality — cross-cutting, unified by the theme share your work rather than accumulate private forks of shared code or private caches of project knowledge (a pattern that becomes especially easy to fall into when code generation is cheap and code review is not): - extending-stisim — before subclassing / monkey-patching stisim, classify the change as (a) fix, (b) opt-in research knob, or (c) project-specific data preprocessing, and enforce PR-upstream for fixes, no-op default for knobs, cleanup of downstream artifacts once their upstream fix lands, and file-an-issue as the fallback. - editable-dep-hygiene — any edit to an editable pip install -e dependency gets committed and PR’d immediately; before a version bump or branch switch, the dep checkout is verified clean of unmerged local work. - comment-hygiene — shared-library docstrings and comments explain the software itself, not project-specific memory. Strips anti-patterns like “Experiment 5”, “the current task”, “we changed this because the user requested it”. - result-extraction — directs to ss.Result / ss.Results methods (annualize, resample, to_df) over hand-rolled df.groupby('year').mean() / .sum(), which silently mishandles the flow-vs-stock distinction and produces errors of n_timesteps_per_year that don’t scream at calibration ranges. - writing-tests — prefer a small number of scientifically meaningful tests over comprehensive enumeration. Every proposed test should have a one-sentence answer to “what meaningful bug would this catch?”; if the answer is “confirms a value we assigned still has that value”, don’t add it.
stisim.data
stisim.data.dedup_deaths— reconstructs a non-AIDS mortality counterfactual from all-cause mortality by log-linear interpolation between pre-epidemic and post-epidemic anchors. Prevents the double-counting that happens whenss.Deaths(all-cause data) andsti.HIV(HIV-specific mortality) both kill agents from the same underlying epidemic. Includesdeleted_fractiondiagnostic. Referenced fromstisim.ai’scalibration-knobs.md.- Percentile-to-distribution-parameter helpers — convert observed quantiles (e.g. “median 5 years, 95th percentile 12 years”) into parameters of the corresponding lognormal / gamma / beta distributions, so calibration priors and duration parameters can be set directly from published summary statistics.
Version 1.6.2 (2026-09-23)
vls_coverageis now a stock target, not a per-initiation flow. Previously, VLS status was set once at ART initiation and never revisited, so a time-varyingvls_coverageaffected only newly-enrolled agents.ART.vls_stock_correction()now runs each step, ranking on-ART agents by a persistentsuppression_propensityand correcting the suppressed set (per stratum) to the target — bidirectional, since suppression is reversible. Aligns ART with the stock-target semantics VMMC (1.5.9) and PrEP (1.5.11) already use.vls_coverage=Nonestill means 100% suppressed, so the default is untouched. (#597)- New
n_vls/p_vls/p_vls_given_artresults (aggregate, per sex, per age-sex band) on theart_coverageanalyzer.p_vls_given_artis the input (ART-conditional suppression);p_vlsis the cascade outcome over all PLHIV (GAM 1.3, the “third 95”). Also adds per-age-sex-bandnew_infections,n_susceptible, andincidence, so stratified incidence is computable from standard outputs. (#597) - Partner counts drifted under concurrency. In
MFNetwork,arr[uids] += 1with duplicated UIDs writes each duplicate only once (NumPy fancy-indexing gotcha), so agents starting or ending more than one edge of the same type on the same timestep had counters updated by 1 rather than by edge count. Fixed vianp.unique(return_counts=True). Baseline endpoints shift <1%. (#601) ti_infectedreturns as an alias forti_exposedonSEISdiseases (chlamydia, gonorrhea, trichomoniasis), so cross-disease code iterating oversim.diseasescan read time of acquisition uniformly without branching on the disease. Read-through only; no numerical change. (#599, #603)
Version 1.6.1 (2026-09-04)
- Age and sex stratification of disease results is now optional. Every
BaseSTIsubclass (Chlamydia,Gonorrhea,Trichomoniasis,Syphilis,HIV,BV) now acceptsage_binsandsex_keys; passingNonefor either skips the corresponding stratified results, leaving the whole-population ones untouched and numerically identical. Defaults are unchanged, so existing models are unaffected. Disabling both takes a gonorrhea module from 205 results to 29, which matters for large multi-sims and low-memory runs.sti.default_age_binsandsti.default_sex_keysare exported for building custom stratifications.SEISalso gainsage_rangeas a constructor argument, previously hardcoded to[15, 65]. (#592)
Version 1.6.0 (2026-08-28)
This version requires starsim>=3.6.0, and the bugs found while migrating to it change simulated epidemiology and reported results enough that chlamydia, gonorrhea, trichomoniasis and syphilis calibrations have all changed.
- Gonorrhea and trichomoniasis over-reported incidence by ~1.9x.
BaseSTI.update_resultscounted new infections asti_infected == ti, butSEISwritesti_infectedtwice:set_exposure()schedules the exposed->infectious time, thenstep_state()overwrites it with the realized one. Withdur_exp = 0(the default for both diseases) those fall on different timesteps, so each infection was counted twice. Incidence is now counted at acquisition viati_exposed, which is written exactly once and never rewritten, so the test is correct by construction;ti_exposedmoved up toBaseSTI, andti_infectedagain means only “became infectious”. Against a direct count ofset_prognoses()calls, the two diseases reported 1.93x and 1.86x the true infections and now report 1.00x. All incidence-derived results were affected; transmission dynamics were not. Regression: both were calibrated to roughly double the true incidence. Chlamydia’s totals are unchanged but shift one timestep earlier. - SEIS
prevalenceandn_infectedexcluded the latent period.SEISusedinfectedfor the I compartment alone, son_infected,prevalenceand every age/sex-stratified variant silently dropped exposed agents. Following Starsim v3.6.0’sss.SEIR,infectiousis now the literal I compartment andinfectedis derived as E plus I;ti_infectedis replaced byti_infectious(time of becoming infectious), withti_exposedremaining the time of acquisition. Transmission still depends oninfectiousalone, so no dynamics change: over a 5000-agent/20-year sim the infection counts are identical, but reported prevalence rises 7.0% (ct), 14.6% (ng) and 7.6% (tv) relative. A newn_infectiousresult gives the transmitting compartment on its own. Migration:disease.ti_infectedonct/ng/tvno longer exists (useti_infectiousorti_exposed), anddisease.infectednow includes exposed agents;SEIS.treatablewas already E plus I, so it is now justinfectedand the override is gone.Chlamydia’s bacterial-load update and the HIV coinfection connectors were switched toinfectious, preserving their previous behaviour.ANCTestkeys its detection offinfected, so if it is pointed at an SEIS disease (disease_names=['ct']; the auto-detected defaults are HIV and syphilis) it now detects exposed agents too, matching thetreatablepathway the other test interventions use. - The syphilis latent transmissibility half-life had no effect.
set_latent_trans()computednp.exp(-np.log(2)/pars.rel_trans_latent_half_life * dur_latent); under starsim < 3.6.0 the ufunc discarded the rate’s denominator, so every half-life gave identical decay and latentrel_transhalved every timestep regardless. The half-life is now converted to timesteps withto_dt(), making decay 12x slower at monthlydt. Regression: over a 5000-agent/20-year sim, endpoint prevalence rose from 0.142 to 0.202 and cumulative infections from 493 to 674 (mean of 3 seeds); syphilis calibrations need redoing. - Trichomoniasis “persistent” female infections lasted 8.3 years instead of 100.
set_durationaddedpars.dur_persist(ss.years(100)) to a timestep index without converting to the module’sdt, so the bare magnitude was used (100 months at monthlydt). Realized persistence is now 1200 timesteps rather than 100. Regression: female prevalence rises substantially in runs longer than ~8 years. - Syphilis stillbirths/miscarriages could be misclassified as live births.
Syphilis.set_congenitalscheduled miscarriage/stillbirth outcomes at the same timestep asti_delivery, butPregnancy.process_prenatal_deathsonly classifies a death as a fetal loss when age<0, which flips to 0 exactly atti_delivery; these babies were counted as live births by the Pregnancy module while syph’s ownnew_stillborns/new_congenital_deathsstill counted them as fetal losses. Fetal-loss outcomes now fire one timestep beforeti_delivery, matching the equivalent fix already applied to starsim’s generic congenital framework in v3.6.0. Also fixedti_miscarriage, which was assigned an outcome but never fired a death or was counted in any result — invisible under default parameters since miscarriage probability is 0 in allbirth_outcomesdistributions, but live for any custom parameterization with nonzero miscarriage probability. Adds anew_miscarriagesresult, now included innew_congenital_deaths. (#588, #590)
Version 1.5.11 (2026-08-24)
Breaking changes
- On-ART mortality is now nonzero by default. Previously, agents on ART had zero HIV-specific mortality; on-ART death now follows an age/sex/CD4/adherence-adjusted rate anchored to (and always ≤) the off-ART CD4-based hazard. Models calibrated against 1.5.10 or earlier will need recalibration. (#556, #561)
- Circumcision state moved from
VMMCtoHIV.VMMC.circumcised/VMMC.pars.eff_circno longer exist — useHIV.circumcise()/HIVPars.eff_circ/eff_circ_traditional. AnyVMMCsubclass reading those attributes directly will break. (#576) HIV.never_artrenamed toart_naive, and ART status is now one of four mutually-exclusive states:art_naive,on_effective_art,on_nonsuppressive_art,art_discontinued. (#556)
Diseases
- HIV: new ART adherence model.
p_effective_art(default 1.0) determines what fraction of newly-initiated agents achieve viral suppression (on_effective_art) vs. non-suppressive ART (on_nonsuppressive_art); adherence status now affects both mortality (rel_art_mortality_effective/unsupp_m/unsupp_f,art_death_age) and transmission (effective_art_efficacy,nonsupp_art_efficacy, ramping in overtime_to_art_efficacy). On-ART mortality is anchored to the off-ART CD4-based hazard so it can never exceed it at the same CD4 count — now enforced by an assertion at init rather than a docstring note. (#556, #561, #583) - HIV:
rel_death_f(the female mortality multiplier) now applies off-ART as well as on-ART; previously it only applied on ART, with no documented reason for the asymmetry. (#583) - HIV: circumcision (
circumcised/circ_traditional/eff_circ/eff_circ_traditional) moved fromVMMContoHIVso multiple circumcision pathways can share the same state.VMMCgains an independent traditional (non-program) circumcision pathway (traditional_prob,traditional_age) alongside program VMMC — both write through the sameHIV.circumcise()sink, so someone circumcised by either pathway is automatically excluded from the other. (#576) - Syphilis: fix a reactivation-from-latent bug where the previous fix for “reactivation never happens” caused the opposite failure — every latent agent flipped back to secondary the timestep after entering latency, regardless of the reactivation draw, collapsing latency to a ~1-timestep blip and effectively disabling tertiary progression. (#569)
- Removed the dead
post_artstate: it was declared and cleared on infection-clear, but nothing ever set itTrue(superseded byart_discontinued, which everything else already reads);hiv_n_post_artwas silently always 0.
Interventions
ARTgainsvls_coverage: the fraction of newly-initiated agents achieving viral suppression, accepting the same scalar / time-varying / age-sex-stratified DataFrame formats ascoverage(replaces the scalar-onlyp_effective_artkwarg). (#577)Prep(PrEP) reimplemented as a stock-target intervention parameterized byprep_eff/prep_dur/prep_adh. State (prep_naive/on_prep/prep_discontinued/etc.) now lives onHIV, so multiplePrepinstances (e.g. oral + long-acting) share enrollment history and are mutually exclusive. (#581)
Bug fixes
vls_coverage: a stratum missing from a stratified DataFrame silently defaulted to 0% suppression instead of the documented 100%; scalar/single-column inputs outside [0, 1] weren’t validated; the legacyp_effective_artkwarg could silently overridevls_coveragewith no warning. (#577)Prep: age-only stratified coverage (noGendercolumn) defaulted to males only, silently enrolling nobody for Prep’s typical (female) target populations. (#581)- The ART transmission-efficacy ramp divided by a raw month count instead of converting to timesteps via
dt— only correct at the default monthly resolution. At otherdt(e.g. weekly), the ramp could overshoot and driverel_transnegative. (#583)
Documentation
- Rewrote the HIV user guide’s “On-ART mortality” and “On-ART transmission” sections for the new adherence model, including a worked derivation of
nonsupp_art_efficacy(a Quinn/Rakai viral-load hazard-ratio calculation) and updatingeffective_art_efficacyto 0.99 (“U=U” — PARTNER/PARTNER2/Opposites Attract found zero linked transmissions from virally suppressed partners across thousands of couple-years). (#582, #583) - Document PrEP’s new parameterization and multi-product usage in the interventions user guide. (#581)
Tests
- Add regression tests for the on-ART mortality invariant, the efficacy-ramp
dtbug,rel_death_fapplying off-ART,vls_coverage’s missing-stratum default and value validation, and Prep’s age-only stratified coverage. (#577, #581, #583)
Version 1.5.10 (2026-07-24)
Bug fixes
HIV.stop_artnow actually setspost_art=True. Thepost_artBoolStatewas defined but only ever cleared toFalseinclear_infection, so downstream cascade-stage attribution (e.g. transmission-source-by-ART-status analyzers) couldn’t distinguish “never on ART” from “on ART, now interrupted”.start_artalso clearspost_artwhen re-enrolling a previously-interrupted agent. (#544, #541)STITest/SyphTestno longer crash with the defaulteligibility=None.get_testerstreatedNoneas a non-callable UID list instead of falling back tocheck_eligibility()(which already handlesNone→ everyone), so any test intervention without an expliciteligibilityfunction crashed on its first step. (#537, #562)- Syphilis: fix
new_nnds/new_stillborns(and derivednew_congenital_deaths/cum_congenital_deaths) always reading 0 —step_stateclearedti_nnd/ti_stillbornto NaN beforeupdate_resultscould match them against the current timestep. Counts are now stashed before clearing, mirroring the existing_new_congenital_countpattern. (#560) ANCTest: fixed a crash under current starsim (ss.dur(1, 'month')→'months') and a bug whereactive_diseaseswas resolved aftersuper().init_pre(sim)had already calledinit_results(), so per-disease results (n_<disease>_positive) were never created.ANCTesthad zero test coverage before this. (#324, #546)
Diseases
- Syphilis: rebalance the
mat_active(primary/secondary maternal infection) birth-outcome distribution — miscarriage probability raised (0.10→0.25 early, 0.20→0.25 late) and neonatal death lowered (0.25→0.05) — to better match Gomez 2013 / Cooper 2016, which put most adverse outcomes earlier in pregnancy rather than post-partum. (#543, #541) - Syphilis: expose per-fetus
mtct_from_{mat_active,early,late}flags recording the mother’s disease stage at the moment of mother-to-child transmission, so downstream analyzers can bucket congenital outcomes by maternal stage without reconstructing mother/fetus linkage. (#548)
Demographics
sti.Migrationgainsrel_migration(default 1), analogous toss.Deaths.rel_death: scales both immigration and emigration each timestep. (#542, #545)
Documentation
- Fill in the “Antenatal and infant screening (PMTCT)” stub in the interventions user guide (ANC → ART → newborn-test cascade,
maternal_care_scalecross-reference) and add a new gallery example,pmtct_scenario.qmd, comparing MTCT infections and pregnant-women diagnosis rates with/without ANC testing. (#324, #546) - Document the
risk_group/sex/sw-stratified DataFrame format accepted bySyphTest.test_prob_data. (#537, #562)
Tests
- Add
tests/test_anc_test.py(smoke test, immediate-ART-scheduling invariant, newborn-test scheduling) andtests/test_sti_interventions.py(default-eligibility regression, stratifiedtest_prob_data) — both interventions had zero coverage before this. - Add
tests/test_demographics.pycoveringrel_migration.
Version 1.5.9 (2026-07-13)
Bug fixes
VMMCnow hits its coverage as a prevalence (stock) target, not a per-step hazard. The 1.5.6 rewrite computed the per-timestep target over the uncircumcised pool and only in aggregate, socoveragebehaved as a per-step uptake rate on the remaining-uncircumcised men: realised circumcision coverage ratcheted toward ~100% regardless of the target, and the age/sex stratification in the input data was ignored (all ages converged to the same level).VMMC.step()now tops up tocoverage × (all eligible males)per(age, sex)stratum each step — the denominator includes already-circumcised men, matching cross-sectional survey definitions — and never removes (circumcision is irreversible). Realised coverage converges to the target and preserves the input age gradient. Models calibrated against 1.5.6–1.5.8 VMMC behaviour will need recalibration. (#535)HIV.set_prognosesnow callssuper(), fixingss.infection_log()for HIV. HIV overrodeDisease.set_prognoses()entirely for its natural-history setup and never calledsuper(), so the infection log’s append hook never fired for HIV specifically (other diseases such as SEIS-based Gonorrhea were unaffected). Infection log entries for a 1000-agent HIV sim went from 0 to 259 after the fix, restoring source/target transmission tracking (e.g. attributing onward transmission to a source subgroup). (#536)GonorrheaTreatment.set_treat_effnow defends against NaNrel_treat: a small fraction of agents allocated but inactive atsim.initkeep an uninitialized slot and read back as NaN when reactivated, which previously propagated totreat_effand made those agents always fail treatment. NaN entries are now replaced with the declared default (1) before computingtreat_eff. (#528)art_coverageanalyzer plotting no longer produces two identical plots. (#530)
Tests
- Add
test_vmmc_hits_target, asserting realised male circumcision prevalence converges to the coverage target. The existingtest_vmmc_specsonly checked that some circumcisions occurred, which is why the overshoot regression went undetected. - Regenerate
baseline.yaml/benchmark.yamlfor starsim 3.5.1 (CRN hash-based random number generation changed the exact values of agent-indexed draws; results are statistically equivalent per starsim’s changelog).test_shorter_swnow usesrand_seed=2—rand_seed=1drew zero FSW-sourced HIV transmissions in both arms from the small FSW pool (~9/2000 agents) under the new CRN scheme, making the comparison degenerate.
Diseases
- Syphilis: clear
early/latesub-flags when a mother exits the latent state (reactivation to secondary, or progression to tertiary). Previously these BoolStates persisted, soset_congenital’s outcome loopfor state in ['mat_active', 'early', 'late']would overwrite the correctmat_activedraw with the (now-stale)early/latetable draw — biasing MTCT outcomes for reactivating mothers towardnormal(the late table is 80% normal vs mat_active’s 25%). Empirical impact in a hot-seed ANC screening sim: untreated-secondary MTCTs read 67% normal instead of the expected 25%. (#538)
Diseases
- Syphilis: fix
new_nndsandnew_stillborns(and the derivednew_congenital_deaths/cum_congenital_deaths) always reading 0.step_stateclearedti_nnd/ti_stillborntonanwhen scheduling the deaths, so the== ticheck inupdate_results(which runs after) never matched. Mirror the existing_new_congenital_countpattern: stash the counts before clearing and read from the stash inupdate_results.
Version 1.5.8 (2026-06-25)
Logistics
- Add a
stisim.logisticspackage for supply-constrained interventions:Product(cost, delivery mode, time-varying efficacy),Supply/Supplies(quantities and accrued cost),ProductCategory/DeliveryModeenums, and the abstractSuppliedInterventionbase that distributes a product across eligible agents subject to available supply. (#492)
Interventions
PartnerNotification: optional edge-type / partner-sex stratification.step()now walks current-channel edges preserving edge-type info and exposescurrent_partner_edges({partner_uid: [edge_type_int, ...]}) to callables onp_notify_current.p/p_attends_current.p. Newsti.pn_rates(rates)helper builds such a callable from a dict spec — either{'stable': 0.20, 'casual': 0.10}or per-partner-sex{'stable': {'f': 0.80, 'm': 0.50}, ...}. Per-edge probabilities for multiply-reached partners are summed and capped at 1. Scalarp_notify_current/p_attends_currentBernoullis are unchanged (backward compatible). (#505)
Documentation
- New user-guide pages for analyzers (
docs/user_guide/analyzers.md) and care-seeking (docs/user_guide/care_seeking.md). - Expand the interventions user guide with syndromic management, antenatal/infant (PMTCT) screening, partner notification, and pregnancy-driven risk reduction.
- List all gallery examples in the sidebar; fix syphilis API drift that broke the docs build.
Tests
- Add
pn_ratesstratification tests. - Regenerate
baseline.yaml/benchmark.yamlfor starsim 3.4.0 (n_female).
Version 1.5.7 (2026-06-15)
Diseases
- Syphilis: add treponemal (
trep) and non-treponemal (nontrep) serology states with seroconversion/reversion dynamics (window period, ~80% lifelong trep persistence, non-trep titre decline + post-treatment reversion). Newtrep_prevalence/nontrep_prevalenceresults (overall, by sex, by age×sex) plussexually_transmissible/symptomatic/primarystage prevalences. Consolidated the duplicateactiveproperty intosymptomatic(removedactive_prevalenceand the*_prevalence_15_64results). Prevalence denominator age band is now configurable viaage_range, exposed as aBaseSTIconstructor argument. (#500)
Networks
- Add
fsw_mf_conc_multonStructuredSexualto scale FSW non-sex-work concurrency (values <1 = fewer non-commercial partners).MFNetwork.set_concurrencyis now negative-binomial-aware: it reparameterizes the target mean correctly instead of silently no-opping whenconcurrency_distisss.nbinom. (#500) - Fix the three MSM networks (
AgeMatchedMSM,AgeApproxMSM,MSMScaleFreeNetwork): all now honour a participation filter and no longer crash at init. The participation parameter is renamedmsm_share→p_msm. (#502)
Interventions
SyndromicManagementandSymptomaticTestingnow take disease names (e.g.diseases=['ng', 'ct']) rather than module objects, resolved againstsim.diseasesat init. Objects passed at construction were deep-copied into orphans the sim never stepped, giving stale state and dropped result-writes. (#503)
Analyzers
- Add
sti.PartnershipFormationAnalyzer. Networks now record the timestep each relationship ends (Noneif still active at sim end), enabling efficient post-run partnership analysis from a shared results store. (#504)
Results
- Remove the unimplemented
partners_12/n_partners_12mreferences. (#473, #507)
Version 1.5.6 (2026-06-02)
Networks
- Breaking — default behaviour change. Split
stisim/networks.pyinto astisim/networks/sub-package (base.py,mf.py,fsw.py,msm.py,layered_networks.py,matchers.py). Add amatch_methodstring parameter onMFNetwork, dispatched through aMATCHERSregistry of pair-formation algorithms (accepts string or callable). The previous default (sort_bisect) collapsed the M-F age gap to ~0 because it didn’t honourage_diff_pars. Eight matchers ship in the registry. Removesstisim/pfa_variants.pyand the sevenMFNetwork_*subclasses. (#472) - New
closest_age_tapered_seekingmatcher, now the default. Orders females bydesired_age = age + normal(target_gap, stddev)(drawn each step for under-partnered females) and males by age; uses batch-binary-search to find each female’s closest available match, with a taper that reduces seeking for older females to bring mean age gaps into data alignment. Realised mean age gap is now within <5% of target (was negatively biased and growing); target stddev preserved. Downstream calibrated models will need recalibration: HIV prevalence shifts ~18% in Zimbabwe and ~59% in Eswatini at default settings. (#477) - Add
sti.MSMScaleFreeNetwork: a preferential-attachment MSM sexual network with continuous-time formation/Markovian deletion (Whittles-2019 S2 kernel). Restricted to post-debut males in this first port; subclass hooks (_get_pool,_mix_node_arrays,_mix_weights_row) let you plug in age-/risk-/λ-weighted variants. Documented as not branching-stable under starsim CRN (global weighted-categorical sampling over a pair catalog). Ported fromstarsim_x/BespokeNet, contributed by Stephen Attwood (Oxford). (#488)
Interventions
- Add
sti.PregnancyRiskReduction: during pregnancy, optionally clears FSW status (fsw_redux), drops high-risk-group membership todefault_risk_group(high_risk_redux), and/or zeros concurrency (concurrency_redux). Ported fromsyph_dx_zim. (#482, #484)
Documentation
- New ART state diagram in the docs illustrating how ART intersects the HIV mortality cascade. (#471)
Tests
- Regenerate
tests/baseline.yamlfor the new default matcher. - Add
tests/update_baselinescript (mirroring hpvsim’s pattern) to regenerate bothbaseline.yamlandbenchmark.yamlin one command.
Version 1.5.5 (2026-05-15)
Sim / parameter routing
- Add
sti.route_parsutility (instisim/utils.py) that auto-discovers par categories (sim, sti, nw, dem) from the par-class registry and routes flat kwargs accordingly. Cross-category clashes broadcast to all matches with a printed note; unknown keys raise whenstrict=True.sti.Sim.separate_parsnow delegates to it and shrinks from ~95 to ~30 lines. (#451, #452) - Auto-add coinfection connectors when
diseases=[...]is supplied without an explicitconnectors=. Lookup triessti.<d1>_<d2>thensti.<d2>_<d1>; pairs with no registered class are silently skipped. Replaces the previousNotImplementedErrorstub. (#442, #453) - Route flat connector pars (e.g.
rel_sus_hiv_syph=3.5) through to the matching connector instance. (#442, #453)
Interventions
- Replace
PartnerNotificationwith aPriorPartners-based implementation. Two channels (current sexual network, optional prior-partner recall network) each with separate notification × attendance probabilities. Tracks notifications and attendance per-channel. The previous transmission-graph-based version is removed. New gallery exampledocs/examples/partner_notification.qmd. (#457) - Add
dur_dx2txparameter toHIVTest(defaultss.constant(0)) for the delay between HIV diagnosis and ART start.HIVTestnow scheduleshiv.ti_art = ti + delayon positive results;ARTbecomes passive and starts agents whoseti_art == ti & ~on_art.ANCTestand initially-diagnosed cases schedule with no delay (backward compatible). (#398, #464) - Fix
ARTstratified coverage to allocate per-(age, sex) stratum independently. Previously, stratified coverage data was parsed correctly but then collapsed to a single aggregate target before correction, washing out the age/sex differentials in the input. Newcompute_stratum_targetshelper ininterventions/utils.py. (#463)
HIV
new_agents_on_artresult now gates onon_art(sinceti_artmay also hold a scheduled future start). (#464)
Documentation
- New gallery example
docs/examples/vmmc_costing.qmd: attach unit costs tosim.results, discount, and compute an ICER for VMMC vs no-VMMC, framed as a quick STIsim analog of Bansi-Matharu et al., Lancet Global Health 2023;11(2):e244–e255. (#455, #465)
Version 1.5.4 (2026-05-04)
Networks
- Split
StructuredSexualinto reusableMFNetwork(heterosexual) andSWNetwork(sex-work) networks built on a newBaseNetwork.StructuredSexualis preserved as the combined default. (#307, #445) - Window sex-work participation by per-agent
age_sw_start+dur_swinstead of lifetime;fsw/client/age_sw_stopare now derived properties. (#307, #445) - Add
condom_smoothnessparameter forwarded tosc.smoothinterpfor time-varying condom data. (#445) - Replace shared
debut/debut_pars_f/debut_pars_mwith per-sexdebut_f/debut_mdistributions; supports scalar shortcuts, fullss.Distinstances, and callables. (#397, #420, #423)
Interventions
- Add
SyndromicManagementto core stisim, consolidating duplicated implementations fromanc_sti_screeningandstisim_vddx_zim.cervical_diseasesparameter generalises the cervical-symptom check. (#369, #443) - Add
ANCTestfor single-visit ANC screening (auto-detects HIV + syphilis; per-disease sensitivity; routes to treatment maps). Raises if disease names are mistyped. (#370, #444) - Add
InfantHIVTestfor newborn/infant testing scheduled byANCTestor byHIVTestvia the maternal network. (#370, #444) - Decouple
Prepeligibility from clinical filters: user-suppliedeligibility=callables now express targeting only;~hiv.infectedand~on_prepare applied unconditionally. (#330, #441) - Refactor
Preplegacyyears=handling: pop from kwargs beforeupdate_parsinstead of reading back fromself.pars. (#444)
HIV
- Update acute HIV defaults to Bellan 2015 central estimates:
rel_trans_acute=N(5.3, 0.5),dur_acute=LogNorm(1.7 mo, 1 mo). EHM drops from 15 to ~7.3. (#396, #426) - Add
rel_sus_age: list of(age_lo, age_hi, sex, multiplier)tuples for age/sex-stratified susceptibility. DefaultNonepreserves existing behaviour. (#395, #427) - Remove
BaseSTI.infect()override; inherits parentss.Infection.infect()for free mixing-pool compatibility. (#412, #421, #422)
Sim / parameter routing
process_networks/process_stis/process_demographicsnow raise on collisions instead of silently winning;separate_parsraises onpars ∩ kwargscollisions in bothsti.Simandhivsim.Sim. (#423)- Improve “ambiguous parameter” error messages to name the offending par. (#423)
hivsim.Simpulls network-level kwargs from user input into the defaultStructuredSexualinstance. (#423)- Remove
BreastfeedingNetfromsti.Simdefault networks; only relevant for diseases with postnatal transmission (HIV, syphilis). (#406, #444)
Bug fixes
- Fix birth data key mismatches in
process_demographicswhenuse_pregnancy=False:'births'→'cbr'/'birth'keys, file path, and metadata columns. (#411, #428)
Documentation
- Switch docs build from MkDocs to Quarto; convert tutorials from
.ipynbto.qmd. (#439) - Documentation audit and uplift via IDM standards plugin (folder READMEs, API reference, navigation polish). (#435)
- Add new publication entry: Stuart et al., “Estimating the value of novel tests for active syphilis in Zimbabwe…” forthcoming in STD. (#447, #448)
Tests
- Add
tests/devtests/devtest_sw_networks.pywith 10 integration tests covering MF only, SW only, MF+SW modular,StructuredSexual, implicit-SW thresholds, windowed entry/exit, and allsti.Sim/hivsim.Sim/hivsim.democreation patterns. (#445) - Add
test_shorter_sw: shorter SW participation window → fewer FSW-attributable transmissions. (#445) - Remove fragile
len(sim.diseases/networks/interventions)count assertions intest_sim. (#444) - Remove fragile
test_doubling_hiv_sexual_beta. (#445) - Regenerate
baseline.yamlto reflect default behaviour changes (windowed SW, BreastfeedingNet removal, Bellan acute pars). (#426, #444, #445)
Version 1.5.3 (2026-04-17)
Interventions
- Extract shared coverage-targeting logic into
interventions/utils.py:parse_coverage,age_sex_mask,compute_coverage_target(#328) - Replace
_parse_age_binwith starsim’sss.parse_age_rangeandss.apply_age_range(#326) - Expose
smoothnessparameter for coverage interpolation (default 0 = linear) (#327) - Support mixed n/p coverage format: historical absolute numbers transitioning to projected proportions within a single DataFrame or dict (#383)
- Remove deprecated
coverage_dataandfuture_coverageparameters; usecoverageinstead (#383) - Refactor PrEP to use
parse_coverage(#327) - Add
pmtct_efficacyparameter to ART (default 0.96), replacing hardcodedrel_sus=0for prenatal MTCT protection (#404) - Extend PMTCT protection to breastfed infants via BreastfeedingNet (#407)
- Add ANC HIV testing pattern using
HIVTestwith pregnancy eligibility (#322)
HIV / MTCT
- Add
new_infections_mtct,new_infections_sex,new_infections_prenatal,new_infections_postnatalresults to BaseSTI (#325) - Implement breastfeeding HIV transmission via BreastfeedingNet with
beta_breastfeedparameter (#323) - Add
p_diagnosed_pregnantresult tracking ANC testing coverage (#322) - Include BreastfeedingNet in
hivsim.Simdefaults (conditional on Pregnancy module)
Bug fixes
- Fix chlamydia
eff_condomdefault: updated to 0.4 based on linked citation (#391) - Fix
hivsim.Sim: conditionally add BreastfeedingNet only when Pregnancy is present (#413) - Fix disease count assertion in
test_sim_creation(#413)
Documentation
- Add examples gallery with first entry: Modeling ART interruptions (#394)
- Add PMTCT section to HIV user guide (ANC testing, prenatal/postnatal protection)
- Document HIV transmission route results (MTCT, prenatal, postnatal)
- Add docstrings across interventions, diseases, networks, and analyzers (#409)
Tests
- Add
test_pmtct: 8-sim combinatorial test for ANC testing, PMTCT efficacy, and breastfeeding duration - Add
test_mtct: prenatal + postnatal MTCT consistency checks
Version 1.5.2 (2026-04-06)
Bug fixes
- Fix
default_build_fnto applyrand_seedwhenreseed=True, so each calibration trial uses its own seed - Fix timepar handling in data loaders (remove unnecessary
TimeParcatch) - Fix stray plot appearing when running pytest (
test_zimbabwecalledhivsim.demowithplot=True)
Improvements
- Add
HIV.plot()with curated 6-panel view (new infections, deaths, prevalence, prevalence 15-49, diagnoses, proportion on ART) - Pin
starsim>=3.3.1andsciris>=3.2.9
Tests
- Add baseline regression test and benchmark test using Zimbabwe HIV example
- Refactor test suite with clear file responsibilities:
test_sim.py(constructor/routing),test_hiv.py(HIV scientific validation),test_stis.py(non-HIV STI validation),test_networks.py(network dynamics) - Add
hivsim.Simconstructor tests (defaults, parameter routing, custom modules) - Add HIV sensitivity tests:
test_par_rangeschecksbeta_m2f,init_prev,dur_falling, andart_efficacyagainst bothcum_infectionsandcum_deaths - Add
test_prevalence_by_sexverifying female > male HIV prevalence under defaults - Add MSM network test
- Rename
test_diseases.pytotest_stis.py,test_hiv_natural_history_verification.pytotest_hiv.py - Merge
test_examples.pyintotest_sim.py
Documentation
- Fix calibration tutorial to use disease instances instead of strings
- Add note about
scikit-learnrequirement forplot_param_importances
Version 1.5.1 (2026-03-27)
Bug fixes
- Fix VMMC eligibility: pass
eligibilityto parent class and usecheck_eligibility()instep(), so user-supplied eligibility functions are no longer silently ignored (#353) - Fix starsim compatibility: convert
ss.years/ss.datetointinget_age_distribution()for newer starsim__len__behavior - Fix rand_seed placement in beta transmission test (#354)
Parameter handling
- Remove parameters from
SimParsthat duplicate starsim’sSim(label,verbose,total_pop,pop_scale,birth_rate,death_rate,use_aging) (#351) - Add
get_valid_pars()to detect ambiguous parameters across namespaces (#351) - Improve error messages for unrecognized parameters with suggestions via
sc.suggest()(#351) - Handle
location/demographicsconflict inremap_pars()(#351) mergepars()now returnssc.objdictfor dot-notation access (#351)
Other changes
- Delete legacy
setup.py(usepyproject.toml) - Relax
sc.requireto only check starsim, withdie=False - Comment out unused
dtparameter in Migration
Tests
- Add VMMC tests: reduces male infections, male-only targeting, eligibility targeting (#251, #355)
- Add ART dropout tests: CD4 decline and rel_trans increase after stopping ART (#244)
- Add exponential early-phase epidemic growth test (#260)
- Add increased testing speeds diagnosis test (#253)
- Add no-HIV-without-seeding test (#271)
Documentation
- Fix
sti.Pregnancy→ss.Pregnancyin Getting Started tutorial (#343) - Fix tutorials and user guide nav nesting
- Update syphilis docs: exposed stage, treatment/reinfection, key properties
- Fix HIV
dur_on_artdefault in docs
Version 1.5.0 (2026-03-13)
Interventions
- Refactor ART and VMMC coverage input: accept scalar, dict, or DataFrame formats with flexible column names (#126)
- Add
art_coverageanalyzer for tracking ART coverage by age and sex - Simplify
art_initiation: accept plain number, dropinit_probbackward compat - Guard pregnancy/maternalnet access in HIV and syphilis interventions so they work without a pregnancy module (#319)
- Document HIV intervention pipeline and ordering (#314)
HIV
- Add
aidsproperty to HIV module (cd4 < 200) - Fix
dur_on_artbeing silently scaled bydur_on_art_trend: default is nowNone(#336) - Add time-varying ART duration and wider care-seeking distribution
- Add custom module support to Sim constructor (#318)
Syphilis dynamics
- Fix
Syphilis.infect(): userel_trans=1for maternal transmission, stage-specific for sexual - Fix
NewbornTreatmentto detect MTC-infected babies (susceptible=False, congenital not yet fired) - Fix congenital syphilis over-counting: clear
ti_*after events fire, mark babies non-susceptible after MTC - Add
step_dieto Syphilis to clear states on death - Add
n_infectionscounter andnew_reinfectionsresult
Calibration API
- New dot-notation parameter routing:
'hiv.beta_m2f'automatically finds and sets the right module parameter viasim.get_module()(requires Starsim 3.2.0+) - Support nested parameter format:
dict(hiv=dict(beta_m2f=dict(low=..., high=...)))alongside flat{'hiv.beta_m2f': dict(...)} - Add
flatten_calib_pars()to normalize between nested and flat formats - Add
set_sim_pars(sim, pars)for setting calibrated parameters on any sim (pre- or post-init) - Add
Calibration.get_pars(n)to extract top-N parameter sets as flat dicts - Add
make_calib_sims()for creating and running sims from calibrated parameters, with filtering (check_fn) and seed replication (seeds_per_par) - Add
Calibration.save()method for shrink/save workflow (replaces manual shrink + saveobj boilerplate) - No custom
build_fnneeded –default_build_fnhandles all module types automatically
Documentation
- Add calibration tutorial with ABC philosophy, custom analyzer fitting, production workflow
- Update co-transmission tutorial: HIV-syphilis example with epidemiological explanation of connector effects
- Add custom results section to results tutorial
- Cross-link calibration and results tutorials
Tests
- Add HIV natural history verification test suite: CD4 decline, transmission doubling, MTCT, AIDS property (#178, #226, #227, #228, #237, #238, #295)
- Add HIV intervention tests: ART coverage formats, duration, effects, parameter sensitivity
- Add
testlib.pywith sharedbuild_testing_sim()helper
Version 1.4.9 (2026-02-24)
- Add
default_build_fnfor calibration: routes parameters by prefix (hiv_*,syph_*,nw_*) to diseases and networks automatically, removing need for custombuild_fn sti.Calibrationnow usesdefault_build_fnwhen nobuild_fnis provided- Fix
make_df()time column to use years fromtimevecinstead of integer index - Fix
parse_study()to guardsim_resultsreordering whensave_results=False - Fix calibration data column names to use dot notation (
hiv.prevalence) - Standardize import conventions:
import stisim as sti,import hivsim(no alias)
Version 1.4.8 (2026-02-23)
- Add
hivsim_examplespackage withsimpleandzimbabwepre-configured examples - Add
hs.demo()function for quickly running example HIV simulations (e.g.hs.demo('zimbabwe')) - Add
Sim.plot()override that auto-selects curated HIV result keys when HIV is present - Add
dataparameter tosti.Simfor passing comparison data (e.g. UNAIDS estimates) to starsim’s plot overlay - Fix
process_demographics()to usetotal_popfrom sim_pars when set, and correctly scale age data (values in thousands) - Fix
separate_pars()so kwargs overridesim_parsdefaults (e.g.stop=1995overridessim_pars=dict(stop=2025)) - Fix
get_age_distribution()to handle CSV files without ayearcolumn
Version 1.4.7 (2026-02-20)
- Fix bugs in coinfection analyzer: age limit typo, variable name errors for male results
- Remove STIsim Pregnancy module, now superseded by the Starsim Pregnancy module
- Add exposed/incubation period for syphilis (
dur_exposed), and allow maternal transmission during this stage - Rename syphilis birth outcome key
activetomat_activeto reflect inclusion of exposed stage - Add
.vscode/and*.code-workspaceto.gitignore
Version 1.4.6 (2026-02-20)
- Add sensible
beta_m2fdefaults for all diseases (NG: 0.06, CT: 0.06, TV: 0.1, HIV: 0.05, syphilis: 0.1) - Add
rel_trans_hiv_ngparameter to the HIV-NG connector - Fix connector handling in
Sim.init()when a single connector is passed - Set
auto_plot=Falseon subpopulation, care-seeking, and detail results sosim.plot()shows only high-level results - Add tutorials: intro (gonorrhea), co-transmission, results/plotting, and interventions
- Add user guide pages for interventions (testing, treatment, ART, VMMC, PrEP)
- Update disease docs with
beta_m2fdefaults and per-act description - Pre-build font cache in docs CI workflow
Version 1.4.5 (2026-02-20)
- Fix BV trimester KeyError during initialization
- Add documentation: intro tutorial, disease reference pages, and network guide
- Configure mkdocs to execute notebooks and add user guide structure
- Update README with Python/R install instructions and example repos
- Move devtests to
tests/devtests/and renametest_hivtodevtest_hiv
Version 1.4.3 (2025-12-08)
- Patch to add super calls to init_results for analyzers
- GitHub info: PR 160
Version 1.4.2 (2025-11-28)
- Patch to ensure that products have different names
- Add
ti_exposedattribute to HIV module - GitHub info: PR 150
Version 1.4 (2025-08-12)
- Add location arg and Sim class
- Update to work with Starsim v3.
- GitHub info: PR 148
Version 1.3 (2025-06-27)
- Fixes to the pair-matching algorithm within the sexual network to better align partner ages
- Improvements to networks, including analyzers for debut age and partner age differences
- GitHub info: PR 143
Version 1.2 (2025-06-10)
- Improvements to networks, including analyzers for relationship duration and network degree
- Adds a
PriorPartnersnetwork for recalling past relationships - for use in partner notification - GitHub info: PR 135
Version 1.1.2 (2025-06-03)
- Bugfix to calibration class for multisims
- GitHub info: PR 138
Version 1.1.1 (2025-05-23)
- Bugfixes to calibration class and BV connector
- Replaces the
match_pairsmethod of theStructuredSexualnetwork with the faster option that was previously in theFastStructuredSexualnetwork (now removed). - GitHub info: PR 124
Version 1.1.0 (2025-05-13)
- Improvements to the Calibration class: this class now inherits directly from the Starsim calibration class, so users will have access to easier parameter constraints, plotting, flexible fit functions, etc
- Generalization of the coinfection class to handle any two diseases
- Addition of a more detailed BV model
- GitHub info: PR 119
Version 1.0.5 (2025-05-08)
- Adds results for syphilis transmission by disease stage
- GitHub info: PR 112
Version 1.0.4 (2025-05-07)
- Adds results for overtreatment among pregnant women
- GitHub info: PR 111
Version 1.0.3 (2025-04-30)
- Bugfixes for congenital syphilis
- GitHub info: PR 85
Version 1.0.2 (2025-04-14)
- Bugfixes for syphilis and GUD
- GitHub info: PR 83
Version 1.0.1 (2025-03-31)
- Track HIV prevalence for 15-49 year olds
- GitHub info: PR 79
Version 1.0.0 (2024-12-11)
- Updates to work with Starsim v2.2.1
- GitHub info: PR 63
Version 0.2.0 (2024-11-01)
- Updates to work with Starsim v2.0
- GitHub info: PR 62
Version 0.1.0 (2024-10-02)
- Collection of updates related to the NG/CT/TV work
- GitHub info: PR 59
Version 0.0.2 (2024-06-07)
- Initial version of STIsim with structured sexual networks, models of HIV and syphilis, worksflows for model calibration, and interventions for testing and treatment.
- GitHub info: PR 22
Version 0.0.1 (2024-05-15)
- Pre-release version