Spaces:
Runtime error
A newer version of the Gradio SDK is available: 6.24.0
FlowTwin — what to fix next
Written at the end of the build, from what actually broke. Ordered by value per hour, not by size. Everything here is a known gap, not a wish list.
P0 — before the final submission
1. The optimizer stops separating once the queue exists
The bug. Intervene at T+450 s and the winner is clearly better (peak density 2.45 → 1.74, queue 2,185 → 1,534). Intervene at T+900 s and every strategy returns identical peak density and critical time; the winner is chosen only because staggering reroutes nobody and so scores lowest on the reroute-cost term.
Why. Once a 4,000-person queue exists it drains at the gate rate (380/min)
no matter where new arrivals are sent. Peak density over the window is already
locked in, so J cannot distinguish the candidates. This is physically correct
and it only appeared after the queue-extent fix landed.
Fix. Add an end-of-window term to J — a strategy that leaves the queue
smaller at T+horizon is better even when the peak is identical. Concretely, in
strategy/counterfactual.py the metrics already carry final_density and
final_queue; add them to OBJECTIVES in optimizer.py with weights around
0.10–0.15 and re-run scripts/run_benchmarks.py to confirm the early case does
not regress. Also consider scoring the integral of density over the window
rather than its maximum.
Test to add. Assert separation at both an early and a late intervention time, so this cannot silently return.
2. Verify the Hugging Face path against real weights
Nothing in the perception chain has ever run against downloaded weights — this
sandbox had no access to huggingface.co. On a networked machine:
pip install -r backend/requirements.txt
python scripts/fetch_hf_model.py
Expect the CSRNet candidate to be the risky one: the repo may ship a full module
rather than a state_dict, or use different layer names. perception/huggingface.py
already handles both shapes and falls through to YOLOv8n and then to YOLOS-tiny,
but the fallthrough has not been exercised. Budget an hour. If CSRNet
resists, take the detector path and say so in the pitch — the architecture is
the point, not which checkpoint won.
Then drop two or three crowd photographs into data/perception/ with an
index.json giving each one a zone_id and zone_area_m2, so the Perception
panel has something to click rather than requiring an upload.
3. Commit and push
git init && git add -A && git commit -m "FlowTwin: AI crowd digital twin for F1 venues"
git branch -M main && git remote add origin <url> && git push -u origin main
data/fallback/*.json is gitignored (16 MB of generated frames). Anyone cloning
regenerates it with python scripts/record_fallback.py. The trained model is
committed (3.8 MB) so a fresh clone predicts properly without retraining. If the
repo must stay under a size limit, move the model to a release asset and have
DensityPredictor fall back to the analytic baseline — it already does that
cleanly.
P1 — makes the submission materially stronger
4. Safety mode (compression risk) as a visible mode
The compression-risk proxy is already implemented — density growth, velocity
drop, queue growth and opposing flow all feed the risk score, and
risk_contributions() exposes the per-term breakdown. What is missing is the
proposal's in-event scenario and the framing.
Cheapest useful version: a third scenario on circuit_alpha where the crowd is
already inside and a large fraction converges on one zone (a fan-zone stage).
That is a new JSON in data/scenarios/ plus demand groups pointing at
CONC_NORTH — no engine changes. Add a "SAFETY" toggle to the header that
switches the alert panel to rank by compression risk instead of flow risk.
Keep the Astroworld framing exactly as the proposal states it: a stress-test reference for the type of scenario, never a claim of prevention.
5. Personnel dispatch
The largest genuinely-missing feature from the master proposal. Scope it small:
- A
resourcesblock in the venue JSON: id, type (security/medical), position, response speed. DispatchScore = expected_risk_reduction − λ·response_time − µ·cost, computed against the primary bottleneck.- A map layer for unit positions and a dashed line to the recommended assignment.
- A
dispatchintervention family so it appears in the strategy table alongside the flow levers.
Half a day. High pitch value because it answers "who should act", which no crowd-monitoring product does.
6. Ablation study
The proposal asks for it and it is nearly free — the benchmark harness already supports arms. Add: routing-only, prediction + routing, and the full prediction + counterfactual loop. This is the cleanest possible answer to "which part is actually doing the work", and right now that question has no data behind it.
7. Barcelona's static baseline is identical to shortest path
Both baselines produce byte-identical results because most origin–destination pairs in the reconstructed topology have exactly one sensible route. That is a legitimate finding and the README says so, but the benchmark column looks broken at a glance. Either add a second rail approach so route choice exists, or annotate the table in-place explaining why the two columns match.
P2 — polish, if time remains
- Pitch deck.
docs/DEMO.mdhas the narrative and every real number; it needs to become slides. Slide 7 (results) should be a screenshot of the strategy table, not retyped figures. - Deploy. One box running
uvicornbehind a reverse proxy is enough. Do not containerise for the sake of it; the demo runs from one command today. - Record a screen capture of both demos as the true last-resort fallback, above the replay recordings.
- Agent groups. Families walk together and comply together; a
group_idalready exists in the schema but nothing uses it. - Uncertainty on predictions. Quantile regression would give the alert a confidence band, which is honest and cheap with the existing training script.
- Mobile/tablet layout. Breakpoints exist down to 900 px but have not been exercised on a real device.
Things not to change
Learned the hard way; each of these was a bug once.
- Do not average density over a whole corridor. Per-cell evaluation is why a queue at a gate does not freeze people 200 m back. Reverting collapses throughput to about a tenth.
- Do not let a link accept people until it is physically full. The backward-wave receiving function is what makes congestion spill back instead of a corridor silently absorbing an impossible crowd.
- Do not measure the queue only at the stop line. Measuring it from everyone who has stopped is what lets a gate discharge at its real rate.
- Do not let routes transit grandstands. They are seating bowls. Allowing it deadlocked Barcelona with 18,000 people stranded.
- Do not remove the U-turn guard or the penalty decay. Without them, repeated interventions leave a residue bouncing between two nodes forever.
- Do not put a language model in the decision path. It is the answer to "where is the AI" that judges respect, and the whole explainability story depends on the reasoning being the same arithmetic that produced the score.
- Do not let panels re-render every frame. Frames arrive 5×/second; rebuilding a card restarts its entry animation and the panel goes invisible. This bug shipped once already.
- Do not unpin the thread limits in
flowtwin/__init__.py. A 66-row inference takes 1,000 ms on two threads and 9 ms on one.
Pre-demo checklist
python scripts/build_venues.py # venues regenerate cleanly
cd backend && python -m pytest -q # 70 tests
cd .. && python scripts/ui_check.py # full acceptance path in a browser
python scripts/record_fallback.py # refresh replay insurance
Then, in the dashboard: run Simulation 1, wait for the prediction to read "critical in N seconds" (roughly T+07:30–T+10:00), and press Simulate strategies then — not after the alert has been red for minutes. See P0-1 for why that timing matters until the optimizer is fixed.