diff --git a/lib/core.ts b/lib/core.ts index 58e9004..44ad336 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -7,6 +7,10 @@ import { isKnownSingleLayerMask, } from "./computeRegionCost" import { countNewIntersectionsWithValues } from "./countNewIntersections" +import { + createDirectedRouteHopHeuristic, + getDirectedRouteHopCount, +} from "./directed-route-hop-heuristic" import { applyInitialAssignments, type TinyHyperGraphInitialAssignment, @@ -167,6 +171,8 @@ export interface TinyHyperGraphProblem { export interface TinyHyperGraphProblemSetup { // portHCostToEndOfRoute[portId * routeCount + routeId] = distance from port to end of route portHCostToEndOfRoute: Float64Array + /** Directed graph distance is only needed for cross-layer routes. */ + directedHopCountToEndByRoute: Array portEndpointNetIds: Array> /** -1 for no endpoint, -2 for endpoints from multiple nets, otherwise the sole endpoint net. */ portEndpointReservationNetId: Int32Array @@ -486,9 +492,27 @@ export class TinyHyperGraphSolver extends BaseSolver { const routeNetId = problem.routeNet[routeId]! recordEndpointNet(problem.routeStartPort[routeId]!, routeNetId) recordEndpointNet(problem.routeEndPort[routeId]!, routeNetId) + } + + const directedHopCountToEndByRoute: Array = + new Array(problem.routeCount) + + for (let routeId = 0; routeId < problem.routeCount; routeId++) { + const startPortId = problem.routeStartPort[routeId]! + const endPortId = problem.routeEndPort[routeId]! + + if (topology.portZ[startPortId] !== topology.portZ[endPortId]) { + directedHopCountToEndByRoute[routeId] = createDirectedRouteHopHeuristic( + { + topology, + problem, + portEndpointReservationNetId, + routeId, + }, + ) + } if (portHCostToEndOfRoute) { - const endPortId = problem.routeEndPort[routeId] const endX = portX[endPortId] const endY = portY[endPortId] @@ -503,6 +527,7 @@ export class TinyHyperGraphSolver extends BaseSolver { return { portHCostToEndOfRoute: portHCostToEndOfRoute as Float64Array, + directedHopCountToEndByRoute, portEndpointNetIds, portEndpointReservationNetId, } @@ -629,7 +654,6 @@ export class TinyHyperGraphSolver extends BaseSolver { const g = this.computeG(currentCandidate, neighborPortId) if (!Number.isFinite(g)) continue - const h = this.computeH(neighborPortId) const nextRegionId = topology.incidentPortRegion[neighborPortId][0] === @@ -644,6 +668,8 @@ export class TinyHyperGraphSolver extends BaseSolver { continue } + const h = this.computeH(neighborPortId, nextRegionId) + const newCandidate = { prevRegionId: currentCandidate.nextRegionId, nextRegionId, @@ -1529,15 +1555,46 @@ export class TinyHyperGraphSolver extends BaseSolver { this.logNeverSuccessfullyRoutedRoutes() } - computeH(neighborPortId: PortId): number { + computeH(neighborPortId: PortId, nextRegionId: RegionId): number { const precomputedHCost = this.problemSetup.portHCostToEndOfRoute + const routeId = this.state.currentRouteId! + const directedHopCountToEnd = + this.problemSetup.directedHopCountToEndByRoute[routeId] + const startPortId = this.problem.routeStartPort[routeId]! + + if ( + directedHopCountToEnd && + this.topology.portZ[neighborPortId] === this.topology.portZ[startPortId] + ) { + const directedHopCount = getDirectedRouteHopCount( + this.topology, + directedHopCountToEnd, + neighborPortId, + nextRegionId, + ) + if (directedHopCount === -1) return Number.POSITIVE_INFINITY + + const directDistanceCost = precomputedHCost + ? precomputedHCost[neighborPortId * this.problem.routeCount + routeId]! + : this.computeDirectDistanceHeuristic(neighborPortId, routeId) + + return directDistanceCost + directedHopCount * this.DISTANCE_TO_COST + } + if (precomputedHCost) { return precomputedHCost[ - neighborPortId * this.problem.routeCount + this.state.currentRouteId! + neighborPortId * this.problem.routeCount + routeId ] } - const endPortId = this.problem.routeEndPort[this.state.currentRouteId!] + return this.computeDirectDistanceHeuristic(neighborPortId, routeId) + } + + private computeDirectDistanceHeuristic( + neighborPortId: PortId, + routeId: RouteId, + ): number { + const endPortId = this.problem.routeEndPort[routeId]! const dx = this.getPortRoutingCostX(neighborPortId) - this.getPortRoutingCostX(endPortId) diff --git a/lib/directed-route-hop-heuristic.ts b/lib/directed-route-hop-heuristic.ts new file mode 100644 index 0000000..aa7a97d --- /dev/null +++ b/lib/directed-route-hop-heuristic.ts @@ -0,0 +1,148 @@ +import type { TinyHyperGraphProblem, TinyHyperGraphTopology } from "./core" +import type { NetId, PortId, RegionId, RouteId } from "./types" + +const UNREACHABLE_HOP_COUNT = -1 + +interface CreateDirectedRouteHopHeuristicContext { + topology: TinyHyperGraphTopology + problem: TinyHyperGraphProblem + portEndpointReservationNetId: Int32Array + routeId: RouteId +} + +const getDirectedHopIndex = ( + topology: TinyHyperGraphTopology, + portId: PortId, + nextRegionId: RegionId, +) => { + const incidentRegions = topology.incidentPortRegion[portId] ?? [] + if (incidentRegions[0] === nextRegionId) return portId * 2 + if (incidentRegions[1] === nextRegionId) return portId * 2 + 1 + return -1 +} + +const isRegionAvailableToNet = ( + problem: TinyHyperGraphProblem, + routeNetId: NetId, + regionId: RegionId, +) => { + const reservedNetId = problem.regionNetId[regionId] + return reservedNetId === -1 || reservedNetId === routeNetId +} + +const isPortAvailableToNet = ( + problem: TinyHyperGraphProblem, + portEndpointReservationNetId: Int32Array, + routeNetId: NetId, + portId: PortId, +) => { + if (problem.portSectionMask[portId] === 0) return false + + const reservedNetId = portEndpointReservationNetId[portId] ?? -1 + return reservedNetId === -1 || reservedNetId === routeNetId +} + +/** + * Computes the remaining number of directed region traversals to a route's + * goal. A directed hop is a port together with the region the candidate will + * enter next, matching the state used by the A* candidate queue. + */ +export const createDirectedRouteHopHeuristic = ({ + topology, + problem, + portEndpointReservationNetId, + routeId, +}: CreateDirectedRouteHopHeuristicContext): Int32Array => { + const hopCountToGoal = new Int32Array(topology.portCount * 2).fill( + UNREACHABLE_HOP_COUNT, + ) + const routeNetId = problem.routeNet[routeId]! + const goalPortId = problem.routeEndPort[routeId]! + const queuedPortIds: PortId[] = [] + const queuedNextRegionIds: RegionId[] = [] + + const queueHop = ( + portId: PortId, + nextRegionId: RegionId, + hopCount: number, + ) => { + const directedHopIndex = getDirectedHopIndex(topology, portId, nextRegionId) + if ( + directedHopIndex === -1 || + hopCountToGoal[directedHopIndex] !== UNREACHABLE_HOP_COUNT + ) { + return + } + + hopCountToGoal[directedHopIndex] = hopCount + queuedPortIds.push(portId) + queuedNextRegionIds.push(nextRegionId) + } + + for (const goalRegionId of topology.incidentPortRegion[goalPortId] ?? []) { + if (!isRegionAvailableToNet(problem, routeNetId, goalRegionId)) continue + + for (const portId of topology.regionIncidentPorts[goalRegionId] ?? []) { + queueHop(portId, goalRegionId, 0) + } + } + + const reverseExpansionCountByRegion = new Uint8Array(topology.regionCount) + + for (let queueIndex = 0; queueIndex < queuedPortIds.length; queueIndex++) { + const exitPortId = queuedPortIds[queueIndex]! + const nextRegionId = queuedNextRegionIds[queueIndex]! + if ( + !isPortAvailableToNet( + problem, + portEndpointReservationNetId, + routeNetId, + exitPortId, + ) + ) { + continue + } + + const incidentRegions = topology.incidentPortRegion[exitPortId] ?? [] + const previousRegionId = + incidentRegions[0] === nextRegionId + ? incidentRegions[1] + : incidentRegions[0] + if ( + previousRegionId === undefined || + !isRegionAvailableToNet(problem, routeNetId, previousRegionId) || + reverseExpansionCountByRegion[previousRegionId]! >= 2 + ) { + continue + } + + reverseExpansionCountByRegion[previousRegionId] += 1 + const currentHopIndex = getDirectedHopIndex( + topology, + exitPortId, + nextRegionId, + ) + const previousHopCount = hopCountToGoal[currentHopIndex]! + 1 + + for (const previousPortId of topology.regionIncidentPorts[ + previousRegionId + ] ?? []) { + if (previousPortId === exitPortId) continue + queueHop(previousPortId, previousRegionId, previousHopCount) + } + } + + return hopCountToGoal +} + +export const getDirectedRouteHopCount = ( + topology: TinyHyperGraphTopology, + hopCountToGoal: Int32Array, + portId: PortId, + nextRegionId: RegionId, +) => { + const directedHopIndex = getDirectedHopIndex(topology, portId, nextRegionId) + return directedHopIndex === -1 + ? UNREACHABLE_HOP_COUNT + : hopCountToGoal[directedHopIndex]! +} diff --git a/tests/solver/__snapshots__/cross-layer-heuristic-detour.snap.svg b/tests/solver/__snapshots__/cross-layer-heuristic-detour.snap.svg index e58d79a..68daeb7 100644 --- a/tests/solver/__snapshots__/cross-layer-heuristic-detour.snap.svg +++ b/tests/solver/__snapshots__/cross-layer-heuristic-detour.snap.svg @@ -1,241 +1,264 @@ input topologysolver resultinput topologysolver result