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