@graphty/algorithms 1.2.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +375 -276
- package/dist/algorithms.d.ts +42 -2
- package/dist/algorithms.js +11402 -2
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +14 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +144 -169
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +35 -1
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +70 -149
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.d.ts +7 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +12 -9
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +57 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +304 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +106 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js +320 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
- package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.js +11 -4
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
- package/dist/src/algorithms/centrality/hits.d.ts +10 -3
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/hits.js +11 -4
- package/dist/src/algorithms/centrality/hits.js.map +1 -1
- package/dist/src/algorithms/centrality/index.d.ts +3 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +2 -1
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +10 -3
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/katz.js +13 -8
- package/dist/src/algorithms/centrality/katz.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +31 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +61 -1
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +67 -57
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/index.d.ts +2 -0
- package/dist/src/algorithms/community/index.d.ts.map +1 -1
- package/dist/src/algorithms/community/index.js.map +1 -1
- package/dist/src/algorithms/community/label-propagation.d.ts +11 -4
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +60 -38
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +3 -3
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +70 -44
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +149 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain-optimized.js +338 -0
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
- package/dist/src/algorithms/community/louvain.d.ts +2 -3
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain.js +35 -81
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +55 -0
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
- package/dist/src/algorithms/community/modularity-utils.js +108 -0
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
- package/dist/src/algorithms/components/connected.d.ts +23 -0
- package/dist/src/algorithms/components/connected.d.ts.map +1 -1
- package/dist/src/algorithms/components/connected.js +29 -3
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
- package/dist/src/algorithms/matching/bipartite.js +18 -40
- package/dist/src/algorithms/matching/bipartite.js.map +1 -1
- package/dist/src/algorithms/matching/index.d.ts.map +1 -1
- package/dist/src/algorithms/matching/index.js.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
- package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.js +36 -2
- package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
- package/dist/src/algorithms/mst/index.d.ts.map +1 -1
- package/dist/src/algorithms/mst/index.js.map +1 -1
- package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
- package/dist/src/algorithms/mst/kruskal.js +17 -3
- package/dist/src/algorithms/mst/kruskal.js.map +1 -1
- package/dist/src/algorithms/mst/prim.d.ts +9 -0
- package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
- package/dist/src/algorithms/mst/prim.js +9 -0
- package/dist/src/algorithms/mst/prim.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +11 -12
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +44 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +195 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +20 -2
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +30 -14
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
- package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/index.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +42 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-unified.js +370 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +77 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.js +402 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +5 -182
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +48 -0
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/index.js.map +1 -1
- package/dist/src/benchmark-all-algorithms.d.ts +25 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
- package/dist/src/benchmark-all-algorithms.js +491 -0
- package/dist/src/benchmark-all-algorithms.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +23 -15
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +226 -224
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts +4 -2
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js +2 -2
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +17 -19
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +68 -30
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/mcl.d.ts +6 -0
- package/dist/src/clustering/mcl.d.ts.map +1 -1
- package/dist/src/clustering/mcl.js +34 -3
- package/dist/src/clustering/mcl.js.map +1 -1
- package/dist/src/clustering/spectral.d.ts +11 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +99 -47
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/core/graph.d.ts +45 -0
- package/dist/src/core/graph.d.ts.map +1 -1
- package/dist/src/core/graph.js +49 -5
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/data-structures/index.d.ts.map +1 -1
- package/dist/src/data-structures/index.js.map +1 -1
- package/dist/src/data-structures/priority-queue.d.ts +18 -1
- package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
- package/dist/src/data-structures/priority-queue.js +26 -5
- package/dist/src/data-structures/priority-queue.js.map +1 -1
- package/dist/src/data-structures/union-find.d.ts +21 -0
- package/dist/src/data-structures/union-find.d.ts.map +1 -1
- package/dist/src/data-structures/union-find.js +21 -0
- package/dist/src/data-structures/union-find.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +20 -12
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +170 -217
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +6 -8
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +34 -12
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
- package/dist/src/link-prediction/adamic-adar.js +47 -30
- package/dist/src/link-prediction/adamic-adar.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +31 -14
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/link-prediction/index.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.d.ts +193 -0
- package/dist/src/optimized/bit-packed.d.ts.map +1 -0
- package/dist/src/optimized/bit-packed.js +342 -0
- package/dist/src/optimized/bit-packed.js.map +1 -0
- package/dist/src/optimized/csr-graph.d.ts +149 -0
- package/dist/src/optimized/csr-graph.d.ts.map +1 -0
- package/dist/src/optimized/csr-graph.js +404 -0
- package/dist/src/optimized/csr-graph.js.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts +95 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.js +242 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
- package/dist/src/optimized/graph-adapter.d.ts +111 -0
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
- package/dist/src/optimized/graph-adapter.js +194 -0
- package/dist/src/optimized/graph-adapter.js.map +1 -0
- package/dist/src/optimized/index.d.ts +14 -0
- package/dist/src/optimized/index.d.ts.map +1 -0
- package/dist/src/optimized/index.js +30 -0
- package/dist/src/optimized/index.js.map +1 -0
- package/dist/src/pathfinding/astar.d.ts +25 -9
- package/dist/src/pathfinding/astar.d.ts.map +1 -1
- package/dist/src/pathfinding/astar.js +25 -10
- package/dist/src/pathfinding/astar.js.map +1 -1
- package/dist/src/pathfinding/utils.d.ts +15 -6
- package/dist/src/pathfinding/utils.d.ts.map +1 -1
- package/dist/src/pathfinding/utils.js +19 -7
- package/dist/src/pathfinding/utils.js.map +1 -1
- package/dist/src/research/grsbm.d.ts +0 -1
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +28 -23
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/index.d.ts.map +1 -1
- package/dist/src/research/index.js.map +1 -1
- package/dist/src/research/sync.d.ts +0 -1
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +31 -36
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +2 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +39 -7
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/types/index.d.ts +8 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/algorithm-utilities.d.ts +2 -0
- package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
- package/dist/src/utils/algorithm-utilities.js +2 -0
- package/dist/src/utils/algorithm-utilities.js.map +1 -0
- package/dist/src/utils/graph-converters.d.ts +110 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -0
- package/dist/src/utils/graph-converters.js +227 -0
- package/dist/src/utils/graph-converters.js.map +1 -0
- package/dist/src/utils/graph-utilities.d.ts +62 -0
- package/dist/src/utils/graph-utilities.d.ts.map +1 -0
- package/dist/src/utils/graph-utilities.js +148 -0
- package/dist/src/utils/graph-utilities.js.map +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +5 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/math-utilities.d.ts +63 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -0
- package/dist/src/utils/math-utilities.js +132 -0
- package/dist/src/utils/math-utilities.js.map +1 -0
- package/dist/src/utils/matrix-utilities.d.ts +2 -0
- package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
- package/dist/src/utils/matrix-utilities.js +2 -0
- package/dist/src/utils/matrix-utilities.js.map +1 -0
- package/dist/src/utils/optimization-helpers.d.ts +42 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
- package/dist/src/utils/optimization-helpers.js +59 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -0
- package/dist/src/utils/priorityQueue.d.ts +24 -0
- package/dist/src/utils/priorityQueue.d.ts.map +1 -1
- package/dist/src/utils/priorityQueue.js +34 -4
- package/dist/src/utils/priorityQueue.js.map +1 -1
- package/package.json +58 -39
- package/src/algorithms/centrality/betweenness.ts +185 -200
- package/src/algorithms/centrality/closeness.ts +93 -194
- package/src/algorithms/centrality/degree.ts +18 -22
- package/src/algorithms/centrality/delta-pagerank-simple.ts +374 -0
- package/src/algorithms/centrality/delta-pagerank.ts +458 -0
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -13
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +91 -16
- package/src/algorithms/community/girvan-newman.ts +90 -86
- package/src/algorithms/community/index.ts +6 -5
- package/src/algorithms/community/label-propagation.ts +69 -48
- package/src/algorithms/community/leiden.ts +88 -65
- package/src/algorithms/community/louvain-optimized.ts +474 -0
- package/src/algorithms/community/louvain.ts +52 -122
- package/src/algorithms/community/modularity-utils.ts +134 -0
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +24 -53
- package/src/algorithms/matching/index.ts +4 -4
- package/src/algorithms/matching/isomorphism.ts +53 -23
- package/src/algorithms/mst/index.ts +3 -4
- package/src/algorithms/mst/kruskal.ts +21 -6
- package/src/algorithms/mst/prim.ts +13 -4
- package/src/algorithms/shortest-path/bellman-ford.ts +16 -28
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
- package/src/algorithms/shortest-path/dijkstra.ts +38 -30
- package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
- package/src/algorithms/shortest-path/index.ts +5 -5
- package/src/algorithms/traversal/bfs-unified.ts +437 -0
- package/src/algorithms/traversal/bfs-variants.ts +520 -0
- package/src/algorithms/traversal/bfs.ts +5 -229
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +580 -0
- package/src/clustering/hierarchical.ts +268 -272
- package/src/clustering/index.ts +8 -6
- package/src/clustering/k-core.ts +93 -61
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +130 -72
- package/src/core/graph.ts +58 -18
- package/src/data-structures/index.ts +2 -2
- package/src/data-structures/priority-queue.ts +36 -11
- package/src/data-structures/union-find.ts +22 -1
- package/src/flow/ford-fulkerson.ts +221 -262
- package/src/flow/min-cut.ts +59 -51
- package/src/index.ts +9 -2
- package/src/link-prediction/adamic-adar.ts +75 -74
- package/src/link-prediction/common-neighbors.ts +49 -49
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +390 -0
- package/src/optimized/csr-graph.ts +518 -0
- package/src/optimized/direction-optimized-bfs.ts +310 -0
- package/src/optimized/graph-adapter.ts +248 -0
- package/src/optimized/index.ts +34 -0
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +36 -39
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +41 -54
- package/src/research/terahac.ts +63 -21
- package/src/types/index.ts +8 -0
- package/src/utils/algorithm-utilities.ts +2 -0
- package/src/utils/graph-converters.ts +269 -0
- package/src/utils/graph-utilities.ts +173 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/math-utilities.ts +149 -0
- package/src/utils/matrix-utilities.ts +2 -0
- package/src/utils/optimization-helpers.ts +87 -0
- package/src/utils/priorityQueue.ts +40 -6
package/src/flow/min-cut.ts
CHANGED
|
@@ -4,19 +4,20 @@
|
|
|
4
4
|
* Various algorithms for finding minimum cuts in graphs
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import type { Graph } from "../core/graph.js";
|
|
8
|
+
import { graphToMap } from "../utils/graph-converters.js";
|
|
9
|
+
import { fordFulkerson } from "./ford-fulkerson.js";
|
|
8
10
|
|
|
9
11
|
export interface MinCutResult {
|
|
10
12
|
cutValue: number;
|
|
11
13
|
partition1: Set<string>;
|
|
12
14
|
partition2: Set<string>;
|
|
13
|
-
cutEdges: {from: string
|
|
15
|
+
cutEdges: { from: string; to: string; weight: number }[];
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Find minimum s-t cut using max flow
|
|
18
20
|
* The minimum cut value equals the maximum flow value (max-flow min-cut theorem)
|
|
19
|
-
*
|
|
20
21
|
* @param graph - Weighted graph
|
|
21
22
|
* @param source - Source node
|
|
22
23
|
* @param sink - Sink node
|
|
@@ -24,11 +25,7 @@ export interface MinCutResult {
|
|
|
24
25
|
*
|
|
25
26
|
* Time Complexity: Same as max flow algorithm used
|
|
26
27
|
*/
|
|
27
|
-
export function minSTCut(
|
|
28
|
-
graph: Map<string, Map<string, number>>,
|
|
29
|
-
source: string,
|
|
30
|
-
sink: string,
|
|
31
|
-
): MinCutResult {
|
|
28
|
+
export function minSTCut(graph: Graph, source: string, sink: string): MinCutResult {
|
|
32
29
|
const flowResult = fordFulkerson(graph, source, sink);
|
|
33
30
|
|
|
34
31
|
if (!flowResult.minCut) {
|
|
@@ -40,13 +37,14 @@ export function minSTCut(
|
|
|
40
37
|
};
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
const cutEdges: {from: string
|
|
40
|
+
const cutEdges: { from: string; to: string; weight: number }[] = [];
|
|
44
41
|
|
|
45
42
|
// Find actual cut edges and their weights
|
|
46
43
|
for (const [u, v] of flowResult.minCut.edges) {
|
|
47
|
-
const
|
|
44
|
+
const edge = graph.getEdge(u, v);
|
|
45
|
+
const weight = edge?.weight ?? 0;
|
|
48
46
|
if (weight > 0) {
|
|
49
|
-
cutEdges.push({from: u, to: v, weight});
|
|
47
|
+
cutEdges.push({ from: u, to: v, weight });
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
|
|
@@ -61,17 +59,18 @@ export function minSTCut(
|
|
|
61
59
|
/**
|
|
62
60
|
* Stoer-Wagner algorithm for finding global minimum cut
|
|
63
61
|
* Finds the minimum cut that separates the graph into two parts
|
|
64
|
-
*
|
|
65
|
-
* @param graph - Undirected weighted graph
|
|
62
|
+
* @param graph - Undirected weighted graph - accepts Graph class or Map representation
|
|
66
63
|
* @returns Global minimum cut
|
|
67
64
|
*
|
|
68
65
|
* Time Complexity: O(V³) or O(VE + V² log V) with heap
|
|
69
66
|
*/
|
|
70
|
-
export function stoerWagner(
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
export function stoerWagner(graph: Graph | Map<string, Map<string, number>>): MinCutResult {
|
|
68
|
+
// Convert Graph to Map representation if needed
|
|
69
|
+
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
73
70
|
// Convert to undirected if necessary
|
|
74
|
-
const undirectedGraph = makeUndirected(
|
|
71
|
+
const undirectedGraph = makeUndirected(graphMap);
|
|
72
|
+
// Keep a copy for finding cut edges later
|
|
73
|
+
const originalGraph = makeUndirected(graphMap);
|
|
75
74
|
|
|
76
75
|
if (undirectedGraph.size < 2) {
|
|
77
76
|
return {
|
|
@@ -84,6 +83,7 @@ export function stoerWagner(
|
|
|
84
83
|
|
|
85
84
|
// Initialize
|
|
86
85
|
const nodes = Array.from(undirectedGraph.keys());
|
|
86
|
+
const originalNodes = new Set(nodes); // Keep track of original nodes
|
|
87
87
|
let minCutValue = Infinity;
|
|
88
88
|
let bestPartition = new Set<string>();
|
|
89
89
|
const contractionMap = new Map<string, Set<string>>();
|
|
@@ -125,20 +125,20 @@ export function stoerWagner(
|
|
|
125
125
|
const partition1 = bestPartition;
|
|
126
126
|
const partition2 = new Set<string>();
|
|
127
127
|
|
|
128
|
-
for (const node of
|
|
128
|
+
for (const node of originalNodes) {
|
|
129
129
|
if (!partition1.has(node)) {
|
|
130
130
|
partition2.add(node);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// Find cut edges
|
|
135
|
-
const cutEdges: {from: string
|
|
135
|
+
const cutEdges: { from: string; to: string; weight: number }[] = [];
|
|
136
136
|
for (const u of partition1) {
|
|
137
|
-
const neighbors =
|
|
137
|
+
const neighbors = originalGraph.get(u);
|
|
138
138
|
if (neighbors) {
|
|
139
139
|
for (const [v, weight] of neighbors) {
|
|
140
140
|
if (partition2.has(v)) {
|
|
141
|
-
cutEdges.push({from: u, to: v, weight});
|
|
141
|
+
cutEdges.push({ from: u, to: v, weight });
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
}
|
|
@@ -154,11 +154,14 @@ export function stoerWagner(
|
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
156
|
* Minimum cut phase of Stoer-Wagner algorithm
|
|
157
|
+
* @param graph - The undirected weighted graph
|
|
158
|
+
* @param nodes - Array of remaining nodes in the graph
|
|
159
|
+
* @returns The cut result with last two nodes, cut value, and partition
|
|
157
160
|
*/
|
|
158
161
|
function minimumCutPhase(
|
|
159
162
|
graph: Map<string, Map<string, number>>,
|
|
160
163
|
nodes: string[],
|
|
161
|
-
): {s: string
|
|
164
|
+
): { s: string; t: string; value: number; partition: string[] } {
|
|
162
165
|
const n = nodes.length;
|
|
163
166
|
const weight = new Map<string, number>();
|
|
164
167
|
const added = new Set<string>();
|
|
@@ -172,7 +175,7 @@ function minimumCutPhase(
|
|
|
172
175
|
// Start with arbitrary node
|
|
173
176
|
let lastAdded = nodes[0];
|
|
174
177
|
if (!lastAdded) {
|
|
175
|
-
return {s: "", t: "", value: 0, partition: []};
|
|
178
|
+
return { s: "", t: "", value: 0, partition: [] };
|
|
176
179
|
}
|
|
177
180
|
|
|
178
181
|
added.add(lastAdded);
|
|
@@ -180,7 +183,7 @@ function minimumCutPhase(
|
|
|
180
183
|
|
|
181
184
|
// Add remaining nodes
|
|
182
185
|
for (let i = 1; i < n; i++) {
|
|
183
|
-
|
|
186
|
+
// Update weights
|
|
184
187
|
if (!lastAdded) {
|
|
185
188
|
continue;
|
|
186
189
|
}
|
|
@@ -217,7 +220,7 @@ function minimumCutPhase(
|
|
|
217
220
|
const s = order[order.length - 2];
|
|
218
221
|
const t = order[order.length - 1];
|
|
219
222
|
if (!s || !t) {
|
|
220
|
-
return {s: "", t: "", value: 0, partition: []};
|
|
223
|
+
return { s: "", t: "", value: 0, partition: [] };
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
const cutValue = weight.get(t) ?? 0;
|
|
@@ -225,18 +228,17 @@ function minimumCutPhase(
|
|
|
225
228
|
// Partition is all nodes except t
|
|
226
229
|
const partition = order.slice(0, -1);
|
|
227
230
|
|
|
228
|
-
return {s, t, value: cutValue, partition};
|
|
231
|
+
return { s, t, value: cutValue, partition };
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
/**
|
|
232
235
|
* Contract two nodes in the graph
|
|
236
|
+
* @param graph - The graph to modify by contracting nodes
|
|
237
|
+
* @param nodes - Array of nodes to update after contraction
|
|
238
|
+
* @param s - The node to keep after contraction
|
|
239
|
+
* @param t - The node to merge into s
|
|
233
240
|
*/
|
|
234
|
-
function contractNodes(
|
|
235
|
-
graph: Map<string, Map<string, number>>,
|
|
236
|
-
nodes: string[],
|
|
237
|
-
s: string,
|
|
238
|
-
t: string,
|
|
239
|
-
): void {
|
|
241
|
+
function contractNodes(graph: Map<string, Map<string, number>>, nodes: string[], s: string, t: string): void {
|
|
240
242
|
// Merge t into s
|
|
241
243
|
const sNeighbors = graph.get(s);
|
|
242
244
|
const tNeighbors = graph.get(t);
|
|
@@ -271,10 +273,10 @@ function contractNodes(
|
|
|
271
273
|
|
|
272
274
|
/**
|
|
273
275
|
* Convert directed graph to undirected
|
|
276
|
+
* @param graph - The directed graph to convert
|
|
277
|
+
* @returns An undirected graph with edges in both directions
|
|
274
278
|
*/
|
|
275
|
-
function makeUndirected(
|
|
276
|
-
graph: Map<string, Map<string, number>>,
|
|
277
|
-
): Map<string, Map<string, number>> {
|
|
279
|
+
function makeUndirected(graph: Map<string, Map<string, number>>): Map<string, Map<string, number>> {
|
|
278
280
|
const undirected = new Map<string, Map<string, number>>();
|
|
279
281
|
|
|
280
282
|
// Initialize all nodes
|
|
@@ -307,23 +309,21 @@ function makeUndirected(
|
|
|
307
309
|
/**
|
|
308
310
|
* Karger's randomized min-cut algorithm
|
|
309
311
|
* Probabilistic algorithm that finds min cut with high probability
|
|
310
|
-
*
|
|
311
|
-
* @param graph - Undirected graph
|
|
312
|
+
* @param graph - Undirected graph - accepts Graph class or Map representation
|
|
312
313
|
* @param iterations - Number of iterations (higher = better accuracy)
|
|
313
314
|
* @returns Minimum cut found
|
|
314
315
|
*
|
|
315
316
|
* Time Complexity: O(V² * iterations)
|
|
316
317
|
*/
|
|
317
|
-
export function kargerMinCut(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
): MinCutResult {
|
|
318
|
+
export function kargerMinCut(graph: Graph | Map<string, Map<string, number>>, iterations = 100): MinCutResult {
|
|
319
|
+
// Convert Graph to Map representation if needed
|
|
320
|
+
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
321
321
|
let minCutValue = Infinity;
|
|
322
322
|
let bestPartition1 = new Set<string>();
|
|
323
323
|
let bestPartition2 = new Set<string>();
|
|
324
324
|
|
|
325
325
|
for (let i = 0; i < iterations; i++) {
|
|
326
|
-
const result = kargerSingleRun(
|
|
326
|
+
const result = kargerSingleRun(graphMap);
|
|
327
327
|
|
|
328
328
|
if (result.cutValue < minCutValue) {
|
|
329
329
|
minCutValue = result.cutValue;
|
|
@@ -333,13 +333,13 @@ export function kargerMinCut(
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
// Find cut edges
|
|
336
|
-
const cutEdges: {from: string
|
|
336
|
+
const cutEdges: { from: string; to: string; weight: number }[] = [];
|
|
337
337
|
for (const u of bestPartition1) {
|
|
338
|
-
const neighbors =
|
|
338
|
+
const neighbors = graphMap.get(u);
|
|
339
339
|
if (neighbors) {
|
|
340
340
|
for (const [v, weight] of neighbors) {
|
|
341
341
|
if (bestPartition2.has(v)) {
|
|
342
|
-
cutEdges.push({from: u, to: v, weight});
|
|
342
|
+
cutEdges.push({ from: u, to: v, weight });
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
345
|
}
|
|
@@ -355,10 +355,14 @@ export function kargerMinCut(
|
|
|
355
355
|
|
|
356
356
|
/**
|
|
357
357
|
* Single run of Karger's algorithm
|
|
358
|
+
* @param graph - The undirected weighted graph
|
|
359
|
+
* @returns The cut result with cut value and two partitions
|
|
358
360
|
*/
|
|
359
|
-
function kargerSingleRun(
|
|
360
|
-
|
|
361
|
-
|
|
361
|
+
function kargerSingleRun(graph: Map<string, Map<string, number>>): {
|
|
362
|
+
cutValue: number;
|
|
363
|
+
partition1: Set<string>;
|
|
364
|
+
partition2: Set<string>;
|
|
365
|
+
} {
|
|
362
366
|
// Create a copy of the graph
|
|
363
367
|
const workGraph = new Map<string, Map<string, number>>();
|
|
364
368
|
const superNodes = new Map<string, Set<string>>();
|
|
@@ -371,11 +375,12 @@ function kargerSingleRun(
|
|
|
371
375
|
|
|
372
376
|
// Contract until 2 nodes remain
|
|
373
377
|
while (workGraph.size > 2) {
|
|
374
|
-
|
|
378
|
+
// Pick random edge
|
|
375
379
|
const edges: [string, string, number][] = [];
|
|
376
380
|
for (const [u, neighbors] of workGraph) {
|
|
377
381
|
for (const [v, weight] of neighbors) {
|
|
378
|
-
if (u < v) {
|
|
382
|
+
if (u < v) {
|
|
383
|
+
// Avoid duplicates
|
|
379
384
|
edges.push([u, v, weight]);
|
|
380
385
|
}
|
|
381
386
|
}
|
|
@@ -430,6 +435,10 @@ function kargerSingleRun(
|
|
|
430
435
|
|
|
431
436
|
/**
|
|
432
437
|
* Contract edge in Karger's algorithm
|
|
438
|
+
* @param graph - The working graph to modify
|
|
439
|
+
* @param superNodes - Map tracking which original nodes are in each super node
|
|
440
|
+
* @param u - First endpoint of the edge to contract
|
|
441
|
+
* @param v - Second endpoint of the edge to contract (merged into u)
|
|
433
442
|
*/
|
|
434
443
|
function contractKarger(
|
|
435
444
|
graph: Map<string, Map<string, number>>,
|
|
@@ -479,4 +488,3 @@ function contractKarger(
|
|
|
479
488
|
graph.delete(v);
|
|
480
489
|
superNodes.delete(v);
|
|
481
490
|
}
|
|
482
|
-
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* graphty/algorithms - Graph algorithms library for browser environments
|
|
3
3
|
*
|
|
4
4
|
* A comprehensive TypeScript library implementing fundamental graph algorithms
|
|
5
5
|
* optimized for browser environments and visualization applications.
|
|
6
|
+
* @module
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
// Core exports
|
|
9
|
-
export {Graph} from "./core/graph.js";
|
|
10
|
+
export { Graph } from "./core/graph.js";
|
|
10
11
|
|
|
11
12
|
// Type exports
|
|
12
13
|
export type {
|
|
@@ -38,3 +39,9 @@ export * from "./research/index.js";
|
|
|
38
39
|
|
|
39
40
|
// Data structure exports
|
|
40
41
|
export * from "./data-structures/index.js";
|
|
42
|
+
|
|
43
|
+
// Optimized algorithm exports
|
|
44
|
+
export * from "./optimized/index.js";
|
|
45
|
+
|
|
46
|
+
// Note: Configuration exports have been removed.
|
|
47
|
+
// The library now automatically optimizes based on graph size.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {Graph} from "../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../types/index.js";
|
|
3
|
-
import
|
|
1
|
+
import type { Graph } from "../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../types/index.js";
|
|
3
|
+
import { getCommonNeighbors, getIntermediateNodes } from "../utils/graph-utilities.js";
|
|
4
|
+
import type { LinkPredictionOptions, LinkPredictionScore } from "./common-neighbors.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Adamic-Adar Index link prediction implementation
|
|
@@ -18,6 +19,11 @@ import type {LinkPredictionOptions, LinkPredictionScore} from "./common-neighbor
|
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Calculate Adamic-Adar index for a pair of nodes
|
|
22
|
+
* @param graph - The input graph
|
|
23
|
+
* @param source - The source node ID
|
|
24
|
+
* @param target - The target node ID
|
|
25
|
+
* @param options - Link prediction options
|
|
26
|
+
* @returns The Adamic-Adar score for the node pair
|
|
21
27
|
*/
|
|
22
28
|
export function adamicAdarScore(
|
|
23
29
|
graph: Graph,
|
|
@@ -29,30 +35,26 @@ export function adamicAdarScore(
|
|
|
29
35
|
return 0;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
const {directed = false} = options;
|
|
38
|
+
const { directed = false } = options;
|
|
33
39
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
directed ? Array.from(graph.inNeighbors(target)) : Array.from(graph.neighbors(target)),
|
|
40
|
-
);
|
|
40
|
+
// Use utility function to get common neighbors
|
|
41
|
+
// For directed graphs, we want intermediate nodes that form paths source->X->target
|
|
42
|
+
const commonNeighborsSet = directed
|
|
43
|
+
? getIntermediateNodes(graph, source, target)
|
|
44
|
+
: getCommonNeighbors(graph, source, target, false);
|
|
41
45
|
|
|
42
46
|
// Calculate Adamic-Adar score
|
|
43
47
|
let score = 0;
|
|
44
|
-
for (const neighbor of
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
score += 1; // or some other reasonable value
|
|
55
|
-
}
|
|
48
|
+
for (const neighbor of commonNeighborsSet) {
|
|
49
|
+
const degree = directed
|
|
50
|
+
? graph.outDegree(neighbor) // Use out-degree for directed graphs
|
|
51
|
+
: graph.degree(neighbor); // Use total degree for undirected graphs
|
|
52
|
+
|
|
53
|
+
if (degree > 1) {
|
|
54
|
+
score += 1 / Math.log(degree);
|
|
55
|
+
} else if (degree === 1) {
|
|
56
|
+
// For degree 1, we can't use log(1) = 0, so use a small constant
|
|
57
|
+
score += 1; // or some other reasonable value
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
|
|
@@ -61,16 +63,12 @@ export function adamicAdarScore(
|
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
65
|
* Calculate Adamic-Adar scores for all possible node pairs
|
|
66
|
+
* @param graph - The input graph
|
|
67
|
+
* @param options - Link prediction options
|
|
68
|
+
* @returns Array of link prediction scores sorted by score descending
|
|
64
69
|
*/
|
|
65
|
-
export function adamicAdarPrediction(
|
|
66
|
-
|
|
67
|
-
options: LinkPredictionOptions = {},
|
|
68
|
-
): LinkPredictionScore[] {
|
|
69
|
-
const {
|
|
70
|
-
directed = false,
|
|
71
|
-
includeExisting = false,
|
|
72
|
-
topK,
|
|
73
|
-
} = options;
|
|
70
|
+
export function adamicAdarPrediction(graph: Graph, options: LinkPredictionOptions = {}): LinkPredictionScore[] {
|
|
71
|
+
const { directed = false, includeExisting = false, topK } = options;
|
|
74
72
|
|
|
75
73
|
const scores: LinkPredictionScore[] = [];
|
|
76
74
|
const nodes = Array.from(graph.nodes()).map((n) => n.id);
|
|
@@ -89,14 +87,14 @@ export function adamicAdarPrediction(
|
|
|
89
87
|
continue;
|
|
90
88
|
}
|
|
91
89
|
|
|
92
|
-
const score = adamicAdarScore(graph, source, target, {directed});
|
|
90
|
+
const score = adamicAdarScore(graph, source, target, { directed });
|
|
93
91
|
|
|
94
92
|
if (score > 0) {
|
|
95
|
-
scores.push({source, target, score});
|
|
93
|
+
scores.push({ source, target, score });
|
|
96
94
|
|
|
97
95
|
// For undirected graphs, also add the reverse pair
|
|
98
96
|
if (!directed && source !== target) {
|
|
99
|
-
scores.push({source: target, target: source, score});
|
|
97
|
+
scores.push({ source: target, target: source, score });
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
100
|
}
|
|
@@ -115,6 +113,10 @@ export function adamicAdarPrediction(
|
|
|
115
113
|
|
|
116
114
|
/**
|
|
117
115
|
* Calculate Adamic-Adar scores for specific node pairs
|
|
116
|
+
* @param graph - The input graph
|
|
117
|
+
* @param pairs - Array of node ID pairs to calculate scores for
|
|
118
|
+
* @param options - Link prediction options
|
|
119
|
+
* @returns Array of link prediction scores for the specified pairs
|
|
118
120
|
*/
|
|
119
121
|
export function adamicAdarForPairs(
|
|
120
122
|
graph: Graph,
|
|
@@ -130,22 +132,21 @@ export function adamicAdarForPairs(
|
|
|
130
132
|
|
|
131
133
|
/**
|
|
132
134
|
* Get top Adamic-Adar candidates for link prediction for a specific node
|
|
135
|
+
* @param graph - The input graph
|
|
136
|
+
* @param node - The node ID to get candidates for
|
|
137
|
+
* @param options - Link prediction options with optional candidate list
|
|
138
|
+
* @returns Array of top link prediction candidates sorted by score descending
|
|
133
139
|
*/
|
|
134
140
|
export function getTopAdamicAdarCandidatesForNode(
|
|
135
141
|
graph: Graph,
|
|
136
142
|
node: NodeId,
|
|
137
|
-
options: LinkPredictionOptions & {candidates?: NodeId[]} = {},
|
|
143
|
+
options: LinkPredictionOptions & { candidates?: NodeId[] } = {},
|
|
138
144
|
): LinkPredictionScore[] {
|
|
139
145
|
if (!graph.hasNode(node)) {
|
|
140
146
|
return [];
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
const {
|
|
144
|
-
directed = false,
|
|
145
|
-
includeExisting = false,
|
|
146
|
-
topK = 10,
|
|
147
|
-
candidates,
|
|
148
|
-
} = options;
|
|
149
|
+
const { directed = false, includeExisting = false, topK = 10, candidates } = options;
|
|
149
150
|
|
|
150
151
|
const scores: LinkPredictionScore[] = [];
|
|
151
152
|
const targetNodes = candidates ?? Array.from(graph.nodes()).map((n) => n.id);
|
|
@@ -160,10 +161,10 @@ export function getTopAdamicAdarCandidatesForNode(
|
|
|
160
161
|
continue;
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
const score = adamicAdarScore(graph, node, target, {directed});
|
|
164
|
+
const score = adamicAdarScore(graph, node, target, { directed });
|
|
164
165
|
|
|
165
166
|
if (score > 0) {
|
|
166
|
-
scores.push({source: node, target, score});
|
|
167
|
+
scores.push({ source: node, target, score });
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
170
|
|
|
@@ -175,6 +176,11 @@ export function getTopAdamicAdarCandidatesForNode(
|
|
|
175
176
|
|
|
176
177
|
/**
|
|
177
178
|
* Calculate precision and recall for Adamic-Adar link prediction evaluation
|
|
179
|
+
* @param trainingGraph - The training graph without test edges
|
|
180
|
+
* @param testEdges - Array of node pairs that are actual edges
|
|
181
|
+
* @param nonEdges - Array of node pairs that are not edges
|
|
182
|
+
* @param options - Link prediction options
|
|
183
|
+
* @returns Evaluation metrics including precision, recall, F1 score, and AUC
|
|
178
184
|
*/
|
|
179
185
|
export function evaluateAdamicAdar(
|
|
180
186
|
trainingGraph: Graph,
|
|
@@ -182,19 +188,19 @@ export function evaluateAdamicAdar(
|
|
|
182
188
|
nonEdges: [NodeId, NodeId][],
|
|
183
189
|
options: LinkPredictionOptions = {},
|
|
184
190
|
): {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
precision: number;
|
|
192
|
+
recall: number;
|
|
193
|
+
f1Score: number;
|
|
194
|
+
auc: number;
|
|
195
|
+
} {
|
|
190
196
|
// Get scores for test edges and non-edges
|
|
191
197
|
const testScores = adamicAdarForPairs(trainingGraph, testEdges, options);
|
|
192
198
|
const nonEdgeScores = adamicAdarForPairs(trainingGraph, nonEdges, options);
|
|
193
199
|
|
|
194
200
|
// Combine and sort all scores
|
|
195
201
|
const allScores = [
|
|
196
|
-
...
|
|
197
|
-
...
|
|
202
|
+
...testScores.map((s) => ({ ...s, isActualEdge: true })),
|
|
203
|
+
...nonEdgeScores.map((s) => ({ ...s, isActualEdge: false })),
|
|
198
204
|
].sort((a, b) => b.score - a.score);
|
|
199
205
|
|
|
200
206
|
// Calculate precision and recall at different thresholds
|
|
@@ -215,7 +221,7 @@ export function evaluateAdamicAdar(
|
|
|
215
221
|
|
|
216
222
|
const precision = truePositives / (truePositives + falsePositives);
|
|
217
223
|
const recall = truePositives / totalPositives;
|
|
218
|
-
const f1 = precision + recall > 0 ? 2 * (precision * recall) / (precision + recall) : 0;
|
|
224
|
+
const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
|
|
219
225
|
|
|
220
226
|
if (f1 > bestF1) {
|
|
221
227
|
bestF1 = f1;
|
|
@@ -254,6 +260,11 @@ export function evaluateAdamicAdar(
|
|
|
254
260
|
|
|
255
261
|
/**
|
|
256
262
|
* Compare Adamic-Adar with Common Neighbors for the same dataset
|
|
263
|
+
* @param graph - The input graph
|
|
264
|
+
* @param testEdges - Array of node pairs that are actual edges
|
|
265
|
+
* @param nonEdges - Array of node pairs that are not edges
|
|
266
|
+
* @param options - Link prediction options
|
|
267
|
+
* @returns Comparison of evaluation metrics for both algorithms
|
|
257
268
|
*/
|
|
258
269
|
export function compareAdamicAdarWithCommonNeighbors(
|
|
259
270
|
graph: Graph,
|
|
@@ -261,39 +272,29 @@ export function compareAdamicAdarWithCommonNeighbors(
|
|
|
261
272
|
nonEdges: [NodeId, NodeId][],
|
|
262
273
|
options: LinkPredictionOptions = {},
|
|
263
274
|
): {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
275
|
+
adamicAdar: ReturnType<typeof evaluateAdamicAdar>;
|
|
276
|
+
commonNeighbors: {
|
|
277
|
+
precision: number;
|
|
278
|
+
recall: number;
|
|
279
|
+
f1Score: number;
|
|
280
|
+
auc: number;
|
|
281
|
+
};
|
|
282
|
+
} {
|
|
272
283
|
// Import common neighbors evaluation function
|
|
273
284
|
// Since we're using ES modules, we can't use require. Instead, we'll implement a simple version here
|
|
274
285
|
const commonNeighborsPairs = (pairs: [NodeId, NodeId][]): LinkPredictionScore[] =>
|
|
275
286
|
pairs.map(([source, target]) => ({
|
|
276
287
|
source,
|
|
277
288
|
target,
|
|
278
|
-
score: (
|
|
279
|
-
const sourceNeighbors = new Set(Array.from(graph.neighbors(source)));
|
|
280
|
-
const targetNeighbors = new Set(Array.from(graph.neighbors(target)));
|
|
281
|
-
let count = 0;
|
|
282
|
-
for (const n of sourceNeighbors) {
|
|
283
|
-
if (targetNeighbors.has(n)) {
|
|
284
|
-
count++;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return count;
|
|
288
|
-
})(),
|
|
289
|
+
score: getCommonNeighbors(graph, source, target, options.directed).size,
|
|
289
290
|
}));
|
|
290
291
|
|
|
291
292
|
const testScores = commonNeighborsPairs(testEdges);
|
|
292
293
|
const nonEdgeScores = commonNeighborsPairs(nonEdges);
|
|
293
294
|
|
|
294
295
|
const allScores = [
|
|
295
|
-
...
|
|
296
|
-
...
|
|
296
|
+
...testScores.map((s) => ({ ...s, isActualEdge: true })),
|
|
297
|
+
...nonEdgeScores.map((s) => ({ ...s, isActualEdge: false })),
|
|
297
298
|
].sort((a, b) => b.score - a.score);
|
|
298
299
|
|
|
299
300
|
let truePositives = 0;
|
|
@@ -312,7 +313,7 @@ export function compareAdamicAdarWithCommonNeighbors(
|
|
|
312
313
|
|
|
313
314
|
const precision = truePositives / (truePositives + falsePositives);
|
|
314
315
|
const recall = truePositives / totalPositives;
|
|
315
|
-
const f1 = precision + recall > 0 ? 2 * (precision * recall) / (precision + recall) : 0;
|
|
316
|
+
const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
|
|
316
317
|
if (f1 > bestF1) {
|
|
317
318
|
bestF1 = f1;
|
|
318
319
|
bestPrecision = precision;
|