Skip to content
Open
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
277 changes: 9 additions & 268 deletions lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts
Original file line number Diff line number Diff line change
@@ -1,277 +1,18 @@
/**
* Creates partitions (small subset groups) surrounding complex chips.
* Divides the layout problem into manageable sections for more efficient processing.
*/

import { BaseSolver } from "../BaseSolver"
import type {
InputProblem,
ChipId,
PinId,
NetId,
PartitionInputProblem,
} from "lib/types/InputProblem"
import type { GraphicsObject } from "graphics-debug"
import { stackGraphicsHorizontally } from "graphics-debug"
import { visualizeInputProblem } from "lib/solvers/LayoutPipelineSolver/visualizeInputProblem"
import { doBasicInputProblemLayout } from "lib/solvers/LayoutPipelineSolver/doBasicInputProblemLayout"
import type { DecouplingCapGroup } from "../IdentifyDecouplingCapsSolver/IdentifyDecouplingCapsSolver"

export class ChipPartitionsSolver extends BaseSolver {
inputProblem: InputProblem
partitions: PartitionInputProblem[] = []
decouplingCapGroups?: DecouplingCapGroup[]

constructor({
inputProblem,
decouplingCapGroups,
}: {
inputProblem: InputProblem
decouplingCapGroups?: DecouplingCapGroup[]
}) {
super()
this.inputProblem = inputProblem
this.decouplingCapGroups = decouplingCapGroups
}

override _step() {
this.partitions = this.createPartitions(this.inputProblem)
this.solved = true
}

/**
* Creates partitions by:
* - Separating each decoupling capacitor group into its own partition (caps only, excluding the main chip)
* - Partitioning remaining chips by connected components through strong pin connections
*/
private createPartitions(inputProblem: InputProblem): InputProblem[] {
const chipIds = Object.keys(inputProblem.chipMap)

// 1) Build decoupling-cap-only partitions (exclude the main chip for each group)
const decapChipIdSet = new Set<ChipId>()
const decapGroupPartitions: ChipId[][] = []

if (this.decouplingCapGroups && this.decouplingCapGroups.length > 0) {
for (const group of this.decouplingCapGroups) {
const capsOnly: ChipId[] = []
for (const capId of group.decouplingCapChipIds) {
if (inputProblem.chipMap[capId]) {
capsOnly.push(capId)
}
}
// Only add a partition if there are at least two caps present in the inputProblem
if (capsOnly.length >= 2) {
decapGroupPartitions.push(capsOnly)
// Mark these caps as handled by decoupling-cap partitions
for (const capId of capsOnly) {
decapChipIdSet.add(capId)
}
}
}
}

// 2) Build adjacency graph for NON-decap chips based on strong pin connections
const nonDecapChipIds = chipIds.filter((id) => !decapChipIdSet.has(id))
const adjacencyMap = new Map<ChipId, Set<ChipId>>()

// Initialize adjacency map for non-decap chips
for (const chipId of nonDecapChipIds) {
adjacencyMap.set(chipId, new Set())
}

// Add edges based on strong pin connections, but exclude any edges touching decap chips
for (const [connKey, isConnected] of Object.entries(
inputProblem.pinStrongConnMap,
)) {
if (!isConnected) continue

const [pin1Id, pin2Id] = connKey.split("-")

// Find which chips own these pins
const owner1 = this.findPinOwner(pin1Id!, inputProblem)
const owner2 = this.findPinOwner(pin2Id!, inputProblem)

// Only connect non-decap chips
if (
owner1 &&
owner2 &&
owner1 !== owner2 &&
!decapChipIdSet.has(owner1) &&
!decapChipIdSet.has(owner2)
) {
adjacencyMap.get(owner1)!.add(owner2)
adjacencyMap.get(owner2)!.add(owner1)
}
}

// 3) Find connected components among non-decap chips using DFS
const visited = new Set<ChipId>()
const nonDecapPartitions: ChipId[][] = []

for (const componentId of nonDecapChipIds) {
if (!visited.has(componentId)) {
const partition = this.dfs(componentId, adjacencyMap, visited)
if (partition.length > 0) {
nonDecapPartitions.push(partition)
}
}
}

return [
...decapGroupPartitions.map((partition) =>
this.createInputProblemFromPartition(partition, inputProblem, {
partitionType: "decoupling_caps",
}),
),
...nonDecapPartitions.map((partition) =>
this.createInputProblemFromPartition(partition, inputProblem),
),
]
}

/**
* Finds the owner chip of a given pin
*/
private findPinOwner(
pinId: PinId,
inputProblem: InputProblem,
): ChipId | null {
// Check if it's a chip pin
const chipPin = inputProblem.chipPinMap[pinId]
if (chipPin) {
// Find the chip that owns this pin
for (const [chipId, chip] of Object.entries(inputProblem.chipMap)) {
if (chip.pins.includes(pinId)) {
return chipId
}
}
}

return null
}

/**
* Depth-first search to find connected components
*/
private dfs(
startId: ChipId,
adjacencyMap: Map<ChipId, Set<ChipId>>,
visited: Set<ChipId>,
): ChipId[] {
const partition: ChipId[] = []
const stack = [startId]
async solve() {
const { inputProblem } = this
const decapGroupPartitions: string[][] = []

while (stack.length > 0) {
const currentId = stack.pop()!

if (visited.has(currentId)) continue

visited.add(currentId)
partition.push(currentId)

const neighbors = adjacencyMap.get(currentId) || new Set()
for (const neighborId of neighbors) {
if (!visited.has(neighborId)) {
stack.push(neighborId)
}
}
}

return partition
}

/**
* Creates a new InputProblem containing only the components in the given partition
*/
private createInputProblemFromPartition(
partition: ChipId[],
originalProblem: InputProblem,
opts?: {
partitionType?: "default" | "decoupling_caps"
},
): PartitionInputProblem {
const chipIds = partition

// Extract relevant pins
const relevantPinIds = new Set<PinId>()
for (const chipId of chipIds) {
const chip = originalProblem.chipMap[chipId]
for (const pinId of chip!.pins) {
relevantPinIds.add(pinId)
}
}

// Build filtered maps
const chipMap: Record<ChipId, any> = {}
const chipPinMap: Record<PinId, any> = {}
const netMap: Record<NetId, any> = {}
const pinStrongConnMap: Record<string, boolean> = {}
const netConnMap: Record<string, boolean> = {}

// Copy chips and chip pins
for (const chipId of chipIds) {
chipMap[chipId] = originalProblem.chipMap[chipId]
}
for (const pinId of relevantPinIds) {
if (originalProblem.chipPinMap[pinId]) {
chipPinMap[pinId] = originalProblem.chipPinMap[pinId]
for (const group of inputProblem.decouplingCapGroups || []) {

Check failure on line 8 in lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts

View workflow job for this annotation

GitHub Actions / test

TypeError: undefined is not an object (evaluating 'inputProblem.decouplingCapGroups')

at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:8:25) at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:4:17) at <anonymous> (/home/runner/work/matchpack/matchpack/tests/ChipPartitionsSolver.test.ts:183:10)

Check failure on line 8 in lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts

View workflow job for this annotation

GitHub Actions / test

TypeError: undefined is not an object (evaluating 'inputProblem.decouplingCapGroups')

at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:8:25) at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:4:17) at <anonymous> (/home/runner/work/matchpack/matchpack/tests/ChipPartitionsSolver.test.ts:157:10)

Check failure on line 8 in lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts

View workflow job for this annotation

GitHub Actions / test

TypeError: undefined is not an object (evaluating 'inputProblem.decouplingCapGroups')

at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:8:25) at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:4:17) at <anonymous> (/home/runner/work/matchpack/matchpack/tests/ChipPartitionsSolver.test.ts:85:10)

Check failure on line 8 in lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts

View workflow job for this annotation

GitHub Actions / test

TypeError: undefined is not an object (evaluating 'inputProblem.decouplingCapGroups')

at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:8:25) at solve (/home/runner/work/matchpack/matchpack/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts:4:17) at <anonymous> (/home/runner/work/matchpack/matchpack/tests/ChipPartitionsSolver.test.ts:43:10)
const capsOnly = group.decouplingCapChipIds.filter(id => inputProblem.chipMap[id])

if (capsOnly.length >= 2) {
decapGroupPartitions.push(capsOnly)
}
}

// Copy relevant strong connections
for (const [connKey, isConnected] of Object.entries(
originalProblem.pinStrongConnMap,
)) {
const [pin1Id, pin2Id] = connKey.split("-")
if (relevantPinIds.has(pin1Id!) && relevantPinIds.has(pin2Id!)) {
pinStrongConnMap[connKey] = isConnected
}
}

// Copy relevant nets and net connections
const relevantNetIds = new Set<NetId>()
for (const [connKey, isConnected] of Object.entries(
originalProblem.netConnMap,
)) {
if (!isConnected) continue

const [pinId, netId] = connKey.split("-")
if (relevantPinIds.has(pinId!)) {
relevantNetIds.add(netId!)
netConnMap[connKey] = isConnected
}
}

for (const netId of relevantNetIds) {
if (originalProblem.netMap[netId]) {
netMap[netId] = originalProblem.netMap[netId]
}
}

return {
...originalProblem,
chipMap,
chipPinMap,
netMap,
pinStrongConnMap,
netConnMap,
isPartition: true,
partitionType: opts?.partitionType,
}
}

override visualize(): GraphicsObject {
if (this.partitions.length === 0) {
return super.visualize()
}

const partitionVisualizations = this.partitions.map((partition) => {
const layout = doBasicInputProblemLayout(partition)
return visualizeInputProblem(partition, layout)
})

const titles = this.partitions.map((_, index) => `partition${index}`)

return stackGraphicsHorizontally(partitionVisualizations, { titles })
this.outputLayout.chipPartitions = decapGroupPartitions
}
}
Loading