Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,43 @@ import { doBasicInputProblemLayout } from "../LayoutPipelineSolver/doBasicInputP

const PIN_SIZE = 0.1

const compareNaturalChipIds = (a: ChipId, b: ChipId) => {
const aParts = a.match(/\d+|\D+/g) ?? [a]
const bParts = b.match(/\d+|\D+/g) ?? [b]
const partCount = Math.min(aParts.length, bParts.length)

for (let i = 0; i < partCount; i++) {
const aPart = aParts[i]!
const bPart = bParts[i]!
const aIsNumber = /^\d+$/.test(aPart)
const bIsNumber = /^\d+$/.test(bPart)

if (aIsNumber && bIsNumber) {
const diff = Number(aPart) - Number(bPart)
if (diff !== 0) return diff
continue
}

if (aPart !== bPart) {
return aPart < bPart ? -1 : 1
}
}

return aParts.length - bParts.length
}

const getDecouplingCapRotation = (
availableRotations?: Array<0 | 90 | 180 | 270>,
) => {
if (!availableRotations?.length) return 0
return availableRotations.includes(0) ? 0 : availableRotations[0]!
}

const getRowWidthForRotation = (
chipSize: { x: number; y: number },
rotation: number,
) => (rotation === 90 || rotation === 270 ? chipSize.y : chipSize.x)

export class SingleInnerPartitionPackingSolver extends BaseSolver {
partitionInputProblem: PartitionInputProblem
layout: OutputLayout | null = null
Expand All @@ -38,6 +75,13 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
}

override _step() {
if (this.partitionInputProblem.partitionType === "decoupling_caps") {
this.layout = this.createDecouplingCapsRowLayout()
this.solved = true
this.activeSubSolver = null
return
}

// Initialize PackSolver2 if not already created
if (!this.activeSubSolver) {
const packInput = this.createPackInput()
Expand All @@ -64,6 +108,41 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
}
}

private createDecouplingCapsRowLayout(): OutputLayout {
const chipEntries = Object.entries(this.partitionInputProblem.chipMap)
.sort(([aChipId], [bChipId]) => compareNaturalChipIds(aChipId, bChipId))
.map(([chipId, chip]) => {
const rotation = getDecouplingCapRotation(chip.availableRotations)
return {
chipId,
rowWidth: getRowWidthForRotation(chip.size, rotation),
rotation,
}
})
const gap =
this.partitionInputProblem.decouplingCapsGap ??
this.partitionInputProblem.chipGap
const totalWidth =
chipEntries.reduce((sum, chipEntry) => sum + chipEntry.rowWidth, 0) +
Math.max(0, chipEntries.length - 1) * gap
const chipPlacements: Record<string, Placement> = {}
let cursorX = -totalWidth / 2

for (const { chipId, rowWidth, rotation } of chipEntries) {
chipPlacements[chipId] = {
x: cursorX + rowWidth / 2,
y: 0,
ccwRotationDegrees: rotation,
}
cursorX += rowWidth + gap
}

return {
chipPlacements,
groupPlacements: {},
}
}

private createPackInput(): PackInput {
// Fall back to filtered mapping (weak + strong)
const pinToNetworkMap = createFilteredNetworkMapping({
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"bpc-graph": "^0.0.66",
"calculate-packing": "^0.0.31",
"circuit-json": "^0.0.226",
"circuit-to-svg": "^0.0.345",
"graphics-debug": "^0.0.64",
"react-cosmos": "^7.0.0",
"react-cosmos-plugin-vite": "^7.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import { expect, test } from "bun:test"
import { SingleInnerPartitionPackingSolver } from "../../lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver"
import type { PartitionInputProblem } from "../../lib/types/InputProblem"

test("SingleInnerPartitionPackingSolver lays out decoupling caps in a centered natural-order row", () => {
const partitionInputProblem: PartitionInputProblem = {
isPartition: true,
partitionType: "decoupling_caps",
chipMap: {
C10: {
chipId: "C10",
pins: [],
size: { x: 1.5, y: 0.5 },
},
C2: {
chipId: "C2",
pins: [],
size: { x: 0.5, y: 0.5 },
},
C1: {
chipId: "C1",
pins: [],
size: { x: 1, y: 0.5 },
},
},
chipPinMap: {},
netMap: {},
pinStrongConnMap: {},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
decouplingCapsGap: 0.3,
}

const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem,
pinIdToStronglyConnectedPins: {},
})
solver.solve()

expect(solver.solved).toBe(true)
expect(solver.layout?.chipPlacements.C1).toEqual({
x: -1.3,
y: 0,
ccwRotationDegrees: 0,
})
expect(solver.layout?.chipPlacements.C2).toEqual({
x: -0.25,
y: 0,
ccwRotationDegrees: 0,
})
expect(solver.layout?.chipPlacements.C10).toEqual({
x: 1.05,
y: 0,
ccwRotationDegrees: 0,
})

const c1Right =
solver.layout!.chipPlacements.C1!.x +
partitionInputProblem.chipMap.C1!.size.x / 2
const c2Left =
solver.layout!.chipPlacements.C2!.x -
partitionInputProblem.chipMap.C2!.size.x / 2
const c2Right =
solver.layout!.chipPlacements.C2!.x +
partitionInputProblem.chipMap.C2!.size.x / 2
const c10Left =
solver.layout!.chipPlacements.C10!.x -
partitionInputProblem.chipMap.C10!.size.x / 2

expect(c2Left - c1Right).toBeCloseTo(0.3)
expect(c10Left - c2Right).toBeCloseTo(0.3)
})

test("SingleInnerPartitionPackingSolver falls back to chipGap for decoupling cap spacing", () => {
const partitionInputProblem: PartitionInputProblem = {
isPartition: true,
partitionType: "decoupling_caps",
chipMap: {
C1: {
chipId: "C1",
pins: [],
size: { x: 1, y: 0.5 },
},
C2: {
chipId: "C2",
pins: [],
size: { x: 1, y: 0.5 },
},
},
chipPinMap: {},
netMap: {},
pinStrongConnMap: {},
netConnMap: {},
chipGap: 0.4,
partitionGap: 2,
}

const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem,
pinIdToStronglyConnectedPins: {},
})
solver.solve()

expect(solver.solved).toBe(true)
expect(solver.layout?.chipPlacements.C1?.x).toBeCloseTo(-0.7)
expect(solver.layout?.chipPlacements.C2?.x).toBeCloseTo(0.7)
})

test("SingleInnerPartitionPackingSolver respects fixed decoupling cap rotations", () => {
const partitionInputProblem: PartitionInputProblem = {
isPartition: true,
partitionType: "decoupling_caps",
chipMap: {
C1: {
chipId: "C1",
pins: [],
size: { x: 1, y: 2 },
availableRotations: [90],
},
C2: {
chipId: "C2",
pins: [],
size: { x: 1, y: 0.5 },
},
},
chipPinMap: {},
netMap: {},
pinStrongConnMap: {},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
}

const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem,
pinIdToStronglyConnectedPins: {},
})
solver.solve()

expect(solver.solved).toBe(true)
expect(solver.layout?.chipPlacements.C1?.x).toBeCloseTo(-0.6)
expect(solver.layout?.chipPlacements.C1?.y).toBe(0)
expect(solver.layout?.chipPlacements.C1?.ccwRotationDegrees).toBe(90)
expect(solver.layout?.chipPlacements.C2?.x).toBeCloseTo(1.1)
expect(solver.layout?.chipPlacements.C2?.y).toBe(0)
expect(solver.layout?.chipPlacements.C2?.ccwRotationDegrees).toBe(0)
})

test("SingleInnerPartitionPackingSolver places a single decoupling cap at the origin", () => {
const partitionInputProblem: PartitionInputProblem = {
isPartition: true,
partitionType: "decoupling_caps",
chipMap: {
C1: {
chipId: "C1",
pins: [],
size: { x: 1, y: 0.5 },
},
},
chipPinMap: {},
netMap: {},
pinStrongConnMap: {},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
}

const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem,
pinIdToStronglyConnectedPins: {},
})
solver.solve()

expect(solver.solved).toBe(true)
expect(solver.layout?.chipPlacements.C1).toEqual({
x: 0,
y: 0,
ccwRotationDegrees: 0,
})
})
Loading