From 82295cb0c742dfbda2b9aed111f4be6b86f29517 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 21 Mar 2026 17:18:25 -0700 Subject: [PATCH 1/4] region net assignments --- lib/compat/loadSerializedHyperGraph.ts | 124 ++++++++++++++---- lib/index.ts | 38 +++++- tests/compat/loadSerializedHyperGraph.test.ts | 107 +++++++++++++++ tests/solver/on-all-routes-routed.test.ts | 2 + tests/solver/region-net-id.test.ts | 47 +++++++ 5 files changed, 290 insertions(+), 28 deletions(-) create mode 100644 tests/compat/loadSerializedHyperGraph.test.ts create mode 100644 tests/solver/region-net-id.test.ts diff --git a/lib/compat/loadSerializedHyperGraph.ts b/lib/compat/loadSerializedHyperGraph.ts index 6684c5e..f0d589a 100644 --- a/lib/compat/loadSerializedHyperGraph.ts +++ b/lib/compat/loadSerializedHyperGraph.ts @@ -5,6 +5,48 @@ import type { TinyHyperGraphTopology, } from "../index" +const isFullObstacleRegion = ( + region: SerializedHyperGraph["regions"][number], +) => region.d?._containsObstacle === true && region.d?._containsTarget !== true + +const filterObstacleRegions = (serializedHyperGraph: SerializedHyperGraph) => { + const removedRegionIds = new Set( + serializedHyperGraph.regions + .filter(isFullObstacleRegion) + .map((region) => region.regionId), + ) + + if (removedRegionIds.size === 0) { + return serializedHyperGraph + } + + const filteredPorts = serializedHyperGraph.ports.filter( + (port) => + !removedRegionIds.has(port.region1Id) && + !removedRegionIds.has(port.region2Id), + ) + + const invalidConnection = (serializedHyperGraph.connections ?? []).find( + (connection) => + removedRegionIds.has(connection.startRegionId) || + removedRegionIds.has(connection.endRegionId), + ) + + if (invalidConnection) { + throw new Error( + `Connection "${invalidConnection.connectionId}" references full-obstacle region`, + ) + } + + return { + ...serializedHyperGraph, + regions: serializedHyperGraph.regions.filter( + (region) => !removedRegionIds.has(region.regionId), + ), + ports: filteredPorts, + } +} + const addSerializedRegionIdToMetadata = ( region: SerializedHyperGraph["regions"][number], ) => { @@ -187,29 +229,30 @@ export const loadSerializedHyperGraph = ( problem: TinyHyperGraphProblem solution: TinyHyperGraphSolution } => { + const filteredHyperGraph = filterObstacleRegions(serializedHyperGraph) const regionIdToIndex = new Map() const portIdToIndex = new Map() const portById = new Map() const solvedRouteByConnectionId = new Map( - (serializedHyperGraph.solvedRoutes ?? []).map((route) => [ + (filteredHyperGraph.solvedRoutes ?? []).map((route) => [ route.connection.connectionId, route, ]), ) - serializedHyperGraph.regions.forEach((region, regionIndex) => { + filteredHyperGraph.regions.forEach((region, regionIndex) => { regionIdToIndex.set(region.regionId, regionIndex) }) - serializedHyperGraph.ports.forEach((port, portIndex) => { + filteredHyperGraph.ports.forEach((port, portIndex) => { portIdToIndex.set(port.portId, portIndex) portById.set(port.portId, port) }) - const regionCount = serializedHyperGraph.regions.length - const portCount = serializedHyperGraph.ports.length + const regionCount = filteredHyperGraph.regions.length + const portCount = filteredHyperGraph.ports.length - const regionIncidentPorts = serializedHyperGraph.regions.map((region) => + const regionIncidentPorts = filteredHyperGraph.regions.map((region) => region.pointIds .map((portId) => portIdToIndex.get(portId)) .filter((portIndex): portIndex is number => portIndex !== undefined), @@ -223,8 +266,9 @@ export const loadSerializedHyperGraph = ( const regionHeight = new Float64Array(regionCount) const regionCenterX = new Float64Array(regionCount) const regionCenterY = new Float64Array(regionCount) + const regionNetId = new Int32Array(regionCount).fill(-1) - serializedHyperGraph.regions.forEach((region, regionIndex) => { + filteredHyperGraph.regions.forEach((region, regionIndex) => { const geometry = getRegionGeometry(region) regionWidth[regionIndex] = geometry.width regionHeight[regionIndex] = geometry.height @@ -238,7 +282,7 @@ export const loadSerializedHyperGraph = ( const portY = new Float64Array(portCount) const portZ = new Int32Array(portCount) - serializedHyperGraph.ports.forEach((port, portIndex) => { + filteredHyperGraph.ports.forEach((port, portIndex) => { const region1Index = regionIdToIndex.get(port.region1Id) const region2Index = regionIdToIndex.get(port.region2Id) @@ -254,20 +298,55 @@ export const loadSerializedHyperGraph = ( portZ[portIndex] = Number(port.d?.z ?? 0) portAngleForRegion1[portIndex] = computePortAngle( port, - serializedHyperGraph.regions[region1Index], + filteredHyperGraph.regions[region1Index], ) portAngleForRegion2[portIndex] = computePortAngle( port, - serializedHyperGraph.regions[region2Index], + filteredHyperGraph.regions[region2Index], ) }) - const connections = serializedHyperGraph.connections ?? [] + const connections = filteredHyperGraph.connections ?? [] + const netIdToIndex = new Map() + let nextNetIndex = 0 + const getNetIndex = (connection: (typeof connections)[number]) => { + const netId = + connection.mutuallyConnectedNetworkId ?? connection.connectionId + let netIndex = netIdToIndex.get(netId) + if (netIndex === undefined) { + netIndex = nextNetIndex++ + netIdToIndex.set(netId, netIndex) + } + return netIndex + } + + const assignRegionNet = (regionId: string, netIndex: number) => { + const regionIndex = regionIdToIndex.get(regionId) + if (regionIndex === undefined) { + throw new Error(`Connection references missing region "${regionId}"`) + } + + const existingNetIndex = regionNetId[regionIndex] + if (existingNetIndex !== -1 && existingNetIndex !== netIndex) { + throw new Error( + `Region "${regionId}" is assigned to multiple nets (${existingNetIndex}, ${netIndex})`, + ) + } + + regionNetId[regionIndex] = netIndex + } + + connections.forEach((connection) => { + const netIndex = getNetIndex(connection) + assignRegionNet(connection.startRegionId, netIndex) + assignRegionNet(connection.endRegionId, netIndex) + }) + const routableConnections = connections .map((connection) => { const solvedRoute = solvedRouteByConnectionId.get(connection.connectionId) const sharedPortIds = getSharedPortIdsForConnection( - serializedHyperGraph, + filteredHyperGraph, connection, ) @@ -288,18 +367,15 @@ export const loadSerializedHyperGraph = ( const routeEndPort = new Int32Array(routeCount) const routeNet = new Int32Array(routeCount) - const netIdToIndex = new Map() - let nextNetIndex = 0 - routableConnections.forEach(({ connection, solvedRoute }, routeIndex) => { const fallbackStartPortId = getCentermostPortIdForRegion( - serializedHyperGraph.regions.find( + filteredHyperGraph.regions.find( (region) => region.regionId === connection.startRegionId, ), portById, ) const fallbackEndPortId = getCentermostPortIdForRegion( - serializedHyperGraph.regions.find( + filteredHyperGraph.regions.find( (region) => region.regionId === connection.endRegionId, ), portById, @@ -324,14 +400,7 @@ export const loadSerializedHyperGraph = ( routeStartPort[routeIndex] = startPortIndex routeEndPort[routeIndex] = endPortIndex - const netId = - connection.mutuallyConnectedNetworkId ?? connection.connectionId - let netIndex = netIdToIndex.get(netId) - if (netIndex === undefined) { - netIndex = nextNetIndex++ - netIdToIndex.set(netId, netIndex) - } - routeNet[routeIndex] = netIndex + routeNet[routeIndex] = getNetIndex(connection) }) const topology: TinyHyperGraphTopology = { @@ -343,7 +412,7 @@ export const loadSerializedHyperGraph = ( regionHeight, regionCenterX, regionCenterY, - regionMetadata: serializedHyperGraph.regions.map((region) => + regionMetadata: filteredHyperGraph.regions.map((region) => addSerializedRegionIdToMetadata(region), ), portAngleForRegion1, @@ -351,7 +420,7 @@ export const loadSerializedHyperGraph = ( portX, portY, portZ, - portMetadata: serializedHyperGraph.ports.map((port) => + portMetadata: filteredHyperGraph.ports.map((port) => addSerializedPortIdToMetadata(port), ), } @@ -363,6 +432,7 @@ export const loadSerializedHyperGraph = ( routeStartPort, routeEndPort, routeNet, + regionNetId, } const solution: TinyHyperGraphSolution = { diff --git a/lib/index.ts b/lib/index.ts index 0848919..fda6453 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -97,6 +97,8 @@ export interface TinyHyperGraphProblem { // routeNet[routeId] = net id of the route routeNet: Int32Array // NetId[] + /** regionNetId[regionId] = reserved net id for the region, -1 means freely traversable */ + regionNetId: Int32Array } export interface TinyHyperGraphProblemSetup { @@ -244,8 +246,24 @@ export class TinyHyperGraphSolver extends BaseSolver { state.candidateBestCostByHopId.fill(Number.POSITIVE_INFINITY) const startingPortId = problem.routeStartPort[state.currentRouteId!] state.candidateQueue.clear() + const startingIncidentRegions = + topology.incidentPortRegion[startingPortId] ?? [] const startingNextRegionId = - topology.incidentPortRegion[startingPortId][0] + startingIncidentRegions.find( + (regionId) => problem.regionNetId[regionId] === -1, + ) ?? + startingIncidentRegions.find( + (regionId) => + problem.regionNetId[regionId] === state.currentRouteNetId, + ) ?? + startingIncidentRegions[0] + + if (startingNextRegionId === undefined) { + this.failed = true + this.error = `Start port ${startingPortId} has no incident regions` + return + } + state.candidateBestCostByHopId[ this.getHopId(startingPortId, startingNextRegionId) ] = 0 @@ -277,6 +295,10 @@ export class TinyHyperGraphSolver extends BaseSolver { return } + if (this.isRegionReservedForDifferentNet(currentCandidate.nextRegionId)) { + return + } + const neighbors = topology.regionIncidentPorts[currentCandidate.nextRegionId] @@ -306,6 +328,13 @@ export class TinyHyperGraphSolver extends BaseSolver { ? topology.incidentPortRegion[neighborPortId][1] : topology.incidentPortRegion[neighborPortId][0] + if ( + nextRegionId === undefined || + this.isRegionReservedForDifferentNet(nextRegionId) + ) { + continue + } + const newCandidate = { prevRegionId: currentCandidate.nextRegionId, nextRegionId, @@ -350,6 +379,13 @@ export class TinyHyperGraphSolver extends BaseSolver { return false } + isRegionReservedForDifferentNet(regionId: RegionId): boolean { + const reservedNetId = this.problem.regionNetId[regionId] + return ( + reservedNetId !== -1 && reservedNetId !== this.state.currentRouteNetId + ) + } + getPortAngleInRegion(portId: PortId, regionId: RegionId): number { const { topology } = this const [firstRegionId, secondRegionId] = diff --git a/tests/compat/loadSerializedHyperGraph.test.ts b/tests/compat/loadSerializedHyperGraph.test.ts new file mode 100644 index 0000000..3448b44 --- /dev/null +++ b/tests/compat/loadSerializedHyperGraph.test.ts @@ -0,0 +1,107 @@ +import { expect, test } from "bun:test" +import type { SerializedHyperGraph } from "@tscircuit/hypergraph" +import { loadSerializedHyperGraph } from "lib/compat/loadSerializedHyperGraph" + +const getRegionIndexBySerializedId = ( + regionMetadata: any[] | undefined, + serializedRegionId: string, +) => + regionMetadata?.findIndex( + (metadata) => metadata?.serializedRegionId === serializedRegionId, + ) ?? -1 + +test("loadSerializedHyperGraph removes full-obstacle regions and attached ports", () => { + const graph: SerializedHyperGraph = { + regions: [ + { + regionId: "free", + pointIds: ["p-start", "p-blocked", "p-end"], + d: { center: { x: 0, y: 0 }, width: 3, height: 1 }, + }, + { + regionId: "target-a", + pointIds: ["p-start"], + d: { + center: { x: -1, y: 0 }, + width: 1, + height: 1, + _containsTarget: true, + }, + }, + { + regionId: "obstacle", + pointIds: ["p-blocked"], + d: { + center: { x: 0, y: -1 }, + width: 1, + height: 1, + _containsObstacle: true, + }, + }, + { + regionId: "target-b", + pointIds: ["p-end"], + d: { + center: { x: 1, y: 0 }, + width: 1, + height: 1, + _containsTarget: true, + }, + }, + ], + ports: [ + { + portId: "p-start", + region1Id: "target-a", + region2Id: "free", + d: { x: -0.5, y: 0, z: 0, distToCentermostPortOnZ: 0 }, + }, + { + portId: "p-blocked", + region1Id: "free", + region2Id: "obstacle", + d: { x: 0, y: -0.5, z: 0, distToCentermostPortOnZ: 0 }, + }, + { + portId: "p-end", + region1Id: "free", + region2Id: "target-b", + d: { x: 0.5, y: 0, z: 0, distToCentermostPortOnZ: 0 }, + }, + ], + connections: [ + { + connectionId: "route-0", + startRegionId: "target-a", + endRegionId: "target-b", + }, + ], + } + + const { topology, problem } = loadSerializedHyperGraph(graph) + const freeRegionId = getRegionIndexBySerializedId( + topology.regionMetadata, + "free", + ) + const startRegionId = getRegionIndexBySerializedId( + topology.regionMetadata, + "target-a", + ) + const endRegionId = getRegionIndexBySerializedId( + topology.regionMetadata, + "target-b", + ) + + expect(topology.regionCount).toBe(3) + expect(topology.portCount).toBe(2) + expect( + getRegionIndexBySerializedId(topology.regionMetadata, "obstacle"), + ).toBe(-1) + expect( + topology.portMetadata?.map((metadata) => metadata?.serializedPortId).sort(), + ).toEqual(["p-end", "p-start"]) + expect(problem.routeCount).toBe(1) + expect(problem.regionNetId[startRegionId]).toBe(problem.routeNet[0]) + expect(problem.regionNetId[endRegionId]).toBe(problem.routeNet[0]) + expect(problem.regionNetId[freeRegionId]).toBe(-1) +}) diff --git a/tests/solver/on-all-routes-routed.test.ts b/tests/solver/on-all-routes-routed.test.ts index f3cfa56..e5f9d0d 100644 --- a/tests/solver/on-all-routes-routed.test.ts +++ b/tests/solver/on-all-routes-routed.test.ts @@ -48,6 +48,7 @@ const createTestSolver = () => { regionHeight: new Float64Array(regionCount).fill(1), regionCenterX: new Float64Array(regionCount).fill(0), regionCenterY: new Float64Array(regionCount).fill(0), + regionNetId: new Int32Array(regionCount).fill(-1), portAngleForRegion1: new Int32Array(portCount), portAngleForRegion2: new Int32Array(portCount), portX, @@ -61,6 +62,7 @@ const createTestSolver = () => { routeStartPort, routeEndPort, routeNet, + regionNetId: new Int32Array(regionCount).fill(-1), } return new TinyHyperGraphSolver(topology, problem) diff --git a/tests/solver/region-net-id.test.ts b/tests/solver/region-net-id.test.ts new file mode 100644 index 0000000..62213f6 --- /dev/null +++ b/tests/solver/region-net-id.test.ts @@ -0,0 +1,47 @@ +import { expect, test } from "bun:test" +import { + type TinyHyperGraphProblem, + TinyHyperGraphSolver, + type TinyHyperGraphTopology, +} from "lib/index" + +test("solver does not traverse regions reserved for a different net", () => { + const topology: TinyHyperGraphTopology = { + portCount: 4, + regionCount: 5, + regionIncidentPorts: [[0, 1], [1, 2], [2, 3], [0], [3]], + incidentPortRegion: [ + [0, 3], + [0, 1], + [1, 2], + [2, 4], + ], + regionWidth: new Float64Array(5).fill(1), + regionHeight: new Float64Array(5).fill(1), + regionCenterX: new Float64Array(5).fill(0), + regionCenterY: new Float64Array(5).fill(0), + portAngleForRegion1: new Int32Array(4), + portAngleForRegion2: new Int32Array(4), + portX: new Float64Array([0, 1, 2, 3]), + portY: new Float64Array(4), + portZ: new Int32Array(4), + } + + const problem: TinyHyperGraphProblem = { + routeCount: 1, + portSectionMask: new Int8Array(4).fill(1), + routeStartPort: new Int32Array([0]), + routeEndPort: new Int32Array([3]), + routeNet: new Int32Array([0]), + regionNetId: Int32Array.from([-1, 1, -1, -1, -1]), + } + + const solver = new TinyHyperGraphSolver(topology, problem) + + solver.step() + solver.step() + + expect(solver.solved).toBe(false) + expect(solver.failed).toBe(true) + expect(solver.error).toBe("No candidates left") +}) From 04e328ab8f69e53b56182cfa4221cd6946cd2ca4 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 21 Mar 2026 17:21:00 -0700 Subject: [PATCH 2/4] refactor shuffle, better name for region ids over cost threshold --- lib/index.ts | 40 +++++++--------------------------------- lib/shuffle.ts | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 33 deletions(-) create mode 100644 lib/shuffle.ts diff --git a/lib/index.ts b/lib/index.ts index fda6453..08d7a9b 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -4,6 +4,7 @@ import { convertToSerializedHyperGraph } from "./compat/convertToSerializedHyper import { computeRegionCost } from "./computeRegionCost" import { countNewIntersections } from "./countNewIntersections" import { MinHeap } from "./MinHeap" +import { shuffle } from "./shuffle" import type { DynamicAnglePair, HopId, @@ -27,30 +28,6 @@ const createEmptyRegionIntersectionCache = (): RegionIntersectionCache => ({ existingRegionCost: 0, }) -const createMulberry32 = (seed: number) => { - let state = seed >>> 0 - - return () => { - state += 0x6d2b79f5 - let t = state - t = Math.imul(t ^ (t >>> 15), t | 1) - t ^= t + Math.imul(t ^ (t >>> 7), t | 61) - return ((t ^ (t >>> 14)) >>> 0) / 4294967296 - } -} - -const shuffleRouteIds = (routeIds: RouteId[], seed: number): RouteId[] => { - const shuffled = [...routeIds] - const random = createMulberry32(seed) - - for (let i = shuffled.length - 1; i > 0; i--) { - const j = Math.floor(random() * (i + 1)) - ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]] - } - - return shuffled -} - export interface TinyHyperGraphTopology { portCount: number regionCount: number @@ -535,10 +512,7 @@ export class TinyHyperGraphSolver extends BaseSolver { ) state.currentRouteNetId = undefined state.currentRouteId = undefined - state.unroutedRoutes = shuffleRouteIds( - range(problem.routeCount), - state.ripCount, - ) + state.unroutedRoutes = shuffle(range(problem.routeCount), state.ripCount) state.candidateQueue.clear() state.candidateBestCostByHopId.fill(Number.POSITIVE_INFINITY) state.goalPortId = -1 @@ -554,7 +528,7 @@ export class TinyHyperGraphSolver extends BaseSolver { this.RIP_THRESHOLD_START + (this.RIP_THRESHOLD_END - this.RIP_THRESHOLD_START) * ripThresholdProgress - const ripRegionIds: RegionId[] = [] + const regionIdsOverCostThreshold: RegionId[] = [] const regionCosts = new Float64Array(topology.regionCount) let maxRegionCost = 0 @@ -565,19 +539,19 @@ export class TinyHyperGraphSolver extends BaseSolver { maxRegionCost = Math.max(maxRegionCost, regionCost) if (regionCost > currentRipThreshold) { - ripRegionIds.push(regionId) + regionIdsOverCostThreshold.push(regionId) } } this.stats = { ...this.stats, currentRipThreshold, - hotRegionCount: ripRegionIds.length, + hotRegionCount: regionIdsOverCostThreshold.length, maxRegionCost, ripCount: state.ripCount, } - if (ripRegionIds.length === 0) { + if (regionIdsOverCostThreshold.length === 0) { this.solved = true return } @@ -592,7 +566,7 @@ export class TinyHyperGraphSolver extends BaseSolver { this.stats = { ...this.stats, ripCount: state.ripCount, - reripRegionCount: ripRegionIds.length, + reripRegionCount: regionIdsOverCostThreshold.length, } } diff --git a/lib/shuffle.ts b/lib/shuffle.ts new file mode 100644 index 0000000..9b7bdfe --- /dev/null +++ b/lib/shuffle.ts @@ -0,0 +1,23 @@ +const createMulberry32 = (seed: number) => { + let state = seed >>> 0 + + return () => { + state += 0x6d2b79f5 + let t = state + t = Math.imul(t ^ (t >>> 15), t | 1) + t ^= t + Math.imul(t ^ (t >>> 7), t | 61) + return ((t ^ (t >>> 14)) >>> 0) / 4294967296 + } +} + +export const shuffle = (items: readonly T[], seed: number): T[] => { + const shuffled = [...items] + const random = createMulberry32(seed) + + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(random() * (i + 1)) + ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]] + } + + return shuffled +} From 8b6ced4ca208e9de376447cd3c3c9dbc00dd68cf Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 21 Mar 2026 17:24:41 -0700 Subject: [PATCH 3/4] add net label for region --- lib/visualizeTinyGraph.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/visualizeTinyGraph.ts b/lib/visualizeTinyGraph.ts index 4dcada4..ec13a06 100644 --- a/lib/visualizeTinyGraph.ts +++ b/lib/visualizeTinyGraph.ts @@ -120,9 +120,12 @@ const getRegionCostLabel = ( const regionCache = solver.state.regionIntersectionCaches[regionId] const regionCost = getRegionDisplayCost(solver, regionId) const congestionCost = solver.state.regionCongestionCost[regionId] ?? 0 + const regionNetId = solver.problem.regionNetId[regionId] + const regionNetLabel = regionNetId === -1 ? "free" : `${regionNetId}` return formatLabel( `region: region-${regionId}`, + `net: ${regionNetLabel}`, `cost: ${regionCost.toFixed(3)}`, `congestion: ${congestionCost.toFixed(3)}`, `same layer X: ${regionCache?.existingSameLayerIntersections ?? 0}`, From 8b2c12bce832930470849d57eca89bdb1605f380 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 21 Mar 2026 17:32:09 -0700 Subject: [PATCH 4/4] add hook for onOutOfCandidates --- lib/index.ts | 22 ++++++++++++++++++++-- tests/solver/region-net-id.test.ts | 28 ++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/lib/index.ts b/lib/index.ts index 08d7a9b..fbf7fb6 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -257,8 +257,7 @@ export class TinyHyperGraphSolver extends BaseSolver { const currentCandidate = state.candidateQueue.dequeue() if (!currentCandidate) { - this.failed = true - this.error = "No candidates left" + this.onOutOfCandidates() return } @@ -570,6 +569,25 @@ export class TinyHyperGraphSolver extends BaseSolver { } } + onOutOfCandidates() { + const { topology, state } = this + + for (let regionId = 0; regionId < topology.regionCount; regionId++) { + const regionCost = + state.regionIntersectionCaches[regionId]?.existingRegionCost ?? 0 + state.regionCongestionCost[regionId] += + regionCost * this.RIP_CONGESTION_REGION_COST_FACTOR + } + + state.ripCount += 1 + this.resetRoutingStateForRerip() + this.stats = { + ...this.stats, + ripCount: state.ripCount, + reripReason: "out_of_candidates", + } + } + onPathFound(finalCandidate: Candidate) { const { state } = this const currentRouteId = state.currentRouteId diff --git a/tests/solver/region-net-id.test.ts b/tests/solver/region-net-id.test.ts index 62213f6..12b1da8 100644 --- a/tests/solver/region-net-id.test.ts +++ b/tests/solver/region-net-id.test.ts @@ -4,6 +4,20 @@ import { TinyHyperGraphSolver, type TinyHyperGraphTopology, } from "lib/index" +import type { RegionIntersectionCache } from "lib/types" + +const createRegionCache = ( + existingRegionCost: number, +): RegionIntersectionCache => ({ + netIds: new Int32Array(0), + lesserAngles: new Int32Array(0), + greaterAngles: new Int32Array(0), + layerMasks: new Int32Array(0), + existingCrossingLayerIntersections: 0, + existingSameLayerIntersections: 0, + existingEntryExitLayerChanges: 0, + existingRegionCost, +}) test("solver does not traverse regions reserved for a different net", () => { const topology: TinyHyperGraphTopology = { @@ -37,11 +51,21 @@ test("solver does not traverse regions reserved for a different net", () => { } const solver = new TinyHyperGraphSolver(topology, problem) + solver.state.regionIntersectionCaches[0] = createRegionCache(0.5) solver.step() solver.step() expect(solver.solved).toBe(false) - expect(solver.failed).toBe(true) - expect(solver.error).toBe("No candidates left") + expect(solver.failed).toBe(false) + expect(solver.error).toBeNull() + expect(solver.state.ripCount).toBe(1) + expect(Array.from(solver.state.unroutedRoutes)).toEqual([0]) + expect(solver.state.currentRouteId).toBeUndefined() + expect(solver.state.currentRouteNetId).toBeUndefined() + expect(solver.state.candidateQueue.length).toBe(0) + expect(solver.state.regionCongestionCost[0]).toBe( + 0.5 * solver.RIP_CONGESTION_REGION_COST_FACTOR, + ) + expect(solver.stats.reripReason).toBe("out_of_candidates") })