Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e2b7700
feat: add Pipeline9 preloaded trace graph routing
seveibar Jul 23, 2026
271eee6
chore: satisfy Pipeline9 format check
seveibar Jul 23, 2026
b83758f
fix Pipeline9 preloaded trace routing
seveibar Jul 24, 2026
1b9803d
fix Pipeline9 regression test formatting
seveibar Jul 24, 2026
602c642
fix Pipeline9 relaxed DRC on srj23
seveibar Jul 24, 2026
8137e3b
fix Pipeline9 prerouted trace DRC and repair
seveibar Jul 25, 2026
42758dc
feat: replace Pipeline9 ijump repair with B01
seveibar Jul 26, 2026
c109fdd
fix: complete Pipeline9 B01 DRC repair
seveibar Jul 26, 2026
c88b69d
fix: preload Pipeline9 traces onto graph ports
seveibar Jul 27, 2026
83441a1
fix: keep duplicated graph ports unassigned
seveibar Jul 27, 2026
a0be700
fix: preload Pipeline9 fixed graph occupancy
seveibar Jul 27, 2026
d3c313c
fix: complete Pipeline9 fixed-copper DRC repair
seveibar Jul 27, 2026
bbfe76e
fix preloaded trace layer visualization
seveibar Jul 27, 2026
735f279
fix: preload Pipeline9 routes as graph assignments
seveibar Jul 27, 2026
9dedf77
chore: format Pipeline9 regression tests
seveibar Jul 27, 2026
8e6c590
fix: stabilize Pipeline9 SRJ23 validation
seveibar Jul 27, 2026
a90bed7
fix: canonicalize merged SRJ connection aliases
seveibar Jul 27, 2026
c152849
fix: scope merged net aliases to continuity DRC
seveibar Jul 27, 2026
e04b2d5
test: bound Pipeline9 SRJ23 regression coverage
seveibar Jul 27, 2026
fdfa33b
test: isolate Pipeline9 DRC alias regression
seveibar Jul 27, 2026
251a825
fix: remove Pipeline9 trace approximation rects
seveibar Jul 28, 2026
cca8c27
fix: simplify around preloaded traces
seveibar Jul 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion benchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ resolve_pipeline_solver_name() {
5) echo "AutoroutingPipelineSolver5" ;;
6) echo "AutoroutingPipelineSolver6" ;;
7) echo "AutoroutingPipelineSolver7_MultiGraph" ;;
9) echo "AutoroutingPipelineSolver9_PreloadedTraceGraph" ;;
krt|KRT) echo "KrtAutoroutingPipelineSolver" ;;
*)
echo "Unknown pipeline: $1" >&2
Expand Down Expand Up @@ -80,7 +81,7 @@ Usage:

Options:
--solver NAME Run only one solver (same as first positional arg)
--pipeline ID Run a pipeline alias (1-7 or krt)
--pipeline ID Run a pipeline alias (1-7, 9, or krt)
--scenario-limit N Run only first N scenarios (same as second positional arg)
--concurrency N Number of Bun workers used per solver, or "auto"
--effort N Override scenario effort multiplier
Expand All @@ -107,6 +108,7 @@ Examples:
./benchmark.sh --pipeline 5
./benchmark.sh --pipeline 6
./benchmark.sh --pipeline 7
./benchmark.sh --pipeline 9
./benchmark.sh --pipeline krt
./benchmark.sh --solver AutoroutingPipelineSolver7_MultiGraph --dataset srj05 --scenario-limit 20
./benchmark.sh --dataset 11 --scenario-limit 20
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ export const createPipeline7RelaxedDrcEvaluator = (
traces,
})

const centeredErrors =
errorsWithCenters.length === errors.length ? errorsWithCenters : errors

return {
errors: errors as unknown as Record<string, unknown>[],
errorsWithCenters: errorsWithCenters as unknown as Record<
string,
unknown
>[],
// high-density-repair03 currently consumes `errors` from its custom
// evaluator snapshot. Put the center-enriched form there as well so
// via-only errors can participate in localized repair.
errors: centeredErrors as unknown as Record<string, unknown>[],
errorsWithCenters: centeredErrors as unknown as Record<string, unknown>[],
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import type { DrcEvaluator } from "high-density-repair03/lib"
import { BaseSolver } from "lib/solvers/BaseSolver"
import type { Obstacle, SimpleRouteJson } from "lib/types"
import {
AutoroutingPipelineSolver7_MultiGraph,
type AutoroutingPipelineSolverOptions,
} from "../AutoroutingPipeline7_MultiGraph/AutoroutingPipelineSolver7_MultiGraph"
import { convertPreloadedTraceToRouteObstacles } from "./pipeline9-b01-rerouter"
import { Pipeline9ExactDrcRepairSolver } from "./pipeline9-exact-drc-repair-solver"
import { lockPipeline9TerminalLayers } from "./lock-pipeline9-terminal-layers"
import { PreloadedTraceGraphSolver } from "./preloaded-trace-graph-solver"
import { PreprocessSimpleRouteJsonWithoutTraceObstaclesSolver } from "./preprocess-simple-route-json-without-trace-obstacles-solver"

type PipelineStep = {
solverName: string
solverClass: new (...args: any[]) => BaseSolver
getConstructorParams: (
instance: AutoroutingPipelineSolver7_MultiGraph,
) => any[]
onSolved?: (instance: AutoroutingPipelineSolver7_MultiGraph) => void
}

const getAxisAlignedRepairObstacle = (obstacle: Obstacle): Obstacle => {
const radians = ((obstacle.ccwRotationDegrees ?? 0) * Math.PI) / 180
if (Math.abs(radians) < 1e-9) return obstacle

return {
...obstacle,
width:
Math.abs(obstacle.width * Math.cos(radians)) +
Math.abs(obstacle.height * Math.sin(radians)),
height:
Math.abs(obstacle.width * Math.sin(radians)) +
Math.abs(obstacle.height * Math.cos(radians)),
ccwRotationDegrees: 0,
}
}

const createPadCenteredDrcEvaluator = (
evaluator: DrcEvaluator | undefined,
originalObstacles: Obstacle[],
): DrcEvaluator | undefined => {
if (!evaluator) return undefined

const physicalObstacleById = new Map<string, Obstacle>()
for (const obstacle of originalObstacles) {
const physicalId = obstacle.connectedTo[0]
if (
physicalId &&
(physicalId.startsWith("pcb_smtpad_") ||
physicalId.startsWith("pcb_plated_hole_"))
) {
physicalObstacleById.set(physicalId, obstacle)
}
}
const addAccuratePadCenters = (
errors: Array<Record<string, unknown>>,
): Array<Record<string, unknown>> =>
errors.map((error) => {
const errorType =
typeof error.error_type === "string"
? error.error_type
: typeof error.type === "string"
? error.type
: undefined
if (
errorType !== "pcb_pad_trace_clearance_error" ||
typeof error.pcb_pad_id !== "string"
) {
return error
}
const obstacle = physicalObstacleById.get(error.pcb_pad_id)
return obstacle ? { ...error, center: obstacle.center } : error
})

return (input) => {
const result = evaluator(input)
if (Array.isArray(result)) {
return addAccuratePadCenters(result)
}
return {
...result,
errors: addAccuratePadCenters(result.errors),
errorsWithCenters: addAccuratePadCenters(
result.errorsWithCenters ?? result.errors,
),
}
}
}

export class AutoroutingPipelineSolver9_PreloadedTraceGraph extends AutoroutingPipelineSolver7_MultiGraph {
preloadedTraceGraphSolver?: PreloadedTraceGraphSolver

constructor(
srj: SimpleRouteJson,
opts: AutoroutingPipelineSolverOptions = {},
) {
super(srj, opts)
const pipelineDef = this.pipelineDef as PipelineStep[]
const preprocessStep = pipelineDef.find(
(step) => step.solverName === "preprocessSimpleRouteJsonSolver",
)
if (!preprocessStep) {
throw new Error("Pipeline9 could not find the preprocessing stage")
}
preprocessStep.solverClass =
PreprocessSimpleRouteJsonWithoutTraceObstaclesSolver

const crampedPortStageIndex = pipelineDef.findIndex(
(step) => step.solverName === "necessaryCrampedPortPointSolver",
)
if (crampedPortStageIndex === -1) {
throw new Error("Pipeline9 could not find the cramped-port stage")
}
pipelineDef.splice(crampedPortStageIndex + 1, 0, {
solverName: "preloadedTraceGraphSolver",
solverClass: PreloadedTraceGraphSolver,
getConstructorParams: (pipeline) => [
pipeline.sharedEdgeSegmentsWithNecessaryCrampedPortPoints ??
pipeline.necessaryCrampedPortPointSolver!.getOutput(),
pipeline.originalSrj,
],
})

const stitchStep = pipelineDef.find(
(step) => step.solverName === "highDensityStitchSolver",
)
if (!stitchStep) {
throw new Error("Pipeline9 could not find the route stitching stage")
}
const getBaseStitchParams = stitchStep.getConstructorParams
stitchStep.getConstructorParams = (pipeline) => {
const [params, ...remainingParams] = getBaseStitchParams(pipeline)
return [
{
...params,
preserveDistinctTerminalSnaps: true,
preserveTerminalLayers: true,
},
...remainingParams,
]
}

const traceSimplificationStep = pipelineDef.find(
(step) => step.solverName === "traceSimplificationSolver",
)
if (!traceSimplificationStep) {
throw new Error("Pipeline9 could not find the trace simplification stage")
}
const getBaseTraceSimplificationParams =
traceSimplificationStep.getConstructorParams
traceSimplificationStep.getConstructorParams = (pipeline) => {
const [params, ...remainingParams] =
getBaseTraceSimplificationParams(pipeline)
const preloadedHdRoutes = (pipeline.srj.traces ?? []).flatMap(
(trace, traceIndex) =>
convertPreloadedTraceToRouteObstacles(
trace,
traceIndex,
pipeline.srj.layerCount,
pipeline.viaDiameter,
pipeline.connMap,
),
)
return [
{
...params,
otherHdRoutes: preloadedHdRoutes,
},
...remainingParams,
]
}

const globalDrcStep = pipelineDef.find(
(step) => step.solverName === "globalDrcForceImproveSolver",
)
if (!globalDrcStep) {
throw new Error("Pipeline9 could not find the global DRC repair stage")
}
const getBaseGlobalDrcParams = globalDrcStep.getConstructorParams
globalDrcStep.getConstructorParams = (pipeline) => {
const [params] = getBaseGlobalDrcParams(pipeline)
return [
{
...params,
hdRoutes: lockPipeline9TerminalLayers(
params.hdRoutes,
pipeline.srjWithPointPairs!.connections,
pipeline.srj.layerCount,
),
},
]
}

const exactDrcStep = pipelineDef.find(
(step) => step.solverName === "exactGeometryDrcForceImproveSolver",
)
if (!exactDrcStep) {
throw new Error("Pipeline9 could not find the exact DRC repair stage")
}
exactDrcStep.solverClass = Pipeline9ExactDrcRepairSolver
const getBaseExactDrcParams = exactDrcStep.getConstructorParams
exactDrcStep.getConstructorParams = (pipeline) => {
const [params] = getBaseExactDrcParams(pipeline)
const originalObstacles = pipeline.originalSrj.obstacles
const drcEvaluator = createPadCenteredDrcEvaluator(
params.drcEvaluator,
originalObstacles,
)
const viaInPadDrcEvaluator = createPadCenteredDrcEvaluator(
params.viaInPadDrcEvaluator,
originalObstacles,
)
return [
{
...params,
drcEvaluator,
viaInPadDrcEvaluator,
originalObstacles,
b01BaseObstacles: originalObstacles,
srj: {
...params.srj,
obstacles: originalObstacles.map(getAxisAlignedRepairObstacle),
},
},
]
}
}

override getSolverName(): string {
return "AutoroutingPipelineSolver9_PreloadedTraceGraph"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getBoundingBox } from "@tscircuit/math-utils"
import type { SimpleRouteJson } from "lib/types"

export const expandImplicitBoundsToConnectedObstacles = (
srj: SimpleRouteJson,
): SimpleRouteJson => {
if (srj.outline && srj.outline.length >= 3) {
return srj
}

const bounds = { ...srj.bounds }
for (const obstacle of srj.obstacles) {
const hasConnectivity =
(obstacle.connectedTo?.length ?? 0) > 0 ||
(obstacle.offBoardConnectsTo?.length ?? 0) > 0 ||
obstacle.netIsAssignable === true
if (!hasConnectivity) continue
const obstacleBounds = getBoundingBox(obstacle)
bounds.minX = Math.min(bounds.minX, obstacleBounds.minX)
bounds.maxX = Math.max(bounds.maxX, obstacleBounds.maxX)
bounds.minY = Math.min(bounds.minY, obstacleBounds.minY)
bounds.maxY = Math.max(bounds.maxY, obstacleBounds.maxY)
}

return { ...srj, bounds }
}
Loading
Loading