graphwise 1.9.0 → 1.10.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 +38 -19
- package/dist/index/index.js +10 -8
- 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/grasp-gpu.d.ts +40 -0
- package/dist/seeds/grasp-gpu.d.ts.map +1 -0
- package/dist/seeds/index.cjs +1 -1
- package/dist/seeds/index.js +1 -1
- 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,242 @@
|
|
|
1
|
+
import { n as data_exports } from "./typegpu-DwnJf28i.js";
|
|
2
|
+
//#region src/gpu/csr.ts
|
|
3
|
+
/**
|
|
4
|
+
* Compressed Sparse Row (CSR) matrix representation for GPU computation.
|
|
5
|
+
*
|
|
6
|
+
* CSR format is memory-efficient for sparse graphs and maps well to
|
|
7
|
+
* GPU parallel operations. The format stores adjacency information
|
|
8
|
+
* in three arrays: row offsets, column indices, and optional values.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Convert a ReadableGraph to CSR format.
|
|
12
|
+
*
|
|
13
|
+
* For undirected graphs, each edge is stored twice (once in each direction).
|
|
14
|
+
* For directed graphs, edges are stored in the out-direction by default.
|
|
15
|
+
*
|
|
16
|
+
* @param graph - The graph to convert
|
|
17
|
+
* @param direction - Edge direction to include (default: 'out' for directed, 'both' for undirected)
|
|
18
|
+
* @returns CSR representation with index mapping
|
|
19
|
+
*/
|
|
20
|
+
function graphToCSR(graph, direction = graph.directed ? "out" : "both") {
|
|
21
|
+
const nodeToIndex = /* @__PURE__ */ new Map();
|
|
22
|
+
const indexToNode = [];
|
|
23
|
+
for (const nodeId of graph.nodeIds()) {
|
|
24
|
+
const index = indexToNode.length;
|
|
25
|
+
nodeToIndex.set(nodeId, index);
|
|
26
|
+
indexToNode.push(nodeId);
|
|
27
|
+
}
|
|
28
|
+
const nodeCount = indexToNode.length;
|
|
29
|
+
const degrees = new Uint32Array(nodeCount);
|
|
30
|
+
for (const nodeId of graph.nodeIds()) {
|
|
31
|
+
const srcIndex = nodeToIndex.get(nodeId);
|
|
32
|
+
if (srcIndex === void 0) continue;
|
|
33
|
+
degrees[srcIndex] = graph.degree(nodeId, direction);
|
|
34
|
+
}
|
|
35
|
+
let totalEdges = 0;
|
|
36
|
+
for (let i = 0; i < nodeCount; i++) totalEdges += degrees[i] ?? 0;
|
|
37
|
+
const rowOffsets = new Uint32Array(nodeCount + 1);
|
|
38
|
+
for (let i = 0; i < nodeCount; i++) rowOffsets[i + 1] = (rowOffsets[i] ?? 0) + (degrees[i] ?? 0);
|
|
39
|
+
const colIndices = new Uint32Array(totalEdges);
|
|
40
|
+
const values = new Float32Array(totalEdges);
|
|
41
|
+
for (const nodeId of graph.nodeIds()) {
|
|
42
|
+
const srcIndex = nodeToIndex.get(nodeId);
|
|
43
|
+
if (srcIndex === void 0) continue;
|
|
44
|
+
const baseOffset = rowOffsets[srcIndex] ?? 0;
|
|
45
|
+
let localOffset = 0;
|
|
46
|
+
for (const neighbourId of graph.neighbours(nodeId, direction)) {
|
|
47
|
+
const dstIndex = nodeToIndex.get(neighbourId);
|
|
48
|
+
if (dstIndex === void 0) continue;
|
|
49
|
+
const edgeIdx = baseOffset + localOffset;
|
|
50
|
+
colIndices[edgeIdx] = dstIndex;
|
|
51
|
+
values[edgeIdx] = graph.getEdge(nodeId, neighbourId)?.weight ?? 1;
|
|
52
|
+
localOffset++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
csr: {
|
|
57
|
+
rowOffsets,
|
|
58
|
+
colIndices,
|
|
59
|
+
values,
|
|
60
|
+
nodeCount,
|
|
61
|
+
edgeCount: graph.directed ? graph.edgeCount : graph.edgeCount * 2
|
|
62
|
+
},
|
|
63
|
+
indexMap: {
|
|
64
|
+
nodeToIndex,
|
|
65
|
+
indexToNode
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create TypeGPU typed buffers from a CSR matrix.
|
|
71
|
+
*
|
|
72
|
+
* Uses TypeGPU's typed buffer API for type-safe GPU computation.
|
|
73
|
+
* Buffers are created with storage usage for compute operations.
|
|
74
|
+
*
|
|
75
|
+
* @param root - TypeGPU root instance
|
|
76
|
+
* @param csr - CSR matrix to upload
|
|
77
|
+
* @returns Typed buffer group
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* import { initGPU, csrToTypedBuffers, graphToCSR } from "graphwise/gpu";
|
|
82
|
+
*
|
|
83
|
+
* const root = await initGPU();
|
|
84
|
+
* const { csr } = graphToCSR(graph);
|
|
85
|
+
* const buffers = csrToTypedBuffers(root, csr);
|
|
86
|
+
*
|
|
87
|
+
* // Read data back to CPU
|
|
88
|
+
* const rowOffsets = await buffers.rowOffsets.read();
|
|
89
|
+
* console.log(rowOffsets); // Uint32Array
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
function csrToTypedBuffers(root, csr) {
|
|
93
|
+
const rowOffsetsArray = Array.from(csr.rowOffsets);
|
|
94
|
+
const colIndicesArray = Array.from(csr.colIndices);
|
|
95
|
+
const rowOffsetsBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, csr.rowOffsets.length), rowOffsetsArray).$usage("storage"), "rowOffsetsBuffer");
|
|
96
|
+
const colIndicesBuffer = (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.u32, csr.colIndices.length), colIndicesArray).$usage("storage"), "colIndicesBuffer");
|
|
97
|
+
if (csr.values !== void 0) {
|
|
98
|
+
const valuesArray = Array.from(csr.values);
|
|
99
|
+
return {
|
|
100
|
+
rowOffsets: rowOffsetsBuffer,
|
|
101
|
+
colIndices: colIndicesBuffer,
|
|
102
|
+
values: (globalThis.__TYPEGPU_AUTONAME__ ?? ((a) => a))(root.createBuffer(data_exports.arrayOf(data_exports.f32, csr.values.length), valuesArray).$usage("storage"), "valuesBuffer"),
|
|
103
|
+
nodeCount: csr.nodeCount,
|
|
104
|
+
edgeCount: csr.edgeCount
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
rowOffsets: rowOffsetsBuffer,
|
|
109
|
+
colIndices: colIndicesBuffer,
|
|
110
|
+
nodeCount: csr.nodeCount,
|
|
111
|
+
edgeCount: csr.edgeCount
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/gpu/kernels/intersection/logic.ts
|
|
116
|
+
/**
|
|
117
|
+
* Binary search in a sorted subarray of colIndices.
|
|
118
|
+
*
|
|
119
|
+
* @param colIndices - CSR column indices array (must be sorted per row)
|
|
120
|
+
* @param start - Start index (inclusive)
|
|
121
|
+
* @param end - End index (exclusive)
|
|
122
|
+
* @param target - Value to search for
|
|
123
|
+
* @returns true if target is found between colIndices[start] and colIndices[end-1]
|
|
124
|
+
*/
|
|
125
|
+
function binarySearch(colIndices, start, end, target) {
|
|
126
|
+
let lo = start;
|
|
127
|
+
let hi = end;
|
|
128
|
+
while (lo < hi) {
|
|
129
|
+
const mid = lo + Math.floor((hi - lo) / 2);
|
|
130
|
+
const midVal = colIndices[mid] ?? 0;
|
|
131
|
+
if (midVal === target) return true;
|
|
132
|
+
else if (midVal < target) lo = mid + 1;
|
|
133
|
+
else hi = mid;
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Compute intersection stats for a single pair of nodes in CSR format.
|
|
139
|
+
*
|
|
140
|
+
* Neighbours must be sorted (CSR guarantees this from graphToCSR).
|
|
141
|
+
*
|
|
142
|
+
* @param rowOffsets - CSR row offset array
|
|
143
|
+
* @param colIndices - CSR column indices array
|
|
144
|
+
* @param u - First node index
|
|
145
|
+
* @param v - Second node index
|
|
146
|
+
* @returns Intersection result with sizes
|
|
147
|
+
*/
|
|
148
|
+
function intersectionPair(rowOffsets, colIndices, u, v) {
|
|
149
|
+
const uStart = rowOffsets[u] ?? 0;
|
|
150
|
+
const uEnd = rowOffsets[u + 1] ?? 0;
|
|
151
|
+
const vStart = rowOffsets[v] ?? 0;
|
|
152
|
+
const vEnd = rowOffsets[v + 1] ?? 0;
|
|
153
|
+
const sizeU = uEnd - uStart;
|
|
154
|
+
const sizeV = vEnd - vStart;
|
|
155
|
+
if (sizeU === 0 || sizeV === 0) return {
|
|
156
|
+
intersection: 0,
|
|
157
|
+
sizeU,
|
|
158
|
+
sizeV
|
|
159
|
+
};
|
|
160
|
+
let intersection = 0;
|
|
161
|
+
if (sizeU <= sizeV) {
|
|
162
|
+
for (let i = uStart; i < uEnd; i++) if (binarySearch(colIndices, vStart, vEnd, colIndices[i] ?? 0)) intersection++;
|
|
163
|
+
} else for (let i = vStart; i < vEnd; i++) if (binarySearch(colIndices, uStart, uEnd, colIndices[i] ?? 0)) intersection++;
|
|
164
|
+
return {
|
|
165
|
+
intersection,
|
|
166
|
+
sizeU,
|
|
167
|
+
sizeV
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Batch intersection stats for multiple node pairs.
|
|
172
|
+
*
|
|
173
|
+
* @param rowOffsets - CSR row offset array
|
|
174
|
+
* @param colIndices - CSR column indices array
|
|
175
|
+
* @param pairs - Array of [u, v] node index pairs
|
|
176
|
+
* @returns Object with parallel arrays: intersections, sizeUs, sizeVs
|
|
177
|
+
*/
|
|
178
|
+
function intersectionBatch(rowOffsets, colIndices, pairs) {
|
|
179
|
+
const n = pairs.length;
|
|
180
|
+
const intersections = new Uint32Array(n);
|
|
181
|
+
const sizeUs = new Uint32Array(n);
|
|
182
|
+
const sizeVs = new Uint32Array(n);
|
|
183
|
+
for (let i = 0; i < n; i++) {
|
|
184
|
+
const pair = pairs[i];
|
|
185
|
+
if (pair !== void 0) {
|
|
186
|
+
const [u, v] = pair;
|
|
187
|
+
const result = intersectionPair(rowOffsets, colIndices, u, v);
|
|
188
|
+
intersections[i] = result.intersection;
|
|
189
|
+
sizeUs[i] = result.sizeU;
|
|
190
|
+
sizeVs[i] = result.sizeV;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
intersections,
|
|
195
|
+
sizeUs,
|
|
196
|
+
sizeVs
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Compute Jaccard from intersection stats.
|
|
201
|
+
* J = intersection / (sizeU + sizeV - intersection)
|
|
202
|
+
*/
|
|
203
|
+
function jaccardFromIntersection(result) {
|
|
204
|
+
const union = result.sizeU + result.sizeV - result.intersection;
|
|
205
|
+
return union === 0 ? 0 : result.intersection / union;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Compute Cosine similarity from intersection stats.
|
|
209
|
+
* Cosine = intersection / sqrt(sizeU * sizeV)
|
|
210
|
+
*/
|
|
211
|
+
function cosineFromIntersection(result) {
|
|
212
|
+
const denominator = Math.sqrt(result.sizeU * result.sizeV);
|
|
213
|
+
return denominator === 0 ? 0 : result.intersection / denominator;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Compute Sorensen-Dice from intersection stats.
|
|
217
|
+
* SD = 2 * intersection / (sizeU + sizeV)
|
|
218
|
+
*/
|
|
219
|
+
function sorensenDiceFromIntersection(result) {
|
|
220
|
+
const denominator = result.sizeU + result.sizeV;
|
|
221
|
+
return denominator === 0 ? 0 : 2 * result.intersection / denominator;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Compute Overlap coefficient from intersection stats.
|
|
225
|
+
* Overlap = intersection / min(sizeU, sizeV)
|
|
226
|
+
*/
|
|
227
|
+
function overlapFromIntersection(result) {
|
|
228
|
+
const minSize = Math.min(result.sizeU, result.sizeV);
|
|
229
|
+
return minSize === 0 ? 0 : result.intersection / minSize;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Compute Hub Promoted from intersection stats.
|
|
233
|
+
* HP = intersection / min(sizeU, sizeV)
|
|
234
|
+
* Note: Same formula as Overlap coefficient
|
|
235
|
+
*/
|
|
236
|
+
function hubPromotedFromIntersection(result) {
|
|
237
|
+
return overlapFromIntersection(result);
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
export { overlapFromIntersection as a, graphToCSR as c, jaccardFromIntersection as i, hubPromotedFromIntersection as n, sorensenDiceFromIntersection as o, intersectionBatch as r, csrToTypedBuffers as s, cosineFromIntersection as t };
|
|
241
|
+
|
|
242
|
+
//# sourceMappingURL=logic-DyBzRg1A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic-DyBzRg1A.js","names":[],"sources":["../src/gpu/csr.ts","../src/gpu/kernels/intersection/logic.ts"],"sourcesContent":["/**\n * Compressed Sparse Row (CSR) matrix representation for GPU computation.\n *\n * CSR format is memory-efficient for sparse graphs and maps well to\n * GPU parallel operations. The format stores adjacency information\n * in three arrays: row offsets, column indices, and optional values.\n */\n\nimport { d, type StorageFlag, type TgpuBuffer } from \"typegpu\";\nimport type { NodeId, Direction, NodeData, EdgeData } from \"../graph/types\";\nimport type { ReadableGraph } from \"../graph\";\nimport type { GraphwiseGPURoot } from \"./root\";\n\n/**\n * CSR matrix representation of a graph adjacency structure.\n *\n * The rowOffsets array has length nodeCount + 1, where rowOffsets[i]\n * gives the start index in colIndices for node i's neighbours.\n * The neighbours of node i are colIndices[rowOffsets[i] : rowOffsets[i+1]].\n */\nexport interface CSRMatrix {\n\t/** Row offsets array (length: nodeCount + 1) */\n\treadonly rowOffsets: Uint32Array;\n\t/** Column indices array (length: edgeCount for directed, 2*edgeCount for undirected) */\n\treadonly colIndices: Uint32Array;\n\t/** Optional edge weights aligned with colIndices */\n\treadonly values?: Float32Array;\n\t/** Number of nodes in the graph */\n\treadonly nodeCount: number;\n\t/** Number of directed edges (undirected edges counted once) */\n\treadonly edgeCount: number;\n}\n\n/**\n * Mapping from node IDs to CSR indices.\n *\n * Required because CSR uses dense integer indices while graphs\n * may use arbitrary string identifiers.\n */\nexport interface CSRIndexMap {\n\t/** Map from NodeId to CSR row index */\n\treadonly nodeToIndex: ReadonlyMap<NodeId, number>;\n\t/** Map from CSR row index to NodeId */\n\treadonly indexToNode: readonly NodeId[];\n}\n\n/**\n * Combined CSR matrix with index mapping.\n */\nexport interface CSRGraph {\n\treadonly csr: CSRMatrix;\n\treadonly indexMap: CSRIndexMap;\n}\n\n/**\n * Group of TypeGPU typed buffers holding a CSR matrix.\n *\n * Uses TypeGPU's typed buffer API for type-safe GPU computation.\n * Buffers are created with storage usage for compute operations.\n */\nexport interface TypedBufferGroup {\n\t/** Buffer containing rowOffsets data (u32 array) */\n\treadonly rowOffsets: TgpuBuffer<ReturnType<typeof d.arrayOf<typeof d.u32>>> &\n\t\tStorageFlag;\n\t/** Buffer containing colIndices data (u32 array) */\n\treadonly colIndices: TgpuBuffer<ReturnType<typeof d.arrayOf<typeof d.u32>>> &\n\t\tStorageFlag;\n\t/** Buffer containing values data (f32 array, optional) */\n\treadonly values?: TgpuBuffer<ReturnType<typeof d.arrayOf<typeof d.f32>>> &\n\t\tStorageFlag;\n\t/** Number of nodes */\n\treadonly nodeCount: number;\n\t/** Number of edges */\n\treadonly edgeCount: number;\n}\n\n/**\n * Convert a ReadableGraph to CSR format.\n *\n * For undirected graphs, each edge is stored twice (once in each direction).\n * For directed graphs, edges are stored in the out-direction by default.\n *\n * @param graph - The graph to convert\n * @param direction - Edge direction to include (default: 'out' for directed, 'both' for undirected)\n * @returns CSR representation with index mapping\n */\nexport function graphToCSR<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tdirection: Direction = graph.directed ? \"out\" : \"both\",\n): CSRGraph {\n\t// Build node index mapping\n\tconst nodeToIndex = new Map<NodeId, number>();\n\tconst indexToNode: NodeId[] = [];\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst index = indexToNode.length;\n\t\tnodeToIndex.set(nodeId, index);\n\t\tindexToNode.push(nodeId);\n\t}\n\n\tconst nodeCount = indexToNode.length;\n\n\t// Count edges per node to build row offsets\n\tconst degrees = new Uint32Array(nodeCount);\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst srcIndex = nodeToIndex.get(nodeId);\n\t\tif (srcIndex === undefined) continue;\n\t\tdegrees[srcIndex] = graph.degree(nodeId, direction);\n\t}\n\n\t// Calculate total edge count\n\tlet totalEdges = 0;\n\tfor (let i = 0; i < nodeCount; i++) {\n\t\ttotalEdges += degrees[i] ?? 0;\n\t}\n\n\t// Build rowOffsets array\n\tconst rowOffsets = new Uint32Array(nodeCount + 1);\n\tfor (let i = 0; i < nodeCount; i++) {\n\t\trowOffsets[i + 1] = (rowOffsets[i] ?? 0) + (degrees[i] ?? 0);\n\t}\n\n\t// Build colIndices and values arrays\n\tconst colIndices = new Uint32Array(totalEdges);\n\tconst values = new Float32Array(totalEdges);\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst srcIndex = nodeToIndex.get(nodeId);\n\t\tif (srcIndex === undefined) continue;\n\n\t\tconst baseOffset = rowOffsets[srcIndex] ?? 0;\n\t\tlet localOffset = 0;\n\n\t\tfor (const neighbourId of graph.neighbours(nodeId, direction)) {\n\t\t\tconst dstIndex = nodeToIndex.get(neighbourId);\n\t\t\tif (dstIndex === undefined) continue;\n\n\t\t\tconst edgeIdx = baseOffset + localOffset;\n\t\t\tcolIndices[edgeIdx] = dstIndex;\n\n\t\t\t// Get edge weight if available\n\t\t\tconst edge = graph.getEdge(nodeId, neighbourId);\n\t\t\tvalues[edgeIdx] = edge?.weight ?? 1.0;\n\n\t\t\tlocalOffset++;\n\t\t}\n\t}\n\n\tconst csr: CSRMatrix = {\n\t\trowOffsets,\n\t\tcolIndices,\n\t\tvalues,\n\t\tnodeCount,\n\t\tedgeCount: graph.directed ? graph.edgeCount : graph.edgeCount * 2,\n\t};\n\n\tconst indexMap: CSRIndexMap = {\n\t\tnodeToIndex,\n\t\tindexToNode,\n\t};\n\n\treturn { csr, indexMap };\n}\n\n/**\n * Create TypeGPU typed buffers from a CSR matrix.\n *\n * Uses TypeGPU's typed buffer API for type-safe GPU computation.\n * Buffers are created with storage usage for compute operations.\n *\n * @param root - TypeGPU root instance\n * @param csr - CSR matrix to upload\n * @returns Typed buffer group\n *\n * @example\n * ```typescript\n * import { initGPU, csrToTypedBuffers, graphToCSR } from \"graphwise/gpu\";\n *\n * const root = await initGPU();\n * const { csr } = graphToCSR(graph);\n * const buffers = csrToTypedBuffers(root, csr);\n *\n * // Read data back to CPU\n * const rowOffsets = await buffers.rowOffsets.read();\n * console.log(rowOffsets); // Uint32Array\n * ```\n */\nexport function csrToTypedBuffers(\n\troot: GraphwiseGPURoot,\n\tcsr: CSRMatrix,\n): TypedBufferGroup {\n\t// Convert typed arrays to regular arrays for TypeGPU\n\tconst rowOffsetsArray = Array.from(csr.rowOffsets);\n\tconst colIndicesArray = Array.from(csr.colIndices);\n\n\t// Row offsets buffer (u32 array)\n\tconst rowOffsetsBuffer = root\n\t\t.createBuffer(d.arrayOf(d.u32, csr.rowOffsets.length), rowOffsetsArray)\n\t\t.$usage(\"storage\");\n\n\t// Column indices buffer (u32 array)\n\tconst colIndicesBuffer = root\n\t\t.createBuffer(d.arrayOf(d.u32, csr.colIndices.length), colIndicesArray)\n\t\t.$usage(\"storage\");\n\n\t// Values buffer (f32 array, optional)\n\tif (csr.values !== undefined) {\n\t\tconst valuesArray = Array.from(csr.values);\n\t\tconst valuesBuffer = root\n\t\t\t.createBuffer(d.arrayOf(d.f32, csr.values.length), valuesArray)\n\t\t\t.$usage(\"storage\");\n\n\t\treturn {\n\t\t\trowOffsets: rowOffsetsBuffer,\n\t\t\tcolIndices: colIndicesBuffer,\n\t\t\tvalues: valuesBuffer,\n\t\t\tnodeCount: csr.nodeCount,\n\t\t\tedgeCount: csr.edgeCount,\n\t\t};\n\t}\n\n\treturn {\n\t\trowOffsets: rowOffsetsBuffer,\n\t\tcolIndices: colIndicesBuffer,\n\t\tnodeCount: csr.nodeCount,\n\t\tedgeCount: csr.edgeCount,\n\t};\n}\n","/**\n * Pure CPU implementation of batch neighbourhood intersection.\n *\n * Computes intersection size and neighbourhood sizes for multiple node pairs.\n * This is the foundation for ALL Jaccard-family MI variants:\n * J(A,B) = |A ∩ B| / |A ∪ B| = intersection / (sizeA + sizeB - intersection)\n * Cosine(A,B) = |A ∩ B| / sqrt(|A| * |B|)\n * Sorensen-Dice(A,B) = 2|A ∩ B| / (|A| + |B|)\n * Overlap(A,B) = |A ∩ B| / min(|A|, |B|)\n *\n * Uses binary search optimisation: iterate smaller neighbourhood, search in larger.\n *\n * @module gpu/kernels/intersection/logic\n */\n\n/**\n * Result of computing intersection for a single pair.\n */\nexport interface IntersectionResult {\n\t/** Size of the intersection |N(u) ∩ N(v)| */\n\treadonly intersection: number;\n\t/** Size of first neighbourhood |N(u)| */\n\treadonly sizeU: number;\n\t/** Size of second neighbourhood |N(v)| */\n\treadonly sizeV: number;\n}\n\n/**\n * Binary search in a sorted subarray of colIndices.\n *\n * @param colIndices - CSR column indices array (must be sorted per row)\n * @param start - Start index (inclusive)\n * @param end - End index (exclusive)\n * @param target - Value to search for\n * @returns true if target is found between colIndices[start] and colIndices[end-1]\n */\nexport function binarySearch(\n\tcolIndices: Uint32Array,\n\tstart: number,\n\tend: number,\n\ttarget: number,\n): boolean {\n\tlet lo = start;\n\tlet hi = end;\n\n\twhile (lo < hi) {\n\t\tconst mid = lo + Math.floor((hi - lo) / 2);\n\t\tconst midVal = colIndices[mid] ?? 0;\n\n\t\tif (midVal === target) {\n\t\t\treturn true;\n\t\t} else if (midVal < target) {\n\t\t\tlo = mid + 1;\n\t\t} else {\n\t\t\thi = mid;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Compute intersection stats for a single pair of nodes in CSR format.\n *\n * Neighbours must be sorted (CSR guarantees this from graphToCSR).\n *\n * @param rowOffsets - CSR row offset array\n * @param colIndices - CSR column indices array\n * @param u - First node index\n * @param v - Second node index\n * @returns Intersection result with sizes\n */\nexport function intersectionPair(\n\trowOffsets: Uint32Array,\n\tcolIndices: Uint32Array,\n\tu: number,\n\tv: number,\n): IntersectionResult {\n\tconst uStart = rowOffsets[u] ?? 0;\n\tconst uEnd = rowOffsets[u + 1] ?? 0;\n\tconst vStart = rowOffsets[v] ?? 0;\n\tconst vEnd = rowOffsets[v + 1] ?? 0;\n\n\tconst sizeU = uEnd - uStart;\n\tconst sizeV = vEnd - vStart;\n\n\t// Empty neighbourhoods → intersection = 0\n\tif (sizeU === 0 || sizeV === 0) {\n\t\treturn { intersection: 0, sizeU, sizeV };\n\t}\n\n\t// Count intersection by iterating smaller neighbourhood, binary searching in larger\n\tlet intersection = 0;\n\n\tif (sizeU <= sizeV) {\n\t\t// Iterate u's neighbours, search in v's\n\t\tfor (let i = uStart; i < uEnd; i++) {\n\t\t\tconst neighbour = colIndices[i] ?? 0;\n\t\t\tif (binarySearch(colIndices, vStart, vEnd, neighbour)) {\n\t\t\t\tintersection++;\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Iterate v's neighbours, search in u's\n\t\tfor (let i = vStart; i < vEnd; i++) {\n\t\t\tconst neighbour = colIndices[i] ?? 0;\n\t\t\tif (binarySearch(colIndices, uStart, uEnd, neighbour)) {\n\t\t\t\tintersection++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { intersection, sizeU, sizeV };\n}\n\n/**\n * Batch intersection stats for multiple node pairs.\n *\n * @param rowOffsets - CSR row offset array\n * @param colIndices - CSR column indices array\n * @param pairs - Array of [u, v] node index pairs\n * @returns Object with parallel arrays: intersections, sizeUs, sizeVs\n */\nexport function intersectionBatch(\n\trowOffsets: Uint32Array,\n\tcolIndices: Uint32Array,\n\tpairs: readonly (readonly [number, number])[],\n): {\n\tintersections: Uint32Array;\n\tsizeUs: Uint32Array;\n\tsizeVs: Uint32Array;\n} {\n\tconst n = pairs.length;\n\tconst intersections = new Uint32Array(n);\n\tconst sizeUs = new Uint32Array(n);\n\tconst sizeVs = new Uint32Array(n);\n\n\tfor (let i = 0; i < n; i++) {\n\t\tconst pair = pairs[i];\n\t\tif (pair !== undefined) {\n\t\t\tconst [u, v] = pair;\n\t\t\tconst result = intersectionPair(rowOffsets, colIndices, u, v);\n\t\t\tintersections[i] = result.intersection;\n\t\t\tsizeUs[i] = result.sizeU;\n\t\t\tsizeVs[i] = result.sizeV;\n\t\t}\n\t}\n\n\treturn { intersections, sizeUs, sizeVs };\n}\n\n/**\n * Compute Jaccard from intersection stats.\n * J = intersection / (sizeU + sizeV - intersection)\n */\nexport function jaccardFromIntersection(result: IntersectionResult): number {\n\tconst union = result.sizeU + result.sizeV - result.intersection;\n\treturn union === 0 ? 0 : result.intersection / union;\n}\n\n/**\n * Compute Cosine similarity from intersection stats.\n * Cosine = intersection / sqrt(sizeU * sizeV)\n */\nexport function cosineFromIntersection(result: IntersectionResult): number {\n\tconst denominator = Math.sqrt(result.sizeU * result.sizeV);\n\treturn denominator === 0 ? 0 : result.intersection / denominator;\n}\n\n/**\n * Compute Sorensen-Dice from intersection stats.\n * SD = 2 * intersection / (sizeU + sizeV)\n */\nexport function sorensenDiceFromIntersection(\n\tresult: IntersectionResult,\n): number {\n\tconst denominator = result.sizeU + result.sizeV;\n\treturn denominator === 0 ? 0 : (2 * result.intersection) / denominator;\n}\n\n/**\n * Compute Overlap coefficient from intersection stats.\n * Overlap = intersection / min(sizeU, sizeV)\n */\nexport function overlapFromIntersection(result: IntersectionResult): number {\n\tconst minSize = Math.min(result.sizeU, result.sizeV);\n\treturn minSize === 0 ? 0 : result.intersection / minSize;\n}\n\n/**\n * Compute Hub Promoted from intersection stats.\n * HP = intersection / min(sizeU, sizeV)\n * Note: Same formula as Overlap coefficient\n */\nexport function hubPromotedFromIntersection(\n\tresult: IntersectionResult,\n): number {\n\treturn overlapFromIntersection(result);\n}\n"],"mappings":"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"}
|