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
@@ -0,0 +1,83 @@
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots

exports[`LayoutPipelineSolver06 - decoupling caps visual snapshot 1`] = `
{
"capPlacements": [
{
"id": "C10",
"rot": 0,
"x": -2.025,
"y": 1.45,
},
{
"id": "C11",
"rot": 0,
"x": -2.025,
"y": -2.15,
},
{
"id": "C12",
"rot": 0,
"x": -1.568,
"y": 5.955,
},
{
"id": "C13",
"rot": 0,
"x": 0.622,
"y": 5.955,
},
{
"id": "C14",
"rot": 0,
"x": -0.838,
"y": 5.955,
},
{
"id": "C15",
"rot": 0,
"x": -2.298,
"y": 5.955,
},
{
"id": "C18",
"rot": 0,
"x": -4.485,
"y": 3.218,
},
{
"id": "C19",
"rot": 0,
"x": 1.353,
"y": 5.955,
},
{
"id": "C7",
"rot": 0,
"x": -3.755,
"y": 3.218,
},
{
"id": "C8",
"rot": 0,
"x": -0.108,
"y": 5.955,
},
{
"id": "C9",
"rot": 0,
"x": -6.215,
"y": 3.218,
},
],
"circleCount": 0,
"lineCount": 133,
"pointCount": 79,
"rectCount": 12,
"u3": {
"rot": 0,
"x": 0.79,
"y": 0,
},
}
`;
129 changes: 129 additions & 0 deletions tests/LayoutPipelineSolver/layout-pipeline-solver06.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { expect, test } from "bun:test"
import { LayoutPipelineSolver } from "lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver"
import { problem } from "../../pages/LayoutPipelineSolver/LayoutPipelineSolver06.page"

test("LayoutPipelineSolver06 - decoupling caps arranged in linear rows with no overlaps", () => {
const solver = new LayoutPipelineSolver(problem)
solver.solve()

// Verify the pipeline completed successfully
expect(solver.solved).toBe(true)
expect(solver.error).toBeNull()

// Verify all phases ran
const phases = Object.keys(solver.timeSpentOnPhase)
expect(phases).toContain("identifyDecouplingCapsSolver")
expect(phases).toContain("chipPartitionsSolver")
expect(phases).toContain("packInnerPartitionsSolver")
expect(phases).toContain("partitionPackingSolver")

const layout = solver.getOutputLayout()
expect(layout).toBeDefined()

// All chips should have valid numeric placements
const chipIds = Object.keys(layout.chipPlacements)
expect(chipIds.length).toBeGreaterThan(0)

for (const chipId of chipIds) {
const placement = layout.chipPlacements[chipId]
expect(typeof placement.x).toBe("number")
expect(typeof placement.y).toBe("number")
expect(typeof placement.ccwRotationDegrees).toBe("number")
expect(Number.isFinite(placement.x)).toBe(true)
expect(Number.isFinite(placement.y)).toBe(true)
}

// Verify no overlaps in final layout
const overlaps = solver.checkForOverlaps(layout)
expect(overlaps).toEqual([])

// Visualization should be valid
const viz = solver.visualize()
expect(viz).toBeDefined()
expect(viz.rects?.length).toBeGreaterThan(0)
})

test("LayoutPipelineSolver06 - decoupling caps form sorted linear rows per net group", () => {
const solver = new LayoutPipelineSolver(problem)
solver.solve()

const layout = solver.getOutputLayout()

// Identify cap chips
const capIds = Object.keys(problem.chipMap).filter((id) => id.startsWith("C"))
expect(capIds.length).toBeGreaterThan(0)

// All caps should be placed
for (const capId of capIds) {
expect(layout.chipPlacements[capId]).toBeDefined()
}

// Group caps by y-coordinate (same y = same row, rounded to 1 decimal)
const capPositions = capIds.map((id) => ({
id,
x: layout.chipPlacements[id].x,
y: layout.chipPlacements[id].y,
}))

const yGroups = new Map<string, typeof capPositions>()
for (const cap of capPositions) {
const yKey = cap.y.toFixed(1)
const group = yGroups.get(yKey) || []
group.push(cap)
yGroups.set(yKey, group)
}

// Each multi-cap row should be sorted left-to-right by x
for (const [_yKey, group] of yGroups) {
if (group.length > 1) {
const sorted = [...group].sort((a, b) => a.x - b.x)
for (let i = 1; i < sorted.length; i++) {
expect(sorted[i]!.x).toBeGreaterThan(sorted[i - 1]!.x)
}
// Verify minimum gap between adjacent caps (decouplingCapsGap = 0.2)
for (let i = 1; i < sorted.length; i++) {
const gap = sorted[i]!.x - sorted[i - 1]!.x
// Caps should have at least a small gap (not overlapping)
expect(gap).toBeGreaterThan(0.1)
}
}
}

// Verify decoupling caps are grouped into expected number of rows
// (should have at least 2 rows for the 2 voltage groups in LayoutPipelineSolver06)
expect(yGroups.size).toBeGreaterThanOrEqual(2)
})

test("LayoutPipelineSolver06 - decoupling caps visual snapshot", () => {
const solver = new LayoutPipelineSolver(problem)
solver.solve()

const layout = solver.getOutputLayout()
const viz = solver.visualize()
expect(viz).toBeDefined()

// Extract cap placement positions for visual regression
const capIds = Object.keys(problem.chipMap)
.filter((id) => id.startsWith("C"))
.sort()
const capPlacements = capIds.map((id) => ({
id,
x: Number(layout.chipPlacements[id].x.toFixed(3)),
y: Number(layout.chipPlacements[id].y.toFixed(3)),
rot: layout.chipPlacements[id].ccwRotationDegrees,
}))

// Snapshot includes element counts AND actual cap positions
expect({
rectCount: viz.rects?.length ?? 0,
lineCount: viz.lines?.length ?? 0,
circleCount: viz.circles?.length ?? 0,
pointCount: viz.points?.length ?? 0,
capPlacements,
u3: {
x: Number(layout.chipPlacements["U3"].x.toFixed(3)),
y: Number(layout.chipPlacements["U3"].y.toFixed(3)),
rot: layout.chipPlacements["U3"].ccwRotationDegrees,
},
}).toMatchSnapshot()
})