@graphty/webgpu-graph-algorithms 0.4.1 → 0.5.1

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.
Files changed (93) hide show
  1. package/README.md +27 -21
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{context-CRbw2Wyo.js → context-BR7fx3vR.js} +47 -5
  4. package/dist/chunks/context-BR7fx3vR.js.map +1 -0
  5. package/dist/node.js +1 -1
  6. package/dist/src/accelerator.d.ts +15 -12
  7. package/dist/src/accelerator.d.ts.map +1 -1
  8. package/dist/src/accelerator.js +35 -12
  9. package/dist/src/accelerator.js.map +1 -1
  10. package/dist/src/constants.d.ts +47 -0
  11. package/dist/src/constants.d.ts.map +1 -1
  12. package/dist/src/constants.js +47 -0
  13. package/dist/src/constants.js.map +1 -1
  14. package/dist/src/index.d.ts +8 -5
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +9 -5
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/kernel/prelude.d.ts.map +1 -1
  19. package/dist/src/kernel/prelude.js +4 -1
  20. package/dist/src/kernel/prelude.js.map +1 -1
  21. package/dist/src/kernels.d.ts +3 -3
  22. package/dist/src/kernels.d.ts.map +1 -1
  23. package/dist/src/kernels.js +27 -11
  24. package/dist/src/kernels.js.map +1 -1
  25. package/dist/src/layouts/force-simulation.d.ts +3 -1
  26. package/dist/src/layouts/force-simulation.d.ts.map +1 -1
  27. package/dist/src/layouts/force-simulation.js +13 -3
  28. package/dist/src/layouts/force-simulation.js.map +1 -1
  29. package/dist/src/layouts/forceatlas2.d.ts +1 -3
  30. package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
  31. package/dist/src/layouts/forceatlas2.js +2 -191
  32. package/dist/src/layouts/forceatlas2.js.map +1 -1
  33. package/dist/src/layouts/fruchterman-reingold.d.ts +195 -0
  34. package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -0
  35. package/dist/src/layouts/fruchterman-reingold.js +579 -0
  36. package/dist/src/layouts/fruchterman-reingold.js.map +1 -0
  37. package/dist/src/layouts/model-common.d.ts +102 -0
  38. package/dist/src/layouts/model-common.d.ts.map +1 -0
  39. package/dist/src/layouts/model-common.js +198 -0
  40. package/dist/src/layouts/model-common.js.map +1 -0
  41. package/dist/src/layouts/spring-electrical.d.ts +179 -0
  42. package/dist/src/layouts/spring-electrical.d.ts.map +1 -0
  43. package/dist/src/layouts/spring-electrical.js +508 -0
  44. package/dist/src/layouts/spring-electrical.js.map +1 -0
  45. package/dist/src/types/accelerator.d.ts +14 -157
  46. package/dist/src/types/accelerator.d.ts.map +1 -1
  47. package/dist/src/types/accelerator.js +4 -5
  48. package/dist/src/types/accelerator.js.map +1 -1
  49. package/dist/src/types/algorithms.d.ts +1 -1
  50. package/dist/src/types/algorithms.js +1 -1
  51. package/dist/src/types/layout.d.ts +32 -0
  52. package/dist/src/types/layout.d.ts.map +1 -1
  53. package/dist/src/types/options.d.ts +43 -1
  54. package/dist/src/types/options.d.ts.map +1 -1
  55. package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +5 -2
  56. package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -1
  57. package/dist/src/wgsl/fa2-attraction.wgsl.js +6 -1
  58. package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -1
  59. package/dist/src/wgsl/fa2-integrate.wgsl.d.ts +7 -2
  60. package/dist/src/wgsl/fa2-integrate.wgsl.d.ts.map +1 -1
  61. package/dist/src/wgsl/fa2-integrate.wgsl.js +28 -2
  62. package/dist/src/wgsl/fa2-integrate.wgsl.js.map +1 -1
  63. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +4 -2
  64. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -1
  65. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +14 -5
  66. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -1
  67. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +8 -1
  68. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
  69. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +33 -0
  70. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -1
  71. package/dist/tsconfig.build.tsbuildinfo +1 -1
  72. package/dist/webgpu-graph-algorithms.js +1358 -284
  73. package/dist/webgpu-graph-algorithms.js.map +1 -1
  74. package/package.json +6 -3
  75. package/src/accelerator.ts +49 -14
  76. package/src/constants.ts +70 -0
  77. package/src/index.ts +19 -8
  78. package/src/kernel/prelude.ts +6 -0
  79. package/src/kernels.ts +27 -11
  80. package/src/layouts/force-simulation.ts +20 -3
  81. package/src/layouts/forceatlas2.ts +17 -220
  82. package/src/layouts/fruchterman-reingold.ts +737 -0
  83. package/src/layouts/model-common.ts +228 -0
  84. package/src/layouts/spring-electrical.ts +666 -0
  85. package/src/types/accelerator.ts +58 -174
  86. package/src/types/algorithms.ts +1 -1
  87. package/src/types/layout.ts +36 -0
  88. package/src/types/options.ts +45 -1
  89. package/src/wgsl/fa2-attraction.wgsl.ts +6 -1
  90. package/src/wgsl/fa2-integrate.wgsl.ts +28 -2
  91. package/src/wgsl/fa2-repulsion-exact.wgsl.ts +14 -5
  92. package/src/wgsl/fa2-stats-finalize.wgsl.ts +33 -0
  93. package/dist/chunks/context-CRbw2Wyo.js.map +0 -1
@@ -50,22 +50,30 @@ import {
50
50
  type StateWriter,
51
51
  } from "./force-simulation.js";
52
52
  import { resolveNodeMass, resolveWeights } from "./inputs.js";
53
+ import {
54
+ describeValue,
55
+ FILL_PARAMS_BUFFER,
56
+ FORCE_BYTES_PER_NODE,
57
+ invalid,
58
+ isPositiveInteger,
59
+ type Overrides,
60
+ pickBoolean,
61
+ pickCenter,
62
+ pickDim,
63
+ pickNumber,
64
+ pickSeed,
65
+ scalar,
66
+ seedWord,
67
+ subset,
68
+ vector,
69
+ } from "./model-common.js";
53
70
  import { RepulsionExact, type RepulsionExactOverrides } from "./repulsion-exact.js";
54
71
 
55
72
  // ============================================================ constants and small helpers
56
73
 
57
- /** An override record as the kernel layer takes it. */
58
- type Overrides = Readonly<Record<string, number | boolean>>;
59
-
60
74
  /** The stage names of one iteration in dispatch order plus the per-batch toScene (spec 7.4; contract 3.13). */
61
75
  const FA2_STAGES = ["K1", "K2", "K3", "K4", "K5", "toScene"] as const;
62
76
 
63
- /** Bytes of the stride-3 f32 force arrays per node. */
64
- const FORCE_BYTES_PER_NODE = 12;
65
-
66
- /** The name of the model-owned FillParams buffer (a BufferSpec, reached through ModelResources.buffer). */
67
- const FILL_PARAMS_BUFFER = "fillParams";
68
-
69
77
  /** The one-workgroup dispatch of K1 (spec 7.4). */
70
78
  const ONE_WORKGROUP: DispatchPlan = { x: 1, y: 1, z: 1, items: 1, stride: null };
71
79
 
@@ -75,9 +83,6 @@ const K2_DEFAULTS: Overrides = { LINLOG: false, DISTRIBUTED: false, TIER: 0, USE
75
83
  /** Every override K5 accepts, with its default. */
76
84
  const K5_DEFAULTS: Overrides = { SWING_MODE: 0 };
77
85
 
78
- /** 2^32, the modulus of the u32 seed word (computed with `%`, never a bitwise operator). */
79
- const U32_MODULUS = 4294967296;
80
-
81
86
  /** The resolved record with no option given: FA2_DEFAULTS plus the null / origin defaults of spec 7.14. */
82
87
  const DEFAULT_RESOLVED: ResolvedForceAtlas2Options = Object.freeze<ResolvedForceAtlas2Options>({
83
88
  ...FA2_DEFAULTS,
@@ -88,214 +93,6 @@ const DEFAULT_RESOLVED: ResolvedForceAtlas2Options = Object.freeze<ResolvedForce
88
93
  seed: null,
89
94
  });
90
95
 
91
- /**
92
- * A short, safe rendering of an argument value for error messages (never String() on an object).
93
- * @param value - the value
94
- * @returns the rendering
95
- */
96
- function describeValue(value: unknown): string {
97
- if (value === null) {
98
- return "null";
99
- }
100
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "string") {
101
- return String(value);
102
- }
103
- if (typeof value === "undefined") {
104
- return "undefined";
105
- }
106
- if (typeof value === "object" && "length" in value && typeof value.length === "number") {
107
- return `[${value.length} values]`;
108
- }
109
- return typeof value;
110
- }
111
-
112
- /**
113
- * The E_INVALID_ARGUMENT error of an option check (contract 3.1: { argument, value, expected }).
114
- * @param argument - the option name
115
- * @param value - the value given
116
- * @param expected - what was expected
117
- * @returns the error (not thrown here)
118
- */
119
- function invalid(argument: string, value: unknown, expected: string): WebGpuGraphError {
120
- return new WebGpuGraphError("E_INVALID_ARGUMENT", `${argument} must be ${expected}; got ${describeValue(value)}`, {
121
- argument,
122
- value,
123
- expected,
124
- });
125
- }
126
-
127
- /**
128
- * A numeric option: the given value when defined, else the fallback; validated by `check` (the value is checked as
129
- * `unknown` so a JS caller's string or object is E_INVALID_ARGUMENT too).
130
- * @param name - the option name
131
- * @param given - the value given (undefined = absent)
132
- * @param fallback - the previous record's value or the default
133
- * @param check - the range predicate over a finite number
134
- * @param expected - the range in words (the error message)
135
- * @returns the value
136
- */
137
- function pickNumber(
138
- name: string,
139
- given: number | undefined,
140
- fallback: number,
141
- check: (value: number) => boolean,
142
- expected: string,
143
- ): number {
144
- const value: unknown = given === undefined ? fallback : given;
145
- if (typeof value !== "number" || !Number.isFinite(value) || !check(value)) {
146
- throw invalid(name, value, expected);
147
- }
148
- return value;
149
- }
150
-
151
- /**
152
- * A boolean option: the given value when defined, else the fallback; a non-boolean is E_INVALID_ARGUMENT.
153
- * @param name - the option name
154
- * @param given - the value given (undefined = absent)
155
- * @param fallback - the previous record's value or the default
156
- * @returns the value
157
- */
158
- function pickBoolean(name: string, given: boolean | undefined, fallback: boolean): boolean {
159
- const value: unknown = given === undefined ? fallback : given;
160
- if (typeof value !== "boolean") {
161
- throw invalid(name, value, "a boolean");
162
- }
163
- return value;
164
- }
165
-
166
- /**
167
- * The layout dimension: 2 or 3.
168
- * @param given - the value given (undefined = absent)
169
- * @param fallback - the previous record's value or the default
170
- * @returns 2 or 3
171
- */
172
- function pickDim(given: 2 | 3 | undefined, fallback: 2 | 3): 2 | 3 {
173
- const value: unknown = given === undefined ? fallback : given;
174
- if (value !== 2 && value !== 3) {
175
- throw invalid("dim", value, "2 or 3");
176
- }
177
- return value;
178
- }
179
-
180
- /**
181
- * The scene-unit center: an array-like of 2 (z = 0) or 3 finite numbers.
182
- * @param given - the value given (undefined = absent)
183
- * @param fallback - the previous record's value or the default
184
- * @returns the three components
185
- */
186
- function pickCenter(
187
- given: ArrayLike<number> | undefined,
188
- fallback: readonly [number, number, number],
189
- ): readonly [number, number, number] {
190
- if (given === undefined) {
191
- return fallback;
192
- }
193
- const expected = "an array of 2 or 3 finite numbers";
194
- const value: unknown = given;
195
- if (typeof value !== "object" || value === null || !("length" in value)) {
196
- throw invalid("center", given, expected);
197
- }
198
- const { length } = value;
199
- if (length !== 2 && length !== 3) {
200
- throw invalid("center", given, expected);
201
- }
202
- const x: unknown = given[0];
203
- const y: unknown = given[1];
204
- const z: unknown = length === 3 ? given[2] : 0;
205
- if (
206
- typeof x !== "number" ||
207
- typeof y !== "number" ||
208
- typeof z !== "number" ||
209
- !Number.isFinite(x) ||
210
- !Number.isFinite(y) ||
211
- !Number.isFinite(z)
212
- ) {
213
- throw invalid("center", given, expected);
214
- }
215
- return [x, y, z];
216
- }
217
-
218
- /**
219
- * The seed: a finite number, or null (unseeded; 0 keeps the port's "0 = unseeded" quirk through the Lcg).
220
- * @param given - the value given (undefined = absent)
221
- * @param fallback - the previous record's value or the default
222
- * @returns the seed or null
223
- */
224
- function pickSeed(given: number | null | undefined, fallback: number | null): number | null {
225
- if (given === undefined) {
226
- return fallback;
227
- }
228
- const value: unknown = given;
229
- if (value !== null && (typeof value !== "number" || !Number.isFinite(value))) {
230
- throw invalid("seed", given, "a finite number or null");
231
- }
232
- return value;
233
- }
234
-
235
- /**
236
- * Integer >= 1.
237
- * @param value - a finite number
238
- * @returns whether it is a positive integer
239
- */
240
- function isPositiveInteger(value: number): boolean {
241
- return Number.isInteger(value) && value >= 1;
242
- }
243
-
244
- /**
245
- * The u32 word written into Fa2Params.seed: 0 for null, else floor(|seed|) mod 2^32.
246
- * @param seed - the resolved seed
247
- * @returns the u32 value
248
- */
249
- function seedWord(seed: number | null): number {
250
- if (seed === null) {
251
- return 0;
252
- }
253
- return Math.floor(Math.abs(seed)) % U32_MODULUS;
254
- }
255
-
256
- /**
257
- * A scalar field of a block's read() result.
258
- * @param values - the values read
259
- * @param name - the field name
260
- * @returns the number
261
- */
262
- function scalar(values: UniformValues, name: string): number {
263
- const value = values[name];
264
- if (typeof value !== "number") {
265
- throw invalid(name, value, "a scalar field");
266
- }
267
- return value;
268
- }
269
-
270
- /**
271
- * A vector field of a block's read() result.
272
- * @param values - the values read
273
- * @param name - the field name
274
- * @returns the components
275
- */
276
- function vector(values: UniformValues, name: string): readonly number[] {
277
- const value = values[name];
278
- if (typeof value === "number") {
279
- throw invalid(name, value, "a vector field");
280
- }
281
- return value;
282
- }
283
-
284
- /**
285
- * The override record a kernel gets: its defaults overlaid with the values present in the merged set (contract 3.9:
286
- * a name a spec does not declare is rejected at compose time, so nothing else is passed through).
287
- * @param merged - the merged override set of the model (plus USE_PERM / HAS_WEIGHTS from the simulation)
288
- * @param defaults - the kernel's accepted names with their defaults
289
- * @returns the kernel's override record, every accepted name explicit
290
- */
291
- function subset(merged: Overrides, defaults: Overrides): Overrides {
292
- const out: Record<string, number | boolean> = {};
293
- for (const name of Object.keys(defaults)) {
294
- out[name] = name in merged ? merged[name] : defaults[name];
295
- }
296
- return out;
297
- }
298
-
299
96
  /**
300
97
  * The K3 / K4 override values of a merged set (typed for RepulsionExact).
301
98
  * @param merged - the merged override set