Skip to content

Commit 5b80494

Browse files
authored
Merge pull request #12 from tscircuit/opt2
Optimize section search with lazy computations
2 parents 209c866 + b3c90fc commit 5b80494

2 files changed

Lines changed: 95 additions & 47 deletions

File tree

lib/core.ts

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { BaseSolver } from "@tscircuit/solver-utils"
22
import type { GraphicsObject } from "graphics-debug"
33
import { convertToSerializedHyperGraph } from "./compat/convertToSerializedHyperGraph"
44
import { computeRegionCost } from "./computeRegionCost"
5-
import { countNewIntersections } from "./countNewIntersections"
5+
import { countNewIntersectionsWithValues } from "./countNewIntersections"
66
import { MinHeap } from "./MinHeap"
77
import { shuffle } from "./shuffle"
88
import type {
@@ -204,9 +204,22 @@ export const getTinyHyperGraphSolverOptions = (
204204
const compareCandidatesByF = (left: Candidate, right: Candidate) =>
205205
left.f - right.f
206206

207+
interface SegmentGeometryScratch {
208+
lesserAngle: number
209+
greaterAngle: number
210+
layerMask: number
211+
entryExitLayerChanges: number
212+
}
213+
207214
export class TinyHyperGraphSolver extends BaseSolver {
208215
state: TinyHyperGraphWorkingState
209-
problemSetup: TinyHyperGraphProblemSetup
216+
private _problemSetup?: TinyHyperGraphProblemSetup
217+
private segmentGeometryScratch: SegmentGeometryScratch = {
218+
lesserAngle: 0,
219+
greaterAngle: 0,
220+
layerMask: 0,
221+
entryExitLayerChanges: 0,
222+
}
210223

211224
DISTANCE_TO_COST = 0.05 // 50mm = 1 cost unit (1 cost unit ~ 100% chance of failure)
212225

@@ -247,7 +260,14 @@ export class TinyHyperGraphSolver extends BaseSolver {
247260
ripCount: 0,
248261
regionCongestionCost: new Float64Array(topology.regionCount).fill(0),
249262
}
250-
this.problemSetup = this.computeProblemSetup()
263+
}
264+
265+
get problemSetup(): TinyHyperGraphProblemSetup {
266+
if (!this._problemSetup) {
267+
this._problemSetup = this.computeProblemSetup()
268+
}
269+
270+
return this._problemSetup
251271
}
252272

253273
computeProblemSetup(): TinyHyperGraphProblemSetup {
@@ -288,6 +308,10 @@ export class TinyHyperGraphSolver extends BaseSolver {
288308
}
289309
}
290310

311+
override _setup() {
312+
void this.problemSetup
313+
}
314+
291315
override _step() {
292316
const { problem, topology, state } = this
293317

@@ -480,41 +504,33 @@ export class TinyHyperGraphSolver extends BaseSolver {
480504
)
481505
}
482506

483-
getPortAngleInRegion(portId: PortId, regionId: RegionId): number {
484-
const { topology } = this
485-
const [firstRegionId, secondRegionId] =
486-
topology.incidentPortRegion[portId] ?? []
487-
488-
if (firstRegionId === regionId) {
489-
return topology.portAngleForRegion1[portId]
490-
}
491-
492-
if (secondRegionId === regionId) {
493-
return (
494-
topology.portAngleForRegion2?.[portId] ??
495-
topology.portAngleForRegion1[portId]
496-
)
497-
}
498-
499-
return topology.portAngleForRegion1[portId]
500-
}
501-
502-
buildDynamicAnglePair(
507+
populateSegmentGeometryScratch(
503508
regionId: RegionId,
504509
port1Id: PortId,
505510
port2Id: PortId,
506-
): DynamicAnglePair {
507-
const { topology, state } = this
508-
const angle1 = this.getPortAngleInRegion(port1Id, regionId)
509-
const angle2 = this.getPortAngleInRegion(port2Id, regionId)
511+
): SegmentGeometryScratch {
512+
const { topology } = this
513+
const scratch = this.segmentGeometryScratch
514+
const port1IncidentRegions = topology.incidentPortRegion[port1Id]
515+
const port2IncidentRegions = topology.incidentPortRegion[port2Id]
516+
const angle1 =
517+
port1IncidentRegions[0] === regionId || port1IncidentRegions[1] !== regionId
518+
? topology.portAngleForRegion1[port1Id]
519+
: topology.portAngleForRegion2?.[port1Id] ??
520+
topology.portAngleForRegion1[port1Id]
521+
const angle2 =
522+
port2IncidentRegions[0] === regionId || port2IncidentRegions[1] !== regionId
523+
? topology.portAngleForRegion1[port2Id]
524+
: topology.portAngleForRegion2?.[port2Id] ??
525+
topology.portAngleForRegion1[port2Id]
510526
const z1 = topology.portZ[port1Id]
511527
const z2 = topology.portZ[port2Id]
528+
scratch.lesserAngle = angle1 < angle2 ? angle1 : angle2
529+
scratch.greaterAngle = angle1 < angle2 ? angle2 : angle1
530+
scratch.layerMask = (1 << z1) | (1 << z2)
531+
scratch.entryExitLayerChanges = z1 !== z2 ? 1 : 0
512532

513-
if (angle1 < angle2) {
514-
return [state.currentRouteNetId!, angle1, z1, angle2, z2]
515-
}
516-
517-
return [state.currentRouteNetId!, angle2, z2, angle1, z1]
533+
return scratch
518534
}
519535

520536
appendSegmentToRegionCache(
@@ -524,30 +540,40 @@ export class TinyHyperGraphSolver extends BaseSolver {
524540
) {
525541
const { topology, state } = this
526542
const regionCache = state.regionIntersectionCaches[regionId]
527-
const newPair = this.buildDynamicAnglePair(regionId, port1Id, port2Id)
543+
const segmentGeometry = this.populateSegmentGeometryScratch(
544+
regionId,
545+
port1Id,
546+
port2Id,
547+
)
528548
const [
529549
newSameLayerIntersections,
530550
newCrossLayerIntersections,
531551
newEntryExitLayerChanges,
532-
] = countNewIntersections(regionCache, newPair)
533-
const [netId, lesserAngle, z1, greaterAngle, z2] = newPair
552+
] = countNewIntersectionsWithValues(
553+
regionCache,
554+
state.currentRouteNetId!,
555+
segmentGeometry.lesserAngle,
556+
segmentGeometry.greaterAngle,
557+
segmentGeometry.layerMask,
558+
segmentGeometry.entryExitLayerChanges,
559+
)
534560
const nextLength = regionCache.netIds.length + 1
535561

536562
const netIds = new Int32Array(nextLength)
537563
netIds.set(regionCache.netIds)
538-
netIds[nextLength - 1] = netId
564+
netIds[nextLength - 1] = state.currentRouteNetId!
539565

540566
const lesserAngles = new Int32Array(nextLength)
541567
lesserAngles.set(regionCache.lesserAngles)
542-
lesserAngles[nextLength - 1] = lesserAngle
568+
lesserAngles[nextLength - 1] = segmentGeometry.lesserAngle
543569

544570
const greaterAngles = new Int32Array(nextLength)
545571
greaterAngles.set(regionCache.greaterAngles)
546-
greaterAngles[nextLength - 1] = greaterAngle
572+
greaterAngles[nextLength - 1] = segmentGeometry.greaterAngle
547573

548574
const layerMasks = new Int32Array(nextLength)
549575
layerMasks.set(regionCache.layerMasks)
550-
layerMasks[nextLength - 1] = (1 << z1) | (1 << z2)
576+
layerMasks[nextLength - 1] = segmentGeometry.layerMask
551577

552578
const existingSameLayerIntersections =
553579
regionCache.existingSameLayerIntersections + newSameLayerIntersections
@@ -742,8 +768,7 @@ export class TinyHyperGraphSolver extends BaseSolver {
742768
const nextRegionId = currentCandidate.nextRegionId
743769

744770
const regionCache = state.regionIntersectionCaches[nextRegionId]
745-
746-
const newPair = this.buildDynamicAnglePair(
771+
const segmentGeometry = this.populateSegmentGeometryScratch(
747772
nextRegionId,
748773
currentCandidate.portId,
749774
neighborPortId,
@@ -753,7 +778,14 @@ export class TinyHyperGraphSolver extends BaseSolver {
753778
newSameLayerIntersections,
754779
newCrossLayerIntersections,
755780
newEntryExitLayerChanges,
756-
] = countNewIntersections(regionCache, newPair)
781+
] = countNewIntersectionsWithValues(
782+
regionCache,
783+
state.currentRouteNetId!,
784+
segmentGeometry.lesserAngle,
785+
segmentGeometry.greaterAngle,
786+
segmentGeometry.layerMask,
787+
segmentGeometry.entryExitLayerChanges,
788+
)
757789

758790
const newRegionCost =
759791
computeRegionCost(

lib/countNewIntersections.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@ export const createDynamicAnglePairArrays = (
2424
}
2525
}
2626

27-
export const countNewIntersections = (
27+
export const countNewIntersectionsWithValues = (
2828
existingPairs: DynamicAnglePairArrays,
29-
newPair: DynamicAnglePair,
29+
newNet: number,
30+
newLesserAngle: number,
31+
newGreaterAngle: number,
32+
newLayerMask: number,
33+
entryExitLayerChanges: number,
3034
): [number, number, number] => {
31-
const [newNet, newLesserAngle, newZ1, newGreaterAngle, newZ2] = newPair
32-
const newLayerMask = (1 << newZ1) | (1 << newZ2)
3335
const { netIds, lesserAngles, greaterAngles, layerMasks } = existingPairs
3436

3537
let sameLayerIntersectionCount = 0
3638
let crossingLayerIntersectionCount = 0
37-
const entryExitLayerChanges = newZ1 !== newZ2 ? 1 : 0
3839

3940
for (let i = 0; i < netIds.length; i++) {
4041
if (newNet === netIds[i]) continue
@@ -60,4 +61,19 @@ export const countNewIntersections = (
6061
]
6162
}
6263

64+
export const countNewIntersections = (
65+
existingPairs: DynamicAnglePairArrays,
66+
newPair: DynamicAnglePair,
67+
): [number, number, number] => {
68+
const [newNet, newLesserAngle, newZ1, newGreaterAngle, newZ2] = newPair
69+
return countNewIntersectionsWithValues(
70+
existingPairs,
71+
newNet,
72+
newLesserAngle,
73+
newGreaterAngle,
74+
(1 << newZ1) | (1 << newZ2),
75+
newZ1 !== newZ2 ? 1 : 0,
76+
)
77+
}
78+
6379
export const countIntersectionsFromAnglePairsDynamic = countNewIntersections

0 commit comments

Comments
 (0)