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
66 changes: 19 additions & 47 deletions lib/DuplicateCongestedPortSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
return {
RIP_THRESHOLD_RAMP_ATTEMPTS: 0,
STATIC_REACHABILITY_PRECHECK: false,
USE_LAZY_ROUTE_HEURISTIC: true,
...this.options.routeSolveOptions,
}
}
Expand All @@ -327,12 +326,6 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
const { topology, problem } = loadSerializedHyperGraph(
this.serializedHyperGraph,
)
const serializedPortIds = Array.from(
{ length: topology.portCount },
(_, portId) => getSerializedPortId(topology, portId),
)
topology.portMetadata = undefined
topology.regionMetadata = undefined
const portUseCounts = new Map<string, number>()

for (let routeId = 0; routeId < problem.routeCount; routeId++) {
Expand All @@ -353,7 +346,7 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
}

for (const portId of getUsedPortIdsForSolvedRoute(routeSolver)) {
const serializedPortId = serializedPortIds[portId] ?? `port-${portId}`
const serializedPortId = getSerializedPortId(topology, portId)
portUseCounts.set(
serializedPortId,
(portUseCounts.get(serializedPortId) ?? 0) + 1,
Expand All @@ -374,48 +367,27 @@ export class DuplicateCongestedPortSolver extends BaseSolver {

const { solvedRoutes: _solvedRoutes, ...restHyperGraph } =
this.serializedHyperGraph
const regions = [...this.serializedHyperGraph.regions]
const ports = [...this.serializedHyperGraph.ports]
const regionById = new Map(
this.serializedHyperGraph.regions.map((region) => [
region.regionId,
region,
]),
const regions: SerializedRegion[] = this.serializedHyperGraph.regions.map(
(region) => ({
...region,
pointIds: [...region.pointIds],
d: cloneSerializableValue(region.d),
}),
)
const regionIndexById = new Map(
this.serializedHyperGraph.regions.map((region, regionIndex) => [
region.regionId,
regionIndex,
]),
const ports: SerializedPort[] = this.serializedHyperGraph.ports.map(
(port) => ({
...port,
d: cloneSerializableValue(port.d),
}),
)
const sourcePortById = new Map(
this.serializedHyperGraph.ports.map(
(port) => [port.portId, port] as const,
),
const regionById = new Map(
regions.map((region) => [region.regionId, region]),
)
const usedPortIds = new Set(
this.serializedHyperGraph.ports.map((port) => port.portId),
const sourcePortById = new Map(
ports.map((port) => [port.portId, port] as const),
)
const usedPortIds = new Set(ports.map((port) => port.portId))
const duplicatedPorts: DuplicatedPortSummary[] = []
const mutableRegionIds = new Set<string>()

const getMutableRegion = (regionId: string) => {
const regionIndex = regionIndexById.get(regionId)
if (regionIndex === undefined) return undefined

const region = regions[regionIndex]
if (!region) return undefined

if (!mutableRegionIds.has(regionId)) {
regions[regionIndex] = {
...region,
pointIds: [...region.pointIds],
}
mutableRegionIds.add(regionId)
}

return regions[regionIndex]
}

for (const [sourcePortId, useCount] of [...portUseCounts.entries()].sort(
([leftPortId], [rightPortId]) => leftPortId.localeCompare(rightPortId),
Expand Down Expand Up @@ -470,7 +442,7 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
}

for (const regionId of [sourcePort.region1Id, sourcePort.region2Id]) {
const region = getMutableRegion(regionId)
const region = regionById.get(regionId)
if (!region) continue
insertDuplicatePortIdsAfterSource(
region.pointIds,
Expand Down Expand Up @@ -498,7 +470,7 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
connections:
this.serializedHyperGraph.connections === undefined
? undefined
: [...this.serializedHyperGraph.connections],
: cloneSerializableValue(this.serializedHyperGraph.connections),
}
}

Expand Down
14 changes: 1 addition & 13 deletions lib/compat/convertToSerializedHyperGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,8 @@ interface RouteSegment {
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null

const copyEnumerableRecord = (
value: Record<string, unknown>,
): Record<string, unknown> => {
const clone: Record<string, unknown> = {}

for (const key in value) {
clone[key] = value[key]
}

return clone
}

const toObjectRecord = (value: unknown): Record<string, unknown> => {
if (isRecord(value)) return copyEnumerableRecord(value)
if (isRecord(value)) return { ...value }
if (value === undefined) return {}
return { value }
}
Expand Down
4 changes: 2 additions & 2 deletions lib/compat/loadSerializedHyperGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const addSerializedRegionIdToMetadata = (
) => {
const metadata =
region.d && typeof region.d === "object" && !Array.isArray(region.d)
? Object.create(region.d)
? { ...region.d }
: { value: region.d }

metadata.layer = layer
Expand All @@ -109,7 +109,7 @@ const addSerializedPortIdToMetadata = (
) => {
const metadata =
port.d && typeof port.d === "object" && !Array.isArray(port.d)
? Object.create(port.d)
? { ...port.d }
: { value: port.d }

metadata.layer = layer
Expand Down
50 changes: 3 additions & 47 deletions lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,6 @@ interface SegmentGeometryScratch {

export class TinyHyperGraphSolver extends BaseSolver {
state: TinyHyperGraphWorkingState
/** Slots per port in the compact hop-cost arrays (max incident regions across ports) */
protected hopSlotStride: number
/** Fallback storage for hops whose region is not incident to the port (encoded as negative hop ids) */
private hopOverflowBestCost?: Map<number, number>
private _problemSetup?: TinyHyperGraphProblemSetup
protected routeAttemptCountByRouteId: Uint32Array
protected routeSuccessCountByRouteId: Uint32Array
Expand Down Expand Up @@ -387,14 +383,6 @@ export class TinyHyperGraphSolver extends BaseSolver {
) {
super()
applyTinyHyperGraphSolverOptions(this, options)
let hopSlotStride = 1
for (let portId = 0; portId < topology.portCount; portId++) {
const incidentCount = topology.incidentPortRegion[portId]?.length ?? 0
if (incidentCount > hopSlotStride) {
hopSlotStride = incidentCount
}
}
this.hopSlotStride = hopSlotStride
this.state = {
portAssignment: new Int32Array(topology.portCount).fill(-1),
regionSegments: Array.from({ length: topology.regionCount }, () => []),
Expand All @@ -408,10 +396,10 @@ export class TinyHyperGraphSolver extends BaseSolver {
candidateQueue: new MinHeap([], compareCandidatesByF),
candidateBestCostByHopId: this.USE_SPARSE_CANDIDATE_STORAGE
? new Map()
: new Float64Array(topology.portCount * hopSlotStride),
: new Float64Array(topology.portCount * topology.regionCount),
candidateBestCostGenerationByHopId: this.USE_SPARSE_CANDIDATE_STORAGE
? new Map()
: new Uint32Array(topology.portCount * hopSlotStride),
: new Uint32Array(topology.portCount * topology.regionCount),
candidateBestCostGeneration: 1,
goalPortId: -1,
ripCount: 0,
Expand Down Expand Up @@ -621,8 +609,6 @@ export class TinyHyperGraphSolver extends BaseSolver {
resetCandidateBestCosts() {
const { state } = this

this.hopOverflowBestCost?.clear()

if (state.candidateBestCostGeneration === 0xffffffff) {
if (state.candidateBestCostByHopId instanceof Map) {
state.candidateBestCostByHopId.clear()
Expand All @@ -640,10 +626,6 @@ export class TinyHyperGraphSolver extends BaseSolver {
}

getCandidateBestCost(hopId: HopId) {
if (hopId < 0) {
return this.hopOverflowBestCost?.get(hopId) ?? Number.POSITIVE_INFINITY
}

const { state } = this
const bestCostGeneration = state.candidateBestCostGenerationByHopId

Expand All @@ -657,11 +639,6 @@ export class TinyHyperGraphSolver extends BaseSolver {
}

setCandidateBestCost(hopId: HopId, bestCost: number) {
if (hopId < 0) {
;(this.hopOverflowBestCost ??= new Map()).set(hopId, bestCost)
return
}

const { state } = this

if (state.candidateBestCostGenerationByHopId instanceof Map) {
Expand All @@ -681,29 +658,8 @@ export class TinyHyperGraphSolver extends BaseSolver {
}
}

/**
* Drop search-only state that is no longer needed once the solver has
* finished. Everything released here is rebuilt on demand: `problemSetup`
* is a lazy getter, and the candidate queue/overflow map are reset at the
* start of every route attempt.
*/
releaseTransientSearchState() {
this._problemSetup = undefined
this.hopOverflowBestCost = undefined
this.bestSolvedStateSnapshot = undefined
this.state.candidateQueue.clear()
}

getHopId(portId: PortId, nextRegionId: RegionId): HopId {
// Hops are keyed by (port, incident-region slot) so the dense cost arrays
// stay O(ports * max-incidence) instead of O(ports * regions).
const incidentRegions = this.topology.incidentPortRegion[portId]
const slot = incidentRegions ? incidentRegions.indexOf(nextRegionId) : -1
if (slot === -1) {
// Non-incident hop: encode as a negative id routed to the overflow map.
return -(portId * this.topology.regionCount + nextRegionId) - 1
}
return portId * this.hopSlotStride + slot
return portId * this.topology.regionCount + nextRegionId
}

getStartingNextRegionId(
Expand Down
58 changes: 6 additions & 52 deletions lib/section-solver/TinyHyperGraphSectionPipelineSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import type {
import { TinyHyperGraphSolver } from "../core"
import type { RegionId } from "../types"
import type { TinyHyperGraphSectionSolverOptions } from "./index"
import {
createSolvedSolverFromSolution,
getActiveSectionRouteIds,
TinyHyperGraphSectionSolver,
} from "./index"
import { getActiveSectionRouteIds, TinyHyperGraphSectionSolver } from "./index"
import {
createSectionMaskCandidatesForHotRegions,
DEFAULT_TINY_HYPERGRAPH_SECTION_CANDIDATE_FAMILIES,
Expand Down Expand Up @@ -76,24 +72,17 @@ const getMaxRegionCost = (solver: TinyHyperGraphSolver) =>

const getSerializedOutputMaxRegionCost = (
serializedHyperGraph: SerializedHyperGraph,
solveGraphOptions?: TinyHyperGraphSolverOptions,
sectionSolverOptions?: TinyHyperGraphSectionSolverOptions,
) => {
const replay = loadSerializedHyperGraph(serializedHyperGraph)
const replayedSolver = createSolvedSolverFromSolution(
const replayedSolver = new TinyHyperGraphSectionSolver(
replay.topology,
replay.problem,
replay.solution,
{
...DEFAULT_SOLVE_GRAPH_OPTIONS,
...solveGraphOptions,
...(sectionSolverOptions?.minViaPadDiameter === undefined
? {}
: { minViaPadDiameter: sectionSolverOptions.minViaPadDiameter }),
},
sectionSolverOptions,
)

return getMaxRegionCost(replayedSolver)
return getMaxRegionCost(replayedSolver.baselineSolver)
}

const createPortSectionMaskForRegionIds = (
Expand Down Expand Up @@ -268,12 +257,6 @@ const findBestAutomaticSectionMask = (
const candidateReplayScoreStartTime = performance.now()
const replayedFinalMaxRegionCost = getSerializedOutputMaxRegionCost(
sectionSolver.getOutput(),
{
...DEFAULT_SOLVE_GRAPH_OPTIONS,
...(sectionSolverOptions.minViaPadDiameter === undefined
? {}
: { minViaPadDiameter: sectionSolverOptions.minViaPadDiameter }),
},
sectionSolverOptions,
)
candidateReplayScoreMs +=
Expand Down Expand Up @@ -340,14 +323,6 @@ export class TinyHyperGraphSectionPipelineSolver extends BasePipelineSolver<Tiny
selectedSectionMask?: Int8Array
selectedSectionCandidateLabel?: string
selectedSectionCandidateFamily?: TinyHyperGraphSectionCandidateFamily
private cachedSectionStageParams?:
| [
TinyHyperGraphTopology,
TinyHyperGraphProblem,
TinyHyperGraphSolution,
TinyHyperGraphSectionSolverOptions,
]
| undefined

constructor(inputProblem: TinyHyperGraphSectionPipelineInput) {
super(inputProblem)
Expand Down Expand Up @@ -397,23 +372,12 @@ export class TinyHyperGraphSectionPipelineSolver extends BasePipelineSolver<Tiny
instance.getSolveGraphOptions(),
] as ConstructorParameters<typeof TinyHyperGraphSolver>
},
onSolved: (instance: TinyHyperGraphSectionPipelineSolver) => {
instance
.getSolver<TinyHyperGraphSolver>("solveGraph")
?.releaseTransientSearchState()
},
},
{
solverName: "optimizeSection",
solverClass: TinyHyperGraphSectionSolver,
getConstructorParams: (instance: TinyHyperGraphSectionPipelineSolver) =>
instance.getSectionStageParams(),
onSolved: (instance: TinyHyperGraphSectionPipelineSolver) => {
instance
.getSolver<TinyHyperGraphSectionSolver>("optimizeSection")
?.releaseTransientSearchState()
instance.cachedSectionStageParams = undefined
},
},
]

Expand All @@ -423,10 +387,6 @@ export class TinyHyperGraphSectionPipelineSolver extends BasePipelineSolver<Tiny
TinyHyperGraphSolution,
TinyHyperGraphSectionSolverOptions,
] {
if (this.cachedSectionStageParams) {
return this.cachedSectionStageParams
}

const solvedSerializedHyperGraph =
this.getStageOutput<SerializedHyperGraph>("solveGraph")

Expand Down Expand Up @@ -510,15 +470,9 @@ export class TinyHyperGraphSectionPipelineSolver extends BasePipelineSolver<Tiny
.length,
}

this.cachedSectionStageParams = [
topology,
problem,
solution,
sectionSolverOptions,
]

return this.cachedSectionStageParams
return [topology, problem, solution, sectionSolverOptions]
}

getInitialVisualizationSolver() {
if (!this.initialVisualizationSolver) {
const { topology, problem } = this.loadHyperGraph(
Expand Down
Loading
Loading