@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
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Link prediction over a snapshot: common-neighbour and Adamic-Adar scores for one pair, a list of
|
|
3
|
+
* pairs, every pair, or one node's candidates, and the ranking metrics of a held-out evaluation.
|
|
4
|
+
*
|
|
5
|
+
* Every score is a merge of two sorted rows with simple-graph semantics: a neighbour reached by
|
|
6
|
+
* parallel arcs counts once, and a self-loop makes a node its own neighbour. With
|
|
7
|
+
* `{ directed: true }` a pair (u, v) intersects out(u) with in(v) -- the nodes on a path u -> z -> v
|
|
8
|
+
* -- and otherwise the two out rows (the two rows of an undirected snapshot).
|
|
9
|
+
*
|
|
10
|
+
* A node index at or beyond `nodeCount`, `INVALID_INDEX` included, is an absent node: its pairs
|
|
11
|
+
* score 0 and it has no candidates.
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { AdjacencyView, F64, GraphSnapshot, U32 } from "@graphty/graph-format";
|
|
16
|
+
|
|
17
|
+
import { type CommonNeighborsOptions, sortedRowMerge } from "./common-neighbors.js";
|
|
18
|
+
|
|
19
|
+
/** Options of the link prediction functions. @public */
|
|
20
|
+
export interface LinkPredictionOptions extends CommonNeighborsOptions {
|
|
21
|
+
/** Also score pairs already joined by an arc u -> v. Default false. */
|
|
22
|
+
readonly includeExisting?: boolean | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Keep only the first `topK` ranked pairs. For the whole-graph predictions a value that is not
|
|
25
|
+
* positive keeps them all; for a node's candidates the default is 10 and the value is a slice
|
|
26
|
+
* end, so 0 keeps none and -2 drops the last two.
|
|
27
|
+
*/
|
|
28
|
+
readonly topK?: number | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Options of the per-node candidate functions. @public */
|
|
32
|
+
export interface CandidateOptions extends LinkPredictionOptions {
|
|
33
|
+
/** The node indices to consider, in this order; default every node in index order. */
|
|
34
|
+
readonly candidates?: ArrayLike<number> | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Node pairs as two parallel index lists: pair k is (sources[k], targets[k]). @public */
|
|
38
|
+
export interface NodePairs {
|
|
39
|
+
readonly sources: ArrayLike<number>;
|
|
40
|
+
readonly targets: ArrayLike<number>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Ranked pairs, highest score first, ties in enumeration order. @public */
|
|
44
|
+
export interface LinkPredictionResult extends NodePairs {
|
|
45
|
+
readonly sources: U32;
|
|
46
|
+
readonly targets: U32;
|
|
47
|
+
readonly scores: F64;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The best-F1 threshold's precision and recall, that F1, and the ranking AUC. @public */
|
|
51
|
+
export interface LinkPredictionMetrics {
|
|
52
|
+
readonly precision: number;
|
|
53
|
+
readonly recall: number;
|
|
54
|
+
readonly f1Score: number;
|
|
55
|
+
readonly auc: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type PairScore = (u: number, v: number) => number;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Adamic-Adar weights are multiples of 2^-36, so any sum of them below 2^16 is exact and the same in
|
|
62
|
+
* whatever order it is added: two pairs whose common neighbours have the same degrees score
|
|
63
|
+
* bit-identically, and a ranking never splits them by rounding. The snap moves a weight by at most
|
|
64
|
+
* 2^-37, far inside the 1e-9 relative tolerance of a score.
|
|
65
|
+
*/
|
|
66
|
+
const WEIGHT_GRID = 2 ** 36;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The Adamic-Adar weight of a common neighbour of the given degree: 1 / ln(degree) snapped to the
|
|
70
|
+
* weight grid, 1 for degree 1 (ln 1 is 0) and 0 for degree 0. The legacy `adamicAdarScore` adds
|
|
71
|
+
* the unsnapped weights in neighbour-visit order, so its scores differ from these in the last bits
|
|
72
|
+
* and it may order two exactly tied pairs either way.
|
|
73
|
+
* @param degree - The neighbour's degree
|
|
74
|
+
* @returns The weight
|
|
75
|
+
*/
|
|
76
|
+
function adamicAdarWeight(degree: number): number {
|
|
77
|
+
// ponytail: exact only while a pair's score stays below 2^16 (tens of thousands of hub neighbours);
|
|
78
|
+
// a coarser grid chosen from the graph's largest row sum lifts that ceiling if it is ever reached.
|
|
79
|
+
if (degree > 1) {
|
|
80
|
+
return Math.round(WEIGHT_GRID / Math.log(degree)) / WEIGHT_GRID;
|
|
81
|
+
}
|
|
82
|
+
return degree === 1 ? 1 : 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The number of distinct entries of one row of a sorted adjacency.
|
|
87
|
+
* @param view - The adjacency
|
|
88
|
+
* @param z - The row
|
|
89
|
+
* @returns The count
|
|
90
|
+
*/
|
|
91
|
+
function distinctRowLength(view: AdjacencyView, z: number): number {
|
|
92
|
+
const start = view.rowPtr[z];
|
|
93
|
+
const end = view.rowPtr[z + 1];
|
|
94
|
+
let count = 0;
|
|
95
|
+
for (let a = start; a < end; a++) {
|
|
96
|
+
if (a === start || view.colIdx[a] !== view.colIdx[a - 1]) {
|
|
97
|
+
count++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return count;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The Adamic-Adar weight of a node as a common neighbour, from its degree in distinct neighbours:
|
|
105
|
+
* out-neighbours for `{ directed: true }` or an undirected snapshot, else out- plus in-neighbours,
|
|
106
|
+
* a self-loop in both. Costs the node's degree, so one pair costs the degrees of its common
|
|
107
|
+
* neighbours, not the whole graph.
|
|
108
|
+
* @param s - The snapshot
|
|
109
|
+
* @param directed - The `directed` option
|
|
110
|
+
* @returns The weight of node z
|
|
111
|
+
*/
|
|
112
|
+
function adamicAdarWeightOf(s: GraphSnapshot, directed: boolean): (z: number) => number {
|
|
113
|
+
const rev = s.directed && !directed ? s.reverse() : undefined;
|
|
114
|
+
return (z) => adamicAdarWeight(distinctRowLength(s, z) + (rev === undefined ? 0 : distinctRowLength(rev, z)));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The pair score: a count of common neighbours, or their weight sum when `weight` is given.
|
|
119
|
+
* @param s - The snapshot
|
|
120
|
+
* @param o - Options
|
|
121
|
+
* @param weight - Optional weight of a common neighbour
|
|
122
|
+
* @returns The score of (u, v); 0 when either is absent
|
|
123
|
+
*/
|
|
124
|
+
function scorer(s: GraphSnapshot, o: CommonNeighborsOptions, weight?: (z: number) => number): PairScore {
|
|
125
|
+
const bwd = o.directed === true ? s.reverse() : s;
|
|
126
|
+
const n = s.nodeCount;
|
|
127
|
+
return (u, v) => (u < n && v < n ? sortedRowMerge(s, bwd, u, v, weight) : 0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The Adamic-Adar pair score for many pairs: every node's weight computed once, up front.
|
|
132
|
+
* @param s - The snapshot
|
|
133
|
+
* @param o - Options
|
|
134
|
+
* @returns The score of (u, v)
|
|
135
|
+
*/
|
|
136
|
+
function adamicAdarScorer(s: GraphSnapshot, o: CommonNeighborsOptions): PairScore {
|
|
137
|
+
const weightOf = adamicAdarWeightOf(s, o.directed === true);
|
|
138
|
+
const w = Float64Array.from({ length: s.nodeCount }, (_, z) => weightOf(z));
|
|
139
|
+
return scorer(s, o, (z) => w[z]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Sort pairs by score, highest first and stable, and keep `order.slice(0, end)`.
|
|
144
|
+
* @param sources - Pair sources in enumeration order
|
|
145
|
+
* @param targets - Pair targets
|
|
146
|
+
* @param scores - Pair scores
|
|
147
|
+
* @param end - The slice end
|
|
148
|
+
* @returns The ranked pairs
|
|
149
|
+
*/
|
|
150
|
+
function ranked(sources: number[], targets: number[], scores: number[], end: number): LinkPredictionResult {
|
|
151
|
+
const order = Array.from(scores, (_, k) => k)
|
|
152
|
+
.sort((a, b) => scores[b] - scores[a])
|
|
153
|
+
.slice(0, end);
|
|
154
|
+
return {
|
|
155
|
+
sources: Uint32Array.from(order, (k) => sources[k]),
|
|
156
|
+
targets: Uint32Array.from(order, (k) => targets[k]),
|
|
157
|
+
scores: Float64Array.from(order, (k) => scores[k]),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function predict(s: GraphSnapshot, o: LinkPredictionOptions, score: PairScore): LinkPredictionResult {
|
|
162
|
+
const sources: number[] = [];
|
|
163
|
+
const targets: number[] = [];
|
|
164
|
+
const scores: number[] = [];
|
|
165
|
+
// Each unordered pair is scored once as (u, v), u < v; without `directed` the reverse pair is
|
|
166
|
+
// listed too with the same score. Existence is tested on the arc u -> v only.
|
|
167
|
+
const mirror = o.directed !== true;
|
|
168
|
+
for (let u = 0; u < s.nodeCount; u++) {
|
|
169
|
+
for (let v = u + 1; v < s.nodeCount; v++) {
|
|
170
|
+
if (o.includeExisting !== true && s.hasArc(u, v)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const x = score(u, v);
|
|
174
|
+
if (x > 0) {
|
|
175
|
+
sources.push(u);
|
|
176
|
+
targets.push(v);
|
|
177
|
+
scores.push(x);
|
|
178
|
+
if (mirror) {
|
|
179
|
+
sources.push(v);
|
|
180
|
+
targets.push(u);
|
|
181
|
+
scores.push(x);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const { topK } = o;
|
|
187
|
+
return ranked(sources, targets, scores, topK !== undefined && topK > 0 ? topK : scores.length);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function forPairs(pairs: NodePairs, score: PairScore): F64 {
|
|
191
|
+
return Float64Array.from(pairs.sources, (u, k) => score(u, pairs.targets[k]));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function candidatesOf(s: GraphSnapshot, u: number, o: CandidateOptions, score: PairScore): LinkPredictionResult {
|
|
195
|
+
const n = s.nodeCount;
|
|
196
|
+
const sources: number[] = [];
|
|
197
|
+
const targets: number[] = [];
|
|
198
|
+
const scores: number[] = [];
|
|
199
|
+
if (u < n) {
|
|
200
|
+
const list = o.candidates ?? Array.from({ length: n }, (_, i) => i);
|
|
201
|
+
for (let k = 0; k < list.length; k++) {
|
|
202
|
+
const v = list[k];
|
|
203
|
+
if (v === u || (o.includeExisting !== true && v < n && s.hasArc(u, v))) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const x = score(u, v);
|
|
207
|
+
if (x > 0) {
|
|
208
|
+
sources.push(u);
|
|
209
|
+
targets.push(v);
|
|
210
|
+
scores.push(x);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return ranked(sources, targets, scores, o.topK ?? 10);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Rank held-out edges against non-edges by score (stable, edges first among equal scores), then
|
|
219
|
+
* report the threshold with the best F1 and the AUC; the AUC is 0.5 when either side is empty.
|
|
220
|
+
* @param edges - The scores of the held-out edges
|
|
221
|
+
* @param nonEdges - The scores of the non-edges
|
|
222
|
+
* @returns The metrics
|
|
223
|
+
*/
|
|
224
|
+
function rankingMetrics(edges: F64, nonEdges: F64): LinkPredictionMetrics {
|
|
225
|
+
const all = [...edges, ...nonEdges];
|
|
226
|
+
const order = Array.from(all, (_, k) => k).sort((a, b) => all[b] - all[a]);
|
|
227
|
+
let tp = 0;
|
|
228
|
+
let fp = 0;
|
|
229
|
+
let bestF1 = 0;
|
|
230
|
+
let bestPrecision = 0;
|
|
231
|
+
let bestRecall = 0;
|
|
232
|
+
let auc = 0;
|
|
233
|
+
for (const k of order) {
|
|
234
|
+
if (k < edges.length) {
|
|
235
|
+
tp++;
|
|
236
|
+
} else {
|
|
237
|
+
auc += tp;
|
|
238
|
+
fp++;
|
|
239
|
+
}
|
|
240
|
+
const precision = tp / (tp + fp);
|
|
241
|
+
const recall = tp / edges.length;
|
|
242
|
+
const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
|
|
243
|
+
if (f1 > bestF1) {
|
|
244
|
+
bestF1 = f1;
|
|
245
|
+
bestPrecision = precision;
|
|
246
|
+
bestRecall = recall;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
auc = tp > 0 && fp > 0 ? auc / (tp * fp) : 0.5;
|
|
250
|
+
return { precision: bestPrecision, recall: bestRecall, f1Score: bestF1, auc };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Common-neighbour scores of every pair not already joined, ranked.
|
|
255
|
+
* @param s - The snapshot
|
|
256
|
+
* @param o - Options
|
|
257
|
+
* @returns The pairs with a positive score, highest first
|
|
258
|
+
* @public
|
|
259
|
+
*/
|
|
260
|
+
export function commonNeighborsPrediction(s: GraphSnapshot, o: LinkPredictionOptions = {}): LinkPredictionResult {
|
|
261
|
+
return predict(s, o, scorer(s, o));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Common-neighbour scores of the given pairs, in order.
|
|
266
|
+
* @param s - The snapshot
|
|
267
|
+
* @param pairs - The pairs
|
|
268
|
+
* @param o - Options
|
|
269
|
+
* @returns One score per pair
|
|
270
|
+
* @public
|
|
271
|
+
*/
|
|
272
|
+
export function commonNeighborsForPairs(s: GraphSnapshot, pairs: NodePairs, o: CommonNeighborsOptions = {}): F64 {
|
|
273
|
+
return forPairs(pairs, scorer(s, o));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* One node's best common-neighbour candidates, ranked.
|
|
278
|
+
* @param s - The snapshot
|
|
279
|
+
* @param u - The node index
|
|
280
|
+
* @param o - Options
|
|
281
|
+
* @returns (u, v) pairs with a positive score, highest first
|
|
282
|
+
* @public
|
|
283
|
+
*/
|
|
284
|
+
export function getTopCandidatesForNode(s: GraphSnapshot, u: number, o: CandidateOptions = {}): LinkPredictionResult {
|
|
285
|
+
return candidatesOf(s, u, o, scorer(s, o));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Ranking metrics of the common-neighbour score on held-out edges against non-edges.
|
|
290
|
+
* @param s - The training snapshot
|
|
291
|
+
* @param edges - The held-out edges
|
|
292
|
+
* @param nonEdges - The non-edges
|
|
293
|
+
* @param o - Options
|
|
294
|
+
* @returns The metrics
|
|
295
|
+
* @public
|
|
296
|
+
*/
|
|
297
|
+
export function evaluateCommonNeighbors(
|
|
298
|
+
s: GraphSnapshot,
|
|
299
|
+
edges: NodePairs,
|
|
300
|
+
nonEdges: NodePairs,
|
|
301
|
+
o: CommonNeighborsOptions = {},
|
|
302
|
+
): LinkPredictionMetrics {
|
|
303
|
+
const score = scorer(s, o);
|
|
304
|
+
return rankingMetrics(forPairs(edges, score), forPairs(nonEdges, score));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* The Adamic-Adar index of two nodes: the sum over distinct common neighbours z of 1 / ln(degree(z)).
|
|
309
|
+
* @param s - The snapshot
|
|
310
|
+
* @param u - A node index
|
|
311
|
+
* @param v - A node index
|
|
312
|
+
* @param o - Options
|
|
313
|
+
* @returns The score
|
|
314
|
+
* @public
|
|
315
|
+
*/
|
|
316
|
+
export function adamicAdarScore(s: GraphSnapshot, u: number, v: number, o: CommonNeighborsOptions = {}): number {
|
|
317
|
+
return scorer(s, o, adamicAdarWeightOf(s, o.directed === true))(u, v);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Adamic-Adar scores of every pair not already joined, ranked.
|
|
322
|
+
* @param s - The snapshot
|
|
323
|
+
* @param o - Options
|
|
324
|
+
* @returns The pairs with a positive score, highest first
|
|
325
|
+
* @public
|
|
326
|
+
*/
|
|
327
|
+
export function adamicAdarPrediction(s: GraphSnapshot, o: LinkPredictionOptions = {}): LinkPredictionResult {
|
|
328
|
+
return predict(s, o, adamicAdarScorer(s, o));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Adamic-Adar scores of the given pairs, in order.
|
|
333
|
+
* @param s - The snapshot
|
|
334
|
+
* @param pairs - The pairs
|
|
335
|
+
* @param o - Options
|
|
336
|
+
* @returns One score per pair
|
|
337
|
+
* @public
|
|
338
|
+
*/
|
|
339
|
+
export function adamicAdarForPairs(s: GraphSnapshot, pairs: NodePairs, o: CommonNeighborsOptions = {}): F64 {
|
|
340
|
+
return forPairs(pairs, adamicAdarScorer(s, o));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* One node's best Adamic-Adar candidates, ranked.
|
|
345
|
+
* @param s - The snapshot
|
|
346
|
+
* @param u - The node index
|
|
347
|
+
* @param o - Options
|
|
348
|
+
* @returns (u, v) pairs with a positive score, highest first
|
|
349
|
+
* @public
|
|
350
|
+
*/
|
|
351
|
+
export function getTopAdamicAdarCandidatesForNode(
|
|
352
|
+
s: GraphSnapshot,
|
|
353
|
+
u: number,
|
|
354
|
+
o: CandidateOptions = {},
|
|
355
|
+
): LinkPredictionResult {
|
|
356
|
+
return candidatesOf(s, u, o, adamicAdarScorer(s, o));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Ranking metrics of the Adamic-Adar score on held-out edges against non-edges.
|
|
361
|
+
* @param s - The training snapshot
|
|
362
|
+
* @param edges - The held-out edges
|
|
363
|
+
* @param nonEdges - The non-edges
|
|
364
|
+
* @param o - Options
|
|
365
|
+
* @returns The metrics
|
|
366
|
+
* @public
|
|
367
|
+
*/
|
|
368
|
+
export function evaluateAdamicAdar(
|
|
369
|
+
s: GraphSnapshot,
|
|
370
|
+
edges: NodePairs,
|
|
371
|
+
nonEdges: NodePairs,
|
|
372
|
+
o: CommonNeighborsOptions = {},
|
|
373
|
+
): LinkPredictionMetrics {
|
|
374
|
+
const score = adamicAdarScorer(s, o);
|
|
375
|
+
return rankingMetrics(forPairs(edges, score), forPairs(nonEdges, score));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Both evaluations on the same held-out sets. The common-neighbour side always intersects the two
|
|
380
|
+
* OUT rows, whatever `directed` says, as the legacy function of this name does; the Adamic-Adar side
|
|
381
|
+
* honours `directed`.
|
|
382
|
+
* @param s - The training snapshot
|
|
383
|
+
* @param edges - The held-out edges
|
|
384
|
+
* @param nonEdges - The non-edges
|
|
385
|
+
* @param o - Options
|
|
386
|
+
* @returns The two sets of metrics
|
|
387
|
+
* @public
|
|
388
|
+
*/
|
|
389
|
+
export function compareAdamicAdarWithCommonNeighbors(
|
|
390
|
+
s: GraphSnapshot,
|
|
391
|
+
edges: NodePairs,
|
|
392
|
+
nonEdges: NodePairs,
|
|
393
|
+
o: CommonNeighborsOptions = {},
|
|
394
|
+
): { adamicAdar: LinkPredictionMetrics; commonNeighbors: LinkPredictionMetrics } {
|
|
395
|
+
return {
|
|
396
|
+
adamicAdar: evaluateAdamicAdar(s, edges, nonEdges, o),
|
|
397
|
+
commonNeighbors: evaluateCommonNeighbors(s, edges, nonEdges),
|
|
398
|
+
};
|
|
399
|
+
}
|