SignalClaw: LLM-Guided Evolutionary Synthesis of Interpretable Traffic Signal Control Skills
Organizations: Business School, Sichuan University, Chengdu, China
Abstract
Traffic signal control TSC requires strategies that are both effective and interpretable for deployment, yet reinforcement learning produces opaque neural policies while program synthesis depends on restrictive domain-specific languages. We present SIGNALCLAW, a framework that uses large language models LLMs as evolutionary skill generators to synthesize and refine interpretable control skills for adaptive TSC. Each skill includes rationale, selection guidance, and executable code, making policies human-inspectable and self-documenting. At each generation, evolution signals from simulation metrics such as queue percentiles, delay trends, and stagnation are translated into natural language feedback to guide improvement. SignalClaw also introduces event-driven compositional evolution: an event detector identifies emergency vehicles, transit priority, incidents, and congestion via TraCI, and a priority dispatcher selects specialized skills. Each skill is evolved independently, and a priority chain enables runtime composition without retraining. We evaluate SignalClaw on routine and event-injected SUMO scenarios against four baselines. On routine scenarios, it achieves average delay of 7.8 to 9.2 seconds, within 3 to 10 percent of the best method, with low variance across random seeds. Under event scenarios, it yields the lowest emergency delay 11.2 to 18.5 seconds versus 42.3 to 72.3 for MaxPressure and 78.5 to 95.3 for DQN, and the lowest transit person delay 9.8 to 11.5 seconds versus 38.7 to 45.2 for MaxPressure. In mixed events, the dispatcher composes skills effectively while maintaining stable overall delay. The evolved skills progress from simple linear rules to conditional strategies with multi-feature interactions, while remaining fully interpretable and directly modifiable by traffic engineers.
Figures & tables
| Method | Prog. | LLM | Signal | Event | Multi-mode | Audit | Strategy Form |
|---|---|---|---|---|---|---|---|
| Fixed-timing | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | Pre-set phase durations |
| MaxPressure ( Varaiya, 2013 ) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | Pressure-based rule |
| PI-Light ( Gu et al., 2024 ) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | DSL expression (MCTS) |
| SymLight ( Liao et al., 2025 ) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | Symbolic expression |
| LLMLight ( Lai et al., 2023 ) | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | LLM direct decision |
| Heuristic-policy evolution ( Wang et al., 2025 ) | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | LLM-guided heuristic search |
| Mode | Decision Point | Output | Granularity |
|---|---|---|---|
| Phase Selection | Every step | Phase score | Per-step |
| Phase Extension | End of min green | Extension (sec) | Per-phase |
| Cycle Planning | Cycle start | Duration per phase | Per-cycle |
| Skill | Scenarios | Initial | Best | Gen | Improv. (%) |
|---|---|---|---|---|---|
| Normal | T1+T2+T3 | 55.20 | 60.50 | 12 | 9.6 |
| Emergency | E1+E2 | 3.15 | 3.92 | 22 | 24.4 |
| Transit | B1+B2 | 2.88 | 3.76 | 8 | 30.6 |
| Incident | I1 | 2.65 | 3.28 | 18 | 23.8 |
| FixedTime | MaxPressure | PI-Light | DQN | SignalClaw | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Scenario | Type | mean | std | mean | std | mean | std | mean | std | mean | std |
| T1 | Train | 47.3 | 1.5 | 13.8 | 0.9 | 8.5 | 0.7 | 7.9 | 1.2 | 8.7 | 0.6 |
| T2 | Train | 43.6 | 1.3 | 12.5 | 0.8 | 8.1 | 0.6 | 8.4 | 1.1 | 7.8 | 0.4 |
| T3 | Train | 52.1 | 1.8 | 14.2 | 1.0 | 7.9 | 0.8 | 8.3 | 1.3 | 8.4 | 0.7 |
| V1 | Valid | 49.8 | 1.6 | 14.5 | 1.0 | 9.3 | 0.8 | 8.7 | 1.4 | 9.1 | 0.6 |
| V2 | Valid | 46.2 | 1.4 | 13.9 | 0.9 | 9.6 | 0.7 | 8.5 | 1.2 | 9.2 | 0.8 |
| Scenario | Method | Avg Delay | Emg Delay | Person-Delay | Queue | ||||
|---|---|---|---|---|---|---|---|---|---|
| mean | std | mean | std | mean | std | mean | std | ||
| E1 | FixedTime | 48.5 | 1.6 | 385.2 | 48.7 | — | — | 32.4 | 1.2 |
| MaxPressure | 14.2 | 1.0 | 42.3 | 6.8 | — | — | 8.9 | 0.7 | |
| PI-Light | 9.8 | 0.8 | 55.2 | 9.1 | — | — | 5.7 | 0.5 | |
| DQN | 11.3 | 2.1 | 78.5 | 32.4 | — | — | 6.8 | 1.5 | |
| SignalClaw | 11.5 | 0.7 | 14.7 | 2.8 | — | — | 6.9 | 0.5 | |
| Comparison | E1 ( , ) | E2 ( , ) | B1 ( , ) | B2 ( , ) | M1 ( , ) |
|---|---|---|---|---|---|
| vs. MaxPressure | 0.001, 5.3 ✓ | 0.001, 14.0 ✓ | 0.001, 7.6 ✓ | 0.001, 7.2 ✓ | 0.001, 6.0 ✓ |
| vs. PI-Light | 0.001, 6.0 ✓ | 0.001, 12.1 ✓ | 0.001, 7.3 ✓ | 0.001, 7.1 ✓ | 0.001, 6.2 ✓ |
| vs. DQN | 0.011, 2.8 ✓ | 0.008, 3.1 ✓ | 0.012, 2.7 ✓ | 0.013, 2.7 ✓ | 0.015, 2.6 ✓ |
| Method | E1 | E2 | ||
|---|---|---|---|---|
| Emerg. Delay | Avg. Delay | Emerg. Delay | Avg. Delay | |
| Handcrafted Preemption | 0.8 s | 14.8 s | 0.5 s | 17.2 s |
| SignalClaw (evolved) | 14.7 s | 11.5 s | 11.2 s | 12.3 s |
| Variant | Improv. (%) |
|---|---|
| Signal ablation | |
| Full signals (skill) | 47.3 |
| No signals (skill) | 26.8 |
| Random direction (skill) | 21.5 |
| Representation ablation | |
| Full signals (code-only) | 39.9 |
| Gen | Strategy Description | Executable Code ( inlane ) |
|---|---|---|
| 0 | Baseline: accumulate waiting vehicles. | value[0] += waiting_vehicle |
| 1 | Nonlinear queue weighting with distance penalty for spread-out queues. | value[0] += waiting**1.5 + min(3, waiting)*dist |
| 3 | Self-reinforcing queue urgency: longer queues get disproportionate priority. | value[0] += waiting * (1 + min(3, waiting)) |
| 19 | Saturation-aware branching: heavy queues ( 5) use distance-adjusted scoring; light queues get linear boost. | if waiting > 5: value[0] += waiting * (max(1, dist) ...) elif waiting > 0: value[0] += waiting * 2 |
| 50 | Ratio-based saturation: score by excess waiting fraction relative to total vehicles. | if waiting > vehicles//3: value[0] += (waiting - vehicles//3)**2 |
| Best-ever skill on T1. | ||
| Method | LOC | AST Nodes | Readable? | Modifiable? |
|---|---|---|---|---|
| DQN | N/A | 16,000 params | ✗ | ✗ |
| PI-Light | 1 | 3–7 | ✓ | ✓ |
| MaxPressure | 1 | 5 | ✓ | ✓ |
| SignalClaw (gen 0) | 1 | 3 | ✓ | ✓ |
| SignalClaw (gen 19) | 4–6 | 15–20 | ✓ | ✓ |
| SignalClaw (final) | 3–5 | 12–18 | ✓ | ✓ |
| Skill Type | LLM Calls | SUMO Runs | Wall-clock | Skill LOC |
|---|---|---|---|---|
| Normal (T1+T2+T3, 30 gen) | 240 | 720 | 8 h | 4–6 |
| Emergency (E1+E2, 30 gen) | 240 | 480 | 6 h | 3–5 |
| Transit (B1+B2, 30 gen) | 240 | 480 | 6 h | 2–4 |
| Incident (I1, 30 gen) | 240 | 240 | 4 h | 3–4 |
Appendix figures & tables7 assets
Supplementary material from the paper’s appendix.
Appendix
| Abstract Name | Lane-Indexed Alias | Source |
| Inlane features (per lane-link) | ||
| num_vehicle | inlane_2_num_vehicle | traci.lane.getLastStepVehicleNumber |
| num_waiting_vehicle | inlane_2_num_waiting_vehicle | traci.lane.getLastStepHaltingNumber |
| vehicle_dist | inlane_2_vehicle_dist | Mean inter-vehicle distance |
| Outlane features (per lane-link) | ||
| num_vehicle | outlane_2_num_vehicle | traci.lane.getLastStepVehicleNumber |
| Component | Content |
|---|---|
| Description | Saturation-aware phase scoring with distance-adjusted urgency. Prioritizes phases with heavy queue buildup ( 5 vehicles) using a distance-modulated scoring function, while maintaining linear responsiveness for moderate queues. |
| Guidance | (1) When waiting vehicles exceed 5 (saturation threshold), apply distance-adjusted weighting to account for spatial queue density. (2) For moderate queues (1–5 vehicles), use simple doubling to maintain responsiveness. (3) Ignore phases with zero waiting vehicles to avoid noise. |
| Inlane Code | if inlane_2_num_waiting_vehicle > 5: value[0] += inlane_2_num_waiting_vehicle * (max(1, inlane_2_vehicle_dist) - inlane_2_vehicle_dist % 3) + inlane_2_num_vehicle // 4 elif inlane_2_num_waiting_vehicle > 0: value[0] += inlane_2_num_waiting_vehicle * 2 |
| Outlane Code | value[0] += min(10, outlane_2_num_vehicle) * max(0, outlane_2_vehicle_dist - 3) |
| Signal | Direction |
|---|---|
| force_innovation | Multiple stagnant generations. Try completely different structure. |
| high_queue | Queue exceeds P75. Focus on queue management. |
| low_throughput | Throughput below P25. Optimize flow efficiency. |
| high_delay | Delay exceeds P75. Reduce vehicle waiting time. |
| performance_gain | Performance improved. Continue optimizing current direction. |
| performance_decline | Performance declined. Try different strategy approach. |
| Scenario | Event Type | Configuration |
|---|---|---|
| E1 | Emergency | Ambulance injected every 300 s (12 total), random O-D routes. Vehicle class: emergency , speed: 20 m/s. |
| E2 | Emergency | Ambulance injected every 120 s (30 total), higher frequency stress test. |
| B1 | Transit | 2 bus lines, headway 180 s (40 buses total). Bus stops at every intersection. Vehicle class: bus . |
| B2 | Transit | 4 bus lines, headway 120 s (120 buses total). Dense bus operation. |
| I1 | Incident | Single vehicle breakdown at s on a major east-west arterial link. Blocks one lane for 300 s (until s). |
| M1 | Mixed | Emergency (every 300 s) + incident at s. Tests compositional dispatch of emergency (P0) and incident (P1) skills. |
| Configuration | Runs | Wall-clock Time |
|---|---|---|
| Normal skill (T1+T2+T3, 30 gen) | 30 gen 8 pop 3 scen | 8 hours |
| Event skills (3 types, 30 gen each) | 3 30 gen 8 pop | 16 hours |
| Ablation (T1, 100 gen, 3 variants) | 3 100 gen 8 pop | 30 hours |
| Baseline evaluation (5 methods 7 scen 5 seeds) | 175 runs | 10 hours |
| DQN training (T1, 200 episodes) | 200 episodes | 4 hours |
| PI-Light MCTS (T1, 16 iterations) | 16 iterations | 2 hours |
| Scenario | Strategy Description | Executable Code |
|---|---|---|
| T1 (gen 19) | Saturation-aware branching with distance-adjusted urgency for heavy queues. | inlane : if waiting > 5: value[0] += waiting * (max(1, dist) - dist % 3) + vehicles // 4 elif waiting > 0: value[0] += waiting * 2 outlane : value[0] += min(10, vehicles) * max(0, dist - 3) |
| T2 (gen 59) | Distance-weighted queue scoring with vehicle density bonus. | inlane : value[0] += waiting * max(1, dist) + vehicles // 5 |
| T3 (gen 79) | Ratio-based saturation detection: penalize excess waiting fraction. | inlane : if waiting > vehicles // 3: value[0] += (waiting - vehicles // 3) ** 2 |
| Event Type | Strategy Description | Executable Code |
|---|---|---|
| Emergency | Distance-aware preemption: grant maximum priority to the phase serving the emergency vehicle’s approach, scaled inversely by distance. | inlane : if emergency_distance > 0: if emergency_phase == index: value[0] += max(0, 200 - emergency_distance) * 10 else: value[0] += waiting * 2 else: value[0] += waiting * 3 outlane : value[0] -= num_vehicle * 0.3 |
| Transit (gen 9) | Bus-priority scoring with amplified waiting weight and density compensation within dispatcher context. | inlane : value[0] += waiting * 4 + vehicles / max(1, dist) outlane : value[0] -= (vehicles / max(1, dist)) * 2 |
| Incident | Incident-aware diversion: prioritize moving vehicles over queued vehicles when lanes are blocked by an incident. | inlane : if incident_blocked > 0: value[0] += max(0, vehicles - waiting) * 5 else: value[0] += waiting * 3 outlane : value[0] -= vehicles * 0.5 |
| Congestion | Nonlinear saturation response with severity-scaled bonus at high congestion levels. | inlane : value[0] += waiting ** 2 if congestion_level > 1: value[0] += waiting * congestion_level * 2 outlane : value[0] += dist * 0.5 |
| Hand-tuned rule, not evolved. | ||
Traffic Signal Control
Traffic signal control studies how to set intersection phases and green times to reduce congestion while balancing safety, emissions, and priority needs.
See all 39 papers