Spaces:
Runtime error
Runtime error
File size: 8,299 Bytes
e7a9f02 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | # 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.
|