@graphty/algorithms 1.2.0 → 1.3.1
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 +490 -336
- package/dist/algorithms.d.ts +42 -2
- package/dist/algorithms.js +11099 -2
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +4 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +124 -170
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +26 -1
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +56 -149
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +3 -3
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +39 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +286 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +80 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js +294 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
- package/dist/src/algorithms/centrality/index.d.ts +2 -0
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +1 -0
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +17 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +45 -0
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +47 -54
- 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 +13 -3
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +50 -35
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +3 -2
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +34 -39
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +112 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain-optimized.js +303 -0
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain.js +17 -76
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +59 -0
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
- package/dist/src/algorithms/community/modularity-utils.js +112 -0
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
- package/dist/src/algorithms/matching/bipartite.js +8 -40
- package/dist/src/algorithms/matching/bipartite.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +1 -12
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +37 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +193 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +5 -2
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +15 -14
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +29 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-unified.js +333 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +54 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.js +368 -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/benchmark-all-algorithms.d.ts +23 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
- package/dist/src/benchmark-all-algorithms.js +462 -0
- package/dist/src/benchmark-all-algorithms.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +16 -15
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +209 -223
- 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 +16 -15
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +57 -21
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/spectral.d.ts +8 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +40 -21
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/core/graph.js +4 -4
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +17 -11
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +141 -217
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +6 -5
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +17 -8
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
- package/dist/src/link-prediction/adamic-adar.js +17 -26
- package/dist/src/link-prediction/adamic-adar.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +7 -11
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/optimized/bit-packed.d.ts +143 -0
- package/dist/src/optimized/bit-packed.d.ts.map +1 -0
- package/dist/src/optimized/bit-packed.js +292 -0
- package/dist/src/optimized/bit-packed.js.map +1 -0
- package/dist/src/optimized/csr-graph.d.ts +86 -0
- package/dist/src/optimized/csr-graph.d.ts.map +1 -0
- package/dist/src/optimized/csr-graph.js +341 -0
- package/dist/src/optimized/csr-graph.js.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts +74 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.js +223 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
- package/dist/src/optimized/graph-adapter.d.ts +62 -0
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
- package/dist/src/optimized/graph-adapter.js +146 -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 +28 -0
- package/dist/src/optimized/index.js.map +1 -0
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +6 -14
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +3 -29
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +2 -0
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +7 -3
- 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 +79 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -0
- package/dist/src/utils/graph-converters.js +196 -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 +52 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -0
- package/dist/src/utils/math-utilities.js +123 -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 +33 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
- package/dist/src/utils/optimization-helpers.js +47 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -0
- package/package.json +18 -3
- package/src/algorithms/centrality/betweenness.ts +167 -199
- package/src/algorithms/centrality/closeness.ts +73 -191
- package/src/algorithms/centrality/degree.ts +3 -3
- package/src/algorithms/centrality/delta-pagerank-simple.ts +366 -0
- package/src/algorithms/centrality/delta-pagerank.ts +435 -0
- package/src/algorithms/centrality/index.ts +2 -0
- package/src/algorithms/centrality/pagerank.ts +66 -0
- package/src/algorithms/community/girvan-newman.ts +52 -59
- package/src/algorithms/community/index.ts +2 -0
- package/src/algorithms/community/label-propagation.ts +64 -35
- package/src/algorithms/community/leiden.ts +40 -41
- package/src/algorithms/community/louvain-optimized.ts +445 -0
- package/src/algorithms/community/louvain.ts +24 -104
- package/src/algorithms/community/modularity-utils.ts +146 -0
- package/src/algorithms/matching/bipartite.ts +8 -44
- package/src/algorithms/shortest-path/bellman-ford.ts +1 -14
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
- package/src/algorithms/shortest-path/dijkstra.ts +17 -15
- package/src/algorithms/traversal/bfs-unified.ts +433 -0
- package/src/algorithms/traversal/bfs-variants.ts +496 -0
- package/src/algorithms/traversal/bfs.ts +10 -228
- package/src/benchmark-all-algorithms.ts +548 -0
- package/src/clustering/hierarchical.ts +241 -251
- package/src/clustering/index.ts +4 -2
- package/src/clustering/k-core.ts +87 -39
- package/src/clustering/spectral.ts +44 -20
- package/src/core/graph.ts +4 -4
- package/src/flow/ford-fulkerson.ts +186 -254
- package/src/flow/min-cut.ts +21 -11
- package/src/index.ts +6 -0
- package/src/link-prediction/adamic-adar.ts +17 -30
- package/src/link-prediction/common-neighbors.ts +7 -16
- package/src/optimized/bit-packed.ts +340 -0
- package/src/optimized/csr-graph.ts +455 -0
- package/src/optimized/direction-optimized-bfs.ts +291 -0
- package/src/optimized/graph-adapter.ts +200 -0
- package/src/optimized/index.ts +36 -0
- package/src/research/grsbm.ts +7 -16
- package/src/research/sync.ts +3 -32
- package/src/research/terahac.ts +10 -2
- package/src/types/index.ts +8 -0
- package/src/utils/algorithm-utilities.ts +2 -0
- package/src/utils/graph-converters.ts +241 -0
- package/src/utils/graph-utilities.ts +199 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/math-utilities.ts +140 -0
- package/src/utils/matrix-utilities.ts +2 -0
- package/src/utils/optimization-helpers.ts +81 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {bfsAugmentingPath} from "../algorithms/traversal/bfs-variants.js";
|
|
2
|
+
import type {Graph} from "../core/graph.js";
|
|
3
|
+
import {graphToMap} from "../utils/graph-converters.js";
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Ford-Fulkerson Algorithm for Maximum Flow
|
|
3
7
|
*
|
|
@@ -23,219 +27,6 @@ export interface MaxFlowResult {
|
|
|
23
27
|
minCut?: {source: Set<string>, sink: Set<string>, edges: [string, string][]};
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
/**
|
|
27
|
-
* Ford-Fulkerson algorithm using DFS for finding augmenting paths
|
|
28
|
-
*
|
|
29
|
-
* @param graph - Adjacency list representation with capacities
|
|
30
|
-
* @param source - Source node
|
|
31
|
-
* @param sink - Sink node
|
|
32
|
-
* @returns Maximum flow value and flow graph
|
|
33
|
-
*
|
|
34
|
-
* Time Complexity: O(E * f) where f is the maximum flow
|
|
35
|
-
* Space Complexity: O(V + E)
|
|
36
|
-
*/
|
|
37
|
-
export function fordFulkerson(
|
|
38
|
-
graph: Map<string, Map<string, number>>,
|
|
39
|
-
source: string,
|
|
40
|
-
sink: string,
|
|
41
|
-
): MaxFlowResult {
|
|
42
|
-
if (!graph.has(source) || !graph.has(sink)) {
|
|
43
|
-
return {maxFlow: 0, flowGraph: new Map()};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Create residual graph
|
|
47
|
-
const residualGraph = createResidualGraph(graph);
|
|
48
|
-
const flowGraph = new Map<string, Map<string, number>>();
|
|
49
|
-
|
|
50
|
-
// Initialize flow graph
|
|
51
|
-
for (const [u, neighbors] of graph) {
|
|
52
|
-
flowGraph.set(u, new Map());
|
|
53
|
-
for (const v of neighbors.keys()) {
|
|
54
|
-
const uFlowNeighbors = flowGraph.get(u);
|
|
55
|
-
if (uFlowNeighbors) {
|
|
56
|
-
uFlowNeighbors.set(v, 0);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let maxFlow = 0;
|
|
62
|
-
let path = findAugmentingPath(residualGraph, source, sink);
|
|
63
|
-
|
|
64
|
-
while (path !== null) {
|
|
65
|
-
// Find minimum capacity along the path
|
|
66
|
-
let pathFlow = Infinity;
|
|
67
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
68
|
-
const u = path[i];
|
|
69
|
-
const v = path[i + 1];
|
|
70
|
-
if (!u || !v) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const uEdges = residualGraph.get(u);
|
|
75
|
-
if (!uEdges) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const capacity = uEdges.get(v);
|
|
80
|
-
if (capacity !== undefined) {
|
|
81
|
-
pathFlow = Math.min(pathFlow, capacity);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Update residual capacities
|
|
86
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
87
|
-
const u = path[i];
|
|
88
|
-
const v = path[i + 1];
|
|
89
|
-
if (!u || !v) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Update residual graph
|
|
94
|
-
const uEdges = residualGraph.get(u);
|
|
95
|
-
if (uEdges) {
|
|
96
|
-
const currentCapacity = uEdges.get(v);
|
|
97
|
-
if (currentCapacity !== undefined) {
|
|
98
|
-
uEdges.set(v, currentCapacity - pathFlow);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Add reverse edge
|
|
103
|
-
if (!residualGraph.has(v)) {
|
|
104
|
-
residualGraph.set(v, new Map());
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const vEdges = residualGraph.get(v);
|
|
108
|
-
if (vEdges) {
|
|
109
|
-
vEdges.set(u, (vEdges.get(u) ?? 0) + pathFlow);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Update flow graph
|
|
113
|
-
if (graph.get(u)?.has(v)) {
|
|
114
|
-
const uFlowEdges = flowGraph.get(u);
|
|
115
|
-
if (uFlowEdges) {
|
|
116
|
-
const currentFlow = uFlowEdges.get(v) ?? 0;
|
|
117
|
-
uFlowEdges.set(v, currentFlow + pathFlow);
|
|
118
|
-
}
|
|
119
|
-
} else if (graph.get(v)?.has(u)) {
|
|
120
|
-
// This is a reverse flow
|
|
121
|
-
const vFlowEdges = flowGraph.get(v);
|
|
122
|
-
if (vFlowEdges) {
|
|
123
|
-
const currentFlow = vFlowEdges.get(u) ?? 0;
|
|
124
|
-
vFlowEdges.set(u, currentFlow - pathFlow);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
maxFlow += pathFlow;
|
|
130
|
-
path = findAugmentingPath(residualGraph, source, sink);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Find minimum cut
|
|
134
|
-
const minCut = findMinCut(residualGraph, source);
|
|
135
|
-
|
|
136
|
-
return {maxFlow, flowGraph, minCut};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Edmonds-Karp algorithm (Ford-Fulkerson with BFS)
|
|
141
|
-
* More efficient implementation with better time complexity
|
|
142
|
-
*
|
|
143
|
-
* Time Complexity: O(V * E²)
|
|
144
|
-
*/
|
|
145
|
-
export function edmondsKarp(
|
|
146
|
-
graph: Map<string, Map<string, number>>,
|
|
147
|
-
source: string,
|
|
148
|
-
sink: string,
|
|
149
|
-
): MaxFlowResult {
|
|
150
|
-
if (!graph.has(source) || !graph.has(sink)) {
|
|
151
|
-
return {maxFlow: 0, flowGraph: new Map()};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const residualGraph = createResidualGraph(graph);
|
|
155
|
-
const flowGraph = new Map<string, Map<string, number>>();
|
|
156
|
-
|
|
157
|
-
// Initialize flow graph
|
|
158
|
-
for (const [u, neighbors] of graph) {
|
|
159
|
-
flowGraph.set(u, new Map());
|
|
160
|
-
for (const v of neighbors.keys()) {
|
|
161
|
-
const uFlowNeighbors = flowGraph.get(u);
|
|
162
|
-
if (uFlowNeighbors) {
|
|
163
|
-
uFlowNeighbors.set(v, 0);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let maxFlow = 0;
|
|
169
|
-
let path = findAugmentingPathBFS(residualGraph, source, sink);
|
|
170
|
-
|
|
171
|
-
while (path !== null) {
|
|
172
|
-
let pathFlow = Infinity;
|
|
173
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
174
|
-
const u = path[i];
|
|
175
|
-
const v = path[i + 1];
|
|
176
|
-
if (!u || !v) {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const uEdges = residualGraph.get(u);
|
|
181
|
-
if (!uEdges) {
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const capacity = uEdges.get(v);
|
|
186
|
-
if (capacity !== undefined) {
|
|
187
|
-
pathFlow = Math.min(pathFlow, capacity);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
192
|
-
const u = path[i];
|
|
193
|
-
const v = path[i + 1];
|
|
194
|
-
if (!u || !v) {
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const uEdges = residualGraph.get(u);
|
|
199
|
-
if (uEdges) {
|
|
200
|
-
const currentCapacity = uEdges.get(v);
|
|
201
|
-
if (currentCapacity !== undefined) {
|
|
202
|
-
uEdges.set(v, currentCapacity - pathFlow);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (!residualGraph.has(v)) {
|
|
207
|
-
residualGraph.set(v, new Map());
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const vEdges = residualGraph.get(v);
|
|
211
|
-
if (vEdges) {
|
|
212
|
-
vEdges.set(u, (vEdges.get(u) ?? 0) + pathFlow);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (graph.get(u)?.has(v)) {
|
|
216
|
-
const uFlowEdges = flowGraph.get(u);
|
|
217
|
-
if (uFlowEdges) {
|
|
218
|
-
const currentFlow = uFlowEdges.get(v) ?? 0;
|
|
219
|
-
uFlowEdges.set(v, currentFlow + pathFlow);
|
|
220
|
-
}
|
|
221
|
-
} else if (graph.get(v)?.has(u)) {
|
|
222
|
-
const vFlowEdges = flowGraph.get(v);
|
|
223
|
-
if (vFlowEdges) {
|
|
224
|
-
const currentFlow = vFlowEdges.get(u) ?? 0;
|
|
225
|
-
vFlowEdges.set(u, currentFlow - pathFlow);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
maxFlow += pathFlow;
|
|
231
|
-
path = findAugmentingPathBFS(residualGraph, source, sink);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const minCut = findMinCut(residualGraph, source);
|
|
235
|
-
|
|
236
|
-
return {maxFlow, flowGraph, minCut};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
30
|
/**
|
|
240
31
|
* Create residual graph from original graph
|
|
241
32
|
*/
|
|
@@ -260,7 +51,7 @@ function createResidualGraph(
|
|
|
260
51
|
/**
|
|
261
52
|
* Find augmenting path using DFS
|
|
262
53
|
*/
|
|
263
|
-
function
|
|
54
|
+
function findAugmentingPathDFS(
|
|
264
55
|
residualGraph: Map<string, Map<string, number>>,
|
|
265
56
|
source: string,
|
|
266
57
|
sink: string,
|
|
@@ -300,55 +91,57 @@ function findAugmentingPath(
|
|
|
300
91
|
}
|
|
301
92
|
|
|
302
93
|
/**
|
|
303
|
-
*
|
|
94
|
+
* Update flow along an augmenting path
|
|
304
95
|
*/
|
|
305
|
-
function
|
|
96
|
+
function updateFlow(
|
|
306
97
|
residualGraph: Map<string, Map<string, number>>,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const current = queue.shift();
|
|
317
|
-
if (!current) {
|
|
98
|
+
flowGraph: Map<string, Map<string, number>>,
|
|
99
|
+
originalGraph: Map<string, Map<string, number>>,
|
|
100
|
+
path: string[],
|
|
101
|
+
pathFlow: number,
|
|
102
|
+
): void {
|
|
103
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
104
|
+
const u = path[i];
|
|
105
|
+
const v = path[i + 1];
|
|
106
|
+
if (!u || !v) {
|
|
318
107
|
continue;
|
|
319
108
|
}
|
|
320
109
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// Reconstruct path
|
|
330
|
-
const path: string[] = [];
|
|
331
|
-
let node = sink;
|
|
332
|
-
while (node !== source) {
|
|
333
|
-
path.unshift(node);
|
|
334
|
-
const parentNode = parent.get(node);
|
|
335
|
-
if (!parentNode) {
|
|
336
|
-
break;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
node = parentNode;
|
|
340
|
-
}
|
|
341
|
-
path.unshift(source);
|
|
342
|
-
return path;
|
|
343
|
-
}
|
|
110
|
+
// Update residual graph
|
|
111
|
+
const uEdges = residualGraph.get(u);
|
|
112
|
+
if (uEdges) {
|
|
113
|
+
const currentCapacity = uEdges.get(v);
|
|
114
|
+
if (currentCapacity !== undefined) {
|
|
115
|
+
uEdges.set(v, currentCapacity - pathFlow);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
344
118
|
|
|
345
|
-
|
|
346
|
-
|
|
119
|
+
// Add reverse edge
|
|
120
|
+
if (!residualGraph.has(v)) {
|
|
121
|
+
residualGraph.set(v, new Map());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const vEdges = residualGraph.get(v);
|
|
125
|
+
if (vEdges) {
|
|
126
|
+
vEdges.set(u, (vEdges.get(u) ?? 0) + pathFlow);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Update flow graph
|
|
130
|
+
if (originalGraph.get(u)?.has(v)) {
|
|
131
|
+
const uFlowEdges = flowGraph.get(u);
|
|
132
|
+
if (uFlowEdges) {
|
|
133
|
+
const currentFlow = uFlowEdges.get(v) ?? 0;
|
|
134
|
+
uFlowEdges.set(v, currentFlow + pathFlow);
|
|
135
|
+
}
|
|
136
|
+
} else if (originalGraph.get(v)?.has(u)) {
|
|
137
|
+
// This is a reverse flow
|
|
138
|
+
const vFlowEdges = flowGraph.get(v);
|
|
139
|
+
if (vFlowEdges) {
|
|
140
|
+
const currentFlow = vFlowEdges.get(u) ?? 0;
|
|
141
|
+
vFlowEdges.set(u, currentFlow - pathFlow);
|
|
347
142
|
}
|
|
348
143
|
}
|
|
349
144
|
}
|
|
350
|
-
|
|
351
|
-
return null;
|
|
352
145
|
}
|
|
353
146
|
|
|
354
147
|
/**
|
|
@@ -405,6 +198,104 @@ function findMinCut(
|
|
|
405
198
|
return {source: sourceSet, sink: sinkSet, edges: cutEdges};
|
|
406
199
|
}
|
|
407
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Core max flow algorithm implementation
|
|
203
|
+
*/
|
|
204
|
+
function maxFlowCore(
|
|
205
|
+
graph: Map<string, Map<string, number>>,
|
|
206
|
+
source: string,
|
|
207
|
+
sink: string,
|
|
208
|
+
findPath: (residual: Map<string, Map<string, number>>, src: string, snk: string) => {path: string[], pathCapacity: number} | null,
|
|
209
|
+
): MaxFlowResult {
|
|
210
|
+
if (!graph.has(source) || !graph.has(sink)) {
|
|
211
|
+
return {maxFlow: 0, flowGraph: new Map()};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Create residual graph
|
|
215
|
+
const residualGraph = createResidualGraph(graph);
|
|
216
|
+
const flowGraph = new Map<string, Map<string, number>>();
|
|
217
|
+
|
|
218
|
+
// Initialize flow graph
|
|
219
|
+
for (const [u, neighbors] of graph) {
|
|
220
|
+
flowGraph.set(u, new Map());
|
|
221
|
+
for (const v of neighbors.keys()) {
|
|
222
|
+
const uFlowNeighbors = flowGraph.get(u);
|
|
223
|
+
if (uFlowNeighbors) {
|
|
224
|
+
uFlowNeighbors.set(v, 0);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let maxFlow = 0;
|
|
230
|
+
let pathResult = findPath(residualGraph, source, sink);
|
|
231
|
+
|
|
232
|
+
while (pathResult !== null) {
|
|
233
|
+
const {path, pathCapacity} = pathResult;
|
|
234
|
+
|
|
235
|
+
// Update flow
|
|
236
|
+
updateFlow(residualGraph, flowGraph, graph, path, pathCapacity);
|
|
237
|
+
maxFlow += pathCapacity;
|
|
238
|
+
|
|
239
|
+
// Find next path
|
|
240
|
+
pathResult = findPath(residualGraph, source, sink);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Find minimum cut
|
|
244
|
+
const minCut = findMinCut(residualGraph, source);
|
|
245
|
+
|
|
246
|
+
return {maxFlow, flowGraph, minCut};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Internal implementation of Ford-Fulkerson algorithm using DFS
|
|
251
|
+
*/
|
|
252
|
+
function fordFulkersonImpl(
|
|
253
|
+
graph: Map<string, Map<string, number>>,
|
|
254
|
+
source: string,
|
|
255
|
+
sink: string,
|
|
256
|
+
): MaxFlowResult {
|
|
257
|
+
// Adapter function for DFS path finding
|
|
258
|
+
const findPathDFS = (residual: Map<string, Map<string, number>>, src: string, snk: string): {path: string[], pathCapacity: number} | null => {
|
|
259
|
+
const path = findAugmentingPathDFS(residual, src, snk);
|
|
260
|
+
if (!path) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Calculate path capacity
|
|
265
|
+
let pathCapacity = Infinity;
|
|
266
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
267
|
+
const u = path[i];
|
|
268
|
+
const v = path[i + 1];
|
|
269
|
+
if (u && v) {
|
|
270
|
+
const capacity = residual.get(u)?.get(v);
|
|
271
|
+
if (capacity !== undefined) {
|
|
272
|
+
pathCapacity = Math.min(pathCapacity, capacity);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {path, pathCapacity};
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
return maxFlowCore(graph, source, sink, findPathDFS);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Internal implementation of Edmonds-Karp algorithm
|
|
285
|
+
*/
|
|
286
|
+
function edmondsKarpImpl(
|
|
287
|
+
graph: Map<string, Map<string, number>>,
|
|
288
|
+
source: string,
|
|
289
|
+
sink: string,
|
|
290
|
+
): MaxFlowResult {
|
|
291
|
+
// Use BFS variant for path finding
|
|
292
|
+
const findPathBFS = (residual: Map<string, Map<string, number>>, src: string, snk: string): {path: string[], pathCapacity: number} | null => {
|
|
293
|
+
return bfsAugmentingPath(residual, src, snk);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
return maxFlowCore(graph, source, sink, findPathBFS);
|
|
297
|
+
}
|
|
298
|
+
|
|
408
299
|
/**
|
|
409
300
|
* Utility function to create a flow network for bipartite matching
|
|
410
301
|
*/
|
|
@@ -457,3 +348,44 @@ export function createBipartiteFlowNetwork(
|
|
|
457
348
|
return {graph, source, sink};
|
|
458
349
|
}
|
|
459
350
|
|
|
351
|
+
/**
|
|
352
|
+
* Ford-Fulkerson algorithm using DFS for finding augmenting paths
|
|
353
|
+
*
|
|
354
|
+
* @param graph - Adjacency list representation with capacities - accepts Graph class or Map
|
|
355
|
+
* @param source - Source node
|
|
356
|
+
* @param sink - Sink node
|
|
357
|
+
* @returns Maximum flow value and flow graph
|
|
358
|
+
*
|
|
359
|
+
* Time Complexity: O(E * f) where f is the maximum flow
|
|
360
|
+
* Space Complexity: O(V + E)
|
|
361
|
+
*/
|
|
362
|
+
export function fordFulkerson(
|
|
363
|
+
graph: Graph,
|
|
364
|
+
source: string,
|
|
365
|
+
sink: string,
|
|
366
|
+
): MaxFlowResult {
|
|
367
|
+
// Convert Graph to Map representation
|
|
368
|
+
const graphMap = graphToMap(graph);
|
|
369
|
+
return fordFulkersonImpl(graphMap, source, sink);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Edmonds-Karp algorithm (Ford-Fulkerson with BFS)
|
|
374
|
+
* More efficient implementation with better time complexity
|
|
375
|
+
*
|
|
376
|
+
* @param graph - Adjacency list representation with capacities - accepts Graph class or Map
|
|
377
|
+
* @param source - Source node
|
|
378
|
+
* @param sink - Sink node
|
|
379
|
+
* @returns Maximum flow value and flow graph
|
|
380
|
+
*
|
|
381
|
+
* Time Complexity: O(V * E²)
|
|
382
|
+
*/
|
|
383
|
+
export function edmondsKarp(
|
|
384
|
+
graph: Graph | Map<string, Map<string, number>>,
|
|
385
|
+
source: string,
|
|
386
|
+
sink: string,
|
|
387
|
+
): MaxFlowResult {
|
|
388
|
+
// Convert Graph to Map representation if needed
|
|
389
|
+
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
390
|
+
return edmondsKarpImpl(graphMap, source, sink);
|
|
391
|
+
}
|
package/src/flow/min-cut.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
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";
|
|
7
9
|
import {fordFulkerson} from "./ford-fulkerson.js";
|
|
8
10
|
|
|
9
11
|
export interface MinCutResult {
|
|
@@ -25,7 +27,7 @@ export interface MinCutResult {
|
|
|
25
27
|
* Time Complexity: Same as max flow algorithm used
|
|
26
28
|
*/
|
|
27
29
|
export function minSTCut(
|
|
28
|
-
graph:
|
|
30
|
+
graph: Graph,
|
|
29
31
|
source: string,
|
|
30
32
|
sink: string,
|
|
31
33
|
): MinCutResult {
|
|
@@ -44,7 +46,8 @@ export function minSTCut(
|
|
|
44
46
|
|
|
45
47
|
// Find actual cut edges and their weights
|
|
46
48
|
for (const [u, v] of flowResult.minCut.edges) {
|
|
47
|
-
const
|
|
49
|
+
const edge = graph.getEdge(u, v);
|
|
50
|
+
const weight = edge?.weight ?? 0;
|
|
48
51
|
if (weight > 0) {
|
|
49
52
|
cutEdges.push({from: u, to: v, weight});
|
|
50
53
|
}
|
|
@@ -62,16 +65,20 @@ export function minSTCut(
|
|
|
62
65
|
* Stoer-Wagner algorithm for finding global minimum cut
|
|
63
66
|
* Finds the minimum cut that separates the graph into two parts
|
|
64
67
|
*
|
|
65
|
-
* @param graph - Undirected weighted graph
|
|
68
|
+
* @param graph - Undirected weighted graph - accepts Graph class or Map representation
|
|
66
69
|
* @returns Global minimum cut
|
|
67
70
|
*
|
|
68
71
|
* Time Complexity: O(V³) or O(VE + V² log V) with heap
|
|
69
72
|
*/
|
|
70
73
|
export function stoerWagner(
|
|
71
|
-
graph: Map<string, Map<string, number>>,
|
|
74
|
+
graph: Graph | Map<string, Map<string, number>>,
|
|
72
75
|
): MinCutResult {
|
|
76
|
+
// Convert Graph to Map representation if needed
|
|
77
|
+
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
73
78
|
// Convert to undirected if necessary
|
|
74
|
-
const undirectedGraph = makeUndirected(
|
|
79
|
+
const undirectedGraph = makeUndirected(graphMap);
|
|
80
|
+
// Keep a copy for finding cut edges later
|
|
81
|
+
const originalGraph = makeUndirected(graphMap);
|
|
75
82
|
|
|
76
83
|
if (undirectedGraph.size < 2) {
|
|
77
84
|
return {
|
|
@@ -84,6 +91,7 @@ export function stoerWagner(
|
|
|
84
91
|
|
|
85
92
|
// Initialize
|
|
86
93
|
const nodes = Array.from(undirectedGraph.keys());
|
|
94
|
+
const originalNodes = new Set(nodes); // Keep track of original nodes
|
|
87
95
|
let minCutValue = Infinity;
|
|
88
96
|
let bestPartition = new Set<string>();
|
|
89
97
|
const contractionMap = new Map<string, Set<string>>();
|
|
@@ -125,7 +133,7 @@ export function stoerWagner(
|
|
|
125
133
|
const partition1 = bestPartition;
|
|
126
134
|
const partition2 = new Set<string>();
|
|
127
135
|
|
|
128
|
-
for (const node of
|
|
136
|
+
for (const node of originalNodes) {
|
|
129
137
|
if (!partition1.has(node)) {
|
|
130
138
|
partition2.add(node);
|
|
131
139
|
}
|
|
@@ -134,7 +142,7 @@ export function stoerWagner(
|
|
|
134
142
|
// Find cut edges
|
|
135
143
|
const cutEdges: {from: string, to: string, weight: number}[] = [];
|
|
136
144
|
for (const u of partition1) {
|
|
137
|
-
const neighbors =
|
|
145
|
+
const neighbors = originalGraph.get(u);
|
|
138
146
|
if (neighbors) {
|
|
139
147
|
for (const [v, weight] of neighbors) {
|
|
140
148
|
if (partition2.has(v)) {
|
|
@@ -308,22 +316,24 @@ function makeUndirected(
|
|
|
308
316
|
* Karger's randomized min-cut algorithm
|
|
309
317
|
* Probabilistic algorithm that finds min cut with high probability
|
|
310
318
|
*
|
|
311
|
-
* @param graph - Undirected graph
|
|
319
|
+
* @param graph - Undirected graph - accepts Graph class or Map representation
|
|
312
320
|
* @param iterations - Number of iterations (higher = better accuracy)
|
|
313
321
|
* @returns Minimum cut found
|
|
314
322
|
*
|
|
315
323
|
* Time Complexity: O(V² * iterations)
|
|
316
324
|
*/
|
|
317
325
|
export function kargerMinCut(
|
|
318
|
-
graph: Map<string, Map<string, number>>,
|
|
326
|
+
graph: Graph | Map<string, Map<string, number>>,
|
|
319
327
|
iterations = 100,
|
|
320
328
|
): MinCutResult {
|
|
329
|
+
// Convert Graph to Map representation if needed
|
|
330
|
+
const graphMap = graph instanceof Map ? graph : graphToMap(graph);
|
|
321
331
|
let minCutValue = Infinity;
|
|
322
332
|
let bestPartition1 = new Set<string>();
|
|
323
333
|
let bestPartition2 = new Set<string>();
|
|
324
334
|
|
|
325
335
|
for (let i = 0; i < iterations; i++) {
|
|
326
|
-
const result = kargerSingleRun(
|
|
336
|
+
const result = kargerSingleRun(graphMap);
|
|
327
337
|
|
|
328
338
|
if (result.cutValue < minCutValue) {
|
|
329
339
|
minCutValue = result.cutValue;
|
|
@@ -335,7 +345,7 @@ export function kargerMinCut(
|
|
|
335
345
|
// Find cut edges
|
|
336
346
|
const cutEdges: {from: string, to: string, weight: number}[] = [];
|
|
337
347
|
for (const u of bestPartition1) {
|
|
338
|
-
const neighbors =
|
|
348
|
+
const neighbors = graphMap.get(u);
|
|
339
349
|
if (neighbors) {
|
|
340
350
|
for (const [v, weight] of neighbors) {
|
|
341
351
|
if (bestPartition2.has(v)) {
|
package/src/index.ts
CHANGED
|
@@ -38,3 +38,9 @@ export * from "./research/index.js";
|
|
|
38
38
|
|
|
39
39
|
// Data structure exports
|
|
40
40
|
export * from "./data-structures/index.js";
|
|
41
|
+
|
|
42
|
+
// Optimized algorithm exports
|
|
43
|
+
export * from "./optimized/index.js";
|
|
44
|
+
|
|
45
|
+
// Note: Configuration exports have been removed.
|
|
46
|
+
// The library now automatically optimizes based on graph size.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {Graph} from "../core/graph.js";
|
|
2
2
|
import type {NodeId} from "../types/index.js";
|
|
3
|
+
import {getCommonNeighbors, getIntermediateNodes} from "../utils/graph-utilities.js";
|
|
3
4
|
import type {LinkPredictionOptions, LinkPredictionScore} from "./common-neighbors.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -31,28 +32,24 @@ export function adamicAdarScore(
|
|
|
31
32
|
|
|
32
33
|
const {directed = false} = options;
|
|
33
34
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
directed ? Array.from(graph.inNeighbors(target)) : Array.from(graph.neighbors(target)),
|
|
40
|
-
);
|
|
35
|
+
// Use utility function to get common neighbors
|
|
36
|
+
// 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);
|
|
41
40
|
|
|
42
41
|
// Calculate Adamic-Adar score
|
|
43
42
|
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
|
-
}
|
|
43
|
+
for (const neighbor of commonNeighborsSet) {
|
|
44
|
+
const degree = directed ?
|
|
45
|
+
graph.outDegree(neighbor) : // Use out-degree for directed graphs
|
|
46
|
+
graph.degree(neighbor); // Use total degree for undirected graphs
|
|
47
|
+
|
|
48
|
+
if (degree > 1) {
|
|
49
|
+
score += 1 / Math.log(degree);
|
|
50
|
+
} else if (degree === 1) {
|
|
51
|
+
// For degree 1, we can't use log(1) = 0, so use a small constant
|
|
52
|
+
score += 1; // or some other reasonable value
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
|
|
@@ -275,17 +272,7 @@ export function compareAdamicAdarWithCommonNeighbors(
|
|
|
275
272
|
pairs.map(([source, target]) => ({
|
|
276
273
|
source,
|
|
277
274
|
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
|
-
})(),
|
|
275
|
+
score: getCommonNeighbors(graph, source, target, options.directed).size,
|
|
289
276
|
}));
|
|
290
277
|
|
|
291
278
|
const testScores = commonNeighborsPairs(testEdges);
|