Files
arah2/routing/test/landmark.test.mjs
T
araharah-executor 5b7a1300c2 feat(08-02): implement the pure landmark compute core — L-suite GREEN
farthestPointAnchors + landmarkTables + landmarkCoverage over the
ĉ metric: edge weights floor-quantized once (Math.floor(seconds/q)),
exact integer Dijkstras (Int32 labels, binary heap, lazy deletion),
0xFFFF sentinel, 65000-quantum loud overflow. Traversal law mirrors
astar.ts verbatim: bit0 edges never relaxed in EITHER direction.

Test-vector corrections caught at GREEN (expectations were wrong,
implementation was right):
- L4: rows index by anchor POSITION (row-major [l*N+v]), not node id
- L6: the c-metric farthest node from corner 0 is interior node 18
  (297q: interior hops cost 2x perimeter), not corner 24 (264q) —
  verified against an independent brute-force Dijkstra
2026-10-25 18:40:17 +08:00

218 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/*
* landmark.test.mjs — the pure landmark compute core gates (L1–L6).
* ------------------------------------------------------------------
* RED first (plan 08-02 task 1): every case fails while
* routing/src/landmark.ts does not exist (lazy-import pattern — each
* case reports its own ERR_MODULE_NOT_FOUND).
*
* The ALT preprocessing core (research Pattern 2): distances live in the
* ĉ metric — edge weights floor-quantized ONCE to fixed 0.1 s units —
* and every table cell is an EXACT integer Dijkstra result. Hand-computed
* expectations below; JS arithmetic is deterministic so the pinned quanta
* (verified: floor(2.55/0.1)=25, floor(3/0.1)=30, floor(1.5/0.1)=15,
* grid perimeter hop=33, interior hop=66) are stable.
*
* L1 anchors — farthestPointAnchors: deterministic, degree>=2
* filtered, well-spread on the 5-node path
* L2 quantum exactness — 2.55 s edge -> 25 quanta; two edges -> 50
* exactly (integer arithmetic, no float drift)
* L3 sentinel + self — unreachable pairs -> 0xFFFF; the landmark's
* own cell = 0 (oneway chain + isolated node)
* L4 asymmetry — to/from rows differ on a oneway graph
* L5 overflow throws — any label > 65000 quanta throws, naming the
* distance and the fix (raise landmarkQuantumS)
* L6 5x5 grid shape — anchors [0, 18] (interior node 18 is the ĉ
* farthest at 297 quanta); coverage 1.0
*
* Run: npm run routing:test (exit 0 = all green)
*/
import { strict as assert } from "node:assert";
import { test } from "./harness.mjs";
let landmarkPromise = null;
function loadLandmark() {
if (!landmarkPromise) landmarkPromise = import("../src/landmark.ts");
return landmarkPromise;
}
/* ————— hand-built vectors ————— */
/** 5-node path 0-1-2-3-4, two-way, 3.0 s per edge (30 quanta @ 0.1). */
function pathInput() {
const edges = [];
for (let u = 0; u < 4; u++) {
edges.push({ from: u, to: u + 1, seconds: 3, flags: 0 });
edges.push({ from: u + 1, to: u, seconds: 3, flags: 0 });
}
return { nodeCount: 5, edges, quantumS: 0.1 };
}
/** Oneway chain 0->1->2 (bit0 mirrors) + isolated node 3, 1.5 s/edge. */
function onewayInput() {
return {
nodeCount: 4,
edges: [
{ from: 0, to: 1, seconds: 1.5, flags: 0 },
{ from: 1, to: 0, seconds: 1.5, flags: 1 }, // blocked mirror
{ from: 1, to: 2, seconds: 1.5, flags: 0 },
{ from: 2, to: 1, seconds: 1.5, flags: 1 }, // blocked mirror
],
quantumS: 0.1,
};
}
/** The fixture builder's exact 5x5 grid shape (25 nodes, 80 edge entries,
* perimeter 60 km/h / interior 30 km/h, one oneway mirror at 3->2). */
async function gridInput() {
const { haversineM } = await import("../src/geo.ts");
const GRID = 5;
const lat = [];
const lng = [];
for (let r = 0; r < GRID; r++) {
for (let c = 0; c < GRID; c++) {
lat.push((3150000 + r * 500) / 1e6);
lng.push((101690000 + c * 500) / 1e6);
}
}
const id = (r, c) => r * GRID + c;
const edges = [];
function addEdge(u, v, perimeter) {
const kmh = perimeter ? 60 : 30;
const meters = haversineM({ lat: lat[u], lng: lng[u] }, { lat: lat[v], lng: lng[v] });
edges.push({ from: u, to: v, seconds: meters / (kmh / 3.6), flags: 0 });
}
for (let r = 0; r < GRID; r++) {
for (let c = 0; c < GRID - 1; c++) {
const perimeter = r === 0 || r === GRID - 1;
addEdge(id(r, c), id(r, c + 1), perimeter);
addEdge(id(r, c + 1), id(r, c), perimeter);
}
}
for (let c = 0; c < GRID; c++) {
for (let r = 0; r < GRID - 1; r++) {
const perimeter = c === 0 || c === GRID - 1;
addEdge(id(r, c), id(r + 1, c), perimeter);
addEdge(id(r + 1, c), id(r, c), perimeter);
}
}
const reverse = edges.find((e) => e.from === 3 && e.to === 2);
reverse.flags = 1; // the fixture's single oneway-reverse mirror
return { nodeCount: GRID * GRID, edges, quantumS: 0.1 };
}
/* ————— L1–L6 ————— */
test("L1 anchors — deterministic, degree>=2-filtered, well-spread on the 5-node path", async () => {
const { farthestPointAnchors } = await loadLandmark();
const input = pathInput();
const a = farthestPointAnchors(input, 2);
assert.ok(a instanceof Uint32Array, "anchors are a Uint32Array");
assert.equal(a.length, 2);
// Candidates (degree>=2) are nodes 1,2,3 — endpoints excluded. First
// anchor = first candidate by id; pass-from-1 minima are [30,0,30,60,90]
// quanta, so the farthest candidate is node 3.
assert.deepEqual([...a], [1, 3], "first candidate by id, then farthest spread");
// L=3: after folding anchor 3, minima are [30,0,30,0,30] -> node 2.
assert.deepEqual([...farthestPointAnchors(input, 3)], [1, 3, 2]);
// Determinism: identical input -> identical anchors (no randomness).
assert.deepEqual([...farthestPointAnchors(pathInput(), 2)], [...a]);
// More anchors than candidates: stop early, never pad.
assert.equal(farthestPointAnchors(input, 10).length, 3, "only 3 candidates exist");
});
test("L2 quantum exactness — floor(2.55/0.1)=25 per edge; two edges sum to exactly 50 quanta", async () => {
const { landmarkTables, LANDMARK_QUANTUM_S } = await loadLandmark();
assert.equal(LANDMARK_QUANTUM_S, 0.1, "the default quantum is the doc'd 0.1 s");
const input = {
nodeCount: 3,
edges: [
{ from: 0, to: 1, seconds: 2.55, flags: 0 },
{ from: 1, to: 0, seconds: 2.55, flags: 0 },
{ from: 1, to: 2, seconds: 2.55, flags: 0 },
{ from: 2, to: 1, seconds: 2.55, flags: 0 },
],
quantumS: 0.1,
};
const { to, from } = landmarkTables(input, Uint32Array.of(0));
assert.equal(from.length, 1 * 3, "row-major L*N cells");
assert.equal(from[0 * 3 + 1], 25, "one 2.55 s edge = exactly 25 quanta (floor(25.4999...))");
assert.equal(from[0 * 3 + 2], 50, "two edges = exactly 50 quanta — integer arithmetic, no drift");
assert.equal(to[0 * 3 + 2], 50, "the to-row mirrors on a two-way path");
});
test("L3 sentinel + self-cell — unreachable pairs -> 0xFFFF; the landmark's own cell = 0", async () => {
const { landmarkTables, LANDMARK_SENTINEL } = await loadLandmark();
assert.equal(LANDMARK_SENTINEL, 0xffff);
const input = onewayInput();
const { to, from } = landmarkTables(input, Uint32Array.of(0));
assert.equal(to[0 * 4 + 0], 0, "landmark's own to-cell is 0");
assert.equal(from[0 * 4 + 0], 0, "landmark's own from-cell is 0");
assert.equal(from[0 * 4 + 1], 15, "0->1 traverses (floor(1.5/0.1)=15 quanta)");
assert.equal(from[0 * 4 + 2], 30, "0->1->2 traverses");
assert.equal(to[0 * 4 + 1], LANDMARK_SENTINEL, "1 cannot reach 0 against the oneway -> sentinel");
assert.equal(to[0 * 4 + 2], LANDMARK_SENTINEL, "2 cannot reach 0 -> sentinel");
// Isolated node: every cell is the sentinel.
assert.equal(to[0 * 4 + 3], LANDMARK_SENTINEL, "isolated node -> sentinel in the to-row");
assert.equal(from[0 * 4 + 3], LANDMARK_SENTINEL, "isolated node -> sentinel in the from-row");
});
test("L4 asymmetry — reversed vs forward rows differ on an oneway graph", async () => {
const { landmarkTables } = await loadLandmark();
const { to, from } = landmarkTables(onewayInput(), Uint32Array.of(1));
// Rows are indexed by anchor POSITION (row-major [ℓ*N+v], ℓ = 0 here),
// not by the anchor's node id. Anchor at the middle node 1: forward
// reaches 2 (15 quanta) but the to-row for 2 is the sentinel (2 cannot
// get back to 1).
assert.equal(from[0 * 4 + 2], 15, "1->2 traverses");
assert.equal(to[0 * 4 + 2], 0xffff, "2->1 does not (oneway) — asymmetry is representable");
assert.notEqual(to[0 * 4 + 2], from[0 * 4 + 2]);
});
test("L5 overflow throws — any label > 65000 quanta fails loud, naming the fix", async () => {
const { landmarkTables, farthestPointAnchors, LANDMARK_MAX_QUANTA } = await loadLandmark();
assert.equal(LANDMARK_MAX_QUANTA, 65000);
// 3-node path so node 1 is a degree-2 candidate — the anchor-selection
// pass actually runs (a 2-node graph has no candidates and returns
// empty before any Dijkstra).
const input = {
nodeCount: 3,
edges: [
{ from: 0, to: 1, seconds: 10, flags: 0 }, // floor(10/0.0001) = 100000 quanta
{ from: 1, to: 0, seconds: 10, flags: 0 },
{ from: 1, to: 2, seconds: 10, flags: 0 },
{ from: 2, to: 1, seconds: 10, flags: 0 },
],
quantumS: 0.0001,
};
assert.throws(
() => landmarkTables(input, Uint32Array.of(0)),
/100000 exceeds 65000 \(raise landmarkQuantumS\)/,
"the table pass names the offending distance and the fix",
);
assert.throws(
() => farthestPointAnchors(input, 1),
/exceeds 65000 \(raise landmarkQuantumS\)/,
"the anchor-selection pass fails loud on the same law",
);
});
test("L6 5x5 grid shape — anchors [0,18]; farthest 297 quanta; coverage 1.0", async () => {
const { farthestPointAnchors, landmarkTables, landmarkCoverage } = await loadLandmark();
const input = await gridInput();
const anchors = farthestPointAnchors(input, 2);
// Farthest-point over the ĉ metric: interior hops cost 66 quanta (2x the
// perimeter's 33), so the farthest node from corner 0 is the INTERIOR
// node 18 at 297 quanta — NOT the opposite corner 24 (264). Verified
// against an independent brute-force Dijkstra (297 unique max).
assert.deepEqual([...anchors], [0, 18], "first candidate is node 0; farthest is node 18");
const { to, from } = landmarkTables(input, anchors);
assert.equal(to.length, 2 * 25, "L*N cells (L=2, N=25)");
assert.equal(to[0 * 25 + 0], 0, "self-cell 0");
assert.equal(from[1 * 25 + 18], 0, "anchor 18's own from-cell 0 (row = position 1)");
assert.equal(from[0 * 25 + 18], 297, "0->18 = 3 perimeter + 3 interior hops = 99+198, exact");
assert.equal(to[1 * 25 + 0], 297, "the to-row mirrors on the grid");
const coverage = landmarkCoverage(to, anchors, 25);
assert.deepEqual(coverage, [1, 1], "the grid is strongly connected — full coverage");
});