Tasks/Transport & Logistics/Signals & Road Networks

City-Wide Signal Coordination

Retime ~600 signalised junctions across a whole city

citywide_signal_coordination Transport & Logistics Signals & Road Networks
instruction.mdthis is what the agent is given

You inherit a full-city traffic microsimulation — a real metropolitan road network with ~600 signalised junctions and a synthetic morning-peak demand — running on its original, un-optimised static signal timing. Improve the signal control so the whole city moves better under load, minimising this frozen cost (the exact definition lives in sim/eval_core.py):

Cost =     1 · Σ timeLoss     seconds of delay over arrived vehicles
     +   160 · teleports      SUMO teleports, charged the time-to-teleport
     +    20 · Σ stops        number of stops (energy / emission proxy)
     +  3600 · unfinished     vehicles still in-network or pending at END

Note the last term: 3600 · unfinished is the largest component at every load in this band, so serving vehicles at all matters more than shaving delay off the ones that already get through. You submit a signal program and/or an online controller; it is re-run on sealed hidden load and seed settings and scored by the same frozen cost, lower is better.

Hard Constraints

  • You may change signals only — phase selection, phase durations, and offsets. You may not modify the network, the demand, or vehicle models.
  • Your submission is the directory methods/main/. It may contain:
  • controller.py exposing class Controller with setup(conn) (called once with the live libsumo connection) and step(t) (called every simulation step), and/or
  • tls.add.xml, a static tlLogic program loaded as a SUMO additional file. It may only contain tlLogic/phase/param elements. The verifier snapshots the file and checks the snapshot against that whitelist before the simulation starts, so any other additional-file element (rerouter, calibrator, ...) — which would change the demand or the network — makes the run invalid (baseline score for every case).
  • Signal programs are handed to SUMO as-is: it is the simulator, not a separate rule checker, that decides what a program does. There is no min-green / yellow-transition / conflict-matrix / cycle-length validator — a program SUMO accepts will run, and you are scored on the traffic it produces, not on whether it looks conventional. A solution that crashes, or whose additional-file is rejected, scores the baseline for that case.
  • An online controller must be light: per-step decisions run inside the sim loop.

What You Have

  • sim/eval_core.py — the exact, frozen scoring core (the cost formula, demand window, and SUMO flags). Read it to understand precisely how you are scored.
  • methods/main/controller.py — the weak baseline you edit (a no-op: the city runs its original static timing). methods/main/tls_info.json — per-junction phase/link structure you can use to build programs or controllers.
  • selfcheck.py — runs your current solution on the visible load settings (scale 0.25 and 0.37) and prints the raw cost. Iterate against it.
  • The full city network and demand are in the container, under $SCENARIO_DATA.

What You Submit

Your edited methods/main/ (a controller.py and/or a tls.add.xml, plus any helper files it reads). The verifier re-runs it on the hidden settings using only the contract above.

How It Is Judged

The sealed verifier runs your solution on several hidden (load-scale, seed) cases drawn from the same load band as the visible ones — some lighter, some heavier — and recomputes the frozen cost per case. Each case's cost is then divided by that case's own no-op baseline cost (the inherited static timing on the identical settings) before averaging — so no single heavy case carries hidden extra weight, and the no-op baseline scores exactly 1.0 by construction. Lower mean normalised cost is better; your score rises monotonically as it drops. Beating the inherited static timing is the goal, and the headroom is real: the inherited timing is a fixed plan that never looks at a queue, on a network where queues build very unevenly. It is also a genuine local optimum — a change that helps one corridor routinely costs more somewhere downstream, so a method has to be judged city-wide, not junction by junction.

Time budget. The budget is simulation time, not wall clock: every run simulates exactly the fixed window in sim/eval_core.py (BEGIN=25200 to END=28200, demand until DEMAND_END=27000), then stops. Vehicles not served by END are charged W_UNFINISHED = 3600 s each — a smooth penalty, not a disqualification — so the cost of a given submission is bit-identical on any machine. Separately, each hidden case has a 1200 s wall-clock backstop: a pathologically slow controller that overruns it scores the baseline (normalised 1.0) for that case. Honest controllers with light per-step work finish well inside it.

Metric

mean normalised cost over the six sealed (scale, seed) cases · lower is better

Cost = timeLoss + 160*teleports + 20*stops + 3600*unfinished, each case divided by its own no-op cost.

anchorvisible setheld-outreward
Binherited no-op static timing1.0000001.0000000.00
Rreference: acyclic max-pressure0.9435110.9446540.30
normalisation
m >= B0
B > m >= R0.3 * (B - m) / (B - R)
m < R1 - 0.7 * y^(-3/7), y = (B - m) / (B - R)

m = this run's held-out metric  ·  B = inherited no-op static timing  ·  R = reference: acyclic max-pressure

B = 1.0 exactly, R = 0.944654. Cases are averaged, then mapped once. No SOTA/UPPER: past R a soft cap nears 1.0 without reaching it.

Rollouts

121 minwall clock
$27.60spend
46.4Mtokens
35versions, 8 kept
0 5M 10M 15M 20M 25M $0 $7.5 $15 $22 cumulative spend on the run self-check cost at scale 0.25 / seed 42, lower is better reference: acyclic max-pressu… · visible · 0.943511 v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33 v34
keptrevertedno scoreturning point
  1. v0Inherited no-op static timing22,420,3265 min · $0.51
  2. v1Batched queue-pressure controller; setup crashed on a stale lane in tls_infosetup failed6 min · $0.69
  3. v2Same controller, stale metadata lanes filtered against SUMO's live lane list23,207,6057 min · $0.77
  4. v3Every controlled movement held at permanent priority green, no phases at allStop scheduling the city, let SUMO's right-of-way rules arbitrate: with nothing ever red, the 4-9 s inter-green budget goes.17,281,81711 min · $1.34
  5. v4All movements permissive green instead, arbitration left entirely to SUMO18,224,41915 min · $1.88
  6. v5Hybrid: 'g' for the 292 links never priority-green in the source plan, 'G' else17,229,26217 min · $2.16
  7. v6Permissive everywhere, protected priority rotating through conflict groups18,076,95521 min · $2.77
  8. v7Promote permissive-only links to 'G' at queue >= 10; never triggers17,229,26224 min · $3.12
  9. v8Promotion hysteresis lowered to queue >= 3 / <= 10.37 only: 34,741,74028 min · $3.74
  10. v9Promote on any halted vehicle; audit finds all 292 such links are U-turns0.37 only: 34,741,74030 min · $4.11
  11. v10Normal movements always 'G', U-turns always red17,690,51432 min · $4.55
  12. v11U-turns set to the unsignalised off state 'O'17,281,81734 min · $4.86
  13. v12U-turns set to the blinking off state 'o'17,229,26235 min · $5.17
  14. v13The 10 most-used U-turn links promoted to 'G', the rest left permissive17,432,31637 min · $5.49
  15. v1490 U-turns used in the demand window kept permissive, the other 202 held redUse the signals to delete demand rather than order it: a U-turn no declared route uses is pure conflict, so it goes red.17,154,58839 min · $5.85
  16. v15Pruning generalised: all 3,402 controlled links absent from routes held red17,373,31343 min · $6.67
  17. v16Route-unused normal links made permissive; unused U-turns still red17,256,15945 min · $7.04
  18. v17Permissive U-turn set widened from 90 to the 110 links used before END17,387,15747 min · $7.42
  19. v18Two declared-window occurrences required to keep a U-turn permissive (66)17,275,76949 min · $7.90
  20. v19Demand-actuated U-turn draft; an apply_patch directive was left in the filedid not compile51 min · $8.38
  21. v20Fixed: U-turn red until a vehicle reports that link as its next17,690,51452 min · $8.45
  22. v21'g' for any movement permissive in an original phase, 'G' only if protected17,423,86854 min · $8.87
  23. v22All 1,848 U-turn indices classified; 599 demand-used permissive, rest red17,338,47855 min · $9.32
  24. v23Downstream spill-back gating at 75% occupancy; no lane meets the trigger17,154,58857 min · $9.75
  25. v24180 s all-red drain pulse draft; setup never cached `conn`, switch failedcontroller failed65 min · $11.41
  26. v25Fixed 180 s all-red pulse at t=27400, then full service restored18,424,75366 min · $11.74
  27. v26All g-only U-turns permissive but the 20 used only after the demand cutoff17,095,07268 min · $12.22
  28. v27From v5, switch permanently to all-'G' once 6,000 vehicles are live17,229,26284 min · $16.38
  29. v28Switch threshold lowered to 5,500 live vehicles17,353,50489 min · $17.54
  30. v29Switch at 5,000 vehicles but only before t=2600017,338,15793 min · $18.72
  31. v30Switch deadline tightened to t=258000.37 only: 35,336,91594 min · $19.18
  32. v31First-step classifier: all-'G' when getMinExpectedNumber() >= 14, else hybridRead the load on step 1 and reproduce v5 when light, v3 when heavy, dropping the path dependence that sank v27-v30.17,229,26298 min · $20.02
  33. v32Classifier threshold moved from 14 to 13, the measured v5/v3 crossover17,229,262113 min · $24.93
  34. v33Behaviour-preserving cleanup: three stale helper JSON files removed17,229,262113 min · $25.03
  35. v34Documentation only; final selfcheck 17,229,262 / 34,684,80117,229,262117 min · $26.26

Thirty-five snapshots in 121 min at $27.60; fourteen are U-turn bookkeeping. v14 led twelve versions until the seed-43 holdout sent it back to v5.

On the hidden set

held-out metricreward
inherited no-op static timing1.0000000.00
reference: acyclic max-pressure0.9446540.30
this run0.8135430.5841
533 minwall clock
$26.29spend
35.6Mtokens
11versions, 9 kept
19M 20M 21M 22M 23M 24M $0 $7.5 $15 $22 cumulative spend on the run self-check cost at scale 0.25 / seed 42, lower is better reference: acyclic max-pressu… · visible: 0.943511 · off this scale v0 v1 v2 v3 v7 v8 v9 v11 v12 v13 v13-final
keptrevertedno scoreturning point
  1. v0Inherited no-op: the city's original static timing22,420,3260 min · $0.21
  2. v1Every inherited program switched to SUMO `actuated`, minDur 5 s23,191,83215 min · $1.27
  3. v2Static, all yellows capped at 3 s; cycle 110 s to 86 s, greens unchangedLost time, not phase choice, is the binding constraint: a shorter inter-green buys 25% more green seconds per hour city-wide.21,563,79720 min · $1.48
  4. v3Adaptive controller: queue-driven phase choice with a spill-back term23,910,30848 min · $2.58
  5. v7Static Webster plan from movement counts parsed out of demand.rou.xmlAdaptivity arrives ~1,000 fewer vehicles at load, and 3600 s each makes that a loss. Size the plan offline from real demand.20,033,305148 min · $7.91
  6. v8Inter-greens rebuilt; zero-flow and under-20-vehicle greens dropped19,274,562205 min · $10.37
  7. v9Safe drop rule: drop a phase only if its movements stay green elsewhere19,746,179298 min · $14.72
  8. v11Three plans per junction, picked on step 2 from SUMO's route look-aheadOne plan cannot serve scale 0.15 and 0.50 at once. The step-2 load read is ~1% accurate and switching there costs nothing.19,175,809417 min · $20.31
  9. v12p2/p3 band boundary moved from 0.43 to 0.40, the measured crossover19,175,809473 min · $22.47
  10. v13Plausibility guard on the look-ahead estimate, falling back to departures19,175,809506 min · $25.11
  11. v13-finalFinal-state copy of the submitted directory; identical to v1319,175,809532 min · $25.89

Eleven snapshots over 533 min and $26.29. The adaptive detour v3 to v7 cost 99 min; about forty further variants were measured, never snapshotted.

On the hidden set

held-out metricreward
inherited no-op static timing1.0000000.00
reference: acyclic max-pressure0.9446540.30
this run0.881290.4953
230 minwall clock
-spend
-tokens
24versions, 7 kept
19.50M 20.25M 21.00M 21.75M 22.50M 0 30 60 90 120 agent step (this harness reports no tokens or timestamps) self-check cost at scale 0.25 / seed 42, lower is better reference: acyclic max-pressu… · visible: 0.943511 · off this scale v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v17 v18 v19 v20 v21 v22 v23
keptrevertedno scoreturning point
  1. v0Inherited no-op static timing22,420,326
  2. v1New plans loaded as programID 0; SUMO rejects the duplicate idload crashed
  3. v2Same plans as programID 'a', all 625 junctions switched to actuated22,098,157
  4. v3607 multi-phase plans collapsed to 2-3 actuated majors, green 8-50 sEach short protected phase is a subset of a major's permissive green, so dropping it buys green seconds and blocks no movement.19,653,909
  5. v4Demand-weighted splits on the 2-phase plans: budget 80 s, floor 14 s, ceil 58 s19,694,986
  6. v5Yellow 4 s to 3 s19,509,303
  7. v6Gap actuation replaced by delay_based, detector range 80 m19,758,001
  8. v7Maximum green 50 s to 60 s, for extend room on the heavy case19,538,142
  9. v8Minimum green 8 s to 6 s, yellow back to 3 s, maximum green 60 s19,447,857
  10. v9MST green-wave offsets, 246 junctions under 123 travel-time constraintsActuation only sees one junction; offsets are the one city-wide term, chaining the corridors into a tree of aligned arrivals.19,338,818
  11. v10Offsets with maximum green 70 s0.37 only: 38,295,102
  12. v11Offsets with minimum green 5 s19,375,947
  13. v12Spill-back cutoff on top of v9, at 58% destination occupancy; never fires19,338,818
  14. v13Static 60/3 s two-phase plan with offsets and a max-pressure early cut22,740,650
  15. v14Static 31/3 s two-phase cycle with offsets, actuation removed20,986,713
  16. v15Actuation max-gap 3.0 s to 2.5 s19,381,666
  17. v16Actuation max-gap 3.0 s to 3.5 s19,507,006
  18. v17Yellow 4 s where the source transition was 7 s or longer, 3 s elsewhere19,393,029
  19. v18Queue-steal draft subscribed to TL_PHASE_INDEX, which returns nothing19,338,818
  20. v19Steal the green when a rival approach leads the served one by 3 after 7 sPut one feedback term back on the fixed offsets: let a starved approach cut in instead of waiting the gap timer out.19,142,622
  21. v20Steal gated to junctions with at most 4 halted on green and 12 in total19,142,622
  22. v21Steal restricted to zero-offset junctions, leaving the green wave alone19,448,254
  23. v22Lagging-left leftover green kept through the 3 s transition, not all-yellow0.37 only: 38,284,816
  24. v23Steal gated on fewer than 6,200 live vehicles19,142,622

Twenty-four snapshots over 230 min; this harness reports no token or cost figures. v9 held the lead for the last fifteen versions, all reverted.

On the hidden set

held-out metricreward
inherited no-op static timing1.0000000.00
reference: acyclic max-pressure0.9446540.30
this run0.9049960.4447
108 minwall clock
$1.29spend
6.4Mtokens
8versions, 4 kept
20.4M 20.8M 21.2M 21.6M 22.0M 22.4M $0 $0.2 $0.5 $0.8 $1 cumulative spend on the run self-check cost at scale 0.25 / seed 42, lower is better reference: acyclic max-pressu… · visible: 0.943511 · off this scale v1 v2 v3 v4 v5 v6 v7 v8
keptrevertedno scoreturning point
  1. v1Out-of-process max-pressure controller, min green 15 s, yellow 4 sDrop the fixed 2013 plan for feedback: each of the 625 junctions serves whichever movement group holds the most queue now.22,339,39934 min · $0.52
  2. v2Pressure weighted by lane waiting time at 0.1, against side-street starvationRaw pressure never serves a short queue, so add accumulated delay and let a starved approach outrank a busy one eventually.21,101,44341 min · $0.60
  3. v3Minimum green 15 s to 10 s20,618,36449 min · $0.67
  4. v4Waiting-time weight 0.1 to 0.220,896,59264 min · $0.78
  5. v5Waiting-time weight 0.1 to 0.0520,718,21864 min · $0.74
  6. v6Switch threshold 2 to 5 vehicles, blocking premature phase changes20,537,29372 min · $0.92
  7. v7Switch threshold 5 to 10 vehicles20,987,83279 min · $0.98
  8. v8Yellow 4 s to 3 s21,299,137107 min · $1.18

Eight snapshots in 108 min at $1.29, all one controller with four knobs swept. Only the light 0.25 case was ever logged; v6 shipped.

On the hidden set

held-out metricreward
inherited no-op static timing1.0000000.00
reference: acyclic max-pressure0.9446540.30
this run0.9339480.3511
214 minwall clock
$54.60spend
94.7Mtokens
39versions, 10 kept
39.0M 40.5M 42.0M 43.5M 45.0M $0 $15 $30 $45 cumulative spend on the run self-check cost at scale 0.37 / seed 42, lower is better reference: acyclic max-pressu… · visible: 0.943511 · off this scale v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v35 v37 v42 v43 v44 v45
keptrevertedno scoreturning point
  1. v0Inherited no-op static timing41,969,9896 min · $0.65
  2. v1Decentralised max-pressure every 5 s, 10 s min green, protected states only42,960,14314 min · $1.59
  3. v2Acyclic pressure with a 5 s explicit clearance state before each switch45,384,11034 min · $4.58
  4. v3Pressure until t=26100, then every TLS restored to its inherited clock phaseAdaptivity pays only inside the demand window; after it the inherited fixed plan drains the network better.40,766,02134 min · $4.77
  5. v4Same hybrid, handoff moved later to t=2640041,829,66835 min · $4.83
  6. v5Same hybrid, handoff at t=26700; best light cutoff, weakest heavy42,150,19235 min · $4.83
  7. v6Handoff releases each green into its local sequence, no clock resync41,199,91946 min · $6.88
  8. v7Exact-clock restoration pulled earlier to t=2580040,763,03457 min · $9.05
  9. v8Restoration at t=2595040,962,303$9.66
  10. v9Restoration at t=2625041,572,289$10.28
  11. v10Restoration at demand-end t=270000.25 only: 22,225,49066 min · $10.89
  12. v11Restoration at t=273000.25 only: 22,633,46066 min · $10.89
  13. v12Handoff time read off the live vehicle count at t=25800, 4700-6900 mapped linearly40,763,03472 min · $12.21
  14. v13Minimum green raised 10 s to 15 s40,683,663$14.90
  15. v14Minimum green raised 10 s to 20 s41,293,066$17.60
  16. v15Decision cadence 5 s to 10 s, minimum green left at 10 s40,359,380105 min · $20.29
  17. v16Decision cadence 15 s40,185,29696 min · $17.95
  18. v17Decision cadence 20 s40,934,234$18.34
  19. v1810 s cadence with 20 s minimum green40,782,230$18.73
  20. v19Start at 15 s cadence, drop to 10 s under 3,500 running at t=255000.25 only: 22,415,979101 min · $19.12
  21. v20Start at 10 s cadence, rise to 15 s for heavy cases at t=2550040,507,595105 min · $20.29
  22. v21Pure stopped-queue pressure; moving-vehicle weight 0.25 to 040,296,379116 min · $23.30
  23. v22Moving-vehicle weight raised 0.25 to 0.540,523,498$24.53
  24. v23Downstream pressure weight halved 1.0 to 0.540,856,994$25.77
  25. v24Switch margin 0.75 to 040,443,712$27.01
  26. v25Switch margin 0.75 to 1.540,641,318$28.24
  27. v26Switch margin 0.75 to 3.040,908,228$29.48
  28. v27Pressure scored on protected 'G' movements only, permissive 'g' excluded43,601,815$30.72
  29. v28Lanes counted once per phase instead of once per controlled link43,680,337$31.95
  30. v29Protected-only scoring plus unique-lane counting43,161,337$33.19
  31. v30Back to static: every odd transition phase capped at 6 s, cycles shortened41,015,965$34.43
  32. v31Transition phases capped at 4 sThe binding cost is inter-green lost time in the fixed plan, not phase order; cutting it beats every feedback law tried.39,755,126$35.67
  33. v32Transitions capped at 4 s, each saved second given to the preceding green40,176,375$36.90
  34. v35Transitions capped at 2 s38,510,229166 min · $38.14
  35. v37Transitions capped at 1 s38,448,213179 min · $42.05
  36. v42Transitions capped at 1.5 sPick on untuned cases, not the tuning mean: v42 loses to v37 on the visible pair and wins on all three held-out ones.38,253,945186 min · $44.53
  37. v43Both plans shipped; start at 1 s, clock-align heavy cases onto 1.5 s at t=2526038,721,020189 min · $45.47
  38. v44Submission cleanup: no-op controller removed, static 1.5 s plan the only mechanism38,253,945208 min · $52.44
  39. v45Cleanup only: stale bytecode and tls_info.json removed, XML hash unchanged38,253,945213 min · $54.36

Thirty-nine snapshots, 214 min, $54.60. Twenty-nine chase adaptive pressure; the winner is the static branch v30-v42, a sweep of the inter-green cap.

On the hidden set

held-out metricreward
inherited no-op static timing1.0000000.00
reference: acyclic max-pressure0.9446540.30
this run0.9386150.3304