@graphty/webgpu-graph-algorithms 0.6.15 → 0.6.16

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 (134) hide show
  1. package/README.md +5 -5
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{context-oXphO3yj.js → context-VIvatQOo.js} +61 -34
  4. package/dist/chunks/context-VIvatQOo.js.map +1 -0
  5. package/dist/node.js +1 -1
  6. package/dist/src/accelerator.d.ts +2 -1
  7. package/dist/src/accelerator.d.ts.map +1 -1
  8. package/dist/src/accelerator.js +53 -1
  9. package/dist/src/accelerator.js.map +1 -1
  10. package/dist/src/algorithms/all-pairs.d.ts +41 -0
  11. package/dist/src/algorithms/all-pairs.d.ts.map +1 -0
  12. package/dist/src/algorithms/all-pairs.js +181 -0
  13. package/dist/src/algorithms/all-pairs.js.map +1 -0
  14. package/dist/src/algorithms/components.d.ts +9 -1
  15. package/dist/src/algorithms/components.d.ts.map +1 -1
  16. package/dist/src/algorithms/components.js +2 -2
  17. package/dist/src/algorithms/components.js.map +1 -1
  18. package/dist/src/algorithms/label-propagation.d.ts +31 -0
  19. package/dist/src/algorithms/label-propagation.d.ts.map +1 -0
  20. package/dist/src/algorithms/label-propagation.js +254 -0
  21. package/dist/src/algorithms/label-propagation.js.map +1 -0
  22. package/dist/src/algorithms/simple-symmetric.d.ts +88 -0
  23. package/dist/src/algorithms/simple-symmetric.d.ts.map +1 -0
  24. package/dist/src/algorithms/simple-symmetric.js +347 -0
  25. package/dist/src/algorithms/simple-symmetric.js.map +1 -0
  26. package/dist/src/algorithms/triangles.d.ts +34 -0
  27. package/dist/src/algorithms/triangles.d.ts.map +1 -0
  28. package/dist/src/algorithms/triangles.js +203 -0
  29. package/dist/src/algorithms/triangles.js.map +1 -0
  30. package/dist/src/constants.d.ts +45 -0
  31. package/dist/src/constants.d.ts.map +1 -1
  32. package/dist/src/constants.js +45 -0
  33. package/dist/src/constants.js.map +1 -1
  34. package/dist/src/index.d.ts +8 -1
  35. package/dist/src/index.d.ts.map +1 -1
  36. package/dist/src/index.js +7 -1
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/kernel/prelude.d.ts.map +1 -1
  39. package/dist/src/kernel/prelude.js +4 -1
  40. package/dist/src/kernel/prelude.js.map +1 -1
  41. package/dist/src/kernels.d.ts +16 -4
  42. package/dist/src/kernels.d.ts.map +1 -1
  43. package/dist/src/kernels.js +223 -3
  44. package/dist/src/kernels.js.map +1 -1
  45. package/dist/src/memory/residency.js +15 -4
  46. package/dist/src/memory/residency.js.map +1 -1
  47. package/dist/src/primitives/coo-to-csr.d.ts +73 -0
  48. package/dist/src/primitives/coo-to-csr.d.ts.map +1 -0
  49. package/dist/src/primitives/coo-to-csr.js +183 -0
  50. package/dist/src/primitives/coo-to-csr.js.map +1 -0
  51. package/dist/src/primitives/group-by-key.d.ts +82 -0
  52. package/dist/src/primitives/group-by-key.d.ts.map +1 -0
  53. package/dist/src/primitives/group-by-key.js +147 -0
  54. package/dist/src/primitives/group-by-key.js.map +1 -0
  55. package/dist/src/types/accelerator.d.ts +10 -2
  56. package/dist/src/types/accelerator.d.ts.map +1 -1
  57. package/dist/src/types/all-pairs.d.ts +35 -0
  58. package/dist/src/types/all-pairs.d.ts.map +1 -0
  59. package/dist/src/types/all-pairs.js +8 -0
  60. package/dist/src/types/all-pairs.js.map +1 -0
  61. package/dist/src/types/community.d.ts +18 -0
  62. package/dist/src/types/community.d.ts.map +1 -0
  63. package/dist/src/types/community.js +5 -0
  64. package/dist/src/types/community.js.map +1 -0
  65. package/dist/src/types/structure.d.ts +27 -0
  66. package/dist/src/types/structure.d.ts.map +1 -0
  67. package/dist/src/types/structure.js +8 -0
  68. package/dist/src/types/structure.js.map +1 -0
  69. package/dist/src/wgsl/apsp-fw.wgsl.d.ts +25 -0
  70. package/dist/src/wgsl/apsp-fw.wgsl.d.ts.map +1 -0
  71. package/dist/src/wgsl/apsp-fw.wgsl.js +113 -0
  72. package/dist/src/wgsl/apsp-fw.wgsl.js.map +1 -0
  73. package/dist/src/wgsl/apsp-init.wgsl.d.ts +12 -0
  74. package/dist/src/wgsl/apsp-init.wgsl.d.ts.map +1 -0
  75. package/dist/src/wgsl/apsp-init.wgsl.js +26 -0
  76. package/dist/src/wgsl/apsp-init.wgsl.js.map +1 -0
  77. package/dist/src/wgsl/coo-emit.wgsl.d.ts +10 -0
  78. package/dist/src/wgsl/coo-emit.wgsl.d.ts.map +1 -0
  79. package/dist/src/wgsl/coo-emit.wgsl.js +33 -0
  80. package/dist/src/wgsl/coo-emit.wgsl.js.map +1 -0
  81. package/dist/src/wgsl/coo-scatter.wgsl.d.ts +15 -0
  82. package/dist/src/wgsl/coo-scatter.wgsl.d.ts.map +1 -0
  83. package/dist/src/wgsl/coo-scatter.wgsl.js +32 -0
  84. package/dist/src/wgsl/coo-scatter.wgsl.js.map +1 -0
  85. package/dist/src/wgsl/group-by-key-row.wgsl.d.ts +26 -0
  86. package/dist/src/wgsl/group-by-key-row.wgsl.d.ts.map +1 -0
  87. package/dist/src/wgsl/group-by-key-row.wgsl.js +146 -0
  88. package/dist/src/wgsl/group-by-key-row.wgsl.js.map +1 -0
  89. package/dist/src/wgsl/lpa-step.wgsl.d.ts +10 -0
  90. package/dist/src/wgsl/lpa-step.wgsl.d.ts.map +1 -0
  91. package/dist/src/wgsl/lpa-step.wgsl.js +35 -0
  92. package/dist/src/wgsl/lpa-step.wgsl.js.map +1 -0
  93. package/dist/src/wgsl/orient-flags.wgsl.d.ts +9 -0
  94. package/dist/src/wgsl/orient-flags.wgsl.d.ts.map +1 -0
  95. package/dist/src/wgsl/orient-flags.wgsl.js +21 -0
  96. package/dist/src/wgsl/orient-flags.wgsl.js.map +1 -0
  97. package/dist/src/wgsl/run-flags.wgsl.d.ts +8 -0
  98. package/dist/src/wgsl/run-flags.wgsl.d.ts.map +1 -0
  99. package/dist/src/wgsl/run-flags.wgsl.js +18 -0
  100. package/dist/src/wgsl/run-flags.wgsl.js.map +1 -0
  101. package/dist/src/wgsl/tri-intersect.wgsl.d.ts +11 -0
  102. package/dist/src/wgsl/tri-intersect.wgsl.d.ts.map +1 -0
  103. package/dist/src/wgsl/tri-intersect.wgsl.js +64 -0
  104. package/dist/src/wgsl/tri-intersect.wgsl.js.map +1 -0
  105. package/dist/webgpu-graph-algorithms.js +1819 -181
  106. package/dist/webgpu-graph-algorithms.js.map +1 -1
  107. package/package.json +2 -2
  108. package/src/accelerator.ts +56 -1
  109. package/src/algorithms/all-pairs.ts +228 -0
  110. package/src/algorithms/components.ts +2 -2
  111. package/src/algorithms/label-propagation.ts +280 -0
  112. package/src/algorithms/simple-symmetric.ts +409 -0
  113. package/src/algorithms/triangles.ts +240 -0
  114. package/src/constants.ts +45 -0
  115. package/src/index.ts +12 -1
  116. package/src/kernel/prelude.ts +6 -0
  117. package/src/kernels.ts +248 -6
  118. package/src/memory/residency.ts +15 -4
  119. package/src/primitives/coo-to-csr.ts +251 -0
  120. package/src/primitives/group-by-key.ts +209 -0
  121. package/src/types/accelerator.ts +10 -2
  122. package/src/types/all-pairs.ts +37 -0
  123. package/src/types/community.ts +18 -0
  124. package/src/types/structure.ts +28 -0
  125. package/src/wgsl/apsp-fw.wgsl.ts +112 -0
  126. package/src/wgsl/apsp-init.wgsl.ts +25 -0
  127. package/src/wgsl/coo-emit.wgsl.ts +32 -0
  128. package/src/wgsl/coo-scatter.wgsl.ts +31 -0
  129. package/src/wgsl/group-by-key-row.wgsl.ts +145 -0
  130. package/src/wgsl/lpa-step.wgsl.ts +34 -0
  131. package/src/wgsl/orient-flags.wgsl.ts +20 -0
  132. package/src/wgsl/run-flags.wgsl.ts +17 -0
  133. package/src/wgsl/tri-intersect.wgsl.ts +63 -0
  134. package/dist/chunks/context-oXphO3yj.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, m as GRID_COARSEST_SIDE, n as GRID_MIN_SIDE, o as GRID_SORT_BITS, p as FA2_DEFAULTS, q as MAX_ITERATIONS_PER_STEP, r as MAX_1D_ITEMS, s as hasErrorCode, t as FA2_FLAG_FIRST, P as PARTIAL_BYTES, E as EXACT_TILES_PER_PASS, I as INDIRECT_ARGS_STRIDE, u as GRID_HUB_CELL, L as LAYOUT_TUNING_DEFAULTS, v as EXACT_MAX_NODES, w as SETTLE_FLOOR_UNBOUNDED, T as TRACE_RECORD_BYTES, x as GRID_BBOX_MARGIN, y as GRID_EXTENT_FLOOR, z as FR_ADAPTIVE_MAX_ITERATIONS, A as FR_START_TEMPERATURE, C as FA2_FLAG_ADAPTIVE, D as SETTLE_FLOOR_FRACTION, H as FR_REHEAT_FRACTION, J as FR_DEFAULTS, K as SE_DEFAULTS, N as SETTLE_FLOOR_REFERENCE_NODES, O as SE_SCALE_REFERENCE_NODES } from "./chunks/context-oXphO3yj.js";
2
- import { Q, G, V, X, Y, Z } from "./chunks/context-oXphO3yj.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 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";
3
3
  import { renumberPartition, INVALID_INDEX, foldArcs, makeMask, maskTest, expandEdges, fromEdgeArrays } from "@graphty/graph-format";
4
4
  class UniformRing {
5
5
  /**
@@ -211,6 +211,10 @@ function plan1d(items, wg, caps) {
211
211
  assertWorkgroupSize(wg);
212
212
  return grid(Math.ceil(items / wg), items, caps);
213
213
  }
214
+ function plan2d(groups, caps) {
215
+ assertCount$1("groups", groups);
216
+ return grid(groups, groups, caps);
217
+ }
214
218
  function planGridStride(items, wg, caps, maxGroups) {
215
219
  assertCount$1("items", items);
216
220
  assertWorkgroupSize(wg);
@@ -605,6 +609,116 @@ fn advance_expand(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocati
605
609
  }
606
610
  `
607
611
  );
612
+ const apspFwWgsl = (
613
+ /* wgsl */
614
+ `
615
+ var<workgroup> tileA: array<f32, APSP_TILE * APSP_TILE>;
616
+ var<workgroup> tileB: array<f32, APSP_TILE * APSP_TILE>;
617
+
618
+ fn tile_row(block: vec2<u32>, c: u32) -> u32 { return block.x * APSP_TILE + c / APSP_TILE; }
619
+ fn tile_col(block: vec2<u32>, c: u32) -> u32 { return block.y * APSP_TILE + c % APSP_TILE; }
620
+
621
+ fn load_cell(block: vec2<u32>, c: u32) -> f32 {
622
+ let i = tile_row(block, c);
623
+ let j = tile_col(block, c);
624
+ if (i < P.n && j < P.n) { return dist[i * P.n + j]; }
625
+ return bitcast<f32>(P.infBits); // outside the matrix: no path through it
626
+ }
627
+
628
+ fn store_cell(block: vec2<u32>, c: u32, v: f32) {
629
+ let i = tile_row(block, c);
630
+ let j = tile_col(block, c);
631
+ if (i >= P.n || j >= P.n) { return; } // an edge tile stores nothing outside n x n
632
+ dist[i * P.n + j] = v;
633
+ }
634
+
635
+ @compute @workgroup_size(WG)
636
+ fn apsp_fw(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
637
+ let g = group_id(wid);
638
+ let r = P.round;
639
+ let others = max(P.blocks, 2u) - 1u; // the blocks of a strip, the pivot excluded
640
+ let cells = APSP_TILE * APSP_TILE;
641
+ var count = 1u;
642
+ var own = vec2<u32>(r, r); // the block this workgroup updates
643
+ var a = vec2<u32>(r, r); // staged in tileA
644
+ var b = vec2<u32>(r, r); // staged in tileB
645
+ if (PHASE == 1u) {
646
+ count = 2u * others;
647
+ let s = g % others;
648
+ let o = select(s, s + 1u, s >= r); // a strip skips the pivot block
649
+ own = select(vec2<u32>(o, r), vec2<u32>(r, o), g < others); // block row r first, then block column r
650
+ b = own;
651
+ }
652
+ if (PHASE == 2u) {
653
+ count = others * others;
654
+ let i = g / others;
655
+ let j = g % others;
656
+ own = vec2<u32>(select(i, i + 1u, i >= r), select(j, j + 1u, j >= r));
657
+ a = vec2<u32>(own.x, r); // the pivot-column block (i, r)
658
+ b = vec2<u32>(r, own.y); // the pivot-row block (r, j)
659
+ }
660
+ if (g >= count) { return; } // uniform: the workgroup id and uniforms only
661
+
662
+ for (var c = lid.x; c < cells; c = c + WG) {
663
+ tileA[c] = load_cell(a, c);
664
+ if (PHASE != 0u) { tileB[c] = load_cell(b, c); }
665
+ }
666
+ workgroupBarrier(); // every staged cell is visible
667
+
668
+ if (PHASE == 2u) {
669
+ for (var c = lid.x; c < cells; c = c + WG) {
670
+ let x = c / APSP_TILE;
671
+ let y = c % APSP_TILE;
672
+ var v = load_cell(own, c);
673
+ for (var kr = 0u; kr < APSP_TILE; kr = kr + 1u) {
674
+ v = min(v, tileA[x * APSP_TILE + kr] + tileB[kr * APSP_TILE + y]);
675
+ }
676
+ store_cell(own, c, v);
677
+ }
678
+ return;
679
+ }
680
+
681
+ let pivotRow = PHASE == 1u && own.x == r; // block row r reads d[x][k] from the pivot
682
+ for (var k = 0u; k < APSP_TILE; k = k + 1u) {
683
+ for (var c = lid.x; c < cells; c = c + WG) {
684
+ let x = c / APSP_TILE;
685
+ let y = c % APSP_TILE;
686
+ if (PHASE == 0u) {
687
+ let via = tileA[x * APSP_TILE + k] + tileA[k * APSP_TILE + y];
688
+ if (via < tileA[c]) { tileA[c] = via; }
689
+ } else {
690
+ let left = select(tileB[x * APSP_TILE + k], tileA[x * APSP_TILE + k], pivotRow);
691
+ let right = select(tileA[k * APSP_TILE + y], tileB[k * APSP_TILE + y], pivotRow);
692
+ let via = left + right;
693
+ if (via < tileB[c]) { tileB[c] = via; }
694
+ }
695
+ }
696
+ workgroupBarrier(); // step k is complete before step k + 1 reads
697
+ }
698
+ for (var c = lid.x; c < cells; c = c + WG) {
699
+ store_cell(own, c, select(tileB[c], tileA[c], PHASE == 0u));
700
+ }
701
+ }
702
+ `
703
+ );
704
+ const apspInitWgsl = (
705
+ /* wgsl */
706
+ `
707
+ @compute @workgroup_size(WG)
708
+ fn apsp_init(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
709
+ let u = linear_id(wid, lid.x);
710
+ if (u >= P.n) { return; }
711
+ let rowBase = u * P.n;
712
+ let end = rowPtr[u + 1u];
713
+ for (var a = rowPtr[u]; a < end; a = a + 1u) {
714
+ let v = colIdx[a];
715
+ let w = select(1.0, weights[a], HAS_WEIGHTS);
716
+ dist[rowBase + v] = min(dist[rowBase + v], w); // parallel arcs collapse to the cheapest
717
+ }
718
+ dist[rowBase + u] = 0.0; // last: a self-loop never displaces the zero
719
+ }
720
+ `
721
+ );
608
722
  const bcBackwardWgsl = (
609
723
  /* wgsl */
610
724
  `
@@ -1249,6 +1363,54 @@ fn compact_scatter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocat
1249
1363
  }
1250
1364
  `
1251
1365
  );
1366
+ const cooEmitWgsl = (
1367
+ /* wgsl */
1368
+ `
1369
+ @compute @workgroup_size(WG)
1370
+ fn coo_emit(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
1371
+ let i = linear_id(wid, lid.x);
1372
+ if (i >= P.count) { return; } // no barrier follows
1373
+ var a = i;
1374
+ if (INDEXED) { a = order[i]; } // the arc this position takes
1375
+ let e = a / 2u; // arc 2e is edge e as declared, arc 2e + 1 its reverse
1376
+ let forward = (a % 2u) == 0u;
1377
+ let u = edgeSrc[e];
1378
+ let v = edgeDst[e];
1379
+ var w = 1.0;
1380
+ if (WEIGHTED) { w = edgeWeight[e]; }
1381
+ if (u == v) { // a self-loop is dropped: both arcs sort last and never open a run
1382
+ outSrc[i] = INVALID_INDEX;
1383
+ outDst[i] = INVALID_INDEX;
1384
+ outWeight[i] = 0.0;
1385
+ return;
1386
+ }
1387
+ outSrc[i] = select(v, u, forward);
1388
+ outDst[i] = select(u, v, forward);
1389
+ outWeight[i] = w;
1390
+ }
1391
+ `
1392
+ );
1393
+ const cooScatterWgsl = (
1394
+ /* wgsl */
1395
+ `
1396
+ @compute @workgroup_size(WG)
1397
+ fn coo_scatter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
1398
+ let i = linear_id(wid, lid.x);
1399
+ if (i >= P.count) { return; } // no barrier follows
1400
+ let s = src[i];
1401
+ var slot = 0u;
1402
+ if (SORTED_INPUT) {
1403
+ if (i > 0u && s < src[i - 1u]) { atomicStore(&cursors[0], 1u); } // unsorted input: the flag the driver refuses
1404
+ let within = i - rowPtr[s]; // the arc's place in its row
1405
+ slot = rowPtr[s] + within;
1406
+ } else {
1407
+ slot = rowPtr[s] + atomicAdd(&cursors[s], 1u); // race order: the row is not sorted in this mode
1408
+ }
1409
+ colIdx[slot] = dst[i];
1410
+ if (WEIGHTED) { outWeight[slot] = weight[i]; }
1411
+ }
1412
+ `
1413
+ );
1252
1414
  const countingScatterWgsl = (
1253
1415
  /* wgsl */
1254
1416
  `
@@ -2188,6 +2350,130 @@ fn grid_near_field(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocat
2188
2350
  }
2189
2351
  `
2190
2352
  );
2353
+ const groupByKeyRowWgsl = (
2354
+ /* wgsl */
2355
+ `
2356
+ var<workgroup> shMax: array<f32, WG>;
2357
+ var<workgroup> shKey: array<u32, WG>;
2358
+ var<workgroup> shSum: array<u32, WG>;
2359
+
2360
+ fn weight_of(a: u32) -> f32 {
2361
+ if (WEIGHTED) { return weights[a]; }
2362
+ return 1.0;
2363
+ }
2364
+ fn scale_of(maxW: f32, d: u32) -> f32 { // 2^s, maxW x d x 2^s in [2^28, 2^30), from exponents alone
2365
+ let em = i32((bitcast<u32>(maxW) >> 23u) & 255u) - 127;
2366
+ let ed = i32(firstLeadingBit(max(d, 1u)));
2367
+ let s = clamp(28 - em - ed, -126, 126);
2368
+ return bitcast<f32>(u32(s + 127) << 23u);
2369
+ }
2370
+ fn inverse_of(scale: f32) -> f32 { // 2^-s, exact
2371
+ let s = i32((bitcast<u32>(scale) >> 23u) & 255u) - 127;
2372
+ return bitcast<f32>(u32(127 - s) << 23u);
2373
+ }
2374
+ fn quantize(w: f32, scale: f32) -> u32 { // nearest integer, halves up; every step exact
2375
+ let x = max(w * scale, 0.0);
2376
+ let i = u32(x);
2377
+ return select(i, i + 1u, x - f32(i) >= 0.5);
2378
+ }
2379
+ fn better(sum: u32, key: u32, bestSum: u32, bestKey0: u32) -> bool { return sum > bestSum || (sum == bestSum && key < bestKey0); }
2380
+
2381
+ fn row_thread(v: u32) {
2382
+ let lo = rowPtr[v];
2383
+ let hi = rowPtr[v + 1u];
2384
+ var maxW = 0.0;
2385
+ for (var a = lo; a < hi; a = a + 1u) { maxW = max(maxW, weight_of(a)); }
2386
+ let scale = scale_of(maxW, hi - lo);
2387
+ var bk = INVALID_INDEX;
2388
+ var bs = 0u;
2389
+ for (var a = lo; a < hi; a = a + 1u) {
2390
+ let k = keyIn[colIdx[a]];
2391
+ var seen = false;
2392
+ for (var b = lo; b < a; b = b + 1u) { if (keyIn[colIdx[b]] == k) { seen = true; break; } }
2393
+ if (seen) { continue; } // this key was summed at its first arc
2394
+ var sum = 0u;
2395
+ for (var b = a; b < hi; b = b + 1u) { if (keyIn[colIdx[b]] == k) { sum = sum + quantize(weight_of(b), scale); } }
2396
+ if (better(sum, k, bs, bk)) { bk = k; bs = sum; }
2397
+ }
2398
+ bestKey[v] = bk;
2399
+ bestScore[v] = f32(bs) * inverse_of(scale);
2400
+ }
2401
+
2402
+ fn row_hash(g: u32, lid: u32) {
2403
+ let v = rows[P.rowsBase + g];
2404
+ let base = rows[P.basesBase + g];
2405
+ let lo = rowPtr[v];
2406
+ let hi = rowPtr[v + 1u];
2407
+ let cap = GROUP_HASH_LOAD_FACTOR * (hi - lo);
2408
+ var m = 0.0;
2409
+ for (var a = lo + lid; a < hi; a = a + WG) { m = max(m, weight_of(a)); }
2410
+ shMax[lid] = m;
2411
+ workgroupBarrier();
2412
+ for (var s = WG / 2u; s > 0u; s = s / 2u) {
2413
+ if (lid < s) { shMax[lid] = max(shMax[lid], shMax[lid + s]); }
2414
+ workgroupBarrier();
2415
+ }
2416
+ let scale = scale_of(shMax[0], hi - lo);
2417
+ for (var j = lid; j < cap; j = j + WG) { // the region is this workgroup's alone: clear it
2418
+ atomicStore(&hashRegion[base + 2u * j], INVALID_INDEX);
2419
+ atomicStore(&hashRegion[base + 2u * j + 1u], 0u);
2420
+ }
2421
+ storageBarrier();
2422
+ var exhausted = false;
2423
+ for (var a = lo + lid; a < hi; a = a + WG) {
2424
+ let k = keyIn[colIdx[a]];
2425
+ let q = quantize(weight_of(a), scale);
2426
+ var slot = lowbias32(k) % cap;
2427
+ var steps = 0u;
2428
+ loop {
2429
+ if (steps >= cap + 64u) { exhausted = true; break; } // bounded: a spurious compare-exchange failure is legal (WGSL 17.8.5)
2430
+ steps = steps + 1u;
2431
+ let r = atomicCompareExchangeWeak(&hashRegion[base + 2u * slot], INVALID_INDEX, k);
2432
+ if (r.exchanged || r.old_value == k) { atomicAdd(&hashRegion[base + 2u * slot + 1u], q); break; }
2433
+ if (r.old_value == INVALID_INDEX) { continue; } // a spurious failure: the same slot again
2434
+ slot = (slot + 1u) % cap; // linear probing
2435
+ }
2436
+ }
2437
+ if (exhausted) { atomicStore(&hashRegion[0], 1u); }
2438
+ storageBarrier();
2439
+ var bk = INVALID_INDEX;
2440
+ var bs = 0u;
2441
+ for (var j = lid; j < cap; j = j + WG) {
2442
+ let k = atomicLoad(&hashRegion[base + 2u * j]);
2443
+ if (k != INVALID_INDEX) {
2444
+ let sum = atomicLoad(&hashRegion[base + 2u * j + 1u]);
2445
+ if (better(sum, k, bs, bk)) { bk = k; bs = sum; }
2446
+ }
2447
+ }
2448
+ shKey[lid] = bk;
2449
+ shSum[lid] = bs;
2450
+ workgroupBarrier();
2451
+ for (var s = WG / 2u; s > 0u; s = s / 2u) {
2452
+ if (lid < s && better(shSum[lid + s], shKey[lid + s], shSum[lid], shKey[lid])) {
2453
+ shKey[lid] = shKey[lid + s];
2454
+ shSum[lid] = shSum[lid + s];
2455
+ }
2456
+ workgroupBarrier();
2457
+ }
2458
+ if (lid == 0u) {
2459
+ bestKey[v] = shKey[0];
2460
+ bestScore[v] = f32(shSum[0]) * inverse_of(scale);
2461
+ }
2462
+ }
2463
+
2464
+ @compute @workgroup_size(WG)
2465
+ fn group_by_key_row(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
2466
+ if (TIER == 0u) {
2467
+ let i = linear_id(wid, lid.x);
2468
+ if (i < P.count) { row_thread(rows[P.rowsBase + i]); }
2469
+ return; // TIER is an override: uniform
2470
+ }
2471
+ let g = group_id(wid);
2472
+ if (g >= P.count) { return; } // uniform: the workgroup id and a uniform
2473
+ row_hash(g, lid.x);
2474
+ }
2475
+ `
2476
+ );
2191
2477
  const histogramWgsl = (
2192
2478
  /* wgsl */
2193
2479
  `
@@ -2222,6 +2508,51 @@ fn indirect_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
2222
2508
  }
2223
2509
  `
2224
2510
  );
2511
+ const lpaStepWgsl = (
2512
+ /* wgsl */
2513
+ `
2514
+ var<workgroup> moved: atomic<u32>;
2515
+
2516
+ @compute @workgroup_size(WG)
2517
+ fn lpa_step(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
2518
+ if (lid.x == 0u) { atomicStore(&moved, 0u); }
2519
+ workgroupBarrier();
2520
+ let v = linear_id(wid, lid.x);
2521
+ if (v < P.n) {
2522
+ let cur = labelsIn[v];
2523
+ let best = bestKey[v];
2524
+ var next = cur;
2525
+ if (best != INVALID_INDEX && best != cur) {
2526
+ let down = best < cur;
2527
+ if (down == (P.direction == 0u)) { next = best; } // the alternating direction rule
2528
+ }
2529
+ labelsOut[v] = next;
2530
+ if (next != cur) { atomicAdd(&moved, 1u); }
2531
+ }
2532
+ workgroupBarrier(); // every lane, unconditionally
2533
+ if (lid.x == 0u) {
2534
+ let m = atomicLoad(&moved);
2535
+ if (m > 0u) { atomicAdd(&counters[P.counterIndex], m); } // one atomic per workgroup
2536
+ }
2537
+ }
2538
+ `
2539
+ );
2540
+ const orientFlagsWgsl = (
2541
+ /* wgsl */
2542
+ `
2543
+ @compute @workgroup_size(WG)
2544
+ fn orient_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
2545
+ let a = linear_id(wid, lid.x);
2546
+ if (a >= P.count) { return; } // no barrier follows
2547
+ let u = src[a];
2548
+ let v = colIdx[a];
2549
+ let du = rowPtr[u + 1u] - rowPtr[u];
2550
+ let dv = rowPtr[v + 1u] - rowPtr[v];
2551
+ let keep = dv > du || (dv == du && v > u); // (degree, id) of the target above the source's
2552
+ flags[a] = select(0u, 1u, keep);
2553
+ }
2554
+ `
2555
+ );
2225
2556
  const prFinalizeWgsl = (
2226
2557
  /* wgsl */
2227
2558
  `
@@ -2407,6 +2738,20 @@ fn reduce(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) l
2407
2738
  }
2408
2739
  `
2409
2740
  );
2741
+ const runFlagsWgsl = (
2742
+ /* wgsl */
2743
+ `
2744
+ @compute @workgroup_size(WG)
2745
+ fn run_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
2746
+ let i = linear_id(wid, lid.x);
2747
+ if (i >= P.count) { return; } // no barrier follows
2748
+ let a = keysA[i];
2749
+ var first = a != INVALID_INDEX; // a dropped arc never opens a run
2750
+ if (i > 0u) { first = first && (a != keysA[i - 1u] || keysB[i] != keysB[i - 1u]); }
2751
+ flags[i] = select(0u, 1u, first);
2752
+ }
2753
+ `
2754
+ );
2410
2755
  const scanAddWgsl = (
2411
2756
  /* wgsl */
2412
2757
  `
@@ -2727,6 +3072,63 @@ fn sssp_relax(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_i
2727
3072
  }
2728
3073
  `
2729
3074
  );
3075
+ const triIntersectWgsl = (
3076
+ /* wgsl */
3077
+ `
3078
+ fn contains(lo0: u32, hi0: u32, x: u32) -> bool { // binary search of x in colIdx[lo0, hi0), sorted ascending
3079
+ var lo = lo0;
3080
+ var hi = hi0;
3081
+ loop {
3082
+ if (lo >= hi) { break; }
3083
+ let mid = lo + (hi - lo) / 2u;
3084
+ let y = colIdx[mid];
3085
+ if (y == x) { return true; }
3086
+ if (y < x) { lo = mid + 1u; } else { hi = mid; }
3087
+ }
3088
+ return false;
3089
+ }
3090
+
3091
+ @compute @workgroup_size(WG)
3092
+ fn tri_intersect(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
3093
+ let a = linear_id(wid, lid.x);
3094
+ if (a >= P.count) { return; } // no barrier follows
3095
+ let u = src[a];
3096
+ let v = colIdx[a];
3097
+ let uLo = rowPtr[u];
3098
+ let uHi = rowPtr[u + 1u];
3099
+ let vLo = rowPtr[v];
3100
+ let vHi = rowPtr[v + 1u];
3101
+ let du = uHi - uLo;
3102
+ let dv = vHi - vLo;
3103
+ var search = SEARCH == 2u;
3104
+ if (SEARCH == 0u) { search = du / TRIANGLE_BINARY_SEARCH_RATIO > dv || dv / TRIANGLE_BINARY_SEARCH_RATIO > du; }
3105
+ var found = 0u;
3106
+ if (search) {
3107
+ var sLo = uLo;
3108
+ var sHi = uHi;
3109
+ var lLo = vLo;
3110
+ var lHi = vHi;
3111
+ if (du > dv) { sLo = vLo; sHi = vHi; lLo = uLo; lHi = uHi; }
3112
+ for (var k = sLo; k < sHi; k = k + 1u) {
3113
+ let w = colIdx[k];
3114
+ if (contains(lLo, lHi, w)) { atomicAdd(&counts[w], 1u); found = found + 1u; }
3115
+ }
3116
+ } else {
3117
+ var i = uLo;
3118
+ var j = vLo;
3119
+ loop {
3120
+ if (i >= uHi || j >= vHi) { break; }
3121
+ let x = colIdx[i];
3122
+ let y = colIdx[j];
3123
+ if (x == y) { atomicAdd(&counts[x], 1u); found = found + 1u; i = i + 1u; j = j + 1u; }
3124
+ else if (x < y) { i = i + 1u; }
3125
+ else { j = j + 1u; }
3126
+ }
3127
+ }
3128
+ if (found > 0u) { atomicAdd(&counts[u], found); atomicAdd(&counts[v], found); }
3129
+ }
3130
+ `
3131
+ );
2730
3132
  const wccCompressWgsl = (
2731
3133
  /* wgsl */
2732
3134
  `
@@ -3116,6 +3518,30 @@ const BF_FLAGS = UniformBlock.define(
3116
3518
  ],
3117
3519
  { layout: "storage" }
3118
3520
  );
3521
+ const APSP_PARAMS = UniformBlock.define("ApspParams", [
3522
+ ["n", "u32"],
3523
+ ["round", "u32"],
3524
+ ["blocks", "u32"],
3525
+ ["infBits", "u32"]
3526
+ ]);
3527
+ const COO_PARAMS = UniformBlock.define("CooParams", [
3528
+ ["count", "u32"],
3529
+ ["pad0", "u32"],
3530
+ ["pad1", "u32"],
3531
+ ["pad2", "u32"]
3532
+ ]);
3533
+ const GROUP_PARAMS = UniformBlock.define("GroupParams", [
3534
+ ["rowsBase", "u32"],
3535
+ ["basesBase", "u32"],
3536
+ ["count", "u32"],
3537
+ ["pad0", "u32"]
3538
+ ]);
3539
+ const LPA_PARAMS = UniformBlock.define("LpaParams", [
3540
+ ["n", "u32"],
3541
+ ["direction", "u32"],
3542
+ ["counterIndex", "u32"],
3543
+ ["pad0", "u32"]
3544
+ ]);
3119
3545
  function decl(group, binding, name, kind, wgslType) {
3120
3546
  return { group, binding, name, kind, wgslType };
3121
3547
  }
@@ -3996,6 +4422,165 @@ const BC_FORWARD_EDGE = {
3996
4422
  snippetSlots: [],
3997
4423
  phase: "P9"
3998
4424
  };
4425
+ const APSP_INIT = {
4426
+ id: "apsp-init",
4427
+ body: apspInitWgsl,
4428
+ entryPoint: "apsp_init",
4429
+ bindings: GRAPH_SLOTS.concat(decl(1, 0, "dist", "storage", "array<f32>"), decl(2, 0, "P", "uniform", "ApspParams")),
4430
+ overrideDecls: [],
4431
+ uniforms: [APSP_PARAMS],
4432
+ needs: [],
4433
+ snippetSlots: [],
4434
+ phase: "P9"
4435
+ };
4436
+ const APSP_FW = {
4437
+ id: "apsp-fw",
4438
+ body: apspFwWgsl,
4439
+ entryPoint: "apsp_fw",
4440
+ bindings: [decl(1, 0, "dist", "storage", "array<f32>"), decl(2, 0, "P", "uniform", "ApspParams")],
4441
+ overrideDecls: [{ name: "PHASE", type: "u32", default: 0 }],
4442
+ uniforms: [APSP_PARAMS],
4443
+ needs: [],
4444
+ snippetSlots: [],
4445
+ phase: "P9"
4446
+ };
4447
+ const COO_EMIT = {
4448
+ id: "coo-emit",
4449
+ body: cooEmitWgsl,
4450
+ entryPoint: "coo_emit",
4451
+ bindings: [
4452
+ decl(1, 0, "edgeSrc", "storage-ro", "array<u32>"),
4453
+ decl(1, 1, "edgeDst", "storage-ro", "array<u32>"),
4454
+ decl(1, 2, "edgeWeight", "storage-ro", "array<f32>"),
4455
+ decl(1, 3, "order", "storage-ro", "array<u32>"),
4456
+ decl(1, 4, "outSrc", "storage", "array<u32>"),
4457
+ decl(1, 5, "outDst", "storage", "array<u32>"),
4458
+ decl(1, 6, "outWeight", "storage", "array<f32>"),
4459
+ decl(2, 0, "P", "uniform", "CooParams")
4460
+ ],
4461
+ overrideDecls: [
4462
+ { name: "INDEXED", type: "bool", default: false },
4463
+ { name: "WEIGHTED", type: "bool", default: false }
4464
+ ],
4465
+ uniforms: [COO_PARAMS],
4466
+ needs: [],
4467
+ snippetSlots: [],
4468
+ phase: "P11"
4469
+ };
4470
+ const RUN_FLAGS = {
4471
+ id: "run-flags",
4472
+ body: runFlagsWgsl,
4473
+ entryPoint: "run_flags",
4474
+ bindings: [
4475
+ decl(1, 0, "keysA", "storage-ro", "array<u32>"),
4476
+ decl(1, 1, "keysB", "storage-ro", "array<u32>"),
4477
+ decl(1, 2, "flags", "storage", "array<u32>"),
4478
+ decl(2, 0, "P", "uniform", "CooParams")
4479
+ ],
4480
+ overrideDecls: [],
4481
+ uniforms: [COO_PARAMS],
4482
+ needs: [],
4483
+ snippetSlots: [],
4484
+ phase: "P11"
4485
+ };
4486
+ const COO_SCATTER = {
4487
+ id: "coo-scatter",
4488
+ body: cooScatterWgsl,
4489
+ entryPoint: "coo_scatter",
4490
+ bindings: [
4491
+ decl(1, 0, "src", "storage-ro", "array<u32>"),
4492
+ decl(1, 1, "dst", "storage-ro", "array<u32>"),
4493
+ decl(1, 2, "weight", "storage-ro", "array<f32>"),
4494
+ decl(1, 3, "rowPtr", "storage-ro", "array<u32>"),
4495
+ decl(1, 4, "cursors", "storage", "array<atomic<u32>>"),
4496
+ decl(1, 5, "colIdx", "storage", "array<u32>"),
4497
+ decl(1, 6, "outWeight", "storage", "array<f32>"),
4498
+ decl(2, 0, "P", "uniform", "CooParams")
4499
+ ],
4500
+ overrideDecls: [
4501
+ { name: "SORTED_INPUT", type: "bool", default: false },
4502
+ { name: "WEIGHTED", type: "bool", default: false }
4503
+ ],
4504
+ uniforms: [COO_PARAMS],
4505
+ needs: [],
4506
+ snippetSlots: [],
4507
+ phase: "P11"
4508
+ };
4509
+ const ORIENT_FLAGS = {
4510
+ id: "orient-flags",
4511
+ body: orientFlagsWgsl,
4512
+ entryPoint: "orient_flags",
4513
+ bindings: [
4514
+ decl(1, 0, "rowPtr", "storage-ro", "array<u32>"),
4515
+ decl(1, 1, "colIdx", "storage-ro", "array<u32>"),
4516
+ decl(1, 2, "src", "storage-ro", "array<u32>"),
4517
+ decl(1, 3, "flags", "storage", "array<u32>"),
4518
+ decl(2, 0, "P", "uniform", "CooParams")
4519
+ ],
4520
+ overrideDecls: [],
4521
+ uniforms: [COO_PARAMS],
4522
+ needs: [],
4523
+ snippetSlots: [],
4524
+ phase: "P11"
4525
+ };
4526
+ const TRI_INTERSECT = {
4527
+ id: "tri-intersect",
4528
+ body: triIntersectWgsl,
4529
+ entryPoint: "tri_intersect",
4530
+ bindings: [
4531
+ decl(1, 0, "rowPtr", "storage-ro", "array<u32>"),
4532
+ decl(1, 1, "colIdx", "storage-ro", "array<u32>"),
4533
+ decl(1, 2, "src", "storage-ro", "array<u32>"),
4534
+ decl(1, 3, "counts", "storage", "array<atomic<u32>>"),
4535
+ decl(2, 0, "P", "uniform", "CooParams")
4536
+ ],
4537
+ overrideDecls: [{ name: "SEARCH", type: "u32", default: 0 }],
4538
+ uniforms: [COO_PARAMS],
4539
+ needs: [],
4540
+ snippetSlots: [],
4541
+ phase: "P11"
4542
+ };
4543
+ const GROUP_BY_KEY_ROW = {
4544
+ id: "group-by-key-row",
4545
+ body: groupByKeyRowWgsl,
4546
+ entryPoint: "group_by_key_row",
4547
+ bindings: [
4548
+ decl(1, 0, "rowPtr", "storage-ro", "array<u32>"),
4549
+ decl(1, 1, "colIdx", "storage-ro", "array<u32>"),
4550
+ decl(1, 2, "weights", "storage-ro", "array<f32>"),
4551
+ decl(1, 3, "keyIn", "storage-ro", "array<u32>"),
4552
+ decl(1, 4, "rows", "storage-ro", "array<u32>"),
4553
+ decl(1, 5, "hashRegion", "storage", "array<atomic<u32>>"),
4554
+ decl(1, 6, "bestKey", "storage", "array<u32>"),
4555
+ decl(1, 7, "bestScore", "storage", "array<f32>"),
4556
+ decl(2, 0, "P", "uniform", "GroupParams")
4557
+ ],
4558
+ overrideDecls: [
4559
+ { name: "TIER", type: "u32", default: 0 },
4560
+ { name: "WEIGHTED", type: "bool", default: false }
4561
+ ],
4562
+ uniforms: [GROUP_PARAMS],
4563
+ needs: [],
4564
+ snippetSlots: [],
4565
+ phase: "P11"
4566
+ };
4567
+ const LPA_STEP = {
4568
+ id: "lpa-step",
4569
+ body: lpaStepWgsl,
4570
+ entryPoint: "lpa_step",
4571
+ bindings: [
4572
+ decl(1, 0, "labelsIn", "storage-ro", "array<u32>"),
4573
+ decl(1, 1, "bestKey", "storage-ro", "array<u32>"),
4574
+ decl(1, 2, "labelsOut", "storage", "array<u32>"),
4575
+ decl(1, 3, "counters", "storage", "array<atomic<u32>>"),
4576
+ decl(2, 0, "P", "uniform", "LpaParams")
4577
+ ],
4578
+ overrideDecls: [],
4579
+ uniforms: [LPA_PARAMS],
4580
+ needs: [],
4581
+ snippetSlots: [],
4582
+ phase: "P11"
4583
+ };
3999
4584
  const REGISTRY = Object.freeze({
4000
4585
  degree: DEGREE,
4001
4586
  reduce: REDUCE,
@@ -4048,7 +4633,16 @@ const REGISTRY = Object.freeze({
4048
4633
  "bc-backward": BC_BACKWARD,
4049
4634
  "bc-gather": BC_GATHER,
4050
4635
  "bc-edge-gather": BC_EDGE_GATHER,
4051
- "bc-forward-edge": BC_FORWARD_EDGE
4636
+ "bc-forward-edge": BC_FORWARD_EDGE,
4637
+ "apsp-init": APSP_INIT,
4638
+ "apsp-fw": APSP_FW,
4639
+ "coo-emit": COO_EMIT,
4640
+ "run-flags": RUN_FLAGS,
4641
+ "coo-scatter": COO_SCATTER,
4642
+ "orient-flags": ORIENT_FLAGS,
4643
+ "tri-intersect": TRI_INTERSECT,
4644
+ "group-by-key-row": GROUP_BY_KEY_ROW,
4645
+ "lpa-step": LPA_STEP
4052
4646
  });
4053
4647
  const bodyOverrides = /* @__PURE__ */ new Map();
4054
4648
  function entryOf(id) {
@@ -4207,7 +4801,7 @@ class ScanPlannerImpl {
4207
4801
  }
4208
4802
  const POISON = 3735928559;
4209
4803
  const CHECK_BLOCKS = 32;
4210
- const RING_SLOTS$7 = 8;
4804
+ const RING_SLOTS$9 = 8;
4211
4805
  const checked = /* @__PURE__ */ new WeakMap();
4212
4806
  function inputAt(i) {
4213
4807
  return i + 1;
@@ -4235,7 +4829,7 @@ async function runCheck(ctx) {
4235
4829
  const count = CHECK_BLOCKS * wg + 1;
4236
4830
  const bytes = 4 * count;
4237
4831
  const lease = ctx.pool.lease();
4238
- const ring = new UniformRing(ctx.device, ctx.allocator, RING_SLOTS$7, "device-check/ring");
4832
+ const ring = new UniformRing(ctx.device, ctx.allocator, RING_SLOTS$9, "device-check/ring");
4239
4833
  try {
4240
4834
  const scope = {
4241
4835
  device: ctx.device,
@@ -4452,7 +5046,7 @@ function coreOfView(v, arcCount) {
4452
5046
  hasWeights: weights !== null
4453
5047
  });
4454
5048
  }
4455
- function checkDest$5(dest, n) {
5049
+ function checkDest$7(dest, n) {
4456
5050
  if (dest === void 0) {
4457
5051
  return null;
4458
5052
  }
@@ -4481,7 +5075,7 @@ async function degree(ctx, s, options) {
4481
5075
  ctx.assertReady();
4482
5076
  await assertDeviceComputes(ctx);
4483
5077
  const n = s.nodeCount;
4484
- const dest = checkDest$5(options?.dest, n);
5078
+ const dest = checkDest$7(options?.dest, n);
4485
5079
  if (options?.signal?.aborted) {
4486
5080
  throw new WebGpuGraphError("E_ABORTED", "degree: the signal was aborted before any work started", {});
4487
5081
  }
@@ -4902,13 +5496,13 @@ function algorithmScope(ctx, label, slots) {
4902
5496
  ringOverruns: () => ring.overruns
4903
5497
  };
4904
5498
  }
4905
- const ALGORITHM$5 = "connectedComponents";
5499
+ const ALGORITHM$8 = "connectedComponents";
4906
5500
  const ROUNDS_PER_BATCH$1 = 4;
4907
5501
  const MAX_WCC_ROUNDS = 64;
4908
5502
  const SAMPLE_SIZE = 1024;
4909
5503
  const MAX_STEPS = 1024;
4910
- const RING_SLOTS$6 = 2 * ROUNDS_PER_BATCH$1;
4911
- function checkDest$4(dest, n) {
5504
+ const RING_SLOTS$8 = 2 * ROUNDS_PER_BATCH$1;
5505
+ function checkDest$6(dest, n) {
4912
5506
  if (dest === void 0) {
4913
5507
  return null;
4914
5508
  }
@@ -4917,7 +5511,7 @@ function checkDest$4(dest, n) {
4917
5511
  }
4918
5512
  throw new WebGpuGraphError(
4919
5513
  "E_INVALID_ARGUMENT",
4920
- `${ALGORITHM$5}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
5514
+ `${ALGORITHM$8}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
4921
5515
  {
4922
5516
  argument: "dest",
4923
5517
  value: `${dest.constructor.name}(${dest.length})`,
@@ -4927,7 +5521,7 @@ function checkDest$4(dest, n) {
4927
5521
  }
4928
5522
  function coreOf$2(ctx, s) {
4929
5523
  const core = ctx.residency.core(s);
4930
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$5);
5524
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$8);
4931
5525
  return core;
4932
5526
  }
4933
5527
  function bindingOf$3(buffer, size) {
@@ -4986,8 +5580,8 @@ function checkLabels(raw) {
4986
5580
  for (let v = 0; v < n; v++) {
4987
5581
  const label = raw[v];
4988
5582
  if (label >= n) {
4989
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$5}: labels[${v}] = ${label} is not a node index`, {
4990
- label: `${ALGORITHM$5}/labels`,
5583
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$8}: labels[${v}] = ${label} is not a node index`, {
5584
+ label: `${ALGORITHM$8}/labels`,
4991
5585
  message: `the device produced a label outside [0, ${n})`
4992
5586
  });
4993
5587
  }
@@ -5003,9 +5597,9 @@ async function connectedComponents(ctx, s, options) {
5003
5597
  await assertDeviceComputes(ctx);
5004
5598
  const n = s.nodeCount;
5005
5599
  const renumber = options?.renumber !== false;
5006
- const dest = checkDest$4(options?.dest, n);
5600
+ const dest = checkDest$6(options?.dest, n);
5007
5601
  if (options?.signal?.aborted) {
5008
- throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$5}: the signal was aborted before any work started`, {});
5602
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$8}: the signal was aborted before any work started`, {});
5009
5603
  }
5010
5604
  if (n === 0) {
5011
5605
  options?.onProgress?.(1, 1);
@@ -5022,7 +5616,7 @@ async function connectedComponents(ctx, s, options) {
5022
5616
  }
5023
5617
  const edges = ctx.residency.view(s, "edgeList");
5024
5618
  const edgeCount = edges.scalars.edgeCount[0];
5025
- const scope = algorithmScope(ctx, ALGORITHM$5, RING_SLOTS$6);
5619
+ const scope = algorithmScope(ctx, ALGORITHM$8, RING_SLOTS$8);
5026
5620
  try {
5027
5621
  const compBytes = 4 * (n + 1);
5028
5622
  const comp = scope.scratch(compBytes, "comp");
@@ -5051,7 +5645,7 @@ async function connectedComponents(ctx, s, options) {
5051
5645
  return batch.submit();
5052
5646
  };
5053
5647
  queue.writeBuffer(comp, 4 * flagIndex, zero);
5054
- const setup = new CommandBatch(ctx, `${ALGORITHM$5}/setup`);
5648
+ const setup = new CommandBatch(ctx, `${ALGORITHM$8}/setup`);
5055
5649
  let pass = setup.pass("sample-rounds");
5056
5650
  const fillParams = scope.params(FILL_PARAMS, { count: n, value: 0, mode: 1, pad0: 0 });
5057
5651
  fill.dispatch(
@@ -5070,7 +5664,7 @@ async function connectedComponents(ctx, s, options) {
5070
5664
  setup.endPass();
5071
5665
  await submit2(setup).readback;
5072
5666
  ctx.assertReady();
5073
- const sampler = new CommandBatch(ctx, `${ALGORITHM$5}/sample`);
5667
+ const sampler = new CommandBatch(ctx, `${ALGORITHM$8}/sample`);
5074
5668
  pass = sampler.pass("sample");
5075
5669
  const sampleParams = wccParams({ items, stride: 0, r: 0, giant: U32_MAX$2 });
5076
5670
  sample.dispatch(
@@ -5088,7 +5682,7 @@ async function connectedComponents(ctx, s, options) {
5088
5682
  let rounds = 0;
5089
5683
  for (; ; ) {
5090
5684
  queue.writeBuffer(comp, 4 * flagIndex, zero);
5091
- const batch = new CommandBatch(ctx, `${ALGORITHM$5}/rounds`);
5685
+ const batch = new CommandBatch(ctx, `${ALGORITHM$8}/rounds`);
5092
5686
  pass = batch.pass("edge-rounds");
5093
5687
  for (let i = 0; i < ROUNDS_PER_BATCH$1; i++) {
5094
5688
  const params = wccParams({ items: edgeCount, stride: edgePlan.stride ?? edgeCount, r: 0, giant });
@@ -5103,7 +5697,7 @@ async function connectedComponents(ctx, s, options) {
5103
5697
  rounds += ROUNDS_PER_BATCH$1;
5104
5698
  ctx.assertReady();
5105
5699
  if (options?.signal?.aborted) {
5106
- throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$5}: the signal was aborted`, {
5700
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$8}: the signal was aborted`, {
5107
5701
  batchId: submitted.id
5108
5702
  });
5109
5703
  }
@@ -5113,12 +5707,12 @@ async function connectedComponents(ctx, s, options) {
5113
5707
  if (rounds >= MAX_WCC_ROUNDS) {
5114
5708
  throw new WebGpuGraphError(
5115
5709
  "E_VALIDATION",
5116
- `${ALGORITHM$5}: the changed flag never settled in ${MAX_WCC_ROUNDS} rounds`,
5117
- { label: ALGORITHM$5, message: `the changed flag never settled in ${MAX_WCC_ROUNDS} rounds` }
5710
+ `${ALGORITHM$8}: the changed flag never settled in ${MAX_WCC_ROUNDS} rounds`,
5711
+ { label: ALGORITHM$8, message: `the changed flag never settled in ${MAX_WCC_ROUNDS} rounds` }
5118
5712
  );
5119
5713
  }
5120
5714
  }
5121
- const final = new CommandBatch(ctx, `${ALGORITHM$5}/final`);
5715
+ const final = new CommandBatch(ctx, `${ALGORITHM$8}/final`);
5122
5716
  recordCompress(final.pass("compress"));
5123
5717
  final.endPass();
5124
5718
  await submit2(final).readback;
@@ -5360,8 +5954,20 @@ class TieredPlanner {
5360
5954
  async function prepareSegmentedReduce(scope, core, options) {
5361
5955
  const op = opCode(options.op);
5362
5956
  validateValueSnippet(options.valueSnippet);
5363
- const perm = null;
5364
- const ranges = [{ tier: 0, start: 0, end: rowCountOf(core, "segmentedReduce") }];
5957
+ const { tiers } = options;
5958
+ const perm = tiers?.perm ?? null;
5959
+ if (tiers !== null && scope.workgroupSize < MID_TIER_LANES) {
5960
+ throw new WebGpuGraphError(
5961
+ "E_UNSUPPORTED",
5962
+ `segmentedReduce: the mid tier folds ${MID_TIER_LANES} lanes per row, more than the workgroup size ${scope.workgroupSize}`,
5963
+ { feature: "segmentedReduce.tiers" }
5964
+ );
5965
+ }
5966
+ const ranges = tiers === null ? [{ tier: 0, start: 0, end: rowCountOf(core, "segmentedReduce") }] : [
5967
+ { tier: 2, start: 0, end: tiers.segmentOffsets[1] },
5968
+ { tier: 1, start: tiers.segmentOffsets[1], end: tiers.segmentOffsets[2] },
5969
+ { tier: 0, start: tiers.segmentOffsets[2], end: tiers.segmentOffsets[4] }
5970
+ ];
5365
5971
  const compiled = [];
5366
5972
  for (const range of ranges) {
5367
5973
  if (range.tier !== 0 && range.end <= range.start) {
@@ -5484,8 +6090,8 @@ async function prepareSpmvPull(scope, rev, options) {
5484
6090
  return new SpmvPullPlannerImpl(scope, compiled, perm, options.weights);
5485
6091
  }
5486
6092
  const PR_BATCH = 8;
5487
- const RING_SLOTS$5 = 2 * PR_BATCH + 2;
5488
- function checkDest$3(dest, n, algorithm) {
6093
+ const RING_SLOTS$7 = 2 * PR_BATCH + 2;
6094
+ function checkDest$5(dest, n, algorithm) {
5489
6095
  if (dest === void 0) {
5490
6096
  return null;
5491
6097
  }
@@ -5532,7 +6138,7 @@ async function run(ctx, s, personalization, options, algorithm) {
5532
6138
  expected: "a positive integer"
5533
6139
  });
5534
6140
  }
5535
- const dest = checkDest$3(options?.dest, n, algorithm);
6141
+ const dest = checkDest$5(options?.dest, n, algorithm);
5536
6142
  if (options?.signal?.aborted) {
5537
6143
  throw aborted$3(algorithm);
5538
6144
  }
@@ -5562,7 +6168,7 @@ async function run(ctx, s, personalization, options, algorithm) {
5562
6168
  const weights = useWeights ? void 0 : null;
5563
6169
  const weightedCore = useWeights ? core : { ...core, weights: null, hasWeights: false };
5564
6170
  const weightedRev = useWeights ? rev : { ...rev, weights: null, hasWeights: false };
5565
- const scope = algorithmScope(ctx, algorithm, RING_SLOTS$5);
6171
+ const scope = algorithmScope(ctx, algorithm, RING_SLOTS$7);
5566
6172
  let uploaded = null;
5567
6173
  try {
5568
6174
  const bytes = 4 * n;
@@ -5725,8 +6331,8 @@ async function personalizedPageRank(ctx, s, personalization, options) {
5725
6331
  return run(ctx, s, normalised2, options, "personalizedPageRank");
5726
6332
  }
5727
6333
  const BATCH = 8;
5728
- const RING_SLOTS$4 = 4 * BATCH + 8;
5729
- function checkDest$2(dest, n, algorithm) {
6334
+ const RING_SLOTS$6 = 4 * BATCH + 8;
6335
+ function checkDest$4(dest, n, algorithm) {
5730
6336
  if (dest === void 0) {
5731
6337
  return null;
5732
6338
  }
@@ -5764,7 +6370,7 @@ function whole(buffer, size) {
5764
6370
  }
5765
6371
  async function runPowerIteration(ctx, n, config) {
5766
6372
  await assertDeviceComputes(ctx);
5767
- const scope = algorithmScope(ctx, config.label, RING_SLOTS$4);
6373
+ const scope = algorithmScope(ctx, config.label, RING_SLOTS$6);
5768
6374
  try {
5769
6375
  const bytes = 4 * n;
5770
6376
  const ring = (config.alternate === null ? ["rankA", "rankB"] : ["rankA", "rankB", "rankC"]).map(
@@ -5876,7 +6482,7 @@ function resolve(ctx, s, options, algorithm) {
5876
6482
  expected: "a positive integer"
5877
6483
  });
5878
6484
  }
5879
- const dest = checkDest$2(options?.dest, n, algorithm);
6485
+ const dest = checkDest$4(options?.dest, n, algorithm);
5880
6486
  if (options?.signal?.aborted) {
5881
6487
  throw aborted$2(algorithm);
5882
6488
  }
@@ -6032,7 +6638,7 @@ function checkCount(what, count) {
6032
6638
  });
6033
6639
  }
6034
6640
  }
6035
- function checkWords$1(what, name, binding, words) {
6641
+ function checkWords$3(what, name, binding, words) {
6036
6642
  if (binding.size < 4 * words) {
6037
6643
  throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${what}: ${name} is smaller than 4 x ${words} bytes`, {
6038
6644
  argument: name,
@@ -6052,15 +6658,15 @@ function checkWordIndex(what, name, index, block) {
6052
6658
  }
6053
6659
  function checkCompactArguments(r) {
6054
6660
  checkCount("compact", r.count);
6055
- checkWords$1("compact", "queue", r.queue, r.count);
6056
- checkWords$1("compact", "flags", r.flags, r.count);
6057
- checkWords$1("compact", "out", r.out, r.count);
6661
+ checkWords$3("compact", "queue", r.queue, r.count);
6662
+ checkWords$3("compact", "flags", r.flags, r.count);
6663
+ checkWords$3("compact", "out", r.out, r.count);
6058
6664
  checkWordIndex("compact", "outIndex", r.outIndex, r.outCount);
6059
6665
  }
6060
6666
  function checkDedupeArguments(r) {
6061
6667
  checkCount("dedupe", r.count);
6062
- checkWords$1("dedupe", "queue", r.queue, r.count);
6063
- checkWords$1("dedupe", "out", r.out, Math.min(r.count, Math.floor(r.owner.size / 4)));
6668
+ checkWords$3("dedupe", "queue", r.queue, r.count);
6669
+ checkWords$3("dedupe", "out", r.out, Math.min(r.count, Math.floor(r.owner.size / 4)));
6064
6670
  checkWordIndex("dedupe", "outIndex", r.outIndex, r.outCount);
6065
6671
  if (r.countIndex === U32_MAX$2) {
6066
6672
  return;
@@ -6544,7 +7150,7 @@ class RadixSortPlannerImpl {
6544
7150
  return src;
6545
7151
  }
6546
7152
  }
6547
- const ALGORITHM$4 = "breadthFirstSearch";
7153
+ const ALGORITHM$7 = "breadthFirstSearch";
6548
7154
  const NEXT_DEGREE_MAX_GROUPS = 128;
6549
7155
  function bfsRingSlots(windows, levelsPerSubmit) {
6550
7156
  return Math.max((5 + 4 * windows) * levelsPerSubmit + 16, RESULT_BATCH_SLOTS + windows);
@@ -6556,7 +7162,7 @@ function normaliseMaxDepth(maxDepth) {
6556
7162
  }
6557
7163
  return Math.max(0, Math.ceil(maxDepth));
6558
7164
  }
6559
- function checkDest$1(dest, n) {
7165
+ function checkDest$3(dest, n) {
6560
7166
  if (dest === void 0) {
6561
7167
  return null;
6562
7168
  }
@@ -6565,7 +7171,7 @@ function checkDest$1(dest, n) {
6565
7171
  }
6566
7172
  throw new WebGpuGraphError(
6567
7173
  "E_INVALID_ARGUMENT",
6568
- `${ALGORITHM$4}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
7174
+ `${ALGORITHM$7}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
6569
7175
  {
6570
7176
  argument: "dest",
6571
7177
  value: `${dest.constructor.name}(${dest.length})`,
@@ -6580,8 +7186,8 @@ function degreeView(ctx, s, name) {
6580
7186
  const { bindings } = ctx.residency.view(s, name);
6581
7187
  const { [name]: binding } = bindings;
6582
7188
  if (binding === void 0) {
6583
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$4}: the ${name} view has no ${name} binding`, {
6584
- label: `${ALGORITHM$4}/${name}`,
7189
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$7}: the ${name} view has no ${name} binding`, {
7190
+ label: `${ALGORITHM$7}/${name}`,
6585
7191
  message: `the ${name} view has no ${name} binding`
6586
7192
  });
6587
7193
  }
@@ -6590,15 +7196,15 @@ function degreeView(ctx, s, name) {
6590
7196
  function aborted$1(batchId) {
6591
7197
  return new WebGpuGraphError(
6592
7198
  "E_ABORTED",
6593
- `${ALGORITHM$4}: the signal was aborted`,
7199
+ `${ALGORITHM$7}: the signal was aborted`,
6594
7200
  batchId === void 0 ? {} : { batchId }
6595
7201
  );
6596
7202
  }
6597
7203
  function wordOf$1(block, name) {
6598
7204
  const value = block[name];
6599
7205
  if (typeof value !== "number") {
6600
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$4}: counters.${name} did not decode to a number`, {
6601
- label: `${ALGORITHM$4}/counters`,
7206
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$7}: counters.${name} did not decode to a number`, {
7207
+ label: `${ALGORITHM$7}/counters`,
6602
7208
  message: `the field ${name} did not decode to a number`
6603
7209
  });
6604
7210
  }
@@ -6609,7 +7215,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6609
7215
  await assertDeviceComputes(ctx);
6610
7216
  const n = s.nodeCount;
6611
7217
  if (!Number.isInteger(source) || source < 0 || source >= n) {
6612
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$4}: source ${source} is outside [0, ${n})`, {
7218
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$7}: source ${source} is outside [0, ${n})`, {
6613
7219
  argument: "source",
6614
7220
  value: source,
6615
7221
  expected: `an integer in [0, ${n})`
@@ -6619,7 +7225,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6619
7225
  if (!Number.isInteger(levelsPerSubmit) || levelsPerSubmit < 1 || levelsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
6620
7226
  throw new WebGpuGraphError(
6621
7227
  "E_INVALID_ARGUMENT",
6622
- `${ALGORITHM$4}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7228
+ `${ALGORITHM$7}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
6623
7229
  {
6624
7230
  argument: "levelsPerSubmit",
6625
7231
  value: levelsPerSubmit,
@@ -6627,7 +7233,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6627
7233
  }
6628
7234
  );
6629
7235
  }
6630
- const dest = checkDest$1(options?.dest, n);
7236
+ const dest = checkDest$3(options?.dest, n);
6631
7237
  const maxDepth = normaliseMaxDepth(options?.maxDepth);
6632
7238
  const predKind = tuning.predKind ?? 1;
6633
7239
  if (options?.signal?.aborted) {
@@ -6638,12 +7244,12 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6638
7244
  if (s.directed && 4 * s.arcCount > ctx.caps.limits.maxStorageBufferBindingSize) {
6639
7245
  throw new WebGpuGraphError(
6640
7246
  "E_TOO_LARGE",
6641
- `${ALGORITHM$4}: 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`,
7247
+ `${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`,
6642
7248
  {
6643
7249
  needed: 4 * s.arcCount,
6644
7250
  limit: ctx.caps.limits.maxStorageBufferBindingSize,
6645
7251
  path: "windowed",
6646
- algorithm: ALGORITHM$4
7252
+ algorithm: ALGORITHM$7
6647
7253
  }
6648
7254
  );
6649
7255
  }
@@ -6651,7 +7257,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6651
7257
  const backward = coreWindows(reverse);
6652
7258
  const outDegree = degreeView(ctx, s, "outDegree");
6653
7259
  const inDegree = degreeView(ctx, s, "inDegree");
6654
- const scope = algorithmScope(ctx, ALGORITHM$4, bfsRingSlots(forward.length, levelsPerSubmit));
7260
+ const scope = algorithmScope(ctx, ALGORITHM$7, bfsRingSlots(forward.length, levelsPerSubmit));
6655
7261
  tuning.onScope?.(scope);
6656
7262
  try {
6657
7263
  const bytes = 4 * n;
@@ -6715,7 +7321,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6715
7321
  scope.flush();
6716
7322
  return batch.submit();
6717
7323
  };
6718
- const setup = new CommandBatch(ctx, `${ALGORITHM$4}/setup`);
7324
+ const setup = new CommandBatch(ctx, `${ALGORITHM$7}/setup`);
6719
7325
  const setupPass = setup.pass("fill");
6720
7326
  recordFill2(setupPass, depth, INVALID_INDEX, 0);
6721
7327
  recordFill2(setupPass, iota, 0, 1);
@@ -6735,7 +7341,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6735
7341
  let submits = 0;
6736
7342
  for (; ; ) {
6737
7343
  queue.writeBuffer(counters.buffer, counters.offset + 4 * W.unvisitedCount, new Uint32Array(3));
6738
- const batch = new CommandBatch(ctx, `${ALGORITHM$4}/levels`);
7344
+ const batch = new CommandBatch(ctx, `${ALGORITHM$7}/levels`);
6739
7345
  const pass2 = batch.pass("bfs");
6740
7346
  recordRebuild(pass2);
6741
7347
  const bitsParams = scope.params(FILL_PARAMS, { count: bitsWords, value: 0, mode: 0, pad0: 0 });
@@ -6845,8 +7451,8 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6845
7451
  if (submits > n + 1) {
6846
7452
  throw new WebGpuGraphError(
6847
7453
  "E_VALIDATION",
6848
- `${ALGORITHM$4}: the done flag never rose in ${submits} submits (a traversal has at most ${n} levels)`,
6849
- { label: ALGORITHM$4, message: `the done flag never rose in ${submits} submits` }
7454
+ `${ALGORITHM$7}: the done flag never rose in ${submits} submits (a traversal has at most ${n} levels)`,
7455
+ { label: ALGORITHM$7, message: `the done flag never rose in ${submits} submits` }
6850
7456
  );
6851
7457
  }
6852
7458
  }
@@ -6860,7 +7466,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6860
7466
  offsets: bindingOf$1(scope.scratch(histBytes, "order/offsets"), histBytes)
6861
7467
  };
6862
7468
  const parent = bindingOf$1(scope.scratch(bytes, "parent"), bytes);
6863
- const result = new CommandBatch(ctx, `${ALGORITHM$4}/result`);
7469
+ const result = new CommandBatch(ctx, `${ALGORITHM$7}/result`);
6864
7470
  result.copy(depth, keys, bytes);
6865
7471
  const pass = result.pass("result");
6866
7472
  recordFill2(pass, vals, 0, 1);
@@ -6897,9 +7503,9 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6897
7503
  if (visitedCount > n) {
6898
7504
  throw new WebGpuGraphError(
6899
7505
  "E_VALIDATION",
6900
- `${ALGORITHM$4}: visitedCount ${visitedCount} exceeds the ${n} vertices (a duplicate claim)`,
7506
+ `${ALGORITHM$7}: visitedCount ${visitedCount} exceeds the ${n} vertices (a duplicate claim)`,
6901
7507
  {
6902
- label: `${ALGORITHM$4}/visitedCount`,
7508
+ label: `${ALGORITHM$7}/visitedCount`,
6903
7509
  message: `the device counted ${visitedCount} visits of ${n} vertices`
6904
7510
  }
6905
7511
  );
@@ -6923,9 +7529,9 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
6923
7529
  function breadthFirstSearch(ctx, s, source, options) {
6924
7530
  return bfsWithTuning(ctx, s, source, options, {});
6925
7531
  }
6926
- const ALGORITHM$3 = "sssp";
7532
+ const ALGORITHM$6 = "sssp";
6927
7533
  const HALF_ALIGN = 64;
6928
- const RING_SLOTS$3 = 4 * MAX_LEVELS_PER_SUBMIT + 40;
7534
+ const RING_SLOTS$5 = 4 * MAX_LEVELS_PER_SUBMIT + 40;
6929
7535
  function bitsOf(value) {
6930
7536
  return new Uint32Array(Float32Array.of(value).buffer)[0];
6931
7537
  }
@@ -6951,14 +7557,14 @@ function assertSource(algorithm, source, n) {
6951
7557
  function wordOf(block, name) {
6952
7558
  const value = block[name];
6953
7559
  if (typeof value !== "number") {
6954
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$3}: counters.${name} did not decode to a number`, {
6955
- label: `${ALGORITHM$3}/counters`,
7560
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$6}: counters.${name} did not decode to a number`, {
7561
+ label: `${ALGORITHM$6}/counters`,
6956
7562
  message: `the field ${name} did not decode to a number`
6957
7563
  });
6958
7564
  }
6959
7565
  return value;
6960
7566
  }
6961
- function checkDest(algorithm, dest, n) {
7567
+ function checkDest$2(algorithm, dest, n) {
6962
7568
  if (dest === void 0) {
6963
7569
  return null;
6964
7570
  }
@@ -7119,12 +7725,12 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7119
7725
  ctx.assertReady();
7120
7726
  await assertDeviceComputes(ctx);
7121
7727
  const n = s.nodeCount;
7122
- assertSource(ALGORITHM$3, source, n);
7728
+ assertSource(ALGORITHM$6, source, n);
7123
7729
  const roundsPerSubmit = tuning.roundsPerSubmit ?? MAX_LEVELS_PER_SUBMIT;
7124
7730
  if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
7125
7731
  throw new WebGpuGraphError(
7126
7732
  "E_INVALID_ARGUMENT",
7127
- `${ALGORITHM$3}: roundsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7733
+ `${ALGORITHM$6}: roundsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7128
7734
  {
7129
7735
  argument: "roundsPerSubmit",
7130
7736
  value: roundsPerSubmit,
@@ -7133,42 +7739,42 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7133
7739
  );
7134
7740
  }
7135
7741
  if (tuning.delta !== void 0 && !(Number.isFinite(tuning.delta) && tuning.delta > 0)) {
7136
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$3}: delta must be a finite positive number`, {
7742
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$6}: delta must be a finite positive number`, {
7137
7743
  argument: "delta",
7138
7744
  value: tuning.delta,
7139
7745
  expected: "a finite positive number"
7140
7746
  });
7141
7747
  }
7142
- const dest = checkDest(ALGORITHM$3, options?.dest, n);
7143
- const vector2 = resolveWeights$1(ALGORITHM$3, s, options?.weights);
7144
- const cutoff = normaliseCutoff(ALGORITHM$3, options?.cutoff);
7748
+ const dest = checkDest$2(ALGORITHM$6, options?.dest, n);
7749
+ const vector2 = resolveWeights$1(ALGORITHM$6, s, options?.weights);
7750
+ const cutoff = normaliseCutoff(ALGORITHM$6, options?.cutoff);
7145
7751
  if (options?.signal?.aborted) {
7146
- throw aborted(ALGORITHM$3);
7752
+ throw aborted(ALGORITHM$6);
7147
7753
  }
7148
7754
  if (vector2 === null || vector2.allOne) {
7149
7755
  return unitWeightRoute(ctx, s, source, cutoff, dest, options);
7150
7756
  }
7151
7757
  if (!vector2.nonNegative) {
7152
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a negative weight has no shortest path here`, {
7758
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$6}: a negative weight has no shortest path here`, {
7153
7759
  feature: "sssp.negativeWeights",
7154
7760
  hint: "use bellmanFord"
7155
7761
  });
7156
7762
  }
7157
7763
  if (!vector2.finite) {
7158
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a NaN or infinite weight has no bit-pattern order`, {
7764
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$6}: a NaN or infinite weight has no bit-pattern order`, {
7159
7765
  feature: "sssp.nonFiniteWeights"
7160
7766
  });
7161
7767
  }
7162
7768
  const { arcCount } = s;
7163
7769
  const core = ctx.residency.core(s);
7164
7770
  const limit = ctx.caps.limits.maxStorageBufferBindingSize;
7165
- assertWholeCore(core, arcCount, limit, ALGORITHM$3);
7771
+ assertWholeCore(core, arcCount, limit, ALGORITHM$6);
7166
7772
  const cap = Math.ceil(Math.max(1, arcCount) / HALF_ALIGN) * HALF_ALIGN;
7167
7773
  if (8 * cap > limit) {
7168
7774
  throw new WebGpuGraphError(
7169
7775
  "E_TOO_LARGE",
7170
- `${ALGORITHM$3}: the near-far queue of ${cap} entries per half needs ${8 * cap} bytes, above the ${limit}-byte binding limit (the relax is never windowed)`,
7171
- { needed: 8 * cap, limit, path: "sssp.queue", algorithm: ALGORITHM$3 }
7776
+ `${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)`,
7777
+ { needed: 8 * cap, limit, path: "sssp.queue", algorithm: ALGORITHM$6 }
7172
7778
  );
7173
7779
  }
7174
7780
  const delta = Math.fround(
@@ -7177,7 +7783,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7177
7783
  const deltaBits = bitsOf(delta);
7178
7784
  const maxRounds = n + Math.ceil(vector2.sum / delta) + 1;
7179
7785
  const maxSubmits = Math.ceil((maxRounds + 1) / roundsPerSubmit) + 1;
7180
- const scope = algorithmScope(ctx, ALGORITHM$3, RING_SLOTS$3);
7786
+ const scope = algorithmScope(ctx, ALGORITHM$6, RING_SLOTS$5);
7181
7787
  try {
7182
7788
  const wg = ctx.workgroupSize;
7183
7789
  const bytes = 4 * n;
@@ -7215,7 +7821,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7215
7821
  scope.flush();
7216
7822
  return batch.submit();
7217
7823
  };
7218
- const setup = new CommandBatch(ctx, `${ALGORITHM$3}/setup`);
7824
+ const setup = new CommandBatch(ctx, `${ALGORITHM$6}/setup`);
7219
7825
  const setupPass = setup.pass("fill");
7220
7826
  recordFill2(setupPass, dist, n, F32_INF_BITS);
7221
7827
  recordFill2(setupPass, pred, predWords, INVALID_INDEX);
@@ -7261,7 +7867,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7261
7867
  let roundsRecorded = 0;
7262
7868
  let submits = 0;
7263
7869
  for (; ; ) {
7264
- const batch = new CommandBatch(ctx, `${ALGORITHM$3}/rounds`);
7870
+ const batch = new CommandBatch(ctx, `${ALGORITHM$6}/rounds`);
7265
7871
  const pass = batch.pass("sssp");
7266
7872
  const near = scope.params(FRONTIER_PARAMS, { ...relaxFields, role: 0 });
7267
7873
  const far = scope.params(FRONTIER_PARAMS, { ...relaxFields, role: 1 });
@@ -7284,7 +7890,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7284
7890
  submits += 1;
7285
7891
  ctx.assertReady();
7286
7892
  if (options?.signal?.aborted) {
7287
- throw aborted(ALGORITHM$3, submitted.id);
7893
+ throw aborted(ALGORITHM$6, submitted.id);
7288
7894
  }
7289
7895
  options?.onProgress?.(Math.min(roundsRecorded, maxRounds), maxRounds);
7290
7896
  if (inspect !== null && tuning.onRound !== void 0) {
@@ -7309,26 +7915,26 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7309
7915
  const needed = Math.max(wordOf(block, "nextFrontierCount"), wordOf(block, "nextFarCount"));
7310
7916
  throw new WebGpuGraphError(
7311
7917
  "E_TOO_LARGE",
7312
- `${ALGORITHM$3}: a raw pile of ${needed} entries overflowed its ${cap}-entry half`,
7313
- { needed, limit: cap, path: "sssp.pile", algorithm: ALGORITHM$3 }
7918
+ `${ALGORITHM$6}: a raw pile of ${needed} entries overflowed its ${cap}-entry half`,
7919
+ { needed, limit: cap, path: "sssp.pile", algorithm: ALGORITHM$6 }
7314
7920
  );
7315
7921
  }
7316
7922
  throw new WebGpuGraphError(
7317
7923
  "E_UNSUPPORTED",
7318
- `${ALGORITHM$3}: the f32 threshold ${wordOf(block, "thresholdBits")} absorbed the delta ${deltaBits} (as bit patterns); the far pile can no longer be bucketed`,
7924
+ `${ALGORITHM$6}: the f32 threshold ${wordOf(block, "thresholdBits")} absorbed the delta ${deltaBits} (as bit patterns); the far pile can no longer be bucketed`,
7319
7925
  { feature: "sssp.thresholdAbsorbed", hint: "the distances outgrew the delta's f32 precision" }
7320
7926
  );
7321
7927
  }
7322
7928
  if (submits > maxSubmits) {
7323
7929
  throw new WebGpuGraphError(
7324
7930
  "E_VALIDATION",
7325
- `${ALGORITHM$3}: the done flag never rose in ${submits} submits (at most ${maxRounds} rounds)`,
7326
- { label: `${ALGORITHM$3}/rounds`, message: `the done flag never rose in ${submits} submits` }
7931
+ `${ALGORITHM$6}: the done flag never rose in ${submits} submits (at most ${maxRounds} rounds)`,
7932
+ { label: `${ALGORITHM$6}/rounds`, message: `the done flag never rose in ${submits} submits` }
7327
7933
  );
7328
7934
  }
7329
7935
  }
7330
7936
  const passed = await predecessorPass({
7331
- algorithm: ALGORITHM$3,
7937
+ algorithm: ALGORITHM$6,
7332
7938
  ctx,
7333
7939
  scope,
7334
7940
  predKernel,
@@ -7344,8 +7950,8 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7344
7950
  if (passed.orphans !== 0) {
7345
7951
  throw new WebGpuGraphError(
7346
7952
  "E_VALIDATION",
7347
- `${ALGORITHM$3}: ${passed.orphans} reached node(s) the predecessor key never reached (a kernel bug)`,
7348
- { label: `${ALGORITHM$3}/pred`, message: `${passed.orphans} orphan(s) in the predecessor pass` }
7953
+ `${ALGORITHM$6}: ${passed.orphans} reached node(s) the predecessor key never reached (a kernel bug)`,
7954
+ { label: `${ALGORITHM$6}/pred`, message: `${passed.orphans} orphan(s) in the predecessor pass` }
7349
7955
  );
7350
7956
  }
7351
7957
  const distOut = dest ?? new Float32Array(n);
@@ -7364,10 +7970,10 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
7364
7970
  function sssp(ctx, s, source, options) {
7365
7971
  return ssspWithTuning(ctx, s, source, options, {});
7366
7972
  }
7367
- const ALGORITHM$2 = "bellmanFord";
7973
+ const ALGORITHM$5 = "bellmanFord";
7368
7974
  const ROUNDS_PER_BATCH = 8;
7369
7975
  const MAX_RETRIES = 16;
7370
- const RING_SLOTS$2 = MAX_LEVELS_PER_SUBMIT + 16;
7976
+ const RING_SLOTS$4 = MAX_LEVELS_PER_SUBMIT + 16;
7371
7977
  function assertSymmetric(s, vector2) {
7372
7978
  const { arcToEdge, edgeToArc } = s;
7373
7979
  for (let a = 0; a < s.arcCount; a++) {
@@ -7375,7 +7981,7 @@ function assertSymmetric(s, vector2) {
7375
7981
  if (vector2[a] !== vector2[forward]) {
7376
7982
  throw new WebGpuGraphError(
7377
7983
  "E_UNSUPPORTED",
7378
- `${ALGORITHM$2}: 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`,
7984
+ `${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`,
7379
7985
  { feature: "bellmanFord.asymmetricUndirectedWeights", hint: "use a directed snapshot" }
7380
7986
  );
7381
7987
  }
@@ -7385,10 +7991,10 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7385
7991
  ctx.assertReady();
7386
7992
  await assertDeviceComputes(ctx);
7387
7993
  const n = s.nodeCount;
7388
- assertSource(ALGORITHM$2, source, n);
7994
+ assertSource(ALGORITHM$5, source, n);
7389
7995
  const maxRetries = tuning.maxRetries ?? MAX_RETRIES;
7390
7996
  if (!Number.isInteger(maxRetries) || maxRetries < 1) {
7391
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$2}: maxRetries must be an integer >= 1`, {
7997
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$5}: maxRetries must be an integer >= 1`, {
7392
7998
  argument: "maxRetries",
7393
7999
  value: maxRetries,
7394
8000
  expected: "an integer >= 1"
@@ -7398,7 +8004,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7398
8004
  if (!Number.isInteger(roundsPerBatch) || roundsPerBatch < 1 || roundsPerBatch > MAX_LEVELS_PER_SUBMIT) {
7399
8005
  throw new WebGpuGraphError(
7400
8006
  "E_INVALID_ARGUMENT",
7401
- `${ALGORITHM$2}: roundsPerBatch must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8007
+ `${ALGORITHM$5}: roundsPerBatch must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
7402
8008
  {
7403
8009
  argument: "roundsPerBatch",
7404
8010
  value: roundsPerBatch,
@@ -7406,18 +8012,18 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7406
8012
  }
7407
8013
  );
7408
8014
  }
7409
- const dest = checkDest(ALGORITHM$2, options?.dest, n);
7410
- const vector2 = resolveWeights$1(ALGORITHM$2, s, options?.weights);
7411
- const cutoff = normaliseCutoff(ALGORITHM$2, options?.cutoff);
8015
+ const dest = checkDest$2(ALGORITHM$5, options?.dest, n);
8016
+ const vector2 = resolveWeights$1(ALGORITHM$5, s, options?.weights);
8017
+ const cutoff = normaliseCutoff(ALGORITHM$5, options?.cutoff);
7412
8018
  if (options?.signal?.aborted) {
7413
- throw aborted(ALGORITHM$2);
8019
+ throw aborted(ALGORITHM$5);
7414
8020
  }
7415
8021
  if (vector2 === null || vector2.allOne) {
7416
8022
  const unit = await unitWeightRoute(ctx, s, source, cutoff, dest, options);
7417
8023
  return { result: { ...unit, hasNegativeCycle: false }, rounds: 0, retryExhaustedRounds: 0 };
7418
8024
  }
7419
8025
  if (!vector2.finite) {
7420
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$2}: a NaN or infinite weight has no shortest path`, {
8026
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$5}: a NaN or infinite weight has no shortest path`, {
7421
8027
  feature: "bellmanFord.nonFiniteWeights"
7422
8028
  });
7423
8029
  }
@@ -7426,10 +8032,10 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7426
8032
  }
7427
8033
  const { arcCount } = s;
7428
8034
  const core = ctx.residency.core(s, ["rowPtr", "colIdx", "weights", "edgeToArc"]);
7429
- assertWholeCore(core, arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$2);
8035
+ assertWholeCore(core, arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$5);
7430
8036
  const edges = ctx.residency.view(s, "edgeList");
7431
8037
  const edgeCount = edges.scalars.edgeCount[0];
7432
- const scope = algorithmScope(ctx, ALGORITHM$2, RING_SLOTS$2);
8038
+ const scope = algorithmScope(ctx, ALGORITHM$5, RING_SLOTS$4);
7433
8039
  try {
7434
8040
  const wg = ctx.workgroupSize;
7435
8041
  const bytes = 4 * n;
@@ -7464,7 +8070,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7464
8070
  scope.flush();
7465
8071
  return batch.submit();
7466
8072
  };
7467
- const setup = new CommandBatch(ctx, `${ALGORITHM$2}/setup`);
8073
+ const setup = new CommandBatch(ctx, `${ALGORITHM$5}/setup`);
7468
8074
  const setupPass = setup.pass("fill");
7469
8075
  recordFill2(setupPass, dist, n, F32_INF_BITS);
7470
8076
  recordFill2(setupPass, pred, predWords, INVALID_INDEX);
@@ -7493,7 +8099,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7493
8099
  const zero = new Uint32Array(BF_FLAGS.byteLength / 4);
7494
8100
  const runRounds = async (count, label) => {
7495
8101
  queue.writeBuffer(flags.buffer, flags.offset, zero);
7496
- const batch = new CommandBatch(ctx, `${ALGORITHM$2}/${label}`);
8102
+ const batch = new CommandBatch(ctx, `${ALGORITHM$5}/${label}`);
7497
8103
  const pass = batch.pass("relax");
7498
8104
  const params = scope.params(BF_PARAMS, relaxFields);
7499
8105
  const bound = relax.bind({ ...relaxBindings, P: params.binding });
@@ -7518,9 +8124,9 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7518
8124
  if (decision.retryExhausted !== 0) {
7519
8125
  throw new WebGpuGraphError(
7520
8126
  "E_VALIDATION",
7521
- `${ALGORITHM$2}: a lane exhausted the ${maxRetries}-retry compare-exchange bound in the decision round, so its change is not a verdict`,
8127
+ `${ALGORITHM$5}: a lane exhausted the ${maxRetries}-retry compare-exchange bound in the decision round, so its change is not a verdict`,
7522
8128
  {
7523
- label: `${ALGORITHM$2}/retry`,
8129
+ label: `${ALGORITHM$5}/retry`,
7524
8130
  message: "retryExhausted in the decision round",
7525
8131
  batchId: decision.id
7526
8132
  }
@@ -7536,7 +8142,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7536
8142
  retryExhaustedRounds += 1;
7537
8143
  }
7538
8144
  if (options?.signal?.aborted) {
7539
- throw aborted(ALGORITHM$2, batch.id);
8145
+ throw aborted(ALGORITHM$5, batch.id);
7540
8146
  }
7541
8147
  options?.onProgress?.(rounds, n);
7542
8148
  if (batch.changed === 0 && batch.retryExhausted === 0) {
@@ -7544,7 +8150,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7544
8150
  }
7545
8151
  }
7546
8152
  const passed = await predecessorPass({
7547
- algorithm: ALGORITHM$2,
8153
+ algorithm: ALGORITHM$5,
7548
8154
  ctx,
7549
8155
  scope,
7550
8156
  predKernel,
@@ -7560,7 +8166,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7560
8166
  if (passed.orphans !== 0 && !hasNegativeCycle) {
7561
8167
  throw new WebGpuGraphError(
7562
8168
  "E_UNSUPPORTED",
7563
- `${ALGORITHM$2}: ${passed.orphans} reached node(s) the tight subgraph never reaches (a cycle of weights below one f32 ulp relaxed once)`,
8169
+ `${ALGORITHM$5}: ${passed.orphans} reached node(s) the tight subgraph never reaches (a cycle of weights below one f32 ulp relaxed once)`,
7564
8170
  {
7565
8171
  feature: "bellmanFord.roundedCycle",
7566
8172
  hint: "a cycle of weights below one f32 ulp relaxed once at a distance above 2^24; scale the weights or shorten the distances"
@@ -7587,17 +8193,17 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
7587
8193
  async function bellmanFord(ctx, s, source, options) {
7588
8194
  return (await bellmanFordWithTuning(ctx, s, source, options, {})).result;
7589
8195
  }
7590
- const ALGORITHM$1 = "betweennessCentrality";
8196
+ const ALGORITHM$4 = "betweennessCentrality";
7591
8197
  const BYTES_PER_NODE_SOURCE = 16;
7592
8198
  const SAMPLE_SEED = 2654435769;
7593
- const RING_SLOTS$1 = BC_BACKWARD_LEVELS_PER_SUBMIT + 16;
8199
+ const RING_SLOTS$3 = BC_BACKWARD_LEVELS_PER_SUBMIT + 16;
7594
8200
  function planBatchSize(n, remaining, limits) {
7595
8201
  const needed = 4 * (n + 2);
7596
8202
  if (needed > limits.maxStorageBufferBindingSize) {
7597
8203
  throw new WebGpuGraphError(
7598
8204
  "E_TOO_LARGE",
7599
- `${ALGORITHM$1}: 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`,
7600
- { needed, limit: limits.maxStorageBufferBindingSize, path: "binding", algorithm: ALGORITHM$1 }
8205
+ `${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`,
8206
+ { needed, limit: limits.maxStorageBufferBindingSize, path: "binding", algorithm: ALGORITHM$4 }
7601
8207
  );
7602
8208
  }
7603
8209
  const kByBinding = Math.floor(limits.maxStorageBufferBindingSize / (4 * n));
@@ -7618,7 +8224,7 @@ function drawSources(n, k) {
7618
8224
  return pool.slice(0, k);
7619
8225
  }
7620
8226
  function badArgument(argument, value, expected) {
7621
- return new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$1}: ${argument} must be ${expected}`, {
8227
+ return new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$4}: ${argument} must be ${expected}`, {
7622
8228
  argument,
7623
8229
  value,
7624
8230
  expected
@@ -7670,7 +8276,7 @@ async function submit(state, batch, signal) {
7670
8276
  const back = await submitted.readback;
7671
8277
  state.ctx.assertReady();
7672
8278
  if (signal?.aborted) {
7673
- throw aborted(ALGORITHM$1, submitted.id);
8279
+ throw aborted(ALGORITHM$4, submitted.id);
7674
8280
  }
7675
8281
  return back;
7676
8282
  }
@@ -7687,7 +8293,7 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
7687
8293
  let overflow = false;
7688
8294
  let endsWords = null;
7689
8295
  for (let first = true; endsWords === null; first = false) {
7690
- const batch = new CommandBatch(ctx, `${ALGORITHM$1}/forward`);
8296
+ const batch = new CommandBatch(ctx, `${ALGORITHM$4}/forward`);
7691
8297
  const pass = batch.pass("forward");
7692
8298
  if (first) {
7693
8299
  recordFill(state, pass, depthK, words, 4294967295);
@@ -7729,8 +8335,8 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
7729
8335
  overflow = words32[W.sigmaOverflow] !== 0;
7730
8336
  endsWords = new Uint32Array(back, endsRequest.offset, endsCount).slice(0, levels + 1);
7731
8337
  } else if (recorded > n + 2) {
7732
- throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: the done flag never rose in ${recorded} levels`, {
7733
- label: ALGORITHM$1,
8338
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$4}: the done flag never rose in ${recorded} levels`, {
8339
+ label: ALGORITHM$4,
7734
8340
  message: `the done flag never rose in ${recorded} levels`
7735
8341
  });
7736
8342
  }
@@ -7743,7 +8349,7 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
7743
8349
  for (let i = 0; ; i += BC_BACKWARD_LEVELS_PER_SUBMIT) {
7744
8350
  const chunk = backwardLevels.slice(i, i + BC_BACKWARD_LEVELS_PER_SUBMIT);
7745
8351
  const last = i + BC_BACKWARD_LEVELS_PER_SUBMIT >= backwardLevels.length;
7746
- const batch = new CommandBatch(ctx, `${ALGORITHM$1}/backward`);
8352
+ const batch = new CommandBatch(ctx, `${ALGORITHM$4}/backward`);
7747
8353
  const pass = batch.pass("backward");
7748
8354
  for (const level of chunk) {
7749
8355
  const start = endsWords[level];
@@ -7807,11 +8413,11 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
7807
8413
  const limits = tuning.limits ?? ctx.caps.limits;
7808
8414
  const kMax = planBatchSize(n, sources.length, limits);
7809
8415
  const core = ctx.residency.core(s);
7810
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$1);
8416
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$4);
7811
8417
  const { edgeCount } = s;
7812
8418
  const mayRunEdge = pinned === "edge" || pinned === "auto" && sources.length > kMax;
7813
8419
  const edgeView = mayRunEdge && edgeCount > 0 ? ctx.residency.view(s, "edgeList") : null;
7814
- const scope = algorithmScope(ctx, ALGORITHM$1, RING_SLOTS$1);
8420
+ const scope = algorithmScope(ctx, ALGORITHM$4, RING_SLOTS$3);
7815
8421
  try {
7816
8422
  const arrayBytes = 4 * n * kMax;
7817
8423
  const lease = (bytes, label) => bindingOf(scope.scratch(bytes, label), bytes);
@@ -7851,7 +8457,7 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
7851
8457
  bound: /* @__PURE__ */ new Map()
7852
8458
  };
7853
8459
  await ctx.allocator.check();
7854
- const setup = new CommandBatch(ctx, `${ALGORITHM$1}/setup`);
8460
+ const setup = new CommandBatch(ctx, `${ALGORITHM$4}/setup`);
7855
8461
  const setupPass = setup.pass("setup");
7856
8462
  recordFill(state, setupPass, state.bc, n, 0);
7857
8463
  if (state.arcScores !== null) {
@@ -7879,7 +8485,7 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
7879
8485
  batches += 1;
7880
8486
  options?.onProgress?.(start, sources.length);
7881
8487
  }
7882
- const result = new CommandBatch(ctx, `${ALGORITHM$1}/result`);
8488
+ const result = new CommandBatch(ctx, `${ALGORITHM$4}/result`);
7883
8489
  const vertexRequest = result.readback(state.bc.buffer, state.bc.offset, 4 * n);
7884
8490
  const arcRequest = state.arcScores === null ? null : result.readback(state.arcScores.buffer, state.arcScores.offset, 4 * s.arcCount);
7885
8491
  const back = await submit(state, result, options?.signal);
@@ -7903,7 +8509,7 @@ async function precheck(ctx, options) {
7903
8509
  ctx.assertReady();
7904
8510
  await assertDeviceComputes(ctx);
7905
8511
  if (options?.endpoints === true) {
7906
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$1}: endpoints: true is not supported`, {
8512
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: endpoints: true is not supported`, {
7907
8513
  feature: "betweenness.endpoints",
7908
8514
  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"
7909
8515
  });
@@ -7912,10 +8518,10 @@ async function precheck(ctx, options) {
7912
8518
  async function betweennessWithTuning(ctx, s, options, tuning) {
7913
8519
  await precheck(ctx, options);
7914
8520
  const n = s.nodeCount;
7915
- const scores = checkDest(ALGORITHM$1, options?.dest, n) ?? new Float32Array(n);
8521
+ const scores = checkDest$2(ALGORITHM$4, options?.dest, n) ?? new Float32Array(n);
7916
8522
  const sources = resolveSources(options, n);
7917
8523
  if (options?.signal?.aborted) {
7918
- throw aborted(ALGORITHM$1);
8524
+ throw aborted(ALGORITHM$4);
7919
8525
  }
7920
8526
  if (n === 0 || sources.length === 0) {
7921
8527
  scores.fill(0);
@@ -7938,10 +8544,10 @@ async function betweennessWithTuning(ctx, s, options, tuning) {
7938
8544
  async function edgeBetweennessWithTuning(ctx, s, options, tuning, onArcs) {
7939
8545
  await precheck(ctx, options);
7940
8546
  const n = s.nodeCount;
7941
- const scores = checkDest("edgeBetweennessCentrality", options?.dest, s.edgeCount) ?? new Float32Array(s.edgeCount);
8547
+ const scores = checkDest$2("edgeBetweennessCentrality", options?.dest, s.edgeCount) ?? new Float32Array(s.edgeCount);
7942
8548
  const sources = resolveSources(options, n);
7943
8549
  if (options?.signal?.aborted) {
7944
- throw aborted(ALGORITHM$1);
8550
+ throw aborted(ALGORITHM$4);
7945
8551
  }
7946
8552
  if (n === 0 || sources.length === 0 || s.arcCount === 0) {
7947
8553
  scores.fill(0);
@@ -7962,10 +8568,10 @@ function betweennessCentrality(ctx, s, options) {
7962
8568
  function edgeBetweennessCentrality(ctx, s, options) {
7963
8569
  return edgeBetweennessWithTuning(ctx, s, options, {});
7964
8570
  }
7965
- const ALGORITHM = "closenessCentrality";
8571
+ const ALGORITHM$3 = "closenessCentrality";
7966
8572
  const SOURCES_PER_BATCH = 32;
7967
8573
  const PER_SOURCE_WORDS = 4 * SOURCES_PER_BATCH;
7968
- const RING_SLOTS = 8 * MAX_LEVELS_PER_SUBMIT + 16;
8574
+ const RING_SLOTS$2 = 8 * MAX_LEVELS_PER_SUBMIT + 16;
7969
8575
  function reusingScratch(scope) {
7970
8576
  const held = /* @__PURE__ */ new Map();
7971
8577
  return {
@@ -7987,7 +8593,7 @@ async function weightedRoute(ctx, s, scores, sources, options) {
7987
8593
  const totals = sources === null ? null : new Float64Array(n);
7988
8594
  for (let i = 0; i < count; i++) {
7989
8595
  if (options?.signal?.aborted) {
7990
- throw aborted(ALGORITHM);
8596
+ throw aborted(ALGORITHM$3);
7991
8597
  }
7992
8598
  const source = sources === null ? i : sources[i];
7993
8599
  const { dist } = await sssp(ctx, s, source, { signal: options?.signal });
@@ -8021,8 +8627,8 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8021
8627
  return { scores, iterations: 0, converged: true, precision: "f32", sourcesUsed: 0 };
8022
8628
  }
8023
8629
  const core = ctx.residency.core(s);
8024
- assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM);
8025
- const scope = algorithmScope(ctx, ALGORITHM, RING_SLOTS);
8630
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$3);
8631
+ const scope = algorithmScope(ctx, ALGORITHM$3, RING_SLOTS$2);
8026
8632
  try {
8027
8633
  const wg = ctx.workgroupSize;
8028
8634
  const bytes = 4 * n;
@@ -8071,7 +8677,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8071
8677
  scope.flush();
8072
8678
  return batch.submit();
8073
8679
  };
8074
- const setup = new CommandBatch(ctx, `${ALGORITHM}/setup`);
8680
+ const setup = new CommandBatch(ctx, `${ALGORITHM$3}/setup`);
8075
8681
  recordFill2(setup.pass("fill"), iota, n, 1);
8076
8682
  setup.endPass();
8077
8683
  await submit2(setup).readback;
@@ -8080,7 +8686,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8080
8686
  for (let batchStart = 0; batchStart < seedCount; batchStart += SOURCES_PER_BATCH) {
8081
8687
  let level = 0;
8082
8688
  for (let first = true; ; first = false) {
8083
- const batch = new CommandBatch(ctx, `${ALGORITHM}/levels`);
8689
+ const batch = new CommandBatch(ctx, `${ALGORITHM$3}/levels`);
8084
8690
  const pass = batch.pass("closeness");
8085
8691
  if (first) {
8086
8692
  recordFill2(pass, bits, 4 * bitsBase, 0);
@@ -8139,7 +8745,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8139
8745
  const back = await submitted.readback;
8140
8746
  ctx.assertReady();
8141
8747
  if (options?.signal?.aborted) {
8142
- throw aborted(ALGORITHM, submitted.id);
8748
+ throw aborted(ALGORITHM$3, submitted.id);
8143
8749
  }
8144
8750
  if (new Uint32Array(back, doneRequest.offset, 1)[0] !== 0) {
8145
8751
  const block = new Uint32Array(back, blockRequest.offset, PER_SOURCE_WORDS);
@@ -8161,8 +8767,8 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
8161
8767
  if (level > n + 3) {
8162
8768
  throw new WebGpuGraphError(
8163
8769
  "E_VALIDATION",
8164
- `${ALGORITHM}: the done flag never rose in ${level} levels of the batch at ${batchStart}`,
8165
- { label: ALGORITHM, message: `the done flag never rose in ${level} levels` }
8770
+ `${ALGORITHM$3}: the done flag never rose in ${level} levels of the batch at ${batchStart}`,
8771
+ { label: ALGORITHM$3, message: `the done flag never rose in ${level} levels` }
8166
8772
  );
8167
8773
  }
8168
8774
  }
@@ -8182,14 +8788,14 @@ function checkSources(s, sources) {
8182
8788
  return null;
8183
8789
  }
8184
8790
  if (s.directed) {
8185
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM}: sampled sources need an undirected snapshot`, {
8791
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: sampled sources need an undirected snapshot`, {
8186
8792
  feature: "closenessCentrality.directedSources",
8187
8793
  hint: "run the CPU port, which searches the in-arcs"
8188
8794
  });
8189
8795
  }
8190
8796
  for (const v of sources) {
8191
8797
  if (!Number.isInteger(v) || v < 0 || v >= s.nodeCount) {
8192
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM}: a source is not a node index`, {
8798
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$3}: a source is not a node index`, {
8193
8799
  argument: "sources",
8194
8800
  value: v,
8195
8801
  expected: `an integer in [0, ${s.nodeCount})`
@@ -8203,7 +8809,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8203
8809
  await assertDeviceComputes(ctx);
8204
8810
  for (const key of ["maxIterations", "tolerance"]) {
8205
8811
  if (options?.[key] !== void 0) {
8206
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM}: ${key} has no meaning for an exact traversal`, {
8812
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: ${key} has no meaning for an exact traversal`, {
8207
8813
  option: key,
8208
8814
  hint: "closeness is an exact traversal; the option has no meaning here"
8209
8815
  });
@@ -8215,7 +8821,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8215
8821
  if (!Number.isInteger(levelsPerSubmit) || levelsPerSubmit < 1 || levelsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
8216
8822
  throw new WebGpuGraphError(
8217
8823
  "E_INVALID_ARGUMENT",
8218
- `${ALGORITHM}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8824
+ `${ALGORITHM$3}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
8219
8825
  {
8220
8826
  argument: "levelsPerSubmit",
8221
8827
  value: levelsPerSubmit,
@@ -8223,16 +8829,16 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8223
8829
  }
8224
8830
  );
8225
8831
  }
8226
- const scores = checkDest(ALGORITHM, options?.dest, n) ?? new Float32Array(n);
8832
+ const scores = checkDest$2(ALGORITHM$3, options?.dest, n) ?? new Float32Array(n);
8227
8833
  const weighted = options?.weighted ?? s.flags.weighted;
8228
8834
  if (options?.signal?.aborted) {
8229
- throw aborted(ALGORITHM);
8835
+ throw aborted(ALGORITHM$3);
8230
8836
  }
8231
8837
  if (weighted && s.weights !== null && !s.flags.allWeightsOne) {
8232
8838
  if (!s.flags.nonNegativeWeights) {
8233
8839
  throw new WebGpuGraphError(
8234
8840
  "E_UNSUPPORTED",
8235
- `${ALGORITHM}: a negative weight has no shortest-path distance to sum`,
8841
+ `${ALGORITHM$3}: a negative weight has no shortest-path distance to sum`,
8236
8842
  {
8237
8843
  feature: "closenessCentrality.negativeWeights",
8238
8844
  hint: "pass weighted: false to ignore the column"
@@ -8240,7 +8846,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8240
8846
  );
8241
8847
  }
8242
8848
  if (!s.flags.finiteWeights) {
8243
- throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM}: a NaN or infinite weight has no shortest path`, {
8849
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a NaN or infinite weight has no shortest path`, {
8244
8850
  feature: "closenessCentrality.nonFiniteWeights"
8245
8851
  });
8246
8852
  }
@@ -8251,32 +8857,269 @@ async function closenessWithTuning(ctx, s, options, tuning) {
8251
8857
  function closenessCentrality(ctx, s, options) {
8252
8858
  return closenessWithTuning(ctx, s, options, {});
8253
8859
  }
8254
- const U32_MAX = 4294967295;
8255
- async function prepareHistogram(scope) {
8256
- return prepareHistogramImpl(scope);
8257
- }
8258
- async function prepareHistogramImpl(scope) {
8259
- const histogram = await scope.pipelines.kernel(kernelSpec("histogram"));
8260
- const fill = await scope.pipelines.kernel(kernelSpec("fill"));
8261
- return new HistogramPlannerImpl(scope, histogram, fill);
8262
- }
8263
- async function prepareCountingSort(scope) {
8264
- const histogram = await prepareHistogramImpl(scope);
8265
- const scan = await prepareScan(scope);
8266
- const scatter = await scope.pipelines.kernel(kernelSpec("counting-scatter"));
8267
- return new CountingSortPlannerImpl(scope, histogram, scan, scatter);
8268
- }
8269
- function checkWords(name, binding, words) {
8270
- if (binding.size < 4 * words) {
8271
- throw new WebGpuGraphError("E_INVALID_ARGUMENT", `histogram: ${name} is smaller than 4 x ${words} bytes`, {
8272
- argument: name,
8273
- value: binding.size,
8274
- expected: 4 * words
8275
- });
8860
+ const ALGORITHM$2 = "allPairsShortestPath";
8861
+ const DEFAULT_ROUNDS_PER_SUBMIT = Math.floor(APSP_MAX_DISPATCHES_PER_SUBMIT / 3);
8862
+ function allPairsCeiling(limits) {
8863
+ const binding = limits.maxStorageBufferBindingSize;
8864
+ const bufferSize = limits.maxBufferSize;
8865
+ const limitName = bufferSize < binding ? "maxBufferSize" : "maxStorageBufferBindingSize";
8866
+ const limit = Math.min(binding, bufferSize);
8867
+ let maxNodes = Math.floor(Math.sqrt(limit / 4));
8868
+ while (4 * maxNodes * maxNodes > limit) {
8869
+ maxNodes -= 1;
8870
+ }
8871
+ while (4 * (maxNodes + 1) * (maxNodes + 1) <= limit) {
8872
+ maxNodes += 1;
8276
8873
  }
8874
+ return { maxNodes, limit, limitName };
8277
8875
  }
8278
- function checkHistogramArguments(keys, count, bins, hist) {
8279
- if (!Number.isSafeInteger(count) || count < 0 || count > U32_MAX) {
8876
+ async function allPairsWithTuning(ctx, s, options, tuning) {
8877
+ ctx.assertReady();
8878
+ const n = s.nodeCount;
8879
+ const roundsPerSubmit = tuning.roundsPerSubmit ?? DEFAULT_ROUNDS_PER_SUBMIT;
8880
+ if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > DEFAULT_ROUNDS_PER_SUBMIT) {
8881
+ throw new WebGpuGraphError(
8882
+ "E_INVALID_ARGUMENT",
8883
+ `${ALGORITHM$2}: roundsPerSubmit must be an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`,
8884
+ {
8885
+ argument: "roundsPerSubmit",
8886
+ value: roundsPerSubmit,
8887
+ expected: `an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`
8888
+ }
8889
+ );
8890
+ }
8891
+ const { maxNodes, limit, limitName } = allPairsCeiling(ctx.caps.limits);
8892
+ if (n > maxNodes) {
8893
+ throw new WebGpuGraphError(
8894
+ "E_TOO_LARGE",
8895
+ `${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`,
8896
+ {
8897
+ needed: 4 * n * n,
8898
+ limit,
8899
+ path: "allPairs.matrix",
8900
+ algorithm: ALGORITHM$2,
8901
+ nodes: n,
8902
+ maxNodes,
8903
+ limitName,
8904
+ hint: `raise ${limitName} through GpuContextOptions.limits`
8905
+ }
8906
+ );
8907
+ }
8908
+ const dist = checkDest$2(ALGORITHM$2, options?.dest, n * n) ?? new Float32Array(n * n);
8909
+ const weighted = (options?.weighted ?? s.weights !== null) && s.weights !== null && !s.flags.allWeightsOne;
8910
+ if (weighted && !s.flags.nonNegativeWeights) {
8911
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$2}: a negative weight is not supported`, {
8912
+ feature: "allPairs.negativeWeights",
8913
+ hint: "the blocked Floyd-Warshall sweep needs non-negative weights; pass weighted: false for hop counts"
8914
+ });
8915
+ }
8916
+ if (weighted && !s.flags.finiteWeights) {
8917
+ throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$2}: a NaN or infinite weight has no shortest path`, {
8918
+ feature: "allPairs.nonFiniteWeights"
8919
+ });
8920
+ }
8921
+ if (options?.signal?.aborted) {
8922
+ throw aborted(ALGORITHM$2);
8923
+ }
8924
+ if (n === 0) {
8925
+ return { dist, n };
8926
+ }
8927
+ await assertDeviceComputes(ctx);
8928
+ const core = ctx.residency.core(s);
8929
+ assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$2);
8930
+ const blocks = Math.ceil(n / APSP_TILE);
8931
+ const scope = algorithmScope(ctx, ALGORITHM$2, Math.min(roundsPerSubmit, blocks) + 2);
8932
+ try {
8933
+ const wg = ctx.workgroupSize;
8934
+ const bytes = 4 * n * n;
8935
+ const matrix = bindingOf(scope.scratch(bytes, "dist"), bytes);
8936
+ await ctx.allocator.check();
8937
+ const weightsBinding = weighted ? void 0 : null;
8938
+ const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
8939
+ const init = await ctx.pipelines.kernel(kernelSpec("apsp-init", graphOverrides(core, null, weightsBinding)));
8940
+ const phases = await Promise.all(
8941
+ [0, 1, 2].map((phase) => ctx.pipelines.kernel(kernelSpec("apsp-fw", { PHASE: phase })))
8942
+ );
8943
+ const graph = graphBindings(core, null, weightsBinding);
8944
+ const others = blocks - 1;
8945
+ const phasePlans = [plan2d(1, ctx.caps), plan2d(2 * others, ctx.caps), plan2d(others * others, ctx.caps)];
8946
+ for (let first = 0; first < blocks; first += roundsPerSubmit) {
8947
+ const batch = new CommandBatch(ctx, `${ALGORITHM$2}/sweep`);
8948
+ const pass = batch.pass("apsp");
8949
+ if (first === 0) {
8950
+ const fillParams = scope.params(FILL_PARAMS, { count: n * n, value: F32_INF_BITS, mode: 0, pad0: 0 });
8951
+ fill.dispatch(pass, fill.bind({ dst: matrix, P: fillParams.binding }), plan1d(n * n, wg, ctx.caps), [
8952
+ fillParams.offset
8953
+ ]);
8954
+ const initParams = scope.params(APSP_PARAMS, { n, round: 0, blocks, infBits: F32_INF_BITS });
8955
+ init.dispatch(
8956
+ pass,
8957
+ init.bind({ ...graph, dist: matrix, P: initParams.binding }),
8958
+ plan1d(n, wg, ctx.caps),
8959
+ [initParams.offset]
8960
+ );
8961
+ }
8962
+ const last = Math.min(first + roundsPerSubmit, blocks);
8963
+ for (let round = first; round < last; round++) {
8964
+ const params = scope.params(APSP_PARAMS, { n, round, blocks, infBits: F32_INF_BITS });
8965
+ for (let phase = 0; phase < 3; phase++) {
8966
+ const kernel = phases[phase];
8967
+ kernel.dispatch(pass, kernel.bind({ dist: matrix, P: params.binding }), phasePlans[phase], [
8968
+ params.offset
8969
+ ]);
8970
+ }
8971
+ }
8972
+ batch.endPass();
8973
+ scope.flush();
8974
+ const submitted = batch.submit();
8975
+ await submitted.readback;
8976
+ ctx.assertReady();
8977
+ options?.onProgress?.(last, blocks);
8978
+ if (options?.signal?.aborted) {
8979
+ throw aborted(ALGORITHM$2, submitted.id);
8980
+ }
8981
+ }
8982
+ await ctx.readback.read(matrix.buffer, bytes, dist);
8983
+ ctx.assertReady();
8984
+ return { dist, n };
8985
+ } finally {
8986
+ scope.dispose();
8987
+ }
8988
+ }
8989
+ function allPairsShortestPath(ctx, s, options) {
8990
+ return allPairsWithTuning(ctx, s, options, {});
8991
+ }
8992
+ function planGroupRows(lengthBound, threadMax = GROUP_ROW_THREAD_MAX) {
8993
+ if (!Number.isInteger(threadMax) || threadMax < 0 || threadMax > GROUP_ROW_THREAD_LIMIT) {
8994
+ throw new WebGpuGraphError(
8995
+ "E_INVALID_ARGUMENT",
8996
+ `planGroupRows: threadMax must be an integer in [0, ${GROUP_ROW_THREAD_LIMIT}]`,
8997
+ { argument: "threadMax", value: threadMax, expected: `[0, ${GROUP_ROW_THREAD_LIMIT}]` }
8998
+ );
8999
+ }
9000
+ const n = lengthBound.length;
9001
+ const thread = [];
9002
+ const hash = [];
9003
+ for (let v = 0; v < n; v++) {
9004
+ (lengthBound[v] <= threadMax ? thread : hash).push(v);
9005
+ }
9006
+ const words = new Uint32Array(thread.length + 2 * hash.length);
9007
+ words.set(thread, 0);
9008
+ words.set(hash, thread.length);
9009
+ let next = 1;
9010
+ for (let g = 0; g < hash.length; g++) {
9011
+ words[thread.length + hash.length + g] = next;
9012
+ next += 2 * GROUP_HASH_LOAD_FACTOR * lengthBound[hash[g]];
9013
+ }
9014
+ return { words, threadCount: thread.length, hashCount: hash.length, regionWords: next };
9015
+ }
9016
+ async function prepareGroupByKeyRow(scope) {
9017
+ const kernels = /* @__PURE__ */ new Map();
9018
+ for (const tier of [0, 1]) {
9019
+ for (const weighted of [false, true]) {
9020
+ const spec = kernelSpec("group-by-key-row", { TIER: tier, WEIGHTED: weighted });
9021
+ kernels.set(`${tier}/${weighted}`, await scope.pipelines.kernel(spec));
9022
+ }
9023
+ }
9024
+ return new GroupByKeyPlannerImpl(scope, kernels);
9025
+ }
9026
+ function checkWords$2(name, binding, words) {
9027
+ if (binding.size < 4 * words) {
9028
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `groupByKeyRow: ${name} is smaller than 4 x ${words} bytes`, {
9029
+ argument: name,
9030
+ value: binding.size,
9031
+ expected: 4 * words
9032
+ });
9033
+ }
9034
+ }
9035
+ class GroupByKeyPlannerImpl {
9036
+ /**
9037
+ * Wraps the compiled variants; use prepareGroupByKeyRow().
9038
+ * @param scope - the caller's scope
9039
+ * @param kernels - the variants by `tier/weighted`
9040
+ */
9041
+ constructor(scope, kernels) {
9042
+ this.scope = scope;
9043
+ this.kernels = kernels;
9044
+ }
9045
+ /**
9046
+ * Records the tier dispatches (see the interface).
9047
+ * @param pass - the compute pass
9048
+ * @param r - the record
9049
+ */
9050
+ record(pass, r) {
9051
+ const { threadCount, hashCount } = r.plan;
9052
+ const rows = threadCount + hashCount;
9053
+ checkWords$2("rows", r.rows, rows + hashCount);
9054
+ checkWords$2("hashRegion", r.hashRegion, r.plan.regionWords);
9055
+ checkWords$2("rowPtr", r.rowPtr, rows + 1);
9056
+ if (rows === 0) {
9057
+ return;
9058
+ }
9059
+ const weighted = r.weights !== null;
9060
+ const tiers = [
9061
+ [0, 0, threadCount],
9062
+ [1, threadCount, hashCount]
9063
+ ];
9064
+ for (const [tier, rowsBase, count] of tiers) {
9065
+ if (count === 0) {
9066
+ continue;
9067
+ }
9068
+ const kernel = this.kernels.get(`${tier}/${weighted}`);
9069
+ if (kernel === void 0) {
9070
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", "groupByKeyRow: no variant was prepared", {
9071
+ argument: "tier",
9072
+ value: tier
9073
+ });
9074
+ }
9075
+ const params = this.scope.params(GROUP_PARAMS, {
9076
+ rowsBase,
9077
+ basesBase: threadCount + hashCount,
9078
+ count,
9079
+ pad0: 0
9080
+ });
9081
+ const bound = kernel.bind({
9082
+ rowPtr: r.rowPtr,
9083
+ colIdx: r.colIdx,
9084
+ weights: r.weights ?? r.colIdx,
9085
+ keyIn: r.keyIn,
9086
+ rows: r.rows,
9087
+ hashRegion: r.hashRegion,
9088
+ bestKey: r.bestKey,
9089
+ bestScore: r.bestScore,
9090
+ P: params.binding
9091
+ });
9092
+ const plan = tier === 0 ? plan1d(count, this.scope.workgroupSize, this.scope.caps) : plan2d(count, this.scope.caps);
9093
+ kernel.dispatch(pass, bound, plan, [params.offset]);
9094
+ }
9095
+ }
9096
+ }
9097
+ const U32_MAX = 4294967295;
9098
+ async function prepareHistogram(scope) {
9099
+ return prepareHistogramImpl(scope);
9100
+ }
9101
+ async function prepareHistogramImpl(scope) {
9102
+ const histogram = await scope.pipelines.kernel(kernelSpec("histogram"));
9103
+ const fill = await scope.pipelines.kernel(kernelSpec("fill"));
9104
+ return new HistogramPlannerImpl(scope, histogram, fill);
9105
+ }
9106
+ async function prepareCountingSort(scope) {
9107
+ const histogram = await prepareHistogramImpl(scope);
9108
+ const scan = await prepareScan(scope);
9109
+ const scatter = await scope.pipelines.kernel(kernelSpec("counting-scatter"));
9110
+ return new CountingSortPlannerImpl(scope, histogram, scan, scatter);
9111
+ }
9112
+ function checkWords$1(name, binding, words) {
9113
+ if (binding.size < 4 * words) {
9114
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `histogram: ${name} is smaller than 4 x ${words} bytes`, {
9115
+ argument: name,
9116
+ value: binding.size,
9117
+ expected: 4 * words
9118
+ });
9119
+ }
9120
+ }
9121
+ function checkHistogramArguments(keys, count, bins, hist) {
9122
+ if (!Number.isSafeInteger(count) || count < 0 || count > U32_MAX) {
8280
9123
  throw new WebGpuGraphError("E_INVALID_ARGUMENT", "histogram: count must be a non-negative integer below 2^32", {
8281
9124
  argument: "count",
8282
9125
  value: count
@@ -8288,8 +9131,8 @@ function checkHistogramArguments(keys, count, bins, hist) {
8288
9131
  value: bins
8289
9132
  });
8290
9133
  }
8291
- checkWords("keys", keys, count);
8292
- checkWords("hist", hist, bins);
9134
+ checkWords$1("keys", keys, count);
9135
+ checkWords$1("hist", hist, bins);
8293
9136
  }
8294
9137
  class HistogramPlannerImpl {
8295
9138
  /**
@@ -8377,9 +9220,9 @@ class CountingSortPlannerImpl {
8377
9220
  */
8378
9221
  record(pass, keys, count, bins, scratch, outIndex, outStart) {
8379
9222
  checkHistogramArguments(keys, count, bins, scratch.hist);
8380
- checkWords("cursor", scratch.cursor, bins);
8381
- checkWords("outIndex", outIndex, count);
8382
- checkWords("outStart", outStart, bins);
9223
+ checkWords$1("cursor", scratch.cursor, bins);
9224
+ checkWords$1("outIndex", outIndex, count);
9225
+ checkWords$1("outStart", outStart, bins);
8383
9226
  this.histogram.record(pass, keys, count, bins, scratch.hist);
8384
9227
  this.scan.record(pass, scratch.hist, bins, outStart);
8385
9228
  this.histogram.recordZero(pass, scratch.cursor, bins);
@@ -8393,6 +9236,751 @@ class CountingSortPlannerImpl {
8393
9236
  this.dispatches += 1;
8394
9237
  }
8395
9238
  }
9239
+ async function prepareCooToCsr(scope) {
9240
+ const histogram = await prepareHistogram(scope);
9241
+ const scan = await prepareScan(scope);
9242
+ const fill = await scope.pipelines.kernel(kernelSpec("fill"));
9243
+ const scatter = /* @__PURE__ */ new Map();
9244
+ for (const sorted of [false, true]) {
9245
+ for (const weighted of [false, true]) {
9246
+ const spec = kernelSpec("coo-scatter", { SORTED_INPUT: sorted, WEIGHTED: weighted });
9247
+ scatter.set(`${sorted}/${weighted}`, await scope.pipelines.kernel(spec));
9248
+ }
9249
+ }
9250
+ return new CooToCsrPlannerImpl(scope, histogram, scan, fill, scatter);
9251
+ }
9252
+ function checkWords(name, binding, words) {
9253
+ if (binding.size < 4 * words) {
9254
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `cooToCsr: ${name} is smaller than 4 x ${words} bytes`, {
9255
+ argument: name,
9256
+ value: binding.size,
9257
+ expected: 4 * words
9258
+ });
9259
+ }
9260
+ }
9261
+ function checkRecord(r) {
9262
+ for (const [name, value] of [
9263
+ ["count", r.count],
9264
+ ["n", r.n]
9265
+ ]) {
9266
+ if (!Number.isSafeInteger(value) || value < 0 || value >= U32_MAX$2) {
9267
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `cooToCsr: ${name} must be an integer in [0, 2^32 - 1)`, {
9268
+ argument: name,
9269
+ value
9270
+ });
9271
+ }
9272
+ }
9273
+ if (r.weights === null !== (r.out.weights === null)) {
9274
+ throw new WebGpuGraphError(
9275
+ "E_INVALID_ARGUMENT",
9276
+ "cooToCsr: weights and out.weights must be both present or both null",
9277
+ {
9278
+ argument: "weights",
9279
+ value: r.weights === null ? "null" : "present",
9280
+ expected: r.out.weights === null ? "null" : "present"
9281
+ }
9282
+ );
9283
+ }
9284
+ checkWords("src", r.src, r.count);
9285
+ checkWords("dst", r.dst, r.count);
9286
+ checkWords("out.rowPtr", r.out.rowPtr, r.n + 1);
9287
+ checkWords("out.colIdx", r.out.colIdx, r.count);
9288
+ checkWords("out.flag", r.out.flag, 1);
9289
+ if (r.weights !== null && r.out.weights !== null) {
9290
+ checkWords("weights", r.weights, r.count);
9291
+ checkWords("out.weights", r.out.weights, r.count);
9292
+ }
9293
+ }
9294
+ class CooToCsrPlannerImpl {
9295
+ /**
9296
+ * Wraps the resolved planners and kernels; use prepareCooToCsr().
9297
+ * @param scope - the caller's scope
9298
+ * @param histogram - the histogram planner
9299
+ * @param scan - the scan planner
9300
+ * @param fill - the `fill` kernel
9301
+ * @param scatter - the `coo-scatter` variants by `sorted/weighted`
9302
+ */
9303
+ constructor(scope, histogram, scan, fill, scatter) {
9304
+ this.scope = scope;
9305
+ this.histogram = histogram;
9306
+ this.scan = scan;
9307
+ this.fill = fill;
9308
+ this.scatter = scatter;
9309
+ }
9310
+ /**
9311
+ * Records one build (see the interface).
9312
+ * @param pass - the compute pass
9313
+ * @param r - the arcs and the output
9314
+ */
9315
+ record(pass, r) {
9316
+ checkRecord(r);
9317
+ const { scope } = this;
9318
+ const wg = scope.workgroupSize;
9319
+ const degreeBytes = 4 * (r.n + 1);
9320
+ const degrees = {
9321
+ buffer: scope.scratch(degreeBytes, "cooToCsr/degrees"),
9322
+ offset: 0,
9323
+ size: degreeBytes,
9324
+ window: null
9325
+ };
9326
+ this.histogram.record(pass, r.src, r.count, r.n + 1, degrees);
9327
+ this.scan.record(pass, degrees, r.n + 1, r.out.rowPtr);
9328
+ let cursors = r.out.flag;
9329
+ if (r.sortedInput) {
9330
+ this.zero(pass, r.out.flag, 1);
9331
+ } else if (r.n > 0) {
9332
+ const bytes = 4 * r.n;
9333
+ cursors = { buffer: scope.scratch(bytes, "cooToCsr/cursors"), offset: 0, size: bytes, window: null };
9334
+ this.zero(pass, cursors, r.n);
9335
+ }
9336
+ if (r.count === 0) {
9337
+ return;
9338
+ }
9339
+ const weighted = r.weights !== null;
9340
+ const kernel = this.scatter.get(`${r.sortedInput}/${weighted}`);
9341
+ if (kernel === void 0) {
9342
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", "cooToCsr: no scatter variant was prepared", {
9343
+ argument: "sortedInput",
9344
+ value: r.sortedInput
9345
+ });
9346
+ }
9347
+ const outWeight = r.out.weights ?? {
9348
+ buffer: scope.scratch(4, "cooToCsr/weight-dummy"),
9349
+ offset: 0,
9350
+ size: 4,
9351
+ window: null
9352
+ };
9353
+ const params = scope.params(COO_PARAMS, { count: r.count, pad0: 0, pad1: 0, pad2: 0 });
9354
+ const bound = kernel.bind({
9355
+ src: r.src,
9356
+ dst: r.dst,
9357
+ weight: r.weights ?? r.dst,
9358
+ rowPtr: r.out.rowPtr,
9359
+ cursors,
9360
+ colIdx: r.out.colIdx,
9361
+ outWeight,
9362
+ P: params.binding
9363
+ });
9364
+ kernel.dispatch(pass, bound, plan1d(r.count, wg, scope.caps), [params.offset]);
9365
+ }
9366
+ /**
9367
+ * Records a `fill` of `words` zeros.
9368
+ * @param pass - the compute pass
9369
+ * @param dst - the words
9370
+ * @param words - how many (>= 1)
9371
+ */
9372
+ zero(pass, dst, words) {
9373
+ const params = this.scope.params(FILL_PARAMS, { count: words, value: 0, mode: 0, pad0: 0 });
9374
+ const bound = this.fill.bind({ dst, P: params.binding });
9375
+ this.fill.dispatch(pass, bound, plan1d(words, this.scope.workgroupSize, this.scope.caps), [params.offset]);
9376
+ }
9377
+ }
9378
+ function sortBitsFor(n) {
9379
+ for (const bits of [8, 16, 24]) {
9380
+ if (n < 2 ** bits) {
9381
+ return bits;
9382
+ }
9383
+ }
9384
+ return 32;
9385
+ }
9386
+ function scratchWords(scope, words, label) {
9387
+ const size = 4 * Math.max(1, words);
9388
+ return { buffer: scope.scratch(size, label), offset: 0, size, window: null };
9389
+ }
9390
+ function assertBindable(ctx, needed, path, algorithm) {
9391
+ const limit = ctx.caps.limits.maxStorageBufferBindingSize;
9392
+ if (needed > limit) {
9393
+ throw new WebGpuGraphError(
9394
+ "E_TOO_LARGE",
9395
+ `${algorithm}: ${path} needs ${needed} bytes in one binding, above the device limit of ${limit}`,
9396
+ { needed, limit, path, algorithm }
9397
+ );
9398
+ }
9399
+ }
9400
+ function assertMergeable(n, src, dst, algorithm) {
9401
+ const incident = new Uint32Array(n);
9402
+ for (let e = 0; e < src.length; e++) {
9403
+ if (src[e] !== dst[e]) {
9404
+ incident[src[e]]++;
9405
+ incident[dst[e]]++;
9406
+ }
9407
+ }
9408
+ const heavy = /* @__PURE__ */ new Map();
9409
+ for (let v = 0; v < n; v++) {
9410
+ if (incident[v] > PARALLEL_MERGE_LIMIT) {
9411
+ heavy.set(v, heavy.size);
9412
+ }
9413
+ }
9414
+ if (heavy.size < 2) {
9415
+ return;
9416
+ }
9417
+ const pairs = new Uint32Array(heavy.size * heavy.size);
9418
+ for (let e = 0; e < src.length; e++) {
9419
+ const a = heavy.get(src[e]);
9420
+ const b = heavy.get(dst[e]);
9421
+ if (a === void 0 || b === void 0 || a === b) {
9422
+ continue;
9423
+ }
9424
+ const key = Math.min(a, b) * heavy.size + Math.max(a, b);
9425
+ if (++pairs[key] > PARALLEL_MERGE_LIMIT) {
9426
+ throw new WebGpuGraphError(
9427
+ "E_UNSUPPORTED",
9428
+ `${algorithm}: nodes ${src[e]} and ${dst[e]} are joined by more than ${PARALLEL_MERGE_LIMIT} parallel edges, more than one weighted merge sums`,
9429
+ { feature: `${algorithm}.parallelEdges`, hint: "run it unweighted, or merge the parallel edges first" }
9430
+ );
9431
+ }
9432
+ }
9433
+ }
9434
+ function assertBuildSorted(bytes, build, label) {
9435
+ if (new Uint32Array(bytes, build.flag.offset, 1)[0] !== 0) {
9436
+ throw new WebGpuGraphError("E_VALIDATION", `${label}: the simple graph's arcs reached cooToCsr out of order`, {
9437
+ label: `${label}/simple-graph`,
9438
+ message: "the sorted-input precondition of cooToCsr failed on the device"
9439
+ });
9440
+ }
9441
+ }
9442
+ async function buildSimpleSymmetric(ctx, s, scope, withWeights, label) {
9443
+ const n = s.nodeCount;
9444
+ const wg = ctx.workgroupSize;
9445
+ const cooToCsr = await prepareCooToCsr(scope);
9446
+ const flag = scratchWords(scope, 1, "simple/flag");
9447
+ const rowPtr = scratchWords(scope, n + 1, "simple/rowPtr");
9448
+ const { edgeCount } = s;
9449
+ const list = s.edgeList();
9450
+ let selfLoops = 0;
9451
+ for (let e = 0; e < edgeCount; e++) {
9452
+ if (list.src[e] === list.dst[e]) {
9453
+ selfLoops++;
9454
+ }
9455
+ }
9456
+ const valid = 2 * (edgeCount - selfLoops);
9457
+ const empty2 = () => {
9458
+ const batch = new CommandBatch(ctx, `${label}/simple-graph`);
9459
+ cooToCsr.record(batch.pass("simple-graph"), {
9460
+ src: flag,
9461
+ dst: flag,
9462
+ weights: null,
9463
+ count: 0,
9464
+ n,
9465
+ sortedInput: true,
9466
+ out: { rowPtr, colIdx: flag, weights: null, flag }
9467
+ });
9468
+ batch.endPass();
9469
+ const graph2 = { n, arcCount: 0, rowPtr, colIdx: null, weights: null, src: null };
9470
+ return { graph: graph2, batch, flag: batch.readback(flag.buffer, 0, 4) };
9471
+ };
9472
+ if (valid === 0) {
9473
+ return empty2();
9474
+ }
9475
+ const arcs = 2 * edgeCount;
9476
+ const tableBytes = Math.max(4, radixHistBytes(arcs, wg));
9477
+ assertBindable(ctx, Math.max(4 * (arcs + 1), tableBytes), "the simple graph's arc arrays", label);
9478
+ if (withWeights) {
9479
+ assertMergeable(n, list.src, list.dst, label);
9480
+ }
9481
+ ctx.residency.core(s, ["rowPtr"]);
9482
+ const edges = ctx.residency.view(s, "edgeList");
9483
+ const edgeWeights = edges.bindings.weights ?? null;
9484
+ const emitPlan = plan1d(arcs, wg, ctx.caps);
9485
+ const emit = /* @__PURE__ */ new Map();
9486
+ for (const indexed of [false, true]) {
9487
+ const spec = kernelSpec("coo-emit", { INDEXED: indexed, WEIGHTED: edgeWeights !== null });
9488
+ emit.set(indexed, await ctx.pipelines.kernel(spec));
9489
+ }
9490
+ const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
9491
+ const runFlags = await ctx.pipelines.kernel(kernelSpec("run-flags"));
9492
+ const sort = await prepareRadixSort(scope);
9493
+ const scan = await prepareScan(scope);
9494
+ const compact = await prepareCompact(scope);
9495
+ const aSrc = scratchWords(scope, arcs, "simple/aSrc");
9496
+ const aDst = scratchWords(scope, arcs, "simple/aDst");
9497
+ const aW = scratchWords(scope, arcs, "simple/aW");
9498
+ const vals = scratchWords(scope, arcs, "simple/vals");
9499
+ const sKeys = scratchWords(scope, arcs, "simple/sortKeys");
9500
+ const sVals = scratchWords(scope, arcs, "simple/sortVals");
9501
+ const hist = {
9502
+ buffer: scope.scratch(tableBytes, "simple/hist"),
9503
+ offset: 0,
9504
+ size: tableBytes,
9505
+ window: null
9506
+ };
9507
+ const offsets = {
9508
+ buffer: scope.scratch(tableBytes, "simple/offsets"),
9509
+ offset: 0,
9510
+ size: tableBytes,
9511
+ window: null
9512
+ };
9513
+ const sortedSrc = scratchWords(scope, arcs, "simple/sortedSrc");
9514
+ const sortedDst = scratchWords(scope, arcs, "simple/sortedDst");
9515
+ const sortedW = scratchWords(scope, arcs, "simple/sortedW");
9516
+ const flags = scratchWords(scope, arcs, "simple/flags");
9517
+ const runIndex = scratchWords(scope, arcs, "simple/runIndex");
9518
+ const dummy = scratchWords(scope, 1, "simple/dummy");
9519
+ await ctx.allocator.check();
9520
+ const recordEmit = (pass3, order, out) => {
9521
+ const kernel = emit.get(order !== null);
9522
+ if (kernel === void 0) {
9523
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", "buildSimpleSymmetric: no coo-emit variant", {
9524
+ argument: "order",
9525
+ value: order === null ? "null" : "present"
9526
+ });
9527
+ }
9528
+ const params2 = scope.params(COO_PARAMS, { count: arcs, pad0: 0, pad1: 0, pad2: 0 });
9529
+ const bound = kernel.bind({
9530
+ edgeSrc: edges.bindings.src,
9531
+ edgeDst: edges.bindings.dst,
9532
+ edgeWeight: edgeWeights ?? edges.bindings.src,
9533
+ order: order ?? dummy,
9534
+ outSrc: out[0],
9535
+ outDst: out[1],
9536
+ outWeight: out[2],
9537
+ P: params2.binding
9538
+ });
9539
+ kernel.dispatch(pass3, bound, emitPlan, [params2.offset]);
9540
+ };
9541
+ const recordIota = (pass3, dst, words) => {
9542
+ const params2 = scope.params(FILL_PARAMS, { count: words, value: 0, mode: 1, pad0: 0 });
9543
+ fill.dispatch(pass3, fill.bind({ dst, P: params2.binding }), plan1d(words, wg, ctx.caps), [params2.offset]);
9544
+ };
9545
+ const bits = sortBitsFor(n);
9546
+ const first = new CommandBatch(ctx, `${label}/simple-sort`);
9547
+ const pass = first.pass("simple-sort");
9548
+ recordEmit(pass, null, [aSrc, aDst, aW]);
9549
+ recordIota(pass, vals, arcs);
9550
+ const byTarget = sort.record(pass, aDst, vals, arcs, bits, { keys: sKeys, vals: sVals, hist, offsets });
9551
+ recordEmit(pass, byTarget.vals, [aSrc, aDst, aW]);
9552
+ const free = byTarget.vals.buffer === vals.buffer ? sVals : vals;
9553
+ const bySource = sort.record(pass, aSrc, byTarget.vals, arcs, bits, {
9554
+ keys: byTarget.keys.buffer === aDst.buffer ? sKeys : aDst,
9555
+ vals: free,
9556
+ hist,
9557
+ offsets
9558
+ });
9559
+ recordEmit(pass, bySource.vals, [sortedSrc, sortedDst, sortedW]);
9560
+ const params = scope.params(COO_PARAMS, { count: arcs, pad0: 0, pad1: 0, pad2: 0 });
9561
+ runFlags.dispatch(pass, runFlags.bind({ keysA: sortedSrc, keysB: sortedDst, flags, P: params.binding }), emitPlan, [
9562
+ params.offset
9563
+ ]);
9564
+ const total = scan.record(pass, flags, arcs, runIndex);
9565
+ first.endPass();
9566
+ const totalRequest = first.readback(total.binding.buffer, total.binding.offset + 4 * total.index, 4);
9567
+ scope.flush();
9568
+ const back = await first.submit().readback;
9569
+ ctx.assertReady();
9570
+ const unique = new Uint32Array(back, totalRequest.offset, 1)[0];
9571
+ if (unique === 0 || unique > valid) {
9572
+ throw new WebGpuGraphError("E_VALIDATION", `${label}: ${unique} distinct arcs from ${valid} valid ones`, {
9573
+ label: `${label}/simple-graph`,
9574
+ message: "the run count of the simple graph build is outside [1, valid arcs]"
9575
+ });
9576
+ }
9577
+ const uSrc = scratchWords(scope, arcs, "simple/src");
9578
+ const uDst = scratchWords(scope, arcs, "simple/dst");
9579
+ const runStart = scratchWords(scope, arcs + 1, "simple/runStart");
9580
+ const counts = scratchWords(scope, 1, "simple/count");
9581
+ const colIdx = scratchWords(scope, unique, "simple/colIdx");
9582
+ const weights = withWeights ? scratchWords(scope, unique, "simple/weights") : null;
9583
+ const merged = withWeights ? scratchWords(scope, unique, "simple/merged") : null;
9584
+ ctx.device.queue.writeBuffer(runStart.buffer, 4 * unique, Uint32Array.of(valid));
9585
+ const reduce = merged === null ? null : await prepareSegmentedReduce(scope, runCore(runStart, unique, sortedDst, sortedW, valid), {
9586
+ op: "sum",
9587
+ valueSnippet: "v = weight;",
9588
+ tiers: null
9589
+ });
9590
+ const second = new CommandBatch(ctx, `${label}/simple-graph`);
9591
+ const pass2 = second.pass("simple-graph");
9592
+ const positions = vals;
9593
+ recordIota(pass2, positions, arcs);
9594
+ for (const [queue, out] of [
9595
+ [sortedSrc, uSrc],
9596
+ [sortedDst, uDst],
9597
+ [positions, runStart]
9598
+ ]) {
9599
+ compact.record(pass2, { queue, flags, count: arcs, out, outCount: counts, outIndex: 0 });
9600
+ }
9601
+ if (reduce !== null && merged !== null) {
9602
+ reduce.record(pass2, runCore(runStart, unique, sortedDst, sortedW, valid), merged);
9603
+ }
9604
+ cooToCsr.record(pass2, {
9605
+ src: uSrc,
9606
+ dst: uDst,
9607
+ weights: merged,
9608
+ count: unique,
9609
+ n,
9610
+ sortedInput: true,
9611
+ out: { rowPtr, colIdx, weights, flag }
9612
+ });
9613
+ second.endPass();
9614
+ const graph = { n, arcCount: unique, rowPtr, colIdx, weights, src: uSrc };
9615
+ return { graph, batch: second, flag: second.readback(flag.buffer, 0, 4) };
9616
+ }
9617
+ function runCore(runStart, runs, sortedDst, sortedW, valid) {
9618
+ return {
9619
+ serial: -1,
9620
+ plan: "perArray",
9621
+ rowPtr: { ...runStart, size: 4 * (runs + 1) },
9622
+ colIdx: { ...sortedDst, size: 4 * valid },
9623
+ weights: { ...sortedW, size: 4 * valid },
9624
+ arcToEdge: null,
9625
+ edgeToArc: null,
9626
+ windows: null,
9627
+ arcBuffers: null,
9628
+ hasWeights: true
9629
+ };
9630
+ }
9631
+ const ALGORITHM$1 = "labelPropagation";
9632
+ const DEFAULT_MAX_ITERATIONS = 100;
9633
+ const RING_SLOTS$1 = 1024;
9634
+ function checkDest$1(dest, n) {
9635
+ if (dest === void 0) {
9636
+ return null;
9637
+ }
9638
+ if (dest instanceof Uint32Array && dest.length === n && dest.buffer instanceof ArrayBuffer) {
9639
+ return dest;
9640
+ }
9641
+ throw new WebGpuGraphError(
9642
+ "E_INVALID_ARGUMENT",
9643
+ `${ALGORITHM$1}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
9644
+ {
9645
+ argument: "dest",
9646
+ value: `${dest.constructor.name}(${dest.length})`,
9647
+ expected: `Uint32Array(${n}) over an ArrayBuffer`
9648
+ }
9649
+ );
9650
+ }
9651
+ function maxIterationsOf(value) {
9652
+ if (value === void 0) {
9653
+ return DEFAULT_MAX_ITERATIONS;
9654
+ }
9655
+ if (!Number.isSafeInteger(value) || value < 0) {
9656
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$1}: maxIterations must be a non-negative integer`, {
9657
+ argument: "maxIterations",
9658
+ value,
9659
+ expected: "a non-negative integer"
9660
+ });
9661
+ }
9662
+ return value;
9663
+ }
9664
+ function neighbourBound(s) {
9665
+ const out = s.outDegree();
9666
+ if (!s.directed) {
9667
+ return out;
9668
+ }
9669
+ const inDegree = s.inDegree();
9670
+ const bound = new Uint32Array(s.nodeCount);
9671
+ for (let v = 0; v < bound.length; v++) {
9672
+ bound[v] = out[v] + inDegree[v];
9673
+ }
9674
+ return bound;
9675
+ }
9676
+ function resultOf(raw, dest) {
9677
+ const n = raw.length;
9678
+ for (let v = 0; v < n; v++) {
9679
+ if (raw[v] >= n) {
9680
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: labels[${v}] = ${raw[v]} is not a node index`, {
9681
+ label: `${ALGORITHM$1}/labels`,
9682
+ message: `the device produced a label outside [0, ${n})`
9683
+ });
9684
+ }
9685
+ }
9686
+ const { labels, count } = renumberPartition(raw, dest ?? void 0);
9687
+ return labelResult(labels, count);
9688
+ }
9689
+ function identityResult(n, dest) {
9690
+ const labels = dest ?? new Uint32Array(n);
9691
+ for (let v = 0; v < n; v++) {
9692
+ labels[v] = v;
9693
+ }
9694
+ return labelResult(labels, n);
9695
+ }
9696
+ async function labelPropagation(ctx, s, options) {
9697
+ ctx.assertReady();
9698
+ await assertDeviceComputes(ctx);
9699
+ const n = s.nodeCount;
9700
+ const dest = checkDest$1(options?.dest, n);
9701
+ const maxIterations = maxIterationsOf(options?.maxIterations);
9702
+ const weighted = options?.weighted !== false;
9703
+ if (options?.signal?.aborted) {
9704
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted before any work started`, {});
9705
+ }
9706
+ if (n === 0 || s.edgeCount === 0 || maxIterations === 0) {
9707
+ options?.onProgress?.(1, 1);
9708
+ return identityResult(n, dest);
9709
+ }
9710
+ const plan = planGroupRows(neighbourBound(s));
9711
+ assertBindable(ctx, 4 * plan.regionWords, "the group-by hash region", ALGORITHM$1);
9712
+ const scope = algorithmScope(ctx, ALGORITHM$1, RING_SLOTS$1);
9713
+ try {
9714
+ const build = await buildSimpleSymmetric(ctx, s, scope, weighted, ALGORITHM$1);
9715
+ const { graph } = build;
9716
+ if (graph.colIdx === null) {
9717
+ scope.flush();
9718
+ const bytes = await build.batch.submit().readback;
9719
+ ctx.assertReady();
9720
+ assertBuildSorted(bytes, build, ALGORITHM$1);
9721
+ options?.onProgress?.(1, 1);
9722
+ return identityResult(n, dest);
9723
+ }
9724
+ const wg = ctx.workgroupSize;
9725
+ const words = (count, label) => {
9726
+ const size = 4 * Math.max(1, count);
9727
+ return { buffer: scope.scratch(size, label), offset: 0, size, window: null };
9728
+ };
9729
+ const rows = words(plan.words.length, "rows");
9730
+ const region = words(plan.regionWords, "hashRegion");
9731
+ const labelsA = words(n, "labelsA");
9732
+ const labelsB = words(n, "labelsB");
9733
+ const bestKey = words(n, "bestKey");
9734
+ const bestScore = words(n, "bestScore");
9735
+ const counters = words(LABEL_PROP_PASSES_PER_SUBMIT, "counters");
9736
+ await ctx.allocator.check();
9737
+ const { queue } = ctx.device;
9738
+ queue.writeBuffer(rows.buffer, 0, plan.words);
9739
+ queue.writeBuffer(region.buffer, 0, new Uint32Array(1));
9740
+ const groupBy = await prepareGroupByKeyRow(scope);
9741
+ const step = await ctx.pipelines.kernel(kernelSpec("lpa-step"));
9742
+ const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
9743
+ const nodePlan = plan1d(n, wg, ctx.caps);
9744
+ let { batch } = build;
9745
+ let first = true;
9746
+ let cur = labelsA;
9747
+ let next = labelsB;
9748
+ let done = 0;
9749
+ let previousLast = -1;
9750
+ for (; ; ) {
9751
+ const k = Math.min(LABEL_PROP_PASSES_PER_SUBMIT, maxIterations - done);
9752
+ queue.writeBuffer(counters.buffer, 0, new Uint32Array(k));
9753
+ const pass = batch.pass("passes");
9754
+ if (first) {
9755
+ const iota = scope.params(FILL_PARAMS, { count: n, value: 0, mode: 1, pad0: 0 });
9756
+ fill.dispatch(pass, fill.bind({ dst: labelsA, P: iota.binding }), nodePlan, [iota.offset]);
9757
+ }
9758
+ for (let i = 0; i < k; i++) {
9759
+ groupBy.record(pass, {
9760
+ rowPtr: graph.rowPtr,
9761
+ colIdx: graph.colIdx,
9762
+ weights: weighted ? graph.weights : null,
9763
+ keyIn: cur,
9764
+ plan,
9765
+ rows,
9766
+ hashRegion: region,
9767
+ bestKey,
9768
+ bestScore
9769
+ });
9770
+ const params = scope.params(LPA_PARAMS, {
9771
+ n,
9772
+ direction: (done + i) % 2,
9773
+ counterIndex: i,
9774
+ pad0: 0
9775
+ });
9776
+ step.dispatch(
9777
+ pass,
9778
+ step.bind({ labelsIn: cur, bestKey, labelsOut: next, counters, P: params.binding }),
9779
+ nodePlan,
9780
+ [params.offset]
9781
+ );
9782
+ [cur, next] = [next, cur];
9783
+ }
9784
+ batch.endPass();
9785
+ const movesRequest = batch.readback(counters.buffer, 0, 4 * k);
9786
+ const exhaustedRequest = batch.readback(region.buffer, 0, 4);
9787
+ scope.flush();
9788
+ const submitted = batch.submit();
9789
+ const bytes = await submitted.readback;
9790
+ ctx.assertReady();
9791
+ if (first) {
9792
+ assertBuildSorted(bytes, build, ALGORITHM$1);
9793
+ first = false;
9794
+ }
9795
+ if (new Uint32Array(bytes, exhaustedRequest.offset, 1)[0] !== 0) {
9796
+ throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: a hash probe exhausted its bound`, {
9797
+ label: `${ALGORITHM$1}/group-by-key`,
9798
+ message: "a compare-exchange loop of the workgroup tier ran out of steps"
9799
+ });
9800
+ }
9801
+ const moves = new Uint32Array(bytes, movesRequest.offset, k);
9802
+ done += k;
9803
+ const lastTwo = k >= 2 ? moves[k - 2] + moves[k - 1] : previousLast + moves[0];
9804
+ previousLast = moves[k - 1];
9805
+ if (options?.signal?.aborted) {
9806
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted`, {
9807
+ batchId: submitted.id
9808
+ });
9809
+ }
9810
+ options?.onProgress?.(done, maxIterations);
9811
+ if (lastTwo === 0 || done >= maxIterations) {
9812
+ break;
9813
+ }
9814
+ batch = new CommandBatch(ctx, `${ALGORITHM$1}/passes`);
9815
+ }
9816
+ const raw = new Uint32Array(n);
9817
+ await ctx.readback.read(cur.buffer, 4 * n, raw);
9818
+ ctx.assertReady();
9819
+ return resultOf(raw, dest);
9820
+ } finally {
9821
+ scope.dispose();
9822
+ }
9823
+ }
9824
+ const ALGORITHM = "triangleCount";
9825
+ const RING_SLOTS = 1024;
9826
+ function checkDest(dest, n) {
9827
+ if (dest === void 0) {
9828
+ return null;
9829
+ }
9830
+ if (dest instanceof Uint32Array && dest.length === n && dest.buffer instanceof ArrayBuffer) {
9831
+ return dest;
9832
+ }
9833
+ throw new WebGpuGraphError(
9834
+ "E_INVALID_ARGUMENT",
9835
+ `${ALGORITHM}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
9836
+ {
9837
+ argument: "dest",
9838
+ value: `${dest.constructor.name}(${dest.length})`,
9839
+ expected: `Uint32Array(${n}) over an ArrayBuffer`
9840
+ }
9841
+ );
9842
+ }
9843
+ function epilogue(perNode, rowPtr) {
9844
+ const n = perNode.length;
9845
+ const coefficient = new Float32Array(n);
9846
+ let sum = 0;
9847
+ let triples = 0;
9848
+ for (let v = 0; v < n; v++) {
9849
+ const d = rowPtr[v + 1] - rowPtr[v];
9850
+ const t = perNode[v];
9851
+ sum += t;
9852
+ if (d >= 2) {
9853
+ const pairs = d * (d - 1) / 2;
9854
+ triples += pairs;
9855
+ coefficient[v] = t / pairs;
9856
+ }
9857
+ }
9858
+ return { perNode, total: sum / 3, coefficient, transitivity: triples === 0 ? 0 : sum / triples };
9859
+ }
9860
+ async function triangleCount(ctx, s, options) {
9861
+ return await triangleCountWithSearch(ctx, s, 0, options);
9862
+ }
9863
+ async function triangleCountWithSearch(ctx, s, search, options) {
9864
+ ctx.assertReady();
9865
+ await assertDeviceComputes(ctx);
9866
+ const n = s.nodeCount;
9867
+ const dest = checkDest(options?.dest, n);
9868
+ if (options?.signal?.aborted) {
9869
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM}: the signal was aborted before any work started`, {});
9870
+ }
9871
+ if (n === 0) {
9872
+ options?.onProgress?.(1, 1);
9873
+ return epilogue(dest ?? new Uint32Array(0), new Uint32Array(1));
9874
+ }
9875
+ const scope = algorithmScope(ctx, ALGORITHM, RING_SLOTS);
9876
+ try {
9877
+ const build = await buildSimpleSymmetric(ctx, s, scope, false, ALGORITHM);
9878
+ const { graph, batch } = build;
9879
+ const wg = ctx.workgroupSize;
9880
+ const countsBytes = 4 * n;
9881
+ const counts = {
9882
+ buffer: scope.scratch(countsBytes, "counts"),
9883
+ offset: 0,
9884
+ size: countsBytes,
9885
+ window: null
9886
+ };
9887
+ const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
9888
+ const pass = batch.pass("triangles");
9889
+ let orientedFlag = null;
9890
+ const zero = scope.params(FILL_PARAMS, { count: n, value: 0, mode: 0, pad0: 0 });
9891
+ fill.dispatch(pass, fill.bind({ dst: counts, P: zero.binding }), plan1d(n, wg, ctx.caps), [zero.offset]);
9892
+ if (graph.arcCount > 0 && graph.colIdx !== null && graph.src !== null) {
9893
+ const oriented = graph.arcCount / 2;
9894
+ const orient = await ctx.pipelines.kernel(kernelSpec("orient-flags"));
9895
+ const intersect = await ctx.pipelines.kernel(kernelSpec("tri-intersect", { SEARCH: search }));
9896
+ const compact = await prepareCompact(scope);
9897
+ const cooToCsr = await prepareCooToCsr(scope);
9898
+ const words = (count, label) => ({
9899
+ buffer: scope.scratch(4 * count, label),
9900
+ offset: 0,
9901
+ size: 4 * count,
9902
+ window: null
9903
+ });
9904
+ const flags = words(graph.arcCount, "orient/flags");
9905
+ const oSrc = words(graph.arcCount, "orient/src");
9906
+ const oDst = words(graph.arcCount, "orient/dst");
9907
+ const oRowPtr = words(n + 1, "orient/rowPtr");
9908
+ const oColIdx = words(oriented, "orient/colIdx");
9909
+ const scratch = words(1, "orient/count");
9910
+ const oFlag = words(1, "orient/flag");
9911
+ const arcPlan = plan1d(graph.arcCount, wg, ctx.caps);
9912
+ const p1 = scope.params(COO_PARAMS, { count: graph.arcCount, pad0: 0, pad1: 0, pad2: 0 });
9913
+ orient.dispatch(
9914
+ pass,
9915
+ orient.bind({ rowPtr: graph.rowPtr, colIdx: graph.colIdx, src: graph.src, flags, P: p1.binding }),
9916
+ arcPlan,
9917
+ [p1.offset]
9918
+ );
9919
+ compact.record(pass, {
9920
+ queue: graph.src,
9921
+ flags,
9922
+ count: graph.arcCount,
9923
+ out: oSrc,
9924
+ outCount: scratch,
9925
+ outIndex: 0
9926
+ });
9927
+ compact.record(pass, {
9928
+ queue: graph.colIdx,
9929
+ flags,
9930
+ count: graph.arcCount,
9931
+ out: oDst,
9932
+ outCount: scratch,
9933
+ outIndex: 0
9934
+ });
9935
+ cooToCsr.record(pass, {
9936
+ src: oSrc,
9937
+ dst: oDst,
9938
+ weights: null,
9939
+ count: oriented,
9940
+ n,
9941
+ sortedInput: true,
9942
+ out: { rowPtr: oRowPtr, colIdx: oColIdx, weights: null, flag: oFlag }
9943
+ });
9944
+ orientedFlag = oFlag;
9945
+ const p2 = scope.params(COO_PARAMS, { count: oriented, pad0: 0, pad1: 0, pad2: 0 });
9946
+ intersect.dispatch(
9947
+ pass,
9948
+ intersect.bind({ rowPtr: oRowPtr, colIdx: oColIdx, src: oSrc, counts, P: p2.binding }),
9949
+ plan1d(oriented, wg, ctx.caps),
9950
+ [p2.offset]
9951
+ );
9952
+ }
9953
+ batch.endPass();
9954
+ const countsRequest = batch.readback(counts.buffer, 0, countsBytes);
9955
+ const rowsRequest = batch.readback(graph.rowPtr.buffer, 0, 4 * (n + 1));
9956
+ const orientedRequest = orientedFlag === null ? null : batch.readback(orientedFlag.buffer, 0, 4);
9957
+ scope.flush();
9958
+ const submitted = batch.submit();
9959
+ const bytes = await submitted.readback;
9960
+ ctx.assertReady();
9961
+ if (options?.signal?.aborted) {
9962
+ throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM}: the signal was aborted`, { batchId: submitted.id });
9963
+ }
9964
+ assertBuildSorted(bytes, build, ALGORITHM);
9965
+ if (orientedRequest !== null && new Uint32Array(bytes, orientedRequest.offset, 1)[0] !== 0) {
9966
+ throw new WebGpuGraphError(
9967
+ "E_VALIDATION",
9968
+ `${ALGORITHM}: the oriented arcs reached cooToCsr out of order`,
9969
+ {
9970
+ label: `${ALGORITHM}/oriented`,
9971
+ message: "the sorted-input precondition of cooToCsr failed on the device"
9972
+ }
9973
+ );
9974
+ }
9975
+ const perNode = dest ?? new Uint32Array(n);
9976
+ perNode.set(new Uint32Array(bytes, countsRequest.offset, n));
9977
+ const rowPtr = new Uint32Array(bytes, rowsRequest.offset, n + 1);
9978
+ options?.onProgress?.(1, 1);
9979
+ return epilogue(perNode, rowPtr);
9980
+ } finally {
9981
+ scope.dispose();
9982
+ }
9983
+ }
8396
9984
  function nextPow2(x) {
8397
9985
  let p = 1;
8398
9986
  while (p < x) {
@@ -13451,6 +15039,53 @@ function createAccelerator(ctx, options) {
13451
15039
  ctx.assertReady();
13452
15040
  return await closenessCentrality(ctx, gs, o);
13453
15041
  },
15042
+ /**
15043
+ * All-pairs shortest paths on the device (design 8.7): blocked Floyd-Warshall, `E_TOO_LARGE` above the device's
15044
+ * storage-binding ceiling. The seam passes `SsspOptions`; neither of its keys has an all-pairs meaning, so a
15045
+ * defined `cutoff` (it would change what `+Infinity` means) or `weights` (a per-arc override is a different
15046
+ * matrix from the snapshot's resident column) is `E_UNSUPPORTED { option }`, never silently dropped.
15047
+ * @param gs - the snapshot
15048
+ * @param o - the seam's `SsspOptions`; both keys refused when defined
15049
+ * @returns the row-major `n x n` distances and `n` (spec 3.3 line 835)
15050
+ */
15051
+ async allPairsShortestPath(gs, o) {
15052
+ ctx.assertReady();
15053
+ for (const key of ["cutoff", "weights"]) {
15054
+ if (o?.[key] !== void 0) {
15055
+ throw new WebGpuGraphError("E_UNSUPPORTED", `allPairsShortestPath: ${key} is not supported`, {
15056
+ option: key,
15057
+ hint: "all-pairs shortest paths runs over the snapshot's own weights with no cutoff"
15058
+ });
15059
+ }
15060
+ }
15061
+ return await allPairsShortestPath(ctx, gs);
15062
+ },
15063
+ /**
15064
+ * Triangle counting with the clustering coefficient and the transitivity (design 8.5; P11).
15065
+ * @param gs - the snapshot
15066
+ * @returns perNode, total, coefficient and transitivity
15067
+ */
15068
+ async triangleCount(gs) {
15069
+ ctx.assertReady();
15070
+ return await triangleCount(ctx, gs);
15071
+ },
15072
+ /**
15073
+ * Label propagation (design 8.6; P11): `maxIterations` and `weighted` are honoured, `tolerance` is refused when
15074
+ * defined (a label propagation stops at a fixed point, not below a tolerance).
15075
+ * @param gs - the snapshot
15076
+ * @param o - the seam's placeholder `HitsOptionsLike`
15077
+ * @returns the labels dense in first-seen order, the community count and groups()
15078
+ */
15079
+ async labelPropagation(gs, o) {
15080
+ ctx.assertReady();
15081
+ if (o?.tolerance !== void 0) {
15082
+ throw new WebGpuGraphError("E_UNSUPPORTED", "labelPropagation: tolerance has no meaning here", {
15083
+ option: "tolerance",
15084
+ hint: "label propagation stops at a fixed point or after maxIterations passes"
15085
+ });
15086
+ }
15087
+ return await labelPropagation(ctx, gs, { maxIterations: o?.maxIterations, weighted: o?.weighted });
15088
+ },
13454
15089
  /**
13455
15090
  * Destroys every device buffer recorded for the snapshot (spec 4.5); delegates to ctx.release.
13456
15091
  * @param s - the snapshot the app is done with
@@ -13560,7 +15195,7 @@ async function calibrateLayout(ctx, options) {
13560
15195
  };
13561
15196
  }
13562
15197
  export {
13563
- Q as ARC_WINDOW_ALIGN,
15198
+ a0 as ARC_WINDOW_ALIGN,
13564
15199
  EXACT_MAX_NODES,
13565
15200
  FA2_DEFAULTS,
13566
15201
  FR_DEFAULTS,
@@ -13568,11 +15203,12 @@ export {
13568
15203
  LAYOUT_TUNING_DEFAULTS,
13569
15204
  MAX_1D_ITEMS,
13570
15205
  MAX_WORKGROUPS_PER_DIM,
13571
- V as PASSTHROUGH_FORMAT_CODES,
15206
+ a1 as PASSTHROUGH_FORMAT_CODES,
13572
15207
  SE_DEFAULTS,
13573
- X as STORAGE_ALIGN,
13574
- Y as WORKGROUP_SIZE,
15208
+ a2 as STORAGE_ALIGN,
15209
+ a3 as WORKGROUP_SIZE,
13575
15210
  WebGpuGraphError,
15211
+ allPairsShortestPath,
13576
15212
  bellmanFord,
13577
15213
  betweennessCentrality,
13578
15214
  breadthFirstSearch,
@@ -13588,13 +15224,15 @@ export {
13588
15224
  eigenvectorCentrality,
13589
15225
  hasErrorCode,
13590
15226
  hits,
13591
- Z as isSoftwareAdapter,
15227
+ a4 as isSoftwareAdapter,
13592
15228
  isWebGpuGraphError,
13593
15229
  katzCentrality,
15230
+ labelPropagation,
13594
15231
  pageRank,
13595
15232
  personalizedPageRank,
13596
15233
  seedPositions,
13597
15234
  sssp,
15235
+ triangleCount,
13598
15236
  verifyDevice
13599
15237
  };
13600
15238
  //# sourceMappingURL=webgpu-graph-algorithms.js.map