@graphty/webgpu-graph-algorithms 0.6.21 → 0.6.22
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 +1 -1
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-VIvatQOo.js → context-B40Z6lV_.js} +39 -30
- package/dist/chunks/context-B40Z6lV_.js.map +1 -0
- package/dist/node.js +1 -1
- package/dist/src/accelerator.d.ts +1 -1
- package/dist/src/accelerator.d.ts.map +1 -1
- package/dist/src/accelerator.js +20 -1
- package/dist/src/accelerator.js.map +1 -1
- package/dist/src/algorithms/mst.d.ts +50 -0
- package/dist/src/algorithms/mst.d.ts.map +1 -0
- package/dist/src/algorithms/mst.js +245 -0
- package/dist/src/algorithms/mst.js.map +1 -0
- package/dist/src/constants.d.ts +6 -1
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +6 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/prelude.d.ts.map +1 -1
- package/dist/src/kernel/prelude.js +7 -1
- package/dist/src/kernel/prelude.js.map +1 -1
- package/dist/src/kernels.d.ts +5 -2
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +58 -2
- package/dist/src/kernels.js.map +1 -1
- package/dist/src/types/accelerator.d.ts +7 -5
- package/dist/src/types/accelerator.d.ts.map +1 -1
- package/dist/src/types/structure.d.ts +17 -0
- package/dist/src/types/structure.d.ts.map +1 -1
- package/dist/src/wgsl/mst-best.wgsl.d.ts +13 -0
- package/dist/src/wgsl/mst-best.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/mst-best.wgsl.js +32 -0
- package/dist/src/wgsl/mst-best.wgsl.js.map +1 -0
- package/dist/src/wgsl/mst-link.wgsl.d.ts +14 -0
- package/dist/src/wgsl/mst-link.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/mst-link.wgsl.js +39 -0
- package/dist/src/wgsl/mst-link.wgsl.js.map +1 -0
- package/dist/webgpu-graph-algorithms.js +473 -162
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +3 -3
- package/src/accelerator.ts +22 -2
- package/src/algorithms/mst.ts +269 -0
- package/src/constants.ts +6 -1
- package/src/index.ts +3 -1
- package/src/kernel/prelude.ts +7 -0
- package/src/kernels.ts +64 -3
- package/src/types/accelerator.ts +7 -3
- package/src/types/structure.ts +18 -0
- package/src/wgsl/mst-best.wgsl.ts +31 -0
- package/src/wgsl/mst-link.wgsl.ts +38 -0
- package/dist/chunks/context-VIvatQOo.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { W as WebGpuGraphError, U as UNIFORM_SLOT_BYTES, B as BufferUsage, M as MAX_WORKGROUPS_PER_DIM, a as WGSL_RESERVED_WORDS, S as STATE_HEADER_BYTES, d as deviceLostError, i as isWebGpuGraphError, b as U32_MAX$2, c as MAX_LEVELS_PER_SUBMIT, R as RADIX_BINS, F as FUSED_FRONTIER_MAX, e as BEAMER_BETA, f as SSSP_DELTA_FACTOR, g as F32_INF_BITS, h as BC_EDGE_PARALLEL_GAMMA, j as BC_BATCH_BUDGET_FRACTION, k as BC_MAX_BATCH, l as BC_BACKWARD_LEVELS_PER_SUBMIT, A as APSP_MAX_DISPATCHES_PER_SUBMIT, m as APSP_TILE, n as GROUP_ROW_THREAD_MAX, o as GROUP_ROW_THREAD_LIMIT, p as GROUP_HASH_LOAD_FACTOR, P as PARALLEL_MERGE_LIMIT, L as LABEL_PROP_PASSES_PER_SUBMIT, q as
|
|
2
|
-
import {
|
|
1
|
+
import { W as WebGpuGraphError, U as UNIFORM_SLOT_BYTES, B as BufferUsage, M as MAX_WORKGROUPS_PER_DIM, a as WGSL_RESERVED_WORDS, S as STATE_HEADER_BYTES, d as deviceLostError, i as isWebGpuGraphError, b as U32_MAX$2, c as MAX_LEVELS_PER_SUBMIT, R as RADIX_BINS, F as FUSED_FRONTIER_MAX, e as BEAMER_BETA, f as SSSP_DELTA_FACTOR, g as F32_INF_BITS, h as BC_EDGE_PARALLEL_GAMMA, j as BC_BATCH_BUDGET_FRACTION, k as BC_MAX_BATCH, l as BC_BACKWARD_LEVELS_PER_SUBMIT, A as APSP_MAX_DISPATCHES_PER_SUBMIT, m as APSP_TILE, n as GROUP_ROW_THREAD_MAX, o as GROUP_ROW_THREAD_LIMIT, p as GROUP_HASH_LOAD_FACTOR, P as PARALLEL_MERGE_LIMIT, L as LABEL_PROP_PASSES_PER_SUBMIT, q as BORUVKA_ROUNDS_PER_SUBMIT, r as GRID_COARSEST_SIDE, s as GRID_MIN_SIDE, t as GRID_SORT_BITS, u as FA2_DEFAULTS, v as MAX_ITERATIONS_PER_STEP, w as MAX_1D_ITEMS, x as hasErrorCode, y as FA2_FLAG_FIRST, z as PARTIAL_BYTES, E as EXACT_TILES_PER_PASS, I as INDIRECT_ARGS_STRIDE, C as GRID_HUB_CELL, D as LAYOUT_TUNING_DEFAULTS, H as EXACT_MAX_NODES, J as SETTLE_FLOOR_UNBOUNDED, T as TRACE_RECORD_BYTES, K as GRID_BBOX_MARGIN, N as GRID_EXTENT_FLOOR, O as FR_ADAPTIVE_MAX_ITERATIONS, Q as FR_START_TEMPERATURE, V as FA2_FLAG_ADAPTIVE, X as SETTLE_FLOOR_FRACTION, Y as FR_REHEAT_FRACTION, Z as FR_DEFAULTS, _ as SE_DEFAULTS, $ as SETTLE_FLOOR_REFERENCE_NODES, a0 as SE_SCALE_REFERENCE_NODES } from "./chunks/context-B40Z6lV_.js";
|
|
2
|
+
import { a1, G, a2, a3, a4, a5 } from "./chunks/context-B40Z6lV_.js";
|
|
3
3
|
import { renumberPartition, INVALID_INDEX, foldArcs, makeMask, maskTest, expandEdges, fromEdgeArrays } from "@graphty/graph-format";
|
|
4
4
|
class UniformRing {
|
|
5
5
|
/**
|
|
@@ -2539,6 +2539,58 @@ fn lpa_step(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id)
|
|
|
2539
2539
|
}
|
|
2540
2540
|
`
|
|
2541
2541
|
);
|
|
2542
|
+
const mstBestWgsl = (
|
|
2543
|
+
/* wgsl */
|
|
2544
|
+
`
|
|
2545
|
+
@compute @workgroup_size(WG)
|
|
2546
|
+
fn mst_best(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2547
|
+
let e = linear_id(wid, lid.x);
|
|
2548
|
+
if (e >= P.count) { return; }
|
|
2549
|
+
let cu = comp[edgeSrc[e]];
|
|
2550
|
+
let cv = comp[edgeDst[e]];
|
|
2551
|
+
if (cu == cv) { return; } // inside one component, or a self-loop
|
|
2552
|
+
var w = 1.0;
|
|
2553
|
+
if (WEIGHTED) { w = edgeWeight[e]; }
|
|
2554
|
+
let k = order_key(w);
|
|
2555
|
+
if (PASS == 0u) {
|
|
2556
|
+
atomicMin(&bestKey[cu], k);
|
|
2557
|
+
atomicMin(&bestKey[cv], k);
|
|
2558
|
+
} else {
|
|
2559
|
+
if (k == atomicLoad(&bestKey[cu])) { atomicMin(&bestEdge[cu], e); }
|
|
2560
|
+
if (k == atomicLoad(&bestKey[cv])) { atomicMin(&bestEdge[cv], e); }
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
`
|
|
2564
|
+
);
|
|
2565
|
+
const mstLinkWgsl = (
|
|
2566
|
+
/* wgsl */
|
|
2567
|
+
`
|
|
2568
|
+
var<workgroup> added: atomic<u32>;
|
|
2569
|
+
|
|
2570
|
+
@compute @workgroup_size(WG)
|
|
2571
|
+
fn mst_link(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2572
|
+
if (lid.x == 0u) { atomicStore(&added, 0u); }
|
|
2573
|
+
workgroupBarrier();
|
|
2574
|
+
let c = linear_id(wid, lid.x);
|
|
2575
|
+
if (c < P.count) {
|
|
2576
|
+
let e = bestEdge[c];
|
|
2577
|
+
if (e != INVALID_INDEX) {
|
|
2578
|
+
let a = atomicLoad(&comp[edgeSrc[e]]);
|
|
2579
|
+
let b = atomicLoad(&comp[edgeDst[e]]);
|
|
2580
|
+
let other = select(a, b, a == c);
|
|
2581
|
+
let keep = bestEdge[other] == e && c <= other; // the lower root of a two-cycle stays
|
|
2582
|
+
if (!keep) { atomicStore(&comp[c], other); }
|
|
2583
|
+
if (!keep) { treeEdge[c] = e; atomicAdd(&added, 1u); }
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
workgroupBarrier(); // every lane, unconditionally
|
|
2587
|
+
if (lid.x == 0u) {
|
|
2588
|
+
let k = atomicLoad(&added);
|
|
2589
|
+
if (k > 0u) { atomicAdd(&counters[P.counterIndex], k); } // one atomic per workgroup
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
`
|
|
2593
|
+
);
|
|
2542
2594
|
const orientFlagsWgsl = (
|
|
2543
2595
|
/* wgsl */
|
|
2544
2596
|
`
|
|
@@ -3544,6 +3596,12 @@ const LPA_PARAMS = UniformBlock.define("LpaParams", [
|
|
|
3544
3596
|
["counterIndex", "u32"],
|
|
3545
3597
|
["pad0", "u32"]
|
|
3546
3598
|
]);
|
|
3599
|
+
const MST_PARAMS = UniformBlock.define("MstParams", [
|
|
3600
|
+
["count", "u32"],
|
|
3601
|
+
["counterIndex", "u32"],
|
|
3602
|
+
["pad0", "u32"],
|
|
3603
|
+
["pad1", "u32"]
|
|
3604
|
+
]);
|
|
3547
3605
|
function decl(group, binding, name, kind, wgslType) {
|
|
3548
3606
|
return { group, binding, name, kind, wgslType };
|
|
3549
3607
|
}
|
|
@@ -4583,6 +4641,47 @@ const LPA_STEP = {
|
|
|
4583
4641
|
snippetSlots: [],
|
|
4584
4642
|
phase: "P11"
|
|
4585
4643
|
};
|
|
4644
|
+
const MST_BEST = {
|
|
4645
|
+
id: "mst-best",
|
|
4646
|
+
body: mstBestWgsl,
|
|
4647
|
+
entryPoint: "mst_best",
|
|
4648
|
+
bindings: [
|
|
4649
|
+
decl(1, 0, "edgeSrc", "storage-ro", "array<u32>"),
|
|
4650
|
+
decl(1, 1, "edgeDst", "storage-ro", "array<u32>"),
|
|
4651
|
+
decl(1, 2, "edgeWeight", "storage-ro", "array<f32>"),
|
|
4652
|
+
decl(1, 3, "comp", "storage-ro", "array<u32>"),
|
|
4653
|
+
decl(1, 4, "bestKey", "storage", "array<atomic<u32>>"),
|
|
4654
|
+
decl(1, 5, "bestEdge", "storage", "array<atomic<u32>>"),
|
|
4655
|
+
decl(2, 0, "P", "uniform", "MstParams")
|
|
4656
|
+
],
|
|
4657
|
+
overrideDecls: [
|
|
4658
|
+
{ name: "PASS", type: "u32", default: 0 },
|
|
4659
|
+
{ name: "WEIGHTED", type: "bool", default: false }
|
|
4660
|
+
],
|
|
4661
|
+
uniforms: [MST_PARAMS],
|
|
4662
|
+
needs: [],
|
|
4663
|
+
snippetSlots: [],
|
|
4664
|
+
phase: "P11"
|
|
4665
|
+
};
|
|
4666
|
+
const MST_LINK = {
|
|
4667
|
+
id: "mst-link",
|
|
4668
|
+
body: mstLinkWgsl,
|
|
4669
|
+
entryPoint: "mst_link",
|
|
4670
|
+
bindings: [
|
|
4671
|
+
decl(1, 0, "edgeSrc", "storage-ro", "array<u32>"),
|
|
4672
|
+
decl(1, 1, "edgeDst", "storage-ro", "array<u32>"),
|
|
4673
|
+
decl(1, 2, "bestEdge", "storage-ro", "array<u32>"),
|
|
4674
|
+
decl(1, 3, "comp", "storage", "array<atomic<u32>>"),
|
|
4675
|
+
decl(1, 4, "treeEdge", "storage", "array<u32>"),
|
|
4676
|
+
decl(1, 5, "counters", "storage", "array<atomic<u32>>"),
|
|
4677
|
+
decl(2, 0, "P", "uniform", "MstParams")
|
|
4678
|
+
],
|
|
4679
|
+
overrideDecls: [],
|
|
4680
|
+
uniforms: [MST_PARAMS],
|
|
4681
|
+
needs: [],
|
|
4682
|
+
snippetSlots: [],
|
|
4683
|
+
phase: "P11"
|
|
4684
|
+
};
|
|
4586
4685
|
const REGISTRY = Object.freeze({
|
|
4587
4686
|
degree: DEGREE,
|
|
4588
4687
|
reduce: REDUCE,
|
|
@@ -4644,7 +4743,9 @@ const REGISTRY = Object.freeze({
|
|
|
4644
4743
|
"orient-flags": ORIENT_FLAGS,
|
|
4645
4744
|
"tri-intersect": TRI_INTERSECT,
|
|
4646
4745
|
"group-by-key-row": GROUP_BY_KEY_ROW,
|
|
4647
|
-
"lpa-step": LPA_STEP
|
|
4746
|
+
"lpa-step": LPA_STEP,
|
|
4747
|
+
"mst-best": MST_BEST,
|
|
4748
|
+
"mst-link": MST_LINK
|
|
4648
4749
|
});
|
|
4649
4750
|
const bodyOverrides = /* @__PURE__ */ new Map();
|
|
4650
4751
|
function entryOf(id) {
|
|
@@ -4803,7 +4904,7 @@ class ScanPlannerImpl {
|
|
|
4803
4904
|
}
|
|
4804
4905
|
const POISON = 3735928559;
|
|
4805
4906
|
const CHECK_BLOCKS = 32;
|
|
4806
|
-
const RING_SLOTS$
|
|
4907
|
+
const RING_SLOTS$a = 8;
|
|
4807
4908
|
const checked = /* @__PURE__ */ new WeakMap();
|
|
4808
4909
|
function inputAt(i) {
|
|
4809
4910
|
return i + 1;
|
|
@@ -4831,7 +4932,7 @@ async function runCheck(ctx) {
|
|
|
4831
4932
|
const count = CHECK_BLOCKS * wg + 1;
|
|
4832
4933
|
const bytes = 4 * count;
|
|
4833
4934
|
const lease = ctx.pool.lease();
|
|
4834
|
-
const ring = new UniformRing(ctx.device, ctx.allocator, RING_SLOTS$
|
|
4935
|
+
const ring = new UniformRing(ctx.device, ctx.allocator, RING_SLOTS$a, "device-check/ring");
|
|
4835
4936
|
try {
|
|
4836
4937
|
const scope = {
|
|
4837
4938
|
device: ctx.device,
|
|
@@ -5498,12 +5599,12 @@ function algorithmScope(ctx, label, slots) {
|
|
|
5498
5599
|
ringOverruns: () => ring.overruns
|
|
5499
5600
|
};
|
|
5500
5601
|
}
|
|
5501
|
-
const ALGORITHM$
|
|
5602
|
+
const ALGORITHM$9 = "connectedComponents";
|
|
5502
5603
|
const ROUNDS_PER_BATCH$1 = 4;
|
|
5503
5604
|
const MAX_WCC_ROUNDS = 64;
|
|
5504
5605
|
const SAMPLE_SIZE = 1024;
|
|
5505
5606
|
const MAX_STEPS = 1024;
|
|
5506
|
-
const RING_SLOTS$
|
|
5607
|
+
const RING_SLOTS$9 = 2 * ROUNDS_PER_BATCH$1;
|
|
5507
5608
|
function checkDest$6(dest, n) {
|
|
5508
5609
|
if (dest === void 0) {
|
|
5509
5610
|
return null;
|
|
@@ -5513,7 +5614,7 @@ function checkDest$6(dest, n) {
|
|
|
5513
5614
|
}
|
|
5514
5615
|
throw new WebGpuGraphError(
|
|
5515
5616
|
"E_INVALID_ARGUMENT",
|
|
5516
|
-
`${ALGORITHM$
|
|
5617
|
+
`${ALGORITHM$9}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
|
|
5517
5618
|
{
|
|
5518
5619
|
argument: "dest",
|
|
5519
5620
|
value: `${dest.constructor.name}(${dest.length})`,
|
|
@@ -5523,7 +5624,7 @@ function checkDest$6(dest, n) {
|
|
|
5523
5624
|
}
|
|
5524
5625
|
function coreOf$2(ctx, s) {
|
|
5525
5626
|
const core = ctx.residency.core(s);
|
|
5526
|
-
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$
|
|
5627
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$9);
|
|
5527
5628
|
return core;
|
|
5528
5629
|
}
|
|
5529
5630
|
function bindingOf$3(buffer, size) {
|
|
@@ -5582,8 +5683,8 @@ function checkLabels(raw) {
|
|
|
5582
5683
|
for (let v = 0; v < n; v++) {
|
|
5583
5684
|
const label = raw[v];
|
|
5584
5685
|
if (label >= n) {
|
|
5585
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
5586
|
-
label: `${ALGORITHM$
|
|
5686
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$9}: labels[${v}] = ${label} is not a node index`, {
|
|
5687
|
+
label: `${ALGORITHM$9}/labels`,
|
|
5587
5688
|
message: `the device produced a label outside [0, ${n})`
|
|
5588
5689
|
});
|
|
5589
5690
|
}
|
|
@@ -5601,7 +5702,7 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5601
5702
|
const renumber = options?.renumber !== false;
|
|
5602
5703
|
const dest = checkDest$6(options?.dest, n);
|
|
5603
5704
|
if (options?.signal?.aborted) {
|
|
5604
|
-
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$
|
|
5705
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$9}: the signal was aborted before any work started`, {});
|
|
5605
5706
|
}
|
|
5606
5707
|
if (n === 0) {
|
|
5607
5708
|
options?.onProgress?.(1, 1);
|
|
@@ -5618,7 +5719,7 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5618
5719
|
}
|
|
5619
5720
|
const edges = ctx.residency.view(s, "edgeList");
|
|
5620
5721
|
const edgeCount = edges.scalars.edgeCount[0];
|
|
5621
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
5722
|
+
const scope = algorithmScope(ctx, ALGORITHM$9, RING_SLOTS$9);
|
|
5622
5723
|
try {
|
|
5623
5724
|
const compBytes = 4 * (n + 1);
|
|
5624
5725
|
const comp = scope.scratch(compBytes, "comp");
|
|
@@ -5647,7 +5748,7 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5647
5748
|
return batch.submit();
|
|
5648
5749
|
};
|
|
5649
5750
|
queue.writeBuffer(comp, 4 * flagIndex, zero);
|
|
5650
|
-
const setup = new CommandBatch(ctx, `${ALGORITHM$
|
|
5751
|
+
const setup = new CommandBatch(ctx, `${ALGORITHM$9}/setup`);
|
|
5651
5752
|
let pass = setup.pass("sample-rounds");
|
|
5652
5753
|
const fillParams = scope.params(FILL_PARAMS, { count: n, value: 0, mode: 1, pad0: 0 });
|
|
5653
5754
|
fill.dispatch(
|
|
@@ -5666,7 +5767,7 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5666
5767
|
setup.endPass();
|
|
5667
5768
|
await submit2(setup).readback;
|
|
5668
5769
|
ctx.assertReady();
|
|
5669
|
-
const sampler = new CommandBatch(ctx, `${ALGORITHM$
|
|
5770
|
+
const sampler = new CommandBatch(ctx, `${ALGORITHM$9}/sample`);
|
|
5670
5771
|
pass = sampler.pass("sample");
|
|
5671
5772
|
const sampleParams = wccParams({ items, stride: 0, r: 0, giant: U32_MAX$2 });
|
|
5672
5773
|
sample.dispatch(
|
|
@@ -5684,7 +5785,7 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5684
5785
|
let rounds = 0;
|
|
5685
5786
|
for (; ; ) {
|
|
5686
5787
|
queue.writeBuffer(comp, 4 * flagIndex, zero);
|
|
5687
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
5788
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$9}/rounds`);
|
|
5688
5789
|
pass = batch.pass("edge-rounds");
|
|
5689
5790
|
for (let i = 0; i < ROUNDS_PER_BATCH$1; i++) {
|
|
5690
5791
|
const params = wccParams({ items: edgeCount, stride: edgePlan.stride ?? edgeCount, r: 0, giant });
|
|
@@ -5699,7 +5800,7 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5699
5800
|
rounds += ROUNDS_PER_BATCH$1;
|
|
5700
5801
|
ctx.assertReady();
|
|
5701
5802
|
if (options?.signal?.aborted) {
|
|
5702
|
-
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$
|
|
5803
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$9}: the signal was aborted`, {
|
|
5703
5804
|
batchId: submitted.id
|
|
5704
5805
|
});
|
|
5705
5806
|
}
|
|
@@ -5709,12 +5810,12 @@ async function connectedComponents(ctx, s, options) {
|
|
|
5709
5810
|
if (rounds >= MAX_WCC_ROUNDS) {
|
|
5710
5811
|
throw new WebGpuGraphError(
|
|
5711
5812
|
"E_VALIDATION",
|
|
5712
|
-
`${ALGORITHM$
|
|
5713
|
-
{ label: ALGORITHM$
|
|
5813
|
+
`${ALGORITHM$9}: the changed flag never settled in ${MAX_WCC_ROUNDS} rounds`,
|
|
5814
|
+
{ label: ALGORITHM$9, message: `the changed flag never settled in ${MAX_WCC_ROUNDS} rounds` }
|
|
5714
5815
|
);
|
|
5715
5816
|
}
|
|
5716
5817
|
}
|
|
5717
|
-
const final = new CommandBatch(ctx, `${ALGORITHM$
|
|
5818
|
+
const final = new CommandBatch(ctx, `${ALGORITHM$9}/final`);
|
|
5718
5819
|
recordCompress(final.pass("compress"));
|
|
5719
5820
|
final.endPass();
|
|
5720
5821
|
await submit2(final).readback;
|
|
@@ -6092,7 +6193,7 @@ async function prepareSpmvPull(scope, rev, options) {
|
|
|
6092
6193
|
return new SpmvPullPlannerImpl(scope, compiled, perm, options.weights);
|
|
6093
6194
|
}
|
|
6094
6195
|
const PR_BATCH = 8;
|
|
6095
|
-
const RING_SLOTS$
|
|
6196
|
+
const RING_SLOTS$8 = 2 * PR_BATCH + 2;
|
|
6096
6197
|
function checkDest$5(dest, n, algorithm) {
|
|
6097
6198
|
if (dest === void 0) {
|
|
6098
6199
|
return null;
|
|
@@ -6170,7 +6271,7 @@ async function run(ctx, s, personalization, options, algorithm) {
|
|
|
6170
6271
|
const weights = useWeights ? void 0 : null;
|
|
6171
6272
|
const weightedCore = useWeights ? core : { ...core, weights: null, hasWeights: false };
|
|
6172
6273
|
const weightedRev = useWeights ? rev : { ...rev, weights: null, hasWeights: false };
|
|
6173
|
-
const scope = algorithmScope(ctx, algorithm, RING_SLOTS$
|
|
6274
|
+
const scope = algorithmScope(ctx, algorithm, RING_SLOTS$8);
|
|
6174
6275
|
let uploaded = null;
|
|
6175
6276
|
try {
|
|
6176
6277
|
const bytes = 4 * n;
|
|
@@ -6333,7 +6434,7 @@ async function personalizedPageRank(ctx, s, personalization, options) {
|
|
|
6333
6434
|
return run(ctx, s, normalised2, options, "personalizedPageRank");
|
|
6334
6435
|
}
|
|
6335
6436
|
const BATCH = 8;
|
|
6336
|
-
const RING_SLOTS$
|
|
6437
|
+
const RING_SLOTS$7 = 4 * BATCH + 8;
|
|
6337
6438
|
function checkDest$4(dest, n, algorithm) {
|
|
6338
6439
|
if (dest === void 0) {
|
|
6339
6440
|
return null;
|
|
@@ -6373,7 +6474,7 @@ function whole(buffer, size) {
|
|
|
6373
6474
|
}
|
|
6374
6475
|
async function runPowerIteration(ctx, n, config) {
|
|
6375
6476
|
await assertDeviceComputes(ctx);
|
|
6376
|
-
const scope = algorithmScope(ctx, config.label, RING_SLOTS$
|
|
6477
|
+
const scope = algorithmScope(ctx, config.label, RING_SLOTS$7);
|
|
6377
6478
|
try {
|
|
6378
6479
|
const bytes = 4 * n;
|
|
6379
6480
|
const ring = (config.alternate === null ? ["rankA", "rankB"] : ["rankA", "rankB", "rankC"]).map(
|
|
@@ -7153,7 +7254,7 @@ class RadixSortPlannerImpl {
|
|
|
7153
7254
|
return src;
|
|
7154
7255
|
}
|
|
7155
7256
|
}
|
|
7156
|
-
const ALGORITHM$
|
|
7257
|
+
const ALGORITHM$8 = "breadthFirstSearch";
|
|
7157
7258
|
const NEXT_DEGREE_MAX_GROUPS = 128;
|
|
7158
7259
|
function bfsRingSlots(windows, levelsPerSubmit) {
|
|
7159
7260
|
return Math.max((5 + 4 * windows) * levelsPerSubmit + 16, RESULT_BATCH_SLOTS + windows);
|
|
@@ -7174,7 +7275,7 @@ function checkDest$3(dest, n) {
|
|
|
7174
7275
|
}
|
|
7175
7276
|
throw new WebGpuGraphError(
|
|
7176
7277
|
"E_INVALID_ARGUMENT",
|
|
7177
|
-
`${ALGORITHM$
|
|
7278
|
+
`${ALGORITHM$8}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
|
|
7178
7279
|
{
|
|
7179
7280
|
argument: "dest",
|
|
7180
7281
|
value: `${dest.constructor.name}(${dest.length})`,
|
|
@@ -7189,8 +7290,8 @@ function degreeView(ctx, s, name) {
|
|
|
7189
7290
|
const { bindings } = ctx.residency.view(s, name);
|
|
7190
7291
|
const { [name]: binding } = bindings;
|
|
7191
7292
|
if (binding === void 0) {
|
|
7192
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
7193
|
-
label: `${ALGORITHM$
|
|
7293
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$8}: the ${name} view has no ${name} binding`, {
|
|
7294
|
+
label: `${ALGORITHM$8}/${name}`,
|
|
7194
7295
|
message: `the ${name} view has no ${name} binding`
|
|
7195
7296
|
});
|
|
7196
7297
|
}
|
|
@@ -7199,15 +7300,15 @@ function degreeView(ctx, s, name) {
|
|
|
7199
7300
|
function aborted$1(batchId) {
|
|
7200
7301
|
return new WebGpuGraphError(
|
|
7201
7302
|
"E_ABORTED",
|
|
7202
|
-
`${ALGORITHM$
|
|
7303
|
+
`${ALGORITHM$8}: the signal was aborted`,
|
|
7203
7304
|
batchId === void 0 ? {} : { batchId }
|
|
7204
7305
|
);
|
|
7205
7306
|
}
|
|
7206
7307
|
function wordOf$1(block, name) {
|
|
7207
7308
|
const value = block[name];
|
|
7208
7309
|
if (typeof value !== "number") {
|
|
7209
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
7210
|
-
label: `${ALGORITHM$
|
|
7310
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$8}: counters.${name} did not decode to a number`, {
|
|
7311
|
+
label: `${ALGORITHM$8}/counters`,
|
|
7211
7312
|
message: `the field ${name} did not decode to a number`
|
|
7212
7313
|
});
|
|
7213
7314
|
}
|
|
@@ -7218,7 +7319,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7218
7319
|
await assertDeviceComputes(ctx);
|
|
7219
7320
|
const n = s.nodeCount;
|
|
7220
7321
|
if (!Number.isInteger(source) || source < 0 || source >= n) {
|
|
7221
|
-
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$
|
|
7322
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$8}: source ${source} is outside [0, ${n})`, {
|
|
7222
7323
|
argument: "source",
|
|
7223
7324
|
value: source,
|
|
7224
7325
|
expected: `an integer in [0, ${n})`
|
|
@@ -7228,7 +7329,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7228
7329
|
if (!Number.isInteger(levelsPerSubmit) || levelsPerSubmit < 1 || levelsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
|
|
7229
7330
|
throw new WebGpuGraphError(
|
|
7230
7331
|
"E_INVALID_ARGUMENT",
|
|
7231
|
-
`${ALGORITHM$
|
|
7332
|
+
`${ALGORITHM$8}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
|
|
7232
7333
|
{
|
|
7233
7334
|
argument: "levelsPerSubmit",
|
|
7234
7335
|
value: levelsPerSubmit,
|
|
@@ -7247,12 +7348,12 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7247
7348
|
if (s.directed && 4 * s.arcCount > ctx.caps.limits.maxStorageBufferBindingSize) {
|
|
7248
7349
|
throw new WebGpuGraphError(
|
|
7249
7350
|
"E_TOO_LARGE",
|
|
7250
|
-
`${ALGORITHM$
|
|
7351
|
+
`${ALGORITHM$8}: the reverse adjacency of a directed snapshot (${4 * s.arcCount} bytes) needs arc windows, which no view executes (spec 4.3); the bottom-up sweep binds it whole`,
|
|
7251
7352
|
{
|
|
7252
7353
|
needed: 4 * s.arcCount,
|
|
7253
7354
|
limit: ctx.caps.limits.maxStorageBufferBindingSize,
|
|
7254
7355
|
path: "windowed",
|
|
7255
|
-
algorithm: ALGORITHM$
|
|
7356
|
+
algorithm: ALGORITHM$8
|
|
7256
7357
|
}
|
|
7257
7358
|
);
|
|
7258
7359
|
}
|
|
@@ -7260,7 +7361,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7260
7361
|
const backward = coreWindows(reverse);
|
|
7261
7362
|
const outDegree = degreeView(ctx, s, "outDegree");
|
|
7262
7363
|
const inDegree = degreeView(ctx, s, "inDegree");
|
|
7263
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
7364
|
+
const scope = algorithmScope(ctx, ALGORITHM$8, bfsRingSlots(forward.length, levelsPerSubmit));
|
|
7264
7365
|
tuning.onScope?.(scope);
|
|
7265
7366
|
try {
|
|
7266
7367
|
const bytes = 4 * n;
|
|
@@ -7324,7 +7425,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7324
7425
|
scope.flush();
|
|
7325
7426
|
return batch.submit();
|
|
7326
7427
|
};
|
|
7327
|
-
const setup = new CommandBatch(ctx, `${ALGORITHM$
|
|
7428
|
+
const setup = new CommandBatch(ctx, `${ALGORITHM$8}/setup`);
|
|
7328
7429
|
const setupPass = setup.pass("fill");
|
|
7329
7430
|
recordFill2(setupPass, depth, INVALID_INDEX, 0);
|
|
7330
7431
|
recordFill2(setupPass, iota, 0, 1);
|
|
@@ -7344,7 +7445,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7344
7445
|
let submits = 0;
|
|
7345
7446
|
for (; ; ) {
|
|
7346
7447
|
queue.writeBuffer(counters.buffer, counters.offset + 4 * W.unvisitedCount, new Uint32Array(3));
|
|
7347
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
7448
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$8}/levels`);
|
|
7348
7449
|
const pass2 = batch.pass("bfs");
|
|
7349
7450
|
recordRebuild(pass2);
|
|
7350
7451
|
const bitsParams = scope.params(FILL_PARAMS, { count: bitsWords, value: 0, mode: 0, pad0: 0 });
|
|
@@ -7454,8 +7555,8 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7454
7555
|
if (submits > n + 1) {
|
|
7455
7556
|
throw new WebGpuGraphError(
|
|
7456
7557
|
"E_VALIDATION",
|
|
7457
|
-
`${ALGORITHM$
|
|
7458
|
-
{ label: ALGORITHM$
|
|
7558
|
+
`${ALGORITHM$8}: the done flag never rose in ${submits} submits (a traversal has at most ${n} levels)`,
|
|
7559
|
+
{ label: ALGORITHM$8, message: `the done flag never rose in ${submits} submits` }
|
|
7459
7560
|
);
|
|
7460
7561
|
}
|
|
7461
7562
|
}
|
|
@@ -7469,7 +7570,7 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7469
7570
|
offsets: bindingOf$1(scope.scratch(histBytes, "order/offsets"), histBytes)
|
|
7470
7571
|
};
|
|
7471
7572
|
const parent = bindingOf$1(scope.scratch(bytes, "parent"), bytes);
|
|
7472
|
-
const result = new CommandBatch(ctx, `${ALGORITHM$
|
|
7573
|
+
const result = new CommandBatch(ctx, `${ALGORITHM$8}/result`);
|
|
7473
7574
|
result.copy(depth, keys, bytes);
|
|
7474
7575
|
const pass = result.pass("result");
|
|
7475
7576
|
recordFill2(pass, vals, 0, 1);
|
|
@@ -7506,9 +7607,9 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7506
7607
|
if (visitedCount > n) {
|
|
7507
7608
|
throw new WebGpuGraphError(
|
|
7508
7609
|
"E_VALIDATION",
|
|
7509
|
-
`${ALGORITHM$
|
|
7610
|
+
`${ALGORITHM$8}: visitedCount ${visitedCount} exceeds the ${n} vertices (a duplicate claim)`,
|
|
7510
7611
|
{
|
|
7511
|
-
label: `${ALGORITHM$
|
|
7612
|
+
label: `${ALGORITHM$8}/visitedCount`,
|
|
7512
7613
|
message: `the device counted ${visitedCount} visits of ${n} vertices`
|
|
7513
7614
|
}
|
|
7514
7615
|
);
|
|
@@ -7532,9 +7633,9 @@ async function bfsWithTuning(ctx, s, source, options, tuning) {
|
|
|
7532
7633
|
function breadthFirstSearch(ctx, s, source, options) {
|
|
7533
7634
|
return bfsWithTuning(ctx, s, source, options, {});
|
|
7534
7635
|
}
|
|
7535
|
-
const ALGORITHM$
|
|
7636
|
+
const ALGORITHM$7 = "sssp";
|
|
7536
7637
|
const HALF_ALIGN = 64;
|
|
7537
|
-
const RING_SLOTS$
|
|
7638
|
+
const RING_SLOTS$6 = 4 * MAX_LEVELS_PER_SUBMIT + 40;
|
|
7538
7639
|
function bitsOf(value) {
|
|
7539
7640
|
return new Uint32Array(Float32Array.of(value).buffer)[0];
|
|
7540
7641
|
}
|
|
@@ -7560,8 +7661,8 @@ function assertSource(algorithm, source, n) {
|
|
|
7560
7661
|
function wordOf(block, name) {
|
|
7561
7662
|
const value = block[name];
|
|
7562
7663
|
if (typeof value !== "number") {
|
|
7563
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
7564
|
-
label: `${ALGORITHM$
|
|
7664
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$7}: counters.${name} did not decode to a number`, {
|
|
7665
|
+
label: `${ALGORITHM$7}/counters`,
|
|
7565
7666
|
message: `the field ${name} did not decode to a number`
|
|
7566
7667
|
});
|
|
7567
7668
|
}
|
|
@@ -7728,12 +7829,12 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7728
7829
|
ctx.assertReady();
|
|
7729
7830
|
await assertDeviceComputes(ctx);
|
|
7730
7831
|
const n = s.nodeCount;
|
|
7731
|
-
assertSource(ALGORITHM$
|
|
7832
|
+
assertSource(ALGORITHM$7, source, n);
|
|
7732
7833
|
const roundsPerSubmit = tuning.roundsPerSubmit ?? MAX_LEVELS_PER_SUBMIT;
|
|
7733
7834
|
if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
|
|
7734
7835
|
throw new WebGpuGraphError(
|
|
7735
7836
|
"E_INVALID_ARGUMENT",
|
|
7736
|
-
`${ALGORITHM$
|
|
7837
|
+
`${ALGORITHM$7}: roundsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
|
|
7737
7838
|
{
|
|
7738
7839
|
argument: "roundsPerSubmit",
|
|
7739
7840
|
value: roundsPerSubmit,
|
|
@@ -7742,42 +7843,42 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7742
7843
|
);
|
|
7743
7844
|
}
|
|
7744
7845
|
if (tuning.delta !== void 0 && !(Number.isFinite(tuning.delta) && tuning.delta > 0)) {
|
|
7745
|
-
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$
|
|
7846
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$7}: delta must be a finite positive number`, {
|
|
7746
7847
|
argument: "delta",
|
|
7747
7848
|
value: tuning.delta,
|
|
7748
7849
|
expected: "a finite positive number"
|
|
7749
7850
|
});
|
|
7750
7851
|
}
|
|
7751
|
-
const dest = checkDest$2(ALGORITHM$
|
|
7752
|
-
const vector2 = resolveWeights$1(ALGORITHM$
|
|
7753
|
-
const cutoff = normaliseCutoff(ALGORITHM$
|
|
7852
|
+
const dest = checkDest$2(ALGORITHM$7, options?.dest, n);
|
|
7853
|
+
const vector2 = resolveWeights$1(ALGORITHM$7, s, options?.weights);
|
|
7854
|
+
const cutoff = normaliseCutoff(ALGORITHM$7, options?.cutoff);
|
|
7754
7855
|
if (options?.signal?.aborted) {
|
|
7755
|
-
throw aborted(ALGORITHM$
|
|
7856
|
+
throw aborted(ALGORITHM$7);
|
|
7756
7857
|
}
|
|
7757
7858
|
if (vector2 === null || vector2.allOne) {
|
|
7758
7859
|
return unitWeightRoute(ctx, s, source, cutoff, dest, options);
|
|
7759
7860
|
}
|
|
7760
7861
|
if (!vector2.nonNegative) {
|
|
7761
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
7862
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$7}: a negative weight has no shortest path here`, {
|
|
7762
7863
|
feature: "sssp.negativeWeights",
|
|
7763
7864
|
hint: "use bellmanFord"
|
|
7764
7865
|
});
|
|
7765
7866
|
}
|
|
7766
7867
|
if (!vector2.finite) {
|
|
7767
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
7868
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$7}: a NaN or infinite weight has no bit-pattern order`, {
|
|
7768
7869
|
feature: "sssp.nonFiniteWeights"
|
|
7769
7870
|
});
|
|
7770
7871
|
}
|
|
7771
7872
|
const { arcCount } = s;
|
|
7772
7873
|
const core = ctx.residency.core(s);
|
|
7773
7874
|
const limit = ctx.caps.limits.maxStorageBufferBindingSize;
|
|
7774
|
-
assertWholeCore(core, arcCount, limit, ALGORITHM$
|
|
7875
|
+
assertWholeCore(core, arcCount, limit, ALGORITHM$7);
|
|
7775
7876
|
const cap = Math.ceil(Math.max(1, arcCount) / HALF_ALIGN) * HALF_ALIGN;
|
|
7776
7877
|
if (8 * cap > limit) {
|
|
7777
7878
|
throw new WebGpuGraphError(
|
|
7778
7879
|
"E_TOO_LARGE",
|
|
7779
|
-
`${ALGORITHM$
|
|
7780
|
-
{ needed: 8 * cap, limit, path: "sssp.queue", algorithm: ALGORITHM$
|
|
7880
|
+
`${ALGORITHM$7}: the near-far queue of ${cap} entries per half needs ${8 * cap} bytes, above the ${limit}-byte binding limit (the relax is never windowed)`,
|
|
7881
|
+
{ needed: 8 * cap, limit, path: "sssp.queue", algorithm: ALGORITHM$7 }
|
|
7781
7882
|
);
|
|
7782
7883
|
}
|
|
7783
7884
|
const delta = Math.fround(
|
|
@@ -7786,7 +7887,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7786
7887
|
const deltaBits = bitsOf(delta);
|
|
7787
7888
|
const maxRounds = n + Math.ceil(vector2.sum / delta) + 1;
|
|
7788
7889
|
const maxSubmits = Math.ceil((maxRounds + 1) / roundsPerSubmit) + 1;
|
|
7789
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
7890
|
+
const scope = algorithmScope(ctx, ALGORITHM$7, RING_SLOTS$6);
|
|
7790
7891
|
try {
|
|
7791
7892
|
const wg = ctx.workgroupSize;
|
|
7792
7893
|
const bytes = 4 * n;
|
|
@@ -7824,7 +7925,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7824
7925
|
scope.flush();
|
|
7825
7926
|
return batch.submit();
|
|
7826
7927
|
};
|
|
7827
|
-
const setup = new CommandBatch(ctx, `${ALGORITHM$
|
|
7928
|
+
const setup = new CommandBatch(ctx, `${ALGORITHM$7}/setup`);
|
|
7828
7929
|
const setupPass = setup.pass("fill");
|
|
7829
7930
|
recordFill2(setupPass, dist, n, F32_INF_BITS);
|
|
7830
7931
|
recordFill2(setupPass, pred, predWords, INVALID_INDEX);
|
|
@@ -7870,7 +7971,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7870
7971
|
let roundsRecorded = 0;
|
|
7871
7972
|
let submits = 0;
|
|
7872
7973
|
for (; ; ) {
|
|
7873
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
7974
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$7}/rounds`);
|
|
7874
7975
|
const pass = batch.pass("sssp");
|
|
7875
7976
|
const near = scope.params(FRONTIER_PARAMS, { ...relaxFields, role: 0 });
|
|
7876
7977
|
const far = scope.params(FRONTIER_PARAMS, { ...relaxFields, role: 1 });
|
|
@@ -7893,7 +7994,7 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7893
7994
|
submits += 1;
|
|
7894
7995
|
ctx.assertReady();
|
|
7895
7996
|
if (options?.signal?.aborted) {
|
|
7896
|
-
throw aborted(ALGORITHM$
|
|
7997
|
+
throw aborted(ALGORITHM$7, submitted.id);
|
|
7897
7998
|
}
|
|
7898
7999
|
options?.onProgress?.(Math.min(roundsRecorded, maxRounds), maxRounds);
|
|
7899
8000
|
if (inspect !== null && tuning.onRound !== void 0) {
|
|
@@ -7918,26 +8019,26 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7918
8019
|
const needed = Math.max(wordOf(block, "nextFrontierCount"), wordOf(block, "nextFarCount"));
|
|
7919
8020
|
throw new WebGpuGraphError(
|
|
7920
8021
|
"E_TOO_LARGE",
|
|
7921
|
-
`${ALGORITHM$
|
|
7922
|
-
{ needed, limit: cap, path: "sssp.pile", algorithm: ALGORITHM$
|
|
8022
|
+
`${ALGORITHM$7}: a raw pile of ${needed} entries overflowed its ${cap}-entry half`,
|
|
8023
|
+
{ needed, limit: cap, path: "sssp.pile", algorithm: ALGORITHM$7 }
|
|
7923
8024
|
);
|
|
7924
8025
|
}
|
|
7925
8026
|
throw new WebGpuGraphError(
|
|
7926
8027
|
"E_UNSUPPORTED",
|
|
7927
|
-
`${ALGORITHM$
|
|
8028
|
+
`${ALGORITHM$7}: the f32 threshold ${wordOf(block, "thresholdBits")} absorbed the delta ${deltaBits} (as bit patterns); the far pile can no longer be bucketed`,
|
|
7928
8029
|
{ feature: "sssp.thresholdAbsorbed", hint: "the distances outgrew the delta's f32 precision" }
|
|
7929
8030
|
);
|
|
7930
8031
|
}
|
|
7931
8032
|
if (submits > maxSubmits) {
|
|
7932
8033
|
throw new WebGpuGraphError(
|
|
7933
8034
|
"E_VALIDATION",
|
|
7934
|
-
`${ALGORITHM$
|
|
7935
|
-
{ label: `${ALGORITHM$
|
|
8035
|
+
`${ALGORITHM$7}: the done flag never rose in ${submits} submits (at most ${maxRounds} rounds)`,
|
|
8036
|
+
{ label: `${ALGORITHM$7}/rounds`, message: `the done flag never rose in ${submits} submits` }
|
|
7936
8037
|
);
|
|
7937
8038
|
}
|
|
7938
8039
|
}
|
|
7939
8040
|
const passed = await predecessorPass({
|
|
7940
|
-
algorithm: ALGORITHM$
|
|
8041
|
+
algorithm: ALGORITHM$7,
|
|
7941
8042
|
ctx,
|
|
7942
8043
|
scope,
|
|
7943
8044
|
predKernel,
|
|
@@ -7953,8 +8054,8 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7953
8054
|
if (passed.orphans !== 0) {
|
|
7954
8055
|
throw new WebGpuGraphError(
|
|
7955
8056
|
"E_VALIDATION",
|
|
7956
|
-
`${ALGORITHM$
|
|
7957
|
-
{ label: `${ALGORITHM$
|
|
8057
|
+
`${ALGORITHM$7}: ${passed.orphans} reached node(s) the predecessor key never reached (a kernel bug)`,
|
|
8058
|
+
{ label: `${ALGORITHM$7}/pred`, message: `${passed.orphans} orphan(s) in the predecessor pass` }
|
|
7958
8059
|
);
|
|
7959
8060
|
}
|
|
7960
8061
|
const distOut = dest ?? new Float32Array(n);
|
|
@@ -7973,10 +8074,10 @@ async function ssspWithTuning(ctx, s, source, options, tuning) {
|
|
|
7973
8074
|
function sssp(ctx, s, source, options) {
|
|
7974
8075
|
return ssspWithTuning(ctx, s, source, options, {});
|
|
7975
8076
|
}
|
|
7976
|
-
const ALGORITHM$
|
|
8077
|
+
const ALGORITHM$6 = "bellmanFord";
|
|
7977
8078
|
const ROUNDS_PER_BATCH = 8;
|
|
7978
8079
|
const MAX_RETRIES = 16;
|
|
7979
|
-
const RING_SLOTS$
|
|
8080
|
+
const RING_SLOTS$5 = MAX_LEVELS_PER_SUBMIT + 16;
|
|
7980
8081
|
function assertSymmetric(s, vector2) {
|
|
7981
8082
|
const { arcToEdge, edgeToArc } = s;
|
|
7982
8083
|
for (let a = 0; a < s.arcCount; a++) {
|
|
@@ -7984,7 +8085,7 @@ function assertSymmetric(s, vector2) {
|
|
|
7984
8085
|
if (vector2[a] !== vector2[forward]) {
|
|
7985
8086
|
throw new WebGpuGraphError(
|
|
7986
8087
|
"E_UNSUPPORTED",
|
|
7987
|
-
`${ALGORITHM$
|
|
8088
|
+
`${ALGORITHM$6}: weights[${a}] = ${vector2[a]} differs from weights[${forward}] = ${vector2[forward]}, the forward arc of the same undirected edge; the kernel reads one weight per edge`,
|
|
7988
8089
|
{ feature: "bellmanFord.asymmetricUndirectedWeights", hint: "use a directed snapshot" }
|
|
7989
8090
|
);
|
|
7990
8091
|
}
|
|
@@ -7994,10 +8095,10 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
7994
8095
|
ctx.assertReady();
|
|
7995
8096
|
await assertDeviceComputes(ctx);
|
|
7996
8097
|
const n = s.nodeCount;
|
|
7997
|
-
assertSource(ALGORITHM$
|
|
8098
|
+
assertSource(ALGORITHM$6, source, n);
|
|
7998
8099
|
const maxRetries = tuning.maxRetries ?? MAX_RETRIES;
|
|
7999
8100
|
if (!Number.isInteger(maxRetries) || maxRetries < 1) {
|
|
8000
|
-
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$
|
|
8101
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$6}: maxRetries must be an integer >= 1`, {
|
|
8001
8102
|
argument: "maxRetries",
|
|
8002
8103
|
value: maxRetries,
|
|
8003
8104
|
expected: "an integer >= 1"
|
|
@@ -8007,7 +8108,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8007
8108
|
if (!Number.isInteger(roundsPerBatch) || roundsPerBatch < 1 || roundsPerBatch > MAX_LEVELS_PER_SUBMIT) {
|
|
8008
8109
|
throw new WebGpuGraphError(
|
|
8009
8110
|
"E_INVALID_ARGUMENT",
|
|
8010
|
-
`${ALGORITHM$
|
|
8111
|
+
`${ALGORITHM$6}: roundsPerBatch must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
|
|
8011
8112
|
{
|
|
8012
8113
|
argument: "roundsPerBatch",
|
|
8013
8114
|
value: roundsPerBatch,
|
|
@@ -8015,18 +8116,18 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8015
8116
|
}
|
|
8016
8117
|
);
|
|
8017
8118
|
}
|
|
8018
|
-
const dest = checkDest$2(ALGORITHM$
|
|
8019
|
-
const vector2 = resolveWeights$1(ALGORITHM$
|
|
8020
|
-
const cutoff = normaliseCutoff(ALGORITHM$
|
|
8119
|
+
const dest = checkDest$2(ALGORITHM$6, options?.dest, n);
|
|
8120
|
+
const vector2 = resolveWeights$1(ALGORITHM$6, s, options?.weights);
|
|
8121
|
+
const cutoff = normaliseCutoff(ALGORITHM$6, options?.cutoff);
|
|
8021
8122
|
if (options?.signal?.aborted) {
|
|
8022
|
-
throw aborted(ALGORITHM$
|
|
8123
|
+
throw aborted(ALGORITHM$6);
|
|
8023
8124
|
}
|
|
8024
8125
|
if (vector2 === null || vector2.allOne) {
|
|
8025
8126
|
const unit = await unitWeightRoute(ctx, s, source, cutoff, dest, options);
|
|
8026
8127
|
return { result: { ...unit, hasNegativeCycle: false }, rounds: 0, retryExhaustedRounds: 0 };
|
|
8027
8128
|
}
|
|
8028
8129
|
if (!vector2.finite) {
|
|
8029
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
8130
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$6}: a NaN or infinite weight has no shortest path`, {
|
|
8030
8131
|
feature: "bellmanFord.nonFiniteWeights"
|
|
8031
8132
|
});
|
|
8032
8133
|
}
|
|
@@ -8035,10 +8136,10 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8035
8136
|
}
|
|
8036
8137
|
const { arcCount } = s;
|
|
8037
8138
|
const core = ctx.residency.core(s, ["rowPtr", "colIdx", "weights", "edgeToArc"]);
|
|
8038
|
-
assertWholeCore(core, arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$
|
|
8139
|
+
assertWholeCore(core, arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$6);
|
|
8039
8140
|
const edges = ctx.residency.view(s, "edgeList");
|
|
8040
8141
|
const edgeCount = edges.scalars.edgeCount[0];
|
|
8041
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
8142
|
+
const scope = algorithmScope(ctx, ALGORITHM$6, RING_SLOTS$5);
|
|
8042
8143
|
try {
|
|
8043
8144
|
const wg = ctx.workgroupSize;
|
|
8044
8145
|
const bytes = 4 * n;
|
|
@@ -8073,7 +8174,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8073
8174
|
scope.flush();
|
|
8074
8175
|
return batch.submit();
|
|
8075
8176
|
};
|
|
8076
|
-
const setup = new CommandBatch(ctx, `${ALGORITHM$
|
|
8177
|
+
const setup = new CommandBatch(ctx, `${ALGORITHM$6}/setup`);
|
|
8077
8178
|
const setupPass = setup.pass("fill");
|
|
8078
8179
|
recordFill2(setupPass, dist, n, F32_INF_BITS);
|
|
8079
8180
|
recordFill2(setupPass, pred, predWords, INVALID_INDEX);
|
|
@@ -8102,7 +8203,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8102
8203
|
const zero = new Uint32Array(BF_FLAGS.byteLength / 4);
|
|
8103
8204
|
const runRounds = async (count, label) => {
|
|
8104
8205
|
queue.writeBuffer(flags.buffer, flags.offset, zero);
|
|
8105
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
8206
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$6}/${label}`);
|
|
8106
8207
|
const pass = batch.pass("relax");
|
|
8107
8208
|
const params = scope.params(BF_PARAMS, relaxFields);
|
|
8108
8209
|
const bound = relax.bind({ ...relaxBindings, P: params.binding });
|
|
@@ -8127,9 +8228,9 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8127
8228
|
if (decision.retryExhausted !== 0) {
|
|
8128
8229
|
throw new WebGpuGraphError(
|
|
8129
8230
|
"E_VALIDATION",
|
|
8130
|
-
`${ALGORITHM$
|
|
8231
|
+
`${ALGORITHM$6}: a lane exhausted the ${maxRetries}-retry compare-exchange bound in the decision round, so its change is not a verdict`,
|
|
8131
8232
|
{
|
|
8132
|
-
label: `${ALGORITHM$
|
|
8233
|
+
label: `${ALGORITHM$6}/retry`,
|
|
8133
8234
|
message: "retryExhausted in the decision round",
|
|
8134
8235
|
batchId: decision.id
|
|
8135
8236
|
}
|
|
@@ -8145,7 +8246,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8145
8246
|
retryExhaustedRounds += 1;
|
|
8146
8247
|
}
|
|
8147
8248
|
if (options?.signal?.aborted) {
|
|
8148
|
-
throw aborted(ALGORITHM$
|
|
8249
|
+
throw aborted(ALGORITHM$6, batch.id);
|
|
8149
8250
|
}
|
|
8150
8251
|
options?.onProgress?.(rounds, n);
|
|
8151
8252
|
if (batch.changed === 0 && batch.retryExhausted === 0) {
|
|
@@ -8153,7 +8254,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8153
8254
|
}
|
|
8154
8255
|
}
|
|
8155
8256
|
const passed = await predecessorPass({
|
|
8156
|
-
algorithm: ALGORITHM$
|
|
8257
|
+
algorithm: ALGORITHM$6,
|
|
8157
8258
|
ctx,
|
|
8158
8259
|
scope,
|
|
8159
8260
|
predKernel,
|
|
@@ -8169,7 +8270,7 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8169
8270
|
if (passed.orphans !== 0 && !hasNegativeCycle) {
|
|
8170
8271
|
throw new WebGpuGraphError(
|
|
8171
8272
|
"E_UNSUPPORTED",
|
|
8172
|
-
`${ALGORITHM$
|
|
8273
|
+
`${ALGORITHM$6}: ${passed.orphans} reached node(s) the tight subgraph never reaches (a cycle of weights below one f32 ulp relaxed once)`,
|
|
8173
8274
|
{
|
|
8174
8275
|
feature: "bellmanFord.roundedCycle",
|
|
8175
8276
|
hint: "a cycle of weights below one f32 ulp relaxed once at a distance above 2^24; scale the weights or shorten the distances"
|
|
@@ -8196,17 +8297,17 @@ async function bellmanFordWithTuning(ctx, s, source, options, tuning) {
|
|
|
8196
8297
|
async function bellmanFord(ctx, s, source, options) {
|
|
8197
8298
|
return (await bellmanFordWithTuning(ctx, s, source, options, {})).result;
|
|
8198
8299
|
}
|
|
8199
|
-
const ALGORITHM$
|
|
8300
|
+
const ALGORITHM$5 = "betweennessCentrality";
|
|
8200
8301
|
const BYTES_PER_NODE_SOURCE = 16;
|
|
8201
8302
|
const SAMPLE_SEED = 2654435769;
|
|
8202
|
-
const RING_SLOTS$
|
|
8303
|
+
const RING_SLOTS$4 = BC_BACKWARD_LEVELS_PER_SUBMIT + 16;
|
|
8203
8304
|
function planBatchSize(n, remaining, limits) {
|
|
8204
8305
|
const needed = 4 * (n + 2);
|
|
8205
8306
|
if (needed > limits.maxStorageBufferBindingSize) {
|
|
8206
8307
|
throw new WebGpuGraphError(
|
|
8207
8308
|
"E_TOO_LARGE",
|
|
8208
|
-
`${ALGORITHM$
|
|
8209
|
-
{ needed, limit: limits.maxStorageBufferBindingSize, path: "binding", algorithm: ALGORITHM$
|
|
8309
|
+
`${ALGORITHM$5}: one source needs ${needed} bytes in its largest binding at n = ${n}, above maxStorageBufferBindingSize = ${limits.maxStorageBufferBindingSize}; a limit of at least ${needed} admits one source per batch`,
|
|
8310
|
+
{ needed, limit: limits.maxStorageBufferBindingSize, path: "binding", algorithm: ALGORITHM$5 }
|
|
8210
8311
|
);
|
|
8211
8312
|
}
|
|
8212
8313
|
const kByBinding = Math.floor(limits.maxStorageBufferBindingSize / (4 * n));
|
|
@@ -8227,7 +8328,7 @@ function drawSources(n, k) {
|
|
|
8227
8328
|
return pool.slice(0, k);
|
|
8228
8329
|
}
|
|
8229
8330
|
function badArgument(argument, value, expected) {
|
|
8230
|
-
return new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$
|
|
8331
|
+
return new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$5}: ${argument} must be ${expected}`, {
|
|
8231
8332
|
argument,
|
|
8232
8333
|
value,
|
|
8233
8334
|
expected
|
|
@@ -8279,7 +8380,7 @@ async function submit(state, batch, signal) {
|
|
|
8279
8380
|
const back = await submitted.readback;
|
|
8280
8381
|
state.ctx.assertReady();
|
|
8281
8382
|
if (signal?.aborted) {
|
|
8282
|
-
throw aborted(ALGORITHM$
|
|
8383
|
+
throw aborted(ALGORITHM$5, submitted.id);
|
|
8283
8384
|
}
|
|
8284
8385
|
return back;
|
|
8285
8386
|
}
|
|
@@ -8296,7 +8397,7 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
|
|
|
8296
8397
|
let overflow = false;
|
|
8297
8398
|
let endsWords = null;
|
|
8298
8399
|
for (let first = true; endsWords === null; first = false) {
|
|
8299
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
8400
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$5}/forward`);
|
|
8300
8401
|
const pass = batch.pass("forward");
|
|
8301
8402
|
if (first) {
|
|
8302
8403
|
recordFill(state, pass, depthK, words, 4294967295);
|
|
@@ -8338,8 +8439,8 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
|
|
|
8338
8439
|
overflow = words32[W.sigmaOverflow] !== 0;
|
|
8339
8440
|
endsWords = new Uint32Array(back, endsRequest.offset, endsCount).slice(0, levels + 1);
|
|
8340
8441
|
} else if (recorded > n + 2) {
|
|
8341
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
8342
|
-
label: ALGORITHM$
|
|
8442
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$5}: the done flag never rose in ${recorded} levels`, {
|
|
8443
|
+
label: ALGORITHM$5,
|
|
8343
8444
|
message: `the done flag never rose in ${recorded} levels`
|
|
8344
8445
|
});
|
|
8345
8446
|
}
|
|
@@ -8352,7 +8453,7 @@ async function runBatch(state, sources, form, levelsPerSubmit, tuning, signal) {
|
|
|
8352
8453
|
for (let i = 0; ; i += BC_BACKWARD_LEVELS_PER_SUBMIT) {
|
|
8353
8454
|
const chunk = backwardLevels.slice(i, i + BC_BACKWARD_LEVELS_PER_SUBMIT);
|
|
8354
8455
|
const last = i + BC_BACKWARD_LEVELS_PER_SUBMIT >= backwardLevels.length;
|
|
8355
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
8456
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$5}/backward`);
|
|
8356
8457
|
const pass = batch.pass("backward");
|
|
8357
8458
|
for (const level of chunk) {
|
|
8358
8459
|
const start = endsWords[level];
|
|
@@ -8416,11 +8517,11 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
|
|
|
8416
8517
|
const limits = tuning.limits ?? ctx.caps.limits;
|
|
8417
8518
|
const kMax = planBatchSize(n, sources.length, limits);
|
|
8418
8519
|
const core = ctx.residency.core(s);
|
|
8419
|
-
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$
|
|
8520
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$5);
|
|
8420
8521
|
const { edgeCount } = s;
|
|
8421
8522
|
const mayRunEdge = pinned === "edge" || pinned === "auto" && sources.length > kMax;
|
|
8422
8523
|
const edgeView = mayRunEdge && edgeCount > 0 ? ctx.residency.view(s, "edgeList") : null;
|
|
8423
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
8524
|
+
const scope = algorithmScope(ctx, ALGORITHM$5, RING_SLOTS$4);
|
|
8424
8525
|
try {
|
|
8425
8526
|
const arrayBytes = 4 * n * kMax;
|
|
8426
8527
|
const lease = (bytes, label) => bindingOf(scope.scratch(bytes, label), bytes);
|
|
@@ -8460,7 +8561,7 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
|
|
|
8460
8561
|
bound: /* @__PURE__ */ new Map()
|
|
8461
8562
|
};
|
|
8462
8563
|
await ctx.allocator.check();
|
|
8463
|
-
const setup = new CommandBatch(ctx, `${ALGORITHM$
|
|
8564
|
+
const setup = new CommandBatch(ctx, `${ALGORITHM$5}/setup`);
|
|
8464
8565
|
const setupPass = setup.pass("setup");
|
|
8465
8566
|
recordFill(state, setupPass, state.bc, n, 0);
|
|
8466
8567
|
if (state.arcScores !== null) {
|
|
@@ -8488,7 +8589,7 @@ async function runRaw(ctx, s, sources, withEdges, tuning, options) {
|
|
|
8488
8589
|
batches += 1;
|
|
8489
8590
|
options?.onProgress?.(start, sources.length);
|
|
8490
8591
|
}
|
|
8491
|
-
const result = new CommandBatch(ctx, `${ALGORITHM$
|
|
8592
|
+
const result = new CommandBatch(ctx, `${ALGORITHM$5}/result`);
|
|
8492
8593
|
const vertexRequest = result.readback(state.bc.buffer, state.bc.offset, 4 * n);
|
|
8493
8594
|
const arcRequest = state.arcScores === null ? null : result.readback(state.arcScores.buffer, state.arcScores.offset, 4 * s.arcCount);
|
|
8494
8595
|
const back = await submit(state, result, options?.signal);
|
|
@@ -8512,7 +8613,7 @@ async function precheck(ctx, options) {
|
|
|
8512
8613
|
ctx.assertReady();
|
|
8513
8614
|
await assertDeviceComputes(ctx);
|
|
8514
8615
|
if (options?.endpoints === true) {
|
|
8515
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
8616
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$5}: endpoints: true is not supported`, {
|
|
8516
8617
|
feature: "betweenness.endpoints",
|
|
8517
8618
|
hint: "the CPU endpoints branch in algorithms/src/algorithms/centrality/betweenness.ts (predecessors.length === 0 && w !== source) never fires, so there is no convention to match"
|
|
8518
8619
|
});
|
|
@@ -8521,10 +8622,10 @@ async function precheck(ctx, options) {
|
|
|
8521
8622
|
async function betweennessWithTuning(ctx, s, options, tuning) {
|
|
8522
8623
|
await precheck(ctx, options);
|
|
8523
8624
|
const n = s.nodeCount;
|
|
8524
|
-
const scores = checkDest$2(ALGORITHM$
|
|
8625
|
+
const scores = checkDest$2(ALGORITHM$5, options?.dest, n) ?? new Float32Array(n);
|
|
8525
8626
|
const sources = resolveSources(options, n);
|
|
8526
8627
|
if (options?.signal?.aborted) {
|
|
8527
|
-
throw aborted(ALGORITHM$
|
|
8628
|
+
throw aborted(ALGORITHM$5);
|
|
8528
8629
|
}
|
|
8529
8630
|
if (n === 0 || sources.length === 0) {
|
|
8530
8631
|
scores.fill(0);
|
|
@@ -8550,7 +8651,7 @@ async function edgeBetweennessWithTuning(ctx, s, options, tuning, onArcs) {
|
|
|
8550
8651
|
const scores = checkDest$2("edgeBetweennessCentrality", options?.dest, s.edgeCount) ?? new Float32Array(s.edgeCount);
|
|
8551
8652
|
const sources = resolveSources(options, n);
|
|
8552
8653
|
if (options?.signal?.aborted) {
|
|
8553
|
-
throw aborted(ALGORITHM$
|
|
8654
|
+
throw aborted(ALGORITHM$5);
|
|
8554
8655
|
}
|
|
8555
8656
|
if (n === 0 || sources.length === 0 || s.arcCount === 0) {
|
|
8556
8657
|
scores.fill(0);
|
|
@@ -8571,10 +8672,10 @@ function betweennessCentrality(ctx, s, options) {
|
|
|
8571
8672
|
function edgeBetweennessCentrality(ctx, s, options) {
|
|
8572
8673
|
return edgeBetweennessWithTuning(ctx, s, options, {});
|
|
8573
8674
|
}
|
|
8574
|
-
const ALGORITHM$
|
|
8675
|
+
const ALGORITHM$4 = "closenessCentrality";
|
|
8575
8676
|
const SOURCES_PER_BATCH = 32;
|
|
8576
8677
|
const PER_SOURCE_WORDS = 4 * SOURCES_PER_BATCH;
|
|
8577
|
-
const RING_SLOTS$
|
|
8678
|
+
const RING_SLOTS$3 = 8 * MAX_LEVELS_PER_SUBMIT + 16;
|
|
8578
8679
|
function reusingScratch(scope) {
|
|
8579
8680
|
const held = /* @__PURE__ */ new Map();
|
|
8580
8681
|
return {
|
|
@@ -8596,7 +8697,7 @@ async function weightedRoute(ctx, s, scores, sources, options) {
|
|
|
8596
8697
|
const totals = sources === null ? null : new Float64Array(n);
|
|
8597
8698
|
for (let i = 0; i < count; i++) {
|
|
8598
8699
|
if (options?.signal?.aborted) {
|
|
8599
|
-
throw aborted(ALGORITHM$
|
|
8700
|
+
throw aborted(ALGORITHM$4);
|
|
8600
8701
|
}
|
|
8601
8702
|
const source = sources === null ? i : sources[i];
|
|
8602
8703
|
const { dist } = await sssp(ctx, s, source, { signal: options?.signal });
|
|
@@ -8630,8 +8731,8 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
|
|
|
8630
8731
|
return { scores, iterations: 0, converged: true, precision: "f32", sourcesUsed: 0 };
|
|
8631
8732
|
}
|
|
8632
8733
|
const core = ctx.residency.core(s);
|
|
8633
|
-
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$
|
|
8634
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
8734
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$4);
|
|
8735
|
+
const scope = algorithmScope(ctx, ALGORITHM$4, RING_SLOTS$3);
|
|
8635
8736
|
try {
|
|
8636
8737
|
const wg = ctx.workgroupSize;
|
|
8637
8738
|
const bytes = 4 * n;
|
|
@@ -8680,7 +8781,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
|
|
|
8680
8781
|
scope.flush();
|
|
8681
8782
|
return batch.submit();
|
|
8682
8783
|
};
|
|
8683
|
-
const setup = new CommandBatch(ctx, `${ALGORITHM$
|
|
8784
|
+
const setup = new CommandBatch(ctx, `${ALGORITHM$4}/setup`);
|
|
8684
8785
|
recordFill2(setup.pass("fill"), iota, n, 1);
|
|
8685
8786
|
setup.endPass();
|
|
8686
8787
|
await submit2(setup).readback;
|
|
@@ -8689,7 +8790,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
|
|
|
8689
8790
|
for (let batchStart = 0; batchStart < seedCount; batchStart += SOURCES_PER_BATCH) {
|
|
8690
8791
|
let level = 0;
|
|
8691
8792
|
for (let first = true; ; first = false) {
|
|
8692
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
8793
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$4}/levels`);
|
|
8693
8794
|
const pass = batch.pass("closeness");
|
|
8694
8795
|
if (first) {
|
|
8695
8796
|
recordFill2(pass, bits, 4 * bitsBase, 0);
|
|
@@ -8748,7 +8849,7 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
|
|
|
8748
8849
|
const back = await submitted.readback;
|
|
8749
8850
|
ctx.assertReady();
|
|
8750
8851
|
if (options?.signal?.aborted) {
|
|
8751
|
-
throw aborted(ALGORITHM$
|
|
8852
|
+
throw aborted(ALGORITHM$4, submitted.id);
|
|
8752
8853
|
}
|
|
8753
8854
|
if (new Uint32Array(back, doneRequest.offset, 1)[0] !== 0) {
|
|
8754
8855
|
const block = new Uint32Array(back, blockRequest.offset, PER_SOURCE_WORDS);
|
|
@@ -8770,8 +8871,8 @@ async function sweepRoute(ctx, s, scores, sources, levelsPerSubmit, options, tun
|
|
|
8770
8871
|
if (level > n + 3) {
|
|
8771
8872
|
throw new WebGpuGraphError(
|
|
8772
8873
|
"E_VALIDATION",
|
|
8773
|
-
`${ALGORITHM$
|
|
8774
|
-
{ label: ALGORITHM$
|
|
8874
|
+
`${ALGORITHM$4}: the done flag never rose in ${level} levels of the batch at ${batchStart}`,
|
|
8875
|
+
{ label: ALGORITHM$4, message: `the done flag never rose in ${level} levels` }
|
|
8775
8876
|
);
|
|
8776
8877
|
}
|
|
8777
8878
|
}
|
|
@@ -8791,14 +8892,14 @@ function checkSources(s, sources) {
|
|
|
8791
8892
|
return null;
|
|
8792
8893
|
}
|
|
8793
8894
|
if (s.directed) {
|
|
8794
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
8895
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: sampled sources need an undirected snapshot`, {
|
|
8795
8896
|
feature: "closenessCentrality.directedSources",
|
|
8796
8897
|
hint: "run the CPU port, which searches the in-arcs"
|
|
8797
8898
|
});
|
|
8798
8899
|
}
|
|
8799
8900
|
for (const v of sources) {
|
|
8800
8901
|
if (!Number.isInteger(v) || v < 0 || v >= s.nodeCount) {
|
|
8801
|
-
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$
|
|
8902
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$4}: a source is not a node index`, {
|
|
8802
8903
|
argument: "sources",
|
|
8803
8904
|
value: v,
|
|
8804
8905
|
expected: `an integer in [0, ${s.nodeCount})`
|
|
@@ -8812,7 +8913,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
|
|
|
8812
8913
|
await assertDeviceComputes(ctx);
|
|
8813
8914
|
for (const key of ["maxIterations", "tolerance"]) {
|
|
8814
8915
|
if (options?.[key] !== void 0) {
|
|
8815
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
8916
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: ${key} has no meaning for an exact traversal`, {
|
|
8816
8917
|
option: key,
|
|
8817
8918
|
hint: "closeness is an exact traversal; the option has no meaning here"
|
|
8818
8919
|
});
|
|
@@ -8824,7 +8925,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
|
|
|
8824
8925
|
if (!Number.isInteger(levelsPerSubmit) || levelsPerSubmit < 1 || levelsPerSubmit > MAX_LEVELS_PER_SUBMIT) {
|
|
8825
8926
|
throw new WebGpuGraphError(
|
|
8826
8927
|
"E_INVALID_ARGUMENT",
|
|
8827
|
-
`${ALGORITHM$
|
|
8928
|
+
`${ALGORITHM$4}: levelsPerSubmit must be an integer in [1, ${MAX_LEVELS_PER_SUBMIT}]`,
|
|
8828
8929
|
{
|
|
8829
8930
|
argument: "levelsPerSubmit",
|
|
8830
8931
|
value: levelsPerSubmit,
|
|
@@ -8832,16 +8933,16 @@ async function closenessWithTuning(ctx, s, options, tuning) {
|
|
|
8832
8933
|
}
|
|
8833
8934
|
);
|
|
8834
8935
|
}
|
|
8835
|
-
const scores = checkDest$2(ALGORITHM$
|
|
8936
|
+
const scores = checkDest$2(ALGORITHM$4, options?.dest, n) ?? new Float32Array(n);
|
|
8836
8937
|
const weighted = options?.weighted ?? s.flags.weighted;
|
|
8837
8938
|
if (options?.signal?.aborted) {
|
|
8838
|
-
throw aborted(ALGORITHM$
|
|
8939
|
+
throw aborted(ALGORITHM$4);
|
|
8839
8940
|
}
|
|
8840
8941
|
if (weighted && s.weights !== null && !s.flags.allWeightsOne) {
|
|
8841
8942
|
if (!s.flags.nonNegativeWeights) {
|
|
8842
8943
|
throw new WebGpuGraphError(
|
|
8843
8944
|
"E_UNSUPPORTED",
|
|
8844
|
-
`${ALGORITHM$
|
|
8945
|
+
`${ALGORITHM$4}: a negative weight has no shortest-path distance to sum`,
|
|
8845
8946
|
{
|
|
8846
8947
|
feature: "closenessCentrality.negativeWeights",
|
|
8847
8948
|
hint: "pass weighted: false to ignore the column"
|
|
@@ -8849,7 +8950,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
|
|
|
8849
8950
|
);
|
|
8850
8951
|
}
|
|
8851
8952
|
if (!s.flags.finiteWeights) {
|
|
8852
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
8953
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$4}: a NaN or infinite weight has no shortest path`, {
|
|
8853
8954
|
feature: "closenessCentrality.nonFiniteWeights"
|
|
8854
8955
|
});
|
|
8855
8956
|
}
|
|
@@ -8860,7 +8961,7 @@ async function closenessWithTuning(ctx, s, options, tuning) {
|
|
|
8860
8961
|
function closenessCentrality(ctx, s, options) {
|
|
8861
8962
|
return closenessWithTuning(ctx, s, options, {});
|
|
8862
8963
|
}
|
|
8863
|
-
const ALGORITHM$
|
|
8964
|
+
const ALGORITHM$3 = "allPairsShortestPath";
|
|
8864
8965
|
const DEFAULT_ROUNDS_PER_SUBMIT = Math.floor(APSP_MAX_DISPATCHES_PER_SUBMIT / 3);
|
|
8865
8966
|
function allPairsCeiling(limits) {
|
|
8866
8967
|
const binding = limits.maxStorageBufferBindingSize;
|
|
@@ -8883,7 +8984,7 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
|
|
|
8883
8984
|
if (!Number.isInteger(roundsPerSubmit) || roundsPerSubmit < 1 || roundsPerSubmit > DEFAULT_ROUNDS_PER_SUBMIT) {
|
|
8884
8985
|
throw new WebGpuGraphError(
|
|
8885
8986
|
"E_INVALID_ARGUMENT",
|
|
8886
|
-
`${ALGORITHM$
|
|
8987
|
+
`${ALGORITHM$3}: roundsPerSubmit must be an integer in [1, ${DEFAULT_ROUNDS_PER_SUBMIT}]`,
|
|
8887
8988
|
{
|
|
8888
8989
|
argument: "roundsPerSubmit",
|
|
8889
8990
|
value: roundsPerSubmit,
|
|
@@ -8895,12 +8996,12 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
|
|
|
8895
8996
|
if (n > maxNodes) {
|
|
8896
8997
|
throw new WebGpuGraphError(
|
|
8897
8998
|
"E_TOO_LARGE",
|
|
8898
|
-
`${ALGORITHM$
|
|
8999
|
+
`${ALGORITHM$3}: ${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`,
|
|
8899
9000
|
{
|
|
8900
9001
|
needed: 4 * n * n,
|
|
8901
9002
|
limit,
|
|
8902
9003
|
path: "allPairs.matrix",
|
|
8903
|
-
algorithm: ALGORITHM$
|
|
9004
|
+
algorithm: ALGORITHM$3,
|
|
8904
9005
|
nodes: n,
|
|
8905
9006
|
maxNodes,
|
|
8906
9007
|
limitName,
|
|
@@ -8908,30 +9009,30 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
|
|
|
8908
9009
|
}
|
|
8909
9010
|
);
|
|
8910
9011
|
}
|
|
8911
|
-
const dist = checkDest$2(ALGORITHM$
|
|
9012
|
+
const dist = checkDest$2(ALGORITHM$3, options?.dest, n * n) ?? new Float32Array(n * n);
|
|
8912
9013
|
const weighted = (options?.weighted ?? s.weights !== null) && s.weights !== null && !s.flags.allWeightsOne;
|
|
8913
9014
|
if (weighted && !s.flags.nonNegativeWeights) {
|
|
8914
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
9015
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a negative weight is not supported`, {
|
|
8915
9016
|
feature: "allPairs.negativeWeights",
|
|
8916
9017
|
hint: "the blocked Floyd-Warshall sweep needs non-negative weights; pass weighted: false for hop counts"
|
|
8917
9018
|
});
|
|
8918
9019
|
}
|
|
8919
9020
|
if (weighted && !s.flags.finiteWeights) {
|
|
8920
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$
|
|
9021
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$3}: a NaN or infinite weight has no shortest path`, {
|
|
8921
9022
|
feature: "allPairs.nonFiniteWeights"
|
|
8922
9023
|
});
|
|
8923
9024
|
}
|
|
8924
9025
|
if (options?.signal?.aborted) {
|
|
8925
|
-
throw aborted(ALGORITHM$
|
|
9026
|
+
throw aborted(ALGORITHM$3);
|
|
8926
9027
|
}
|
|
8927
9028
|
if (n === 0) {
|
|
8928
9029
|
return { dist, n };
|
|
8929
9030
|
}
|
|
8930
9031
|
await assertDeviceComputes(ctx);
|
|
8931
9032
|
const core = ctx.residency.core(s);
|
|
8932
|
-
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$
|
|
9033
|
+
assertWholeCore(core, s.arcCount, ctx.caps.limits.maxStorageBufferBindingSize, ALGORITHM$3);
|
|
8933
9034
|
const blocks = Math.ceil(n / APSP_TILE);
|
|
8934
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
9035
|
+
const scope = algorithmScope(ctx, ALGORITHM$3, Math.min(roundsPerSubmit, blocks) + 2);
|
|
8935
9036
|
try {
|
|
8936
9037
|
const wg = ctx.workgroupSize;
|
|
8937
9038
|
const bytes = 4 * n * n;
|
|
@@ -8947,7 +9048,7 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
|
|
|
8947
9048
|
const others = blocks - 1;
|
|
8948
9049
|
const phasePlans = [plan2d(1, ctx.caps), plan2d(2 * others, ctx.caps), plan2d(others * others, ctx.caps)];
|
|
8949
9050
|
for (let first = 0; first < blocks; first += roundsPerSubmit) {
|
|
8950
|
-
const batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
9051
|
+
const batch = new CommandBatch(ctx, `${ALGORITHM$3}/sweep`);
|
|
8951
9052
|
const pass = batch.pass("apsp");
|
|
8952
9053
|
if (first === 0) {
|
|
8953
9054
|
const fillParams = scope.params(FILL_PARAMS, { count: n * n, value: F32_INF_BITS, mode: 0, pad0: 0 });
|
|
@@ -8979,7 +9080,7 @@ async function allPairsWithTuning(ctx, s, options, tuning) {
|
|
|
8979
9080
|
ctx.assertReady();
|
|
8980
9081
|
options?.onProgress?.(last, blocks);
|
|
8981
9082
|
if (options?.signal?.aborted) {
|
|
8982
|
-
throw aborted(ALGORITHM$
|
|
9083
|
+
throw aborted(ALGORITHM$3, submitted.id);
|
|
8983
9084
|
}
|
|
8984
9085
|
}
|
|
8985
9086
|
await ctx.readback.read(matrix.buffer, bytes, dist);
|
|
@@ -9631,9 +9732,9 @@ function runCore(runStart, runs, sortedDst, sortedW, valid) {
|
|
|
9631
9732
|
hasWeights: true
|
|
9632
9733
|
};
|
|
9633
9734
|
}
|
|
9634
|
-
const ALGORITHM$
|
|
9735
|
+
const ALGORITHM$2 = "labelPropagation";
|
|
9635
9736
|
const DEFAULT_MAX_ITERATIONS = 100;
|
|
9636
|
-
const RING_SLOTS$
|
|
9737
|
+
const RING_SLOTS$2 = 1024;
|
|
9637
9738
|
function checkDest$1(dest, n) {
|
|
9638
9739
|
if (dest === void 0) {
|
|
9639
9740
|
return null;
|
|
@@ -9643,7 +9744,7 @@ function checkDest$1(dest, n) {
|
|
|
9643
9744
|
}
|
|
9644
9745
|
throw new WebGpuGraphError(
|
|
9645
9746
|
"E_INVALID_ARGUMENT",
|
|
9646
|
-
`${ALGORITHM$
|
|
9747
|
+
`${ALGORITHM$2}: dest must be a Uint32Array of length ${n} over an ArrayBuffer`,
|
|
9647
9748
|
{
|
|
9648
9749
|
argument: "dest",
|
|
9649
9750
|
value: `${dest.constructor.name}(${dest.length})`,
|
|
@@ -9656,7 +9757,7 @@ function maxIterationsOf(value) {
|
|
|
9656
9757
|
return DEFAULT_MAX_ITERATIONS;
|
|
9657
9758
|
}
|
|
9658
9759
|
if (!Number.isSafeInteger(value) || value < 0) {
|
|
9659
|
-
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$
|
|
9760
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${ALGORITHM$2}: maxIterations must be a non-negative integer`, {
|
|
9660
9761
|
argument: "maxIterations",
|
|
9661
9762
|
value,
|
|
9662
9763
|
expected: "a non-negative integer"
|
|
@@ -9676,12 +9777,12 @@ function neighbourBound(s) {
|
|
|
9676
9777
|
}
|
|
9677
9778
|
return bound;
|
|
9678
9779
|
}
|
|
9679
|
-
function resultOf(raw, dest) {
|
|
9780
|
+
function resultOf$1(raw, dest) {
|
|
9680
9781
|
const n = raw.length;
|
|
9681
9782
|
for (let v = 0; v < n; v++) {
|
|
9682
9783
|
if (raw[v] >= n) {
|
|
9683
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
9684
|
-
label: `${ALGORITHM$
|
|
9784
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$2}: labels[${v}] = ${raw[v]} is not a node index`, {
|
|
9785
|
+
label: `${ALGORITHM$2}/labels`,
|
|
9685
9786
|
message: `the device produced a label outside [0, ${n})`
|
|
9686
9787
|
});
|
|
9687
9788
|
}
|
|
@@ -9704,23 +9805,23 @@ async function labelPropagation(ctx, s, options) {
|
|
|
9704
9805
|
const maxIterations = maxIterationsOf(options?.maxIterations);
|
|
9705
9806
|
const weighted = options?.weighted !== false;
|
|
9706
9807
|
if (options?.signal?.aborted) {
|
|
9707
|
-
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$
|
|
9808
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$2}: the signal was aborted before any work started`, {});
|
|
9708
9809
|
}
|
|
9709
9810
|
if (n === 0 || s.edgeCount === 0 || maxIterations === 0) {
|
|
9710
9811
|
options?.onProgress?.(1, 1);
|
|
9711
9812
|
return identityResult(n, dest);
|
|
9712
9813
|
}
|
|
9713
9814
|
const plan = planGroupRows(neighbourBound(s));
|
|
9714
|
-
assertBindable(ctx, 4 * plan.regionWords, "the group-by hash region", ALGORITHM$
|
|
9715
|
-
const scope = algorithmScope(ctx, ALGORITHM$
|
|
9815
|
+
assertBindable(ctx, 4 * plan.regionWords, "the group-by hash region", ALGORITHM$2);
|
|
9816
|
+
const scope = algorithmScope(ctx, ALGORITHM$2, RING_SLOTS$2);
|
|
9716
9817
|
try {
|
|
9717
|
-
const build = await buildSimpleSymmetric(ctx, s, scope, weighted, ALGORITHM$
|
|
9818
|
+
const build = await buildSimpleSymmetric(ctx, s, scope, weighted, ALGORITHM$2);
|
|
9718
9819
|
const { graph } = build;
|
|
9719
9820
|
if (graph.colIdx === null) {
|
|
9720
9821
|
scope.flush();
|
|
9721
9822
|
const bytes = await build.batch.submit().readback;
|
|
9722
9823
|
ctx.assertReady();
|
|
9723
|
-
assertBuildSorted(bytes, build, ALGORITHM$
|
|
9824
|
+
assertBuildSorted(bytes, build, ALGORITHM$2);
|
|
9724
9825
|
options?.onProgress?.(1, 1);
|
|
9725
9826
|
return identityResult(n, dest);
|
|
9726
9827
|
}
|
|
@@ -9792,12 +9893,12 @@ async function labelPropagation(ctx, s, options) {
|
|
|
9792
9893
|
const bytes = await submitted.readback;
|
|
9793
9894
|
ctx.assertReady();
|
|
9794
9895
|
if (first) {
|
|
9795
|
-
assertBuildSorted(bytes, build, ALGORITHM$
|
|
9896
|
+
assertBuildSorted(bytes, build, ALGORITHM$2);
|
|
9796
9897
|
first = false;
|
|
9797
9898
|
}
|
|
9798
9899
|
if (new Uint32Array(bytes, exhaustedRequest.offset, 1)[0] !== 0) {
|
|
9799
|
-
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$
|
|
9800
|
-
label: `${ALGORITHM$
|
|
9900
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$2}: a hash probe exhausted its bound`, {
|
|
9901
|
+
label: `${ALGORITHM$2}/group-by-key`,
|
|
9801
9902
|
message: "a compare-exchange loop of the workgroup tier ran out of steps"
|
|
9802
9903
|
});
|
|
9803
9904
|
}
|
|
@@ -9806,7 +9907,7 @@ async function labelPropagation(ctx, s, options) {
|
|
|
9806
9907
|
const lastTwo = k >= 2 ? moves[k - 2] + moves[k - 1] : previousLast + moves[0];
|
|
9807
9908
|
previousLast = moves[k - 1];
|
|
9808
9909
|
if (options?.signal?.aborted) {
|
|
9809
|
-
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$
|
|
9910
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$2}: the signal was aborted`, {
|
|
9810
9911
|
batchId: submitted.id
|
|
9811
9912
|
});
|
|
9812
9913
|
}
|
|
@@ -9814,12 +9915,203 @@ async function labelPropagation(ctx, s, options) {
|
|
|
9814
9915
|
if (lastTwo === 0 || done >= maxIterations) {
|
|
9815
9916
|
break;
|
|
9816
9917
|
}
|
|
9817
|
-
batch = new CommandBatch(ctx, `${ALGORITHM$
|
|
9918
|
+
batch = new CommandBatch(ctx, `${ALGORITHM$2}/passes`);
|
|
9818
9919
|
}
|
|
9819
9920
|
const raw = new Uint32Array(n);
|
|
9820
9921
|
await ctx.readback.read(cur.buffer, 4 * n, raw);
|
|
9821
9922
|
ctx.assertReady();
|
|
9822
|
-
return resultOf(raw, dest);
|
|
9923
|
+
return resultOf$1(raw, dest);
|
|
9924
|
+
} finally {
|
|
9925
|
+
scope.dispose();
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
const ALGORITHM$1 = "minimumSpanningTree";
|
|
9929
|
+
const COMPRESS_STEPS = 1024;
|
|
9930
|
+
const MAX_ROUNDS = 64;
|
|
9931
|
+
const RING_SLOTS$1 = 5 + BORUVKA_ROUNDS_PER_SUBMIT;
|
|
9932
|
+
function compressPasses(n) {
|
|
9933
|
+
let passes = 1;
|
|
9934
|
+
for (let reach = COMPRESS_STEPS; reach < n; reach *= COMPRESS_STEPS) {
|
|
9935
|
+
passes++;
|
|
9936
|
+
}
|
|
9937
|
+
return passes;
|
|
9938
|
+
}
|
|
9939
|
+
function resultOf(s, treeEdge, expected) {
|
|
9940
|
+
const edges = new Uint32Array(expected);
|
|
9941
|
+
const { weights } = s.edgeList();
|
|
9942
|
+
let taken = 0;
|
|
9943
|
+
let totalWeight = 0;
|
|
9944
|
+
for (const e of treeEdge) {
|
|
9945
|
+
if (e === INVALID_INDEX) {
|
|
9946
|
+
continue;
|
|
9947
|
+
}
|
|
9948
|
+
if (e >= s.edgeCount || taken === expected) {
|
|
9949
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: the device recorded an edge it did not count`, {
|
|
9950
|
+
label: `${ALGORITHM$1}/treeEdge`,
|
|
9951
|
+
message: `edge ${e} at forest position ${taken} of ${expected}`
|
|
9952
|
+
});
|
|
9953
|
+
}
|
|
9954
|
+
edges[taken++] = e;
|
|
9955
|
+
totalWeight += weights === null ? 1 : weights[e];
|
|
9956
|
+
}
|
|
9957
|
+
if (taken !== expected) {
|
|
9958
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: ${taken} recorded edges, ${expected} counted`, {
|
|
9959
|
+
label: `${ALGORITHM$1}/treeEdge`,
|
|
9960
|
+
message: "the per-round counts disagree with the recorded forest"
|
|
9961
|
+
});
|
|
9962
|
+
}
|
|
9963
|
+
return { edges, totalWeight };
|
|
9964
|
+
}
|
|
9965
|
+
async function minimumSpanningTree(ctx, s, options) {
|
|
9966
|
+
ctx.assertReady();
|
|
9967
|
+
await assertDeviceComputes(ctx);
|
|
9968
|
+
if (options?.dest !== void 0) {
|
|
9969
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `${ALGORITHM$1}: dest is not supported`, {
|
|
9970
|
+
option: "dest",
|
|
9971
|
+
hint: "the forest's edge count is known only after the run"
|
|
9972
|
+
});
|
|
9973
|
+
}
|
|
9974
|
+
if (options?.signal?.aborted) {
|
|
9975
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted before any work started`, {});
|
|
9976
|
+
}
|
|
9977
|
+
const n = s.nodeCount;
|
|
9978
|
+
const m = s.edgeCount;
|
|
9979
|
+
if (n === 0 || m === 0) {
|
|
9980
|
+
options?.onProgress?.(1, 1);
|
|
9981
|
+
return { edges: new Uint32Array(0), totalWeight: 0 };
|
|
9982
|
+
}
|
|
9983
|
+
ctx.residency.core(s, ["rowPtr"]);
|
|
9984
|
+
const edges = ctx.residency.view(s, "edgeList");
|
|
9985
|
+
const edgeWeight = edges.bindings.weights ?? null;
|
|
9986
|
+
const scope = algorithmScope(ctx, ALGORITHM$1, RING_SLOTS$1);
|
|
9987
|
+
try {
|
|
9988
|
+
const wg = ctx.workgroupSize;
|
|
9989
|
+
const words = (count, label) => {
|
|
9990
|
+
const size = 4 * Math.max(1, count);
|
|
9991
|
+
return { buffer: scope.scratch(size, label), offset: 0, size, window: null };
|
|
9992
|
+
};
|
|
9993
|
+
const comp = words(n, "comp");
|
|
9994
|
+
const bestKey = words(n, "bestKey");
|
|
9995
|
+
const bestEdge = words(n, "bestEdge");
|
|
9996
|
+
const treeEdge = words(n, "treeEdge");
|
|
9997
|
+
const counters = words(BORUVKA_ROUNDS_PER_SUBMIT, "counters");
|
|
9998
|
+
await ctx.allocator.check();
|
|
9999
|
+
const fill = await ctx.pipelines.kernel(kernelSpec("fill"));
|
|
10000
|
+
const weighted = edgeWeight !== null;
|
|
10001
|
+
const minKey = await ctx.pipelines.kernel(kernelSpec("mst-best", { PASS: 0, WEIGHTED: weighted }));
|
|
10002
|
+
const minEdge = await ctx.pipelines.kernel(kernelSpec("mst-best", { PASS: 1, WEIGHTED: weighted }));
|
|
10003
|
+
const link = await ctx.pipelines.kernel(kernelSpec("mst-link"));
|
|
10004
|
+
const compress = await ctx.pipelines.kernel(kernelSpec("wcc-compress"));
|
|
10005
|
+
const nodePlan = plan1d(n, wg, ctx.caps);
|
|
10006
|
+
const edgePlan = plan1d(m, wg, ctx.caps);
|
|
10007
|
+
const compressPlan = planGridStride(n, wg, ctx.caps);
|
|
10008
|
+
const passes = compressPasses(n);
|
|
10009
|
+
const { queue } = ctx.device;
|
|
10010
|
+
let batch = new CommandBatch(ctx, `${ALGORITHM$1}/rounds`);
|
|
10011
|
+
let first = true;
|
|
10012
|
+
let rounds = 0;
|
|
10013
|
+
let recorded = 0;
|
|
10014
|
+
for (; ; ) {
|
|
10015
|
+
queue.writeBuffer(counters.buffer, 0, new Uint32Array(BORUVKA_ROUNDS_PER_SUBMIT));
|
|
10016
|
+
const pass = batch.pass("rounds");
|
|
10017
|
+
const fillFor = (value, mode) => scope.params(FILL_PARAMS, { count: n, value, mode, pad0: 0 });
|
|
10018
|
+
if (first) {
|
|
10019
|
+
const iota = fillFor(0, 1);
|
|
10020
|
+
fill.dispatch(pass, fill.bind({ dst: comp, P: iota.binding }), nodePlan, [iota.offset]);
|
|
10021
|
+
const none = fillFor(INVALID_INDEX, 0);
|
|
10022
|
+
fill.dispatch(pass, fill.bind({ dst: treeEdge, P: none.binding }), nodePlan, [none.offset]);
|
|
10023
|
+
}
|
|
10024
|
+
const reset = fillFor(U32_MAX$2, 0);
|
|
10025
|
+
const edgeParams = scope.params(MST_PARAMS, { count: m, counterIndex: 0, pad0: 0, pad1: 0 });
|
|
10026
|
+
const compressParams = scope.params(WCC_PARAMS, {
|
|
10027
|
+
n,
|
|
10028
|
+
items: n,
|
|
10029
|
+
stride: compressPlan.stride ?? n,
|
|
10030
|
+
r: 0,
|
|
10031
|
+
flagIndex: 0,
|
|
10032
|
+
giant: U32_MAX$2,
|
|
10033
|
+
maxSteps: COMPRESS_STEPS,
|
|
10034
|
+
pad0: 0
|
|
10035
|
+
});
|
|
10036
|
+
const graph = {
|
|
10037
|
+
edgeSrc: edges.bindings.src,
|
|
10038
|
+
edgeDst: edges.bindings.dst,
|
|
10039
|
+
edgeWeight: edgeWeight ?? edges.bindings.src
|
|
10040
|
+
};
|
|
10041
|
+
for (let i = 0; i < BORUVKA_ROUNDS_PER_SUBMIT; i++) {
|
|
10042
|
+
for (const dst of [bestKey, bestEdge]) {
|
|
10043
|
+
fill.dispatch(pass, fill.bind({ dst, P: reset.binding }), nodePlan, [reset.offset]);
|
|
10044
|
+
}
|
|
10045
|
+
for (const kernel of [minKey, minEdge]) {
|
|
10046
|
+
kernel.dispatch(
|
|
10047
|
+
pass,
|
|
10048
|
+
kernel.bind({ ...graph, comp, bestKey, bestEdge, P: edgeParams.binding }),
|
|
10049
|
+
edgePlan,
|
|
10050
|
+
[edgeParams.offset]
|
|
10051
|
+
);
|
|
10052
|
+
}
|
|
10053
|
+
const linkParams = scope.params(MST_PARAMS, { count: n, counterIndex: i, pad0: 0, pad1: 0 });
|
|
10054
|
+
link.dispatch(
|
|
10055
|
+
pass,
|
|
10056
|
+
link.bind({
|
|
10057
|
+
edgeSrc: graph.edgeSrc,
|
|
10058
|
+
edgeDst: graph.edgeDst,
|
|
10059
|
+
bestEdge,
|
|
10060
|
+
comp,
|
|
10061
|
+
treeEdge,
|
|
10062
|
+
counters,
|
|
10063
|
+
P: linkParams.binding
|
|
10064
|
+
}),
|
|
10065
|
+
nodePlan,
|
|
10066
|
+
[linkParams.offset]
|
|
10067
|
+
);
|
|
10068
|
+
for (let j = 0; j < passes; j++) {
|
|
10069
|
+
compress.dispatch(pass, compress.bind({ comp, P: compressParams.binding }), compressPlan, [
|
|
10070
|
+
compressParams.offset
|
|
10071
|
+
]);
|
|
10072
|
+
}
|
|
10073
|
+
}
|
|
10074
|
+
batch.endPass();
|
|
10075
|
+
const countsRequest = batch.readback(counters.buffer, 0, 4 * BORUVKA_ROUNDS_PER_SUBMIT);
|
|
10076
|
+
scope.flush();
|
|
10077
|
+
const submitted = batch.submit();
|
|
10078
|
+
const bytes = await submitted.readback;
|
|
10079
|
+
ctx.assertReady();
|
|
10080
|
+
first = false;
|
|
10081
|
+
if (options?.signal?.aborted) {
|
|
10082
|
+
throw new WebGpuGraphError("E_ABORTED", `${ALGORITHM$1}: the signal was aborted`, {
|
|
10083
|
+
batchId: submitted.id
|
|
10084
|
+
});
|
|
10085
|
+
}
|
|
10086
|
+
const counts = new Uint32Array(bytes, countsRequest.offset, BORUVKA_ROUNDS_PER_SUBMIT);
|
|
10087
|
+
let settled2 = false;
|
|
10088
|
+
for (const count of counts) {
|
|
10089
|
+
recorded += count;
|
|
10090
|
+
settled2 ||= count === 0;
|
|
10091
|
+
}
|
|
10092
|
+
rounds += BORUVKA_ROUNDS_PER_SUBMIT;
|
|
10093
|
+
if (recorded > n - 1) {
|
|
10094
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: ${recorded} forest edges on ${n} nodes`, {
|
|
10095
|
+
label: `${ALGORITHM$1}/counters`,
|
|
10096
|
+
message: "a spanning forest has at most n - 1 edges"
|
|
10097
|
+
});
|
|
10098
|
+
}
|
|
10099
|
+
if (settled2) {
|
|
10100
|
+
break;
|
|
10101
|
+
}
|
|
10102
|
+
if (rounds >= MAX_ROUNDS) {
|
|
10103
|
+
throw new WebGpuGraphError("E_VALIDATION", `${ALGORITHM$1}: still merging after ${rounds} rounds`, {
|
|
10104
|
+
label: `${ALGORITHM$1}/rounds`,
|
|
10105
|
+
message: "Boruvka halves the components every round; the device did not"
|
|
10106
|
+
});
|
|
10107
|
+
}
|
|
10108
|
+
batch = new CommandBatch(ctx, `${ALGORITHM$1}/rounds`);
|
|
10109
|
+
}
|
|
10110
|
+
const raw = new Uint32Array(n);
|
|
10111
|
+
await ctx.readback.read(treeEdge.buffer, 4 * n, raw);
|
|
10112
|
+
ctx.assertReady();
|
|
10113
|
+
options?.onProgress?.(1, 1);
|
|
10114
|
+
return resultOf(s, raw, recorded);
|
|
9823
10115
|
} finally {
|
|
9824
10116
|
scope.dispose();
|
|
9825
10117
|
}
|
|
@@ -15089,6 +15381,24 @@ function createAccelerator(ctx, options) {
|
|
|
15089
15381
|
}
|
|
15090
15382
|
return await labelPropagation(ctx, gs, { maxIterations: o?.maxIterations, weighted: o?.weighted });
|
|
15091
15383
|
},
|
|
15384
|
+
/**
|
|
15385
|
+
* Boruvka's minimum spanning forest (design 8.5; P11-T4): the forest of the total edge order (weight, then
|
|
15386
|
+
* edge index), which is the one `kruskalMST` accepts. The seam's per-arc `weights` override is refused when
|
|
15387
|
+
* defined: the forest runs over the snapshot's own edge weights.
|
|
15388
|
+
* @param gs - the snapshot
|
|
15389
|
+
* @param o - the seam's `MstOptions`; `weights` refused when defined
|
|
15390
|
+
* @returns the forest's logical edge indices and its f64 total weight
|
|
15391
|
+
*/
|
|
15392
|
+
async minimumSpanningTree(gs, o) {
|
|
15393
|
+
ctx.assertReady();
|
|
15394
|
+
if (o?.weights !== void 0) {
|
|
15395
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", "minimumSpanningTree: weights is not supported", {
|
|
15396
|
+
option: "weights",
|
|
15397
|
+
hint: "the spanning forest runs over the snapshot's own edge weights"
|
|
15398
|
+
});
|
|
15399
|
+
}
|
|
15400
|
+
return await minimumSpanningTree(ctx, gs);
|
|
15401
|
+
},
|
|
15092
15402
|
/**
|
|
15093
15403
|
* Destroys every device buffer recorded for the snapshot (spec 4.5); delegates to ctx.release.
|
|
15094
15404
|
* @param s - the snapshot the app is done with
|
|
@@ -15198,7 +15508,7 @@ async function calibrateLayout(ctx, options) {
|
|
|
15198
15508
|
};
|
|
15199
15509
|
}
|
|
15200
15510
|
export {
|
|
15201
|
-
|
|
15511
|
+
a1 as ARC_WINDOW_ALIGN,
|
|
15202
15512
|
EXACT_MAX_NODES,
|
|
15203
15513
|
FA2_DEFAULTS,
|
|
15204
15514
|
FR_DEFAULTS,
|
|
@@ -15206,10 +15516,10 @@ export {
|
|
|
15206
15516
|
LAYOUT_TUNING_DEFAULTS,
|
|
15207
15517
|
MAX_1D_ITEMS,
|
|
15208
15518
|
MAX_WORKGROUPS_PER_DIM,
|
|
15209
|
-
|
|
15519
|
+
a2 as PASSTHROUGH_FORMAT_CODES,
|
|
15210
15520
|
SE_DEFAULTS,
|
|
15211
|
-
|
|
15212
|
-
|
|
15521
|
+
a3 as STORAGE_ALIGN,
|
|
15522
|
+
a4 as WORKGROUP_SIZE,
|
|
15213
15523
|
WebGpuGraphError,
|
|
15214
15524
|
allPairsShortestPath,
|
|
15215
15525
|
bellmanFord,
|
|
@@ -15227,10 +15537,11 @@ export {
|
|
|
15227
15537
|
eigenvectorCentrality,
|
|
15228
15538
|
hasErrorCode,
|
|
15229
15539
|
hits,
|
|
15230
|
-
|
|
15540
|
+
a5 as isSoftwareAdapter,
|
|
15231
15541
|
isWebGpuGraphError,
|
|
15232
15542
|
katzCentrality,
|
|
15233
15543
|
labelPropagation,
|
|
15544
|
+
minimumSpanningTree,
|
|
15234
15545
|
pageRank,
|
|
15235
15546
|
personalizedPageRank,
|
|
15236
15547
|
seedPositions,
|