@graphty/algorithms 1.3.1 → 1.4.2
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.
- package/README.md +358 -413
- package/dist/algorithms.js +564 -261
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +10 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +27 -6
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +11 -2
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +17 -3
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.d.ts +7 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +9 -6
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +21 -3
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +25 -7
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +28 -2
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.js +30 -4
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.js +11 -4
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
- package/dist/src/algorithms/centrality/hits.d.ts +10 -3
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/hits.js +11 -4
- package/dist/src/algorithms/centrality/hits.js.map +1 -1
- package/dist/src/algorithms/centrality/index.d.ts +1 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +1 -1
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +10 -3
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/katz.js +13 -8
- package/dist/src/algorithms/centrality/katz.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +14 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +16 -1
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +21 -4
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/index.d.ts.map +1 -1
- package/dist/src/algorithms/community/index.js.map +1 -1
- package/dist/src/algorithms/community/label-propagation.d.ts +0 -3
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +13 -6
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +0 -1
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +37 -6
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +37 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.js +42 -7
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -1
- package/dist/src/algorithms/community/louvain.d.ts +2 -3
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain.js +19 -6
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +0 -4
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.js +1 -5
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -1
- package/dist/src/algorithms/components/connected.d.ts +23 -0
- package/dist/src/algorithms/components/connected.d.ts.map +1 -1
- package/dist/src/algorithms/components/connected.js +29 -3
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
- package/dist/src/algorithms/matching/bipartite.js +10 -0
- package/dist/src/algorithms/matching/bipartite.js.map +1 -1
- package/dist/src/algorithms/matching/index.d.ts.map +1 -1
- package/dist/src/algorithms/matching/index.js.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
- package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.js +36 -2
- package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
- package/dist/src/algorithms/mst/index.d.ts.map +1 -1
- package/dist/src/algorithms/mst/index.js.map +1 -1
- package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
- package/dist/src/algorithms/mst/kruskal.js +17 -3
- package/dist/src/algorithms/mst/kruskal.js.map +1 -1
- package/dist/src/algorithms/mst/prim.d.ts +9 -0
- package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
- package/dist/src/algorithms/mst/prim.js +9 -0
- package/dist/src/algorithms/mst/prim.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +11 -4
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +18 -16
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +15 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +15 -0
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
- package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/index.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +13 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +37 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +23 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.js +34 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +1 -1
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +48 -0
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/index.js.map +1 -1
- package/dist/src/benchmark-all-algorithms.d.ts +2 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -1
- package/dist/src/benchmark-all-algorithms.js +32 -3
- package/dist/src/benchmark-all-algorithms.js.map +1 -1
- package/dist/src/clustering/hierarchical.d.ts +8 -1
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +22 -6
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +2 -5
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +14 -12
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/mcl.d.ts +6 -0
- package/dist/src/clustering/mcl.d.ts.map +1 -1
- package/dist/src/clustering/mcl.js +34 -3
- package/dist/src/clustering/mcl.js.map +1 -1
- package/dist/src/clustering/spectral.d.ts +4 -1
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +61 -28
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/core/graph.d.ts +45 -0
- package/dist/src/core/graph.d.ts.map +1 -1
- package/dist/src/core/graph.js +45 -1
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/data-structures/index.d.ts.map +1 -1
- package/dist/src/data-structures/index.js.map +1 -1
- package/dist/src/data-structures/priority-queue.d.ts +18 -1
- package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
- package/dist/src/data-structures/priority-queue.js +26 -5
- package/dist/src/data-structures/priority-queue.js.map +1 -1
- package/dist/src/data-structures/union-find.d.ts +21 -0
- package/dist/src/data-structures/union-find.d.ts.map +1 -1
- package/dist/src/data-structures/union-find.js +21 -0
- package/dist/src/data-structures/union-find.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +4 -2
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +31 -2
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +0 -3
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +17 -4
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
- package/dist/src/link-prediction/adamic-adar.js +36 -10
- package/dist/src/link-prediction/adamic-adar.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +27 -6
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/link-prediction/index.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.d.ts +103 -53
- package/dist/src/optimized/bit-packed.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.js +104 -54
- package/dist/src/optimized/bit-packed.js.map +1 -1
- package/dist/src/optimized/csr-graph.d.ts +87 -24
- package/dist/src/optimized/csr-graph.d.ts.map +1 -1
- package/dist/src/optimized/csr-graph.js +87 -24
- package/dist/src/optimized/csr-graph.js.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.d.ts +37 -16
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.js +39 -20
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
- package/dist/src/optimized/graph-adapter.d.ts +58 -9
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -1
- package/dist/src/optimized/graph-adapter.js +58 -10
- package/dist/src/optimized/graph-adapter.js.map +1 -1
- package/dist/src/optimized/index.d.ts +1 -1
- package/dist/src/optimized/index.d.ts.map +1 -1
- package/dist/src/optimized/index.js +3 -1
- package/dist/src/optimized/index.js.map +1 -1
- package/dist/src/pathfinding/astar.d.ts +25 -9
- package/dist/src/pathfinding/astar.d.ts.map +1 -1
- package/dist/src/pathfinding/astar.js +25 -10
- package/dist/src/pathfinding/astar.js.map +1 -1
- package/dist/src/pathfinding/utils.d.ts +15 -6
- package/dist/src/pathfinding/utils.d.ts.map +1 -1
- package/dist/src/pathfinding/utils.js +19 -7
- package/dist/src/pathfinding/utils.js.map +1 -1
- package/dist/src/research/grsbm.d.ts +0 -1
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +22 -9
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/index.d.ts.map +1 -1
- package/dist/src/research/index.js.map +1 -1
- package/dist/src/research/sync.d.ts +0 -1
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +28 -7
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +0 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +32 -4
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/utils/graph-converters.d.ts +31 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -1
- package/dist/src/utils/graph-converters.js +31 -0
- package/dist/src/utils/graph-converters.js.map +1 -1
- package/dist/src/utils/graph-utilities.d.ts.map +1 -1
- package/dist/src/utils/graph-utilities.js.map +1 -1
- package/dist/src/utils/math-utilities.d.ts +11 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -1
- package/dist/src/utils/math-utilities.js +14 -5
- package/dist/src/utils/math-utilities.js.map +1 -1
- package/dist/src/utils/optimization-helpers.d.ts +9 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -1
- package/dist/src/utils/optimization-helpers.js +12 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -1
- package/dist/src/utils/priorityQueue.d.ts +24 -0
- package/dist/src/utils/priorityQueue.d.ts.map +1 -1
- package/dist/src/utils/priorityQueue.js +34 -4
- package/dist/src/utils/priorityQueue.js.map +1 -1
- package/package.json +49 -45
- package/src/algorithms/centrality/betweenness.ts +34 -17
- package/src/algorithms/centrality/closeness.ts +34 -17
- package/src/algorithms/centrality/degree.ts +15 -19
- package/src/algorithms/centrality/delta-pagerank-simple.ts +36 -28
- package/src/algorithms/centrality/delta-pagerank.ts +34 -11
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -15
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +26 -17
- package/src/algorithms/community/girvan-newman.ts +39 -28
- package/src/algorithms/community/index.ts +6 -7
- package/src/algorithms/community/label-propagation.ts +20 -28
- package/src/algorithms/community/leiden.ts +61 -37
- package/src/algorithms/community/louvain-optimized.ts +49 -20
- package/src/algorithms/community/louvain.ts +35 -25
- package/src/algorithms/community/modularity-utils.ts +5 -17
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +17 -10
- package/src/algorithms/matching/index.ts +4 -4
- package/src/algorithms/matching/isomorphism.ts +53 -23
- package/src/algorithms/mst/index.ts +3 -4
- package/src/algorithms/mst/kruskal.ts +21 -6
- package/src/algorithms/mst/prim.ts +13 -4
- package/src/algorithms/shortest-path/bellman-ford.ts +16 -15
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +28 -28
- package/src/algorithms/shortest-path/dijkstra.ts +23 -17
- package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
- package/src/algorithms/shortest-path/index.ts +5 -5
- package/src/algorithms/traversal/bfs-unified.ts +64 -60
- package/src/algorithms/traversal/bfs-variants.ts +61 -37
- package/src/algorithms/traversal/bfs.ts +1 -7
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +57 -25
- package/src/clustering/hierarchical.ts +40 -34
- package/src/clustering/index.ts +8 -8
- package/src/clustering/k-core.ts +26 -42
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +91 -57
- package/src/core/graph.ts +54 -14
- package/src/data-structures/index.ts +2 -2
- package/src/data-structures/priority-queue.ts +36 -11
- package/src/data-structures/union-find.ts +22 -1
- package/src/flow/ford-fulkerson.ts +62 -35
- package/src/flow/min-cut.ts +43 -45
- package/src/index.ts +3 -2
- package/src/link-prediction/adamic-adar.ts +65 -51
- package/src/link-prediction/common-neighbors.ts +45 -36
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +106 -56
- package/src/optimized/csr-graph.ts +93 -30
- package/src/optimized/direction-optimized-bfs.ts +44 -25
- package/src/optimized/graph-adapter.ts +64 -16
- package/src/optimized/index.ts +7 -9
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +30 -24
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +39 -23
- package/src/research/terahac.ts +54 -20
- package/src/utils/graph-converters.ts +42 -14
- package/src/utils/graph-utilities.ts +11 -37
- package/src/utils/math-utilities.ts +17 -8
- package/src/utils/optimization-helpers.ts +27 -21
- package/src/utils/priorityQueue.ts +40 -6
package/dist/algorithms.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
class Graph {
|
|
2
|
+
/**
|
|
3
|
+
* Creates a new Graph instance.
|
|
4
|
+
* @param config - Configuration options for the graph
|
|
5
|
+
*/
|
|
2
6
|
constructor(config = {}) {
|
|
3
7
|
this.config = {
|
|
4
8
|
directed: false,
|
|
@@ -13,6 +17,8 @@ class Graph {
|
|
|
13
17
|
}
|
|
14
18
|
/**
|
|
15
19
|
* Add a node to the graph
|
|
20
|
+
* @param id - The unique identifier for the node
|
|
21
|
+
* @param data - Optional key-value data to attach to the node
|
|
16
22
|
*/
|
|
17
23
|
addNode(id, data) {
|
|
18
24
|
if (!this.nodeMap.has(id)) {
|
|
@@ -25,6 +31,8 @@ class Graph {
|
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Remove a node from the graph
|
|
34
|
+
* @param id - The unique identifier of the node to remove
|
|
35
|
+
* @returns True if the node was removed, false if it did not exist
|
|
28
36
|
*/
|
|
29
37
|
removeNode(id) {
|
|
30
38
|
if (!this.nodeMap.has(id)) {
|
|
@@ -59,6 +67,10 @@ class Graph {
|
|
|
59
67
|
}
|
|
60
68
|
/**
|
|
61
69
|
* Add an edge to the graph
|
|
70
|
+
* @param source - The source node identifier
|
|
71
|
+
* @param target - The target node identifier
|
|
72
|
+
* @param weight - The weight of the edge (defaults to 1)
|
|
73
|
+
* @param data - Optional key-value data to attach to the edge
|
|
62
74
|
*/
|
|
63
75
|
addEdge(source, target, weight = 1, data) {
|
|
64
76
|
this.addNode(source);
|
|
@@ -92,6 +104,9 @@ class Graph {
|
|
|
92
104
|
}
|
|
93
105
|
/**
|
|
94
106
|
* Remove an edge from the graph
|
|
107
|
+
* @param source - The source node identifier
|
|
108
|
+
* @param target - The target node identifier
|
|
109
|
+
* @returns True if the edge was removed, false if it did not exist
|
|
95
110
|
*/
|
|
96
111
|
removeEdge(source, target) {
|
|
97
112
|
const sourceEdges = this.adjacencyList.get(source);
|
|
@@ -115,12 +130,17 @@ class Graph {
|
|
|
115
130
|
}
|
|
116
131
|
/**
|
|
117
132
|
* Check if a node exists in the graph
|
|
133
|
+
* @param id - The unique identifier of the node to check
|
|
134
|
+
* @returns True if the node exists, false otherwise
|
|
118
135
|
*/
|
|
119
136
|
hasNode(id) {
|
|
120
137
|
return this.nodeMap.has(id);
|
|
121
138
|
}
|
|
122
139
|
/**
|
|
123
140
|
* Check if an edge exists in the graph
|
|
141
|
+
* @param source - The source node identifier
|
|
142
|
+
* @param target - The target node identifier
|
|
143
|
+
* @returns True if the edge exists, false otherwise
|
|
124
144
|
*/
|
|
125
145
|
hasEdge(source, target) {
|
|
126
146
|
const sourceEdges = this.adjacencyList.get(source);
|
|
@@ -128,12 +148,17 @@ class Graph {
|
|
|
128
148
|
}
|
|
129
149
|
/**
|
|
130
150
|
* Get a node by ID
|
|
151
|
+
* @param id - The unique identifier of the node to retrieve
|
|
152
|
+
* @returns The node if found, undefined otherwise
|
|
131
153
|
*/
|
|
132
154
|
getNode(id) {
|
|
133
155
|
return this.nodeMap.get(id);
|
|
134
156
|
}
|
|
135
157
|
/**
|
|
136
158
|
* Get an edge by source and target
|
|
159
|
+
* @param source - The source node identifier
|
|
160
|
+
* @param target - The target node identifier
|
|
161
|
+
* @returns The edge if found, undefined otherwise
|
|
137
162
|
*/
|
|
138
163
|
getEdge(source, target) {
|
|
139
164
|
const sourceEdges = this.adjacencyList.get(source);
|
|
@@ -141,30 +166,35 @@ class Graph {
|
|
|
141
166
|
}
|
|
142
167
|
/**
|
|
143
168
|
* Get the number of nodes in the graph
|
|
169
|
+
* @returns The total count of nodes
|
|
144
170
|
*/
|
|
145
171
|
get nodeCount() {
|
|
146
172
|
return this.nodeMap.size;
|
|
147
173
|
}
|
|
148
174
|
/**
|
|
149
175
|
* Get the number of edges in the graph
|
|
176
|
+
* @returns The total count of edges
|
|
150
177
|
*/
|
|
151
178
|
get totalEdgeCount() {
|
|
152
179
|
return this.edgeCount;
|
|
153
180
|
}
|
|
154
181
|
/**
|
|
155
182
|
* Check if the graph is directed
|
|
183
|
+
* @returns True if the graph is directed, false otherwise
|
|
156
184
|
*/
|
|
157
185
|
get isDirected() {
|
|
158
186
|
return this.config.directed;
|
|
159
187
|
}
|
|
160
188
|
/**
|
|
161
189
|
* Get all nodes in the graph
|
|
190
|
+
* @returns An iterator over all nodes
|
|
162
191
|
*/
|
|
163
192
|
nodes() {
|
|
164
193
|
return this.nodeMap.values();
|
|
165
194
|
}
|
|
166
195
|
/**
|
|
167
196
|
* Get all edges in the graph
|
|
197
|
+
* @yields Each unique edge in the graph
|
|
168
198
|
*/
|
|
169
199
|
*edges() {
|
|
170
200
|
for (const [source, edges] of this.adjacencyList) {
|
|
@@ -178,6 +208,8 @@ class Graph {
|
|
|
178
208
|
}
|
|
179
209
|
/**
|
|
180
210
|
* Get neighbors of a node (outgoing edges)
|
|
211
|
+
* @param nodeId - The node identifier to get neighbors for
|
|
212
|
+
* @returns An iterator over the neighbor node identifiers
|
|
181
213
|
*/
|
|
182
214
|
neighbors(nodeId) {
|
|
183
215
|
const edges = this.adjacencyList.get(nodeId);
|
|
@@ -185,6 +217,8 @@ class Graph {
|
|
|
185
217
|
}
|
|
186
218
|
/**
|
|
187
219
|
* Get incoming neighbors of a node (directed graphs only)
|
|
220
|
+
* @param nodeId - The node identifier to get incoming neighbors for
|
|
221
|
+
* @returns An iterator over the incoming neighbor node identifiers
|
|
188
222
|
*/
|
|
189
223
|
inNeighbors(nodeId) {
|
|
190
224
|
if (!this.config.directed) {
|
|
@@ -195,12 +229,16 @@ class Graph {
|
|
|
195
229
|
}
|
|
196
230
|
/**
|
|
197
231
|
* Get outgoing neighbors of a node
|
|
232
|
+
* @param nodeId - The node identifier to get outgoing neighbors for
|
|
233
|
+
* @returns An iterator over the outgoing neighbor node identifiers
|
|
198
234
|
*/
|
|
199
235
|
outNeighbors(nodeId) {
|
|
200
236
|
return this.neighbors(nodeId);
|
|
201
237
|
}
|
|
202
238
|
/**
|
|
203
239
|
* Get the degree of a node
|
|
240
|
+
* @param nodeId - The node identifier to get the degree for
|
|
241
|
+
* @returns The total degree of the node
|
|
204
242
|
*/
|
|
205
243
|
degree(nodeId) {
|
|
206
244
|
if (this.config.directed) {
|
|
@@ -211,6 +249,8 @@ class Graph {
|
|
|
211
249
|
}
|
|
212
250
|
/**
|
|
213
251
|
* Get the in-degree of a node
|
|
252
|
+
* @param nodeId - The node identifier to get the in-degree for
|
|
253
|
+
* @returns The number of incoming edges to the node
|
|
214
254
|
*/
|
|
215
255
|
inDegree(nodeId) {
|
|
216
256
|
if (!this.config.directed) {
|
|
@@ -221,6 +261,8 @@ class Graph {
|
|
|
221
261
|
}
|
|
222
262
|
/**
|
|
223
263
|
* Get the out-degree of a node
|
|
264
|
+
* @param nodeId - The node identifier to get the out-degree for
|
|
265
|
+
* @returns The number of outgoing edges from the node
|
|
224
266
|
*/
|
|
225
267
|
outDegree(nodeId) {
|
|
226
268
|
const edges = this.adjacencyList.get(nodeId);
|
|
@@ -228,6 +270,7 @@ class Graph {
|
|
|
228
270
|
}
|
|
229
271
|
/**
|
|
230
272
|
* Create a copy of the graph
|
|
273
|
+
* @returns A new Graph instance with the same nodes and edges
|
|
231
274
|
*/
|
|
232
275
|
clone() {
|
|
233
276
|
const cloned = new Graph(this.config);
|
|
@@ -235,17 +278,13 @@ class Graph {
|
|
|
235
278
|
cloned.addNode(node.id, node.data ? { ...node.data } : void 0);
|
|
236
279
|
}
|
|
237
280
|
for (const edge of this.edges()) {
|
|
238
|
-
cloned.addEdge(
|
|
239
|
-
edge.source,
|
|
240
|
-
edge.target,
|
|
241
|
-
edge.weight,
|
|
242
|
-
edge.data ? { ...edge.data } : void 0
|
|
243
|
-
);
|
|
281
|
+
cloned.addEdge(edge.source, edge.target, edge.weight, edge.data ? { ...edge.data } : void 0);
|
|
244
282
|
}
|
|
245
283
|
return cloned;
|
|
246
284
|
}
|
|
247
285
|
/**
|
|
248
286
|
* Get graph configuration
|
|
287
|
+
* @returns A copy of the graph configuration object
|
|
249
288
|
*/
|
|
250
289
|
getConfig() {
|
|
251
290
|
return { ...this.config };
|
|
@@ -262,6 +301,7 @@ class Graph {
|
|
|
262
301
|
/**
|
|
263
302
|
* Get the number of unique edges in the graph
|
|
264
303
|
* For undirected graphs, each edge is counted once
|
|
304
|
+
* @returns The count of unique edges
|
|
265
305
|
*/
|
|
266
306
|
get uniqueEdgeCount() {
|
|
267
307
|
if (this.config.directed) {
|
|
@@ -339,7 +379,7 @@ function requireSparseTypedFastBitSet() {
|
|
|
339
379
|
}
|
|
340
380
|
var SparseTypedFastBitSet$1 = (
|
|
341
381
|
/** @class */
|
|
342
|
-
function() {
|
|
382
|
+
(function() {
|
|
343
383
|
function SparseTypedFastBitSet2(iterable, data) {
|
|
344
384
|
var e_1, _a;
|
|
345
385
|
if (data === void 0) {
|
|
@@ -1520,7 +1560,7 @@ function requireSparseTypedFastBitSet() {
|
|
|
1520
1560
|
}
|
|
1521
1561
|
};
|
|
1522
1562
|
return SparseTypedFastBitSet2;
|
|
1523
|
-
}()
|
|
1563
|
+
})()
|
|
1524
1564
|
);
|
|
1525
1565
|
SparseTypedFastBitSet.SparseTypedFastBitSet = SparseTypedFastBitSet$1;
|
|
1526
1566
|
return SparseTypedFastBitSet;
|
|
@@ -1552,7 +1592,7 @@ function requireTypedFastBitSet() {
|
|
|
1552
1592
|
}
|
|
1553
1593
|
var TypedFastBitSet$1 = (
|
|
1554
1594
|
/** @class */
|
|
1555
|
-
function() {
|
|
1595
|
+
(function() {
|
|
1556
1596
|
function TypedFastBitSet2(iterable, words) {
|
|
1557
1597
|
var e_1, _a;
|
|
1558
1598
|
if (words === void 0) {
|
|
@@ -2044,7 +2084,7 @@ function requireTypedFastBitSet() {
|
|
|
2044
2084
|
return answer;
|
|
2045
2085
|
};
|
|
2046
2086
|
return TypedFastBitSet2;
|
|
2047
|
-
}()
|
|
2087
|
+
})()
|
|
2048
2088
|
);
|
|
2049
2089
|
TypedFastBitSet.TypedFastBitSet = TypedFastBitSet$1;
|
|
2050
2090
|
return TypedFastBitSet;
|
|
@@ -2053,8 +2093,8 @@ var hasRequiredLib;
|
|
|
2053
2093
|
function requireLib() {
|
|
2054
2094
|
if (hasRequiredLib) return lib;
|
|
2055
2095
|
hasRequiredLib = 1;
|
|
2056
|
-
(function(exports) {
|
|
2057
|
-
var __createBinding = lib && lib.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
2096
|
+
(function(exports$1) {
|
|
2097
|
+
var __createBinding = lib && lib.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
2058
2098
|
if (k2 === void 0) k2 = k;
|
|
2059
2099
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2060
2100
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
@@ -2063,21 +2103,25 @@ function requireLib() {
|
|
|
2063
2103
|
} };
|
|
2064
2104
|
}
|
|
2065
2105
|
Object.defineProperty(o, k2, desc);
|
|
2066
|
-
} : function(o, m, k, k2) {
|
|
2106
|
+
}) : (function(o, m, k, k2) {
|
|
2067
2107
|
if (k2 === void 0) k2 = k;
|
|
2068
2108
|
o[k2] = m[k];
|
|
2069
|
-
});
|
|
2070
|
-
var __exportStar = lib && lib.__exportStar || function(m,
|
|
2071
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(
|
|
2109
|
+
}));
|
|
2110
|
+
var __exportStar = lib && lib.__exportStar || function(m, exports$12) {
|
|
2111
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$12, p)) __createBinding(exports$12, m, p);
|
|
2072
2112
|
};
|
|
2073
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2074
|
-
__exportStar(requireSparseTypedFastBitSet(), exports);
|
|
2075
|
-
__exportStar(requireTypedFastBitSet(), exports);
|
|
2113
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
2114
|
+
__exportStar(requireSparseTypedFastBitSet(), exports$1);
|
|
2115
|
+
__exportStar(requireTypedFastBitSet(), exports$1);
|
|
2076
2116
|
})(lib);
|
|
2077
2117
|
return lib;
|
|
2078
2118
|
}
|
|
2079
2119
|
var libExports = requireLib();
|
|
2080
2120
|
class GraphBitSet {
|
|
2121
|
+
/**
|
|
2122
|
+
* Creates a new GraphBitSet with optional pre-allocated capacity.
|
|
2123
|
+
* @param capacity - The initial capacity to pre-allocate for the bitset
|
|
2124
|
+
*/
|
|
2081
2125
|
constructor(capacity) {
|
|
2082
2126
|
this._cardinality = 0;
|
|
2083
2127
|
this.bitset = new libExports.TypedFastBitSet();
|
|
@@ -2086,8 +2130,9 @@ class GraphBitSet {
|
|
|
2086
2130
|
}
|
|
2087
2131
|
}
|
|
2088
2132
|
/**
|
|
2089
|
-
|
|
2090
|
-
|
|
2133
|
+
* Add a single element to the set.
|
|
2134
|
+
* @param index - The element index to add
|
|
2135
|
+
*/
|
|
2091
2136
|
add(index) {
|
|
2092
2137
|
if (!this.bitset.has(index)) {
|
|
2093
2138
|
this.bitset.add(index);
|
|
@@ -2095,8 +2140,9 @@ class GraphBitSet {
|
|
|
2095
2140
|
}
|
|
2096
2141
|
}
|
|
2097
2142
|
/**
|
|
2098
|
-
|
|
2099
|
-
|
|
2143
|
+
* Remove a single element from the set.
|
|
2144
|
+
* @param index - The element index to remove
|
|
2145
|
+
*/
|
|
2100
2146
|
remove(index) {
|
|
2101
2147
|
if (this.bitset.has(index)) {
|
|
2102
2148
|
this.bitset.remove(index);
|
|
@@ -2104,27 +2150,32 @@ class GraphBitSet {
|
|
|
2104
2150
|
}
|
|
2105
2151
|
}
|
|
2106
2152
|
/**
|
|
2107
|
-
|
|
2108
|
-
|
|
2153
|
+
* Check if an element exists in the set.
|
|
2154
|
+
* @param index - The element index to check
|
|
2155
|
+
* @returns True if the element exists, false otherwise
|
|
2156
|
+
*/
|
|
2109
2157
|
has(index) {
|
|
2110
2158
|
return this.bitset.has(index);
|
|
2111
2159
|
}
|
|
2112
2160
|
/**
|
|
2113
|
-
|
|
2114
|
-
|
|
2161
|
+
* Clear all elements
|
|
2162
|
+
*/
|
|
2115
2163
|
clear() {
|
|
2116
2164
|
this.bitset.clear();
|
|
2117
2165
|
this._cardinality = 0;
|
|
2118
2166
|
}
|
|
2119
2167
|
/**
|
|
2120
|
-
|
|
2121
|
-
|
|
2168
|
+
* Check if the set is empty.
|
|
2169
|
+
* @returns True if the set contains no elements, false otherwise
|
|
2170
|
+
*/
|
|
2122
2171
|
isEmpty() {
|
|
2123
2172
|
return this._cardinality === 0;
|
|
2124
2173
|
}
|
|
2125
2174
|
/**
|
|
2126
|
-
|
|
2127
|
-
|
|
2175
|
+
* Optimized for graph algorithms - add a range of indices.
|
|
2176
|
+
* @param start - The starting index (inclusive)
|
|
2177
|
+
* @param end - The ending index (exclusive)
|
|
2178
|
+
*/
|
|
2128
2179
|
addRange(start, end) {
|
|
2129
2180
|
for (let i = start; i < end; i++) {
|
|
2130
2181
|
if (!this.bitset.has(i)) {
|
|
@@ -2134,39 +2185,51 @@ class GraphBitSet {
|
|
|
2134
2185
|
}
|
|
2135
2186
|
}
|
|
2136
2187
|
/**
|
|
2137
|
-
|
|
2138
|
-
|
|
2188
|
+
* Fast cardinality tracking - get the number of elements in the set.
|
|
2189
|
+
* @returns The number of elements in the set
|
|
2190
|
+
*/
|
|
2139
2191
|
size() {
|
|
2140
2192
|
return this._cardinality;
|
|
2141
2193
|
}
|
|
2142
2194
|
/**
|
|
2143
|
-
|
|
2144
|
-
|
|
2195
|
+
* Batch operations for frontier management - swap contents with another GraphBitSet.
|
|
2196
|
+
* @param other - The GraphBitSet to swap contents with
|
|
2197
|
+
*/
|
|
2145
2198
|
swap(other) {
|
|
2146
2199
|
[this.bitset, other.bitset] = [other.bitset, this.bitset];
|
|
2147
2200
|
[this._cardinality, other._cardinality] = [other._cardinality, this._cardinality];
|
|
2148
2201
|
}
|
|
2149
2202
|
/**
|
|
2150
|
-
|
|
2151
|
-
|
|
2203
|
+
* Efficient iteration over all elements in the set.
|
|
2204
|
+
* @yields The indices of elements in the set
|
|
2205
|
+
*/
|
|
2152
2206
|
*[Symbol.iterator]() {
|
|
2153
2207
|
yield* this.bitset;
|
|
2154
2208
|
}
|
|
2155
2209
|
/**
|
|
2156
|
-
|
|
2157
|
-
|
|
2210
|
+
* Perform union operation with another GraphBitSet, updating this set in place.
|
|
2211
|
+
* @param other - The GraphBitSet to union with
|
|
2212
|
+
*/
|
|
2158
2213
|
union(other) {
|
|
2159
2214
|
const result = this.bitset.union(other.bitset);
|
|
2160
2215
|
this.bitset = result;
|
|
2161
2216
|
const size = this.bitset.size();
|
|
2162
2217
|
this._cardinality = size;
|
|
2163
2218
|
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Perform intersection operation with another GraphBitSet, updating this set in place.
|
|
2221
|
+
* @param other - The GraphBitSet to intersect with
|
|
2222
|
+
*/
|
|
2164
2223
|
intersection(other) {
|
|
2165
2224
|
const result = this.bitset.intersection(other.bitset);
|
|
2166
2225
|
this.bitset = result;
|
|
2167
2226
|
const size = this.bitset.size();
|
|
2168
2227
|
this._cardinality = size;
|
|
2169
2228
|
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Perform difference operation with another GraphBitSet, updating this set in place.
|
|
2231
|
+
* @param other - The GraphBitSet to subtract from this set
|
|
2232
|
+
*/
|
|
2170
2233
|
difference(other) {
|
|
2171
2234
|
const result = this.bitset.difference(other.bitset);
|
|
2172
2235
|
this.bitset = result;
|
|
@@ -2174,8 +2237,9 @@ class GraphBitSet {
|
|
|
2174
2237
|
this._cardinality = size;
|
|
2175
2238
|
}
|
|
2176
2239
|
/**
|
|
2177
|
-
|
|
2178
|
-
|
|
2240
|
+
* Clone the bitset.
|
|
2241
|
+
* @returns A new GraphBitSet with the same elements
|
|
2242
|
+
*/
|
|
2179
2243
|
clone() {
|
|
2180
2244
|
const cloned = new GraphBitSet();
|
|
2181
2245
|
cloned.bitset = this.bitset.clone();
|
|
@@ -2184,14 +2248,19 @@ class GraphBitSet {
|
|
|
2184
2248
|
}
|
|
2185
2249
|
}
|
|
2186
2250
|
class VisitedBitArray {
|
|
2251
|
+
/**
|
|
2252
|
+
* Creates a new VisitedBitArray with the specified size.
|
|
2253
|
+
* @param size - The number of bits to allocate
|
|
2254
|
+
*/
|
|
2187
2255
|
constructor(size) {
|
|
2188
2256
|
this._size = size;
|
|
2189
2257
|
this.wordCount = Math.ceil(size / 32);
|
|
2190
2258
|
this.words = new Uint32Array(this.wordCount);
|
|
2191
2259
|
}
|
|
2192
2260
|
/**
|
|
2193
|
-
|
|
2194
|
-
|
|
2261
|
+
* Set bit at the specified index.
|
|
2262
|
+
* @param index - The bit index to set
|
|
2263
|
+
*/
|
|
2195
2264
|
set(index) {
|
|
2196
2265
|
if (index < 0 || index >= this._size) {
|
|
2197
2266
|
throw new Error(`Index ${String(index)} out of bounds [0, ${String(this._size)})`);
|
|
@@ -2204,8 +2273,10 @@ class VisitedBitArray {
|
|
|
2204
2273
|
}
|
|
2205
2274
|
}
|
|
2206
2275
|
/**
|
|
2207
|
-
|
|
2208
|
-
|
|
2276
|
+
* Get bit value at the specified index.
|
|
2277
|
+
* @param index - The bit index to get
|
|
2278
|
+
* @returns True if the bit is set, false otherwise
|
|
2279
|
+
*/
|
|
2209
2280
|
get(index) {
|
|
2210
2281
|
if (index < 0 || index >= this._size) {
|
|
2211
2282
|
return false;
|
|
@@ -2216,14 +2287,15 @@ class VisitedBitArray {
|
|
|
2216
2287
|
return word !== void 0 && (word & 1 << bitIndex) !== 0;
|
|
2217
2288
|
}
|
|
2218
2289
|
/**
|
|
2219
|
-
|
|
2220
|
-
|
|
2290
|
+
* Clear all bits
|
|
2291
|
+
*/
|
|
2221
2292
|
clear() {
|
|
2222
2293
|
this.words.fill(0);
|
|
2223
2294
|
}
|
|
2224
2295
|
/**
|
|
2225
|
-
|
|
2226
|
-
|
|
2296
|
+
* Toggle bit at the specified index.
|
|
2297
|
+
* @param index - The bit index to toggle
|
|
2298
|
+
*/
|
|
2227
2299
|
toggle(index) {
|
|
2228
2300
|
if (index < 0 || index >= this._size) {
|
|
2229
2301
|
throw new Error(`Index ${String(index)} out of bounds [0, ${String(this._size)})`);
|
|
@@ -2236,8 +2308,9 @@ class VisitedBitArray {
|
|
|
2236
2308
|
}
|
|
2237
2309
|
}
|
|
2238
2310
|
/**
|
|
2239
|
-
|
|
2240
|
-
|
|
2311
|
+
* Population count for statistics - count the number of set bits.
|
|
2312
|
+
* @returns The number of bits set to 1
|
|
2313
|
+
*/
|
|
2241
2314
|
popcount() {
|
|
2242
2315
|
let count = 0;
|
|
2243
2316
|
for (let i = 0; i < this.wordCount; i++) {
|
|
@@ -2249,9 +2322,11 @@ class VisitedBitArray {
|
|
|
2249
2322
|
return count;
|
|
2250
2323
|
}
|
|
2251
2324
|
/**
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2325
|
+
* Efficient population count for a single word.
|
|
2326
|
+
* Uses bit manipulation tricks for fast counting.
|
|
2327
|
+
* @param n - The 32-bit word to count
|
|
2328
|
+
* @returns The number of bits set to 1 in the word
|
|
2329
|
+
*/
|
|
2255
2330
|
popcountWord(n) {
|
|
2256
2331
|
let x = n;
|
|
2257
2332
|
x = x - (x >>> 1 & 1431655765);
|
|
@@ -2259,14 +2334,16 @@ class VisitedBitArray {
|
|
|
2259
2334
|
return (x + (x >>> 4) & 252645135) * 16843009 >>> 24;
|
|
2260
2335
|
}
|
|
2261
2336
|
/**
|
|
2262
|
-
|
|
2263
|
-
|
|
2337
|
+
* Get size of the bit array.
|
|
2338
|
+
* @returns The total number of bits in the array
|
|
2339
|
+
*/
|
|
2264
2340
|
size() {
|
|
2265
2341
|
return this._size;
|
|
2266
2342
|
}
|
|
2267
2343
|
/**
|
|
2268
|
-
|
|
2269
|
-
|
|
2344
|
+
* Check if all bits are zero.
|
|
2345
|
+
* @returns True if no bits are set, false otherwise
|
|
2346
|
+
*/
|
|
2270
2347
|
isEmpty() {
|
|
2271
2348
|
for (let i = 0; i < this.wordCount; i++) {
|
|
2272
2349
|
if (this.words[i] !== 0) {
|
|
@@ -2276,16 +2353,18 @@ class VisitedBitArray {
|
|
|
2276
2353
|
return true;
|
|
2277
2354
|
}
|
|
2278
2355
|
/**
|
|
2279
|
-
|
|
2280
|
-
|
|
2356
|
+
* Set multiple bits from an array of indices.
|
|
2357
|
+
* @param indices - Array of bit indices to set
|
|
2358
|
+
*/
|
|
2281
2359
|
setMultiple(indices) {
|
|
2282
2360
|
for (const index of indices) {
|
|
2283
2361
|
this.set(index);
|
|
2284
2362
|
}
|
|
2285
2363
|
}
|
|
2286
2364
|
/**
|
|
2287
|
-
|
|
2288
|
-
|
|
2365
|
+
* Get indices of all set bits.
|
|
2366
|
+
* @returns Array containing the indices of all bits that are set to 1
|
|
2367
|
+
*/
|
|
2289
2368
|
getSetIndices() {
|
|
2290
2369
|
const indices = [];
|
|
2291
2370
|
for (let wordIndex = 0; wordIndex < this.wordCount; wordIndex++) {
|
|
@@ -2306,14 +2385,20 @@ class VisitedBitArray {
|
|
|
2306
2385
|
}
|
|
2307
2386
|
}
|
|
2308
2387
|
const _CompactDistanceArray = class _CompactDistanceArray {
|
|
2388
|
+
/**
|
|
2389
|
+
* Creates a new CompactDistanceArray with the specified size.
|
|
2390
|
+
* @param size - The number of distance values to store
|
|
2391
|
+
*/
|
|
2309
2392
|
constructor(size) {
|
|
2310
2393
|
this._size = size;
|
|
2311
2394
|
this.data = new Uint16Array(size);
|
|
2312
2395
|
this.data.fill(_CompactDistanceArray.INFINITY);
|
|
2313
2396
|
}
|
|
2314
2397
|
/**
|
|
2315
|
-
|
|
2316
|
-
|
|
2398
|
+
* Set distance at the specified index.
|
|
2399
|
+
* @param index - The node index to set the distance for
|
|
2400
|
+
* @param distance - The distance value to set
|
|
2401
|
+
*/
|
|
2317
2402
|
set(index, distance) {
|
|
2318
2403
|
if (distance >= _CompactDistanceArray.INFINITY) {
|
|
2319
2404
|
throw new Error(`Distance exceeds maximum value (${String(_CompactDistanceArray.INFINITY - 1)})`);
|
|
@@ -2321,27 +2406,32 @@ const _CompactDistanceArray = class _CompactDistanceArray {
|
|
|
2321
2406
|
this.data[index] = distance;
|
|
2322
2407
|
}
|
|
2323
2408
|
/**
|
|
2324
|
-
|
|
2325
|
-
|
|
2409
|
+
* Get distance at the specified index.
|
|
2410
|
+
* @param index - The node index to get the distance for
|
|
2411
|
+
* @returns The distance value, or INFINITY if not set
|
|
2412
|
+
*/
|
|
2326
2413
|
get(index) {
|
|
2327
2414
|
const value = this.data[index];
|
|
2328
2415
|
return value ?? _CompactDistanceArray.INFINITY;
|
|
2329
2416
|
}
|
|
2330
2417
|
/**
|
|
2331
|
-
|
|
2332
|
-
|
|
2418
|
+
* Check if a node has been visited (has a valid distance).
|
|
2419
|
+
* @param index - The node index to check
|
|
2420
|
+
* @returns True if the node has been visited, false otherwise
|
|
2421
|
+
*/
|
|
2333
2422
|
isVisited(index) {
|
|
2334
2423
|
return this.data[index] !== _CompactDistanceArray.INFINITY;
|
|
2335
2424
|
}
|
|
2336
2425
|
/**
|
|
2337
|
-
|
|
2338
|
-
|
|
2426
|
+
* Reset all distances to unvisited state.
|
|
2427
|
+
*/
|
|
2339
2428
|
clear() {
|
|
2340
2429
|
this.data.fill(_CompactDistanceArray.INFINITY);
|
|
2341
2430
|
}
|
|
2342
2431
|
/**
|
|
2343
|
-
|
|
2344
|
-
|
|
2432
|
+
* Get the size of the distance array.
|
|
2433
|
+
* @returns The number of elements in the array
|
|
2434
|
+
*/
|
|
2345
2435
|
size() {
|
|
2346
2436
|
return this._size;
|
|
2347
2437
|
}
|
|
@@ -2349,6 +2439,11 @@ const _CompactDistanceArray = class _CompactDistanceArray {
|
|
|
2349
2439
|
_CompactDistanceArray.INFINITY = 65535;
|
|
2350
2440
|
let CompactDistanceArray = _CompactDistanceArray;
|
|
2351
2441
|
class DirectionOptimizedBFS {
|
|
2442
|
+
/**
|
|
2443
|
+
* Creates a new DirectionOptimizedBFS instance.
|
|
2444
|
+
* @param graph - The CSR graph to perform BFS on
|
|
2445
|
+
* @param options - Optional configuration for switching thresholds
|
|
2446
|
+
*/
|
|
2352
2447
|
constructor(graph, options) {
|
|
2353
2448
|
this.graph = graph;
|
|
2354
2449
|
this.alpha = options?.alpha ?? 15;
|
|
@@ -2360,8 +2455,10 @@ class DirectionOptimizedBFS {
|
|
|
2360
2455
|
this.distances = new CompactDistanceArray(nodeCount);
|
|
2361
2456
|
}
|
|
2362
2457
|
/**
|
|
2363
|
-
|
|
2364
|
-
|
|
2458
|
+
* Perform BFS from a single source node.
|
|
2459
|
+
* @param source - The source node ID to start the BFS from
|
|
2460
|
+
* @returns The BFS result containing distances and parent pointers
|
|
2461
|
+
*/
|
|
2365
2462
|
search(source) {
|
|
2366
2463
|
const sourceIndex = this.graph.nodeToIndex(source);
|
|
2367
2464
|
this.parent[sourceIndex] = -2;
|
|
@@ -2396,8 +2493,9 @@ class DirectionOptimizedBFS {
|
|
|
2396
2493
|
return this.buildResult();
|
|
2397
2494
|
}
|
|
2398
2495
|
/**
|
|
2399
|
-
|
|
2400
|
-
|
|
2496
|
+
* Top-down BFS step - explore from frontier.
|
|
2497
|
+
* @returns The scout count (total degree of newly discovered nodes)
|
|
2498
|
+
*/
|
|
2401
2499
|
topDownStep() {
|
|
2402
2500
|
let scoutCount = 0;
|
|
2403
2501
|
for (const node of this.frontier) {
|
|
@@ -2414,8 +2512,9 @@ class DirectionOptimizedBFS {
|
|
|
2414
2512
|
return scoutCount;
|
|
2415
2513
|
}
|
|
2416
2514
|
/**
|
|
2417
|
-
|
|
2418
|
-
|
|
2515
|
+
* Bottom-up BFS step - check unvisited nodes for frontier neighbors.
|
|
2516
|
+
* @returns The number of newly discovered nodes
|
|
2517
|
+
*/
|
|
2419
2518
|
bottomUpStep() {
|
|
2420
2519
|
const nodeCount = this.graph.nodeCount();
|
|
2421
2520
|
let awakeCount = 0;
|
|
@@ -2436,8 +2535,9 @@ class DirectionOptimizedBFS {
|
|
|
2436
2535
|
return awakeCount;
|
|
2437
2536
|
}
|
|
2438
2537
|
/**
|
|
2439
|
-
|
|
2440
|
-
|
|
2538
|
+
* Calculate edges to check for switching heuristic.
|
|
2539
|
+
* @returns The total number of outgoing edges from frontier nodes
|
|
2540
|
+
*/
|
|
2441
2541
|
calculateEdgesToCheck() {
|
|
2442
2542
|
let count = 0;
|
|
2443
2543
|
for (const node of this.frontier) {
|
|
@@ -2446,8 +2546,9 @@ class DirectionOptimizedBFS {
|
|
|
2446
2546
|
return count;
|
|
2447
2547
|
}
|
|
2448
2548
|
/**
|
|
2449
|
-
|
|
2450
|
-
|
|
2549
|
+
* Build result map from internal data structures.
|
|
2550
|
+
* @returns The BFS result containing distances and parent pointers
|
|
2551
|
+
*/
|
|
2451
2552
|
buildResult() {
|
|
2452
2553
|
const distances = /* @__PURE__ */ new Map();
|
|
2453
2554
|
const parents = /* @__PURE__ */ new Map();
|
|
@@ -2470,8 +2571,10 @@ class DirectionOptimizedBFS {
|
|
|
2470
2571
|
return { distances, parents, visitedCount };
|
|
2471
2572
|
}
|
|
2472
2573
|
/**
|
|
2473
|
-
|
|
2474
|
-
|
|
2574
|
+
* Perform multi-source BFS from multiple source nodes.
|
|
2575
|
+
* @param sources - Array of source node IDs to start the BFS from
|
|
2576
|
+
* @returns The BFS result containing distances and parent pointers
|
|
2577
|
+
*/
|
|
2475
2578
|
searchMultiple(sources) {
|
|
2476
2579
|
for (const source of sources) {
|
|
2477
2580
|
const sourceIndex = this.graph.nodeToIndex(source);
|
|
@@ -2513,8 +2616,8 @@ class DirectionOptimizedBFS {
|
|
|
2513
2616
|
return this.buildResult();
|
|
2514
2617
|
}
|
|
2515
2618
|
/**
|
|
2516
|
-
|
|
2517
|
-
|
|
2619
|
+
* Reset internal state for reuse
|
|
2620
|
+
*/
|
|
2518
2621
|
reset() {
|
|
2519
2622
|
this.parent.fill(-1);
|
|
2520
2623
|
this.frontier.clear();
|
|
@@ -2523,12 +2626,22 @@ class DirectionOptimizedBFS {
|
|
|
2523
2626
|
}
|
|
2524
2627
|
}
|
|
2525
2628
|
class CSRGraph {
|
|
2629
|
+
/**
|
|
2630
|
+
* Creates a new CSRGraph from an adjacency list representation.
|
|
2631
|
+
* @param adjacencyList - Map of node IDs to arrays of neighbor node IDs
|
|
2632
|
+
* @param weights - Optional map of edge weights keyed by "source-target" strings
|
|
2633
|
+
* @param buildReverse - Whether to build reverse edges for bottom-up BFS (default true)
|
|
2634
|
+
*/
|
|
2526
2635
|
constructor(adjacencyList, weights, buildReverse = true) {
|
|
2527
2636
|
this.data = this.buildCSR(adjacencyList, weights, buildReverse);
|
|
2528
2637
|
}
|
|
2529
2638
|
/**
|
|
2530
|
-
|
|
2531
|
-
|
|
2639
|
+
* Build CSR structure from adjacency list.
|
|
2640
|
+
* @param adjacencyList - Map of node IDs to arrays of neighbor node IDs
|
|
2641
|
+
* @param weights - Optional map of edge weights keyed by "source-target" strings
|
|
2642
|
+
* @param buildReverse - Whether to build reverse edges for bottom-up BFS
|
|
2643
|
+
* @returns The constructed CSR graph data structure
|
|
2644
|
+
*/
|
|
2532
2645
|
buildCSR(adjacencyList, weights, buildReverse = true) {
|
|
2533
2646
|
const allNodes = /* @__PURE__ */ new Set();
|
|
2534
2647
|
for (const [source, neighbors] of adjacencyList) {
|
|
@@ -2631,15 +2744,34 @@ class CSRGraph {
|
|
|
2631
2744
|
return result;
|
|
2632
2745
|
}
|
|
2633
2746
|
// Core API methods
|
|
2747
|
+
/**
|
|
2748
|
+
* Get the total number of nodes in the graph.
|
|
2749
|
+
* @returns The number of nodes
|
|
2750
|
+
*/
|
|
2634
2751
|
nodeCount() {
|
|
2635
2752
|
return this.data.indexToNodeId.length;
|
|
2636
2753
|
}
|
|
2754
|
+
/**
|
|
2755
|
+
* Get the total number of edges in the graph.
|
|
2756
|
+
* @returns The number of edges
|
|
2757
|
+
*/
|
|
2637
2758
|
edgeCount() {
|
|
2638
2759
|
return this.data.columnIndices.length;
|
|
2639
2760
|
}
|
|
2761
|
+
/**
|
|
2762
|
+
* Check if a node exists in the graph.
|
|
2763
|
+
* @param nodeId - The node ID to check
|
|
2764
|
+
* @returns True if the node exists, false otherwise
|
|
2765
|
+
*/
|
|
2640
2766
|
hasNode(nodeId) {
|
|
2641
2767
|
return this.data.nodeIdToIndex.has(nodeId);
|
|
2642
2768
|
}
|
|
2769
|
+
/**
|
|
2770
|
+
* Check if an edge exists between two nodes.
|
|
2771
|
+
* @param source - The source node ID
|
|
2772
|
+
* @param target - The target node ID
|
|
2773
|
+
* @returns True if the edge exists, false otherwise
|
|
2774
|
+
*/
|
|
2643
2775
|
hasEdge(source, target) {
|
|
2644
2776
|
const sourceIndex = this.data.nodeIdToIndex.get(source);
|
|
2645
2777
|
const targetIndex = this.data.nodeIdToIndex.get(target);
|
|
@@ -2654,8 +2786,10 @@ class CSRGraph {
|
|
|
2654
2786
|
return this.binarySearch(this.data.columnIndices, targetIndex, start, end) !== -1;
|
|
2655
2787
|
}
|
|
2656
2788
|
/**
|
|
2657
|
-
|
|
2658
|
-
|
|
2789
|
+
* Get neighbors of a node as node IDs.
|
|
2790
|
+
* @param nodeId - The node ID to get neighbors for
|
|
2791
|
+
* @returns An iterator over neighbor node IDs
|
|
2792
|
+
*/
|
|
2659
2793
|
neighbors(nodeId) {
|
|
2660
2794
|
const nodeIndex = this.data.nodeIdToIndex.get(nodeId);
|
|
2661
2795
|
if (nodeIndex === void 0) {
|
|
@@ -2680,14 +2814,17 @@ class CSRGraph {
|
|
|
2680
2814
|
return generateNeighbors();
|
|
2681
2815
|
}
|
|
2682
2816
|
/**
|
|
2683
|
-
|
|
2684
|
-
|
|
2817
|
+
* Get all nodes in the graph.
|
|
2818
|
+
* @returns An iterator over all node IDs
|
|
2819
|
+
*/
|
|
2685
2820
|
nodes() {
|
|
2686
2821
|
return this.data.indexToNodeId.values();
|
|
2687
2822
|
}
|
|
2688
2823
|
/**
|
|
2689
|
-
|
|
2690
|
-
|
|
2824
|
+
* Get neighbors as indices (internal use).
|
|
2825
|
+
* @param nodeIndex - The internal index of the node
|
|
2826
|
+
* @returns Array of neighbor indices
|
|
2827
|
+
*/
|
|
2691
2828
|
getNeighborIndices(nodeIndex) {
|
|
2692
2829
|
if (nodeIndex < 0 || nodeIndex >= this.data.indexToNodeId.length) {
|
|
2693
2830
|
return [];
|
|
@@ -2699,6 +2836,11 @@ class CSRGraph {
|
|
|
2699
2836
|
}
|
|
2700
2837
|
return Array.from(this.data.columnIndices.subarray(start, end));
|
|
2701
2838
|
}
|
|
2839
|
+
/**
|
|
2840
|
+
* Get the out-degree (number of outgoing edges) of a node.
|
|
2841
|
+
* @param nodeId - The node ID to get the out-degree for
|
|
2842
|
+
* @returns The number of outgoing edges from the node
|
|
2843
|
+
*/
|
|
2702
2844
|
outDegree(nodeId) {
|
|
2703
2845
|
const nodeIndex = this.data.nodeIdToIndex.get(nodeId);
|
|
2704
2846
|
if (nodeIndex === void 0) {
|
|
@@ -2707,8 +2849,10 @@ class CSRGraph {
|
|
|
2707
2849
|
return this.outDegreeByIndex(nodeIndex);
|
|
2708
2850
|
}
|
|
2709
2851
|
/**
|
|
2710
|
-
|
|
2711
|
-
|
|
2852
|
+
* Get out-degree by index (internal use).
|
|
2853
|
+
* @param nodeIndex - The internal index of the node
|
|
2854
|
+
* @returns The number of outgoing edges from the node
|
|
2855
|
+
*/
|
|
2712
2856
|
outDegreeByIndex(nodeIndex) {
|
|
2713
2857
|
const start = this.data.rowPointers[nodeIndex];
|
|
2714
2858
|
const end = this.data.rowPointers[nodeIndex + 1];
|
|
@@ -2718,8 +2862,10 @@ class CSRGraph {
|
|
|
2718
2862
|
return 0;
|
|
2719
2863
|
}
|
|
2720
2864
|
/**
|
|
2721
|
-
|
|
2722
|
-
|
|
2865
|
+
* Iterator support for neighbor indices.
|
|
2866
|
+
* @param nodeIndex - The internal index of the node
|
|
2867
|
+
* @yields The indices of neighboring nodes
|
|
2868
|
+
*/
|
|
2723
2869
|
*iterateNeighborIndices(nodeIndex) {
|
|
2724
2870
|
const start = this.data.rowPointers[nodeIndex];
|
|
2725
2871
|
const end = this.data.rowPointers[nodeIndex + 1];
|
|
@@ -2733,8 +2879,10 @@ class CSRGraph {
|
|
|
2733
2879
|
}
|
|
2734
2880
|
}
|
|
2735
2881
|
/**
|
|
2736
|
-
|
|
2737
|
-
|
|
2882
|
+
* Iterator support for incoming neighbor indices (for bottom-up BFS).
|
|
2883
|
+
* @param nodeIndex - The internal index of the node
|
|
2884
|
+
* @yields The indices of nodes with edges pointing to this node
|
|
2885
|
+
*/
|
|
2738
2886
|
*iterateIncomingNeighborIndices(nodeIndex) {
|
|
2739
2887
|
if (!this.data.reverseRowPointers || !this.data.reverseColumnIndices) {
|
|
2740
2888
|
return;
|
|
@@ -2751,8 +2899,10 @@ class CSRGraph {
|
|
|
2751
2899
|
}
|
|
2752
2900
|
}
|
|
2753
2901
|
/**
|
|
2754
|
-
|
|
2755
|
-
|
|
2902
|
+
* Convert node ID to internal index.
|
|
2903
|
+
* @param nodeId - The node ID to convert
|
|
2904
|
+
* @returns The internal index for the node
|
|
2905
|
+
*/
|
|
2756
2906
|
nodeToIndex(nodeId) {
|
|
2757
2907
|
const index = this.data.nodeIdToIndex.get(nodeId);
|
|
2758
2908
|
if (index === void 0) {
|
|
@@ -2761,8 +2911,10 @@ class CSRGraph {
|
|
|
2761
2911
|
return index;
|
|
2762
2912
|
}
|
|
2763
2913
|
/**
|
|
2764
|
-
|
|
2765
|
-
|
|
2914
|
+
* Convert internal index to node ID.
|
|
2915
|
+
* @param index - The internal index to convert
|
|
2916
|
+
* @returns The node ID for the given index
|
|
2917
|
+
*/
|
|
2766
2918
|
indexToNodeId(index) {
|
|
2767
2919
|
const nodeId = this.data.indexToNodeId[index];
|
|
2768
2920
|
if (nodeId === void 0) {
|
|
@@ -2771,8 +2923,11 @@ class CSRGraph {
|
|
|
2771
2923
|
return nodeId;
|
|
2772
2924
|
}
|
|
2773
2925
|
/**
|
|
2774
|
-
|
|
2775
|
-
|
|
2926
|
+
* Get edge weight between two nodes.
|
|
2927
|
+
* @param source - The source node ID
|
|
2928
|
+
* @param target - The target node ID
|
|
2929
|
+
* @returns The edge weight, or undefined if the edge doesn't exist or has no weight
|
|
2930
|
+
*/
|
|
2776
2931
|
getEdgeWeight(source, target) {
|
|
2777
2932
|
if (!this.data.edgeWeights) {
|
|
2778
2933
|
return void 0;
|
|
@@ -2794,8 +2949,13 @@ class CSRGraph {
|
|
|
2794
2949
|
return this.data.edgeWeights[edgeIndex];
|
|
2795
2950
|
}
|
|
2796
2951
|
/**
|
|
2797
|
-
|
|
2798
|
-
|
|
2952
|
+
* Binary search for target in sorted array.
|
|
2953
|
+
* @param arr - The typed array to search in
|
|
2954
|
+
* @param target - The value to search for
|
|
2955
|
+
* @param start - The starting index (inclusive)
|
|
2956
|
+
* @param end - The ending index (exclusive)
|
|
2957
|
+
* @returns The index of the target, or -1 if not found
|
|
2958
|
+
*/
|
|
2799
2959
|
binarySearch(arr, target, start, end) {
|
|
2800
2960
|
let left = start;
|
|
2801
2961
|
let right = end - 1;
|
|
@@ -2817,8 +2977,14 @@ class CSRGraph {
|
|
|
2817
2977
|
return -1;
|
|
2818
2978
|
}
|
|
2819
2979
|
/**
|
|
2820
|
-
|
|
2821
|
-
|
|
2980
|
+
* Create CSR graph from standard Graph interface.
|
|
2981
|
+
* @param graph - The graph object to convert, must implement nodes, neighbors, hasNode, and optionally getEdge
|
|
2982
|
+
* @param graph.nodes - Function that returns an iterator over node objects with id property
|
|
2983
|
+
* @param graph.neighbors - Function that returns an iterator over neighbor node IDs
|
|
2984
|
+
* @param graph.hasNode - Function that checks if a node exists in the graph
|
|
2985
|
+
* @param graph.getEdge - Optional function that returns edge data including weight
|
|
2986
|
+
* @returns A new CSRGraph instance
|
|
2987
|
+
*/
|
|
2822
2988
|
static fromGraph(graph) {
|
|
2823
2989
|
const adjacencyList = /* @__PURE__ */ new Map();
|
|
2824
2990
|
const weights = /* @__PURE__ */ new Map();
|
|
@@ -2839,6 +3005,10 @@ class CSRGraph {
|
|
|
2839
3005
|
}
|
|
2840
3006
|
}
|
|
2841
3007
|
class GraphAdapter {
|
|
3008
|
+
/**
|
|
3009
|
+
* Creates a new GraphAdapter that wraps or converts a graph to CSR format.
|
|
3010
|
+
* @param graph - The graph to adapt (either standard Graph or ReadonlyGraph)
|
|
3011
|
+
*/
|
|
2842
3012
|
constructor(graph) {
|
|
2843
3013
|
if (graph instanceof CSRGraph) {
|
|
2844
3014
|
this.csrGraph = graph;
|
|
@@ -2847,8 +3017,10 @@ class GraphAdapter {
|
|
|
2847
3017
|
}
|
|
2848
3018
|
}
|
|
2849
3019
|
/**
|
|
2850
|
-
|
|
2851
|
-
|
|
3020
|
+
* Convert standard graph to CSR format.
|
|
3021
|
+
* @param graph - The graph to convert
|
|
3022
|
+
* @returns A new CSRGraph instance
|
|
3023
|
+
*/
|
|
2852
3024
|
convertToCSR(graph) {
|
|
2853
3025
|
const adjacencyList = /* @__PURE__ */ new Map();
|
|
2854
3026
|
const weights = /* @__PURE__ */ new Map();
|
|
@@ -2888,30 +3060,64 @@ class GraphAdapter {
|
|
|
2888
3060
|
return new CSRGraph(adjacencyList, weights.size > 0 ? weights : void 0);
|
|
2889
3061
|
}
|
|
2890
3062
|
// Delegate all methods to CSR implementation
|
|
3063
|
+
/**
|
|
3064
|
+
* Get the total number of nodes in the graph.
|
|
3065
|
+
* @returns The number of nodes
|
|
3066
|
+
*/
|
|
2891
3067
|
nodeCount() {
|
|
2892
3068
|
return this.csrGraph.nodeCount();
|
|
2893
3069
|
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Get the total number of edges in the graph.
|
|
3072
|
+
* @returns The number of edges
|
|
3073
|
+
*/
|
|
2894
3074
|
edgeCount() {
|
|
2895
3075
|
return this.csrGraph.edgeCount();
|
|
2896
3076
|
}
|
|
3077
|
+
/**
|
|
3078
|
+
* Check if a node exists in the graph.
|
|
3079
|
+
* @param nodeId - The node ID to check
|
|
3080
|
+
* @returns True if the node exists, false otherwise
|
|
3081
|
+
*/
|
|
2897
3082
|
hasNode(nodeId) {
|
|
2898
3083
|
return this.csrGraph.hasNode(nodeId);
|
|
2899
3084
|
}
|
|
3085
|
+
/**
|
|
3086
|
+
* Check if an edge exists between two nodes.
|
|
3087
|
+
* @param source - The source node ID
|
|
3088
|
+
* @param target - The target node ID
|
|
3089
|
+
* @returns True if the edge exists, false otherwise
|
|
3090
|
+
*/
|
|
2900
3091
|
hasEdge(source, target) {
|
|
2901
3092
|
return this.csrGraph.hasEdge(source, target);
|
|
2902
3093
|
}
|
|
3094
|
+
/**
|
|
3095
|
+
* Get neighbors of a node as node IDs.
|
|
3096
|
+
* @param nodeId - The node ID to get neighbors for
|
|
3097
|
+
* @returns An iterator over neighbor node IDs
|
|
3098
|
+
*/
|
|
2903
3099
|
neighbors(nodeId) {
|
|
2904
3100
|
return this.csrGraph.neighbors(nodeId);
|
|
2905
3101
|
}
|
|
3102
|
+
/**
|
|
3103
|
+
* Get the out-degree (number of outgoing edges) of a node.
|
|
3104
|
+
* @param nodeId - The node ID to get the out-degree for
|
|
3105
|
+
* @returns The number of outgoing edges from the node
|
|
3106
|
+
*/
|
|
2906
3107
|
outDegree(nodeId) {
|
|
2907
3108
|
return this.csrGraph.outDegree(nodeId);
|
|
2908
3109
|
}
|
|
3110
|
+
/**
|
|
3111
|
+
* Get all nodes in the graph.
|
|
3112
|
+
* @returns An iterator over all node IDs
|
|
3113
|
+
*/
|
|
2909
3114
|
nodes() {
|
|
2910
3115
|
return this.csrGraph.nodes();
|
|
2911
3116
|
}
|
|
2912
3117
|
/**
|
|
2913
|
-
|
|
2914
|
-
|
|
3118
|
+
* Get the underlying CSR graph.
|
|
3119
|
+
* @returns The CSRGraph instance used internally
|
|
3120
|
+
*/
|
|
2915
3121
|
getCSRGraph() {
|
|
2916
3122
|
return this.csrGraph;
|
|
2917
3123
|
}
|
|
@@ -2962,12 +3168,8 @@ function reconstructPath(target, predecessor) {
|
|
|
2962
3168
|
return path;
|
|
2963
3169
|
}
|
|
2964
3170
|
function getCommonNeighbors(graph, source, target, directed = false) {
|
|
2965
|
-
const sourceNeighbors = new Set(
|
|
2966
|
-
|
|
2967
|
-
);
|
|
2968
|
-
const targetNeighbors = new Set(
|
|
2969
|
-
directed ? graph.outNeighbors(target) : graph.neighbors(target)
|
|
2970
|
-
);
|
|
3171
|
+
const sourceNeighbors = new Set(directed ? graph.outNeighbors(source) : graph.neighbors(source));
|
|
3172
|
+
const targetNeighbors = new Set(directed ? graph.outNeighbors(target) : graph.neighbors(target));
|
|
2971
3173
|
const common = /* @__PURE__ */ new Set();
|
|
2972
3174
|
for (const neighbor of sourceNeighbors) {
|
|
2973
3175
|
if (targetNeighbors.has(neighbor)) {
|
|
@@ -3528,12 +3730,18 @@ function hasNegativeCycle(graph) {
|
|
|
3528
3730
|
return false;
|
|
3529
3731
|
}
|
|
3530
3732
|
class PriorityQueue {
|
|
3733
|
+
/**
|
|
3734
|
+
* Creates a new PriorityQueue instance.
|
|
3735
|
+
* @param compareFn - Custom comparison function for priorities. Returns negative if a has higher priority than b.
|
|
3736
|
+
*/
|
|
3531
3737
|
constructor(compareFn) {
|
|
3532
3738
|
this.heap = [];
|
|
3533
3739
|
this.compareFn = compareFn ?? ((a, b) => a - b);
|
|
3534
3740
|
}
|
|
3535
3741
|
/**
|
|
3536
3742
|
* Add an item with the given priority to the queue
|
|
3743
|
+
* @param item - The item to add to the queue
|
|
3744
|
+
* @param priority - The priority value for the item (lower values have higher priority in min-heap)
|
|
3537
3745
|
*/
|
|
3538
3746
|
enqueue(item, priority) {
|
|
3539
3747
|
const element = { item, priority };
|
|
@@ -3542,6 +3750,7 @@ class PriorityQueue {
|
|
|
3542
3750
|
}
|
|
3543
3751
|
/**
|
|
3544
3752
|
* Remove and return the item with the highest priority
|
|
3753
|
+
* @returns The item with the highest priority, or undefined if the queue is empty
|
|
3545
3754
|
*/
|
|
3546
3755
|
dequeue() {
|
|
3547
3756
|
if (this.heap.length === 0) {
|
|
@@ -3563,6 +3772,7 @@ class PriorityQueue {
|
|
|
3563
3772
|
}
|
|
3564
3773
|
/**
|
|
3565
3774
|
* View the item with the highest priority without removing it
|
|
3775
|
+
* @returns The item with the highest priority, or undefined if the queue is empty
|
|
3566
3776
|
*/
|
|
3567
3777
|
peek() {
|
|
3568
3778
|
const first = this.heap[0];
|
|
@@ -3570,19 +3780,23 @@ class PriorityQueue {
|
|
|
3570
3780
|
}
|
|
3571
3781
|
/**
|
|
3572
3782
|
* Check if the queue is empty
|
|
3783
|
+
* @returns True if the queue has no items, false otherwise
|
|
3573
3784
|
*/
|
|
3574
3785
|
isEmpty() {
|
|
3575
3786
|
return this.heap.length === 0;
|
|
3576
3787
|
}
|
|
3577
3788
|
/**
|
|
3578
3789
|
* Get the number of items in the queue
|
|
3790
|
+
* @returns The number of items currently in the queue
|
|
3579
3791
|
*/
|
|
3580
3792
|
size() {
|
|
3581
3793
|
return this.heap.length;
|
|
3582
3794
|
}
|
|
3583
3795
|
/**
|
|
3584
3796
|
* Update the priority of an item if it exists in the queue
|
|
3585
|
-
*
|
|
3797
|
+
* @param item - The item whose priority should be updated
|
|
3798
|
+
* @param newPriority - The new priority value for the item
|
|
3799
|
+
* @returns True if the item was found and updated, false otherwise
|
|
3586
3800
|
*/
|
|
3587
3801
|
updatePriority(item, newPriority) {
|
|
3588
3802
|
const index = this.heap.findIndex((element2) => element2.item === item);
|
|
@@ -3610,12 +3824,14 @@ class PriorityQueue {
|
|
|
3610
3824
|
}
|
|
3611
3825
|
/**
|
|
3612
3826
|
* Convert queue to array (for testing/debugging)
|
|
3827
|
+
* @returns An array of all items with their priorities
|
|
3613
3828
|
*/
|
|
3614
3829
|
toArray() {
|
|
3615
3830
|
return [...this.heap];
|
|
3616
3831
|
}
|
|
3617
3832
|
/**
|
|
3618
3833
|
* Move element up the heap until heap property is satisfied
|
|
3834
|
+
* @param index - The index of the element to heapify up
|
|
3619
3835
|
*/
|
|
3620
3836
|
heapifyUp(index) {
|
|
3621
3837
|
if (index === 0) {
|
|
@@ -3631,6 +3847,7 @@ class PriorityQueue {
|
|
|
3631
3847
|
}
|
|
3632
3848
|
/**
|
|
3633
3849
|
* Move element down the heap until heap property is satisfied
|
|
3850
|
+
* @param index - The index of the element to heapify down
|
|
3634
3851
|
*/
|
|
3635
3852
|
heapifyDown(index) {
|
|
3636
3853
|
const leftChildIndex = 2 * index + 1;
|
|
@@ -3653,6 +3870,8 @@ class PriorityQueue {
|
|
|
3653
3870
|
}
|
|
3654
3871
|
/**
|
|
3655
3872
|
* Swap two elements in the heap
|
|
3873
|
+
* @param i - Index of the first element
|
|
3874
|
+
* @param j - Index of the second element
|
|
3656
3875
|
*/
|
|
3657
3876
|
swap(i, j) {
|
|
3658
3877
|
const temp = this.heap[i];
|
|
@@ -3664,6 +3883,10 @@ class PriorityQueue {
|
|
|
3664
3883
|
}
|
|
3665
3884
|
}
|
|
3666
3885
|
class BidirectionalDijkstra {
|
|
3886
|
+
/**
|
|
3887
|
+
* Creates a new BidirectionalDijkstra instance
|
|
3888
|
+
* @param graph - The graph to search for shortest paths
|
|
3889
|
+
*/
|
|
3667
3890
|
constructor(graph) {
|
|
3668
3891
|
this.meetingNode = null;
|
|
3669
3892
|
this.shortestDistance = Infinity;
|
|
@@ -3680,8 +3903,11 @@ class BidirectionalDijkstra {
|
|
|
3680
3903
|
};
|
|
3681
3904
|
}
|
|
3682
3905
|
/**
|
|
3683
|
-
|
|
3684
|
-
|
|
3906
|
+
* Find shortest path between source and target nodes
|
|
3907
|
+
* @param source - The starting node for the path
|
|
3908
|
+
* @param target - The destination node for the path
|
|
3909
|
+
* @returns The shortest path result or null if no path exists
|
|
3910
|
+
*/
|
|
3685
3911
|
findShortestPath(source, target) {
|
|
3686
3912
|
this.reset();
|
|
3687
3913
|
if (!this.graph.hasNode(source)) {
|
|
@@ -3806,8 +4032,8 @@ class BidirectionalDijkstra {
|
|
|
3806
4032
|
return path;
|
|
3807
4033
|
}
|
|
3808
4034
|
/**
|
|
3809
|
-
|
|
3810
|
-
|
|
4035
|
+
* Reset the search state for reuse
|
|
4036
|
+
*/
|
|
3811
4037
|
reset() {
|
|
3812
4038
|
this.forwardSearch = this.initSearchState();
|
|
3813
4039
|
this.backwardSearch = this.initSearchState();
|
|
@@ -4583,7 +4809,12 @@ function nodeClosenessCentrality(graph, node, options = {}) {
|
|
|
4583
4809
|
if (!graph.hasNode(node)) {
|
|
4584
4810
|
throw new Error(`Node ${String(node)} not found in graph`);
|
|
4585
4811
|
}
|
|
4586
|
-
const distances = bfsDistancesOnly(
|
|
4812
|
+
const distances = bfsDistancesOnly(
|
|
4813
|
+
graph,
|
|
4814
|
+
node,
|
|
4815
|
+
options.cutoff,
|
|
4816
|
+
options.optimized !== void 0 ? { optimized: options.optimized } : {}
|
|
4817
|
+
);
|
|
4587
4818
|
const totalNodes = graph.nodeCount;
|
|
4588
4819
|
return calculateClosenessFromDistances(distances, node, totalNodes, options);
|
|
4589
4820
|
}
|
|
@@ -4599,7 +4830,12 @@ function nodeWeightedClosenessCentrality(graph, node, options = {}) {
|
|
|
4599
4830
|
if (!graph.hasNode(node)) {
|
|
4600
4831
|
throw new Error(`Node ${String(node)} not found in graph`);
|
|
4601
4832
|
}
|
|
4602
|
-
const distances = bfsWeightedDistances(
|
|
4833
|
+
const distances = bfsWeightedDistances(
|
|
4834
|
+
graph,
|
|
4835
|
+
node,
|
|
4836
|
+
options.cutoff,
|
|
4837
|
+
options.optimized !== void 0 ? { optimized: options.optimized } : {}
|
|
4838
|
+
);
|
|
4603
4839
|
const totalNodes = graph.nodeCount;
|
|
4604
4840
|
return calculateClosenessFromDistances(distances, node, totalNodes, options);
|
|
4605
4841
|
}
|
|
@@ -4666,6 +4902,10 @@ function nodeDegreeCentrality(graph, nodeId, options = {}) {
|
|
|
4666
4902
|
return options.normalized && maxPossibleDegree > 0 ? degree / maxPossibleDegree : degree;
|
|
4667
4903
|
}
|
|
4668
4904
|
class DeltaPageRank {
|
|
4905
|
+
/**
|
|
4906
|
+
* Creates a new DeltaPageRank instance for the given graph.
|
|
4907
|
+
* @param graph - The directed graph to compute PageRank on
|
|
4908
|
+
*/
|
|
4669
4909
|
constructor(graph) {
|
|
4670
4910
|
if (!graph.isDirected) {
|
|
4671
4911
|
throw new Error("DeltaPageRank requires a directed graph");
|
|
@@ -4701,6 +4941,11 @@ class DeltaPageRank {
|
|
|
4701
4941
|
}
|
|
4702
4942
|
}
|
|
4703
4943
|
}
|
|
4944
|
+
/**
|
|
4945
|
+
* Computes PageRank scores using delta-based iteration for faster convergence.
|
|
4946
|
+
* @param options - Configuration options for PageRank computation
|
|
4947
|
+
* @returns Map of node IDs to their PageRank scores
|
|
4948
|
+
*/
|
|
4704
4949
|
compute(options = {}) {
|
|
4705
4950
|
const {
|
|
4706
4951
|
dampingFactor = 0.85,
|
|
@@ -4796,8 +5041,11 @@ class DeltaPageRank {
|
|
|
4796
5041
|
return new Map(this.scores);
|
|
4797
5042
|
}
|
|
4798
5043
|
/**
|
|
4799
|
-
* Update PageRank scores after graph modification
|
|
4800
|
-
* This is where delta-based approach really shines
|
|
5044
|
+
* Update PageRank scores after graph modification.
|
|
5045
|
+
* This is where delta-based approach really shines.
|
|
5046
|
+
* @param modifiedNodes - Set of nodes that were modified (edges added/removed)
|
|
5047
|
+
* @param options - Configuration options for PageRank computation
|
|
5048
|
+
* @returns Updated PageRank scores
|
|
4801
5049
|
*/
|
|
4802
5050
|
update(modifiedNodes, options = {}) {
|
|
4803
5051
|
this.activeNodes.clear();
|
|
@@ -4825,6 +5073,10 @@ class DeltaPageRank {
|
|
|
4825
5073
|
}
|
|
4826
5074
|
}
|
|
4827
5075
|
class PriorityDeltaPageRank {
|
|
5076
|
+
/**
|
|
5077
|
+
* Creates a new PriorityDeltaPageRank instance for the given graph.
|
|
5078
|
+
* @param graph - The directed graph to compute PageRank on
|
|
5079
|
+
*/
|
|
4828
5080
|
constructor(graph) {
|
|
4829
5081
|
if (!graph.isDirected) {
|
|
4830
5082
|
throw new Error("PriorityDeltaPageRank requires a directed graph");
|
|
@@ -4858,6 +5110,12 @@ class PriorityDeltaPageRank {
|
|
|
4858
5110
|
}
|
|
4859
5111
|
}
|
|
4860
5112
|
}
|
|
5113
|
+
/**
|
|
5114
|
+
* Computes PageRank scores using priority queue-based delta iteration.
|
|
5115
|
+
* Processes nodes in order of their delta magnitude for optimal convergence.
|
|
5116
|
+
* @param options - Configuration options for PageRank computation
|
|
5117
|
+
* @returns Map of node IDs to their PageRank scores
|
|
5118
|
+
*/
|
|
4861
5119
|
computeWithPriority(options = {}) {
|
|
4862
5120
|
const {
|
|
4863
5121
|
dampingFactor = 0.85,
|
|
@@ -4935,12 +5193,7 @@ class PriorityDeltaPageRank {
|
|
|
4935
5193
|
}
|
|
4936
5194
|
}
|
|
4937
5195
|
function eigenvectorCentrality(graph, options = {}) {
|
|
4938
|
-
const {
|
|
4939
|
-
maxIterations = 100,
|
|
4940
|
-
tolerance = 1e-6,
|
|
4941
|
-
normalized = true,
|
|
4942
|
-
startVector
|
|
4943
|
-
} = options;
|
|
5196
|
+
const { maxIterations = 100, tolerance = 1e-6, normalized = true, startVector } = options;
|
|
4944
5197
|
const centrality = {};
|
|
4945
5198
|
const nodes = Array.from(graph.nodes());
|
|
4946
5199
|
const nodeIds = nodes.map((node) => node.id);
|
|
@@ -5031,11 +5284,7 @@ function nodeEigenvectorCentrality(graph, nodeId, options = {}) {
|
|
|
5031
5284
|
return centrality[nodeId.toString()] ?? 0;
|
|
5032
5285
|
}
|
|
5033
5286
|
function hits(graph, options = {}) {
|
|
5034
|
-
const {
|
|
5035
|
-
maxIterations = 100,
|
|
5036
|
-
tolerance = 1e-6,
|
|
5037
|
-
normalized = true
|
|
5038
|
-
} = options;
|
|
5287
|
+
const { maxIterations = 100, tolerance = 1e-6, normalized = true } = options;
|
|
5039
5288
|
const hubs = {};
|
|
5040
5289
|
const authorities = {};
|
|
5041
5290
|
const nodes = Array.from(graph.nodes());
|
|
@@ -5159,13 +5408,7 @@ function nodeHITS(graph, nodeId, options = {}) {
|
|
|
5159
5408
|
};
|
|
5160
5409
|
}
|
|
5161
5410
|
function katzCentrality(graph, options = {}) {
|
|
5162
|
-
const {
|
|
5163
|
-
alpha = 0.1,
|
|
5164
|
-
beta = 1,
|
|
5165
|
-
maxIterations = 100,
|
|
5166
|
-
tolerance = 1e-6,
|
|
5167
|
-
normalized = true
|
|
5168
|
-
} = options;
|
|
5411
|
+
const { alpha = 0.1, beta = 1, maxIterations = 100, tolerance = 1e-6, normalized = true } = options;
|
|
5169
5412
|
const centrality = {};
|
|
5170
5413
|
const nodes = Array.from(graph.nodes());
|
|
5171
5414
|
const nodeIds = nodes.map((node) => node.id);
|
|
@@ -5229,6 +5472,10 @@ function nodeKatzCentrality(graph, nodeId, options = {}) {
|
|
|
5229
5472
|
return centrality[nodeId.toString()] ?? 0;
|
|
5230
5473
|
}
|
|
5231
5474
|
class SimpleDeltaPageRank {
|
|
5475
|
+
/**
|
|
5476
|
+
* Creates a new SimpleDeltaPageRank instance for the given graph.
|
|
5477
|
+
* @param graph - The directed graph to compute PageRank on
|
|
5478
|
+
*/
|
|
5232
5479
|
constructor(graph) {
|
|
5233
5480
|
this.previousScores = null;
|
|
5234
5481
|
if (!graph.isDirected) {
|
|
@@ -5237,14 +5484,18 @@ class SimpleDeltaPageRank {
|
|
|
5237
5484
|
this.graph = graph;
|
|
5238
5485
|
this.nodeCount = graph.nodeCount;
|
|
5239
5486
|
}
|
|
5487
|
+
/**
|
|
5488
|
+
* Computes PageRank scores for all nodes in the graph using power iteration.
|
|
5489
|
+
* @param options - Configuration options for PageRank computation
|
|
5490
|
+
* @param options.dampingFactor - Probability of following a link (default: 0.85)
|
|
5491
|
+
* @param options.tolerance - Convergence tolerance threshold (default: 1e-6)
|
|
5492
|
+
* @param options.maxIterations - Maximum number of iterations (default: 100)
|
|
5493
|
+
* @param options.personalization - Personalization vector for Personalized PageRank
|
|
5494
|
+
* @param options.weight - Edge attribute name for weighted PageRank
|
|
5495
|
+
* @returns Map of node IDs to their PageRank scores
|
|
5496
|
+
*/
|
|
5240
5497
|
compute(options = {}) {
|
|
5241
|
-
const {
|
|
5242
|
-
dampingFactor = 0.85,
|
|
5243
|
-
tolerance = 1e-6,
|
|
5244
|
-
maxIterations = 100,
|
|
5245
|
-
personalization,
|
|
5246
|
-
weight
|
|
5247
|
-
} = options;
|
|
5498
|
+
const { dampingFactor = 0.85, tolerance = 1e-6, maxIterations = 100, personalization, weight } = options;
|
|
5248
5499
|
if (this.nodeCount === 0) {
|
|
5249
5500
|
return /* @__PURE__ */ new Map();
|
|
5250
5501
|
}
|
|
@@ -5348,22 +5599,20 @@ class SimpleDeltaPageRank {
|
|
|
5348
5599
|
/**
|
|
5349
5600
|
* Perform incremental update after graph modification.
|
|
5350
5601
|
* This is where delta-based approach provides significant speedup.
|
|
5351
|
-
*
|
|
5352
|
-
* @param
|
|
5353
|
-
* @param options
|
|
5602
|
+
* @param modifiedNodes - Set of nodes that were modified (edges added/removed)
|
|
5603
|
+
* @param options - Computation options
|
|
5604
|
+
* @param options.dampingFactor - Probability of following a link (default: 0.85)
|
|
5605
|
+
* @param options.tolerance - Convergence tolerance threshold (default: 1e-6)
|
|
5606
|
+
* @param options.maxIterations - Maximum number of iterations (default: 100)
|
|
5607
|
+
* @param options.personalization - Personalization vector for Personalized PageRank
|
|
5608
|
+
* @param options.weight - Edge attribute name for weighted PageRank
|
|
5354
5609
|
* @returns Updated PageRank scores
|
|
5355
5610
|
*/
|
|
5356
5611
|
update(modifiedNodes, options = {}) {
|
|
5357
5612
|
if (!this.previousScores) {
|
|
5358
5613
|
return this.compute(options);
|
|
5359
5614
|
}
|
|
5360
|
-
const {
|
|
5361
|
-
dampingFactor = 0.85,
|
|
5362
|
-
tolerance = 1e-6,
|
|
5363
|
-
maxIterations = 100,
|
|
5364
|
-
personalization,
|
|
5365
|
-
weight
|
|
5366
|
-
} = options;
|
|
5615
|
+
const { dampingFactor = 0.85, tolerance = 1e-6, maxIterations = 100, personalization, weight } = options;
|
|
5367
5616
|
const scores = new Map(this.previousScores);
|
|
5368
5617
|
const newScores = /* @__PURE__ */ new Map();
|
|
5369
5618
|
for (const node of this.graph.nodes()) {
|
|
@@ -5679,6 +5928,10 @@ function normalizeRanks(ranks) {
|
|
|
5679
5928
|
}
|
|
5680
5929
|
}
|
|
5681
5930
|
class UnionFind {
|
|
5931
|
+
/**
|
|
5932
|
+
* Creates a new UnionFind data structure with the given elements.
|
|
5933
|
+
* @param elements - Array of elements to initialize the data structure with
|
|
5934
|
+
*/
|
|
5682
5935
|
constructor(elements) {
|
|
5683
5936
|
this.parent = /* @__PURE__ */ new Map();
|
|
5684
5937
|
this.rank = /* @__PURE__ */ new Map();
|
|
@@ -5690,6 +5943,8 @@ class UnionFind {
|
|
|
5690
5943
|
}
|
|
5691
5944
|
/**
|
|
5692
5945
|
* Find the root of the set containing the element with path compression
|
|
5946
|
+
* @param element - The element to find the root for
|
|
5947
|
+
* @returns The root element of the set containing the given element
|
|
5693
5948
|
*/
|
|
5694
5949
|
find(element) {
|
|
5695
5950
|
const parent = this.parent.get(element);
|
|
@@ -5707,6 +5962,8 @@ class UnionFind {
|
|
|
5707
5962
|
}
|
|
5708
5963
|
/**
|
|
5709
5964
|
* Union two sets using union by rank
|
|
5965
|
+
* @param elementA - The first element whose set should be merged
|
|
5966
|
+
* @param elementB - The second element whose set should be merged
|
|
5710
5967
|
*/
|
|
5711
5968
|
union(elementA, elementB) {
|
|
5712
5969
|
const rootA = this.find(elementA);
|
|
@@ -5731,6 +5988,9 @@ class UnionFind {
|
|
|
5731
5988
|
}
|
|
5732
5989
|
/**
|
|
5733
5990
|
* Check if two elements are in the same connected component
|
|
5991
|
+
* @param elementA - The first element to check
|
|
5992
|
+
* @param elementB - The second element to check
|
|
5993
|
+
* @returns True if both elements are in the same component, false otherwise
|
|
5734
5994
|
*/
|
|
5735
5995
|
connected(elementA, elementB) {
|
|
5736
5996
|
try {
|
|
@@ -5741,12 +6001,15 @@ class UnionFind {
|
|
|
5741
6001
|
}
|
|
5742
6002
|
/**
|
|
5743
6003
|
* Get the number of connected components
|
|
6004
|
+
* @returns The current count of disjoint components
|
|
5744
6005
|
*/
|
|
5745
6006
|
getComponentCount() {
|
|
5746
6007
|
return this.componentCount;
|
|
5747
6008
|
}
|
|
5748
6009
|
/**
|
|
5749
6010
|
* Get all elements that belong to the same component as the given element
|
|
6011
|
+
* @param element - The element whose component should be retrieved
|
|
6012
|
+
* @returns Array of all elements in the same component
|
|
5750
6013
|
*/
|
|
5751
6014
|
getComponent(element) {
|
|
5752
6015
|
const root = this.find(element);
|
|
@@ -5760,6 +6023,7 @@ class UnionFind {
|
|
|
5760
6023
|
}
|
|
5761
6024
|
/**
|
|
5762
6025
|
* Get all connected components as separate arrays
|
|
6026
|
+
* @returns Array of arrays, where each inner array contains elements of one component
|
|
5763
6027
|
*/
|
|
5764
6028
|
getAllComponents() {
|
|
5765
6029
|
const componentMap = /* @__PURE__ */ new Map();
|
|
@@ -5777,12 +6041,15 @@ class UnionFind {
|
|
|
5777
6041
|
}
|
|
5778
6042
|
/**
|
|
5779
6043
|
* Get the size of the component containing the given element
|
|
6044
|
+
* @param element - The element whose component size should be retrieved
|
|
6045
|
+
* @returns The number of elements in the component containing the given element
|
|
5780
6046
|
*/
|
|
5781
6047
|
getComponentSize(element) {
|
|
5782
6048
|
return this.getComponent(element).length;
|
|
5783
6049
|
}
|
|
5784
6050
|
/**
|
|
5785
6051
|
* Add a new element to the data structure
|
|
6052
|
+
* @param element - The element to add as a new singleton set
|
|
5786
6053
|
*/
|
|
5787
6054
|
addElement(element) {
|
|
5788
6055
|
if (this.parent.has(element)) {
|
|
@@ -5794,12 +6061,15 @@ class UnionFind {
|
|
|
5794
6061
|
}
|
|
5795
6062
|
/**
|
|
5796
6063
|
* Check if an element exists in the data structure
|
|
6064
|
+
* @param element - The element to check for
|
|
6065
|
+
* @returns True if the element exists, false otherwise
|
|
5797
6066
|
*/
|
|
5798
6067
|
hasElement(element) {
|
|
5799
6068
|
return this.parent.has(element);
|
|
5800
6069
|
}
|
|
5801
6070
|
/**
|
|
5802
6071
|
* Get the total number of elements
|
|
6072
|
+
* @returns The total count of all elements in the data structure
|
|
5803
6073
|
*/
|
|
5804
6074
|
size() {
|
|
5805
6075
|
return this.parent.size;
|
|
@@ -5807,7 +6077,9 @@ class UnionFind {
|
|
|
5807
6077
|
}
|
|
5808
6078
|
function connectedComponents(graph) {
|
|
5809
6079
|
if (graph.isDirected) {
|
|
5810
|
-
throw new Error(
|
|
6080
|
+
throw new Error(
|
|
6081
|
+
"Connected components algorithm requires an undirected graph. Use stronglyConnectedComponents for directed graphs."
|
|
6082
|
+
);
|
|
5811
6083
|
}
|
|
5812
6084
|
const nodes = Array.from(graph.nodes()).map((node) => node.id);
|
|
5813
6085
|
if (nodes.length === 0) {
|
|
@@ -5854,9 +6126,7 @@ function largestConnectedComponent(graph) {
|
|
|
5854
6126
|
if (components.length === 0) {
|
|
5855
6127
|
return [];
|
|
5856
6128
|
}
|
|
5857
|
-
return components.reduce(
|
|
5858
|
-
(largest, current) => current.length > largest.length ? current : largest
|
|
5859
|
-
);
|
|
6129
|
+
return components.reduce((largest, current) => current.length > largest.length ? current : largest);
|
|
5860
6130
|
}
|
|
5861
6131
|
function getConnectedComponent(graph, nodeId) {
|
|
5862
6132
|
if (!graph.hasNode(nodeId)) {
|
|
@@ -5932,7 +6202,9 @@ function isStronglyConnected(graph) {
|
|
|
5932
6202
|
}
|
|
5933
6203
|
function weaklyConnectedComponents(graph) {
|
|
5934
6204
|
if (!graph.isDirected) {
|
|
5935
|
-
throw new Error(
|
|
6205
|
+
throw new Error(
|
|
6206
|
+
"Weakly connected components are for directed graphs. Use connectedComponents for undirected graphs."
|
|
6207
|
+
);
|
|
5936
6208
|
}
|
|
5937
6209
|
const nodes = Array.from(graph.nodes()).map((node) => node.id);
|
|
5938
6210
|
if (nodes.length === 0) {
|
|
@@ -6116,9 +6388,7 @@ function girvanNewman(graph, options = {}) {
|
|
|
6116
6388
|
workingGraph.removeEdge(source, target);
|
|
6117
6389
|
}
|
|
6118
6390
|
components = getConnectedComponentsResult(workingGraph);
|
|
6119
|
-
const validCommunities = components.components.filter(
|
|
6120
|
-
(community) => community.length >= minCommunitySize
|
|
6121
|
-
);
|
|
6391
|
+
const validCommunities = components.components.filter((community) => community.length >= minCommunitySize);
|
|
6122
6392
|
const modularity = calculateModularity$3(graph, components.componentMap);
|
|
6123
6393
|
dendrogram.push({
|
|
6124
6394
|
communities: validCommunities,
|
|
@@ -6324,6 +6594,10 @@ function graphToMap(graph) {
|
|
|
6324
6594
|
return map;
|
|
6325
6595
|
}
|
|
6326
6596
|
class SeededRandom {
|
|
6597
|
+
/**
|
|
6598
|
+
* Creates a new SeededRandom instance with the given seed.
|
|
6599
|
+
* @param seed - The seed value for reproducible random number generation
|
|
6600
|
+
*/
|
|
6327
6601
|
constructor(seed) {
|
|
6328
6602
|
this.m = 2147483648;
|
|
6329
6603
|
this.a = 1103515245;
|
|
@@ -6332,6 +6606,7 @@ class SeededRandom {
|
|
|
6332
6606
|
}
|
|
6333
6607
|
/**
|
|
6334
6608
|
* Generate next random number between 0 and 1
|
|
6609
|
+
* @returns A pseudo-random number in the range [0, 1]
|
|
6335
6610
|
*/
|
|
6336
6611
|
next() {
|
|
6337
6612
|
this.seed = (this.a * this.seed + this.c) % this.m;
|
|
@@ -6339,6 +6614,8 @@ class SeededRandom {
|
|
|
6339
6614
|
}
|
|
6340
6615
|
/**
|
|
6341
6616
|
* Create a generator function for backward compatibility
|
|
6617
|
+
* @param seed - The seed value for reproducible random number generation
|
|
6618
|
+
* @returns A function that returns the next random number when called
|
|
6342
6619
|
*/
|
|
6343
6620
|
static createGenerator(seed) {
|
|
6344
6621
|
const rng = new SeededRandom(seed);
|
|
@@ -6373,10 +6650,7 @@ function euclideanDistance(a, b) {
|
|
|
6373
6650
|
return Math.sqrt(sum);
|
|
6374
6651
|
}
|
|
6375
6652
|
function labelPropagationImpl(graph, options = {}) {
|
|
6376
|
-
const {
|
|
6377
|
-
maxIterations = 100,
|
|
6378
|
-
randomSeed = 42
|
|
6379
|
-
} = options;
|
|
6653
|
+
const { maxIterations = 100, randomSeed = 42 } = options;
|
|
6380
6654
|
if (graph.size === 0) {
|
|
6381
6655
|
return {
|
|
6382
6656
|
communities: /* @__PURE__ */ new Map(),
|
|
@@ -6459,9 +6733,7 @@ function labelPropagationImpl(graph, options = {}) {
|
|
|
6459
6733
|
};
|
|
6460
6734
|
}
|
|
6461
6735
|
function labelPropagationAsyncImpl(graph, options = {}) {
|
|
6462
|
-
const {
|
|
6463
|
-
maxIterations = 100
|
|
6464
|
-
} = options;
|
|
6736
|
+
const { maxIterations = 100 } = options;
|
|
6465
6737
|
if (graph.size === 0) {
|
|
6466
6738
|
return {
|
|
6467
6739
|
communities: /* @__PURE__ */ new Map(),
|
|
@@ -6535,10 +6807,7 @@ function labelPropagationAsyncImpl(graph, options = {}) {
|
|
|
6535
6807
|
};
|
|
6536
6808
|
}
|
|
6537
6809
|
function labelPropagationSemiSupervisedImpl(graph, seedLabels, options = {}) {
|
|
6538
|
-
const {
|
|
6539
|
-
maxIterations = 100,
|
|
6540
|
-
randomSeed = 42
|
|
6541
|
-
} = options;
|
|
6810
|
+
const { maxIterations = 100, randomSeed = 42 } = options;
|
|
6542
6811
|
const random = SeededRandom.createGenerator(randomSeed);
|
|
6543
6812
|
const labels = /* @__PURE__ */ new Map();
|
|
6544
6813
|
const nodes = Array.from(graph.keys());
|
|
@@ -6620,12 +6889,7 @@ function labelPropagationSemiSupervised(graph, seedLabels, options = {}) {
|
|
|
6620
6889
|
return labelPropagationSemiSupervisedImpl(graphMap, seedLabels, options);
|
|
6621
6890
|
}
|
|
6622
6891
|
function leidenImpl(inputGraph, options = {}) {
|
|
6623
|
-
const {
|
|
6624
|
-
resolution = 1,
|
|
6625
|
-
randomSeed = 42,
|
|
6626
|
-
maxIterations = 100,
|
|
6627
|
-
threshold = 1e-7
|
|
6628
|
-
} = options;
|
|
6892
|
+
const { resolution = 1, randomSeed = 42, maxIterations = 100, threshold = 1e-7 } = options;
|
|
6629
6893
|
if (inputGraph.size === 0) {
|
|
6630
6894
|
return {
|
|
6631
6895
|
communities: /* @__PURE__ */ new Map(),
|
|
@@ -6691,10 +6955,7 @@ function leidenImpl(inputGraph, options = {}) {
|
|
|
6691
6955
|
}
|
|
6692
6956
|
}
|
|
6693
6957
|
createAggregateNetwork(currentGraph, communities);
|
|
6694
|
-
const subsetPartition = refinePartition(
|
|
6695
|
-
currentGraph,
|
|
6696
|
-
communities
|
|
6697
|
-
);
|
|
6958
|
+
const subsetPartition = refinePartition(currentGraph, communities);
|
|
6698
6959
|
for (const [node, newCommunity] of subsetPartition) {
|
|
6699
6960
|
communities.set(node, newCommunity);
|
|
6700
6961
|
}
|
|
@@ -6976,6 +7237,10 @@ function leiden(graph, options = {}) {
|
|
|
6976
7237
|
return leidenImpl(graphMap, options);
|
|
6977
7238
|
}
|
|
6978
7239
|
class OptimizedLouvain {
|
|
7240
|
+
/**
|
|
7241
|
+
* Create an optimized Louvain detector for the given graph
|
|
7242
|
+
* @param graph - The input graph to detect communities in
|
|
7243
|
+
*/
|
|
6979
7244
|
constructor(graph) {
|
|
6980
7245
|
this.graph = graph;
|
|
6981
7246
|
this.communities = /* @__PURE__ */ new Map();
|
|
@@ -6991,6 +7256,8 @@ class OptimizedLouvain {
|
|
|
6991
7256
|
}
|
|
6992
7257
|
/**
|
|
6993
7258
|
* Run optimized Louvain algorithm
|
|
7259
|
+
* @param options - Algorithm configuration options
|
|
7260
|
+
* @returns Community detection result with communities, modularity, and iterations
|
|
6994
7261
|
*/
|
|
6995
7262
|
detectCommunities(options = {}) {
|
|
6996
7263
|
const {
|
|
@@ -7074,6 +7341,7 @@ class OptimizedLouvain {
|
|
|
7074
7341
|
}
|
|
7075
7342
|
/**
|
|
7076
7343
|
* Get nodes ordered by importance (degree * log(weight))
|
|
7344
|
+
* @returns Array of node IDs sorted by descending importance
|
|
7077
7345
|
*/
|
|
7078
7346
|
getNodesInImportanceOrder() {
|
|
7079
7347
|
const nodeImportance = /* @__PURE__ */ new Map();
|
|
@@ -7086,6 +7354,12 @@ class OptimizedLouvain {
|
|
|
7086
7354
|
}
|
|
7087
7355
|
/**
|
|
7088
7356
|
* Perform local moving phase with optimizations
|
|
7357
|
+
* @param nodes - Array of node IDs to process
|
|
7358
|
+
* @param options - Local moving options
|
|
7359
|
+
* @param options.pruneLeaves - Whether to skip leaf nodes
|
|
7360
|
+
* @param options.threshold - Minimum gain threshold for moves
|
|
7361
|
+
* @param options.resolution - Resolution parameter for modularity
|
|
7362
|
+
* @returns True if any improvement was made, false otherwise
|
|
7089
7363
|
*/
|
|
7090
7364
|
performLocalMoving(nodes, options) {
|
|
7091
7365
|
const { pruneLeaves, threshold, resolution } = options;
|
|
@@ -7129,6 +7403,8 @@ class OptimizedLouvain {
|
|
|
7129
7403
|
}
|
|
7130
7404
|
/**
|
|
7131
7405
|
* Check if node is a leaf (degree 1)
|
|
7406
|
+
* @param nodeId - The node ID to check
|
|
7407
|
+
* @returns True if the node has degree 1, false otherwise
|
|
7132
7408
|
*/
|
|
7133
7409
|
isLeafNode(nodeId) {
|
|
7134
7410
|
const degree = this.nodeDegrees.get(nodeId) ?? 0;
|
|
@@ -7136,12 +7412,19 @@ class OptimizedLouvain {
|
|
|
7136
7412
|
}
|
|
7137
7413
|
/**
|
|
7138
7414
|
* Get adaptive threshold that decreases with iterations
|
|
7415
|
+
* @param iteration - Current iteration number
|
|
7416
|
+
* @param baseThreshold - Base threshold value to scale
|
|
7417
|
+
* @returns Adaptive threshold value that decays over iterations
|
|
7139
7418
|
*/
|
|
7140
7419
|
getAdaptiveThreshold(iteration, baseThreshold) {
|
|
7141
7420
|
return baseThreshold * Math.pow(0.5, iteration / 10);
|
|
7142
7421
|
}
|
|
7143
7422
|
/**
|
|
7144
7423
|
* Calculate modularity gain from moving a node to a community
|
|
7424
|
+
* @param nodeId - The node ID to move
|
|
7425
|
+
* @param targetCommunity - The target community ID
|
|
7426
|
+
* @param resolution - Resolution parameter for modularity calculation
|
|
7427
|
+
* @returns The modularity gain from moving the node
|
|
7145
7428
|
*/
|
|
7146
7429
|
calculateModularityGain(nodeId, targetCommunity, resolution) {
|
|
7147
7430
|
const nodeWeight = this.nodeWeights.get(nodeId) ?? 0;
|
|
@@ -7166,6 +7449,8 @@ class OptimizedLouvain {
|
|
|
7166
7449
|
}
|
|
7167
7450
|
/**
|
|
7168
7451
|
* Remove node from community (for gain calculation)
|
|
7452
|
+
* @param nodeId - The node ID to remove
|
|
7453
|
+
* @param community - The community ID to remove from
|
|
7169
7454
|
*/
|
|
7170
7455
|
removeNodeFromCommunity(nodeId, community) {
|
|
7171
7456
|
const nodeWeight = this.nodeWeights.get(nodeId) ?? 0;
|
|
@@ -7174,6 +7459,8 @@ class OptimizedLouvain {
|
|
|
7174
7459
|
}
|
|
7175
7460
|
/**
|
|
7176
7461
|
* Add node to community
|
|
7462
|
+
* @param nodeId - The node ID to add
|
|
7463
|
+
* @param community - The community ID to add to
|
|
7177
7464
|
*/
|
|
7178
7465
|
addNodeToCommunity(nodeId, community) {
|
|
7179
7466
|
const nodeWeight = this.nodeWeights.get(nodeId) ?? 0;
|
|
@@ -7182,6 +7469,8 @@ class OptimizedLouvain {
|
|
|
7182
7469
|
}
|
|
7183
7470
|
/**
|
|
7184
7471
|
* Get neighboring communities of a node
|
|
7472
|
+
* @param nodeId - The node ID to find neighbor communities for
|
|
7473
|
+
* @returns Set of community IDs that neighbors belong to
|
|
7185
7474
|
*/
|
|
7186
7475
|
getNeighborCommunities(nodeId) {
|
|
7187
7476
|
const communities = /* @__PURE__ */ new Set();
|
|
@@ -7203,6 +7492,8 @@ class OptimizedLouvain {
|
|
|
7203
7492
|
}
|
|
7204
7493
|
/**
|
|
7205
7494
|
* Calculate total modularity
|
|
7495
|
+
* @param resolution - Resolution parameter for modularity calculation
|
|
7496
|
+
* @returns The modularity score of the current partition
|
|
7206
7497
|
*/
|
|
7207
7498
|
calculateModularity(resolution) {
|
|
7208
7499
|
if (this.totalWeight === 0) {
|
|
@@ -7233,6 +7524,7 @@ class OptimizedLouvain {
|
|
|
7233
7524
|
}
|
|
7234
7525
|
/**
|
|
7235
7526
|
* Get pruning statistics
|
|
7527
|
+
* @returns Statistics about nodes pruned during optimization
|
|
7236
7528
|
*/
|
|
7237
7529
|
getPruningStats() {
|
|
7238
7530
|
return { ...this.pruningStats };
|
|
@@ -7433,6 +7725,10 @@ function extractCommunities(communities) {
|
|
|
7433
7725
|
return Array.from(communityMap.values());
|
|
7434
7726
|
}
|
|
7435
7727
|
class MinPriorityQueue {
|
|
7728
|
+
/**
|
|
7729
|
+
* Creates a new MinPriorityQueue instance.
|
|
7730
|
+
* @param compareFunction - Custom comparison function. Returns negative if a has higher priority than b.
|
|
7731
|
+
*/
|
|
7436
7732
|
constructor(compareFunction) {
|
|
7437
7733
|
this.heap = [];
|
|
7438
7734
|
this.compare = compareFunction ?? ((a, b) => this.defaultCompare(a, b));
|
|
@@ -7498,10 +7794,18 @@ class MinPriorityQueue {
|
|
|
7498
7794
|
}
|
|
7499
7795
|
}
|
|
7500
7796
|
}
|
|
7797
|
+
/**
|
|
7798
|
+
* Insert a value into the priority queue.
|
|
7799
|
+
* @param value - The value to insert
|
|
7800
|
+
*/
|
|
7501
7801
|
insert(value) {
|
|
7502
7802
|
this.heap.push(value);
|
|
7503
7803
|
this.heapifyUp(this.heap.length - 1);
|
|
7504
7804
|
}
|
|
7805
|
+
/**
|
|
7806
|
+
* Remove and return the minimum value from the priority queue.
|
|
7807
|
+
* @returns The minimum value, or undefined if the queue is empty
|
|
7808
|
+
*/
|
|
7505
7809
|
extractMin() {
|
|
7506
7810
|
if (this.heap.length === 0) {
|
|
7507
7811
|
return void 0;
|
|
@@ -7517,20 +7821,35 @@ class MinPriorityQueue {
|
|
|
7517
7821
|
}
|
|
7518
7822
|
return min;
|
|
7519
7823
|
}
|
|
7824
|
+
/**
|
|
7825
|
+
* View the minimum value without removing it.
|
|
7826
|
+
* @returns The minimum value, or undefined if the queue is empty
|
|
7827
|
+
*/
|
|
7520
7828
|
peek() {
|
|
7521
7829
|
return this.heap[0];
|
|
7522
7830
|
}
|
|
7831
|
+
/**
|
|
7832
|
+
* Check if the priority queue is empty.
|
|
7833
|
+
* @returns True if the queue has no items, false otherwise
|
|
7834
|
+
*/
|
|
7523
7835
|
isEmpty() {
|
|
7524
7836
|
return this.heap.length === 0;
|
|
7525
7837
|
}
|
|
7838
|
+
/**
|
|
7839
|
+
* Get the number of items in the priority queue.
|
|
7840
|
+
* @returns The number of items currently in the queue
|
|
7841
|
+
*/
|
|
7526
7842
|
size() {
|
|
7527
7843
|
return this.heap.length;
|
|
7528
7844
|
}
|
|
7529
7845
|
}
|
|
7530
7846
|
const pathfindingUtils = {
|
|
7531
7847
|
/**
|
|
7532
|
-
|
|
7533
|
-
|
|
7848
|
+
* Reconstructs a path from start to goal using the cameFrom map
|
|
7849
|
+
* @param cameFrom - Map of each node to its predecessor on the path
|
|
7850
|
+
* @param goal - The goal node to reconstruct the path to
|
|
7851
|
+
* @returns An array of nodes representing the path from start to goal
|
|
7852
|
+
*/
|
|
7534
7853
|
reconstructPath(cameFrom, goal) {
|
|
7535
7854
|
const path = [goal];
|
|
7536
7855
|
let current = goal;
|
|
@@ -7545,8 +7864,12 @@ const pathfindingUtils = {
|
|
|
7545
7864
|
return path;
|
|
7546
7865
|
},
|
|
7547
7866
|
/**
|
|
7548
|
-
|
|
7549
|
-
|
|
7867
|
+
* Creates a grid graph for testing pathfinding algorithms
|
|
7868
|
+
* @param width - The width of the grid
|
|
7869
|
+
* @param height - The height of the grid
|
|
7870
|
+
* @param obstacles - Set of obstacle coordinates as "x,y" strings
|
|
7871
|
+
* @returns A grid graph with 4-directional movement
|
|
7872
|
+
*/
|
|
7550
7873
|
createGridGraph(width, height, obstacles = /* @__PURE__ */ new Set()) {
|
|
7551
7874
|
const graph = /* @__PURE__ */ new Map();
|
|
7552
7875
|
for (let x = 0; x < width; x++) {
|
|
@@ -7581,8 +7904,10 @@ const pathfindingUtils = {
|
|
|
7581
7904
|
return graph;
|
|
7582
7905
|
},
|
|
7583
7906
|
/**
|
|
7584
|
-
|
|
7585
|
-
|
|
7907
|
+
* Parses a grid coordinate string
|
|
7908
|
+
* @param coord - The coordinate string in "x,y" format
|
|
7909
|
+
* @returns A tuple of [x, y] coordinates
|
|
7910
|
+
*/
|
|
7586
7911
|
parseGridCoordinate(coord) {
|
|
7587
7912
|
const parts = coord.split(",").map(Number);
|
|
7588
7913
|
const x = parts[0];
|
|
@@ -7722,27 +8047,38 @@ function astarWithDetails(graph, start, goal, heuristic) {
|
|
|
7722
8047
|
}
|
|
7723
8048
|
const heuristics = {
|
|
7724
8049
|
/**
|
|
7725
|
-
|
|
7726
|
-
|
|
8050
|
+
* Manhattan distance heuristic for grid-based graphs
|
|
8051
|
+
* @param a - First coordinate as [x, y] tuple
|
|
8052
|
+
* @param b - Second coordinate as [x, y] tuple
|
|
8053
|
+
* @returns The Manhattan distance between the two points
|
|
8054
|
+
*/
|
|
7727
8055
|
manhattan: (a, b) => {
|
|
7728
8056
|
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
|
|
7729
8057
|
},
|
|
7730
8058
|
/**
|
|
7731
|
-
|
|
7732
|
-
|
|
8059
|
+
* Euclidean distance heuristic
|
|
8060
|
+
* @param a - First coordinate as [x, y] tuple
|
|
8061
|
+
* @param b - Second coordinate as [x, y] tuple
|
|
8062
|
+
* @returns The Euclidean distance between the two points
|
|
8063
|
+
*/
|
|
7733
8064
|
euclidean: (a, b) => {
|
|
7734
8065
|
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
|
|
7735
8066
|
},
|
|
7736
8067
|
/**
|
|
7737
|
-
|
|
7738
|
-
|
|
8068
|
+
* Chebyshev distance heuristic (diagonal movement allowed)
|
|
8069
|
+
* @param a - First coordinate as [x, y] tuple
|
|
8070
|
+
* @param b - Second coordinate as [x, y] tuple
|
|
8071
|
+
* @returns The Chebyshev distance between the two points
|
|
8072
|
+
*/
|
|
7739
8073
|
chebyshev: (a, b) => {
|
|
7740
8074
|
return Math.max(Math.abs(a[0] - b[0]), Math.abs(a[1] - b[1]));
|
|
7741
8075
|
},
|
|
7742
8076
|
/**
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
8077
|
+
* Zero heuristic (makes A* behave like Dijkstra)
|
|
8078
|
+
* @param _a - First node (unused)
|
|
8079
|
+
* @param _b - Second node (unused)
|
|
8080
|
+
* @returns Always returns 0
|
|
8081
|
+
*/
|
|
7746
8082
|
zero: (_a, _b) => 0
|
|
7747
8083
|
};
|
|
7748
8084
|
function createResidualGraph(graph) {
|
|
@@ -8900,10 +9236,12 @@ function extractClusters(matrix, nodeIds) {
|
|
|
8900
9236
|
}
|
|
8901
9237
|
}
|
|
8902
9238
|
if (clusterNodes.length > 0) {
|
|
8903
|
-
communities.push(
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
9239
|
+
communities.push(
|
|
9240
|
+
clusterNodes.map((idx) => {
|
|
9241
|
+
const nodeId = nodeIds[idx];
|
|
9242
|
+
return nodeId;
|
|
9243
|
+
}).filter((node) => node !== void 0)
|
|
9244
|
+
);
|
|
8907
9245
|
clusterIndex++;
|
|
8908
9246
|
}
|
|
8909
9247
|
}
|
|
@@ -8943,12 +9281,7 @@ function calculateMCLModularity(graph, communities) {
|
|
|
8943
9281
|
return modularity / (2 * m);
|
|
8944
9282
|
}
|
|
8945
9283
|
function spectralClustering(graph, options) {
|
|
8946
|
-
const {
|
|
8947
|
-
k,
|
|
8948
|
-
laplacianType = "normalized",
|
|
8949
|
-
maxIterations = 100,
|
|
8950
|
-
tolerance = 1e-4
|
|
8951
|
-
} = options;
|
|
9284
|
+
const { k, laplacianType = "normalized", maxIterations = 100, tolerance = 1e-4 } = options;
|
|
8952
9285
|
if (k < 1 || !Number.isInteger(k)) {
|
|
8953
9286
|
throw new Error("k must be a positive integer");
|
|
8954
9287
|
}
|
|
@@ -9738,10 +10071,7 @@ function adamicAdarScore(graph, source, target, options = {}) {
|
|
|
9738
10071
|
const commonNeighborsSet = directed ? getIntermediateNodes(graph, source, target) : getCommonNeighbors(graph, source, target, false);
|
|
9739
10072
|
let score = 0;
|
|
9740
10073
|
for (const neighbor of commonNeighborsSet) {
|
|
9741
|
-
const degree = directed ? graph.outDegree(neighbor) : (
|
|
9742
|
-
// Use out-degree for directed graphs
|
|
9743
|
-
graph.degree(neighbor)
|
|
9744
|
-
);
|
|
10074
|
+
const degree = directed ? graph.outDegree(neighbor) : graph.degree(neighbor);
|
|
9745
10075
|
if (degree > 1) {
|
|
9746
10076
|
score += 1 / Math.log(degree);
|
|
9747
10077
|
} else if (degree === 1) {
|
|
@@ -9751,11 +10081,7 @@ function adamicAdarScore(graph, source, target, options = {}) {
|
|
|
9751
10081
|
return score;
|
|
9752
10082
|
}
|
|
9753
10083
|
function adamicAdarPrediction(graph, options = {}) {
|
|
9754
|
-
const {
|
|
9755
|
-
directed = false,
|
|
9756
|
-
includeExisting = false,
|
|
9757
|
-
topK
|
|
9758
|
-
} = options;
|
|
10084
|
+
const { directed = false, includeExisting = false, topK } = options;
|
|
9759
10085
|
const scores = [];
|
|
9760
10086
|
const nodes = Array.from(graph.nodes()).map((n) => n.id);
|
|
9761
10087
|
for (let i = 0; i < nodes.length; i++) {
|
|
@@ -9794,12 +10120,7 @@ function getTopAdamicAdarCandidatesForNode(graph, node, options = {}) {
|
|
|
9794
10120
|
if (!graph.hasNode(node)) {
|
|
9795
10121
|
return [];
|
|
9796
10122
|
}
|
|
9797
|
-
const {
|
|
9798
|
-
directed = false,
|
|
9799
|
-
includeExisting = false,
|
|
9800
|
-
topK = 10,
|
|
9801
|
-
candidates
|
|
9802
|
-
} = options;
|
|
10123
|
+
const { directed = false, includeExisting = false, topK = 10, candidates } = options;
|
|
9803
10124
|
const scores = [];
|
|
9804
10125
|
const targetNodes = candidates ?? Array.from(graph.nodes()).map((n) => n.id);
|
|
9805
10126
|
for (const target of targetNodes) {
|
|
@@ -9938,11 +10259,7 @@ function commonNeighborsScore(graph, source, target, options = {}) {
|
|
|
9938
10259
|
return commonNeighborsSet.size;
|
|
9939
10260
|
}
|
|
9940
10261
|
function commonNeighborsPrediction(graph, options = {}) {
|
|
9941
|
-
const {
|
|
9942
|
-
directed = false,
|
|
9943
|
-
includeExisting = false,
|
|
9944
|
-
topK
|
|
9945
|
-
} = options;
|
|
10262
|
+
const { directed = false, includeExisting = false, topK } = options;
|
|
9946
10263
|
const scores = [];
|
|
9947
10264
|
const nodes = Array.from(graph.nodes()).map((n) => n.id);
|
|
9948
10265
|
for (let i = 0; i < nodes.length; i++) {
|
|
@@ -9981,12 +10298,7 @@ function getTopCandidatesForNode(graph, node, options = {}) {
|
|
|
9981
10298
|
if (!graph.hasNode(node)) {
|
|
9982
10299
|
return [];
|
|
9983
10300
|
}
|
|
9984
|
-
const {
|
|
9985
|
-
directed = false,
|
|
9986
|
-
includeExisting = false,
|
|
9987
|
-
topK = 10,
|
|
9988
|
-
candidates
|
|
9989
|
-
} = options;
|
|
10301
|
+
const { directed = false, includeExisting = false, topK = 10, candidates } = options;
|
|
9990
10302
|
const scores = [];
|
|
9991
10303
|
const targetNodes = candidates ?? Array.from(graph.nodes()).map((n) => n.id);
|
|
9992
10304
|
for (const target of targetNodes) {
|
|
@@ -10056,14 +10368,7 @@ function evaluateCommonNeighbors(trainingGraph, testEdges, nonEdges, options = {
|
|
|
10056
10368
|
};
|
|
10057
10369
|
}
|
|
10058
10370
|
function syncClustering(graph, config) {
|
|
10059
|
-
const {
|
|
10060
|
-
numClusters,
|
|
10061
|
-
maxIterations = 100,
|
|
10062
|
-
tolerance = 1e-6,
|
|
10063
|
-
seed = 42,
|
|
10064
|
-
learningRate = 0.01,
|
|
10065
|
-
lambda = 0.1
|
|
10066
|
-
} = config;
|
|
10371
|
+
const { numClusters, maxIterations = 100, tolerance = 1e-6, seed = 42, learningRate = 0.01, lambda = 0.1 } = config;
|
|
10067
10372
|
const originalRandom = Math.random;
|
|
10068
10373
|
const rng = SeededRandom.createGenerator(seed);
|
|
10069
10374
|
Math.random = rng;
|
|
@@ -10079,7 +10384,9 @@ function syncClustering(graph, config) {
|
|
|
10079
10384
|
};
|
|
10080
10385
|
}
|
|
10081
10386
|
if (numClusters <= 0 || numClusters > nodeCount) {
|
|
10082
|
-
throw new Error(
|
|
10387
|
+
throw new Error(
|
|
10388
|
+
`Invalid number of clusters: ${String(numClusters)}. Must be between 1 and ${String(nodeCount)}`
|
|
10389
|
+
);
|
|
10083
10390
|
}
|
|
10084
10391
|
const embeddingDim = Math.min(64, nodeCount);
|
|
10085
10392
|
const embeddings = /* @__PURE__ */ new Map();
|
|
@@ -10344,7 +10651,9 @@ function teraHAC(graph, config = {}) {
|
|
|
10344
10651
|
throw new Error("Cannot cluster empty graph");
|
|
10345
10652
|
}
|
|
10346
10653
|
if (nodeCount > maxNodes) {
|
|
10347
|
-
onWarning(
|
|
10654
|
+
onWarning(
|
|
10655
|
+
`Graph has ${String(nodeCount)} nodes, which exceeds maxNodes (${String(maxNodes)}). Performance may be degraded.`
|
|
10656
|
+
);
|
|
10348
10657
|
}
|
|
10349
10658
|
const clusters = /* @__PURE__ */ new Map();
|
|
10350
10659
|
let nextClusterId = nodeCount;
|
|
@@ -10680,13 +10989,7 @@ function grsbm(graph, config = {}) {
|
|
|
10680
10989
|
if (currentCluster.depth >= maxDepth || currentCluster.members.size < minClusterSize * 2) {
|
|
10681
10990
|
continue;
|
|
10682
10991
|
}
|
|
10683
|
-
const bisectionResult = spectralBisection(
|
|
10684
|
-
graph,
|
|
10685
|
-
currentCluster,
|
|
10686
|
-
numEigenvectors,
|
|
10687
|
-
tolerance,
|
|
10688
|
-
maxIterations
|
|
10689
|
-
);
|
|
10992
|
+
const bisectionResult = spectralBisection(graph, currentCluster, numEigenvectors, tolerance, maxIterations);
|
|
10690
10993
|
if (!bisectionResult) {
|
|
10691
10994
|
continue;
|
|
10692
10995
|
}
|