@graphty/webgpu-graph-algorithms 0.6.14 → 0.6.16
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 +52 -52
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-Bi6AhScG.js → context-VIvatQOo.js} +69 -34
- package/dist/chunks/context-VIvatQOo.js.map +1 -0
- package/dist/node.js +1 -1
- package/dist/src/accelerator.d.ts +5 -3
- package/dist/src/accelerator.d.ts.map +1 -1
- package/dist/src/accelerator.js +101 -5
- package/dist/src/accelerator.js.map +1 -1
- package/dist/src/algorithms/all-pairs.d.ts +41 -0
- package/dist/src/algorithms/all-pairs.d.ts.map +1 -0
- package/dist/src/algorithms/all-pairs.js +181 -0
- package/dist/src/algorithms/all-pairs.js.map +1 -0
- package/dist/src/algorithms/betweenness.d.ts +70 -0
- package/dist/src/algorithms/betweenness.d.ts.map +1 -0
- package/dist/src/algorithms/betweenness.js +538 -0
- package/dist/src/algorithms/betweenness.js.map +1 -0
- package/dist/src/algorithms/closeness.d.ts +15 -5
- package/dist/src/algorithms/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/closeness.js +112 -26
- package/dist/src/algorithms/closeness.js.map +1 -1
- package/dist/src/algorithms/components.d.ts +9 -1
- package/dist/src/algorithms/components.d.ts.map +1 -1
- package/dist/src/algorithms/components.js +2 -2
- package/dist/src/algorithms/components.js.map +1 -1
- package/dist/src/algorithms/label-propagation.d.ts +31 -0
- package/dist/src/algorithms/label-propagation.d.ts.map +1 -0
- package/dist/src/algorithms/label-propagation.js +254 -0
- package/dist/src/algorithms/label-propagation.js.map +1 -0
- package/dist/src/algorithms/simple-symmetric.d.ts +88 -0
- package/dist/src/algorithms/simple-symmetric.d.ts.map +1 -0
- package/dist/src/algorithms/simple-symmetric.js +347 -0
- package/dist/src/algorithms/simple-symmetric.js.map +1 -0
- package/dist/src/algorithms/triangles.d.ts +34 -0
- package/dist/src/algorithms/triangles.d.ts.map +1 -0
- package/dist/src/algorithms/triangles.js +203 -0
- package/dist/src/algorithms/triangles.js.map +1 -0
- package/dist/src/constants.d.ts +53 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +53 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +12 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/prelude.d.ts.map +1 -1
- package/dist/src/kernel/prelude.js +4 -1
- package/dist/src/kernel/prelude.js.map +1 -1
- package/dist/src/kernels.d.ts +24 -6
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +373 -7
- package/dist/src/kernels.js.map +1 -1
- package/dist/src/memory/residency.js +15 -4
- package/dist/src/memory/residency.js.map +1 -1
- package/dist/src/primitives/coo-to-csr.d.ts +73 -0
- package/dist/src/primitives/coo-to-csr.d.ts.map +1 -0
- package/dist/src/primitives/coo-to-csr.js +183 -0
- package/dist/src/primitives/coo-to-csr.js.map +1 -0
- package/dist/src/primitives/frontier.d.ts +2 -0
- package/dist/src/primitives/frontier.d.ts.map +1 -1
- package/dist/src/primitives/frontier.js +2 -0
- package/dist/src/primitives/frontier.js.map +1 -1
- package/dist/src/primitives/group-by-key.d.ts +82 -0
- package/dist/src/primitives/group-by-key.d.ts.map +1 -0
- package/dist/src/primitives/group-by-key.js +147 -0
- package/dist/src/primitives/group-by-key.js.map +1 -0
- package/dist/src/types/accelerator.d.ts +19 -7
- package/dist/src/types/accelerator.d.ts.map +1 -1
- package/dist/src/types/algorithms.d.ts +4 -0
- package/dist/src/types/algorithms.d.ts.map +1 -1
- package/dist/src/types/all-pairs.d.ts +35 -0
- package/dist/src/types/all-pairs.d.ts.map +1 -0
- package/dist/src/types/all-pairs.js +8 -0
- package/dist/src/types/all-pairs.js.map +1 -0
- package/dist/src/types/betweenness.d.ts +35 -0
- package/dist/src/types/betweenness.d.ts.map +1 -0
- package/dist/src/types/betweenness.js +7 -0
- package/dist/src/types/betweenness.js.map +1 -0
- package/dist/src/types/community.d.ts +18 -0
- package/dist/src/types/community.d.ts.map +1 -0
- package/dist/src/types/community.js +5 -0
- package/dist/src/types/community.js.map +1 -0
- package/dist/src/types/structure.d.ts +27 -0
- package/dist/src/types/structure.d.ts.map +1 -0
- package/dist/src/types/structure.js +8 -0
- package/dist/src/types/structure.js.map +1 -0
- package/dist/src/wgsl/apsp-fw.wgsl.d.ts +25 -0
- package/dist/src/wgsl/apsp-fw.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/apsp-fw.wgsl.js +113 -0
- package/dist/src/wgsl/apsp-fw.wgsl.js.map +1 -0
- package/dist/src/wgsl/apsp-init.wgsl.d.ts +12 -0
- package/dist/src/wgsl/apsp-init.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/apsp-init.wgsl.js +26 -0
- package/dist/src/wgsl/apsp-init.wgsl.js.map +1 -0
- package/dist/src/wgsl/bc-backward.wgsl.d.ts +15 -0
- package/dist/src/wgsl/bc-backward.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/bc-backward.wgsl.js +34 -0
- package/dist/src/wgsl/bc-backward.wgsl.js.map +1 -0
- package/dist/src/wgsl/bc-edge-gather.wgsl.d.ts +12 -0
- package/dist/src/wgsl/bc-edge-gather.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/bc-edge-gather.wgsl.js +36 -0
- package/dist/src/wgsl/bc-edge-gather.wgsl.js.map +1 -0
- package/dist/src/wgsl/bc-finalize.wgsl.d.ts +21 -0
- package/dist/src/wgsl/bc-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/bc-finalize.wgsl.js +47 -0
- package/dist/src/wgsl/bc-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/bc-forward-edge.wgsl.d.ts +15 -0
- package/dist/src/wgsl/bc-forward-edge.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/bc-forward-edge.wgsl.js +76 -0
- package/dist/src/wgsl/bc-forward-edge.wgsl.js.map +1 -0
- package/dist/src/wgsl/bc-forward.wgsl.d.ts +23 -0
- package/dist/src/wgsl/bc-forward.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/bc-forward.wgsl.js +106 -0
- package/dist/src/wgsl/bc-forward.wgsl.js.map +1 -0
- package/dist/src/wgsl/bc-gather.wgsl.d.ts +9 -0
- package/dist/src/wgsl/bc-gather.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/bc-gather.wgsl.js +20 -0
- package/dist/src/wgsl/bc-gather.wgsl.js.map +1 -0
- package/dist/src/wgsl/closeness-reduce.wgsl.d.ts +4 -1
- package/dist/src/wgsl/closeness-reduce.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/closeness-reduce.wgsl.js +8 -4
- package/dist/src/wgsl/closeness-reduce.wgsl.js.map +1 -1
- package/dist/src/wgsl/closeness-sweep.wgsl.d.ts +4 -2
- package/dist/src/wgsl/closeness-sweep.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/closeness-sweep.wgsl.js +12 -2
- package/dist/src/wgsl/closeness-sweep.wgsl.js.map +1 -1
- package/dist/src/wgsl/coo-emit.wgsl.d.ts +10 -0
- package/dist/src/wgsl/coo-emit.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/coo-emit.wgsl.js +33 -0
- package/dist/src/wgsl/coo-emit.wgsl.js.map +1 -0
- package/dist/src/wgsl/coo-scatter.wgsl.d.ts +15 -0
- package/dist/src/wgsl/coo-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/coo-scatter.wgsl.js +32 -0
- package/dist/src/wgsl/coo-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/group-by-key-row.wgsl.d.ts +26 -0
- package/dist/src/wgsl/group-by-key-row.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/group-by-key-row.wgsl.js +146 -0
- package/dist/src/wgsl/group-by-key-row.wgsl.js.map +1 -0
- package/dist/src/wgsl/lpa-step.wgsl.d.ts +10 -0
- package/dist/src/wgsl/lpa-step.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/lpa-step.wgsl.js +35 -0
- package/dist/src/wgsl/lpa-step.wgsl.js.map +1 -0
- package/dist/src/wgsl/orient-flags.wgsl.d.ts +9 -0
- package/dist/src/wgsl/orient-flags.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/orient-flags.wgsl.js +21 -0
- package/dist/src/wgsl/orient-flags.wgsl.js.map +1 -0
- package/dist/src/wgsl/run-flags.wgsl.d.ts +8 -0
- package/dist/src/wgsl/run-flags.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/run-flags.wgsl.js +18 -0
- package/dist/src/wgsl/run-flags.wgsl.js.map +1 -0
- package/dist/src/wgsl/tri-intersect.wgsl.d.ts +11 -0
- package/dist/src/wgsl/tri-intersect.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/tri-intersect.wgsl.js +64 -0
- package/dist/src/wgsl/tri-intersect.wgsl.js.map +1 -0
- package/dist/webgpu-graph-algorithms.js +2828 -321
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +5 -5
- package/src/accelerator.ts +130 -7
- package/src/algorithms/all-pairs.ts +228 -0
- package/src/algorithms/betweenness.ts +739 -0
- package/src/algorithms/closeness.ts +124 -32
- package/src/algorithms/components.ts +2 -2
- package/src/algorithms/label-propagation.ts +280 -0
- package/src/algorithms/simple-symmetric.ts +409 -0
- package/src/algorithms/triangles.ts +240 -0
- package/src/constants.ts +53 -0
- package/src/index.ts +20 -1
- package/src/kernel/prelude.ts +6 -0
- package/src/kernels.ts +411 -10
- package/src/memory/residency.ts +15 -4
- package/src/primitives/coo-to-csr.ts +251 -0
- package/src/primitives/frontier.ts +4 -0
- package/src/primitives/group-by-key.ts +209 -0
- package/src/types/accelerator.ts +26 -6
- package/src/types/algorithms.ts +5 -0
- package/src/types/all-pairs.ts +37 -0
- package/src/types/betweenness.ts +38 -0
- package/src/types/community.ts +18 -0
- package/src/types/structure.ts +28 -0
- package/src/wgsl/apsp-fw.wgsl.ts +112 -0
- package/src/wgsl/apsp-init.wgsl.ts +25 -0
- package/src/wgsl/bc-backward.wgsl.ts +33 -0
- package/src/wgsl/bc-edge-gather.wgsl.ts +35 -0
- package/src/wgsl/bc-finalize.wgsl.ts +46 -0
- package/src/wgsl/bc-forward-edge.wgsl.ts +75 -0
- package/src/wgsl/bc-forward.wgsl.ts +105 -0
- package/src/wgsl/bc-gather.wgsl.ts +19 -0
- package/src/wgsl/closeness-reduce.wgsl.ts +8 -4
- package/src/wgsl/closeness-sweep.wgsl.ts +12 -2
- package/src/wgsl/coo-emit.wgsl.ts +32 -0
- package/src/wgsl/coo-scatter.wgsl.ts +31 -0
- package/src/wgsl/group-by-key-row.wgsl.ts +145 -0
- package/src/wgsl/lpa-step.wgsl.ts +34 -0
- package/src/wgsl/orient-flags.wgsl.ts +20 -0
- package/src/wgsl/run-flags.wgsl.ts +17 -0
- package/src/wgsl/tri-intersect.wgsl.ts +63 -0
- package/dist/chunks/context-Bi6AhScG.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphty/webgpu-graph-algorithms",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.16",
|
|
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",
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"homepage": "https://github.com/graphty-org/graphty-monorepo/tree/master/webgpu-graph-algorithms#readme",
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@webgpu/types": "^0.1.72",
|
|
64
|
-
"@graphty/graph-format": "^1.2.
|
|
64
|
+
"@graphty/graph-format": "^1.2.2"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@graphty/algorithms": "^1.0.0 || ^2.0.0",
|
|
67
|
+
"@graphty/algorithms": "^1.0.0 || ^2.0.0 || ^3.0.0",
|
|
68
68
|
"@graphty/graph-format": "^1.0.0",
|
|
69
69
|
"@graphty/layout": "^1.7.0 || ^2.0.0",
|
|
70
70
|
"webgpu": ">=0.4.0 <1.0.0"
|
|
@@ -95,8 +95,8 @@
|
|
|
95
95
|
"vite": "^7.0.5",
|
|
96
96
|
"vitest": "4.1.11",
|
|
97
97
|
"webgpu": "0.4.0",
|
|
98
|
-
"@graphty/layout": "^2.0.
|
|
99
|
-
"@graphty/algorithms": "^
|
|
98
|
+
"@graphty/layout": "^2.0.2",
|
|
99
|
+
"@graphty/algorithms": "^3.1.0"
|
|
100
100
|
},
|
|
101
101
|
"scripts": {
|
|
102
102
|
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
|
package/src/accelerator.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* P5's `fruchtermanReingold` and `springElectrical` (the two other layout members of spec 9.3, landed together once
|
|
8
8
|
* both models were green, P5 PD-19), P7's seven algorithm members (spec 8.2, 8.3; M8b-T8, PD-14) and P8's four
|
|
9
9
|
* traversal members (spec 8.4; P8-T13 PD-16, PD-19: `breadthFirstSearch`, `sssp`, `bellmanFord`,
|
|
10
|
-
* `closenessCentrality`, each taking the seam's own option type)
|
|
10
|
+
* `closenessCentrality`, each taking the seam's own option type), `allPairsShortestPath` (design 8.7), P11's
|
|
11
|
+
* `triangleCount` and `labelPropagation`, and nothing else: the CPU-side dispatchers
|
|
11
12
|
* (`accelerated()`, `createSimulation()`) test `acc.betweennessCentrality !== undefined` /
|
|
12
13
|
* `acc.fruchtermanReingold !== undefined` and route to the CPU when the member is absent (spec 2.4 row "method
|
|
13
14
|
* missing"), so a method the GPU does not implement must not exist here -- never a throwing stub. The remaining
|
|
@@ -16,20 +17,27 @@
|
|
|
16
17
|
|
|
17
18
|
import { type F32, type F64, type GraphSnapshot } from "@graphty/graph-format";
|
|
18
19
|
|
|
20
|
+
import { allPairsShortestPath } from "./algorithms/all-pairs.js";
|
|
19
21
|
import { bellmanFord } from "./algorithms/bellman-ford.js";
|
|
22
|
+
import { betweennessCentrality, edgeBetweennessCentrality } from "./algorithms/betweenness.js";
|
|
20
23
|
import { breadthFirstSearch } from "./algorithms/bfs.js";
|
|
21
24
|
import { closenessCentrality } from "./algorithms/closeness.js";
|
|
22
25
|
import { connectedComponents } from "./algorithms/components.js";
|
|
26
|
+
import { labelPropagation } from "./algorithms/label-propagation.js";
|
|
23
27
|
import { pageRank, personalizedPageRank } from "./algorithms/pagerank.js";
|
|
24
28
|
import { eigenvectorCentrality, hits, katzCentrality } from "./algorithms/spectral.js";
|
|
25
29
|
import { sssp } from "./algorithms/sssp.js";
|
|
30
|
+
import { triangleCount } from "./algorithms/triangles.js";
|
|
26
31
|
import { type GpuContext } from "./context.js";
|
|
32
|
+
import { WebGpuGraphError } from "./errors.js";
|
|
27
33
|
import { createForceAtlas2 } from "./layouts/forceatlas2.js";
|
|
28
34
|
import { createFruchtermanReingold } from "./layouts/fruchterman-reingold.js";
|
|
29
35
|
import { createSpringElectrical } from "./layouts/spring-electrical.js";
|
|
30
36
|
import {
|
|
31
37
|
type AcceleratorOptions,
|
|
38
|
+
type BetweennessAcceleratorOptions,
|
|
32
39
|
type BfsOptions,
|
|
40
|
+
type ClosenessAcceleratorOptions,
|
|
33
41
|
type GpuAccelerator,
|
|
34
42
|
type HitsOptionsLike,
|
|
35
43
|
type SsspOptions,
|
|
@@ -37,6 +45,7 @@ import {
|
|
|
37
45
|
import {
|
|
38
46
|
type ComponentsOptions,
|
|
39
47
|
type EigenvectorOptions,
|
|
48
|
+
type GpuClosenessResult,
|
|
40
49
|
type GpuHitsResult,
|
|
41
50
|
type GpuLabelResult,
|
|
42
51
|
type GpuPageRankResult,
|
|
@@ -45,6 +54,8 @@ import {
|
|
|
45
54
|
type KatzOptions,
|
|
46
55
|
type PageRankOptions,
|
|
47
56
|
} from "./types/algorithms.js";
|
|
57
|
+
import { type GpuApspResult } from "./types/all-pairs.js";
|
|
58
|
+
import { type GpuBetweennessResult, type GpuEdgeScoresResult } from "./types/betweenness.js";
|
|
48
59
|
import {
|
|
49
60
|
type ForceAtlas2Stats,
|
|
50
61
|
type FruchtermanReingoldStats,
|
|
@@ -57,6 +68,7 @@ import {
|
|
|
57
68
|
type FruchtermanReingoldOptions,
|
|
58
69
|
type SpringElectricalOptions,
|
|
59
70
|
} from "./types/options.js";
|
|
71
|
+
import { type GpuTriangleResult } from "./types/structure.js";
|
|
60
72
|
import { type GpuBellmanFordResult, type GpuBfsResult, type GpuSsspResult } from "./types/traversal.js";
|
|
61
73
|
|
|
62
74
|
/** The `algorithms` record of AcceleratorOptions (spec 3.3), named for the copy helpers. */
|
|
@@ -79,6 +91,30 @@ function copyBetweenness(defaults: BetweennessDefaults): BetweennessDefaults {
|
|
|
79
91
|
return Object.freeze(copy);
|
|
80
92
|
}
|
|
81
93
|
|
|
94
|
+
/**
|
|
95
|
+
* A betweenness call's options with the accelerator's `algorithms.betweenness` defaults applied: the defaults supply
|
|
96
|
+
* `sources` / `k` only when the call names neither, so a call's own sampling always wins whole. The defaults serve
|
|
97
|
+
* graphs of every size, so they are fitted to this one: a default `sources` list keeps only the indices below
|
|
98
|
+
* `nodeCount` (and then wins over a default `k`), and a default `k` of `nodeCount` or more runs every vertex.
|
|
99
|
+
* @param defaults - the frozen defaults, if any
|
|
100
|
+
* @param options - the call's options
|
|
101
|
+
* @param nodeCount - the snapshot's vertex count
|
|
102
|
+
* @returns the options the driver runs with
|
|
103
|
+
*/
|
|
104
|
+
function withBetweennessDefaults(
|
|
105
|
+
defaults: BetweennessDefaults | undefined,
|
|
106
|
+
options: BetweennessAcceleratorOptions | undefined,
|
|
107
|
+
nodeCount: number,
|
|
108
|
+
): BetweennessAcceleratorOptions | undefined {
|
|
109
|
+
if (defaults === undefined || options?.sources !== undefined || options?.k !== undefined) {
|
|
110
|
+
return options;
|
|
111
|
+
}
|
|
112
|
+
if (defaults.sources !== undefined) {
|
|
113
|
+
return { ...options, sources: defaults.sources.filter((v) => v < nodeCount) };
|
|
114
|
+
}
|
|
115
|
+
return { ...options, k: defaults.k !== undefined && defaults.k < nodeCount ? defaults.k : undefined };
|
|
116
|
+
}
|
|
117
|
+
|
|
82
118
|
/**
|
|
83
119
|
* Frozen copy of the algorithms record, one level deeper for `betweenness`.
|
|
84
120
|
* @param algorithms - the caller's record
|
|
@@ -118,8 +154,9 @@ function freezeOptions(options: AcceleratorOptions | undefined): Readonly<Accele
|
|
|
118
154
|
* Spec 3.3 createAccelerator, verbatim: the object implementing AlgorithmAccelerator & LayoutAccelerator
|
|
119
155
|
* structurally; P3's forceAtlas2, release and dispose, P5's fruchtermanReingold and springElectrical (the same
|
|
120
156
|
* `{ ...o, ...options.layout }` shape as forceAtlas2) plus P7's seven algorithm members and P8's four traversal
|
|
121
|
-
* members, each a delegation to its algorithm with `ctx.assertReady()` first.
|
|
122
|
-
*
|
|
157
|
+
* members and the two betweenness members, each a delegation to its algorithm with `ctx.assertReady()` first. Only
|
|
158
|
+
* the betweenness members consult the accelerator's algorithm defaults (`algorithms.betweenness` supplies `sources` /
|
|
159
|
+
* `k` when a call names neither). One per call (the app creates one and injects
|
|
123
160
|
* it, spec 2.4); `kind` is "webgpu"; `options` is a frozen deep copy; `forceAtlas2(o)` is
|
|
124
161
|
* `createForceAtlas2(ctx, { ...o, ...options.layout })`, so the GPU tuning given here wins over anything the
|
|
125
162
|
* CPU-typed option object carries (spec 3.3: tuning never comes from the caller of the accelerator method);
|
|
@@ -163,7 +200,9 @@ export function createAccelerator(ctx: GpuContext, options?: AcceleratorOptions)
|
|
|
163
200
|
* @param o - the CPU option type (spec 9.3 SpringElectricalOptions, ngraph's names)
|
|
164
201
|
* @returns a fresh simulation in state "created"
|
|
165
202
|
*/
|
|
166
|
-
springElectrical(
|
|
203
|
+
springElectrical(
|
|
204
|
+
o?: SpringElectricalOptions,
|
|
205
|
+
): GpuLayoutSimulation<SpringElectricalOptions, SpringElectricalStats> {
|
|
167
206
|
ctx.assertReady();
|
|
168
207
|
return createSpringElectrical(ctx, { ...o, ...frozen.layout });
|
|
169
208
|
},
|
|
@@ -278,17 +317,101 @@ export function createAccelerator(ctx: GpuContext, options?: AcceleratorOptions)
|
|
|
278
317
|
ctx.assertReady();
|
|
279
318
|
return await bellmanFord(ctx, gs, source, o);
|
|
280
319
|
},
|
|
320
|
+
/**
|
|
321
|
+
* Betweenness centrality on the device (spec 8.4): exact, or sampled through `sources` / `k` (the call's own,
|
|
322
|
+
* else the accelerator's `algorithms.betweenness` defaults), the unscaled sum over the sources run.
|
|
323
|
+
* `endpoints: true` is refused.
|
|
324
|
+
* @param gs - the snapshot
|
|
325
|
+
* @param o - the seam's `BetweennessAcceleratorOptions`
|
|
326
|
+
* @returns the f32 scores with `sourcesUsed` and `sigmaOverflow`
|
|
327
|
+
*/
|
|
328
|
+
async betweennessCentrality(
|
|
329
|
+
gs: GraphSnapshot,
|
|
330
|
+
o?: BetweennessAcceleratorOptions,
|
|
331
|
+
): Promise<GpuBetweennessResult> {
|
|
332
|
+
ctx.assertReady();
|
|
333
|
+
return await betweennessCentrality(
|
|
334
|
+
ctx,
|
|
335
|
+
gs,
|
|
336
|
+
withBetweennessDefaults(frozen.algorithms?.betweenness, o, gs.nodeCount),
|
|
337
|
+
);
|
|
338
|
+
},
|
|
339
|
+
/**
|
|
340
|
+
* Edge betweenness on the device (spec 8.4): one score per edge, arcs summed and halved when undirected; sampling
|
|
341
|
+
* and defaults as `betweennessCentrality`.
|
|
342
|
+
* @param gs - the snapshot
|
|
343
|
+
* @param o - the seam's `BetweennessAcceleratorOptions`
|
|
344
|
+
* @returns the f32 per-edge scores with `sourcesUsed` and `sigmaOverflow`
|
|
345
|
+
*/
|
|
346
|
+
async edgeBetweennessCentrality(
|
|
347
|
+
gs: GraphSnapshot,
|
|
348
|
+
o?: BetweennessAcceleratorOptions,
|
|
349
|
+
): Promise<GpuEdgeScoresResult> {
|
|
350
|
+
ctx.assertReady();
|
|
351
|
+
return await edgeBetweennessCentrality(
|
|
352
|
+
ctx,
|
|
353
|
+
gs,
|
|
354
|
+
withBetweennessDefaults(frozen.algorithms?.betweenness, o, gs.nodeCount),
|
|
355
|
+
);
|
|
356
|
+
},
|
|
281
357
|
/**
|
|
282
358
|
* Closeness centrality on the device (spec 8.4; P8-T13): the bit-parallel multi-source sweep, or one `sssp`
|
|
283
359
|
* per source when `weighted`. `maxIterations` / `tolerance` are refused when defined (P8 PD-25).
|
|
284
360
|
* @param gs - the snapshot
|
|
285
|
-
* @param o -
|
|
286
|
-
* @returns the f32 scores with `precision: "f32"` (spec 9.7)
|
|
361
|
+
* @param o - `weighted`, and a sampled run's `sources` (undirected snapshots only)
|
|
362
|
+
* @returns the f32 scores with `precision: "f32"` (spec 9.7) and `sourcesUsed`
|
|
287
363
|
*/
|
|
288
|
-
async closenessCentrality(gs: GraphSnapshot, o?:
|
|
364
|
+
async closenessCentrality(gs: GraphSnapshot, o?: ClosenessAcceleratorOptions): Promise<GpuClosenessResult> {
|
|
289
365
|
ctx.assertReady();
|
|
290
366
|
return await closenessCentrality(ctx, gs, o);
|
|
291
367
|
},
|
|
368
|
+
/**
|
|
369
|
+
* All-pairs shortest paths on the device (design 8.7): blocked Floyd-Warshall, `E_TOO_LARGE` above the device's
|
|
370
|
+
* storage-binding ceiling. The seam passes `SsspOptions`; neither of its keys has an all-pairs meaning, so a
|
|
371
|
+
* defined `cutoff` (it would change what `+Infinity` means) or `weights` (a per-arc override is a different
|
|
372
|
+
* matrix from the snapshot's resident column) is `E_UNSUPPORTED { option }`, never silently dropped.
|
|
373
|
+
* @param gs - the snapshot
|
|
374
|
+
* @param o - the seam's `SsspOptions`; both keys refused when defined
|
|
375
|
+
* @returns the row-major `n x n` distances and `n` (spec 3.3 line 835)
|
|
376
|
+
*/
|
|
377
|
+
async allPairsShortestPath(gs: GraphSnapshot, o?: SsspOptions): Promise<GpuApspResult> {
|
|
378
|
+
ctx.assertReady();
|
|
379
|
+
for (const key of ["cutoff", "weights"] as const) {
|
|
380
|
+
if (o?.[key] !== undefined) {
|
|
381
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `allPairsShortestPath: ${key} is not supported`, {
|
|
382
|
+
option: key,
|
|
383
|
+
hint: "all-pairs shortest paths runs over the snapshot's own weights with no cutoff",
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return await allPairsShortestPath(ctx, gs);
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* Triangle counting with the clustering coefficient and the transitivity (design 8.5; P11).
|
|
391
|
+
* @param gs - the snapshot
|
|
392
|
+
* @returns perNode, total, coefficient and transitivity
|
|
393
|
+
*/
|
|
394
|
+
async triangleCount(gs: GraphSnapshot): Promise<GpuTriangleResult> {
|
|
395
|
+
ctx.assertReady();
|
|
396
|
+
return await triangleCount(ctx, gs);
|
|
397
|
+
},
|
|
398
|
+
/**
|
|
399
|
+
* Label propagation (design 8.6; P11): `maxIterations` and `weighted` are honoured, `tolerance` is refused when
|
|
400
|
+
* defined (a label propagation stops at a fixed point, not below a tolerance).
|
|
401
|
+
* @param gs - the snapshot
|
|
402
|
+
* @param o - the seam's placeholder `HitsOptionsLike`
|
|
403
|
+
* @returns the labels dense in first-seen order, the community count and groups()
|
|
404
|
+
*/
|
|
405
|
+
async labelPropagation(gs: GraphSnapshot, o?: HitsOptionsLike): Promise<GpuLabelResult> {
|
|
406
|
+
ctx.assertReady();
|
|
407
|
+
if (o?.tolerance !== undefined) {
|
|
408
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", "labelPropagation: tolerance has no meaning here", {
|
|
409
|
+
option: "tolerance",
|
|
410
|
+
hint: "label propagation stops at a fixed point or after maxIterations passes",
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return await labelPropagation(ctx, gs, { maxIterations: o?.maxIterations, weighted: o?.weighted });
|
|
414
|
+
},
|
|
292
415
|
/**
|
|
293
416
|
* Destroys every device buffer recorded for the snapshot (spec 4.5); delegates to ctx.release.
|
|
294
417
|
* @param s - the snapshot the app is done with
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All-pairs shortest paths on the device (design 8.7, 3.3 lines 813 and 835, 9.7): blocked Floyd-Warshall over
|
|
3
|
+
* `APSP_TILE x APSP_TILE` tiles of ONE `n x n` f32 matrix. `fill` sets the matrix to `+Infinity`, `apsp-init` writes
|
|
4
|
+
* the arcs (the cheapest of parallel arcs) and the diagonal zero, then `B = ceil(n / APSP_TILE)` rounds each run the
|
|
5
|
+
* three `apsp-fw` phases in order: the pivot block, the pivot row and column, everything else. The whole sweep --
|
|
6
|
+
* `3 B` dispatches -- is recorded into ONE compute pass: WebGPU runs the dispatches of a pass in order and makes each
|
|
7
|
+
* one's writes visible to the next, so nothing is read back until the matrix is done. Above
|
|
8
|
+
* `APSP_MAX_DISPATCHES_PER_SUBMIT` dispatches the sweep is split into further submits and `signal` is checked
|
|
9
|
+
* between them -- but no binding WebGPU devices offer today reaches that (a 4 GiB binding is 32,767 nodes, 3,072
|
|
10
|
+
* dispatches), so in practice the sweep is one submit: `signal` is checked before it and after it, never during,
|
|
11
|
+
* and `onProgress` fires once. The result is bitwise reproducible (no atomics, and a cell is only ever written by one
|
|
12
|
+
* lane per dispatch), exact for hop counts, and the minimum of f32 path sums for weights.
|
|
13
|
+
*
|
|
14
|
+
* The ceiling: the matrix is exactly `n * n` (never padded to the tile) and is bound as ONE storage binding, so
|
|
15
|
+
* `n <= floor(sqrt(limit / 4))` with `limit` the smaller of the device's `maxStorageBufferBindingSize` and
|
|
16
|
+
* `maxBufferSize`. A context's default `limits: "raise"` takes the adapter's own limits: 23,170 nodes on a hardware
|
|
17
|
+
* adapter under Dawn (a 2 GiB binding), 5,792 on lavapipe or under `limits: "default"` (the 128 MiB spec default).
|
|
18
|
+
* Above it the call throws `E_TOO_LARGE` naming the node count, the ceiling, the limit it read and
|
|
19
|
+
* `GpuContextOptions.limits` as the way to raise it; the rows are never windowed. Refused before any device work: a negative weight (`E_UNSUPPORTED
|
|
20
|
+
* allPairs.negativeWeights` -- Floyd-Warshall's in-place tile update is race-free only while the diagonal stays 0)
|
|
21
|
+
* and a NaN or infinite one (`allPairs.nonFiniteWeights`). `weighted` defaults to "the snapshot has weights";
|
|
22
|
+
* `weighted: false` on a weighted snapshot computes hop counts. The empty graph returns an empty matrix without a
|
|
23
|
+
* dispatch. `allPairsWithTuning` is what the tests drive; nothing public exposes it.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { type GraphSnapshot } from "@graphty/graph-format";
|
|
27
|
+
|
|
28
|
+
import { APSP_MAX_DISPATCHES_PER_SUBMIT, APSP_TILE, F32_INF_BITS } from "../constants.js";
|
|
29
|
+
import { type GpuContext } from "../context.js";
|
|
30
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
31
|
+
import { CommandBatch } from "../kernel/batch.js";
|
|
32
|
+
import { plan1d, plan2d } from "../kernel/dispatch.js";
|
|
33
|
+
import { APSP_PARAMS, FILL_PARAMS, graphBindings, graphOverrides, kernelSpec } from "../kernels.js";
|
|
34
|
+
import { assertWholeCore } from "../primitives/core-shape.js";
|
|
35
|
+
import { assertDeviceComputes } from "../primitives/verify.js";
|
|
36
|
+
import { type ApspOptions, type GpuApspResult } from "../types/all-pairs.js";
|
|
37
|
+
import { type GpuRunOptions } from "../types/run.js";
|
|
38
|
+
import { algorithmScope } from "./scope.js";
|
|
39
|
+
import { aborted, bindingOf, checkDest } from "./sssp.js";
|
|
40
|
+
|
|
41
|
+
const ALGORITHM = "allPairsShortestPath";
|
|
42
|
+
|
|
43
|
+
/** The rounds one submit holds by default: three dispatches per round. */
|
|
44
|
+
const DEFAULT_ROUNDS_PER_SUBMIT = Math.floor(APSP_MAX_DISPATCHES_PER_SUBMIT / 3);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The knobs the tests need and nothing public offers.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export interface AllPairsTuning {
|
|
51
|
+
/** Rounds recorded per submit (default `floor(APSP_MAX_DISPATCHES_PER_SUBMIT / 3)`); 1 submits every round alone. */
|
|
52
|
+
readonly roundsPerSubmit?: number | undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The largest node count whose `n x n` f32 matrix fits the device (design 8.7): `floor(sqrt(limit / 4))` over the
|
|
57
|
+
* smaller of `maxStorageBufferBindingSize` and `maxBufferSize`, corrected so the float square root can never
|
|
58
|
+
* overshoot by one.
|
|
59
|
+
* @internal
|
|
60
|
+
* @param limits - the device limits
|
|
61
|
+
* @returns the ceiling and the limit that set it
|
|
62
|
+
*/
|
|
63
|
+
export function allPairsCeiling(limits: Pick<GPUSupportedLimits, "maxStorageBufferBindingSize" | "maxBufferSize">): {
|
|
64
|
+
readonly maxNodes: number;
|
|
65
|
+
readonly limit: number;
|
|
66
|
+
readonly limitName: "maxStorageBufferBindingSize" | "maxBufferSize";
|
|
67
|
+
} {
|
|
68
|
+
const binding = limits.maxStorageBufferBindingSize;
|
|
69
|
+
const bufferSize = limits.maxBufferSize;
|
|
70
|
+
const limitName = bufferSize < binding ? "maxBufferSize" : "maxStorageBufferBindingSize";
|
|
71
|
+
const limit = Math.min(binding, bufferSize);
|
|
72
|
+
let maxNodes = Math.floor(Math.sqrt(limit / 4));
|
|
73
|
+
while (4 * maxNodes * maxNodes > limit) {
|
|
74
|
+
maxNodes -= 1;
|
|
75
|
+
}
|
|
76
|
+
while (4 * (maxNodes + 1) * (maxNodes + 1) <= limit) {
|
|
77
|
+
maxNodes += 1;
|
|
78
|
+
}
|
|
79
|
+
return { maxNodes, limit, limitName };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* All-pairs shortest paths with the test knobs; `allPairsShortestPath` is this with an empty tuning.
|
|
84
|
+
* @internal
|
|
85
|
+
* @param ctx - the context whose device runs the kernels
|
|
86
|
+
* @param s - the snapshot (uploaded through ctx.residency, or found there)
|
|
87
|
+
* @param options - `weighted`, plus dest (a Float32Array of length n * n) / signal / onProgress (rounds done of `ceil(n / 32)`)
|
|
88
|
+
* @param tuning - the knobs
|
|
89
|
+
* @returns the row-major `n x n` distances and `n`
|
|
90
|
+
*/
|
|
91
|
+
export async function allPairsWithTuning(
|
|
92
|
+
ctx: GpuContext,
|
|
93
|
+
s: GraphSnapshot,
|
|
94
|
+
options: (ApspOptions & GpuRunOptions) | undefined,
|
|
95
|
+
tuning: AllPairsTuning,
|
|
96
|
+
): Promise<GpuApspResult> {
|
|
97
|
+
ctx.assertReady();
|
|
98
|
+
const n = s.nodeCount;
|
|
99
|
+
const roundsPerSubmit = tuning.roundsPerSubmit ?? DEFAULT_ROUNDS_PER_SUBMIT;
|
|
100
|
+
if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > DEFAULT_ROUNDS_PER_SUBMIT) {
|
|
101
|
+
throw new WebGpuGraphError(
|
|
102
|
+
"E_INVALID_ARGUMENT",
|
|
103
|
+
`${ALGORITHM}: roundsPerSubmit must be an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`,
|
|
104
|
+
{
|
|
105
|
+
argument: "roundsPerSubmit",
|
|
106
|
+
value: roundsPerSubmit,
|
|
107
|
+
expected: `an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`,
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const { maxNodes, limit, limitName } = allPairsCeiling(ctx.caps.limits);
|
|
112
|
+
if (n > maxNodes) {
|
|
113
|
+
throw new WebGpuGraphError(
|
|
114
|
+
"E_TOO_LARGE",
|
|
115
|
+
`${ALGORITHM}: ${n} nodes need a ${4 * n * n}-byte distance matrix in one storage binding; this device's ${limitName} of ${limit} bytes holds at most ${maxNodes} nodes -- raise it through GpuContextOptions.limits`,
|
|
116
|
+
{
|
|
117
|
+
needed: 4 * n * n,
|
|
118
|
+
limit,
|
|
119
|
+
path: "allPairs.matrix",
|
|
120
|
+
algorithm: ALGORITHM,
|
|
121
|
+
nodes: n,
|
|
122
|
+
maxNodes,
|
|
123
|
+
limitName,
|
|
124
|
+
hint: `raise ${limitName} through GpuContextOptions.limits`,
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const dist = checkDest(ALGORITHM, options?.dest, n * n) ?? new Float32Array(n * n);
|
|
129
|
+
const weighted = (options?.weighted ?? s.weights !== null) && s.weights !== null && !s.flags.allWeightsOne;
|
|
130
|
+
if (weighted && !s.flags.nonNegativeWeights) {
|
|
131
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM}: a negative weight is not supported`, {
|
|
132
|
+
feature: "allPairs.negativeWeights",
|
|
133
|
+
hint: "the blocked Floyd-Warshall sweep needs non-negative weights; pass weighted: false for hop counts",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (weighted && !s.flags.finiteWeights) {
|
|
137
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM}: a NaN or infinite weight has no shortest path`, {
|
|
138
|
+
feature: "allPairs.nonFiniteWeights",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (options?.signal?.aborted) {
|
|
142
|
+
throw aborted(ALGORITHM);
|
|
143
|
+
}
|
|
144
|
+
if (n === 0) {
|
|
145
|
+
return { dist, n };
|
|
146
|
+
}
|
|
147
|
+
await assertDeviceComputes(ctx);
|
|
148
|
+
const core = ctx.residency.core(s);
|
|
149
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM);
|
|
150
|
+
const blocks = Math.ceil(n / APSP_TILE);
|
|
151
|
+
const scope = algorithmScope(ctx, ALGORITHM, Math.min(roundsPerSubmit, blocks) + 2);
|
|
152
|
+
try {
|
|
153
|
+
const wg = ctx.workgroupSize;
|
|
154
|
+
const bytes = 4 * n * n;
|
|
155
|
+
const matrix = bindingOf(scope.scratch(bytes, "dist"), bytes);
|
|
156
|
+
await ctx.allocator.check();
|
|
157
|
+
const weightsBinding = weighted ? undefined : null;
|
|
158
|
+
const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
|
|
159
|
+
const init = await ctx.pipelines.kernel(kernelSpec("apsp-init", graphOverrides(core, null, weightsBinding)));
|
|
160
|
+
const phases = await Promise.all(
|
|
161
|
+
[0, 1, 2].map((phase) => ctx.pipelines.kernel(kernelSpec("apsp-fw", { PHASE: phase }))),
|
|
162
|
+
);
|
|
163
|
+
const graph = graphBindings(core, null, weightsBinding);
|
|
164
|
+
const others = blocks - 1;
|
|
165
|
+
const phasePlans = [plan2d(1, ctx.caps), plan2d(2 * others, ctx.caps), plan2d(others * others, ctx.caps)];
|
|
166
|
+
|
|
167
|
+
for (let first = 0; first < blocks; first += roundsPerSubmit) {
|
|
168
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM}/sweep`);
|
|
169
|
+
const pass = batch.pass("apsp");
|
|
170
|
+
if (first === 0) {
|
|
171
|
+
const fillParams = scope.params(FILL_PARAMS, { count: n * n, value: F32_INF_BITS, mode: 0, pad0: 0 });
|
|
172
|
+
fill.dispatch(pass, fill.bind({ dst: matrix, P: fillParams.binding }), plan1d(n * n, wg, ctx.caps), [
|
|
173
|
+
fillParams.offset,
|
|
174
|
+
]);
|
|
175
|
+
const initParams = scope.params(APSP_PARAMS, { n, round: 0, blocks, infBits: F32_INF_BITS });
|
|
176
|
+
init.dispatch(
|
|
177
|
+
pass,
|
|
178
|
+
init.bind({ ...graph, dist: matrix, P: initParams.binding }),
|
|
179
|
+
plan1d(n, wg, ctx.caps),
|
|
180
|
+
[initParams.offset],
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const last = Math.min(first + roundsPerSubmit, blocks);
|
|
184
|
+
for (let round = first; round < last; round++) {
|
|
185
|
+
const params = scope.params(APSP_PARAMS, { n, round, blocks, infBits: F32_INF_BITS });
|
|
186
|
+
for (let phase = 0; phase < 3; phase++) {
|
|
187
|
+
const kernel = phases[phase];
|
|
188
|
+
kernel.dispatch(pass, kernel.bind({ dist: matrix, P: params.binding }), phasePlans[phase], [
|
|
189
|
+
params.offset,
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
batch.endPass();
|
|
194
|
+
scope.flush();
|
|
195
|
+
const submitted = batch.submit();
|
|
196
|
+
await submitted.readback;
|
|
197
|
+
ctx.assertReady();
|
|
198
|
+
options?.onProgress?.(last, blocks);
|
|
199
|
+
if (options?.signal?.aborted) {
|
|
200
|
+
throw aborted(ALGORITHM, submitted.id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
await ctx.readback.read(matrix.buffer, bytes, dist);
|
|
204
|
+
ctx.assertReady();
|
|
205
|
+
return { dist, n };
|
|
206
|
+
} finally {
|
|
207
|
+
scope.dispose();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* All-pairs shortest paths on the device (design 8.7, 3.3 line 813): blocked Floyd-Warshall over 32 x 32 tiles of one
|
|
213
|
+
* `n x n` f32 matrix. `dist[i * n + j]` is the distance from `i` to `j`, `+Infinity` when unreachable, `0` on the
|
|
214
|
+
* diagonal. `E_TOO_LARGE` above `floor(sqrt(maxStorageBufferBindingSize / 4))` nodes (23,170 at a 2 GiB binding, the
|
|
215
|
+
* usual hardware adapter under the context's default `limits: "raise"`; 5,792 at the 128 MiB spec default);
|
|
216
|
+
* `E_UNSUPPORTED` for a negative or non-finite weight unless `weighted: false`.
|
|
217
|
+
* @param ctx - the context whose device runs the kernels
|
|
218
|
+
* @param s - the snapshot (uploaded through ctx.residency, or found there)
|
|
219
|
+
* @param options - `weighted` (default: the snapshot has weights), plus dest (a Float32Array of length n * n) / signal / onProgress
|
|
220
|
+
* @returns the row-major `n x n` distances and `n`
|
|
221
|
+
*/
|
|
222
|
+
export function allPairsShortestPath(
|
|
223
|
+
ctx: GpuContext,
|
|
224
|
+
s: GraphSnapshot,
|
|
225
|
+
options?: ApspOptions & GpuRunOptions,
|
|
226
|
+
): Promise<GpuApspResult> {
|
|
227
|
+
return allPairsWithTuning(ctx, s, options, {});
|
|
228
|
+
}
|