graphwise 1.9.1 → 1.11.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.
- package/dist/async/index.cjs +98 -1
- package/dist/async/index.cjs.map +1 -0
- package/dist/async/index.d.ts +1 -0
- package/dist/async/index.d.ts.map +1 -1
- package/dist/async/index.js +96 -2
- package/dist/async/index.js.map +1 -0
- package/dist/async/ops.d.ts +2 -0
- package/dist/async/ops.d.ts.map +1 -1
- package/dist/async/protocol.d.ts +14 -0
- package/dist/async/protocol.d.ts.map +1 -1
- package/dist/async/runner-batched.d.ts +21 -0
- package/dist/async/runner-batched.d.ts.map +1 -0
- package/dist/async/runner-batched.unit.test.d.ts +2 -0
- package/dist/async/runner-batched.unit.test.d.ts.map +1 -0
- package/dist/async/runners.d.ts.map +1 -1
- package/dist/expansion/base-core.d.ts.map +1 -1
- package/dist/expansion/fuse.d.ts +24 -1
- package/dist/expansion/fuse.d.ts.map +1 -1
- package/dist/expansion/index.cjs +9 -1
- package/dist/expansion/index.js +2 -2
- package/dist/expansion/lace.d.ts +23 -2
- package/dist/expansion/lace.d.ts.map +1 -1
- package/dist/expansion/priority-helpers.d.ts +20 -1
- package/dist/expansion/priority-helpers.d.ts.map +1 -1
- package/dist/expansion/sift.d.ts +24 -1
- package/dist/expansion/sift.d.ts.map +1 -1
- package/dist/expansion/types.d.ts +30 -0
- package/dist/expansion/types.d.ts.map +1 -1
- package/dist/{expansion-DaTroIyv.cjs → expansion--UuRowv-.cjs} +267 -4
- package/dist/expansion--UuRowv-.cjs.map +1 -0
- package/dist/{expansion-ClDhlMK8.js → expansion-CZLNK6Pr.js} +220 -5
- package/dist/expansion-CZLNK6Pr.js.map +1 -0
- package/dist/gpu/csr-graph.d.ts +68 -0
- package/dist/gpu/csr-graph.d.ts.map +1 -0
- package/dist/gpu/csr-graph.unit.test.d.ts +2 -0
- package/dist/gpu/csr-graph.unit.test.d.ts.map +1 -0
- package/dist/gpu/index.cjs +220 -15
- package/dist/gpu/index.cjs.map +1 -0
- package/dist/gpu/index.d.ts +1 -0
- package/dist/gpu/index.d.ts.map +1 -1
- package/dist/gpu/index.js +204 -2
- package/dist/gpu/index.js.map +1 -0
- package/dist/gpu/kernels/adamic-adar/kernel.d.ts +39 -0
- package/dist/gpu/kernels/adamic-adar/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/intersection/kernel.d.ts +50 -0
- package/dist/gpu/kernels/intersection/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/intersection/logic.d.ts +87 -0
- package/dist/gpu/kernels/intersection/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/intersection/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/intersection/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/kmeans/index.d.ts +6 -0
- package/dist/gpu/kernels/kmeans/index.d.ts.map +1 -0
- package/dist/gpu/kernels/kmeans/kernel.d.ts +34 -0
- package/dist/gpu/kernels/kmeans/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/kmeans/logic.d.ts +111 -0
- package/dist/gpu/kernels/kmeans/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/kmeans/logic.unit.test.d.ts +5 -0
- package/dist/gpu/kernels/kmeans/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/operations.d.ts +52 -0
- package/dist/gpu/operations.d.ts.map +1 -1
- package/dist/index/index.cjs +42 -19
- package/dist/index/index.js +11 -9
- package/dist/{jaccard-Bys9_dGW.cjs → jaccard-Bdw4B0i4.cjs} +1 -1
- package/dist/{jaccard-Bys9_dGW.cjs.map → jaccard-Bdw4B0i4.cjs.map} +1 -1
- package/dist/{jaccard-3rCdilwm.js → jaccard-BwC_NuQu.js} +1 -1
- package/dist/{jaccard-3rCdilwm.js.map → jaccard-BwC_NuQu.js.map} +1 -1
- package/dist/kernel-2oH4Cn32.cjs +1001 -0
- package/dist/kernel-2oH4Cn32.cjs.map +1 -0
- package/dist/kernel-6deK9fh1.js +724 -0
- package/dist/kernel-6deK9fh1.js.map +1 -0
- package/dist/kernel-CXeGBH3s.cjs +467 -0
- package/dist/kernel-CXeGBH3s.cjs.map +1 -0
- package/dist/kernel-CigCjrts.js +467 -0
- package/dist/kernel-CigCjrts.js.map +1 -0
- package/dist/kernel-CvnRsF7E.js +1001 -0
- package/dist/kernel-CvnRsF7E.js.map +1 -0
- package/dist/kernel-DukrXtVb.cjs +724 -0
- package/dist/kernel-DukrXtVb.cjs.map +1 -0
- package/dist/{kmeans-B8x9D1kt.cjs → kmeans-CZ7tJFYw.cjs} +1 -1
- package/dist/{kmeans-B8x9D1kt.cjs.map → kmeans-CZ7tJFYw.cjs.map} +1 -1
- package/dist/{kmeans-DKkL9rAN.js → kmeans-DLrlrp6i.js} +1 -1
- package/dist/{kmeans-DKkL9rAN.js.map → kmeans-DLrlrp6i.js.map} +1 -1
- package/dist/logic-Dbyfb_-7.cjs +289 -0
- package/dist/logic-Dbyfb_-7.cjs.map +1 -0
- package/dist/logic-DyBzRg1A.js +242 -0
- package/dist/logic-DyBzRg1A.js.map +1 -0
- package/dist/operations-D-RB67WP.cjs +2269 -0
- package/dist/operations-D-RB67WP.cjs.map +1 -0
- package/dist/operations-D9otVlIH.js +2198 -0
- package/dist/operations-D9otVlIH.js.map +1 -0
- package/dist/{ops-upIi6JIi.js → ops-D5xZr4fV.js} +60 -2
- package/dist/ops-D5xZr4fV.js.map +1 -0
- package/dist/{ops-djAsQQSh.cjs → ops-paa1Nvlf.cjs} +71 -1
- package/dist/ops-paa1Nvlf.cjs.map +1 -0
- package/dist/ranking/baselines/communicability.d.ts +12 -0
- package/dist/ranking/baselines/communicability.d.ts.map +1 -1
- package/dist/ranking/baselines/katz.d.ts +12 -0
- package/dist/ranking/baselines/katz.d.ts.map +1 -1
- package/dist/ranking/baselines/pagerank.d.ts +15 -0
- package/dist/ranking/baselines/pagerank.d.ts.map +1 -1
- package/dist/ranking/baselines/types.d.ts +3 -0
- package/dist/ranking/baselines/types.d.ts.map +1 -1
- package/dist/ranking/index.cjs +5 -2
- package/dist/ranking/index.js +3 -3
- package/dist/ranking/mi/index.cjs +1 -1
- package/dist/ranking/mi/index.js +1 -1
- package/dist/ranking/parse-gpu.d.ts +31 -0
- package/dist/ranking/parse-gpu.d.ts.map +1 -0
- package/dist/ranking/parse-gpu.unit.test.d.ts +5 -0
- package/dist/ranking/parse-gpu.unit.test.d.ts.map +1 -0
- package/dist/ranking/parse.d.ts.map +1 -1
- package/dist/{ranking-3ez5m67U.js → ranking-DOKDBcIR.js} +237 -11
- package/dist/ranking-DOKDBcIR.js.map +1 -0
- package/dist/{ranking-DVvajgUZ.cjs → ranking-pe5UaxKg.cjs} +254 -10
- package/dist/ranking-pe5UaxKg.cjs.map +1 -0
- package/dist/schemas/graph.d.ts +1 -1
- package/dist/seeds/crest.d.ts +48 -0
- package/dist/seeds/crest.d.ts.map +1 -0
- package/dist/seeds/crest.unit.test.d.ts +2 -0
- package/dist/seeds/crest.unit.test.d.ts.map +1 -0
- package/dist/seeds/crisp.d.ts +57 -0
- package/dist/seeds/crisp.d.ts.map +1 -0
- package/dist/seeds/crisp.unit.test.d.ts +2 -0
- package/dist/seeds/crisp.unit.test.d.ts.map +1 -0
- package/dist/seeds/grasp-gpu.d.ts +40 -0
- package/dist/seeds/grasp-gpu.d.ts.map +1 -0
- package/dist/seeds/index.cjs +715 -5
- package/dist/seeds/index.cjs.map +1 -1
- package/dist/seeds/index.d.ts +4 -0
- package/dist/seeds/index.d.ts.map +1 -1
- package/dist/seeds/index.js +712 -6
- package/dist/seeds/index.js.map +1 -1
- package/dist/seeds/spine.d.ts +50 -0
- package/dist/seeds/spine.d.ts.map +1 -0
- package/dist/seeds/spine.unit.test.d.ts +2 -0
- package/dist/seeds/spine.unit.test.d.ts.map +1 -0
- package/dist/seeds/stride.d.ts +55 -0
- package/dist/seeds/stride.d.ts.map +1 -0
- package/dist/seeds/stride.unit.test.d.ts +2 -0
- package/dist/seeds/stride.unit.test.d.ts.map +1 -0
- package/dist/{gpu-CHiCN0wa.js → typegpu-Dq5FfUB8.cjs} +16 -2041
- package/dist/typegpu-Dq5FfUB8.cjs.map +1 -0
- package/dist/{gpu-Y6owRVMi.cjs → typegpu-DwnJf28i.js} +2 -2127
- package/dist/typegpu-DwnJf28i.js.map +1 -0
- package/dist/utils/index.cjs +1 -1
- package/dist/utils/index.js +1 -1
- package/package.json +1 -1
- package/dist/expansion-ClDhlMK8.js.map +0 -1
- package/dist/expansion-DaTroIyv.cjs.map +0 -1
- package/dist/gpu-CHiCN0wa.js.map +0 -1
- package/dist/gpu-Y6owRVMi.cjs.map +0 -1
- package/dist/ops-djAsQQSh.cjs.map +0 -1
- package/dist/ops-upIi6JIi.js.map +0 -1
- package/dist/ranking-3ez5m67U.js.map +0 -1
- package/dist/ranking-DVvajgUZ.cjs.map +0 -1
|
@@ -0,0 +1,2198 @@
|
|
|
1
|
+
import { n as data_exports, t as src_default } from "./typegpu-DwnJf28i.js";
|
|
2
|
+
import { a as overlapFromIntersection, c as graphToCSR, i as jaccardFromIntersection, n as hubPromotedFromIntersection, o as sorensenDiceFromIntersection, r as intersectionBatch, s as csrToTypedBuffers, t as cosineFromIntersection } from "./logic-DyBzRg1A.js";
|
|
3
|
+
//#region src/gpu/detect.ts
|
|
4
|
+
/**
|
|
5
|
+
* Detect WebGPU availability in the current environment.
|
|
6
|
+
*
|
|
7
|
+
* Checks for:
|
|
8
|
+
* - Browser: navigator.gpu
|
|
9
|
+
* - Node.js: global GPU constructor (Node.js 21+ with --experimental-webgpu)
|
|
10
|
+
* - Deno: global GPU constructor
|
|
11
|
+
*
|
|
12
|
+
* @returns Detection result with availability status and reason
|
|
13
|
+
*/
|
|
14
|
+
function detectWebGPU() {
|
|
15
|
+
if (typeof navigator !== "undefined" && "gpu" in navigator) return { available: true };
|
|
16
|
+
if (typeof globalThis !== "undefined" && "GPU" in globalThis) return { available: true };
|
|
17
|
+
const reasons = [];
|
|
18
|
+
if (typeof navigator === "undefined" && typeof globalThis === "undefined") reasons.push("no global scope detected");
|
|
19
|
+
else if (typeof navigator !== "undefined" && !("gpu" in navigator)) reasons.push("navigator.gpu not present (browser may not support WebGPU)");
|
|
20
|
+
else if (typeof globalThis !== "undefined" && !("GPU" in globalThis)) reasons.push("global GPU not present (Node.js requires v21+ with --experimental-webgpu flag)");
|
|
21
|
+
return {
|
|
22
|
+
available: false,
|
|
23
|
+
reason: reasons.length > 0 ? reasons.join("; ") : "unknown environment"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if WebGPU is available (convenience function).
|
|
28
|
+
*
|
|
29
|
+
* @returns true if WebGPU is available, false otherwise
|
|
30
|
+
*/
|
|
31
|
+
function isWebGPUAvailable() {
|
|
32
|
+
return detectWebGPU().available;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Assert that WebGPU is available, throwing an error if not.
|
|
36
|
+
*
|
|
37
|
+
* @throws Error if WebGPU is not available
|
|
38
|
+
*/
|
|
39
|
+
function assertWebGPUAvailable() {
|
|
40
|
+
const result = detectWebGPU();
|
|
41
|
+
if (!result.available) throw new Error(`WebGPU required but not available: ${result.reason ?? "unknown reason"}`);
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/gpu/types.ts
|
|
45
|
+
/**
|
|
46
|
+
* Error thrown when GPU backend is requested but unavailable.
|
|
47
|
+
*/
|
|
48
|
+
var GPUNotAvailableError = class extends Error {
|
|
49
|
+
constructor(reason) {
|
|
50
|
+
super(`WebGPU not available: ${reason}`);
|
|
51
|
+
this.name = "GPUNotAvailableError";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/gpu/dispatch.ts
|
|
56
|
+
/**
|
|
57
|
+
* Backend selection and dispatch utilities for GPU compute operations.
|
|
58
|
+
*
|
|
59
|
+
* Provides the `withBackend` function that handles auto/gpu/cpu backend
|
|
60
|
+
* selection with timing and fallback logic.
|
|
61
|
+
*
|
|
62
|
+
* @module gpu/dispatch
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* Execute a compute operation with automatic backend selection.
|
|
66
|
+
*
|
|
67
|
+
* @param options - Backend selection and cancellation options
|
|
68
|
+
* @param cpuFn - CPU fallback implementation
|
|
69
|
+
* @param gpuFn - GPU implementation (receives TypeGPU root)
|
|
70
|
+
* @returns Compute result with backend used and timing
|
|
71
|
+
*/
|
|
72
|
+
async function withBackend(options, cpuFn, gpuFn) {
|
|
73
|
+
const backend = options.backend ?? "auto";
|
|
74
|
+
if (options.signal?.aborted === true) throw new DOMException("Operation aborted", "AbortError");
|
|
75
|
+
if (backend === "cpu") {
|
|
76
|
+
const start = performance.now();
|
|
77
|
+
return {
|
|
78
|
+
value: cpuFn(),
|
|
79
|
+
backend: "cpu",
|
|
80
|
+
elapsedMs: performance.now() - start
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (backend === "gpu") {
|
|
84
|
+
const detection = detectWebGPU();
|
|
85
|
+
if (!detection.available) throw new GPUNotAvailableError(detection.reason ?? "Unknown reason");
|
|
86
|
+
const root = options.root;
|
|
87
|
+
if (root === void 0) throw new Error("GPU backend requires a GraphwiseGPURoot. Pass one via options.root or use backend: 'auto'.");
|
|
88
|
+
const start = performance.now();
|
|
89
|
+
return {
|
|
90
|
+
value: await gpuFn(root),
|
|
91
|
+
backend: "gpu",
|
|
92
|
+
elapsedMs: performance.now() - start
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!detectWebGPU().available) {
|
|
96
|
+
const start = performance.now();
|
|
97
|
+
return {
|
|
98
|
+
value: cpuFn(),
|
|
99
|
+
backend: "cpu",
|
|
100
|
+
elapsedMs: performance.now() - start
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const root = options.root;
|
|
104
|
+
if (root === void 0) {
|
|
105
|
+
const start = performance.now();
|
|
106
|
+
return {
|
|
107
|
+
value: cpuFn(),
|
|
108
|
+
backend: "cpu",
|
|
109
|
+
elapsedMs: performance.now() - start
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const start = performance.now();
|
|
113
|
+
return {
|
|
114
|
+
value: await gpuFn(root),
|
|
115
|
+
backend: "gpu",
|
|
116
|
+
elapsedMs: performance.now() - start
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/gpu/kernels/spmv/kernel.ts
|
|
121
|
+
/**
|
|
122
|
+
* TypeGPU compute kernel for Sparse Matrix-Vector Multiplication (SpMV).
|
|
123
|
+
*
|
|
124
|
+
* Computes y = A * x where A is a sparse matrix in CSR format.
|
|
125
|
+
* Uses TypeGPU's compute pipeline for parallel GPU execution.
|
|
126
|
+
*
|
|
127
|
+
* @module gpu/kernels/spmv/kernel
|
|
128
|
+
*/
|
|
129
|
+
/**
|
|
130
|
+
* Bind group layout for SpMV kernel.
|
|
131
|
+
*/
|
|
132
|
+
var SpMVLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(src_default.bindGroupLayout({
|
|
133
|
+
rowOffsets: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
134
|
+
colIndices: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
135
|
+
values: { storage: data_exports.arrayOf(data_exports.f32) },
|
|
136
|
+
x: { storage: data_exports.arrayOf(data_exports.f32) },
|
|
137
|
+
y: {
|
|
138
|
+
storage: data_exports.arrayOf(data_exports.f32),
|
|
139
|
+
access: "mutable"
|
|
140
|
+
},
|
|
141
|
+
nodeCount: { uniform: data_exports.u32 },
|
|
142
|
+
hasValues: { uniform: data_exports.u32 }
|
|
143
|
+
}), "SpMVLayout");
|
|
144
|
+
/**
|
|
145
|
+
* SpMV compute pipeline: y[row] = sum(A[row,col] * x[col]).
|
|
146
|
+
*
|
|
147
|
+
* Each thread computes one row of the output vector using the CSR format:
|
|
148
|
+
* - rowOffsets: start/end indices for each row
|
|
149
|
+
* - colIndices: column index for each non-zero entry
|
|
150
|
+
* - values: edge weights (optional, defaults to 1.0)
|
|
151
|
+
*/
|
|
152
|
+
var spmvPipeline = (($) => (globalThis.__TYPEGPU_META__ ??= /* @__PURE__ */ new WeakMap()).set($.f = ((row) => {
|
|
153
|
+
"use gpu";
|
|
154
|
+
const start = SpMVLayout.$.rowOffsets[row] ?? 0;
|
|
155
|
+
const end = SpMVLayout.$.rowOffsets[__tsover_add(row, 1)] ?? 0;
|
|
156
|
+
let sum = 0;
|
|
157
|
+
for (let i = start; i < end; i = __tsover_add(i, 1)) {
|
|
158
|
+
const col = SpMVLayout.$.colIndices[i] ?? 0;
|
|
159
|
+
const weight = SpMVLayout.$.hasValues !== 0 ? SpMVLayout.$.values[i] ?? 1 : 1;
|
|
160
|
+
sum = __tsover_add(sum, __tsover_mul(weight, SpMVLayout.$.x[col] ?? 0));
|
|
161
|
+
}
|
|
162
|
+
SpMVLayout.$.y[row] = sum;
|
|
163
|
+
}), {
|
|
164
|
+
v: 1,
|
|
165
|
+
name: "spmvPipeline",
|
|
166
|
+
ast: {
|
|
167
|
+
"params": [{
|
|
168
|
+
"type": "i",
|
|
169
|
+
"name": "row"
|
|
170
|
+
}],
|
|
171
|
+
"body": [0, [
|
|
172
|
+
[
|
|
173
|
+
13,
|
|
174
|
+
"start",
|
|
175
|
+
[
|
|
176
|
+
3,
|
|
177
|
+
[
|
|
178
|
+
8,
|
|
179
|
+
[
|
|
180
|
+
7,
|
|
181
|
+
[
|
|
182
|
+
7,
|
|
183
|
+
"SpMVLayout",
|
|
184
|
+
"$"
|
|
185
|
+
],
|
|
186
|
+
"rowOffsets"
|
|
187
|
+
],
|
|
188
|
+
"row"
|
|
189
|
+
],
|
|
190
|
+
"??",
|
|
191
|
+
[5, "0"]
|
|
192
|
+
]
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
13,
|
|
196
|
+
"end",
|
|
197
|
+
[
|
|
198
|
+
3,
|
|
199
|
+
[
|
|
200
|
+
8,
|
|
201
|
+
[
|
|
202
|
+
7,
|
|
203
|
+
[
|
|
204
|
+
7,
|
|
205
|
+
"SpMVLayout",
|
|
206
|
+
"$"
|
|
207
|
+
],
|
|
208
|
+
"rowOffsets"
|
|
209
|
+
],
|
|
210
|
+
[
|
|
211
|
+
1,
|
|
212
|
+
"row",
|
|
213
|
+
"+",
|
|
214
|
+
[5, "1"]
|
|
215
|
+
]
|
|
216
|
+
],
|
|
217
|
+
"??",
|
|
218
|
+
[5, "0"]
|
|
219
|
+
]
|
|
220
|
+
],
|
|
221
|
+
[
|
|
222
|
+
12,
|
|
223
|
+
"sum",
|
|
224
|
+
[5, "0"]
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
14,
|
|
228
|
+
[
|
|
229
|
+
12,
|
|
230
|
+
"i",
|
|
231
|
+
"start"
|
|
232
|
+
],
|
|
233
|
+
[
|
|
234
|
+
1,
|
|
235
|
+
"i",
|
|
236
|
+
"<",
|
|
237
|
+
"end"
|
|
238
|
+
],
|
|
239
|
+
[
|
|
240
|
+
2,
|
|
241
|
+
"i",
|
|
242
|
+
"=",
|
|
243
|
+
[
|
|
244
|
+
1,
|
|
245
|
+
"i",
|
|
246
|
+
"+",
|
|
247
|
+
[5, "1"]
|
|
248
|
+
]
|
|
249
|
+
],
|
|
250
|
+
[0, [
|
|
251
|
+
[
|
|
252
|
+
13,
|
|
253
|
+
"col",
|
|
254
|
+
[
|
|
255
|
+
3,
|
|
256
|
+
[
|
|
257
|
+
8,
|
|
258
|
+
[
|
|
259
|
+
7,
|
|
260
|
+
[
|
|
261
|
+
7,
|
|
262
|
+
"SpMVLayout",
|
|
263
|
+
"$"
|
|
264
|
+
],
|
|
265
|
+
"colIndices"
|
|
266
|
+
],
|
|
267
|
+
"i"
|
|
268
|
+
],
|
|
269
|
+
"??",
|
|
270
|
+
[5, "0"]
|
|
271
|
+
]
|
|
272
|
+
],
|
|
273
|
+
[
|
|
274
|
+
13,
|
|
275
|
+
"weight",
|
|
276
|
+
[
|
|
277
|
+
105,
|
|
278
|
+
[
|
|
279
|
+
1,
|
|
280
|
+
[
|
|
281
|
+
7,
|
|
282
|
+
[
|
|
283
|
+
7,
|
|
284
|
+
"SpMVLayout",
|
|
285
|
+
"$"
|
|
286
|
+
],
|
|
287
|
+
"hasValues"
|
|
288
|
+
],
|
|
289
|
+
"!==",
|
|
290
|
+
[5, "0"]
|
|
291
|
+
],
|
|
292
|
+
[
|
|
293
|
+
3,
|
|
294
|
+
[
|
|
295
|
+
8,
|
|
296
|
+
[
|
|
297
|
+
7,
|
|
298
|
+
[
|
|
299
|
+
7,
|
|
300
|
+
"SpMVLayout",
|
|
301
|
+
"$"
|
|
302
|
+
],
|
|
303
|
+
"values"
|
|
304
|
+
],
|
|
305
|
+
"i"
|
|
306
|
+
],
|
|
307
|
+
"??",
|
|
308
|
+
[5, "1"]
|
|
309
|
+
],
|
|
310
|
+
[5, "1"]
|
|
311
|
+
]
|
|
312
|
+
],
|
|
313
|
+
[
|
|
314
|
+
2,
|
|
315
|
+
"sum",
|
|
316
|
+
"=",
|
|
317
|
+
[
|
|
318
|
+
1,
|
|
319
|
+
"sum",
|
|
320
|
+
"+",
|
|
321
|
+
[
|
|
322
|
+
1,
|
|
323
|
+
"weight",
|
|
324
|
+
"*",
|
|
325
|
+
[
|
|
326
|
+
3,
|
|
327
|
+
[
|
|
328
|
+
8,
|
|
329
|
+
[
|
|
330
|
+
7,
|
|
331
|
+
[
|
|
332
|
+
7,
|
|
333
|
+
"SpMVLayout",
|
|
334
|
+
"$"
|
|
335
|
+
],
|
|
336
|
+
"x"
|
|
337
|
+
],
|
|
338
|
+
"col"
|
|
339
|
+
],
|
|
340
|
+
"??",
|
|
341
|
+
[5, "0"]
|
|
342
|
+
]
|
|
343
|
+
]
|
|
344
|
+
]
|
|
345
|
+
]
|
|
346
|
+
]]
|
|
347
|
+
],
|
|
348
|
+
[
|
|
349
|
+
2,
|
|
350
|
+
[
|
|
351
|
+
8,
|
|
352
|
+
[
|
|
353
|
+
7,
|
|
354
|
+
[
|
|
355
|
+
7,
|
|
356
|
+
"SpMVLayout",
|
|
357
|
+
"$"
|
|
358
|
+
],
|
|
359
|
+
"y"
|
|
360
|
+
],
|
|
361
|
+
"row"
|
|
362
|
+
],
|
|
363
|
+
"=",
|
|
364
|
+
"sum"
|
|
365
|
+
]
|
|
366
|
+
]],
|
|
367
|
+
"externalNames": ["SpMVLayout"]
|
|
368
|
+
},
|
|
369
|
+
externals: () => ({ SpMVLayout })
|
|
370
|
+
}) && $.f)({});
|
|
371
|
+
/**
|
|
372
|
+
* Dispatch SpMV on GPU.
|
|
373
|
+
*
|
|
374
|
+
* @param root - TypeGPU root instance
|
|
375
|
+
* @param csrBuffers - CSR matrix as typed buffers
|
|
376
|
+
* @param x - Input vector buffer
|
|
377
|
+
* @param y - Output vector buffer (mutable)
|
|
378
|
+
* @param nodeCount - Number of nodes/rows
|
|
379
|
+
* @param hasValues - Whether edge weights are present
|
|
380
|
+
*/
|
|
381
|
+
function dispatchSpmv(root, csrBuffers, x, y, nodeCount, hasValues) {
|
|
382
|
+
const pipeline = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createGuardedComputePipeline(spmvPipeline), "pipeline");
|
|
383
|
+
const valuesBuffer = csrBuffers.values ?? root.createBuffer(data_exports.arrayOf(data_exports.f32, csrBuffers.edgeCount), new Array(csrBuffers.edgeCount).fill(1)).$usage("storage");
|
|
384
|
+
const nodeCountBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.u32, nodeCount).$usage("uniform"), "nodeCountBuffer");
|
|
385
|
+
const hasValuesBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.u32, hasValues ? 1 : 0).$usage("uniform"), "hasValuesBuffer");
|
|
386
|
+
const bindGroup = root.createBindGroup(SpMVLayout, {
|
|
387
|
+
rowOffsets: csrBuffers.rowOffsets,
|
|
388
|
+
colIndices: csrBuffers.colIndices,
|
|
389
|
+
values: valuesBuffer,
|
|
390
|
+
x,
|
|
391
|
+
y,
|
|
392
|
+
nodeCount: nodeCountBuffer,
|
|
393
|
+
hasValues: hasValuesBuffer
|
|
394
|
+
});
|
|
395
|
+
pipeline.with(bindGroup).dispatchThreads(nodeCount);
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/gpu/kernels/pagerank/kernel.ts
|
|
399
|
+
/**
|
|
400
|
+
* TypeGPU compute kernel for PageRank power iteration.
|
|
401
|
+
*
|
|
402
|
+
* Computes one iteration of PageRank:
|
|
403
|
+
* r(v) = (1 - d)/N + d * sum(r(u) / deg_out(u)) for u -> v
|
|
404
|
+
*
|
|
405
|
+
* Uses TypeGPU's compute pipeline for parallel GPU execution.
|
|
406
|
+
*
|
|
407
|
+
* @module gpu/kernels/pagerank/kernel
|
|
408
|
+
*/
|
|
409
|
+
/**
|
|
410
|
+
* Bind group layout for PageRank kernel.
|
|
411
|
+
*/
|
|
412
|
+
var PageRankLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(src_default.bindGroupLayout({
|
|
413
|
+
rowOffsets: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
414
|
+
colIndices: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
415
|
+
ranks: { storage: data_exports.arrayOf(data_exports.f32) },
|
|
416
|
+
outDegrees: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
417
|
+
newRanks: {
|
|
418
|
+
storage: data_exports.arrayOf(data_exports.f32),
|
|
419
|
+
access: "mutable"
|
|
420
|
+
},
|
|
421
|
+
nodeCount: { uniform: data_exports.u32 },
|
|
422
|
+
damping: { uniform: data_exports.f32 }
|
|
423
|
+
}), "PageRankLayout");
|
|
424
|
+
/**
|
|
425
|
+
* PageRank compute pipeline: one power iteration per dispatch.
|
|
426
|
+
*
|
|
427
|
+
* Each thread computes one node's new rank from incoming neighbours:
|
|
428
|
+
* newRank[v] = (1 - damping) / N + damping * sum(rank[u] / outDegree[u])
|
|
429
|
+
*/
|
|
430
|
+
var pagerankPipeline = (($) => (globalThis.__TYPEGPU_META__ ??= /* @__PURE__ */ new WeakMap()).set($.f = ((node) => {
|
|
431
|
+
"use gpu";
|
|
432
|
+
const n = PageRankLayout.$.nodeCount ?? 0;
|
|
433
|
+
const damp = PageRankLayout.$.damping ?? .85;
|
|
434
|
+
const start = PageRankLayout.$.rowOffsets[node] ?? 0;
|
|
435
|
+
const end = PageRankLayout.$.rowOffsets[__tsover_add(node, 1)] ?? 0;
|
|
436
|
+
let contribution = 0;
|
|
437
|
+
for (let i = start; i < end; i = __tsover_add(i, 1)) {
|
|
438
|
+
const source = PageRankLayout.$.colIndices[i] ?? 0;
|
|
439
|
+
const deg = PageRankLayout.$.outDegrees[source] ?? 0;
|
|
440
|
+
if (deg > 0) {
|
|
441
|
+
const rank = PageRankLayout.$.ranks[source] ?? 0;
|
|
442
|
+
contribution = __tsover_add(contribution, __tsover_div(rank, deg));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const teleport = __tsover_div(__tsover_sub(1, damp), n);
|
|
446
|
+
PageRankLayout.$.newRanks[node] = __tsover_add(teleport, __tsover_mul(damp, contribution));
|
|
447
|
+
}), {
|
|
448
|
+
v: 1,
|
|
449
|
+
name: "pagerankPipeline",
|
|
450
|
+
ast: {
|
|
451
|
+
"params": [{
|
|
452
|
+
"type": "i",
|
|
453
|
+
"name": "node"
|
|
454
|
+
}],
|
|
455
|
+
"body": [0, [
|
|
456
|
+
[
|
|
457
|
+
13,
|
|
458
|
+
"n",
|
|
459
|
+
[
|
|
460
|
+
3,
|
|
461
|
+
[
|
|
462
|
+
7,
|
|
463
|
+
[
|
|
464
|
+
7,
|
|
465
|
+
"PageRankLayout",
|
|
466
|
+
"$"
|
|
467
|
+
],
|
|
468
|
+
"nodeCount"
|
|
469
|
+
],
|
|
470
|
+
"??",
|
|
471
|
+
[5, "0"]
|
|
472
|
+
]
|
|
473
|
+
],
|
|
474
|
+
[
|
|
475
|
+
13,
|
|
476
|
+
"damp",
|
|
477
|
+
[
|
|
478
|
+
3,
|
|
479
|
+
[
|
|
480
|
+
7,
|
|
481
|
+
[
|
|
482
|
+
7,
|
|
483
|
+
"PageRankLayout",
|
|
484
|
+
"$"
|
|
485
|
+
],
|
|
486
|
+
"damping"
|
|
487
|
+
],
|
|
488
|
+
"??",
|
|
489
|
+
[5, "0.85"]
|
|
490
|
+
]
|
|
491
|
+
],
|
|
492
|
+
[
|
|
493
|
+
13,
|
|
494
|
+
"start",
|
|
495
|
+
[
|
|
496
|
+
3,
|
|
497
|
+
[
|
|
498
|
+
8,
|
|
499
|
+
[
|
|
500
|
+
7,
|
|
501
|
+
[
|
|
502
|
+
7,
|
|
503
|
+
"PageRankLayout",
|
|
504
|
+
"$"
|
|
505
|
+
],
|
|
506
|
+
"rowOffsets"
|
|
507
|
+
],
|
|
508
|
+
"node"
|
|
509
|
+
],
|
|
510
|
+
"??",
|
|
511
|
+
[5, "0"]
|
|
512
|
+
]
|
|
513
|
+
],
|
|
514
|
+
[
|
|
515
|
+
13,
|
|
516
|
+
"end",
|
|
517
|
+
[
|
|
518
|
+
3,
|
|
519
|
+
[
|
|
520
|
+
8,
|
|
521
|
+
[
|
|
522
|
+
7,
|
|
523
|
+
[
|
|
524
|
+
7,
|
|
525
|
+
"PageRankLayout",
|
|
526
|
+
"$"
|
|
527
|
+
],
|
|
528
|
+
"rowOffsets"
|
|
529
|
+
],
|
|
530
|
+
[
|
|
531
|
+
1,
|
|
532
|
+
"node",
|
|
533
|
+
"+",
|
|
534
|
+
[5, "1"]
|
|
535
|
+
]
|
|
536
|
+
],
|
|
537
|
+
"??",
|
|
538
|
+
[5, "0"]
|
|
539
|
+
]
|
|
540
|
+
],
|
|
541
|
+
[
|
|
542
|
+
12,
|
|
543
|
+
"contribution",
|
|
544
|
+
[5, "0"]
|
|
545
|
+
],
|
|
546
|
+
[
|
|
547
|
+
14,
|
|
548
|
+
[
|
|
549
|
+
12,
|
|
550
|
+
"i",
|
|
551
|
+
"start"
|
|
552
|
+
],
|
|
553
|
+
[
|
|
554
|
+
1,
|
|
555
|
+
"i",
|
|
556
|
+
"<",
|
|
557
|
+
"end"
|
|
558
|
+
],
|
|
559
|
+
[
|
|
560
|
+
2,
|
|
561
|
+
"i",
|
|
562
|
+
"=",
|
|
563
|
+
[
|
|
564
|
+
1,
|
|
565
|
+
"i",
|
|
566
|
+
"+",
|
|
567
|
+
[5, "1"]
|
|
568
|
+
]
|
|
569
|
+
],
|
|
570
|
+
[0, [
|
|
571
|
+
[
|
|
572
|
+
13,
|
|
573
|
+
"source",
|
|
574
|
+
[
|
|
575
|
+
3,
|
|
576
|
+
[
|
|
577
|
+
8,
|
|
578
|
+
[
|
|
579
|
+
7,
|
|
580
|
+
[
|
|
581
|
+
7,
|
|
582
|
+
"PageRankLayout",
|
|
583
|
+
"$"
|
|
584
|
+
],
|
|
585
|
+
"colIndices"
|
|
586
|
+
],
|
|
587
|
+
"i"
|
|
588
|
+
],
|
|
589
|
+
"??",
|
|
590
|
+
[5, "0"]
|
|
591
|
+
]
|
|
592
|
+
],
|
|
593
|
+
[
|
|
594
|
+
13,
|
|
595
|
+
"deg",
|
|
596
|
+
[
|
|
597
|
+
3,
|
|
598
|
+
[
|
|
599
|
+
8,
|
|
600
|
+
[
|
|
601
|
+
7,
|
|
602
|
+
[
|
|
603
|
+
7,
|
|
604
|
+
"PageRankLayout",
|
|
605
|
+
"$"
|
|
606
|
+
],
|
|
607
|
+
"outDegrees"
|
|
608
|
+
],
|
|
609
|
+
"source"
|
|
610
|
+
],
|
|
611
|
+
"??",
|
|
612
|
+
[5, "0"]
|
|
613
|
+
]
|
|
614
|
+
],
|
|
615
|
+
[
|
|
616
|
+
11,
|
|
617
|
+
[
|
|
618
|
+
1,
|
|
619
|
+
"deg",
|
|
620
|
+
">",
|
|
621
|
+
[5, "0"]
|
|
622
|
+
],
|
|
623
|
+
[0, [[
|
|
624
|
+
13,
|
|
625
|
+
"rank",
|
|
626
|
+
[
|
|
627
|
+
3,
|
|
628
|
+
[
|
|
629
|
+
8,
|
|
630
|
+
[
|
|
631
|
+
7,
|
|
632
|
+
[
|
|
633
|
+
7,
|
|
634
|
+
"PageRankLayout",
|
|
635
|
+
"$"
|
|
636
|
+
],
|
|
637
|
+
"ranks"
|
|
638
|
+
],
|
|
639
|
+
"source"
|
|
640
|
+
],
|
|
641
|
+
"??",
|
|
642
|
+
[5, "0"]
|
|
643
|
+
]
|
|
644
|
+
], [
|
|
645
|
+
2,
|
|
646
|
+
"contribution",
|
|
647
|
+
"=",
|
|
648
|
+
[
|
|
649
|
+
1,
|
|
650
|
+
"contribution",
|
|
651
|
+
"+",
|
|
652
|
+
[
|
|
653
|
+
1,
|
|
654
|
+
"rank",
|
|
655
|
+
"/",
|
|
656
|
+
"deg"
|
|
657
|
+
]
|
|
658
|
+
]
|
|
659
|
+
]]]
|
|
660
|
+
]
|
|
661
|
+
]]
|
|
662
|
+
],
|
|
663
|
+
[
|
|
664
|
+
13,
|
|
665
|
+
"teleport",
|
|
666
|
+
[
|
|
667
|
+
1,
|
|
668
|
+
[
|
|
669
|
+
1,
|
|
670
|
+
[5, "1"],
|
|
671
|
+
"-",
|
|
672
|
+
"damp"
|
|
673
|
+
],
|
|
674
|
+
"/",
|
|
675
|
+
"n"
|
|
676
|
+
]
|
|
677
|
+
],
|
|
678
|
+
[
|
|
679
|
+
2,
|
|
680
|
+
[
|
|
681
|
+
8,
|
|
682
|
+
[
|
|
683
|
+
7,
|
|
684
|
+
[
|
|
685
|
+
7,
|
|
686
|
+
"PageRankLayout",
|
|
687
|
+
"$"
|
|
688
|
+
],
|
|
689
|
+
"newRanks"
|
|
690
|
+
],
|
|
691
|
+
"node"
|
|
692
|
+
],
|
|
693
|
+
"=",
|
|
694
|
+
[
|
|
695
|
+
1,
|
|
696
|
+
"teleport",
|
|
697
|
+
"+",
|
|
698
|
+
[
|
|
699
|
+
1,
|
|
700
|
+
"damp",
|
|
701
|
+
"*",
|
|
702
|
+
"contribution"
|
|
703
|
+
]
|
|
704
|
+
]
|
|
705
|
+
]
|
|
706
|
+
]],
|
|
707
|
+
"externalNames": ["PageRankLayout"]
|
|
708
|
+
},
|
|
709
|
+
externals: () => ({ PageRankLayout })
|
|
710
|
+
}) && $.f)({});
|
|
711
|
+
/**
|
|
712
|
+
* Dispatch one PageRank iteration on GPU.
|
|
713
|
+
*
|
|
714
|
+
* @param root - TypeGPU root instance
|
|
715
|
+
* @param csrBuffers - CSR matrix as typed buffers (transpose graph: in-edges)
|
|
716
|
+
* @param ranks - Current rank values buffer
|
|
717
|
+
* @param outDegrees - Out-degree buffer for each node
|
|
718
|
+
* @param newRanks - Output buffer for new rank values (mutable)
|
|
719
|
+
* @param nodeCount - Number of nodes
|
|
720
|
+
* @param damping - Damping factor (typically 0.85)
|
|
721
|
+
*/
|
|
722
|
+
function dispatchPagerank(root, csrBuffers, ranks, outDegrees, newRanks, nodeCount, damping) {
|
|
723
|
+
const pipeline = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createGuardedComputePipeline(pagerankPipeline), "pipeline");
|
|
724
|
+
const nodeCountBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.u32, nodeCount).$usage("uniform"), "nodeCountBuffer");
|
|
725
|
+
const dampingBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.f32, damping).$usage("uniform"), "dampingBuffer");
|
|
726
|
+
const bindGroup = root.createBindGroup(PageRankLayout, {
|
|
727
|
+
rowOffsets: csrBuffers.rowOffsets,
|
|
728
|
+
colIndices: csrBuffers.colIndices,
|
|
729
|
+
ranks,
|
|
730
|
+
outDegrees,
|
|
731
|
+
newRanks,
|
|
732
|
+
nodeCount: nodeCountBuffer,
|
|
733
|
+
damping: dampingBuffer
|
|
734
|
+
});
|
|
735
|
+
pipeline.with(bindGroup).dispatchThreads(nodeCount);
|
|
736
|
+
}
|
|
737
|
+
//#endregion
|
|
738
|
+
//#region src/gpu/kernels/jaccard/kernel.ts
|
|
739
|
+
/**
|
|
740
|
+
* TypeGPU compute kernel for batch Jaccard similarity.
|
|
741
|
+
*
|
|
742
|
+
* Computes Jaccard coefficient for multiple node pairs in parallel:
|
|
743
|
+
* J(u, v) = |N(u) ∩ N(v)| / |N(u) ∪ N(v)|
|
|
744
|
+
*
|
|
745
|
+
* Uses binary search optimisation: iterate smaller neighbourhood, search in larger.
|
|
746
|
+
*
|
|
747
|
+
* @module gpu/kernels/jaccard/kernel
|
|
748
|
+
*/
|
|
749
|
+
/**
|
|
750
|
+
* Bind group layout for Jaccard kernel.
|
|
751
|
+
*/
|
|
752
|
+
var JaccardLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(src_default.bindGroupLayout({
|
|
753
|
+
rowOffsets: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
754
|
+
colIndices: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
755
|
+
pairsU: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
756
|
+
pairsV: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
757
|
+
results: {
|
|
758
|
+
storage: data_exports.arrayOf(data_exports.f32),
|
|
759
|
+
access: "mutable"
|
|
760
|
+
},
|
|
761
|
+
pairCount: { uniform: data_exports.u32 }
|
|
762
|
+
}), "JaccardLayout");
|
|
763
|
+
/**
|
|
764
|
+
* Jaccard compute pipeline: one thread per pair.
|
|
765
|
+
*
|
|
766
|
+
* For each pair (u, v):
|
|
767
|
+
* - Count intersection by iterating smaller neighbourhood
|
|
768
|
+
* - Binary search in larger neighbourhood
|
|
769
|
+
* - Jaccard = intersection / (degU + degV - intersection)
|
|
770
|
+
*/
|
|
771
|
+
var jaccardPipeline = (($) => (globalThis.__TYPEGPU_META__ ??= /* @__PURE__ */ new WeakMap()).set($.f = ((pairIdx) => {
|
|
772
|
+
"use gpu";
|
|
773
|
+
const u = JaccardLayout.$.pairsU[pairIdx] ?? 0;
|
|
774
|
+
const v = JaccardLayout.$.pairsV[pairIdx] ?? 0;
|
|
775
|
+
const uStart = JaccardLayout.$.rowOffsets[u] ?? 0;
|
|
776
|
+
const uEnd = JaccardLayout.$.rowOffsets[__tsover_add(u, 1)] ?? 0;
|
|
777
|
+
const vStart = JaccardLayout.$.rowOffsets[v] ?? 0;
|
|
778
|
+
const vEnd = JaccardLayout.$.rowOffsets[__tsover_add(v, 1)] ?? 0;
|
|
779
|
+
const degU = __tsover_sub(uEnd, uStart);
|
|
780
|
+
const degV = __tsover_sub(vEnd, vStart);
|
|
781
|
+
if (degU === 0 || degV === 0) {
|
|
782
|
+
JaccardLayout.$.results[pairIdx] = 0;
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
let intersection = 0;
|
|
786
|
+
if (degU <= degV) for (let i = uStart; i < uEnd; i = __tsover_add(i, 1)) {
|
|
787
|
+
const neighbour = JaccardLayout.$.colIndices[i] ?? 0;
|
|
788
|
+
let lo = vStart;
|
|
789
|
+
let hi = vEnd;
|
|
790
|
+
while (lo < hi) {
|
|
791
|
+
const mid = __tsover_add(lo, __tsover_div(__tsover_sub(hi, lo), 2));
|
|
792
|
+
const midVal = JaccardLayout.$.colIndices[mid] ?? 0;
|
|
793
|
+
if (midVal === neighbour) {
|
|
794
|
+
intersection = __tsover_add(intersection, 1);
|
|
795
|
+
lo = hi;
|
|
796
|
+
} else if (midVal < neighbour) lo = __tsover_add(mid, 1);
|
|
797
|
+
else hi = mid;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
else for (let i = vStart; i < vEnd; i = __tsover_add(i, 1)) {
|
|
801
|
+
const neighbour = JaccardLayout.$.colIndices[i] ?? 0;
|
|
802
|
+
let lo = uStart;
|
|
803
|
+
let hi = uEnd;
|
|
804
|
+
while (lo < hi) {
|
|
805
|
+
const mid = __tsover_add(lo, __tsover_div(__tsover_sub(hi, lo), 2));
|
|
806
|
+
const midVal = JaccardLayout.$.colIndices[mid] ?? 0;
|
|
807
|
+
if (midVal === neighbour) {
|
|
808
|
+
intersection = __tsover_add(intersection, 1);
|
|
809
|
+
lo = hi;
|
|
810
|
+
} else if (midVal < neighbour) lo = __tsover_add(mid, 1);
|
|
811
|
+
else hi = mid;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const unionSize = __tsover_sub(__tsover_add(degU, degV), intersection);
|
|
815
|
+
JaccardLayout.$.results[pairIdx] = __tsover_div(intersection, unionSize);
|
|
816
|
+
}), {
|
|
817
|
+
v: 1,
|
|
818
|
+
name: "jaccardPipeline",
|
|
819
|
+
ast: {
|
|
820
|
+
"params": [{
|
|
821
|
+
"type": "i",
|
|
822
|
+
"name": "pairIdx"
|
|
823
|
+
}],
|
|
824
|
+
"body": [0, [
|
|
825
|
+
[
|
|
826
|
+
13,
|
|
827
|
+
"u",
|
|
828
|
+
[
|
|
829
|
+
3,
|
|
830
|
+
[
|
|
831
|
+
8,
|
|
832
|
+
[
|
|
833
|
+
7,
|
|
834
|
+
[
|
|
835
|
+
7,
|
|
836
|
+
"JaccardLayout",
|
|
837
|
+
"$"
|
|
838
|
+
],
|
|
839
|
+
"pairsU"
|
|
840
|
+
],
|
|
841
|
+
"pairIdx"
|
|
842
|
+
],
|
|
843
|
+
"??",
|
|
844
|
+
[5, "0"]
|
|
845
|
+
]
|
|
846
|
+
],
|
|
847
|
+
[
|
|
848
|
+
13,
|
|
849
|
+
"v",
|
|
850
|
+
[
|
|
851
|
+
3,
|
|
852
|
+
[
|
|
853
|
+
8,
|
|
854
|
+
[
|
|
855
|
+
7,
|
|
856
|
+
[
|
|
857
|
+
7,
|
|
858
|
+
"JaccardLayout",
|
|
859
|
+
"$"
|
|
860
|
+
],
|
|
861
|
+
"pairsV"
|
|
862
|
+
],
|
|
863
|
+
"pairIdx"
|
|
864
|
+
],
|
|
865
|
+
"??",
|
|
866
|
+
[5, "0"]
|
|
867
|
+
]
|
|
868
|
+
],
|
|
869
|
+
[
|
|
870
|
+
13,
|
|
871
|
+
"uStart",
|
|
872
|
+
[
|
|
873
|
+
3,
|
|
874
|
+
[
|
|
875
|
+
8,
|
|
876
|
+
[
|
|
877
|
+
7,
|
|
878
|
+
[
|
|
879
|
+
7,
|
|
880
|
+
"JaccardLayout",
|
|
881
|
+
"$"
|
|
882
|
+
],
|
|
883
|
+
"rowOffsets"
|
|
884
|
+
],
|
|
885
|
+
"u"
|
|
886
|
+
],
|
|
887
|
+
"??",
|
|
888
|
+
[5, "0"]
|
|
889
|
+
]
|
|
890
|
+
],
|
|
891
|
+
[
|
|
892
|
+
13,
|
|
893
|
+
"uEnd",
|
|
894
|
+
[
|
|
895
|
+
3,
|
|
896
|
+
[
|
|
897
|
+
8,
|
|
898
|
+
[
|
|
899
|
+
7,
|
|
900
|
+
[
|
|
901
|
+
7,
|
|
902
|
+
"JaccardLayout",
|
|
903
|
+
"$"
|
|
904
|
+
],
|
|
905
|
+
"rowOffsets"
|
|
906
|
+
],
|
|
907
|
+
[
|
|
908
|
+
1,
|
|
909
|
+
"u",
|
|
910
|
+
"+",
|
|
911
|
+
[5, "1"]
|
|
912
|
+
]
|
|
913
|
+
],
|
|
914
|
+
"??",
|
|
915
|
+
[5, "0"]
|
|
916
|
+
]
|
|
917
|
+
],
|
|
918
|
+
[
|
|
919
|
+
13,
|
|
920
|
+
"vStart",
|
|
921
|
+
[
|
|
922
|
+
3,
|
|
923
|
+
[
|
|
924
|
+
8,
|
|
925
|
+
[
|
|
926
|
+
7,
|
|
927
|
+
[
|
|
928
|
+
7,
|
|
929
|
+
"JaccardLayout",
|
|
930
|
+
"$"
|
|
931
|
+
],
|
|
932
|
+
"rowOffsets"
|
|
933
|
+
],
|
|
934
|
+
"v"
|
|
935
|
+
],
|
|
936
|
+
"??",
|
|
937
|
+
[5, "0"]
|
|
938
|
+
]
|
|
939
|
+
],
|
|
940
|
+
[
|
|
941
|
+
13,
|
|
942
|
+
"vEnd",
|
|
943
|
+
[
|
|
944
|
+
3,
|
|
945
|
+
[
|
|
946
|
+
8,
|
|
947
|
+
[
|
|
948
|
+
7,
|
|
949
|
+
[
|
|
950
|
+
7,
|
|
951
|
+
"JaccardLayout",
|
|
952
|
+
"$"
|
|
953
|
+
],
|
|
954
|
+
"rowOffsets"
|
|
955
|
+
],
|
|
956
|
+
[
|
|
957
|
+
1,
|
|
958
|
+
"v",
|
|
959
|
+
"+",
|
|
960
|
+
[5, "1"]
|
|
961
|
+
]
|
|
962
|
+
],
|
|
963
|
+
"??",
|
|
964
|
+
[5, "0"]
|
|
965
|
+
]
|
|
966
|
+
],
|
|
967
|
+
[
|
|
968
|
+
13,
|
|
969
|
+
"degU",
|
|
970
|
+
[
|
|
971
|
+
1,
|
|
972
|
+
"uEnd",
|
|
973
|
+
"-",
|
|
974
|
+
"uStart"
|
|
975
|
+
]
|
|
976
|
+
],
|
|
977
|
+
[
|
|
978
|
+
13,
|
|
979
|
+
"degV",
|
|
980
|
+
[
|
|
981
|
+
1,
|
|
982
|
+
"vEnd",
|
|
983
|
+
"-",
|
|
984
|
+
"vStart"
|
|
985
|
+
]
|
|
986
|
+
],
|
|
987
|
+
[
|
|
988
|
+
11,
|
|
989
|
+
[
|
|
990
|
+
3,
|
|
991
|
+
[
|
|
992
|
+
1,
|
|
993
|
+
"degU",
|
|
994
|
+
"===",
|
|
995
|
+
[5, "0"]
|
|
996
|
+
],
|
|
997
|
+
"||",
|
|
998
|
+
[
|
|
999
|
+
1,
|
|
1000
|
+
"degV",
|
|
1001
|
+
"===",
|
|
1002
|
+
[5, "0"]
|
|
1003
|
+
]
|
|
1004
|
+
],
|
|
1005
|
+
[0, [[
|
|
1006
|
+
2,
|
|
1007
|
+
[
|
|
1008
|
+
8,
|
|
1009
|
+
[
|
|
1010
|
+
7,
|
|
1011
|
+
[
|
|
1012
|
+
7,
|
|
1013
|
+
"JaccardLayout",
|
|
1014
|
+
"$"
|
|
1015
|
+
],
|
|
1016
|
+
"results"
|
|
1017
|
+
],
|
|
1018
|
+
"pairIdx"
|
|
1019
|
+
],
|
|
1020
|
+
"=",
|
|
1021
|
+
[5, "0"]
|
|
1022
|
+
], [10]]]
|
|
1023
|
+
],
|
|
1024
|
+
[
|
|
1025
|
+
12,
|
|
1026
|
+
"intersection",
|
|
1027
|
+
[5, "0"]
|
|
1028
|
+
],
|
|
1029
|
+
[
|
|
1030
|
+
11,
|
|
1031
|
+
[
|
|
1032
|
+
1,
|
|
1033
|
+
"degU",
|
|
1034
|
+
"<=",
|
|
1035
|
+
"degV"
|
|
1036
|
+
],
|
|
1037
|
+
[0, [[
|
|
1038
|
+
14,
|
|
1039
|
+
[
|
|
1040
|
+
12,
|
|
1041
|
+
"i",
|
|
1042
|
+
"uStart"
|
|
1043
|
+
],
|
|
1044
|
+
[
|
|
1045
|
+
1,
|
|
1046
|
+
"i",
|
|
1047
|
+
"<",
|
|
1048
|
+
"uEnd"
|
|
1049
|
+
],
|
|
1050
|
+
[
|
|
1051
|
+
2,
|
|
1052
|
+
"i",
|
|
1053
|
+
"=",
|
|
1054
|
+
[
|
|
1055
|
+
1,
|
|
1056
|
+
"i",
|
|
1057
|
+
"+",
|
|
1058
|
+
[5, "1"]
|
|
1059
|
+
]
|
|
1060
|
+
],
|
|
1061
|
+
[0, [
|
|
1062
|
+
[
|
|
1063
|
+
13,
|
|
1064
|
+
"neighbour",
|
|
1065
|
+
[
|
|
1066
|
+
3,
|
|
1067
|
+
[
|
|
1068
|
+
8,
|
|
1069
|
+
[
|
|
1070
|
+
7,
|
|
1071
|
+
[
|
|
1072
|
+
7,
|
|
1073
|
+
"JaccardLayout",
|
|
1074
|
+
"$"
|
|
1075
|
+
],
|
|
1076
|
+
"colIndices"
|
|
1077
|
+
],
|
|
1078
|
+
"i"
|
|
1079
|
+
],
|
|
1080
|
+
"??",
|
|
1081
|
+
[5, "0"]
|
|
1082
|
+
]
|
|
1083
|
+
],
|
|
1084
|
+
[
|
|
1085
|
+
12,
|
|
1086
|
+
"lo",
|
|
1087
|
+
"vStart"
|
|
1088
|
+
],
|
|
1089
|
+
[
|
|
1090
|
+
12,
|
|
1091
|
+
"hi",
|
|
1092
|
+
"vEnd"
|
|
1093
|
+
],
|
|
1094
|
+
[
|
|
1095
|
+
15,
|
|
1096
|
+
[
|
|
1097
|
+
1,
|
|
1098
|
+
"lo",
|
|
1099
|
+
"<",
|
|
1100
|
+
"hi"
|
|
1101
|
+
],
|
|
1102
|
+
[0, [
|
|
1103
|
+
[
|
|
1104
|
+
13,
|
|
1105
|
+
"mid",
|
|
1106
|
+
[
|
|
1107
|
+
1,
|
|
1108
|
+
"lo",
|
|
1109
|
+
"+",
|
|
1110
|
+
[
|
|
1111
|
+
1,
|
|
1112
|
+
[
|
|
1113
|
+
1,
|
|
1114
|
+
"hi",
|
|
1115
|
+
"-",
|
|
1116
|
+
"lo"
|
|
1117
|
+
],
|
|
1118
|
+
"/",
|
|
1119
|
+
[5, "2"]
|
|
1120
|
+
]
|
|
1121
|
+
]
|
|
1122
|
+
],
|
|
1123
|
+
[
|
|
1124
|
+
13,
|
|
1125
|
+
"midVal",
|
|
1126
|
+
[
|
|
1127
|
+
3,
|
|
1128
|
+
[
|
|
1129
|
+
8,
|
|
1130
|
+
[
|
|
1131
|
+
7,
|
|
1132
|
+
[
|
|
1133
|
+
7,
|
|
1134
|
+
"JaccardLayout",
|
|
1135
|
+
"$"
|
|
1136
|
+
],
|
|
1137
|
+
"colIndices"
|
|
1138
|
+
],
|
|
1139
|
+
"mid"
|
|
1140
|
+
],
|
|
1141
|
+
"??",
|
|
1142
|
+
[5, "0"]
|
|
1143
|
+
]
|
|
1144
|
+
],
|
|
1145
|
+
[
|
|
1146
|
+
11,
|
|
1147
|
+
[
|
|
1148
|
+
1,
|
|
1149
|
+
"midVal",
|
|
1150
|
+
"===",
|
|
1151
|
+
"neighbour"
|
|
1152
|
+
],
|
|
1153
|
+
[0, [[
|
|
1154
|
+
2,
|
|
1155
|
+
"intersection",
|
|
1156
|
+
"=",
|
|
1157
|
+
[
|
|
1158
|
+
1,
|
|
1159
|
+
"intersection",
|
|
1160
|
+
"+",
|
|
1161
|
+
[5, "1"]
|
|
1162
|
+
]
|
|
1163
|
+
], [
|
|
1164
|
+
2,
|
|
1165
|
+
"lo",
|
|
1166
|
+
"=",
|
|
1167
|
+
"hi"
|
|
1168
|
+
]]],
|
|
1169
|
+
[
|
|
1170
|
+
11,
|
|
1171
|
+
[
|
|
1172
|
+
1,
|
|
1173
|
+
"midVal",
|
|
1174
|
+
"<",
|
|
1175
|
+
"neighbour"
|
|
1176
|
+
],
|
|
1177
|
+
[0, [[
|
|
1178
|
+
2,
|
|
1179
|
+
"lo",
|
|
1180
|
+
"=",
|
|
1181
|
+
[
|
|
1182
|
+
1,
|
|
1183
|
+
"mid",
|
|
1184
|
+
"+",
|
|
1185
|
+
[5, "1"]
|
|
1186
|
+
]
|
|
1187
|
+
]]],
|
|
1188
|
+
[0, [[
|
|
1189
|
+
2,
|
|
1190
|
+
"hi",
|
|
1191
|
+
"=",
|
|
1192
|
+
"mid"
|
|
1193
|
+
]]]
|
|
1194
|
+
]
|
|
1195
|
+
]
|
|
1196
|
+
]]
|
|
1197
|
+
]
|
|
1198
|
+
]]
|
|
1199
|
+
]]],
|
|
1200
|
+
[0, [[
|
|
1201
|
+
14,
|
|
1202
|
+
[
|
|
1203
|
+
12,
|
|
1204
|
+
"i",
|
|
1205
|
+
"vStart"
|
|
1206
|
+
],
|
|
1207
|
+
[
|
|
1208
|
+
1,
|
|
1209
|
+
"i",
|
|
1210
|
+
"<",
|
|
1211
|
+
"vEnd"
|
|
1212
|
+
],
|
|
1213
|
+
[
|
|
1214
|
+
2,
|
|
1215
|
+
"i",
|
|
1216
|
+
"=",
|
|
1217
|
+
[
|
|
1218
|
+
1,
|
|
1219
|
+
"i",
|
|
1220
|
+
"+",
|
|
1221
|
+
[5, "1"]
|
|
1222
|
+
]
|
|
1223
|
+
],
|
|
1224
|
+
[0, [
|
|
1225
|
+
[
|
|
1226
|
+
13,
|
|
1227
|
+
"neighbour",
|
|
1228
|
+
[
|
|
1229
|
+
3,
|
|
1230
|
+
[
|
|
1231
|
+
8,
|
|
1232
|
+
[
|
|
1233
|
+
7,
|
|
1234
|
+
[
|
|
1235
|
+
7,
|
|
1236
|
+
"JaccardLayout",
|
|
1237
|
+
"$"
|
|
1238
|
+
],
|
|
1239
|
+
"colIndices"
|
|
1240
|
+
],
|
|
1241
|
+
"i"
|
|
1242
|
+
],
|
|
1243
|
+
"??",
|
|
1244
|
+
[5, "0"]
|
|
1245
|
+
]
|
|
1246
|
+
],
|
|
1247
|
+
[
|
|
1248
|
+
12,
|
|
1249
|
+
"lo",
|
|
1250
|
+
"uStart"
|
|
1251
|
+
],
|
|
1252
|
+
[
|
|
1253
|
+
12,
|
|
1254
|
+
"hi",
|
|
1255
|
+
"uEnd"
|
|
1256
|
+
],
|
|
1257
|
+
[
|
|
1258
|
+
15,
|
|
1259
|
+
[
|
|
1260
|
+
1,
|
|
1261
|
+
"lo",
|
|
1262
|
+
"<",
|
|
1263
|
+
"hi"
|
|
1264
|
+
],
|
|
1265
|
+
[0, [
|
|
1266
|
+
[
|
|
1267
|
+
13,
|
|
1268
|
+
"mid",
|
|
1269
|
+
[
|
|
1270
|
+
1,
|
|
1271
|
+
"lo",
|
|
1272
|
+
"+",
|
|
1273
|
+
[
|
|
1274
|
+
1,
|
|
1275
|
+
[
|
|
1276
|
+
1,
|
|
1277
|
+
"hi",
|
|
1278
|
+
"-",
|
|
1279
|
+
"lo"
|
|
1280
|
+
],
|
|
1281
|
+
"/",
|
|
1282
|
+
[5, "2"]
|
|
1283
|
+
]
|
|
1284
|
+
]
|
|
1285
|
+
],
|
|
1286
|
+
[
|
|
1287
|
+
13,
|
|
1288
|
+
"midVal",
|
|
1289
|
+
[
|
|
1290
|
+
3,
|
|
1291
|
+
[
|
|
1292
|
+
8,
|
|
1293
|
+
[
|
|
1294
|
+
7,
|
|
1295
|
+
[
|
|
1296
|
+
7,
|
|
1297
|
+
"JaccardLayout",
|
|
1298
|
+
"$"
|
|
1299
|
+
],
|
|
1300
|
+
"colIndices"
|
|
1301
|
+
],
|
|
1302
|
+
"mid"
|
|
1303
|
+
],
|
|
1304
|
+
"??",
|
|
1305
|
+
[5, "0"]
|
|
1306
|
+
]
|
|
1307
|
+
],
|
|
1308
|
+
[
|
|
1309
|
+
11,
|
|
1310
|
+
[
|
|
1311
|
+
1,
|
|
1312
|
+
"midVal",
|
|
1313
|
+
"===",
|
|
1314
|
+
"neighbour"
|
|
1315
|
+
],
|
|
1316
|
+
[0, [[
|
|
1317
|
+
2,
|
|
1318
|
+
"intersection",
|
|
1319
|
+
"=",
|
|
1320
|
+
[
|
|
1321
|
+
1,
|
|
1322
|
+
"intersection",
|
|
1323
|
+
"+",
|
|
1324
|
+
[5, "1"]
|
|
1325
|
+
]
|
|
1326
|
+
], [
|
|
1327
|
+
2,
|
|
1328
|
+
"lo",
|
|
1329
|
+
"=",
|
|
1330
|
+
"hi"
|
|
1331
|
+
]]],
|
|
1332
|
+
[
|
|
1333
|
+
11,
|
|
1334
|
+
[
|
|
1335
|
+
1,
|
|
1336
|
+
"midVal",
|
|
1337
|
+
"<",
|
|
1338
|
+
"neighbour"
|
|
1339
|
+
],
|
|
1340
|
+
[0, [[
|
|
1341
|
+
2,
|
|
1342
|
+
"lo",
|
|
1343
|
+
"=",
|
|
1344
|
+
[
|
|
1345
|
+
1,
|
|
1346
|
+
"mid",
|
|
1347
|
+
"+",
|
|
1348
|
+
[5, "1"]
|
|
1349
|
+
]
|
|
1350
|
+
]]],
|
|
1351
|
+
[0, [[
|
|
1352
|
+
2,
|
|
1353
|
+
"hi",
|
|
1354
|
+
"=",
|
|
1355
|
+
"mid"
|
|
1356
|
+
]]]
|
|
1357
|
+
]
|
|
1358
|
+
]
|
|
1359
|
+
]]
|
|
1360
|
+
]
|
|
1361
|
+
]]
|
|
1362
|
+
]]]
|
|
1363
|
+
],
|
|
1364
|
+
[
|
|
1365
|
+
13,
|
|
1366
|
+
"unionSize",
|
|
1367
|
+
[
|
|
1368
|
+
1,
|
|
1369
|
+
[
|
|
1370
|
+
1,
|
|
1371
|
+
"degU",
|
|
1372
|
+
"+",
|
|
1373
|
+
"degV"
|
|
1374
|
+
],
|
|
1375
|
+
"-",
|
|
1376
|
+
"intersection"
|
|
1377
|
+
]
|
|
1378
|
+
],
|
|
1379
|
+
[
|
|
1380
|
+
2,
|
|
1381
|
+
[
|
|
1382
|
+
8,
|
|
1383
|
+
[
|
|
1384
|
+
7,
|
|
1385
|
+
[
|
|
1386
|
+
7,
|
|
1387
|
+
"JaccardLayout",
|
|
1388
|
+
"$"
|
|
1389
|
+
],
|
|
1390
|
+
"results"
|
|
1391
|
+
],
|
|
1392
|
+
"pairIdx"
|
|
1393
|
+
],
|
|
1394
|
+
"=",
|
|
1395
|
+
[
|
|
1396
|
+
1,
|
|
1397
|
+
"intersection",
|
|
1398
|
+
"/",
|
|
1399
|
+
"unionSize"
|
|
1400
|
+
]
|
|
1401
|
+
]
|
|
1402
|
+
]],
|
|
1403
|
+
"externalNames": ["JaccardLayout"]
|
|
1404
|
+
},
|
|
1405
|
+
externals: () => ({ JaccardLayout })
|
|
1406
|
+
}) && $.f)({});
|
|
1407
|
+
/**
|
|
1408
|
+
* Dispatch batch Jaccard on GPU.
|
|
1409
|
+
*
|
|
1410
|
+
* @param root - TypeGPU root instance
|
|
1411
|
+
* @param csrBuffers - CSR matrix as typed buffers
|
|
1412
|
+
* @param pairsU - First node of each pair (u32 array)
|
|
1413
|
+
* @param pairsV - Second node of each pair (u32 array)
|
|
1414
|
+
* @param results - Output Jaccard coefficients (f32 array, mutable)
|
|
1415
|
+
* @param pairCount - Number of pairs to compute
|
|
1416
|
+
*/
|
|
1417
|
+
function dispatchJaccard(root, csrBuffers, pairsU, pairsV, results, pairCount) {
|
|
1418
|
+
const pipeline = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createGuardedComputePipeline(jaccardPipeline), "pipeline");
|
|
1419
|
+
const pairCountBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.u32, pairCount).$usage("uniform"), "pairCountBuffer");
|
|
1420
|
+
const bindGroup = root.createBindGroup(JaccardLayout, {
|
|
1421
|
+
rowOffsets: csrBuffers.rowOffsets,
|
|
1422
|
+
colIndices: csrBuffers.colIndices,
|
|
1423
|
+
pairsU,
|
|
1424
|
+
pairsV,
|
|
1425
|
+
results,
|
|
1426
|
+
pairCount: pairCountBuffer
|
|
1427
|
+
});
|
|
1428
|
+
pipeline.with(bindGroup).dispatchThreads(pairCount);
|
|
1429
|
+
}
|
|
1430
|
+
//#endregion
|
|
1431
|
+
//#region src/gpu/kernels/degree-histogram/kernel.ts
|
|
1432
|
+
/**
|
|
1433
|
+
* TypeGPU compute kernel for degree computation.
|
|
1434
|
+
*
|
|
1435
|
+
* Computes degree per node from CSR row offsets.
|
|
1436
|
+
* Note: Histogram aggregation requires atomic operations or CPU reduction.
|
|
1437
|
+
*
|
|
1438
|
+
* @module gpu/kernels/degree-histogram/kernel
|
|
1439
|
+
*/
|
|
1440
|
+
/**
|
|
1441
|
+
* Bind group layout for degree kernel.
|
|
1442
|
+
*/
|
|
1443
|
+
var DegreeLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(src_default.bindGroupLayout({
|
|
1444
|
+
rowOffsets: { storage: data_exports.arrayOf(data_exports.u32) },
|
|
1445
|
+
degrees: {
|
|
1446
|
+
storage: data_exports.arrayOf(data_exports.u32),
|
|
1447
|
+
access: "mutable"
|
|
1448
|
+
},
|
|
1449
|
+
nodeCount: { uniform: data_exports.u32 }
|
|
1450
|
+
}), "DegreeLayout");
|
|
1451
|
+
/**
|
|
1452
|
+
* Degree compute pipeline: compute degree for each node.
|
|
1453
|
+
*
|
|
1454
|
+
* Each thread computes one node's degree from CSR row offsets:
|
|
1455
|
+
* degree[node] = rowOffsets[node+1] - rowOffsets[node]
|
|
1456
|
+
*/
|
|
1457
|
+
var degreePipeline = (($) => (globalThis.__TYPEGPU_META__ ??= /* @__PURE__ */ new WeakMap()).set($.f = ((node) => {
|
|
1458
|
+
"use gpu";
|
|
1459
|
+
if (node >= (DegreeLayout.$.nodeCount ?? 0)) return;
|
|
1460
|
+
const start = DegreeLayout.$.rowOffsets[node] ?? 0;
|
|
1461
|
+
const end = DegreeLayout.$.rowOffsets[__tsover_add(node, 1)] ?? 0;
|
|
1462
|
+
const deg = __tsover_sub(end, start);
|
|
1463
|
+
DegreeLayout.$.degrees[node] = deg;
|
|
1464
|
+
}), {
|
|
1465
|
+
v: 1,
|
|
1466
|
+
name: "degreePipeline",
|
|
1467
|
+
ast: {
|
|
1468
|
+
"params": [{
|
|
1469
|
+
"type": "i",
|
|
1470
|
+
"name": "node"
|
|
1471
|
+
}],
|
|
1472
|
+
"body": [0, [
|
|
1473
|
+
[
|
|
1474
|
+
13,
|
|
1475
|
+
"n",
|
|
1476
|
+
[
|
|
1477
|
+
3,
|
|
1478
|
+
[
|
|
1479
|
+
7,
|
|
1480
|
+
[
|
|
1481
|
+
7,
|
|
1482
|
+
"DegreeLayout",
|
|
1483
|
+
"$"
|
|
1484
|
+
],
|
|
1485
|
+
"nodeCount"
|
|
1486
|
+
],
|
|
1487
|
+
"??",
|
|
1488
|
+
[5, "0"]
|
|
1489
|
+
]
|
|
1490
|
+
],
|
|
1491
|
+
[
|
|
1492
|
+
11,
|
|
1493
|
+
[
|
|
1494
|
+
1,
|
|
1495
|
+
"node",
|
|
1496
|
+
">=",
|
|
1497
|
+
"n"
|
|
1498
|
+
],
|
|
1499
|
+
[0, [[10]]]
|
|
1500
|
+
],
|
|
1501
|
+
[
|
|
1502
|
+
13,
|
|
1503
|
+
"start",
|
|
1504
|
+
[
|
|
1505
|
+
3,
|
|
1506
|
+
[
|
|
1507
|
+
8,
|
|
1508
|
+
[
|
|
1509
|
+
7,
|
|
1510
|
+
[
|
|
1511
|
+
7,
|
|
1512
|
+
"DegreeLayout",
|
|
1513
|
+
"$"
|
|
1514
|
+
],
|
|
1515
|
+
"rowOffsets"
|
|
1516
|
+
],
|
|
1517
|
+
"node"
|
|
1518
|
+
],
|
|
1519
|
+
"??",
|
|
1520
|
+
[5, "0"]
|
|
1521
|
+
]
|
|
1522
|
+
],
|
|
1523
|
+
[
|
|
1524
|
+
13,
|
|
1525
|
+
"end",
|
|
1526
|
+
[
|
|
1527
|
+
3,
|
|
1528
|
+
[
|
|
1529
|
+
8,
|
|
1530
|
+
[
|
|
1531
|
+
7,
|
|
1532
|
+
[
|
|
1533
|
+
7,
|
|
1534
|
+
"DegreeLayout",
|
|
1535
|
+
"$"
|
|
1536
|
+
],
|
|
1537
|
+
"rowOffsets"
|
|
1538
|
+
],
|
|
1539
|
+
[
|
|
1540
|
+
1,
|
|
1541
|
+
"node",
|
|
1542
|
+
"+",
|
|
1543
|
+
[5, "1"]
|
|
1544
|
+
]
|
|
1545
|
+
],
|
|
1546
|
+
"??",
|
|
1547
|
+
[5, "0"]
|
|
1548
|
+
]
|
|
1549
|
+
],
|
|
1550
|
+
[
|
|
1551
|
+
13,
|
|
1552
|
+
"deg",
|
|
1553
|
+
[
|
|
1554
|
+
1,
|
|
1555
|
+
"end",
|
|
1556
|
+
"-",
|
|
1557
|
+
"start"
|
|
1558
|
+
]
|
|
1559
|
+
],
|
|
1560
|
+
[
|
|
1561
|
+
2,
|
|
1562
|
+
[
|
|
1563
|
+
8,
|
|
1564
|
+
[
|
|
1565
|
+
7,
|
|
1566
|
+
[
|
|
1567
|
+
7,
|
|
1568
|
+
"DegreeLayout",
|
|
1569
|
+
"$"
|
|
1570
|
+
],
|
|
1571
|
+
"degrees"
|
|
1572
|
+
],
|
|
1573
|
+
"node"
|
|
1574
|
+
],
|
|
1575
|
+
"=",
|
|
1576
|
+
"deg"
|
|
1577
|
+
]
|
|
1578
|
+
]],
|
|
1579
|
+
"externalNames": ["DegreeLayout"]
|
|
1580
|
+
},
|
|
1581
|
+
externals: () => ({ DegreeLayout })
|
|
1582
|
+
}) && $.f)({});
|
|
1583
|
+
/**
|
|
1584
|
+
* Dispatch degree computation on GPU.
|
|
1585
|
+
*
|
|
1586
|
+
* Note: This computes per-node degrees. Histogram aggregation should be
|
|
1587
|
+
* done on CPU or with a separate reduction kernel.
|
|
1588
|
+
*
|
|
1589
|
+
* @param root - TypeGPU root instance
|
|
1590
|
+
* @param csrBuffers - CSR matrix as typed buffers
|
|
1591
|
+
* @param degrees - Output degree array (u32, mutable)
|
|
1592
|
+
* @param nodeCount - Number of nodes
|
|
1593
|
+
*/
|
|
1594
|
+
function dispatchDegreeHistogram(root, csrBuffers, degrees, nodeCount) {
|
|
1595
|
+
const pipeline = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createGuardedComputePipeline(degreePipeline), "pipeline");
|
|
1596
|
+
const nodeCountBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.u32, nodeCount).$usage("uniform"), "nodeCountBuffer");
|
|
1597
|
+
const bindGroup = root.createBindGroup(DegreeLayout, {
|
|
1598
|
+
rowOffsets: csrBuffers.rowOffsets,
|
|
1599
|
+
degrees,
|
|
1600
|
+
nodeCount: nodeCountBuffer
|
|
1601
|
+
});
|
|
1602
|
+
pipeline.with(bindGroup).dispatchThreads(nodeCount);
|
|
1603
|
+
}
|
|
1604
|
+
//#endregion
|
|
1605
|
+
//#region src/gpu/operations.ts
|
|
1606
|
+
/**
|
|
1607
|
+
* Sparse matrix-vector multiply on GPU.
|
|
1608
|
+
*
|
|
1609
|
+
* Computes y = A * x where A is the graph adjacency matrix in CSR format.
|
|
1610
|
+
*
|
|
1611
|
+
* @param graph - Input graph
|
|
1612
|
+
* @param x - Input vector (must have length = graph.nodeCount)
|
|
1613
|
+
* @param options - Compute options (backend, root, signal)
|
|
1614
|
+
* @returns Result vector y
|
|
1615
|
+
*/
|
|
1616
|
+
async function gpuSpmv(graph, x, options) {
|
|
1617
|
+
const nodeCount = graph.nodeCount;
|
|
1618
|
+
if (x.length !== nodeCount) throw new Error(`Input vector length (${String(x.length)}) must match node count (${String(nodeCount)})`);
|
|
1619
|
+
const cpuFn = () => {
|
|
1620
|
+
const { csr } = graphToCSR(graph);
|
|
1621
|
+
const y = new Float32Array(nodeCount);
|
|
1622
|
+
for (let row = 0; row < nodeCount; row++) {
|
|
1623
|
+
const start = csr.rowOffsets[row] ?? 0;
|
|
1624
|
+
const end = csr.rowOffsets[row + 1] ?? 0;
|
|
1625
|
+
let sum = 0;
|
|
1626
|
+
for (let i = start; i < end; i++) {
|
|
1627
|
+
const col = csr.colIndices[i] ?? 0;
|
|
1628
|
+
const weight = csr.values?.[i] ?? 1;
|
|
1629
|
+
sum += weight * (x[col] ?? 0);
|
|
1630
|
+
}
|
|
1631
|
+
y[row] = sum;
|
|
1632
|
+
}
|
|
1633
|
+
return y;
|
|
1634
|
+
};
|
|
1635
|
+
const gpuFn = async (root) => {
|
|
1636
|
+
const { csr } = graphToCSR(graph);
|
|
1637
|
+
const csrBuffers = csrToTypedBuffers(root, csr);
|
|
1638
|
+
const xBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, nodeCount), Array.from(x)).$usage("storage"), "xBuffer");
|
|
1639
|
+
const yBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, nodeCount)).$usage("storage"), "yBuffer");
|
|
1640
|
+
dispatchSpmv(root, csrBuffers, xBuffer, yBuffer, nodeCount, csr.values !== void 0);
|
|
1641
|
+
const result = await yBuffer.read();
|
|
1642
|
+
return new Float32Array(result);
|
|
1643
|
+
};
|
|
1644
|
+
return withBackend({
|
|
1645
|
+
backend: options?.backend,
|
|
1646
|
+
root: options?.root,
|
|
1647
|
+
signal: options?.signal
|
|
1648
|
+
}, cpuFn, gpuFn);
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* PageRank via GPU-accelerated power iteration.
|
|
1652
|
+
*
|
|
1653
|
+
* @param graph - Input graph (will be converted to transpose CSR for in-edges)
|
|
1654
|
+
* @param options - Compute options plus damping factor and iteration count
|
|
1655
|
+
* @returns PageRank scores for each node
|
|
1656
|
+
*/
|
|
1657
|
+
async function gpuPageRank(graph, options) {
|
|
1658
|
+
const nodeCount = graph.nodeCount;
|
|
1659
|
+
const damping = options?.damping ?? .85;
|
|
1660
|
+
const iterations = options?.iterations ?? 20;
|
|
1661
|
+
const cpuFn = () => {
|
|
1662
|
+
const { csr } = graphToCSR(graph);
|
|
1663
|
+
const ranks = new Float32Array(nodeCount).fill(1 / nodeCount);
|
|
1664
|
+
const outDegrees = new Uint32Array(nodeCount);
|
|
1665
|
+
for (let i = 0; i < nodeCount; i++) outDegrees[i] = (csr.rowOffsets[i + 1] ?? 0) - (csr.rowOffsets[i] ?? 0);
|
|
1666
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
1667
|
+
const newRanks = new Float32Array(nodeCount);
|
|
1668
|
+
for (let v = 0; v < nodeCount; v++) {
|
|
1669
|
+
const start = csr.rowOffsets[v] ?? 0;
|
|
1670
|
+
const end = csr.rowOffsets[v + 1] ?? 0;
|
|
1671
|
+
let contribution = 0;
|
|
1672
|
+
for (let i = start; i < end; i++) {
|
|
1673
|
+
const source = csr.colIndices[i] ?? 0;
|
|
1674
|
+
const deg = outDegrees[source] ?? 0;
|
|
1675
|
+
if (deg > 0) contribution += (ranks[source] ?? 0) / deg;
|
|
1676
|
+
}
|
|
1677
|
+
newRanks[v] = (1 - damping) / nodeCount + damping * contribution;
|
|
1678
|
+
}
|
|
1679
|
+
ranks.set(newRanks);
|
|
1680
|
+
}
|
|
1681
|
+
return ranks;
|
|
1682
|
+
};
|
|
1683
|
+
const gpuFn = async (root) => {
|
|
1684
|
+
const { csr } = graphToCSR(graph);
|
|
1685
|
+
const csrBuffers = csrToTypedBuffers(root, csr);
|
|
1686
|
+
const outDegrees = new Uint32Array(nodeCount);
|
|
1687
|
+
for (let i = 0; i < nodeCount; i++) outDegrees[i] = (csr.rowOffsets[i + 1] ?? 0) - (csr.rowOffsets[i] ?? 0);
|
|
1688
|
+
const outDegreesBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, nodeCount), Array.from(outDegrees)).$usage("storage"), "outDegreesBuffer");
|
|
1689
|
+
const ranksBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, nodeCount), Array.from(new Float32Array(nodeCount).fill(1 / nodeCount))).$usage("storage"), "ranksBuffer");
|
|
1690
|
+
const newRanksBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, nodeCount)).$usage("storage"), "newRanksBuffer");
|
|
1691
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
1692
|
+
dispatchPagerank(root, csrBuffers, ranksBuffer, outDegreesBuffer, newRanksBuffer, nodeCount, damping);
|
|
1693
|
+
const newRanks = await newRanksBuffer.read();
|
|
1694
|
+
ranksBuffer.write(Array.from(newRanks));
|
|
1695
|
+
}
|
|
1696
|
+
const result = await ranksBuffer.read();
|
|
1697
|
+
return new Float32Array(result);
|
|
1698
|
+
};
|
|
1699
|
+
return withBackend({
|
|
1700
|
+
backend: options?.backend,
|
|
1701
|
+
root: options?.root,
|
|
1702
|
+
signal: options?.signal
|
|
1703
|
+
}, cpuFn, gpuFn);
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Batch Jaccard similarity for node pairs on GPU.
|
|
1707
|
+
*
|
|
1708
|
+
* @param graph - Input graph
|
|
1709
|
+
* @param pairs - Array of [u, v] node ID pairs
|
|
1710
|
+
* @param options - Compute options
|
|
1711
|
+
* @returns Jaccard coefficients for each pair
|
|
1712
|
+
*/
|
|
1713
|
+
async function gpuJaccardBatch(graph, pairs, options) {
|
|
1714
|
+
const pairCount = pairs.length;
|
|
1715
|
+
const { indexMap } = graphToCSR(graph);
|
|
1716
|
+
const cpuFn = () => {
|
|
1717
|
+
const { csr } = graphToCSR(graph);
|
|
1718
|
+
const results = new Float32Array(pairCount);
|
|
1719
|
+
for (let p = 0; p < pairCount; p++) {
|
|
1720
|
+
const pair = pairs[p];
|
|
1721
|
+
if (pair === void 0) continue;
|
|
1722
|
+
const [u, v] = pair;
|
|
1723
|
+
const uIdx = indexMap.nodeToIndex.get(u);
|
|
1724
|
+
const vIdx = indexMap.nodeToIndex.get(v);
|
|
1725
|
+
if (uIdx === void 0 || vIdx === void 0) {
|
|
1726
|
+
results[p] = 0;
|
|
1727
|
+
continue;
|
|
1728
|
+
}
|
|
1729
|
+
const uStart = csr.rowOffsets[uIdx] ?? 0;
|
|
1730
|
+
const uEnd = csr.rowOffsets[uIdx + 1] ?? 0;
|
|
1731
|
+
const vStart = csr.rowOffsets[vIdx] ?? 0;
|
|
1732
|
+
const vEnd = csr.rowOffsets[vIdx + 1] ?? 0;
|
|
1733
|
+
const uNeighbours = new Set(Array.from(csr.colIndices.slice(uStart, uEnd)));
|
|
1734
|
+
const vNeighbours = new Set(Array.from(csr.colIndices.slice(vStart, vEnd)));
|
|
1735
|
+
let intersection = 0;
|
|
1736
|
+
for (const n of uNeighbours) if (vNeighbours.has(n)) intersection++;
|
|
1737
|
+
const union = uNeighbours.size + vNeighbours.size - intersection;
|
|
1738
|
+
results[p] = union > 0 ? intersection / union : 0;
|
|
1739
|
+
}
|
|
1740
|
+
return results;
|
|
1741
|
+
};
|
|
1742
|
+
const gpuFn = async (root) => {
|
|
1743
|
+
const { csr } = graphToCSR(graph);
|
|
1744
|
+
const csrBuffers = csrToTypedBuffers(root, csr);
|
|
1745
|
+
const pairsU = new Uint32Array(pairCount);
|
|
1746
|
+
const pairsV = new Uint32Array(pairCount);
|
|
1747
|
+
for (let i = 0; i < pairCount; i++) {
|
|
1748
|
+
const pair = pairs[i];
|
|
1749
|
+
if (pair === void 0) continue;
|
|
1750
|
+
const uIdx = indexMap.nodeToIndex.get(pair[0]);
|
|
1751
|
+
const vIdx = indexMap.nodeToIndex.get(pair[1]);
|
|
1752
|
+
pairsU[i] = uIdx ?? 0;
|
|
1753
|
+
pairsV[i] = vIdx ?? 0;
|
|
1754
|
+
}
|
|
1755
|
+
const pairsUBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount), Array.from(pairsU)).$usage("storage"), "pairsUBuffer");
|
|
1756
|
+
const pairsVBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount), Array.from(pairsV)).$usage("storage"), "pairsVBuffer");
|
|
1757
|
+
const resultsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, pairCount)).$usage("storage"), "resultsBuffer");
|
|
1758
|
+
dispatchJaccard(root, csrBuffers, pairsUBuffer, pairsVBuffer, resultsBuffer, pairCount);
|
|
1759
|
+
const result = await resultsBuffer.read();
|
|
1760
|
+
return new Float32Array(result);
|
|
1761
|
+
};
|
|
1762
|
+
return withBackend({
|
|
1763
|
+
backend: options?.backend,
|
|
1764
|
+
root: options?.root,
|
|
1765
|
+
signal: options?.signal
|
|
1766
|
+
}, cpuFn, gpuFn);
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* BFS level assignment from source node on GPU.
|
|
1770
|
+
*
|
|
1771
|
+
* Note: Full GPU BFS requires atomics not yet available in TypeGPU.
|
|
1772
|
+
* This implementation uses CPU for now but maintains the API.
|
|
1773
|
+
*
|
|
1774
|
+
* @param graph - Input graph
|
|
1775
|
+
* @param source - Source node ID
|
|
1776
|
+
* @param options - Compute options
|
|
1777
|
+
* @returns BFS level for each node (-1 for unreachable)
|
|
1778
|
+
*/
|
|
1779
|
+
async function gpuBfsLevels(graph, source, options) {
|
|
1780
|
+
const nodeCount = graph.nodeCount;
|
|
1781
|
+
const { indexMap } = graphToCSR(graph);
|
|
1782
|
+
const cpuFn = () => {
|
|
1783
|
+
const { csr } = graphToCSR(graph);
|
|
1784
|
+
const levels = new Int32Array(nodeCount).fill(-1);
|
|
1785
|
+
const visited = new Uint8Array(nodeCount);
|
|
1786
|
+
const sourceIndex = indexMap.nodeToIndex.get(source);
|
|
1787
|
+
if (sourceIndex === void 0) throw new Error(`Source node ${source} not found in graph`);
|
|
1788
|
+
const queue = [sourceIndex];
|
|
1789
|
+
levels[sourceIndex] = 0;
|
|
1790
|
+
visited[sourceIndex] = 1;
|
|
1791
|
+
while (queue.length > 0) {
|
|
1792
|
+
const current = queue.shift();
|
|
1793
|
+
if (current === void 0) break;
|
|
1794
|
+
const currentLevel = levels[current] ?? 0;
|
|
1795
|
+
const start = csr.rowOffsets[current] ?? 0;
|
|
1796
|
+
const end = csr.rowOffsets[current + 1] ?? 0;
|
|
1797
|
+
for (let i = start; i < end; i++) {
|
|
1798
|
+
const neighbour = csr.colIndices[i] ?? 0;
|
|
1799
|
+
if (visited[neighbour] === 0) {
|
|
1800
|
+
visited[neighbour] = 1;
|
|
1801
|
+
levels[neighbour] = currentLevel + 1;
|
|
1802
|
+
queue.push(neighbour);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
return levels;
|
|
1807
|
+
};
|
|
1808
|
+
const gpuFn = (_root) => {
|
|
1809
|
+
return cpuFn();
|
|
1810
|
+
};
|
|
1811
|
+
return withBackend({
|
|
1812
|
+
backend: options?.backend,
|
|
1813
|
+
root: options?.root,
|
|
1814
|
+
signal: options?.signal
|
|
1815
|
+
}, cpuFn, gpuFn);
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Degree histogram and statistics on GPU.
|
|
1819
|
+
*
|
|
1820
|
+
* @param graph - Input graph
|
|
1821
|
+
* @param options - Compute options
|
|
1822
|
+
* @returns Degree statistics with histogram
|
|
1823
|
+
*/
|
|
1824
|
+
async function gpuDegreeHistogram(graph, options) {
|
|
1825
|
+
const nodeCount = graph.nodeCount;
|
|
1826
|
+
const cpuFn = () => {
|
|
1827
|
+
const { csr } = graphToCSR(graph);
|
|
1828
|
+
const degrees = new Uint32Array(nodeCount);
|
|
1829
|
+
for (let i = 0; i < nodeCount; i++) degrees[i] = (csr.rowOffsets[i + 1] ?? 0) - (csr.rowOffsets[i] ?? 0);
|
|
1830
|
+
const max = degrees.length > 0 ? Math.max(...degrees) : 0;
|
|
1831
|
+
const histogram = Array.from({ length: max + 1 }, () => 0);
|
|
1832
|
+
let sum = 0;
|
|
1833
|
+
let min = Infinity;
|
|
1834
|
+
for (const d of degrees) {
|
|
1835
|
+
histogram[d] = (histogram[d] ?? 0) + 1;
|
|
1836
|
+
sum += d;
|
|
1837
|
+
if (d < min) min = d;
|
|
1838
|
+
}
|
|
1839
|
+
if (degrees.length === 0) min = 0;
|
|
1840
|
+
return {
|
|
1841
|
+
min,
|
|
1842
|
+
max,
|
|
1843
|
+
mean: nodeCount > 0 ? sum / nodeCount : 0,
|
|
1844
|
+
histogram
|
|
1845
|
+
};
|
|
1846
|
+
};
|
|
1847
|
+
const gpuFn = async (root) => {
|
|
1848
|
+
const { csr } = graphToCSR(graph);
|
|
1849
|
+
const csrBuffers = csrToTypedBuffers(root, csr);
|
|
1850
|
+
const degreesBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, nodeCount)).$usage("storage"), "degreesBuffer");
|
|
1851
|
+
dispatchDegreeHistogram(root, csrBuffers, degreesBuffer, nodeCount);
|
|
1852
|
+
const degrees = await degreesBuffer.read();
|
|
1853
|
+
const max = degrees.length > 0 ? Math.max(...degrees) : 0;
|
|
1854
|
+
const histogram = Array.from({ length: max + 1 }, () => 0);
|
|
1855
|
+
let sum = 0;
|
|
1856
|
+
let min = Infinity;
|
|
1857
|
+
for (const d of degrees) {
|
|
1858
|
+
histogram[d] = (histogram[d] ?? 0) + 1;
|
|
1859
|
+
sum += d;
|
|
1860
|
+
if (d < min) min = d;
|
|
1861
|
+
}
|
|
1862
|
+
if (degrees.length === 0) min = 0;
|
|
1863
|
+
return {
|
|
1864
|
+
min,
|
|
1865
|
+
max,
|
|
1866
|
+
mean: nodeCount > 0 ? sum / nodeCount : 0,
|
|
1867
|
+
histogram
|
|
1868
|
+
};
|
|
1869
|
+
};
|
|
1870
|
+
return withBackend({
|
|
1871
|
+
backend: options?.backend,
|
|
1872
|
+
root: options?.root,
|
|
1873
|
+
signal: options?.signal
|
|
1874
|
+
}, cpuFn, gpuFn);
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Batch MI computation for multiple node pairs on GPU.
|
|
1878
|
+
*
|
|
1879
|
+
* For Jaccard-family variants, uses the intersection kernel for raw counts
|
|
1880
|
+
* then applies the variant-specific formula on CPU.
|
|
1881
|
+
*
|
|
1882
|
+
* @param graph - Input graph
|
|
1883
|
+
* @param pairs - Array of [u, v] node ID pairs
|
|
1884
|
+
* @param variant - MI variant to compute (default: jaccard)
|
|
1885
|
+
* @param options - Compute options
|
|
1886
|
+
* @returns MI scores and raw intersection data for each pair
|
|
1887
|
+
*/
|
|
1888
|
+
async function gpuMIBatch(graph, pairs, variant = "jaccard", options) {
|
|
1889
|
+
const pairCount = pairs.length;
|
|
1890
|
+
const { csr, indexMap } = graphToCSR(graph);
|
|
1891
|
+
const cpuFn = () => {
|
|
1892
|
+
const indexPairs = [];
|
|
1893
|
+
const validPair = [];
|
|
1894
|
+
for (const pair of pairs) {
|
|
1895
|
+
const uIdx = indexMap.nodeToIndex.get(pair[0]);
|
|
1896
|
+
const vIdx = indexMap.nodeToIndex.get(pair[1]);
|
|
1897
|
+
if (uIdx !== void 0 && vIdx !== void 0) {
|
|
1898
|
+
indexPairs.push([uIdx, vIdx]);
|
|
1899
|
+
validPair.push(true);
|
|
1900
|
+
} else {
|
|
1901
|
+
indexPairs.push([0, 0]);
|
|
1902
|
+
validPair.push(false);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
const { intersections, sizeUs, sizeVs } = intersectionBatch(csr.rowOffsets, csr.colIndices, indexPairs);
|
|
1906
|
+
const scores = new Float32Array(pairCount);
|
|
1907
|
+
for (let i = 0; i < pairCount; i++) {
|
|
1908
|
+
if (validPair[i] !== true) {
|
|
1909
|
+
scores[i] = 0;
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
const result = {
|
|
1913
|
+
intersection: intersections[i] ?? 0,
|
|
1914
|
+
sizeU: sizeUs[i] ?? 0,
|
|
1915
|
+
sizeV: sizeVs[i] ?? 0
|
|
1916
|
+
};
|
|
1917
|
+
const base = applyMIVariant(result, "jaccard");
|
|
1918
|
+
switch (variant) {
|
|
1919
|
+
case "scale": {
|
|
1920
|
+
const rho = computeGraphDensity(csr);
|
|
1921
|
+
scores[i] = rho > 0 ? base / rho : base;
|
|
1922
|
+
break;
|
|
1923
|
+
}
|
|
1924
|
+
case "skew": {
|
|
1925
|
+
const N = csr.rowOffsets.length - 1;
|
|
1926
|
+
const degU = result.sizeU;
|
|
1927
|
+
const degV = result.sizeV;
|
|
1928
|
+
scores[i] = base * (Math.log(N / (degU + 1)) * Math.log(N / (degV + 1)));
|
|
1929
|
+
break;
|
|
1930
|
+
}
|
|
1931
|
+
case "span": {
|
|
1932
|
+
const clustering = computeClusteringCoefficients(csr);
|
|
1933
|
+
const pair = pairs[i];
|
|
1934
|
+
const u0 = pair ? indexMap.nodeToIndex.get(pair[0]) : void 0;
|
|
1935
|
+
const v0 = pair ? indexMap.nodeToIndex.get(pair[1]) : void 0;
|
|
1936
|
+
const cu = (u0 !== void 0 ? clustering[u0] : void 0) ?? 0;
|
|
1937
|
+
const cv = (v0 !== void 0 ? clustering[v0] : void 0) ?? 0;
|
|
1938
|
+
scores[i] = base * (1 - Math.max(cu, cv));
|
|
1939
|
+
break;
|
|
1940
|
+
}
|
|
1941
|
+
default: scores[i] = applyMIVariant(result, variant);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
return {
|
|
1945
|
+
scores,
|
|
1946
|
+
intersections,
|
|
1947
|
+
sizeUs,
|
|
1948
|
+
sizeVs
|
|
1949
|
+
};
|
|
1950
|
+
};
|
|
1951
|
+
const gpuFn = async (root) => {
|
|
1952
|
+
const csrBuffers = csrToTypedBuffers(root, csr);
|
|
1953
|
+
const uArr = new Uint32Array(pairCount);
|
|
1954
|
+
const vArr = new Uint32Array(pairCount);
|
|
1955
|
+
const validPair = Array.from({ length: pairCount }, () => false);
|
|
1956
|
+
for (let i = 0; i < pairCount; i++) {
|
|
1957
|
+
const p = pairs[i];
|
|
1958
|
+
const uIdxRaw = p ? indexMap.nodeToIndex.get(p[0]) : void 0;
|
|
1959
|
+
const vIdxRaw = p ? indexMap.nodeToIndex.get(p[1]) : void 0;
|
|
1960
|
+
const uIdx = uIdxRaw ?? 0;
|
|
1961
|
+
const vIdx = vIdxRaw ?? 0;
|
|
1962
|
+
if (uIdxRaw !== void 0 && vIdxRaw !== void 0) {
|
|
1963
|
+
uArr[i] = uIdx;
|
|
1964
|
+
vArr[i] = vIdx;
|
|
1965
|
+
validPair[i] = true;
|
|
1966
|
+
} else {
|
|
1967
|
+
uArr[i] = 0;
|
|
1968
|
+
vArr[i] = 0;
|
|
1969
|
+
validPair[i] = false;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
const pairsUBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount), Array.from(uArr)).$usage("storage"), "pairsUBuffer");
|
|
1973
|
+
const pairsVBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount), Array.from(vArr)).$usage("storage"), "pairsVBuffer");
|
|
1974
|
+
const intersectionsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount)).$usage("storage"), "intersectionsBuffer");
|
|
1975
|
+
const sizeUsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount)).$usage("storage"), "sizeUsBuffer");
|
|
1976
|
+
const sizeVsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pairCount)).$usage("storage"), "sizeVsBuffer");
|
|
1977
|
+
if (variant === "adamic-adar") {
|
|
1978
|
+
const resultsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, pairCount)).$usage("storage"), "resultsBuffer");
|
|
1979
|
+
const { dispatchAdamicAdar } = await import("./kernel-CvnRsF7E.js");
|
|
1980
|
+
dispatchAdamicAdar(root, csrBuffers, pairsUBuffer, pairsVBuffer, resultsBuffer, intersectionsBuffer, sizeUsBuffer, sizeVsBuffer, pairCount);
|
|
1981
|
+
const scoresRaw = await resultsBuffer.read();
|
|
1982
|
+
const intersectionsRaw = await intersectionsBuffer.read();
|
|
1983
|
+
const sizeUsRaw = await sizeUsBuffer.read();
|
|
1984
|
+
const sizeVsRaw = await sizeVsBuffer.read();
|
|
1985
|
+
const scores = new Float32Array(scoresRaw);
|
|
1986
|
+
const intersections = new Uint32Array(intersectionsRaw);
|
|
1987
|
+
const sizeUs = new Uint32Array(sizeUsRaw);
|
|
1988
|
+
const sizeVs = new Uint32Array(sizeVsRaw);
|
|
1989
|
+
for (let i = 0; i < pairCount; i++) if (validPair[i] !== true) {
|
|
1990
|
+
scores[i] = 0;
|
|
1991
|
+
intersections[i] = 0;
|
|
1992
|
+
sizeUs[i] = 0;
|
|
1993
|
+
sizeVs[i] = 0;
|
|
1994
|
+
}
|
|
1995
|
+
return {
|
|
1996
|
+
scores,
|
|
1997
|
+
intersections,
|
|
1998
|
+
sizeUs,
|
|
1999
|
+
sizeVs
|
|
2000
|
+
};
|
|
2001
|
+
} else {
|
|
2002
|
+
const { dispatchIntersection } = await import("./kernel-6deK9fh1.js");
|
|
2003
|
+
dispatchIntersection(root, csrBuffers, pairsUBuffer, pairsVBuffer, intersectionsBuffer, sizeUsBuffer, sizeVsBuffer, pairCount);
|
|
2004
|
+
const intersectionsRaw = await intersectionsBuffer.read();
|
|
2005
|
+
const sizeUsRaw = await sizeUsBuffer.read();
|
|
2006
|
+
const sizeVsRaw = await sizeVsBuffer.read();
|
|
2007
|
+
const intersections = new Uint32Array(intersectionsRaw);
|
|
2008
|
+
const sizeUs = new Uint32Array(sizeUsRaw);
|
|
2009
|
+
const sizeVs = new Uint32Array(sizeVsRaw);
|
|
2010
|
+
const scores = new Float32Array(pairCount);
|
|
2011
|
+
if (variant === "scale" || variant === "skew" || variant === "span") {
|
|
2012
|
+
const clustering = variant === "span" ? computeClusteringCoefficients(csr) : void 0;
|
|
2013
|
+
const N = csr.rowOffsets.length - 1;
|
|
2014
|
+
const rho = variant === "scale" ? computeGraphDensity(csr) : void 0;
|
|
2015
|
+
for (let i = 0; i < pairCount; i++) {
|
|
2016
|
+
if (validPair[i] !== true) {
|
|
2017
|
+
scores[i] = 0;
|
|
2018
|
+
continue;
|
|
2019
|
+
}
|
|
2020
|
+
const result = {
|
|
2021
|
+
intersection: intersections[i] ?? 0,
|
|
2022
|
+
sizeU: sizeUs[i] ?? 0,
|
|
2023
|
+
sizeV: sizeVs[i] ?? 0
|
|
2024
|
+
};
|
|
2025
|
+
const base = applyMIVariant(result, "jaccard");
|
|
2026
|
+
switch (variant) {
|
|
2027
|
+
case "scale":
|
|
2028
|
+
if (rho !== void 0 && rho > 0) scores[i] = base / rho;
|
|
2029
|
+
else scores[i] = base;
|
|
2030
|
+
break;
|
|
2031
|
+
case "skew": {
|
|
2032
|
+
const degU = result.sizeU;
|
|
2033
|
+
const degV = result.sizeV;
|
|
2034
|
+
scores[i] = base * (Math.log(N / (degU + 1)) * Math.log(N / (degV + 1)));
|
|
2035
|
+
break;
|
|
2036
|
+
}
|
|
2037
|
+
case "span": {
|
|
2038
|
+
const pair = pairs[i];
|
|
2039
|
+
const uId = pair ? pair[0] : void 0;
|
|
2040
|
+
const vId = pair ? pair[1] : void 0;
|
|
2041
|
+
const uIdx = uId !== void 0 ? indexMap.nodeToIndex.get(uId) : void 0;
|
|
2042
|
+
const vIdx = vId !== void 0 ? indexMap.nodeToIndex.get(vId) : void 0;
|
|
2043
|
+
const cu = uIdx !== void 0 ? clustering?.[uIdx] ?? 0 : 0;
|
|
2044
|
+
const cv = vIdx !== void 0 ? clustering?.[vIdx] ?? 0 : 0;
|
|
2045
|
+
scores[i] = base * (1 - Math.max(cu, cv));
|
|
2046
|
+
break;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
} else for (let i = 0; i < pairCount; i++) {
|
|
2051
|
+
if (validPair[i] !== true) {
|
|
2052
|
+
scores[i] = 0;
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
scores[i] = applyMIVariant({
|
|
2056
|
+
intersection: intersections[i] ?? 0,
|
|
2057
|
+
sizeU: sizeUs[i] ?? 0,
|
|
2058
|
+
sizeV: sizeVs[i] ?? 0
|
|
2059
|
+
}, variant);
|
|
2060
|
+
}
|
|
2061
|
+
return {
|
|
2062
|
+
scores,
|
|
2063
|
+
intersections,
|
|
2064
|
+
sizeUs,
|
|
2065
|
+
sizeVs
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
return withBackend({
|
|
2070
|
+
backend: options?.backend,
|
|
2071
|
+
root: options?.root,
|
|
2072
|
+
signal: options?.signal
|
|
2073
|
+
}, cpuFn, gpuFn);
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* Apply MI variant formula to intersection result.
|
|
2077
|
+
*/
|
|
2078
|
+
function computeGraphDensity(csr) {
|
|
2079
|
+
const n = csr.rowOffsets.length - 1;
|
|
2080
|
+
const m = csr.colIndices.length;
|
|
2081
|
+
return n > 1 ? 2 * m / (n * (n - 1)) : 0;
|
|
2082
|
+
}
|
|
2083
|
+
function computeClusteringCoefficients(csr) {
|
|
2084
|
+
const n = csr.rowOffsets.length - 1;
|
|
2085
|
+
const coeffs = new Array(n).fill(0);
|
|
2086
|
+
for (let v = 0; v < n; v++) {
|
|
2087
|
+
const start = csr.rowOffsets[v] ?? 0;
|
|
2088
|
+
const end = csr.rowOffsets[v + 1] ?? 0;
|
|
2089
|
+
const deg = end - start;
|
|
2090
|
+
if (deg < 2) {
|
|
2091
|
+
coeffs[v] = 0;
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
const neighbours = new Set(csr.colIndices.slice(start, end));
|
|
2095
|
+
let links = 0;
|
|
2096
|
+
for (const u of neighbours) {
|
|
2097
|
+
const uStart = csr.rowOffsets[u] ?? 0;
|
|
2098
|
+
const uEnd = csr.rowOffsets[u + 1] ?? 0;
|
|
2099
|
+
for (let k = uStart; k < uEnd; k++) {
|
|
2100
|
+
const colIdx = csr.colIndices[k] ?? 0;
|
|
2101
|
+
if (neighbours.has(colIdx)) links++;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
coeffs[v] = 2 * links / (deg * (deg - 1));
|
|
2105
|
+
}
|
|
2106
|
+
return coeffs;
|
|
2107
|
+
}
|
|
2108
|
+
function applyMIVariant(result, variant) {
|
|
2109
|
+
switch (variant) {
|
|
2110
|
+
case "jaccard": return jaccardFromIntersection(result);
|
|
2111
|
+
case "cosine": return cosineFromIntersection(result);
|
|
2112
|
+
case "sorensen": return sorensenDiceFromIntersection(result);
|
|
2113
|
+
case "overlap-coefficient": return overlapFromIntersection(result);
|
|
2114
|
+
case "hub-promoted": return hubPromotedFromIntersection(result);
|
|
2115
|
+
case "resource-allocation": return jaccardFromIntersection(result);
|
|
2116
|
+
case "adamic-adar": return jaccardFromIntersection(result);
|
|
2117
|
+
case "scale":
|
|
2118
|
+
case "skew":
|
|
2119
|
+
case "span":
|
|
2120
|
+
case "etch":
|
|
2121
|
+
case "notch":
|
|
2122
|
+
case "adaptive": return jaccardFromIntersection(result);
|
|
2123
|
+
default: return jaccardFromIntersection(result);
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2127
|
+
* Assign points to nearest centroids using GPU.
|
|
2128
|
+
*
|
|
2129
|
+
* For each point, computes distance to all centroids and assigns to nearest.
|
|
2130
|
+
* This is the main parallelizable operation in K-means clustering.
|
|
2131
|
+
*
|
|
2132
|
+
* @param points - Array of 3D points to assign
|
|
2133
|
+
* @param centroids - Array of 3D centroid coordinates
|
|
2134
|
+
* @param options - Compute options
|
|
2135
|
+
* @returns Assignment indices and distances
|
|
2136
|
+
*/
|
|
2137
|
+
async function gpuKMeansAssign(points, centroids, options) {
|
|
2138
|
+
const pointCount = points.length;
|
|
2139
|
+
const k = centroids.length;
|
|
2140
|
+
const cpuFn = () => {
|
|
2141
|
+
const assignments = new Uint32Array(pointCount);
|
|
2142
|
+
const distances = new Float32Array(pointCount);
|
|
2143
|
+
for (let i = 0; i < pointCount; i++) {
|
|
2144
|
+
const point = points[i];
|
|
2145
|
+
if (point === void 0) {
|
|
2146
|
+
assignments[i] = 0;
|
|
2147
|
+
distances[i] = 0;
|
|
2148
|
+
continue;
|
|
2149
|
+
}
|
|
2150
|
+
const [px, py, pz] = point;
|
|
2151
|
+
let minDist = Infinity;
|
|
2152
|
+
let minIdx = 0;
|
|
2153
|
+
for (let j = 0; j < k; j++) {
|
|
2154
|
+
const centroid = centroids[j];
|
|
2155
|
+
if (centroid === void 0) continue;
|
|
2156
|
+
const [cx, cy, cz] = centroid;
|
|
2157
|
+
const dx = px - cx;
|
|
2158
|
+
const dy = py - cy;
|
|
2159
|
+
const dz = pz - cz;
|
|
2160
|
+
const distSq = dx * dx + dy * dy + dz * dz;
|
|
2161
|
+
if (distSq < minDist) {
|
|
2162
|
+
minDist = distSq;
|
|
2163
|
+
minIdx = j;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
assignments[i] = minIdx;
|
|
2167
|
+
distances[i] = minDist;
|
|
2168
|
+
}
|
|
2169
|
+
return {
|
|
2170
|
+
assignments,
|
|
2171
|
+
distances
|
|
2172
|
+
};
|
|
2173
|
+
};
|
|
2174
|
+
const gpuFn = async (root) => {
|
|
2175
|
+
if (pointCount < 100) return cpuFn();
|
|
2176
|
+
const pointsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.vec3f, pointCount), Array.from(points)).$usage("storage"), "pointsBuffer");
|
|
2177
|
+
const centroidsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.vec3f, k), Array.from(centroids)).$usage("storage"), "centroidsBuffer");
|
|
2178
|
+
const assignmentsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, pointCount)).$usage("storage"), "assignmentsBuffer");
|
|
2179
|
+
const distancesBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, pointCount)).$usage("storage"), "distancesBuffer");
|
|
2180
|
+
const { dispatchKMeansAssign: dispatch } = await import("./kernel-CigCjrts.js");
|
|
2181
|
+
dispatch(root, pointsBuffer, centroidsBuffer, assignmentsBuffer, distancesBuffer, pointCount, k);
|
|
2182
|
+
const assignments = await assignmentsBuffer.read();
|
|
2183
|
+
const distances = await distancesBuffer.read();
|
|
2184
|
+
return {
|
|
2185
|
+
assignments: new Uint32Array(assignments),
|
|
2186
|
+
distances: new Float32Array(distances)
|
|
2187
|
+
};
|
|
2188
|
+
};
|
|
2189
|
+
return withBackend({
|
|
2190
|
+
backend: options?.backend,
|
|
2191
|
+
root: options?.root,
|
|
2192
|
+
signal: options?.signal
|
|
2193
|
+
}, cpuFn, gpuFn);
|
|
2194
|
+
}
|
|
2195
|
+
//#endregion
|
|
2196
|
+
export { gpuMIBatch as a, withBackend as c, detectWebGPU as d, isWebGPUAvailable as f, gpuKMeansAssign as i, GPUNotAvailableError as l, gpuDegreeHistogram as n, gpuPageRank as o, gpuJaccardBatch as r, gpuSpmv as s, gpuBfsLevels as t, assertWebGPUAvailable as u };
|
|
2197
|
+
|
|
2198
|
+
//# sourceMappingURL=operations-D9otVlIH.js.map
|