Skip to main content

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

Tests Lint Coverage PyPI Python Downloads License: Apache 2.0 Documentation Code style: black

DocumentationPyPIQuick 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

ElectoralSim Dashboard

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

ElectoralSim Dashboard

ElectoralSim Dashboard

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. See benchmarks/ 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: 502 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


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 currently uses a specialized high-performance path separate from the generic ElectionModel; improvements to the core engine do not automatically benefit India.
  • 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

electoral_sim-0.2.0.tar.gz (165.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

electoral_sim-0.2.0-py3-none-any.whl (161.9 kB view details)

Uploaded Python 3

File details

Details for the file electoral_sim-0.2.0.tar.gz.

File metadata

  • Download URL: electoral_sim-0.2.0.tar.gz
  • Upload date:
  • Size: 165.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for electoral_sim-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e84873c47a26044214fe60c395aa50741a62f7e1a925bdeb956bfb4677afc10c
MD5 f68c98e97d9babecfe6e5319a73a57a7
BLAKE2b-256 b5ee0fd1976ea1012dd94da600053bb5b378c103f520bad8cbbc6f5e0b5723dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for electoral_sim-0.2.0.tar.gz:

Publisher: release.yml on Ayush12358/ElectoralSim

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file electoral_sim-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: electoral_sim-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 161.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for electoral_sim-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cee9a4b4a596894474bae3648d67c381438cad49531d345efa2a2b6bfb8bfd18
MD5 48eda0fa9e6e224fba8902a3e6dcaf31
BLAKE2b-256 d49c6aefaa98568d04c3a4b01a1818f31a1673456273827fb4ba13a676d36f10

See more details on using hashes here.

Provenance

The following attestation bundles were made for electoral_sim-0.2.0-py3-none-any.whl:

Publisher: release.yml on Ayush12358/ElectoralSim

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page