@graphty/webgpu-graph-algorithms 0.6.15 → 0.6.17

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 +4 -4
  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
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The `group-by-key-row` kernel body (design 8.6; the per-row group-by-key): for every listed row `v`, the arcs of
3
+ * the row are grouped by the key of their target (`keyIn[colIdx[a]]`), the weights are summed per key, and the key
4
+ * with the largest sum wins, ties going to the LOWEST key -- which makes the answer independent of the order the arcs
5
+ * are visited in, and so bitwise reproducible. `bestKey[v]` gets the key (`INVALID_INDEX` for an empty row) and
6
+ * `bestScore[v]` the summed weight.
7
+ *
8
+ * The sums are u32 fixed point, because WGSL has no float atomic: every weight is scaled by `2^s`, a power of two
9
+ * chosen from the exponents of the row's largest weight and of its degree alone so that `maxWeight x degree x 2^s`
10
+ * lies in [2^28, 2^30), and rounded to the nearest integer, halves up. No step rounds a float: the scale needs no
11
+ * product, scaling by a power of two is exact, and so are the integer part and the fraction of the scaled weight.
12
+ * That matters because WGSL lets `x + y` and `x * y` round to EITHER neighbour of an inexact result, so a rounded
13
+ * step could differ between devices; as written, both tiers, every device and any CPU reference compute identical
14
+ * integers. A weight smaller than `2^-s / 2` contributes nothing, and a negative weight counts as zero. Without
15
+ * WEIGHTED every weight is 1.
16
+ *
17
+ * TIER 0: one thread per row (`rows[P.rowsBase + i]`), a pairwise scan in registers -- for short rows, and at most
18
+ * GROUP_ROW_THREAD_LIMIT arcs, since llvmpipe stops an invocation's loops after 65,535 steps. Any other TIER: one
19
+ * workgroup per row (`rows[P.rowsBase + g]`) over its own open-addressing region of `GROUP_HASH_LOAD_FACTOR x degree`
20
+ * slot pairs (key, sum) starting at word `rows[P.basesBase + g]` of `hashRegion`, cleared by the workgroup itself;
21
+ * a key is claimed by a bounded compare-exchange loop with linear probing, and a lane that exhausts its bound raises
22
+ * `hashRegion[0]`, which the driver reads and refuses. Every barrier is reached in uniform control flow: the only
23
+ * early return keys on the workgroup id and a uniform. Body only (spec 3.5, D9); normative text.
24
+ */
25
+ export const groupByKeyRowWgsl = /* wgsl */ `
26
+ var<workgroup> shMax: array<f32, WG>;
27
+ var<workgroup> shKey: array<u32, WG>;
28
+ var<workgroup> shSum: array<u32, WG>;
29
+
30
+ fn weight_of(a: u32) -> f32 {
31
+ if (WEIGHTED) { return weights[a]; }
32
+ return 1.0;
33
+ }
34
+ fn scale_of(maxW: f32, d: u32) -> f32 { // 2^s, maxW x d x 2^s in [2^28, 2^30), from exponents alone
35
+ let em = i32((bitcast<u32>(maxW) >> 23u) & 255u) - 127;
36
+ let ed = i32(firstLeadingBit(max(d, 1u)));
37
+ let s = clamp(28 - em - ed, -126, 126);
38
+ return bitcast<f32>(u32(s + 127) << 23u);
39
+ }
40
+ fn inverse_of(scale: f32) -> f32 { // 2^-s, exact
41
+ let s = i32((bitcast<u32>(scale) >> 23u) & 255u) - 127;
42
+ return bitcast<f32>(u32(127 - s) << 23u);
43
+ }
44
+ fn quantize(w: f32, scale: f32) -> u32 { // nearest integer, halves up; every step exact
45
+ let x = max(w * scale, 0.0);
46
+ let i = u32(x);
47
+ return select(i, i + 1u, x - f32(i) >= 0.5);
48
+ }
49
+ fn better(sum: u32, key: u32, bestSum: u32, bestKey0: u32) -> bool { return sum > bestSum || (sum == bestSum && key < bestKey0); }
50
+
51
+ fn row_thread(v: u32) {
52
+ let lo = rowPtr[v];
53
+ let hi = rowPtr[v + 1u];
54
+ var maxW = 0.0;
55
+ for (var a = lo; a < hi; a = a + 1u) { maxW = max(maxW, weight_of(a)); }
56
+ let scale = scale_of(maxW, hi - lo);
57
+ var bk = INVALID_INDEX;
58
+ var bs = 0u;
59
+ for (var a = lo; a < hi; a = a + 1u) {
60
+ let k = keyIn[colIdx[a]];
61
+ var seen = false;
62
+ for (var b = lo; b < a; b = b + 1u) { if (keyIn[colIdx[b]] == k) { seen = true; break; } }
63
+ if (seen) { continue; } // this key was summed at its first arc
64
+ var sum = 0u;
65
+ for (var b = a; b < hi; b = b + 1u) { if (keyIn[colIdx[b]] == k) { sum = sum + quantize(weight_of(b), scale); } }
66
+ if (better(sum, k, bs, bk)) { bk = k; bs = sum; }
67
+ }
68
+ bestKey[v] = bk;
69
+ bestScore[v] = f32(bs) * inverse_of(scale);
70
+ }
71
+
72
+ fn row_hash(g: u32, lid: u32) {
73
+ let v = rows[P.rowsBase + g];
74
+ let base = rows[P.basesBase + g];
75
+ let lo = rowPtr[v];
76
+ let hi = rowPtr[v + 1u];
77
+ let cap = GROUP_HASH_LOAD_FACTOR * (hi - lo);
78
+ var m = 0.0;
79
+ for (var a = lo + lid; a < hi; a = a + WG) { m = max(m, weight_of(a)); }
80
+ shMax[lid] = m;
81
+ workgroupBarrier();
82
+ for (var s = WG / 2u; s > 0u; s = s / 2u) {
83
+ if (lid < s) { shMax[lid] = max(shMax[lid], shMax[lid + s]); }
84
+ workgroupBarrier();
85
+ }
86
+ let scale = scale_of(shMax[0], hi - lo);
87
+ for (var j = lid; j < cap; j = j + WG) { // the region is this workgroup's alone: clear it
88
+ atomicStore(&hashRegion[base + 2u * j], INVALID_INDEX);
89
+ atomicStore(&hashRegion[base + 2u * j + 1u], 0u);
90
+ }
91
+ storageBarrier();
92
+ var exhausted = false;
93
+ for (var a = lo + lid; a < hi; a = a + WG) {
94
+ let k = keyIn[colIdx[a]];
95
+ let q = quantize(weight_of(a), scale);
96
+ var slot = lowbias32(k) % cap;
97
+ var steps = 0u;
98
+ loop {
99
+ if (steps >= cap + 64u) { exhausted = true; break; } // bounded: a spurious compare-exchange failure is legal (WGSL 17.8.5)
100
+ steps = steps + 1u;
101
+ let r = atomicCompareExchangeWeak(&hashRegion[base + 2u * slot], INVALID_INDEX, k);
102
+ if (r.exchanged || r.old_value == k) { atomicAdd(&hashRegion[base + 2u * slot + 1u], q); break; }
103
+ if (r.old_value == INVALID_INDEX) { continue; } // a spurious failure: the same slot again
104
+ slot = (slot + 1u) % cap; // linear probing
105
+ }
106
+ }
107
+ if (exhausted) { atomicStore(&hashRegion[0], 1u); }
108
+ storageBarrier();
109
+ var bk = INVALID_INDEX;
110
+ var bs = 0u;
111
+ for (var j = lid; j < cap; j = j + WG) {
112
+ let k = atomicLoad(&hashRegion[base + 2u * j]);
113
+ if (k != INVALID_INDEX) {
114
+ let sum = atomicLoad(&hashRegion[base + 2u * j + 1u]);
115
+ if (better(sum, k, bs, bk)) { bk = k; bs = sum; }
116
+ }
117
+ }
118
+ shKey[lid] = bk;
119
+ shSum[lid] = bs;
120
+ workgroupBarrier();
121
+ for (var s = WG / 2u; s > 0u; s = s / 2u) {
122
+ if (lid < s && better(shSum[lid + s], shKey[lid + s], shSum[lid], shKey[lid])) {
123
+ shKey[lid] = shKey[lid + s];
124
+ shSum[lid] = shSum[lid + s];
125
+ }
126
+ workgroupBarrier();
127
+ }
128
+ if (lid == 0u) {
129
+ bestKey[v] = shKey[0];
130
+ bestScore[v] = f32(shSum[0]) * inverse_of(scale);
131
+ }
132
+ }
133
+
134
+ @compute @workgroup_size(WG)
135
+ fn group_by_key_row(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
136
+ if (TIER == 0u) {
137
+ let i = linear_id(wid, lid.x);
138
+ if (i < P.count) { row_thread(rows[P.rowsBase + i]); }
139
+ return; // TIER is an override: uniform
140
+ }
141
+ let g = group_id(wid);
142
+ if (g >= P.count) { return; } // uniform: the workgroup id and a uniform
143
+ row_hash(g, lid.x);
144
+ }
145
+ `;
146
+ //# sourceMappingURL=group-by-key-row.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-by-key-row.wgsl.js","sourceRoot":"","sources":["../../../src/wgsl/group-by-key-row.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwH3C,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The `lpa-step` kernel body (design 8.6; label propagation): after `group-by-key-row` has written every vertex's
3
+ * best neighbour label into `bestKey`, a vertex adopts it only when the move goes the way the pass allows -- down to
4
+ * a lower label on a pass whose `P.direction` is 0, up to a higher one when it is 1. That is cuGraph's swap-avoidance
5
+ * rule: two neighbours can no longer trade labels forever, because on any one pass only one of the two moves is
6
+ * legal. Updates are synchronous (`labelsIn` read, `labelsOut` written) and the moves of a workgroup are added to
7
+ * `counters[P.counterIndex]` with ONE atomic per workgroup. Body only (spec 3.5, D9); normative text.
8
+ */
9
+ export declare const lpaStepWgsl = "\nvar<workgroup> moved: atomic<u32>;\n\n@compute @workgroup_size(WG)\nfn lpa_step(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n if (lid.x == 0u) { atomicStore(&moved, 0u); }\n workgroupBarrier();\n let v = linear_id(wid, lid.x);\n if (v < P.n) {\n let cur = labelsIn[v];\n let best = bestKey[v];\n var next = cur;\n if (best != INVALID_INDEX && best != cur) {\n let down = best < cur;\n if (down == (P.direction == 0u)) { next = best; } // the alternating direction rule\n }\n labelsOut[v] = next;\n if (next != cur) { atomicAdd(&moved, 1u); }\n }\n workgroupBarrier(); // every lane, unconditionally\n if (lid.x == 0u) {\n let m = atomicLoad(&moved);\n if (m > 0u) { atomicAdd(&counters[P.counterIndex], m); } // one atomic per workgroup\n }\n}\n";
10
+ //# sourceMappingURL=lpa-step.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lpa-step.wgsl.d.ts","sourceRoot":"","sources":["../../../src/wgsl/lpa-step.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,27BAyBvB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The `lpa-step` kernel body (design 8.6; label propagation): after `group-by-key-row` has written every vertex's
3
+ * best neighbour label into `bestKey`, a vertex adopts it only when the move goes the way the pass allows -- down to
4
+ * a lower label on a pass whose `P.direction` is 0, up to a higher one when it is 1. That is cuGraph's swap-avoidance
5
+ * rule: two neighbours can no longer trade labels forever, because on any one pass only one of the two moves is
6
+ * legal. Updates are synchronous (`labelsIn` read, `labelsOut` written) and the moves of a workgroup are added to
7
+ * `counters[P.counterIndex]` with ONE atomic per workgroup. Body only (spec 3.5, D9); normative text.
8
+ */
9
+ export const lpaStepWgsl = /* wgsl */ `
10
+ var<workgroup> moved: atomic<u32>;
11
+
12
+ @compute @workgroup_size(WG)
13
+ fn lpa_step(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
14
+ if (lid.x == 0u) { atomicStore(&moved, 0u); }
15
+ workgroupBarrier();
16
+ let v = linear_id(wid, lid.x);
17
+ if (v < P.n) {
18
+ let cur = labelsIn[v];
19
+ let best = bestKey[v];
20
+ var next = cur;
21
+ if (best != INVALID_INDEX && best != cur) {
22
+ let down = best < cur;
23
+ if (down == (P.direction == 0u)) { next = best; } // the alternating direction rule
24
+ }
25
+ labelsOut[v] = next;
26
+ if (next != cur) { atomicAdd(&moved, 1u); }
27
+ }
28
+ workgroupBarrier(); // every lane, unconditionally
29
+ if (lid.x == 0u) {
30
+ let m = atomicLoad(&moved);
31
+ if (m > 0u) { atomicAdd(&counters[P.counterIndex], m); } // one atomic per workgroup
32
+ }
33
+ }
34
+ `;
35
+ //# sourceMappingURL=lpa-step.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lpa-step.wgsl.js","sourceRoot":"","sources":["../../../src/wgsl/lpa-step.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;CAyBrC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The `orient-flags` kernel body (design 8.5; triangle counting): over the arcs of the simple symmetric graph, arc
3
+ * `(u, v)` is kept when `(degree(v), v)` is above `(degree(u), u)` lexicographically. The order is strict and total,
4
+ * so exactly one arc of every undirected edge survives and every triangle is found once, at its lowest-ranked vertex;
5
+ * the rows stay sorted by target because a compaction preserves order. `src` holds each arc's source. Body only
6
+ * (spec 3.5, D9); normative text.
7
+ */
8
+ export declare const orientFlagsWgsl = "\n@compute @workgroup_size(WG)\nfn orient_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let a = linear_id(wid, lid.x);\n if (a >= P.count) { return; } // no barrier follows\n let u = src[a];\n let v = colIdx[a];\n let du = rowPtr[u + 1u] - rowPtr[u];\n let dv = rowPtr[v + 1u] - rowPtr[v];\n let keep = dv > du || (dv == du && v > u); // (degree, id) of the target above the source's\n flags[a] = select(0u, 1u, keep);\n}\n";
9
+ //# sourceMappingURL=orient-flags.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orient-flags.wgsl.d.ts","sourceRoot":"","sources":["../../../src/wgsl/orient-flags.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,0iBAY3B,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The `orient-flags` kernel body (design 8.5; triangle counting): over the arcs of the simple symmetric graph, arc
3
+ * `(u, v)` is kept when `(degree(v), v)` is above `(degree(u), u)` lexicographically. The order is strict and total,
4
+ * so exactly one arc of every undirected edge survives and every triangle is found once, at its lowest-ranked vertex;
5
+ * the rows stay sorted by target because a compaction preserves order. `src` holds each arc's source. Body only
6
+ * (spec 3.5, D9); normative text.
7
+ */
8
+ export const orientFlagsWgsl = /* wgsl */ `
9
+ @compute @workgroup_size(WG)
10
+ fn orient_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
11
+ let a = linear_id(wid, lid.x);
12
+ if (a >= P.count) { return; } // no barrier follows
13
+ let u = src[a];
14
+ let v = colIdx[a];
15
+ let du = rowPtr[u + 1u] - rowPtr[u];
16
+ let dv = rowPtr[v + 1u] - rowPtr[v];
17
+ let keep = dv > du || (dv == du && v > u); // (degree, id) of the target above the source's
18
+ flags[a] = select(0u, 1u, keep);
19
+ }
20
+ `;
21
+ //# sourceMappingURL=orient-flags.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orient-flags.wgsl.js","sourceRoot":"","sources":["../../../src/wgsl/orient-flags.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;CAYzC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The `run-flags` kernel body (design 6 row 10; the simple symmetric graph build): over arcs sorted by the pair
3
+ * (`keysA`, `keysB`), `flags[i]` is 1 exactly when arc `i` opens a run of equal pairs -- the first arc, or one whose
4
+ * pair differs from its predecessor's -- and is not a dropped arc (`keysA[i] == INVALID_INDEX`). An exclusive scan
5
+ * of the flags numbers the runs, which is how parallel arcs merge into one. Body only (spec 3.5, D9); normative text.
6
+ */
7
+ export declare const runFlagsWgsl = "\n@compute @workgroup_size(WG)\nfn run_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let i = linear_id(wid, lid.x);\n if (i >= P.count) { return; } // no barrier follows\n let a = keysA[i];\n var first = a != INVALID_INDEX; // a dropped arc never opens a run\n if (i > 0u) { first = first && (a != keysA[i - 1u] || keysB[i] != keysB[i - 1u]); }\n flags[i] = select(0u, 1u, first);\n}\n";
8
+ //# sourceMappingURL=run-flags.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-flags.wgsl.d.ts","sourceRoot":"","sources":["../../../src/wgsl/run-flags.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,YAAY,ygBAUxB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The `run-flags` kernel body (design 6 row 10; the simple symmetric graph build): over arcs sorted by the pair
3
+ * (`keysA`, `keysB`), `flags[i]` is 1 exactly when arc `i` opens a run of equal pairs -- the first arc, or one whose
4
+ * pair differs from its predecessor's -- and is not a dropped arc (`keysA[i] == INVALID_INDEX`). An exclusive scan
5
+ * of the flags numbers the runs, which is how parallel arcs merge into one. Body only (spec 3.5, D9); normative text.
6
+ */
7
+ export const runFlagsWgsl = /* wgsl */ `
8
+ @compute @workgroup_size(WG)
9
+ fn run_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
10
+ let i = linear_id(wid, lid.x);
11
+ if (i >= P.count) { return; } // no barrier follows
12
+ let a = keysA[i];
13
+ var first = a != INVALID_INDEX; // a dropped arc never opens a run
14
+ if (i > 0u) { first = first && (a != keysA[i - 1u] || keysB[i] != keysB[i - 1u]); }
15
+ flags[i] = select(0u, 1u, first);
16
+ }
17
+ `;
18
+ //# sourceMappingURL=run-flags.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-flags.wgsl.js","sourceRoot":"","sources":["../../../src/wgsl/run-flags.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;;;;;;;;;;CAUtC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The `tri-intersect` kernel body (design 8.5): one invocation per ORIENTED arc `(u, v)`; every common target `w` of
3
+ * the oriented rows of `u` and `v` closes a triangle, found exactly once. The rows are sorted by target, so they are
4
+ * intersected by a merge, or -- when one row is more than TRIANGLE_BINARY_SEARCH_RATIO times the other -- by a binary
5
+ * search of each element of the shorter row in the longer. SEARCH forces a path: 0 chooses, 1 always merges, 2
6
+ * always searches (the tier-agreement tests compare the two). `counts` gains one per triangle at `w` as it is found
7
+ * and the arc's triangle count at `u` and `v` once at the end: u32 atomics, so the counts are exact and
8
+ * order-independent. Body only (spec 3.5, D9); normative text.
9
+ */
10
+ export declare const triIntersectWgsl = "\nfn contains(lo0: u32, hi0: u32, x: u32) -> bool { // binary search of x in colIdx[lo0, hi0), sorted ascending\n var lo = lo0;\n var hi = hi0;\n loop {\n if (lo >= hi) { break; }\n let mid = lo + (hi - lo) / 2u;\n let y = colIdx[mid];\n if (y == x) { return true; }\n if (y < x) { lo = mid + 1u; } else { hi = mid; }\n }\n return false;\n}\n\n@compute @workgroup_size(WG)\nfn tri_intersect(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let a = linear_id(wid, lid.x);\n if (a >= P.count) { return; } // no barrier follows\n let u = src[a];\n let v = colIdx[a];\n let uLo = rowPtr[u];\n let uHi = rowPtr[u + 1u];\n let vLo = rowPtr[v];\n let vHi = rowPtr[v + 1u];\n let du = uHi - uLo;\n let dv = vHi - vLo;\n var search = SEARCH == 2u;\n if (SEARCH == 0u) { search = du / TRIANGLE_BINARY_SEARCH_RATIO > dv || dv / TRIANGLE_BINARY_SEARCH_RATIO > du; }\n var found = 0u;\n if (search) {\n var sLo = uLo;\n var sHi = uHi;\n var lLo = vLo;\n var lHi = vHi;\n if (du > dv) { sLo = vLo; sHi = vHi; lLo = uLo; lHi = uHi; }\n for (var k = sLo; k < sHi; k = k + 1u) {\n let w = colIdx[k];\n if (contains(lLo, lHi, w)) { atomicAdd(&counts[w], 1u); found = found + 1u; }\n }\n } else {\n var i = uLo;\n var j = vLo;\n loop {\n if (i >= uHi || j >= vHi) { break; }\n let x = colIdx[i];\n let y = colIdx[j];\n if (x == y) { atomicAdd(&counts[x], 1u); found = found + 1u; i = i + 1u; j = j + 1u; }\n else if (x < y) { i = i + 1u; }\n else { j = j + 1u; }\n }\n }\n if (found > 0u) { atomicAdd(&counts[u], found); atomicAdd(&counts[v], found); }\n}\n";
11
+ //# sourceMappingURL=tri-intersect.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tri-intersect.wgsl.d.ts","sourceRoot":"","sources":["../../../src/wgsl/tri-intersect.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,22DAqD5B,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The `tri-intersect` kernel body (design 8.5): one invocation per ORIENTED arc `(u, v)`; every common target `w` of
3
+ * the oriented rows of `u` and `v` closes a triangle, found exactly once. The rows are sorted by target, so they are
4
+ * intersected by a merge, or -- when one row is more than TRIANGLE_BINARY_SEARCH_RATIO times the other -- by a binary
5
+ * search of each element of the shorter row in the longer. SEARCH forces a path: 0 chooses, 1 always merges, 2
6
+ * always searches (the tier-agreement tests compare the two). `counts` gains one per triangle at `w` as it is found
7
+ * and the arc's triangle count at `u` and `v` once at the end: u32 atomics, so the counts are exact and
8
+ * order-independent. Body only (spec 3.5, D9); normative text.
9
+ */
10
+ export const triIntersectWgsl = /* wgsl */ `
11
+ fn contains(lo0: u32, hi0: u32, x: u32) -> bool { // binary search of x in colIdx[lo0, hi0), sorted ascending
12
+ var lo = lo0;
13
+ var hi = hi0;
14
+ loop {
15
+ if (lo >= hi) { break; }
16
+ let mid = lo + (hi - lo) / 2u;
17
+ let y = colIdx[mid];
18
+ if (y == x) { return true; }
19
+ if (y < x) { lo = mid + 1u; } else { hi = mid; }
20
+ }
21
+ return false;
22
+ }
23
+
24
+ @compute @workgroup_size(WG)
25
+ fn tri_intersect(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
26
+ let a = linear_id(wid, lid.x);
27
+ if (a >= P.count) { return; } // no barrier follows
28
+ let u = src[a];
29
+ let v = colIdx[a];
30
+ let uLo = rowPtr[u];
31
+ let uHi = rowPtr[u + 1u];
32
+ let vLo = rowPtr[v];
33
+ let vHi = rowPtr[v + 1u];
34
+ let du = uHi - uLo;
35
+ let dv = vHi - vLo;
36
+ var search = SEARCH == 2u;
37
+ if (SEARCH == 0u) { search = du / TRIANGLE_BINARY_SEARCH_RATIO > dv || dv / TRIANGLE_BINARY_SEARCH_RATIO > du; }
38
+ var found = 0u;
39
+ if (search) {
40
+ var sLo = uLo;
41
+ var sHi = uHi;
42
+ var lLo = vLo;
43
+ var lHi = vHi;
44
+ if (du > dv) { sLo = vLo; sHi = vHi; lLo = uLo; lHi = uHi; }
45
+ for (var k = sLo; k < sHi; k = k + 1u) {
46
+ let w = colIdx[k];
47
+ if (contains(lLo, lHi, w)) { atomicAdd(&counts[w], 1u); found = found + 1u; }
48
+ }
49
+ } else {
50
+ var i = uLo;
51
+ var j = vLo;
52
+ loop {
53
+ if (i >= uHi || j >= vHi) { break; }
54
+ let x = colIdx[i];
55
+ let y = colIdx[j];
56
+ if (x == y) { atomicAdd(&counts[x], 1u); found = found + 1u; i = i + 1u; j = j + 1u; }
57
+ else if (x < y) { i = i + 1u; }
58
+ else { j = j + 1u; }
59
+ }
60
+ }
61
+ if (found > 0u) { atomicAdd(&counts[u], found); atomicAdd(&counts[v], found); }
62
+ }
63
+ `;
64
+ //# sourceMappingURL=tri-intersect.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tri-intersect.wgsl.js","sourceRoot":"","sources":["../../../src/wgsl/tri-intersect.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqD1C,CAAC"}