umap-gpu 0.2.14 → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,3 @@
1
- # ⚠️ NOT READY YET | WORK IN PROGRESS ⚠️
2
-
3
1
  # umap-gpu
4
2
 
5
3
  UMAP dimensionality reduction with WebGPU-accelerated SGD and HNSW approximate nearest neighbors.
@@ -9,7 +9,7 @@ export interface CPUSgdParams {
9
9
  * CPU fallback SGD optimizer for environments without WebGPU.
10
10
  * Mirrors the GPU shader logic: per-edge attraction + negative-sample repulsion.
11
11
  */
12
- export declare function cpuSgd(embedding: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: CPUSgdParams, onProgress?: (epoch: number, nEpochs: number) => void): Float32Array;
12
+ export declare function cpuSgd(embedding: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: CPUSgdParams, onProgress?: (epoch: number, nEpochs: number) => void, rng?: () => number): Float32Array;
13
13
  /**
14
14
  * CPU SGD for UMAP.transform(): optimizes only the new-point embeddings.
15
15
  * The training embedding is read-only; attraction pulls new points toward
@@ -26,4 +26,4 @@ export declare function cpuSgd(embedding: Float32Array, graph: FuzzyGraph, epoch
26
26
  * @param nEpochs - Number of optimization epochs
27
27
  * @param params - UMAP curve parameters
28
28
  */
29
- export declare function cpuSgdTransform(embeddingNew: Float32Array, embeddingTrain: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nNew: number, nTrain: number, nComponents: number, nEpochs: number, params: CPUSgdParams, onProgress?: (epoch: number, nEpochs: number) => void): Float32Array;
29
+ export declare function cpuSgdTransform(embeddingNew: Float32Array, embeddingTrain: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nNew: number, nTrain: number, nComponents: number, nEpochs: number, params: CPUSgdParams, onProgress?: (epoch: number, nEpochs: number) => void, rng?: () => number): Float32Array;
package/dist/gpu/sgd.d.ts CHANGED
@@ -37,6 +37,6 @@ export declare class GPUSgd {
37
37
  * @param params - UMAP curve parameters and repulsion settings
38
38
  * @returns Optimized embedding as Float32Array
39
39
  */
40
- optimize(embedding: Float32Array, head: Uint32Array, tail: Uint32Array, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: SGDParams, onProgress?: (epoch: number, nEpochs: number) => void): Promise<Float32Array>;
40
+ optimize(embedding: Float32Array, head: Uint32Array, tail: Uint32Array, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: SGDParams, onProgress?: (epoch: number, nEpochs: number) => void, rng?: () => number): Promise<Float32Array>;
41
41
  private makeBuffer;
42
42
  }
package/dist/index.js CHANGED
@@ -1,93 +1,83 @@
1
- var te = Object.defineProperty;
2
- var ne = (e, t, f) => t in e ? te(e, t, { enumerable: !0, configurable: !0, writable: !0, value: f }) : e[t] = f;
3
- var C = (e, t, f) => ne(e, typeof t != "symbol" ? t + "" : t, f);
4
- import { loadHnswlib as H } from "hnswlib-wasm";
5
- async function se(e, t, f = {}) {
6
- const { M: a = 16, efConstruction: s = 200, efSearch: u = 50 } = f, c = await H(), d = e[0].length, i = e.length, n = new c.HierarchicalNSW("l2", d, "");
7
- n.initIndex(i, a, s, 200), n.setEfSearch(Math.max(u, t)), n.addItems(e, !1);
8
- const r = [], o = [];
9
- for (let h = 0; h < i; h++) {
10
- const l = n.searchKnn(e[h], t + 1, void 0), p = l.neighbors.map((g, _) => ({ idx: g, dist: l.distances[_] })).filter(({ idx: g }) => g !== h).slice(0, t);
11
- r.push(p.map(({ idx: g }) => g)), o.push(p.map(({ dist: g }) => Math.sqrt(g)));
1
+ var X = Object.defineProperty;
2
+ var Z = (t, e, s) => e in t ? X(t, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : t[e] = s;
3
+ var G = (t, e, s) => Z(t, typeof e != "symbol" ? e + "" : e, s);
4
+ import { loadHnswlib as $ } from "hnswlib-wasm";
5
+ async function ee(t, e, s = {}) {
6
+ const { M: r = 16, efConstruction: n = 200, efSearch: u = 50 } = s, o = await $(), f = t[0].length, c = t.length, a = new o.HierarchicalNSW("l2", f, "");
7
+ a.initIndex(c, r, n, 200), a.setEfSearch(Math.max(u, e)), a.addItems(t, !1);
8
+ const d = [], i = [];
9
+ for (let l = 0; l < c; l++) {
10
+ const g = a.searchKnn(t[l], e + 1, void 0), _ = g.neighbors.map((w, x) => ({ idx: w, dist: g.distances[x] })).filter(({ idx: w }) => w !== l).slice(0, e);
11
+ d.push(_.map(({ idx: w }) => w)), i.push(_.map(({ dist: w }) => Math.sqrt(w)));
12
12
  }
13
- return { indices: r, distances: o };
14
- }
15
- async function ae(e, t, f = {}) {
16
- const { M: a = 16, efConstruction: s = 200, efSearch: u = 50 } = f, c = await H(), d = e[0].length, i = e.length, n = new c.HierarchicalNSW("l2", d, "");
17
- n.initIndex(i, a, s, 200), n.setEfSearch(Math.max(u, t)), n.addItems(e, !1);
18
- const r = [], o = [];
19
- for (let l = 0; l < i; l++) {
20
- const p = n.searchKnn(e[l], t + 1, void 0), g = p.neighbors.map((_, m) => ({ idx: _, dist: p.distances[m] })).filter(({ idx: _ }) => _ !== l).slice(0, t);
21
- r.push(g.map(({ idx: _ }) => _)), o.push(g.map(({ dist: _ }) => Math.sqrt(_)));
22
- }
23
- return { knn: { indices: r, distances: o }, index: {
24
- searchKnn(l, p) {
25
- const g = [], _ = [];
26
- for (const m of l) {
27
- const y = n.searchKnn(m, p, void 0), b = y.neighbors.map((x, v) => ({ idx: x, dist: y.distances[v] })).sort((x, v) => x.dist - v.dist).slice(0, p);
28
- g.push(b.map(({ idx: x }) => x)), _.push(b.map(({ dist: x }) => Math.sqrt(x)));
13
+ return { knn: { indices: d, distances: i }, index: {
14
+ searchKnn(l, g) {
15
+ const _ = [], w = [];
16
+ for (const x of l) {
17
+ const m = a.searchKnn(x, g, void 0), F = m.neighbors.map((p, M) => ({ idx: p, dist: m.distances[M] })).sort((p, M) => p.dist - M.dist).slice(0, g);
18
+ _.push(F.map(({ idx: p }) => p)), w.push(F.map(({ dist: p }) => Math.sqrt(p)));
29
19
  }
30
- return { indices: g, distances: _ };
20
+ return { indices: _, distances: w };
31
21
  }
32
22
  } };
33
23
  }
34
- function V(e, t, f, a = 1) {
35
- const s = e.length, { sigmas: u, rhos: c } = Y(t, f), d = [], i = [], n = [];
36
- for (let o = 0; o < s; o++)
37
- for (let h = 0; h < e[o].length; h++) {
38
- const l = t[o][h], p = l <= c[o] ? 1 : Math.exp(-((l - c[o]) / u[o]));
39
- d.push(o), i.push(e[o][h]), n.push(p);
24
+ function te(t, e, s, r = 1) {
25
+ const n = t.length, { sigmas: u, rhos: o } = H(e, s), f = [], c = [], a = [];
26
+ for (let i = 0; i < n; i++)
27
+ for (let h = 0; h < t[i].length; h++) {
28
+ const l = e[i][h], g = l <= o[i] ? 1 : Math.exp(-((l - o[i]) / u[i]));
29
+ f.push(i), c.push(t[i][h]), a.push(g);
40
30
  }
41
- return { ...oe(d, i, n, s, a), nVertices: s };
31
+ return { ...se(f, c, a, n, r), nVertices: n };
42
32
  }
43
- function re(e, t, f) {
44
- const a = e.length, { sigmas: s, rhos: u } = Y(t, f), c = [], d = [], i = [];
45
- for (let n = 0; n < a; n++)
46
- for (let r = 0; r < e[n].length; r++) {
47
- const o = t[n][r], h = o <= u[n] ? 1 : Math.exp(-((o - u[n]) / s[n]));
48
- c.push(n), d.push(e[n][r]), i.push(h);
33
+ function ne(t, e, s) {
34
+ const r = t.length, { sigmas: n, rhos: u } = H(e, s), o = [], f = [], c = [];
35
+ for (let a = 0; a < r; a++)
36
+ for (let d = 0; d < t[a].length; d++) {
37
+ const i = e[a][d], h = i <= u[a] ? 1 : Math.exp(-((i - u[a]) / n[a]));
38
+ o.push(a), f.push(t[a][d]), c.push(h);
49
39
  }
50
40
  return {
51
- rows: new Uint32Array(c),
52
- cols: new Uint32Array(d),
53
- vals: new Float32Array(i),
54
- nVertices: a
41
+ rows: new Uint32Array(o),
42
+ cols: new Uint32Array(f),
43
+ vals: new Float32Array(c),
44
+ nVertices: r
55
45
  };
56
46
  }
57
- function Y(e, t) {
58
- const a = e.length, s = new Float32Array(a), u = new Float32Array(a);
59
- for (let c = 0; c < a; c++) {
60
- const d = e[c];
61
- u[c] = d.find((h) => h > 0) ?? 0;
62
- let i = 0, n = 1 / 0, r = 1;
63
- const o = Math.log2(t);
47
+ function H(t, e) {
48
+ const r = t.length, n = new Float32Array(r), u = new Float32Array(r);
49
+ for (let o = 0; o < r; o++) {
50
+ const f = t[o];
51
+ u[o] = f.find((h) => h > 0) ?? 0;
52
+ let c = 0, a = 1 / 0, d = 1;
53
+ const i = Math.log2(e);
64
54
  for (let h = 0; h < 64; h++) {
65
55
  let l = 0;
66
- for (let p = 0; p < d.length; p++)
67
- l += Math.exp(-Math.max(0, d[p] - u[c]) / r);
68
- if (Math.abs(l - o) < 1e-5) break;
69
- l > o ? (n = r, r = (i + n) / 2) : (i = r, r = n === 1 / 0 ? r * 2 : (i + n) / 2);
56
+ for (let g = 0; g < f.length; g++)
57
+ l += Math.exp(-Math.max(0, f[g] - u[o]) / d);
58
+ if (Math.abs(l - i) < 1e-5) break;
59
+ l > i ? (a = d, d = (c + a) / 2) : (c = d, d = a === 1 / 0 ? d * 2 : (c + a) / 2);
70
60
  }
71
- s[c] = r;
61
+ n[o] = d;
72
62
  }
73
- return { sigmas: s, rhos: u };
63
+ return { sigmas: n, rhos: u };
74
64
  }
75
- function oe(e, t, f, a, s) {
65
+ function se(t, e, s, r, n) {
76
66
  const u = /* @__PURE__ */ new Map();
77
- for (let n = 0; n < e.length; n++)
78
- u.set(e[n] * a + t[n], f[n]);
79
- const c = [], d = [], i = [];
80
- for (const [n, r] of u) {
81
- const o = Math.floor(n / a), h = n % a, l = u.get(h * a + o) ?? 0, p = r + l - r * l, g = r * l;
82
- c.push(o), d.push(h), i.push(s * p + (1 - s) * g);
67
+ for (let a = 0; a < t.length; a++)
68
+ u.set(t[a] * r + e[a], s[a]);
69
+ const o = [], f = [], c = [];
70
+ for (const [a, d] of u) {
71
+ const i = Math.floor(a / r), h = a % r, l = u.get(h * r + i) ?? 0, g = d + l - d * l, _ = d * l;
72
+ o.push(i), f.push(h), c.push(n * g + (1 - n) * _);
83
73
  }
84
74
  return {
85
- rows: new Uint32Array(c),
86
- cols: new Uint32Array(d),
87
- vals: new Float32Array(i)
75
+ rows: new Uint32Array(o),
76
+ cols: new Uint32Array(f),
77
+ vals: new Float32Array(c)
88
78
  };
89
79
  }
90
- const ie = `// UMAP SGD compute shader — processes one graph edge per GPU thread.
80
+ const ae = `// UMAP SGD compute shader — processes one graph edge per GPU thread.
91
81
  // Computes attraction and repulsion forces and accumulates them atomically
92
82
  // into a forces buffer. A separate apply-forces pass then updates embeddings,
93
83
  // eliminating write-write races on shared vertex positions.
@@ -181,7 +171,10 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
181
171
  epoch_of_next_negative_sample[edge_idx] += f32(n_neg) * epochs_per_neg;
182
172
  }
183
173
 
184
- var rng = xorshift(rng_seeds[edge_idx] + params.current_epoch * 6364136223u);
174
+ // 2654435761u is the 32-bit golden-ratio hash constant (0x9E3779B1),
175
+ // which fits in u32 unlike the 64-bit LCG value 6364136223 used originally.
176
+ // Bug 14 fix: the original constant exceeded u32 range and failed WGSL validation.
177
+ var rng = xorshift(rng_seeds[edge_idx] + params.current_epoch * 2654435761u);
185
178
 
186
179
  for (var s = 0u; s < n_neg; s++) {
187
180
  rng = xorshift(rng);
@@ -204,7 +197,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
204
197
  }
205
198
  }
206
199
  }
207
- `, ce = `// Apply-forces shader — second pass of the two-pass GPU SGD.
200
+ `, re = `// Apply-forces shader — second pass of the two-pass GPU SGD.
208
201
  //
209
202
  // After the SGD pass has atomically accumulated all gradients into the forces
210
203
  // buffer, this shader applies each element's accumulated force to the
@@ -233,40 +226,40 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
233
226
  }
234
227
  `;
235
228
  let z = null;
236
- async function Q() {
229
+ async function Y() {
237
230
  if (z) return z;
238
231
  if (typeof navigator > "u" || !navigator.gpu)
239
232
  return null;
240
- const e = await navigator.gpu.requestAdapter();
241
- return e ? (z = await e.requestDevice(), z.lost.then(() => {
233
+ const t = await navigator.gpu.requestAdapter();
234
+ return t ? (z = await t.requestDevice(), z.lost.then(() => {
242
235
  z = null;
243
236
  }), z) : null;
244
237
  }
245
- function J() {
238
+ function ie() {
246
239
  return typeof navigator < "u" && !!navigator.gpu;
247
240
  }
248
- async function he() {
249
- return await Q() !== null;
241
+ async function pe() {
242
+ return await Y() !== null;
250
243
  }
251
- class X {
244
+ class oe {
252
245
  constructor() {
253
- C(this, "device");
254
- C(this, "sgdPipeline");
255
- C(this, "applyForcesPipeline");
246
+ G(this, "device");
247
+ G(this, "sgdPipeline");
248
+ G(this, "applyForcesPipeline");
256
249
  }
257
250
  async init() {
258
- const t = await Q();
259
- if (!t) throw new Error("WebGPU not supported");
260
- this.device = t, this.sgdPipeline = this.device.createComputePipeline({
251
+ const e = await Y();
252
+ if (!e) throw new Error("WebGPU not supported");
253
+ this.device = e, this.sgdPipeline = this.device.createComputePipeline({
261
254
  layout: "auto",
262
255
  compute: {
263
- module: this.device.createShaderModule({ code: ie }),
256
+ module: this.device.createShaderModule({ code: ae }),
264
257
  entryPoint: "main"
265
258
  }
266
259
  }), this.applyForcesPipeline = this.device.createComputePipeline({
267
260
  layout: "auto",
268
261
  compute: {
269
- module: this.device.createShaderModule({ code: ce }),
262
+ module: this.device.createShaderModule({ code: re }),
270
263
  entryPoint: "main"
271
264
  }
272
265
  });
@@ -284,254 +277,225 @@ class X {
284
277
  * @param params - UMAP curve parameters and repulsion settings
285
278
  * @returns Optimized embedding as Float32Array
286
279
  */
287
- async optimize(t, f, a, s, u, c, d, i, n) {
288
- const { device: r } = this, o = f.length, h = u * c, l = this.makeBuffer(
289
- t,
280
+ async optimize(e, s, r, n, u, o, f, c, a, d = Math.random) {
281
+ const { device: i } = this, h = s.length, l = u * o, g = this.makeBuffer(
282
+ e,
290
283
  GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
291
- ), p = this.makeBuffer(f, GPUBufferUsage.STORAGE), g = this.makeBuffer(a, GPUBufferUsage.STORAGE), _ = this.makeBuffer(s, GPUBufferUsage.STORAGE), m = new Float32Array(s), y = this.makeBuffer(m, GPUBufferUsage.STORAGE), b = new Float32Array(o);
292
- for (let A = 0; A < o; A++)
293
- b[A] = s[A] / i.negativeSampleRate;
294
- const x = this.makeBuffer(b, GPUBufferUsage.STORAGE), v = new Uint32Array(o);
295
- for (let A = 0; A < o; A++)
296
- v[A] = Math.random() * 4294967295 | 0;
297
- const P = this.makeBuffer(v, GPUBufferUsage.STORAGE), G = r.createBuffer({
298
- size: h * 4,
284
+ ), _ = this.makeBuffer(s, GPUBufferUsage.STORAGE), w = this.makeBuffer(r, GPUBufferUsage.STORAGE), x = this.makeBuffer(n, GPUBufferUsage.STORAGE), m = new Float32Array(n), F = this.makeBuffer(m, GPUBufferUsage.STORAGE), p = new Float32Array(h);
285
+ for (let y = 0; y < h; y++)
286
+ p[y] = n[y] / c.negativeSampleRate;
287
+ const M = this.makeBuffer(p, GPUBufferUsage.STORAGE), B = new Uint32Array(h);
288
+ for (let y = 0; y < h; y++)
289
+ B[y] = d() * 4294967295 | 0;
290
+ const S = this.makeBuffer(B, GPUBufferUsage.STORAGE), v = i.createBuffer({
291
+ size: l * 4,
299
292
  usage: GPUBufferUsage.STORAGE,
300
293
  mappedAtCreation: !0
301
294
  });
302
- new Int32Array(G.getMappedRange()).fill(0), G.unmap();
303
- const U = r.createBuffer({
295
+ new Int32Array(v.getMappedRange()).fill(0), v.unmap();
296
+ const R = i.createBuffer({
304
297
  size: 40,
305
298
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
306
- }), F = r.createBuffer({
299
+ }), O = i.createBuffer({
307
300
  size: 16,
308
301
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
309
- }), N = r.createBindGroup({
302
+ }), C = i.createBindGroup({
310
303
  layout: this.sgdPipeline.getBindGroupLayout(0),
311
304
  entries: [
312
- { binding: 0, resource: { buffer: _ } },
313
- { binding: 1, resource: { buffer: p } },
314
- { binding: 2, resource: { buffer: g } },
315
- { binding: 3, resource: { buffer: l } },
316
- { binding: 4, resource: { buffer: y } },
317
- { binding: 5, resource: { buffer: x } },
318
- { binding: 6, resource: { buffer: U } },
319
- { binding: 7, resource: { buffer: P } },
320
- { binding: 8, resource: { buffer: G } }
305
+ { binding: 0, resource: { buffer: x } },
306
+ { binding: 1, resource: { buffer: _ } },
307
+ { binding: 2, resource: { buffer: w } },
308
+ { binding: 3, resource: { buffer: g } },
309
+ { binding: 4, resource: { buffer: F } },
310
+ { binding: 5, resource: { buffer: M } },
311
+ { binding: 6, resource: { buffer: R } },
312
+ { binding: 7, resource: { buffer: S } },
313
+ { binding: 8, resource: { buffer: v } }
321
314
  ]
322
- }), M = r.createBindGroup({
315
+ }), U = i.createBindGroup({
323
316
  layout: this.applyForcesPipeline.getBindGroupLayout(0),
324
317
  entries: [
325
- { binding: 0, resource: { buffer: l } },
326
- { binding: 1, resource: { buffer: G } },
327
- { binding: 2, resource: { buffer: F } }
318
+ { binding: 0, resource: { buffer: g } },
319
+ { binding: 1, resource: { buffer: v } },
320
+ { binding: 2, resource: { buffer: O } }
328
321
  ]
329
322
  });
330
- for (let A = 0; A < d; A++) {
331
- const B = 1 - A / d, O = new ArrayBuffer(40), S = new Uint32Array(O), k = new Float32Array(O);
332
- S[0] = o, S[1] = u, S[2] = c, S[3] = A, S[4] = d, k[5] = B, k[6] = i.a, k[7] = i.b, k[8] = i.gamma, S[9] = i.negativeSampleRate, r.queue.writeBuffer(U, 0, O);
333
- const D = new ArrayBuffer(16), $ = new Uint32Array(D), ee = new Float32Array(D);
334
- $[0] = h, ee[1] = B, r.queue.writeBuffer(F, 0, D);
335
- const T = r.createCommandEncoder(), q = T.beginComputePass();
336
- q.setPipeline(this.sgdPipeline), q.setBindGroup(0, N), q.dispatchWorkgroups(Math.ceil(o / 256)), q.end();
337
- const I = T.beginComputePass();
338
- I.setPipeline(this.applyForcesPipeline), I.setBindGroup(0, M), I.dispatchWorkgroups(Math.ceil(h / 256)), I.end(), r.queue.submit([T.finish()]), A % 10 === 0 && (await r.queue.onSubmittedWorkDone(), n == null || n(A, d));
323
+ for (let y = 0; y < f; y++) {
324
+ const N = 1 - y / f, k = new ArrayBuffer(40), P = new Uint32Array(k), q = new Float32Array(k);
325
+ P[0] = h, P[1] = u, P[2] = o, P[3] = y, P[4] = f, q[5] = N, q[6] = c.a, q[7] = c.b, q[8] = c.gamma, P[9] = c.negativeSampleRate, i.queue.writeBuffer(R, 0, k);
326
+ const I = new ArrayBuffer(16), Q = new Uint32Array(I), J = new Float32Array(I);
327
+ Q[0] = l, J[1] = N, i.queue.writeBuffer(O, 0, I);
328
+ const D = i.createCommandEncoder(), L = D.beginComputePass();
329
+ L.setPipeline(this.sgdPipeline), L.setBindGroup(0, C), L.dispatchWorkgroups(Math.ceil(h / 256)), L.end();
330
+ const j = D.beginComputePass();
331
+ j.setPipeline(this.applyForcesPipeline), j.setBindGroup(0, U), j.dispatchWorkgroups(Math.ceil(l / 256)), j.end(), i.queue.submit([D.finish()]), y % 10 === 0 && (await i.queue.onSubmittedWorkDone(), a == null || a(y, f));
339
332
  }
340
- const E = r.createBuffer({
341
- size: t.byteLength,
333
+ const A = i.createBuffer({
334
+ size: e.byteLength,
342
335
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
343
- }), w = r.createCommandEncoder();
344
- w.copyBufferToBuffer(l, 0, E, 0, t.byteLength), r.queue.submit([w.finish()]), await E.mapAsync(GPUMapMode.READ);
345
- const R = new Float32Array(E.getMappedRange().slice(0));
346
- return E.unmap(), l.destroy(), p.destroy(), g.destroy(), _.destroy(), y.destroy(), x.destroy(), P.destroy(), G.destroy(), U.destroy(), F.destroy(), E.destroy(), R;
336
+ }), b = i.createCommandEncoder();
337
+ b.copyBufferToBuffer(g, 0, A, 0, e.byteLength), i.queue.submit([b.finish()]), await A.mapAsync(GPUMapMode.READ);
338
+ const E = new Float32Array(A.getMappedRange().slice(0));
339
+ return A.unmap(), g.destroy(), _.destroy(), w.destroy(), x.destroy(), F.destroy(), M.destroy(), S.destroy(), v.destroy(), R.destroy(), O.destroy(), A.destroy(), E;
347
340
  }
348
- makeBuffer(t, f) {
349
- const a = this.device.createBuffer({
350
- size: t.byteLength,
351
- usage: f,
341
+ makeBuffer(e, s) {
342
+ const r = this.device.createBuffer({
343
+ size: e.byteLength,
344
+ usage: s,
352
345
  mappedAtCreation: !0
353
346
  });
354
- return t instanceof Float32Array ? new Float32Array(a.getMappedRange()).set(t) : new Uint32Array(a.getMappedRange()).set(t), a.unmap(), a;
347
+ return e instanceof Float32Array ? new Float32Array(r.getMappedRange()).set(e) : new Uint32Array(r.getMappedRange()).set(e), r.unmap(), r;
355
348
  }
356
349
  }
357
- function j(e) {
358
- return Math.max(-4, Math.min(4, e));
350
+ function T(t) {
351
+ return Math.max(-4, Math.min(4, t));
359
352
  }
360
- function L(e, t, f, a, s, u, c, d) {
361
- const { a: i, b: n, gamma: r = 1, negativeSampleRate: o = 5 } = c, h = t.rows.length, l = new Uint32Array(t.rows), p = new Uint32Array(t.cols), g = new Float32Array(f), _ = new Float32Array(h);
362
- for (let m = 0; m < h; m++)
363
- _[m] = f[m] / o;
353
+ function W(t, e, s, r, n, u, o, f, c = Math.random) {
354
+ const { a, b: d, gamma: i = 1, negativeSampleRate: h = 5 } = o, l = e.rows.length, g = new Uint32Array(e.rows), _ = new Uint32Array(e.cols), w = new Float32Array(s), x = new Float32Array(l);
355
+ for (let m = 0; m < l; m++)
356
+ x[m] = s[m] / h;
364
357
  for (let m = 0; m < u; m++) {
365
- d == null || d(m, u);
366
- const y = 1 - m / u;
367
- for (let b = 0; b < h; b++) {
368
- if (g[b] > m) continue;
369
- const x = l[b], v = p[b];
370
- let P = 0;
371
- for (let M = 0; M < s; M++) {
372
- const E = e[x * s + M] - e[v * s + M];
373
- P += E * E;
358
+ f == null || f(m, u);
359
+ const F = 1 - m / u;
360
+ for (let p = 0; p < l; p++) {
361
+ if (w[p] > m) continue;
362
+ const M = g[p], B = _[p];
363
+ let S = 0;
364
+ for (let U = 0; U < n; U++) {
365
+ const A = t[M * n + U] - t[B * n + U];
366
+ S += A * A;
374
367
  }
375
- const G = Math.pow(P, n), U = -2 * i * n * (P > 0 ? G / P : 0) / (i * G + 1);
376
- for (let M = 0; M < s; M++) {
377
- const E = e[x * s + M] - e[v * s + M], w = j(U * E);
378
- e[x * s + M] += y * w, e[v * s + M] -= y * w;
368
+ const v = Math.pow(S, d), R = -2 * a * d * (S > 0 ? v / S : 0) / (a * v + 1);
369
+ for (let U = 0; U < n; U++) {
370
+ const A = t[M * n + U] - t[B * n + U], b = T(R * A);
371
+ t[M * n + U] += F * b, t[B * n + U] -= F * b;
379
372
  }
380
- g[b] += f[b];
381
- const F = f[b] / o, N = Math.max(0, Math.floor(
382
- (m - _[b]) / F
373
+ w[p] += s[p];
374
+ const O = s[p] / h, C = Math.max(0, Math.floor(
375
+ (m - x[p]) / O
383
376
  ));
384
- _[b] += N * F;
385
- for (let M = 0; M < N; M++) {
386
- const E = Math.floor(Math.random() * a);
387
- if (E === x) continue;
388
- let w = 0;
389
- for (let B = 0; B < s; B++) {
390
- const O = e[x * s + B] - e[E * s + B];
391
- w += O * O;
377
+ x[p] += C * O;
378
+ for (let U = 0; U < C; U++) {
379
+ const A = Math.floor(c() * r);
380
+ if (A === M) continue;
381
+ let b = 0;
382
+ for (let N = 0; N < n; N++) {
383
+ const k = t[M * n + N] - t[A * n + N];
384
+ b += k * k;
392
385
  }
393
- const R = Math.pow(w, n), A = 2 * r * n / ((1e-3 + w) * (i * R + 1));
394
- for (let B = 0; B < s; B++) {
395
- const O = e[x * s + B] - e[E * s + B], S = j(A * O);
396
- e[x * s + B] += y * S;
386
+ const E = Math.pow(b, d), y = 2 * i * d / ((1e-3 + b) * (a * E + 1));
387
+ for (let N = 0; N < n; N++) {
388
+ const k = t[M * n + N] - t[A * n + N], P = T(y * k);
389
+ t[M * n + N] += F * P;
397
390
  }
398
391
  }
399
392
  }
400
393
  }
401
- return e;
394
+ return t;
402
395
  }
403
- function fe(e, t, f, a, s, u, c, d, i, n) {
404
- const { a: r, b: o, gamma: h = 1, negativeSampleRate: l = 5 } = i, p = f.rows.length, g = new Uint32Array(f.rows), _ = new Uint32Array(f.cols), m = new Float32Array(a), y = new Float32Array(p);
405
- for (let b = 0; b < p; b++)
406
- y[b] = a[b] / l;
407
- for (let b = 0; b < d; b++) {
408
- const x = 1 - b / d;
409
- for (let v = 0; v < p; v++) {
410
- if (m[v] > b) continue;
411
- const P = g[v], G = _[v];
412
- let U = 0;
413
- for (let w = 0; w < c; w++) {
414
- const R = e[P * c + w] - t[G * c + w];
415
- U += R * R;
396
+ function ce(t, e, s, r, n, u, o, f, c, a, d = Math.random) {
397
+ const { a: i, b: h, gamma: l = 1, negativeSampleRate: g = 5 } = c, _ = s.rows.length, w = new Uint32Array(s.rows), x = new Uint32Array(s.cols), m = new Float32Array(r), F = new Float32Array(_);
398
+ for (let p = 0; p < _; p++)
399
+ F[p] = r[p] / g;
400
+ for (let p = 0; p < f; p++) {
401
+ const M = 1 - p / f;
402
+ for (let B = 0; B < _; B++) {
403
+ if (m[B] > p) continue;
404
+ const S = w[B], v = x[B];
405
+ let R = 0;
406
+ for (let b = 0; b < o; b++) {
407
+ const E = t[S * o + b] - e[v * o + b];
408
+ R += E * E;
416
409
  }
417
- const F = Math.pow(U, o), N = -2 * r * o * (U > 0 ? F / U : 0) / (r * F + 1);
418
- for (let w = 0; w < c; w++) {
419
- const R = e[P * c + w] - t[G * c + w];
420
- e[P * c + w] += x * j(N * R);
410
+ const O = Math.pow(R, h), C = -2 * i * h * (R > 0 ? O / R : 0) / (i * O + 1);
411
+ for (let b = 0; b < o; b++) {
412
+ const E = t[S * o + b] - e[v * o + b];
413
+ t[S * o + b] += M * T(C * E);
421
414
  }
422
- m[v] += a[v];
423
- const M = a[v] / l, E = Math.max(0, Math.floor(
424
- (b - y[v]) / M
415
+ m[B] += r[B];
416
+ const U = r[B] / g, A = Math.max(0, Math.floor(
417
+ (p - F[B]) / U
425
418
  ));
426
- y[v] += E * M;
427
- for (let w = 0; w < E; w++) {
428
- const R = Math.floor(Math.random() * u);
429
- if (R === G) continue;
430
- let A = 0;
431
- for (let S = 0; S < c; S++) {
432
- const k = e[P * c + S] - t[R * c + S];
433
- A += k * k;
419
+ F[B] += A * U;
420
+ for (let b = 0; b < A; b++) {
421
+ const E = Math.floor(d() * u);
422
+ if (E === v) continue;
423
+ let y = 0;
424
+ for (let P = 0; P < o; P++) {
425
+ const q = t[S * o + P] - e[E * o + P];
426
+ y += q * q;
434
427
  }
435
- const B = Math.pow(A, o), O = 2 * h * o / ((1e-3 + A) * (r * B + 1));
436
- for (let S = 0; S < c; S++) {
437
- const k = e[P * c + S] - t[R * c + S];
438
- e[P * c + S] += x * j(O * k);
428
+ const N = Math.pow(y, h), k = 2 * l * h / ((1e-3 + y) * (i * N + 1));
429
+ for (let P = 0; P < o; P++) {
430
+ const q = t[S * o + P] - e[E * o + P];
431
+ t[S * o + P] += M * T(k * q);
439
432
  }
440
433
  }
441
434
  }
442
435
  }
443
- return e;
436
+ return t;
444
437
  }
445
- async function pe(e, t = {}, f) {
446
- const {
447
- nComponents: a = 2,
448
- nNeighbors: s = 15,
449
- minDist: u = 0.1,
450
- spread: c = 1,
451
- hnsw: d = {}
452
- } = t, i = t.nEpochs ?? (e.length > 1e4 ? 200 : 500);
453
- console.time("knn");
454
- const { indices: n, distances: r } = await se(e, s, {
455
- M: d.M ?? 16,
456
- efConstruction: d.efConstruction ?? 200,
457
- efSearch: d.efSearch ?? 50
458
- });
459
- console.timeEnd("knn"), console.time("fuzzy-set");
460
- const o = V(n, r, s);
461
- console.timeEnd("fuzzy-set");
462
- const { a: h, b: l } = Z(u, c), p = W(o.vals), g = e.length, _ = new Float32Array(g * a);
463
- for (let y = 0; y < _.length; y++)
464
- _[y] = Math.random() * 20 - 10;
465
- console.time("sgd");
466
- let m;
467
- if (J())
468
- try {
469
- const y = new X();
470
- await y.init(), m = await y.optimize(
471
- _,
472
- new Uint32Array(o.rows),
473
- new Uint32Array(o.cols),
474
- p,
475
- g,
476
- a,
477
- i,
478
- { a: h, b: l, gamma: 1, negativeSampleRate: 5 },
479
- f
480
- );
481
- } catch (y) {
482
- console.warn("WebGPU SGD failed, falling back to CPU:", y), m = L(_, o, p, g, a, i, { a: h, b: l }, f);
483
- }
484
- else
485
- m = L(_, o, p, g, a, i, { a: h, b: l }, f);
486
- return console.timeEnd("sgd"), m;
438
+ function fe(t) {
439
+ if (t === void 0) return Math.random;
440
+ let e = t | 0;
441
+ return () => {
442
+ e = e + 1831565813 | 0;
443
+ let s = Math.imul(e ^ e >>> 15, 1 | e);
444
+ return s = s + Math.imul(s ^ s >>> 7, 61 | s) ^ s, ((s ^ s >>> 14) >>> 0) / 4294967296;
445
+ };
487
446
  }
488
- function Z(e, t) {
489
- return Math.abs(t - 1) < 1e-6 && Math.abs(e - 0.1) < 1e-6 ? { a: 1.9292, b: 0.7915 } : Math.abs(t - 1) < 1e-6 && Math.abs(e - 0) < 1e-6 ? { a: 1.8956, b: 0.8006 } : Math.abs(t - 1) < 1e-6 && Math.abs(e - 0.5) < 1e-6 ? { a: 1.5769, b: 0.8951 } : de(e, t);
447
+ async function _e(t, e = {}, s) {
448
+ return new ue(e).fit_transform(t, s);
490
449
  }
491
- function de(e, t) {
492
- const a = [], s = [];
493
- for (let i = 0; i < 299; i++) {
494
- const n = (i + 1) / 299 * t * 3;
495
- a.push(n), s.push(n < e ? 1 : Math.exp(-(n - e) / t));
450
+ function de(t, e) {
451
+ return Math.abs(e - 1) < 1e-6 && Math.abs(t - 0.1) < 1e-6 ? { a: 1.9292, b: 0.7915 } : Math.abs(e - 1) < 1e-6 && Math.abs(t - 0) < 1e-6 ? { a: 1.8956, b: 0.8006 } : Math.abs(e - 1) < 1e-6 && Math.abs(t - 0.5) < 1e-6 ? { a: 1.5769, b: 0.8951 } : le(t, e);
452
+ }
453
+ function le(t, e) {
454
+ const r = [], n = [];
455
+ for (let c = 0; c < 299; c++) {
456
+ const a = (c + 1) / 299 * e * 3;
457
+ r.push(a), n.push(a < t ? 1 : Math.exp(-(a - t) / e));
496
458
  }
497
- let u = 1, c = 1, d = 1e-3;
498
- for (let i = 0; i < 500; i++) {
499
- let n = 0, r = 0, o = 0, h = 0, l = 0, p = 0;
500
- for (let U = 0; U < 299; U++) {
501
- const F = a[U], N = Math.pow(F, 2 * c), M = 1 + u * N, w = 1 / M - s[U];
502
- p += w * w;
503
- const R = M * M, A = -N / R, B = F > 0 ? -2 * Math.log(F) * u * N / R : 0;
504
- n += A * w, r += B * w, o += A * A, h += B * B, l += A * B;
459
+ let u = 1, o = 1, f = 1e-3;
460
+ for (let c = 0; c < 500; c++) {
461
+ let a = 0, d = 0, i = 0, h = 0, l = 0, g = 0;
462
+ for (let v = 0; v < 299; v++) {
463
+ const R = r[v], O = Math.pow(R, 2 * o), C = 1 + u * O, A = 1 / C - n[v];
464
+ g += A * A;
465
+ const b = C * C, E = -O / b, y = R > 0 ? -2 * Math.log(R) * u * O / b : 0;
466
+ a += E * A, d += y * A, i += E * E, h += y * y, l += E * y;
505
467
  }
506
- const g = o + d, _ = h + d, m = l, y = g * _ - m * m;
507
- if (Math.abs(y) < 1e-20) break;
508
- const b = -(_ * n - m * r) / y, x = -(g * r - m * n) / y, v = Math.max(1e-4, u + b), P = Math.max(1e-4, c + x);
509
- let G = 0;
510
- for (let U = 0; U < 299; U++) {
511
- const F = Math.pow(a[U], 2 * P), N = 1 / (1 + v * F) - s[U];
512
- G += N * N;
468
+ const _ = i + f, w = h + f, x = l, m = _ * w - x * x;
469
+ if (Math.abs(m) < 1e-20) break;
470
+ const F = -(w * a - x * d) / m, p = -(_ * d - x * a) / m, M = Math.max(1e-4, u + F), B = Math.max(1e-4, o + p);
471
+ let S = 0;
472
+ for (let v = 0; v < 299; v++) {
473
+ const R = Math.pow(r[v], 2 * B), O = 1 / (1 + M * R) - n[v];
474
+ S += O * O;
513
475
  }
514
- if (G < p ? (u = v, c = P, d = Math.max(1e-10, d / 10)) : d = Math.min(1e10, d * 10), Math.abs(b) < 1e-8 && Math.abs(x) < 1e-8) break;
476
+ if (S < g ? (u = M, o = B, f = Math.max(1e-10, f / 10)) : f = Math.min(1e10, f * 10), Math.abs(F) < 1e-8 && Math.abs(p) < 1e-8) break;
515
477
  }
516
- return { a: u, b: c };
478
+ return { a: u, b: o };
517
479
  }
518
- class ge {
519
- constructor(t = {}) {
520
- C(this, "_nComponents");
521
- C(this, "_nNeighbors");
522
- C(this, "_minDist");
523
- C(this, "_spread");
524
- C(this, "_nEpochs");
525
- C(this, "_hnswOpts");
526
- C(this, "_a");
527
- C(this, "_b");
480
+ class ue {
481
+ constructor(e = {}) {
482
+ G(this, "_nComponents");
483
+ G(this, "_nNeighbors");
484
+ G(this, "_minDist");
485
+ G(this, "_spread");
486
+ G(this, "_nEpochs");
487
+ G(this, "_hnswOpts");
488
+ G(this, "_a");
489
+ G(this, "_b");
490
+ G(this, "_debug");
491
+ G(this, "_rng");
528
492
  /** The low-dimensional embedding produced by the last fit() call. */
529
- C(this, "embedding", null);
530
- C(this, "_hnswIndex", null);
531
- C(this, "_nTrain", 0);
532
- this._nComponents = t.nComponents ?? 2, this._nNeighbors = t.nNeighbors ?? 15, this._minDist = t.minDist ?? 0.1, this._spread = t.spread ?? 1, this._nEpochs = t.nEpochs, this._hnswOpts = t.hnsw ?? {};
533
- const { a: f, b: a } = Z(this._minDist, this._spread);
534
- this._a = f, this._b = a;
493
+ G(this, "embedding", null);
494
+ G(this, "_hnswIndex", null);
495
+ G(this, "_nTrain", 0);
496
+ this._nComponents = e.nComponents ?? 2, this._nNeighbors = e.nNeighbors ?? 15, this._minDist = e.minDist ?? 0.1, this._spread = e.spread ?? 1, this._nEpochs = e.nEpochs, this._hnswOpts = e.hnsw ?? {}, this._debug = e.debug ?? !1, this._rng = fe(e.seed);
497
+ const { a: s, b: r } = de(this._minDist, this._spread);
498
+ this._a = s, this._b = r;
535
499
  }
536
500
  /**
537
501
  * Train UMAP on `vectors`.
@@ -539,46 +503,47 @@ class ge {
539
503
  * index so that transform() can project new points later.
540
504
  * Returns `this` for chaining.
541
505
  */
542
- async fit(t, f) {
543
- const a = t.length, s = this._nEpochs ?? (a > 1e4 ? 200 : 500), { M: u = 16, efConstruction: c = 200, efSearch: d = 50 } = this._hnswOpts;
544
- console.time("knn");
545
- const { knn: i, index: n } = await ae(t, this._nNeighbors, {
506
+ async fit(e, s) {
507
+ const r = e.length, n = this._nEpochs ?? (r > 1e4 ? 200 : 500), { M: u = 16, efConstruction: o = 200, efSearch: f = 50 } = this._hnswOpts;
508
+ this._debug && console.time("knn");
509
+ const { knn: c, index: a } = await ee(e, this._nNeighbors, {
546
510
  M: u,
547
- efConstruction: c,
548
- efSearch: d
511
+ efConstruction: o,
512
+ efSearch: f
549
513
  });
550
- this._hnswIndex = n, this._nTrain = a, console.timeEnd("knn"), console.time("fuzzy-set");
551
- const r = V(i.indices, i.distances, this._nNeighbors);
552
- console.timeEnd("fuzzy-set");
553
- const o = W(r.vals), h = new Float32Array(a * this._nComponents);
514
+ this._hnswIndex = a, this._nTrain = r, this._debug && console.timeEnd("knn"), this._debug && console.time("fuzzy-set");
515
+ const d = te(c.indices, c.distances, this._nNeighbors);
516
+ this._debug && console.timeEnd("fuzzy-set");
517
+ const i = V(d.vals), h = new Float32Array(r * this._nComponents);
554
518
  for (let l = 0; l < h.length; l++)
555
- h[l] = Math.random() * 20 - 10;
556
- if (console.time("sgd"), J())
519
+ h[l] = this._rng() * 20 - 10;
520
+ if (this._debug && console.time("sgd"), ie())
557
521
  try {
558
- const l = new X();
522
+ const l = new oe();
559
523
  await l.init(), this.embedding = await l.optimize(
560
524
  h,
561
- new Uint32Array(r.rows),
562
- new Uint32Array(r.cols),
563
- o,
564
- a,
525
+ new Uint32Array(d.rows),
526
+ new Uint32Array(d.cols),
527
+ i,
528
+ r,
565
529
  this._nComponents,
566
- s,
530
+ n,
567
531
  { a: this._a, b: this._b, gamma: 1, negativeSampleRate: 5 },
568
- f
532
+ s,
533
+ this._rng
569
534
  );
570
535
  } catch (l) {
571
- console.warn("WebGPU SGD failed, falling back to CPU:", l), this.embedding = L(h, r, o, a, this._nComponents, s, {
536
+ console.warn("WebGPU SGD failed, falling back to CPU:", l), this.embedding = W(h, d, i, r, this._nComponents, n, {
572
537
  a: this._a,
573
538
  b: this._b
574
- }, f);
539
+ }, s, this._rng);
575
540
  }
576
541
  else
577
- this.embedding = L(h, r, o, a, this._nComponents, s, {
542
+ this.embedding = W(h, d, i, r, this._nComponents, n, {
578
543
  a: this._a,
579
544
  b: this._b
580
- }, f);
581
- return console.timeEnd("sgd"), this;
545
+ }, s, this._rng);
546
+ return this._debug && console.timeEnd("sgd"), this;
582
547
  }
583
548
  /**
584
549
  * Project new (unseen) `vectors` into the embedding space learned by fit().
@@ -591,35 +556,37 @@ class ge {
591
556
  * returned embedding to [0, 1]. The stored training embedding is never
592
557
  * mutated. Defaults to `false`.
593
558
  */
594
- async transform(t, f = !1) {
559
+ async transform(e, s = !1) {
595
560
  if (!this._hnswIndex || !this.embedding)
596
561
  throw new Error("UMAP.transform() must be called after fit()");
597
- const a = t.length, s = this._nEpochs ?? (this._nTrain > 1e4 ? 200 : 500), u = Math.max(100, Math.floor(s / 4)), c = this._hnswIndex.searchKnn(t, this._nNeighbors), d = re(c.indices, c.distances, this._nNeighbors), i = new Uint32Array(d.rows), n = new Uint32Array(d.cols), r = new Float32Array(a), o = new Float32Array(a * this._nComponents);
598
- for (let p = 0; p < i.length; p++) {
599
- const g = i[p], _ = n[p], m = d.vals[p];
600
- r[g] += m;
601
- for (let y = 0; y < this._nComponents; y++)
602
- o[g * this._nComponents + y] += m * this.embedding[_ * this._nComponents + y];
562
+ const r = e.length, n = this._nEpochs ?? (this._nTrain > 1e4 ? 200 : 500), u = Math.max(100, Math.floor(n / 4)), o = this._hnswIndex.searchKnn(e, this._nNeighbors), f = ne(o.indices, o.distances, this._nNeighbors), c = new Uint32Array(f.rows), a = new Uint32Array(f.cols), d = new Float32Array(r), i = new Float32Array(r * this._nComponents);
563
+ for (let g = 0; g < c.length; g++) {
564
+ const _ = c[g], w = a[g], x = f.vals[g];
565
+ d[_] += x;
566
+ for (let m = 0; m < this._nComponents; m++)
567
+ i[_ * this._nComponents + m] += x * this.embedding[w * this._nComponents + m];
603
568
  }
604
- for (let p = 0; p < a; p++)
605
- if (r[p] > 0)
606
- for (let g = 0; g < this._nComponents; g++)
607
- o[p * this._nComponents + g] /= r[p];
569
+ for (let g = 0; g < r; g++)
570
+ if (d[g] > 0)
571
+ for (let _ = 0; _ < this._nComponents; _++)
572
+ i[g * this._nComponents + _] /= d[g];
608
573
  else
609
- for (let g = 0; g < this._nComponents; g++)
610
- o[p * this._nComponents + g] = Math.random() * 20 - 10;
611
- const h = W(d.vals), l = fe(
612
- o,
574
+ for (let _ = 0; _ < this._nComponents; _++)
575
+ i[g * this._nComponents + _] = this._rng() * 20 - 10;
576
+ const h = V(f.vals), l = ce(
577
+ i,
613
578
  this.embedding,
614
- d,
579
+ f,
615
580
  h,
616
- a,
581
+ r,
617
582
  this._nTrain,
618
583
  this._nComponents,
619
584
  u,
620
- { a: this._a, b: this._b }
585
+ { a: this._a, b: this._b },
586
+ void 0,
587
+ this._rng
621
588
  );
622
- return f ? K(l, a, this._nComponents) : l;
589
+ return s ? K(l, r, this._nComponents) : l;
623
590
  }
624
591
  /**
625
592
  * Convenience method equivalent to `fit(vectors)` followed by
@@ -630,38 +597,38 @@ class ge {
630
597
  * returned embedding to [0, 1]. `this.embedding` is never mutated.
631
598
  * Defaults to `false`.
632
599
  */
633
- async fit_transform(t, f, a = !1) {
634
- return await this.fit(t, f), a ? K(this.embedding, t.length, this._nComponents) : this.embedding;
600
+ async fit_transform(e, s, r = !1) {
601
+ return await this.fit(e, s), r ? K(this.embedding, e.length, this._nComponents) : this.embedding;
635
602
  }
636
603
  }
637
- function K(e, t, f) {
638
- const a = new Float32Array(e.length);
639
- for (let s = 0; s < f; s++) {
640
- let u = 1 / 0, c = -1 / 0;
641
- for (let i = 0; i < t; i++) {
642
- const n = e[i * f + s];
643
- n < u && (u = n), n > c && (c = n);
604
+ function K(t, e, s) {
605
+ const r = new Float32Array(t.length);
606
+ for (let n = 0; n < s; n++) {
607
+ let u = 1 / 0, o = -1 / 0;
608
+ for (let c = 0; c < e; c++) {
609
+ const a = t[c * s + n];
610
+ a < u && (u = a), a > o && (o = a);
644
611
  }
645
- const d = c - u;
646
- for (let i = 0; i < t; i++)
647
- a[i * f + s] = d > 0 ? (e[i * f + s] - u) / d : 0;
612
+ const f = o - u;
613
+ for (let c = 0; c < e; c++)
614
+ r[c * s + n] = f > 0 ? (t[c * s + n] - u) / f : 0;
648
615
  }
649
- return a;
616
+ return r;
650
617
  }
651
- function W(e, t) {
652
- let f = -1 / 0;
653
- for (let s = 0; s < e.length; s++)
654
- e[s] > f && (f = e[s]);
655
- const a = new Float32Array(e.length);
656
- for (let s = 0; s < e.length; s++) {
657
- const u = e[s] / f;
658
- a[s] = u > 0 ? 1 / u : -1;
618
+ function V(t, e) {
619
+ let s = -1 / 0;
620
+ for (let n = 0; n < t.length; n++)
621
+ t[n] > s && (s = t[n]);
622
+ const r = new Float32Array(t.length);
623
+ for (let n = 0; n < t.length; n++) {
624
+ const u = t[n] / s;
625
+ r[n] = u > 0 ? 1 / u : -1;
659
626
  }
660
- return a;
627
+ return r;
661
628
  }
662
629
  export {
663
- ge as UMAP,
664
- he as checkWebGPUAvailable,
665
- pe as fit,
666
- J as isWebGPUAvailable
630
+ ue as UMAP,
631
+ pe as checkWebGPUAvailable,
632
+ _e as fit,
633
+ ie as isWebGPUAvailable
667
634
  };
package/dist/rng.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Returns a seeded PRNG if seed is provided, otherwise wraps Math.random. */
2
+ export declare function makeRng(seed?: number): () => number;
package/dist/umap.d.ts CHANGED
@@ -15,6 +15,10 @@ export interface UMAPOptions {
15
15
  efConstruction?: number;
16
16
  efSearch?: number;
17
17
  };
18
+ /** Enable timing instrumentation via console.time/timeEnd (default: false) */
19
+ debug?: boolean;
20
+ /** Random seed for reproducible results. When omitted, Math.random() is used. */
21
+ seed?: number;
18
22
  }
19
23
  /**
20
24
  * Called after each completed SGD epoch (or every 10 epochs on the GPU path,
@@ -71,6 +75,8 @@ export declare class UMAP {
71
75
  private readonly _hnswOpts;
72
76
  private readonly _a;
73
77
  private readonly _b;
78
+ private readonly _debug;
79
+ private readonly _rng;
74
80
  /** The low-dimensional embedding produced by the last fit() call. */
75
81
  embedding: Float32Array | null;
76
82
  private _hnswIndex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umap-gpu",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "UMAP with HNSW kNN and WebGPU-accelerated SGD",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,6 +40,7 @@
40
40
  "build": "vite build && tsc",
41
41
  "dev": "vite",
42
42
  "test": "vitest run",
43
+ "bench": "npx vitest run --config vitest.bench.config.ts",
43
44
  "prepublishOnly": "bun test && bun run build",
44
45
  "docs:dev": "vitepress dev docs",
45
46
  "docs:build": "vitepress build docs",
@@ -55,7 +56,8 @@
55
56
  "vite": "^5.0.0",
56
57
  "vitepress": "^1.6.4",
57
58
  "vitepress-plugin-llms": "^1.11.0",
58
- "vitest": "^4.0.18"
59
+ "vitest": "^4.0.18",
60
+ "webgpu": "^0.3.8"
59
61
  },
60
62
  "license": "MIT"
61
63
  }