@graphty/webgpu-graph-algorithms 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +45 -36
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{context-E6iKaeuJ.js → context-CRbw2Wyo.js} +178 -19
  4. package/dist/chunks/{context-E6iKaeuJ.js.map → context-CRbw2Wyo.js.map} +1 -1
  5. package/dist/node.js +1 -1
  6. package/dist/src/accelerator.d.ts +9 -6
  7. package/dist/src/accelerator.d.ts.map +1 -1
  8. package/dist/src/accelerator.js +85 -6
  9. package/dist/src/accelerator.js.map +1 -1
  10. package/dist/src/algorithms/components.d.ts +30 -0
  11. package/dist/src/algorithms/components.d.ts.map +1 -0
  12. package/dist/src/algorithms/components.js +300 -0
  13. package/dist/src/algorithms/components.js.map +1 -0
  14. package/dist/src/algorithms/pagerank.d.ts +39 -0
  15. package/dist/src/algorithms/pagerank.d.ts.map +1 -0
  16. package/dist/src/algorithms/pagerank.js +298 -0
  17. package/dist/src/algorithms/pagerank.js.map +1 -0
  18. package/dist/src/algorithms/power-iteration.d.ts +109 -0
  19. package/dist/src/algorithms/power-iteration.d.ts.map +1 -0
  20. package/dist/src/algorithms/power-iteration.js +206 -0
  21. package/dist/src/algorithms/power-iteration.js.map +1 -0
  22. package/dist/src/algorithms/scope.d.ts +26 -0
  23. package/dist/src/algorithms/scope.d.ts.map +1 -0
  24. package/dist/src/algorithms/scope.js +41 -0
  25. package/dist/src/algorithms/scope.js.map +1 -0
  26. package/dist/src/algorithms/spectral.d.ts +50 -0
  27. package/dist/src/algorithms/spectral.d.ts.map +1 -0
  28. package/dist/src/algorithms/spectral.js +247 -0
  29. package/dist/src/algorithms/spectral.js.map +1 -0
  30. package/dist/src/index.d.ts +4 -0
  31. package/dist/src/index.d.ts.map +1 -1
  32. package/dist/src/index.js +4 -0
  33. package/dist/src/index.js.map +1 -1
  34. package/dist/src/kernel/dispatch.d.ts +4 -1
  35. package/dist/src/kernel/dispatch.d.ts.map +1 -1
  36. package/dist/src/kernel/dispatch.js +12 -5
  37. package/dist/src/kernel/dispatch.js.map +1 -1
  38. package/dist/src/kernels.d.ts +20 -4
  39. package/dist/src/kernels.d.ts.map +1 -1
  40. package/dist/src/kernels.js +172 -2
  41. package/dist/src/kernels.js.map +1 -1
  42. package/dist/src/memory/residency.d.ts.map +1 -1
  43. package/dist/src/memory/residency.js +164 -11
  44. package/dist/src/memory/residency.js.map +1 -1
  45. package/dist/src/primitives/core-shape.d.ts +41 -0
  46. package/dist/src/primitives/core-shape.d.ts.map +1 -0
  47. package/dist/src/primitives/core-shape.js +89 -0
  48. package/dist/src/primitives/core-shape.js.map +1 -0
  49. package/dist/src/primitives/segmented-reduce.d.ts.map +1 -1
  50. package/dist/src/primitives/segmented-reduce.js +4 -30
  51. package/dist/src/primitives/segmented-reduce.js.map +1 -1
  52. package/dist/src/primitives/spmv.d.ts +56 -0
  53. package/dist/src/primitives/spmv.d.ts.map +1 -0
  54. package/dist/src/primitives/spmv.js +101 -0
  55. package/dist/src/primitives/spmv.js.map +1 -0
  56. package/dist/src/types/accelerator.d.ts +14 -2
  57. package/dist/src/types/accelerator.d.ts.map +1 -1
  58. package/dist/src/types/algorithms.d.ts +73 -0
  59. package/dist/src/types/algorithms.d.ts.map +1 -0
  60. package/dist/src/types/algorithms.js +17 -0
  61. package/dist/src/types/algorithms.js.map +1 -0
  62. package/dist/src/wgsl/pr-finalize.wgsl.d.ts +11 -0
  63. package/dist/src/wgsl/pr-finalize.wgsl.d.ts.map +1 -0
  64. package/dist/src/wgsl/pr-finalize.wgsl.js +36 -0
  65. package/dist/src/wgsl/pr-finalize.wgsl.js.map +1 -0
  66. package/dist/src/wgsl/pr-scale.wgsl.d.ts +14 -0
  67. package/dist/src/wgsl/pr-scale.wgsl.d.ts.map +1 -0
  68. package/dist/src/wgsl/pr-scale.wgsl.js +48 -0
  69. package/dist/src/wgsl/pr-scale.wgsl.js.map +1 -0
  70. package/dist/src/wgsl/spmv-pull.wgsl.d.ts +15 -0
  71. package/dist/src/wgsl/spmv-pull.wgsl.d.ts.map +1 -0
  72. package/dist/src/wgsl/spmv-pull.wgsl.js +47 -0
  73. package/dist/src/wgsl/spmv-pull.wgsl.js.map +1 -0
  74. package/dist/src/wgsl/wcc-compress.wgsl.d.ts +9 -0
  75. package/dist/src/wgsl/wcc-compress.wgsl.d.ts.map +1 -0
  76. package/dist/src/wgsl/wcc-compress.wgsl.js +26 -0
  77. package/dist/src/wgsl/wcc-compress.wgsl.js.map +1 -0
  78. package/dist/src/wgsl/wcc-link-edges.wgsl.d.ts +13 -0
  79. package/dist/src/wgsl/wcc-link-edges.wgsl.d.ts.map +1 -0
  80. package/dist/src/wgsl/wcc-link-edges.wgsl.js +46 -0
  81. package/dist/src/wgsl/wcc-link-edges.wgsl.js.map +1 -0
  82. package/dist/src/wgsl/wcc-link-sample.wgsl.d.ts +11 -0
  83. package/dist/src/wgsl/wcc-link-sample.wgsl.d.ts.map +1 -0
  84. package/dist/src/wgsl/wcc-link-sample.wgsl.js +45 -0
  85. package/dist/src/wgsl/wcc-link-sample.wgsl.js.map +1 -0
  86. package/dist/src/wgsl/wcc-sample.wgsl.d.ts +10 -0
  87. package/dist/src/wgsl/wcc-sample.wgsl.d.ts.map +1 -0
  88. package/dist/src/wgsl/wcc-sample.wgsl.js +18 -0
  89. package/dist/src/wgsl/wcc-sample.wgsl.js.map +1 -0
  90. package/dist/tsconfig.build.tsbuildinfo +1 -1
  91. package/dist/webgpu-graph-algorithms.js +1550 -29
  92. package/dist/webgpu-graph-algorithms.js.map +1 -1
  93. package/package.json +1 -1
  94. package/src/accelerator.ts +101 -7
  95. package/src/algorithms/components.ts +348 -0
  96. package/src/algorithms/pagerank.ts +343 -0
  97. package/src/algorithms/power-iteration.ts +278 -0
  98. package/src/algorithms/scope.ts +52 -0
  99. package/src/algorithms/spectral.ts +300 -0
  100. package/src/index.ts +18 -0
  101. package/src/kernel/dispatch.ts +12 -5
  102. package/src/kernels.ts +206 -5
  103. package/src/memory/residency.ts +200 -11
  104. package/src/primitives/core-shape.ts +103 -0
  105. package/src/primitives/segmented-reduce.ts +4 -36
  106. package/src/primitives/spmv.ts +155 -0
  107. package/src/types/accelerator.ts +28 -2
  108. package/src/types/algorithms.ts +82 -0
  109. package/src/wgsl/pr-finalize.wgsl.ts +36 -0
  110. package/src/wgsl/pr-scale.wgsl.ts +48 -0
  111. package/src/wgsl/spmv-pull.wgsl.ts +47 -0
  112. package/src/wgsl/wcc-compress.wgsl.ts +26 -0
  113. package/src/wgsl/wcc-link-edges.wgsl.ts +46 -0
  114. package/src/wgsl/wcc-link-sample.wgsl.ts +45 -0
  115. package/src/wgsl/wcc-sample.wgsl.ts +18 -0
package/README.md CHANGED
@@ -287,32 +287,39 @@ are the T-table of plan section 10.4. A missed target is re-fixed by a recorded
287
287
 
288
288
  ### The dev box (nvidia-lovelace-driver580)
289
289
 
290
- Measured on nvidia-lovelace-driver580 (NVIDIA: 580.173.02 580.173.2.0), session 2026-09-16T02:07:45.933Z, medians of 5 runs; Chromium: nvidia / lovelace (nvidia-lovelace-driver0, the description is redacted by Chromium), session 2026-09-16T02:18:11.896Z.
291
-
292
- | Id | What | Target | Measured |
293
- | --- | -------------------------------------------------------------------------------------------- | ------------------- | -------------------- |
294
- | T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB) | <= 10 ms; <= 100 ms | 6.032 ms; 127.531 ms |
295
- | T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 0.878 ms |
296
- | T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 0.170 ms |
297
- | T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.586 ms; 1.052 ms |
298
- | T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets) | <= 6 ms | 2.400 ms (0.738 ms) |
299
-
300
- Two rows miss their target in this session: the 1M / 10M upload (127.5 ms against 100 ms, the open owner decision of
301
- `docs/decisions/G1.md` section 7) and the empty-submit round trip (0.170 ms against 0.1 ms: the row is measured after the
302
- `upload` group, whose CPU-heavy setup lets the SM clock fall to its idle state; the same row measures 0.041-0.074 ms at
303
- the working clock -- finding G3-F2 of `docs/decisions/G3.md` section 10).
290
+ Measured on nvidia-lovelace-driver580 (NVIDIA: 580.173.02 580.173.2.0), session 2026-09-20T01:06:48.656Z, medians of 5 runs; Chromium: nvidia / lovelace (nvidia-lovelace-driver0, the description is redacted by Chromium), session 2026-09-16T02:18:11.896Z.
291
+
292
+ | Id | What | Target | Measured |
293
+ | --- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------- |
294
+ | T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB) | <= 10 ms; <= 100 ms | 5.980 ms; 125.738 ms |
295
+ | T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 0.870 ms |
296
+ | T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 0.181 ms |
297
+ | T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.586 ms; 1.053 ms |
298
+ | T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets) | <= 6 ms | 2.400 ms (0.726 ms) |
299
+ | T-8 | PageRank, 100 iterations, wall end to end including the upload, at 100k / 1M; at 1M / 10M | <= 150 ms; <= 1.5 s | 17.204 ms; 198.645 ms |
300
+ | T-9 | Weakly connected components (Afforest), wall end to end including the upload and the label readback, at 1M / 10M (100k / 1M in brackets) | <= 100 ms | 145.970 ms (12.613 ms) |
301
+
302
+ Three rows miss their target in this session: the 1M / 10M upload (125.7 ms against 100 ms, the open owner decision of
303
+ `docs/decisions/G1.md` section 7), the empty-submit round trip (0.181 ms against 0.1 ms: the row is measured after the
304
+ `upload` group, whose CPU-heavy setup lets the SM clock fall to its idle state; the same row measures 0.041-0.075 ms at
305
+ the working clock -- finding G3-F2 of `docs/decisions/G3.md` section 10), and WCC at 1M / 10M (146.0 ms against
306
+ 100 ms: the row is wall end to end from a released core, so it carries the same 164 MB upload T-1 times at 125.7 ms;
307
+ with the core resident the same call takes 11-16 ms). The T-9 target is therefore below
308
+ the T-1 upload it includes, an owner decision for `docs/decisions/G7.md`. The `pagerank` rows run all 100 iterations
309
+ (`tolerance: 0`): at the NetworkX tolerance of 1e-6 the seeded G(n, m) input converges from the uniform start in one to
310
+ four iterations, which would time one pull and call it a hundred.
304
311
 
305
312
  The exact curve (the `layout-exact` group: 2D, E = 10n, seeded G(n, m), one simulation per rung; ms / iteration from the profiler):
306
313
 
307
314
  | n | ms / iteration | step(1) wall (ms) | pairs / s |
308
315
  | ----- | -------------- | ----------------- | --------- |
309
- | 1024 | 0.096 | 0.228 | 1.09e+10 |
310
- | 4096 | 0.255 | 0.415 | 6.58e+10 |
311
- | 8192 | 0.478 | 0.630 | 1.40e+11 |
312
- | 10000 | 0.586 | 0.738 | 1.71e+11 |
313
- | 16384 | 1.052 | 1.226 | 2.55e+11 |
314
- | 32768 | 2.560 | 2.787 | 4.19e+11 |
315
- | 65536 | 8.405 | 8.862 | 5.11e+11 |
316
+ | 1024 | 0.087 | 0.224 | 1.20e+10 |
317
+ | 4096 | 0.255 | 0.381 | 6.58e+10 |
318
+ | 8192 | 0.478 | 0.612 | 1.40e+11 |
319
+ | 10000 | 0.586 | 0.726 | 1.71e+11 |
320
+ | 16384 | 1.053 | 1.207 | 2.55e+11 |
321
+ | 32768 | 2.561 | 2.757 | 4.19e+11 |
322
+ | 65536 | 8.402 | 8.705 | 5.11e+11 |
316
323
 
317
324
  The end-to-end run of `benchmarks/layout-run.ts --nodes 100000 --edges 1000000` (the exact tier at 100k, 100
318
325
  iterations, batches of 8) takes 18.971 ms per iteration on the same card, uploads and readbacks included (16.975 ms of
@@ -327,27 +334,29 @@ T-4 and T-5 and misses T-1 (both uploads), T-2 and T-3, which is the class diffe
327
334
  cloud vCPU (host-side copies and submit latency), not a regression: the exact tier's `ms / iteration` is 1.7x the
328
335
  RTX 4070 SUPER's at 10k and 3.0x at 65k.
329
336
 
330
- Measured on gpu-linux-t4 (NVIDIA: 580.126.20 580.126.20.0), session 2026-09-18T07:03:17.146Z, medians of 5 runs; Chromium: nvidia / turing (nvidia-turing-driver0, the description is redacted by Chromium), session 2026-09-18T07:02:07.834Z.
337
+ Measured on gpu-linux-t4 (NVIDIA: 580.126.20 580.126.20.0), session 2026-09-20T02:29:33.210Z (run 35483512705), medians of 5 runs; Chromium: nvidia / turing (nvidia-turing-driver0, the description is redacted by Chromium), session 2026-09-20T02:28:10.676Z.
331
338
 
332
- | Id | What | Target | Measured |
333
- | --- | -------------------------------------------------------------------------------------------- | ------------------- | --------------------- |
334
- | T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB) | <= 10 ms; <= 100 ms | 14.458 ms; 257.954 ms |
335
- | T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 2.298 ms |
336
- | T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 0.171 ms |
337
- | T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.977 ms; 1.879 ms |
338
- | T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets) | <= 6 ms | 2.600 ms (1.357 ms) |
339
+ | Id | What | Target | Measured |
340
+ | --- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------- |
341
+ | T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB) | <= 10 ms; <= 100 ms | 14.338 ms; 256.418 ms |
342
+ | T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 2.957 ms |
343
+ | T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 1.296 ms |
344
+ | T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.972 ms; 1.953 ms |
345
+ | T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets) | <= 6 ms | 2.600 ms (1.359 ms) |
346
+ | T-8 | PageRank, 100 iterations, wall end to end including the upload, at 100k / 1M; at 1M / 10M | <= 150 ms; <= 1.5 s | 45.461 ms; 1092.799 ms |
347
+ | T-9 | Weakly connected components (Afforest), wall end to end including the upload and the label readback, at 1M / 10M (100k / 1M in brackets) | <= 100 ms | 293.079 ms (28.544 ms) |
339
348
 
340
349
  The exact curve (the `layout-exact` group: 2D, E = 10n, seeded G(n, m), one simulation per rung; ms / iteration from the profiler):
341
350
 
342
351
  | n | ms / iteration | step(1) wall (ms) | pairs / s |
343
352
  | ----- | -------------- | ----------------- | --------- |
344
- | 1024 | 0.334 | 0.977 | 3.13e+9 |
345
- | 4096 | 0.426 | 0.881 | 3.93e+10 |
346
- | 8192 | 0.801 | 1.184 | 8.38e+10 |
347
- | 10000 | 0.977 | 1.357 | 1.02e+11 |
348
- | 16384 | 1.879 | 2.317 | 1.43e+11 |
349
- | 32768 | 6.534 | 7.241 | 1.64e+11 |
350
- | 65536 | 24.883 | 25.955 | 1.73e+11 |
353
+ | 1024 | 0.340 | 0.711 | 3.08e+9 |
354
+ | 4096 | 0.431 | 0.814 | 3.90e+10 |
355
+ | 8192 | 0.797 | 1.172 | 8.42e+10 |
356
+ | 10000 | 0.972 | 1.359 | 1.03e+11 |
357
+ | 16384 | 1.953 | 2.383 | 1.37e+11 |
358
+ | 32768 | 6.861 | 7.394 | 1.57e+11 |
359
+ | 65536 | 25.236 | 26.366 | 1.70e+11 |
351
360
 
352
361
  ## Development
353
362
 
package/dist/browser.js CHANGED
@@ -1,4 +1,4 @@
1
- import { G as GpuContext, W as WebGpuGraphError } from "./chunks/context-E6iKaeuJ.js";
1
+ import { G as GpuContext, W as WebGpuGraphError } from "./chunks/context-CRbw2Wyo.js";
2
2
  function navigatorGpu() {
3
3
  if (typeof navigator === "undefined") {
4
4
  return void 0;
@@ -2457,22 +2457,38 @@ class GraphResidency {
2457
2457
  });
2458
2458
  }
2459
2459
  /**
2460
- * Uploads (or finds) a view; P1-P3 support outDegree / inDegree / degreeOrder / reverseDegreeOrder; the others
2461
- * -> E_UNSUPPORTED until P7. packViews is accepted and ignored until P7 (a `true` request is E_UNSUPPORTED
2462
- * { option: "packViews" }, PLAN DECISION 9).
2460
+ * Uploads (or finds) a view: outDegree / inDegree / degreeOrder / reverseDegreeOrder upload one array each;
2461
+ * reverse and edgeList (P7) upload their arrays perArray, never into the arena, and are memoised per record so
2462
+ * a second call uploads nothing (spec 4.3). coo and mate -> E_UNSUPPORTED until P11. packViews concatenates a
2463
+ * reverse or edgeList view into ONE buffer at STORAGE_ALIGN offsets; on any other view `true` is E_UNSUPPORTED
2464
+ * { option: "packViews" }.
2463
2465
  * @param s - the snapshot
2464
2466
  * @param name - the view
2465
2467
  * @param options - view options
2466
- * @param options.packViews - P7's packed reverse upload; `true` is E_UNSUPPORTED until then
2468
+ * @param options.packViews - pack the reverse / edgeList arrays into one buffer (a no-op on an undirected reverse)
2467
2469
  * @returns the view binding
2468
2470
  */
2469
2471
  view(s, name, options) {
2470
2472
  this.assertLive();
2471
2473
  assertAttached(s);
2472
- if (options?.packViews === true) {
2473
- throw new WebGpuGraphError("E_UNSUPPORTED", "packViews is not supported before the P7 view uploads", {
2474
+ const packed = options?.packViews === true;
2475
+ if (name === "reverse" || name === "edgeList") {
2476
+ this.assertNotReleased(s);
2477
+ this.assertNonEmpty(s);
2478
+ const memoKey = packed ? `${name}:packed` : name;
2479
+ const record2 = this.ensureRecord(s);
2480
+ const memo = record2.views.get(memoKey);
2481
+ if (memo !== void 0) {
2482
+ return memo;
2483
+ }
2484
+ const built = name === "reverse" ? this.buildReverse(s, record2, packed) : this.buildEdgeList(s, record2, packed);
2485
+ record2.views.set(memoKey, built);
2486
+ return built;
2487
+ }
2488
+ if (packed) {
2489
+ throw new WebGpuGraphError("E_UNSUPPORTED", "packViews applies to the reverse and edgeList views only", {
2474
2490
  option: "packViews",
2475
- hint: "omit packViews; the four reverse arrays are uploaded separately"
2491
+ hint: `the ${name} view uploads one array`
2476
2492
  });
2477
2493
  }
2478
2494
  let array;
@@ -2509,13 +2525,11 @@ class GraphResidency {
2509
2525
  scalars = { segmentOffsets: Array.from(order.segmentOffsets) };
2510
2526
  break;
2511
2527
  }
2512
- case "reverse":
2513
2528
  case "coo":
2514
- case "edgeList":
2515
2529
  case "mate":
2516
- throw new WebGpuGraphError("E_UNSUPPORTED", `the ${name} view is not uploaded before P7`, {
2530
+ throw new WebGpuGraphError("E_UNSUPPORTED", `the ${name} view is not uploaded before P11`, {
2517
2531
  feature: `view:${name}`,
2518
- hint: "P1-P3 upload outDegree, inDegree, degreeOrder and reverseDegreeOrder"
2532
+ hint: "outDegree, inDegree, degreeOrder, reverseDegreeOrder, reverse and edgeList are uploaded"
2519
2533
  });
2520
2534
  default:
2521
2535
  throw invalid("name", name, "a view name");
@@ -2529,6 +2543,146 @@ class GraphResidency {
2529
2543
  scalars: Object.freeze(scalars)
2530
2544
  });
2531
2545
  }
2546
+ /**
2547
+ * The reverse adjacency's bindings. On an UNDIRECTED snapshot graph-format invariant I7 makes reverse() return
2548
+ * the FORWARD arrays, so the core bindings ARE the reverse bindings and nothing is uploaded -- delegating to
2549
+ * core() rather than re-uploading the arrays is what makes that true on the arena plan as well, where the
2550
+ * per-array WeakMap key resolves to the whole-arena resident (spec 4.3 lines 1182-1186). `fwdArc` is never
2551
+ * uploaded (spec 13 row P7). `packed` is ignored when undirected: there is nothing to pack.
2552
+ * @param s - the snapshot
2553
+ * @param record - its residency record
2554
+ * @param packed - concatenate the arrays into one buffer (directed only)
2555
+ * @returns the view binding
2556
+ */
2557
+ buildReverse(s, record, packed) {
2558
+ const rev = s.reverse();
2559
+ if (!s.directed) {
2560
+ const core = this.core(s, ["rowPtr", "colIdx", "weights"]);
2561
+ const bindings2 = { rowPtr: core.rowPtr };
2562
+ if (core.colIdx !== null) {
2563
+ bindings2.colIdx = core.colIdx;
2564
+ }
2565
+ if (core.weights !== null) {
2566
+ bindings2.weights = core.weights;
2567
+ }
2568
+ return Object.freeze({
2569
+ view: "reverse",
2570
+ bindings: Object.freeze(bindings2),
2571
+ scalars: Object.freeze({ arcCount: [rev.arcCount], directed: [0] })
2572
+ });
2573
+ }
2574
+ const arrays = [
2575
+ ["rowPtr", rev.rowPtr],
2576
+ ["colIdx", rev.colIdx]
2577
+ ];
2578
+ if (rev.weights !== null) {
2579
+ arrays.push(["weights", rev.weights]);
2580
+ }
2581
+ const bindings = packed ? this.packArrays(
2582
+ record,
2583
+ arrays,
2584
+ this.packKey(record, "reverse:packed"),
2585
+ `residency:view:${record.serial}:reverse:packed`
2586
+ ) : this.separateArrays(record, arrays, `residency:view:${record.serial}:reverse`);
2587
+ return Object.freeze({
2588
+ view: "reverse",
2589
+ bindings,
2590
+ scalars: Object.freeze({ arcCount: [rev.arcCount], directed: [1] })
2591
+ });
2592
+ }
2593
+ /**
2594
+ * The each-edge-once binding an edge-parallel kernel uses on directed and undirected snapshots alike (spec 8.1
2595
+ * row 2); `arc` is not uploaded (no kernel of P7 reads it).
2596
+ * @param s - the snapshot
2597
+ * @param record - its residency record
2598
+ * @param packed - concatenate into one buffer
2599
+ * @returns the view binding
2600
+ */
2601
+ buildEdgeList(s, record, packed) {
2602
+ const list = s.edgeList();
2603
+ const arrays = [
2604
+ ["src", list.src],
2605
+ ["dst", list.dst]
2606
+ ];
2607
+ if (list.weights !== null) {
2608
+ arrays.push(["weights", list.weights]);
2609
+ }
2610
+ const bindings = packed ? this.packArrays(
2611
+ record,
2612
+ arrays,
2613
+ this.packKey(record, "edgeList:packed"),
2614
+ `residency:view:${record.serial}:edgeList:packed`
2615
+ ) : this.separateArrays(record, arrays, `residency:view:${record.serial}:edgeList`);
2616
+ return Object.freeze({
2617
+ view: "edgeList",
2618
+ bindings,
2619
+ scalars: Object.freeze({ edgeCount: [s.edgeCount] })
2620
+ });
2621
+ }
2622
+ /**
2623
+ * One resident per array (spec 4.3: views upload in perArray mode, never into the arena).
2624
+ * @param record - the owning record
2625
+ * @param arrays - the named arrays
2626
+ * @param label - the buffer label prefix
2627
+ * @returns the bindings by name
2628
+ */
2629
+ separateArrays(record, arrays, label) {
2630
+ const bindings = {};
2631
+ for (const [name, array] of arrays) {
2632
+ const resident = this.upload(record, array, array, `${label}:${name}`);
2633
+ bindings[name] = { buffer: resident.buffer, offset: 0, size: resident.byteLength, window: null };
2634
+ }
2635
+ return Object.freeze(bindings);
2636
+ }
2637
+ /**
2638
+ * The arrays concatenated into ONE buffer at STORAGE_ALIGN-aligned offsets (spec 4.3 packViews): one
2639
+ * createBuffer and one writeBuffer instead of three of each, which is what the option buys. The resident is
2640
+ * keyed on `key`, a marker object owned by the record (packKey), NOT on one of the snapshot's arrays: upload()
2641
+ * returns an existing resident whenever the key matches and the serial matches, without comparing byte
2642
+ * lengths, so keying the packed buffer on `rev.rowPtr` would make the packed and the unpacked view of one
2643
+ * snapshot collide -- whichever was built second would get the other's buffer. The record still owns the
2644
+ * resident, so release(s) destroys it with the rest. Offsets are STORAGE_ALIGN-aligned because Kernel.bind
2645
+ * rejects any other offset synchronously (E_INVALID_ARGUMENT { argument: "offset" }).
2646
+ * @param record - the owning record
2647
+ * @param arrays - the named arrays, in buffer order
2648
+ * @param key - the marker object the resident is keyed on
2649
+ * @param label - the buffer label
2650
+ * @returns the bindings by name, all into the one buffer
2651
+ */
2652
+ packArrays(record, arrays, key, label) {
2653
+ const offsets = [];
2654
+ let total = 0;
2655
+ for (const [, array] of arrays) {
2656
+ offsets.push(total);
2657
+ total += Math.ceil(array.byteLength / STORAGE_ALIGN) * STORAGE_ALIGN;
2658
+ }
2659
+ const staging = new Uint8Array(total);
2660
+ arrays.forEach(([, array], i) => {
2661
+ staging.set(new Uint8Array(array.buffer, array.byteOffset, array.byteLength), offsets[i]);
2662
+ });
2663
+ const resident = this.upload(record, key, staging, label);
2664
+ const bindings = {};
2665
+ arrays.forEach(([name, array], i) => {
2666
+ bindings[name] = { buffer: resident.buffer, offset: offsets[i], size: array.byteLength, window: null };
2667
+ });
2668
+ return Object.freeze(bindings);
2669
+ }
2670
+ /**
2671
+ * The upload key of a packed view: a marker object allocated once per record and memo name, never one of the
2672
+ * snapshot's arrays.
2673
+ * @param record - the owning record
2674
+ * @param memoKey - the `views` memo key of this packed view
2675
+ * @returns the stable marker object
2676
+ */
2677
+ packKey(record, memoKey) {
2678
+ const existing = record.packKeys.get(memoKey);
2679
+ if (existing !== void 0) {
2680
+ return existing;
2681
+ }
2682
+ const created = {};
2683
+ record.packKeys.set(memoKey, created);
2684
+ return created;
2685
+ }
2532
2686
  /**
2533
2687
  * gpuView(name) + column.version; re-uploads in place when the version changed and the byte length did not
2534
2688
  * (spec 4.3). CONTRACT DECISION: `owner` is required so release(owner) can find the buffer (a table has no
@@ -2764,6 +2918,8 @@ class GraphResidency {
2764
2918
  forget(record) {
2765
2919
  record.released = true;
2766
2920
  record.bindings.clear();
2921
+ record.views.clear();
2922
+ record.packKeys.clear();
2767
2923
  record.arena = null;
2768
2924
  record.plan = null;
2769
2925
  this.bySerial.delete(record.serial);
@@ -2789,6 +2945,8 @@ class GraphResidency {
2789
2945
  plan: null,
2790
2946
  arena: null,
2791
2947
  bindings: /* @__PURE__ */ new Map(),
2948
+ views: /* @__PURE__ */ new Map(),
2949
+ packKeys: /* @__PURE__ */ new Map(),
2792
2950
  released: false
2793
2951
  };
2794
2952
  this.bySerial.set(s.serial, record);
@@ -3122,15 +3280,16 @@ export {
3122
3280
  UNIFORM_SLOT_BYTES as U,
3123
3281
  WebGpuGraphError as W,
3124
3282
  WGSL_RESERVED_WORDS as a,
3125
- MAX_ITERATIONS_PER_STEP as b,
3126
- MAX_1D_ITEMS as c,
3283
+ U32_MAX as b,
3284
+ MAX_ITERATIONS_PER_STEP as c,
3127
3285
  deviceLostError as d,
3128
- FA2_FLAG_FIRST as e,
3129
- PASSTHROUGH_FORMAT_CODES as f,
3130
- STORAGE_ALIGN as g,
3286
+ MAX_1D_ITEMS as e,
3287
+ FA2_FLAG_FIRST as f,
3288
+ PASSTHROUGH_FORMAT_CODES as g,
3131
3289
  hasErrorCode as h,
3132
3290
  isWebGpuGraphError as i,
3133
- WORKGROUP_SIZE as j,
3134
- isSoftwareAdapter as k
3291
+ STORAGE_ALIGN as j,
3292
+ WORKGROUP_SIZE as k,
3293
+ isSoftwareAdapter as l
3135
3294
  };
3136
- //# sourceMappingURL=context-E6iKaeuJ.js.map
3295
+ //# sourceMappingURL=context-CRbw2Wyo.js.map