chore(planning): close phase 8 — verification decision recorded (CH as 08.1), direction-split research handoff written
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
---
|
||||
status: resolved_f1_applied
|
||||
trigger: "ALT triangle-inequality potentials made bidirectional A* ~4x SLOWER on real KL graph (p95 38ms -> 152-185ms); L=32 worse than L=24 — implementation-cost smell, not theoretical wall. Diagnose + propose fix inside potential()/per-search setup only."
|
||||
created: 2026-10-25T00:00:00Z
|
||||
updated: 2026-10-25T14:12:00Z
|
||||
---
|
||||
|
||||
## Current Focus
|
||||
|
||||
resolution: F1 APPLIED (post-plan remediation session) — cleanColumnsFor per-graph memo + search-scoped activeL (clean columns only); per-node sentinel skip REMOVED; RC-1 closed. Verification: routing 81/81 (A11 re-spec'd RED-first, A13 new mixed-column case), bot 126/126, typechecks clean, new committed oracle sweep 0/200 suboptimal (seed 0x08f1), CPU back in the pre-ALT band (p95 136.6 ms this host vs 1037.6 pre-fix; honest c3 FAIL vs 8 ms stands), D-03 gate green (0 violations). Commits c071e20 / 2a5a0b5 / 97d6e35. F2 (transposed table) remains FUTURE — only relevant when a clean-column binary is rebuilt; ALT is inert (exact haversine) on the current 0/24-clean binary by design.
|
||||
test: complete — re-specified suite is the standing seal (A9 all-dirty, A10 search-scope skip, A11 F1 structural pins, A12 all-clean ALT-active, A13 subset active list) + measure-astar-oracle-sweep.mjs is the committed fresh-pair optimality pin D-03's fixed corpus structurally cannot be
|
||||
expecting: nothing — RC-1 resolved; RC-2 escalation (c3) unchanged and owned by the maintainer decision recorded in 08-05-SUMMARY
|
||||
next_action: none (maintainer may reopen for F2 when a clean-column binary exists)
|
||||
|
||||
## Symptoms
|
||||
|
||||
expected: ALT bounds dominate haversine bound → fewer expansions → faster or equal CPU (gate p95 budget 8 ms)
|
||||
actual: p95 152.788 ms (25-route gate), 185.692 ms (100-pair); pre-ALT baseline p95 ≈ 38 ms; L=32 probe WORSE (182–204 ms)
|
||||
errors: none (correctness green — A1 625-pair equivalence + D-03 optimality gate pass)
|
||||
reproduction: routing/scripts/measure-route-cpu.mjs over real kl-roadgraph.v1.bin (311k nodes, L=24, coverage 0.980)
|
||||
started: after Phase 8-03 integration of ALT steering into astar.ts
|
||||
|
||||
## Eliminated
|
||||
|
||||
- hypothesis: "ALT reduces expansions; the slowdown is purely per-node cost" — evidence: bench2 counters show expansions INCREASE +15.7% with ALT (66,286 -> 76,671/route; every routed pair; +51% on mid-valley->bukit-bintang); unidirectional control shows the SAME tables PRUNE (0.59x) — the increase is specific to the shared-average BH
|
||||
- hypothesis: "per-search setup fills cause the regression" — evidence: git ea9584d shows hfCache/hbCache/pf/big arrays existed pre-ALT; measured fills 1.7 ms/search; goalToU/origToU are Float64Array(24) — trivial
|
||||
- hypothesis: "NaN-sentinel float compare path is float-heavy" — evidence: violation dump shows sentinel handling is a CORRECTNESS problem (family-set variance), not a cost problem; branches are predictable
|
||||
- hypothesis: "L=32 worse because of table size alone" — evidence: per-node read count scales with L (2×32 vs 2×24 scattered DRAM reads) — consistent with +20% time at L=32; secondary to RC-1/RC-2
|
||||
|
||||
## Evidence
|
||||
|
||||
- timestamp: 2026-10-25T21:05Z
|
||||
checked: git ea9584d (pre-ALT astar.ts) vs current
|
||||
found: pre-ALT potential() = same hfCache/hbCache/pf structure, bound body = pure metersTo; ONLY delta is the ALT L-scan in bound()
|
||||
implication: the entire regression lives inside bound() + its per-search constant tables
|
||||
- timestamp: 2026-10-25T21:10Z
|
||||
checked: real gate measure-route-cpu.mjs run today on this host
|
||||
found: p95 1037.618 ms (campaign record: 152.788) — host ~7x slower than campaign conditions; bench 'shipped' variant 1124 ms = within 8% of real gate today
|
||||
implication: bench faithful; use ratios, quote environment caveat
|
||||
- timestamp: 2026-10-25T21:20Z
|
||||
checked: bench2 (clean per-variant functions, 25-route seeded set, best-of-3)
|
||||
found: hav p50 37.6/p95 363.2/mean 121.2, exp 66,286 | shipped 123.2/899.4/332.7, exp 76,671 (+15.7%) | transposed+merged+pfCache 72.3/546.5/197.3 | no parity failures (bench seconds == shipped seconds all pairs)
|
||||
implication: costlier nodes (~2.5 µs/new-node scan) + more nodes (+15.7%) both real; transposed recovers 41% but still +63% over hav
|
||||
- timestamp: 2026-10-25T21:25Z
|
||||
checked: unidirectional A* probes (hav vs max(hav,ALT))
|
||||
found: uni expansions alt/hav = 0.59 (54,447 -> 31,927 mean); ratio < 1 on 21/23 pairs
|
||||
implication: tables + bounds genuinely prune UNIDIRECTIONALLY; the BH (h_f-h_b)/2 average is what flips the sign
|
||||
- timestamp: 2026-10-25T21:35Z
|
||||
checked: bench3 alpha sweep (max(hav, a*ALT), transposed, real seconds vs shipped)
|
||||
found: a=0.25/0.5 returned +3.48 s suboptimal on sunway-pyramid-to-pavilion-kl (1120.89 vs 1117.41); a=0 and a=1 clean on these 25 pairs
|
||||
implication: intermediate scaling TRIGGERS the latent inconsistency stochastically — a soundness mechanism exists beyond the fromTable issue
|
||||
- timestamp: 2026-10-25T21:45Z
|
||||
checked: bench5/bench6 unit-correct per-edge consistency check + violation dump
|
||||
found: a=0: 0/0 violations | a=0.25: 56 bwd (worst 66.3 s) | a=1 SHIPPED: 334 fwd (worst 1026 s) + 11,369 bwd (worst 774 s); all dumped violations have to[v,l*]=0xffff with to[u,l*] finite; table triangle check: 0 violations on all sampled landmarks
|
||||
implication: per-node sentinel skip = per-node family-set change = the inconsistency; tables exact; code comment's own "no per-node l-mask" law is violated by the sentinel skip
|
||||
- timestamp: 2026-10-25T21:50Z
|
||||
checked: bench7 shipped-vs-Dijkstra-oracle on 200 fresh seeded cross-quadrant pairs
|
||||
found: shipped 1/194 SUBOPTIMAL (+0.278%, pair 141683->292552: 1859.3 vs 1854.1); pure-hav control 0/194
|
||||
implication: RC-1 is a LIVE correctness bug, not just latent; D-03's fixed corpus is insufficient coverage
|
||||
- timestamp: 2026-10-25T21:55Z
|
||||
checked: bench8 landmark column census
|
||||
found: 0/24 clean columns; each column ~6,285-6,292 sentinels (2.02%); 6,285 nodes reach NO landmark (one disconnected island fragment)
|
||||
implication: search-scoped clean-column dropping degrades to exactly haversine on this binary (sound); ALT can only be live on a rebuilt binary whose anchors have sentinel-free columns — impossible while the island exists (08-05's no-filler proof generalizes)
|
||||
|
||||
## Resolution
|
||||
|
||||
root_cause: TWO defects share one surface (potential()/bound() in astar.ts). RC-1 CORRECTNESS: the per-node sentinel skip (`if (tv === 0xffff) continue`) varies the active landmark family set PER NODE — exactly the "per-node ℓ-mask" the code's own comment forbids — so the toTable bound is NOT cross-edge consistent: when node u has finite to[u,ℓ] and neighbor v has to[v,ℓ]=0xffff (v is in the 6,285-node island, 2.02%), h_f(u) stays at the ALT bound (up to 1,905 s) while h_f(v) degrades to haversine → measured 334 forward + 11,369 backward consistency violations on real edges (worst 1,026 s / 774 s); shipped routeAstar returns SUBOPTIMAL seconds on 1/194 fresh random pairs (+0.278% vs Dijkstra oracle, pair 141683->292552); pure-haversine control 0/194. Tables are triangle-exact (0 violations all-landmark sweep) — the skip logic is the bug. D-03 green = sampling luck (recorded pairs avoid the island). RC-2 PERFORMANCE (the 4×): per-new-node bound() = 2 passes x 24 scattered u16 reads (row-major [l*n+v], 622 KB stride) into the 14.9 MB table > 12 MB L3 → DRAM-bound ~2.5 µs/new-node x 77k new nodes ≈ +190-250 ms; PLUS expansions INCREASE +15.7% (all 23 routed pairs; +51% worst) — the shared-Ikeda average (h_f-h_b)/2 flattens the max-composite bound's directional guidance (unidirectional control: SAME bounds PRUNE 0.59x — tables good, BH-average config is the problem). Setup fills 1.7 ms negligible.
|
||||
fix: F1 (soundness, in-constraint, reverseFor-precedent): per-graph WeakMap memo `reachableTo[l] = (to-column has zero 0xffff)` (O(L*n) once per isolate); per-search active-landmark list = clean columns only; bound() loops the active list with NO per-node sentinel branch (constant-table NaN goal/origin checks retained). On the current binary 0/24 columns are clean -> potentials degrade to exactly haversine -> sound (0/194 control), restores pre-ALT CPU band, A1 fixture (strongly connected) keeps ALT live so the logic stays tested. F2 (performance, for future clean-column binaries): transposed toTable [v*L+l] per-graph memo (14.9 MB one-time, 104-192 ms build) + merged single-pass (read tv once, update both bestG/bestO) + single pfCache Float64Array replacing hfCache/hbCache/pf — measured mean 332.7 -> 197.3 ms (-41%), bound cost 3.5 -> 0.31 µs/node, bit-identical potential values (verified: identical expansions + seconds on all 25 pairs). REQUIRED TEST WORK: A9's island-fixture assertions must be re-specified (island columns are dirty -> ALT drops search-scoped everywhere in that fixture); add a strongly-connected fixture for ALT-active coverage; add the bench7-style seeded random-pair Dijkstra-oracle sweep (200 pairs) as a real-graph optimality pin (this is the gate that would have caught RC-1; D-03's fixed corpus cannot).
|
||||
verification: bench1/2/3/5/6/7/8 throwaway harnesses in /tmp/opencode/alt-bench/ (never committed); bench replica parity: shipped seconds matched on all 25 gate pairs, no PARITY FAIL lines; alpha-sweep consistency check (unit-correct) pins the violation mechanism to per-node sentinel skips; alpha=0.25/0.5 reproduced REAL suboptimality (+3.48 s on sunway-pyramid-to-pavilion-kl) proving the mechanism end-to-end; bench8 census: 0/24 clean columns, 6,285 no-reach nodes, 2.02% sentinel rate per column. Environment caveat: this host measured the real gate ~7x slower than the 08-05 campaign record (p95 1037.6 ms today vs 152.788 recorded) — all ratios self-consistent within-session; bench-shipped matched real-gate-today within 8%.
|
||||
files_changed: []
|
||||
Reference in New Issue
Block a user