Congestion Propagation GNN
Predicts each venue zone's crowd density 5 ticks ahead from the current densities and the venue's walkway graph. Trained on synthetic runs from the Crowd Flow Optimiser simulator.
Two-layer GraphSAGE. Message passing is the whole point: a gate backing up should raise the predicted risk of the walkway feeding it, which a per-zone threshold cannot see.
Input
features rows must be in this exact column order:
density, trend, capacity_norm, degree_norm, neighbour_max_density, density_delta
{{"inputs": {{
"node_ids": ["gate-a", "walk", "exit-e"],
"features": [[0.93, 1.0, 0.35, 0.20, 0.44, 0.32],
[0.44, 0.0, 0.55, 1.00, 0.93, 0.04],
[0.05, 0.0, 0.44, 0.40, 0.44, 0.01]],
"edge_index": [[0, 1], [1, 2]]
}}}}
edge_index is [all_sources, all_targets] — PyTorch Geometric's [2, num_edges] layout,
not a list of pairs.
Output
{{"risk": {{"gate-a": 1.0, "walk": 0.8, "exit-e": 0.1}}}}
Note the second zone: 44% full, but scored 0.80 because its neighbour is at 93% and pushing into it. That is the behaviour this model exists to provide.
Consumed by
ai-service/app/clients/hf_gnn_client.py in the Crowd Flow Optimiser, which falls back to a
local linear model whenever this endpoint is unset or unreachable.