Skip to content

Commit 4e4c92d

Browse files
Fail pipeline 4 when a node solver returns incomplete routing
1 parent 845c8db commit 4e4c92d

File tree

3 files changed

+104
-3
lines changed

3 files changed

+104
-3
lines changed

lib/solvers/HyperHighDensitySolver/HyperSingleIntraNodeSolver.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
HighDensityIntraNodeRoute,
33
NodeWithPortPoints,
44
} from "lib/types/high-density-types"
5+
import { pointToSegmentDistance } from "@tscircuit/math-utils"
56
import { CachedIntraNodeRouteSolver } from "../HighDensitySolver/CachedIntraNodeRouteSolver"
67
import { IntraNodeRouteSolver } from "../HighDensitySolver/IntraNodeSolver"
78
import {
@@ -324,6 +325,20 @@ export class HyperSingleIntraNodeSolver extends HyperParameterSupervisorSolver<
324325
} else {
325326
routes = solver.solver.solvedRoutes
326327
}
328+
329+
const invalidConnectionName = getFirstConnectionWithUncoveredPortPoint(
330+
routes,
331+
this.nodeWithPortPoints,
332+
)
333+
if (invalidConnectionName) {
334+
solver.solver.solved = false
335+
solver.solver.failed = true
336+
solver.solver.error = `Solver returned partial intra-node routing for connection "${invalidConnectionName}" in node "${this.nodeWithPortPoints.capacityMeshNodeId}"`
337+
this.solved = false
338+
this.winningSolver = undefined
339+
return
340+
}
341+
327342
this.solvedRoutes = routes.map((route) => {
328343
const matchingPortPoint = this.nodeWithPortPoints.portPoints.find(
329344
(p) => p.connectionName === route.connectionName,
@@ -338,3 +353,66 @@ export class HyperSingleIntraNodeSolver extends HyperParameterSupervisorSolver<
338353
})
339354
}
340355
}
356+
357+
const isSamePoint3 = (
358+
a: { x: number; y: number; z: number },
359+
b: { x: number; y: number; z: number },
360+
epsilon = 1e-6,
361+
) =>
362+
Math.abs(a.x - b.x) <= epsilon &&
363+
Math.abs(a.y - b.y) <= epsilon &&
364+
a.z === b.z
365+
366+
const doesRouteCoverPoint = (
367+
route: Array<{ x: number; y: number; z: number }>,
368+
point: { x: number; y: number; z: number },
369+
) => {
370+
for (const routePoint of route) {
371+
if (isSamePoint3(routePoint, point)) return true
372+
}
373+
374+
for (let i = 0; i < route.length - 1; i++) {
375+
const A = route[i]
376+
const B = route[i + 1]
377+
if (A.z !== point.z || B.z !== point.z) continue
378+
if (pointToSegmentDistance(point, A, B) <= 1e-6) return true
379+
}
380+
381+
return false
382+
}
383+
384+
const getFirstConnectionWithUncoveredPortPoint = (
385+
routes: HighDensityIntraNodeRoute[],
386+
nodeWithPortPoints: NodeWithPortPoints,
387+
) => {
388+
const routesByConnection = new Map<string, HighDensityIntraNodeRoute[]>()
389+
for (const route of routes) {
390+
if (!routesByConnection.has(route.connectionName)) {
391+
routesByConnection.set(route.connectionName, [])
392+
}
393+
routesByConnection.get(route.connectionName)!.push(route)
394+
}
395+
396+
const portPointsByConnection = new Map<
397+
string,
398+
Array<{ x: number; y: number; z: number }>
399+
>()
400+
for (const portPoint of nodeWithPortPoints.portPoints) {
401+
if (!portPointsByConnection.has(portPoint.connectionName)) {
402+
portPointsByConnection.set(portPoint.connectionName, [])
403+
}
404+
portPointsByConnection.get(portPoint.connectionName)!.push(portPoint)
405+
}
406+
407+
for (const [connectionName, points] of portPointsByConnection) {
408+
const connectionRoutes = routesByConnection.get(connectionName) ?? []
409+
const coversAllPoints = points.every((point) =>
410+
connectionRoutes.some((route) => doesRouteCoverPoint(route.route, point)),
411+
)
412+
if (!coversAllPoints) {
413+
return connectionName
414+
}
415+
}
416+
417+
return null
418+
}

tests/features/pipeline4-circuit003-cmn3-no-center-via.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ test(
1414
const solver = new AutoroutingPipelineSolver4(circuit003)
1515
solver.solve()
1616

17-
expect(solver.solved).toBe(true)
18-
expect(solver.failed).toBe(false)
19-
2017
const cmn3Meta =
2118
solver.highDensityRouteSolver?.nodeSolveMetadataById.get("cmn_3")
2219
expect(cmn3Meta).toBeDefined()
20+
expect(cmn3Meta?.status).toBe("solved")
2321

2422
const center = cmn3Meta!.node.center
2523
const sourceNet5Routes = (
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { expect, test } from "bun:test"
2+
import * as dataset01 from "@tscircuit/autorouting-dataset-01"
3+
import { AutoroutingPipelineSolver4 } from "lib/autorouter-pipelines/AutoroutingPipeline4_TinyHypergraph/AutoroutingPipelineSolver4_TinyHypergraph"
4+
import type { SimpleRouteJson } from "lib/types"
5+
6+
test(
7+
"pipeline4 circuit004 fails when cmn_35 only has a partial multi-point route",
8+
() => {
9+
const circuit004 = (dataset01 as Record<string, unknown>)
10+
.circuit004 as SimpleRouteJson
11+
const solver = new AutoroutingPipelineSolver4(circuit004)
12+
solver.solve()
13+
14+
expect(solver.solved).toBe(false)
15+
expect(solver.failed).toBe(true)
16+
expect(solver.error).toContain("cmn_35")
17+
18+
const cmn35Meta =
19+
solver.highDensityRouteSolver?.nodeSolveMetadataById.get("cmn_35")
20+
expect(cmn35Meta).toBeDefined()
21+
expect(cmn35Meta?.status).toBe("failed")
22+
expect(cmn35Meta?.error).toContain("partial intra-node routing")
23+
},
24+
{ timeout: 60000 },
25+
)

0 commit comments

Comments
 (0)