@graphty/webgpu-graph-algorithms 0.6.5 → 0.6.7

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 (147) hide show
  1. package/README.md +38 -17
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{context-hzGggHeM.js → context-Cezi7qpi.js} +46 -22
  4. package/dist/chunks/context-Cezi7qpi.js.map +1 -0
  5. package/dist/node.js +19 -11
  6. package/dist/node.js.map +1 -1
  7. package/dist/src/algorithms/bfs.d.ts +10 -6
  8. package/dist/src/algorithms/bfs.d.ts.map +1 -1
  9. package/dist/src/algorithms/bfs.js +32 -9
  10. package/dist/src/algorithms/bfs.js.map +1 -1
  11. package/dist/src/algorithms/pagerank.d.ts.map +1 -1
  12. package/dist/src/algorithms/pagerank.js +19 -5
  13. package/dist/src/algorithms/pagerank.js.map +1 -1
  14. package/dist/src/algorithms/power-iteration.d.ts.map +1 -1
  15. package/dist/src/algorithms/power-iteration.js +8 -2
  16. package/dist/src/algorithms/power-iteration.js.map +1 -1
  17. package/dist/src/constants.d.ts +33 -0
  18. package/dist/src/constants.d.ts.map +1 -1
  19. package/dist/src/constants.js +33 -0
  20. package/dist/src/constants.js.map +1 -1
  21. package/dist/src/kernel/dispatch.d.ts +2 -2
  22. package/dist/src/kernel/dispatch.d.ts.map +1 -1
  23. package/dist/src/kernel/kernel.d.ts +1 -1
  24. package/dist/src/kernel/kernel.js +2 -2
  25. package/dist/src/kernel/kernel.js.map +1 -1
  26. package/dist/src/kernel/prelude.d.ts.map +1 -1
  27. package/dist/src/kernel/prelude.js +2 -1
  28. package/dist/src/kernel/prelude.js.map +1 -1
  29. package/dist/src/kernels.d.ts +10 -7
  30. package/dist/src/kernels.d.ts.map +1 -1
  31. package/dist/src/kernels.js +33 -9
  32. package/dist/src/kernels.js.map +1 -1
  33. package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
  34. package/dist/src/layouts/forceatlas2.js +2 -1
  35. package/dist/src/layouts/forceatlas2.js.map +1 -1
  36. package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -1
  37. package/dist/src/layouts/fruchterman-reingold.js +4 -2
  38. package/dist/src/layouts/fruchterman-reingold.js.map +1 -1
  39. package/dist/src/layouts/repulsion-exact.d.ts +16 -0
  40. package/dist/src/layouts/repulsion-exact.d.ts.map +1 -1
  41. package/dist/src/layouts/repulsion-exact.js +21 -1
  42. package/dist/src/layouts/repulsion-exact.js.map +1 -1
  43. package/dist/src/layouts/repulsion-grid.d.ts +1 -1
  44. package/dist/src/layouts/repulsion-grid.js +1 -1
  45. package/dist/src/layouts/spring-electrical.d.ts.map +1 -1
  46. package/dist/src/layouts/spring-electrical.js +6 -2
  47. package/dist/src/layouts/spring-electrical.js.map +1 -1
  48. package/dist/src/memory/residency.js +14 -4
  49. package/dist/src/memory/residency.js.map +1 -1
  50. package/dist/src/node/index.d.ts +13 -8
  51. package/dist/src/node/index.d.ts.map +1 -1
  52. package/dist/src/node/index.js +36 -17
  53. package/dist/src/node/index.js.map +1 -1
  54. package/dist/src/primitives/frontier.d.ts +1 -0
  55. package/dist/src/primitives/frontier.d.ts.map +1 -1
  56. package/dist/src/primitives/frontier.js +1 -0
  57. package/dist/src/primitives/frontier.js.map +1 -1
  58. package/dist/src/primitives/grid-pyramid.d.ts +4 -4
  59. package/dist/src/primitives/grid-pyramid.d.ts.map +1 -1
  60. package/dist/src/primitives/grid-pyramid.js +4 -3
  61. package/dist/src/primitives/grid-pyramid.js.map +1 -1
  62. package/dist/src/primitives/grid.d.ts +13 -10
  63. package/dist/src/primitives/grid.d.ts.map +1 -1
  64. package/dist/src/primitives/grid.js +10 -7
  65. package/dist/src/primitives/grid.js.map +1 -1
  66. package/dist/src/wgsl/advance-expand.wgsl.d.ts +4 -3
  67. package/dist/src/wgsl/advance-expand.wgsl.d.ts.map +1 -1
  68. package/dist/src/wgsl/advance-expand.wgsl.js +4 -3
  69. package/dist/src/wgsl/advance-expand.wgsl.js.map +1 -1
  70. package/dist/src/wgsl/bfs-bottom-up.wgsl.d.ts +4 -3
  71. package/dist/src/wgsl/bfs-bottom-up.wgsl.d.ts.map +1 -1
  72. package/dist/src/wgsl/bfs-bottom-up.wgsl.js +4 -3
  73. package/dist/src/wgsl/bfs-bottom-up.wgsl.js.map +1 -1
  74. package/dist/src/wgsl/bfs-fused.wgsl.d.ts +6 -6
  75. package/dist/src/wgsl/bfs-fused.wgsl.d.ts.map +1 -1
  76. package/dist/src/wgsl/bfs-fused.wgsl.js +6 -6
  77. package/dist/src/wgsl/bfs-next-degree.wgsl.d.ts +23 -0
  78. package/dist/src/wgsl/bfs-next-degree.wgsl.d.ts.map +1 -0
  79. package/dist/src/wgsl/bfs-next-degree.wgsl.js +34 -0
  80. package/dist/src/wgsl/bfs-next-degree.wgsl.js.map +1 -0
  81. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +4 -1
  82. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -1
  83. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +18 -2
  84. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -1
  85. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +1 -1
  86. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
  87. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +4 -2
  88. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -1
  89. package/dist/src/wgsl/frontier-finalize.wgsl.d.ts +21 -20
  90. package/dist/src/wgsl/frontier-finalize.wgsl.d.ts.map +1 -1
  91. package/dist/src/wgsl/frontier-finalize.wgsl.js +26 -25
  92. package/dist/src/wgsl/frontier-finalize.wgsl.js.map +1 -1
  93. package/dist/src/wgsl/grid-cell-key.wgsl.d.ts +3 -2
  94. package/dist/src/wgsl/grid-cell-key.wgsl.d.ts.map +1 -1
  95. package/dist/src/wgsl/grid-cell-key.wgsl.js +4 -2
  96. package/dist/src/wgsl/grid-cell-key.wgsl.js.map +1 -1
  97. package/dist/src/wgsl/grid-centroid.wgsl.d.ts +3 -2
  98. package/dist/src/wgsl/grid-centroid.wgsl.d.ts.map +1 -1
  99. package/dist/src/wgsl/grid-centroid.wgsl.js +3 -2
  100. package/dist/src/wgsl/grid-centroid.wgsl.js.map +1 -1
  101. package/dist/src/wgsl/grid-downsample.wgsl.d.ts +1 -1
  102. package/dist/src/wgsl/grid-downsample.wgsl.js +1 -1
  103. package/dist/src/wgsl/grid-far-field.wgsl.d.ts +6 -4
  104. package/dist/src/wgsl/grid-far-field.wgsl.d.ts.map +1 -1
  105. package/dist/src/wgsl/grid-far-field.wgsl.js +15 -8
  106. package/dist/src/wgsl/grid-far-field.wgsl.js.map +1 -1
  107. package/dist/src/wgsl/grid-near-field.wgsl.d.ts +2 -2
  108. package/dist/src/wgsl/grid-near-field.wgsl.d.ts.map +1 -1
  109. package/dist/src/wgsl/grid-near-field.wgsl.js +6 -2
  110. package/dist/src/wgsl/grid-near-field.wgsl.js.map +1 -1
  111. package/dist/src/wgsl/histogram.wgsl.d.ts +1 -1
  112. package/dist/src/wgsl/histogram.wgsl.js +1 -1
  113. package/dist/webgpu-graph-algorithms.js +144 -45
  114. package/dist/webgpu-graph-algorithms.js.map +1 -1
  115. package/package.json +3 -3
  116. package/src/algorithms/bfs.ts +33 -9
  117. package/src/algorithms/pagerank.ts +19 -5
  118. package/src/algorithms/power-iteration.ts +8 -2
  119. package/src/constants.ts +35 -0
  120. package/src/kernel/dispatch.ts +2 -2
  121. package/src/kernel/kernel.ts +2 -2
  122. package/src/kernel/prelude.ts +2 -0
  123. package/src/kernels.ts +35 -9
  124. package/src/layouts/forceatlas2.ts +2 -0
  125. package/src/layouts/fruchterman-reingold.ts +4 -1
  126. package/src/layouts/repulsion-exact.ts +29 -1
  127. package/src/layouts/repulsion-grid.ts +1 -1
  128. package/src/layouts/spring-electrical.ts +8 -1
  129. package/src/memory/residency.ts +14 -4
  130. package/src/node/index.ts +42 -18
  131. package/src/primitives/frontier.ts +2 -0
  132. package/src/primitives/grid-pyramid.ts +6 -5
  133. package/src/primitives/grid.ts +17 -12
  134. package/src/wgsl/advance-expand.wgsl.ts +4 -3
  135. package/src/wgsl/bfs-bottom-up.wgsl.ts +4 -3
  136. package/src/wgsl/bfs-fused.wgsl.ts +6 -6
  137. package/src/wgsl/bfs-next-degree.wgsl.ts +33 -0
  138. package/src/wgsl/fa2-repulsion-exact.wgsl.ts +18 -2
  139. package/src/wgsl/fa2-stats-finalize.wgsl.ts +4 -2
  140. package/src/wgsl/frontier-finalize.wgsl.ts +26 -25
  141. package/src/wgsl/grid-cell-key.wgsl.ts +4 -2
  142. package/src/wgsl/grid-centroid.wgsl.ts +3 -2
  143. package/src/wgsl/grid-downsample.wgsl.ts +1 -1
  144. package/src/wgsl/grid-far-field.wgsl.ts +15 -8
  145. package/src/wgsl/grid-near-field.wgsl.ts +6 -2
  146. package/src/wgsl/histogram.wgsl.ts +1 -1
  147. package/dist/chunks/context-hzGggHeM.js.map +0 -1
package/README.md CHANGED
@@ -143,7 +143,7 @@ names and defaults as the CPU port in `@graphty/layout`) plus the GPU tuning:
143
143
  | `nodeSize` | `null` | `E_UNSUPPORTED` when non-null (no overlap prevention) |
144
144
  | `weight` | unset | `true`: the snapshot's arc weights; an edge column name: that numeric column; unset / `false` / `null`: every edge weighs 1 |
145
145
  | `dissuadeHubs` | `false` | accepted and ignored |
146
- | `settleThreshold`, `settleWindow` | `0.001`, `10` | settled when the mean displacement stayed below the threshold for `settleWindow` iterations |
146
+ | `settleThreshold`, `settleWindow` | `0.001`, `10` | settled when the mean displacement stayed below `settleThreshold x rmsRadius` AND below an absolute floor (a fraction of the model's length unit: 0.003 `springLength` x (2000 / n)^(1/4) for spring-electrical, 0.002 `k` for Fruchterman-Reingold; ForceAtlas2 has none) for `settleWindow` iterations |
147
147
  | `iterationsPerStep` | `1` | iterations per `step()` (the Node `run()` uses its own `batch`) |
148
148
  | `maxInFlight` | `2` | batches in flight before `step()` coalesces; `1` for the strictest freshness |
149
149
 
@@ -692,22 +692,22 @@ are the T-table of plan section 10.4. A missed target is re-fixed by a recorded
692
692
 
693
693
  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. The T-5 (100k), T-6 and T-7 rows are from the later session 2026-09-21T06:15:17.027Z (the file's last session, the current baseline, the one the crossover re-check reads; its Chromium session 2026-09-21T05:48:14.190Z), whose other rows are within 1.25x of this table's.
694
694
 
695
- | Id | What | Target | Measured |
696
- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
697
- | 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 |
698
- | T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 0.870 ms |
699
- | T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 0.181 ms |
700
- | T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.586 ms; 1.053 ms |
701
- | 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) |
702
- | T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 100k on the grid tier, Chromium (Node in brackets) | <= 12 ms | 3.700 ms (2.087 ms) |
703
- | T-6 | ForceAtlas2 grid tier, GPU time per iteration (profiler) at 100k 2D; at 1M 2D; at 100k 3D | <= 10 ms; <= 100 ms; <= 20 ms | 0.634 ms; 5.414 ms; 1.306 ms |
704
- | T-7 | Attraction gather (the `fa2-attraction` pass of the grid tier), GPU time per iteration (profiler) at 1M / 10M | <= 15 ms | 1.493 ms |
705
- | 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 |
706
- | 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) |
707
- | T-14 | Fruchterman-Reingold exact tier, GPU time per iteration (profiler) at 10k; at 100k (`repulsion: "exact"`) | recorded | 0.617 ms; 16.367 ms |
708
- | T-10 | BFS direction-optimizing (`breadthFirstSearch`), wall end to end including the upload, from node 0 of the undirected RMAT at 1M / 10M (577,681 nodes reached, 6 levels; 100k / 1M in brackets; top-down 233.064 ms / 97.259 ms) | <= 100 ms | 237.388 ms (101.183 ms) in the baseline session; 156.560 ms (16.731 ms) re-measured 2026-09-25 under load after the direct-dispatch change, top-down 88.606 ms -- see below and G8-F5 / G8-F21 of `docs/decisions/G8.md` |
709
- | T-10 | BFS on the 1000 x 1000 grid from a corner (1,999 levels; 64 `mapAsync` calls = ceil(levels / 32) + 1), wall including the upload | <= 1.5 s | 5680.192 ms in the baseline session; 427.235 ms re-measured 2026-09-25 under load after the direct-dispatch change, MET -- see below and G8-F5 of `docs/decisions/G8.md` |
710
- | T-10 | `sssp` (the near-far queue), wall end to end including the upload, random f32 weights in [0.1, 10), at 1M / 10M (100k / 1M in brackets) | recorded | 310.351 ms (118.064 ms); 271.040 ms (50.871 ms) re-measured 2026-09-25 under load |
695
+ | Id | What | Target | Measured |
696
+ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
697
+ | 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 |
698
+ | T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 0.870 ms |
699
+ | T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 0.181 ms |
700
+ | T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.586 ms; 1.053 ms |
701
+ | 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) |
702
+ | T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 100k on the grid tier, Chromium (Node in brackets) | <= 12 ms | 3.700 ms (2.087 ms) |
703
+ | T-6 | ForceAtlas2 grid tier, GPU time per iteration (profiler) at 100k 2D; at 1M 2D; at 100k 3D | <= 10 ms; <= 100 ms; <= 20 ms | 0.634 ms; 5.414 ms; 1.306 ms |
704
+ | T-7 | Attraction gather (the `fa2-attraction` pass of the grid tier), GPU time per iteration (profiler) at 1M / 10M | <= 15 ms | 1.493 ms |
705
+ | 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 |
706
+ | 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) |
707
+ | T-14 | Fruchterman-Reingold exact tier, GPU time per iteration (profiler) at 10k; at 100k (`repulsion: "exact"`) | recorded | 0.617 ms; 16.367 ms |
708
+ | T-10 | BFS direction-optimizing (`breadthFirstSearch`), wall end to end including the upload, from node 0 of the undirected RMAT at 1M / 10M (577,681 nodes reached, 6 levels; 100k / 1M in brackets; top-down 233.064 ms / 97.259 ms) | <= 100 ms | 237.388 ms (101.183 ms) in the baseline session; 89.212 ms (14.925 ms) re-measured 2026-09-25 at load average 7.3 after the direct-dispatch change and the m_f fix of issue #391, top-down 95.014 ms (14.744 ms); a sample one minute earlier at load average 10.5 read 134.674 ms (15.183 ms) auto and 99.002 ms (14.630 ms) top-down -- see below and G8-F5 / G8-F21 of `docs/decisions/G8.md` |
709
+ | T-10 | BFS on the 1000 x 1000 grid from a corner (1,999 levels; 64 `mapAsync` calls = ceil(levels / 32) + 1), wall including the upload | <= 1.5 s | 5680.192 ms in the baseline session; 427.235 ms re-measured 2026-09-25 under load after the direct-dispatch change, 542.370 ms (536.980 ms one minute earlier) after the m_f fix at load average 7.3-10.5, 382.680 ms at load average 2.5 once `bfs-next-degree` took a capped grid, MET -- see below and G8-F5 of `docs/decisions/G8.md` |
710
+ | T-10 | `sssp` (the near-far queue), wall end to end including the upload, random f32 weights in [0.1, 10), at 1M / 10M (100k / 1M in brackets) | recorded | 310.351 ms (118.064 ms); 271.040 ms (50.871 ms) re-measured 2026-09-25 under load; 275.474 ms (48.326 ms) after the m_f fix at load average 7.3 |
711
711
 
712
712
  Three rows miss their target in this session: the 1M / 10M upload (125.7 ms against 100 ms, the open owner decision of
713
713
  `docs/decisions/G1.md` section 7), the empty-submit round trip (0.181 ms against 0.1 ms: the row is measured after the
@@ -741,6 +741,27 @@ to the baseline): a 34-node BFS 7.2 ms (from 89.3 with the core resident), the g
741
741
  auto row 156.6 ms and the top-down row 88.6 ms. The auto row now misses only because the direction-optimizing
742
742
  choice itself is slower than top-down on that graph, which the validation cost had hidden (G8-F21).
743
743
 
744
+ That miss was not the direction choice either (G8-F21, closed by issue #391). The 156.6 against 88.6 ms gap was the
745
+ card clocking down to P5 and then ramping back up: the benchmark runs the auto row first, so whichever row runs
746
+ first carries the ramp, and alternating the two directions end to end reads the same 75 ms from the third iteration
747
+ on. The upload cannot explain it, being paid by both rows alike. The real defect the issue uncovered was Beamer's m_f
748
+ measured one level stale -- `frontierDegreeSum` is the degree of the frontier the PREVIOUS level expanded, which is
749
+ 0 after a bottom-up level -- so the switch into bottom-up was missed at the level holding 13.6M of the 21M arcs. The
750
+ `bfs-next-degree` kernel now sums the out-degree of the frontier about to be expanded (counters word 25) and the
751
+ boundary compares that. With the core resident on the card the 1M / 10M auto traversal went from 37.8 to 31.4 ms
752
+ and 100k / 1M from 10.3 to 8.8 ms; the three bottom-up levels of the 1M / 10M run read 649,743 arcs in total. At
753
+ 100k / 1M the direction-optimizing traversal still loses to plain top-down on this card, 9.55 against 9.34 ms
754
+ resident, which is a smaller loss than the 10.87 against 9.91 ms it read before the fix, and still a loss. The
755
+ new kernel is dispatched on every level, so a high-diameter traversal pays its fixed cost per level: the grid
756
+ traversal resident on the card went 339 -> 421 ms with the full-width grid `planGridStride(n, wg)` gives it and
757
+ back to 356 ms with the grid capped at 128 workgroups (`NEXT_DEGREE_MAX_GROUPS`), which is still enough to sum n
758
+ out-degrees by grid stride; neither R-MAT row moved. The
759
+ rows in the table (`--no-save --runs 3`, not appended to the baseline; both samples on 2026-09-25, one minute
760
+ apart): at load average 7.3 the 1M / 10M auto row is 89.2 ms against 95.0 ms top-down, the 100k / 1M row 14.9
761
+ against 14.7 ms, the grid row 542 ms and `sssp` 275.5 ms (48.3 ms); at load average 10.5 the same auto row read
762
+ 134.7 ms and top-down 99.0 ms. The two samples ran the same code on the same card, so the 100 ms target is met or
763
+ missed by the upload and the clock state the row starts in, not by the traversal.
764
+
744
765
  The exact curve (the `layout-exact` group: 2D, E = 10n, seeded G(n, m), one simulation per rung; ms / iteration from the profiler):
745
766
 
746
767
  | n | ms / iteration | step(1) wall (ms) | pairs / s |
package/dist/browser.js CHANGED
@@ -1,4 +1,4 @@
1
- import { G as GpuContext, W as WebGpuGraphError } from "./chunks/context-hzGggHeM.js";
1
+ import { G as GpuContext, W as WebGpuGraphError } from "./chunks/context-Cezi7qpi.js";
2
2
  function navigatorGpu() {
3
3
  if (typeof navigator === "undefined") {
4
4
  return void 0;
@@ -49,6 +49,7 @@ const LAYOUT_TUNING_DEFAULTS = Object.freeze({
49
49
  const FA2_DISTANCE_FLOOR = 0.01;
50
50
  const FA2_DISTANCE_FLOOR_SQ = 1e-4;
51
51
  const FA2_COINCIDENT_SQ = 1e-8;
52
+ const EXACT_TILES_PER_PASS = 128;
52
53
  const FA2_FLAG_FIRST = 1;
53
54
  const FA2_FLAG_ADAPTIVE = 2;
54
55
  const FR_START_TEMPERATURE = 0.1;
@@ -82,6 +83,14 @@ const SE_DEFAULTS = Object.freeze({
82
83
  iterationsPerStep: 1,
83
84
  maxInFlight: 2
84
85
  });
86
+ const SETTLE_FLOOR_FRACTION = Object.freeze(
87
+ {
88
+ springElectrical: 3e-3,
89
+ fruchtermanReingold: 2e-3
90
+ }
91
+ );
92
+ const SETTLE_FLOOR_REFERENCE_NODES = 2e3;
93
+ const SETTLE_FLOOR_UNBOUNDED = 2 ** 128 - 2 ** 104;
85
94
  const GRID_MIN_SIDE = 8;
86
95
  const GRID_COARSEST_SIDE = 4;
87
96
  const GRID_HUB_CELL = 1024;
@@ -733,7 +742,7 @@ class Kernel {
733
742
  return bound;
734
743
  }
735
744
  /**
736
- * setPipeline + setBindGroup for every group (dynamic offsets in dynamicGroups order) + dispatchWorkgroups(plan.x, plan.y, 1); a plan with x === 0 records nothing (spec 5.6).
745
+ * setPipeline + setBindGroup for every group (dynamic offsets in dynamicGroups order) + dispatchWorkgroups(plan.x, plan.y, plan.z); a plan with x === 0 records nothing (spec 5.6).
737
746
  * PLAN DECISION: one dynamic offset per dynamic GROUP, replicated over every uniform binding of that group (every
738
747
  * P1-P3 kernel has exactly one params uniform per group); absent offsets mean 0; a BoundKernel of another kernel
739
748
  * or an offset list of the wrong length is E_INVALID_ARGUMENT.
@@ -751,7 +760,7 @@ class Kernel {
751
760
  return;
752
761
  }
753
762
  this.setUp(pass, bound, dynamicOffsets);
754
- pass.dispatchWorkgroups(plan.x, plan.y, 1);
763
+ pass.dispatchWorkgroups(plan.x, plan.y, plan.z);
755
764
  }
756
765
  /**
757
766
  * setPipeline + the bind groups exactly as dispatch(), then dispatchWorkgroupsIndirect(args.buffer, args.offset +
@@ -890,6 +899,7 @@ const INVALID_INDEX: u32 = ${INVALID_INDEX}u;
890
899
  const U32_MAX: u32 = ${U32_MAX}u;
891
900
  const F32_INF_BITS: u32 = ${F32_INF_BITS}u;
892
901
  const MAX_WORKGROUPS_PER_DIM: u32 = ${MAX_WORKGROUPS_PER_DIM}u;
902
+ const EXACT_TILES_PER_PASS: u32 = ${EXACT_TILES_PER_PASS}u;
893
903
  const FA2_DIST_FLOOR: f32 = ${wgslF32Literal(FA2_DISTANCE_FLOOR)};
894
904
  const FA2_DIST_FLOOR_SQ: f32 = ${wgslF32Literal(FA2_DISTANCE_FLOOR_SQ)};
895
905
  const FA2_COINCIDENT_SQ: f32 = ${wgslF32Literal(FA2_COINCIDENT_SQ)};
@@ -2786,7 +2796,9 @@ class GraphResidency {
2786
2796
  });
2787
2797
  }
2788
2798
  /**
2789
- * One resident per array (spec 4.3: views upload in perArray mode, never into the arena).
2799
+ * One resident per array (spec 4.3: views upload in perArray mode, never into the arena). An empty array (the
2800
+ * colIdx of an edgeless directed reverse view, the src / dst of an edgeless edgeList) is skipped: spec 5.6 never
2801
+ * uploads a zero-length array, and Kernel.bind rejects a zero-size binding, so it is absent as in core().
2790
2802
  * @param record - the owning record
2791
2803
  * @param arrays - the named arrays
2792
2804
  * @param label - the buffer label prefix
@@ -2795,6 +2807,9 @@ class GraphResidency {
2795
2807
  separateArrays(record, arrays, label) {
2796
2808
  const bindings = {};
2797
2809
  for (const [name, array] of arrays) {
2810
+ if (array.byteLength === 0) {
2811
+ continue;
2812
+ }
2798
2813
  const resident = this.upload(record, array, array, `${label}:${name}`);
2799
2814
  bindings[name] = { buffer: resident.buffer, offset: 0, size: resident.byteLength, window: null };
2800
2815
  }
@@ -2808,14 +2823,19 @@ class GraphResidency {
2808
2823
  * lengths, so keying the packed buffer on `rev.rowPtr` would make the packed and the unpacked view of one
2809
2824
  * snapshot collide -- whichever was built second would get the other's buffer. The record still owns the
2810
2825
  * resident, so release(s) destroys it with the rest. Offsets are STORAGE_ALIGN-aligned because Kernel.bind
2811
- * rejects any other offset synchronously (E_INVALID_ARGUMENT { argument: "offset" }).
2826
+ * rejects any other offset synchronously (E_INVALID_ARGUMENT { argument: "offset" }). Empty arrays are left
2827
+ * out as in separateArrays; when nothing is left, nothing is uploaded.
2812
2828
  * @param record - the owning record
2813
- * @param arrays - the named arrays, in buffer order
2829
+ * @param all - the named arrays, in buffer order
2814
2830
  * @param key - the marker object the resident is keyed on
2815
2831
  * @param label - the buffer label
2816
2832
  * @returns the bindings by name, all into the one buffer
2817
2833
  */
2818
- packArrays(record, arrays, key, label) {
2834
+ packArrays(record, all, key, label) {
2835
+ const arrays = all.filter(([, array]) => array.byteLength > 0);
2836
+ if (arrays.length === 0) {
2837
+ return Object.freeze({});
2838
+ }
2819
2839
  const offsets = [];
2820
2840
  let total = 0;
2821
2841
  for (const [, array] of arrays) {
@@ -3483,19 +3503,23 @@ class GpuContext {
3483
3503
  }
3484
3504
  }
3485
3505
  export {
3486
- ARC_WINDOW_ALIGN as A,
3506
+ FR_DEFAULTS as A,
3487
3507
  BufferUsage as B,
3488
- PASSTHROUGH_FORMAT_CODES as C,
3489
- STORAGE_ALIGN as D,
3490
- EXACT_MAX_NODES as E,
3508
+ SE_DEFAULTS as C,
3509
+ SETTLE_FLOOR_REFERENCE_NODES as D,
3510
+ EXACT_TILES_PER_PASS as E,
3491
3511
  FUSED_FRONTIER_MAX as F,
3492
3512
  GpuContext as G,
3493
- WORKGROUP_SIZE as H,
3513
+ SE_SCALE_REFERENCE_NODES as H,
3494
3514
  INDIRECT_ARGS_STRIDE as I,
3495
- isSoftwareAdapter as J,
3515
+ ARC_WINDOW_ALIGN as J,
3516
+ PASSTHROUGH_FORMAT_CODES as K,
3496
3517
  LAYOUT_TUNING_DEFAULTS as L,
3497
3518
  MAX_WORKGROUPS_PER_DIM as M,
3519
+ STORAGE_ALIGN as N,
3520
+ WORKGROUP_SIZE as O,
3498
3521
  PARTIAL_BYTES as P,
3522
+ isSoftwareAdapter as Q,
3499
3523
  RADIX_BINS as R,
3500
3524
  STATE_HEADER_BYTES as S,
3501
3525
  TRACE_RECORD_BYTES as T,
@@ -3518,14 +3542,14 @@ export {
3518
3542
  hasErrorCode as o,
3519
3543
  FA2_FLAG_FIRST as p,
3520
3544
  GRID_HUB_CELL as q,
3521
- GRID_BBOX_MARGIN as r,
3522
- GRID_EXTENT_FLOOR as s,
3523
- FR_ADAPTIVE_MAX_ITERATIONS as t,
3524
- FR_START_TEMPERATURE as u,
3525
- FA2_FLAG_ADAPTIVE as v,
3526
- FR_REHEAT_FRACTION as w,
3527
- FR_DEFAULTS as x,
3528
- SE_DEFAULTS as y,
3529
- SE_SCALE_REFERENCE_NODES as z
3545
+ EXACT_MAX_NODES as r,
3546
+ SETTLE_FLOOR_UNBOUNDED as s,
3547
+ GRID_BBOX_MARGIN as t,
3548
+ GRID_EXTENT_FLOOR as u,
3549
+ FR_ADAPTIVE_MAX_ITERATIONS as v,
3550
+ FR_START_TEMPERATURE as w,
3551
+ FA2_FLAG_ADAPTIVE as x,
3552
+ SETTLE_FLOOR_FRACTION as y,
3553
+ FR_REHEAT_FRACTION as z
3530
3554
  };
3531
- //# sourceMappingURL=context-hzGggHeM.js.map
3555
+ //# sourceMappingURL=context-Cezi7qpi.js.map