Skip to content

Version 3.5.0

Choose a tag to compare

@cliffckerr cliffckerr released this 05 Jul 05:13
d5380b1

Version 3.5.0 (2026-07-05)

This release provides several major performance improvements, focused on random number generation, networks, and pregnancy. On a representative large simulation (50,000 agents, 100 years, SIS on a random network with births and deaths), the random number changes alone make the default configuration roughly 1.4x faster, or about 2x faster with common random numbers disabled (ss.options.crn = False); the network, transmission, and pregnancy changes described below provide further ~1.3x speedups on top of this, for a total of ~1.8-2.6x speedups.

Common random numbers

  • Common random numbers (CRN) work by giving each agent a "slot" and drawing one random number per slot, so that the same agent makes the same decision regardless of what other agents do. Previously this was implemented by drawing slots.max()+1 numbers and keeping only those at the requested slots. Because newborn agents are assigned slots spread over a wide range (up to slot_scale times the population size), this could mean generating up to 5x more random numbers than were actually needed. The default CRN path now generates only the number of values needed, via a counter-based hash (ss.distributions.hash_uniforms(), a Numba-compiled splitmix64 algorithm keyed by the distribution seed and draw index) that maps each slot directly to a uniform value. This preserves CRN reproducibility while removing the redundant draws.
  • Added a new crn option (ss.options.crn, default True). Setting ss.options.crn = False skips the slot machinery entirely. This is not CRN-safe across scenarios, but is statistically valid and considerably faster; it is useful when reproducibility across counterfactual scenarios is not required.
  • Sped up ss.multi_random (used for pairwise transmission probabilities) by including a dedicated fast path for the common two-distribution case that avoids constructing a typed list on each call.
  • Regression: the single_rng option has been removed. Its purpose was to demonstrate the "single centralized RNG" behavior of other agent-based modeling frameworks; use ss.options.crn = False instead, which is both faster and demonstrates the same stochastic-branching problem. (Note, however, that it does not use a single RNG; this option is no longer available.)
  • Regression: because the default CRN mechanism changed, the exact random values produced for agent-indexed draws are different from previous versions. Results remain statistically equivalent and CRN reproducibility is preserved, but stored regression baselines will need to be regenerated.

RandomNet and HouseholdNet

  • Regression: ss.RandomNet is now a faster (~1.8x), approximate network in which the target end of each edge is drawn independently. The per-agent degree is therefore only approximately Poisson rather than exact. The previous exact behavior is available as the new ss.RandomExactNet; switch to it if you rely on the exact degree distribution.
  • Edge generation for ss.RandomExact networks (what were previously called ss.Random) now uses an in-place Fisher-Yates shuffle (ss.networks.fisher_yates_shuffle()) instead of np.random.permutation, about 1.3x faster.
  • Networks whose edges last a single timestep (dur=0, e.g. ss.RandomNet) now regenerate edges via a zero-copy fast path that avoids concatenating onto the previous edge list each step.
  • ssl.HouseholdNet initialization is now fully vectorized across household assignment, edge construction, and female-head-of-household selection, for a performance improvement of 10-20x for large populations.
  • ssl.HouseholdNet.add_births() was also vectorized, for a performance improvement of ~1.3x in high-turnover simulations.

Pregnancy

  • The ss.Pregnancy conception logic was optimized, for a ~20% performance improvement.
  • Regression: the default slot_scale for ss.Pregnancy was increased from 5 to 100. Newborns are assigned a random slot in the range [n_agents, slot_scale·n_agents]; a larger range reduces the chance that two newborns share a slot (and therefore make identical random draws). Previously a larger slot_scale also meant proportionally more random draws, but with the new hash-based CRN the number of draws no longer depends on the slot range, so the default was raised to reduce these collision artifacts at no performance cost. This changes results for models with pregnancy.

Other performance optimizations

  • The disease transmission step now uses a Numba-compiled kernel (_nb_transmit) that identifies transmitting edges in a single branchless pass and returns its result as a zero-copy view, about 1.4x faster at typical transmission densities. Effective transmissibility and susceptibility are computed directly on the raw state arrays, avoiding intermediate Arr wrapper allocations.
  • Extracting the UIDs of a boolean state (Arr.true(), Arr.uids, and boolean filtering) now uses a branchless Numba compaction kernel above a threshold number of elements.
  • The numba_indexing threshold (the array size above which Numba is used instead of NumPy for indexing) was lowered from 5000 to 2000, since Numba wins for both gather and compaction above ~2000 elements.

Other changes

  • starsim.library now exports its contents at the top level, so classes can be accessed either via their submodule (e.g. ssl.networks.HouseholdNet, ssl.diseases.Cholera) or directly (e.g. ssl.HouseholdNet, ssl.Cholera), like core Starsim.
  • Added tests/benchmark_large.py and associated benchmark data for tracking performance on larger simulations.

Regression information

  • The default random number values for agent-indexed draws have changed (see "Common random numbers" above); results are statistically equivalent but not bit-for-bit identical, and baselines should be regenerated.
  • ss.RandomNet is now approximate; use ss.RandomExactNet for the previous exact-degree behavior.
  • The single_rng option has been removed; use ss.options.crn = False instead.
  • The default slot_scale for ss.Pregnancy was increased from 5 to 100, which changes results for models with pregnancy.
  • GitHub info: PR 1378