High-performance agent-based electoral simulation toolkit with support for 11+ countries, multiple voting systems, and advanced voter behavior models
Project description
ElectoralSim
High-Performance Electoral Simulation Toolkit
Early-stage • Functional • Under active development
Documentation • PyPI • Quick Start
Disclaimer: ElectoralSim is a simulation and electoral-system comparison toolkit, not an election forecasting model. Default country presets are structural demonstrations unless explicitly marked as calibrated against real election data. See Limitations for more.
A modular simulation toolkit for electoral systems, voter behavior, and political dynamics. Uses a hybrid approach: voter populations are stored as vectorized Polars DataFrames (not individual Python objects) for million-scale performance, with Mesa for model orchestration, Numba for JIT acceleration, and NetworkX for opinion dynamics.
Feature Status
| Area | Status | Notes |
|---|---|---|
| FPTP, PR allocation (D'Hondt, Sainte-Laguë, Hare, Droop, MMP, parallel) | ✅ Stable | Numba-accelerated, tested |
| IRV/RCV, STV, Approval, Condorcet, Borda, Score, PAV | ✅ Stable | Full alternative system coverage |
| Voter generation (demographics, ideology, Big Five, Moral Foundations) | ✅ Stable | Rich synthetic voter profiles |
| Behavior models (Proximity, Valence, Retrospective, Strategic, WastedVote, Sociotropic) | ✅ Stable | Weighted composition via BehaviorEngine |
| Metrics (Gallagher, ENP, HHI, VSE, Efficiency Gap, partisan bias, compactness) | ✅ Stable | Validated against known values |
| BatchRunner (parameter sweeps, sensitivity, calibration, uncertainty) | ✅ Stable | Sequential & parallel; CI, quantiles, MCSE |
| Country presets (23 countries + EU Parliament) | 🟡 Beta | Structural presets; 13 calibrated-status metadata |
| India simulator (543 Lok Sabha) | ✅ Stable | BehaviorEngine, vote_mnl_fast, StateConfig |
| EU Parliament (720 MEPs, 27 states) | ✅ Stable | D'Hondt per-country, BehaviorEngine refactored |
| Opinion dynamics (BA, WS, ER networks, noisy voter, bounded confidence, zealots) | 🟡 Beta | Core algorithms tested; needs empirical validation |
| Coalition & government (MWC, MCW, Laver-Shepsle, collapse, hazard, strain) | ✅ Stable | Full formation + survival + hazard models |
| Streamlit dashboard | 🟡 Beta | 23 countries, scenario save/load, warning banners |
| Redistricting/gerrymandering (PrecinctGraph, ReCom, ensemble) | 🟡 Beta | Optional geometry dependencies |
| Campaign models (finance, media, registration, targeting, mobilization, polling) | 🟡 Beta | Full campaign-ecosystem modeling |
| Primary elections (open/closed, candidate selection) | 🟡 Beta | US-style intra-party competition |
| Event manager (scandals, shocks, election timeline, polls) | 🔶 Experimental | Activated when explicitly configured |
| GPU acceleration (CuPy) | 🔶 Experimental | Utility computation & MNL sampling only |
Key Features
Electoral Systems
| System | Methods | Status |
|---|---|---|
| Plurality | First Past The Post (FPTP) | Stable |
| Proportional | D'Hondt, Sainte-Laguë, Hare Quota, Droop Quota, Open-list, Closed-list | Stable |
| Mixed | MMP (Mixed-Member Proportional), Parallel Mixed | Stable |
| Ranked Choice | IRV/RCV, STV (Single Transferable Vote), Borda Count | Stable |
| Scoring | Score (Range) Voting, Approval Voting, Condorcet Winner | Stable |
| Committee | PAV (Proportional Approval Voting) | Stable |
Voter Behavior Models
- Proximity Model — Spatial voting based on ideological distance (Stable)
- Valence Model — Non-policy candidate appeal (Stable)
- Retrospective Model — Economic voting (reward/punish incumbents) (Stable)
- Strategic Voting — Wasted vote model with district-level viability (Stable)
- Sociotropic/Pocketbook — National vs personal economic evaluation (Beta)
Opinion Dynamics (Beta)
- Network Topologies — Barabási-Albert, Watts-Strogatz, Erdős-Rényi, Random Regular
- Models — Bounded Confidence, Noisy Voter, Zealots (with Numba acceleration)
Coalition & Government (Stable)
- Formation — MWC, MCW, Laver-Shepsle portfolio allocation
- Stability — Sigmoid/Linear/Exponential collapse, hazard models
- Analysis — Coalition strain, junior partner penalty, Cox hazard
Metrics
- Gallagher Index, Loosemore-Hanby (disproportionality)
- Effective Number of Parties (Laakso-Taagepera), Herfindahl-Hirschman Index
- Efficiency Gap, Partisan Bias, Mean-Median Gap, Partisan Gini (gerrymandering)
- Polsby-Popper, Convex-Hull Compactness (district geometry)
- Voter Satisfaction Efficiency (VSE), Responsiveness, Swing Ratio
Country Presets (23 Countries + EU)
These are structural presets — they encode electoral-system rules, approximate party positions, and default parameters for demonstration and comparative simulation. They are not calibrated election forecasts. 13 have calibration-status metadata.
| Region | Countries |
|---|---|
| Asia | 🇮🇳 India (543 Lok Sabha), 🇯🇵 Japan |
| Europe | 🇬🇧 UK, 🇩🇪 Germany, 🇫🇷 France, 🇪🇺 EU Parliament (720 MEPs), 🇮🇪 Ireland, 🇳🇱 Netherlands, 🇨🇭 Switzerland, 🇳🇴 Norway, 🇸🇪 Sweden, 🇪🇸 Spain, 🏴 Scotland, 🏴 Wales |
| Americas | 🇺🇸 USA, 🇧🇷 Brazil, 🇨🇦 Canada, 🇲🇽 Mexico, 🇨🇱 Chile |
| Oceania/Africa | 🇦🇺 Australia (House + Senate), 🇳🇿 New Zealand, 🇿🇦 South Africa |
| Middle East | 🇮🇱 Israel |
Performance
- Vectorized Polars DataFrames for voter storage (not individual Python objects)
- Numba JIT acceleration for vote counting and MNL sampling (~10-50x over pure Python)
- Batch parameter sweeps for systematic exploration
- Parallel execution with multiprocessing (see parallel caveat)
Installation
pip install electoral-sim
From source:
git clone https://github.com/Ayush12358/ElectoralSim.git
cd ElectoralSim
pip install -e .
Optional dependencies:
pip install electoral-sim[viz] # Visualization (matplotlib, plotly)
pip install electoral-sim[gpu] # GPU acceleration (cupy)
pip install electoral-sim[all] # Everything
Command-Line Interface
Run simulations directly from the command line:
# Basic simulation
electoral-sim run --voters 50000 --constituencies 10
# Use country preset
electoral-sim run --preset india --output results.json
# Batch parameter sweeps
electoral-sim batch --config batch_config.json --output results.csv
# List available presets
electoral-sim list-presets
See the CLI Guide for comprehensive usage.
Quick Start
Basic Election
from electoral_sim import ElectionModel
# Create model with 100K voters
model = ElectionModel(n_voters=100_000, seed=42)
results = model.run_election()
print(f"Turnout: {results['turnout']:.1%}")
print(f"Gallagher Index: {results['gallagher']:.2f}")
print(f"ENP (votes): {results['enp_votes']:.2f}")
Country Presets
# India - Full Lok Sabha simulation
from electoral_sim import simulate_india_election
result = simulate_india_election(n_voters_per_constituency=1000)
print(f"BJP: {result.seats['BJP']} seats")
print(f"NDA Alliance: {result.nda_seats} seats")
# Other countries
model = ElectionModel.from_preset("germany") # MMP with 5% threshold
model = ElectionModel.from_preset("usa") # 435 House districts
model = ElectionModel.from_preset("uk") # 650 Commons seats
Chainable API
results = (
ElectionModel(n_voters=100_000)
.with_system("PR")
.with_allocation("sainte_lague")
.with_threshold(0.05)
.with_temperature(0.3) # More deterministic voting
.run_election()
)
Batch Runner - Parameter Sweeps
Systematic parameter exploration for sensitivity analysis:
from electoral_sim.analysis import BatchRunner, ParameterSweep
# Define parameter sweep
sweep = ParameterSweep({
'n_voters': [10_000, 50_000, 100_000],
'temperature': [0.3, 0.5, 0.7],
'economic_growth': [-0.02, 0.0, 0.02]
})
# Run batch with parallel execution
runner = BatchRunner(
model_class=ElectionModel,
parameter_sweep=sweep,
n_runs_per_config=5,
n_jobs=4
)
results_df = runner.run()
runner.export_results('results.csv')
Custom Behavior Engine
from electoral_sim import (
ElectionModel, BehaviorEngine,
ProximityModel, ValenceModel, StrategicVotingModel
)
# Build custom voter behavior
engine = BehaviorEngine()
engine.add_model(ProximityModel(weight=1.0))
engine.add_model(ValenceModel(weight=0.5))
engine.add_model(StrategicVotingModel(sensitivity=2.0))
model = ElectionModel(n_voters=50_000, behavior_engine=engine)
results = model.run_election()
Opinion Dynamics
from electoral_sim import ElectionModel, OpinionDynamics
# Create social network
od = OpinionDynamics(n_agents=10_000, topology="barabasi_albert", m=3)
# Simulate with opinion evolution
model = ElectionModel(n_voters=10_000, opinion_dynamics=od)
for _ in range(100):
model.step() # Opinions evolve
result = model.run_election()
Coalition Formation
from electoral_sim import form_government, coalition_strain
import numpy as np
seats = np.array([150, 120, 80, 50])
positions = np.array([0.6, -0.3, 0.1, -0.6])
names = ["Right", "Left", "Center", "Far-Left"]
gov = form_government(seats, positions, names)
print(f"Coalition: {gov['coalition_names']}")
print(f"Majority: {gov['seats']} seats")
print(f"Stability: {gov['stability']:.2f}")
Streamlit Dashboard
Launch the interactive election explorer:
streamlit run app.py
Features:
- Multi-country simulation (India, USA, UK, Germany, etc.)
- Dynamic parameters (economic growth, national mood, anti-incumbency)
- Real-time seat distribution and vote share charts
- Swing analysis and ideological landscape visualization
More screenshots: docs/assets/
Project Structure
electoral_sim/
├── core/ # ElectionModel, Config, CLI, Voter Generation, Provenance
├── agents/ # Voter, Party, Adaptive Strategy
├── behavior/ # Behavior models + Campaign models (6 campaign classes)
├── dynamics/ # Opinion dynamics (networks, noisy voter, bounded confidence)
├── engine/ # Numba acceleration, GPU, Coalition, Government, Hazards
├── events/ # Event manager, election timeline, poll generator
├── metrics/ # Indices (Gallagher, ENP, VSE), Gerrymandering metrics
├── presets/ # 23 countries + EU (config.py + election.py per preset)
│ ├── india/ # 543 constituencies, 17 parties, StateConfig
│ └── eu/ # 27 member states, 720 MEPs, per-country BehaviorEngine
├── systems/ # Allocation (PR, MMP, mixed), Alternative (IRV, STV, Borda, Score, PAV)
├── analysis/ # BatchRunner, Duverger, VSE, Sensitivity, Calibration, Redistricting
├── visualization/ # Plots (seats, votes, summary, ideological), Specialized
└── data/ # Historical election data, ingestion pipeline
Performance Benchmarks
Benchmarks below were run on an Intel i5-1135G7 (4C/8T, 16 GB RAM, Ubuntu 22.04, Python 3.12, Numba 0.65). Timings exclude Numba JIT warmup. Memory is RSS delta measured via
psutil. Seebenchmarks/for reproducible scripts.
| Scale | Create Time | Election Time | Memory (RSS delta) |
|---|---|---|---|
| 10K voters | ~18ms | ~5ms | ~52 MB |
| 100K voters | ~109ms | ~35ms | ~15 MB |
| 500K voters | ~608ms | ~176ms | ~95 MB |
| 1M voters | ~1.2s | ~316ms | ~148 MB |
Batch throughput: ~5 elections/sec at 500K voters; up to ~30 elections/sec at smaller 10K-50K configurations (FPTP, default behavior, after JIT warmup).
Testing
# Run all tests
pytest tests/ -v
# Run integration tests only
pytest tests/test_integration.py -v
# Run stress tests
python benchmarks/stress_test.py
Test suite: 454 tests including:
- Property-based tests (Hypothesis) — random input generation
- Parameterized tests — all systems, presets, allocation methods
- Performance smoke tests — 1K, 10K voters timing
- Error handling — invalid inputs, edge cases
- Preset smoke tests — UK, US, Germany, India, etc.
- Invariant tests — seats non-negative, vote shares sum to 1, determinism with seeds
Documentation
- Quick Start Guide — Detailed API usage examples
- docs/ — Full documentation
- API Reference — Complete function/class documentation
- Country Presets — India, EU, and other country guides
- Advanced Topics — Voter psychology, performance tuning
- CITATIONS.md — Academic references and citations
- CITATION.cff — Software citation metadata
- FEATURE_COMPARISON.md — How ElectoralSim compares to other tools
Limitations
What ElectoralSim Is
- A simulation and electoral-system comparison toolkit
- A teaching and research prototyping tool
- A way to explore how different electoral systems and voter assumptions interact
What ElectoralSim Is Not
- ❌ An election forecasting model
- ❌ A calibrated public-opinion model (by default)
- ❌ A substitute for survey data or polling
- ❌ A validated behavioral model for all included countries
Known Limitations
- Country presets use synthetic party positions and valence values — they are structural demos, not calibrated to real election data.
- Voter psychology features (Big Five, Moral Foundations, affective polarization) are synthetically generated and not derived from survey instruments.
- 2D ideological space is a deliberate simplification; complex political systems (India, Brazil, EU) have dimensions (caste, region, language, religion) not captured.
- India simulator previously used a separate path but now shares the core BehaviorEngine (CHANGELOG v0.2.0).
- GPU acceleration is experimental and limited to utility computation and MNL sampling kernels; it has not been correctness-tested against CPU outputs.
Parallel Execution Caveat
On Linux, Numba's OpenMP threading layer conflicts with Python's default fork()-based multiprocessing. If using BatchRunner with n_jobs > 1, you may encounter BrokenProcessPool. Workarounds:
import multiprocessing
multiprocessing.set_start_method("spawn")
# or
import os; os.environ["OMP_NUM_THREADS"] = "1"
Tech Stack
| Component | Library |
|---|---|
| Agent-Based Modeling | Mesa |
| DataFrames | Polars |
| JIT Acceleration | Numba |
| GPU Support | CuPy |
| Social Networks | NetworkX |
| Visualization | Plotly, Matplotlib |
| Dashboard | Streamlit |
License
Apache License 2.0 - see LICENSE for details.
Acknowledgments
- Mesa for the agent-based modeling framework
- Political science research on spatial voting, opinion dynamics, and coalition theory
- Electoral data from various national election commissions
Built with love for computational political science
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file electoral_sim-0.2.1.tar.gz.
File metadata
- Download URL: electoral_sim-0.2.1.tar.gz
- Upload date:
- Size: 167.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed3197cd2663478537e3dd597b7c8b158aea8b55f76cc8d75c38ecaefb3c352a
|
|
| MD5 |
e0cbaeaa4bd780a4b77f3ea942ef778e
|
|
| BLAKE2b-256 |
f07c3418f6b52c51ac60c61c7851e4dc8823238a82e745e28ff6c056f96ea85c
|
Provenance
The following attestation bundles were made for electoral_sim-0.2.1.tar.gz:
Publisher:
release.yml on Ayush12358/ElectoralSim
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
electoral_sim-0.2.1.tar.gz -
Subject digest:
ed3197cd2663478537e3dd597b7c8b158aea8b55f76cc8d75c38ecaefb3c352a - Sigstore transparency entry: 1734455449
- Sigstore integration time:
-
Permalink:
Ayush12358/ElectoralSim@57741f94849df0e92b36ccc3f74568098d7f3378 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/Ayush12358
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@57741f94849df0e92b36ccc3f74568098d7f3378 -
Trigger Event:
push
-
Statement type:
File details
Details for the file electoral_sim-0.2.1-py3-none-any.whl.
File metadata
- Download URL: electoral_sim-0.2.1-py3-none-any.whl
- Upload date:
- Size: 196.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
340b8c6c0fd526cd01aab37c2d545e5e2a7924f88a0cc9e56d7beb6f27ca0ce1
|
|
| MD5 |
05f903aff0b5741f45071fab7441ae03
|
|
| BLAKE2b-256 |
5382fbb9435ca2a7d6a1e50dafe1b9796fbf5b1ec7ecc0bc3283482c03873b72
|
Provenance
The following attestation bundles were made for electoral_sim-0.2.1-py3-none-any.whl:
Publisher:
release.yml on Ayush12358/ElectoralSim
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
electoral_sim-0.2.1-py3-none-any.whl -
Subject digest:
340b8c6c0fd526cd01aab37c2d545e5e2a7924f88a0cc9e56d7beb6f27ca0ce1 - Sigstore transparency entry: 1734455500
- Sigstore integration time:
-
Permalink:
Ayush12358/ElectoralSim@57741f94849df0e92b36ccc3f74568098d7f3378 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/Ayush12358
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@57741f94849df0e92b36ccc3f74568098d7f3378 -
Trigger Event:
push
-
Statement type: