Spaces:
Runtime error
Runtime error
| # 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: | |
| ```bash | |
| 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 | |
| ```bash | |
| 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 `resources` block 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 `dispatch` intervention 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.md` has 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 `uvicorn` behind 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_id` already 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. | |
| 1. **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. | |
| 2. **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. | |
| 3. **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. | |
| 4. **Do not let routes transit grandstands.** They are seating bowls. Allowing it deadlocked Barcelona with 18,000 people stranded. | |
| 5. **Do not remove the U-turn guard or the penalty decay.** Without them, repeated interventions leave a residue bouncing between two nodes forever. | |
| 6. **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. | |
| 7. **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. | |
| 8. **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 | |
| ```bash | |
| 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. | |