@graphty/webgpu-graph-algorithms 0.6.21 → 0.6.23

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 (54) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{context-VIvatQOo.js → context-B40Z6lV_.js} +39 -30
  4. package/dist/chunks/context-B40Z6lV_.js.map +1 -0
  5. package/dist/node.js +1 -1
  6. package/dist/src/accelerator.d.ts +1 -1
  7. package/dist/src/accelerator.d.ts.map +1 -1
  8. package/dist/src/accelerator.js +20 -1
  9. package/dist/src/accelerator.js.map +1 -1
  10. package/dist/src/algorithms/mst.d.ts +50 -0
  11. package/dist/src/algorithms/mst.d.ts.map +1 -0
  12. package/dist/src/algorithms/mst.js +245 -0
  13. package/dist/src/algorithms/mst.js.map +1 -0
  14. package/dist/src/constants.d.ts +6 -1
  15. package/dist/src/constants.d.ts.map +1 -1
  16. package/dist/src/constants.js +6 -1
  17. package/dist/src/constants.js.map +1 -1
  18. package/dist/src/index.d.ts +3 -2
  19. package/dist/src/index.d.ts.map +1 -1
  20. package/dist/src/index.js +1 -0
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/kernel/prelude.d.ts.map +1 -1
  23. package/dist/src/kernel/prelude.js +7 -1
  24. package/dist/src/kernel/prelude.js.map +1 -1
  25. package/dist/src/kernels.d.ts +5 -2
  26. package/dist/src/kernels.d.ts.map +1 -1
  27. package/dist/src/kernels.js +58 -2
  28. package/dist/src/kernels.js.map +1 -1
  29. package/dist/src/types/accelerator.d.ts +7 -5
  30. package/dist/src/types/accelerator.d.ts.map +1 -1
  31. package/dist/src/types/structure.d.ts +17 -0
  32. package/dist/src/types/structure.d.ts.map +1 -1
  33. package/dist/src/wgsl/mst-best.wgsl.d.ts +13 -0
  34. package/dist/src/wgsl/mst-best.wgsl.d.ts.map +1 -0
  35. package/dist/src/wgsl/mst-best.wgsl.js +32 -0
  36. package/dist/src/wgsl/mst-best.wgsl.js.map +1 -0
  37. package/dist/src/wgsl/mst-link.wgsl.d.ts +14 -0
  38. package/dist/src/wgsl/mst-link.wgsl.d.ts.map +1 -0
  39. package/dist/src/wgsl/mst-link.wgsl.js +39 -0
  40. package/dist/src/wgsl/mst-link.wgsl.js.map +1 -0
  41. package/dist/webgpu-graph-algorithms.js +473 -162
  42. package/dist/webgpu-graph-algorithms.js.map +1 -1
  43. package/package.json +3 -3
  44. package/src/accelerator.ts +22 -2
  45. package/src/algorithms/mst.ts +269 -0
  46. package/src/constants.ts +6 -1
  47. package/src/index.ts +3 -1
  48. package/src/kernel/prelude.ts +7 -0
  49. package/src/kernels.ts +64 -3
  50. package/src/types/accelerator.ts +7 -3
  51. package/src/types/structure.ts +18 -0
  52. package/src/wgsl/mst-best.wgsl.ts +31 -0
  53. package/src/wgsl/mst-link.wgsl.ts +38 -0
  54. package/dist/chunks/context-VIvatQOo.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { W as WebGpuGraphError, U as UNIFORM_SLOT_BYTES, B as BufferUsage, M as MAX_WORKGROUPS_PER_DIM, a as WGSL_RESERVED_WORDS, S as STATE_HEADER_BYTES, d as deviceLostError, i as isWebGpuGraphError, b as U32_MAX$2, c as MAX_LEVELS_PER_SUBMIT, R as RADIX_BINS, F as FUSED_FRONTIER_MAX, e as BEAMER_BETA, f as SSSP_DELTA_FACTOR, g as F32_INF_BITS, h as BC_EDGE_PARALLEL_GAMMA, j as BC_BATCH_BUDGET_FRACTION, k as BC_MAX_BATCH, l as BC_BACKWARD_LEVELS_PER_SUBMIT, A as APSP_MAX_DISPATCHES_PER_SUBMIT, m as APSP_TILE, n as GROUP_ROW_THREAD_MAX, o as GROUP_ROW_THREAD_LIMIT, p as GROUP_HASH_LOAD_FACTOR, P as PARALLEL_MERGE_LIMIT, L as LABEL_PROP_PASSES_PER_SUBMIT, q as GRID_COARSEST_SIDE, r as GRID_MIN_SIDE, s as GRID_SORT_BITS, t as FA2_DEFAULTS, u as MAX_ITERATIONS_PER_STEP, v as MAX_1D_ITEMS, w as hasErrorCode, x as FA2_FLAG_FIRST, y as PARTIAL_BYTES, E as EXACT_TILES_PER_PASS, I as INDIRECT_ARGS_STRIDE, z as GRID_HUB_CELL, C as LAYOUT_TUNING_DEFAULTS, D as EXACT_MAX_NODES, H as SETTLE_FLOOR_UNBOUNDED, T as TRACE_RECORD_BYTES, J as GRID_BBOX_MARGIN, K as GRID_EXTENT_FLOOR, N as FR_ADAPTIVE_MAX_ITERATIONS, O as FR_START_TEMPERATURE, Q as FA2_FLAG_ADAPTIVE, V as SETTLE_FLOOR_FRACTION, X as FR_REHEAT_FRACTION, Y as FR_DEFAULTS, Z as SE_DEFAULTS, _ as SETTLE_FLOOR_REFERENCE_NODES, $ as SE_SCALE_REFERENCE_NODES } from "./chunks/context-VIvatQOo.js";
2
- import { a0, G, a1, a2, a3, a4 } from "./chunks/context-VIvatQOo.js";
1
+ import { W as WebGpuGraphError, U as UNIFORM_SLOT_BYTES, B as BufferUsage, M as MAX_WORKGROUPS_PER_DIM, a as WGSL_RESERVED_WORDS, S as STATE_HEADER_BYTES, d as deviceLostError, i as isWebGpuGraphError, b as U32_MAX$2, c as MAX_LEVELS_PER_SUBMIT, R as RADIX_BINS, F as FUSED_FRONTIER_MAX, e as BEAMER_BETA, f as SSSP_DELTA_FACTOR, g as F32_INF_BITS, h as BC_EDGE_PARALLEL_GAMMA, j as BC_BATCH_BUDGET_FRACTION, k as BC_MAX_BATCH, l as BC_BACKWARD_LEVELS_PER_SUBMIT, A as APSP_MAX_DISPATCHES_PER_SUBMIT, m as APSP_TILE, n as GROUP_ROW_THREAD_MAX, o as GROUP_ROW_THREAD_LIMIT, p as GROUP_HASH_LOAD_FACTOR, P as PARALLEL_MERGE_LIMIT, L as LABEL_PROP_PASSES_PER_SUBMIT, q as BORUVKA_ROUNDS_PER_SUBMIT, r as GRID_COARSEST_SIDE, s as GRID_MIN_SIDE, t as GRID_SORT_BITS, u as FA2_DEFAULTS, v as MAX_ITERATIONS_PER_STEP, w as MAX_1D_ITEMS, x as hasErrorCode, y as FA2_FLAG_FIRST, z as PARTIAL_BYTES, E as EXACT_TILES_PER_PASS, I as INDIRECT_ARGS_STRIDE, C as GRID_HUB_CELL, D as LAYOUT_TUNING_DEFAULTS, H as EXACT_MAX_NODES, J as SETTLE_FLOOR_UNBOUNDED, T as TRACE_RECORD_BYTES, K as GRID_BBOX_MARGIN, N as GRID_EXTENT_FLOOR, O as FR_ADAPTIVE_MAX_ITERATIONS, Q as FR_START_TEMPERATURE, V as FA2_FLAG_ADAPTIVE, X as SETTLE_FLOOR_FRACTION, Y as FR_REHEAT_FRACTION, Z as FR_DEFAULTS, _ as SE_DEFAULTS, $ as SETTLE_FLOOR_REFERENCE_NODES, a0 as SE_SCALE_REFERENCE_NODES } from "./chunks/context-B40Z6lV_.js";
2
+ import { a1, G, a2, a3, a4, a5 } from "./chunks/context-B40Z6lV_.js";
3
3
  import { renumberPartition, INVALID_INDEX, foldArcs, makeMask, maskTest, expandEdges, fromEdgeArrays } from "@graphty/graph-format";
4
4
  class UniformRing {
5
5
  /**
@@ -2539,6 +2539,58 @@ fn lpa_step(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id)
2539
2539
  }
2540
2540
  `
2541
2541
  );
2542
+ const mstBestWgsl = (
2543
+ /* wgsl */
2544
+ `
2545
+ @compute @workgroup_size(WG)
2546
+ fn mst_best(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
2547
+ let e = linear_id(wid, lid.x);
2548
+ if (e >= P.count) { return; }
2549
+ let cu = comp[edgeSrc[e]];
2550
+ let cv = comp[edgeDst[e]];
2551
+ if (cu == cv) { return; } // inside one component, or a self-loop
2552
+ var w = 1.0;
2553
+ if (WEIGHTED) { w = edgeWeight[e]; }
2554
+ let k = order_key(w);
2555
+ if (PASS == 0u) {
2556
+ atomicMin(&bestKey[cu], k);
2557
+ atomicMin(&bestKey[cv], k);
2558
+ } else {
2559
+ if (k == atomicLoad(&bestKey[cu])) { atomicMin(&bestEdge[cu], e); }
2560
+ if (k == atomicLoad(&bestKey[cv])) { atomicMin(&bestEdge[cv], e); }
2561
+ }
2562
+ }
2563
+ `
2564
+ );
2565
+ const mstLinkWgsl = (
2566
+ /* wgsl */
2567
+ `
2568
+ var<workgroup> added: atomic<u32>;
2569
+
2570
+ @compute @workgroup_size(WG)
2571
+ fn mst_link(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
2572
+ if (lid.x == 0u) { atomicStore(&added, 0u); }
2573
+ workgroupBarrier();
2574
+ let c = linear_id(wid, lid.x);
2575
+ if (c < P.count) {
2576
+ let e = bestEdge[c];
2577
+ if (e != INVALID_INDEX) {
2578
+ let a = atomicLoad(&comp[edgeSrc[e]]);
2579
+ let b = atomicLoad(&comp[edgeDst[e]]);
2580
+ let other = select(a, b, a == c);
2581
+ let keep = bestEdge[other] == e && c <= other; // the lower root of a two-cycle stays
2582
+ if (!keep) { atomicStore(&comp[c], other); }
2583
+ if (!keep) { treeEdge[c] = e; atomicAdd(&added, 1u); }
2584
+ }
2585
+ }
2586
+ workgroupBarrier(); // every lane, unconditionally
2587
+ if (lid.x == 0u) {
2588
+ let k = atomicLoad(&added);
2589
+ if (k > 0u) { atomicAdd(&counters[P.counterIndex], k); } // one atomic per workgroup
2590
+ }
2591
+ }
2592
+ `
2593
+ );
2542
2594
  const orientFlagsWgsl = (
2543
2595
  /* wgsl */
2544
2596
  `
@@ -3544,6 +3596,12 @@ const LPA_PARAMS = UniformBlock.define("LpaParams", [
3544
3596
  ["counterIndex", "u32"],
3545
3597
  ["pad0", "u32"]
3546
3598
  ]);
3599
+ const MST_PARAMS = UniformBlock.define("MstParams", [
3600
+ ["count", "u32"],
3601
+ ["counterIndex", "u32"],
3602
+ ["pad0", "u32"],
3603
+ ["pad1", "u32"]
3604
+ ]);
3547
3605
  function decl(group, binding, name, kind, wgslType) {
3548
3606
  return { group, binding, name, kind, wgslType };
3549
3607
  }
@@ -4583,6 +4641,47 @@ const LPA_STEP = {
4583
4641
  snippetSlots: [],
4584
4642
  phase: "P11"
4585
4643
  };
4644
+ const MST_BEST = {
4645
+ id: "mst-best",
4646
+ body: mstBestWgsl,
4647
+ entryPoint: "mst_best",
4648
+ bindings: [
4649
+ decl(1, 0, "edgeSrc", "storage-ro", "array<u32>"),
4650
+ decl(1, 1, "edgeDst", "storage-ro", "array<u32>"),
4651
+ decl(1, 2, "edgeWeight", "storage-ro", "array<f32>"),
4652
+ decl(1, 3, "comp", "storage-ro", "array<u32>"),
4653
+ decl(1, 4, "bestKey", "storage", "array<atomic<u32>>"),
4654
+ decl(1, 5, "bestEdge", "storage", "array<atomic<u32>>"),
4655
+ decl(2, 0, "P", "uniform", "MstParams")
4656
+ ],
4657
+ overrideDecls: [
4658
+ { name: "PASS", type: "u32", default: 0 },
4659
+ { name: "WEIGHTED", type: "bool", default: false }
4660
+ ],
4661
+ uniforms: [MST_PARAMS],
4662
+ needs: [],
4663
+ snippetSlots: [],
4664
+ phase: "P11"
4665
+ };
4666
+ const MST_LINK = {
4667
+ id: "mst-link",
4668
+ body: mstLinkWgsl,
4669
+ entryPoint: "mst_link",
4670
+ bindings: [
4671
+ decl(1, 0, "edgeSrc", "storage-ro", "array<u32>"),
4672
+ decl(1, 1, "edgeDst", "storage-ro", "array<u32>"),
4673
+ decl(1, 2, "bestEdge", "storage-ro", "array<u32>"),
4674
+ decl(1, 3, "comp", "storage", "array<atomic<u32>>"),
4675
+ decl(1, 4, "treeEdge", "storage", "array<u32>"),
4676
+ decl(1, 5, "counters", "storage", "array<atomic<u32>>"),
4677
+ decl(2, 0, "P", "uniform", "MstParams")
4678
+ ],
4679
+ overrideDecls: [],
4680
+ uniforms: [MST_PARAMS],
4681
+ needs: [],
4682
+ snippetSlots: [],
4683
+ phase: "P11"
4684
+ };
4586
4685
  const REGISTRY = Object.freeze({
4587
4686
  degree: DEGREE,
4588
4687
  reduce: REDUCE,
@@ -4644,7 +4743,9 @@ const REGISTRY = Object.freeze({
4644
4743
  "orient-flags": ORIENT_FLAGS,
4645
4744
  "tri-intersect": TRI_INTERSECT,
4646
4745
  "group-by-key-row": GROUP_BY_KEY_ROW,
4647
- "lpa-step": LPA_STEP
4746
+ "lpa-step": LPA_STEP,
4747
+ "mst-best": MST_BEST,
4748
+ "mst-link": MST_LINK
4648
4749
  });
4649
4750
  const bodyOverrides = /* @__PURE__ */ new Map();
4650
4751
  function entryOf(id) {
@@ -4803,7 +4904,7 @@ class ScanPlannerImpl {
4803
4904
  }
4804
4905
  const POISON = 3735928559;
4805
4906
  const CHECK_BLOCKS = 32;
4806
- const RING_SLOTS$9 = 8;
4907
+ const RING_SLOTS$a = 8;
4807
4908
  const checked = /* @__PURE__ */ new WeakMap();
4808
4909
  function inputAt(i) {
4809
4910
  return i + 1;
@@ -4831,7 +4932,7 @@ async function runCheck(ctx) {
4831
4932
  const count = CHECK_BLOCKS * wg + 1;
4832
4933
  const bytes = 4 * count;
4833
4934
  const lease = ctx.pool.lease();
4834
- const ring = new UniformRing(ctx.device, ctx.allocator, RING_SLOTS$9, "device-check/ring");
4935
+ const ring = new UniformRing(ctx.device, ctx.allocator, RING_SLOTS$a, "device-check/ring");
4835
4936
  try {
4836
4937
  const scope = {
4837
4938
  device: ctx.device,
@@ -5498,12 +5599,12 @@ function algorithmScope(ctx, label, slots) {
5498
5599
  ringOverruns: () => ring.overruns
5499
5600
  };
5500
5601
  }
5501
- const ALGORITHM$8 = "connectedComponents";
5602
+ const ALGORITHM$9 = "connectedComponents";
5502
5603
  const ROUNDS_PER_BATCH$1 = 4;
5503
5604
  const MAX_WCC_ROUNDS = 64;
5504
5605
  const SAMPLE_SIZE = 1024;
5505
5606
  const MAX_STEPS = 1024;
5506
- const RING_SLOTS$8 = 2 * ROUNDS_PER_BATCH$1;
5607
+ const RING_SLOTS$9 = 2 * ROUNDS_PER_BATCH$1;
5507
5608
  function checkDest$6(dest, n) {
5508
5609
  if (dest === void 0) {
5509
5610
  return null;
@@ -5513,7 +5614,7 @@ function checkDest$6(dest, n) {
5513
5614
  }
5514
5615
  throw new WebGpuGraphError(
5515
5616
  "E_INVALID_ARGUMENT",
5516
- `${ALGORITHM$8}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
5617
+ `${ALGORITHM$9}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
5517
5618
  {
5518
5619
  argument: "dest",
5519
5620
  value: `${dest.constructor.name}(${dest.length})`,
@@ -5523,7 +5624,7 @@ function checkDest$6(dest, n) {
5523
5624
  }
5524
5625
  function coreOf$2(ctx, s) {
5525
5626
  const core = ctx.residency.core(s);
5526
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$8);
5627
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$9);
5527
5628
  return core;
5528
5629
  }
5529
5630
  function bindingOf$3(buffer, size) {
@@ -5582,8 +5683,8 @@ function checkLabels(raw) {
5582
5683
  for (let v = 0; v < n; v++) {
5583
5684
  const label = raw[v];
5584
5685
  if (label >= n) {
5585
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$8}: labels[${v}] = ${label} is not a node index`, {
5586
- label: `${ALGORITHM$8}/labels`,
5686
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$9}: labels[${v}] = ${label} is not a node index`, {
5687
+ label: `${ALGORITHM$9}/labels`,
5587
5688
  message: `the device produced a label outside [0, ${n})`
5588
5689
  });
5589
5690
  }
@@ -5601,7 +5702,7 @@ async function connectedComponents(ctx, s, options) {
5601
5702
  const renumber = options?.renumber !== false;
5602
5703
  const dest = checkDest$6(options?.dest, n);
5603
5704
  if (options?.signal?.aborted) {
5604
- throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$8}: the signal was aborted before any work started`, {});
5705
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$9}: the signal was aborted before any work started`, {});
5605
5706
  }
5606
5707
  if (n === 0) {
5607
5708
  options?.onProgress?.(1, 1);
@@ -5618,7 +5719,7 @@ async function connectedComponents(ctx, s, options) {
5618
5719
  }
5619
5720
  const edges = ctx.residency.view(s, "edgeList");
5620
5721
  const edgeCount = edges.scalars.edgeCount[0];
5621
- const scope = algorithmScope(ctx, ALGORITHM$8, RING_SLOTS$8);
5722
+ const scope = algorithmScope(ctx, ALGORITHM$9, RING_SLOTS$9);
5622
5723
  try {
5623
5724
  const compBytes = 4 * (n + 1);
5624
5725
  const comp = scope.scratch(compBytes, "comp");
@@ -5647,7 +5748,7 @@ async function connectedComponents(ctx, s, options) {
5647
5748
  return batch.submit();
5648
5749
  };
5649
5750
  queue.writeBuffer(comp, 4 * flagIndex, zero);
5650
- const setup = new CommandBatch(ctx, `${ALGORITHM$8}/setup`);
5751
+ const setup = new CommandBatch(ctx, `${ALGORITHM$9}/setup`);
5651
5752
  let pass = setup.pass("sample-rounds");
5652
5753
  const fillParams = scope.params(FILL_PARAMS, { count: n, value: 0, mode: 1, pad0: 0 });
5653
5754
  fill.dispatch(
@@ -5666,7 +5767,7 @@ async function connectedComponents(ctx, s, options) {
5666
5767
  setup.endPass();
5667
5768
  await submit2(setup).readback;
5668
5769
  ctx.assertReady();
5669
- const sampler = new CommandBatch(ctx, `${ALGORITHM$8}/sample`);
5770
+ const sampler = new CommandBatch(ctx, `${ALGORITHM$9}/sample`);
5670
5771
  pass = sampler.pass("sample");
5671
5772
  const sampleParams = wccParams({ items, stride: 0, r: 0, giant: U32_MAX$2 });
5672
5773
  sample.dispatch(
@@ -5684,7 +5785,7 @@ async function connectedComponents(ctx, s, options) {
5684
5785
  let rounds = 0;
5685
5786
  for (; ; ) {
5686
5787
  queue.writeBuffer(comp, 4 * flagIndex, zero);
5687
- const batch = new CommandBatch(ctx, `${ALGORITHM$8}/rounds`);
5788
+ const batch = new CommandBatch(ctx, `${ALGORITHM$9}/rounds`);
5688
5789
  pass = batch.pass("edge-rounds");
5689
5790
  for (let i = 0; i < ROUNDS_PER_BATCH$1; i++) {
5690
5791
  const params = wccParams({ items: edgeCount, stride: edgePlan.stride ?? edgeCount, r: 0, giant });
@@ -5699,7 +5800,7 @@ async function connectedComponents(ctx, s, options) {
5699
5800
  rounds += ROUNDS_PER_BATCH$1;
5700
5801
  ctx.assertReady();
5701
5802
  if (options?.signal?.aborted) {
5702
- throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$8}: the signal was aborted`, {
5803
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$9}: the signal was aborted`, {
5703
5804
  batchId: submitted.id
5704
5805
  });
5705
5806
  }
@@ -5709,12 +5810,12 @@ async function connectedComponents(ctx, s, options) {
5709
5810
  if (rounds >= MAX_WCC_ROUNDS) {
5710
5811
  throw new WebGpuGraphError(
5711
5812
  "E_VALIDATION",
5712
- `${ALGORITHM$8}: the changed flag never settled in ${MAX_WCC_ROUNDS} rounds`,
5713
- { label: ALGORITHM$8, message: `the changed flag never settled in ${MAX_WCC_ROUNDS} rounds` }
5813
+ `${ALGORITHM$9}: the changed flag never settled in ${MAX_WCC_ROUNDS} rounds`,
5814
+ { label: ALGORITHM$9, message: `the changed flag never settled in ${MAX_WCC_ROUNDS} rounds` }
5714
5815
  );
5715
5816
  }
5716
5817
  }
5717
- const final = new CommandBatch(ctx, `${ALGORITHM$8}/final`);
5818
+ const final = new CommandBatch(ctx, `${ALGORITHM$9}/final`);
5718
5819
  recordCompress(final.pass("compress"));
5719
5820
  final.endPass();
5720
5821
  await submit2(final).readback;
@@ -6092,7 +6193,7 @@ async function prepareSpmvPull(scope, rev, options) {
6092
6193
  return new SpmvPullPlannerImpl(scope, compiled, perm, options.weights);
6093
6194
  }
6094
6195
  const PR_BATCH = 8;
6095
- const RING_SLOTS$7 = 2 * PR_BATCH + 2;
6196
+ const RING_SLOTS$8 = 2 * PR_BATCH + 2;
6096
6197
  function checkDest$5(dest, n, algorithm) {
6097
6198
  if (dest === void 0) {
6098
6199
  return null;
@@ -6170,7 +6271,7 @@ async function run(ctx, s, personalization, options, algorithm) {
6170
6271
  const weights = useWeights ? void 0 : null;
6171
6272
  const weightedCore = useWeights ? core : { ...core, weights: null, hasWeights: false };
6172
6273
  const weightedRev = useWeights ? rev : { ...rev, weights: null, hasWeights: false };
6173
- const scope = algorithmScope(ctx, algorithm, RING_SLOTS$7);
6274
+ const scope = algorithmScope(ctx, algorithm, RING_SLOTS$8);
6174
6275
  let uploaded = null;
6175
6276
  try {
6176
6277
  const bytes = 4 * n;
@@ -6333,7 +6434,7 @@ async function personalizedPageRank(ctx, s, personalization, options) {
6333
6434
  return run(ctx, s, normalised2, options, "personalizedPageRank");
6334
6435
  }
6335
6436
  const BATCH = 8;
6336
- const RING_SLOTS$6 = 4 * BATCH + 8;
6437
+ const RING_SLOTS$7 = 4 * BATCH + 8;
6337
6438
  function checkDest$4(dest, n, algorithm) {
6338
6439
  if (dest === void 0) {
6339
6440
  return null;
@@ -6373,7 +6474,7 @@ function whole(buffer, size) {
6373
6474
  }
6374
6475
  async function runPowerIteration(ctx, n, config) {
6375
6476
  await assertDeviceComputes(ctx);
6376
- const scope = algorithmScope(ctx, config.label, RING_SLOTS$6);
6477
+ const scope = algorithmScope(ctx, config.label, RING_SLOTS$7);
6377
6478
  try {
6378
6479
  const bytes = 4 * n;
6379
6480
  const ring = (config.alternate === null ? ["rankA", "rankB"] : ["rankA", "rankB", "rankC"]).map(
@@ -7153,7 +7254,7 @@ class RadixSortPlannerImpl {
7153
7254
  return src;
7154
7255
  }
7155
7256
  }
7156
- const ALGORITHM$7 = "breadthFirstSearch";
7257
+ const ALGORITHM$8 = "breadthFirstSearch";
7157
7258
  const NEXT_DEGREE_MAX_GROUPS = 128;
7158
7259
  function bfsRingSlots(windows, levelsPerSubmit) {
7159
7260
  return Math.max((5 + 4 * windows) * levelsPerSubmit + 16, RESULT_BATCH_SLOTS + windows);
@@ -7174,7 +7275,7 @@ function checkDest$3(dest, n) {
7174
7275
  }
7175
7276
  throw new WebGpuGraphError(
7176
7277
  "E_INVALID_ARGUMENT",
7177
- `${ALGORITHM$7}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
7278
+ `${ALGORITHM$8}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
7178
7279
  {
7179
7280
  argument: "dest",
7180
7281
  value: `${dest.constructor.name}(${dest.length})`,
@@ -7189,8 +7290,8 @@ function degreeView(ctx, s, name) {
7189
7290
  const { bindings } = ctx.residency.view(s, name);
7190
7291
  const { [name]: binding } = bindings;
7191
7292
  if (binding === void 0) {
7192
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$7}: the ${name} view has no ${name} binding`, {
7193
- label: `${ALGORITHM$7}/${name}`,
7293
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$8}: the ${name} view has no ${name} binding`, {
7294
+ label: `${ALGORITHM$8}/${name}`,
7194
7295
  message: `the ${name} view has no ${name} binding`
7195
7296
  });
7196
7297
  }
@@ -7199,15 +7300,15 @@ function degreeView(ctx, s, name) {
7199
7300
  function aborted$1(batchId) {
7200
7301
  return new WebGpuGraphError(
7201
7302
  "E_ABORTED",
7202
- `${ALGORITHM$7}: the signal was aborted`,
7303
+ `${ALGORITHM$8}: the signal was aborted`,
7203
7304
  batchId === void 0 ? {} : { batchId }
7204
7305
  );
7205
7306
  }
7206
7307
  function wordOf$1(block, name) {
7207
7308
  const value = block[name];
7208
7309
  if (typeof value !== "number") {
7209
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$7}: counters.${name} did not decode to a number`, {
7210
- label: `${ALGORITHM$7}/counters`,
7310
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$8}: counters.${name} did not decode to a number`, {
7311
+ label: `${ALGORITHM$8}/counters`,
7211
7312
  message: `the field ${name} did not decode to a number`
7212
7313
  });
7213
7314
  }
@@ -7218,7 +7319,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7218
7319
  await assertDeviceComputes(ctx);
7219
7320
  const n = s.nodeCount;
7220
7321
  if (!Number.isInteger(source) || source < 0 || source >= n) {
7221
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$7}: source ${source} is outside [0, ${n})`, {
7322
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$8}: source ${source} is outside [0, ${n})`, {
7222
7323
  argument: "source",
7223
7324
  value: source,
7224
7325
  expected: `an integer in [0, ${n})`
@@ -7228,7 +7329,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7228
7329
  if (!Number.isInteger(levelsPerSubmit) || levelsPerSubmit < 1 || levelsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
7229
7330
  throw new WebGpuGraphError(
7230
7331
  "E_INVALID_ARGUMENT",
7231
- `${ALGORITHM$7}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7332
+ `${ALGORITHM$8}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7232
7333
  {
7233
7334
  argument: "levelsPerSubmit",
7234
7335
  value: levelsPerSubmit,
@@ -7247,12 +7348,12 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7247
7348
  if (s.directed && 4 * s.arcCount > ctx.caps.limits.maxStorageBufferBindingSize) {
7248
7349
  throw new WebGpuGraphError(
7249
7350
  "E_TOO_LARGE",
7250
- `${ALGORITHM$7}: the reverse adjacency of a directed snapshot (${4 * s.arcCount} bytes) needs arc windows, which no view executes (spec 4.3); the bottom-up sweep binds it whole`,
7351
+ `${ALGORITHM$8}: the reverse adjacency of a directed snapshot (${4 * s.arcCount} bytes) needs arc windows, which no view executes (spec 4.3); the bottom-up sweep binds it whole`,
7251
7352
  {
7252
7353
  needed: 4 * s.arcCount,
7253
7354
  limit: ctx.caps.limits.maxStorageBufferBindingSize,
7254
7355
  path: "windowed",
7255
- algorithm: ALGORITHM$7
7356
+ algorithm: ALGORITHM$8
7256
7357
  }
7257
7358
  );
7258
7359
  }
@@ -7260,7 +7361,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7260
7361
  const backward = coreWindows(reverse);
7261
7362
  const outDegree = degreeView(ctx, s, "outDegree");
7262
7363
  const inDegree = degreeView(ctx, s, "inDegree");
7263
- const scope = algorithmScope(ctx, ALGORITHM$7, bfsRingSlots(forward.length, levelsPerSubmit));
7364
+ const scope = algorithmScope(ctx, ALGORITHM$8, bfsRingSlots(forward.length, levelsPerSubmit));
7264
7365
  tuning.onScope?.(scope);
7265
7366
  try {
7266
7367
  const bytes = 4 * n;
@@ -7324,7 +7425,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7324
7425
  scope.flush();
7325
7426
  return batch.submit();
7326
7427
  };
7327
- const setup = new CommandBatch(ctx, `${ALGORITHM$7}/setup`);
7428
+ const setup = new CommandBatch(ctx, `${ALGORITHM$8}/setup`);
7328
7429
  const setupPass = setup.pass("fill");
7329
7430
  recordFill2(setupPass, depth, INVALID_INDEX, 0);
7330
7431
  recordFill2(setupPass, iota, 0, 1);
@@ -7344,7 +7445,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7344
7445
  let submits = 0;
7345
7446
  for (; ; ) {
7346
7447
  queue.writeBuffer(counters.buffer, counters.offset + 4 * W.unvisitedCount, new Uint32Array(3));
7347
- const batch = new CommandBatch(ctx, `${ALGORITHM$7}/levels`);
7448
+ const batch = new CommandBatch(ctx, `${ALGORITHM$8}/levels`);
7348
7449
  const pass2 = batch.pass("bfs");
7349
7450
  recordRebuild(pass2);
7350
7451
  const bitsParams = scope.params(FILL_PARAMS, { count: bitsWords, value: 0, mode: 0, pad0: 0 });
@@ -7454,8 +7555,8 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7454
7555
  if (submits > n + 1) {
7455
7556
  throw new WebGpuGraphError(
7456
7557
  "E_VALIDATION",
7457
- `${ALGORITHM$7}: the done flag never rose in ${submits} submits (a traversal has at most ${n} levels)`,
7458
- { label: ALGORITHM$7, message: `the done flag never rose in ${submits} submits` }
7558
+ `${ALGORITHM$8}: the done flag never rose in ${submits} submits (a traversal has at most ${n} levels)`,
7559
+ { label: ALGORITHM$8, message: `the done flag never rose in ${submits} submits` }
7459
7560
  );
7460
7561
  }
7461
7562
  }
@@ -7469,7 +7570,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7469
7570
  offsets: bindingOf$1(scope.scratch(histBytes, "order/offsets"), histBytes)
7470
7571
  };
7471
7572
  const parent = bindingOf$1(scope.scratch(bytes, "parent"), bytes);
7472
- const result = new CommandBatch(ctx, `${ALGORITHM$7}/result`);
7573
+ const result = new CommandBatch(ctx, `${ALGORITHM$8}/result`);
7473
7574
  result.copy(depth, keys, bytes);
7474
7575
  const pass = result.pass("result");
7475
7576
  recordFill2(pass, vals, 0, 1);
@@ -7506,9 +7607,9 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7506
7607
  if (visitedCount > n) {
7507
7608
  throw new WebGpuGraphError(
7508
7609
  "E_VALIDATION",
7509
- `${ALGORITHM$7}: visitedCount ${visitedCount} exceeds the ${n} vertices (a duplicate claim)`,
7610
+ `${ALGORITHM$8}: visitedCount ${visitedCount} exceeds the ${n} vertices (a duplicate claim)`,
7510
7611
  {
7511
- label: `${ALGORITHM$7}/visitedCount`,
7612
+ label: `${ALGORITHM$8}/visitedCount`,
7512
7613
  message: `the device counted ${visitedCount} visits of ${n} vertices`
7513
7614
  }
7514
7615
  );
@@ -7532,9 +7633,9 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
7532
7633
  function breadthFirstSearch(ctx, s, source, options) {
7533
7634
  return bfsWithTuning(ctx, s, source, options, {});
7534
7635
  }
7535
- const ALGORITHM$6 = "sssp";
7636
+ const ALGORITHM$7 = "sssp";
7536
7637
  const HALF_ALIGN = 64;
7537
- const RING_SLOTS$5 = 4 * MAX_LEVELS_PER_SUBMIT + 40;
7638
+ const RING_SLOTS$6 = 4 * MAX_LEVELS_PER_SUBMIT + 40;
7538
7639
  function bitsOf(value) {
7539
7640
  return new Uint32Array(Float32Array.of(value).buffer)[0];
7540
7641
  }
@@ -7560,8 +7661,8 @@ function assertSource(algorithm, source, n) {
7560
7661
  function wordOf(block, name) {
7561
7662
  const value = block[name];
7562
7663
  if (typeof value !== "number") {
7563
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$6}: counters.${name} did not decode to a number`, {
7564
- label: `${ALGORITHM$6}/counters`,
7664
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$7}: counters.${name} did not decode to a number`, {
7665
+ label: `${ALGORITHM$7}/counters`,
7565
7666
  message: `the field ${name} did not decode to a number`
7566
7667
  });
7567
7668
  }
@@ -7728,12 +7829,12 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7728
7829
  ctx.assertReady();
7729
7830
  await assertDeviceComputes(ctx);
7730
7831
  const n = s.nodeCount;
7731
- assertSource(ALGORITHM$6, source, n);
7832
+ assertSource(ALGORITHM$7, source, n);
7732
7833
  const roundsPerSubmit = tuning.roundsPerSubmit ?? MAX_LEVELS_PER_SUBMIT;
7733
7834
  if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
7734
7835
  throw new WebGpuGraphError(
7735
7836
  "E_INVALID_ARGUMENT",
7736
- `${ALGORITHM$6}: roundsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7837
+ `${ALGORITHM$7}: roundsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7737
7838
  {
7738
7839
  argument: "roundsPerSubmit",
7739
7840
  value: roundsPerSubmit,
@@ -7742,42 +7843,42 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7742
7843
  );
7743
7844
  }
7744
7845
  if (tuning.delta !== void 0 && !(Number.isFinite(tuning.delta) && tuning.delta > 0)) {
7745
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$6}: delta must be a finite positive number`, {
7846
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$7}: delta must be a finite positive number`, {
7746
7847
  argument: "delta",
7747
7848
  value: tuning.delta,
7748
7849
  expected: "a finite positive number"
7749
7850
  });
7750
7851
  }
7751
- const dest = checkDest$2(ALGORITHM$6, options?.dest, n);
7752
- const vector2 = resolveWeights$1(ALGORITHM$6, s, options?.weights);
7753
- const cutoff = normaliseCutoff(ALGORITHM$6, options?.cutoff);
7852
+ const dest = checkDest$2(ALGORITHM$7, options?.dest, n);
7853
+ const vector2 = resolveWeights$1(ALGORITHM$7, s, options?.weights);
7854
+ const cutoff = normaliseCutoff(ALGORITHM$7, options?.cutoff);
7754
7855
  if (options?.signal?.aborted) {
7755
- throw aborted(ALGORITHM$6);
7856
+ throw aborted(ALGORITHM$7);
7756
7857
  }
7757
7858
  if (vector2 === null || vector2.allOne) {
7758
7859
  return unitWeightRoute(ctx, s, source, cutoff, dest, options);
7759
7860
  }
7760
7861
  if (!vector2.nonNegative) {
7761
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$6}: a negative weight has no shortest path here`, {
7862
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$7}: a negative weight has no shortest path here`, {
7762
7863
  feature: "sssp.negativeWeights",
7763
7864
  hint: "use bellmanFord"
7764
7865
  });
7765
7866
  }
7766
7867
  if (!vector2.finite) {
7767
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$6}: a NaN or infinite weight has no bit-pattern order`, {
7868
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$7}: a NaN or infinite weight has no bit-pattern order`, {
7768
7869
  feature: "sssp.nonFiniteWeights"
7769
7870
  });
7770
7871
  }
7771
7872
  const { arcCount } = s;
7772
7873
  const core = ctx.residency.core(s);
7773
7874
  const limit = ctx.caps.limits.maxStorageBufferBindingSize;
7774
- assertWholeCore(core, arcCount, limit, ALGORITHM$6);
7875
+ assertWholeCore(core, arcCount, limit, ALGORITHM$7);
7775
7876
  const cap = Math.ceil(Math.max(1, arcCount) / HALF_ALIGN) * HALF_ALIGN;
7776
7877
  if (8 * cap > limit) {
7777
7878
  throw new WebGpuGraphError(
7778
7879
  "E_TOO_LARGE",
7779
- `${ALGORITHM$6}: the near-far queue of ${cap} entries per half needs ${8 * cap} bytes, above the ${limit}-byte binding limit (the relax is never windowed)`,
7780
- { needed: 8 * cap, limit, path: "sssp.queue", algorithm: ALGORITHM$6 }
7880
+ `${ALGORITHM$7}: the near-far queue of ${cap} entries per half needs ${8 * cap} bytes, above the ${limit}-byte binding limit (the relax is never windowed)`,
7881
+ { needed: 8 * cap, limit, path: "sssp.queue", algorithm: ALGORITHM$7 }
7781
7882
  );
7782
7883
  }
7783
7884
  const delta = Math.fround(
@@ -7786,7 +7887,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7786
7887
  const deltaBits = bitsOf(delta);
7787
7888
  const maxRounds = n + Math.ceil(vector2.sum / delta) + 1;
7788
7889
  const maxSubmits = Math.ceil((maxRounds + 1) / roundsPerSubmit) + 1;
7789
- const scope = algorithmScope(ctx, ALGORITHM$6, RING_SLOTS$5);
7890
+ const scope = algorithmScope(ctx, ALGORITHM$7, RING_SLOTS$6);
7790
7891
  try {
7791
7892
  const wg = ctx.workgroupSize;
7792
7893
  const bytes = 4 * n;
@@ -7824,7 +7925,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7824
7925
  scope.flush();
7825
7926
  return batch.submit();
7826
7927
  };
7827
- const setup = new CommandBatch(ctx, `${ALGORITHM$6}/setup`);
7928
+ const setup = new CommandBatch(ctx, `${ALGORITHM$7}/setup`);
7828
7929
  const setupPass = setup.pass("fill");
7829
7930
  recordFill2(setupPass, dist, n, F32_INF_BITS);
7830
7931
  recordFill2(setupPass, pred, predWords, INVALID_INDEX);
@@ -7870,7 +7971,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7870
7971
  let roundsRecorded = 0;
7871
7972
  let submits = 0;
7872
7973
  for (; ; ) {
7873
- const batch = new CommandBatch(ctx, `${ALGORITHM$6}/rounds`);
7974
+ const batch = new CommandBatch(ctx, `${ALGORITHM$7}/rounds`);
7874
7975
  const pass = batch.pass("sssp");
7875
7976
  const near = scope.params(FRONTIER_PARAMS, { ...relaxFields, role: 0 });
7876
7977
  const far = scope.params(FRONTIER_PARAMS, { ...relaxFields, role: 1 });
@@ -7893,7 +7994,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7893
7994
  submits += 1;
7894
7995
  ctx.assertReady();
7895
7996
  if (options?.signal?.aborted) {
7896
- throw aborted(ALGORITHM$6, submitted.id);
7997
+ throw aborted(ALGORITHM$7, submitted.id);
7897
7998
  }
7898
7999
  options?.onProgress?.(Math.min(roundsRecorded, maxRounds), maxRounds);
7899
8000
  if (inspect !== null && tuning.onRound !== void 0) {
@@ -7918,26 +8019,26 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7918
8019
  const needed = Math.max(wordOf(block, "nextFrontierCount"), wordOf(block, "nextFarCount"));
7919
8020
  throw new WebGpuGraphError(
7920
8021
  "E_TOO_LARGE",
7921
- `${ALGORITHM$6}: a raw pile of ${needed} entries overflowed its ${cap}-entry half`,
7922
- { needed, limit: cap, path: "sssp.pile", algorithm: ALGORITHM$6 }
8022
+ `${ALGORITHM$7}: a raw pile of ${needed} entries overflowed its ${cap}-entry half`,
8023
+ { needed, limit: cap, path: "sssp.pile", algorithm: ALGORITHM$7 }
7923
8024
  );
7924
8025
  }
7925
8026
  throw new WebGpuGraphError(
7926
8027
  "E_UNSUPPORTED",
7927
- `${ALGORITHM$6}: the f32 threshold ${wordOf(block, "thresholdBits")} absorbed the delta ${deltaBits} (as bit patterns); the far pile can no longer be bucketed`,
8028
+ `${ALGORITHM$7}: the f32 threshold ${wordOf(block, "thresholdBits")} absorbed the delta ${deltaBits} (as bit patterns); the far pile can no longer be bucketed`,
7928
8029
  { feature: "sssp.thresholdAbsorbed", hint: "the distances outgrew the delta's f32 precision" }
7929
8030
  );
7930
8031
  }
7931
8032
  if (submits > maxSubmits) {
7932
8033
  throw new WebGpuGraphError(
7933
8034
  "E_VALIDATION",
7934
- `${ALGORITHM$6}: the done flag never rose in ${submits} submits (at most ${maxRounds} rounds)`,
7935
- { label: `${ALGORITHM$6}/rounds`, message: `the done flag never rose in ${submits} submits` }
8035
+ `${ALGORITHM$7}: the done flag never rose in ${submits} submits (at most ${maxRounds} rounds)`,
8036
+ { label: `${ALGORITHM$7}/rounds`, message: `the done flag never rose in ${submits} submits` }
7936
8037
  );
7937
8038
  }
7938
8039
  }
7939
8040
  const passed = await predecessorPass({
7940
- algorithm: ALGORITHM$6,
8041
+ algorithm: ALGORITHM$7,
7941
8042
  ctx,
7942
8043
  scope,
7943
8044
  predKernel,
@@ -7953,8 +8054,8 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7953
8054
  if (passed.orphans !== 0) {
7954
8055
  throw new WebGpuGraphError(
7955
8056
  "E_VALIDATION",
7956
- `${ALGORITHM$6}: ${passed.orphans} reached node(s) the predecessor key never reached (a kernel bug)`,
7957
- { label: `${ALGORITHM$6}/pred`, message: `${passed.orphans} orphan(s) in the predecessor pass` }
8057
+ `${ALGORITHM$7}: ${passed.orphans} reached node(s) the predecessor key never reached (a kernel bug)`,
8058
+ { label: `${ALGORITHM$7}/pred`, message: `${passed.orphans} orphan(s) in the predecessor pass` }
7958
8059
  );
7959
8060
  }
7960
8061
  const distOut = dest ?? new Float32Array(n);
@@ -7973,10 +8074,10 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7973
8074
  function sssp(ctx, s, source, options) {
7974
8075
  return ssspWithTuning(ctx, s, source, options, {});
7975
8076
  }
7976
- const ALGORITHM$5 = "bellmanFord";
8077
+ const ALGORITHM$6 = "bellmanFord";
7977
8078
  const ROUNDS_PER_BATCH = 8;
7978
8079
  const MAX_RETRIES = 16;
7979
- const RING_SLOTS$4 = MAX_LEVELS_PER_SUBMIT + 16;
8080
+ const RING_SLOTS$5 = MAX_LEVELS_PER_SUBMIT + 16;
7980
8081
  function assertSymmetric(s, vector2) {
7981
8082
  const { arcToEdge, edgeToArc } = s;
7982
8083
  for (let a = 0; a < s.arcCount; a++) {
@@ -7984,7 +8085,7 @@ function assertSymmetric(s, vector2) {
7984
8085
  if (vector2[a] !== vector2[forward]) {
7985
8086
  throw new WebGpuGraphError(
7986
8087
  "E_UNSUPPORTED",
7987
- `${ALGORITHM$5}: weights[${a}] = ${vector2[a]} differs from weights[${forward}] = ${vector2[forward]}, the forward arc of the same undirected edge; the kernel reads one weight per edge`,
8088
+ `${ALGORITHM$6}: weights[${a}] = ${vector2[a]} differs from weights[${forward}] = ${vector2[forward]}, the forward arc of the same undirected edge; the kernel reads one weight per edge`,
7988
8089
  { feature: "bellmanFord.asymmetricUndirectedWeights", hint: "use a directed snapshot" }
7989
8090
  );
7990
8091
  }
@@ -7994,10 +8095,10 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7994
8095
  ctx.assertReady();
7995
8096
  await assertDeviceComputes(ctx);
7996
8097
  const n = s.nodeCount;
7997
- assertSource(ALGORITHM$5, source, n);
8098
+ assertSource(ALGORITHM$6, source, n);
7998
8099
  const maxRetries = tuning.maxRetries ?? MAX_RETRIES;
7999
8100
  if (!Number.isInteger(maxRetries) || maxRetries < 1) {
8000
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$5}: maxRetries must be an integer >= 1`, {
8101
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$6}: maxRetries must be an integer >= 1`, {
8001
8102
  argument: "maxRetries",
8002
8103
  value: maxRetries,
8003
8104
  expected: "an integer >= 1"
@@ -8007,7 +8108,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8007
8108
  if (!Number.isInteger(roundsPerBatch) || roundsPerBatch < 1 || roundsPerBatch > MAX_LEVELS_PER_SUBMIT) {
8008
8109
  throw new WebGpuGraphError(
8009
8110
  "E_INVALID_ARGUMENT",
8010
- `${ALGORITHM$5}: roundsPerBatch must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8111
+ `${ALGORITHM$6}: roundsPerBatch must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8011
8112
  {
8012
8113
  argument: "roundsPerBatch",
8013
8114
  value: roundsPerBatch,
@@ -8015,18 +8116,18 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8015
8116
  }
8016
8117
  );
8017
8118
  }
8018
- const dest = checkDest$2(ALGORITHM$5, options?.dest, n);
8019
- const vector2 = resolveWeights$1(ALGORITHM$5, s, options?.weights);
8020
- const cutoff = normaliseCutoff(ALGORITHM$5, options?.cutoff);
8119
+ const dest = checkDest$2(ALGORITHM$6, options?.dest, n);
8120
+ const vector2 = resolveWeights$1(ALGORITHM$6, s, options?.weights);
8121
+ const cutoff = normaliseCutoff(ALGORITHM$6, options?.cutoff);
8021
8122
  if (options?.signal?.aborted) {
8022
- throw aborted(ALGORITHM$5);
8123
+ throw aborted(ALGORITHM$6);
8023
8124
  }
8024
8125
  if (vector2 === null || vector2.allOne) {
8025
8126
  const unit = await unitWeightRoute(ctx, s, source, cutoff, dest, options);
8026
8127
  return { result: { ...unit, hasNegativeCycle: false }, rounds: 0, retryExhaustedRounds: 0 };
8027
8128
  }
8028
8129
  if (!vector2.finite) {
8029
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$5}: a NaN or infinite weight has no shortest path`, {
8130
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$6}: a NaN or infinite weight has no shortest path`, {
8030
8131
  feature: "bellmanFord.nonFiniteWeights"
8031
8132
  });
8032
8133
  }
@@ -8035,10 +8136,10 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8035
8136
  }
8036
8137
  const { arcCount } = s;
8037
8138
  const core = ctx.residency.core(s, ["rowPtr", "colIdx", "weights", "edgeToArc"]);
8038
- assertWholeCore(core, arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$5);
8139
+ assertWholeCore(core, arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$6);
8039
8140
  const edges = ctx.residency.view(s, "edgeList");
8040
8141
  const edgeCount = edges.scalars.edgeCount[0];
8041
- const scope = algorithmScope(ctx, ALGORITHM$5, RING_SLOTS$4);
8142
+ const scope = algorithmScope(ctx, ALGORITHM$6, RING_SLOTS$5);
8042
8143
  try {
8043
8144
  const wg = ctx.workgroupSize;
8044
8145
  const bytes = 4 * n;
@@ -8073,7 +8174,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8073
8174
  scope.flush();
8074
8175
  return batch.submit();
8075
8176
  };
8076
- const setup = new CommandBatch(ctx, `${ALGORITHM$5}/setup`);
8177
+ const setup = new CommandBatch(ctx, `${ALGORITHM$6}/setup`);
8077
8178
  const setupPass = setup.pass("fill");
8078
8179
  recordFill2(setupPass, dist, n, F32_INF_BITS);
8079
8180
  recordFill2(setupPass, pred, predWords, INVALID_INDEX);
@@ -8102,7 +8203,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8102
8203
  const zero = new Uint32Array(BF_FLAGS.byteLength / 4);
8103
8204
  const runRounds = async (count, label) => {
8104
8205
  queue.writeBuffer(flags.buffer, flags.offset, zero);
8105
- const batch = new CommandBatch(ctx, `${ALGORITHM$5}/${label}`);
8206
+ const batch = new CommandBatch(ctx, `${ALGORITHM$6}/${label}`);
8106
8207
  const pass = batch.pass("relax");
8107
8208
  const params = scope.params(BF_PARAMS, relaxFields);
8108
8209
  const bound = relax.bind({ ...relaxBindings, P: params.binding });
@@ -8127,9 +8228,9 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8127
8228
  if (decision.retryExhausted !== 0) {
8128
8229
  throw new WebGpuGraphError(
8129
8230
  "E_VALIDATION",
8130
- `${ALGORITHM$5}: a lane exhausted the ${maxRetries}-retry compare-exchange bound in the decision round, so its change is not a verdict`,
8231
+ `${ALGORITHM$6}: a lane exhausted the ${maxRetries}-retry compare-exchange bound in the decision round, so its change is not a verdict`,
8131
8232
  {
8132
- label: `${ALGORITHM$5}/retry`,
8233
+ label: `${ALGORITHM$6}/retry`,
8133
8234
  message: "retryExhausted in the decision round",
8134
8235
  batchId: decision.id
8135
8236
  }
@@ -8145,7 +8246,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8145
8246
  retryExhaustedRounds += 1;
8146
8247
  }
8147
8248
  if (options?.signal?.aborted) {
8148
- throw aborted(ALGORITHM$5, batch.id);
8249
+ throw aborted(ALGORITHM$6, batch.id);
8149
8250
  }
8150
8251
  options?.onProgress?.(rounds, n);
8151
8252
  if (batch.changed === 0 && batch.retryExhausted === 0) {
@@ -8153,7 +8254,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8153
8254
  }
8154
8255
  }
8155
8256
  const passed = await predecessorPass({
8156
- algorithm: ALGORITHM$5,
8257
+ algorithm: ALGORITHM$6,
8157
8258
  ctx,
8158
8259
  scope,
8159
8260
  predKernel,
@@ -8169,7 +8270,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8169
8270
  if (passed.orphans !== 0 && !hasNegativeCycle) {
8170
8271
  throw new WebGpuGraphError(
8171
8272
  "E_UNSUPPORTED",
8172
- `${ALGORITHM$5}: ${passed.orphans} reached node(s) the tight subgraph never reaches (a cycle of weights below one f32 ulp relaxed once)`,
8273
+ `${ALGORITHM$6}: ${passed.orphans} reached node(s) the tight subgraph never reaches (a cycle of weights below one f32 ulp relaxed once)`,
8173
8274
  {
8174
8275
  feature: "bellmanFord.roundedCycle",
8175
8276
  hint: "a cycle of weights below one f32 ulp relaxed once at a distance above 2^24; scale the weights or shorten the distances"
@@ -8196,17 +8297,17 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
8196
8297
  async function bellmanFord(ctx, s, source, options) {
8197
8298
  return (await bellmanFordWithTuning(ctx, s, source, options, {})).result;
8198
8299
  }
8199
- const ALGORITHM$4 = "betweennessCentrality";
8300
+ const ALGORITHM$5 = "betweennessCentrality";
8200
8301
  const BYTES_PER_NODE_SOURCE = 16;
8201
8302
  const SAMPLE_SEED = 2654435769;
8202
- const RING_SLOTS$3 = BC_BACKWARD_LEVELS_PER_SUBMIT + 16;
8303
+ const RING_SLOTS$4 = BC_BACKWARD_LEVELS_PER_SUBMIT + 16;
8203
8304
  function planBatchSize(n, remaining, limits) {
8204
8305
  const needed = 4 * (n + 2);
8205
8306
  if (needed > limits.maxStorageBufferBindingSize) {
8206
8307
  throw new WebGpuGraphError(
8207
8308
  "E_TOO_LARGE",
8208
- `${ALGORITHM$4}: one source needs ${needed} bytes in its largest binding at n = ${n}, above maxStorageBufferBindingSize = ${limits.maxStorageBufferBindingSize}; a limit of at least ${needed} admits one source per batch`,
8209
- { needed, limit: limits.maxStorageBufferBindingSize, path: "binding", algorithm: ALGORITHM$4 }
8309
+ `${ALGORITHM$5}: one source needs ${needed} bytes in its largest binding at n = ${n}, above maxStorageBufferBindingSize = ${limits.maxStorageBufferBindingSize}; a limit of at least ${needed} admits one source per batch`,
8310
+ { needed, limit: limits.maxStorageBufferBindingSize, path: "binding", algorithm: ALGORITHM$5 }
8210
8311
  );
8211
8312
  }
8212
8313
  const kByBinding = Math.floor(limits.maxStorageBufferBindingSize / (4 * n));
@@ -8227,7 +8328,7 @@ function drawSources(n, k) {
8227
8328
  return pool.slice(0, k);
8228
8329
  }
8229
8330
  function badArgument(argument, value, expected) {
8230
- return new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$4}: ${argument} must be ${expected}`, {
8331
+ return new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$5}: ${argument} must be ${expected}`, {
8231
8332
  argument,
8232
8333
  value,
8233
8334
  expected
@@ -8279,7 +8380,7 @@ async function submit(state, batch, signal) {
8279
8380
  const back = await submitted.readback;
8280
8381
  state.ctx.assertReady();
8281
8382
  if (signal?.aborted) {
8282
- throw aborted(ALGORITHM$4, submitted.id);
8383
+ throw aborted(ALGORITHM$5, submitted.id);
8283
8384
  }
8284
8385
  return back;
8285
8386
  }
@@ -8296,7 +8397,7 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
8296
8397
  let overflow = false;
8297
8398
  let endsWords = null;
8298
8399
  for (let first = true; endsWords === null; first = false) {
8299
- const batch = new CommandBatch(ctx, `${ALGORITHM$4}/forward`);
8400
+ const batch = new CommandBatch(ctx, `${ALGORITHM$5}/forward`);
8300
8401
  const pass = batch.pass("forward");
8301
8402
  if (first) {
8302
8403
  recordFill(state, pass, depthK, words, 4294967295);
@@ -8338,8 +8439,8 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
8338
8439
  overflow = words32[W.sigmaOverflow] !== 0;
8339
8440
  endsWords = new Uint32Array(back, endsRequest.offset, endsCount).slice(0, levels + 1);
8340
8441
  } else if (recorded > n + 2) {
8341
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$4}: the done flag never rose in ${recorded} levels`, {
8342
- label: ALGORITHM$4,
8442
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$5}: the done flag never rose in ${recorded} levels`, {
8443
+ label: ALGORITHM$5,
8343
8444
  message: `the done flag never rose in ${recorded} levels`
8344
8445
  });
8345
8446
  }
@@ -8352,7 +8453,7 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
8352
8453
  for (let i = 0; ; i += BC_BACKWARD_LEVELS_PER_SUBMIT) {
8353
8454
  const chunk = backwardLevels.slice(i, i + BC_BACKWARD_LEVELS_PER_SUBMIT);
8354
8455
  const last = i + BC_BACKWARD_LEVELS_PER_SUBMIT >= backwardLevels.length;
8355
- const batch = new CommandBatch(ctx, `${ALGORITHM$4}/backward`);
8456
+ const batch = new CommandBatch(ctx, `${ALGORITHM$5}/backward`);
8356
8457
  const pass = batch.pass("backward");
8357
8458
  for (const level of chunk) {
8358
8459
  const start = endsWords[level];
@@ -8416,11 +8517,11 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
8416
8517
  const limits = tuning.limits ?? ctx.caps.limits;
8417
8518
  const kMax = planBatchSize(n, sources.length, limits);
8418
8519
  const core = ctx.residency.core(s);
8419
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$4);
8520
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$5);
8420
8521
  const { edgeCount } = s;
8421
8522
  const mayRunEdge = pinned === "edge" || pinned === "auto" && sources.length > kMax;
8422
8523
  const edgeView = mayRunEdge && edgeCount > 0 ? ctx.residency.view(s, "edgeList") : null;
8423
- const scope = algorithmScope(ctx, ALGORITHM$4, RING_SLOTS$3);
8524
+ const scope = algorithmScope(ctx, ALGORITHM$5, RING_SLOTS$4);
8424
8525
  try {
8425
8526
  const arrayBytes = 4 * n * kMax;
8426
8527
  const lease = (bytes, label) => bindingOf(scope.scratch(bytes, label), bytes);
@@ -8460,7 +8561,7 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
8460
8561
  bound: /* @__PURE__ */ new Map()
8461
8562
  };
8462
8563
  await ctx.allocator.check();
8463
- const setup = new CommandBatch(ctx, `${ALGORITHM$4}/setup`);
8564
+ const setup = new CommandBatch(ctx, `${ALGORITHM$5}/setup`);
8464
8565
  const setupPass = setup.pass("setup");
8465
8566
  recordFill(state, setupPass, state.bc, n, 0);
8466
8567
  if (state.arcScores !== null) {
@@ -8488,7 +8589,7 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
8488
8589
  batches += 1;
8489
8590
  options?.onProgress?.(start, sources.length);
8490
8591
  }
8491
- const result = new CommandBatch(ctx, `${ALGORITHM$4}/result`);
8592
+ const result = new CommandBatch(ctx, `${ALGORITHM$5}/result`);
8492
8593
  const vertexRequest = result.readback(state.bc.buffer, state.bc.offset, 4 * n);
8493
8594
  const arcRequest = state.arcScores === null ? null : result.readback(state.arcScores.buffer, state.arcScores.offset, 4 * s.arcCount);
8494
8595
  const back = await submit(state, result, options?.signal);
@@ -8512,7 +8613,7 @@ async function precheck(ctx, options) {
8512
8613
  ctx.assertReady();
8513
8614
  await assertDeviceComputes(ctx);
8514
8615
  if (options?.endpoints === true) {
8515
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: endpoints: true is not supported`, {
8616
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$5}: endpoints: true is not supported`, {
8516
8617
  feature: "betweenness.endpoints",
8517
8618
  hint: "the CPU endpoints branch in algorithms/src/algorithms/centrality/betweenness.ts (predecessors.length === 0 && w !== source) never fires, so there is no convention to match"
8518
8619
  });
@@ -8521,10 +8622,10 @@ async function precheck(ctx, options) {
8521
8622
  async function betweennessWithTuning(ctx, s, options, tuning) {
8522
8623
  await precheck(ctx, options);
8523
8624
  const n = s.nodeCount;
8524
- const scores = checkDest$2(ALGORITHM$4, options?.dest, n) ?? new Float32Array(n);
8625
+ const scores = checkDest$2(ALGORITHM$5, options?.dest, n) ?? new Float32Array(n);
8525
8626
  const sources = resolveSources(options, n);
8526
8627
  if (options?.signal?.aborted) {
8527
- throw aborted(ALGORITHM$4);
8628
+ throw aborted(ALGORITHM$5);
8528
8629
  }
8529
8630
  if (n === 0 || sources.length === 0) {
8530
8631
  scores.fill(0);
@@ -8550,7 +8651,7 @@ async function edgeBetweennessWithTuning(ctx, s, options, tuning, onArcs) {
8550
8651
  const scores = checkDest$2("edgeBetweennessCentrality", options?.dest, s.edgeCount) ?? new Float32Array(s.edgeCount);
8551
8652
  const sources = resolveSources(options, n);
8552
8653
  if (options?.signal?.aborted) {
8553
- throw aborted(ALGORITHM$4);
8654
+ throw aborted(ALGORITHM$5);
8554
8655
  }
8555
8656
  if (n === 0 || sources.length === 0 || s.arcCount === 0) {
8556
8657
  scores.fill(0);
@@ -8571,10 +8672,10 @@ function betweennessCentrality(ctx, s, options) {
8571
8672
  function edgeBetweennessCentrality(ctx, s, options) {
8572
8673
  return edgeBetweennessWithTuning(ctx, s, options, {});
8573
8674
  }
8574
- const ALGORITHM$3 = "closenessCentrality";
8675
+ const ALGORITHM$4 = "closenessCentrality";
8575
8676
  const SOURCES_PER_BATCH = 32;
8576
8677
  const PER_SOURCE_WORDS = 4 * SOURCES_PER_BATCH;
8577
- const RING_SLOTS$2 = 8 * MAX_LEVELS_PER_SUBMIT + 16;
8678
+ const RING_SLOTS$3 = 8 * MAX_LEVELS_PER_SUBMIT + 16;
8578
8679
  function reusingScratch(scope) {
8579
8680
  const held = /* @__PURE__ */ new Map();
8580
8681
  return {
@@ -8596,7 +8697,7 @@ async function weightedRoute(ctx, s, scores, sources, options) {
8596
8697
  const totals = sources === null ? null : new Float64Array(n);
8597
8698
  for (let i = 0; i < count; i++) {
8598
8699
  if (options?.signal?.aborted) {
8599
- throw aborted(ALGORITHM$3);
8700
+ throw aborted(ALGORITHM$4);
8600
8701
  }
8601
8702
  const source = sources === null ? i : sources[i];
8602
8703
  const { dist } = await sssp(ctx, s, source, { signal: options?.signal });
@@ -8630,8 +8731,8 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8630
8731
  return { scores, iterations: 0, converged: true, precision: "f32", sourcesUsed: 0 };
8631
8732
  }
8632
8733
  const core = ctx.residency.core(s);
8633
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$3);
8634
- const scope = algorithmScope(ctx, ALGORITHM$3, RING_SLOTS$2);
8734
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$4);
8735
+ const scope = algorithmScope(ctx, ALGORITHM$4, RING_SLOTS$3);
8635
8736
  try {
8636
8737
  const wg = ctx.workgroupSize;
8637
8738
  const bytes = 4 * n;
@@ -8680,7 +8781,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8680
8781
  scope.flush();
8681
8782
  return batch.submit();
8682
8783
  };
8683
- const setup = new CommandBatch(ctx, `${ALGORITHM$3}/setup`);
8784
+ const setup = new CommandBatch(ctx, `${ALGORITHM$4}/setup`);
8684
8785
  recordFill2(setup.pass("fill"), iota, n, 1);
8685
8786
  setup.endPass();
8686
8787
  await submit2(setup).readback;
@@ -8689,7 +8790,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8689
8790
  for (let batchStart = 0; batchStart < seedCount; batchStart += SOURCES_PER_BATCH) {
8690
8791
  let level = 0;
8691
8792
  for (let first = true; ; first = false) {
8692
- const batch = new CommandBatch(ctx, `${ALGORITHM$3}/levels`);
8793
+ const batch = new CommandBatch(ctx, `${ALGORITHM$4}/levels`);
8693
8794
  const pass = batch.pass("closeness");
8694
8795
  if (first) {
8695
8796
  recordFill2(pass, bits, 4 * bitsBase, 0);
@@ -8748,7 +8849,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8748
8849
  const back = await submitted.readback;
8749
8850
  ctx.assertReady();
8750
8851
  if (options?.signal?.aborted) {
8751
- throw aborted(ALGORITHM$3, submitted.id);
8852
+ throw aborted(ALGORITHM$4, submitted.id);
8752
8853
  }
8753
8854
  if (new Uint32Array(back, doneRequest.offset, 1)[0] !== 0) {
8754
8855
  const block = new Uint32Array(back, blockRequest.offset, PER_SOURCE_WORDS);
@@ -8770,8 +8871,8 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8770
8871
  if (level > n + 3) {
8771
8872
  throw new WebGpuGraphError(
8772
8873
  "E_VALIDATION",
8773
- `${ALGORITHM$3}: the done flag never rose in ${level} levels of the batch at ${batchStart}`,
8774
- { label: ALGORITHM$3, message: `the done flag never rose in ${level} levels` }
8874
+ `${ALGORITHM$4}: the done flag never rose in ${level} levels of the batch at ${batchStart}`,
8875
+ { label: ALGORITHM$4, message: `the done flag never rose in ${level} levels` }
8775
8876
  );
8776
8877
  }
8777
8878
  }
@@ -8791,14 +8892,14 @@ function checkSources(s, sources) {
8791
8892
  return null;
8792
8893
  }
8793
8894
  if (s.directed) {
8794
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: sampled sources need an undirected snapshot`, {
8895
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: sampled sources need an undirected snapshot`, {
8795
8896
  feature: "closenessCentrality.directedSources",
8796
8897
  hint: "run the CPU port, which searches the in-arcs"
8797
8898
  });
8798
8899
  }
8799
8900
  for (const v of sources) {
8800
8901
  if (!Number.isInteger(v) || v < 0 || v >= s.nodeCount) {
8801
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$3}: a source is not a node index`, {
8902
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$4}: a source is not a node index`, {
8802
8903
  argument: "sources",
8803
8904
  value: v,
8804
8905
  expected: `an integer in [0, ${s.nodeCount})`
@@ -8812,7 +8913,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8812
8913
  await assertDeviceComputes(ctx);
8813
8914
  for (const key of ["maxIterations", "tolerance"]) {
8814
8915
  if (options?.[key] !== void 0) {
8815
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: ${key} has no meaning for an exact traversal`, {
8916
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: ${key} has no meaning for an exact traversal`, {
8816
8917
  option: key,
8817
8918
  hint: "closeness is an exact traversal; the option has no meaning here"
8818
8919
  });
@@ -8824,7 +8925,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8824
8925
  if (!Number.isInteger(levelsPerSubmit) || levelsPerSubmit < 1 || levelsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
8825
8926
  throw new WebGpuGraphError(
8826
8927
  "E_INVALID_ARGUMENT",
8827
- `${ALGORITHM$3}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8928
+ `${ALGORITHM$4}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8828
8929
  {
8829
8930
  argument: "levelsPerSubmit",
8830
8931
  value: levelsPerSubmit,
@@ -8832,16 +8933,16 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8832
8933
  }
8833
8934
  );
8834
8935
  }
8835
- const scores = checkDest$2(ALGORITHM$3, options?.dest, n) ?? new Float32Array(n);
8936
+ const scores = checkDest$2(ALGORITHM$4, options?.dest, n) ?? new Float32Array(n);
8836
8937
  const weighted = options?.weighted ?? s.flags.weighted;
8837
8938
  if (options?.signal?.aborted) {
8838
- throw aborted(ALGORITHM$3);
8939
+ throw aborted(ALGORITHM$4);
8839
8940
  }
8840
8941
  if (weighted && s.weights !== null && !s.flags.allWeightsOne) {
8841
8942
  if (!s.flags.nonNegativeWeights) {
8842
8943
  throw new WebGpuGraphError(
8843
8944
  "E_UNSUPPORTED",
8844
- `${ALGORITHM$3}: a negative weight has no shortest-path distance to sum`,
8945
+ `${ALGORITHM$4}: a negative weight has no shortest-path distance to sum`,
8845
8946
  {
8846
8947
  feature: "closenessCentrality.negativeWeights",
8847
8948
  hint: "pass weighted: false to ignore the column"
@@ -8849,7 +8950,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8849
8950
  );
8850
8951
  }
8851
8952
  if (!s.flags.finiteWeights) {
8852
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a NaN or infinite weight has no shortest path`, {
8953
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: a NaN or infinite weight has no shortest path`, {
8853
8954
  feature: "closenessCentrality.nonFiniteWeights"
8854
8955
  });
8855
8956
  }
@@ -8860,7 +8961,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8860
8961
  function closenessCentrality(ctx, s, options) {
8861
8962
  return closenessWithTuning(ctx, s, options, {});
8862
8963
  }
8863
- const ALGORITHM$2 = "allPairsShortestPath";
8964
+ const ALGORITHM$3 = "allPairsShortestPath";
8864
8965
  const DEFAULT_ROUNDS_PER_SUBMIT = Math.floor(APSP_MAX_DISPATCHES_PER_SUBMIT / 3);
8865
8966
  function allPairsCeiling(limits) {
8866
8967
  const binding = limits.maxStorageBufferBindingSize;
@@ -8883,7 +8984,7 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
8883
8984
  if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > DEFAULT_ROUNDS_PER_SUBMIT) {
8884
8985
  throw new WebGpuGraphError(
8885
8986
  "E_INVALID_ARGUMENT",
8886
- `${ALGORITHM$2}: roundsPerSubmit must be an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`,
8987
+ `${ALGORITHM$3}: roundsPerSubmit must be an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`,
8887
8988
  {
8888
8989
  argument: "roundsPerSubmit",
8889
8990
  value: roundsPerSubmit,
@@ -8895,12 +8996,12 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
8895
8996
  if (n > maxNodes) {
8896
8997
  throw new WebGpuGraphError(
8897
8998
  "E_TOO_LARGE",
8898
- `${ALGORITHM$2}: ${n} nodes need a ${4 * n * n}-byte distance matrix in one storage binding; this device's ${limitName} of ${limit} bytes holds at most ${maxNodes} nodes -- raise it through GpuContextOptions.limits`,
8999
+ `${ALGORITHM$3}: ${n} nodes need a ${4 * n * n}-byte distance matrix in one storage binding; this device's ${limitName} of ${limit} bytes holds at most ${maxNodes} nodes -- raise it through GpuContextOptions.limits`,
8899
9000
  {
8900
9001
  needed: 4 * n * n,
8901
9002
  limit,
8902
9003
  path: "allPairs.matrix",
8903
- algorithm: ALGORITHM$2,
9004
+ algorithm: ALGORITHM$3,
8904
9005
  nodes: n,
8905
9006
  maxNodes,
8906
9007
  limitName,
@@ -8908,30 +9009,30 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
8908
9009
  }
8909
9010
  );
8910
9011
  }
8911
- const dist = checkDest$2(ALGORITHM$2, options?.dest, n * n) ?? new Float32Array(n * n);
9012
+ const dist = checkDest$2(ALGORITHM$3, options?.dest, n * n) ?? new Float32Array(n * n);
8912
9013
  const weighted = (options?.weighted ?? s.weights !== null) && s.weights !== null && !s.flags.allWeightsOne;
8913
9014
  if (weighted && !s.flags.nonNegativeWeights) {
8914
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$2}: a negative weight is not supported`, {
9015
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a negative weight is not supported`, {
8915
9016
  feature: "allPairs.negativeWeights",
8916
9017
  hint: "the blocked Floyd-Warshall sweep needs non-negative weights; pass weighted: false for hop counts"
8917
9018
  });
8918
9019
  }
8919
9020
  if (weighted && !s.flags.finiteWeights) {
8920
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$2}: a NaN or infinite weight has no shortest path`, {
9021
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a NaN or infinite weight has no shortest path`, {
8921
9022
  feature: "allPairs.nonFiniteWeights"
8922
9023
  });
8923
9024
  }
8924
9025
  if (options?.signal?.aborted) {
8925
- throw aborted(ALGORITHM$2);
9026
+ throw aborted(ALGORITHM$3);
8926
9027
  }
8927
9028
  if (n === 0) {
8928
9029
  return { dist, n };
8929
9030
  }
8930
9031
  await assertDeviceComputes(ctx);
8931
9032
  const core = ctx.residency.core(s);
8932
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$2);
9033
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$3);
8933
9034
  const blocks = Math.ceil(n / APSP_TILE);
8934
- const scope = algorithmScope(ctx, ALGORITHM$2, Math.min(roundsPerSubmit, blocks) + 2);
9035
+ const scope = algorithmScope(ctx, ALGORITHM$3, Math.min(roundsPerSubmit, blocks) + 2);
8935
9036
  try {
8936
9037
  const wg = ctx.workgroupSize;
8937
9038
  const bytes = 4 * n * n;
@@ -8947,7 +9048,7 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
8947
9048
  const others = blocks - 1;
8948
9049
  const phasePlans = [plan2d(1, ctx.caps), plan2d(2 * others, ctx.caps), plan2d(others * others, ctx.caps)];
8949
9050
  for (let first = 0; first < blocks; first += roundsPerSubmit) {
8950
- const batch = new CommandBatch(ctx, `${ALGORITHM$2}/sweep`);
9051
+ const batch = new CommandBatch(ctx, `${ALGORITHM$3}/sweep`);
8951
9052
  const pass = batch.pass("apsp");
8952
9053
  if (first === 0) {
8953
9054
  const fillParams = scope.params(FILL_PARAMS, { count: n * n, value: F32_INF_BITS, mode: 0, pad0: 0 });
@@ -8979,7 +9080,7 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
8979
9080
  ctx.assertReady();
8980
9081
  options?.onProgress?.(last, blocks);
8981
9082
  if (options?.signal?.aborted) {
8982
- throw aborted(ALGORITHM$2, submitted.id);
9083
+ throw aborted(ALGORITHM$3, submitted.id);
8983
9084
  }
8984
9085
  }
8985
9086
  await ctx.readback.read(matrix.buffer, bytes, dist);
@@ -9631,9 +9732,9 @@ function runCore(runStart, runs, sortedDst, sortedW, valid) {
9631
9732
  hasWeights: true
9632
9733
  };
9633
9734
  }
9634
- const ALGORITHM$1 = "labelPropagation";
9735
+ const ALGORITHM$2 = "labelPropagation";
9635
9736
  const DEFAULT_MAX_ITERATIONS = 100;
9636
- const RING_SLOTS$1 = 1024;
9737
+ const RING_SLOTS$2 = 1024;
9637
9738
  function checkDest$1(dest, n) {
9638
9739
  if (dest === void 0) {
9639
9740
  return null;
@@ -9643,7 +9744,7 @@ function checkDest$1(dest, n) {
9643
9744
  }
9644
9745
  throw new WebGpuGraphError(
9645
9746
  "E_INVALID_ARGUMENT",
9646
- `${ALGORITHM$1}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
9747
+ `${ALGORITHM$2}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
9647
9748
  {
9648
9749
  argument: "dest",
9649
9750
  value: `${dest.constructor.name}(${dest.length})`,
@@ -9656,7 +9757,7 @@ function maxIterationsOf(value) {
9656
9757
  return DEFAULT_MAX_ITERATIONS;
9657
9758
  }
9658
9759
  if (!Number.isSafeInteger(value) || value < 0) {
9659
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$1}: maxIterations must be a non-negative integer`, {
9760
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$2}: maxIterations must be a non-negative integer`, {
9660
9761
  argument: "maxIterations",
9661
9762
  value,
9662
9763
  expected: "a non-negative integer"
@@ -9676,12 +9777,12 @@ function neighbourBound(s) {
9676
9777
  }
9677
9778
  return bound;
9678
9779
  }
9679
- function resultOf(raw, dest) {
9780
+ function resultOf$1(raw, dest) {
9680
9781
  const n = raw.length;
9681
9782
  for (let v = 0; v < n; v++) {
9682
9783
  if (raw[v] >= n) {
9683
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: labels[${v}] = ${raw[v]} is not a node index`, {
9684
- label: `${ALGORITHM$1}/labels`,
9784
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$2}: labels[${v}] = ${raw[v]} is not a node index`, {
9785
+ label: `${ALGORITHM$2}/labels`,
9685
9786
  message: `the device produced a label outside [0, ${n})`
9686
9787
  });
9687
9788
  }
@@ -9704,23 +9805,23 @@ async function labelPropagation(ctx, s, options) {
9704
9805
  const maxIterations = maxIterationsOf(options?.maxIterations);
9705
9806
  const weighted = options?.weighted !== false;
9706
9807
  if (options?.signal?.aborted) {
9707
- throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted before any work started`, {});
9808
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$2}: the signal was aborted before any work started`, {});
9708
9809
  }
9709
9810
  if (n === 0 || s.edgeCount === 0 || maxIterations === 0) {
9710
9811
  options?.onProgress?.(1, 1);
9711
9812
  return identityResult(n, dest);
9712
9813
  }
9713
9814
  const plan = planGroupRows(neighbourBound(s));
9714
- assertBindable(ctx, 4 * plan.regionWords, "the group-by hash region", ALGORITHM$1);
9715
- const scope = algorithmScope(ctx, ALGORITHM$1, RING_SLOTS$1);
9815
+ assertBindable(ctx, 4 * plan.regionWords, "the group-by hash region", ALGORITHM$2);
9816
+ const scope = algorithmScope(ctx, ALGORITHM$2, RING_SLOTS$2);
9716
9817
  try {
9717
- const build = await buildSimpleSymmetric(ctx, s, scope, weighted, ALGORITHM$1);
9818
+ const build = await buildSimpleSymmetric(ctx, s, scope, weighted, ALGORITHM$2);
9718
9819
  const { graph } = build;
9719
9820
  if (graph.colIdx === null) {
9720
9821
  scope.flush();
9721
9822
  const bytes = await build.batch.submit().readback;
9722
9823
  ctx.assertReady();
9723
- assertBuildSorted(bytes, build, ALGORITHM$1);
9824
+ assertBuildSorted(bytes, build, ALGORITHM$2);
9724
9825
  options?.onProgress?.(1, 1);
9725
9826
  return identityResult(n, dest);
9726
9827
  }
@@ -9792,12 +9893,12 @@ async function labelPropagation(ctx, s, options) {
9792
9893
  const bytes = await submitted.readback;
9793
9894
  ctx.assertReady();
9794
9895
  if (first) {
9795
- assertBuildSorted(bytes, build, ALGORITHM$1);
9896
+ assertBuildSorted(bytes, build, ALGORITHM$2);
9796
9897
  first = false;
9797
9898
  }
9798
9899
  if (new Uint32Array(bytes, exhaustedRequest.offset, 1)[0] !== 0) {
9799
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: a hash probe exhausted its bound`, {
9800
- label: `${ALGORITHM$1}/group-by-key`,
9900
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$2}: a hash probe exhausted its bound`, {
9901
+ label: `${ALGORITHM$2}/group-by-key`,
9801
9902
  message: "a compare-exchange loop of the workgroup tier ran out of steps"
9802
9903
  });
9803
9904
  }
@@ -9806,7 +9907,7 @@ async function labelPropagation(ctx, s, options) {
9806
9907
  const lastTwo = k >= 2 ? moves[k - 2] + moves[k - 1] : previousLast + moves[0];
9807
9908
  previousLast = moves[k - 1];
9808
9909
  if (options?.signal?.aborted) {
9809
- throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted`, {
9910
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$2}: the signal was aborted`, {
9810
9911
  batchId: submitted.id
9811
9912
  });
9812
9913
  }
@@ -9814,12 +9915,203 @@ async function labelPropagation(ctx, s, options) {
9814
9915
  if (lastTwo === 0 || done >= maxIterations) {
9815
9916
  break;
9816
9917
  }
9817
- batch = new CommandBatch(ctx, `${ALGORITHM$1}/passes`);
9918
+ batch = new CommandBatch(ctx, `${ALGORITHM$2}/passes`);
9818
9919
  }
9819
9920
  const raw = new Uint32Array(n);
9820
9921
  await ctx.readback.read(cur.buffer, 4 * n, raw);
9821
9922
  ctx.assertReady();
9822
- return resultOf(raw, dest);
9923
+ return resultOf$1(raw, dest);
9924
+ } finally {
9925
+ scope.dispose();
9926
+ }
9927
+ }
9928
+ const ALGORITHM$1 = "minimumSpanningTree";
9929
+ const COMPRESS_STEPS = 1024;
9930
+ const MAX_ROUNDS = 64;
9931
+ const RING_SLOTS$1 = 5 + BORUVKA_ROUNDS_PER_SUBMIT;
9932
+ function compressPasses(n) {
9933
+ let passes = 1;
9934
+ for (let reach = COMPRESS_STEPS; reach < n; reach *= COMPRESS_STEPS) {
9935
+ passes++;
9936
+ }
9937
+ return passes;
9938
+ }
9939
+ function resultOf(s, treeEdge, expected) {
9940
+ const edges = new Uint32Array(expected);
9941
+ const { weights } = s.edgeList();
9942
+ let taken = 0;
9943
+ let totalWeight = 0;
9944
+ for (const e of treeEdge) {
9945
+ if (e === INVALID_INDEX) {
9946
+ continue;
9947
+ }
9948
+ if (e >= s.edgeCount || taken === expected) {
9949
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: the device recorded an edge it did not count`, {
9950
+ label: `${ALGORITHM$1}/treeEdge`,
9951
+ message: `edge ${e} at forest position ${taken} of ${expected}`
9952
+ });
9953
+ }
9954
+ edges[taken++] = e;
9955
+ totalWeight += weights === null ? 1 : weights[e];
9956
+ }
9957
+ if (taken !== expected) {
9958
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: ${taken} recorded edges, ${expected} counted`, {
9959
+ label: `${ALGORITHM$1}/treeEdge`,
9960
+ message: "the per-round counts disagree with the recorded forest"
9961
+ });
9962
+ }
9963
+ return { edges, totalWeight };
9964
+ }
9965
+ async function minimumSpanningTree(ctx, s, options) {
9966
+ ctx.assertReady();
9967
+ await assertDeviceComputes(ctx);
9968
+ if (options?.dest !== void 0) {
9969
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$1}: dest is not supported`, {
9970
+ option: "dest",
9971
+ hint: "the forest's edge count is known only after the run"
9972
+ });
9973
+ }
9974
+ if (options?.signal?.aborted) {
9975
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted before any work started`, {});
9976
+ }
9977
+ const n = s.nodeCount;
9978
+ const m = s.edgeCount;
9979
+ if (n === 0 || m === 0) {
9980
+ options?.onProgress?.(1, 1);
9981
+ return { edges: new Uint32Array(0), totalWeight: 0 };
9982
+ }
9983
+ ctx.residency.core(s, ["rowPtr"]);
9984
+ const edges = ctx.residency.view(s, "edgeList");
9985
+ const edgeWeight = edges.bindings.weights ?? null;
9986
+ const scope = algorithmScope(ctx, ALGORITHM$1, RING_SLOTS$1);
9987
+ try {
9988
+ const wg = ctx.workgroupSize;
9989
+ const words = (count, label) => {
9990
+ const size = 4 * Math.max(1, count);
9991
+ return { buffer: scope.scratch(size, label), offset: 0, size, window: null };
9992
+ };
9993
+ const comp = words(n, "comp");
9994
+ const bestKey = words(n, "bestKey");
9995
+ const bestEdge = words(n, "bestEdge");
9996
+ const treeEdge = words(n, "treeEdge");
9997
+ const counters = words(BORUVKA_ROUNDS_PER_SUBMIT, "counters");
9998
+ await ctx.allocator.check();
9999
+ const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
10000
+ const weighted = edgeWeight !== null;
10001
+ const minKey = await ctx.pipelines.kernel(kernelSpec("mst-best", { PASS: 0, WEIGHTED: weighted }));
10002
+ const minEdge = await ctx.pipelines.kernel(kernelSpec("mst-best", { PASS: 1, WEIGHTED: weighted }));
10003
+ const link = await ctx.pipelines.kernel(kernelSpec("mst-link"));
10004
+ const compress = await ctx.pipelines.kernel(kernelSpec("wcc-compress"));
10005
+ const nodePlan = plan1d(n, wg, ctx.caps);
10006
+ const edgePlan = plan1d(m, wg, ctx.caps);
10007
+ const compressPlan = planGridStride(n, wg, ctx.caps);
10008
+ const passes = compressPasses(n);
10009
+ const { queue } = ctx.device;
10010
+ let batch = new CommandBatch(ctx, `${ALGORITHM$1}/rounds`);
10011
+ let first = true;
10012
+ let rounds = 0;
10013
+ let recorded = 0;
10014
+ for (; ; ) {
10015
+ queue.writeBuffer(counters.buffer, 0, new Uint32Array(BORUVKA_ROUNDS_PER_SUBMIT));
10016
+ const pass = batch.pass("rounds");
10017
+ const fillFor = (value, mode) => scope.params(FILL_PARAMS, { count: n, value, mode, pad0: 0 });
10018
+ if (first) {
10019
+ const iota = fillFor(0, 1);
10020
+ fill.dispatch(pass, fill.bind({ dst: comp, P: iota.binding }), nodePlan, [iota.offset]);
10021
+ const none = fillFor(INVALID_INDEX, 0);
10022
+ fill.dispatch(pass, fill.bind({ dst: treeEdge, P: none.binding }), nodePlan, [none.offset]);
10023
+ }
10024
+ const reset = fillFor(U32_MAX$2, 0);
10025
+ const edgeParams = scope.params(MST_PARAMS, { count: m, counterIndex: 0, pad0: 0, pad1: 0 });
10026
+ const compressParams = scope.params(WCC_PARAMS, {
10027
+ n,
10028
+ items: n,
10029
+ stride: compressPlan.stride ?? n,
10030
+ r: 0,
10031
+ flagIndex: 0,
10032
+ giant: U32_MAX$2,
10033
+ maxSteps: COMPRESS_STEPS,
10034
+ pad0: 0
10035
+ });
10036
+ const graph = {
10037
+ edgeSrc: edges.bindings.src,
10038
+ edgeDst: edges.bindings.dst,
10039
+ edgeWeight: edgeWeight ?? edges.bindings.src
10040
+ };
10041
+ for (let i = 0; i < BORUVKA_ROUNDS_PER_SUBMIT; i++) {
10042
+ for (const dst of [bestKey, bestEdge]) {
10043
+ fill.dispatch(pass, fill.bind({ dst, P: reset.binding }), nodePlan, [reset.offset]);
10044
+ }
10045
+ for (const kernel of [minKey, minEdge]) {
10046
+ kernel.dispatch(
10047
+ pass,
10048
+ kernel.bind({ ...graph, comp, bestKey, bestEdge, P: edgeParams.binding }),
10049
+ edgePlan,
10050
+ [edgeParams.offset]
10051
+ );
10052
+ }
10053
+ const linkParams = scope.params(MST_PARAMS, { count: n, counterIndex: i, pad0: 0, pad1: 0 });
10054
+ link.dispatch(
10055
+ pass,
10056
+ link.bind({
10057
+ edgeSrc: graph.edgeSrc,
10058
+ edgeDst: graph.edgeDst,
10059
+ bestEdge,
10060
+ comp,
10061
+ treeEdge,
10062
+ counters,
10063
+ P: linkParams.binding
10064
+ }),
10065
+ nodePlan,
10066
+ [linkParams.offset]
10067
+ );
10068
+ for (let j = 0; j < passes; j++) {
10069
+ compress.dispatch(pass, compress.bind({ comp, P: compressParams.binding }), compressPlan, [
10070
+ compressParams.offset
10071
+ ]);
10072
+ }
10073
+ }
10074
+ batch.endPass();
10075
+ const countsRequest = batch.readback(counters.buffer, 0, 4 * BORUVKA_ROUNDS_PER_SUBMIT);
10076
+ scope.flush();
10077
+ const submitted = batch.submit();
10078
+ const bytes = await submitted.readback;
10079
+ ctx.assertReady();
10080
+ first = false;
10081
+ if (options?.signal?.aborted) {
10082
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted`, {
10083
+ batchId: submitted.id
10084
+ });
10085
+ }
10086
+ const counts = new Uint32Array(bytes, countsRequest.offset, BORUVKA_ROUNDS_PER_SUBMIT);
10087
+ let settled2 = false;
10088
+ for (const count of counts) {
10089
+ recorded += count;
10090
+ settled2 ||= count === 0;
10091
+ }
10092
+ rounds += BORUVKA_ROUNDS_PER_SUBMIT;
10093
+ if (recorded > n - 1) {
10094
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: ${recorded} forest edges on ${n} nodes`, {
10095
+ label: `${ALGORITHM$1}/counters`,
10096
+ message: "a spanning forest has at most n - 1 edges"
10097
+ });
10098
+ }
10099
+ if (settled2) {
10100
+ break;
10101
+ }
10102
+ if (rounds >= MAX_ROUNDS) {
10103
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: still merging after ${rounds} rounds`, {
10104
+ label: `${ALGORITHM$1}/rounds`,
10105
+ message: "Boruvka halves the components every round; the device did not"
10106
+ });
10107
+ }
10108
+ batch = new CommandBatch(ctx, `${ALGORITHM$1}/rounds`);
10109
+ }
10110
+ const raw = new Uint32Array(n);
10111
+ await ctx.readback.read(treeEdge.buffer, 4 * n, raw);
10112
+ ctx.assertReady();
10113
+ options?.onProgress?.(1, 1);
10114
+ return resultOf(s, raw, recorded);
9823
10115
  } finally {
9824
10116
  scope.dispose();
9825
10117
  }
@@ -15089,6 +15381,24 @@ function createAccelerator(ctx, options) {
15089
15381
  }
15090
15382
  return await labelPropagation(ctx, gs, { maxIterations: o?.maxIterations, weighted: o?.weighted });
15091
15383
  },
15384
+ /**
15385
+ * Boruvka's minimum spanning forest (design 8.5; P11-T4): the forest of the total edge order (weight, then
15386
+ * edge index), which is the one `kruskalMST` accepts. The seam's per-arc `weights` override is refused when
15387
+ * defined: the forest runs over the snapshot's own edge weights.
15388
+ * @param gs - the snapshot
15389
+ * @param o - the seam's `MstOptions`; `weights` refused when defined
15390
+ * @returns the forest's logical edge indices and its f64 total weight
15391
+ */
15392
+ async minimumSpanningTree(gs, o) {
15393
+ ctx.assertReady();
15394
+ if (o?.weights !== void 0) {
15395
+ throw new WebGpuGraphError("E_UNSUPPORTED", "minimumSpanningTree: weights is not supported", {
15396
+ option: "weights",
15397
+ hint: "the spanning forest runs over the snapshot's own edge weights"
15398
+ });
15399
+ }
15400
+ return await minimumSpanningTree(ctx, gs);
15401
+ },
15092
15402
  /**
15093
15403
  * Destroys every device buffer recorded for the snapshot (spec 4.5); delegates to ctx.release.
15094
15404
  * @param s - the snapshot the app is done with
@@ -15198,7 +15508,7 @@ async function calibrateLayout(ctx, options) {
15198
15508
  };
15199
15509
  }
15200
15510
  export {
15201
- a0 as ARC_WINDOW_ALIGN,
15511
+ a1 as ARC_WINDOW_ALIGN,
15202
15512
  EXACT_MAX_NODES,
15203
15513
  FA2_DEFAULTS,
15204
15514
  FR_DEFAULTS,
@@ -15206,10 +15516,10 @@ export {
15206
15516
  LAYOUT_TUNING_DEFAULTS,
15207
15517
  MAX_1D_ITEMS,
15208
15518
  MAX_WORKGROUPS_PER_DIM,
15209
- a1 as PASSTHROUGH_FORMAT_CODES,
15519
+ a2 as PASSTHROUGH_FORMAT_CODES,
15210
15520
  SE_DEFAULTS,
15211
- a2 as STORAGE_ALIGN,
15212
- a3 as WORKGROUP_SIZE,
15521
+ a3 as STORAGE_ALIGN,
15522
+ a4 as WORKGROUP_SIZE,
15213
15523
  WebGpuGraphError,
15214
15524
  allPairsShortestPath,
15215
15525
  bellmanFord,
@@ -15227,10 +15537,11 @@ export {
15227
15537
  eigenvectorCentrality,
15228
15538
  hasErrorCode,
15229
15539
  hits,
15230
- a4 as isSoftwareAdapter,
15540
+ a5 as isSoftwareAdapter,
15231
15541
  isWebGpuGraphError,
15232
15542
  katzCentrality,
15233
15543
  labelPropagation,
15544
+ minimumSpanningTree,
15234
15545
  pageRank,
15235
15546
  personalizedPageRank,
15236
15547
  seedPositions,