Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ existing ports and contribute to region congestion immediately, but remain
eligible for the normal rip-and-reroute process. They do not create regions or
otherwise change the hypergraph topology.

Set `isFixed: true` on a serialized connection to preserve its assignments
through global retries, selective and partial rerips, and section optimization.
The loader stores this policy in `problem.routeIsFixed`, and serialized output
preserves it. A fixed route requires complete initial assignments connecting its
endpoints. Its ports and segments remain native routing state, so other routes
on the same net can reuse them. Omitted `isFixed` keeps the normal mutable policy.
The independent bus and region-path solvers reject fixed routes.

### Outside-in partial reripping

`SelectiveReripTinyHyperGraphSolver` preserves the unaffected prefix and
Expand Down
3 changes: 3 additions & 0 deletions lib/bus-solver/TinyHyperGraphBusSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ export class TinyHyperGraphBusSolver extends TinyHyperGraphSolver {
problem: TinyHyperGraphProblem,
options?: TinyHyperGraphBusSolverOptions,
) {
if (problem.routeIsFixed?.some((isFixed) => isFixed === 1)) {
throw new Error("TinyHyperGraphBusSolver does not support fixed routes")
}
super(topology, problem, options)

this.busTraceOrder = deriveBusTraceOrder(topology, problem)
Expand Down
1 change: 1 addition & 0 deletions lib/compat/convertToSerializedHyperGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ const getSerializedConnection = (
endRegionId: metadataEndRegionId ?? endRegionId,
mutuallyConnectedNetworkId:
metadataNetworkId ?? `net-${solver.problem.routeNet[routeId]}`,
...(solver.problem.routeIsFixed?.[routeId] === 1 && { isFixed: true }),
}
}

Expand Down
5 changes: 5 additions & 0 deletions lib/compat/loadSerializedHyperGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ export const loadSerializedHyperGraph = (
const routeStartPort = new Int32Array(routeCount)
const routeEndPort = new Int32Array(routeCount)
const routeNet = new Int32Array(routeCount)
const routeIsFixed = new Uint8Array(routeCount)

routableConnections.forEach(({ connection, solvedRoute }, routeIndex) => {
const fallbackStartPortId = getCentermostPortIdForRegion(
Expand Down Expand Up @@ -543,6 +544,9 @@ export const loadSerializedHyperGraph = (
routeEndPort[routeIndex] = endPortIndex

routeNet[routeIndex] = getNetIndex(connection)
if ("isFixed" in connection && connection.isFixed === true) {
routeIsFixed[routeIndex] = 1
}
})

const topology: TinyHyperGraphTopology = {
Expand Down Expand Up @@ -612,6 +616,7 @@ export const loadSerializedHyperGraph = (
routeEndPort,
routeNet,
regionNetId,
...(routeIsFixed.some((isFixed) => isFixed === 1) && { routeIsFixed }),
...(initialAssignments.length > 0 && { initialAssignments }),
...(hasPortPenalties && { portPenalty }),
}
Expand Down
17 changes: 16 additions & 1 deletion lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export interface TinyHyperGraphProblem {

// routeNet[routeId] = net id of the route
routeNet: Int32Array // NetId[]
/** routeIsFixed[routeId] = 1 preserves the route's initial assignments during every rerip. */
routeIsFixed?: Uint8Array
/** regionNetId[regionId] = reserved net id for the region, -1 means freely traversable */
regionNetId: Int32Array

Expand All @@ -154,7 +156,7 @@ export interface TinyHyperGraphProblem {
* Existing serialized region assignments, converted to numeric ids.
*
* These are regular route-owned assignments: they seed the initial routing
* state and may be ripped and rerouted by the normal solver machinery.
* state and may be ripped and rerouted unless routeIsFixed marks them fixed.
*/
initialAssignments?: TinyHyperGraphInitialAssignment[]
}
Expand Down Expand Up @@ -1452,6 +1454,19 @@ export class TinyHyperGraphSolver extends BaseSolver {
state.candidateQueue.clear()
this.resetCandidateBestCosts()
state.goalPortId = -1
applyInitialAssignments({
topology,
problem: {
...problem,
initialAssignments: problem.initialAssignments?.filter(
({ routeId }) => problem.routeIsFixed?.[routeId] === 1,
),
},
state,
routeSuccessCountByRouteId: this.routeSuccessCountByRouteId,
appendSegmentToRegionCache: (regionId, fromPortId, toPortId) =>
this.appendSegmentToRegionCache(regionId, fromPortId, toPortId),
})
}

protected getMaxRegionCost() {
Expand Down
18 changes: 17 additions & 1 deletion lib/initialAssignments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface InitialAssignmentProblem {
routeStartPort: Int32Array
routeEndPort: Int32Array
routeNet: Int32Array
routeIsFixed?: Uint8Array
initialAssignments?: TinyHyperGraphInitialAssignment[]
}

Expand Down Expand Up @@ -98,7 +99,12 @@ export const applyInitialAssignments = ({
}
| undefined => {
const assignments = problem.initialAssignments ?? []
if (assignments.length === 0) return
if (
problem.routeIsFixed !== undefined &&
problem.routeIsFixed.length !== problem.routeCount
) {
throw new Error("routeIsFixed must contain one entry per route")
}

const assignmentsByRoute = new Map<
RouteId,
Expand Down Expand Up @@ -147,6 +153,16 @@ export const applyInitialAssignments = ({
assertAssignmentsConnectRoute(problem, routeId, routeAssignments)
}

for (let routeId = 0; routeId < problem.routeCount; routeId++) {
if (
problem.routeIsFixed?.[routeId] === 1 &&
!assignmentsByRoute.has(routeId)
) {
throw new Error(`Fixed route ${routeId} requires initial assignments`)
}
}
if (assignments.length === 0) return

const initiallyRoutedRouteIds = new Set(assignmentsByRoute.keys())
for (const { routeId, regionId, fromPortId, toPortId } of assignments) {
const routeNetId = problem.routeNet[routeId]!
Expand Down
1 change: 1 addition & 0 deletions lib/outside-in-partial-rip-tiny-hypergraph-solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ export class OutsideInPartialRipTinyHyperGraphSolver extends DistanceAwareTinyHy
if (!orderedSegments) return false

if (
this.problem.routeIsFixed?.[routeId] === 1 ||
!routeIdsTouchingHotRegions.has(routeId) ||
(hasNonPreferredRouteTouchingHotRegions &&
preferredPreservedRouteIds.has(routeId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ export class SelectiveReripTinyHyperGraphSolverWithStableInitialAssignments exte

applyInitialAssignments({
topology: this.topology,
problem: this.problem,
problem: {
...this.problem,
routeIsFixed: undefined,
initialAssignments: this.problem.initialAssignments.filter(
({ routeId }) => this.problem.routeIsFixed?.[routeId] !== 1,
),
},
state: this.state,
routeSuccessCountByRouteId: this.routeSuccessCountByRouteId,
appendSegmentToRegionCache: (regionId, fromPortId, toPortId) =>
Expand Down
3 changes: 3 additions & 0 deletions lib/region-graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ export const createRegionPathProblem = (
topology: TinyHyperGraphTopology,
problem: TinyHyperGraphProblem,
): RegionPathProblem => {
if (problem.routeIsFixed?.some((isFixed) => isFixed === 1)) {
throw new Error("RegionPathSolver does not support fixed routes")
}
const regionIndexBySerializedId = getRegionIndexBySerializedId(topology)
const routeStartRegion = new Int32Array(problem.routeCount)
const routeEndRegion = new Int32Array(problem.routeCount)
Expand Down
2 changes: 2 additions & 0 deletions lib/section-solver/TinyHyperGraphSectionPipelineSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ const createProblemWithPortSectionMask = (
routeStartPort: new Int32Array(problem.routeStartPort),
routeEndPort: new Int32Array(problem.routeEndPort),
routeNet: new Int32Array(problem.routeNet),
routeIsFixed: problem.routeIsFixed,
initialAssignments: problem.initialAssignments,
regionNetId: new Int32Array(problem.regionNetId),
portPenalty:
problem.portPenalty === undefined
Expand Down
11 changes: 10 additions & 1 deletion lib/section-solver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ const createSectionRoutePlans = (
})
}

if (maskedRuns.length === 0) {
if (problem.routeIsFixed?.[routeId] === 1 || maskedRuns.length === 0) {
for (let portIndex = 1; portIndex < orderedPortIds.length; portIndex++) {
routePlan.fixedSegments.push({
regionId: orderedRegionIds[portIndex - 1]!,
Expand Down Expand Up @@ -565,6 +565,14 @@ const createSectionRoutePlans = (
routeStartPort,
routeEndPort,
routeNet: new Int32Array(problem.routeNet),
routeIsFixed: problem.routeIsFixed,
initialAssignments: routePlans.flatMap((routePlan) => {
if (problem.routeIsFixed?.[routePlan.routeId] !== 1) return []
return routePlan.fixedSegments.map((segment) => ({
routeId: routePlan.routeId,
...segment,
}))
}),
regionNetId: new Int32Array(problem.regionNetId),
portPenalty:
problem.portPenalty === undefined
Expand Down Expand Up @@ -636,6 +644,7 @@ class TinyHyperGraphSectionSearchSolver extends TinyHyperGraphSolver {

applyFixedSegments() {
for (const routePlan of this.routePlans) {
if (this.problem.routeIsFixed?.[routePlan.routeId] === 1) continue
for (const {
regionId,
fromPortId,
Expand Down
17 changes: 16 additions & 1 deletion lib/selective-rerip-tiny-hyper-graph-solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,16 @@ export class SelectiveReripTinyHyperGraphSolver extends OutsideInPartialRipTinyH
)
}

const forbiddenOwners = new Set(forbiddenOwnerRouteIds)
for (
let ownerRouteId = 0;
ownerRouteId < this.problem.routeCount;
ownerRouteId++
) {
if (this.problem.routeIsFixed?.[ownerRouteId] === 1) {
forbiddenOwners.add(ownerRouteId)
}
}
const portOwners = this.getPortOwners()
const boundedReachabilityGeometry = this.hasBoundedReachabilityGeometry({
startPortId,
Expand All @@ -399,7 +409,7 @@ export class SelectiveReripTinyHyperGraphSolver extends OutsideInPartialRipTinyH
portOwners,
portResources,
hopTemplatesByRegion,
forbiddenOwnerRouteIds,
forbiddenOwnerRouteIds: forbiddenOwners,
}
const maxExpandedLabels = this.getRelaxedSearchExpansionLimit()
const searchOptions: DistinctOwnerBlockerSearchOptions<
Expand Down Expand Up @@ -805,6 +815,11 @@ export class SelectiveReripTinyHyperGraphSolver extends OutsideInPartialRipTinyH
}

private rebuildCommittedState(rippedRouteIds: ReadonlySet<RouteId>): void {
for (const routeId of rippedRouteIds) {
if (this.problem.routeIsFixed?.[routeId] === 1) {
throw new Error(`Cannot rip fixed route ${routeId}`)
}
}
const previousRegionSegments = this.state.regionSegments
this.state.regionSegments = this.state.regionSegments.map((segments) =>
segments.filter(([routeId]) => !rippedRouteIds.has(routeId)),
Expand Down
12 changes: 12 additions & 0 deletions tests/outside-in-partial-rip-tiny-hypergraph-solver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ test("partial rip leaves preferred routes unchanged when another hot route can m
])
})

test("partial rip keeps fixed routes even when no movable hot route remains", () => {
const solver = createLinearSolver()
solver.problem.routeIsFixed = new Uint8Array([1])
const initialRegionSegments = structuredClone(solver.state.regionSegments)
const regionCosts = new Float64Array(6)
regionCosts[3] = 1

expect(solver.prepare([3], regionCosts)).toBe(false)
expect(solver.state.regionSegments).toEqual(initialRegionSegments)
expect(solver.state.unroutedRoutes).toEqual([])
})

test("partial rip locally rerips a preferred route when every hot route is preferred", () => {
const solver = createLinearSolver()
solver.preferredPreservedRouteIds.add(0)
Expand Down
9 changes: 9 additions & 0 deletions tests/selective-rerip-blocker-reachability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ test("selective search rejects exact owner cuts and keeps preservation searches
})
expect(solver.searchPreferringPreservedRoutes()).toEqual(direct)

problem.routeIsFixed = new Uint8Array([0, 1])
expect(solver.search()).toEqual({
found: false,
reason: "no_path",
expandedLabelCount: 1,
})
expect(solver.searchPreferringPreservedRoutes()).toEqual(solver.search())
problem.routeIsFixed = undefined

problem.regionNetId[1] = 2
expect(solver.search()).toEqual({
found: false,
Expand Down
Loading
Loading