diff --git a/lib/DuplicateCongestedPortSolver.ts b/lib/DuplicateCongestedPortSolver.ts index 2e47e77..9e9aa50 100644 --- a/lib/DuplicateCongestedPortSolver.ts +++ b/lib/DuplicateCongestedPortSolver.ts @@ -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 @@ -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[] = [] @@ -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 diff --git a/lib/compat/loadSerializedHyperGraph.ts b/lib/compat/loadSerializedHyperGraph.ts index 9d55265..347d081 100644 --- a/lib/compat/loadSerializedHyperGraph.ts +++ b/lib/compat/loadSerializedHyperGraph.ts @@ -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, @@ -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) => { @@ -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, @@ -560,6 +572,8 @@ export const loadSerializedHyperGraph = ( portAngleForRegion2, portX, portY, + portRoutingCostX, + portRoutingCostY, portZ, portMetadata, } diff --git a/lib/core.ts b/lib/core.ts index c224a31..4d9f13e 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -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 @@ -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 - const portY = topology.portY as unknown as ArrayLike + const portX = (topology.portRoutingCostX ?? + topology.portX) as unknown as ArrayLike + const portY = (topology.portRoutingCostY ?? + topology.portY) as unknown as ArrayLike const portEndpointNetIds = Array.from( { length: topology.portCount }, () => new Set(), @@ -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) } diff --git a/lib/distance-aware-tiny-hypergraph-solver.ts b/lib/distance-aware-tiny-hypergraph-solver.ts index 376c4a5..3064fda 100644 --- a/lib/distance-aware-tiny-hypergraph-solver.ts +++ b/lib/distance-aware-tiny-hypergraph-solver.ts @@ -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 } diff --git a/lib/selective-rerip-tiny-hyper-graph-solver.ts b/lib/selective-rerip-tiny-hyper-graph-solver.ts index 64a7f2c..f054e1c 100644 --- a/lib/selective-rerip-tiny-hyper-graph-solver.ts +++ b/lib/selective-rerip-tiny-hyper-graph-solver.ts @@ -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 }, diff --git a/tests/solver/__snapshots__/duplicate-congested-port-physical-placement-repro.snap.svg b/tests/solver/__snapshots__/duplicate-congested-port-physical-placement-repro.snap.svg index abe98e5..24cf8e4 100644 --- a/tests/solver/__snapshots__/duplicate-congested-port-physical-placement-repro.snap.svg +++ b/tests/solver/__snapshots__/duplicate-congested-port-physical-placement-repro.snap.svg @@ -1,4 +1,4 @@ -Actual lane spacing: 0.025 mm / 0.200 mm requiredRed = 0.1 mm trace Orange = trace + clearance Teal = shared boundary