@graphty/webgpu-graph-algorithms 0.6.19 → 0.6.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphty/webgpu-graph-algorithms",
3
- "version": "0.6.19",
3
+ "version": "0.6.20",
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,7 +61,7 @@
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.5"
64
+ "@graphty/graph-format": "^1.2.6"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@graphty/algorithms": "^1.0.0 || ^2.0.0 || ^3.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/algorithms": "^3.1.3",
99
- "@graphty/layout": "^2.0.5"
98
+ "@graphty/algorithms": "^3.1.4",
99
+ "@graphty/layout": "^2.0.6"
100
100
  },
101
101
  "scripts": {
102
102
  "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
@@ -8,7 +8,7 @@
8
8
  * `changed` after it IS `hasNegativeCycle`; the last batch is clamped so the reported round count never exceeds
9
9
  * `n - 1`. `dist` holds f32 bit patterns and the claim is a BOUNDED compare-exchange (PD-12: `MAX_RETRIES`, because
10
10
  * with a negative distance the bit-pattern order reverses and `atomicMin` is wrong, and WGSL lets the exchange fail
11
- * spuriously): a lane that exhausts the bound raises `retryExhausted`, which the driver treats as changed and runs
11
+ * spuriously; only those spurious failures count, never a loss to another lane's improvement, issue #470): a lane that exhausts the bound raises `retryExhausted`, which the driver treats as changed and runs
12
12
  * on -- the lost update is retried by the next round, which examines every edge anyway -- and tallies for the tests;
13
13
  * raised in the decision round it is E_VALIDATION, never a guess.
14
14
  *
@@ -74,7 +74,7 @@ const ALGORITHM = "bellmanFord";
74
74
  /** Design 8.4: the changed flag is read every 8 rounds (one `mapAsync` per batch). */
75
75
  const ROUNDS_PER_BATCH = 8;
76
76
 
77
- /** PD-12: the compare-exchange retry bound per lane (contention is per vertex, not global); `components.ts`'s `MAX_STEPS` idiom. */
77
+ /** PD-12: the bound on one lane's SPURIOUS compare-exchange failures (a loss to another lane is not counted, issue #470). */
78
78
  const MAX_RETRIES = 16;
79
79
 
80
80
  /**
@@ -89,7 +89,7 @@ const RING_SLOTS = MAX_LEVELS_PER_SUBMIT + 16;
89
89
  * @internal
90
90
  */
91
91
  export interface BellmanFordTuning {
92
- /** The compare-exchange retry bound per lane (default `MAX_RETRIES`); 1 makes every failed exchange exhaust it. */
92
+ /** The compare-exchange retry bound per lane (default `MAX_RETRIES`), on spurious failures only; 1 makes the first one exhaust it. */
93
93
  readonly maxRetries?: number | undefined;
94
94
  /** Rounds recorded per batch (default `ROUNDS_PER_BATCH`), each batch one readback of the flags. */
95
95
  readonly roundsPerBatch?: number | undefined;
@@ -124,6 +124,9 @@ export function coreOf(ctx: GpuContext, s: GraphSnapshot, algorithm: string): Co
124
124
  * @returns the reverse core
125
125
  */
126
126
  export function reverseOf(ctx: GpuContext, s: GraphSnapshot): CoreBinding {
127
+ // core() is what records (or re-records, after a release) the snapshot in the residency; Katz binds only this
128
+ // view, so without it a released snapshot is E_RELEASED instead of uploaded again (issue #623)
129
+ ctx.residency.core(s, ["rowPtr"]);
127
130
  const view = ctx.residency.view(s, "reverse");
128
131
  return coreOfView(view, view.scalars.arcCount[0]);
129
132
  }
@@ -8,7 +8,10 @@
8
8
  * the values for NON-NEGATIVE floats only (PD-9); with a negative distance the bit-pattern order reverses, so the
9
9
  * claim is a compare-exchange loop on the pattern: read, compute, compare as floats, try to exchange. WGSL lets
10
10
  * `atomicCompareExchangeWeak` fail spuriously, so the loop is BOUNDED (PD-12: `P.maxRetries`, the driver's
11
- * `MAX_RETRIES`; contention is per vertex, not global) and a lane that exhausts it sets `flags[1]`
11
+ * `MAX_RETRIES`) -- on SPURIOUS failures only (`old_value == cur`). A failure that returns another value lost to
12
+ * another lane's successful exchange, which strictly lowered `dist[v]`; that happens at most once per edge into
13
+ * `v` per round, so those iterations are bounded by the in-degree and never counted (issue #470: counted, 32
14
+ * descending candidates in one 32-wide subgroup needed 31 attempts and exhausted 16). A lane that exhausts it sets `flags[1]`
12
15
  * (`retryExhausted`): the driver treats the round as changed and runs on, so the lost update is retried by the next
13
16
  * round, which examines every edge anyway; a bound hit in the decision round is E_VALIDATION, never a guess. A
14
17
  * successful exchange sets `flags[0]` (`changed`); the driver stops when a batch of rounds changed nothing, and a
@@ -26,9 +29,11 @@ fn relax(v: u32, nd: f32) {
26
29
  if (!(nd < bitcast<f32>(cur))) { break; } // no improvement; +Inf is greater than every finite nd
27
30
  let r = atomicCompareExchangeWeak(&dist[v], cur, bitcast<u32>(nd));
28
31
  if (r.exchanged) { atomicStore(&flags[0], 1u); break; } // changed
29
- cur = r.old_value;
30
- tries = tries + 1u;
31
- if (tries >= P.maxRetries) { atomicStore(&flags[1], 1u); break; } // retryExhausted (PD-12)
32
+ if (r.old_value == cur) { // a spurious failure: the only kind the bound counts
33
+ tries = tries + 1u;
34
+ if (tries >= P.maxRetries) { atomicStore(&flags[1], 1u); break; } // retryExhausted (PD-12)
35
+ }
36
+ cur = r.old_value; // else another lane's improvement won (issue #470)
32
37
  }
33
38
  }
34
39