@tscircuit/hypergraph 0.0.68 → 0.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +102 -5
  2. package/dist/index.js +841 -213
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -83,6 +83,10 @@ type SerializedRegionPortAssignment = {
83
83
  type SerializedHyperGraph = {
84
84
  ports: SerializedGraphPort[];
85
85
  regions: SerializedGraphRegion[];
86
+ solvedRoutes?: SerializedSolvedRoute[];
87
+ connections?: SerializedConnection[];
88
+ _sectionRouteBindings?: SerializedSectionRouteBinding[];
89
+ _sectionCentralRegionId?: RegionId;
86
90
  };
87
91
  type SerializedCandidate = {
88
92
  portId: PortId;
@@ -100,8 +104,10 @@ type SerializedSolvedRoute = {
100
104
  connection: SerializedConnection;
101
105
  requiredRip: boolean;
102
106
  };
103
- type SerializedHyperGraphWithSolvedRoutes = SerializedHyperGraph & {
104
- solvedRoutes: SerializedSolvedRoute[];
107
+ type SerializedSectionRouteBinding = {
108
+ connectionId: ConnectionId;
109
+ solvedPathStartIndex: number;
110
+ solvedPathEndIndex: number;
105
111
  };
106
112
  type Connection = {
107
113
  connectionId: ConnectionId;
@@ -120,11 +126,13 @@ declare const convertConnectionsToSerializedConnections: (connections: Connectio
120
126
 
121
127
  declare const convertHyperGraphToSerializedHyperGraph: (graph: HyperGraph) => SerializedHyperGraph;
122
128
 
129
+ declare const createBlankHyperGraph: (inputGraph: SerializedHyperGraph) => SerializedHyperGraph;
130
+
123
131
  declare const extractSectionOfHyperGraph: (input: {
124
- graph: SerializedHyperGraphWithSolvedRoutes;
132
+ graph: SerializedHyperGraph;
125
133
  centralRegionId: RegionId;
126
134
  expansionHopsFromCentralRegion: number;
127
- }) => SerializedHyperGraphWithSolvedRoutes;
135
+ }) => SerializedHyperGraph;
128
136
 
129
137
  type Node = {
130
138
  f: number;
@@ -335,6 +343,13 @@ declare class HyperGraphSolver<RegionType extends Region = Region, RegionPortTyp
335
343
  _step(): void;
336
344
  }
337
345
 
346
+ declare const reattachSectionToGraph: (input: {
347
+ fullGraph: SerializedHyperGraph;
348
+ solvedSectionGraph: SerializedHyperGraph;
349
+ }) => SerializedHyperGraph;
350
+
351
+ declare const pruneDeadEndPorts: (graph: HyperGraph, retainedPortIds?: Iterable<PortId>) => HyperGraph;
352
+
338
353
  type CreateHyperGraphSolverInput = {
339
354
  inputGraph: HyperGraph | SerializedHyperGraph;
340
355
  inputConnections: (Connection | SerializedConnection)[];
@@ -431,6 +446,88 @@ declare class HyperGraphSectionOptimizer extends BaseSolver {
431
446
  _step(): void;
432
447
  }
433
448
 
449
+ type CreateSectionSolverInput = {
450
+ inputGraph: SerializedHyperGraph;
451
+ inputConnections: SerializedConnection[];
452
+ inputSolvedRoutes: SerializedSolvedRoute[];
453
+ };
454
+ type HyperGraphSectionOptimizer2Input = {
455
+ inputGraph: SerializedHyperGraph;
456
+ inputConnections?: SerializedConnection[];
457
+ inputSolvedRoutes?: SerializedSolvedRoute[];
458
+ sectionExpansionHops?: number;
459
+ maxTargetRegionAttempts?: number;
460
+ maxSectionAttempts?: number;
461
+ minCentralRegionCost?: number;
462
+ effort?: number;
463
+ expansionHopsFromCentralRegion?: number;
464
+ MAX_ATTEMPTS_PER_REGION?: number;
465
+ MAX_ATTEMPTS_PER_SECTION?: number;
466
+ ACCEPTABLE_CENTRAL_REGION_COST?: number;
467
+ };
468
+ type NormalizedHyperGraphSectionOptimizer2Input = {
469
+ inputGraph: SerializedHyperGraph;
470
+ inputConnections: SerializedConnection[];
471
+ inputSolvedRoutes: SerializedSolvedRoute[];
472
+ sectionExpansionHops: number;
473
+ maxTargetRegionAttempts: number;
474
+ maxSectionAttempts: number;
475
+ minCentralRegionCost: number;
476
+ effort: number;
477
+ };
478
+ type SectionSolveAttempt = {
479
+ targetRegionId: RegionId;
480
+ sectionRegionIds: Set<RegionId>;
481
+ fullGraphSnapshot: SerializedHyperGraph;
482
+ blankSectionProblem: SerializedHyperGraph;
483
+ currentSectionCost: number;
484
+ };
485
+ declare class HyperGraphSectionOptimizer2 extends BaseSolver {
486
+ readonly config: NormalizedHyperGraphSectionOptimizer2Input;
487
+ readonly rootSolver: HyperGraphSolver<Region, RegionPort>;
488
+ graph: HyperGraph;
489
+ connections: Connection[];
490
+ solvedRoutes: SolvedRoute[];
491
+ activeAttempt: SectionSolveAttempt | null;
492
+ targetRegionAttemptCounts: Map<string, number>;
493
+ attemptedSectionCount: number;
494
+ activeSubSolver: HyperGraphSolver<Region, RegionPort> | null;
495
+ constructor(input: HyperGraphSectionOptimizer2Input);
496
+ getSolverName(): string;
497
+ getConstructorParams(): {
498
+ inputGraph: SerializedHyperGraph;
499
+ inputConnections: SerializedConnection[];
500
+ inputSolvedRoutes: SerializedSolvedRoute[];
501
+ sectionExpansionHops: number;
502
+ maxTargetRegionAttempts: number;
503
+ maxSectionAttempts: number;
504
+ minCentralRegionCost: number;
505
+ effort: number;
506
+ };
507
+ getOutput(): SolvedRoute[];
508
+ _setup(): void;
509
+ visualize(): GraphicsObject;
510
+ protected createHyperGraphSolver(input: CreateSectionSolverInput): HyperGraphSolver<Region, RegionPort>;
511
+ getCostOfCentralRegion(region: Region): number;
512
+ getSectionCost(input: {
513
+ solvedGraph: SerializedHyperGraph;
514
+ sectionRegionIds: Set<RegionId>;
515
+ }): number;
516
+ _step(): void;
517
+ private startNextSectionAttempt;
518
+ private selectTargetRegion;
519
+ private createSectionSolveAttempt;
520
+ private acceptMergedGraph;
521
+ private rejectActiveAttempt;
522
+ private clearActiveAttempt;
523
+ private bumpTargetRegionAttemptCount;
524
+ private serializeSolvedGraph;
525
+ private deserializeSolvedRoutes;
526
+ private getSectionRegionIds;
527
+ private pruneSectionForBlanking;
528
+ private getRegionSolutionCost;
529
+ }
530
+
434
531
  type Bounds$1 = {
435
532
  minX: number;
436
533
  minY: number;
@@ -1312,4 +1409,4 @@ declare function generateConvexViaTopologyRegions(opts: {
1312
1409
  };
1313
1410
  };
1314
1411
 
1315
- export { type Bounds$1 as Bounds, type BuildOpts, type Candidate, ConnectBuilder, type ConnectOpts, type Connection, type ConnectionId, type ConvexViaGraphFromXYConnectionsResult, type CreateHyperGraphSolver, type CreateHyperGraphSolverInput, type GScore, type GraphEdgeId, type HyperGraph, type HyperGraphSection, HyperGraphSectionOptimizer, HyperGraphSolver, type JPort, type JRegion, JUMPER_GRAPH_SOLVER_DEFAULTS, type JumperGraph, JumperGraphSolver, type JumperGraphWithConnections, type NetworkId, type PortAssignment, PortBuilder, type PortData, type PortId, type PortSpread, type Region, RegionBuilder, type RegionData, type RegionId, type RegionPort, type RegionPortAssignment, type RegionRef, type RouteSegment, type SectionRoute, type SerializedCandidate, type SerializedConnection, type SerializedGraphPort, type SerializedGraphRegion, type SerializedHyperGraph, type SerializedHyperGraphWithSolvedRoutes, type SerializedRegionPortAssignment, type SerializedSolvedRoute, type SharedBoundary, type SolvedRoute, Topology, TopologyError, VIA_GRAPH_SOLVER_DEFAULTS, type ValidateOpts, type ViaByNet, type ViaData, ViaGraphSolver, type ViaGraphWithConnections, type ViaTile, type ViaTileRecommendation, type ViaTileRecommendationCandidate, type ViaTileRecommendationProblemInput, type XYConnection, applyTransformToGraph, calculateGraphBounds, convertConnectionsToSerializedConnections, convertHyperGraphToSerializedHyperGraph, createConvexViaGraphFromXYConnections, createGraphWithConnectionsFromBaseGraph, createViaGraphWithConnections, extractSectionOfHyperGraph, generateConvexViaTopologyRegions, generateDefaultViaTopologyRegions, generateJumperGrid, generateJumperX4Grid, generateViaTopologyRegions, recommendViaTileFromGraphInput, rotateGraph90Degrees, viaTile4Regions as viaTile };
1412
+ export { type Bounds$1 as Bounds, type BuildOpts, type Candidate, ConnectBuilder, type ConnectOpts, type Connection, type ConnectionId, type ConvexViaGraphFromXYConnectionsResult, type CreateHyperGraphSolver, type CreateHyperGraphSolverInput, type CreateSectionSolverInput, type GScore, type GraphEdgeId, type HyperGraph, type HyperGraphSection, HyperGraphSectionOptimizer, HyperGraphSectionOptimizer2, type HyperGraphSectionOptimizer2Input, HyperGraphSolver, type JPort, type JRegion, JUMPER_GRAPH_SOLVER_DEFAULTS, type JumperGraph, JumperGraphSolver, type JumperGraphWithConnections, type NetworkId, type PortAssignment, PortBuilder, type PortData, type PortId, type PortSpread, type Region, RegionBuilder, type RegionData, type RegionId, type RegionPort, type RegionPortAssignment, type RegionRef, type RouteSegment, type SectionRoute, type SerializedCandidate, type SerializedConnection, type SerializedGraphPort, type SerializedGraphRegion, type SerializedHyperGraph, type SerializedRegionPortAssignment, type SerializedSectionRouteBinding, type SerializedSolvedRoute, type SharedBoundary, type SolvedRoute, Topology, TopologyError, VIA_GRAPH_SOLVER_DEFAULTS, type ValidateOpts, type ViaByNet, type ViaData, ViaGraphSolver, type ViaGraphWithConnections, type ViaTile, type ViaTileRecommendation, type ViaTileRecommendationCandidate, type ViaTileRecommendationProblemInput, type XYConnection, applyTransformToGraph, calculateGraphBounds, convertConnectionsToSerializedConnections, convertHyperGraphToSerializedHyperGraph, createBlankHyperGraph, createConvexViaGraphFromXYConnections, createGraphWithConnectionsFromBaseGraph, createViaGraphWithConnections, extractSectionOfHyperGraph, generateConvexViaTopologyRegions, generateDefaultViaTopologyRegions, generateJumperGrid, generateJumperX4Grid, generateViaTopologyRegions, pruneDeadEndPorts, reattachSectionToGraph, recommendViaTileFromGraphInput, rotateGraph90Degrees, viaTile4Regions as viaTile };
package/dist/index.js CHANGED
@@ -1497,6 +1497,320 @@ var convertHyperGraphToSerializedHyperGraph = (graph) => {
1497
1497
  };
1498
1498
  };
1499
1499
 
1500
+ // lib/convertSerializedHyperGraphToHyperGraph.ts
1501
+ var convertSerializedHyperGraphToHyperGraph = (inputGraph) => {
1502
+ if (inputGraph.ports.length > 0 && "region1" in inputGraph.ports[0] && typeof inputGraph.ports[0].region1 === "object") {
1503
+ return inputGraph;
1504
+ }
1505
+ const portMap = /* @__PURE__ */ new Map();
1506
+ const regionMap = /* @__PURE__ */ new Map();
1507
+ for (const region of inputGraph.regions) {
1508
+ const { assignments: _, ...regionWithoutAssignments } = region;
1509
+ regionMap.set(region.regionId, {
1510
+ ...regionWithoutAssignments,
1511
+ d: regionWithoutAssignments.d ? structuredClone(regionWithoutAssignments.d) : regionWithoutAssignments.d,
1512
+ ports: [],
1513
+ assignments: void 0
1514
+ });
1515
+ }
1516
+ for (const port of inputGraph.ports) {
1517
+ const region1 = regionMap.get(port.region1Id ?? port.region1?.regionId);
1518
+ const region2 = regionMap.get(port.region2Id ?? port.region2?.regionId);
1519
+ const deserializedPort = {
1520
+ portId: port.portId,
1521
+ region1,
1522
+ region2,
1523
+ d: port.d
1524
+ };
1525
+ portMap.set(port.portId, deserializedPort);
1526
+ region1.ports.push(deserializedPort);
1527
+ region2.ports.push(deserializedPort);
1528
+ }
1529
+ return {
1530
+ ports: Array.from(portMap.values()),
1531
+ regions: Array.from(regionMap.values())
1532
+ };
1533
+ };
1534
+
1535
+ // lib/convertSerializedConnectionsToConnections.ts
1536
+ var convertSerializedConnectionsToConnections = (inputConnections, graph) => {
1537
+ const connections = [];
1538
+ for (const inputConn of inputConnections) {
1539
+ if ("startRegionId" in inputConn) {
1540
+ connections.push({
1541
+ connectionId: inputConn.connectionId,
1542
+ mutuallyConnectedNetworkId: inputConn.mutuallyConnectedNetworkId ?? inputConn.connectionId,
1543
+ startRegion: graph.regions.find(
1544
+ (region) => region.regionId === inputConn.startRegionId
1545
+ ),
1546
+ endRegion: graph.regions.find(
1547
+ (region) => region.regionId === inputConn.endRegionId
1548
+ )
1549
+ });
1550
+ } else {
1551
+ connections.push(inputConn);
1552
+ }
1553
+ }
1554
+ return connections;
1555
+ };
1556
+
1557
+ // lib/convertSerializedSolvedRoutesToSolvedRoutes.ts
1558
+ var convertSerializedSolvedRoutesToSolvedRoutes = (inputSolvedRoutes, graph) => {
1559
+ const portMap = new Map(graph.ports.map((port) => [port.portId, port]));
1560
+ const regionMap = new Map(
1561
+ graph.regions.map((region) => [region.regionId, region])
1562
+ );
1563
+ const connectionMap = new Map(
1564
+ convertSerializedConnectionsToConnections(
1565
+ inputSolvedRoutes.map((route) => route.connection),
1566
+ graph
1567
+ ).map((connection) => [connection.connectionId, connection])
1568
+ );
1569
+ return inputSolvedRoutes.map((inputSolvedRoute) => {
1570
+ const path = [];
1571
+ for (const originalCandidate of inputSolvedRoute.path) {
1572
+ const port = portMap.get(originalCandidate.portId);
1573
+ if (!port) {
1574
+ throw new Error(
1575
+ `Port ${originalCandidate.portId} not found while deserializing solved route ${inputSolvedRoute.connection.connectionId}`
1576
+ );
1577
+ }
1578
+ const candidate = {
1579
+ port,
1580
+ g: originalCandidate.g,
1581
+ h: originalCandidate.h,
1582
+ f: originalCandidate.f,
1583
+ hops: originalCandidate.hops,
1584
+ ripRequired: originalCandidate.ripRequired
1585
+ };
1586
+ if (originalCandidate.lastPortId) {
1587
+ candidate.lastPort = getRequiredPort(
1588
+ portMap,
1589
+ originalCandidate.lastPortId,
1590
+ inputSolvedRoute.connection.connectionId
1591
+ );
1592
+ }
1593
+ if (originalCandidate.lastRegionId) {
1594
+ candidate.lastRegion = getRequiredRegion(
1595
+ regionMap,
1596
+ originalCandidate.lastRegionId,
1597
+ inputSolvedRoute.connection.connectionId
1598
+ );
1599
+ }
1600
+ if (originalCandidate.nextRegionId) {
1601
+ candidate.nextRegion = getRequiredRegion(
1602
+ regionMap,
1603
+ originalCandidate.nextRegionId,
1604
+ inputSolvedRoute.connection.connectionId
1605
+ );
1606
+ }
1607
+ const parent = path[path.length - 1];
1608
+ if (parent) candidate.parent = parent;
1609
+ path.push(candidate);
1610
+ }
1611
+ const connection = connectionMap.get(
1612
+ inputSolvedRoute.connection.connectionId
1613
+ );
1614
+ if (!connection) {
1615
+ throw new Error(
1616
+ `Connection ${inputSolvedRoute.connection.connectionId} not found while deserializing solved route`
1617
+ );
1618
+ }
1619
+ return {
1620
+ path,
1621
+ connection,
1622
+ requiredRip: inputSolvedRoute.requiredRip
1623
+ };
1624
+ });
1625
+ };
1626
+ var getRequiredPort = (portMap, portId, connectionId) => {
1627
+ const port = portMap.get(portId);
1628
+ if (!port) {
1629
+ throw new Error(
1630
+ `Port ${portId} not found while deserializing solved route ${connectionId}`
1631
+ );
1632
+ }
1633
+ return port;
1634
+ };
1635
+ var getRequiredRegion = (regionMap, regionId, connectionId) => {
1636
+ const region = regionMap.get(regionId);
1637
+ if (!region) {
1638
+ throw new Error(
1639
+ `Region ${regionId} not found while deserializing solved route ${connectionId}`
1640
+ );
1641
+ }
1642
+ return region;
1643
+ };
1644
+
1645
+ // lib/createBlankHyperGraph.ts
1646
+ var createBlankHyperGraph = (inputGraph) => {
1647
+ const deserializedGraph = convertSerializedHyperGraphToHyperGraph(inputGraph);
1648
+ if (!inputGraph.solvedRoutes) {
1649
+ throw new Error(
1650
+ "createBlankHyperGraph requires graph.solvedRoutes to be present"
1651
+ );
1652
+ }
1653
+ const solvedRoutes = convertSerializedSolvedRoutesToSolvedRoutes(
1654
+ inputGraph.solvedRoutes,
1655
+ deserializedGraph
1656
+ );
1657
+ const removableLeafRegionIds = getRemovableLeafRegionIds(deserializedGraph);
1658
+ const replacedEndpointRegionIds = getReplacedEndpointRegionIds(solvedRoutes);
1659
+ const blankGraph = cloneGraphExcludingRegions(
1660
+ deserializedGraph,
1661
+ removableLeafRegionIds
1662
+ );
1663
+ const connections = [];
1664
+ for (const solvedRoute of solvedRoutes) {
1665
+ const startRegion = getBlankConnectionEndpointRegion({
1666
+ solvedRoute,
1667
+ blankGraph,
1668
+ replacedEndpointRegionIds,
1669
+ endpoint: "start"
1670
+ });
1671
+ const endRegion = getBlankConnectionEndpointRegion({
1672
+ solvedRoute,
1673
+ blankGraph,
1674
+ replacedEndpointRegionIds,
1675
+ endpoint: "end"
1676
+ });
1677
+ connections.push({
1678
+ connectionId: solvedRoute.connection.connectionId,
1679
+ mutuallyConnectedNetworkId: solvedRoute.connection.mutuallyConnectedNetworkId,
1680
+ startRegion,
1681
+ endRegion
1682
+ });
1683
+ }
1684
+ return {
1685
+ ...convertHyperGraphToSerializedHyperGraph(blankGraph),
1686
+ connections: convertConnectionsToSerializedConnections(connections),
1687
+ _sectionCentralRegionId: inputGraph._sectionCentralRegionId,
1688
+ _sectionRouteBindings: inputGraph._sectionRouteBindings ? structuredClone(inputGraph._sectionRouteBindings) : void 0
1689
+ };
1690
+ };
1691
+ var getRemovableLeafRegionIds = (graph) => {
1692
+ return new Set(
1693
+ graph.regions.filter((region) => region.ports.length === 1).map((region) => region.regionId)
1694
+ );
1695
+ };
1696
+ var getReplacedEndpointRegionIds = (solvedRoutes) => {
1697
+ const replacedEndpointRegionIds = /* @__PURE__ */ new Set();
1698
+ for (const solvedRoute of solvedRoutes) {
1699
+ const startCandidate = solvedRoute.path[0];
1700
+ if (startCandidate && shouldReplaceEndpointRegion(
1701
+ solvedRoute.connection.startRegion,
1702
+ startCandidate
1703
+ )) {
1704
+ replacedEndpointRegionIds.add(solvedRoute.connection.startRegion.regionId);
1705
+ }
1706
+ const endCandidate = solvedRoute.path[solvedRoute.path.length - 1];
1707
+ if (endCandidate && shouldReplaceEndpointRegion(
1708
+ solvedRoute.connection.endRegion,
1709
+ endCandidate
1710
+ )) {
1711
+ replacedEndpointRegionIds.add(solvedRoute.connection.endRegion.regionId);
1712
+ }
1713
+ }
1714
+ return replacedEndpointRegionIds;
1715
+ };
1716
+ var shouldReplaceEndpointRegion = (endpointRegion, endpointCandidate) => {
1717
+ return endpointRegion.ports.length === 1 && endpointRegion.ports[0]?.portId === endpointCandidate.port.portId;
1718
+ };
1719
+ var cloneGraphExcludingRegions = (graph, excludedRegionIds) => {
1720
+ const clonedRegionMap = /* @__PURE__ */ new Map();
1721
+ const clonedPorts = [];
1722
+ for (const region of graph.regions) {
1723
+ if (excludedRegionIds.has(region.regionId)) continue;
1724
+ clonedRegionMap.set(region.regionId, {
1725
+ regionId: region.regionId,
1726
+ ports: [],
1727
+ d: region.d ? structuredClone(region.d) : region.d,
1728
+ assignments: []
1729
+ });
1730
+ }
1731
+ for (const port of graph.ports) {
1732
+ if (excludedRegionIds.has(port.region1.regionId) || excludedRegionIds.has(port.region2.regionId)) {
1733
+ continue;
1734
+ }
1735
+ const clonedPort = {
1736
+ portId: port.portId,
1737
+ region1: clonedRegionMap.get(port.region1.regionId),
1738
+ region2: clonedRegionMap.get(port.region2.regionId),
1739
+ d: port.d ? structuredClone(port.d) : port.d
1740
+ };
1741
+ clonedPort.region1.ports.push(clonedPort);
1742
+ clonedPort.region2.ports.push(clonedPort);
1743
+ clonedPorts.push(clonedPort);
1744
+ }
1745
+ return {
1746
+ regions: Array.from(clonedRegionMap.values()),
1747
+ ports: clonedPorts
1748
+ };
1749
+ };
1750
+ var getBlankConnectionEndpointRegion = (input) => {
1751
+ const { solvedRoute, blankGraph, replacedEndpointRegionIds, endpoint } = input;
1752
+ const originalRegion = endpoint === "start" ? solvedRoute.connection.startRegion : solvedRoute.connection.endRegion;
1753
+ const existingRegion = blankGraph.regions.find(
1754
+ (region) => region.regionId === originalRegion.regionId
1755
+ );
1756
+ if (existingRegion) return existingRegion;
1757
+ if (!replacedEndpointRegionIds.has(originalRegion.regionId)) {
1758
+ throw new Error(
1759
+ `Connection endpoint region ${originalRegion.regionId} is missing from blank graph`
1760
+ );
1761
+ }
1762
+ const endpointCandidate = endpoint === "start" ? solvedRoute.path[0] : solvedRoute.path[solvedRoute.path.length - 1];
1763
+ if (!endpointCandidate) {
1764
+ throw new Error(
1765
+ `Solved route ${solvedRoute.connection.connectionId} has no path candidates`
1766
+ );
1767
+ }
1768
+ const attachedRegionId = getAttachedRegionId({
1769
+ port: endpointCandidate.port,
1770
+ originalRegionId: originalRegion.regionId,
1771
+ preferredRegionId: endpoint === "start" ? endpointCandidate.nextRegion?.regionId : endpointCandidate.lastRegion?.regionId
1772
+ });
1773
+ if (!attachedRegionId) {
1774
+ throw new Error(
1775
+ `Could not determine ${endpoint} region for connection ${solvedRoute.connection.connectionId}`
1776
+ );
1777
+ }
1778
+ const attachedRegion = blankGraph.regions.find(
1779
+ (region) => region.regionId === attachedRegionId
1780
+ );
1781
+ if (!attachedRegion) {
1782
+ throw new Error(
1783
+ `Region ${attachedRegionId} not found in blank graph for connection ${solvedRoute.connection.connectionId}`
1784
+ );
1785
+ }
1786
+ const connectionRegion = {
1787
+ regionId: `connection:${solvedRoute.connection.connectionId}:${endpoint}`,
1788
+ ports: [],
1789
+ d: originalRegion.d ? structuredClone(originalRegion.d) : originalRegion.d,
1790
+ assignments: []
1791
+ };
1792
+ blankGraph.regions.push(connectionRegion);
1793
+ const connectionPort = {
1794
+ portId: `connection:${solvedRoute.connection.connectionId}:${endpoint}-port`,
1795
+ region1: connectionRegion,
1796
+ region2: attachedRegion,
1797
+ d: endpointCandidate.port.d ? structuredClone(endpointCandidate.port.d) : endpointCandidate.port.d
1798
+ };
1799
+ connectionRegion.ports.push(connectionPort);
1800
+ attachedRegion.ports.push(connectionPort);
1801
+ blankGraph.ports.push(connectionPort);
1802
+ return connectionRegion;
1803
+ };
1804
+ var getAttachedRegionId = (input) => {
1805
+ const { port, originalRegionId, preferredRegionId } = input;
1806
+ if (preferredRegionId && preferredRegionId !== originalRegionId) {
1807
+ return preferredRegionId;
1808
+ }
1809
+ if (port.region1.regionId !== originalRegionId) return port.region1.regionId;
1810
+ if (port.region2.regionId !== originalRegionId) return port.region2.regionId;
1811
+ return void 0;
1812
+ };
1813
+
1500
1814
  // lib/HyperGraphSectionOptimizer/getOrCreateBoundaryRegion.ts
1501
1815
  var getOrCreateBoundaryRegion = ({
1502
1816
  port,
@@ -1656,14 +1970,30 @@ var getSectionOfHyperGraphAsHyperGraph = (input) => {
1656
1970
  };
1657
1971
  const sectionRoutes = [];
1658
1972
  const sectionConnections = [];
1973
+ const sectionRouteSegments = [];
1974
+ for (const solvedRoute of solvedRoutes) {
1975
+ const routePathSegment = getRouteSectionSpan(solvedRoute, sectionRegionIds);
1976
+ if (!routePathSegment) continue;
1977
+ const startCandidate = solvedRoute.path[routePathSegment.startIndex];
1978
+ const endCandidate = solvedRoute.path[routePathSegment.endIndex];
1979
+ sectionRouteSegments.push({
1980
+ solvedRoute,
1981
+ solvedPathStartIndex: routePathSegment.startIndex,
1982
+ solvedPathEndIndex: routePathSegment.endIndex,
1983
+ startCandidate,
1984
+ endCandidate
1985
+ });
1986
+ }
1659
1987
  const sectionRegionMap = new Map(
1660
1988
  sectionGraph.regions.map((region) => [region.regionId, region])
1661
1989
  );
1662
- for (const solvedRoute of solvedRoutes) {
1663
- const span = getRouteSectionSpan(solvedRoute, sectionRegionIds);
1664
- if (!span) continue;
1665
- const startCandidate = solvedRoute.path[span.startIndex];
1666
- const endCandidate = solvedRoute.path[span.endIndex];
1990
+ for (const {
1991
+ solvedRoute,
1992
+ solvedPathStartIndex,
1993
+ solvedPathEndIndex,
1994
+ startCandidate,
1995
+ endCandidate
1996
+ } of sectionRouteSegments) {
1667
1997
  let startRegionId;
1668
1998
  let startRegion;
1669
1999
  if (sectionRegionIds.has(solvedRoute.connection.startRegion.regionId)) {
@@ -1707,213 +2037,71 @@ var getSectionOfHyperGraphAsHyperGraph = (input) => {
1707
2037
  console.error(
1708
2038
  ` Original startRegion: ${solvedRoute.connection.startRegion.regionId}`
1709
2039
  );
1710
- console.error(` startCandidate port: ${startCandidate.port.portId}`);
1711
- console.error(
1712
- ` Available regions in sectionRegionMap:`,
1713
- Array.from(sectionRegionMap.keys())
1714
- );
1715
- throw new Error(
1716
- `startRegion ${startRegionId} not found in sectionRegionMap`
1717
- );
1718
- }
1719
- if (!endRegion) {
1720
- console.error(
1721
- `[getSectionOfHyperGraphAsHyperGraph] CRITICAL ERROR: endRegion not found!`
1722
- );
1723
- console.error(` Looking for: ${endRegionId}`);
1724
- console.error(
1725
- ` Route connection: ${solvedRoute.connection.connectionId}`
1726
- );
1727
- console.error(
1728
- ` Original endRegion: ${solvedRoute.connection.endRegion.regionId}`
1729
- );
1730
- console.error(` endCandidate port: ${endCandidate.port.portId}`);
1731
- console.error(
1732
- ` Available regions in sectionRegionMap:`,
1733
- Array.from(sectionRegionMap.keys())
1734
- );
1735
- throw new Error(`endRegion ${endRegionId} not found in sectionRegionMap`);
1736
- }
1737
- const sectionConnection = {
1738
- connectionId: solvedRoute.connection.connectionId,
1739
- mutuallyConnectedNetworkId: solvedRoute.connection.mutuallyConnectedNetworkId,
1740
- startRegion,
1741
- endRegion
1742
- };
1743
- const rawPath = solvedRoute.path.slice(span.startIndex, span.endIndex + 1);
1744
- const sectionRouteBase = {
1745
- globalRoute: solvedRoute,
1746
- globalConnection: solvedRoute.connection,
1747
- sectionConnection,
1748
- sectionStartIndex: span.startIndex,
1749
- sectionEndIndex: span.endIndex
1750
- };
1751
- sectionRoutes.push({
1752
- ...sectionRouteBase,
1753
- canRemainFixedInSectionSolve: rawPath.every(
1754
- (candidate) => sectionGraph.ports.some(
1755
- (port) => port.portId === candidate.port.portId
1756
- )
1757
- ),
1758
- sectionRoute: sliceSolvedRouteIntoLocalSection({
1759
- sectionRoute: sectionRouteBase,
1760
- graph: sectionGraph
1761
- })
1762
- });
1763
- sectionConnections.push(sectionConnection);
1764
- }
1765
- return {
1766
- centralRegionId: centralRegion.regionId,
1767
- sectionRegionIds,
1768
- graph: sectionGraph,
1769
- connections: sectionConnections,
1770
- sectionRoutes
1771
- };
1772
- };
1773
-
1774
- // lib/convertSerializedHyperGraphToHyperGraph.ts
1775
- var convertSerializedHyperGraphToHyperGraph = (inputGraph) => {
1776
- if (inputGraph.ports.length > 0 && "region1" in inputGraph.ports[0] && typeof inputGraph.ports[0].region1 === "object") {
1777
- return inputGraph;
1778
- }
1779
- const portMap = /* @__PURE__ */ new Map();
1780
- const regionMap = /* @__PURE__ */ new Map();
1781
- for (const region of inputGraph.regions) {
1782
- const { assignments: _, ...regionWithoutAssignments } = region;
1783
- regionMap.set(region.regionId, {
1784
- ...regionWithoutAssignments,
1785
- d: regionWithoutAssignments.d ? structuredClone(regionWithoutAssignments.d) : regionWithoutAssignments.d,
1786
- ports: [],
1787
- assignments: void 0
1788
- });
1789
- }
1790
- for (const port of inputGraph.ports) {
1791
- const region1 = regionMap.get(port.region1Id ?? port.region1?.regionId);
1792
- const region2 = regionMap.get(port.region2Id ?? port.region2?.regionId);
1793
- const deserializedPort = {
1794
- portId: port.portId,
1795
- region1,
1796
- region2,
1797
- d: port.d
1798
- };
1799
- portMap.set(port.portId, deserializedPort);
1800
- region1.ports.push(deserializedPort);
1801
- region2.ports.push(deserializedPort);
1802
- }
1803
- return {
1804
- ports: Array.from(portMap.values()),
1805
- regions: Array.from(regionMap.values())
1806
- };
1807
- };
1808
-
1809
- // lib/convertSerializedConnectionsToConnections.ts
1810
- var convertSerializedConnectionsToConnections = (inputConnections, graph) => {
1811
- const connections = [];
1812
- for (const inputConn of inputConnections) {
1813
- if ("startRegionId" in inputConn) {
1814
- connections.push({
1815
- connectionId: inputConn.connectionId,
1816
- mutuallyConnectedNetworkId: inputConn.mutuallyConnectedNetworkId ?? inputConn.connectionId,
1817
- startRegion: graph.regions.find(
1818
- (region) => region.regionId === inputConn.startRegionId
1819
- ),
1820
- endRegion: graph.regions.find(
1821
- (region) => region.regionId === inputConn.endRegionId
1822
- )
1823
- });
1824
- } else {
1825
- connections.push(inputConn);
1826
- }
1827
- }
1828
- return connections;
1829
- };
1830
-
1831
- // lib/convertSerializedSolvedRoutesToSolvedRoutes.ts
1832
- var convertSerializedSolvedRoutesToSolvedRoutes = (inputSolvedRoutes, graph) => {
1833
- const portMap = new Map(graph.ports.map((port) => [port.portId, port]));
1834
- const regionMap = new Map(
1835
- graph.regions.map((region) => [region.regionId, region])
1836
- );
1837
- const connectionMap = new Map(
1838
- convertSerializedConnectionsToConnections(
1839
- inputSolvedRoutes.map((route) => route.connection),
1840
- graph
1841
- ).map((connection) => [connection.connectionId, connection])
1842
- );
1843
- return inputSolvedRoutes.map((inputSolvedRoute) => {
1844
- const path = [];
1845
- for (const originalCandidate of inputSolvedRoute.path) {
1846
- const port = portMap.get(originalCandidate.portId);
1847
- if (!port) {
1848
- throw new Error(
1849
- `Port ${originalCandidate.portId} not found while deserializing solved route ${inputSolvedRoute.connection.connectionId}`
1850
- );
1851
- }
1852
- const candidate = {
1853
- port,
1854
- g: originalCandidate.g,
1855
- h: originalCandidate.h,
1856
- f: originalCandidate.f,
1857
- hops: originalCandidate.hops,
1858
- ripRequired: originalCandidate.ripRequired
1859
- };
1860
- if (originalCandidate.lastPortId) {
1861
- candidate.lastPort = getRequiredPort(
1862
- portMap,
1863
- originalCandidate.lastPortId,
1864
- inputSolvedRoute.connection.connectionId
1865
- );
1866
- }
1867
- if (originalCandidate.lastRegionId) {
1868
- candidate.lastRegion = getRequiredRegion(
1869
- regionMap,
1870
- originalCandidate.lastRegionId,
1871
- inputSolvedRoute.connection.connectionId
1872
- );
1873
- }
1874
- if (originalCandidate.nextRegionId) {
1875
- candidate.nextRegion = getRequiredRegion(
1876
- regionMap,
1877
- originalCandidate.nextRegionId,
1878
- inputSolvedRoute.connection.connectionId
1879
- );
1880
- }
1881
- const parent = path[path.length - 1];
1882
- if (parent) candidate.parent = parent;
1883
- path.push(candidate);
1884
- }
1885
- const connection = connectionMap.get(
1886
- inputSolvedRoute.connection.connectionId
1887
- );
1888
- if (!connection) {
2040
+ console.error(` startCandidate port: ${startCandidate.port.portId}`);
2041
+ console.error(
2042
+ ` Available regions in sectionRegionMap:`,
2043
+ Array.from(sectionRegionMap.keys())
2044
+ );
1889
2045
  throw new Error(
1890
- `Connection ${inputSolvedRoute.connection.connectionId} not found while deserializing solved route`
2046
+ `startRegion ${startRegionId} not found in sectionRegionMap`
1891
2047
  );
1892
2048
  }
1893
- return {
1894
- path,
1895
- connection,
1896
- requiredRip: inputSolvedRoute.requiredRip
2049
+ if (!endRegion) {
2050
+ console.error(
2051
+ `[getSectionOfHyperGraphAsHyperGraph] CRITICAL ERROR: endRegion not found!`
2052
+ );
2053
+ console.error(` Looking for: ${endRegionId}`);
2054
+ console.error(
2055
+ ` Route connection: ${solvedRoute.connection.connectionId}`
2056
+ );
2057
+ console.error(
2058
+ ` Original endRegion: ${solvedRoute.connection.endRegion.regionId}`
2059
+ );
2060
+ console.error(` endCandidate port: ${endCandidate.port.portId}`);
2061
+ console.error(
2062
+ ` Available regions in sectionRegionMap:`,
2063
+ Array.from(sectionRegionMap.keys())
2064
+ );
2065
+ throw new Error(`endRegion ${endRegionId} not found in sectionRegionMap`);
2066
+ }
2067
+ const sectionConnection = {
2068
+ connectionId: solvedRoute.connection.connectionId,
2069
+ mutuallyConnectedNetworkId: solvedRoute.connection.mutuallyConnectedNetworkId,
2070
+ startRegion,
2071
+ endRegion
1897
2072
  };
1898
- });
1899
- };
1900
- var getRequiredPort = (portMap, portId, connectionId) => {
1901
- const port = portMap.get(portId);
1902
- if (!port) {
1903
- throw new Error(
1904
- `Port ${portId} not found while deserializing solved route ${connectionId}`
1905
- );
1906
- }
1907
- return port;
1908
- };
1909
- var getRequiredRegion = (regionMap, regionId, connectionId) => {
1910
- const region = regionMap.get(regionId);
1911
- if (!region) {
1912
- throw new Error(
1913
- `Region ${regionId} not found while deserializing solved route ${connectionId}`
2073
+ const rawPath = solvedRoute.path.slice(
2074
+ solvedPathStartIndex,
2075
+ solvedPathEndIndex + 1
1914
2076
  );
2077
+ const sectionRouteBase = {
2078
+ globalRoute: solvedRoute,
2079
+ globalConnection: solvedRoute.connection,
2080
+ sectionConnection,
2081
+ sectionStartIndex: solvedPathStartIndex,
2082
+ sectionEndIndex: solvedPathEndIndex
2083
+ };
2084
+ sectionRoutes.push({
2085
+ ...sectionRouteBase,
2086
+ canRemainFixedInSectionSolve: rawPath.every(
2087
+ (candidate) => sectionGraph.ports.some(
2088
+ (port) => port.portId === candidate.port.portId
2089
+ )
2090
+ ),
2091
+ sectionRoute: sliceSolvedRouteIntoLocalSection({
2092
+ sectionRoute: sectionRouteBase,
2093
+ graph: sectionGraph
2094
+ })
2095
+ });
2096
+ sectionConnections.push(sectionConnection);
1915
2097
  }
1916
- return region;
2098
+ return {
2099
+ centralRegionId: centralRegion.regionId,
2100
+ sectionRegionIds,
2101
+ graph: sectionGraph,
2102
+ connections: sectionConnections,
2103
+ sectionRoutes
2104
+ };
1917
2105
  };
1918
2106
 
1919
2107
  // lib/convertSolvedRoutesToSerializedSolvedRoutes.ts
@@ -1948,6 +2136,11 @@ var extractSectionOfHyperGraph = (input) => {
1948
2136
  `Central region ${input.centralRegionId} not found in hypergraph`
1949
2137
  );
1950
2138
  }
2139
+ if (!input.graph.solvedRoutes) {
2140
+ throw new Error(
2141
+ "extractSectionOfHyperGraph requires graph.solvedRoutes to be present"
2142
+ );
2143
+ }
1951
2144
  const solvedRoutes = convertSerializedSolvedRoutesToSolvedRoutes(
1952
2145
  input.graph.solvedRoutes,
1953
2146
  deserializedGraph
@@ -1960,9 +2153,16 @@ var extractSectionOfHyperGraph = (input) => {
1960
2153
  });
1961
2154
  return {
1962
2155
  ...convertHyperGraphToSerializedHyperGraph(section.graph),
2156
+ connections: convertConnectionsToSerializedConnections(section.connections),
1963
2157
  solvedRoutes: convertSolvedRoutesToSerializedSolvedRoutes(
1964
2158
  section.sectionRoutes.map((route) => route.sectionRoute)
1965
- )
2159
+ ),
2160
+ _sectionCentralRegionId: section.centralRegionId,
2161
+ _sectionRouteBindings: section.sectionRoutes.map((route) => ({
2162
+ connectionId: route.globalConnection.connectionId,
2163
+ solvedPathStartIndex: route.sectionStartIndex,
2164
+ solvedPathEndIndex: route.sectionEndIndex
2165
+ }))
1966
2166
  };
1967
2167
  };
1968
2168
 
@@ -2575,6 +2775,118 @@ var HyperGraphSolver = class extends BaseSolver {
2575
2775
  }
2576
2776
  };
2577
2777
 
2778
+ // lib/reattachSectionToGraph.ts
2779
+ var reattachSectionToGraph = (input) => {
2780
+ const { fullGraph, solvedSectionGraph } = input;
2781
+ if (!fullGraph.solvedRoutes) {
2782
+ throw new Error("reattachSectionToGraph requires fullGraph.solvedRoutes");
2783
+ }
2784
+ if (!solvedSectionGraph.solvedRoutes) {
2785
+ throw new Error(
2786
+ "reattachSectionToGraph requires solvedSectionGraph.solvedRoutes"
2787
+ );
2788
+ }
2789
+ if (!solvedSectionGraph._sectionRouteBindings) {
2790
+ throw new Error(
2791
+ "reattachSectionToGraph requires solvedSectionGraph._sectionRouteBindings"
2792
+ );
2793
+ }
2794
+ const bindingByConnectionId = new Map(
2795
+ solvedSectionGraph._sectionRouteBindings.map((binding) => [
2796
+ binding.connectionId,
2797
+ binding
2798
+ ])
2799
+ );
2800
+ const replacementByConnectionId = new Map(
2801
+ solvedSectionGraph.solvedRoutes.map((route) => [
2802
+ route.connection.connectionId,
2803
+ route
2804
+ ])
2805
+ );
2806
+ return {
2807
+ ...fullGraph,
2808
+ solvedRoutes: fullGraph.solvedRoutes.map((fullSolvedRoute) => {
2809
+ const binding = bindingByConnectionId.get(
2810
+ fullSolvedRoute.connection.connectionId
2811
+ );
2812
+ if (!binding) return fullSolvedRoute;
2813
+ const replacementSolvedRoute = replacementByConnectionId.get(
2814
+ fullSolvedRoute.connection.connectionId
2815
+ );
2816
+ if (!replacementSolvedRoute) return fullSolvedRoute;
2817
+ const replacementInteriorPath = replacementSolvedRoute.path.slice(1, -1);
2818
+ const replacementPath = binding.solvedPathStartIndex === binding.solvedPathEndIndex ? [fullSolvedRoute.path[binding.solvedPathStartIndex]] : [
2819
+ fullSolvedRoute.path[binding.solvedPathStartIndex],
2820
+ ...replacementInteriorPath.map((candidate) => ({
2821
+ ...candidate
2822
+ })),
2823
+ fullSolvedRoute.path[binding.solvedPathEndIndex]
2824
+ ];
2825
+ return {
2826
+ connection: fullSolvedRoute.connection,
2827
+ requiredRip: fullSolvedRoute.requiredRip || replacementSolvedRoute.requiredRip,
2828
+ path: normalizeSerializedPath(
2829
+ [
2830
+ ...fullSolvedRoute.path.slice(0, binding.solvedPathStartIndex),
2831
+ ...replacementPath.map((candidate) => ({
2832
+ ...candidate
2833
+ })),
2834
+ ...fullSolvedRoute.path.slice(binding.solvedPathEndIndex + 1)
2835
+ ],
2836
+ fullGraph
2837
+ )
2838
+ };
2839
+ })
2840
+ };
2841
+ };
2842
+ var normalizeSerializedPath = (path, graph) => {
2843
+ const portMap = new Map(graph.ports.map((port) => [port.portId, port]));
2844
+ return path.map((candidate, index) => {
2845
+ const previousCandidate = index > 0 ? path[index - 1] : void 0;
2846
+ const nextCandidate = index < path.length - 1 ? path[index + 1] : void 0;
2847
+ return {
2848
+ ...candidate,
2849
+ hops: index,
2850
+ lastPortId: previousCandidate?.portId,
2851
+ lastRegionId: previousCandidate ? getSharedRegionId(portMap, previousCandidate.portId, candidate.portId) : void 0,
2852
+ nextRegionId: nextCandidate ? getSharedRegionId(portMap, candidate.portId, nextCandidate.portId) : void 0
2853
+ };
2854
+ });
2855
+ };
2856
+ var getSharedRegionId = (portMap, firstPortId, secondPortId) => {
2857
+ const firstPort = portMap.get(firstPortId);
2858
+ const secondPort = portMap.get(secondPortId);
2859
+ if (!firstPort || !secondPort) return void 0;
2860
+ const firstRegionIds = [firstPort.region1Id, firstPort.region2Id];
2861
+ return firstRegionIds.find(
2862
+ (regionId) => regionId === secondPort.region1Id || regionId === secondPort.region2Id
2863
+ );
2864
+ };
2865
+
2866
+ // lib/pruneDeadEndPorts.ts
2867
+ var pruneDeadEndPorts = (graph, retainedPortIds = []) => {
2868
+ const retainedPortIdSet = new Set(retainedPortIds);
2869
+ const regionPortCounts = new Map(
2870
+ graph.regions.map((region) => [region.regionId, region.ports.length])
2871
+ );
2872
+ const nextPorts = [];
2873
+ for (const region of graph.regions) {
2874
+ region.ports = [];
2875
+ }
2876
+ for (const port of graph.ports) {
2877
+ const isDeadEndPort = regionPortCounts.get(port.region1.regionId) === 1 || regionPortCounts.get(port.region2.regionId) === 1;
2878
+ if (isDeadEndPort && !retainedPortIdSet.has(port.portId)) {
2879
+ continue;
2880
+ }
2881
+ nextPorts.push(port);
2882
+ port.region1.ports.push(port);
2883
+ port.region2.ports.push(port);
2884
+ }
2885
+ graph.ports = nextPorts;
2886
+ graph.regions = graph.regions.filter((region) => region.ports.length > 0);
2887
+ return graph;
2888
+ };
2889
+
2578
2890
  // lib/HyperGraphSectionOptimizer/HyperGraphSectionOptimizer.ts
2579
2891
  import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
2580
2892
 
@@ -3380,6 +3692,318 @@ var HyperGraphSectionOptimizer = class extends BaseSolver2 {
3380
3692
  }
3381
3693
  };
3382
3694
 
3695
+ // lib/HyperGraphSectionOptimizer/HyperGraphSectionOptimizer2.ts
3696
+ import { BaseSolver as BaseSolver3 } from "@tscircuit/solver-utils";
3697
+ var HyperGraphSectionOptimizer2 = class extends BaseSolver3 {
3698
+ config;
3699
+ rootSolver;
3700
+ graph;
3701
+ connections;
3702
+ solvedRoutes;
3703
+ activeAttempt = null;
3704
+ targetRegionAttemptCounts = /* @__PURE__ */ new Map();
3705
+ attemptedSectionCount = 0;
3706
+ constructor(input) {
3707
+ super();
3708
+ this.config = normalizeInput(input);
3709
+ this.rootSolver = this.createHyperGraphSolver({
3710
+ inputGraph: this.config.inputGraph,
3711
+ inputConnections: this.config.inputConnections,
3712
+ inputSolvedRoutes: this.config.inputSolvedRoutes
3713
+ });
3714
+ this.graph = this.rootSolver.graph;
3715
+ this.connections = this.rootSolver.connections;
3716
+ this.solvedRoutes = this.rootSolver.solvedRoutes;
3717
+ this.MAX_ITERATIONS = Math.ceil(this.MAX_ITERATIONS * this.config.effort);
3718
+ }
3719
+ getSolverName() {
3720
+ return "HyperGraphSectionOptimizer2";
3721
+ }
3722
+ getConstructorParams() {
3723
+ return {
3724
+ inputGraph: convertHyperGraphToSerializedHyperGraph(this.graph),
3725
+ inputConnections: convertConnectionsToSerializedConnections(
3726
+ this.connections
3727
+ ),
3728
+ inputSolvedRoutes: convertSolvedRoutesToSerializedSolvedRoutes(
3729
+ this.solvedRoutes
3730
+ ),
3731
+ sectionExpansionHops: this.config.sectionExpansionHops,
3732
+ maxTargetRegionAttempts: this.config.maxTargetRegionAttempts,
3733
+ maxSectionAttempts: this.config.maxSectionAttempts,
3734
+ minCentralRegionCost: this.config.minCentralRegionCost,
3735
+ effort: this.config.effort
3736
+ };
3737
+ }
3738
+ getOutput() {
3739
+ return this.solvedRoutes;
3740
+ }
3741
+ _setup() {
3742
+ this.startNextSectionAttempt();
3743
+ }
3744
+ visualize() {
3745
+ if (this.activeSubSolver) {
3746
+ return this.activeSubSolver.visualize();
3747
+ }
3748
+ return {
3749
+ title: "HyperGraphSectionOptimizer2",
3750
+ points: [],
3751
+ lines: [],
3752
+ rects: [],
3753
+ circles: [],
3754
+ texts: [],
3755
+ polygons: [],
3756
+ arrows: []
3757
+ };
3758
+ }
3759
+ createHyperGraphSolver(input) {
3760
+ const graph = convertSerializedHyperGraphToHyperGraph(input.inputGraph);
3761
+ return new HyperGraphSolver({
3762
+ inputGraph: graph,
3763
+ inputConnections: input.inputConnections,
3764
+ inputSolvedRoutes: convertSerializedSolvedRoutesToSolvedRoutes(
3765
+ input.inputSolvedRoutes,
3766
+ graph
3767
+ )
3768
+ });
3769
+ }
3770
+ getCostOfCentralRegion(region) {
3771
+ const attempts = this.targetRegionAttemptCounts.get(region.regionId) ?? 0;
3772
+ return this.getRegionSolutionCost(this.rootSolver, region) + attempts * 1e4;
3773
+ }
3774
+ getSectionCost(input) {
3775
+ const sectionSolver = this.createHyperGraphSolver({
3776
+ inputGraph: input.solvedGraph,
3777
+ inputConnections: input.solvedGraph.connections ?? [],
3778
+ inputSolvedRoutes: input.solvedGraph.solvedRoutes ?? []
3779
+ });
3780
+ let totalCost = 0;
3781
+ for (const region of sectionSolver.graph.regions) {
3782
+ if (!input.sectionRegionIds.has(region.regionId)) continue;
3783
+ totalCost += this.getRegionSolutionCost(sectionSolver, region);
3784
+ }
3785
+ return totalCost;
3786
+ }
3787
+ _step() {
3788
+ if (!this.activeSubSolver) {
3789
+ this.startNextSectionAttempt();
3790
+ return;
3791
+ }
3792
+ this.activeSubSolver.step();
3793
+ if (!this.activeAttempt) return;
3794
+ if (this.activeSubSolver.failed) {
3795
+ this.rejectActiveAttempt();
3796
+ return;
3797
+ }
3798
+ if (!this.activeSubSolver.solved) return;
3799
+ const solvedBlankSection = {
3800
+ ...this.activeAttempt.blankSectionProblem,
3801
+ solvedRoutes: convertSolvedRoutesToSerializedSolvedRoutes(
3802
+ this.activeSubSolver.solvedRoutes
3803
+ )
3804
+ };
3805
+ const mergedGraph = reattachSectionToGraph({
3806
+ fullGraph: this.activeAttempt.fullGraphSnapshot,
3807
+ solvedSectionGraph: solvedBlankSection
3808
+ });
3809
+ const mergedSectionCost = this.getSectionCost({
3810
+ solvedGraph: mergedGraph,
3811
+ sectionRegionIds: this.activeAttempt.sectionRegionIds
3812
+ });
3813
+ if (mergedSectionCost < this.activeAttempt.currentSectionCost) {
3814
+ this.acceptMergedGraph(mergedGraph);
3815
+ return;
3816
+ }
3817
+ this.rejectActiveAttempt();
3818
+ }
3819
+ startNextSectionAttempt() {
3820
+ if (this.attemptedSectionCount >= this.config.maxSectionAttempts) {
3821
+ this.solved = true;
3822
+ return;
3823
+ }
3824
+ const targetRegion = this.selectTargetRegion();
3825
+ if (!targetRegion) {
3826
+ this.solved = true;
3827
+ return;
3828
+ }
3829
+ this.attemptedSectionCount += 1;
3830
+ const nextAttempt = this.createSectionSolveAttempt(targetRegion);
3831
+ if (!nextAttempt) {
3832
+ this.bumpTargetRegionAttemptCount(targetRegion.regionId);
3833
+ return;
3834
+ }
3835
+ this.activeAttempt = nextAttempt;
3836
+ this.activeSubSolver = this.createHyperGraphSolver({
3837
+ inputGraph: nextAttempt.blankSectionProblem,
3838
+ inputConnections: nextAttempt.blankSectionProblem.connections ?? [],
3839
+ inputSolvedRoutes: []
3840
+ });
3841
+ }
3842
+ selectTargetRegion() {
3843
+ let bestRegion = null;
3844
+ let bestCost = Infinity;
3845
+ for (const region of this.graph.regions) {
3846
+ if ((region.assignments?.length ?? 0) === 0) continue;
3847
+ if ((this.targetRegionAttemptCounts.get(region.regionId) ?? 0) >= this.config.maxTargetRegionAttempts) {
3848
+ continue;
3849
+ }
3850
+ const cost = this.getCostOfCentralRegion(region);
3851
+ if (cost <= this.config.minCentralRegionCost) continue;
3852
+ if (cost >= bestCost) continue;
3853
+ bestCost = cost;
3854
+ bestRegion = region;
3855
+ }
3856
+ return bestRegion;
3857
+ }
3858
+ createSectionSolveAttempt(targetRegion) {
3859
+ const fullGraphSnapshot = this.serializeSolvedGraph();
3860
+ const extractedSection = extractSectionOfHyperGraph({
3861
+ graph: fullGraphSnapshot,
3862
+ centralRegionId: targetRegion.regionId,
3863
+ expansionHopsFromCentralRegion: this.config.sectionExpansionHops
3864
+ });
3865
+ const prunedSection = this.pruneSectionForBlanking(extractedSection);
3866
+ if ((prunedSection.connections?.length ?? 0) === 0) {
3867
+ return null;
3868
+ }
3869
+ const sectionRegionIds = this.getSectionRegionIds(extractedSection);
3870
+ return {
3871
+ targetRegionId: targetRegion.regionId,
3872
+ sectionRegionIds,
3873
+ fullGraphSnapshot,
3874
+ blankSectionProblem: createBlankHyperGraph(prunedSection),
3875
+ currentSectionCost: this.getSectionCost({
3876
+ solvedGraph: fullGraphSnapshot,
3877
+ sectionRegionIds
3878
+ })
3879
+ };
3880
+ }
3881
+ acceptMergedGraph(mergedGraph) {
3882
+ this.rootSolver.solvedRoutes = commitSolvedRoutes({
3883
+ graph: this.rootSolver.graph,
3884
+ connections: this.rootSolver.connections,
3885
+ solvedRoutes: this.deserializeSolvedRoutes(mergedGraph)
3886
+ });
3887
+ this.solvedRoutes = this.rootSolver.solvedRoutes;
3888
+ this.graph = this.rootSolver.graph;
3889
+ this.connections = this.rootSolver.connections;
3890
+ for (const regionId of this.activeAttempt?.sectionRegionIds ?? []) {
3891
+ this.targetRegionAttemptCounts.set(regionId, 0);
3892
+ }
3893
+ this.clearActiveAttempt();
3894
+ }
3895
+ rejectActiveAttempt() {
3896
+ if (this.activeSubSolver?.failed) {
3897
+ this.failedSubSolvers ??= [];
3898
+ this.failedSubSolvers.push(this.activeSubSolver);
3899
+ }
3900
+ if (this.activeAttempt) {
3901
+ this.bumpTargetRegionAttemptCount(this.activeAttempt.targetRegionId);
3902
+ }
3903
+ this.clearActiveAttempt();
3904
+ }
3905
+ clearActiveAttempt() {
3906
+ this.activeSubSolver = null;
3907
+ this.activeAttempt = null;
3908
+ }
3909
+ bumpTargetRegionAttemptCount(regionId) {
3910
+ this.targetRegionAttemptCounts.set(
3911
+ regionId,
3912
+ (this.targetRegionAttemptCounts.get(regionId) ?? 0) + 1
3913
+ );
3914
+ }
3915
+ serializeSolvedGraph() {
3916
+ return {
3917
+ ...convertHyperGraphToSerializedHyperGraph(this.graph),
3918
+ connections: convertConnectionsToSerializedConnections(this.connections),
3919
+ solvedRoutes: convertSolvedRoutesToSerializedSolvedRoutes(
3920
+ this.solvedRoutes
3921
+ )
3922
+ };
3923
+ }
3924
+ deserializeSolvedRoutes(graph) {
3925
+ if (!graph.solvedRoutes) return [];
3926
+ return convertSerializedSolvedRoutesToSolvedRoutes(
3927
+ graph.solvedRoutes,
3928
+ convertSerializedHyperGraphToHyperGraph(graph)
3929
+ );
3930
+ }
3931
+ getSectionRegionIds(sectionGraph) {
3932
+ const fullRegionIds = new Set(
3933
+ this.graph.regions.map((region) => region.regionId)
3934
+ );
3935
+ return new Set(
3936
+ sectionGraph.regions.map((region) => region.regionId).filter((regionId) => fullRegionIds.has(regionId))
3937
+ );
3938
+ }
3939
+ pruneSectionForBlanking(extractedSection) {
3940
+ const mutableSectionGraph = convertSerializedHyperGraphToHyperGraph(extractedSection);
3941
+ const retainedPortIds = extractedSection.solvedRoutes?.flatMap((solvedRoute) => {
3942
+ const firstPortId = solvedRoute.path[0]?.portId;
3943
+ const lastPortId = solvedRoute.path[solvedRoute.path.length - 1]?.portId;
3944
+ return [firstPortId, lastPortId].filter(
3945
+ (portId) => Boolean(portId)
3946
+ );
3947
+ }) ?? [];
3948
+ pruneDeadEndPorts(mutableSectionGraph, retainedPortIds);
3949
+ return {
3950
+ ...convertHyperGraphToSerializedHyperGraph(mutableSectionGraph),
3951
+ connections: extractedSection.connections ? structuredClone(extractedSection.connections) : void 0,
3952
+ solvedRoutes: extractedSection.solvedRoutes ? structuredClone(extractedSection.solvedRoutes) : void 0,
3953
+ _sectionCentralRegionId: extractedSection._sectionCentralRegionId,
3954
+ _sectionRouteBindings: extractedSection._sectionRouteBindings ? structuredClone(extractedSection._sectionRouteBindings) : void 0
3955
+ };
3956
+ }
3957
+ getRegionSolutionCost(solver, region) {
3958
+ const previousConnection = solver.currentConnection;
3959
+ let totalCost = 0;
3960
+ for (const assignment of region.assignments ?? []) {
3961
+ solver.currentConnection = assignment.connection;
3962
+ totalCost += solver.computeIncreasedRegionCostIfPortsAreUsed(
3963
+ region,
3964
+ assignment.regionPort1,
3965
+ assignment.regionPort2
3966
+ );
3967
+ }
3968
+ solver.currentConnection = previousConnection;
3969
+ return totalCost;
3970
+ }
3971
+ };
3972
+ var normalizeInput = (input) => {
3973
+ const inputConnections = input.inputConnections ?? input.inputGraph.connections;
3974
+ const inputSolvedRoutes = input.inputSolvedRoutes ?? input.inputGraph.solvedRoutes;
3975
+ const sectionExpansionHops = input.sectionExpansionHops ?? input.expansionHopsFromCentralRegion;
3976
+ const maxTargetRegionAttempts = input.maxTargetRegionAttempts ?? input.MAX_ATTEMPTS_PER_REGION;
3977
+ if (!inputConnections) {
3978
+ throw new Error("HyperGraphSectionOptimizer2 requires inputConnections");
3979
+ }
3980
+ if (!inputSolvedRoutes) {
3981
+ throw new Error("HyperGraphSectionOptimizer2 requires inputSolvedRoutes");
3982
+ }
3983
+ if (sectionExpansionHops === void 0) {
3984
+ throw new Error("HyperGraphSectionOptimizer2 requires sectionExpansionHops");
3985
+ }
3986
+ if (maxTargetRegionAttempts === void 0) {
3987
+ throw new Error(
3988
+ "HyperGraphSectionOptimizer2 requires maxTargetRegionAttempts"
3989
+ );
3990
+ }
3991
+ return {
3992
+ inputGraph: {
3993
+ ...input.inputGraph,
3994
+ connections: void 0,
3995
+ solvedRoutes: void 0
3996
+ },
3997
+ inputConnections: structuredClone(inputConnections),
3998
+ inputSolvedRoutes: structuredClone(inputSolvedRoutes),
3999
+ sectionExpansionHops,
4000
+ maxTargetRegionAttempts,
4001
+ maxSectionAttempts: input.maxSectionAttempts ?? input.MAX_ATTEMPTS_PER_SECTION ?? 500,
4002
+ minCentralRegionCost: input.minCentralRegionCost ?? input.ACCEPTABLE_CENTRAL_REGION_COST ?? 0,
4003
+ effort: input.effort ?? 1
4004
+ };
4005
+ };
4006
+
3383
4007
  // node_modules/transformation-matrix/src/applyToPoint.js
3384
4008
  function applyToPoint(matrix2, point2) {
3385
4009
  return Array.isArray(point2) ? [
@@ -15305,7 +15929,7 @@ function setStepOfAllObjects(graphics, step) {
15305
15929
  }
15306
15930
  return graphics;
15307
15931
  }
15308
- var BaseSolver3 = class {
15932
+ var BaseSolver4 = class {
15309
15933
  MAX_ITERATIONS = 1e5;
15310
15934
  solved = false;
15311
15935
  failed = false;
@@ -15412,7 +16036,7 @@ function definePipelineStep(solverName, solverClass, getConstructorParams, opts
15412
16036
  onSolved: opts.onSolved
15413
16037
  };
15414
16038
  }
15415
- var BasePipelineSolver = class extends BaseSolver3 {
16039
+ var BasePipelineSolver = class extends BaseSolver4 {
15416
16040
  startTimeOfStage = {};
15417
16041
  endTimeOfStage = {};
15418
16042
  timeSpentOnStage = {};
@@ -15627,7 +16251,7 @@ var clampPointToBounds = (point4, bounds) => ({
15627
16251
  x: Math.min(bounds.maxX, Math.max(bounds.minX, point4.x)),
15628
16252
  y: Math.min(bounds.maxY, Math.max(bounds.minY, point4.y))
15629
16253
  });
15630
- var BuildRegionsSolver = class extends BaseSolver3 {
16254
+ var BuildRegionsSolver = class extends BaseSolver4 {
15631
16255
  input;
15632
16256
  output = null;
15633
16257
  constructor(input) {
@@ -16048,7 +16672,7 @@ var generateBoundaryPointsWithEdges = (params) => {
16048
16672
  hadCrossings: resolved.hadCrossings
16049
16673
  };
16050
16674
  };
16051
- var GeneratePointsSolver = class extends BaseSolver3 {
16675
+ var GeneratePointsSolver = class extends BaseSolver4 {
16052
16676
  input;
16053
16677
  output = null;
16054
16678
  constructor(input) {
@@ -16503,7 +17127,7 @@ var mergeCellsPolyanya = (params) => {
16503
17127
  const depths = liveCells.map(() => 0);
16504
17128
  return { cells: liveCells, depths };
16505
17129
  };
16506
- var MergeCellsSolver = class extends BaseSolver3 {
17130
+ var MergeCellsSolver = class extends BaseSolver4 {
16507
17131
  input;
16508
17132
  output = null;
16509
17133
  constructor(input) {
@@ -16761,7 +17385,7 @@ var filterTris = (params) => {
16761
17385
  return true;
16762
17386
  });
16763
17387
  };
16764
- var TriangulateSolver = class extends BaseSolver3 {
17388
+ var TriangulateSolver = class extends BaseSolver4 {
16765
17389
  input;
16766
17390
  output = null;
16767
17391
  constructor(input) {
@@ -24719,6 +25343,7 @@ function createConvexViaGraphFromXYConnections(xyConnections, viaTileOrProblem,
24719
25343
  export {
24720
25344
  ConnectBuilder,
24721
25345
  HyperGraphSectionOptimizer,
25346
+ HyperGraphSectionOptimizer2,
24722
25347
  HyperGraphSolver,
24723
25348
  JUMPER_GRAPH_SOLVER_DEFAULTS,
24724
25349
  JumperGraphSolver,
@@ -24732,6 +25357,7 @@ export {
24732
25357
  calculateGraphBounds,
24733
25358
  convertConnectionsToSerializedConnections,
24734
25359
  convertHyperGraphToSerializedHyperGraph,
25360
+ createBlankHyperGraph,
24735
25361
  createConvexViaGraphFromXYConnections,
24736
25362
  createGraphWithConnectionsFromBaseGraph,
24737
25363
  createViaGraphWithConnections,
@@ -24741,6 +25367,8 @@ export {
24741
25367
  generateJumperGrid,
24742
25368
  generateJumperX4Grid,
24743
25369
  generateViaTopologyRegions,
25370
+ pruneDeadEndPorts,
25371
+ reattachSectionToGraph,
24744
25372
  recommendViaTileFromGraphInput,
24745
25373
  rotateGraph90Degrees,
24746
25374
  via_tile_4_regions_default as viaTile
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/hypergraph",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.68",
4
+ "version": "0.0.70",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",