Debug Simulation Outcomes

This guide helps you diagnose unexpected simulation results systematically. Use these techniques when outcomes don’t match theoretical predictions or when values fall outside expected ranges.

Prerequisites

Symptom Identification

Before debugging, identify which symptom you’re experiencing:

Values Out of Range
  • Probability values outside [0, 1]

  • Negative wealth

  • Consciousness exceeding bounds

Unexpected Bifurcation
  • Fascism when solidarity edges exist

  • Revolution without sufficient P(S|R)

  • Stalled consciousness drift

Timing Issues
  • Death occurring too early/late

  • Crossover threshold never reached

  • Rent extraction not accumulating

Step 1: Verify Formula Correctness

Run the formula doctests to ensure mathematical operations work correctly:

mise run doctest

This validates all formulas in src/babylon/systems/formulas.py against their documented examples. If tests fail, the bug is in formula implementation.

See also

Formulas Reference for the complete formula specification.

Step 2: Run a Trace Analysis

Capture full simulation state over time:

mise run analyze-trace

This outputs a CSV to results/trace.csv with per-tick data:

  • Entity wealth, consciousness, organization

  • Survival probabilities (P(S|A), P(S|R))

  • Edge tension and value flows

Open the CSV in a spreadsheet to identify:

  • Inflection points: Where do values change direction?

  • Correlations: Do related values move together?

  • Anomalies: Any sudden jumps or flat periods?

Step 3: Use Structured Logging

For detailed event-by-event debugging, use the vertical slice tool:

poetry run python tools/vertical_slice.py

This tool provides:

  1. Tick-by-tick state display: All entity values per tick

  2. Event logging: What events triggered each state change

  3. JSON structured logs: Machine-readable logs in logs/

Interpreting JSON Logs

The logs/vertical_slice_<timestamp>.json file contains:

{
  "event_type": "simulation_tick",
  "data": {
    "tick": 5,
    "entities": {
      "C001_periphery_worker": 0.0823,
      "C004_labor_aristocracy": 0.2156
    },
    "economy": {
      "imperial_rent_pool": 0.15,
      "super_wage_rate": 0.08
    },
    "tension": 0.42,
    "events": ["SURPLUS_EXTRACTION: 0.018 from C001"]
  }
}

Search the JSON for:

  • "event_type": "error" to find logged errors

  • "success": false to find failed operations

  • Specific entity IDs to trace their state changes

Step 4: Compare Against Theory

Use the survival calculus formulas to verify expected behavior:

P(S|A) Should:
  • Increase when wealth increases

  • Approach 0 as wealth approaches subsistence threshold

  • Follow sigmoid curve centered at subsistence level

P(S|R) Should:
  • Increase with organization

  • Decrease with repression

  • Cross P(S|A) when revolution becomes rational choice

Bifurcation Should:
  • Route to fascism when solidarity edges absent

  • Route to revolution when solidarity edges present

  • Trigger when wages fall and agitation increases

See also

George Jackson Bifurcation Model for bifurcation theory.

Step 5: Isolate the Problem

Once you’ve identified which system is misbehaving:

For formula bugs:

Create a minimal test case in tests/unit/formulas/.

For system bugs:

Check the relevant system in src/babylon/systems/:

  • economic.py - Imperial rent extraction

  • solidarity.py - Consciousness transmission

  • ideology.py - Bifurcation and drift

  • survival.py - P(S|A) and P(S|R)

For graph bugs:

Use state.to_graph() to inspect the NetworkX graph directly:

from babylon.engine.scenarios import create_imperial_circuit_scenario

state, config, defines = create_imperial_circuit_scenario()
G = state.to_graph()

# Inspect edges
print(list(G.edges(data=True)))

# Check for missing solidarity edges
solidarity_edges = [
    (u, v) for u, v, d in G.edges(data=True)
    if d.get("type") == "SOLIDARITY"
]

Common Failure Patterns

Death at Tick 1

Symptom: Periphery worker dies immediately.

Cause: Initial wealth below subsistence threshold combined with high extraction efficiency.

Fix: Check extraction_efficiency in GameDefines or increase initial periphery wealth in scenario.

P(S|R) Always Zero

Symptom: Revolution probability never increases.

Cause: Organization value not increasing, or repression too high.

Fix: Verify solidarity edges exist and organization field updates.

Consciousness Never Drifts

Symptom: class_consciousness stays constant.

Cause: Missing solidarity edges (required for transmission) or drift_sensitivity_k set too low.

Fix: Check graph structure for SOLIDARITY edges between entities.

Fascism Without Trigger

Symptom: Ideology shifts to +1 without visible cause.

Cause: Agitation accumulated while solidarity edges were absent.

Fix: Review edge creation in scenario setup; ensure solidarity edges added before agitation triggers.

Getting Help

If you’ve followed these steps and still can’t identify the issue:

  1. Create a minimal reproduction script

  2. Include the trace CSV output

  3. Note which tick the unexpected behavior occurs

  4. Check existing tests for similar scenarios

See Also