Skip to content
Draft
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
21 changes: 14 additions & 7 deletions lib/DuplicateCongestedPortSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,9 +562,6 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
const availableLaneCount = physicalLanePoints?.length
const capacityIsInsufficient =
availableLaneCount !== undefined && availableLaneCount < useCount
const capacityLimitedLanePoints = capacityIsInsufficient
? physicalLanePoints
: undefined
const duplicateCount = capacityIsInsufficient
? Math.max(0, availableLaneCount - 1)
: useCount - 1
Expand All @@ -578,10 +575,14 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
regionById,
)
const sourcePoint = getPortPoint(sourcePort)
if (capacityLimitedLanePoints?.[0]) {
if (physicalLanePoints?.[0]) {
const sourcePortData = toObjectRecord(sourcePort.d)
sourcePortData.x = capacityLimitedLanePoints[0].x
sourcePortData.y = capacityLimitedLanePoints[0].y
if (!capacityIsInsufficient) {
sourcePortData.routingCostX = sourcePoint.x
sourcePortData.routingCostY = sourcePoint.y
}
sourcePortData.x = physicalLanePoints[0].x
sourcePortData.y = physicalLanePoints[0].y
sourcePort.d = sourcePortData
}
const duplicatePortIds: string[] = []
Expand All @@ -598,15 +599,21 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
)
const offset =
(duplicatePortProximity * duplicateIndex) / (duplicateCount + 1)
const duplicatePoint = capacityLimitedLanePoints?.[duplicateIndex] ?? {
const legacyDuplicatePoint = {
x: sourcePoint.x + duplicateDirection.x * offset,
y: sourcePoint.y + duplicateDirection.y * offset,
}
const duplicatePoint =
physicalLanePoints?.[duplicateIndex] ?? legacyDuplicatePoint
const duplicatedPortData = toObjectRecord(
cloneSerializableValue(sourcePort.d),
)
duplicatedPortData.x = duplicatePoint.x
duplicatedPortData.y = duplicatePoint.y
if (physicalLanePoints && !capacityIsInsufficient) {
duplicatedPortData.routingCostX = legacyDuplicatePoint.x
duplicatedPortData.routingCostY = legacyDuplicatePoint.y
}
duplicatedPortData.duplicatedFromPortId = sourcePortId
duplicatedPortData.duplicateIndex = duplicateIndex
duplicatedPortData.duplicatePortUseCount = useCount
Expand Down
14 changes: 14 additions & 0 deletions lib/compat/loadSerializedHyperGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ const getSerializedPortY = (
port: SerializedHyperGraph["ports"][number],
): number => Number(port.d?.y ?? 0)

const getSerializedPortRoutingCostX = (
port: SerializedHyperGraph["ports"][number],
): number => Number(port.d?.routingCostX ?? port.d?.x ?? 0)

const getSerializedPortRoutingCostY = (
port: SerializedHyperGraph["ports"][number],
): number => Number(port.d?.routingCostY ?? port.d?.y ?? 0)

const computePortAngle = (
port: SerializedHyperGraph["ports"][number],
region: SerializedHyperGraph["regions"][number] | undefined,
Expand Down Expand Up @@ -389,6 +397,8 @@ export const loadSerializedHyperGraph = (
const portAngleForRegion2 = new Int32Array(portCount)
const portX = new Float64Array(portCount)
const portY = new Float64Array(portCount)
const portRoutingCostX = new Float64Array(portCount)
const portRoutingCostY = new Float64Array(portCount)
const portZ = new Int32Array(portCount)

filteredHyperGraph.ports.forEach((port, portIndex) => {
Expand All @@ -404,6 +414,8 @@ export const loadSerializedHyperGraph = (
incidentPortRegion[portIndex] = [region1Index, region2Index]
portX[portIndex] = getSerializedPortX(port)
portY[portIndex] = getSerializedPortY(port)
portRoutingCostX[portIndex] = getSerializedPortRoutingCostX(port)
portRoutingCostY[portIndex] = getSerializedPortRoutingCostY(port)
portZ[portIndex] = getSerializedPortZ(port)
portAngleForRegion1[portIndex] = computePortAngle(
port,
Expand Down Expand Up @@ -560,6 +572,8 @@ export const loadSerializedHyperGraph = (
portAngleForRegion2,
portX,
portY,
portRoutingCostX,
portRoutingCostY,
portZ,
portMetadata,
}
Expand Down
33 changes: 29 additions & 4 deletions lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ export interface TinyHyperGraphTopology {
portAngleForRegion1: Int32Array
/** portAngleForRegion2[portId] = CCW angle of the port on incidentPortRegion[portId][1] */
portAngleForRegion2?: Int32Array
/**
* Optional virtual coordinates used only by routing distance costs.
*
* Physical port coordinates still define boundary order and serialized
* output. These arrays let topology repair move a port to a legal physical
* lane without also changing the solver's existing distance preference.
*/
portRoutingCostX?: Float64Array
portRoutingCostY?: Float64Array
portX: Float64Array
portY: Float64Array
portZ: Int32Array
Expand Down Expand Up @@ -452,8 +461,10 @@ export class TinyHyperGraphSolver extends BaseSolver {
const portHCostToEndOfRoute = this.USE_LAZY_ROUTE_HEURISTIC
? undefined
: new Float64Array(topology.portCount * problem.routeCount)
const portX = topology.portX as unknown as ArrayLike<number>
const portY = topology.portY as unknown as ArrayLike<number>
const portX = (topology.portRoutingCostX ??
topology.portX) as unknown as ArrayLike<number>
const portY = (topology.portRoutingCostY ??
topology.portY) as unknown as ArrayLike<number>
const portEndpointNetIds = Array.from(
{ length: topology.portCount },
() => new Set<NetId>(),
Expand Down Expand Up @@ -1518,12 +1529,26 @@ export class TinyHyperGraphSolver extends BaseSolver {

const endPortId = this.problem.routeEndPort[this.state.currentRouteId!]
const dx =
this.topology.portX[neighborPortId] - this.topology.portX[endPortId]
this.getPortRoutingCostX(neighborPortId) -
this.getPortRoutingCostX(endPortId)
const dy =
this.topology.portY[neighborPortId] - this.topology.portY[endPortId]
this.getPortRoutingCostY(neighborPortId) -
this.getPortRoutingCostY(endPortId)
return Math.hypot(dx, dy) * this.DISTANCE_TO_COST
}

protected getPortRoutingCostX(portId: PortId): number {
return (
this.topology.portRoutingCostX?.[portId] ?? this.topology.portX[portId]
)
}

protected getPortRoutingCostY(portId: PortId): number {
return (
this.topology.portRoutingCostY?.[portId] ?? this.topology.portY[portId]
)
}

override visualize(): GraphicsObject {
return visualizeTinyGraph(this)
}
Expand Down
8 changes: 4 additions & 4 deletions lib/distance-aware-tiny-hypergraph-solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ export class DistanceAwareTinyHyperGraphSolver extends TinyHyperGraphSolver {
if (!Number.isFinite(baseCost)) return baseCost

const dx =
this.topology.portX[currentCandidate.portId]! -
this.topology.portX[neighborPortId]!
this.getPortRoutingCostX(currentCandidate.portId) -
this.getPortRoutingCostX(neighborPortId)
const dy =
this.topology.portY[currentCandidate.portId]! -
this.topology.portY[neighborPortId]!
this.getPortRoutingCostY(currentCandidate.portId) -
this.getPortRoutingCostY(neighborPortId)
return baseCost + Math.hypot(dx, dy) * this.DISTANCE_TO_COST
}

Expand Down
8 changes: 4 additions & 4 deletions lib/selective-rerip-tiny-hyper-graph-solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,10 +429,10 @@ export class SelectiveReripTinyHyperGraphSolver extends DistanceAwareTinyHyperGr
hops.push({
state: { portId: neighborPortId, nextRegionId },
distance: Math.hypot(
this.topology.portX[state.portId]! -
this.topology.portX[neighborPortId]!,
this.topology.portY[state.portId]! -
this.topology.portY[neighborPortId]!,
this.getPortRoutingCostX(state.portId) -
this.getPortRoutingCostX(neighborPortId),
this.getPortRoutingCostY(state.portId) -
this.getPortRoutingCostY(neighborPortId),
),
owners,
data: { resources },
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 30 additions & 7 deletions tests/solver/duplicate-congested-port-sufficient-capacity.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { expect, test } from "bun:test"
import type { SerializedHyperGraph } from "@tscircuit/hypergraph"
import { DuplicateCongestedPortSolver } from "lib/index"
import {
DuplicateCongestedPortSolver,
loadSerializedHyperGraph,
} from "lib/index"

const createRegion = (
regionId: string,
Expand Down Expand Up @@ -70,15 +73,35 @@ const createFixture = (): SerializedHyperGraph => ({
],
})

test("preserves legacy duplicate placement when the boundary has enough capacity", () => {
const legacySolver = new DuplicateCongestedPortSolver(createFixture())
const capacityAwareSolver = new DuplicateCongestedPortSolver(createFixture(), {
test("uses physical duplicate placement when the boundary has enough capacity", () => {
const solver = new DuplicateCongestedPortSolver(createFixture(), {
minimumDuplicatePortSpacing: 0.2,
duplicatePortWidth: 0.1,
})

legacySolver.solve()
capacityAwareSolver.solve()
solver.solve()

expect(capacityAwareSolver.getOutput()).toEqual(legacySolver.getOutput())
const lanePorts = solver
.getOutput()
.ports.filter(
(port) =>
port.portId === "shared" || port.d?.duplicatedFromPortId === "shared",
)
expect(lanePorts).toHaveLength(2)
expect(
Math.abs(Number(lanePorts[0]!.d?.y) - Number(lanePorts[1]!.d?.y)),
).toBeCloseTo(0.2)

const { topology } = loadSerializedHyperGraph(solver.getOutput())
const lanePortIndexes = lanePorts.map((lanePort) =>
topology.portMetadata?.findIndex(
(metadata) => metadata.serializedPortId === lanePort.portId,
),
)
expect(
Math.abs(
topology.portRoutingCostY![lanePortIndexes[0]!]! -
topology.portRoutingCostY![lanePortIndexes[1]!]!,
),
).toBeCloseTo(0.025)
})
Loading