diff --git a/lib/core.ts b/lib/core.ts index c224a31..69d2679 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -12,6 +12,7 @@ import { type TinyHyperGraphInitialAssignment, } from "./initialAssignments" import { MinHeap } from "./MinHeap" +import { RegionPathSolver } from "./region-graph/region-path-solver" import { shuffle } from "./shuffle" import type { StaticallyUnroutableRouteSummary } from "./static-reachability" import { @@ -247,6 +248,7 @@ export interface TinyHyperGraphSolverOptions { RIP_THRESHOLD_RAMP_ATTEMPTS?: number RIP_CONGESTION_REGION_COST_FACTOR?: number USE_LAZY_ROUTE_HEURISTIC?: boolean + USE_REGION_PATH_CORRIDORS?: boolean USE_SPARSE_CANDIDATE_STORAGE?: boolean MAX_ITERATIONS?: number VERBOSE?: boolean @@ -264,6 +266,7 @@ export interface TinyHyperGraphSolverOptionTarget { RIP_THRESHOLD_RAMP_ATTEMPTS: number RIP_CONGESTION_REGION_COST_FACTOR: number USE_LAZY_ROUTE_HEURISTIC?: boolean + USE_REGION_PATH_CORRIDORS?: boolean USE_SPARSE_CANDIDATE_STORAGE?: boolean MAX_ITERATIONS: number VERBOSE: boolean @@ -303,6 +306,9 @@ export const applyTinyHyperGraphSolverOptions = ( if (options.USE_LAZY_ROUTE_HEURISTIC !== undefined) { solver.USE_LAZY_ROUTE_HEURISTIC = options.USE_LAZY_ROUTE_HEURISTIC } + if (options.USE_REGION_PATH_CORRIDORS !== undefined) { + solver.USE_REGION_PATH_CORRIDORS = options.USE_REGION_PATH_CORRIDORS + } if (options.USE_SPARSE_CANDIDATE_STORAGE !== undefined) { solver.USE_SPARSE_CANDIDATE_STORAGE = options.USE_SPARSE_CANDIDATE_STORAGE } @@ -338,6 +344,7 @@ export const getTinyHyperGraphSolverOptions = ( RIP_THRESHOLD_RAMP_ATTEMPTS: solver.RIP_THRESHOLD_RAMP_ATTEMPTS, RIP_CONGESTION_REGION_COST_FACTOR: solver.RIP_CONGESTION_REGION_COST_FACTOR, USE_LAZY_ROUTE_HEURISTIC: solver.USE_LAZY_ROUTE_HEURISTIC, + USE_REGION_PATH_CORRIDORS: solver.USE_REGION_PATH_CORRIDORS, USE_SPARSE_CANDIDATE_STORAGE: solver.USE_SPARSE_CANDIDATE_STORAGE, MAX_ITERATIONS: solver.MAX_ITERATIONS, VERBOSE: solver.VERBOSE, @@ -366,6 +373,10 @@ export class TinyHyperGraphSolver extends BaseSolver { protected bestSolvedStateSnapshot?: SolvedStateSnapshot protected bestSolvedStateSummary?: RegionCostSummary private hasLoggedNeverSuccessfullyRoutedRoutes = false + private preferredRegionCorridorByRoute: Array> = [] + private expandedRegionCorridorByRoute: Array> = [] + private wideRegionCorridorByRoute: Array> = [] + private routeSearchScope: Uint8Array private staticallyUnroutableRoutes: StaticallyUnroutableRouteSummary[] = [] private segmentGeometryScratch: SegmentGeometryScratch = { lesserAngle: 0, @@ -383,6 +394,7 @@ export class TinyHyperGraphSolver extends BaseSolver { RIP_CONGESTION_REGION_COST_FACTOR = 0.1 USE_LAZY_ROUTE_HEURISTIC = false + USE_REGION_PATH_CORRIDORS = false USE_SPARSE_CANDIDATE_STORAGE = false override MAX_ITERATIONS = 1e6 @@ -423,6 +435,7 @@ export class TinyHyperGraphSolver extends BaseSolver { } this.routeAttemptCountByRouteId = new Uint32Array(problem.routeCount) this.routeSuccessCountByRouteId = new Uint32Array(problem.routeCount) + this.routeSearchScope = new Uint8Array(problem.routeCount) const initialAssignmentStats = applyInitialAssignments({ topology, problem, @@ -437,6 +450,7 @@ export class TinyHyperGraphSolver extends BaseSolver { ...initialAssignmentStats, } } + this.computePreferredRegionCorridors() } get problemSetup(): TinyHyperGraphProblemSetup { @@ -593,6 +607,15 @@ export class TinyHyperGraphSolver extends BaseSolver { for (const neighborPortId of neighbors) { const assignedNetId = state.portAssignment[neighborPortId] if (this.isPortReservedForDifferentNet(neighborPortId)) continue + if ( + this.USE_REGION_PATH_CORRIDORS && + !this.isPortAllowedByPreferredRegionCorridor( + currentCandidate.nextRegionId, + neighborPortId, + ) + ) { + continue + } if (neighborPortId === state.goalPortId) { if (assignedNetId !== -1 && assignedNetId !== state.currentRouteNetId) { continue @@ -1340,6 +1363,13 @@ export class TinyHyperGraphSolver extends BaseSolver { } onOutOfCandidates() { + if ( + this.USE_REGION_PATH_CORRIDORS && + this.retryCurrentRouteWithGlobalSearch() + ) { + return + } + const { topology, state } = this const currentRouteId = state.currentRouteId const maxRegionCostBeforeRip = this.getMaxRegionCost() @@ -1370,6 +1400,127 @@ export class TinyHyperGraphSolver extends BaseSolver { }) } + protected retryCurrentRouteWithGlobalSearch(): boolean { + const routeId = this.state.currentRouteId + if ( + routeId === undefined || + !this.preferredRegionCorridorByRoute[routeId]?.size + ) { + return false + } + + const currentScope = this.routeSearchScope[routeId] ?? 0 + if (currentScope >= 3) return false + + const nextScope = currentScope + 1 + this.routeSearchScope[routeId] = nextScope + this.state.unroutedRoutes.unshift(routeId) + this.state.currentRouteId = undefined + this.state.currentRouteNetId = undefined + this.state.candidateQueue.clear() + this.resetCandidateBestCosts() + this.state.goalPortId = -1 + this.stats = { + ...this.stats, + regionPathExpandedCorridorRouteCount: this.routeSearchScope.filter( + (scope) => scope >= 1, + ).length, + regionPathWideCorridorRouteCount: this.routeSearchScope.filter( + (scope) => scope >= 2, + ).length, + regionPathGlobalFallbackRouteCount: this.routeSearchScope.filter( + (scope) => scope >= 3, + ).length, + } + return true + } + + private computePreferredRegionCorridors(): void { + if (!this.USE_REGION_PATH_CORRIDORS) return + + const maxNegotiationPasses = 4 + const regionPathSolver = new RegionPathSolver(this.topology, this.problem, { + MAX_ITERATIONS: Math.max( + 1_000_000, + this.problem.routeCount * 3_000 * (maxNegotiationPasses + 1), + ), + MAX_NEGOTIATION_PASSES: maxNegotiationPasses, + SKIP_UNROUTABLE_ROUTES: true, + USE_TOPOLOGY_CAPACITY: true, + }) + regionPathSolver.solve() + + this.preferredRegionCorridorByRoute = + regionPathSolver.state.solvedRouteRegionIds.map( + (regionIds) => new Set(regionIds), + ) + this.expandedRegionCorridorByRoute = + regionPathSolver.state.solvedRouteRegionIds.map((regionIds) => { + const expandedCorridor = new Set(regionIds) + for (const regionId of regionIds) { + for (const edge of regionPathSolver.regionGraph.incidentEdges[ + regionId + ] ?? []) { + expandedCorridor.add(edge.regionIdA) + expandedCorridor.add(edge.regionIdB) + } + } + return expandedCorridor + }) + this.wideRegionCorridorByRoute = this.expandedRegionCorridorByRoute.map( + (expandedCorridor) => { + const wideCorridor = new Set(expandedCorridor) + for (const regionId of expandedCorridor) { + for (const edge of regionPathSolver.regionGraph.incidentEdges[ + regionId + ] ?? []) { + wideCorridor.add(edge.regionIdA) + wideCorridor.add(edge.regionIdB) + } + } + return wideCorridor + }, + ) + this.stats = { + ...this.stats, + regionPathCorridorRouteCount: + this.preferredRegionCorridorByRoute.filter( + (regionIds) => regionIds.size > 0, + ).length, + regionPathPlanningIterations: regionPathSolver.iterations, + regionPathPlanningSolved: regionPathSolver.solved, + } + } + + protected isPortAllowedByPreferredRegionCorridor( + currentRegionId: RegionId, + portId: PortId, + searchScopeOverride?: number, + ): boolean { + const routeId = this.state.currentRouteId + const searchScope = + searchScopeOverride ?? + (routeId === undefined + ? Number.POSITIVE_INFINITY + : this.routeSearchScope[routeId]) + if (routeId === undefined || searchScope >= 3) { + return true + } + + const corridor = [ + this.preferredRegionCorridorByRoute, + this.expandedRegionCorridorByRoute, + this.wideRegionCorridorByRoute, + ][searchScope]?.[routeId] + if (!corridor?.size || !corridor.has(currentRegionId)) return true + if (portId === this.state.goalPortId) return true + + const nextRegionId = (this.topology.incidentPortRegion[portId] ?? []).find( + (regionId) => regionId !== currentRegionId, + ) + return nextRegionId !== undefined && corridor.has(nextRegionId) + } + onPathFound(finalCandidate: Candidate) { const { state } = this const currentRouteId = state.currentRouteId diff --git a/lib/region-graph/graph.ts b/lib/region-graph/graph.ts index bc4d2a1..ef4d707 100644 --- a/lib/region-graph/graph.ts +++ b/lib/region-graph/graph.ts @@ -18,6 +18,7 @@ export interface RegionGraph { regionWidth: Float64Array regionHeight: Float64Array regionCapacity: Float64Array + regionTrackCapacity: Int32Array regionMetadata?: any[] edges: RegionGraphEdge[] incidentEdges: RegionGraphEdge[][] @@ -155,6 +156,21 @@ export const createRegionGraph = ( incidentEdges[edge.regionIdB]!.push(edge) } + const regionTrackCapacity = Int32Array.from( + { length: topology.regionCount }, + (_, regionId) => { + const boundaryPortCount = incidentEdges[regionId]!.reduce( + (sum, edge) => sum + edge.portIds.length, + 0, + ) + + // A route passing through a region consumes an entrance and an exit. + // Boundary port-points already include the available layers, so half + // their count is a topology-derived upper bound on simultaneous tracks. + return Math.max(1, Math.floor(boundaryPortCount / 2)) + }, + ) + return { regionCount: topology.regionCount, edgeCount: edges.length, @@ -170,6 +186,7 @@ export const createRegionGraph = ( topology.regionWidth[regionId] * topology.regionHeight[regionId], ), ), + regionTrackCapacity, regionMetadata: topology.regionMetadata, edges, incidentEdges, diff --git a/lib/region-graph/region-path-solver.ts b/lib/region-graph/region-path-solver.ts index a3a76a2..3c6d906 100644 --- a/lib/region-graph/region-path-solver.ts +++ b/lib/region-graph/region-path-solver.ts @@ -18,12 +18,16 @@ export interface RegionPathSolverOptions { MAX_ITERATIONS?: number /** Enables topology-derived capacity in implementations that support it. */ USE_TOPOLOGY_CAPACITY?: boolean + MAX_NEGOTIATION_PASSES?: number + SKIP_UNROUTABLE_ROUTES?: boolean + USE_TOPOLOGY_CAPACITY?: boolean } export interface RegionPathCandidate { regionId: RegionId prevCandidate?: RegionPathCandidate prevRegionId?: RegionId + prevEdgeId?: number g: number h: number f: number @@ -44,6 +48,11 @@ export interface RegionPathSolverOutput { export interface RegionPathWorkingState { regionUsage: Int32Array regionAssignedRoutes: Array + regionAssignedNets: Array> + edgeUsage: Int32Array + edgeAssignedNets: Array> + regionHistoricalCost: Float64Array + edgeHistoricalCost: Float64Array solvedRouteRegionIds: Array solvedRouteCosts: Float64Array currentRouteId: RouteId | undefined @@ -68,6 +77,11 @@ export class RegionPathSolver extends BaseSolver { MM_COST_FOR_FULL_REGION = 20 override MAX_ITERATIONS = 1e6 USE_TOPOLOGY_CAPACITY = false + MAX_NEGOTIATION_PASSES = 4 + SKIP_UNROUTABLE_ROUTES = false + USE_TOPOLOGY_CAPACITY = false + skippedRouteIds: RouteId[] = [] + negotiationPass = 0 state: RegionPathWorkingState @@ -90,6 +104,15 @@ export class RegionPathSolver extends BaseSolver { if (options?.USE_TOPOLOGY_CAPACITY !== undefined) { this.USE_TOPOLOGY_CAPACITY = options.USE_TOPOLOGY_CAPACITY } + if (options?.MAX_NEGOTIATION_PASSES !== undefined) { + this.MAX_NEGOTIATION_PASSES = options.MAX_NEGOTIATION_PASSES + } + if (options?.SKIP_UNROUTABLE_ROUTES !== undefined) { + this.SKIP_UNROUTABLE_ROUTES = options.SKIP_UNROUTABLE_ROUTES + } + if (options?.USE_TOPOLOGY_CAPACITY !== undefined) { + this.USE_TOPOLOGY_CAPACITY = options.USE_TOPOLOGY_CAPACITY + } this.state = { regionUsage: new Int32Array(this.regionGraph.regionCount), @@ -97,6 +120,17 @@ export class RegionPathSolver extends BaseSolver { { length: this.regionGraph.regionCount }, () => [] as RouteId[], ), + regionAssignedNets: Array.from( + { length: this.regionGraph.regionCount }, + () => new Set(), + ), + edgeUsage: new Int32Array(this.regionGraph.edgeCount), + edgeAssignedNets: Array.from( + { length: this.regionGraph.edgeCount }, + () => new Set(), + ), + regionHistoricalCost: new Float64Array(this.regionGraph.regionCount), + edgeHistoricalCost: new Float64Array(this.regionGraph.edgeCount), solvedRouteRegionIds: Array.from( { length: this.regionProblem.routeCount }, () => [] as RegionId[], @@ -126,6 +160,13 @@ export class RegionPathSolver extends BaseSolver { if (state.currentRouteId === undefined) { if (state.unroutedRoutes.length === 0) { + if ( + this.hasOverloadedResources() && + this.negotiationPass < this.MAX_NEGOTIATION_PASSES + ) { + this.startNegotiationPass() + return + } this.solved = true this.updateStats() return @@ -173,6 +214,17 @@ export class RegionPathSolver extends BaseSolver { const currentCandidate = state.candidateQueue.dequeue() if (!currentCandidate) { + if (this.SKIP_UNROUTABLE_ROUTES) { + if (state.currentRouteId !== undefined) { + this.skippedRouteIds.push(state.currentRouteId) + } + state.currentRouteId = undefined + state.currentRouteNetId = undefined + state.goalRegionId = -1 + state.candidateQueue.clear() + this.updateStats() + return + } this.failed = true this.error = `No region path found for route ${state.currentRouteId}` return @@ -206,7 +258,10 @@ export class RegionPathSolver extends BaseSolver { continue } - const g = currentCandidate.g + this.computeRegionEntryCost(nextRegionId) + const g = + currentCandidate.g + + this.computeRegionEntryCost(nextRegionId) + + this.computeEdgeEntryCost(edge.edgeId) if (!Number.isFinite(g)) { continue } @@ -218,6 +273,7 @@ export class RegionPathSolver extends BaseSolver { const nextCandidate: RegionPathCandidate = { regionId: nextRegionId, prevRegionId: currentCandidate.regionId, + prevEdgeId: edge.edgeId, prevCandidate: currentCandidate, g, h: 0, @@ -265,10 +321,104 @@ export class RegionPathSolver extends BaseSolver { ) } - computeRegionEntryCost(regionId: RegionId) { - const nextUsage = this.state.regionUsage[regionId] + 1 + computeRegionEntryCost(regionId: RegionId): number { + const currentNetId = this.state.currentRouteNetId + const nextUsage = + this.state.regionUsage[regionId] + + (currentNetId !== undefined && + this.state.regionAssignedNets[regionId]!.has(currentNetId) + ? 0 + : 1) const regionCapacity = this.regionGraph.regionCapacity[regionId] - return (nextUsage / regionCapacity) * this.MM_COST_FOR_FULL_REGION + const trackCapacity = this.getRegionCapacity(regionId) + const overflow = Math.max(0, nextUsage - trackCapacity) + return ( + (nextUsage / regionCapacity) * this.MM_COST_FOR_FULL_REGION + + overflow * this.getOverCapacityCost() + + this.state.regionHistoricalCost[regionId] + ) + } + + computeEdgeEntryCost(edgeId: number): number { + if (!this.USE_TOPOLOGY_CAPACITY) return 0 + + const currentNetId = this.state.currentRouteNetId + const nextUsage = + this.state.edgeUsage[edgeId] + + (currentNetId !== undefined && + this.state.edgeAssignedNets[edgeId]!.has(currentNetId) + ? 0 + : 1) + const edgeCapacity = this.regionGraph.edges[edgeId]!.portIds.length + const overflow = Math.max(0, nextUsage - edgeCapacity) + return ( + overflow * this.getOverCapacityCost() + + this.state.edgeHistoricalCost[edgeId] + ) + } + + getRegionCapacity(regionId: RegionId): number { + return this.USE_TOPOLOGY_CAPACITY + ? this.regionGraph.regionTrackCapacity[regionId]! + : this.regionGraph.regionCapacity[regionId]! + } + + getOverCapacityCost(): number { + // A simple path visits at most one region and one boundary per graph hop. + // This makes one overflow more expensive than any capacity-respecting path. + return this.regionGraph.regionCount * this.MM_COST_FOR_FULL_REGION * 2 + } + + hasOverloadedResources(): boolean { + if (!this.USE_TOPOLOGY_CAPACITY) return false + + for (let regionId = 0; regionId < this.regionGraph.regionCount; regionId++) { + if ( + this.state.regionUsage[regionId] > this.getRegionCapacity(regionId) + ) { + return true + } + } + for (const edge of this.regionGraph.edges) { + if (this.state.edgeUsage[edge.edgeId] > edge.portIds.length) return true + } + return false + } + + startNegotiationPass(): void { + const { state, regionGraph, regionProblem } = this + const overCapacityCost = this.getOverCapacityCost() + for (let regionId = 0; regionId < regionGraph.regionCount; regionId++) { + const overflow = Math.max( + 0, + state.regionUsage[regionId] - this.getRegionCapacity(regionId), + ) + state.regionHistoricalCost[regionId] += overflow * overCapacityCost + } + for (const edge of regionGraph.edges) { + const overflow = Math.max( + 0, + state.edgeUsage[edge.edgeId] - edge.portIds.length, + ) + state.edgeHistoricalCost[edge.edgeId] += overflow * overCapacityCost + } + + this.negotiationPass += 1 + state.regionUsage.fill(0) + state.edgeUsage.fill(0) + for (const routes of state.regionAssignedRoutes) routes.length = 0 + for (const nets of state.regionAssignedNets) nets.clear() + for (const nets of state.edgeAssignedNets) nets.clear() + for (const regionIds of state.solvedRouteRegionIds) regionIds.length = 0 + state.solvedRouteCosts.fill(0) + state.currentRouteId = undefined + state.currentRouteNetId = undefined + state.goalRegionId = -1 + state.unroutedRoutes = range(regionProblem.routeCount) + this.skippedRouteIds = [] + state.candidateQueue.clear() + this.resetCandidateBestCosts() + this.updateStats() } getSolvedRegionPath(finalCandidate: RegionPathCandidate): RegionId[] { @@ -283,6 +433,18 @@ export class RegionPathSolver extends BaseSolver { return regionPath } + getSolvedEdgePath(finalCandidate: RegionPathCandidate): number[] { + const edgePath: number[] = [] + let cursor: RegionPathCandidate | undefined = finalCandidate + + while (cursor) { + if (cursor.prevEdgeId !== undefined) edgePath.unshift(cursor.prevEdgeId) + cursor = cursor.prevCandidate + } + + return edgePath + } + onPathFound(finalCandidate: RegionPathCandidate) { const { state } = this const currentRouteId = state.currentRouteId @@ -292,13 +454,24 @@ export class RegionPathSolver extends BaseSolver { } const solvedRegionPath = this.getSolvedRegionPath(finalCandidate) + const solvedEdgePath = this.getSolvedEdgePath(finalCandidate) + const routeNetId = this.regionProblem.routeNet[currentRouteId]! state.solvedRouteRegionIds[currentRouteId] = solvedRegionPath state.solvedRouteCosts[currentRouteId] = finalCandidate.g for (const regionId of solvedRegionPath) { - state.regionUsage[regionId] += 1 + if (!state.regionAssignedNets[regionId]!.has(routeNetId)) { + state.regionAssignedNets[regionId]!.add(routeNetId) + state.regionUsage[regionId] += 1 + } state.regionAssignedRoutes[regionId]!.push(currentRouteId) } + for (const edgeId of solvedEdgePath) { + if (!state.edgeAssignedNets[edgeId]!.has(routeNetId)) { + state.edgeAssignedNets[edgeId]!.add(routeNetId) + state.edgeUsage[edgeId] += 1 + } + } state.currentRouteId = undefined state.currentRouteNetId = undefined @@ -313,13 +486,21 @@ export class RegionPathSolver extends BaseSolver { let maxRegionUsage = 0 let maxUtilization = 0 + let maxEdgeUsage = 0 + let maxEdgeUtilization = 0 for (let regionId = 0; regionId < regionGraph.regionCount; regionId++) { const usage = state.regionUsage[regionId] - const utilization = usage / regionGraph.regionCapacity[regionId] + const utilization = usage / this.getRegionCapacity(regionId) maxRegionUsage = Math.max(maxRegionUsage, usage) maxUtilization = Math.max(maxUtilization, utilization) } + for (let edgeId = 0; edgeId < regionGraph.edgeCount; edgeId++) { + const usage = state.edgeUsage[edgeId] + const utilization = usage / regionGraph.edges[edgeId]!.portIds.length + maxEdgeUsage = Math.max(maxEdgeUsage, usage) + maxEdgeUtilization = Math.max(maxEdgeUtilization, utilization) + } this.stats = { ...this.stats, @@ -333,8 +514,13 @@ export class RegionPathSolver extends BaseSolver { currentGoalRegionId: state.goalRegionId >= 0 ? state.goalRegionId : undefined, openCandidateCount: state.candidateQueue.length, + skippedRouteCount: this.skippedRouteIds.length, + skippedRouteIds: [...this.skippedRouteIds], maxRegionUsage, maxUtilization, + maxEdgeUsage, + maxEdgeUtilization, + negotiationPass: this.negotiationPass, } } diff --git a/lib/region-graph/visualizeRegionGraph.ts b/lib/region-graph/visualizeRegionGraph.ts index 13eded5..def7578 100644 --- a/lib/region-graph/visualizeRegionGraph.ts +++ b/lib/region-graph/visualizeRegionGraph.ts @@ -165,7 +165,7 @@ const getRegionFill = ( regionId: RegionId, ) => { const regionUsage = usage.regionUsage[regionId] - const capacity = solver.regionGraph.regionCapacity[regionId] + const capacity = solver.getRegionCapacity(regionId) const utilization = clamp01(regionUsage / capacity) const red = Math.round(216 + (239 - 216) * utilization) const green = Math.round(240 - 112 * utilization) @@ -181,7 +181,7 @@ const getRegionLabel = ( regionId: RegionId, ) => { const regionUsage = usage.regionUsage[regionId] - const capacity = solver.regionGraph.regionCapacity[regionId] + const capacity = solver.getRegionCapacity(regionId) const utilization = regionUsage / capacity const reservedNetId = solver.regionProblem.regionNetId[regionId] const assignedRoutes = solver.state.regionAssignedRoutes[regionId] @@ -265,7 +265,7 @@ const pushSmallGraphRegionLabels = ( { x: center.x, y: center.y, - text: `${usage.regionUsage[regionId]}/${solver.regionGraph.regionCapacity[regionId]} nets`, + text: `${usage.regionUsage[regionId]}/${solver.getRegionCapacity(regionId)} nets`, fontSize: 0.11, color: "rgb(15, 23, 42)", anchorSide: "center", diff --git a/lib/selective-rerip-tiny-hyper-graph-solver.ts b/lib/selective-rerip-tiny-hyper-graph-solver.ts index 64a7f2c..f30b467 100644 --- a/lib/selective-rerip-tiny-hyper-graph-solver.ts +++ b/lib/selective-rerip-tiny-hyper-graph-solver.ts @@ -186,6 +186,13 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr } override onOutOfCandidates(): void { + if ( + this.USE_REGION_PATH_CORRIDORS && + this.retryCurrentRouteWithGlobalSearch() + ) { + return + } + const failedRouteId = this.state.currentRouteId if (failedRouteId === undefined) { throw new Error( @@ -193,7 +200,13 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr ) } - const directPath = this.findRelaxedBlockerPath() + let directPath = this.findRelaxedBlockerPath(new Set(), 2) + if (!directPath.found) { + directPath = this.findRelaxedBlockerPath( + new Set(), + Number.POSITIVE_INFINITY, + ) + } if (!directPath.found || directPath.owners.size === 0) { this.selectiveReripStats.globalReripCount += 1 this.selectiveReripStats.globalReripReason = !directPath.found @@ -309,6 +322,7 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr protected findRelaxedBlockerPath( forbiddenOwnerRouteIds: ReadonlySet = new Set(), + searchScope = Number.POSITIVE_INFINITY, ): DistinctOwnerBlockerSearchResult< RelaxedSearchState, RouteId, @@ -344,6 +358,7 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr routeNetId, portOwners, forbiddenOwnerRouteIds, + searchScope, }), maxExpandedLabels: this.getRelaxedSearchExpansionLimit(), }) @@ -367,6 +382,7 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr routeNetId: number portOwners: ReadonlyMap> forbiddenOwnerRouteIds: ReadonlySet + searchScope: number }): Array<{ state: RelaxedSearchState distance: number @@ -387,6 +403,16 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr ] ?? []) { if (neighborPortId === state.portId) continue if (this.isPortReservedForDifferentNet(neighborPortId)) continue + if ( + this.USE_REGION_PATH_CORRIDORS && + !this.isPortAllowedByPreferredRegionCorridor( + state.nextRegionId, + neighborPortId, + params.searchScope, + ) + ) { + continue + } if ( neighborPortId !== goalPortId && this.problem.portSectionMask[neighborPortId] === 0 diff --git a/tests/solver/__snapshots__/region-path-capacity-negotiation.snap.svg b/tests/solver/__snapshots__/region-path-capacity-negotiation.snap.svg index 84f6819..71e7979 100644 --- a/tests/solver/__snapshots__/region-path-capacity-negotiation.snap.svg +++ b/tests/solver/__snapshots__/region-path-capacity-negotiation.snap.svg @@ -1,37 +1,37 @@ 1/1 nets1/1 nets2/1 nets1/1 nets1/1 nets0/1 nets0/1 nets0/1 nets0/1 netsflexible-start1/1 netsconstrained-start1/1 netsshared-left2/1 netsshared-right2/1 netsflexible-end1/1 netsconstrained-end1/1 netsdetour-left0/1 netsdetour-center0/1 netsdetour-right0/1 nets0/1 nets1/1 nets1/1 nets0/1 nets1/1 nets1/1 nets1/1 nets1/1 nets1/1 netsflexible-start1/1 netsconstrained-start1/1 netsshared-left1/1 netsshared-right1/1 netsflexible-end1/1 netsconstrained-end1/1 netsdetour-left1/1 netsdetour-center1/1 netsdetour-right1/1 nets {