@graphty/algorithms 1.2.0 → 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 +375 -276
- package/dist/algorithms.d.ts +42 -2
- package/dist/algorithms.js +11402 -2
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +14 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +144 -169
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +35 -1
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +70 -149
- 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 +12 -9
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +57 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +304 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +106 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js +320 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
- 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 +3 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +2 -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 +31 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +61 -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 +67 -57
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/index.d.ts +2 -0
- 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 +11 -4
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +60 -38
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +3 -3
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +70 -44
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +149 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain-optimized.js +338 -0
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
- 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 +35 -81
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +55 -0
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
- package/dist/src/algorithms/community/modularity-utils.js +108 -0
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
- 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 +18 -40
- 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 +11 -12
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +44 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +195 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +20 -2
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +30 -14
- 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 +42 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-unified.js +370 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +77 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.js +402 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +5 -182
- 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 +25 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
- package/dist/src/benchmark-all-algorithms.js +491 -0
- package/dist/src/benchmark-all-algorithms.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +23 -15
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +226 -224
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts +4 -2
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js +2 -2
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +17 -19
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +68 -30
- 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 +11 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +99 -47
- 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 +49 -5
- 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 +20 -12
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +170 -217
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +6 -8
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +34 -12
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -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 +47 -30
- 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 +31 -14
- 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 +193 -0
- package/dist/src/optimized/bit-packed.d.ts.map +1 -0
- package/dist/src/optimized/bit-packed.js +342 -0
- package/dist/src/optimized/bit-packed.js.map +1 -0
- package/dist/src/optimized/csr-graph.d.ts +149 -0
- package/dist/src/optimized/csr-graph.d.ts.map +1 -0
- package/dist/src/optimized/csr-graph.js +404 -0
- package/dist/src/optimized/csr-graph.js.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts +95 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.js +242 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
- package/dist/src/optimized/graph-adapter.d.ts +111 -0
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
- package/dist/src/optimized/graph-adapter.js +194 -0
- package/dist/src/optimized/graph-adapter.js.map +1 -0
- package/dist/src/optimized/index.d.ts +14 -0
- package/dist/src/optimized/index.d.ts.map +1 -0
- package/dist/src/optimized/index.js +30 -0
- package/dist/src/optimized/index.js.map +1 -0
- 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 +28 -23
- 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 +31 -36
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +2 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +39 -7
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/types/index.d.ts +8 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/algorithm-utilities.d.ts +2 -0
- package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
- package/dist/src/utils/algorithm-utilities.js +2 -0
- package/dist/src/utils/algorithm-utilities.js.map +1 -0
- package/dist/src/utils/graph-converters.d.ts +110 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -0
- package/dist/src/utils/graph-converters.js +227 -0
- package/dist/src/utils/graph-converters.js.map +1 -0
- package/dist/src/utils/graph-utilities.d.ts +62 -0
- package/dist/src/utils/graph-utilities.d.ts.map +1 -0
- package/dist/src/utils/graph-utilities.js +148 -0
- package/dist/src/utils/graph-utilities.js.map +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +5 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/math-utilities.d.ts +63 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -0
- package/dist/src/utils/math-utilities.js +132 -0
- package/dist/src/utils/math-utilities.js.map +1 -0
- package/dist/src/utils/matrix-utilities.d.ts +2 -0
- package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
- package/dist/src/utils/matrix-utilities.js +2 -0
- package/dist/src/utils/matrix-utilities.js.map +1 -0
- package/dist/src/utils/optimization-helpers.d.ts +42 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
- package/dist/src/utils/optimization-helpers.js +59 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -0
- 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 +58 -39
- package/src/algorithms/centrality/betweenness.ts +185 -200
- package/src/algorithms/centrality/closeness.ts +93 -194
- package/src/algorithms/centrality/degree.ts +18 -22
- package/src/algorithms/centrality/delta-pagerank-simple.ts +374 -0
- package/src/algorithms/centrality/delta-pagerank.ts +458 -0
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -13
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +91 -16
- package/src/algorithms/community/girvan-newman.ts +90 -86
- package/src/algorithms/community/index.ts +6 -5
- package/src/algorithms/community/label-propagation.ts +69 -48
- package/src/algorithms/community/leiden.ts +88 -65
- package/src/algorithms/community/louvain-optimized.ts +474 -0
- package/src/algorithms/community/louvain.ts +52 -122
- package/src/algorithms/community/modularity-utils.ts +134 -0
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +24 -53
- 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 -28
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
- package/src/algorithms/shortest-path/dijkstra.ts +38 -30
- 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 +437 -0
- package/src/algorithms/traversal/bfs-variants.ts +520 -0
- package/src/algorithms/traversal/bfs.ts +5 -229
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +580 -0
- package/src/clustering/hierarchical.ts +268 -272
- package/src/clustering/index.ts +8 -6
- package/src/clustering/k-core.ts +93 -61
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +130 -72
- package/src/core/graph.ts +58 -18
- 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 +221 -262
- package/src/flow/min-cut.ts +59 -51
- package/src/index.ts +9 -2
- package/src/link-prediction/adamic-adar.ts +75 -74
- package/src/link-prediction/common-neighbors.ts +49 -49
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +390 -0
- package/src/optimized/csr-graph.ts +518 -0
- package/src/optimized/direction-optimized-bfs.ts +310 -0
- package/src/optimized/graph-adapter.ts +248 -0
- package/src/optimized/index.ts +34 -0
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +36 -39
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +41 -54
- package/src/research/terahac.ts +63 -21
- package/src/types/index.ts +8 -0
- package/src/utils/algorithm-utilities.ts +2 -0
- package/src/utils/graph-converters.ts +269 -0
- package/src/utils/graph-utilities.ts +173 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/math-utilities.ts +149 -0
- package/src/utils/matrix-utilities.ts +2 -0
- package/src/utils/optimization-helpers.ts +87 -0
- package/src/utils/priorityQueue.ts +40 -6
package/src/clustering/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export type {
|
|
4
|
-
export {
|
|
5
|
-
export type {
|
|
6
|
-
export {
|
|
1
|
+
export type { ClusterNode, HierarchicalClusteringResult, LinkageMethod } from "./hierarchical.js";
|
|
2
|
+
export { hierarchicalClustering } from "./hierarchical.js";
|
|
3
|
+
export type { KCoreResult } from "./k-core.js";
|
|
4
|
+
export { getKCore, kCoreDecomposition } from "./k-core.js";
|
|
5
|
+
export type { MCLOptions, MCLResult } from "./mcl.js";
|
|
6
|
+
export { calculateMCLModularity, markovClustering } from "./mcl.js";
|
|
7
|
+
export type { SpectralClusteringOptions, SpectralClusteringResult } from "./spectral.js";
|
|
8
|
+
export { spectralClustering } from "./spectral.js";
|
package/src/clustering/k-core.ts
CHANGED
|
@@ -6,6 +6,43 @@
|
|
|
6
6
|
* understanding graph structure.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { Graph } from "../core/graph.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Convert Graph to adjacency set representation
|
|
13
|
+
* @param graph - The input graph to convert
|
|
14
|
+
* @returns Map of node IDs to sets of neighbor node IDs
|
|
15
|
+
*/
|
|
16
|
+
function graphToAdjacencySet(graph: Graph): Map<string, Set<string>> {
|
|
17
|
+
const adjacency = new Map<string, Set<string>>();
|
|
18
|
+
|
|
19
|
+
// Initialize all nodes
|
|
20
|
+
for (const node of graph.nodes()) {
|
|
21
|
+
adjacency.set(String(node.id), new Set());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Add edges
|
|
25
|
+
for (const edge of graph.edges()) {
|
|
26
|
+
const source = String(edge.source);
|
|
27
|
+
const target = String(edge.target);
|
|
28
|
+
|
|
29
|
+
const sourceSet = adjacency.get(source);
|
|
30
|
+
if (sourceSet) {
|
|
31
|
+
sourceSet.add(target);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// For undirected graphs, add reverse edge
|
|
35
|
+
if (!graph.isDirected) {
|
|
36
|
+
const targetSet = adjacency.get(target);
|
|
37
|
+
if (targetSet) {
|
|
38
|
+
targetSet.add(source);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return adjacency;
|
|
44
|
+
}
|
|
45
|
+
|
|
9
46
|
export interface KCoreResult<T> {
|
|
10
47
|
cores: Map<number, Set<T>>;
|
|
11
48
|
coreness: Map<T, number>;
|
|
@@ -13,20 +50,13 @@ export interface KCoreResult<T> {
|
|
|
13
50
|
}
|
|
14
51
|
|
|
15
52
|
/**
|
|
16
|
-
* K-Core decomposition algorithm
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* @param graph - Undirected graph as adjacency list
|
|
20
|
-
* @returns K-core decomposition results
|
|
21
|
-
*
|
|
22
|
-
* Time Complexity: O(V + E)
|
|
23
|
-
* Space Complexity: O(V)
|
|
53
|
+
* Internal implementation of K-Core decomposition algorithm
|
|
54
|
+
* @param graph - Adjacency map representation of the graph
|
|
55
|
+
* @returns K-core decomposition result with cores, coreness values, and maximum core number
|
|
24
56
|
*/
|
|
25
|
-
|
|
26
|
-
graph: Map<T, Set<T>>,
|
|
27
|
-
): KCoreResult<T> {
|
|
57
|
+
function kCoreDecompositionImpl<T>(graph: Map<T, Set<T>>): KCoreResult<T> {
|
|
28
58
|
if (graph.size === 0) {
|
|
29
|
-
return {cores: new Map(), coreness: new Map(), maxCore: 0};
|
|
59
|
+
return { cores: new Map(), coreness: new Map(), maxCore: 0 };
|
|
30
60
|
}
|
|
31
61
|
|
|
32
62
|
const n = graph.size;
|
|
@@ -180,23 +210,19 @@ export function kCoreDecomposition<T>(
|
|
|
180
210
|
maxCore = Math.max(maxCore, core);
|
|
181
211
|
}
|
|
182
212
|
|
|
183
|
-
return {cores, coreness, maxCore};
|
|
213
|
+
return { cores, coreness, maxCore };
|
|
184
214
|
}
|
|
185
215
|
|
|
186
216
|
/**
|
|
187
217
|
* Extract the k-core subgraph
|
|
188
218
|
* Returns nodes that belong to k-core or higher
|
|
189
|
-
*
|
|
190
219
|
* @param graph - Undirected graph
|
|
191
220
|
* @param k - Core number
|
|
192
221
|
* @returns Set of nodes in k-core or higher
|
|
193
222
|
*/
|
|
194
|
-
export function getKCore<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
): Set<T> {
|
|
198
|
-
const {coreness} = kCoreDecomposition(graph);
|
|
199
|
-
const kCore = new Set<T>();
|
|
223
|
+
export function getKCore(graph: Graph, k: number): Set<string> {
|
|
224
|
+
const { coreness } = kCoreDecomposition(graph);
|
|
225
|
+
const kCore = new Set<string>();
|
|
200
226
|
|
|
201
227
|
for (const [node, core] of coreness) {
|
|
202
228
|
if (core >= k) {
|
|
@@ -210,22 +236,19 @@ export function getKCore<T>(
|
|
|
210
236
|
/**
|
|
211
237
|
* Get the induced subgraph for k-core
|
|
212
238
|
* Returns the actual subgraph containing only k-core nodes
|
|
213
|
-
*
|
|
214
239
|
* @param graph - Original graph
|
|
215
240
|
* @param k - Core number
|
|
216
241
|
* @returns K-core subgraph
|
|
217
242
|
*/
|
|
218
|
-
export function getKCoreSubgraph<
|
|
219
|
-
graph: Map<T, Set<T>>,
|
|
220
|
-
k: number,
|
|
221
|
-
): Map<T, Set<T>> {
|
|
243
|
+
export function getKCoreSubgraph(graph: Graph, k: number): Map<string, Set<string>> {
|
|
222
244
|
const kCoreNodes = getKCore(graph, k);
|
|
223
|
-
const
|
|
245
|
+
const adjacencySet = graphToAdjacencySet(graph);
|
|
246
|
+
const subgraph = new Map<string, Set<string>>();
|
|
224
247
|
|
|
225
248
|
for (const node of kCoreNodes) {
|
|
226
|
-
const neighbors =
|
|
249
|
+
const neighbors = adjacencySet.get(node);
|
|
227
250
|
if (neighbors) {
|
|
228
|
-
const coreNeighbors = new Set<
|
|
251
|
+
const coreNeighbors = new Set<string>();
|
|
229
252
|
for (const neighbor of neighbors) {
|
|
230
253
|
if (kCoreNodes.has(neighbor)) {
|
|
231
254
|
coreNeighbors.add(neighbor);
|
|
@@ -241,28 +264,26 @@ export function getKCoreSubgraph<T>(
|
|
|
241
264
|
/**
|
|
242
265
|
* Degeneracy ordering of the graph
|
|
243
266
|
* Orders nodes by their coreness values
|
|
244
|
-
*
|
|
245
267
|
* @param graph - Undirected graph
|
|
246
268
|
* @returns Array of nodes ordered by degeneracy
|
|
247
269
|
*/
|
|
248
|
-
export function degeneracyOrdering
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
const ordering: T[] = [];
|
|
270
|
+
export function degeneracyOrdering(graph: Graph): string[] {
|
|
271
|
+
const adjacencySet = graphToAdjacencySet(graph);
|
|
272
|
+
const degree = new Map<string, number>();
|
|
273
|
+
const remaining = new Map<string, Set<string>>();
|
|
274
|
+
const ordering: string[] = [];
|
|
254
275
|
|
|
255
276
|
// Initialize
|
|
256
|
-
for (const [node, neighbors] of
|
|
277
|
+
for (const [node, neighbors] of adjacencySet) {
|
|
257
278
|
degree.set(node, neighbors.size);
|
|
258
279
|
remaining.set(node, new Set(neighbors));
|
|
259
280
|
}
|
|
260
281
|
|
|
261
282
|
// Build ordering
|
|
262
|
-
while (ordering.length <
|
|
263
|
-
|
|
283
|
+
while (ordering.length < adjacencySet.size) {
|
|
284
|
+
// Find minimum degree node
|
|
264
285
|
let minDegree = Infinity;
|
|
265
|
-
let minNode:
|
|
286
|
+
let minNode: string | undefined;
|
|
266
287
|
|
|
267
288
|
for (const [node, deg] of degree) {
|
|
268
289
|
if (!ordering.includes(node) && deg < minDegree) {
|
|
@@ -297,28 +318,27 @@ export function degeneracyOrdering<T>(
|
|
|
297
318
|
/**
|
|
298
319
|
* Find k-truss subgraph (triangular k-cores)
|
|
299
320
|
* Each edge must be part of at least k-2 triangles
|
|
300
|
-
*
|
|
301
321
|
* @param graph - Undirected graph
|
|
302
322
|
* @param k - Truss number (k >= 2)
|
|
303
323
|
* @returns Edges in k-truss
|
|
304
324
|
*/
|
|
305
|
-
export function kTruss<
|
|
306
|
-
graph: Map<T, Set<T>>,
|
|
307
|
-
k: number,
|
|
308
|
-
): Set<string> {
|
|
325
|
+
export function kTruss(graph: Graph, k: number): Set<string> {
|
|
309
326
|
if (k < 2) {
|
|
310
327
|
throw new Error("k must be at least 2 for k-truss");
|
|
311
328
|
}
|
|
312
329
|
|
|
330
|
+
const adjacencySet = graphToAdjacencySet(graph);
|
|
331
|
+
|
|
313
332
|
// Count triangles for each edge
|
|
314
333
|
const edgeTriangles = new Map<string, number>();
|
|
315
334
|
const edges = new Set<string>();
|
|
316
335
|
|
|
317
336
|
// Initialize edges
|
|
318
|
-
for (const [u, neighbors] of
|
|
337
|
+
for (const [u, neighbors] of adjacencySet) {
|
|
319
338
|
for (const v of neighbors) {
|
|
320
|
-
if (u < v) {
|
|
321
|
-
|
|
339
|
+
if (u < v) {
|
|
340
|
+
// Avoid duplicates
|
|
341
|
+
const edge = `${u},${v}`;
|
|
322
342
|
edges.add(edge);
|
|
323
343
|
edgeTriangles.set(edge, 0);
|
|
324
344
|
}
|
|
@@ -326,18 +346,18 @@ export function kTruss<T>(
|
|
|
326
346
|
}
|
|
327
347
|
|
|
328
348
|
// Count triangles
|
|
329
|
-
for (const [u, uNeighbors] of
|
|
349
|
+
for (const [u, uNeighbors] of adjacencySet) {
|
|
330
350
|
for (const v of uNeighbors) {
|
|
331
351
|
if (u < v) {
|
|
332
|
-
const vNeighbors =
|
|
352
|
+
const vNeighbors = adjacencySet.get(v);
|
|
333
353
|
if (vNeighbors) {
|
|
334
354
|
// Find common neighbors (triangles)
|
|
335
355
|
for (const w of uNeighbors) {
|
|
336
356
|
if (v < w && vNeighbors.has(w)) {
|
|
337
357
|
// Triangle found: u-v-w
|
|
338
|
-
const edge1 = `${
|
|
339
|
-
const edge2 = `${
|
|
340
|
-
const edge3 = `${
|
|
358
|
+
const edge1 = `${u},${v}`;
|
|
359
|
+
const edge2 = `${u},${w}`;
|
|
360
|
+
const edge3 = `${v},${w}`;
|
|
341
361
|
|
|
342
362
|
const edge1Count = edgeTriangles.get(edge1);
|
|
343
363
|
const edge2Count = edgeTriangles.get(edge2);
|
|
@@ -390,16 +410,14 @@ export function kTruss<T>(
|
|
|
390
410
|
}
|
|
391
411
|
|
|
392
412
|
// Update triangle counts for affected edges
|
|
393
|
-
const
|
|
394
|
-
const
|
|
395
|
-
const uNeighbors = graph.get(uNode);
|
|
396
|
-
const vNeighbors = graph.get(vNode);
|
|
413
|
+
const uNeighbors = adjacencySet.get(u);
|
|
414
|
+
const vNeighbors = adjacencySet.get(v);
|
|
397
415
|
|
|
398
416
|
if (uNeighbors && vNeighbors) {
|
|
399
417
|
for (const w of uNeighbors) {
|
|
400
418
|
if (vNeighbors.has(w)) {
|
|
401
|
-
const edge1 =
|
|
402
|
-
const edge2 =
|
|
419
|
+
const edge1 = u < w ? `${u},${w}` : `${w},${u}`;
|
|
420
|
+
const edge2 = v < w ? `${v},${w}` : `${w},${v}`;
|
|
403
421
|
|
|
404
422
|
if (kTrussEdges.has(edge1)) {
|
|
405
423
|
const edge1Count = edgeTriangles.get(edge1);
|
|
@@ -425,10 +443,10 @@ export function kTruss<T>(
|
|
|
425
443
|
|
|
426
444
|
/**
|
|
427
445
|
* Convert directed graph to undirected for k-core analysis
|
|
446
|
+
* @param directedGraph - Directed graph represented as adjacency map with edge weights
|
|
447
|
+
* @returns Undirected graph represented as adjacency map with neighbor sets
|
|
428
448
|
*/
|
|
429
|
-
export function toUndirected<T>(
|
|
430
|
-
directedGraph: Map<T, Map<T, number>>,
|
|
431
|
-
): Map<T, Set<T>> {
|
|
449
|
+
export function toUndirected<T>(directedGraph: Map<T, Map<T, number>>): Map<T, Set<T>> {
|
|
432
450
|
const undirected = new Map<T, Set<T>>();
|
|
433
451
|
|
|
434
452
|
// Initialize all nodes
|
|
@@ -458,3 +476,17 @@ export function toUndirected<T>(
|
|
|
458
476
|
return undirected;
|
|
459
477
|
}
|
|
460
478
|
|
|
479
|
+
/**
|
|
480
|
+
* K-Core decomposition algorithm
|
|
481
|
+
* Finds all k-cores in the graph and assigns coreness values to nodes
|
|
482
|
+
* @param graph - Undirected graph - accepts Graph class or Map<T, Set<T>>
|
|
483
|
+
* @returns K-core decomposition results
|
|
484
|
+
*
|
|
485
|
+
* Time Complexity: O(V + E)
|
|
486
|
+
* Space Complexity: O(V)
|
|
487
|
+
*/
|
|
488
|
+
export function kCoreDecomposition(graph: Graph): KCoreResult<string> {
|
|
489
|
+
// Convert Graph to adjacency set representation
|
|
490
|
+
const adjacencySet = graphToAdjacencySet(graph);
|
|
491
|
+
return kCoreDecompositionImpl(adjacencySet);
|
|
492
|
+
}
|
package/src/clustering/mcl.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {Graph} from "../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../types/index.js";
|
|
1
|
+
import type { Graph } from "../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../types/index.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Markov Clustering (MCL) algorithm implementation
|
|
@@ -31,11 +31,11 @@ export interface MCLResult {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Perform Markov Clustering on a graph
|
|
34
|
+
* @param graph - The input graph to cluster
|
|
35
|
+
* @param options - MCL algorithm options (expansion, inflation, iterations, etc.)
|
|
36
|
+
* @returns MCL clustering result with communities, attractors, and convergence info
|
|
34
37
|
*/
|
|
35
|
-
export function markovClustering(
|
|
36
|
-
graph: Graph,
|
|
37
|
-
options: MCLOptions = {},
|
|
38
|
-
): MCLResult {
|
|
38
|
+
export function markovClustering(graph: Graph, options: MCLOptions = {}): MCLResult {
|
|
39
39
|
const {
|
|
40
40
|
expansion = 2,
|
|
41
41
|
inflation = 2,
|
|
@@ -65,7 +65,7 @@ export function markovClustering(
|
|
|
65
65
|
let iteration = 0;
|
|
66
66
|
|
|
67
67
|
for (iteration = 0; iteration < maxIterations; iteration++) {
|
|
68
|
-
const oldMatrix = matrix.map((row) => [...
|
|
68
|
+
const oldMatrix = matrix.map((row) => [...row]);
|
|
69
69
|
|
|
70
70
|
// Expansion step (matrix multiplication)
|
|
71
71
|
matrix = matrixPower(matrix, expansion);
|
|
@@ -84,7 +84,7 @@ export function markovClustering(
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Extract clusters from final matrix
|
|
87
|
-
const {communities, attractors} = extractClusters(matrix, nodeIds);
|
|
87
|
+
const { communities, attractors } = extractClusters(matrix, nodeIds);
|
|
88
88
|
|
|
89
89
|
return {
|
|
90
90
|
communities,
|
|
@@ -96,10 +96,14 @@ export function markovClustering(
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Build initial transition matrix from graph
|
|
99
|
+
* @param graph - The input graph
|
|
100
|
+
* @param nodeIds - Array of node IDs in the graph
|
|
101
|
+
* @param selfLoops - Whether to add self-loops to the matrix
|
|
102
|
+
* @returns Column-normalized transition matrix
|
|
99
103
|
*/
|
|
100
104
|
function buildTransitionMatrix(graph: Graph, nodeIds: NodeId[], selfLoops: boolean): number[][] {
|
|
101
105
|
const n = nodeIds.length;
|
|
102
|
-
const matrix = Array.from({length: n}, (): number[] => Array(n).fill(0) as number[]);
|
|
106
|
+
const matrix = Array.from({ length: n }, (): number[] => Array(n).fill(0) as number[]);
|
|
103
107
|
const nodeToIndex = new Map<NodeId, number>();
|
|
104
108
|
|
|
105
109
|
nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
|
|
@@ -168,6 +172,9 @@ function buildTransitionMatrix(graph: Graph, nodeIds: NodeId[], selfLoops: boole
|
|
|
168
172
|
|
|
169
173
|
/**
|
|
170
174
|
* Raise matrix to a power (for expansion step)
|
|
175
|
+
* @param matrix - Input square matrix
|
|
176
|
+
* @param power - Power to raise the matrix to
|
|
177
|
+
* @returns Matrix raised to the specified power
|
|
171
178
|
*/
|
|
172
179
|
function matrixPower(matrix: number[][], power: number): number[][] {
|
|
173
180
|
if (power === 1) {
|
|
@@ -187,13 +194,16 @@ function matrixPower(matrix: number[][], power: number): number[][] {
|
|
|
187
194
|
|
|
188
195
|
/**
|
|
189
196
|
* Multiply two matrices
|
|
197
|
+
* @param a - First matrix
|
|
198
|
+
* @param b - Second matrix
|
|
199
|
+
* @returns Product matrix a * b
|
|
190
200
|
*/
|
|
191
201
|
function matrixMultiply(a: number[][], b: number[][]): number[][] {
|
|
192
202
|
const n = a.length;
|
|
193
203
|
const m = b[0]?.length ?? 0;
|
|
194
204
|
const p = b.length;
|
|
195
205
|
|
|
196
|
-
const result: number[][] = Array.from({length: n}, () => Array(m).fill(0) as number[]);
|
|
206
|
+
const result: number[][] = Array.from({ length: n }, () => Array(m).fill(0) as number[]);
|
|
197
207
|
|
|
198
208
|
for (let i = 0; i < n; i++) {
|
|
199
209
|
for (let j = 0; j < m; j++) {
|
|
@@ -207,7 +217,7 @@ function matrixMultiply(a: number[][], b: number[][]): number[][] {
|
|
|
207
217
|
|
|
208
218
|
const prevVal = resultRow[j];
|
|
209
219
|
if (prevVal !== undefined) {
|
|
210
|
-
resultRow[j] = prevVal +
|
|
220
|
+
resultRow[j] = prevVal + aVal * bVal;
|
|
211
221
|
}
|
|
212
222
|
}
|
|
213
223
|
}
|
|
@@ -218,10 +228,13 @@ function matrixMultiply(a: number[][], b: number[][]): number[][] {
|
|
|
218
228
|
|
|
219
229
|
/**
|
|
220
230
|
* Inflation step: element-wise powering and column normalization
|
|
231
|
+
* @param matrix - Input matrix to inflate
|
|
232
|
+
* @param inflation - Inflation parameter for element-wise powering
|
|
233
|
+
* @returns Inflated and column-normalized matrix
|
|
221
234
|
*/
|
|
222
235
|
function inflate(matrix: number[][], inflation: number): number[][] {
|
|
223
236
|
const n = matrix.length;
|
|
224
|
-
const result: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
|
|
237
|
+
const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
|
|
225
238
|
|
|
226
239
|
// Element-wise powering
|
|
227
240
|
for (let i = 0; i < n; i++) {
|
|
@@ -266,10 +279,13 @@ function inflate(matrix: number[][], inflation: number): number[][] {
|
|
|
266
279
|
|
|
267
280
|
/**
|
|
268
281
|
* Pruning step: remove small values
|
|
282
|
+
* @param matrix - Input matrix to prune
|
|
283
|
+
* @param threshold - Values below this threshold are set to zero
|
|
284
|
+
* @returns Pruned and re-normalized matrix
|
|
269
285
|
*/
|
|
270
286
|
function prune(matrix: number[][], threshold: number): number[][] {
|
|
271
287
|
const n = matrix.length;
|
|
272
|
-
const result: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
|
|
288
|
+
const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
|
|
273
289
|
|
|
274
290
|
for (let i = 0; i < n; i++) {
|
|
275
291
|
for (let j = 0; j < n; j++) {
|
|
@@ -323,6 +339,10 @@ function prune(matrix: number[][], threshold: number): number[][] {
|
|
|
323
339
|
|
|
324
340
|
/**
|
|
325
341
|
* Check if the algorithm has converged
|
|
342
|
+
* @param oldMatrix - Matrix from previous iteration
|
|
343
|
+
* @param newMatrix - Matrix from current iteration
|
|
344
|
+
* @param tolerance - Convergence tolerance threshold
|
|
345
|
+
* @returns True if the maximum difference between matrices is below tolerance
|
|
326
346
|
*/
|
|
327
347
|
function hasConverged(oldMatrix: number[][], newMatrix: number[][], tolerance: number): boolean {
|
|
328
348
|
const n = oldMatrix.length;
|
|
@@ -348,8 +368,14 @@ function hasConverged(oldMatrix: number[][], newMatrix: number[][], tolerance: n
|
|
|
348
368
|
|
|
349
369
|
/**
|
|
350
370
|
* Extract clusters from the final matrix
|
|
371
|
+
* @param matrix - Final converged MCL matrix
|
|
372
|
+
* @param nodeIds - Array of node IDs corresponding to matrix indices
|
|
373
|
+
* @returns Object containing communities and attractor nodes
|
|
351
374
|
*/
|
|
352
|
-
function extractClusters(
|
|
375
|
+
function extractClusters(
|
|
376
|
+
matrix: number[][],
|
|
377
|
+
nodeIds: NodeId[],
|
|
378
|
+
): {
|
|
353
379
|
communities: NodeId[][];
|
|
354
380
|
attractors: Set<NodeId>;
|
|
355
381
|
} {
|
|
@@ -391,10 +417,14 @@ function extractClusters(matrix: number[][], nodeIds: NodeId[]): {
|
|
|
391
417
|
}
|
|
392
418
|
|
|
393
419
|
if (clusterNodes.length > 0) {
|
|
394
|
-
communities.push(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
420
|
+
communities.push(
|
|
421
|
+
clusterNodes
|
|
422
|
+
.map((idx) => {
|
|
423
|
+
const nodeId = nodeIds[idx];
|
|
424
|
+
return nodeId;
|
|
425
|
+
})
|
|
426
|
+
.filter((node): node is NodeId => node !== undefined),
|
|
427
|
+
);
|
|
398
428
|
clusterIndex++;
|
|
399
429
|
}
|
|
400
430
|
}
|
|
@@ -411,16 +441,16 @@ function extractClusters(matrix: number[][], nodeIds: NodeId[]): {
|
|
|
411
441
|
}
|
|
412
442
|
}
|
|
413
443
|
|
|
414
|
-
return {communities, attractors};
|
|
444
|
+
return { communities, attractors };
|
|
415
445
|
}
|
|
416
446
|
|
|
417
447
|
/**
|
|
418
448
|
* Calculate modularity of MCL clustering result
|
|
449
|
+
* @param graph - The original graph that was clustered
|
|
450
|
+
* @param communities - Array of communities (each community is an array of node IDs)
|
|
451
|
+
* @returns Modularity score between -0.5 and 1.0
|
|
419
452
|
*/
|
|
420
|
-
export function calculateMCLModularity(
|
|
421
|
-
graph: Graph,
|
|
422
|
-
communities: NodeId[][],
|
|
423
|
-
): number {
|
|
453
|
+
export function calculateMCLModularity(graph: Graph, communities: NodeId[][]): number {
|
|
424
454
|
const m = graph.totalEdgeCount;
|
|
425
455
|
if (m === 0) {
|
|
426
456
|
return 0;
|