Skip to content

Commit 73a6f04

Browse files
Copilotsteffenaxer
andauthored
Compare CH vs SpeedyALT (not Dijkstra); show route before final overlay; info-boxes in video
Java exporter (CHQueryExporter): - Switch to identity graph ordering (SpeedyGraphBuilder.build) - Build SpeedyALTData (16 landmarks) for ALT heuristic - Replace instrumented Dijkstra with instrumented SpeedyALT query (uses ALT estimation, dead-end pruning — matches SpeedyALT.java) - Rename JSON field 'dijkstraEvents' → 'speedyALTEvents' HTML visualization: - Rename all 'Dijkstra' labels → 'SpeedyALT' (title, panels, phase messages, final overlay, video labels) - Support both speedyALTEvents and dijkstraEvents JSON fields (backward compat) - Draw info-box overlay text onto composite canvas during video recording (new drawInfoBoxToCanvas helper with rounded-rect pill background) - Delay final overlay by ~2s after search ends so the route path is visible first (phase 0→1→2 state machine) - Keep recording for 3s after final overlay appears to capture it README: Updated to reflect SpeedyALT comparison Agent-Logs-Url: https://github.com/steffenaxer/matsim-libs/sessions/b1cbd2a4-34d0-4967-9e33-d5d221f864be Co-authored-by: steffenaxer <26229392+steffenaxer@users.noreply.github.com>
1 parent 19f1135 commit 73a6f04

3 files changed

Lines changed: 212 additions & 127 deletions

File tree

matsim/src/main/java/org/matsim/core/router/speedy/CHQueryExporter.java

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
import java.util.Locale;
4242

4343
/**
44-
* Standalone tool that runs an instrumented CH query and Dijkstra query on the
44+
* Standalone tool that runs an instrumented CH query and SpeedyALT query on the
4545
* Berlin network and exports the step-by-step search state as JSON for the
46-
* {@code ch-visualization.html} interactive D3.js visualization.
46+
* {@code ch-visualization.html} interactive visualization.
4747
*
4848
* <h3>Output format ({@code ch-query-data.json})</h3>
4949
* <pre>
@@ -56,7 +56,7 @@
5656
* "odIndex": 0,
5757
* "startNode": 42, "endNode": 7,
5858
* "chEvents": [ { "step":0, "dir":"fwd", "node":42, "cost":0.0, "parent":-1, "stalled":false, "meeting":false }, ... ],
59-
* "dijkstraEvents":[ { "step":0, "node":42, "cost":0.0 }, ... ],
59+
* "speedyALTEvents":[ { "step":0, "node":42, "cost":0.0, "parent":-1 }, ... ],
6060
* "path": [ nodeIdx, ... ],
6161
* "pathLinks": [ { "from": nodeIdx, "to": nodeIdx }, ... ],
6262
* "shortcutTree": { "gIdx":5, "isShortcut":true, "children":[...] }
@@ -111,11 +111,14 @@ public static void main(String[] args) throws Exception {
111111
Network network = NetworkUtils.readNetwork(networkFile);
112112
LOG.info("Network loaded: {} nodes, {} links", network.getNodes().size(), network.getLinks().size());
113113

114-
LOG.info("Building SpeedyGraph with spatial ordering...");
115-
SpeedyGraph baseGraph = SpeedyGraphBuilder.buildWithSpatialOrdering(network);
114+
LOG.info("Building SpeedyGraph (identity ordering)...");
115+
SpeedyGraph baseGraph = SpeedyGraphBuilder.build(network);
116116

117117
FreespeedTravelTimeAndDisutility tc = new FreespeedTravelTimeAndDisutility(new ScoringConfigGroup());
118118

119+
LOG.info("Building SpeedyALTData (16 landmarks)...");
120+
SpeedyALTData altData = new SpeedyALTData(baseGraph, Math.min(16, baseGraph.nodeCount), tc, 4);
121+
119122
LOG.info("Building CHGraph (this may take a minute for the Berlin network)...");
120123
CHGraph chGraph = new CHBuilder(baseGraph, tc).build();
121124
new CHCustomizer().customize(chGraph, tc);
@@ -130,7 +133,7 @@ public static void main(String[] args) throws Exception {
130133
int[][] odNodePairs = resolveODPairs(baseGraph, network, transform);
131134

132135
LOG.info("Exporting query data to: {}", outputFile);
133-
exportJSON(baseGraph, chGraph, transform, odNodePairs, outputFile);
136+
exportJSON(baseGraph, chGraph, altData, transform, odNodePairs, outputFile);
134137
LOG.info("Done. Output: {}", outputFile);
135138
}
136139

@@ -174,7 +177,7 @@ private static int findNearestNode(SpeedyGraph graph, Network network,
174177
// JSON export
175178
// -----------------------------------------------------------------------
176179

177-
private static void exportJSON(SpeedyGraph graph, CHGraph chGraph,
180+
private static void exportJSON(SpeedyGraph graph, CHGraph chGraph, SpeedyALTData altData,
178181
CoordinateTransformation transform,
179182
int[][] odPairs, String outputFile) throws IOException {
180183
try (PrintWriter pw = new PrintWriter(new FileWriter(outputFile))) {
@@ -238,14 +241,14 @@ private static void exportJSON(SpeedyGraph graph, CHGraph chGraph,
238241
}
239242
pw.println(" ],");
240243

241-
// Dijkstra query
242-
DijkstraQueryResult dijResult = runInstrumentedDijkstra(graph, chGraph, startIdx, endIdx);
243-
pw.println(" \"dijkstraEvents\": [");
244-
for (int ei = 0; ei < dijResult.events.size(); ei++) {
245-
DijkstraEvent e = dijResult.events.get(ei);
244+
// SpeedyALT query
245+
SpeedyALTQueryResult altResult = runInstrumentedSpeedyALT(graph, altData, startIdx, endIdx);
246+
pw.println(" \"speedyALTEvents\": [");
247+
for (int ei = 0; ei < altResult.events.size(); ei++) {
248+
SettleEvent e = altResult.events.get(ei);
246249
pw.printf(Locale.US, " {\"step\":%d,\"node\":%d,\"cost\":%.4f,\"parent\":%d}%s%n",
247250
e.step(), e.node(), e.cost(), e.parent(),
248-
ei < dijResult.events.size() - 1 ? "," : "");
251+
ei < altResult.events.size() - 1 ? "," : "");
249252
}
250253
pw.println(" ],");
251254

@@ -518,63 +521,90 @@ private static void unpackEdgeToPath(CHGraph chGraph, SpeedyGraph baseGraph,
518521
}
519522

520523
// -----------------------------------------------------------------------
521-
// Instrumented Dijkstra query (based on SpeedyDijkstra.calcLeastCostPathImpl)
524+
// Instrumented SpeedyALT query (based on SpeedyALT.calcLeastCostPathImpl)
522525
// -----------------------------------------------------------------------
523526

524-
private static DijkstraQueryResult runInstrumentedDijkstra(SpeedyGraph graph, CHGraph chGraph,
525-
int startIdx, int endIdx) {
527+
private static SpeedyALTQueryResult runInstrumentedSpeedyALT(SpeedyGraph graph,
528+
SpeedyALTData altData,
529+
int startIdx, int endIdx) {
526530
int n = graph.nodeCount;
527-
double[] cost = new double[n];
528-
int[] iterIds = new int[n];
529-
int[] comingFrom = new int[n];
530-
int[] usedLink = new int[n];
531+
double[] cost = new double[n];
532+
int[] iterIds = new int[n];
533+
int[] comingFrom = new int[n];
531534
Arrays.fill(iterIds, 0);
532535

533536
int iteration = 1;
534537
DAryMinHeap pq = new DAryMinHeap(n, 6);
535538

539+
int startDeadend = altData.getNodeDeadend(startIdx);
540+
int endDeadend = altData.getNodeDeadend(endIdx);
541+
542+
double estimation = estimateMinCost(altData, startIdx, endIdx);
543+
536544
cost[startIdx] = 0.0;
537545
comingFrom[startIdx] = -1;
538546
iterIds[startIdx] = iteration;
539-
pq.insert(startIdx, 0.0);
547+
pq.insert(startIdx, 0.0 + estimation);
540548

541-
List<DijkstraEvent> events = new ArrayList<>();
549+
List<SettleEvent> events = new ArrayList<>();
542550
int step = 0;
543551
SpeedyGraph.LinkIterator outLI = graph.getOutLinkIterator();
544552

545553
while (!pq.isEmpty()) {
546554
int nodeIdx = pq.poll();
547555
double currCost = cost[nodeIdx];
548556

549-
events.add(new DijkstraEvent(step++, nodeIdx, currCost, comingFrom[nodeIdx]));
557+
events.add(new SettleEvent(step++, nodeIdx, currCost, comingFrom[nodeIdx]));
550558

551559
if (nodeIdx == endIdx) break;
552560

561+
// Dead-end pruning (same as SpeedyALT)
562+
int deadend = altData.getNodeDeadend(nodeIdx);
563+
if (deadend >= 0 && deadend != startDeadend && deadend != endDeadend) {
564+
continue;
565+
}
566+
553567
outLI.reset(nodeIdx);
554568
while (outLI.next()) {
555-
int toNode = outLI.getToNodeIndex();
569+
int toNode = outLI.getToNodeIndex();
556570
double travelTime = outLI.getFreespeedTravelTime();
557-
double newCost = currCost + travelTime;
571+
double newCost = currCost + travelTime;
558572

559573
if (iterIds[toNode] == iteration) {
560574
if (newCost < cost[toNode]) {
561575
cost[toNode] = newCost;
562576
comingFrom[toNode] = nodeIdx;
563-
usedLink[toNode] = outLI.getLinkIndex();
564-
pq.decreaseKey(toNode, newCost);
577+
estimation = estimateMinCost(altData, toNode, endIdx);
578+
pq.decreaseKey(toNode, newCost + estimation);
565579
}
566580
} else {
567581
cost[toNode] = newCost;
568582
comingFrom[toNode] = nodeIdx;
569-
usedLink[toNode] = outLI.getLinkIndex();
570583
iterIds[toNode] = iteration;
571-
pq.insert(toNode, newCost);
584+
estimation = estimateMinCost(altData, toNode, endIdx);
585+
pq.insert(toNode, newCost + estimation);
572586
}
573587
}
574588
}
575589

576-
LOG.info("Dijkstra query {}->{}: {} events settled", startIdx, endIdx, events.size());
577-
return new DijkstraQueryResult(events);
590+
LOG.info("SpeedyALT query {}->{}: {} events settled", startIdx, endIdx, events.size());
591+
return new SpeedyALTQueryResult(events);
592+
}
593+
594+
/**
595+
* ALT lower-bound estimation: max over all landmarks of (SL-TL) and (LT-LS).
596+
*/
597+
private static double estimateMinCost(SpeedyALTData altData, int nodeIdx, int destinationIdx) {
598+
double best = 0;
599+
for (int i = 0, nL = altData.getLandmarksCount(); i < nL; i++) {
600+
double sl = altData.getTravelCostToLandmark(nodeIdx, i);
601+
double ls = altData.getTravelCostFromLandmark(nodeIdx, i);
602+
double tl = altData.getTravelCostToLandmark(destinationIdx, i);
603+
double lt = altData.getTravelCostFromLandmark(destinationIdx, i);
604+
double estimate = Math.max(sl - tl, lt - ls);
605+
if (estimate > best) best = estimate;
606+
}
607+
return best;
578608
}
579609

580610
// -----------------------------------------------------------------------
@@ -607,7 +637,7 @@ private static void writeShortcutNode(PrintWriter pw, CHGraph chGraph, int gIdx,
607637
private record CHEvent(int step, String dir, int node, double cost, int parent,
608638
boolean stalled, boolean meeting) {}
609639

610-
private record DijkstraEvent(int step, int node, double cost, int parent) {}
640+
private record SettleEvent(int step, int node, double cost, int parent) {}
611641

612642
private static class CHQueryResult {
613643
final List<CHEvent> events;
@@ -624,9 +654,9 @@ private static class CHQueryResult {
624654
}
625655
}
626656

627-
private static class DijkstraQueryResult {
628-
final List<DijkstraEvent> events;
629-
DijkstraQueryResult(List<DijkstraEvent> events) { this.events = events; }
657+
private static class SpeedyALTQueryResult {
658+
final List<SettleEvent> events;
659+
SpeedyALTQueryResult(List<SettleEvent> events) { this.events = events; }
630660
}
631661

632662
// -----------------------------------------------------------------------

matsim/tools/ch-viz/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CH Router Visualization
22

3-
Interactive Leaflet.js and canvas visualization of the Contraction Hierarchies (CH) routing algorithm vs Dijkstra on the Berlin road network.
3+
Interactive Leaflet.js and canvas visualization of the Contraction Hierarchies (CH) routing algorithm vs SpeedyALT (A* with Landmarks) on the Berlin road network.
44

55
Created by Steffen Axer ([iteratively.io](https://iteratively.io)) to demonstrate CH routing efficiency for LinkedIn.
66

@@ -48,7 +48,7 @@ Most browsers block `fetch()` from `file://` URLs. Use a local server as above,
4848

4949
## Visualization Features
5050

51-
- **Split-screen** Dijkstra vs CH comparison (synchronized pan/zoom via Leaflet maps)
51+
- **Split-screen** SpeedyALT vs CH comparison (synchronized pan/zoom via Leaflet maps)
5252
- **OSM tile background** — dark-themed OpenStreetMap tiles with WGS84 coordinates
5353
- **Play / Pause / Step** controls with speed slider (1×–50× events per frame)
5454
- **Step slider** — drag to seek forward/backward through events; forward (▸) and backward (◂) step buttons
@@ -68,14 +68,14 @@ Most browsers block `fetch()` from `file://` URLs. Use a local server as above,
6868
| Network edges | `rgba(255,255,255,0.09)` |
6969
| CH forward search | `#38bdf8` (sky blue) |
7070
| CH backward search | `#f472b6` (pink) |
71-
| Dijkstra settled | `#ef4444` (red) |
71+
| Dijkstra/SpeedyALT settled | `#ef4444` (red) |
7272
| Final path | `#34d399` (emerald) |
7373
| Meeting point | `#fbbf24` (amber) |
7474
| Stalled nodes | `#64748b` (slate) |
7575

7676
## OD Pairs
7777

78-
| Route | ~Dijkstra settled | ~CH settled | Savings |
78+
| Route | ~SpeedyALT settled | ~CH settled | Savings |
7979
|---|---|---|---|
8080
| Spandau → Marzahn (West-East) | ~8,000–12,000 | ~200–350 | ~97% |
8181
| Frohnau → Lichtenrade (North-South) | ~8,000–12,000 | ~200–350 | ~97% |

0 commit comments

Comments
 (0)