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.
Files changed (84) hide show
  1. package/README.md +81 -30
  2. package/dist/async/index.cjs +243 -0
  3. package/dist/async/index.cjs.map +1 -0
  4. package/dist/async/index.js +230 -0
  5. package/dist/async/index.js.map +1 -0
  6. package/dist/expansion/base-core.d.ts +24 -0
  7. package/dist/expansion/base-core.d.ts.map +1 -0
  8. package/dist/expansion/base-core.unit.test.d.ts +10 -0
  9. package/dist/expansion/base-core.unit.test.d.ts.map +1 -0
  10. package/dist/expansion/base-helpers.d.ts +57 -0
  11. package/dist/expansion/base-helpers.d.ts.map +1 -0
  12. package/dist/expansion/base.d.ts +32 -1
  13. package/dist/expansion/base.d.ts.map +1 -1
  14. package/dist/expansion/dfs-priority.d.ts +11 -0
  15. package/dist/expansion/dfs-priority.d.ts.map +1 -1
  16. package/dist/expansion/dome.d.ts +20 -0
  17. package/dist/expansion/dome.d.ts.map +1 -1
  18. package/dist/expansion/edge.d.ts +18 -0
  19. package/dist/expansion/edge.d.ts.map +1 -1
  20. package/dist/expansion/flux.d.ts +16 -0
  21. package/dist/expansion/flux.d.ts.map +1 -1
  22. package/dist/expansion/frontier-balanced.d.ts +11 -0
  23. package/dist/expansion/frontier-balanced.d.ts.map +1 -1
  24. package/dist/expansion/fuse.d.ts +16 -0
  25. package/dist/expansion/fuse.d.ts.map +1 -1
  26. package/dist/expansion/hae.d.ts +16 -0
  27. package/dist/expansion/hae.d.ts.map +1 -1
  28. package/dist/expansion/lace.d.ts +16 -0
  29. package/dist/expansion/lace.d.ts.map +1 -1
  30. package/dist/expansion/maze.d.ts +17 -0
  31. package/dist/expansion/maze.d.ts.map +1 -1
  32. package/dist/expansion/pipe.d.ts +16 -0
  33. package/dist/expansion/pipe.d.ts.map +1 -1
  34. package/dist/expansion/random-priority.d.ts +18 -0
  35. package/dist/expansion/random-priority.d.ts.map +1 -1
  36. package/dist/expansion/reach.d.ts +17 -0
  37. package/dist/expansion/reach.d.ts.map +1 -1
  38. package/dist/expansion/sage.d.ts +15 -0
  39. package/dist/expansion/sage.d.ts.map +1 -1
  40. package/dist/expansion/sift.d.ts +16 -0
  41. package/dist/expansion/sift.d.ts.map +1 -1
  42. package/dist/expansion/standard-bfs.d.ts +11 -0
  43. package/dist/expansion/standard-bfs.d.ts.map +1 -1
  44. package/dist/expansion/tide.d.ts +16 -0
  45. package/dist/expansion/tide.d.ts.map +1 -1
  46. package/dist/expansion/warp.d.ts +16 -0
  47. package/dist/expansion/warp.d.ts.map +1 -1
  48. package/dist/index/index.cjs +1060 -99
  49. package/dist/index/index.cjs.map +1 -1
  50. package/dist/index/index.js +1015 -100
  51. package/dist/index/index.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/ranking/mi/adamic-adar.d.ts +8 -0
  55. package/dist/ranking/mi/adamic-adar.d.ts.map +1 -1
  56. package/dist/ranking/mi/adaptive.d.ts +8 -0
  57. package/dist/ranking/mi/adaptive.d.ts.map +1 -1
  58. package/dist/ranking/mi/cosine.d.ts +7 -0
  59. package/dist/ranking/mi/cosine.d.ts.map +1 -1
  60. package/dist/ranking/mi/etch.d.ts +8 -0
  61. package/dist/ranking/mi/etch.d.ts.map +1 -1
  62. package/dist/ranking/mi/hub-promoted.d.ts +7 -0
  63. package/dist/ranking/mi/hub-promoted.d.ts.map +1 -1
  64. package/dist/ranking/mi/jaccard.d.ts +7 -0
  65. package/dist/ranking/mi/jaccard.d.ts.map +1 -1
  66. package/dist/ranking/mi/notch.d.ts +8 -0
  67. package/dist/ranking/mi/notch.d.ts.map +1 -1
  68. package/dist/ranking/mi/overlap-coefficient.d.ts +7 -0
  69. package/dist/ranking/mi/overlap-coefficient.d.ts.map +1 -1
  70. package/dist/ranking/mi/resource-allocation.d.ts +8 -0
  71. package/dist/ranking/mi/resource-allocation.d.ts.map +1 -1
  72. package/dist/ranking/mi/scale.d.ts +7 -0
  73. package/dist/ranking/mi/scale.d.ts.map +1 -1
  74. package/dist/ranking/mi/skew.d.ts +7 -0
  75. package/dist/ranking/mi/skew.d.ts.map +1 -1
  76. package/dist/ranking/mi/sorensen.d.ts +7 -0
  77. package/dist/ranking/mi/sorensen.d.ts.map +1 -1
  78. package/dist/ranking/mi/span.d.ts +8 -0
  79. package/dist/ranking/mi/span.d.ts.map +1 -1
  80. package/dist/ranking/mi/types.d.ts +12 -0
  81. package/dist/ranking/mi/types.d.ts.map +1 -1
  82. package/dist/ranking/parse.d.ts +24 -1
  83. package/dist/ranking/parse.d.ts.map +1 -1
  84. package/package.json +6 -1
@@ -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
- //#region src/expansion/base.ts
9
+ const require_async = require("../async/index.cjs");
10
+ //#region src/expansion/base-helpers.ts
10
11
  /**
11
- * Default priority function - degree-ordered (DOME).
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
- * Run BASE expansion algorithm.
122
+ * Generator core of the BASE expansion algorithm.
18
123
  *
19
- * @param graph - Source graph
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
- * @returns Expansion result with discovered paths
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 base(graph, seeds, config) {
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 seedPriority = priority(seedNode, createPriorityContext(graph, seedNode, i, combinedVisited, allVisited, [], 0));
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 continueExpansion = () => {
57
- if (maxIterations > 0 && iterations >= maxIterations) {
58
- termination = "limit";
59
- return false;
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
- while (continueExpansion()) {
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 = graph.neighbours(nodeId);
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 frontierVisited = visitedByFrontier[activeFrontier];
126
- if (frontierVisited === void 0 || frontierVisited.has(neighbour)) continue;
127
- const neighbourPriority = priority(neighbour, createPriorityContext(graph, neighbour, activeFrontier, combinedVisited, allVisited, discoveredPaths, iterations + 1));
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 priority context for a node.
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 createPriorityContext(graph, nodeId, frontierIndex, combinedVisited, allVisited, discoveredPaths, iteration) {
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
- graph,
162
- degree: graph.degree(nodeId),
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
- * Reconstruct path from collision point.
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 reconstructPath$1(collisionNode, frontierA, frontierB, predecessors, seeds) {
174
- const pathA = [collisionNode];
175
- const predA = predecessors[frontierA];
176
- if (predA !== void 0) {
177
- let node = collisionNode;
178
- let next = predA.get(node);
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
- * Create an empty result for early termination.
208
- */
209
- function emptyResult$2(algorithm, startTime) {
210
- return {
211
- paths: [],
212
- sampledNodes: /* @__PURE__ */ new Set(),
213
- sampledEdges: /* @__PURE__ */ new Set(),
214
- visitedPerFrontier: [],
215
- stats: {
216
- iterations: 0,
217
- nodesVisited: 0,
218
- edgesTraversed: 0,
219
- pathsFound: 0,
220
- durationMs: performance.now() - startTime,
221
- algorithm,
222
- termination: "exhausted"
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: domePriority
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: randomPriorityFn
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