From 9a200d5839275a70f404713b611516c9b6427d2c Mon Sep 17 00:00:00 2001 From: ShiboSoftwareDev Date: Sun, 2 Aug 2026 12:29:24 +0200 Subject: [PATCH 1/4] fix: preserve hard constraints in greedy final routing --- lib/core.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core.ts b/lib/core.ts index 4d9f13e..57278fb 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -1561,8 +1561,11 @@ export class TinyHyperGraphSolver extends BaseSolver { class GreedyFinalRouteSolver extends TinyHyperGraphSolver { override computeG( currentCandidate: Candidate, - _neighborPortId: PortId, + neighborPortId: PortId, ): number { - return currentCandidate.g + const constrainedCost = super.computeG(currentCandidate, neighborPortId) + return Number.isFinite(constrainedCost) + ? currentCandidate.g + : constrainedCost } } From e197fe2be4a4d427bcc8260265e9fb0ee59c89a8 Mon Sep 17 00:00:00 2001 From: ShiboSoftwareDev Date: Sun, 2 Aug 2026 12:35:29 +0200 Subject: [PATCH 2/4] fix: validate the final route hop --- lib/core.ts | 12 +++++++++- lib/distance-aware-tiny-hypergraph-solver.ts | 25 -------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/lib/core.ts b/lib/core.ts index 57278fb..58e9004 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -608,7 +608,17 @@ export class TinyHyperGraphSolver extends BaseSolver { if (assignedNetId !== -1 && assignedNetId !== state.currentRouteNetId) { continue } - this.onPathFound(currentCandidate) + const g = this.computeG(currentCandidate, neighborPortId) + if (!Number.isFinite(g)) continue + this.onPathFound({ + prevRegionId: currentCandidate.nextRegionId, + nextRegionId: currentCandidate.nextRegionId, + portId: neighborPortId, + g, + h: 0, + f: g, + prevCandidate: currentCandidate, + }) return } if (assignedNetId !== -1 && assignedNetId !== state.currentRouteNetId) { diff --git a/lib/distance-aware-tiny-hypergraph-solver.ts b/lib/distance-aware-tiny-hypergraph-solver.ts index 3064fda..2dfd20e 100644 --- a/lib/distance-aware-tiny-hypergraph-solver.ts +++ b/lib/distance-aware-tiny-hypergraph-solver.ts @@ -46,29 +46,4 @@ export class DistanceAwareTinyHyperGraphSolver extends TinyHyperGraphSolver { this.getPortRoutingCostY(neighborPortId) return baseCost + Math.hypot(dx, dy) * this.DISTANCE_TO_COST } - - override onPathFound(finalCandidate: Candidate): void { - const goalPortId = this.state.goalPortId - if (finalCandidate.portId === goalPortId) { - super.onPathFound(finalCandidate) - return - } - - const g = this.computeG(finalCandidate, goalPortId) - if (!Number.isFinite(g)) return - - const goalHopId = this.getHopId(goalPortId, finalCandidate.nextRegionId) - if (g >= this.getCandidateBestCost(goalHopId)) return - - this.setCandidateBestCost(goalHopId, g) - this.state.candidateQueue.queue({ - prevRegionId: finalCandidate.nextRegionId, - nextRegionId: finalCandidate.nextRegionId, - portId: goalPortId, - g, - h: 0, - f: g, - prevCandidate: finalCandidate, - }) - } } From d53c46c9f2264da927fdb46156cb92682c69e62a Mon Sep 17 00:00:00 2001 From: ShiboSoftwareDev Date: Sun, 2 Aug 2026 12:45:17 +0200 Subject: [PATCH 3/4] test: assert shared final-hop handling --- ...tance-aware-tiny-hypergraph-solver.test.ts | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/distance-aware-tiny-hypergraph-solver.test.ts b/tests/distance-aware-tiny-hypergraph-solver.test.ts index c8740db..bedee08 100644 --- a/tests/distance-aware-tiny-hypergraph-solver.test.ts +++ b/tests/distance-aware-tiny-hypergraph-solver.test.ts @@ -1,12 +1,31 @@ import { expect, test } from "bun:test" import { + type Candidate, DistanceAwareTinyHyperGraphSolver, type TinyHyperGraphProblem, type TinyHyperGraphTopology, } from "lib/index" import { IndexedCandidateHeap } from "lib/indexed-candidate-heap" -test("queues a costed goal candidate before committing the path", () => { +class ObservedDistanceAwareTinyHyperGraphSolver extends DistanceAwareTinyHyperGraphSolver { + computedHops: Array<{ fromPortId: number; toPortId: number; cost: number }> = + [] + + override computeG( + currentCandidate: Candidate, + neighborPortId: number, + ): number { + const cost = super.computeG(currentCandidate, neighborPortId) + this.computedHops.push({ + fromPortId: currentCandidate.portId, + toPortId: neighborPortId, + cost, + }) + return cost + } +} + +test("costs the final goal hop before committing the path", () => { const topology: TinyHyperGraphTopology = { portCount: 2, regionCount: 2, @@ -33,21 +52,22 @@ test("queues a costed goal candidate before committing the path", () => { routeNet: new Int32Array([0]), regionNetId: new Int32Array([-1, -1]), } - const solver = new DistanceAwareTinyHyperGraphSolver(topology, problem, { - DISTANCE_TO_COST: 2, - STATIC_REACHABILITY_PRECHECK: false, - }) + const solver = new ObservedDistanceAwareTinyHyperGraphSolver( + topology, + problem, + { + DISTANCE_TO_COST: 2, + STATIC_REACHABILITY_PRECHECK: false, + }, + ) solver.step() expect(solver.state.candidateQueue).toBeInstanceOf(IndexedCandidateHeap) - const queuedGoal = solver.state.candidateQueue.toArray()[0] - expect(queuedGoal?.portId).toBe(1) - expect(queuedGoal?.g).toBeGreaterThanOrEqual(20) - expect(solver.state.currentRouteId).toBe(0) - - solver.step() - + const finalHop = solver.computedHops.find( + ({ fromPortId, toPortId }) => fromPortId === 0 && toPortId === 1, + ) + expect(finalHop?.cost).toBeGreaterThanOrEqual(20) expect(solver.state.regionSegments[0]).toEqual([[0, 0, 1]]) expect(solver.state.currentRouteId).toBeUndefined() }) From 2ac358656476d25c4a938a75cac464b499d1bdbf Mon Sep 17 00:00:00 2001 From: ShiboSoftwareDev Date: Sun, 2 Aug 2026 12:45:33 +0200 Subject: [PATCH 4/4] test: update hosted greedy safety snapshot --- ...eedy-final-route-hard-constraints.snap.svg | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tests/solver/__snapshots__/greedy-final-route-hard-constraints.snap.svg b/tests/solver/__snapshots__/greedy-final-route-hard-constraints.snap.svg index 5a4ae62..075c2a3 100644 --- a/tests/solver/__snapshots__/greedy-final-route-hard-constraints.snap.svg +++ b/tests/solver/__snapshots__/greedy-final-route-hard-constraints.snap.svg @@ -1,14 +1,13 @@