@graphty/algorithms 2.1.1 → 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
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { INVALID_INDEX, renumberPartition, } from "@graphty/graph-format";
|
|
2
|
+
import { withGroups } from "./components.js";
|
|
3
|
+
/** Epochs run 0 .. EPOCH_LIMIT - 1 between resets of the Int32 stamp arrays. */
|
|
4
|
+
const EPOCH_LIMIT = 0x7fffffff;
|
|
5
|
+
/**
|
|
6
|
+
* mulberry32: 32-bit state, output in [0, 1). The exact sequence is part of the result contract
|
|
7
|
+
* (design section 2.4): changing it changes every partition.
|
|
8
|
+
* @param seed - Generator seed; only its low 32 bits are used
|
|
9
|
+
* @returns The generator
|
|
10
|
+
*/
|
|
11
|
+
export function mulberry32(seed) {
|
|
12
|
+
let a = seed >>> 0;
|
|
13
|
+
return () => {
|
|
14
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
15
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
16
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
17
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Whether any arc other than a self-loop carries a positive vote under the chosen weighting, which
|
|
22
|
+
* is exactly when the identity labelling is NOT dominant everywhere.
|
|
23
|
+
* @param s - The snapshot
|
|
24
|
+
* @param weights - The arc weights in use, or null when every arc votes 1
|
|
25
|
+
* @returns True when some node has a positive-weight neighbour
|
|
26
|
+
*/
|
|
27
|
+
function hasVotingArc(s, weights) {
|
|
28
|
+
for (let u = 0; u < s.nodeCount; u++) {
|
|
29
|
+
const end = s.rowPtr[u + 1];
|
|
30
|
+
for (let a = s.rowPtr[u]; a < end; a++) {
|
|
31
|
+
if (s.colIdx[a] !== u && (weights === null || weights[a] > 0)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether every node not held fixed holds a label at the largest vote among its neighbours (or has
|
|
40
|
+
* no neighbour that votes).
|
|
41
|
+
* @param tally - A tally over the voting rows
|
|
42
|
+
* @param label - The label of every node
|
|
43
|
+
* @param fixed - 1 for a node held at its label, or null when none is
|
|
44
|
+
* @returns True when no free node would move
|
|
45
|
+
*/
|
|
46
|
+
function allDominant(tally, label, fixed) {
|
|
47
|
+
for (let u = 0; u < label.length; u++) {
|
|
48
|
+
if (fixed !== null && fixed[u] === 1) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const max = tally.collect(u, label);
|
|
52
|
+
if (max !== 0 && tally.voteOf(label[u]) !== max) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The per-arc weights of `view` that the votes use: the snapshot's exact f64 weights when it keeps
|
|
60
|
+
* them (graph-format keeps a role-"weight" f64 edge column whenever some weight is not f32-exact),
|
|
61
|
+
* gathered through arcToEdge; otherwise the view's own f32 arc array. Voting on the f32 values would
|
|
62
|
+
* turn 1 and 1 + 1e-9 into a tie, 1e39 into Infinity and 1e-50 into 0.
|
|
63
|
+
* @param view - The forward snapshot or its reverse view
|
|
64
|
+
* @param exact - The exact per-edge weights, or null when the f32 arc array is exact
|
|
65
|
+
* @returns The arc weights, or null when every arc weighs 1
|
|
66
|
+
*/
|
|
67
|
+
export function arcWeightsOf(view, exact) {
|
|
68
|
+
if (exact === null) {
|
|
69
|
+
return view.weights;
|
|
70
|
+
}
|
|
71
|
+
const out = new Float64Array(view.arcCount);
|
|
72
|
+
for (let a = 0; a < out.length; a++) {
|
|
73
|
+
out[a] = exact[view.arcToEdge[a]];
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The exact per-edge weights of `s`: its role-"weight" f64 edge column when it keeps one, else null
|
|
79
|
+
* (the f32 arc array is then exact).
|
|
80
|
+
* @param s - The snapshot
|
|
81
|
+
* @returns The exact weights per logical edge, or null
|
|
82
|
+
*/
|
|
83
|
+
export function exactEdgeWeights(s) {
|
|
84
|
+
const shadow = s.edges.byRole("weight");
|
|
85
|
+
return shadow !== null && shadow.dtype === "f64" ? shadow.data : null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Gather the voting rows of `s` and reject weights a vote cannot use.
|
|
89
|
+
* @param s - The snapshot
|
|
90
|
+
* @param weighted - Sum arc weights (true) or count each arc as 1 (false)
|
|
91
|
+
* @returns The rows and their weights
|
|
92
|
+
* @throws RangeError when a weight is negative, NaN or infinite
|
|
93
|
+
*/
|
|
94
|
+
function votingRows(s, weighted) {
|
|
95
|
+
const weights = weighted ? arcWeightsOf(s, exactEdgeWeights(s)) : null;
|
|
96
|
+
if (weights !== null) {
|
|
97
|
+
for (let a = 0; a < weights.length; a++) {
|
|
98
|
+
const w = weights[a];
|
|
99
|
+
if (!(w >= 0) || w === Infinity) {
|
|
100
|
+
throw new RangeError(`arc ${a} has weight ${w}; label propagation needs finite, non-negative weights`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const rev = s.directed ? s.reverse() : null;
|
|
105
|
+
return {
|
|
106
|
+
sides: rev === null ? 1 : 2,
|
|
107
|
+
rowPtrs: [s.rowPtr, rev === null ? s.rowPtr : rev.rowPtr],
|
|
108
|
+
colIdxs: [s.colIdx, rev === null ? s.colIdx : rev.colIdx],
|
|
109
|
+
arcWeights: [weights, weights === null || rev === null ? weights : arcWeightsOf(rev, exactEdgeWeights(s))],
|
|
110
|
+
weights,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check the options every label propagation shares.
|
|
115
|
+
* @param n - The node count
|
|
116
|
+
* @param maxIterations - The work cap
|
|
117
|
+
* @param randomSeed - The seed, or null when the variant takes none
|
|
118
|
+
* @throws RangeError for a bad option
|
|
119
|
+
*/
|
|
120
|
+
function checkOptions(n, maxIterations, randomSeed) {
|
|
121
|
+
if (!Number.isInteger(maxIterations) || maxIterations < 0) {
|
|
122
|
+
throw new RangeError(`maxIterations must be a non-negative integer, got ${maxIterations}`);
|
|
123
|
+
}
|
|
124
|
+
if (randomSeed !== null && !Number.isInteger(randomSeed)) {
|
|
125
|
+
throw new RangeError(`randomSeed must be a finite integer, got ${randomSeed}`);
|
|
126
|
+
}
|
|
127
|
+
if (maxIterations * n > Number.MAX_SAFE_INTEGER) {
|
|
128
|
+
throw new RangeError(`maxIterations * nodeCount (${maxIterations} * ${n}) exceeds Number.MAX_SAFE_INTEGER`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The per-node vote tally shared by the queue kernel and the synchronous passes: an accumulator
|
|
133
|
+
* per label, stamped by visit so it never needs clearing.
|
|
134
|
+
*/
|
|
135
|
+
class Tally {
|
|
136
|
+
constructor(n, rows, weighted) {
|
|
137
|
+
this.rows = rows;
|
|
138
|
+
this.epoch = -1;
|
|
139
|
+
this.count = 0;
|
|
140
|
+
this.acc = new Float64Array(n);
|
|
141
|
+
this.stamp = new Int32Array(n).fill(-1);
|
|
142
|
+
this.touched = new Uint32Array(n);
|
|
143
|
+
this.seen = weighted ? null : new Int32Array(n).fill(-1);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Sum the votes of u's neighbours per label into `acc`, listing the labels met in `touched`.
|
|
147
|
+
* @param u - The node
|
|
148
|
+
* @param label - The label of every node
|
|
149
|
+
* @returns The largest vote, 0 when no neighbour votes
|
|
150
|
+
*/
|
|
151
|
+
collect(u, label) {
|
|
152
|
+
if (++this.epoch === EPOCH_LIMIT) {
|
|
153
|
+
// Reset before the epoch leaves the Int32 range: O(n) once per 2^31 - 1 visits.
|
|
154
|
+
this.stamp.fill(-1);
|
|
155
|
+
this.seen?.fill(-1);
|
|
156
|
+
this.epoch = 0;
|
|
157
|
+
}
|
|
158
|
+
const { acc, stamp, touched, seen, rows } = this;
|
|
159
|
+
const visit = this.epoch;
|
|
160
|
+
let count = 0;
|
|
161
|
+
for (let side = 0; side < rows.sides; side++) {
|
|
162
|
+
const rowPtr = rows.rowPtrs[side];
|
|
163
|
+
const colIdx = rows.colIdxs[side];
|
|
164
|
+
const w = rows.arcWeights[side];
|
|
165
|
+
const end = rowPtr[u + 1];
|
|
166
|
+
for (let a = rowPtr[u]; a < end; a++) {
|
|
167
|
+
const v = colIdx[a];
|
|
168
|
+
if (v === u) {
|
|
169
|
+
continue; // a self-loop does not vote
|
|
170
|
+
}
|
|
171
|
+
if (seen !== null) {
|
|
172
|
+
if (seen[v] === visit) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
seen[v] = visit;
|
|
176
|
+
}
|
|
177
|
+
const c = label[v];
|
|
178
|
+
const vote = w === null ? 1 : w[a];
|
|
179
|
+
if (stamp[c] !== visit) {
|
|
180
|
+
stamp[c] = visit;
|
|
181
|
+
acc[c] = vote;
|
|
182
|
+
touched[count++] = c;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
acc[c] += vote;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.count = count;
|
|
190
|
+
let max = 0;
|
|
191
|
+
for (let i = 0; i < count; i++) {
|
|
192
|
+
if (acc[touched[i]] > max) {
|
|
193
|
+
max = acc[touched[i]];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return max;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* The vote label c received in the last `collect`.
|
|
200
|
+
* @param c - A label
|
|
201
|
+
* @returns Its vote, 0 when no neighbour holds it
|
|
202
|
+
*/
|
|
203
|
+
voteOf(c) {
|
|
204
|
+
return this.stamp[c] === this.epoch ? this.acc[c] : 0;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* The FLPA queue kernel over `label`, updated in place. Nodes marked in `fixed` never enter the
|
|
209
|
+
* queue, so they keep their starting label.
|
|
210
|
+
* @param s - The snapshot
|
|
211
|
+
* @param rows - Its voting rows
|
|
212
|
+
* @param weighted - Whether the rows carry weights
|
|
213
|
+
* @param label - Starting label of every node, each below nodeCount; overwritten with the result
|
|
214
|
+
* @param fixed - 1 for a node whose label never changes, or null
|
|
215
|
+
* @param maxIterations - Work cap in node visits per node
|
|
216
|
+
* @param randomSeed - Seed of the visit order and the tie draws
|
|
217
|
+
* @returns The node visits run and the nodes still queued
|
|
218
|
+
*/
|
|
219
|
+
function flpa(s, rows, weighted, label, fixed, maxIterations, randomSeed) {
|
|
220
|
+
const n = s.nodeCount;
|
|
221
|
+
const rand = mulberry32(randomSeed);
|
|
222
|
+
const tally = new Tally(n, rows, weighted);
|
|
223
|
+
// A ring of n + 1 slots: `queued` keeps a node in it at most once.
|
|
224
|
+
const capacity = n + 1;
|
|
225
|
+
const queue = new Uint32Array(capacity);
|
|
226
|
+
const queued = new Uint8Array(n);
|
|
227
|
+
let size = 0;
|
|
228
|
+
for (let i = 0; i < n; i++) {
|
|
229
|
+
if (fixed === null || fixed[i] === 0) {
|
|
230
|
+
queue[size++] = i;
|
|
231
|
+
queued[i] = 1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (let i = size - 1; i >= 1; i--) {
|
|
235
|
+
const j = Math.floor(rand() * (i + 1));
|
|
236
|
+
const t = queue[i];
|
|
237
|
+
queue[i] = queue[j];
|
|
238
|
+
queue[j] = t;
|
|
239
|
+
}
|
|
240
|
+
let head = 0;
|
|
241
|
+
let tail = size;
|
|
242
|
+
const maxVisits = maxIterations * n;
|
|
243
|
+
let visits = 0;
|
|
244
|
+
const { touched, acc } = tally;
|
|
245
|
+
while (size > 0 && visits < maxVisits) {
|
|
246
|
+
const u = queue[head];
|
|
247
|
+
head = head + 1 === capacity ? 0 : head + 1;
|
|
248
|
+
size--;
|
|
249
|
+
queued[u] = 0;
|
|
250
|
+
visits++;
|
|
251
|
+
const max = tally.collect(u, label);
|
|
252
|
+
if (max === 0) {
|
|
253
|
+
continue; // no positive-weight neighbour: keep the label
|
|
254
|
+
}
|
|
255
|
+
// Reservoir draw over the labels at the maximum: the k-th replaces the pick with
|
|
256
|
+
// probability 1/k, so a single dominant label consumes no random number.
|
|
257
|
+
let pick = 0;
|
|
258
|
+
let tied = 0;
|
|
259
|
+
for (let i = 0; i < tally.count; i++) {
|
|
260
|
+
const c = touched[i];
|
|
261
|
+
if (acc[c] === max) {
|
|
262
|
+
tied++;
|
|
263
|
+
if (tied === 1 || rand() * tied < 1) {
|
|
264
|
+
pick = c;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (pick === label[u]) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
label[u] = pick;
|
|
272
|
+
// A neighbour already holding the new label can only have been strengthened by the move.
|
|
273
|
+
// Both rows, so on a directed snapshot the nodes that read u are requeued too.
|
|
274
|
+
for (let side = 0; side < rows.sides; side++) {
|
|
275
|
+
const rowPtr = rows.rowPtrs[side];
|
|
276
|
+
const colIdx = rows.colIdxs[side];
|
|
277
|
+
const end = rowPtr[u + 1];
|
|
278
|
+
for (let a = rowPtr[u]; a < end; a++) {
|
|
279
|
+
const v = colIdx[a];
|
|
280
|
+
if (queued[v] === 0 && label[v] !== pick && (fixed === null || fixed[v] === 0)) {
|
|
281
|
+
queued[v] = 1;
|
|
282
|
+
queue[tail] = v;
|
|
283
|
+
tail = tail + 1 === capacity ? 0 : tail + 1;
|
|
284
|
+
size++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return { visits, size };
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Community detection by fast label propagation (FLPA; Traag and Subelj, Sci. Rep. 13:2701, 2023,
|
|
293
|
+
* Algorithm 3), the queue-driven form of Raghavan, Albert and Kumara's asynchronous label
|
|
294
|
+
* propagation.
|
|
295
|
+
*
|
|
296
|
+
* Every node starts in its own community. Nodes are visited from a queue that starts as a seeded
|
|
297
|
+
* shuffle of all of them; a visited node adopts the label with the largest summed arc weight among
|
|
298
|
+
* its neighbours, drawing uniformly at random among tied labels (its current label included, with
|
|
299
|
+
* no priority). When its label changes, every neighbour holding a different label is queued again.
|
|
300
|
+
* The run ends when the queue empties -- then every node's label is dominant among its neighbours
|
|
301
|
+
* and `converged` is true -- or after `maxIterations * nodeCount` node visits.
|
|
302
|
+
*
|
|
303
|
+
* Conventions: self-loops are skipped; parallel arcs are summed; on a directed snapshot a node's
|
|
304
|
+
* neighbours are its out-arcs AND its in-arcs, so a reciprocal pair counts twice. With
|
|
305
|
+
* `weighted: false` each distinct neighbour votes once. A negative, NaN or infinite weight throws.
|
|
306
|
+
* Votes use the exact f64 weights when the snapshot keeps them (as `toSnapshot` does for a legacy
|
|
307
|
+
* graph whose weights are not all f32-exact), so a weight rounded in the f32 arc array never
|
|
308
|
+
* decides a vote.
|
|
309
|
+
*
|
|
310
|
+
* One `randomSeed` gives one result, bit for bit. The partitions differ from the legacy
|
|
311
|
+
* `labelPropagation` for the same seed: the random stream and the stop rule are different
|
|
312
|
+
* (design `design/algorithms/label-propagation-indexed-port-design.md`, section 3).
|
|
313
|
+
* @param s - Any snapshot
|
|
314
|
+
* @param options - Work cap, seed and weighting
|
|
315
|
+
* @returns The partition, the full-sweep equivalents run, and whether the work queue emptied
|
|
316
|
+
* @public
|
|
317
|
+
*/
|
|
318
|
+
export function labelPropagation(s, options = {}) {
|
|
319
|
+
const maxIterations = options.maxIterations ?? 100;
|
|
320
|
+
const randomSeed = options.randomSeed ?? 42;
|
|
321
|
+
const weighted = options.weighted ?? true;
|
|
322
|
+
const n = s.nodeCount;
|
|
323
|
+
checkOptions(n, maxIterations, randomSeed);
|
|
324
|
+
const rows = votingRows(s, weighted);
|
|
325
|
+
const label = new Uint32Array(n);
|
|
326
|
+
for (let i = 0; i < n; i++) {
|
|
327
|
+
label[i] = i;
|
|
328
|
+
}
|
|
329
|
+
if (n === 0 || maxIterations === 0) {
|
|
330
|
+
const { labels, count } = renumberPartition(label);
|
|
331
|
+
return { ...withGroups(labels, count), iterations: 0, converged: !hasVotingArc(s, rows.weights) };
|
|
332
|
+
}
|
|
333
|
+
const { visits, size } = flpa(s, rows, weighted, label, null, maxIterations, randomSeed);
|
|
334
|
+
const { labels, count } = renumberPartition(label);
|
|
335
|
+
return { ...withGroups(labels, count), iterations: Math.ceil(visits / n), converged: size === 0 };
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Semi-supervised label propagation: the FLPA kernel of {@link labelPropagation} with some nodes
|
|
339
|
+
* held at a given label (the `initial` / `fixed` inputs of igraph's `community_label_propagation`).
|
|
340
|
+
*
|
|
341
|
+
* A seeded node keeps its label for the whole run and is never visited; every other node starts in
|
|
342
|
+
* its own community and moves as in `labelPropagation`. Seeds that share a label share one community
|
|
343
|
+
* from the start, and seeds with different labels are never merged, so in the result two seeds
|
|
344
|
+
* carry the same label exactly when their seed labels are equal. The result is renumbered to
|
|
345
|
+
* `0..count-1` in first-seen node order like every other partition here, so a seed label's VALUE is
|
|
346
|
+
* not kept: read a seed's community through `labels[seedNode]`. With no seed at all this is
|
|
347
|
+
* `labelPropagation` with the same seed and options, bit for bit.
|
|
348
|
+
*
|
|
349
|
+
* `converged` is true when the work queue emptied, or when `maxIterations` is 0 and the starting
|
|
350
|
+
* labels already are dominant: every unseeded node's label is dominant among its neighbours.
|
|
351
|
+
* Seeded nodes are not held to that.
|
|
352
|
+
* @param s - Any snapshot
|
|
353
|
+
* @param seeds - One entry per node: its fixed label, any value but `INVALID_INDEX`, or
|
|
354
|
+
* `INVALID_INDEX` for a node free to move
|
|
355
|
+
* @param options - Work cap, seed of the visit order and weighting
|
|
356
|
+
* @returns The partition, the full-sweep equivalents run, and whether the work queue emptied
|
|
357
|
+
* @throws RangeError when `seeds` is not one entry per node, or for a bad option or weight
|
|
358
|
+
* @public
|
|
359
|
+
*/
|
|
360
|
+
export function labelPropagationSemiSupervised(s, seeds, options = {}) {
|
|
361
|
+
const maxIterations = options.maxIterations ?? 100;
|
|
362
|
+
const randomSeed = options.randomSeed ?? 42;
|
|
363
|
+
const weighted = options.weighted ?? true;
|
|
364
|
+
const n = s.nodeCount;
|
|
365
|
+
if (seeds.length !== n) {
|
|
366
|
+
throw new RangeError(`seeds has ${seeds.length} entries; the snapshot has ${n} nodes`);
|
|
367
|
+
}
|
|
368
|
+
checkOptions(n, maxIterations, randomSeed);
|
|
369
|
+
const rows = votingRows(s, weighted);
|
|
370
|
+
// The kernel's labels are node indices, so a seed label becomes the index of the first node
|
|
371
|
+
// seeded with it: a free node's own index can never collide with it.
|
|
372
|
+
const label = new Uint32Array(n);
|
|
373
|
+
const fixed = new Uint8Array(n);
|
|
374
|
+
const firstWith = new Map();
|
|
375
|
+
for (let u = 0; u < n; u++) {
|
|
376
|
+
const seed = seeds[u];
|
|
377
|
+
if (seed === INVALID_INDEX) {
|
|
378
|
+
label[u] = u;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
let first = firstWith.get(seed);
|
|
382
|
+
if (first === undefined) {
|
|
383
|
+
first = u;
|
|
384
|
+
firstWith.set(seed, u);
|
|
385
|
+
}
|
|
386
|
+
label[u] = first;
|
|
387
|
+
fixed[u] = 1;
|
|
388
|
+
}
|
|
389
|
+
const { visits, size } = flpa(s, rows, weighted, label, fixed, maxIterations, randomSeed);
|
|
390
|
+
// At maxIterations 0 nothing was visited, so the queue says nothing: check the labels, as
|
|
391
|
+
// labelPropagation does.
|
|
392
|
+
const converged = size === 0 || (maxIterations === 0 && allDominant(new Tally(n, rows, weighted), label, fixed));
|
|
393
|
+
const { labels, count } = renumberPartition(label);
|
|
394
|
+
return { ...withGroups(labels, count), iterations: n === 0 ? 0 : Math.ceil(visits / n), converged };
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Label propagation in synchronous passes: every node reads its neighbours' labels from the
|
|
398
|
+
* previous pass, and all nodes move at once. Deterministic, with no random stream.
|
|
399
|
+
*
|
|
400
|
+
* A node keeps its label when that label is dominant among its neighbours; otherwise it takes the
|
|
401
|
+
* LOWEST label with the largest summed vote. Synchronous updates alone make two neighbours trade
|
|
402
|
+
* labels for ever (on a single edge a-b, a takes b's label while b takes a's), so passes alternate
|
|
403
|
+
* a swap guard: an even pass (the first is pass 0) lets a node move only to a higher label, an odd
|
|
404
|
+
* pass only to a lower one (the up/down rule of cuGraph's Louvain move phase). The run stops after
|
|
405
|
+
* two passes in a row with no move, one up and one down -- then every node's label is dominant and
|
|
406
|
+
* `converged` is true -- or after `maxIterations` passes.
|
|
407
|
+
*
|
|
408
|
+
* The guard stops the single-edge swap but not every cycle: on some weighted graphs a node climbs to
|
|
409
|
+
* a higher label on each up pass and falls back on each down pass. When a pass returns the labels
|
|
410
|
+
* of two passes before, the run can only repeat itself, so it stops there with `converged` false;
|
|
411
|
+
* the result is then the same for any larger `maxIterations`. Longer cycles run to the cap.
|
|
412
|
+
*
|
|
413
|
+
* This is what the legacy `labelPropagationAsync` does, despite its name, with the swap guard it
|
|
414
|
+
* lacks and the tie rule applied to the finished tally rather than to a running one. Conventions
|
|
415
|
+
* match {@link labelPropagation}: self-loops are skipped, parallel arcs summed, a directed snapshot
|
|
416
|
+
* reads out- and in-neighbours, weights are the exact f64 ones when the snapshot keeps them, and a
|
|
417
|
+
* negative, NaN or infinite weight throws.
|
|
418
|
+
* @param s - Any snapshot
|
|
419
|
+
* @param options - Pass cap and weighting
|
|
420
|
+
* @returns The partition, the passes run, and whether every label ended dominant
|
|
421
|
+
* @public
|
|
422
|
+
*/
|
|
423
|
+
export function labelPropagationSynchronous(s, options = {}) {
|
|
424
|
+
const maxIterations = options.maxIterations ?? 100;
|
|
425
|
+
const weighted = options.weighted ?? true;
|
|
426
|
+
const n = s.nodeCount;
|
|
427
|
+
checkOptions(n, maxIterations, null);
|
|
428
|
+
const rows = votingRows(s, weighted);
|
|
429
|
+
const tally = new Tally(n, rows, weighted);
|
|
430
|
+
const { touched, acc } = tally;
|
|
431
|
+
let label = new Uint32Array(n);
|
|
432
|
+
let next = new Uint32Array(n);
|
|
433
|
+
// The labels two passes back, to catch a period-2 cycle.
|
|
434
|
+
let before = new Uint32Array(n).fill(INVALID_INDEX);
|
|
435
|
+
for (let i = 0; i < n; i++) {
|
|
436
|
+
label[i] = i;
|
|
437
|
+
}
|
|
438
|
+
let passes = 0;
|
|
439
|
+
let quiet = 0;
|
|
440
|
+
let cycling = false;
|
|
441
|
+
while (quiet < 2 && passes < maxIterations && !cycling) {
|
|
442
|
+
const up = passes % 2 === 0;
|
|
443
|
+
passes++;
|
|
444
|
+
let moved = false;
|
|
445
|
+
for (let u = 0; u < n; u++) {
|
|
446
|
+
const current = label[u];
|
|
447
|
+
next[u] = current;
|
|
448
|
+
const max = tally.collect(u, label);
|
|
449
|
+
if (max === 0 || tally.voteOf(current) === max) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
let best = INVALID_INDEX;
|
|
453
|
+
for (let i = 0; i < tally.count; i++) {
|
|
454
|
+
const c = touched[i];
|
|
455
|
+
if (acc[c] === max && c < best) {
|
|
456
|
+
best = c;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (up ? best > current : best < current) {
|
|
460
|
+
next[u] = best;
|
|
461
|
+
moved = true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
cycling = moved && sameLabels(next, before);
|
|
465
|
+
[before, label, next] = [label, next, before];
|
|
466
|
+
quiet = moved ? 0 : quiet + 1;
|
|
467
|
+
}
|
|
468
|
+
const converged = quiet >= 2 || allDominant(tally, label, null);
|
|
469
|
+
const { labels, count } = renumberPartition(label);
|
|
470
|
+
return { ...withGroups(labels, count), iterations: passes, converged };
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Whether two label arrays are equal.
|
|
474
|
+
* @param a - Labels
|
|
475
|
+
* @param b - Labels of the same length
|
|
476
|
+
* @returns True when every entry matches
|
|
477
|
+
*/
|
|
478
|
+
function sameLabels(a, b) {
|
|
479
|
+
for (let i = 0; i < a.length; i++) {
|
|
480
|
+
if (a[i] !== b[i]) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
//# sourceMappingURL=label-propagation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label-propagation.js","sourceRoot":"","sources":["../../../src/indexed/label-propagation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type GraphSnapshot } from "@graphty/graph-format";
|
|
2
|
+
import { type LabelResult } from "./components.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options of the index-based Leiden. The names and defaults are the legacy `leiden`'s, but
|
|
5
|
+
* `maxIterations` counts something else: see below.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export interface LeidenOptions {
|
|
9
|
+
/** Resolution gamma: above 1 favours smaller communities; default 1. */
|
|
10
|
+
readonly resolution?: number | undefined;
|
|
11
|
+
/** Seed of the visit orders; default 42. */
|
|
12
|
+
readonly randomSeed?: number | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Cap on whole passes over the original graph, each running as many levels as it needs; default
|
|
15
|
+
* 100. The legacy `leiden` caps levels instead, so its `maxIterations: 1` is one level where
|
|
16
|
+
* this is one full pass. The result's `iterations` counts levels.
|
|
17
|
+
*/
|
|
18
|
+
readonly maxIterations?: number | undefined;
|
|
19
|
+
/** Stop once a pass improves modularity by no more than this; default 1e-7. */
|
|
20
|
+
readonly threshold?: number | undefined;
|
|
21
|
+
}
|
|
22
|
+
/** Result of the index-based Leiden: a partition plus the modularity it reaches. @public */
|
|
23
|
+
export interface LeidenResult extends LabelResult {
|
|
24
|
+
/** Modularity of the returned partition, at the requested resolution. */
|
|
25
|
+
readonly modularity: number;
|
|
26
|
+
/** Levels processed, summed over every pass. */
|
|
27
|
+
readonly iterations: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Leiden community detection (Traag, Waltman and van Eck, Sci. Rep. 9:5233, 2019) over an
|
|
31
|
+
* undirected snapshot: fast local moving, refinement into well-connected sub-communities, and
|
|
32
|
+
* aggregation by `contract()` once per level. Whole passes repeat from the previous answer, keeping
|
|
33
|
+
* a pass only when it raises modularity, until three passes in a row improve it by no more than
|
|
34
|
+
* `threshold` or `maxIterations` passes have run; every pass starts by moving single original
|
|
35
|
+
* nodes, so the answer is also a local optimum of those moves.
|
|
36
|
+
*
|
|
37
|
+
* Modularity follows graph-format's `weightedDegree()` convention (a self-loop counts twice), which
|
|
38
|
+
* on a graph without self-loops equals the legacy `leiden`'s. Weights are the exact f64 ones when
|
|
39
|
+
* the snapshot keeps them. The partition is not the legacy function's move for move: the legacy
|
|
40
|
+
* `leiden` splits disconnected communities rather than refining them, and visits nodes in another
|
|
41
|
+
* order; what this guarantees is connected communities. Both are randomised heuristics, so for one
|
|
42
|
+
* seed either can come out ahead; on the shared fixtures this one's modularity is at least the
|
|
43
|
+
* legacy's at the default seed and on average over seeds.
|
|
44
|
+
* @param s - An undirected snapshot
|
|
45
|
+
* @param options - Resolution, seed, pass cap and stopping threshold
|
|
46
|
+
* @returns The partition, its modularity, and the levels processed
|
|
47
|
+
* @throws Error on a directed snapshot; RangeError for a negative, NaN or infinite weight or a
|
|
48
|
+
* fractional seed
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export declare function leiden(s: GraphSnapshot, options?: LeidenOptions): LeidenResult;
|
|
52
|
+
//# sourceMappingURL=leiden.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leiden.d.ts","sourceRoot":"","sources":["../../../src/indexed/leiden.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAA+B,MAAM,uBAAuB,CAAC;AAElG,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,iBAAiB,CAAC;AAI/D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,4FAA4F;AAC5F,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC7C,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AA0UD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CAmDlF"}
|