@graphty/algorithms 2.1.2 → 2.2.0
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 +139 -50
- package/dist/algorithms.js +15260 -10390
- package/dist/algorithms.js.map +1 -1
- package/dist/algorithms.standalone.js +26675 -21542
- package/dist/algorithms.standalone.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +18 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +40 -1
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +20 -0
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +93 -8
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.d.ts +9 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +18 -0
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -0
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.js +29 -0
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
- package/dist/src/algorithms/centrality/hits.d.ts +9 -0
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/hits.js +23 -0
- package/dist/src/algorithms/centrality/hits.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +9 -0
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/katz.js +31 -0
- package/dist/src/algorithms/centrality/katz.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +13 -13
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +47 -10
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman-legacy.d.ts +27 -0
- package/dist/src/algorithms/community/girvan-newman-legacy.d.ts.map +1 -0
- package/dist/src/algorithms/community/girvan-newman-legacy.js +335 -0
- package/dist/src/algorithms/community/girvan-newman-legacy.js.map +1 -0
- package/dist/src/algorithms/community/girvan-newman.d.ts +5 -3
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +34 -308
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/label-propagation.d.ts +9 -6
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +24 -110
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/components/connected.d.ts.map +1 -1
- package/dist/src/algorithms/components/connected.js +28 -77
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
- package/dist/src/algorithms/mst/kruskal.js +59 -3
- package/dist/src/algorithms/mst/kruskal.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +46 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +27 -0
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +15 -5
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +97 -107
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +2 -12
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +47 -308
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.js +5 -25
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts +0 -3
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +0 -4
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +33 -154
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/clustering/hierarchical-legacy.d.ts +56 -0
- package/dist/src/clustering/hierarchical-legacy.d.ts.map +1 -0
- package/dist/src/clustering/hierarchical-legacy.js +441 -0
- package/dist/src/clustering/hierarchical-legacy.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +7 -39
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +56 -417
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/k-core-legacy.d.ts +30 -0
- package/dist/src/clustering/k-core-legacy.d.ts.map +1 -0
- package/dist/src/clustering/k-core-legacy.js +191 -0
- package/dist/src/clustering/k-core-legacy.js.map +1 -0
- package/dist/src/clustering/k-core.d.ts +8 -7
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +33 -174
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/mcl-legacy.d.ts +42 -0
- package/dist/src/clustering/mcl-legacy.d.ts.map +1 -0
- package/dist/src/clustering/mcl-legacy.js +386 -0
- package/dist/src/clustering/mcl-legacy.js.map +1 -0
- package/dist/src/clustering/mcl.d.ts +9 -32
- package/dist/src/clustering/mcl.d.ts.map +1 -1
- package/dist/src/clustering/mcl.js +48 -374
- package/dist/src/clustering/mcl.js.map +1 -1
- package/dist/src/index.d.ts +29 -3
- 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/indexed/accelerator.d.ts +141 -11
- package/dist/src/indexed/accelerator.d.ts.map +1 -1
- package/dist/src/indexed/accelerator.js +319 -5
- package/dist/src/indexed/accelerator.js.map +1 -1
- package/dist/src/indexed/all-pairs.d.ts +59 -0
- package/dist/src/indexed/all-pairs.d.ts.map +1 -0
- package/dist/src/indexed/all-pairs.js +274 -0
- package/dist/src/indexed/all-pairs.js.map +1 -0
- package/dist/src/indexed/bellman-ford.d.ts +25 -0
- package/dist/src/indexed/bellman-ford.d.ts.map +1 -0
- package/dist/src/indexed/bellman-ford.js +59 -0
- package/dist/src/indexed/bellman-ford.js.map +1 -0
- package/dist/src/indexed/betweenness.d.ts +85 -0
- package/dist/src/indexed/betweenness.d.ts.map +1 -0
- package/dist/src/indexed/betweenness.js +188 -0
- package/dist/src/indexed/betweenness.js.map +1 -0
- package/dist/src/indexed/bfs.d.ts +69 -2
- package/dist/src/indexed/bfs.d.ts.map +1 -1
- package/dist/src/indexed/bfs.js +148 -1
- package/dist/src/indexed/bfs.js.map +1 -1
- package/dist/src/indexed/bipartite.d.ts +37 -0
- package/dist/src/indexed/bipartite.d.ts.map +1 -0
- package/dist/src/indexed/bipartite.js +54 -0
- package/dist/src/indexed/bipartite.js.map +1 -0
- package/dist/src/indexed/closeness.d.ts +49 -0
- package/dist/src/indexed/closeness.d.ts.map +1 -0
- package/dist/src/indexed/closeness.js +127 -0
- package/dist/src/indexed/closeness.js.map +1 -0
- package/dist/src/indexed/common-neighbors.d.ts +14 -1
- package/dist/src/indexed/common-neighbors.d.ts.map +1 -1
- package/dist/src/indexed/common-neighbors.js +24 -14
- package/dist/src/indexed/common-neighbors.js.map +1 -1
- package/dist/src/indexed/degree.d.ts +19 -0
- package/dist/src/indexed/degree.d.ts.map +1 -0
- package/dist/src/indexed/degree.js +45 -0
- package/dist/src/indexed/degree.js.map +1 -0
- package/dist/src/indexed/delta-pagerank.d.ts +145 -0
- package/dist/src/indexed/delta-pagerank.d.ts.map +1 -0
- package/dist/src/indexed/delta-pagerank.js +428 -0
- package/dist/src/indexed/delta-pagerank.js.map +1 -0
- package/dist/src/indexed/dfs.d.ts +55 -0
- package/dist/src/indexed/dfs.d.ts.map +1 -0
- package/dist/src/indexed/dfs.js +146 -0
- package/dist/src/indexed/dfs.js.map +1 -0
- package/dist/src/indexed/eigenvector.d.ts +48 -0
- package/dist/src/indexed/eigenvector.d.ts.map +1 -0
- package/dist/src/indexed/eigenvector.js +162 -0
- package/dist/src/indexed/eigenvector.js.map +1 -0
- package/dist/src/indexed/facade.d.ts +109 -0
- package/dist/src/indexed/facade.d.ts.map +1 -0
- package/dist/src/indexed/facade.js +204 -0
- package/dist/src/indexed/facade.js.map +1 -0
- package/dist/src/indexed/flow.d.ts +120 -0
- package/dist/src/indexed/flow.d.ts.map +1 -0
- package/dist/src/indexed/flow.js +377 -0
- package/dist/src/indexed/flow.js.map +1 -0
- package/dist/src/indexed/girvan-newman.d.ts +42 -0
- package/dist/src/indexed/girvan-newman.d.ts.map +1 -0
- package/dist/src/indexed/girvan-newman.js +90 -0
- package/dist/src/indexed/girvan-newman.js.map +1 -0
- package/dist/src/indexed/grsbm.d.ts +85 -0
- package/dist/src/indexed/grsbm.d.ts.map +1 -0
- package/dist/src/indexed/grsbm.js +287 -0
- package/dist/src/indexed/grsbm.js.map +1 -0
- package/dist/src/indexed/hierarchical.d.ts +69 -0
- package/dist/src/indexed/hierarchical.d.ts.map +1 -0
- package/dist/src/indexed/hierarchical.js +245 -0
- package/dist/src/indexed/hierarchical.js.map +1 -0
- package/dist/src/indexed/index.d.ts +29 -3
- package/dist/src/indexed/index.d.ts.map +1 -1
- package/dist/src/indexed/index.js +29 -3
- package/dist/src/indexed/index.js.map +1 -1
- package/dist/src/indexed/isomorphism.d.ts +42 -0
- package/dist/src/indexed/isomorphism.d.ts.map +1 -0
- package/dist/src/indexed/isomorphism.js +269 -0
- package/dist/src/indexed/isomorphism.js.map +1 -0
- package/dist/src/indexed/label-propagation.d.ts +134 -0
- package/dist/src/indexed/label-propagation.d.ts.map +1 -0
- package/dist/src/indexed/label-propagation.js +486 -0
- package/dist/src/indexed/label-propagation.js.map +1 -0
- package/dist/src/indexed/leiden.d.ts +52 -0
- package/dist/src/indexed/leiden.d.ts.map +1 -0
- package/dist/src/indexed/leiden.js +384 -0
- package/dist/src/indexed/leiden.js.map +1 -0
- package/dist/src/indexed/link-prediction.d.ts +147 -0
- package/dist/src/indexed/link-prediction.d.ts.map +1 -0
- package/dist/src/indexed/link-prediction.js +317 -0
- package/dist/src/indexed/link-prediction.js.map +1 -0
- package/dist/src/indexed/markov.d.ts +54 -0
- package/dist/src/indexed/markov.d.ts.map +1 -0
- package/dist/src/indexed/markov.js +284 -0
- package/dist/src/indexed/markov.js.map +1 -0
- package/dist/src/indexed/matching.d.ts +53 -0
- package/dist/src/indexed/matching.d.ts.map +1 -0
- package/dist/src/indexed/matching.js +139 -0
- package/dist/src/indexed/matching.js.map +1 -0
- package/dist/src/indexed/min-cut.d.ts +48 -0
- package/dist/src/indexed/min-cut.d.ts.map +1 -0
- package/dist/src/indexed/min-cut.js +175 -0
- package/dist/src/indexed/min-cut.js.map +1 -0
- package/dist/src/indexed/modularity.d.ts +34 -0
- package/dist/src/indexed/modularity.d.ts.map +1 -0
- package/dist/src/indexed/modularity.js +74 -0
- package/dist/src/indexed/modularity.js.map +1 -0
- package/dist/src/indexed/mst.d.ts +29 -1
- package/dist/src/indexed/mst.d.ts.map +1 -1
- package/dist/src/indexed/mst.js +62 -0
- package/dist/src/indexed/mst.js.map +1 -1
- package/dist/src/indexed/pagerank.d.ts +29 -6
- package/dist/src/indexed/pagerank.d.ts.map +1 -1
- package/dist/src/indexed/pagerank.js +107 -11
- package/dist/src/indexed/pagerank.js.map +1 -1
- package/dist/src/indexed/point-to-point.d.ts +53 -0
- package/dist/src/indexed/point-to-point.d.ts.map +1 -0
- package/dist/src/indexed/point-to-point.js +173 -0
- package/dist/src/indexed/point-to-point.js.map +1 -0
- package/dist/src/indexed/scc.d.ts +53 -0
- package/dist/src/indexed/scc.d.ts.map +1 -0
- package/dist/src/indexed/scc.js +110 -0
- package/dist/src/indexed/scc.js.map +1 -0
- package/dist/src/indexed/spectral.d.ts +61 -0
- package/dist/src/indexed/spectral.d.ts.map +1 -0
- package/dist/src/indexed/spectral.js +476 -0
- package/dist/src/indexed/spectral.js.map +1 -0
- package/dist/src/indexed/structures/bit-set.d.ts +45 -0
- package/dist/src/indexed/structures/bit-set.d.ts.map +1 -0
- package/dist/src/indexed/structures/bit-set.js +57 -0
- package/dist/src/indexed/structures/bit-set.js.map +1 -0
- package/dist/src/indexed/structures/max-heap.d.ts +52 -0
- package/dist/src/indexed/structures/max-heap.d.ts.map +1 -0
- package/dist/src/indexed/structures/max-heap.js +66 -0
- package/dist/src/indexed/structures/max-heap.js.map +1 -0
- package/dist/src/indexed/structures/min-heap.d.ts +30 -1
- package/dist/src/indexed/structures/min-heap.d.ts.map +1 -1
- package/dist/src/indexed/structures/min-heap.js +46 -6
- package/dist/src/indexed/structures/min-heap.js.map +1 -1
- package/dist/src/indexed/structures/ring-queue.d.ts +41 -0
- package/dist/src/indexed/structures/ring-queue.d.ts.map +1 -0
- package/dist/src/indexed/structures/ring-queue.js +64 -0
- package/dist/src/indexed/structures/ring-queue.js.map +1 -0
- package/dist/src/indexed/sync.d.ts +50 -0
- package/dist/src/indexed/sync.d.ts.map +1 -0
- package/dist/src/indexed/sync.js +175 -0
- package/dist/src/indexed/sync.js.map +1 -0
- package/dist/src/indexed/terahac.d.ts +58 -0
- package/dist/src/indexed/terahac.d.ts.map +1 -0
- package/dist/src/indexed/terahac.js +244 -0
- package/dist/src/indexed/terahac.js.map +1 -0
- package/dist/src/indexed/to-snapshot.d.ts +45 -2
- package/dist/src/indexed/to-snapshot.d.ts.map +1 -1
- package/dist/src/indexed/to-snapshot.js +140 -6
- package/dist/src/indexed/to-snapshot.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors-legacy.d.ts +71 -0
- package/dist/src/link-prediction/common-neighbors-legacy.d.ts.map +1 -0
- package/dist/src/link-prediction/common-neighbors-legacy.js +172 -0
- package/dist/src/link-prediction/common-neighbors-legacy.js.map +1 -0
- package/dist/src/link-prediction/common-neighbors.d.ts +11 -19
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +56 -123
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.d.ts +6 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.js +6 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
- package/dist/src/research/grsbm-legacy.d.ts +82 -0
- package/dist/src/research/grsbm-legacy.d.ts.map +1 -0
- package/dist/src/research/grsbm-legacy.js +416 -0
- package/dist/src/research/grsbm-legacy.js.map +1 -0
- package/dist/src/research/grsbm.d.ts +9 -66
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +67 -391
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/sync-legacy.d.ts +48 -0
- package/dist/src/research/sync-legacy.d.ts.map +1 -0
- package/dist/src/research/sync-legacy.js +337 -0
- package/dist/src/research/sync-legacy.js.map +1 -0
- package/dist/src/research/sync.d.ts +10 -32
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +28 -316
- package/dist/src/research/sync.js.map +1 -1
- package/package.json +4 -3
- package/src/algorithms/centrality/betweenness.ts +51 -1
- package/src/algorithms/centrality/closeness.ts +112 -12
- package/src/algorithms/centrality/degree.ts +19 -0
- package/src/algorithms/centrality/eigenvector.ts +34 -0
- package/src/algorithms/centrality/hits.ts +24 -0
- package/src/algorithms/centrality/katz.ts +32 -0
- package/src/algorithms/centrality/pagerank.ts +53 -13
- package/src/algorithms/community/girvan-newman-legacy.ts +414 -0
- package/src/algorithms/community/girvan-newman.ts +36 -386
- package/src/algorithms/community/label-propagation.ts +24 -133
- package/src/algorithms/components/connected.ts +28 -95
- package/src/algorithms/mst/kruskal.ts +65 -4
- package/src/algorithms/shortest-path/bellman-ford.ts +49 -0
- package/src/algorithms/shortest-path/dijkstra.ts +29 -0
- package/src/algorithms/shortest-path/floyd-warshall.ts +106 -136
- package/src/algorithms/traversal/bfs-unified.ts +52 -366
- package/src/algorithms/traversal/bfs-variants.ts +5 -28
- package/src/algorithms/traversal/bfs.ts +0 -4
- package/src/algorithms/traversal/dfs.ts +39 -182
- package/src/clustering/hierarchical-legacy.ts +551 -0
- package/src/clustering/hierarchical.ts +64 -520
- package/src/clustering/k-core-legacy.ts +229 -0
- package/src/clustering/k-core.ts +38 -209
- package/src/clustering/mcl-legacy.ts +498 -0
- package/src/clustering/mcl.ts +46 -479
- package/src/index.ts +90 -3
- package/src/indexed/accelerator.ts +502 -16
- package/src/indexed/all-pairs.ts +342 -0
- package/src/indexed/bellman-ford.ts +72 -0
- package/src/indexed/betweenness.ts +258 -0
- package/src/indexed/bfs.ts +189 -3
- package/src/indexed/bipartite.ts +77 -0
- package/src/indexed/closeness.ts +160 -0
- package/src/indexed/common-neighbors.ts +31 -14
- package/src/indexed/degree.ts +55 -0
- package/src/indexed/delta-pagerank.ts +513 -0
- package/src/indexed/dfs.ts +189 -0
- package/src/indexed/eigenvector.ts +202 -0
- package/src/indexed/facade.ts +229 -0
- package/src/indexed/flow.ts +500 -0
- package/src/indexed/girvan-newman.ts +120 -0
- package/src/indexed/grsbm.ts +390 -0
- package/src/indexed/hierarchical.ts +295 -0
- package/src/indexed/index.ts +94 -3
- package/src/indexed/isomorphism.ts +305 -0
- package/src/indexed/label-propagation.ts +582 -0
- package/src/indexed/leiden.ts +436 -0
- package/src/indexed/link-prediction.ts +399 -0
- package/src/indexed/markov.ts +324 -0
- package/src/indexed/matching.ts +192 -0
- package/src/indexed/min-cut.ts +201 -0
- package/src/indexed/modularity.ts +81 -0
- package/src/indexed/mst.ts +79 -1
- package/src/indexed/pagerank.ts +128 -15
- package/src/indexed/point-to-point.ts +220 -0
- package/src/indexed/scc.ts +132 -0
- package/src/indexed/spectral.ts +550 -0
- package/src/indexed/structures/bit-set.ts +65 -0
- package/src/indexed/structures/max-heap.ts +74 -0
- package/src/indexed/structures/min-heap.ts +51 -6
- package/src/indexed/structures/ring-queue.ts +70 -0
- package/src/indexed/sync.ts +229 -0
- package/src/indexed/terahac.ts +290 -0
- package/src/indexed/to-snapshot.ts +156 -6
- package/src/link-prediction/common-neighbors-legacy.ts +252 -0
- package/src/link-prediction/common-neighbors.ts +65 -168
- package/src/optimized/direction-optimized-bfs.ts +6 -1
- package/src/research/grsbm-legacy.ts +586 -0
- package/src/research/grsbm.ts +76 -555
- package/src/research/sync-legacy.ts +456 -0
- package/src/research/sync.ts +30 -431
package/src/clustering/mcl.ts
CHANGED
|
@@ -1,498 +1,65 @@
|
|
|
1
|
-
import type { Graph } from "../core/graph.js";
|
|
2
|
-
import type { NodeId } from "../types/index.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Markov Clustering (MCL) algorithm implementation
|
|
6
|
-
*
|
|
7
|
-
* MCL simulates flow in graphs and finds clusters based on the notion that
|
|
8
|
-
* random walks stay within clusters and rarely move between clusters.
|
|
9
|
-
* The algorithm alternates between expansion (matrix squaring) and
|
|
10
|
-
* inflation (element-wise powering and normalization).
|
|
11
|
-
*
|
|
12
|
-
* Time complexity: O(V³) per iteration
|
|
13
|
-
* Space complexity: O(V²)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export interface MCLOptions {
|
|
17
|
-
expansion?: number; // Expansion parameter (default: 2)
|
|
18
|
-
inflation?: number; // Inflation parameter (default: 2)
|
|
19
|
-
maxIterations?: number; // Maximum iterations (default: 100)
|
|
20
|
-
tolerance?: number; // Convergence tolerance (default: 1e-6)
|
|
21
|
-
pruningThreshold?: number; // Pruning threshold (default: 1e-5)
|
|
22
|
-
selfLoops?: boolean; // Add self-loops (default: true)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface MCLResult {
|
|
26
|
-
communities: NodeId[][];
|
|
27
|
-
attractors: Set<NodeId>; // Attractor nodes (cluster representatives)
|
|
28
|
-
iterations: number;
|
|
29
|
-
converged: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
1
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
2
|
+
* Markov Clustering (MCL) over a legacy `Graph`. `markovClustering` delegates to
|
|
3
|
+
* `indexed.markovClustering` through `toSnapshot`, with the graph's exact f64 weights; the result
|
|
4
|
+
* equals the pre-migration implementation in `mcl-legacy.ts` exactly. Parameters or weights the port
|
|
5
|
+
* refuses (a fractional expansion, a negative, infinite or NaN weight, an undirected edge whose two
|
|
6
|
+
* stored halves carry different weights, ...) keep the old code.
|
|
37
7
|
*/
|
|
38
|
-
export function markovClustering(graph: Graph, options: MCLOptions = {}): MCLResult {
|
|
39
|
-
const {
|
|
40
|
-
expansion = 2,
|
|
41
|
-
inflation = 2,
|
|
42
|
-
maxIterations = 100,
|
|
43
|
-
tolerance = 1e-6,
|
|
44
|
-
pruningThreshold = 1e-5,
|
|
45
|
-
selfLoops = true,
|
|
46
|
-
} = options;
|
|
47
|
-
|
|
48
|
-
const nodes = Array.from(graph.nodes());
|
|
49
|
-
const nodeIds = nodes.map((node) => node.id);
|
|
50
|
-
const n = nodeIds.length;
|
|
51
|
-
|
|
52
|
-
if (n === 0) {
|
|
53
|
-
return {
|
|
54
|
-
communities: [],
|
|
55
|
-
attractors: new Set(),
|
|
56
|
-
iterations: 0,
|
|
57
|
-
converged: true,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Build initial transition matrix
|
|
62
|
-
let matrix = buildTransitionMatrix(graph, nodeIds, selfLoops);
|
|
63
|
-
|
|
64
|
-
let converged = false;
|
|
65
|
-
let iteration = 0;
|
|
66
|
-
|
|
67
|
-
for (iteration = 0; iteration < maxIterations; iteration++) {
|
|
68
|
-
const oldMatrix = matrix.map((row) => [...row]);
|
|
69
|
-
|
|
70
|
-
// Expansion step (matrix multiplication)
|
|
71
|
-
matrix = matrixPower(matrix, expansion);
|
|
72
|
-
|
|
73
|
-
// Inflation step (element-wise powering and column normalization)
|
|
74
|
-
matrix = inflate(matrix, inflation);
|
|
75
|
-
|
|
76
|
-
// Pruning step (remove small values)
|
|
77
|
-
matrix = prune(matrix, pruningThreshold);
|
|
78
|
-
|
|
79
|
-
// Check for convergence
|
|
80
|
-
if (hasConverged(oldMatrix, matrix, tolerance)) {
|
|
81
|
-
converged = true;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Extract clusters from final matrix
|
|
87
|
-
const { communities, attractors } = extractClusters(matrix, nodeIds);
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
communities,
|
|
91
|
-
attractors,
|
|
92
|
-
iterations: iteration + 1,
|
|
93
|
-
converged,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
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
|
|
103
|
-
*/
|
|
104
|
-
function buildTransitionMatrix(graph: Graph, nodeIds: NodeId[], selfLoops: boolean): number[][] {
|
|
105
|
-
const n = nodeIds.length;
|
|
106
|
-
const matrix = Array.from({ length: n }, (): number[] => Array(n).fill(0) as number[]);
|
|
107
|
-
const nodeToIndex = new Map<NodeId, number>();
|
|
108
|
-
|
|
109
|
-
nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
|
|
110
8
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const neighbors = graph.neighbors(nodeId);
|
|
119
|
-
|
|
120
|
-
for (const neighbor of neighbors) {
|
|
121
|
-
const j = nodeToIndex.get(neighbor);
|
|
122
|
-
if (j !== undefined) {
|
|
123
|
-
const edge = graph.getEdge(nodeId, neighbor);
|
|
124
|
-
const weight = edge?.weight ?? 1;
|
|
125
|
-
const row = matrix[i];
|
|
126
|
-
if (!row) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
row[j] = weight;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Add self-loops
|
|
135
|
-
if (selfLoops) {
|
|
136
|
-
const row = matrix[i];
|
|
137
|
-
if (!row) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
row[i] = 1;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Column-normalize the matrix
|
|
146
|
-
for (let j = 0; j < n; j++) {
|
|
147
|
-
let colSum = 0;
|
|
148
|
-
for (let i = 0; i < n; i++) {
|
|
149
|
-
const val = matrix[i]?.[j];
|
|
150
|
-
if (val !== undefined) {
|
|
151
|
-
colSum += val;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (colSum > 0) {
|
|
156
|
-
for (let i = 0; i < n; i++) {
|
|
157
|
-
const row = matrix[i];
|
|
158
|
-
if (!row) {
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const val = row[j];
|
|
163
|
-
if (val !== undefined) {
|
|
164
|
-
row[j] = val / colSum;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
9
|
+
import type { Graph } from "../core/graph.js";
|
|
10
|
+
import { exactArcWeights, labelsToGroups } from "../indexed/facade.js";
|
|
11
|
+
import { markovClustering as indexedMarkovClustering } from "../indexed/markov.js";
|
|
12
|
+
import { toSnapshot } from "../indexed/to-snapshot.js";
|
|
13
|
+
import { markovClustering as legacyMarkovClustering, type MCLOptions, type MCLResult } from "./mcl-legacy.js";
|
|
169
14
|
|
|
170
|
-
|
|
171
|
-
}
|
|
15
|
+
export type { MCLOptions, MCLResult } from "./mcl-legacy.js";
|
|
16
|
+
export { calculateMCLModularity } from "./mcl-legacy.js";
|
|
172
17
|
|
|
173
18
|
/**
|
|
174
|
-
*
|
|
175
|
-
* @param
|
|
176
|
-
* @param
|
|
177
|
-
* @returns
|
|
19
|
+
* Whether the port accepts these parameters and the graph's weights.
|
|
20
|
+
* @param graph - The caller's graph
|
|
21
|
+
* @param options - The caller's options
|
|
22
|
+
* @returns True when the port runs without throwing
|
|
178
23
|
*/
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
24
|
+
function portAccepts(graph: Graph, options: MCLOptions): boolean {
|
|
25
|
+
const { expansion = 2, inflation = 2, maxIterations = 100, tolerance = 1e-6, pruningThreshold = 1e-5 } = options;
|
|
26
|
+
if (!Number.isInteger(expansion) || expansion < 1 || !(inflation > 0) || inflation === Infinity) {
|
|
27
|
+
return false;
|
|
182
28
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return matrixMultiply(matrix, matrix);
|
|
29
|
+
if (!Number.isInteger(maxIterations) || maxIterations < 0 || !(tolerance >= 0) || !(pruningThreshold >= 0)) {
|
|
30
|
+
return false;
|
|
186
31
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
result = matrixMultiply(result, matrix);
|
|
191
|
-
}
|
|
192
|
-
return result;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Multiply two matrices
|
|
197
|
-
* @param a - First matrix
|
|
198
|
-
* @param b - Second matrix
|
|
199
|
-
* @returns Product matrix a * b
|
|
200
|
-
*/
|
|
201
|
-
function matrixMultiply(a: number[][], b: number[][]): number[][] {
|
|
202
|
-
const n = a.length;
|
|
203
|
-
const m = b[0]?.length ?? 0;
|
|
204
|
-
const p = b.length;
|
|
205
|
-
|
|
206
|
-
const result: number[][] = Array.from({ length: n }, () => Array(m).fill(0) as number[]);
|
|
207
|
-
|
|
208
|
-
for (let i = 0; i < n; i++) {
|
|
209
|
-
for (let j = 0; j < m; j++) {
|
|
210
|
-
for (let k = 0; k < p; k++) {
|
|
211
|
-
const aVal = a[i]?.[k] ?? 0;
|
|
212
|
-
const bVal = b[k]?.[j] ?? 0;
|
|
213
|
-
const resultRow = result[i];
|
|
214
|
-
if (!resultRow) {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const prevVal = resultRow[j];
|
|
219
|
-
if (prevVal !== undefined) {
|
|
220
|
-
resultRow[j] = prevVal + aVal * bVal;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
32
|
+
for (const { source, target, weight = 1 } of graph.edges()) {
|
|
33
|
+
if (!(weight >= 0) || weight === Infinity) {
|
|
34
|
+
return false;
|
|
223
35
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
/**
|
|
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
|
|
234
|
-
*/
|
|
235
|
-
function inflate(matrix: number[][], inflation: number): number[][] {
|
|
236
|
-
const n = matrix.length;
|
|
237
|
-
const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
|
|
238
|
-
|
|
239
|
-
// Element-wise powering
|
|
240
|
-
for (let i = 0; i < n; i++) {
|
|
241
|
-
for (let j = 0; j < n; j++) {
|
|
242
|
-
const val = matrix[i]?.[j];
|
|
243
|
-
if (val !== undefined) {
|
|
244
|
-
const resultRow = result[i];
|
|
245
|
-
if (resultRow) {
|
|
246
|
-
resultRow[j] = Math.pow(val, inflation);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
36
|
+
// An undirected edge is stored once per direction, and `getEdge` hands out either half, so
|
|
37
|
+
// a weight set in place can leave the halves disagreeing. The snapshot holds one weight per
|
|
38
|
+
// edge; the old code reads each half, so it keeps such a graph.
|
|
39
|
+
if (!graph.isDirected && (graph.getEdge(target, source)?.weight ?? 1) !== weight) {
|
|
40
|
+
return false;
|
|
249
41
|
}
|
|
250
42
|
}
|
|
251
|
-
|
|
252
|
-
// Column normalization
|
|
253
|
-
for (let j = 0; j < n; j++) {
|
|
254
|
-
let colSum = 0;
|
|
255
|
-
for (let i = 0; i < n; i++) {
|
|
256
|
-
const val = result[i]?.[j];
|
|
257
|
-
if (val !== undefined) {
|
|
258
|
-
colSum += val;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (colSum > 0) {
|
|
263
|
-
for (let i = 0; i < n; i++) {
|
|
264
|
-
const row = result[i];
|
|
265
|
-
if (!row) {
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const val = row[j];
|
|
270
|
-
if (val !== undefined) {
|
|
271
|
-
row[j] = val / colSum;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return result;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
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
|
|
285
|
-
*/
|
|
286
|
-
function prune(matrix: number[][], threshold: number): number[][] {
|
|
287
|
-
const n = matrix.length;
|
|
288
|
-
const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
|
|
289
|
-
|
|
290
|
-
for (let i = 0; i < n; i++) {
|
|
291
|
-
for (let j = 0; j < n; j++) {
|
|
292
|
-
const matrixRow = matrix[i];
|
|
293
|
-
if (!matrixRow) {
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const matrixVal = matrixRow[j];
|
|
298
|
-
if (matrixVal !== undefined && matrixVal >= threshold) {
|
|
299
|
-
const resultRow = result[i];
|
|
300
|
-
if (resultRow) {
|
|
301
|
-
resultRow[j] = matrixVal;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Re-normalize columns after pruning
|
|
308
|
-
for (let j = 0; j < n; j++) {
|
|
309
|
-
let colSum = 0;
|
|
310
|
-
for (let i = 0; i < n; i++) {
|
|
311
|
-
const resultRow = result[i];
|
|
312
|
-
if (!resultRow) {
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const val = resultRow[j];
|
|
317
|
-
if (val !== undefined) {
|
|
318
|
-
colSum += val;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (colSum > 0) {
|
|
323
|
-
for (let i = 0; i < n; i++) {
|
|
324
|
-
const resultRow = result[i];
|
|
325
|
-
if (!resultRow) {
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const val = resultRow[j];
|
|
330
|
-
if (val !== undefined) {
|
|
331
|
-
resultRow[j] = val / colSum;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return result;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
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
|
|
346
|
-
*/
|
|
347
|
-
function hasConverged(oldMatrix: number[][], newMatrix: number[][], tolerance: number): boolean {
|
|
348
|
-
const n = oldMatrix.length;
|
|
349
|
-
|
|
350
|
-
for (let i = 0; i < n; i++) {
|
|
351
|
-
for (let j = 0; j < n; j++) {
|
|
352
|
-
const oldRow = oldMatrix[i];
|
|
353
|
-
const newRow = newMatrix[i];
|
|
354
|
-
if (!oldRow || !newRow) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const oldVal = oldRow[j];
|
|
359
|
-
const newVal = newRow[j];
|
|
360
|
-
if (oldVal !== undefined && newVal !== undefined && Math.abs(oldVal - newVal) > tolerance) {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
43
|
return true;
|
|
367
44
|
}
|
|
368
45
|
|
|
369
46
|
/**
|
|
370
|
-
*
|
|
371
|
-
* @param
|
|
372
|
-
* @param
|
|
373
|
-
* @returns
|
|
374
|
-
*/
|
|
375
|
-
function extractClusters(
|
|
376
|
-
matrix: number[][],
|
|
377
|
-
nodeIds: NodeId[],
|
|
378
|
-
): {
|
|
379
|
-
communities: NodeId[][];
|
|
380
|
-
attractors: Set<NodeId>;
|
|
381
|
-
} {
|
|
382
|
-
const n = matrix.length;
|
|
383
|
-
const attractors = new Set<NodeId>();
|
|
384
|
-
const communities: NodeId[][] = [];
|
|
385
|
-
const nodeToCluster = new Map<number, number>();
|
|
386
|
-
|
|
387
|
-
// Find attractors (columns with non-zero diagonal elements)
|
|
388
|
-
for (let i = 0; i < n; i++) {
|
|
389
|
-
const matrixRow = matrix[i];
|
|
390
|
-
if (!matrixRow) {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const diagonalVal = matrixRow[i];
|
|
395
|
-
const nodeId = nodeIds[i];
|
|
396
|
-
if (diagonalVal !== undefined && diagonalVal > 0 && nodeId !== undefined) {
|
|
397
|
-
attractors.add(nodeId);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Assign nodes (columns) to clusters based on which attractor (row) they flow to
|
|
402
|
-
// After MCL convergence, matrix[i][j] > 0 means node j belongs to attractor i's cluster
|
|
403
|
-
const attractorToCommunity = new Map<number, number[]>();
|
|
404
|
-
|
|
405
|
-
for (let j = 0; j < n; j++) {
|
|
406
|
-
// Find the attractor for this column (node j)
|
|
407
|
-
// Look for the row with the highest non-zero value in column j
|
|
408
|
-
let maxVal = 0;
|
|
409
|
-
let attractorRow = -1;
|
|
410
|
-
|
|
411
|
-
for (let i = 0; i < n; i++) {
|
|
412
|
-
const matrixRow = matrix[i];
|
|
413
|
-
if (!matrixRow) {
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const val = matrixRow[j];
|
|
418
|
-
if (val !== undefined && val > maxVal) {
|
|
419
|
-
maxVal = val;
|
|
420
|
-
attractorRow = i;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (attractorRow >= 0) {
|
|
425
|
-
// Node j belongs to the cluster of attractor at row attractorRow
|
|
426
|
-
let community = attractorToCommunity.get(attractorRow);
|
|
427
|
-
if (!community) {
|
|
428
|
-
community = [];
|
|
429
|
-
attractorToCommunity.set(attractorRow, community);
|
|
430
|
-
}
|
|
431
|
-
community.push(j);
|
|
432
|
-
nodeToCluster.set(j, attractorRow);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Convert attractor communities to node ID arrays
|
|
437
|
-
for (const [, memberIndices] of attractorToCommunity) {
|
|
438
|
-
communities.push(
|
|
439
|
-
memberIndices
|
|
440
|
-
.map((idx) => {
|
|
441
|
-
const nodeId = nodeIds[idx];
|
|
442
|
-
return nodeId;
|
|
443
|
-
})
|
|
444
|
-
.filter((node): node is NodeId => node !== undefined),
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Handle isolated nodes (nodes with all-zero columns)
|
|
449
|
-
for (let j = 0; j < n; j++) {
|
|
450
|
-
if (!nodeToCluster.has(j)) {
|
|
451
|
-
const nodeId = nodeIds[j];
|
|
452
|
-
if (nodeId !== undefined) {
|
|
453
|
-
communities.push([nodeId]);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return { communities, attractors };
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Calculate modularity of MCL clustering result
|
|
463
|
-
* @param graph - The original graph that was clustered
|
|
464
|
-
* @param communities - Array of communities (each community is an array of node IDs)
|
|
465
|
-
* @returns Modularity score between -0.5 and 1.0
|
|
47
|
+
* Perform Markov Clustering on a graph
|
|
48
|
+
* @param graph - The input graph to cluster
|
|
49
|
+
* @param options - MCL algorithm options (expansion, inflation, iterations, etc.)
|
|
50
|
+
* @returns MCL clustering result with communities, attractors, and convergence info
|
|
466
51
|
*/
|
|
467
|
-
export function
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
return 0;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
let modularity = 0;
|
|
474
|
-
const communityMap = new Map<NodeId, number>();
|
|
475
|
-
|
|
476
|
-
// Build community map
|
|
477
|
-
communities.forEach((community, index) => {
|
|
478
|
-
community.forEach((nodeId) => {
|
|
479
|
-
communityMap.set(nodeId, index);
|
|
480
|
-
});
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
// Calculate modularity
|
|
484
|
-
for (const edge of graph.edges()) {
|
|
485
|
-
const sourceCommunity = communityMap.get(edge.source);
|
|
486
|
-
const targetCommunity = communityMap.get(edge.target);
|
|
487
|
-
|
|
488
|
-
if (sourceCommunity !== undefined && targetCommunity !== undefined && sourceCommunity === targetCommunity) {
|
|
489
|
-
modularity += 1;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const sourceDegree = graph.degree(edge.source);
|
|
493
|
-
const targetDegree = graph.degree(edge.target);
|
|
494
|
-
modularity -= (sourceDegree * targetDegree) / (2 * m);
|
|
52
|
+
export function markovClustering(graph: Graph, options: MCLOptions = {}): MCLResult {
|
|
53
|
+
if (!portAccepts(graph, options)) {
|
|
54
|
+
return legacyMarkovClustering(graph, options);
|
|
495
55
|
}
|
|
496
|
-
|
|
497
|
-
|
|
56
|
+
const s = toSnapshot(graph);
|
|
57
|
+
const r = indexedMarkovClustering(s, { ...options, weights: exactArcWeights(s) });
|
|
58
|
+
return {
|
|
59
|
+
communities: labelsToGroups(s.ids, r.labels, r.count),
|
|
60
|
+
attractors: new Set(Array.from(r.attractors, (i) => s.ids.idOf(i))),
|
|
61
|
+
// The old loop reported one round more than it ran when it stopped at the cap.
|
|
62
|
+
iterations: r.iterations + (r.converged ? 0 : 1),
|
|
63
|
+
converged: r.converged,
|
|
64
|
+
};
|
|
498
65
|
}
|
package/src/index.ts
CHANGED
|
@@ -54,7 +54,8 @@ export * from "./optimized/index.js";
|
|
|
54
54
|
|
|
55
55
|
// Index-based implementations over @graphty/graph-format snapshots (graph-format design 14.1 rule 2).
|
|
56
56
|
// A NAMESPACE, not a flat re-export: indexed.pageRank, indexed.dijkstra, indexed.breadthFirstSearch,
|
|
57
|
-
// indexed.connectedComponents
|
|
57
|
+
// indexed.connectedComponents, indexed.kruskalMST, indexed.DeltaPageRank and
|
|
58
|
+
// indexed.PriorityDeltaPageRank all collide by name with the legacy exports above.
|
|
58
59
|
export * as indexed from "./indexed/index.js";
|
|
59
60
|
|
|
60
61
|
// The graph-format bridge (graph-format design 14.6 row A1).
|
|
@@ -65,6 +66,7 @@ export { toSnapshot } from "./indexed/to-snapshot.js";
|
|
|
65
66
|
export type {
|
|
66
67
|
AcceleratedAlgorithms,
|
|
67
68
|
AlgorithmAccelerator,
|
|
69
|
+
ApspCycleResultLike,
|
|
68
70
|
ApspResultLike,
|
|
69
71
|
BellmanFordResultLike,
|
|
70
72
|
BetweennessAcceleratorOptions,
|
|
@@ -74,33 +76,118 @@ export type {
|
|
|
74
76
|
EdgeScoresResultLike,
|
|
75
77
|
HitsOptionsLike,
|
|
76
78
|
HitsResultLike,
|
|
79
|
+
KatzOptionsLike,
|
|
77
80
|
LabelResultLike,
|
|
78
81
|
MstResultLike,
|
|
82
|
+
PageRankOptionsLike,
|
|
79
83
|
PageRankResultLike,
|
|
80
84
|
ScoresResultLike,
|
|
81
85
|
SsspResultLike,
|
|
82
86
|
} from "./indexed/accelerator.js";
|
|
83
87
|
export { accelerated } from "./indexed/accelerator.js";
|
|
84
|
-
|
|
88
|
+
// `Indexed` prefix: @graphty/webgpu-graph-algorithms publishes a different ApspOptions.
|
|
89
|
+
export type { ApspOptions as IndexedApspOptions, ApspResult as IndexedApspResult } from "./indexed/all-pairs.js";
|
|
90
|
+
// `Indexed` prefix: the flat BellmanFordResult is the legacy function's.
|
|
91
|
+
export type { BellmanFordResult as IndexedBellmanFordResult } from "./indexed/bellman-ford.js";
|
|
92
|
+
export type {
|
|
93
|
+
BetweennessOptions,
|
|
94
|
+
EdgeBetweennessOptions,
|
|
95
|
+
EdgeScoresResult,
|
|
96
|
+
ScoresResult,
|
|
97
|
+
} from "./indexed/betweenness.js";
|
|
98
|
+
export type { ArcOrderOption, BfsOptions, BfsResult, DirectionOptimizedBfsOptions } from "./indexed/bfs.js";
|
|
99
|
+
export type { BipartiteOptions, BipartiteResult } from "./indexed/bipartite.js";
|
|
100
|
+
export type { ClosenessOptions } from "./indexed/closeness.js";
|
|
85
101
|
export type { CommonNeighborsOptions } from "./indexed/common-neighbors.js";
|
|
86
102
|
export type { LabelResult } from "./indexed/components.js";
|
|
103
|
+
export type { DegreeCentralityOptions } from "./indexed/degree.js";
|
|
104
|
+
export type { DeltaPageRankComputeOptions, DeltaPageRankEngineOptions } from "./indexed/delta-pagerank.js";
|
|
105
|
+
export type { DfsOptions, DfsResult } from "./indexed/dfs.js";
|
|
87
106
|
export type { SsspOptions, SsspResult } from "./indexed/dijkstra.js";
|
|
107
|
+
// Aliased: the flat IsomorphismOptions / IsomorphismResult and BipartiteMatchingOptions /
|
|
108
|
+
// BipartiteMatchingResult name the legacy matching functions' types.
|
|
109
|
+
export type {
|
|
110
|
+
IsomorphismOptions as IndexedIsomorphismOptions,
|
|
111
|
+
IsomorphismResult as IndexedIsomorphismResult,
|
|
112
|
+
} from "./indexed/isomorphism.js";
|
|
113
|
+
export type {
|
|
114
|
+
BipartiteMatchingOptions as IndexedBipartiteMatchingOptions,
|
|
115
|
+
BipartiteMatchingResult as IndexedBipartiteMatchingResult,
|
|
116
|
+
} from "./indexed/matching.js";
|
|
117
|
+
// Aliased: the flat MaxFlowResult and MinCutResult name the legacy flow functions' types.
|
|
118
|
+
export type {
|
|
119
|
+
BipartiteFlowNetwork as IndexedBipartiteFlowNetwork,
|
|
120
|
+
MaxFlowOptions as IndexedMaxFlowOptions,
|
|
121
|
+
MaxFlowResult as IndexedMaxFlowResult,
|
|
122
|
+
MinCutResult as IndexedMinCutResult,
|
|
123
|
+
} from "./indexed/flow.js";
|
|
88
124
|
// The `Indexed` prefix, as on the PageRank pair below: the flat names HITSOptions / HITSResult,
|
|
89
125
|
// KatzCentralityOptions and LouvainOptions already belong to the legacy functions above, and two
|
|
90
126
|
// option types one capital letter apart in the same barrel is a trap, not a convenience.
|
|
127
|
+
export type {
|
|
128
|
+
EigenvectorOptions as IndexedEigenvectorOptions,
|
|
129
|
+
EigenvectorResult as IndexedEigenvectorResult,
|
|
130
|
+
} from "./indexed/eigenvector.js";
|
|
131
|
+
// Unprefixed: the legacy clustering types are named LinkageMethod, MCL* and SpectralClustering*.
|
|
132
|
+
export type { HierarchicalOptions, HierarchicalResult, Linkage } from "./indexed/hierarchical.js";
|
|
133
|
+
// Aliased: the flat GRSBMConfig / GRSBMResult / GRSBMCluster, SynCConfig / SynCResult and
|
|
134
|
+
// TeraHACConfig / TeraHACResult name the legacy research functions' types, one capital letter away.
|
|
135
|
+
export type {
|
|
136
|
+
GrsbmCluster as IndexedGrsbmCluster,
|
|
137
|
+
GrsbmOptions as IndexedGrsbmOptions,
|
|
138
|
+
GrsbmResult as IndexedGrsbmResult,
|
|
139
|
+
GrsbmSplit as IndexedGrsbmSplit,
|
|
140
|
+
} from "./indexed/grsbm.js";
|
|
91
141
|
export type { HitsOptions as IndexedHitsOptions, HitsResult as IndexedHitsResult } from "./indexed/hits.js";
|
|
92
142
|
export type { CorenessResult } from "./indexed/k-core.js";
|
|
93
143
|
export type { KatzOptions as IndexedKatzOptions, KatzResult as IndexedKatzResult } from "./indexed/katz.js";
|
|
144
|
+
export type {
|
|
145
|
+
SyncClusteringOptions as IndexedSyncClusteringOptions,
|
|
146
|
+
SyncClusteringResult as IndexedSyncClusteringResult,
|
|
147
|
+
} from "./indexed/sync.js";
|
|
148
|
+
export type {
|
|
149
|
+
TeraHacOptions as IndexedTeraHacOptions,
|
|
150
|
+
TeraHacResult as IndexedTeraHacResult,
|
|
151
|
+
} from "./indexed/terahac.js";
|
|
152
|
+
// Aliased: the flat LabelPropagationOptions / LabelPropagationResult name the legacy function's types.
|
|
153
|
+
export type {
|
|
154
|
+
LabelPropagationOptions as IndexedLabelPropagationOptions,
|
|
155
|
+
LabelPropagationResult as IndexedLabelPropagationResult,
|
|
156
|
+
SynchronousLabelPropagationOptions as IndexedSynchronousLabelPropagationOptions,
|
|
157
|
+
} from "./indexed/label-propagation.js";
|
|
158
|
+
// Aliased: the flat LeidenOptions / LeidenResult and GirvanNewmanOptions name the legacy functions' types.
|
|
159
|
+
export type {
|
|
160
|
+
GirvanNewmanOptions as IndexedGirvanNewmanOptions,
|
|
161
|
+
GirvanNewmanResult as IndexedGirvanNewmanResult,
|
|
162
|
+
} from "./indexed/girvan-newman.js";
|
|
163
|
+
export type { LeidenOptions as IndexedLeidenOptions, LeidenResult as IndexedLeidenResult } from "./indexed/leiden.js";
|
|
94
164
|
export type {
|
|
95
165
|
LouvainOptions as IndexedLouvainOptions,
|
|
96
166
|
LouvainResult as IndexedLouvainResult,
|
|
97
167
|
} from "./indexed/louvain.js";
|
|
98
|
-
export type {
|
|
168
|
+
export type {
|
|
169
|
+
KargerOptions as IndexedKargerOptions,
|
|
170
|
+
StoerWagnerOptions as IndexedStoerWagnerOptions,
|
|
171
|
+
} from "./indexed/min-cut.js";
|
|
172
|
+
export type { MstOptions, MstResult, PrimOptions, PrimResult } from "./indexed/mst.js";
|
|
173
|
+
export type { CondensationResult } from "./indexed/scc.js";
|
|
174
|
+
// Aliased: the flat LinkPredictionOptions names the legacy functions' options.
|
|
175
|
+
export type {
|
|
176
|
+
CandidateOptions as IndexedCandidateOptions,
|
|
177
|
+
LinkPredictionOptions as IndexedLinkPredictionOptions,
|
|
178
|
+
LinkPredictionMetrics,
|
|
179
|
+
LinkPredictionResult,
|
|
180
|
+
NodePairs,
|
|
181
|
+
} from "./indexed/link-prediction.js";
|
|
182
|
+
export type { MarkovOptions, MarkovResult } from "./indexed/markov.js";
|
|
183
|
+
export type { ModularityOptions } from "./indexed/modularity.js";
|
|
99
184
|
// Aliased: the flat names are taken twice over (types/index.ts:96 and centrality/pagerank.ts:15).
|
|
100
185
|
export type {
|
|
101
186
|
PageRankOptions as IndexedPageRankOptions,
|
|
102
187
|
PageRankResult as IndexedPageRankResult,
|
|
103
188
|
} from "./indexed/pagerank.js";
|
|
189
|
+
export type { AstarResult, PathOptions, PathResult } from "./indexed/point-to-point.js";
|
|
190
|
+
export type { LaplacianType, SpectralOptions, SpectralResult } from "./indexed/spectral.js";
|
|
104
191
|
|
|
105
192
|
// Note: Configuration exports have been removed.
|
|
106
193
|
// The library now automatically optimizes based on graph size.
|