From f322f8a8c2f904b9180441cd19f63bf5ff343afa Mon Sep 17 00:00:00 2001 From: Sangit Manandhar Date: Wed, 3 Jun 2026 19:50:26 -0700 Subject: [PATCH 1/2] code cleanup --- .../SingleInnerPartitionPackingSolver.ts | 35 +-- .../PartitionPackingSolver.ts | 198 +++++++---------- ...getInputProblemFromCircuitJsonSchematic.ts | 2 +- package.json | 26 +-- pages/examples/example01/example01.page.tsx | 8 + pages/examples/example01/inputProblem.json | 206 +++++++++++++++++ pages/examples/example02/example02.page.tsx | 8 + pages/examples/example02/inputProblem.json | 210 ++++++++++++++++++ .../LayoutPipelineSolverFixed.test.ts | 187 ++++++++++++++++ ...ProblemFromCircuitJsonSchematic01.test.tsx | 140 ++++++------ vite.config.js | 1 + 11 files changed, 801 insertions(+), 220 deletions(-) create mode 100644 pages/examples/example01/example01.page.tsx create mode 100644 pages/examples/example01/inputProblem.json create mode 100644 pages/examples/example02/example02.page.tsx create mode 100644 pages/examples/example02/inputProblem.json create mode 100644 tests/LayoutPipelineSolver/LayoutPipelineSolverFixed.test.ts diff --git a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts index 88db103..d556086 100644 --- a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts +++ b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts @@ -10,8 +10,6 @@ import type { OutputLayout, Placement } from "../../types/OutputLayout" import type { InputProblem, PinId, - ChipId, - NetId, ChipPin, PartitionInputProblem, } from "../../types/InputProblem" @@ -40,9 +38,13 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { override _step() { // Initialize PackSolver2 if not already created if (!this.activeSubSolver) { - const packInput = this.createPackInput() + const pinToNetworkMap = createFilteredNetworkMapping({ + inputProblem: this.partitionInputProblem, + pinIdToStronglyConnectedPins: this.pinIdToStronglyConnectedPins, + }).pinToNetworkMap + + const packInput = this.createPackInput(pinToNetworkMap) this.activeSubSolver = new PackSolver2(packInput) - this.activeSubSolver = this.activeSubSolver } // Run one step of the PackSolver2 @@ -64,14 +66,7 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } } - private createPackInput(): PackInput { - // Fall back to filtered mapping (weak + strong) - const pinToNetworkMap = createFilteredNetworkMapping({ - inputProblem: this.partitionInputProblem, - pinIdToStronglyConnectedPins: this.pinIdToStronglyConnectedPins, - }).pinToNetworkMap - - // Create pack components for each chip + private createPackInput(pinToNetworkMap: Map): PackInput { const packComponents = Object.entries( this.partitionInputProblem.chipMap, ).map(([chipId, chip]) => { @@ -94,10 +89,10 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { pads.push({ padId: pinId, - networkId: networkId, + networkId, type: "rect" as const, offset: { x: pin.offset.x, y: pin.offset.y }, - size: { x: PIN_SIZE, y: PIN_SIZE }, // Small size for pins + size: { x: PIN_SIZE, y: PIN_SIZE }, }) } @@ -121,10 +116,16 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { }, }) + const fixedRotation = chip.availableRotations?.[0] ?? 0 return { componentId: chipId, pads, - availableRotationDegrees: chip.availableRotations || [0, 90, 180, 270], + availableRotationDegrees: chip.availableRotations ?? [0, 90, 180, 270], + ...(chip.fixedPosition && { + isStatic: true as const, + center: chip.fixedPosition, + ccwRotationOffset: fixedRotation, + }), } }) @@ -153,8 +154,8 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { x: packedComponent.center.x, y: packedComponent.center.y, ccwRotationDegrees: - packedComponent.ccwRotationOffset || - packedComponent.ccwRotationDegrees || + packedComponent.ccwRotationDegrees ?? + packedComponent.ccwRotationOffset ?? 0, } } diff --git a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts index 149d617..6e938ec 100644 --- a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts +++ b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts @@ -16,6 +16,17 @@ export interface PartitionPackingSolverInput { inputProblem: InputProblem } +type PartitionGroup = { + partitionIndex: number + chipIds: string[] + bounds: { + minX: number + maxX: number + minY: number + maxY: number + } +} + export class PartitionPackingSolver extends BaseSolver { packedPartitions: PackedPartition[] inputProblem: InputProblem @@ -28,14 +39,18 @@ export class PartitionPackingSolver extends BaseSolver { this.inputProblem = input.inputProblem } + private partitionHasFixedChip(partitionIndex: number): boolean { + const packedPartition = this.packedPartitions[partitionIndex] + if (!packedPartition) return false + return Object.values(packedPartition.inputProblem.chipMap).some( + (chip) => chip.fixedPosition !== undefined, + ) + } + override _step() { try { if (this.packedPartitions.length === 0) { - // No partitions to pack, create empty layout - this.finalLayout = { - chipPlacements: {}, - groupPlacements: {}, - } + this.finalLayout = { chipPlacements: {}, groupPlacements: {} } this.solved = true return } @@ -82,27 +97,39 @@ export class PartitionPackingSolver extends BaseSolver { } } - private organizePackedPartitions(): Array<{ - partitionIndex: number - chipIds: string[] - bounds: { - minX: number - maxX: number - minY: number - maxY: number - } - }> { - // Group chips by partition based on packed partitions - const partitionGroups: Array<{ - partitionIndex: number - chipIds: string[] - bounds: { - minX: number - maxX: number - minY: number - maxY: number + private buildConnectivityMap(): Map { + const pinToNetworkMap = new Map() + for (const packedPartition of this.packedPartitions) { + for (const [connKey, connected] of Object.entries( + packedPartition.inputProblem.netConnMap, + )) { + if (!connected) continue + const [pinId, netId] = connKey.split("-") + if (pinId && netId) pinToNetworkMap.set(pinId, netId) + } + for (const [connKey, connected] of Object.entries( + packedPartition.inputProblem.pinStrongConnMap, + )) { + if (!connected) continue + const pins = connKey.split("-") + if (pins.length === 2 && pins[0] && pins[1]) { + const existingNet = + pinToNetworkMap.get(pins[0]) || pinToNetworkMap.get(pins[1]) + if (existingNet) { + pinToNetworkMap.set(pins[0], existingNet) + pinToNetworkMap.set(pins[1], existingNet) + } else { + pinToNetworkMap.set(pins[0], connKey) + pinToNetworkMap.set(pins[1], connKey) + } + } } - }> = [] + } + return pinToNetworkMap + } + + private organizePackedPartitions(): PartitionGroup[] { + const partitionGroups: PartitionGroup[] = [] for (let i = 0; i < this.packedPartitions.length; i++) { const packedPartition = this.packedPartitions[i]! @@ -143,12 +170,10 @@ export class PartitionPackingSolver extends BaseSolver { maxY = Math.max(maxY, chipMaxY) } - const bounds = { minX, maxX, minY, maxY } - partitionGroups.push({ partitionIndex: i, chipIds: partitionChipIds, - bounds, + bounds: { minX, maxX, minY, maxY }, }) } } @@ -156,59 +181,12 @@ export class PartitionPackingSolver extends BaseSolver { return partitionGroups } - private createPackInput( - partitionGroups: Array<{ - partitionIndex: number - chipIds: string[] - bounds: { - minX: number - maxX: number - minY: number - maxY: number - } - }>, - ): PackInput { - // Build a global connectivity map to properly assign networkIds - const pinToNetworkMap = new Map() - - // First, process all partitions to build the connectivity map - for (const packedPartition of this.packedPartitions) { - // Process net connections - for (const [connKey, connected] of Object.entries( - packedPartition.inputProblem.netConnMap, - )) { - if (!connected) continue - const [pinId, netId] = connKey.split("-") - if (pinId && netId) { - pinToNetworkMap.set(pinId, netId) - } - } + private createPackInput(groups: PartitionGroup[]): PackInput { + const pinToNetworkMap = this.buildConnectivityMap() - // Process strong connections - these form their own networks - for (const [connKey, connected] of Object.entries( - packedPartition.inputProblem.pinStrongConnMap, - )) { - if (!connected) continue - const pins = connKey.split("-") - if (pins.length === 2 && pins[0] && pins[1]) { - // If either pin already has a net connection, use that network for both - const existingNet = - pinToNetworkMap.get(pins[0]) || pinToNetworkMap.get(pins[1]) - if (existingNet) { - pinToNetworkMap.set(pins[0], existingNet) - pinToNetworkMap.set(pins[1], existingNet) - } else { - // Otherwise, use the connection itself as the network - pinToNetworkMap.set(pins[0], connKey) - pinToNetworkMap.set(pins[1], connKey) - } - } - } - } - - // Create pack components for each partition group - const packComponents = partitionGroups.map((group) => { + const packComponents = groups.map((group) => { const packedPartition = this.packedPartitions[group.partitionIndex]! + const isFixed = this.partitionHasFixedChip(group.partitionIndex) // Calculate partition size from bounds const partitionWidth = group.bounds.maxX - group.bounds.minX @@ -232,7 +210,6 @@ export class PartitionPackingSolver extends BaseSolver { // Add all pins from this partition as pads const addedNetworks = new Set() - const pinPositions = new Map() // Calculate pin positions for all chips in the partition for (const chipId of group.chipIds) { @@ -243,43 +220,30 @@ export class PartitionPackingSolver extends BaseSolver { const chipPin = packedPartition.inputProblem.chipPinMap[pinId] if (!chipPin) continue - // Transform pin offset based on chip rotation - let transformedOffset = { x: chipPin.offset.x, y: chipPin.offset.y } - - const rotation = chipPlacement.ccwRotationDegrees || 0 - if (rotation === 90) { - transformedOffset = { x: -chipPin.offset.y, y: chipPin.offset.x } - } else if (rotation === 180) { - transformedOffset = { x: -chipPin.offset.x, y: -chipPin.offset.y } - } else if (rotation === 270) { - transformedOffset = { x: chipPin.offset.y, y: -chipPin.offset.x } + let rotatedPinOffset = { x: chipPin.offset.x, y: chipPin.offset.y } + const chipRotationDeg = chipPlacement.ccwRotationDegrees ?? 0 + if (chipRotationDeg === 90) { + rotatedPinOffset = { x: -chipPin.offset.y, y: chipPin.offset.x } + } else if (chipRotationDeg === 180) { + rotatedPinOffset = { x: -chipPin.offset.x, y: -chipPin.offset.y } + } else if (chipRotationDeg === 270) { + rotatedPinOffset = { x: chipPin.offset.y, y: -chipPin.offset.x } } - // Calculate absolute pin position - const absolutePinX = chipPlacement.x + transformedOffset.x - const absolutePinY = chipPlacement.y + transformedOffset.y - - // Store pin position for use in pad offset calculation - pinPositions.set(pinId, { x: absolutePinX, y: absolutePinY }) - - // Get the network ID for this pin + const absolutePinX = chipPlacement.x + rotatedPinOffset.x + const absolutePinY = chipPlacement.y + rotatedPinOffset.y const networkId = - pinToNetworkMap.get(pinId) || `${pinId}_disconnected` + pinToNetworkMap.get(pinId) ?? `${pinId}_disconnected` // Only add one pad per network to avoid overlapping if (!addedNetworks.has(networkId)) { addedNetworks.add(networkId) - - // Calculate offset relative to partition center - const padOffsetX = absolutePinX - centerX - const padOffsetY = absolutePinY - centerY - pads.push({ padId: `${group.partitionIndex}_pin_${pinId}`, - networkId: networkId, + networkId, type: "rect" as const, - offset: { x: padOffsetX, y: padOffsetY }, - size: { x: 0.01, y: 0.01 }, // Small pin pad + offset: { x: absolutePinX - centerX, y: absolutePinY - centerY }, + size: { x: 0.01, y: 0.01 }, }) } } @@ -288,13 +252,18 @@ export class PartitionPackingSolver extends BaseSolver { return { componentId: `partition_${group.partitionIndex}`, pads, - availableRotationDegrees: [0] as Array<0 | 90 | 180 | 270>, // Keep partitions unrotated + availableRotationDegrees: [0] as Array<0 | 90 | 180 | 270>, + ...(isFixed && { + isStatic: true as const, + center: { x: centerX, y: centerY }, + ccwRotationOffset: 0, + }), } }) return { components: packComponents, - minGap: this.inputProblem.partitionGap, // Use partitionGap from input problem + minGap: this.inputProblem.partitionGap, packOrderStrategy: "largest_to_smallest", packPlacementStrategy: "minimum_sum_squared_distance_to_network", } @@ -302,16 +271,7 @@ export class PartitionPackingSolver extends BaseSolver { private applyPackingResult( packedComponents: PackSolver2["packedComponents"], - partitionGroups: Array<{ - partitionIndex: number - chipIds: string[] - bounds: { - minX: number - maxX: number - minY: number - maxY: number - } - }>, + partitionGroups: PartitionGroup[], ): OutputLayout { // Apply the partition offsets to individual components const newChipPlacements: Record = {} diff --git a/lib/testing/getInputProblemFromCircuitJsonSchematic.ts b/lib/testing/getInputProblemFromCircuitJsonSchematic.ts index 587cc74..26b7504 100644 --- a/lib/testing/getInputProblemFromCircuitJsonSchematic.ts +++ b/lib/testing/getInputProblemFromCircuitJsonSchematic.ts @@ -30,7 +30,7 @@ export const getInputProblemFromCircuitJsonSchematic = ( const cjChips = db.schematic_component.list().map((schematic_component) => ({ schematic_component, source_component: db.source_component.get( - schematic_component.source_component_id, + schematic_component.source_component_id!, ), ports: db.schematic_port .list({ diff --git a/package.json b/package.json index df3ebd7..06fc351 100644 --- a/package.json +++ b/package.json @@ -14,21 +14,21 @@ "build:site": "cosmos-export" }, "devDependencies": { - "@biomejs/biome": "^2.1.3", + "@biomejs/biome": "^2.4.16", "@react-hook/resize-observer": "^2.0.2", - "@tscircuit/circuit-json-util": "^0.0.64", - "@tscircuit/math-utils": "^0.0.19", - "@tscircuit/schematic-viewer": "^2.0.26", - "@types/bun": "latest", + "@tscircuit/circuit-json-util": "^0.0.95", + "@tscircuit/math-utils": "^0.0.36", + "@tscircuit/schematic-viewer": "^2.0.61", + "@types/bun": "^1.3.14", "bpc-graph": "^0.0.66", - "calculate-packing": "^0.0.31", - "circuit-json": "^0.0.226", - "graphics-debug": "^0.0.64", - "react-cosmos": "^7.0.0", - "react-cosmos-plugin-vite": "^7.0.0", - "tscircuit": "^0.0.593", - "tsup": "^8.5.0", - "circuit-to-svg": "^0.0.350" + "calculate-packing": "^0.0.74", + "circuit-json": "^0.0.432", + "graphics-debug": "^0.0.95", + "react-cosmos": "^7.3.0", + "react-cosmos-plugin-vite": "^7.3.0", + "tscircuit": "^0.0.1819", + "tsup": "^8.5.1", + "circuit-to-svg": "^0.0.351" }, "peerDependencies": { "typescript": "^5" diff --git a/pages/examples/example01/example01.page.tsx b/pages/examples/example01/example01.page.tsx new file mode 100644 index 0000000..bbe3174 --- /dev/null +++ b/pages/examples/example01/example01.page.tsx @@ -0,0 +1,8 @@ +import { LayoutPipelineDebugger } from "lib/components/LayoutPipelineDebugger" +import type { InputProblem } from "lib/types/InputProblem" +import inputProblem from "./inputProblem.json" + +export default function LayoutPipelineSolver05Page() { + const problem: InputProblem = inputProblem as InputProblem + return +} diff --git a/pages/examples/example01/inputProblem.json b/pages/examples/example01/inputProblem.json new file mode 100644 index 0000000..06edc48 --- /dev/null +++ b/pages/examples/example01/inputProblem.json @@ -0,0 +1,206 @@ +{ + "chipMap": { + "C6": { + "chipId": "C6", + "pins": ["C6.1", "C6.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "availableRotations": [0] + }, + "C1": { + "chipId": "C1", + "pins": ["C1.1", "C1.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "availableRotations": [0] + }, + "C2": { + "chipId": "C2", + "pins": ["C2.1", "C2.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "availableRotations": [0] + }, + "C5": { + "chipId": "C5", + "pins": ["C5.1", "C5.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "fixedPosition": { + "x": 4, + "y": 2 + }, + "availableRotations": [0] + }, + "U1": { + "chipId": "U1", + "pins": ["U1.1", "U1.2", "U1.3", "U1.4", "U1.5"], + "size": { + "x": 1.2000000000000002, + "y": 0.8 + }, + "availableRotations": [0, 90, 180, 270] + } + }, + "chipPinMap": { + "C6.1": { + "pinId": "C6.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C6.2": { + "pinId": "C6.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "C1.1": { + "pinId": "C1.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C1.2": { + "pinId": "C1.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "C2.1": { + "pinId": "C2.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C2.2": { + "pinId": "C2.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "C5.1": { + "pinId": "C5.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C5.2": { + "pinId": "C5.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "U1.1": { + "pinId": "U1.1", + "offset": { + "x": -1, + "y": 0.2 + }, + "side": "x-" + }, + "U1.2": { + "pinId": "U1.2", + "offset": { + "x": -1, + "y": 0 + }, + "side": "x-" + }, + "U1.3": { + "pinId": "U1.3", + "offset": { + "x": -1, + "y": -0.2 + }, + "side": "x-" + }, + "U1.4": { + "pinId": "U1.4", + "offset": { + "x": 1, + "y": -0.1 + }, + "side": "x+" + }, + "U1.5": { + "pinId": "U1.5", + "offset": { + "x": 1, + "y": 0.1 + }, + "side": "x+" + } + }, + "groupMap": {}, + "groupPinMap": {}, + "netMap": { + "unnamedsubcircuit52_connectivity_net0": { + "netId": "unnamedsubcircuit52_connectivity_net0" + }, + "unnamedsubcircuit52_connectivity_net1": { + "netId": "unnamedsubcircuit52_connectivity_net1" + }, + "unnamedsubcircuit52_connectivity_net2": { + "netId": "unnamedsubcircuit52_connectivity_net2" + } + }, + "pinStrongConnMap": { + "U1.1-C6.1": true, + "C6.1-U1.1": true, + "U1.1-C1.1": true, + "C1.1-U1.1": true, + "U1.1-C2.1": true, + "C2.1-U1.1": true, + "U1.3-U1.1": true, + "U1.1-U1.3": true, + "U1.2-C6.2": true, + "C6.2-U1.2": true, + "U1.2-C1.2": true, + "C1.2-U1.2": true, + "U1.2-C2.2": true, + "C2.2-U1.2": true, + "U1.5-C5.1": true, + "C5.1-U1.5": true + }, + "netConnMap": { + "C6.1-unnamedsubcircuit52_connectivity_net0": true, + "C1.1-unnamedsubcircuit52_connectivity_net0": true, + "C2.1-unnamedsubcircuit52_connectivity_net0": true, + "U1.1-unnamedsubcircuit52_connectivity_net0": true, + "U1.3-unnamedsubcircuit52_connectivity_net0": true, + "C6.2-unnamedsubcircuit52_connectivity_net1": true, + "C1.2-unnamedsubcircuit52_connectivity_net1": true, + "C2.2-unnamedsubcircuit52_connectivity_net1": true, + "C5.2-unnamedsubcircuit52_connectivity_net1": true, + "U1.2-unnamedsubcircuit52_connectivity_net1": true, + "C5.1-unnamedsubcircuit52_connectivity_net2": true, + "U1.5-unnamedsubcircuit52_connectivity_net2": true + }, + "chipGap": 0.4, + "partitionGap": 1.2 +} diff --git a/pages/examples/example02/example02.page.tsx b/pages/examples/example02/example02.page.tsx new file mode 100644 index 0000000..bbe3174 --- /dev/null +++ b/pages/examples/example02/example02.page.tsx @@ -0,0 +1,8 @@ +import { LayoutPipelineDebugger } from "lib/components/LayoutPipelineDebugger" +import type { InputProblem } from "lib/types/InputProblem" +import inputProblem from "./inputProblem.json" + +export default function LayoutPipelineSolver05Page() { + const problem: InputProblem = inputProblem as InputProblem + return +} diff --git a/pages/examples/example02/inputProblem.json b/pages/examples/example02/inputProblem.json new file mode 100644 index 0000000..f091241 --- /dev/null +++ b/pages/examples/example02/inputProblem.json @@ -0,0 +1,210 @@ +{ + "chipMap": { + "C6": { + "chipId": "C6", + "pins": ["C6.1", "C6.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "availableRotations": [0] + }, + "C1": { + "chipId": "C1", + "pins": ["C1.1", "C1.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "availableRotations": [0] + }, + "C2": { + "chipId": "C2", + "pins": ["C2.1", "C2.2"], + "fixedPosition": { + "x": 2, + "y": 2 + }, + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "availableRotations": [0] + }, + "C5": { + "chipId": "C5", + "pins": ["C5.1", "C5.2"], + "size": { + "x": 0.5291665999999999, + "y": 1.0583333000000001 + }, + "fixedPosition": { + "x": 4, + "y": 2 + }, + "availableRotations": [0] + }, + "U1": { + "chipId": "U1", + "pins": ["U1.1", "U1.2", "U1.3", "U1.4", "U1.5"], + "size": { + "x": 1.2000000000000002, + "y": 0.8 + }, + "availableRotations": [0, 90, 180, 270] + } + }, + "chipPinMap": { + "C6.1": { + "pinId": "C6.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C6.2": { + "pinId": "C6.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "C1.1": { + "pinId": "C1.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C1.2": { + "pinId": "C1.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "C2.1": { + "pinId": "C2.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C2.2": { + "pinId": "C2.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "C5.1": { + "pinId": "C5.1", + "offset": { + "x": -0.00027334999999965165, + "y": 0.5512093000000002 + }, + "side": "y+" + }, + "C5.2": { + "pinId": "C5.2", + "offset": { + "x": 0.00027334999999965165, + "y": -0.5512093000000002 + }, + "side": "y-" + }, + "U1.1": { + "pinId": "U1.1", + "offset": { + "x": -1, + "y": 0.2 + }, + "side": "x-" + }, + "U1.2": { + "pinId": "U1.2", + "offset": { + "x": -1, + "y": 0 + }, + "side": "x-" + }, + "U1.3": { + "pinId": "U1.3", + "offset": { + "x": -1, + "y": -0.2 + }, + "side": "x-" + }, + "U1.4": { + "pinId": "U1.4", + "offset": { + "x": 1, + "y": -0.1 + }, + "side": "x+" + }, + "U1.5": { + "pinId": "U1.5", + "offset": { + "x": 1, + "y": 0.1 + }, + "side": "x+" + } + }, + "groupMap": {}, + "groupPinMap": {}, + "netMap": { + "unnamedsubcircuit52_connectivity_net0": { + "netId": "unnamedsubcircuit52_connectivity_net0" + }, + "unnamedsubcircuit52_connectivity_net1": { + "netId": "unnamedsubcircuit52_connectivity_net1" + }, + "unnamedsubcircuit52_connectivity_net2": { + "netId": "unnamedsubcircuit52_connectivity_net2" + } + }, + "pinStrongConnMap": { + "U1.1-C6.1": true, + "C6.1-U1.1": true, + "U1.1-C1.1": true, + "C1.1-U1.1": true, + "U1.1-C2.1": true, + "C2.1-U1.1": true, + "U1.3-U1.1": true, + "U1.1-U1.3": true, + "U1.2-C6.2": true, + "C6.2-U1.2": true, + "U1.2-C1.2": true, + "C1.2-U1.2": true, + "U1.2-C2.2": true, + "C2.2-U1.2": true, + "U1.5-C5.1": true, + "C5.1-U1.5": true + }, + "netConnMap": { + "C6.1-unnamedsubcircuit52_connectivity_net0": true, + "C1.1-unnamedsubcircuit52_connectivity_net0": true, + "C2.1-unnamedsubcircuit52_connectivity_net0": true, + "U1.1-unnamedsubcircuit52_connectivity_net0": true, + "U1.3-unnamedsubcircuit52_connectivity_net0": true, + "C6.2-unnamedsubcircuit52_connectivity_net1": true, + "C1.2-unnamedsubcircuit52_connectivity_net1": true, + "C2.2-unnamedsubcircuit52_connectivity_net1": true, + "C5.2-unnamedsubcircuit52_connectivity_net1": true, + "U1.2-unnamedsubcircuit52_connectivity_net1": true, + "C5.1-unnamedsubcircuit52_connectivity_net2": true, + "U1.5-unnamedsubcircuit52_connectivity_net2": true + }, + "chipGap": 0.4, + "partitionGap": 1.2 +} diff --git a/tests/LayoutPipelineSolver/LayoutPipelineSolverFixed.test.ts b/tests/LayoutPipelineSolver/LayoutPipelineSolverFixed.test.ts new file mode 100644 index 0000000..1fc374d --- /dev/null +++ b/tests/LayoutPipelineSolver/LayoutPipelineSolverFixed.test.ts @@ -0,0 +1,187 @@ +import { expect, test } from "bun:test" +import { LayoutPipelineSolver } from "lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver" +import type { InputProblem } from "lib/types/InputProblem" +import { normalizeSide } from "lib/types/Side" + +test("LayoutPipelineSolverFixed01: fixed chip + connected free chip in same partition", () => { + const problem: InputProblem = { + chipMap: { + U1: { + chipId: "U1", + pins: ["U1.A", "U1.B"], + size: { x: 2, y: 2 }, + fixedPosition: { x: 0, y: 0 }, + availableRotations: [0], + }, + C1: { + chipId: "C1", + pins: ["C1.A", "C1.B"], + size: { x: 1, y: 0.5 }, + }, + }, + chipPinMap: { + "U1.A": { + pinId: "U1.A", + offset: { x: -1, y: 0 }, + side: normalizeSide("left"), + }, + "U1.B": { + pinId: "U1.B", + offset: { x: 1, y: 0 }, + side: normalizeSide("right"), + }, + "C1.A": { + pinId: "C1.A", + offset: { x: -0.5, y: 0 }, + side: normalizeSide("left"), + }, + "C1.B": { + pinId: "C1.B", + offset: { x: 0.5, y: 0 }, + side: normalizeSide("right"), + }, + }, + netMap: { N1: { netId: "N1" } }, + // U1.B strongly connected to C1.A + pinStrongConnMap: { + "U1.B-C1.A": true, + "C1.A-U1.B": true, + }, + netConnMap: {}, + chipGap: 0.2, + partitionGap: 2, + } + + const solver = new LayoutPipelineSolver(problem) + solver.solve() + + expect(solver.solved).toBe(true) + expect(solver.failed).toBe(false) + + const layout = solver.getOutputLayout() + expect(layout.chipPlacements["U1"]).toBeDefined() + expect(layout.chipPlacements["C1"]).toBeDefined() + + // Fixed chip must stay at its declared position + expect(layout.chipPlacements["U1"]!.x).toBeCloseTo(0, 5) + expect(layout.chipPlacements["U1"]!.y).toBeCloseTo(0, 5) + expect(layout.chipPlacements["U1"]!.ccwRotationDegrees).toBe(0) + + // No overlaps + const overlaps = solver.checkForOverlaps(layout) + expect(overlaps.length).toBe(0) +}) + +test("LayoutPipelineSolverFixed02: fixed chip in one partition, free chip in separate partition", () => { + const problem: InputProblem = { + chipMap: { + U1: { + chipId: "U1", + pins: ["U1.A"], + size: { x: 2, y: 2 }, + fixedPosition: { x: 5, y: 5 }, + }, + R1: { + chipId: "R1", + pins: ["R1.A", "R1.B"], + size: { x: 1, y: 0.5 }, + }, + }, + chipPinMap: { + "U1.A": { + pinId: "U1.A", + offset: { x: 1, y: 0 }, + side: normalizeSide("right"), + }, + "R1.A": { + pinId: "R1.A", + offset: { x: -0.5, y: 0 }, + side: normalizeSide("left"), + }, + "R1.B": { + pinId: "R1.B", + offset: { x: 0.5, y: 0 }, + side: normalizeSide("right"), + }, + }, + netMap: { VCC: { netId: "VCC" } }, + pinStrongConnMap: {}, + netConnMap: { + "U1.A-VCC": true, + "R1.A-VCC": true, + }, + chipGap: 0.2, + partitionGap: 2, + } + + const solver = new LayoutPipelineSolver(problem) + solver.solve() + + expect(solver.solved).toBe(true) + + const layout = solver.getOutputLayout() + + // Fixed chip must be at its declared position + expect(layout.chipPlacements["U1"]!.x).toBeCloseTo(5, 5) + expect(layout.chipPlacements["U1"]!.y).toBeCloseTo(5, 5) + + // No overlaps + const overlaps = solver.checkForOverlaps(layout) + expect(overlaps.length).toBe(0) +}) + +test("LayoutPipelineSolverFixed03: all chips fixed", () => { + const problem: InputProblem = { + chipMap: { + U1: { + chipId: "U1", + pins: ["U1.A"], + size: { x: 2, y: 2 }, + fixedPosition: { x: -3, y: 0 }, + availableRotations: [0], + }, + U2: { + chipId: "U2", + pins: ["U2.A"], + size: { x: 1.5, y: 1.5 }, + fixedPosition: { x: 3, y: 1 }, + availableRotations: [90], + }, + }, + chipPinMap: { + "U1.A": { + pinId: "U1.A", + offset: { x: 1, y: 0 }, + side: normalizeSide("right"), + }, + "U2.A": { + pinId: "U2.A", + offset: { x: -0.75, y: 0 }, + side: normalizeSide("left"), + }, + }, + netMap: { N1: { netId: "N1" } }, + pinStrongConnMap: {}, + netConnMap: {}, + chipGap: 0.2, + partitionGap: 2, + } + + const solver = new LayoutPipelineSolver(problem) + solver.solve() + + expect(solver.solved).toBe(true) + + const layout = solver.getOutputLayout() + + expect(layout.chipPlacements["U1"]!.x).toBeCloseTo(-3, 5) + expect(layout.chipPlacements["U1"]!.y).toBeCloseTo(0, 5) + expect(layout.chipPlacements["U1"]!.ccwRotationDegrees).toBe(0) + + expect(layout.chipPlacements["U2"]!.x).toBeCloseTo(3, 5) + expect(layout.chipPlacements["U2"]!.y).toBeCloseTo(1, 5) + expect(layout.chipPlacements["U2"]!.ccwRotationDegrees).toBe(90) + + const overlaps = solver.checkForOverlaps(layout) + expect(overlaps.length).toBe(0) +}) diff --git a/tests/getInputProblemFromCircuitJsonSchematic/getInputProblemFromCircuitJsonSchematic01.test.tsx b/tests/getInputProblemFromCircuitJsonSchematic/getInputProblemFromCircuitJsonSchematic01.test.tsx index a24974e..e637656 100644 --- a/tests/getInputProblemFromCircuitJsonSchematic/getInputProblemFromCircuitJsonSchematic01.test.tsx +++ b/tests/getInputProblemFromCircuitJsonSchematic/getInputProblemFromCircuitJsonSchematic01.test.tsx @@ -19,8 +19,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "C1.2", ], "size": { - "x": 0.5291665999999999, - "y": 1.0583333000000001, + "x": 0.53, + "y": 1.1, }, }, "D1": { @@ -30,8 +30,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "D1.2", ], "size": { - "x": 0.6221256000000088, - "y": 1.0521572000000003, + "x": 0.62, + "y": 1.08, }, }, "I2C": { @@ -42,8 +42,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "I2C.3", ], "size": { - "x": 0.8843008999999997, - "y": 0.5299361999999987, + "x": 0.9, + "y": 0.53, }, }, "INT_JP": { @@ -53,8 +53,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "INT_JP.2", ], "size": { - "x": 0.30829299999999904, - "y": 0.8811970999999998, + "x": 0.3, + "y": 0.9, }, }, "J1": { @@ -105,8 +105,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "LED.2", ], "size": { - "x": 0.30829299999999904, - "y": 0.8811970999999998, + "x": 0.3, + "y": 0.9, }, }, "Q1": { @@ -117,8 +117,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "Q1.3", ], "size": { - "x": 0.8935117710000002, - "y": 1.1601665819999987, + "x": 0.89, + "y": 1.16, }, }, "R2": { @@ -128,8 +128,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "R2.2", ], "size": { - "x": 0.40790845000000175, - "y": 1.0583332999999997, + "x": 0.4094553499999995, + "y": 1.1, }, }, "R3": { @@ -139,8 +139,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "R3.2", ], "size": { - "x": 0.40790845000000175, - "y": 1.0583332999999997, + "x": 0.4094553499999995, + "y": 1.1, }, }, "R4": { @@ -150,8 +150,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "R4.2", ], "size": { - "x": 0.40790845000000175, - "y": 1.0583332999999997, + "x": 0.4094553499999995, + "y": 1.1, }, }, "R5": { @@ -161,8 +161,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "R5.2", ], "size": { - "x": 0.40790845000000175, - "y": 1.0583332999999997, + "x": 0.4094553499999995, + "y": 1.1, }, }, "R7": { @@ -172,8 +172,8 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "R7.2", ], "size": { - "x": 0.40790845000000175, - "y": 1.0583332999999997, + "x": 0.4094553499999995, + "y": 1.1, }, }, "U1": { @@ -195,72 +195,72 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { "chipPinMap": { "C1.1": { "offset": { - "x": -0.00027334999999961695, - "y": 0.5512093000000002, + "x": 0, + "y": 0.55, }, "pinId": "C1.1", "side": "y+", }, "C1.2": { "offset": { - "x": 0.00027334999999961695, - "y": -0.5512093000000002, + "x": 0, + "y": -0.55, }, "pinId": "C1.2", "side": "y-", }, "D1.1": { "offset": { - "x": 0.004432900000001183, - "y": 0.5362093000000003, + "x": 0, + "y": 0.54, }, "pinId": "D1.1", "side": "y+", }, "D1.2": { "offset": { - "x": 0.004886400000003732, - "y": -0.5362092999999994, + "x": 0, + "y": -0.5399999999999991, }, "pinId": "D1.2", "side": "y-", }, "I2C.1": { "offset": { - "x": 0.44580080000000066, - "y": -0.10158727049999955, + "x": 0.4499999999999993, + "y": -0.09999999999999995, }, "pinId": "I2C.1", "side": "x+", }, "I2C.2": { "offset": { - "x": 0.0034928000000000736, - "y": 0.25259902949999957, + "x": 0, + "y": 0.25, }, "pinId": "I2C.2", "side": "y+", }, "I2C.3": { "offset": { - "x": -0.44580080000000066, - "y": -0.10146287049999964, + "x": -0.4499999999999993, + "y": -0.10000000000000006, }, "pinId": "I2C.3", "side": "x-", }, "INT_JP.1": { "offset": { - "x": 0.00006220000000034531, - "y": 0.4458007999999998, + "x": 0, + "y": 0.4500000000000002, }, "pinId": "INT_JP.1", "side": "y+", }, "INT_JP.2": { "offset": { - "x": -0.00006220000000034531, - "y": -0.4458007999999998, + "x": 0, + "y": -0.4499999999999993, }, "pinId": "INT_JP.2", "side": "y-", @@ -379,120 +379,120 @@ test("getInputProblemFromCircuitJsonSchematic01", () => { }, "LED.1": { "offset": { - "x": 0.00006220000000034531, - "y": 0.44580080000000066, + "x": 0, + "y": 0.4499999999999993, }, "pinId": "LED.1", "side": "y+", }, "LED.2": { "offset": { - "x": -0.00006220000000034531, - "y": -0.44580080000000066, + "x": 0, + "y": -0.45000000000000107, }, "pinId": "LED.2", "side": "y-", }, "Q1.1": { "offset": { - "x": 0.30397715550000004, - "y": 0.5519248499999994, + "x": 0.30000000000000004, + "y": 0.55, }, "pinId": "Q1.1", "side": "y+", }, "Q1.2": { "offset": { - "x": 0.31067575550000137, - "y": -0.5519248499999994, + "x": 0.31000000000000005, + "y": -0.55, }, "pinId": "Q1.2", "side": "y-", }, "Q1.3": { "offset": { - "x": -0.4185974445, - "y": -0.10250625000000019, + "x": -0.41999999999999993, + "y": -0.1, }, "pinId": "Q1.3", "side": "x-", }, "R2.1": { "offset": { - "x": -0.0002732499999993365, - "y": -0.5512907000000005, + "x": 0, + "y": -0.55, }, "pinId": "R2.1", "side": "y-", }, "R2.2": { "offset": { - "x": 0.0002732499999993365, - "y": 0.5512907000000002, + "x": 0, + "y": 0.5499999999999999, }, "pinId": "R2.2", "side": "y+", }, "R3.1": { "offset": { - "x": -0.0002732499999993365, - "y": -0.5512907, + "x": 0, + "y": -0.5499999999999998, }, "pinId": "R3.1", "side": "y-", }, "R3.2": { "offset": { - "x": 0.0002732499999993365, - "y": 0.5512907, + "x": 0, + "y": 0.5499999999999998, }, "pinId": "R3.2", "side": "y+", }, "R4.1": { "offset": { - "x": -0.0002732499999993365, - "y": -0.5512907000000009, + "x": 0, + "y": -0.5500000000000007, }, "pinId": "R4.1", "side": "y-", }, "R4.2": { "offset": { - "x": 0.0002732499999993365, - "y": 0.5512907, + "x": 0, + "y": 0.5499999999999989, }, "pinId": "R4.2", "side": "y+", }, "R5.1": { "offset": { - "x": -0.0002732499999993365, - "y": -0.5512907000000005, + "x": 0, + "y": -0.55, }, "pinId": "R5.1", "side": "y-", }, "R5.2": { "offset": { - "x": 0.0002732499999993365, - "y": 0.5512907000000002, + "x": 0, + "y": 0.5499999999999999, }, "pinId": "R5.2", "side": "y+", }, "R7.1": { "offset": { - "x": -0.0002732499999993365, - "y": -0.5512907000000005, + "x": 0, + "y": -0.55, }, "pinId": "R7.1", "side": "y-", }, "R7.2": { "offset": { - "x": 0.0002732499999993365, - "y": 0.5512907000000002, + "x": 0, + "y": 0.5499999999999999, }, "pinId": "R7.2", "side": "y+", diff --git a/vite.config.js b/vite.config.js index 56af053..c0adb11 100644 --- a/vite.config.js +++ b/vite.config.js @@ -6,5 +6,6 @@ export default defineConfig({ alias: { lib: resolve(__dirname, "lib"), }, + dedupe: ["react", "react-dom"], }, }) From 50734fe8dc64b9d9f72d4c44f26ec65adb2b5197 Mon Sep 17 00:00:00 2001 From: Sangit Manandhar Date: Wed, 3 Jun 2026 19:59:10 -0700 Subject: [PATCH 2/2] code cleanup --- .../PartitionPackingSolver/PartitionPackingSolver.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts index 6e938ec..e04353b 100644 --- a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts +++ b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts @@ -7,7 +7,7 @@ import type { GraphicsObject } from "graphics-debug" import { type PackInput, PackSolver2 } from "calculate-packing" import { BaseSolver } from "../BaseSolver" import type { OutputLayout, Placement } from "../../types/OutputLayout" -import type { InputProblem } from "../../types/InputProblem" +import type { InputProblem, PinId, NetId } from "../../types/InputProblem" import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem" import type { PackedPartition } from "../PackInnerPartitionsSolver/PackInnerPartitionsSolver" @@ -97,8 +97,8 @@ export class PartitionPackingSolver extends BaseSolver { } } - private buildConnectivityMap(): Map { - const pinToNetworkMap = new Map() + private buildConnectivityMap(): Map { + const pinToNetworkMap = new Map() for (const packedPartition of this.packedPartitions) { for (const [connKey, connected] of Object.entries( packedPartition.inputProblem.netConnMap,