Imported from Ayush12358/ElectoralSim (
electoral_sim/engine/AGENTS.md). Install upstream withnpx skills add Ayush12358/ElectoralSim --skill engine. Copyright stays with the author.
engine/ Knowledge Base
Scope: Performance-critical compute kernels and political coalition/government logic.
Overview
Two distinct responsibilities live here: (1) accelerated backend functions (Numba JIT, optional CuPy GPU) that hot-path vote counting, seat allocation, and utility computation, and (2) coalition formation and government stability models that sit atop election results.
Files
| File | Lines | Purpose |
|---|---|---|
numba_accel.py |
377 | JIT-compiled dhondt_numba(), sainte_lague_numba(), fptp_count_numba() (parallel), mnl_sample_numba(), compute_utilities_numba(). Wrapper functions (fptp_count_fast, vote_mnl_fast, dhondt_fast) decide at runtime whether to call JIT or fallback. Includes benchmark_numba(). |
gpu_accel.py |
150 | CuPy-based GPU kernels for utility matrix and MNL sampling. is_gpu_available() gates usage. |
coalition.py |
558 | minimum_winning_coalitions(), minimum_connected_winning(), predict_coalition_stability(), form_government(), allocate_portfolios_laver_shepsle(), junior_partner_penalty(), form_coalition_with_utility(). |
strain.py |
66 | coalition_strain() weighted pairwise policy-distance strain calculation. |
government.py |
224 | GovernmentSimulator class with step()/simulate() lifecycle, collapse_probability() (sigmoid/linear/exponential models). |
_hazards.py |
105 | hazard_rate() (bathtub curve + event weights) and cox_proportional_hazard() (Warwick 1994 defaults). Extracted from government.py. |
Key Patterns
- Numba fallback:
try/except ImportErrorat module level. If Numba is missing, a no-op@jitdecorator returns the raw function, andprangealiases torange. Wrapper functions (dhondt_fast, etc.) checkNUMBA_AVAILABLEflag to branch. Callers never import Numba directly. - Arrays only to JIT: All
@jitfunctions accept/returnnp.ndarray(int64/float64). No dicts, lists, or optional params. - Pure NumPy fallbacks use vectorized operations (
bincount,argmax, broadcasting) to avoid performance cliffs. - GPU gating:
gpu_accel.pychecks presence at import time; functions raise at call time. Callers should checkis_gpu_available()first.
Conventions
- Numba functions get
_numbasuffix; wrapper functions get_fastsuffix. - Wrapper functions own the dtype casting (callers pass whatever, wrappers convert to int64/float64 for Numba).
- Coalition functions accept bare
np.ndarray(not Polars) for seat/position vectors. @jit(nopython=True, cache=True)everywhere; useparallel=True+prangeonly for embarrassingly parallel loops (constituency-level FPTP, per-voter utility).- Benchmark function in
numba_accel.pyis guarded byif __name__ == "__main__".
Anti-Patterns
- Do not pass Python objects to Numba functions. Lists, dicts, tuples, and
Optionalargs cause compilation failure. Cast to ndarray before calling. - Do not import Numba in callers. Use the
_fastwrappers. Importing Numba directly breaks the graceful fallback contract. - Do not use Polars Series inside
@jit. Convert tonp.ndarrayfirst. Numba has zero Polars awareness. - Do not use
@jiton functions that return variable-length structures. Return fixed-size arrays. - Do not call
gpu_accelfunctions without checkingis_gpu_available(). They raiseRuntimeErroron failure, not fall back.