@graphty/webgpu-graph-algorithms 0.5.0 → 0.6.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/README.md +104 -52
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-CRbw2Wyo.js → context-BXqgCifx.js} +225 -33
- package/dist/chunks/context-BXqgCifx.js.map +1 -0
- package/dist/node.js +1 -1
- package/dist/src/accelerator.d.ts +12 -10
- package/dist/src/accelerator.d.ts.map +1 -1
- package/dist/src/accelerator.js +32 -10
- package/dist/src/accelerator.js.map +1 -1
- package/dist/src/algorithms/components.d.ts.map +1 -1
- package/dist/src/algorithms/components.js +12 -13
- package/dist/src/algorithms/components.js.map +1 -1
- package/dist/src/algorithms/degree.d.ts +6 -8
- package/dist/src/algorithms/degree.d.ts.map +1 -1
- package/dist/src/algorithms/degree.js +58 -35
- package/dist/src/algorithms/degree.js.map +1 -1
- package/dist/src/algorithms/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/pagerank.js +16 -14
- package/dist/src/algorithms/pagerank.js.map +1 -1
- package/dist/src/algorithms/power-iteration.d.ts +2 -2
- package/dist/src/algorithms/power-iteration.d.ts.map +1 -1
- package/dist/src/algorithms/power-iteration.js +17 -14
- package/dist/src/algorithms/power-iteration.js.map +1 -1
- package/dist/src/constants.d.ts +85 -8
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +85 -8
- package/dist/src/constants.js.map +1 -1
- package/dist/src/errors.d.ts +3 -2
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +2 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +10 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +14 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/dispatch.d.ts +8 -3
- package/dist/src/kernel/dispatch.d.ts.map +1 -1
- package/dist/src/kernel/dispatch.js +18 -7
- package/dist/src/kernel/dispatch.js.map +1 -1
- package/dist/src/kernel/kernel.d.ts +30 -1
- package/dist/src/kernel/kernel.d.ts.map +1 -1
- package/dist/src/kernel/kernel.js +49 -5
- package/dist/src/kernel/kernel.js.map +1 -1
- package/dist/src/kernel/prelude.d.ts.map +1 -1
- package/dist/src/kernel/prelude.js +9 -1
- package/dist/src/kernel/prelude.js.map +1 -1
- package/dist/src/kernel/profiler.d.ts +15 -3
- package/dist/src/kernel/profiler.d.ts.map +1 -1
- package/dist/src/kernel/profiler.js +27 -4
- package/dist/src/kernel/profiler.js.map +1 -1
- package/dist/src/kernels.d.ts +18 -8
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +345 -22
- package/dist/src/kernels.js.map +1 -1
- package/dist/src/layouts/calibrate.d.ts +51 -0
- package/dist/src/layouts/calibrate.d.ts.map +1 -0
- package/dist/src/layouts/calibrate.js +172 -0
- package/dist/src/layouts/calibrate.js.map +1 -0
- package/dist/src/layouts/force-simulation.d.ts +42 -5
- package/dist/src/layouts/force-simulation.d.ts.map +1 -1
- package/dist/src/layouts/force-simulation.js +84 -22
- package/dist/src/layouts/force-simulation.js.map +1 -1
- package/dist/src/layouts/forceatlas2.d.ts +107 -38
- package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
- package/dist/src/layouts/forceatlas2.js +297 -290
- package/dist/src/layouts/forceatlas2.js.map +1 -1
- package/dist/src/layouts/fruchterman-reingold.d.ts +241 -0
- package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -0
- package/dist/src/layouts/fruchterman-reingold.js +739 -0
- package/dist/src/layouts/fruchterman-reingold.js.map +1 -0
- package/dist/src/layouts/model-common.d.ts +140 -0
- package/dist/src/layouts/model-common.d.ts.map +1 -0
- package/dist/src/layouts/model-common.js +269 -0
- package/dist/src/layouts/model-common.js.map +1 -0
- package/dist/src/layouts/repulsion-grid.d.ts +152 -0
- package/dist/src/layouts/repulsion-grid.d.ts.map +1 -0
- package/dist/src/layouts/repulsion-grid.js +318 -0
- package/dist/src/layouts/repulsion-grid.js.map +1 -0
- package/dist/src/layouts/spring-electrical.d.ts +224 -0
- package/dist/src/layouts/spring-electrical.d.ts.map +1 -0
- package/dist/src/layouts/spring-electrical.js +665 -0
- package/dist/src/layouts/spring-electrical.js.map +1 -0
- package/dist/src/memory/residency.d.ts +6 -2
- package/dist/src/memory/residency.d.ts.map +1 -1
- package/dist/src/memory/residency.js +84 -14
- package/dist/src/memory/residency.js.map +1 -1
- package/dist/src/primitives/core-shape.d.ts +38 -2
- package/dist/src/primitives/core-shape.d.ts.map +1 -1
- package/dist/src/primitives/core-shape.js +71 -3
- package/dist/src/primitives/core-shape.js.map +1 -1
- package/dist/src/primitives/grid-pyramid.d.ts +71 -0
- package/dist/src/primitives/grid-pyramid.d.ts.map +1 -0
- package/dist/src/primitives/grid-pyramid.js +143 -0
- package/dist/src/primitives/grid-pyramid.js.map +1 -0
- package/dist/src/primitives/grid.d.ts +118 -0
- package/dist/src/primitives/grid.d.ts.map +1 -0
- package/dist/src/primitives/grid.js +225 -0
- package/dist/src/primitives/grid.js.map +1 -0
- package/dist/src/primitives/histogram.d.ts +67 -0
- package/dist/src/primitives/histogram.d.ts.map +1 -0
- package/dist/src/primitives/histogram.js +190 -0
- package/dist/src/primitives/histogram.js.map +1 -0
- package/dist/src/primitives/radix-sort.d.ts +75 -0
- package/dist/src/primitives/radix-sort.d.ts.map +1 -0
- package/dist/src/primitives/radix-sort.js +168 -0
- package/dist/src/primitives/radix-sort.js.map +1 -0
- package/dist/src/primitives/scan.d.ts +44 -0
- package/dist/src/primitives/scan.d.ts.map +1 -0
- package/dist/src/primitives/scan.js +151 -0
- package/dist/src/primitives/scan.js.map +1 -0
- package/dist/src/primitives/segmented-reduce.d.ts +25 -17
- package/dist/src/primitives/segmented-reduce.d.ts.map +1 -1
- package/dist/src/primitives/segmented-reduce.js +166 -47
- package/dist/src/primitives/segmented-reduce.js.map +1 -1
- package/dist/src/primitives/spmv.d.ts +18 -14
- package/dist/src/primitives/spmv.d.ts.map +1 -1
- package/dist/src/primitives/spmv.js +94 -58
- package/dist/src/primitives/spmv.js.map +1 -1
- package/dist/src/primitives/verify.d.ts +49 -0
- package/dist/src/primitives/verify.d.ts.map +1 -0
- package/dist/src/primitives/verify.js +229 -0
- package/dist/src/primitives/verify.js.map +1 -0
- package/dist/src/types/accelerator.d.ts +7 -3
- package/dist/src/types/accelerator.d.ts.map +1 -1
- package/dist/src/types/context.d.ts +53 -0
- package/dist/src/types/context.d.ts.map +1 -1
- package/dist/src/types/layout.d.ts +52 -0
- package/dist/src/types/layout.d.ts.map +1 -1
- package/dist/src/types/options.d.ts +43 -1
- package/dist/src/types/options.d.ts.map +1 -1
- package/dist/src/wgsl/counting-scatter.wgsl.d.ts +8 -0
- package/dist/src/wgsl/counting-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/counting-scatter.wgsl.js +17 -0
- package/dist/src/wgsl/counting-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +23 -8
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-attraction.wgsl.js +100 -17
- package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts +7 -2
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-integrate.wgsl.js +28 -2
- package/dist/src/wgsl/fa2-integrate.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +4 -2
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +14 -5
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +12 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +54 -0
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -1
- package/dist/src/wgsl/grid-cell-key.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.js +30 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.js +29 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-centroid.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-centroid.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-centroid.wgsl.js +29 -0
- package/dist/src/wgsl/grid-centroid.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-downsample.wgsl.d.ts +7 -0
- package/dist/src/wgsl/grid-downsample.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-downsample.wgsl.js +28 -0
- package/dist/src/wgsl/grid-downsample.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-far-field.wgsl.d.ts +13 -0
- package/dist/src/wgsl/grid-far-field.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-far-field.wgsl.js +98 -0
- package/dist/src/wgsl/grid-far-field.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-near-field.wgsl.d.ts +19 -0
- package/dist/src/wgsl/grid-near-field.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-near-field.wgsl.js +129 -0
- package/dist/src/wgsl/grid-near-field.wgsl.js.map +1 -0
- package/dist/src/wgsl/histogram.wgsl.d.ts +7 -0
- package/dist/src/wgsl/histogram.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/histogram.wgsl.js +15 -0
- package/dist/src/wgsl/histogram.wgsl.js.map +1 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.d.ts +8 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.js +26 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/radix-hist.wgsl.d.ts +9 -0
- package/dist/src/wgsl/radix-hist.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/radix-hist.wgsl.js +31 -0
- package/dist/src/wgsl/radix-hist.wgsl.js.map +1 -0
- package/dist/src/wgsl/radix-scatter.wgsl.d.ts +9 -0
- package/dist/src/wgsl/radix-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/radix-scatter.wgsl.js +40 -0
- package/dist/src/wgsl/radix-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/scan-add.wgsl.d.ts +6 -0
- package/dist/src/wgsl/scan-add.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/scan-add.wgsl.js +14 -0
- package/dist/src/wgsl/scan-add.wgsl.js.map +1 -0
- package/dist/src/wgsl/scan-block.wgsl.d.ts +8 -0
- package/dist/src/wgsl/scan-block.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/scan-block.wgsl.js +30 -0
- package/dist/src/wgsl/scan-block.wgsl.js.map +1 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts +22 -8
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/segmented-reduce.wgsl.js +84 -15
- package/dist/src/wgsl/segmented-reduce.wgsl.js.map +1 -1
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts +22 -11
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/spmv-pull.wgsl.js +110 -36
- package/dist/src/wgsl/spmv-pull.wgsl.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/webgpu-graph-algorithms.js +5016 -1130
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +10 -7
- package/src/accelerator.ts +46 -12
- package/src/algorithms/components.ts +12 -16
- package/src/algorithms/degree.ts +58 -43
- package/src/algorithms/pagerank.ts +20 -18
- package/src/algorithms/power-iteration.ts +19 -18
- package/src/constants.ts +108 -8
- package/src/errors.ts +3 -1
- package/src/index.ts +25 -5
- package/src/kernel/dispatch.ts +18 -7
- package/src/kernel/kernel.ts +59 -5
- package/src/kernel/prelude.ts +15 -0
- package/src/kernel/profiler.ts +28 -4
- package/src/kernels.ts +378 -24
- package/src/layouts/calibrate.ts +187 -0
- package/src/layouts/force-simulation.ts +111 -26
- package/src/layouts/forceatlas2.ts +346 -324
- package/src/layouts/fruchterman-reingold.ts +918 -0
- package/src/layouts/model-common.ts +323 -0
- package/src/layouts/repulsion-grid.ts +451 -0
- package/src/layouts/spring-electrical.ts +845 -0
- package/src/memory/residency.ts +126 -20
- package/src/primitives/core-shape.ts +91 -4
- package/src/primitives/grid-pyramid.ts +221 -0
- package/src/primitives/grid.ts +349 -0
- package/src/primitives/histogram.ts +273 -0
- package/src/primitives/radix-sort.ts +246 -0
- package/src/primitives/scan.ts +197 -0
- package/src/primitives/segmented-reduce.ts +214 -56
- package/src/primitives/spmv.ts +125 -65
- package/src/primitives/verify.ts +249 -0
- package/src/types/accelerator.ts +15 -3
- package/src/types/context.ts +56 -0
- package/src/types/layout.ts +58 -0
- package/src/types/options.ts +45 -1
- package/src/wgsl/counting-scatter.wgsl.ts +16 -0
- package/src/wgsl/fa2-attraction.wgsl.ts +100 -17
- package/src/wgsl/fa2-integrate.wgsl.ts +28 -2
- package/src/wgsl/fa2-repulsion-exact.wgsl.ts +14 -5
- package/src/wgsl/fa2-stats-finalize.wgsl.ts +54 -0
- package/src/wgsl/grid-cell-key.wgsl.ts +29 -0
- package/src/wgsl/grid-centroid-hub.wgsl.ts +28 -0
- package/src/wgsl/grid-centroid.wgsl.ts +28 -0
- package/src/wgsl/grid-downsample.wgsl.ts +27 -0
- package/src/wgsl/grid-far-field.wgsl.ts +97 -0
- package/src/wgsl/grid-near-field.wgsl.ts +128 -0
- package/src/wgsl/histogram.wgsl.ts +14 -0
- package/src/wgsl/indirect-finalize.wgsl.ts +25 -0
- package/src/wgsl/radix-hist.wgsl.ts +30 -0
- package/src/wgsl/radix-scatter.wgsl.ts +39 -0
- package/src/wgsl/scan-add.wgsl.ts +13 -0
- package/src/wgsl/scan-block.wgsl.ts +29 -0
- package/src/wgsl/segmented-reduce.wgsl.ts +84 -15
- package/src/wgsl/spmv-pull.wgsl.ts +110 -36
- package/dist/chunks/context-CRbw2Wyo.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphty/webgpu-graph-algorithms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "WebGPU-accelerated graph algorithms and layouts over the @graphty/graph-format snapshot, for Node (Dawn) and browsers",
|
|
5
5
|
"author": "Adam Powers <apowers@ato.ms>",
|
|
6
6
|
"type": "module",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"homepage": "https://github.com/graphty-org/graphty-monorepo/tree/master/webgpu-graph-algorithms#readme",
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@webgpu/types": "^0.1.72",
|
|
63
|
-
"@graphty/graph-format": "^1.0.
|
|
63
|
+
"@graphty/graph-format": "^1.0.2"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"@graphty/algorithms": "^1.0.0",
|
|
@@ -84,14 +84,16 @@
|
|
|
84
84
|
"@vitest/coverage-v8": "^3.2.4",
|
|
85
85
|
"@vitest/ui": "^3.2.4",
|
|
86
86
|
"fast-check": "^4.2.0",
|
|
87
|
+
"ngraph.forcelayout": "^3.3.1",
|
|
88
|
+
"ngraph.graph": "^20.0.1",
|
|
87
89
|
"playwright": "^1.54.1",
|
|
88
90
|
"tsx": "^4.20.3",
|
|
89
91
|
"typescript": "^5.9.3",
|
|
90
92
|
"vite": "^7.0.5",
|
|
91
93
|
"vitest": "^3.2.4",
|
|
92
94
|
"webgpu": "0.4.0",
|
|
93
|
-
"@graphty/layout": "^1.
|
|
94
|
-
"@graphty/algorithms": "^1.8.
|
|
95
|
+
"@graphty/layout": "^1.8.0",
|
|
96
|
+
"@graphty/algorithms": "^1.8.1"
|
|
95
97
|
},
|
|
96
98
|
"scripts": {
|
|
97
99
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -103,12 +105,13 @@
|
|
|
103
105
|
"typecheck:strict-consumer": "tsc -p tsconfig.strict-consumer.json",
|
|
104
106
|
"test": "vitest",
|
|
105
107
|
"test:ui": "vitest --ui",
|
|
106
|
-
"test:run": "vitest run --project=node",
|
|
107
|
-
"test:node": "vitest run --project=node",
|
|
108
|
+
"test:run": "vitest run --project=node --project=node-device-errors",
|
|
109
|
+
"test:node": "vitest run --project=node --project=node-device-errors",
|
|
110
|
+
"test:node:ci": "node scripts/run-node-shard.js",
|
|
108
111
|
"test:browser": "vitest run --project=browser",
|
|
109
112
|
"test:browser:ci": "node scripts/run-browser-project.js",
|
|
110
113
|
"test:limits": "vitest run --project=node-limits",
|
|
111
|
-
"coverage": "vitest run --project=node --coverage",
|
|
114
|
+
"coverage": "vitest run --project=node --project=node-device-errors --coverage",
|
|
112
115
|
"coverage:preview": "npx serve coverage -p 9058",
|
|
113
116
|
"bench": "tsx benchmarks/run.ts",
|
|
114
117
|
"bench:compare": "node scripts/bench-compare.js",
|
package/src/accelerator.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* AlgorithmAccelerator and LayoutAccelerator interfaces (spec 9.2, 9.3). Both are the REAL declarations,
|
|
4
4
|
* `LayoutAccelerator` from `@graphty/layout` (W1b, layout half) and `AlgorithmAccelerator` from
|
|
5
5
|
* `@graphty/algorithms` (W1b, algorithms half), `import type`d by src/types/accelerator.ts, so the object built
|
|
6
|
-
* here is checked against the CPU packages' own contracts. It carries P3's `forceAtlas2`, `release` and `dispose
|
|
7
|
-
*
|
|
8
|
-
* algorithm members (spec 8.2, 8.3; M8b-T8, PD-14) and nothing
|
|
9
|
-
* `createSimulation()`) test `acc.betweennessCentrality !== undefined`
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* here is checked against the CPU packages' own contracts. It carries P3's `forceAtlas2`, `release` and `dispose`,
|
|
7
|
+
* P5's `fruchtermanReingold` and `springElectrical` (the two other layout members of spec 9.3, landed together once
|
|
8
|
+
* both models were green, P5 PD-19) and P7's seven algorithm members (spec 8.2, 8.3; M8b-T8, PD-14) and nothing
|
|
9
|
+
* else: the CPU-side dispatchers (`accelerated()`, `createSimulation()`) test `acc.betweennessCentrality !== undefined`
|
|
10
|
+
* / `acc.fruchtermanReingold !== undefined` and route to the CPU when the member is absent (spec 2.4 row "method
|
|
11
|
+
* missing"), so a method the GPU does not implement must not exist here -- never a throwing stub. The remaining
|
|
12
|
+
* algorithm members arrive one per shipped algorithm from P8.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { type F32, type F64, type GraphSnapshot } from "@graphty/graph-format";
|
|
@@ -19,6 +19,8 @@ import { pageRank, personalizedPageRank } from "./algorithms/pagerank.js";
|
|
|
19
19
|
import { eigenvectorCentrality, hits, katzCentrality } from "./algorithms/spectral.js";
|
|
20
20
|
import { type GpuContext } from "./context.js";
|
|
21
21
|
import { createForceAtlas2 } from "./layouts/forceatlas2.js";
|
|
22
|
+
import { createFruchtermanReingold } from "./layouts/fruchterman-reingold.js";
|
|
23
|
+
import { createSpringElectrical } from "./layouts/spring-electrical.js";
|
|
22
24
|
import { type AcceleratorOptions, type GpuAccelerator } from "./types/accelerator.js";
|
|
23
25
|
import {
|
|
24
26
|
type ComponentsOptions,
|
|
@@ -31,8 +33,18 @@ import {
|
|
|
31
33
|
type KatzOptions,
|
|
32
34
|
type PageRankOptions,
|
|
33
35
|
} from "./types/algorithms.js";
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
+
import {
|
|
37
|
+
type ForceAtlas2Stats,
|
|
38
|
+
type FruchtermanReingoldStats,
|
|
39
|
+
type GpuLayoutSimulation,
|
|
40
|
+
type GpuLayoutTuning,
|
|
41
|
+
type SpringElectricalStats,
|
|
42
|
+
} from "./types/layout.js";
|
|
43
|
+
import {
|
|
44
|
+
type ForceAtlas2Options,
|
|
45
|
+
type FruchtermanReingoldOptions,
|
|
46
|
+
type SpringElectricalOptions,
|
|
47
|
+
} from "./types/options.js";
|
|
36
48
|
|
|
37
49
|
/** The `algorithms` record of AcceleratorOptions (spec 3.3), named for the copy helpers. */
|
|
38
50
|
type AlgorithmDefaults = NonNullable<AcceleratorOptions["algorithms"]>;
|
|
@@ -91,7 +103,8 @@ function freezeOptions(options: AcceleratorOptions | undefined): Readonly<Accele
|
|
|
91
103
|
|
|
92
104
|
/**
|
|
93
105
|
* Spec 3.3 createAccelerator, verbatim: the object implementing AlgorithmAccelerator & LayoutAccelerator
|
|
94
|
-
* structurally; P3's forceAtlas2, release and dispose
|
|
106
|
+
* structurally; P3's forceAtlas2, release and dispose, P5's fruchtermanReingold and springElectrical (the same
|
|
107
|
+
* `{ ...o, ...options.layout }` shape as forceAtlas2) plus P7's seven algorithm members, each a delegation to
|
|
95
108
|
* its algorithm with `ctx.assertReady()` first. The accelerator's algorithm defaults are not consulted by any of
|
|
96
109
|
* them: only `betweenness` has any, and it belongs to P9. One per call (the app creates one and injects
|
|
97
110
|
* it, spec 2.4); `kind` is "webgpu"; `options` is a frozen deep copy; `forceAtlas2(o)` is
|
|
@@ -101,8 +114,9 @@ function freezeOptions(options: AcceleratorOptions | undefined): Readonly<Accele
|
|
|
101
114
|
* @param ctx - the context every simulation the accelerator creates runs on
|
|
102
115
|
* @param options - GPU-only defaults inherited by every simulation (`layout`) and, from P9, the algorithm defaults
|
|
103
116
|
* @returns the injectable accelerator
|
|
104
|
-
* @throws E_DISPOSED / E_DEVICE_LOST from `ctx.assertReady()` (here and inside
|
|
105
|
-
* also throws what createForceAtlas2 throws (E_UNSUPPORTED for `nodeSize`, E_INVALID_ARGUMENT for a bad range)
|
|
117
|
+
* @throws E_DISPOSED / E_DEVICE_LOST from `ctx.assertReady()` (here and inside every creating member); `forceAtlas2()`
|
|
118
|
+
* also throws what createForceAtlas2 throws (E_UNSUPPORTED for `nodeSize`, E_INVALID_ARGUMENT for a bad range), and
|
|
119
|
+
* `fruchtermanReingold()` / `springElectrical()` what their factories throw (E_INVALID_ARGUMENT for a bad range)
|
|
106
120
|
*/
|
|
107
121
|
export function createAccelerator(ctx: GpuContext, options?: AcceleratorOptions): GpuAccelerator {
|
|
108
122
|
ctx.assertReady();
|
|
@@ -120,6 +134,26 @@ export function createAccelerator(ctx: GpuContext, options?: AcceleratorOptions)
|
|
|
120
134
|
ctx.assertReady();
|
|
121
135
|
return createForceAtlas2(ctx, { ...o, ...frozen.layout });
|
|
122
136
|
},
|
|
137
|
+
/**
|
|
138
|
+
* The Fruchterman-Reingold simulation with this accelerator's layout tuning (spec 3.3, 7.20; P5).
|
|
139
|
+
* @param o - the CPU option type (spec 9.3 FruchtermanReingoldOptions); GPU tuning keys come from `options.layout`
|
|
140
|
+
* @returns a fresh simulation in state "created"
|
|
141
|
+
*/
|
|
142
|
+
fruchtermanReingold(
|
|
143
|
+
o?: FruchtermanReingoldOptions,
|
|
144
|
+
): GpuLayoutSimulation<FruchtermanReingoldOptions, FruchtermanReingoldStats> {
|
|
145
|
+
ctx.assertReady();
|
|
146
|
+
return createFruchtermanReingold(ctx, { ...o, ...frozen.layout });
|
|
147
|
+
},
|
|
148
|
+
/**
|
|
149
|
+
* The spring-electrical preset with this accelerator's layout tuning (spec 3.3, 7.20; P5).
|
|
150
|
+
* @param o - the CPU option type (spec 9.3 SpringElectricalOptions, ngraph's names)
|
|
151
|
+
* @returns a fresh simulation in state "created"
|
|
152
|
+
*/
|
|
153
|
+
springElectrical(o?: SpringElectricalOptions): GpuLayoutSimulation<SpringElectricalOptions, SpringElectricalStats> {
|
|
154
|
+
ctx.assertReady();
|
|
155
|
+
return createSpringElectrical(ctx, { ...o, ...frozen.layout });
|
|
156
|
+
},
|
|
123
157
|
/**
|
|
124
158
|
* PageRank on the device (spec 8.2; contract 3.14).
|
|
125
159
|
* @param gs - the snapshot
|
|
@@ -19,11 +19,13 @@ import { type GraphSnapshot, renumberPartition, type U32 } from "@graphty/graph-
|
|
|
19
19
|
|
|
20
20
|
import { U32_MAX } from "../constants.js";
|
|
21
21
|
import { type GpuContext } from "../context.js";
|
|
22
|
-
import {
|
|
22
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
23
23
|
import { CommandBatch } from "../kernel/batch.js";
|
|
24
24
|
import { plan1d, planGridStride } from "../kernel/dispatch.js";
|
|
25
25
|
import { FILL_PARAMS, graphBindings, graphOverrides, kernelSpec, WCC_PARAMS } from "../kernels.js";
|
|
26
26
|
import { type CoreBinding } from "../memory/residency.js";
|
|
27
|
+
import { assertWholeCore } from "../primitives/core-shape.js";
|
|
28
|
+
import { assertDeviceComputes } from "../primitives/verify.js";
|
|
27
29
|
import { type ComponentsOptions, type GpuLabelResult } from "../types/algorithms.js";
|
|
28
30
|
import { type Binding } from "../types/memory.js";
|
|
29
31
|
import { type GpuRunOptions } from "../types/run.js";
|
|
@@ -66,25 +68,16 @@ function checkDest(dest: Float32Array | Uint32Array | undefined, n: number): U32
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
|
-
* The resident core; a windowed plan
|
|
70
|
-
*
|
|
71
|
+
* The resident core; a windowed plan is refused with `E_TOO_LARGE { path: "windowed", algorithm }` (spec 3.8, 3.12;
|
|
72
|
+
* DEP-P4-B: only degree and segmentedReduce execute windows).
|
|
71
73
|
* @param ctx - the context
|
|
72
74
|
* @param s - the snapshot
|
|
73
75
|
* @returns the core binding
|
|
74
76
|
*/
|
|
75
77
|
function coreOf(ctx: GpuContext, s: GraphSnapshot): CoreBinding {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (isWebGpuGraphError(error) && error.code === "E_TOO_LARGE" && error.details.path === "windowed") {
|
|
80
|
-
throw new WebGpuGraphError(
|
|
81
|
-
"E_TOO_LARGE",
|
|
82
|
-
`${ALGORITHM}: the arc arrays need a windowed upload, which P1-P3 plan but do not execute`,
|
|
83
|
-
{ ...error.details, algorithm: ALGORITHM },
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
78
|
+
const core = ctx.residency.core(s);
|
|
79
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM);
|
|
80
|
+
return core;
|
|
88
81
|
}
|
|
89
82
|
|
|
90
83
|
/**
|
|
@@ -197,6 +190,7 @@ export async function connectedComponents(
|
|
|
197
190
|
options?: ComponentsOptions & GpuRunOptions,
|
|
198
191
|
): Promise<GpuLabelResult> {
|
|
199
192
|
ctx.assertReady();
|
|
193
|
+
await assertDeviceComputes(ctx);
|
|
200
194
|
const n = s.nodeCount;
|
|
201
195
|
const renumber = options?.renumber !== false;
|
|
202
196
|
const dest = checkDest(options?.dest, n);
|
|
@@ -312,7 +306,9 @@ export async function connectedComponents(
|
|
|
312
306
|
rounds += ROUNDS_PER_BATCH;
|
|
313
307
|
ctx.assertReady();
|
|
314
308
|
if (options?.signal?.aborted) {
|
|
315
|
-
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM}: the signal was aborted`, {
|
|
309
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM}: the signal was aborted`, {
|
|
310
|
+
batchId: submitted.id,
|
|
311
|
+
});
|
|
316
312
|
}
|
|
317
313
|
if (new Uint32Array(back, flagRequest.offset, 1)[0] === 0) {
|
|
318
314
|
break;
|
package/src/algorithms/degree.ts
CHANGED
|
@@ -9,24 +9,24 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Contract (3.12): `ctx.assertReady()` first; `nodeCount === 0` returns an empty `Uint32Array` (or `dest`) with no
|
|
11
11
|
* GPU work (spec 5.6: there is no work, which is not a fallback); an already-aborted `signal` is `E_ABORTED` before
|
|
12
|
-
* any work; `dest` must be a `Uint32Array` of length n over an `ArrayBuffer`;
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* fill precedes it), a readback into the result, `onProgress(1, 1)`, and the scratch returned in a finally. Two runs
|
|
19
|
-
* are bitwise identical (spec 11.9 item 4).
|
|
12
|
+
* any work; `dest` must be a `Uint32Array` of length n over an `ArrayBuffer`; `arcCount === 0` skips the dispatch
|
|
13
|
+
* (only `rowPtr` is resident) and the result is zeros; otherwise ONE dispatch over rows [0, n) with `accumulate = 0`
|
|
14
|
+
* (every row is written, so no fill precedes it) -- or, on a windowed core (spec 4.2, PD-8), a `fill` of zeros and
|
|
15
|
+
* one dispatch per window over its rows with `arcBase = w.start`, `arcEnd = w.end`, `accumulate = 1`, so a row split
|
|
16
|
+
* across windows adds its partial counts -- a readback into the result, `onProgress(1, 1)`, and the scratch returned
|
|
17
|
+
* in a finally. Two runs are bitwise identical (spec 11.9 item 4).
|
|
20
18
|
*/
|
|
21
19
|
|
|
22
20
|
import { type GraphSnapshot, type U32 } from "@graphty/graph-format";
|
|
23
21
|
|
|
24
22
|
import { type GpuContext } from "../context.js";
|
|
25
23
|
import { BufferUsage } from "../device/webgpu-constants.js";
|
|
26
|
-
import {
|
|
24
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
27
25
|
import { plan1d } from "../kernel/dispatch.js";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
26
|
+
import { type UniformBlock, type UniformValues } from "../kernel/struct-block.js";
|
|
27
|
+
import { FILL_PARAMS, graphBindings, graphOverrides, kernelSpec, RANGE_PARAMS } from "../kernels.js";
|
|
28
|
+
import { windowBinding } from "../primitives/core-shape.js";
|
|
29
|
+
import { assertDeviceComputes } from "../primitives/verify.js";
|
|
30
30
|
import { type Binding } from "../types/memory.js";
|
|
31
31
|
import { type GpuRunOptions } from "../types/run.js";
|
|
32
32
|
|
|
@@ -55,26 +55,21 @@ function checkDest(dest: Float32Array | Uint32Array | undefined, n: number): U32
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* @param
|
|
62
|
-
* @param
|
|
63
|
-
* @
|
|
58
|
+
* One pool-acquired uniform buffer holding one params record (a params buffer per dispatch; the caller releases
|
|
59
|
+
* `pooled` in its finally).
|
|
60
|
+
* @param ctx - the context whose pool and queue are used
|
|
61
|
+
* @param pooled - the list the buffer is appended to for release
|
|
62
|
+
* @param block - the uniform block
|
|
63
|
+
* @param values - the record's values
|
|
64
|
+
* @returns the whole-buffer binding
|
|
64
65
|
*/
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"degree: the arc arrays need a windowed upload, which P1-P3 plan but do not execute",
|
|
73
|
-
{ ...error.details, algorithm: "degree" },
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
66
|
+
function paramsBinding(ctx: GpuContext, pooled: GPUBuffer[], block: UniformBlock, values: UniformValues): Binding {
|
|
67
|
+
const buffer = ctx.pool.acquire(block.byteLength, BufferUsage.UNIFORM | BufferUsage.COPY_DST, "degree/params");
|
|
68
|
+
pooled.push(buffer);
|
|
69
|
+
const bytes = new ArrayBuffer(block.byteLength);
|
|
70
|
+
block.write(new DataView(bytes), values);
|
|
71
|
+
ctx.device.queue.writeBuffer(buffer, 0, bytes);
|
|
72
|
+
return { buffer, offset: 0, size: block.byteLength, window: null };
|
|
78
73
|
}
|
|
79
74
|
|
|
80
75
|
/**
|
|
@@ -89,6 +84,7 @@ function coreOf(ctx: GpuContext, s: GraphSnapshot): CoreBinding {
|
|
|
89
84
|
*/
|
|
90
85
|
export async function degree(ctx: GpuContext, s: GraphSnapshot, options?: GpuRunOptions): Promise<U32> {
|
|
91
86
|
ctx.assertReady();
|
|
87
|
+
await assertDeviceComputes(ctx);
|
|
92
88
|
const n = s.nodeCount;
|
|
93
89
|
const dest = checkDest(options?.dest, n);
|
|
94
90
|
if (options?.signal?.aborted) {
|
|
@@ -98,7 +94,7 @@ export async function degree(ctx: GpuContext, s: GraphSnapshot, options?: GpuRun
|
|
|
98
94
|
options?.onProgress?.(1, 1);
|
|
99
95
|
return dest ?? new Uint32Array(0);
|
|
100
96
|
}
|
|
101
|
-
const core =
|
|
97
|
+
const core = ctx.residency.core(s);
|
|
102
98
|
if (s.arcCount === 0) {
|
|
103
99
|
const zeros = dest ?? new Uint32Array(n);
|
|
104
100
|
zeros.fill(0);
|
|
@@ -111,23 +107,40 @@ export async function degree(ctx: GpuContext, s: GraphSnapshot, options?: GpuRun
|
|
|
111
107
|
BufferUsage.STORAGE | BufferUsage.COPY_SRC | BufferUsage.COPY_DST,
|
|
112
108
|
"degree/out",
|
|
113
109
|
);
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
BufferUsage.UNIFORM | BufferUsage.COPY_DST,
|
|
117
|
-
"degree/params",
|
|
118
|
-
);
|
|
110
|
+
const pooled: GPUBuffer[] = [];
|
|
111
|
+
const params = (block: UniformBlock, values: UniformValues): Binding => paramsBinding(ctx, pooled, block, values);
|
|
119
112
|
try {
|
|
120
113
|
await ctx.allocator.check();
|
|
121
114
|
const kernel = await ctx.pipelines.kernel(kernelSpec("degree", graphOverrides(core, null)));
|
|
122
|
-
const bytes = new ArrayBuffer(RANGE_PARAMS.byteLength);
|
|
123
|
-
RANGE_PARAMS.write(new DataView(bytes), { start: 0, end: n, arcBase: 0, arcEnd: s.arcCount, accumulate: 0, n });
|
|
124
|
-
ctx.device.queue.writeBuffer(params, 0, bytes);
|
|
125
115
|
const outBinding: Binding = { buffer: out, offset: 0, size: byteLength, window: null };
|
|
126
|
-
const paramsBinding: Binding = { buffer: params, offset: 0, size: RANGE_PARAMS.byteLength, window: null };
|
|
127
|
-
const bound = kernel.bind({ ...graphBindings(core, null), out: outBinding, P: paramsBinding });
|
|
128
116
|
const encoder = ctx.device.createCommandEncoder({ label: "degree" });
|
|
129
117
|
const pass = encoder.beginComputePass({ label: "degree" });
|
|
130
|
-
|
|
118
|
+
if (core.windows === null) {
|
|
119
|
+
const P = params(RANGE_PARAMS, { start: 0, end: n, arcBase: 0, arcEnd: s.arcCount, accumulate: 0, n });
|
|
120
|
+
const bound = kernel.bind({ ...graphBindings(core, null), out: outBinding, P });
|
|
121
|
+
kernel.dispatch(pass, bound, plan1d(n, ctx.workgroupSize, ctx.caps), [0]);
|
|
122
|
+
} else {
|
|
123
|
+
const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
|
|
124
|
+
const zero = fill.bind({ dst: outBinding, P: params(FILL_PARAMS, { count: n, value: 0, mode: 0 }) });
|
|
125
|
+
fill.dispatch(pass, zero, plan1d(n, ctx.workgroupSize, ctx.caps), [0]);
|
|
126
|
+
for (const w of core.windows) {
|
|
127
|
+
const windowed = {
|
|
128
|
+
...core,
|
|
129
|
+
colIdx: windowBinding(core, "colIdx", w),
|
|
130
|
+
weights: core.weights === null ? null : windowBinding(core, "weights", w),
|
|
131
|
+
};
|
|
132
|
+
const P = params(RANGE_PARAMS, {
|
|
133
|
+
start: w.rowFirst,
|
|
134
|
+
end: w.rowLast + 1,
|
|
135
|
+
arcBase: w.start,
|
|
136
|
+
arcEnd: w.end,
|
|
137
|
+
accumulate: 1,
|
|
138
|
+
n,
|
|
139
|
+
});
|
|
140
|
+
const bound = kernel.bind({ ...graphBindings(windowed, null), out: outBinding, P });
|
|
141
|
+
kernel.dispatch(pass, bound, plan1d(w.rowLast - w.rowFirst + 1, ctx.workgroupSize, ctx.caps), [0]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
131
144
|
pass.end();
|
|
132
145
|
ctx.device.queue.submit([encoder.finish()]);
|
|
133
146
|
ctx.assertReady();
|
|
@@ -136,7 +149,9 @@ export async function degree(ctx: GpuContext, s: GraphSnapshot, options?: GpuRun
|
|
|
136
149
|
options?.onProgress?.(1, 1);
|
|
137
150
|
return dest ?? new Uint32Array(result);
|
|
138
151
|
} finally {
|
|
139
|
-
|
|
152
|
+
for (const buffer of pooled) {
|
|
153
|
+
ctx.pool.release(buffer);
|
|
154
|
+
}
|
|
140
155
|
ctx.pool.release(out);
|
|
141
156
|
}
|
|
142
157
|
}
|
|
@@ -18,14 +18,15 @@ import { type F32, type GraphSnapshot } from "@graphty/graph-format";
|
|
|
18
18
|
|
|
19
19
|
import { U32_MAX } from "../constants.js";
|
|
20
20
|
import { type GpuContext } from "../context.js";
|
|
21
|
-
import {
|
|
21
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
22
22
|
import { CommandBatch } from "../kernel/batch.js";
|
|
23
23
|
import { groupsOf, plan1d } from "../kernel/dispatch.js";
|
|
24
24
|
import { kernelSpec, PR_PARAMS, PR_PARTIAL } from "../kernels.js";
|
|
25
25
|
import { type ArrayBinding, type CoreBinding } from "../memory/residency.js";
|
|
26
|
-
import { coreOfView } from "../primitives/core-shape.js";
|
|
26
|
+
import { assertWholeCore, coreOfView } from "../primitives/core-shape.js";
|
|
27
27
|
import { prepareSegmentedReduce } from "../primitives/segmented-reduce.js";
|
|
28
28
|
import { prepareSpmvPull } from "../primitives/spmv.js";
|
|
29
|
+
import { assertDeviceComputes } from "../primitives/verify.js";
|
|
29
30
|
import { type GpuPageRankResult, type PageRankOptions } from "../types/algorithms.js";
|
|
30
31
|
import { type Binding } from "../types/memory.js";
|
|
31
32
|
import { type GpuRunOptions } from "../types/run.js";
|
|
@@ -62,26 +63,17 @@ function checkDest(dest: Float32Array | Uint32Array | undefined, n: number, algo
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
|
-
* The resident core; a windowed plan
|
|
66
|
-
*
|
|
66
|
+
* The resident core; a windowed plan is refused with `E_TOO_LARGE { path: "windowed", algorithm }` (spec 3.8, 3.12;
|
|
67
|
+
* DEP-P4-B: only degree and segmentedReduce execute windows).
|
|
67
68
|
* @param ctx - the context
|
|
68
69
|
* @param s - the snapshot
|
|
69
70
|
* @param algorithm - the caller's name
|
|
70
71
|
* @returns the core binding
|
|
71
72
|
*/
|
|
72
73
|
function coreOf(ctx: GpuContext, s: GraphSnapshot, algorithm: string): CoreBinding {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (isWebGpuGraphError(error) && error.code === "E_TOO_LARGE" && error.details.path === "windowed") {
|
|
77
|
-
throw new WebGpuGraphError(
|
|
78
|
-
"E_TOO_LARGE",
|
|
79
|
-
`${algorithm}: the arc arrays need a windowed upload, which P1-P3 plan but do not execute`,
|
|
80
|
-
{ ...error.details, algorithm },
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
74
|
+
const core = ctx.residency.core(s);
|
|
75
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, algorithm);
|
|
76
|
+
return core;
|
|
85
77
|
}
|
|
86
78
|
|
|
87
79
|
/**
|
|
@@ -126,6 +118,7 @@ async function run(
|
|
|
126
118
|
algorithm: string,
|
|
127
119
|
): Promise<GpuPageRankResult> {
|
|
128
120
|
ctx.assertReady();
|
|
121
|
+
await assertDeviceComputes(ctx);
|
|
129
122
|
const n = s.nodeCount;
|
|
130
123
|
const alpha = options?.dampingFactor ?? 0.85;
|
|
131
124
|
const maxIterations = options?.maxIterations ?? 100;
|
|
@@ -144,7 +137,13 @@ async function run(
|
|
|
144
137
|
}
|
|
145
138
|
if (n === 0) {
|
|
146
139
|
options?.onProgress?.(maxIterations, maxIterations);
|
|
147
|
-
return {
|
|
140
|
+
return {
|
|
141
|
+
scores: dest ?? new Float32Array(0),
|
|
142
|
+
iterations: 0,
|
|
143
|
+
converged: true,
|
|
144
|
+
danglingMass: 0,
|
|
145
|
+
precision: "f32",
|
|
146
|
+
};
|
|
148
147
|
}
|
|
149
148
|
const core = coreOf(ctx, s, algorithm);
|
|
150
149
|
if (s.arcCount === 0) {
|
|
@@ -322,7 +321,10 @@ export async function personalizedPageRank(
|
|
|
322
321
|
expected,
|
|
323
322
|
});
|
|
324
323
|
if (!(personalization instanceof Float32Array) || personalization.length !== n) {
|
|
325
|
-
throw invalid(
|
|
324
|
+
throw invalid(
|
|
325
|
+
`${personalization.constructor.name}(${personalization.length})`,
|
|
326
|
+
`a Float32Array of length ${n}`,
|
|
327
|
+
);
|
|
326
328
|
}
|
|
327
329
|
let total = 0;
|
|
328
330
|
for (let v = 0; v < n; v++) {
|
|
@@ -20,13 +20,14 @@ import { type F32, type GraphSnapshot } from "@graphty/graph-format";
|
|
|
20
20
|
|
|
21
21
|
import { U32_MAX } from "../constants.js";
|
|
22
22
|
import { type GpuContext } from "../context.js";
|
|
23
|
-
import {
|
|
23
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
24
24
|
import { CommandBatch } from "../kernel/batch.js";
|
|
25
25
|
import { groupsOf, plan1d } from "../kernel/dispatch.js";
|
|
26
26
|
import { kernelSpec, PR_PARAMS, PR_PARTIAL } from "../kernels.js";
|
|
27
27
|
import { type CoreBinding } from "../memory/residency.js";
|
|
28
|
-
import { coreOfView } from "../primitives/core-shape.js";
|
|
28
|
+
import { assertWholeCore, coreOfView } from "../primitives/core-shape.js";
|
|
29
29
|
import { prepareSpmvPull } from "../primitives/spmv.js";
|
|
30
|
+
import { assertDeviceComputes } from "../primitives/verify.js";
|
|
30
31
|
import { type Binding } from "../types/memory.js";
|
|
31
32
|
import { algorithmScope } from "./scope.js";
|
|
32
33
|
|
|
@@ -103,26 +104,17 @@ export function checkDest(dest: Float32Array | Uint32Array | undefined, n: numbe
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
|
-
* The resident core; a windowed plan
|
|
107
|
-
*
|
|
107
|
+
* The resident core; a windowed plan is refused with `E_TOO_LARGE { path: "windowed", algorithm }` (spec 3.8, 3.12;
|
|
108
|
+
* DEP-P4-B: only degree and segmentedReduce execute windows).
|
|
108
109
|
* @param ctx - the context
|
|
109
110
|
* @param s - the snapshot
|
|
110
111
|
* @param algorithm - the caller's name
|
|
111
112
|
* @returns the core binding
|
|
112
113
|
*/
|
|
113
114
|
export function coreOf(ctx: GpuContext, s: GraphSnapshot, algorithm: string): CoreBinding {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (isWebGpuGraphError(error) && error.code === "E_TOO_LARGE" && error.details.path === "windowed") {
|
|
118
|
-
throw new WebGpuGraphError(
|
|
119
|
-
"E_TOO_LARGE",
|
|
120
|
-
`${algorithm}: the arc arrays need a windowed upload, which P1-P3 plan but do not execute`,
|
|
121
|
-
{ ...error.details, algorithm },
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
115
|
+
const core = ctx.residency.core(s);
|
|
116
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, algorithm);
|
|
117
|
+
return core;
|
|
126
118
|
}
|
|
127
119
|
|
|
128
120
|
/**
|
|
@@ -179,6 +171,7 @@ export async function runPowerIteration(
|
|
|
179
171
|
n: number,
|
|
180
172
|
config: PowerIterationConfig,
|
|
181
173
|
): Promise<PowerIterationRun> {
|
|
174
|
+
await assertDeviceComputes(ctx);
|
|
182
175
|
const scope = algorithmScope(ctx, config.label, RING_SLOTS);
|
|
183
176
|
try {
|
|
184
177
|
const bytes = 4 * n;
|
|
@@ -231,7 +224,14 @@ export async function runPowerIteration(
|
|
|
231
224
|
const rankOut = ring[iteration % ring.length];
|
|
232
225
|
const { core, pull } = pulls[(iteration - 1) % pulls.length];
|
|
233
226
|
// outWeightSum takes rankIn as its dummy: storage-ro, read only under NORM_MODE 0, never compiled here
|
|
234
|
-
const scaleBindings = {
|
|
227
|
+
const scaleBindings = {
|
|
228
|
+
rankIn,
|
|
229
|
+
rankPrev: rankOut,
|
|
230
|
+
outWeightSum: rankIn,
|
|
231
|
+
xNorm,
|
|
232
|
+
partials,
|
|
233
|
+
P: params.binding,
|
|
234
|
+
};
|
|
235
235
|
scaleNorm.dispatch(pass, scaleNorm.bind(scaleBindings), scalePlan, [params.offset]);
|
|
236
236
|
finalize.dispatch(pass, finalize.bind({ partials, P: params.binding }), finalizePlan, [params.offset]);
|
|
237
237
|
if (scaleApply !== null) {
|
|
@@ -265,7 +265,8 @@ export async function runPowerIteration(
|
|
|
265
265
|
}
|
|
266
266
|
return {
|
|
267
267
|
scores: new Float32Array(back, scoresRequest.offset, n).slice(),
|
|
268
|
-
previous:
|
|
268
|
+
previous:
|
|
269
|
+
previousRequest === null ? null : new Float32Array(back, previousRequest.offset, n).slice(),
|
|
269
270
|
iterations: converged ? firstConverged : iterationsRun,
|
|
270
271
|
converged,
|
|
271
272
|
iterationsRun,
|