@graphty/algorithms 1.3.1 → 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 +358 -413
- package/dist/algorithms.js +564 -261
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +10 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +27 -6
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +11 -2
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +17 -3
- 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 +9 -6
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +21 -3
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +25 -7
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +28 -2
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.js +30 -4
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -1
- 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 +1 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +1 -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 +14 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +16 -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 +21 -4
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- 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 +0 -3
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +13 -6
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +0 -1
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +37 -6
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +37 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.js +42 -7
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -1
- 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 +19 -6
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +0 -4
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.js +1 -5
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -1
- 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 +10 -0
- 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 +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +11 -4
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +18 -16
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +15 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +15 -0
- 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 +13 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +37 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +23 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.js +34 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +1 -1
- 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 +2 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -1
- package/dist/src/benchmark-all-algorithms.js +32 -3
- package/dist/src/benchmark-all-algorithms.js.map +1 -1
- package/dist/src/clustering/hierarchical.d.ts +8 -1
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +22 -6
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +2 -5
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +14 -12
- 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 +4 -1
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +61 -28
- 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 +45 -1
- 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 +4 -2
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +31 -2
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +0 -3
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +17 -4
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- 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/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 +36 -10
- 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 +27 -6
- 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 +103 -53
- package/dist/src/optimized/bit-packed.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.js +104 -54
- package/dist/src/optimized/bit-packed.js.map +1 -1
- package/dist/src/optimized/csr-graph.d.ts +87 -24
- package/dist/src/optimized/csr-graph.d.ts.map +1 -1
- package/dist/src/optimized/csr-graph.js +87 -24
- package/dist/src/optimized/csr-graph.js.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.d.ts +37 -16
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.js +39 -20
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
- package/dist/src/optimized/graph-adapter.d.ts +58 -9
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -1
- package/dist/src/optimized/graph-adapter.js +58 -10
- package/dist/src/optimized/graph-adapter.js.map +1 -1
- package/dist/src/optimized/index.d.ts +1 -1
- package/dist/src/optimized/index.d.ts.map +1 -1
- package/dist/src/optimized/index.js +3 -1
- package/dist/src/optimized/index.js.map +1 -1
- 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 +22 -9
- 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 +28 -7
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +0 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +32 -4
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/utils/graph-converters.d.ts +31 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -1
- package/dist/src/utils/graph-converters.js +31 -0
- package/dist/src/utils/graph-converters.js.map +1 -1
- package/dist/src/utils/graph-utilities.d.ts.map +1 -1
- package/dist/src/utils/graph-utilities.js.map +1 -1
- package/dist/src/utils/math-utilities.d.ts +11 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -1
- package/dist/src/utils/math-utilities.js +14 -5
- package/dist/src/utils/math-utilities.js.map +1 -1
- package/dist/src/utils/optimization-helpers.d.ts +9 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -1
- package/dist/src/utils/optimization-helpers.js +12 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -1
- 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 +49 -45
- package/src/algorithms/centrality/betweenness.ts +34 -17
- package/src/algorithms/centrality/closeness.ts +34 -17
- package/src/algorithms/centrality/degree.ts +15 -19
- package/src/algorithms/centrality/delta-pagerank-simple.ts +36 -28
- package/src/algorithms/centrality/delta-pagerank.ts +34 -11
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -15
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +26 -17
- package/src/algorithms/community/girvan-newman.ts +39 -28
- package/src/algorithms/community/index.ts +6 -7
- package/src/algorithms/community/label-propagation.ts +20 -28
- package/src/algorithms/community/leiden.ts +61 -37
- package/src/algorithms/community/louvain-optimized.ts +49 -20
- package/src/algorithms/community/louvain.ts +35 -25
- package/src/algorithms/community/modularity-utils.ts +5 -17
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +17 -10
- 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 -15
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +28 -28
- package/src/algorithms/shortest-path/dijkstra.ts +23 -17
- 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 +64 -60
- package/src/algorithms/traversal/bfs-variants.ts +61 -37
- package/src/algorithms/traversal/bfs.ts +1 -7
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +57 -25
- package/src/clustering/hierarchical.ts +40 -34
- package/src/clustering/index.ts +8 -8
- package/src/clustering/k-core.ts +26 -42
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +91 -57
- package/src/core/graph.ts +54 -14
- 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 +62 -35
- package/src/flow/min-cut.ts +43 -45
- package/src/index.ts +3 -2
- package/src/link-prediction/adamic-adar.ts +65 -51
- package/src/link-prediction/common-neighbors.ts +45 -36
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +106 -56
- package/src/optimized/csr-graph.ts +93 -30
- package/src/optimized/direction-optimized-bfs.ts +44 -25
- package/src/optimized/graph-adapter.ts +64 -16
- package/src/optimized/index.ts +7 -9
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +30 -24
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +39 -23
- package/src/research/terahac.ts +54 -20
- package/src/utils/graph-converters.ts +42 -14
- package/src/utils/graph-utilities.ts +11 -37
- package/src/utils/math-utilities.ts +17 -8
- package/src/utils/optimization-helpers.ts +27 -21
- package/src/utils/priorityQueue.ts +40 -6
package/src/flow/min-cut.ts
CHANGED
|
@@ -4,21 +4,20 @@
|
|
|
4
4
|
* Various algorithms for finding minimum cuts in graphs
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type {Graph} from "../core/graph.js";
|
|
8
|
-
import {graphToMap} from "../utils/graph-converters.js";
|
|
9
|
-
import {fordFulkerson} from "./ford-fulkerson.js";
|
|
7
|
+
import type { Graph } from "../core/graph.js";
|
|
8
|
+
import { graphToMap } from "../utils/graph-converters.js";
|
|
9
|
+
import { fordFulkerson } from "./ford-fulkerson.js";
|
|
10
10
|
|
|
11
11
|
export interface MinCutResult {
|
|
12
12
|
cutValue: number;
|
|
13
13
|
partition1: Set<string>;
|
|
14
14
|
partition2: Set<string>;
|
|
15
|
-
cutEdges: {from: string
|
|
15
|
+
cutEdges: { from: string; to: string; weight: number }[];
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Find minimum s-t cut using max flow
|
|
20
20
|
* The minimum cut value equals the maximum flow value (max-flow min-cut theorem)
|
|
21
|
-
*
|
|
22
21
|
* @param graph - Weighted graph
|
|
23
22
|
* @param source - Source node
|
|
24
23
|
* @param sink - Sink node
|
|
@@ -26,11 +25,7 @@ export interface MinCutResult {
|
|
|
26
25
|
*
|
|
27
26
|
* Time Complexity: Same as max flow algorithm used
|
|
28
27
|
*/
|
|
29
|
-
export function minSTCut(
|
|
30
|
-
graph: Graph,
|
|
31
|
-
source: string,
|
|
32
|
-
sink: string,
|
|
33
|
-
): MinCutResult {
|
|
28
|
+
export function minSTCut(graph: Graph, source: string, sink: string): MinCutResult {
|
|
34
29
|
const flowResult = fordFulkerson(graph, source, sink);
|
|
35
30
|
|
|
36
31
|
if (!flowResult.minCut) {
|
|
@@ -42,14 +37,14 @@ export function minSTCut(
|
|
|
42
37
|
};
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
const cutEdges: {from: string
|
|
40
|
+
const cutEdges: { from: string; to: string; weight: number }[] = [];
|
|
46
41
|
|
|
47
42
|
// Find actual cut edges and their weights
|
|
48
43
|
for (const [u, v] of flowResult.minCut.edges) {
|
|
49
44
|
const edge = graph.getEdge(u, v);
|
|
50
45
|
const weight = edge?.weight ?? 0;
|
|
51
46
|
if (weight > 0) {
|
|
52
|
-
cutEdges.push({from: u, to: v, weight});
|
|
47
|
+
cutEdges.push({ from: u, to: v, weight });
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
50
|
|
|
@@ -64,15 +59,12 @@ export function minSTCut(
|
|
|
64
59
|
/**
|
|
65
60
|
* Stoer-Wagner algorithm for finding global minimum cut
|
|
66
61
|
* Finds the minimum cut that separates the graph into two parts
|
|
67
|
-
*
|
|
68
62
|
* @param graph - Undirected weighted graph - accepts Graph class or Map representation
|
|
69
63
|
* @returns Global minimum cut
|
|
70
64
|
*
|
|
71
65
|
* Time Complexity: O(V³) or O(VE + V² log V) with heap
|
|
72
66
|
*/
|
|
73
|
-
export function stoerWagner(
|
|
74
|
-
graph: Graph | Map<string, Map<string, number>>,
|
|
75
|
-
): MinCutResult {
|
|
67
|
+
export function stoerWagner(graph: Graph | Map<string, Map<string, number>>): MinCutResult {
|
|
76
68
|
// Convert Graph to Map representation if needed
|
|
77
69
|
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
78
70
|
// Convert to undirected if necessary
|
|
@@ -140,13 +132,13 @@ export function stoerWagner(
|
|
|
140
132
|
}
|
|
141
133
|
|
|
142
134
|
// Find cut edges
|
|
143
|
-
const cutEdges: {from: string
|
|
135
|
+
const cutEdges: { from: string; to: string; weight: number }[] = [];
|
|
144
136
|
for (const u of partition1) {
|
|
145
137
|
const neighbors = originalGraph.get(u);
|
|
146
138
|
if (neighbors) {
|
|
147
139
|
for (const [v, weight] of neighbors) {
|
|
148
140
|
if (partition2.has(v)) {
|
|
149
|
-
cutEdges.push({from: u, to: v, weight});
|
|
141
|
+
cutEdges.push({ from: u, to: v, weight });
|
|
150
142
|
}
|
|
151
143
|
}
|
|
152
144
|
}
|
|
@@ -162,11 +154,14 @@ export function stoerWagner(
|
|
|
162
154
|
|
|
163
155
|
/**
|
|
164
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
|
|
165
160
|
*/
|
|
166
161
|
function minimumCutPhase(
|
|
167
162
|
graph: Map<string, Map<string, number>>,
|
|
168
163
|
nodes: string[],
|
|
169
|
-
): {s: string
|
|
164
|
+
): { s: string; t: string; value: number; partition: string[] } {
|
|
170
165
|
const n = nodes.length;
|
|
171
166
|
const weight = new Map<string, number>();
|
|
172
167
|
const added = new Set<string>();
|
|
@@ -180,7 +175,7 @@ function minimumCutPhase(
|
|
|
180
175
|
// Start with arbitrary node
|
|
181
176
|
let lastAdded = nodes[0];
|
|
182
177
|
if (!lastAdded) {
|
|
183
|
-
return {s: "", t: "", value: 0, partition: []};
|
|
178
|
+
return { s: "", t: "", value: 0, partition: [] };
|
|
184
179
|
}
|
|
185
180
|
|
|
186
181
|
added.add(lastAdded);
|
|
@@ -188,7 +183,7 @@ function minimumCutPhase(
|
|
|
188
183
|
|
|
189
184
|
// Add remaining nodes
|
|
190
185
|
for (let i = 1; i < n; i++) {
|
|
191
|
-
|
|
186
|
+
// Update weights
|
|
192
187
|
if (!lastAdded) {
|
|
193
188
|
continue;
|
|
194
189
|
}
|
|
@@ -225,7 +220,7 @@ function minimumCutPhase(
|
|
|
225
220
|
const s = order[order.length - 2];
|
|
226
221
|
const t = order[order.length - 1];
|
|
227
222
|
if (!s || !t) {
|
|
228
|
-
return {s: "", t: "", value: 0, partition: []};
|
|
223
|
+
return { s: "", t: "", value: 0, partition: [] };
|
|
229
224
|
}
|
|
230
225
|
|
|
231
226
|
const cutValue = weight.get(t) ?? 0;
|
|
@@ -233,18 +228,17 @@ function minimumCutPhase(
|
|
|
233
228
|
// Partition is all nodes except t
|
|
234
229
|
const partition = order.slice(0, -1);
|
|
235
230
|
|
|
236
|
-
return {s, t, value: cutValue, partition};
|
|
231
|
+
return { s, t, value: cutValue, partition };
|
|
237
232
|
}
|
|
238
233
|
|
|
239
234
|
/**
|
|
240
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
|
|
241
240
|
*/
|
|
242
|
-
function contractNodes(
|
|
243
|
-
graph: Map<string, Map<string, number>>,
|
|
244
|
-
nodes: string[],
|
|
245
|
-
s: string,
|
|
246
|
-
t: string,
|
|
247
|
-
): void {
|
|
241
|
+
function contractNodes(graph: Map<string, Map<string, number>>, nodes: string[], s: string, t: string): void {
|
|
248
242
|
// Merge t into s
|
|
249
243
|
const sNeighbors = graph.get(s);
|
|
250
244
|
const tNeighbors = graph.get(t);
|
|
@@ -279,10 +273,10 @@ function contractNodes(
|
|
|
279
273
|
|
|
280
274
|
/**
|
|
281
275
|
* Convert directed graph to undirected
|
|
276
|
+
* @param graph - The directed graph to convert
|
|
277
|
+
* @returns An undirected graph with edges in both directions
|
|
282
278
|
*/
|
|
283
|
-
function makeUndirected(
|
|
284
|
-
graph: Map<string, Map<string, number>>,
|
|
285
|
-
): Map<string, Map<string, number>> {
|
|
279
|
+
function makeUndirected(graph: Map<string, Map<string, number>>): Map<string, Map<string, number>> {
|
|
286
280
|
const undirected = new Map<string, Map<string, number>>();
|
|
287
281
|
|
|
288
282
|
// Initialize all nodes
|
|
@@ -315,17 +309,13 @@ function makeUndirected(
|
|
|
315
309
|
/**
|
|
316
310
|
* Karger's randomized min-cut algorithm
|
|
317
311
|
* Probabilistic algorithm that finds min cut with high probability
|
|
318
|
-
*
|
|
319
312
|
* @param graph - Undirected graph - accepts Graph class or Map representation
|
|
320
313
|
* @param iterations - Number of iterations (higher = better accuracy)
|
|
321
314
|
* @returns Minimum cut found
|
|
322
315
|
*
|
|
323
316
|
* Time Complexity: O(V² * iterations)
|
|
324
317
|
*/
|
|
325
|
-
export function kargerMinCut(
|
|
326
|
-
graph: Graph | Map<string, Map<string, number>>,
|
|
327
|
-
iterations = 100,
|
|
328
|
-
): MinCutResult {
|
|
318
|
+
export function kargerMinCut(graph: Graph | Map<string, Map<string, number>>, iterations = 100): MinCutResult {
|
|
329
319
|
// Convert Graph to Map representation if needed
|
|
330
320
|
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
331
321
|
let minCutValue = Infinity;
|
|
@@ -343,13 +333,13 @@ export function kargerMinCut(
|
|
|
343
333
|
}
|
|
344
334
|
|
|
345
335
|
// Find cut edges
|
|
346
|
-
const cutEdges: {from: string
|
|
336
|
+
const cutEdges: { from: string; to: string; weight: number }[] = [];
|
|
347
337
|
for (const u of bestPartition1) {
|
|
348
338
|
const neighbors = graphMap.get(u);
|
|
349
339
|
if (neighbors) {
|
|
350
340
|
for (const [v, weight] of neighbors) {
|
|
351
341
|
if (bestPartition2.has(v)) {
|
|
352
|
-
cutEdges.push({from: u, to: v, weight});
|
|
342
|
+
cutEdges.push({ from: u, to: v, weight });
|
|
353
343
|
}
|
|
354
344
|
}
|
|
355
345
|
}
|
|
@@ -365,10 +355,14 @@ export function kargerMinCut(
|
|
|
365
355
|
|
|
366
356
|
/**
|
|
367
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
|
|
368
360
|
*/
|
|
369
|
-
function kargerSingleRun(
|
|
370
|
-
|
|
371
|
-
|
|
361
|
+
function kargerSingleRun(graph: Map<string, Map<string, number>>): {
|
|
362
|
+
cutValue: number;
|
|
363
|
+
partition1: Set<string>;
|
|
364
|
+
partition2: Set<string>;
|
|
365
|
+
} {
|
|
372
366
|
// Create a copy of the graph
|
|
373
367
|
const workGraph = new Map<string, Map<string, number>>();
|
|
374
368
|
const superNodes = new Map<string, Set<string>>();
|
|
@@ -381,11 +375,12 @@ function kargerSingleRun(
|
|
|
381
375
|
|
|
382
376
|
// Contract until 2 nodes remain
|
|
383
377
|
while (workGraph.size > 2) {
|
|
384
|
-
|
|
378
|
+
// Pick random edge
|
|
385
379
|
const edges: [string, string, number][] = [];
|
|
386
380
|
for (const [u, neighbors] of workGraph) {
|
|
387
381
|
for (const [v, weight] of neighbors) {
|
|
388
|
-
if (u < v) {
|
|
382
|
+
if (u < v) {
|
|
383
|
+
// Avoid duplicates
|
|
389
384
|
edges.push([u, v, weight]);
|
|
390
385
|
}
|
|
391
386
|
}
|
|
@@ -440,6 +435,10 @@ function kargerSingleRun(
|
|
|
440
435
|
|
|
441
436
|
/**
|
|
442
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)
|
|
443
442
|
*/
|
|
444
443
|
function contractKarger(
|
|
445
444
|
graph: Map<string, Map<string, number>>,
|
|
@@ -489,4 +488,3 @@ function contractKarger(
|
|
|
489
488
|
graph.delete(v);
|
|
490
489
|
superNodes.delete(v);
|
|
491
490
|
}
|
|
492
|
-
|
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 {
|
|
@@ -1,7 +1,7 @@
|
|
|
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";
|
|
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";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Adamic-Adar Index link prediction implementation
|
|
@@ -19,6 +19,11 @@ import type {LinkPredictionOptions, LinkPredictionScore} from "./common-neighbor
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
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
|
|
22
27
|
*/
|
|
23
28
|
export function adamicAdarScore(
|
|
24
29
|
graph: Graph,
|
|
@@ -30,20 +35,20 @@ export function adamicAdarScore(
|
|
|
30
35
|
return 0;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
const {directed = false} = options;
|
|
38
|
+
const { directed = false } = options;
|
|
34
39
|
|
|
35
40
|
// Use utility function to get common neighbors
|
|
36
41
|
// For directed graphs, we want intermediate nodes that form paths source->X->target
|
|
37
|
-
const commonNeighborsSet = directed
|
|
38
|
-
getIntermediateNodes(graph, source, target)
|
|
39
|
-
getCommonNeighbors(graph, source, target, false);
|
|
42
|
+
const commonNeighborsSet = directed
|
|
43
|
+
? getIntermediateNodes(graph, source, target)
|
|
44
|
+
: getCommonNeighbors(graph, source, target, false);
|
|
40
45
|
|
|
41
46
|
// Calculate Adamic-Adar score
|
|
42
47
|
let score = 0;
|
|
43
48
|
for (const neighbor of commonNeighborsSet) {
|
|
44
|
-
const degree = directed
|
|
45
|
-
graph.outDegree(neighbor)
|
|
46
|
-
graph.degree(neighbor); // Use total degree for undirected graphs
|
|
49
|
+
const degree = directed
|
|
50
|
+
? graph.outDegree(neighbor) // Use out-degree for directed graphs
|
|
51
|
+
: graph.degree(neighbor); // Use total degree for undirected graphs
|
|
47
52
|
|
|
48
53
|
if (degree > 1) {
|
|
49
54
|
score += 1 / Math.log(degree);
|
|
@@ -58,16 +63,12 @@ export function adamicAdarScore(
|
|
|
58
63
|
|
|
59
64
|
/**
|
|
60
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
|
|
61
69
|
*/
|
|
62
|
-
export function adamicAdarPrediction(
|
|
63
|
-
|
|
64
|
-
options: LinkPredictionOptions = {},
|
|
65
|
-
): LinkPredictionScore[] {
|
|
66
|
-
const {
|
|
67
|
-
directed = false,
|
|
68
|
-
includeExisting = false,
|
|
69
|
-
topK,
|
|
70
|
-
} = options;
|
|
70
|
+
export function adamicAdarPrediction(graph: Graph, options: LinkPredictionOptions = {}): LinkPredictionScore[] {
|
|
71
|
+
const { directed = false, includeExisting = false, topK } = options;
|
|
71
72
|
|
|
72
73
|
const scores: LinkPredictionScore[] = [];
|
|
73
74
|
const nodes = Array.from(graph.nodes()).map((n) => n.id);
|
|
@@ -86,14 +87,14 @@ export function adamicAdarPrediction(
|
|
|
86
87
|
continue;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
const score = adamicAdarScore(graph, source, target, {directed});
|
|
90
|
+
const score = adamicAdarScore(graph, source, target, { directed });
|
|
90
91
|
|
|
91
92
|
if (score > 0) {
|
|
92
|
-
scores.push({source, target, score});
|
|
93
|
+
scores.push({ source, target, score });
|
|
93
94
|
|
|
94
95
|
// For undirected graphs, also add the reverse pair
|
|
95
96
|
if (!directed && source !== target) {
|
|
96
|
-
scores.push({source: target, target: source, score});
|
|
97
|
+
scores.push({ source: target, target: source, score });
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -112,6 +113,10 @@ export function adamicAdarPrediction(
|
|
|
112
113
|
|
|
113
114
|
/**
|
|
114
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
|
|
115
120
|
*/
|
|
116
121
|
export function adamicAdarForPairs(
|
|
117
122
|
graph: Graph,
|
|
@@ -127,22 +132,21 @@ export function adamicAdarForPairs(
|
|
|
127
132
|
|
|
128
133
|
/**
|
|
129
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
|
|
130
139
|
*/
|
|
131
140
|
export function getTopAdamicAdarCandidatesForNode(
|
|
132
141
|
graph: Graph,
|
|
133
142
|
node: NodeId,
|
|
134
|
-
options: LinkPredictionOptions & {candidates?: NodeId[]} = {},
|
|
143
|
+
options: LinkPredictionOptions & { candidates?: NodeId[] } = {},
|
|
135
144
|
): LinkPredictionScore[] {
|
|
136
145
|
if (!graph.hasNode(node)) {
|
|
137
146
|
return [];
|
|
138
147
|
}
|
|
139
148
|
|
|
140
|
-
const {
|
|
141
|
-
directed = false,
|
|
142
|
-
includeExisting = false,
|
|
143
|
-
topK = 10,
|
|
144
|
-
candidates,
|
|
145
|
-
} = options;
|
|
149
|
+
const { directed = false, includeExisting = false, topK = 10, candidates } = options;
|
|
146
150
|
|
|
147
151
|
const scores: LinkPredictionScore[] = [];
|
|
148
152
|
const targetNodes = candidates ?? Array.from(graph.nodes()).map((n) => n.id);
|
|
@@ -157,10 +161,10 @@ export function getTopAdamicAdarCandidatesForNode(
|
|
|
157
161
|
continue;
|
|
158
162
|
}
|
|
159
163
|
|
|
160
|
-
const score = adamicAdarScore(graph, node, target, {directed});
|
|
164
|
+
const score = adamicAdarScore(graph, node, target, { directed });
|
|
161
165
|
|
|
162
166
|
if (score > 0) {
|
|
163
|
-
scores.push({source: node, target, score});
|
|
167
|
+
scores.push({ source: node, target, score });
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
|
|
@@ -172,6 +176,11 @@ export function getTopAdamicAdarCandidatesForNode(
|
|
|
172
176
|
|
|
173
177
|
/**
|
|
174
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
|
|
175
184
|
*/
|
|
176
185
|
export function evaluateAdamicAdar(
|
|
177
186
|
trainingGraph: Graph,
|
|
@@ -179,19 +188,19 @@ export function evaluateAdamicAdar(
|
|
|
179
188
|
nonEdges: [NodeId, NodeId][],
|
|
180
189
|
options: LinkPredictionOptions = {},
|
|
181
190
|
): {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
precision: number;
|
|
192
|
+
recall: number;
|
|
193
|
+
f1Score: number;
|
|
194
|
+
auc: number;
|
|
195
|
+
} {
|
|
187
196
|
// Get scores for test edges and non-edges
|
|
188
197
|
const testScores = adamicAdarForPairs(trainingGraph, testEdges, options);
|
|
189
198
|
const nonEdgeScores = adamicAdarForPairs(trainingGraph, nonEdges, options);
|
|
190
199
|
|
|
191
200
|
// Combine and sort all scores
|
|
192
201
|
const allScores = [
|
|
193
|
-
...
|
|
194
|
-
...
|
|
202
|
+
...testScores.map((s) => ({ ...s, isActualEdge: true })),
|
|
203
|
+
...nonEdgeScores.map((s) => ({ ...s, isActualEdge: false })),
|
|
195
204
|
].sort((a, b) => b.score - a.score);
|
|
196
205
|
|
|
197
206
|
// Calculate precision and recall at different thresholds
|
|
@@ -212,7 +221,7 @@ export function evaluateAdamicAdar(
|
|
|
212
221
|
|
|
213
222
|
const precision = truePositives / (truePositives + falsePositives);
|
|
214
223
|
const recall = truePositives / totalPositives;
|
|
215
|
-
const f1 = precision + recall > 0 ? 2 * (precision * recall) / (precision + recall) : 0;
|
|
224
|
+
const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
|
|
216
225
|
|
|
217
226
|
if (f1 > bestF1) {
|
|
218
227
|
bestF1 = f1;
|
|
@@ -251,6 +260,11 @@ export function evaluateAdamicAdar(
|
|
|
251
260
|
|
|
252
261
|
/**
|
|
253
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
|
|
254
268
|
*/
|
|
255
269
|
export function compareAdamicAdarWithCommonNeighbors(
|
|
256
270
|
graph: Graph,
|
|
@@ -258,14 +272,14 @@ export function compareAdamicAdarWithCommonNeighbors(
|
|
|
258
272
|
nonEdges: [NodeId, NodeId][],
|
|
259
273
|
options: LinkPredictionOptions = {},
|
|
260
274
|
): {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
275
|
+
adamicAdar: ReturnType<typeof evaluateAdamicAdar>;
|
|
276
|
+
commonNeighbors: {
|
|
277
|
+
precision: number;
|
|
278
|
+
recall: number;
|
|
279
|
+
f1Score: number;
|
|
280
|
+
auc: number;
|
|
281
|
+
};
|
|
282
|
+
} {
|
|
269
283
|
// Import common neighbors evaluation function
|
|
270
284
|
// Since we're using ES modules, we can't use require. Instead, we'll implement a simple version here
|
|
271
285
|
const commonNeighborsPairs = (pairs: [NodeId, NodeId][]): LinkPredictionScore[] =>
|
|
@@ -279,8 +293,8 @@ export function compareAdamicAdarWithCommonNeighbors(
|
|
|
279
293
|
const nonEdgeScores = commonNeighborsPairs(nonEdges);
|
|
280
294
|
|
|
281
295
|
const allScores = [
|
|
282
|
-
...
|
|
283
|
-
...
|
|
296
|
+
...testScores.map((s) => ({ ...s, isActualEdge: true })),
|
|
297
|
+
...nonEdgeScores.map((s) => ({ ...s, isActualEdge: false })),
|
|
284
298
|
].sort((a, b) => b.score - a.score);
|
|
285
299
|
|
|
286
300
|
let truePositives = 0;
|
|
@@ -299,7 +313,7 @@ export function compareAdamicAdarWithCommonNeighbors(
|
|
|
299
313
|
|
|
300
314
|
const precision = truePositives / (truePositives + falsePositives);
|
|
301
315
|
const recall = truePositives / totalPositives;
|
|
302
|
-
const f1 = precision + recall > 0 ? 2 * (precision * recall) / (precision + recall) : 0;
|
|
316
|
+
const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
|
|
303
317
|
if (f1 > bestF1) {
|
|
304
318
|
bestF1 = f1;
|
|
305
319
|
bestPrecision = precision;
|
|
@@ -1,6 +1,6 @@
|
|
|
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";
|
|
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
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Common Neighbors link prediction implementation
|
|
@@ -27,6 +27,11 @@ export interface LinkPredictionOptions {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Calculate common neighbors score for a pair of nodes
|
|
30
|
+
* @param graph - The input graph
|
|
31
|
+
* @param source - The source node ID
|
|
32
|
+
* @param target - The target node ID
|
|
33
|
+
* @param options - Link prediction options
|
|
34
|
+
* @returns The number of common neighbors
|
|
30
35
|
*/
|
|
31
36
|
export function commonNeighborsScore(
|
|
32
37
|
graph: Graph,
|
|
@@ -38,29 +43,25 @@ export function commonNeighborsScore(
|
|
|
38
43
|
return 0;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
const {directed = false} = options;
|
|
46
|
+
const { directed = false } = options;
|
|
42
47
|
|
|
43
48
|
// Use utility function to get common neighbors
|
|
44
49
|
// For directed graphs, we want intermediate nodes that form paths source->X->target
|
|
45
|
-
const commonNeighborsSet = directed
|
|
46
|
-
getIntermediateNodes(graph, source, target)
|
|
47
|
-
getCommonNeighbors(graph, source, target, false);
|
|
50
|
+
const commonNeighborsSet = directed
|
|
51
|
+
? getIntermediateNodes(graph, source, target)
|
|
52
|
+
: getCommonNeighbors(graph, source, target, false);
|
|
48
53
|
|
|
49
54
|
return commonNeighborsSet.size;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
58
|
* Calculate common neighbors scores for all possible node pairs
|
|
59
|
+
* @param graph - The input graph
|
|
60
|
+
* @param options - Link prediction options
|
|
61
|
+
* @returns Array of link prediction scores sorted by score descending
|
|
54
62
|
*/
|
|
55
|
-
export function commonNeighborsPrediction(
|
|
56
|
-
|
|
57
|
-
options: LinkPredictionOptions = {},
|
|
58
|
-
): LinkPredictionScore[] {
|
|
59
|
-
const {
|
|
60
|
-
directed = false,
|
|
61
|
-
includeExisting = false,
|
|
62
|
-
topK,
|
|
63
|
-
} = options;
|
|
63
|
+
export function commonNeighborsPrediction(graph: Graph, options: LinkPredictionOptions = {}): LinkPredictionScore[] {
|
|
64
|
+
const { directed = false, includeExisting = false, topK } = options;
|
|
64
65
|
|
|
65
66
|
const scores: LinkPredictionScore[] = [];
|
|
66
67
|
const nodes = Array.from(graph.nodes()).map((n) => n.id);
|
|
@@ -79,14 +80,14 @@ export function commonNeighborsPrediction(
|
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
const score = commonNeighborsScore(graph, source, target, {directed});
|
|
83
|
+
const score = commonNeighborsScore(graph, source, target, { directed });
|
|
83
84
|
|
|
84
85
|
if (score > 0) {
|
|
85
|
-
scores.push({source, target, score});
|
|
86
|
+
scores.push({ source, target, score });
|
|
86
87
|
|
|
87
88
|
// For undirected graphs, also add the reverse pair
|
|
88
89
|
if (!directed && source !== target) {
|
|
89
|
-
scores.push({source: target, target: source, score});
|
|
90
|
+
scores.push({ source: target, target: source, score });
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -105,6 +106,10 @@ export function commonNeighborsPrediction(
|
|
|
105
106
|
|
|
106
107
|
/**
|
|
107
108
|
* Calculate common neighbors scores for specific node pairs
|
|
109
|
+
* @param graph - The input graph
|
|
110
|
+
* @param pairs - Array of node ID pairs to calculate scores for
|
|
111
|
+
* @param options - Link prediction options
|
|
112
|
+
* @returns Array of link prediction scores for the specified pairs
|
|
108
113
|
*/
|
|
109
114
|
export function commonNeighborsForPairs(
|
|
110
115
|
graph: Graph,
|
|
@@ -120,22 +125,21 @@ export function commonNeighborsForPairs(
|
|
|
120
125
|
|
|
121
126
|
/**
|
|
122
127
|
* Get top candidates for link prediction for a specific node
|
|
128
|
+
* @param graph - The input graph
|
|
129
|
+
* @param node - The node ID to get candidates for
|
|
130
|
+
* @param options - Link prediction options with optional candidate list
|
|
131
|
+
* @returns Array of top link prediction candidates sorted by score descending
|
|
123
132
|
*/
|
|
124
133
|
export function getTopCandidatesForNode(
|
|
125
134
|
graph: Graph,
|
|
126
135
|
node: NodeId,
|
|
127
|
-
options: LinkPredictionOptions & {candidates?: NodeId[]} = {},
|
|
136
|
+
options: LinkPredictionOptions & { candidates?: NodeId[] } = {},
|
|
128
137
|
): LinkPredictionScore[] {
|
|
129
138
|
if (!graph.hasNode(node)) {
|
|
130
139
|
return [];
|
|
131
140
|
}
|
|
132
141
|
|
|
133
|
-
const {
|
|
134
|
-
directed = false,
|
|
135
|
-
includeExisting = false,
|
|
136
|
-
topK = 10,
|
|
137
|
-
candidates,
|
|
138
|
-
} = options;
|
|
142
|
+
const { directed = false, includeExisting = false, topK = 10, candidates } = options;
|
|
139
143
|
|
|
140
144
|
const scores: LinkPredictionScore[] = [];
|
|
141
145
|
const targetNodes = candidates ?? Array.from(graph.nodes()).map((n) => n.id);
|
|
@@ -150,10 +154,10 @@ export function getTopCandidatesForNode(
|
|
|
150
154
|
continue;
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
const score = commonNeighborsScore(graph, node, target, {directed});
|
|
157
|
+
const score = commonNeighborsScore(graph, node, target, { directed });
|
|
154
158
|
|
|
155
159
|
if (score > 0) {
|
|
156
|
-
scores.push({source: node, target, score});
|
|
160
|
+
scores.push({ source: node, target, score });
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
163
|
|
|
@@ -165,6 +169,11 @@ export function getTopCandidatesForNode(
|
|
|
165
169
|
|
|
166
170
|
/**
|
|
167
171
|
* Calculate precision and recall for link prediction evaluation
|
|
172
|
+
* @param trainingGraph - The training graph without test edges
|
|
173
|
+
* @param testEdges - Array of node pairs that are actual edges
|
|
174
|
+
* @param nonEdges - Array of node pairs that are not edges
|
|
175
|
+
* @param options - Link prediction options
|
|
176
|
+
* @returns Evaluation metrics including precision, recall, F1 score, and AUC
|
|
168
177
|
*/
|
|
169
178
|
export function evaluateCommonNeighbors(
|
|
170
179
|
trainingGraph: Graph,
|
|
@@ -172,19 +181,19 @@ export function evaluateCommonNeighbors(
|
|
|
172
181
|
nonEdges: [NodeId, NodeId][],
|
|
173
182
|
options: LinkPredictionOptions = {},
|
|
174
183
|
): {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
184
|
+
precision: number;
|
|
185
|
+
recall: number;
|
|
186
|
+
f1Score: number;
|
|
187
|
+
auc: number;
|
|
188
|
+
} {
|
|
180
189
|
// Get scores for test edges and non-edges
|
|
181
190
|
const testScores = commonNeighborsForPairs(trainingGraph, testEdges, options);
|
|
182
191
|
const nonEdgeScores = commonNeighborsForPairs(trainingGraph, nonEdges, options);
|
|
183
192
|
|
|
184
193
|
// Combine and sort all scores
|
|
185
194
|
const allScores = [
|
|
186
|
-
...
|
|
187
|
-
...
|
|
195
|
+
...testScores.map((s) => ({ ...s, isActualEdge: true })),
|
|
196
|
+
...nonEdgeScores.map((s) => ({ ...s, isActualEdge: false })),
|
|
188
197
|
].sort((a, b) => b.score - a.score);
|
|
189
198
|
|
|
190
199
|
// Calculate precision and recall at different thresholds
|
|
@@ -205,7 +214,7 @@ export function evaluateCommonNeighbors(
|
|
|
205
214
|
|
|
206
215
|
const precision = truePositives / (truePositives + falsePositives);
|
|
207
216
|
const recall = truePositives / totalPositives;
|
|
208
|
-
const f1 = precision + recall > 0 ? 2 * (precision * recall) / (precision + recall) : 0;
|
|
217
|
+
const f1 = precision + recall > 0 ? (2 * (precision * recall)) / (precision + recall) : 0;
|
|
209
218
|
|
|
210
219
|
if (f1 > bestF1) {
|
|
211
220
|
bestF1 = f1;
|