graphwise 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -30
- package/dist/async/index.cjs +243 -0
- package/dist/async/index.cjs.map +1 -0
- package/dist/async/index.js +230 -0
- package/dist/async/index.js.map +1 -0
- package/dist/expansion/base-core.d.ts +24 -0
- package/dist/expansion/base-core.d.ts.map +1 -0
- package/dist/expansion/base-core.unit.test.d.ts +10 -0
- package/dist/expansion/base-core.unit.test.d.ts.map +1 -0
- package/dist/expansion/base-helpers.d.ts +57 -0
- package/dist/expansion/base-helpers.d.ts.map +1 -0
- package/dist/expansion/base.d.ts +32 -1
- package/dist/expansion/base.d.ts.map +1 -1
- package/dist/expansion/dfs-priority.d.ts +11 -0
- package/dist/expansion/dfs-priority.d.ts.map +1 -1
- package/dist/expansion/dome.d.ts +20 -0
- package/dist/expansion/dome.d.ts.map +1 -1
- package/dist/expansion/edge.d.ts +18 -0
- package/dist/expansion/edge.d.ts.map +1 -1
- package/dist/expansion/flux.d.ts +16 -0
- package/dist/expansion/flux.d.ts.map +1 -1
- package/dist/expansion/frontier-balanced.d.ts +11 -0
- package/dist/expansion/frontier-balanced.d.ts.map +1 -1
- package/dist/expansion/fuse.d.ts +16 -0
- package/dist/expansion/fuse.d.ts.map +1 -1
- package/dist/expansion/hae.d.ts +16 -0
- package/dist/expansion/hae.d.ts.map +1 -1
- package/dist/expansion/lace.d.ts +16 -0
- package/dist/expansion/lace.d.ts.map +1 -1
- package/dist/expansion/maze.d.ts +17 -0
- package/dist/expansion/maze.d.ts.map +1 -1
- package/dist/expansion/pipe.d.ts +16 -0
- package/dist/expansion/pipe.d.ts.map +1 -1
- package/dist/expansion/random-priority.d.ts +18 -0
- package/dist/expansion/random-priority.d.ts.map +1 -1
- package/dist/expansion/reach.d.ts +17 -0
- package/dist/expansion/reach.d.ts.map +1 -1
- package/dist/expansion/sage.d.ts +15 -0
- package/dist/expansion/sage.d.ts.map +1 -1
- package/dist/expansion/sift.d.ts +16 -0
- package/dist/expansion/sift.d.ts.map +1 -1
- package/dist/expansion/standard-bfs.d.ts +11 -0
- package/dist/expansion/standard-bfs.d.ts.map +1 -1
- package/dist/expansion/tide.d.ts +16 -0
- package/dist/expansion/tide.d.ts.map +1 -1
- package/dist/expansion/warp.d.ts +16 -0
- package/dist/expansion/warp.d.ts.map +1 -1
- package/dist/index/index.cjs +1060 -99
- package/dist/index/index.cjs.map +1 -1
- package/dist/index/index.js +1015 -100
- package/dist/index/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/ranking/mi/adamic-adar.d.ts +8 -0
- package/dist/ranking/mi/adamic-adar.d.ts.map +1 -1
- package/dist/ranking/mi/adaptive.d.ts +8 -0
- package/dist/ranking/mi/adaptive.d.ts.map +1 -1
- package/dist/ranking/mi/cosine.d.ts +7 -0
- package/dist/ranking/mi/cosine.d.ts.map +1 -1
- package/dist/ranking/mi/etch.d.ts +8 -0
- package/dist/ranking/mi/etch.d.ts.map +1 -1
- package/dist/ranking/mi/hub-promoted.d.ts +7 -0
- package/dist/ranking/mi/hub-promoted.d.ts.map +1 -1
- package/dist/ranking/mi/jaccard.d.ts +7 -0
- package/dist/ranking/mi/jaccard.d.ts.map +1 -1
- package/dist/ranking/mi/notch.d.ts +8 -0
- package/dist/ranking/mi/notch.d.ts.map +1 -1
- package/dist/ranking/mi/overlap-coefficient.d.ts +7 -0
- package/dist/ranking/mi/overlap-coefficient.d.ts.map +1 -1
- package/dist/ranking/mi/resource-allocation.d.ts +8 -0
- package/dist/ranking/mi/resource-allocation.d.ts.map +1 -1
- package/dist/ranking/mi/scale.d.ts +7 -0
- package/dist/ranking/mi/scale.d.ts.map +1 -1
- package/dist/ranking/mi/skew.d.ts +7 -0
- package/dist/ranking/mi/skew.d.ts.map +1 -1
- package/dist/ranking/mi/sorensen.d.ts +7 -0
- package/dist/ranking/mi/sorensen.d.ts.map +1 -1
- package/dist/ranking/mi/span.d.ts +8 -0
- package/dist/ranking/mi/span.d.ts.map +1 -1
- package/dist/ranking/mi/types.d.ts +12 -0
- package/dist/ranking/mi/types.d.ts.map +1 -1
- package/dist/ranking/parse.d.ts +24 -1
- package/dist/ranking/parse.d.ts.map +1 -1
- package/package.json +6 -1
package/dist/index/index.cjs
CHANGED
|
@@ -6,22 +6,134 @@ const require_kmeans = require("../kmeans-BIgSyGKu.cjs");
|
|
|
6
6
|
const require_utils = require("../utils/index.cjs");
|
|
7
7
|
const require_seeds = require("../seeds/index.cjs");
|
|
8
8
|
const require_gpu = require("../gpu/index.cjs");
|
|
9
|
-
|
|
9
|
+
const require_async = require("../async/index.cjs");
|
|
10
|
+
//#region src/expansion/base-helpers.ts
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Check whether expansion should continue given current progress.
|
|
13
|
+
*
|
|
14
|
+
* Returns shouldContinue=false as soon as any configured limit is reached,
|
|
15
|
+
* along with the appropriate termination reason.
|
|
16
|
+
*
|
|
17
|
+
* @param iterations - Number of iterations completed so far
|
|
18
|
+
* @param nodesVisited - Number of distinct nodes visited so far
|
|
19
|
+
* @param pathsFound - Number of paths discovered so far
|
|
20
|
+
* @param limits - Configured expansion limits (0 = unlimited)
|
|
21
|
+
* @returns Whether to continue and the termination reason if stopping
|
|
22
|
+
*/
|
|
23
|
+
function continueExpansion(iterations, nodesVisited, pathsFound, limits) {
|
|
24
|
+
if (limits.maxIterations > 0 && iterations >= limits.maxIterations) return {
|
|
25
|
+
shouldContinue: false,
|
|
26
|
+
termination: "limit"
|
|
27
|
+
};
|
|
28
|
+
if (limits.maxNodes > 0 && nodesVisited >= limits.maxNodes) return {
|
|
29
|
+
shouldContinue: false,
|
|
30
|
+
termination: "limit"
|
|
31
|
+
};
|
|
32
|
+
if (limits.maxPaths > 0 && pathsFound >= limits.maxPaths) return {
|
|
33
|
+
shouldContinue: false,
|
|
34
|
+
termination: "limit"
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
shouldContinue: true,
|
|
38
|
+
termination: "exhausted"
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reconstruct path from collision point.
|
|
43
|
+
*
|
|
44
|
+
* Traces backwards through the predecessor maps of both frontiers from the
|
|
45
|
+
* collision node, then concatenates the two halves to form the full path.
|
|
46
|
+
*
|
|
47
|
+
* @param collisionNode - The node where the two frontiers met
|
|
48
|
+
* @param frontierA - Index of the first frontier
|
|
49
|
+
* @param frontierB - Index of the second frontier
|
|
50
|
+
* @param predecessors - Predecessor maps, one per frontier
|
|
51
|
+
* @param seeds - Seed nodes, one per frontier
|
|
52
|
+
* @returns The reconstructed path, or null if seeds are missing
|
|
53
|
+
*/
|
|
54
|
+
function reconstructPath$1(collisionNode, frontierA, frontierB, predecessors, seeds) {
|
|
55
|
+
const pathA = [collisionNode];
|
|
56
|
+
const predA = predecessors[frontierA];
|
|
57
|
+
if (predA !== void 0) {
|
|
58
|
+
let node = collisionNode;
|
|
59
|
+
let next = predA.get(node);
|
|
60
|
+
while (next !== null && next !== void 0) {
|
|
61
|
+
node = next;
|
|
62
|
+
pathA.unshift(node);
|
|
63
|
+
next = predA.get(node);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const pathB = [];
|
|
67
|
+
const predB = predecessors[frontierB];
|
|
68
|
+
if (predB !== void 0) {
|
|
69
|
+
let node = collisionNode;
|
|
70
|
+
let next = predB.get(node);
|
|
71
|
+
while (next !== null && next !== void 0) {
|
|
72
|
+
node = next;
|
|
73
|
+
pathB.push(node);
|
|
74
|
+
next = predB.get(node);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const fullPath = [...pathA, ...pathB];
|
|
78
|
+
const seedA = seeds[frontierA];
|
|
79
|
+
const seedB = seeds[frontierB];
|
|
80
|
+
if (seedA === void 0 || seedB === void 0) return null;
|
|
81
|
+
return {
|
|
82
|
+
fromSeed: seedA,
|
|
83
|
+
toSeed: seedB,
|
|
84
|
+
nodes: fullPath
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create an empty expansion result for early termination (e.g. no seeds given).
|
|
89
|
+
*
|
|
90
|
+
* @param algorithm - Name of the algorithm producing this result
|
|
91
|
+
* @param startTime - performance.now() timestamp taken before the algorithm began
|
|
92
|
+
* @returns An ExpansionResult with zero paths and zero stats
|
|
93
|
+
*/
|
|
94
|
+
function emptyResult$2(algorithm, startTime) {
|
|
95
|
+
return {
|
|
96
|
+
paths: [],
|
|
97
|
+
sampledNodes: /* @__PURE__ */ new Set(),
|
|
98
|
+
sampledEdges: /* @__PURE__ */ new Set(),
|
|
99
|
+
visitedPerFrontier: [],
|
|
100
|
+
stats: {
|
|
101
|
+
iterations: 0,
|
|
102
|
+
nodesVisited: 0,
|
|
103
|
+
edgesTraversed: 0,
|
|
104
|
+
pathsFound: 0,
|
|
105
|
+
durationMs: performance.now() - startTime,
|
|
106
|
+
algorithm,
|
|
107
|
+
termination: "exhausted"
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/expansion/base-core.ts
|
|
113
|
+
/**
|
|
114
|
+
* Default priority function — degree-ordered (DOME).
|
|
115
|
+
*
|
|
116
|
+
* Lower degree = higher priority, so sparse nodes are explored before hubs.
|
|
12
117
|
*/
|
|
13
118
|
function degreePriority(_nodeId, context) {
|
|
14
119
|
return context.degree;
|
|
15
120
|
}
|
|
16
121
|
/**
|
|
17
|
-
*
|
|
122
|
+
* Generator core of the BASE expansion algorithm.
|
|
18
123
|
*
|
|
19
|
-
*
|
|
124
|
+
* Yields GraphOp objects to request graph data, allowing the caller to
|
|
125
|
+
* provide a sync or async runner. The optional `graphRef` parameter is
|
|
126
|
+
* required when the priority function accesses `context.graph` — it is
|
|
127
|
+
* populated in sync mode by `base()`. In async mode (Phase 4+), a proxy
|
|
128
|
+
* graph may be supplied instead.
|
|
129
|
+
*
|
|
130
|
+
* @param graphMeta - Immutable graph metadata (directed, nodeCount, edgeCount)
|
|
20
131
|
* @param seeds - Seed nodes for expansion
|
|
21
|
-
* @param config - Expansion configuration
|
|
22
|
-
* @
|
|
132
|
+
* @param config - Expansion configuration (priority, limits, debug)
|
|
133
|
+
* @param graphRef - Optional real graph reference for context.graph in priority functions
|
|
134
|
+
* @returns An ExpansionResult with all discovered paths and statistics
|
|
23
135
|
*/
|
|
24
|
-
function
|
|
136
|
+
function* baseCore(graphMeta, seeds, config, graphRef) {
|
|
25
137
|
const startTime = performance.now();
|
|
26
138
|
const { maxNodes = 0, maxIterations = 0, maxPaths = 0, priority = degreePriority, debug = false } = config ?? {};
|
|
27
139
|
if (seeds.length === 0) return emptyResult$2("base", startTime);
|
|
@@ -41,7 +153,8 @@ function base(graph, seeds, config) {
|
|
|
41
153
|
predecessors[i]?.set(seedNode, null);
|
|
42
154
|
combinedVisited.set(seedNode, i);
|
|
43
155
|
allVisited.add(seedNode);
|
|
44
|
-
const
|
|
156
|
+
const seedDegree = yield* require_async.opDegree(seedNode);
|
|
157
|
+
const seedPriority = priority(seedNode, buildPriorityContext(seedNode, i, combinedVisited, allVisited, [], 0, seedDegree, graphRef));
|
|
45
158
|
queues[i]?.push({
|
|
46
159
|
nodeId: seedNode,
|
|
47
160
|
frontierIndex: i,
|
|
@@ -53,22 +166,17 @@ function base(graph, seeds, config) {
|
|
|
53
166
|
let iterations = 0;
|
|
54
167
|
let edgesTraversed = 0;
|
|
55
168
|
let termination = "exhausted";
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
if (maxNodes > 0 && allVisited.size >= maxNodes) {
|
|
62
|
-
termination = "limit";
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
if (maxPaths > 0 && discoveredPaths.length >= maxPaths) {
|
|
66
|
-
termination = "limit";
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
169
|
+
const limits = {
|
|
170
|
+
maxIterations,
|
|
171
|
+
maxNodes,
|
|
172
|
+
maxPaths
|
|
70
173
|
};
|
|
71
|
-
|
|
174
|
+
for (;;) {
|
|
175
|
+
const check = continueExpansion(iterations, allVisited.size, discoveredPaths.length, limits);
|
|
176
|
+
if (!check.shouldContinue) {
|
|
177
|
+
termination = check.termination;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
72
180
|
let lowestPriority = Number.POSITIVE_INFINITY;
|
|
73
181
|
let activeFrontier = -1;
|
|
74
182
|
for (let i = 0; i < numFrontiers; i++) {
|
|
@@ -112,7 +220,7 @@ function base(graph, seeds, config) {
|
|
|
112
220
|
}
|
|
113
221
|
}
|
|
114
222
|
}
|
|
115
|
-
const neighbours =
|
|
223
|
+
const neighbours = yield* require_async.opNeighbours(nodeId);
|
|
116
224
|
for (const neighbour of neighbours) {
|
|
117
225
|
edgesTraversed++;
|
|
118
226
|
const [s, t] = nodeId < neighbour ? [nodeId, neighbour] : [neighbour, nodeId];
|
|
@@ -122,9 +230,10 @@ function base(graph, seeds, config) {
|
|
|
122
230
|
sampledEdgeMap.set(s, targets);
|
|
123
231
|
}
|
|
124
232
|
targets.add(t);
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
const
|
|
233
|
+
const fv = visitedByFrontier[activeFrontier];
|
|
234
|
+
if (fv === void 0 || fv.has(neighbour)) continue;
|
|
235
|
+
const neighbourDegree = yield* require_async.opDegree(neighbour);
|
|
236
|
+
const neighbourPriority = priority(neighbour, buildPriorityContext(neighbour, activeFrontier, combinedVisited, allVisited, discoveredPaths, iterations + 1, neighbourDegree, graphRef));
|
|
128
237
|
queue.push({
|
|
129
238
|
nodeId: neighbour,
|
|
130
239
|
frontierIndex: activeFrontier,
|
|
@@ -154,12 +263,50 @@ function base(graph, seeds, config) {
|
|
|
154
263
|
};
|
|
155
264
|
}
|
|
156
265
|
/**
|
|
157
|
-
* Create
|
|
266
|
+
* Create a sentinel ReadableGraph that throws if any member is accessed.
|
|
267
|
+
*
|
|
268
|
+
* Used in async mode when no graphRef is provided. Gives a clear error
|
|
269
|
+
* message rather than silently returning incorrect results if a priority
|
|
270
|
+
* function attempts to access `context.graph` before Phase 4b introduces
|
|
271
|
+
* a real async proxy.
|
|
158
272
|
*/
|
|
159
|
-
function
|
|
273
|
+
function makeNoGraphSentinel() {
|
|
274
|
+
const msg = "Priority function accessed context.graph in async mode without a graph proxy. Pass a graphRef or use a priority function that does not access context.graph.";
|
|
275
|
+
const fail = () => {
|
|
276
|
+
throw new Error(msg);
|
|
277
|
+
};
|
|
160
278
|
return {
|
|
161
|
-
|
|
162
|
-
|
|
279
|
+
get directed() {
|
|
280
|
+
return fail();
|
|
281
|
+
},
|
|
282
|
+
get nodeCount() {
|
|
283
|
+
return fail();
|
|
284
|
+
},
|
|
285
|
+
get edgeCount() {
|
|
286
|
+
return fail();
|
|
287
|
+
},
|
|
288
|
+
hasNode: fail,
|
|
289
|
+
getNode: fail,
|
|
290
|
+
nodeIds: fail,
|
|
291
|
+
neighbours: fail,
|
|
292
|
+
degree: fail,
|
|
293
|
+
getEdge: fail,
|
|
294
|
+
edges: fail
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Build a PriorityContext for a node using a pre-fetched degree.
|
|
299
|
+
*
|
|
300
|
+
* When `graphRef` is provided (sync mode), it is used as `context.graph` so
|
|
301
|
+
* priority functions can access the graph directly. When it is absent (async
|
|
302
|
+
* mode), a Proxy is used in its place that throws a clear error if any
|
|
303
|
+
* property is accessed — this prevents silent failures until Phase 4b
|
|
304
|
+
* introduces a real async proxy graph.
|
|
305
|
+
*/
|
|
306
|
+
function buildPriorityContext(_nodeId, frontierIndex, combinedVisited, allVisited, discoveredPaths, iteration, degree, graphRef) {
|
|
307
|
+
return {
|
|
308
|
+
graph: graphRef ?? makeNoGraphSentinel(),
|
|
309
|
+
degree,
|
|
163
310
|
frontierIndex,
|
|
164
311
|
visitedByFrontier: combinedVisited,
|
|
165
312
|
allVisited,
|
|
@@ -167,65 +314,71 @@ function createPriorityContext(graph, nodeId, frontierIndex, combinedVisited, al
|
|
|
167
314
|
iteration
|
|
168
315
|
};
|
|
169
316
|
}
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/expansion/base.ts
|
|
170
319
|
/**
|
|
171
|
-
*
|
|
320
|
+
* Run BASE expansion synchronously.
|
|
321
|
+
*
|
|
322
|
+
* Delegates to baseCore + runSync. Behaviour is identical to the previous
|
|
323
|
+
* direct implementation — all existing callers are unaffected.
|
|
324
|
+
*
|
|
325
|
+
* @param graph - Source graph
|
|
326
|
+
* @param seeds - Seed nodes for expansion
|
|
327
|
+
* @param config - Expansion configuration
|
|
328
|
+
* @returns Expansion result with discovered paths
|
|
172
329
|
*/
|
|
173
|
-
function
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
while (next !== null && next !== void 0) {
|
|
180
|
-
node = next;
|
|
181
|
-
pathA.unshift(node);
|
|
182
|
-
next = predA.get(node);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const pathB = [];
|
|
186
|
-
const predB = predecessors[frontierB];
|
|
187
|
-
if (predB !== void 0) {
|
|
188
|
-
let node = collisionNode;
|
|
189
|
-
let next = predB.get(node);
|
|
190
|
-
while (next !== null && next !== void 0) {
|
|
191
|
-
node = next;
|
|
192
|
-
pathB.push(node);
|
|
193
|
-
next = predB.get(node);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const fullPath = [...pathA, ...pathB];
|
|
197
|
-
const seedA = seeds[frontierA];
|
|
198
|
-
const seedB = seeds[frontierB];
|
|
199
|
-
if (seedA === void 0 || seedB === void 0) return null;
|
|
200
|
-
return {
|
|
201
|
-
fromSeed: seedA,
|
|
202
|
-
toSeed: seedB,
|
|
203
|
-
nodes: fullPath
|
|
204
|
-
};
|
|
330
|
+
function base(graph, seeds, config) {
|
|
331
|
+
return require_async.runSync(baseCore({
|
|
332
|
+
directed: graph.directed,
|
|
333
|
+
nodeCount: graph.nodeCount,
|
|
334
|
+
edgeCount: graph.edgeCount
|
|
335
|
+
}, seeds, config, graph), graph);
|
|
205
336
|
}
|
|
206
337
|
/**
|
|
207
|
-
*
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
338
|
+
* Run BASE expansion asynchronously.
|
|
339
|
+
*
|
|
340
|
+
* Delegates to baseCore + runAsync. Supports:
|
|
341
|
+
* - Cancellation via AbortSignal (config.signal)
|
|
342
|
+
* - Progress callbacks (config.onProgress)
|
|
343
|
+
* - Custom cooperative yield strategies (config.yieldStrategy)
|
|
344
|
+
*
|
|
345
|
+
* Note: priority functions that access `context.graph` are not supported in
|
|
346
|
+
* async mode without a graph proxy (Phase 4b). The default degree-based
|
|
347
|
+
* priority (DOME) does not access context.graph and works correctly.
|
|
348
|
+
*
|
|
349
|
+
* @param graph - Async source graph
|
|
350
|
+
* @param seeds - Seed nodes for expansion
|
|
351
|
+
* @param config - Expansion and async runner configuration
|
|
352
|
+
* @returns Promise resolving to the expansion result
|
|
353
|
+
*/
|
|
354
|
+
async function baseAsync(graph, seeds, config) {
|
|
355
|
+
const [nodeCount, edgeCount] = await Promise.all([graph.nodeCount, graph.edgeCount]);
|
|
356
|
+
const gen = baseCore({
|
|
357
|
+
directed: graph.directed,
|
|
358
|
+
nodeCount,
|
|
359
|
+
edgeCount
|
|
360
|
+
}, seeds, config);
|
|
361
|
+
const runnerOptions = {};
|
|
362
|
+
if (config?.signal !== void 0) runnerOptions.signal = config.signal;
|
|
363
|
+
if (config?.onProgress !== void 0) runnerOptions.onProgress = config.onProgress;
|
|
364
|
+
if (config?.yieldStrategy !== void 0) runnerOptions.yieldStrategy = config.yieldStrategy;
|
|
365
|
+
return require_async.runAsync(gen, graph, runnerOptions);
|
|
225
366
|
}
|
|
226
367
|
//#endregion
|
|
227
368
|
//#region src/expansion/dome.ts
|
|
228
369
|
/**
|
|
370
|
+
* DOME priority: lower degree is expanded first.
|
|
371
|
+
*/
|
|
372
|
+
function domePriority(_nodeId, context) {
|
|
373
|
+
return context.degree;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* DOME high-degree priority: negate degree to prioritise high-degree nodes.
|
|
377
|
+
*/
|
|
378
|
+
function domeHighDegreePriority(_nodeId, context) {
|
|
379
|
+
return -context.degree;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
229
382
|
* Run DOME expansion (degree-ordered).
|
|
230
383
|
*
|
|
231
384
|
* @param graph - Source graph
|
|
@@ -234,24 +387,46 @@ function emptyResult$2(algorithm, startTime) {
|
|
|
234
387
|
* @returns Expansion result with discovered paths
|
|
235
388
|
*/
|
|
236
389
|
function dome(graph, seeds, config) {
|
|
237
|
-
const domePriority = (nodeId, context) => {
|
|
238
|
-
return context.degree;
|
|
239
|
-
};
|
|
240
390
|
return base(graph, seeds, {
|
|
241
391
|
...config,
|
|
242
392
|
priority: domePriority
|
|
243
393
|
});
|
|
244
394
|
}
|
|
245
395
|
/**
|
|
396
|
+
* Run DOME expansion asynchronously (degree-ordered).
|
|
397
|
+
*
|
|
398
|
+
* @param graph - Async source graph
|
|
399
|
+
* @param seeds - Seed nodes for expansion
|
|
400
|
+
* @param config - Expansion and async runner configuration
|
|
401
|
+
* @returns Promise resolving to the expansion result
|
|
402
|
+
*/
|
|
403
|
+
async function domeAsync(graph, seeds, config) {
|
|
404
|
+
return baseAsync(graph, seeds, {
|
|
405
|
+
...config,
|
|
406
|
+
priority: domePriority
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
246
410
|
* DOME with reverse priority (high degree first).
|
|
247
411
|
*/
|
|
248
412
|
function domeHighDegree(graph, seeds, config) {
|
|
249
|
-
const domePriority = (nodeId, context) => {
|
|
250
|
-
return -context.degree;
|
|
251
|
-
};
|
|
252
413
|
return base(graph, seeds, {
|
|
253
414
|
...config,
|
|
254
|
-
priority:
|
|
415
|
+
priority: domeHighDegreePriority
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Run DOME high-degree expansion asynchronously (high degree first).
|
|
420
|
+
*
|
|
421
|
+
* @param graph - Async source graph
|
|
422
|
+
* @param seeds - Seed nodes for expansion
|
|
423
|
+
* @param config - Expansion and async runner configuration
|
|
424
|
+
* @returns Promise resolving to the expansion result
|
|
425
|
+
*/
|
|
426
|
+
async function domeHighDegreeAsync(graph, seeds, config) {
|
|
427
|
+
return baseAsync(graph, seeds, {
|
|
428
|
+
...config,
|
|
429
|
+
priority: domeHighDegreePriority
|
|
255
430
|
});
|
|
256
431
|
}
|
|
257
432
|
//#endregion
|
|
@@ -296,6 +471,26 @@ function hae(graph, seeds, config) {
|
|
|
296
471
|
priority: createHAEPriority(typeMapper)
|
|
297
472
|
});
|
|
298
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Run HAE expansion asynchronously.
|
|
476
|
+
*
|
|
477
|
+
* Note: the HAE priority function accesses `context.graph` to retrieve
|
|
478
|
+
* neighbour types. Full async equivalence requires PriorityContext
|
|
479
|
+
* refactoring (Phase 4b deferred). This export establishes the async API
|
|
480
|
+
* surface; use with a `wrapAsync`-wrapped sync graph for testing.
|
|
481
|
+
*
|
|
482
|
+
* @param graph - Async source graph
|
|
483
|
+
* @param seeds - Seed nodes for expansion
|
|
484
|
+
* @param config - HAE configuration combined with async runner options
|
|
485
|
+
* @returns Promise resolving to the expansion result
|
|
486
|
+
*/
|
|
487
|
+
async function haeAsync(graph, seeds, config) {
|
|
488
|
+
const typeMapper = config?.typeMapper ?? defaultTypeMapper$1;
|
|
489
|
+
return baseAsync(graph, seeds, {
|
|
490
|
+
...config,
|
|
491
|
+
priority: createHAEPriority(typeMapper)
|
|
492
|
+
});
|
|
493
|
+
}
|
|
299
494
|
//#endregion
|
|
300
495
|
//#region src/expansion/edge.ts
|
|
301
496
|
/** Default type mapper: reads `node.type`, falling back to "default". */
|
|
@@ -317,6 +512,27 @@ function edge(graph, seeds, config) {
|
|
|
317
512
|
typeMapper: defaultTypeMapper
|
|
318
513
|
});
|
|
319
514
|
}
|
|
515
|
+
/**
|
|
516
|
+
* Run EDGE expansion asynchronously.
|
|
517
|
+
*
|
|
518
|
+
* Delegates to `haeAsync` with the default `node.type` mapper.
|
|
519
|
+
*
|
|
520
|
+
* Note: the HAE priority function accesses `context.graph` to retrieve
|
|
521
|
+
* neighbour types. Full async equivalence requires PriorityContext
|
|
522
|
+
* refactoring (Phase 4b deferred). This export establishes the async API
|
|
523
|
+
* surface; use with a `wrapAsync`-wrapped sync graph for testing.
|
|
524
|
+
*
|
|
525
|
+
* @param graph - Async source graph
|
|
526
|
+
* @param seeds - Seed nodes for expansion
|
|
527
|
+
* @param config - Expansion and async runner configuration
|
|
528
|
+
* @returns Promise resolving to the expansion result
|
|
529
|
+
*/
|
|
530
|
+
async function edgeAsync(graph, seeds, config) {
|
|
531
|
+
return haeAsync(graph, seeds, {
|
|
532
|
+
...config,
|
|
533
|
+
typeMapper: defaultTypeMapper
|
|
534
|
+
});
|
|
535
|
+
}
|
|
320
536
|
//#endregion
|
|
321
537
|
//#region src/expansion/pipe.ts
|
|
322
538
|
/**
|
|
@@ -352,6 +568,25 @@ function pipe(graph, seeds, config) {
|
|
|
352
568
|
priority: pipePriority
|
|
353
569
|
});
|
|
354
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* Run PIPE expansion asynchronously.
|
|
573
|
+
*
|
|
574
|
+
* Note: the PIPE priority function accesses `context.graph` to retrieve
|
|
575
|
+
* neighbour lists. Full async equivalence requires PriorityContext
|
|
576
|
+
* refactoring (Phase 4b deferred). This export establishes the async API
|
|
577
|
+
* surface; use with a `wrapAsync`-wrapped sync graph for testing.
|
|
578
|
+
*
|
|
579
|
+
* @param graph - Async source graph
|
|
580
|
+
* @param seeds - Seed nodes for expansion
|
|
581
|
+
* @param config - Expansion and async runner configuration
|
|
582
|
+
* @returns Promise resolving to the expansion result
|
|
583
|
+
*/
|
|
584
|
+
async function pipeAsync(graph, seeds, config) {
|
|
585
|
+
return baseAsync(graph, seeds, {
|
|
586
|
+
...config,
|
|
587
|
+
priority: pipePriority
|
|
588
|
+
});
|
|
589
|
+
}
|
|
355
590
|
//#endregion
|
|
356
591
|
//#region src/expansion/priority-helpers.ts
|
|
357
592
|
/**
|
|
@@ -448,6 +683,34 @@ function sage(graph, seeds, config) {
|
|
|
448
683
|
priority: sagePriority
|
|
449
684
|
});
|
|
450
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* Run SAGE expansion asynchronously.
|
|
688
|
+
*
|
|
689
|
+
* Creates fresh closure state (salienceCounts, phase tracking) for this
|
|
690
|
+
* invocation. The SAGE priority function does not access `context.graph`
|
|
691
|
+
* directly, so it is safe to use in async mode via `baseAsync`.
|
|
692
|
+
*
|
|
693
|
+
* @param graph - Async source graph
|
|
694
|
+
* @param seeds - Seed nodes for expansion
|
|
695
|
+
* @param config - Expansion and async runner configuration
|
|
696
|
+
* @returns Promise resolving to the expansion result
|
|
697
|
+
*/
|
|
698
|
+
async function sageAsync(graph, seeds, config) {
|
|
699
|
+
const salienceCounts = /* @__PURE__ */ new Map();
|
|
700
|
+
let inPhase2 = false;
|
|
701
|
+
let lastPathCount = 0;
|
|
702
|
+
function sagePriority(nodeId, context) {
|
|
703
|
+
const pathCount = context.discoveredPaths.length;
|
|
704
|
+
if (pathCount > 0 && !inPhase2) inPhase2 = true;
|
|
705
|
+
if (pathCount > lastPathCount) lastPathCount = updateSalienceCounts(salienceCounts, context.discoveredPaths, lastPathCount);
|
|
706
|
+
if (!inPhase2) return Math.log(context.degree + 1);
|
|
707
|
+
return -((salienceCounts.get(nodeId) ?? 0) * 1e3 - context.degree);
|
|
708
|
+
}
|
|
709
|
+
return baseAsync(graph, seeds, {
|
|
710
|
+
...config,
|
|
711
|
+
priority: sagePriority
|
|
712
|
+
});
|
|
713
|
+
}
|
|
451
714
|
//#endregion
|
|
452
715
|
//#region src/ranking/mi/jaccard.ts
|
|
453
716
|
/**
|
|
@@ -465,6 +728,23 @@ function jaccard(graph, source, target, config) {
|
|
|
465
728
|
if (sourceNeighbours.size === 0 && targetNeighbours.size === 0) return 0;
|
|
466
729
|
return Math.max(epsilon, jaccardScore);
|
|
467
730
|
}
|
|
731
|
+
/**
|
|
732
|
+
* Async variant of Jaccard similarity for use with async graph data sources.
|
|
733
|
+
*
|
|
734
|
+
* Fetches both neighbourhoods concurrently, then applies the same formula.
|
|
735
|
+
*/
|
|
736
|
+
async function jaccardAsync(graph, source, target, config) {
|
|
737
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
738
|
+
const [sourceNeighboursArr, targetNeighboursArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
739
|
+
const srcSet = new Set(sourceNeighboursArr.filter((n) => n !== target));
|
|
740
|
+
const tgtSet = new Set(targetNeighboursArr.filter((n) => n !== source));
|
|
741
|
+
if (srcSet.size === 0 && tgtSet.size === 0) return 0;
|
|
742
|
+
let intersection = 0;
|
|
743
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
744
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
745
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
746
|
+
return Math.max(epsilon, jaccardScore);
|
|
747
|
+
}
|
|
468
748
|
//#endregion
|
|
469
749
|
//#region src/expansion/reach.ts
|
|
470
750
|
/**
|
|
@@ -521,6 +801,40 @@ function reach(graph, seeds, config) {
|
|
|
521
801
|
priority: reachPriority
|
|
522
802
|
});
|
|
523
803
|
}
|
|
804
|
+
/**
|
|
805
|
+
* Run REACH expansion asynchronously.
|
|
806
|
+
*
|
|
807
|
+
* Creates fresh closure state (phase tracking, Jaccard cache) for this
|
|
808
|
+
* invocation. The REACH priority function uses `jaccard(context.graph, ...)`
|
|
809
|
+
* in Phase 2; in async mode `context.graph` is the sentinel and will throw.
|
|
810
|
+
* Full async equivalence requires PriorityContext refactoring (Phase 4b
|
|
811
|
+
* deferred). This export establishes the async API surface.
|
|
812
|
+
*
|
|
813
|
+
* @param graph - Async source graph
|
|
814
|
+
* @param seeds - Seed nodes for expansion
|
|
815
|
+
* @param config - Expansion and async runner configuration
|
|
816
|
+
* @returns Promise resolving to the expansion result
|
|
817
|
+
*/
|
|
818
|
+
async function reachAsync(graph, seeds, config) {
|
|
819
|
+
let inPhase2 = false;
|
|
820
|
+
function reachPriority(nodeId, context) {
|
|
821
|
+
if (context.discoveredPaths.length > 0 && !inPhase2) inPhase2 = true;
|
|
822
|
+
if (!inPhase2) return Math.log(context.degree + 1);
|
|
823
|
+
let totalMI = 0;
|
|
824
|
+
let endpointCount = 0;
|
|
825
|
+
for (const path of context.discoveredPaths) {
|
|
826
|
+
totalMI += jaccard(context.graph, nodeId, path.fromSeed.id);
|
|
827
|
+
totalMI += jaccard(context.graph, nodeId, path.toSeed.id);
|
|
828
|
+
endpointCount += 2;
|
|
829
|
+
}
|
|
830
|
+
const miHat = endpointCount > 0 ? totalMI / endpointCount : 0;
|
|
831
|
+
return Math.log(context.degree + 1) * (1 - miHat);
|
|
832
|
+
}
|
|
833
|
+
return baseAsync(graph, seeds, {
|
|
834
|
+
...config,
|
|
835
|
+
priority: reachPriority
|
|
836
|
+
});
|
|
837
|
+
}
|
|
524
838
|
//#endregion
|
|
525
839
|
//#region src/expansion/maze.ts
|
|
526
840
|
/** Default threshold for switching to phase 2 (after M paths) */
|
|
@@ -567,6 +881,46 @@ function maze(graph, seeds, config) {
|
|
|
567
881
|
priority: mazePriority
|
|
568
882
|
});
|
|
569
883
|
}
|
|
884
|
+
/**
|
|
885
|
+
* Run MAZE expansion asynchronously.
|
|
886
|
+
*
|
|
887
|
+
* Creates fresh closure state (salienceCounts, phase tracking) for this
|
|
888
|
+
* invocation. The MAZE priority function accesses `context.graph` to
|
|
889
|
+
* retrieve neighbour lists for path potential computation. Full async
|
|
890
|
+
* equivalence requires PriorityContext refactoring (Phase 4b deferred).
|
|
891
|
+
* This export establishes the async API surface.
|
|
892
|
+
*
|
|
893
|
+
* @param graph - Async source graph
|
|
894
|
+
* @param seeds - Seed nodes for expansion
|
|
895
|
+
* @param config - Expansion and async runner configuration
|
|
896
|
+
* @returns Promise resolving to the expansion result
|
|
897
|
+
*/
|
|
898
|
+
async function mazeAsync(graph, seeds, config) {
|
|
899
|
+
const salienceCounts = /* @__PURE__ */ new Map();
|
|
900
|
+
let inPhase2 = false;
|
|
901
|
+
let lastPathCount = 0;
|
|
902
|
+
function mazePriority(nodeId, context) {
|
|
903
|
+
const pathCount = context.discoveredPaths.length;
|
|
904
|
+
if (pathCount >= DEFAULT_PHASE2_THRESHOLD && !inPhase2) {
|
|
905
|
+
inPhase2 = true;
|
|
906
|
+
updateSalienceCounts(salienceCounts, context.discoveredPaths, 0);
|
|
907
|
+
}
|
|
908
|
+
if (inPhase2 && pathCount > lastPathCount) lastPathCount = updateSalienceCounts(salienceCounts, context.discoveredPaths, lastPathCount);
|
|
909
|
+
const nodeNeighbours = context.graph.neighbours(nodeId);
|
|
910
|
+
let pathPotential = 0;
|
|
911
|
+
for (const neighbour of nodeNeighbours) {
|
|
912
|
+
const visitedBy = context.visitedByFrontier.get(neighbour);
|
|
913
|
+
if (visitedBy !== void 0 && visitedBy !== context.frontierIndex) pathPotential++;
|
|
914
|
+
}
|
|
915
|
+
if (!inPhase2) return context.degree / (1 + pathPotential);
|
|
916
|
+
const salience = salienceCounts.get(nodeId) ?? 0;
|
|
917
|
+
return context.degree / (1 + pathPotential) * (1 / (1 + SALIENCE_WEIGHT * salience));
|
|
918
|
+
}
|
|
919
|
+
return baseAsync(graph, seeds, {
|
|
920
|
+
...config,
|
|
921
|
+
priority: mazePriority
|
|
922
|
+
});
|
|
923
|
+
}
|
|
570
924
|
//#endregion
|
|
571
925
|
//#region src/expansion/tide.ts
|
|
572
926
|
/**
|
|
@@ -598,6 +952,25 @@ function tide(graph, seeds, config) {
|
|
|
598
952
|
priority: tidePriority
|
|
599
953
|
});
|
|
600
954
|
}
|
|
955
|
+
/**
|
|
956
|
+
* Run TIDE expansion asynchronously.
|
|
957
|
+
*
|
|
958
|
+
* Note: the TIDE priority function accesses `context.graph` to retrieve
|
|
959
|
+
* neighbour lists and per-neighbour degrees. Full async equivalence
|
|
960
|
+
* requires PriorityContext refactoring (Phase 4b deferred). This export
|
|
961
|
+
* establishes the async API surface.
|
|
962
|
+
*
|
|
963
|
+
* @param graph - Async source graph
|
|
964
|
+
* @param seeds - Seed nodes for expansion
|
|
965
|
+
* @param config - Expansion and async runner configuration
|
|
966
|
+
* @returns Promise resolving to the expansion result
|
|
967
|
+
*/
|
|
968
|
+
async function tideAsync(graph, seeds, config) {
|
|
969
|
+
return baseAsync(graph, seeds, {
|
|
970
|
+
...config,
|
|
971
|
+
priority: tidePriority
|
|
972
|
+
});
|
|
973
|
+
}
|
|
601
974
|
//#endregion
|
|
602
975
|
//#region src/expansion/lace.ts
|
|
603
976
|
/**
|
|
@@ -628,6 +1001,27 @@ function lace(graph, seeds, config) {
|
|
|
628
1001
|
priority
|
|
629
1002
|
});
|
|
630
1003
|
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Run LACE expansion asynchronously.
|
|
1006
|
+
*
|
|
1007
|
+
* Note: the LACE priority function accesses `context.graph` via
|
|
1008
|
+
* `avgFrontierMI`. Full async equivalence requires PriorityContext
|
|
1009
|
+
* refactoring (Phase 4b deferred). This export establishes the async
|
|
1010
|
+
* API surface.
|
|
1011
|
+
*
|
|
1012
|
+
* @param graph - Async source graph
|
|
1013
|
+
* @param seeds - Seed nodes for expansion
|
|
1014
|
+
* @param config - LACE configuration combined with async runner options
|
|
1015
|
+
* @returns Promise resolving to the expansion result
|
|
1016
|
+
*/
|
|
1017
|
+
async function laceAsync(graph, seeds, config) {
|
|
1018
|
+
const { mi = jaccard, ...restConfig } = config ?? {};
|
|
1019
|
+
const priority = (nodeId, context) => lacePriority(nodeId, context, mi);
|
|
1020
|
+
return baseAsync(graph, seeds, {
|
|
1021
|
+
...restConfig,
|
|
1022
|
+
priority
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
631
1025
|
//#endregion
|
|
632
1026
|
//#region src/expansion/warp.ts
|
|
633
1027
|
/**
|
|
@@ -660,6 +1054,25 @@ function warp(graph, seeds, config) {
|
|
|
660
1054
|
priority: warpPriority
|
|
661
1055
|
});
|
|
662
1056
|
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Run WARP expansion asynchronously.
|
|
1059
|
+
*
|
|
1060
|
+
* Note: the WARP priority function accesses `context.graph` via
|
|
1061
|
+
* `countCrossFrontierNeighbours`. Full async equivalence requires
|
|
1062
|
+
* PriorityContext refactoring (Phase 4b deferred). This export
|
|
1063
|
+
* establishes the async API surface.
|
|
1064
|
+
*
|
|
1065
|
+
* @param graph - Async source graph
|
|
1066
|
+
* @param seeds - Seed nodes for expansion
|
|
1067
|
+
* @param config - Expansion and async runner configuration
|
|
1068
|
+
* @returns Promise resolving to the expansion result
|
|
1069
|
+
*/
|
|
1070
|
+
async function warpAsync(graph, seeds, config) {
|
|
1071
|
+
return baseAsync(graph, seeds, {
|
|
1072
|
+
...config,
|
|
1073
|
+
priority: warpPriority
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
663
1076
|
//#endregion
|
|
664
1077
|
//#region src/expansion/fuse.ts
|
|
665
1078
|
/**
|
|
@@ -692,6 +1105,27 @@ function fuse(graph, seeds, config) {
|
|
|
692
1105
|
priority
|
|
693
1106
|
});
|
|
694
1107
|
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Run FUSE expansion asynchronously.
|
|
1110
|
+
*
|
|
1111
|
+
* Note: the FUSE priority function accesses `context.graph` via
|
|
1112
|
+
* `avgFrontierMI`. Full async equivalence requires PriorityContext
|
|
1113
|
+
* refactoring (Phase 4b deferred). This export establishes the async
|
|
1114
|
+
* API surface.
|
|
1115
|
+
*
|
|
1116
|
+
* @param graph - Async source graph
|
|
1117
|
+
* @param seeds - Seed nodes for expansion
|
|
1118
|
+
* @param config - FUSE configuration combined with async runner options
|
|
1119
|
+
* @returns Promise resolving to the expansion result
|
|
1120
|
+
*/
|
|
1121
|
+
async function fuseAsync(graph, seeds, config) {
|
|
1122
|
+
const { mi = jaccard, salienceWeight = .5, ...restConfig } = config ?? {};
|
|
1123
|
+
const priority = (nodeId, context) => fusePriority(nodeId, context, mi, salienceWeight);
|
|
1124
|
+
return baseAsync(graph, seeds, {
|
|
1125
|
+
...restConfig,
|
|
1126
|
+
priority
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
695
1129
|
//#endregion
|
|
696
1130
|
//#region src/expansion/sift.ts
|
|
697
1131
|
/**
|
|
@@ -724,6 +1158,27 @@ function sift(graph, seeds, config) {
|
|
|
724
1158
|
priority
|
|
725
1159
|
});
|
|
726
1160
|
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Run SIFT expansion asynchronously.
|
|
1163
|
+
*
|
|
1164
|
+
* Note: the SIFT priority function accesses `context.graph` via
|
|
1165
|
+
* `avgFrontierMI`. Full async equivalence requires PriorityContext
|
|
1166
|
+
* refactoring (Phase 4b deferred). This export establishes the async
|
|
1167
|
+
* API surface.
|
|
1168
|
+
*
|
|
1169
|
+
* @param graph - Async source graph
|
|
1170
|
+
* @param seeds - Seed nodes for expansion
|
|
1171
|
+
* @param config - SIFT (REACHConfig) configuration combined with async runner options
|
|
1172
|
+
* @returns Promise resolving to the expansion result
|
|
1173
|
+
*/
|
|
1174
|
+
async function siftAsync(graph, seeds, config) {
|
|
1175
|
+
const { mi = jaccard, miThreshold = .25, ...restConfig } = config ?? {};
|
|
1176
|
+
const priority = (nodeId, context) => siftPriority(nodeId, context, mi, miThreshold);
|
|
1177
|
+
return baseAsync(graph, seeds, {
|
|
1178
|
+
...restConfig,
|
|
1179
|
+
priority
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
727
1182
|
//#endregion
|
|
728
1183
|
//#region src/expansion/flux.ts
|
|
729
1184
|
/**
|
|
@@ -780,9 +1235,36 @@ function flux(graph, seeds, config) {
|
|
|
780
1235
|
priority
|
|
781
1236
|
});
|
|
782
1237
|
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Run FLUX expansion asynchronously.
|
|
1240
|
+
*
|
|
1241
|
+
* Note: the FLUX priority function accesses `context.graph` to compute
|
|
1242
|
+
* local density and cross-frontier bridge scores. Full async equivalence
|
|
1243
|
+
* requires PriorityContext refactoring (Phase 4b deferred). This export
|
|
1244
|
+
* establishes the async API surface.
|
|
1245
|
+
*
|
|
1246
|
+
* @param graph - Async source graph
|
|
1247
|
+
* @param seeds - Seed nodes for expansion
|
|
1248
|
+
* @param config - FLUX (MAZEConfig) configuration combined with async runner options
|
|
1249
|
+
* @returns Promise resolving to the expansion result
|
|
1250
|
+
*/
|
|
1251
|
+
async function fluxAsync(graph, seeds, config) {
|
|
1252
|
+
const { densityThreshold = .5, bridgeThreshold = .3, ...restConfig } = config ?? {};
|
|
1253
|
+
const priority = (nodeId, context) => fluxPriority(nodeId, context, densityThreshold, bridgeThreshold);
|
|
1254
|
+
return baseAsync(graph, seeds, {
|
|
1255
|
+
...restConfig,
|
|
1256
|
+
priority
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
783
1259
|
//#endregion
|
|
784
1260
|
//#region src/expansion/standard-bfs.ts
|
|
785
1261
|
/**
|
|
1262
|
+
* BFS priority: discovery iteration order (FIFO).
|
|
1263
|
+
*/
|
|
1264
|
+
function bfsPriority(_nodeId, context) {
|
|
1265
|
+
return context.iteration;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
786
1268
|
* Run standard BFS expansion (FIFO discovery order).
|
|
787
1269
|
*
|
|
788
1270
|
* @param graph - Source graph
|
|
@@ -791,17 +1273,35 @@ function flux(graph, seeds, config) {
|
|
|
791
1273
|
* @returns Expansion result with discovered paths
|
|
792
1274
|
*/
|
|
793
1275
|
function standardBfs(graph, seeds, config) {
|
|
794
|
-
const bfsPriority = (_nodeId, context) => {
|
|
795
|
-
return context.iteration;
|
|
796
|
-
};
|
|
797
1276
|
return base(graph, seeds, {
|
|
798
1277
|
...config,
|
|
799
1278
|
priority: bfsPriority
|
|
800
1279
|
});
|
|
801
1280
|
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Run standard BFS expansion asynchronously (FIFO discovery order).
|
|
1283
|
+
*
|
|
1284
|
+
* @param graph - Async source graph
|
|
1285
|
+
* @param seeds - Seed nodes for expansion
|
|
1286
|
+
* @param config - Expansion and async runner configuration
|
|
1287
|
+
* @returns Promise resolving to the expansion result
|
|
1288
|
+
*/
|
|
1289
|
+
async function standardBfsAsync(graph, seeds, config) {
|
|
1290
|
+
return baseAsync(graph, seeds, {
|
|
1291
|
+
...config,
|
|
1292
|
+
priority: bfsPriority
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
802
1295
|
//#endregion
|
|
803
1296
|
//#region src/expansion/frontier-balanced.ts
|
|
804
1297
|
/**
|
|
1298
|
+
* Frontier-balanced priority: frontier index dominates, then discovery iteration.
|
|
1299
|
+
* Scales frontier index by 1e9 to ensure round-robin ordering across frontiers.
|
|
1300
|
+
*/
|
|
1301
|
+
function balancedPriority(_nodeId, context) {
|
|
1302
|
+
return context.frontierIndex * 1e9 + context.iteration;
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
805
1305
|
* Run frontier-balanced expansion (round-robin across frontiers).
|
|
806
1306
|
*
|
|
807
1307
|
* @param graph - Source graph
|
|
@@ -810,14 +1310,25 @@ function standardBfs(graph, seeds, config) {
|
|
|
810
1310
|
* @returns Expansion result with discovered paths
|
|
811
1311
|
*/
|
|
812
1312
|
function frontierBalanced(graph, seeds, config) {
|
|
813
|
-
const balancedPriority = (_nodeId, context) => {
|
|
814
|
-
return context.frontierIndex * 1e9 + context.iteration;
|
|
815
|
-
};
|
|
816
1313
|
return base(graph, seeds, {
|
|
817
1314
|
...config,
|
|
818
1315
|
priority: balancedPriority
|
|
819
1316
|
});
|
|
820
1317
|
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Run frontier-balanced expansion asynchronously (round-robin across frontiers).
|
|
1320
|
+
*
|
|
1321
|
+
* @param graph - Async source graph
|
|
1322
|
+
* @param seeds - Seed nodes for expansion
|
|
1323
|
+
* @param config - Expansion and async runner configuration
|
|
1324
|
+
* @returns Promise resolving to the expansion result
|
|
1325
|
+
*/
|
|
1326
|
+
async function frontierBalancedAsync(graph, seeds, config) {
|
|
1327
|
+
return baseAsync(graph, seeds, {
|
|
1328
|
+
...config,
|
|
1329
|
+
priority: balancedPriority
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
821
1332
|
//#endregion
|
|
822
1333
|
//#region src/expansion/random-priority.ts
|
|
823
1334
|
/**
|
|
@@ -837,6 +1348,12 @@ function seededRandom$1(input, seed = 0) {
|
|
|
837
1348
|
return (h >>> 0) / 4294967295;
|
|
838
1349
|
}
|
|
839
1350
|
/**
|
|
1351
|
+
* Build a seeded random priority function for a given seed value.
|
|
1352
|
+
*/
|
|
1353
|
+
function makeRandomPriorityFn(seed) {
|
|
1354
|
+
return (nodeId) => seededRandom$1(nodeId, seed);
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
840
1357
|
* Run random-priority expansion (null hypothesis baseline).
|
|
841
1358
|
*
|
|
842
1359
|
* @param graph - Source graph
|
|
@@ -846,12 +1363,24 @@ function seededRandom$1(input, seed = 0) {
|
|
|
846
1363
|
*/
|
|
847
1364
|
function randomPriority(graph, seeds, config) {
|
|
848
1365
|
const { seed = 0 } = config ?? {};
|
|
849
|
-
const randomPriorityFn = (nodeId, context) => {
|
|
850
|
-
return seededRandom$1(nodeId, seed);
|
|
851
|
-
};
|
|
852
1366
|
return base(graph, seeds, {
|
|
853
1367
|
...config,
|
|
854
|
-
priority:
|
|
1368
|
+
priority: makeRandomPriorityFn(seed)
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Run random-priority expansion asynchronously (null hypothesis baseline).
|
|
1373
|
+
*
|
|
1374
|
+
* @param graph - Async source graph
|
|
1375
|
+
* @param seeds - Seed nodes for expansion
|
|
1376
|
+
* @param config - Expansion and async runner configuration
|
|
1377
|
+
* @returns Promise resolving to the expansion result
|
|
1378
|
+
*/
|
|
1379
|
+
async function randomPriorityAsync(graph, seeds, config) {
|
|
1380
|
+
const { seed = 0 } = config ?? {};
|
|
1381
|
+
return baseAsync(graph, seeds, {
|
|
1382
|
+
...config,
|
|
1383
|
+
priority: makeRandomPriorityFn(seed)
|
|
855
1384
|
});
|
|
856
1385
|
}
|
|
857
1386
|
//#endregion
|
|
@@ -883,6 +1412,20 @@ function dfsPriority(graph, seeds, config) {
|
|
|
883
1412
|
priority: dfsPriorityFn
|
|
884
1413
|
});
|
|
885
1414
|
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Run DFS-priority expansion asynchronously (LIFO discovery order).
|
|
1417
|
+
*
|
|
1418
|
+
* @param graph - Async source graph
|
|
1419
|
+
* @param seeds - Seed nodes for expansion
|
|
1420
|
+
* @param config - Expansion and async runner configuration
|
|
1421
|
+
* @returns Promise resolving to the expansion result
|
|
1422
|
+
*/
|
|
1423
|
+
async function dfsPriorityAsync(graph, seeds, config) {
|
|
1424
|
+
return baseAsync(graph, seeds, {
|
|
1425
|
+
...config,
|
|
1426
|
+
priority: dfsPriorityFn
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
886
1429
|
//#endregion
|
|
887
1430
|
//#region src/expansion/k-hop.ts
|
|
888
1431
|
/**
|
|
@@ -1240,6 +1783,74 @@ function parse(graph, paths, config) {
|
|
|
1240
1783
|
};
|
|
1241
1784
|
}
|
|
1242
1785
|
/**
|
|
1786
|
+
* Rank paths using async PARSE (Path-Aware Ranking via Salience Estimation).
|
|
1787
|
+
*
|
|
1788
|
+
* Async variant suitable for use with remote or lazy graph data sources.
|
|
1789
|
+
* Computes geometric mean of edge MI scores for each path using Promise.all
|
|
1790
|
+
* for parallelism, then sorts by salience (highest first).
|
|
1791
|
+
*
|
|
1792
|
+
* @param graph - Async source graph
|
|
1793
|
+
* @param paths - Paths to rank
|
|
1794
|
+
* @param config - Configuration options
|
|
1795
|
+
* @returns Ranked paths with statistics
|
|
1796
|
+
*/
|
|
1797
|
+
async function parseAsync(graph, paths, config) {
|
|
1798
|
+
const startTime = performance.now();
|
|
1799
|
+
const { mi = jaccardAsync, epsilon = 1e-10 } = config ?? {};
|
|
1800
|
+
const rankedPaths = [];
|
|
1801
|
+
for (const path of paths) {
|
|
1802
|
+
const salience = await computePathSalienceAsync(graph, path, mi, epsilon);
|
|
1803
|
+
rankedPaths.push({
|
|
1804
|
+
...path,
|
|
1805
|
+
salience
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
rankedPaths.sort((a, b) => b.salience - a.salience);
|
|
1809
|
+
const endTime = performance.now();
|
|
1810
|
+
const saliences = rankedPaths.map((p) => p.salience);
|
|
1811
|
+
const meanSalience = saliences.length > 0 ? saliences.reduce((a, b) => a + b, 0) / saliences.length : 0;
|
|
1812
|
+
const sortedSaliences = [...saliences].sort((a, b) => a - b);
|
|
1813
|
+
const mid = Math.floor(sortedSaliences.length / 2);
|
|
1814
|
+
const medianSalience = sortedSaliences.length > 0 ? sortedSaliences.length % 2 !== 0 ? sortedSaliences[mid] ?? 0 : ((sortedSaliences[mid - 1] ?? 0) + (sortedSaliences[mid] ?? 0)) / 2 : 0;
|
|
1815
|
+
const maxSalience = sortedSaliences.length > 0 ? sortedSaliences[sortedSaliences.length - 1] ?? 0 : 0;
|
|
1816
|
+
const minSalience = sortedSaliences.length > 0 ? sortedSaliences[0] ?? 0 : 0;
|
|
1817
|
+
return {
|
|
1818
|
+
paths: rankedPaths,
|
|
1819
|
+
stats: {
|
|
1820
|
+
pathsRanked: rankedPaths.length,
|
|
1821
|
+
meanSalience,
|
|
1822
|
+
medianSalience,
|
|
1823
|
+
maxSalience,
|
|
1824
|
+
minSalience,
|
|
1825
|
+
durationMs: endTime - startTime
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Compute salience for a single path asynchronously.
|
|
1831
|
+
*
|
|
1832
|
+
* Uses geometric mean of edge MI scores for length-unbiased ranking.
|
|
1833
|
+
* Edge MI values are computed in parallel via Promise.all.
|
|
1834
|
+
*/
|
|
1835
|
+
async function computePathSalienceAsync(graph, path, mi, epsilon) {
|
|
1836
|
+
const nodes = path.nodes;
|
|
1837
|
+
if (nodes.length < 2) return epsilon;
|
|
1838
|
+
const edgeMIs = await Promise.all(nodes.slice(0, -1).map((source, i) => {
|
|
1839
|
+
const target = nodes[i + 1];
|
|
1840
|
+
if (target !== void 0) return mi(graph, source, target);
|
|
1841
|
+
return Promise.resolve(epsilon);
|
|
1842
|
+
}));
|
|
1843
|
+
let productMi = 1;
|
|
1844
|
+
let edgeCount = 0;
|
|
1845
|
+
for (const edgeMi of edgeMIs) {
|
|
1846
|
+
productMi *= Math.max(epsilon, edgeMi);
|
|
1847
|
+
edgeCount++;
|
|
1848
|
+
}
|
|
1849
|
+
if (edgeCount === 0) return epsilon;
|
|
1850
|
+
const salience = Math.pow(productMi, 1 / edgeCount);
|
|
1851
|
+
return Math.max(epsilon, Math.min(1, salience));
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1243
1854
|
* Compute salience for a single path.
|
|
1244
1855
|
*
|
|
1245
1856
|
* Uses geometric mean of edge MI scores for length-unbiased ranking.
|
|
@@ -1287,6 +1898,29 @@ function adamicAdar(graph, source, target, config) {
|
|
|
1287
1898
|
}
|
|
1288
1899
|
return Math.max(epsilon, score);
|
|
1289
1900
|
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Async variant of Adamic-Adar index for use with async graph data sources.
|
|
1903
|
+
*
|
|
1904
|
+
* Fetches both neighbourhoods concurrently, then fetches degree for each common
|
|
1905
|
+
* neighbour to compute the inverse-log-degree weighted sum.
|
|
1906
|
+
*/
|
|
1907
|
+
async function adamicAdarAsync(graph, source, target, config) {
|
|
1908
|
+
const { epsilon = 1e-10, normalise = true } = config ?? {};
|
|
1909
|
+
const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
1910
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
1911
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
1912
|
+
const commonNeighbours = [];
|
|
1913
|
+
for (const n of srcSet) if (tgtSet.has(n)) commonNeighbours.push(n);
|
|
1914
|
+
if (commonNeighbours.length === 0) return epsilon;
|
|
1915
|
+
const degrees = await Promise.all(commonNeighbours.map((n) => graph.degree(n)));
|
|
1916
|
+
let score = 0;
|
|
1917
|
+
for (const degree of degrees) score += 1 / Math.log(degree + 1);
|
|
1918
|
+
if (normalise) {
|
|
1919
|
+
const maxScore = commonNeighbours.length / Math.log(2);
|
|
1920
|
+
score = score / maxScore;
|
|
1921
|
+
}
|
|
1922
|
+
return Math.max(epsilon, score);
|
|
1923
|
+
}
|
|
1290
1924
|
//#endregion
|
|
1291
1925
|
//#region src/ranking/mi/cosine.ts
|
|
1292
1926
|
/**
|
|
@@ -1308,6 +1942,23 @@ function cosine(graph, source, target, config) {
|
|
|
1308
1942
|
const score = intersection / denominator;
|
|
1309
1943
|
return Math.max(epsilon, score);
|
|
1310
1944
|
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Async variant of cosine similarity for use with async graph data sources.
|
|
1947
|
+
*
|
|
1948
|
+
* Fetches both neighbourhoods concurrently, then applies the same formula.
|
|
1949
|
+
*/
|
|
1950
|
+
async function cosineAsync(graph, source, target, config) {
|
|
1951
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
1952
|
+
const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
1953
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
1954
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
1955
|
+
let intersection = 0;
|
|
1956
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
1957
|
+
const denominator = Math.sqrt(srcSet.size) * Math.sqrt(tgtSet.size);
|
|
1958
|
+
if (denominator === 0) return 0;
|
|
1959
|
+
const score = intersection / denominator;
|
|
1960
|
+
return Math.max(epsilon, score);
|
|
1961
|
+
}
|
|
1311
1962
|
//#endregion
|
|
1312
1963
|
//#region src/ranking/mi/sorensen.ts
|
|
1313
1964
|
/**
|
|
@@ -1329,6 +1980,23 @@ function sorensen(graph, source, target, config) {
|
|
|
1329
1980
|
const score = 2 * intersection / denominator;
|
|
1330
1981
|
return Math.max(epsilon, score);
|
|
1331
1982
|
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Async variant of Sorensen-Dice coefficient for use with async graph data sources.
|
|
1985
|
+
*
|
|
1986
|
+
* Fetches both neighbourhoods concurrently, then applies the same formula.
|
|
1987
|
+
*/
|
|
1988
|
+
async function sorensenAsync(graph, source, target, config) {
|
|
1989
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
1990
|
+
const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
1991
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
1992
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
1993
|
+
let intersection = 0;
|
|
1994
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
1995
|
+
const denominator = srcSet.size + tgtSet.size;
|
|
1996
|
+
if (denominator === 0) return 0;
|
|
1997
|
+
const score = 2 * intersection / denominator;
|
|
1998
|
+
return Math.max(epsilon, score);
|
|
1999
|
+
}
|
|
1332
2000
|
//#endregion
|
|
1333
2001
|
//#region src/ranking/mi/resource-allocation.ts
|
|
1334
2002
|
/**
|
|
@@ -1354,6 +2022,29 @@ function resourceAllocation(graph, source, target, config) {
|
|
|
1354
2022
|
}
|
|
1355
2023
|
return Math.max(epsilon, score);
|
|
1356
2024
|
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Async variant of Resource Allocation index for use with async graph data sources.
|
|
2027
|
+
*
|
|
2028
|
+
* Fetches both neighbourhoods concurrently, then fetches degree for each common
|
|
2029
|
+
* neighbour to compute the inverse-degree weighted sum.
|
|
2030
|
+
*/
|
|
2031
|
+
async function resourceAllocationAsync(graph, source, target, config) {
|
|
2032
|
+
const { epsilon = 1e-10, normalise = true } = config ?? {};
|
|
2033
|
+
const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
2034
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2035
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2036
|
+
const commonNeighbours = [];
|
|
2037
|
+
for (const n of srcSet) if (tgtSet.has(n)) commonNeighbours.push(n);
|
|
2038
|
+
if (commonNeighbours.length === 0) return epsilon;
|
|
2039
|
+
const degrees = await Promise.all(commonNeighbours.map((n) => graph.degree(n)));
|
|
2040
|
+
let score = 0;
|
|
2041
|
+
for (const degree of degrees) if (degree > 0) score += 1 / degree;
|
|
2042
|
+
if (normalise) {
|
|
2043
|
+
const maxScore = commonNeighbours.length;
|
|
2044
|
+
score = score / maxScore;
|
|
2045
|
+
}
|
|
2046
|
+
return Math.max(epsilon, score);
|
|
2047
|
+
}
|
|
1357
2048
|
//#endregion
|
|
1358
2049
|
//#region src/ranking/mi/overlap-coefficient.ts
|
|
1359
2050
|
/**
|
|
@@ -1375,6 +2066,23 @@ function overlapCoefficient(graph, source, target, config) {
|
|
|
1375
2066
|
const score = intersection / denominator;
|
|
1376
2067
|
return Math.max(epsilon, score);
|
|
1377
2068
|
}
|
|
2069
|
+
/**
|
|
2070
|
+
* Async variant of Overlap Coefficient for use with async graph data sources.
|
|
2071
|
+
*
|
|
2072
|
+
* Fetches both neighbourhoods concurrently, then applies the same formula.
|
|
2073
|
+
*/
|
|
2074
|
+
async function overlapCoefficientAsync(graph, source, target, config) {
|
|
2075
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2076
|
+
const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
2077
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2078
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2079
|
+
let intersection = 0;
|
|
2080
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2081
|
+
const denominator = Math.min(srcSet.size, tgtSet.size);
|
|
2082
|
+
if (denominator === 0) return 0;
|
|
2083
|
+
const score = intersection / denominator;
|
|
2084
|
+
return Math.max(epsilon, score);
|
|
2085
|
+
}
|
|
1378
2086
|
//#endregion
|
|
1379
2087
|
//#region src/ranking/mi/hub-promoted.ts
|
|
1380
2088
|
/**
|
|
@@ -1396,6 +2104,28 @@ function hubPromoted(graph, source, target, config) {
|
|
|
1396
2104
|
const score = intersection / denominator;
|
|
1397
2105
|
return Math.max(epsilon, score);
|
|
1398
2106
|
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Async variant of Hub Promoted index for use with async graph data sources.
|
|
2109
|
+
*
|
|
2110
|
+
* Fetches both neighbourhoods and degrees concurrently, then applies the same formula.
|
|
2111
|
+
*/
|
|
2112
|
+
async function hubPromotedAsync(graph, source, target, config) {
|
|
2113
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2114
|
+
const [sourceArr, targetArr, sourceDegree, targetDegree] = await Promise.all([
|
|
2115
|
+
require_async.collectAsyncIterable(graph.neighbours(source)),
|
|
2116
|
+
require_async.collectAsyncIterable(graph.neighbours(target)),
|
|
2117
|
+
graph.degree(source),
|
|
2118
|
+
graph.degree(target)
|
|
2119
|
+
]);
|
|
2120
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2121
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2122
|
+
let intersection = 0;
|
|
2123
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2124
|
+
const denominator = Math.min(sourceDegree, targetDegree);
|
|
2125
|
+
if (denominator === 0) return 0;
|
|
2126
|
+
const score = intersection / denominator;
|
|
2127
|
+
return Math.max(epsilon, score);
|
|
2128
|
+
}
|
|
1399
2129
|
//#endregion
|
|
1400
2130
|
//#region src/ranking/mi/scale.ts
|
|
1401
2131
|
/**
|
|
@@ -1412,6 +2142,31 @@ function scale(graph, source, target, config) {
|
|
|
1412
2142
|
const score = jaccardScore / density;
|
|
1413
2143
|
return Math.max(epsilon, score);
|
|
1414
2144
|
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Async variant of SCALE MI for use with async graph data sources.
|
|
2147
|
+
*
|
|
2148
|
+
* Fetches both neighbourhoods, node count, and edge count concurrently.
|
|
2149
|
+
*/
|
|
2150
|
+
async function scaleAsync(graph, source, target, config) {
|
|
2151
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2152
|
+
const [sourceArr, targetArr, n, m] = await Promise.all([
|
|
2153
|
+
require_async.collectAsyncIterable(graph.neighbours(source)),
|
|
2154
|
+
require_async.collectAsyncIterable(graph.neighbours(target)),
|
|
2155
|
+
graph.nodeCount,
|
|
2156
|
+
graph.edgeCount
|
|
2157
|
+
]);
|
|
2158
|
+
const srcSet = new Set(sourceArr.filter((node) => node !== target));
|
|
2159
|
+
const tgtSet = new Set(targetArr.filter((node) => node !== source));
|
|
2160
|
+
let intersection = 0;
|
|
2161
|
+
for (const node of srcSet) if (tgtSet.has(node)) intersection++;
|
|
2162
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
2163
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
2164
|
+
const possibleEdges = n * (n - 1);
|
|
2165
|
+
const density = possibleEdges > 0 ? (graph.directed ? m : 2 * m) / possibleEdges : 0;
|
|
2166
|
+
if (density === 0) return epsilon;
|
|
2167
|
+
const score = jaccardScore / density;
|
|
2168
|
+
return Math.max(epsilon, score);
|
|
2169
|
+
}
|
|
1415
2170
|
//#endregion
|
|
1416
2171
|
//#region src/ranking/mi/skew.ts
|
|
1417
2172
|
/**
|
|
@@ -1428,6 +2183,31 @@ function skew(graph, source, target, config) {
|
|
|
1428
2183
|
const score = jaccardScore * sourceIdf * targetIdf;
|
|
1429
2184
|
return Math.max(epsilon, score);
|
|
1430
2185
|
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Async variant of SKEW MI for use with async graph data sources.
|
|
2188
|
+
*
|
|
2189
|
+
* Fetches both neighbourhoods, degrees, and node count concurrently.
|
|
2190
|
+
*/
|
|
2191
|
+
async function skewAsync(graph, source, target, config) {
|
|
2192
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2193
|
+
const [sourceArr, targetArr, N, sourceDegree, targetDegree] = await Promise.all([
|
|
2194
|
+
require_async.collectAsyncIterable(graph.neighbours(source)),
|
|
2195
|
+
require_async.collectAsyncIterable(graph.neighbours(target)),
|
|
2196
|
+
graph.nodeCount,
|
|
2197
|
+
graph.degree(source),
|
|
2198
|
+
graph.degree(target)
|
|
2199
|
+
]);
|
|
2200
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2201
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2202
|
+
let intersection = 0;
|
|
2203
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2204
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
2205
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
2206
|
+
const sourceIdf = Math.log(N / (sourceDegree + 1));
|
|
2207
|
+
const targetIdf = Math.log(N / (targetDegree + 1));
|
|
2208
|
+
const score = jaccardScore * sourceIdf * targetIdf;
|
|
2209
|
+
return Math.max(epsilon, score);
|
|
2210
|
+
}
|
|
1431
2211
|
//#endregion
|
|
1432
2212
|
//#region src/ranking/mi/span.ts
|
|
1433
2213
|
/**
|
|
@@ -1441,6 +2221,40 @@ function span(graph, source, target, config) {
|
|
|
1441
2221
|
const score = jaccardScore * (1 - Math.max(sourceCc, targetCc));
|
|
1442
2222
|
return Math.max(epsilon, score);
|
|
1443
2223
|
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Async variant of SPAN MI for use with async graph data sources.
|
|
2226
|
+
*
|
|
2227
|
+
* Fetches both neighbourhoods concurrently, then computes the clustering
|
|
2228
|
+
* coefficient for each endpoint from the collected neighbour arrays.
|
|
2229
|
+
*/
|
|
2230
|
+
async function spanAsync(graph, source, target, config) {
|
|
2231
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2232
|
+
const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
|
|
2233
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2234
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2235
|
+
let intersection = 0;
|
|
2236
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2237
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
2238
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
2239
|
+
const computeClusteringCoefficient = async (nodeId, neighbourArr) => {
|
|
2240
|
+
const degree = neighbourArr.length;
|
|
2241
|
+
if (degree < 2) return 0;
|
|
2242
|
+
const pairs = [];
|
|
2243
|
+
for (let i = 0; i < neighbourArr.length; i++) for (let j = i + 1; j < neighbourArr.length; j++) {
|
|
2244
|
+
const u = neighbourArr[i];
|
|
2245
|
+
const v = neighbourArr[j];
|
|
2246
|
+
if (u !== void 0 && v !== void 0) pairs.push([u, v]);
|
|
2247
|
+
}
|
|
2248
|
+
const edgeResults = await Promise.all(pairs.flatMap(([u, v]) => [graph.getEdge(u, v), graph.getEdge(v, u)]));
|
|
2249
|
+
let triangleCount = 0;
|
|
2250
|
+
for (let i = 0; i < pairs.length; i++) if (edgeResults[2 * i] !== void 0 || edgeResults[2 * i + 1] !== void 0) triangleCount++;
|
|
2251
|
+
const possibleTriangles = degree * (degree - 1) / 2;
|
|
2252
|
+
return triangleCount / possibleTriangles;
|
|
2253
|
+
};
|
|
2254
|
+
const [sourceCc, targetCc] = await Promise.all([computeClusteringCoefficient(source, sourceArr), computeClusteringCoefficient(target, targetArr)]);
|
|
2255
|
+
const score = jaccardScore * (1 - Math.max(sourceCc, targetCc));
|
|
2256
|
+
return Math.max(epsilon, score);
|
|
2257
|
+
}
|
|
1444
2258
|
//#endregion
|
|
1445
2259
|
//#region src/ranking/mi/etch.ts
|
|
1446
2260
|
/**
|
|
@@ -1456,6 +2270,37 @@ function etch(graph, source, target, config) {
|
|
|
1456
2270
|
const score = jaccardScore * Math.log(graph.edgeCount / edgeTypeCount);
|
|
1457
2271
|
return Math.max(epsilon, score);
|
|
1458
2272
|
}
|
|
2273
|
+
/**
|
|
2274
|
+
* Async variant of ETCH MI for use with async graph data sources.
|
|
2275
|
+
*
|
|
2276
|
+
* Fetches both neighbourhoods and edge data concurrently, then counts
|
|
2277
|
+
* edges of the same type by iterating the async edge stream.
|
|
2278
|
+
*/
|
|
2279
|
+
async function etchAsync(graph, source, target, config) {
|
|
2280
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2281
|
+
const [sourceArr, targetArr, edge] = await Promise.all([
|
|
2282
|
+
require_async.collectAsyncIterable(graph.neighbours(source)),
|
|
2283
|
+
require_async.collectAsyncIterable(graph.neighbours(target)),
|
|
2284
|
+
graph.getEdge(source, target)
|
|
2285
|
+
]);
|
|
2286
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2287
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2288
|
+
let intersection = 0;
|
|
2289
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2290
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
2291
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
2292
|
+
if (edge?.type === void 0) return Math.max(epsilon, jaccardScore);
|
|
2293
|
+
const edgeType = edge.type;
|
|
2294
|
+
let edgeTypeCount = 0;
|
|
2295
|
+
let totalEdges = 0;
|
|
2296
|
+
for await (const e of graph.edges()) {
|
|
2297
|
+
totalEdges++;
|
|
2298
|
+
if (e.type === edgeType) edgeTypeCount++;
|
|
2299
|
+
}
|
|
2300
|
+
if (edgeTypeCount === 0) return Math.max(epsilon, jaccardScore);
|
|
2301
|
+
const score = jaccardScore * Math.log(totalEdges / edgeTypeCount);
|
|
2302
|
+
return Math.max(epsilon, score);
|
|
2303
|
+
}
|
|
1459
2304
|
//#endregion
|
|
1460
2305
|
//#region src/ranking/mi/notch.ts
|
|
1461
2306
|
/**
|
|
@@ -1475,6 +2320,44 @@ function notch(graph, source, target, config) {
|
|
|
1475
2320
|
const score = jaccardScore * sourceRarity * targetRarity;
|
|
1476
2321
|
return Math.max(epsilon, score);
|
|
1477
2322
|
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Async variant of NOTCH MI for use with async graph data sources.
|
|
2325
|
+
*
|
|
2326
|
+
* Fetches both neighbourhoods and node data concurrently, then counts
|
|
2327
|
+
* nodes of each type by iterating the async node stream.
|
|
2328
|
+
*/
|
|
2329
|
+
async function notchAsync(graph, source, target, config) {
|
|
2330
|
+
const { epsilon = 1e-10 } = config ?? {};
|
|
2331
|
+
const [sourceArr, targetArr, sourceNode, targetNode] = await Promise.all([
|
|
2332
|
+
require_async.collectAsyncIterable(graph.neighbours(source)),
|
|
2333
|
+
require_async.collectAsyncIterable(graph.neighbours(target)),
|
|
2334
|
+
graph.getNode(source),
|
|
2335
|
+
graph.getNode(target)
|
|
2336
|
+
]);
|
|
2337
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2338
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2339
|
+
let intersection = 0;
|
|
2340
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2341
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
2342
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
2343
|
+
if (sourceNode?.type === void 0 || targetNode?.type === void 0) return Math.max(epsilon, jaccardScore);
|
|
2344
|
+
const sourceType = sourceNode.type;
|
|
2345
|
+
const targetType = targetNode.type;
|
|
2346
|
+
let totalNodes = 0;
|
|
2347
|
+
let sourceTypeCount = 0;
|
|
2348
|
+
let targetTypeCount = 0;
|
|
2349
|
+
for await (const nodeId of graph.nodeIds()) {
|
|
2350
|
+
totalNodes++;
|
|
2351
|
+
const node = await graph.getNode(nodeId);
|
|
2352
|
+
if (node?.type === sourceType) sourceTypeCount++;
|
|
2353
|
+
if (node?.type === targetType) targetTypeCount++;
|
|
2354
|
+
}
|
|
2355
|
+
if (sourceTypeCount === 0 || targetTypeCount === 0) return Math.max(epsilon, jaccardScore);
|
|
2356
|
+
const sourceRarity = Math.log(totalNodes / sourceTypeCount);
|
|
2357
|
+
const targetRarity = Math.log(totalNodes / targetTypeCount);
|
|
2358
|
+
const score = jaccardScore * sourceRarity * targetRarity;
|
|
2359
|
+
return Math.max(epsilon, score);
|
|
2360
|
+
}
|
|
1478
2361
|
//#endregion
|
|
1479
2362
|
//#region src/ranking/mi/adaptive.ts
|
|
1480
2363
|
/**
|
|
@@ -1507,6 +2390,38 @@ function adaptive(graph, source, target, config) {
|
|
|
1507
2390
|
const score = (structuralWeight * structural + degreeWeight * degreeComponent + overlapWeight * overlap) / totalWeight;
|
|
1508
2391
|
return Math.max(epsilon, Math.min(1, score));
|
|
1509
2392
|
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Async variant of Adaptive MI for use with async graph data sources.
|
|
2395
|
+
*
|
|
2396
|
+
* Fetches both neighbourhoods concurrently, then delegates degree-weighted
|
|
2397
|
+
* component to the async Adamic-Adar variant.
|
|
2398
|
+
*/
|
|
2399
|
+
async function adaptiveAsync(graph, source, target, config) {
|
|
2400
|
+
const { epsilon = 1e-10, structuralWeight = .4, degreeWeight = .3, overlapWeight = .3 } = config ?? {};
|
|
2401
|
+
const [sourceArr, targetArr, degreeComponent] = await Promise.all([
|
|
2402
|
+
require_async.collectAsyncIterable(graph.neighbours(source)),
|
|
2403
|
+
require_async.collectAsyncIterable(graph.neighbours(target)),
|
|
2404
|
+
adamicAdarAsync(graph, source, target, {
|
|
2405
|
+
epsilon,
|
|
2406
|
+
normalise: true
|
|
2407
|
+
})
|
|
2408
|
+
]);
|
|
2409
|
+
const srcSet = new Set(sourceArr.filter((n) => n !== target));
|
|
2410
|
+
const tgtSet = new Set(targetArr.filter((n) => n !== source));
|
|
2411
|
+
let intersection = 0;
|
|
2412
|
+
for (const n of srcSet) if (tgtSet.has(n)) intersection++;
|
|
2413
|
+
const union = srcSet.size + tgtSet.size - intersection;
|
|
2414
|
+
const jaccardScore = union > 0 ? intersection / union : 0;
|
|
2415
|
+
const structural = srcSet.size === 0 && tgtSet.size === 0 ? 0 : Math.max(epsilon, jaccardScore);
|
|
2416
|
+
let overlap;
|
|
2417
|
+
if (srcSet.size > 0 && tgtSet.size > 0) {
|
|
2418
|
+
const minDegree = Math.min(srcSet.size, tgtSet.size);
|
|
2419
|
+
overlap = minDegree > 0 ? intersection / minDegree : epsilon;
|
|
2420
|
+
} else overlap = epsilon;
|
|
2421
|
+
const totalWeight = structuralWeight + degreeWeight + overlapWeight;
|
|
2422
|
+
const score = (structuralWeight * structural + degreeWeight * degreeComponent + overlapWeight * overlap) / totalWeight;
|
|
2423
|
+
return Math.max(epsilon, Math.min(1, score));
|
|
2424
|
+
}
|
|
1510
2425
|
//#endregion
|
|
1511
2426
|
//#region src/ranking/baselines/utils.ts
|
|
1512
2427
|
/**
|
|
@@ -3007,60 +3922,79 @@ exports.GPUNotAvailableError = require_gpu.GPUNotAvailableError;
|
|
|
3007
3922
|
exports.PriorityQueue = require_structures.PriorityQueue;
|
|
3008
3923
|
exports._computeMean = require_kmeans._computeMean;
|
|
3009
3924
|
exports.adamicAdar = adamicAdar;
|
|
3925
|
+
exports.adamicAdarAsync = adamicAdarAsync;
|
|
3010
3926
|
exports.adaptive = adaptive;
|
|
3927
|
+
exports.adaptiveAsync = adaptiveAsync;
|
|
3011
3928
|
exports.approximateClusteringCoefficient = require_utils.approximateClusteringCoefficient;
|
|
3012
3929
|
exports.assertWebGPUAvailable = require_gpu.assertWebGPUAvailable;
|
|
3013
3930
|
exports.base = base;
|
|
3931
|
+
exports.baseAsync = baseAsync;
|
|
3014
3932
|
exports.batchClusteringCoefficients = require_utils.batchClusteringCoefficients;
|
|
3015
3933
|
exports.betweenness = betweenness;
|
|
3016
3934
|
exports.bfs = require_traversal.bfs;
|
|
3017
3935
|
exports.bfsWithPath = require_traversal.bfsWithPath;
|
|
3936
|
+
exports.collectAsyncIterable = require_async.collectAsyncIterable;
|
|
3018
3937
|
exports.communicability = communicability;
|
|
3019
3938
|
exports.computeJaccard = require_utils.computeJaccard;
|
|
3020
3939
|
exports.computeTrussNumbers = computeTrussNumbers;
|
|
3021
3940
|
exports.cosine = cosine;
|
|
3941
|
+
exports.cosineAsync = cosineAsync;
|
|
3022
3942
|
exports.countEdgesOfType = require_utils.countEdgesOfType;
|
|
3023
3943
|
exports.countNodesOfType = require_utils.countNodesOfType;
|
|
3024
3944
|
exports.createGPUContext = require_gpu.createGPUContext;
|
|
3025
3945
|
exports.createResultBuffer = require_gpu.createResultBuffer;
|
|
3026
3946
|
exports.csrToGPUBuffers = require_gpu.csrToGPUBuffers;
|
|
3947
|
+
exports.defaultYieldStrategy = require_async.defaultYieldStrategy;
|
|
3027
3948
|
exports.degreeSum = degreeSum;
|
|
3028
3949
|
exports.detectWebGPU = require_gpu.detectWebGPU;
|
|
3029
3950
|
exports.dfs = require_traversal.dfs;
|
|
3030
3951
|
exports.dfsPriority = dfsPriority;
|
|
3952
|
+
exports.dfsPriorityAsync = dfsPriorityAsync;
|
|
3031
3953
|
exports.dfsPriorityFn = dfsPriorityFn;
|
|
3032
3954
|
exports.dfsWithPath = require_traversal.dfsWithPath;
|
|
3033
3955
|
exports.dome = dome;
|
|
3956
|
+
exports.domeAsync = domeAsync;
|
|
3034
3957
|
exports.domeHighDegree = domeHighDegree;
|
|
3958
|
+
exports.domeHighDegreeAsync = domeHighDegreeAsync;
|
|
3035
3959
|
exports.edge = edge;
|
|
3960
|
+
exports.edgeAsync = edgeAsync;
|
|
3036
3961
|
exports.entropyFromCounts = require_utils.entropyFromCounts;
|
|
3037
3962
|
exports.enumerateMotifs = enumerateMotifs;
|
|
3038
3963
|
exports.enumerateMotifsWithInstances = enumerateMotifsWithInstances;
|
|
3039
3964
|
exports.etch = etch;
|
|
3965
|
+
exports.etchAsync = etchAsync;
|
|
3040
3966
|
exports.extractEgoNetwork = extractEgoNetwork;
|
|
3041
3967
|
exports.extractInducedSubgraph = extractInducedSubgraph;
|
|
3042
3968
|
exports.extractKCore = extractKCore;
|
|
3043
3969
|
exports.extractKTruss = extractKTruss;
|
|
3044
3970
|
exports.filterSubgraph = filterSubgraph;
|
|
3045
3971
|
exports.flux = flux;
|
|
3972
|
+
exports.fluxAsync = fluxAsync;
|
|
3046
3973
|
exports.frontierBalanced = frontierBalanced;
|
|
3974
|
+
exports.frontierBalancedAsync = frontierBalancedAsync;
|
|
3047
3975
|
exports.fuse = fuse;
|
|
3976
|
+
exports.fuseAsync = fuseAsync;
|
|
3048
3977
|
exports.getGPUContext = require_gpu.getGPUContext;
|
|
3049
3978
|
exports.getMotifName = getMotifName;
|
|
3050
3979
|
exports.graphToCSR = require_gpu.graphToCSR;
|
|
3051
3980
|
exports.grasp = require_seeds.grasp;
|
|
3052
3981
|
exports.hae = hae;
|
|
3982
|
+
exports.haeAsync = haeAsync;
|
|
3053
3983
|
exports.hittingTime = hittingTime;
|
|
3054
3984
|
exports.hubPromoted = hubPromoted;
|
|
3985
|
+
exports.hubPromotedAsync = hubPromotedAsync;
|
|
3055
3986
|
exports.isWebGPUAvailable = require_gpu.isWebGPUAvailable;
|
|
3056
3987
|
exports.jaccard = jaccard;
|
|
3057
3988
|
exports.jaccardArithmetic = jaccardArithmetic;
|
|
3989
|
+
exports.jaccardAsync = jaccardAsync;
|
|
3058
3990
|
exports.kHop = kHop;
|
|
3059
3991
|
exports.katz = katz;
|
|
3060
3992
|
exports.lace = lace;
|
|
3993
|
+
exports.laceAsync = laceAsync;
|
|
3061
3994
|
exports.localClusteringCoefficient = require_utils.localClusteringCoefficient;
|
|
3062
3995
|
exports.localTypeEntropy = require_utils.localTypeEntropy;
|
|
3063
3996
|
exports.maze = maze;
|
|
3997
|
+
exports.mazeAsync = mazeAsync;
|
|
3064
3998
|
exports.miniBatchKMeans = require_kmeans.miniBatchKMeans;
|
|
3065
3999
|
exports.neighbourIntersection = require_utils.neighbourIntersection;
|
|
3066
4000
|
exports.neighbourOverlap = require_utils.neighbourOverlap;
|
|
@@ -3069,29 +4003,56 @@ exports.normaliseFeatures = require_kmeans.normaliseFeatures;
|
|
|
3069
4003
|
exports.zScoreNormalise = require_kmeans.normaliseFeatures;
|
|
3070
4004
|
exports.normalisedEntropy = require_utils.normalisedEntropy;
|
|
3071
4005
|
exports.notch = notch;
|
|
4006
|
+
exports.notchAsync = notchAsync;
|
|
4007
|
+
exports.opDegree = require_async.opDegree;
|
|
4008
|
+
exports.opGetEdge = require_async.opGetEdge;
|
|
4009
|
+
exports.opGetNode = require_async.opGetNode;
|
|
4010
|
+
exports.opHasNode = require_async.opHasNode;
|
|
4011
|
+
exports.opNeighbours = require_async.opNeighbours;
|
|
4012
|
+
exports.opProgress = require_async.opProgress;
|
|
4013
|
+
exports.opYield = require_async.opYield;
|
|
3072
4014
|
exports.overlapCoefficient = overlapCoefficient;
|
|
4015
|
+
exports.overlapCoefficientAsync = overlapCoefficientAsync;
|
|
3073
4016
|
exports.pagerank = pagerank;
|
|
3074
4017
|
exports.parse = parse;
|
|
4018
|
+
exports.parseAsync = parseAsync;
|
|
3075
4019
|
exports.pipe = pipe;
|
|
4020
|
+
exports.pipeAsync = pipeAsync;
|
|
3076
4021
|
exports.randomPriority = randomPriority;
|
|
4022
|
+
exports.randomPriorityAsync = randomPriorityAsync;
|
|
3077
4023
|
exports.randomRanking = randomRanking;
|
|
3078
4024
|
exports.randomWalk = randomWalk;
|
|
3079
4025
|
exports.reach = reach;
|
|
4026
|
+
exports.reachAsync = reachAsync;
|
|
3080
4027
|
exports.readBufferToCPU = require_gpu.readBufferToCPU;
|
|
3081
4028
|
exports.resistanceDistance = resistanceDistance;
|
|
4029
|
+
exports.resolveAsyncOp = require_async.resolveAsyncOp;
|
|
4030
|
+
exports.resolveSyncOp = require_async.resolveSyncOp;
|
|
3082
4031
|
exports.resourceAllocation = resourceAllocation;
|
|
4032
|
+
exports.resourceAllocationAsync = resourceAllocationAsync;
|
|
4033
|
+
exports.runAsync = require_async.runAsync;
|
|
4034
|
+
exports.runSync = require_async.runSync;
|
|
3083
4035
|
exports.sage = sage;
|
|
4036
|
+
exports.sageAsync = sageAsync;
|
|
3084
4037
|
exports.scale = scale;
|
|
4038
|
+
exports.scaleAsync = scaleAsync;
|
|
3085
4039
|
exports.shannonEntropy = require_utils.shannonEntropy;
|
|
3086
4040
|
exports.shortest = shortest;
|
|
3087
4041
|
exports.sift = sift;
|
|
4042
|
+
exports.siftAsync = siftAsync;
|
|
3088
4043
|
exports.skew = skew;
|
|
4044
|
+
exports.skewAsync = skewAsync;
|
|
3089
4045
|
exports.sorensen = sorensen;
|
|
4046
|
+
exports.sorensenAsync = sorensenAsync;
|
|
3090
4047
|
exports.span = span;
|
|
4048
|
+
exports.spanAsync = spanAsync;
|
|
3091
4049
|
exports.standardBfs = standardBfs;
|
|
4050
|
+
exports.standardBfsAsync = standardBfsAsync;
|
|
3092
4051
|
exports.stratified = require_seeds.stratified;
|
|
3093
4052
|
exports.tide = tide;
|
|
4053
|
+
exports.tideAsync = tideAsync;
|
|
3094
4054
|
exports.warp = warp;
|
|
4055
|
+
exports.warpAsync = warpAsync;
|
|
3095
4056
|
exports.widestPath = widestPath;
|
|
3096
4057
|
|
|
3097
4058
|
//# sourceMappingURL=index.cjs.map
|