umap-gpu 0.2.15 → 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 R = (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, o = e.length, n = new c.HierarchicalNSW("l2", d, "");
7
- n.initIndex(o, a, s, 200), n.setEfSearch(Math.max(u, t)), n.addItems(e, !1);
8
- const r = [], i = [];
9
- for (let h = 0; h < o; 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)), i.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: i };
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, o = e.length, n = new c.HierarchicalNSW("l2", d, "");
17
- n.initIndex(o, a, s, 200), n.setEfSearch(Math.max(u, t)), n.addItems(e, !1);
18
- const r = [], i = [];
19
- for (let l = 0; l < o; 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: _ }) => _)), i.push(g.map(({ dist: _ }) => Math.sqrt(_)));
22
- }
23
- return { knn: { indices: r, distances: i }, index: {
24
- searchKnn(l, p) {
25
- const g = [], _ = [];
26
- for (const m of l) {
27
- const b = n.searchKnn(m, p, void 0), y = b.neighbors.map((x, v) => ({ idx: x, dist: b.distances[v] })).sort((x, v) => x.dist - v.dist).slice(0, p);
28
- g.push(y.map(({ idx: x }) => x)), _.push(y.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 = [], o = [], n = [];
36
- for (let i = 0; i < s; i++)
37
- for (let h = 0; h < e[i].length; h++) {
38
- const l = t[i][h], p = l <= c[i] ? 1 : Math.exp(-((l - c[i]) / u[i]));
39
- d.push(i), o.push(e[i][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 { ...ie(d, o, 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 = [], o = [];
45
- for (let n = 0; n < a; n++)
46
- for (let r = 0; r < e[n].length; r++) {
47
- const i = t[n][r], h = i <= u[n] ? 1 : Math.exp(-((i - u[n]) / s[n]));
48
- c.push(n), d.push(e[n][r]), o.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(o),
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 o = 0, n = 1 / 0, r = 1;
63
- const i = 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);
56
+ for (let g = 0; g < f.length; g++)
57
+ l += Math.exp(-Math.max(0, f[g] - u[o]) / d);
68
58
  if (Math.abs(l - i) < 1e-5) break;
69
- l > i ? (n = r, r = (o + n) / 2) : (o = r, r = n === 1 / 0 ? r * 2 : (o + n) / 2);
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 ie(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 = [], o = [];
80
- for (const [n, r] of u) {
81
- const i = Math.floor(n / a), h = n % a, l = u.get(h * a + i) ?? 0, p = r + l - r * l, g = r * l;
82
- c.push(i), d.push(h), o.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(o)
75
+ rows: new Uint32Array(o),
76
+ cols: new Uint32Array(f),
77
+ vals: new Float32Array(c)
88
78
  };
89
79
  }
90
- const oe = `// 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.
@@ -207,7 +197,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
207
197
  }
208
198
  }
209
199
  }
210
- `, 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.
211
201
  //
212
202
  // After the SGD pass has atomically accumulated all gradients into the forces
213
203
  // buffer, this shader applies each element's accumulated force to the
@@ -236,40 +226,40 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
236
226
  }
237
227
  `;
238
228
  let z = null;
239
- async function Q() {
229
+ async function Y() {
240
230
  if (z) return z;
241
231
  if (typeof navigator > "u" || !navigator.gpu)
242
232
  return null;
243
- const e = await navigator.gpu.requestAdapter();
244
- 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(() => {
245
235
  z = null;
246
236
  }), z) : null;
247
237
  }
248
- function J() {
238
+ function ie() {
249
239
  return typeof navigator < "u" && !!navigator.gpu;
250
240
  }
251
- async function he() {
252
- return await Q() !== null;
241
+ async function pe() {
242
+ return await Y() !== null;
253
243
  }
254
- class X {
244
+ class oe {
255
245
  constructor() {
256
- R(this, "device");
257
- R(this, "sgdPipeline");
258
- R(this, "applyForcesPipeline");
246
+ G(this, "device");
247
+ G(this, "sgdPipeline");
248
+ G(this, "applyForcesPipeline");
259
249
  }
260
250
  async init() {
261
- const t = await Q();
262
- if (!t) throw new Error("WebGPU not supported");
263
- 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({
264
254
  layout: "auto",
265
255
  compute: {
266
- module: this.device.createShaderModule({ code: oe }),
256
+ module: this.device.createShaderModule({ code: ae }),
267
257
  entryPoint: "main"
268
258
  }
269
259
  }), this.applyForcesPipeline = this.device.createComputePipeline({
270
260
  layout: "auto",
271
261
  compute: {
272
- module: this.device.createShaderModule({ code: ce }),
262
+ module: this.device.createShaderModule({ code: re }),
273
263
  entryPoint: "main"
274
264
  }
275
265
  });
@@ -287,254 +277,225 @@ class X {
287
277
  * @param params - UMAP curve parameters and repulsion settings
288
278
  * @returns Optimized embedding as Float32Array
289
279
  */
290
- async optimize(t, f, a, s, u, c, d, o, n) {
291
- const { device: r } = this, i = f.length, h = u * c, l = this.makeBuffer(
292
- 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,
293
283
  GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
294
- ), p = this.makeBuffer(f, GPUBufferUsage.STORAGE), g = this.makeBuffer(a, GPUBufferUsage.STORAGE), _ = this.makeBuffer(s, GPUBufferUsage.STORAGE), m = new Float32Array(s), b = this.makeBuffer(m, GPUBufferUsage.STORAGE), y = new Float32Array(i);
295
- for (let A = 0; A < i; A++)
296
- y[A] = s[A] / o.negativeSampleRate;
297
- const x = this.makeBuffer(y, GPUBufferUsage.STORAGE), v = new Uint32Array(i);
298
- for (let A = 0; A < i; A++)
299
- v[A] = Math.random() * 4294967295 | 0;
300
- const G = this.makeBuffer(v, GPUBufferUsage.STORAGE), P = r.createBuffer({
301
- 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,
302
292
  usage: GPUBufferUsage.STORAGE,
303
293
  mappedAtCreation: !0
304
294
  });
305
- new Int32Array(P.getMappedRange()).fill(0), P.unmap();
306
- const U = r.createBuffer({
295
+ new Int32Array(v.getMappedRange()).fill(0), v.unmap();
296
+ const R = i.createBuffer({
307
297
  size: 40,
308
298
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
309
- }), F = r.createBuffer({
299
+ }), O = i.createBuffer({
310
300
  size: 16,
311
301
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
312
- }), N = r.createBindGroup({
302
+ }), C = i.createBindGroup({
313
303
  layout: this.sgdPipeline.getBindGroupLayout(0),
314
304
  entries: [
315
- { binding: 0, resource: { buffer: _ } },
316
- { binding: 1, resource: { buffer: p } },
317
- { binding: 2, resource: { buffer: g } },
318
- { binding: 3, resource: { buffer: l } },
319
- { binding: 4, resource: { buffer: b } },
320
- { binding: 5, resource: { buffer: x } },
321
- { binding: 6, resource: { buffer: U } },
322
- { binding: 7, resource: { buffer: G } },
323
- { binding: 8, resource: { buffer: P } }
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 } }
324
314
  ]
325
- }), M = r.createBindGroup({
315
+ }), U = i.createBindGroup({
326
316
  layout: this.applyForcesPipeline.getBindGroupLayout(0),
327
317
  entries: [
328
- { binding: 0, resource: { buffer: l } },
329
- { binding: 1, resource: { buffer: P } },
330
- { binding: 2, resource: { buffer: F } }
318
+ { binding: 0, resource: { buffer: g } },
319
+ { binding: 1, resource: { buffer: v } },
320
+ { binding: 2, resource: { buffer: O } }
331
321
  ]
332
322
  });
333
- for (let A = 0; A < d; A++) {
334
- const B = 1 - A / d, O = new ArrayBuffer(40), S = new Uint32Array(O), k = new Float32Array(O);
335
- S[0] = i, S[1] = u, S[2] = c, S[3] = A, S[4] = d, k[5] = B, k[6] = o.a, k[7] = o.b, k[8] = o.gamma, S[9] = o.negativeSampleRate, r.queue.writeBuffer(U, 0, O);
336
- const D = new ArrayBuffer(16), $ = new Uint32Array(D), ee = new Float32Array(D);
337
- $[0] = h, ee[1] = B, r.queue.writeBuffer(F, 0, D);
338
- const T = r.createCommandEncoder(), q = T.beginComputePass();
339
- q.setPipeline(this.sgdPipeline), q.setBindGroup(0, N), q.dispatchWorkgroups(Math.ceil(i / 256)), q.end();
340
- const L = T.beginComputePass();
341
- L.setPipeline(this.applyForcesPipeline), L.setBindGroup(0, M), L.dispatchWorkgroups(Math.ceil(h / 256)), L.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));
342
332
  }
343
- const E = r.createBuffer({
344
- size: t.byteLength,
333
+ const A = i.createBuffer({
334
+ size: e.byteLength,
345
335
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
346
- }), w = r.createCommandEncoder();
347
- w.copyBufferToBuffer(l, 0, E, 0, t.byteLength), r.queue.submit([w.finish()]), await E.mapAsync(GPUMapMode.READ);
348
- const C = new Float32Array(E.getMappedRange().slice(0));
349
- return E.unmap(), l.destroy(), p.destroy(), g.destroy(), _.destroy(), b.destroy(), x.destroy(), G.destroy(), P.destroy(), U.destroy(), F.destroy(), E.destroy(), C;
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;
350
340
  }
351
- makeBuffer(t, f) {
352
- const a = this.device.createBuffer({
353
- size: t.byteLength,
354
- usage: f,
341
+ makeBuffer(e, s) {
342
+ const r = this.device.createBuffer({
343
+ size: e.byteLength,
344
+ usage: s,
355
345
  mappedAtCreation: !0
356
346
  });
357
- 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;
358
348
  }
359
349
  }
360
- function I(e) {
361
- return Math.max(-4, Math.min(4, e));
350
+ function T(t) {
351
+ return Math.max(-4, Math.min(4, t));
362
352
  }
363
- function j(e, t, f, a, s, u, c, d) {
364
- const { a: o, b: n, gamma: r = 1, negativeSampleRate: i = 5 } = c, h = t.rows.length, l = new Uint32Array(t.rows), p = new Uint32Array(t.cols), g = new Float32Array(f), _ = new Float32Array(h);
365
- for (let m = 0; m < h; m++)
366
- _[m] = f[m] / i;
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;
367
357
  for (let m = 0; m < u; m++) {
368
- d == null || d(m, u);
369
- const b = 1 - m / u;
370
- for (let y = 0; y < h; y++) {
371
- if (g[y] > m) continue;
372
- const x = l[y], v = p[y];
373
- let G = 0;
374
- for (let M = 0; M < s; M++) {
375
- const E = e[x * s + M] - e[v * s + M];
376
- G += 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;
377
367
  }
378
- const P = Math.pow(G, n), U = -2 * o * n * (G > 0 ? P / G : 0) / (o * P + 1);
379
- for (let M = 0; M < s; M++) {
380
- const E = e[x * s + M] - e[v * s + M], w = I(U * E);
381
- e[x * s + M] += b * w, e[v * s + M] -= b * 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;
382
372
  }
383
- g[y] += f[y];
384
- const F = f[y] / i, N = Math.max(0, Math.floor(
385
- (m - _[y]) / F
373
+ w[p] += s[p];
374
+ const O = s[p] / h, C = Math.max(0, Math.floor(
375
+ (m - x[p]) / O
386
376
  ));
387
- _[y] += N * F;
388
- for (let M = 0; M < N; M++) {
389
- const E = Math.floor(Math.random() * a);
390
- if (E === x) continue;
391
- let w = 0;
392
- for (let B = 0; B < s; B++) {
393
- const O = e[x * s + B] - e[E * s + B];
394
- 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;
395
385
  }
396
- const C = Math.pow(w, n), A = 2 * r * n / ((1e-3 + w) * (o * C + 1));
397
- for (let B = 0; B < s; B++) {
398
- const O = e[x * s + B] - e[E * s + B], S = I(A * O);
399
- e[x * s + B] += b * 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;
400
390
  }
401
391
  }
402
392
  }
403
393
  }
404
- return e;
394
+ return t;
405
395
  }
406
- function fe(e, t, f, a, s, u, c, d, o, n) {
407
- const { a: r, b: i, gamma: h = 1, negativeSampleRate: l = 5 } = o, p = f.rows.length, g = new Uint32Array(f.rows), _ = new Uint32Array(f.cols), m = new Float32Array(a), b = new Float32Array(p);
408
- for (let y = 0; y < p; y++)
409
- b[y] = a[y] / l;
410
- for (let y = 0; y < d; y++) {
411
- const x = 1 - y / d;
412
- for (let v = 0; v < p; v++) {
413
- if (m[v] > y) continue;
414
- const G = g[v], P = _[v];
415
- let U = 0;
416
- for (let w = 0; w < c; w++) {
417
- const C = e[G * c + w] - t[P * c + w];
418
- U += C * C;
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;
419
409
  }
420
- const F = Math.pow(U, i), N = -2 * r * i * (U > 0 ? F / U : 0) / (r * F + 1);
421
- for (let w = 0; w < c; w++) {
422
- const C = e[G * c + w] - t[P * c + w];
423
- e[G * c + w] += x * I(N * C);
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);
424
414
  }
425
- m[v] += a[v];
426
- const M = a[v] / l, E = Math.max(0, Math.floor(
427
- (y - b[v]) / M
415
+ m[B] += r[B];
416
+ const U = r[B] / g, A = Math.max(0, Math.floor(
417
+ (p - F[B]) / U
428
418
  ));
429
- b[v] += E * M;
430
- for (let w = 0; w < E; w++) {
431
- const C = Math.floor(Math.random() * u);
432
- if (C === P) continue;
433
- let A = 0;
434
- for (let S = 0; S < c; S++) {
435
- const k = e[G * c + S] - t[C * c + S];
436
- 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;
437
427
  }
438
- const B = Math.pow(A, i), O = 2 * h * i / ((1e-3 + A) * (r * B + 1));
439
- for (let S = 0; S < c; S++) {
440
- const k = e[G * c + S] - t[C * c + S];
441
- e[G * c + S] += x * I(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);
442
432
  }
443
433
  }
444
434
  }
445
435
  }
446
- return e;
436
+ return t;
447
437
  }
448
- async function pe(e, t = {}, f) {
449
- const {
450
- nComponents: a = 2,
451
- nNeighbors: s = 15,
452
- minDist: u = 0.1,
453
- spread: c = 1,
454
- hnsw: d = {}
455
- } = t, o = t.nEpochs ?? (e.length > 1e4 ? 200 : 500);
456
- console.time("knn");
457
- const { indices: n, distances: r } = await se(e, s, {
458
- M: d.M ?? 16,
459
- efConstruction: d.efConstruction ?? 200,
460
- efSearch: d.efSearch ?? 50
461
- });
462
- console.timeEnd("knn"), console.time("fuzzy-set");
463
- const i = V(n, r, s);
464
- console.timeEnd("fuzzy-set");
465
- const { a: h, b: l } = Z(u, c), p = W(i.vals), g = e.length, _ = new Float32Array(g * a);
466
- for (let b = 0; b < _.length; b++)
467
- _[b] = Math.random() * 20 - 10;
468
- console.time("sgd");
469
- let m;
470
- if (J())
471
- try {
472
- const b = new X();
473
- await b.init(), m = await b.optimize(
474
- _,
475
- new Uint32Array(i.rows),
476
- new Uint32Array(i.cols),
477
- p,
478
- g,
479
- a,
480
- o,
481
- { a: h, b: l, gamma: 1, negativeSampleRate: 5 },
482
- f
483
- );
484
- } catch (b) {
485
- console.warn("WebGPU SGD failed, falling back to CPU:", b), m = j(_, i, p, g, a, o, { a: h, b: l }, f);
486
- }
487
- else
488
- m = j(_, i, p, g, a, o, { a: h, b: l }, f);
489
- 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
+ };
490
446
  }
491
- function Z(e, t) {
492
- 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);
493
449
  }
494
- function de(e, t) {
495
- const a = [], s = [];
496
- for (let o = 0; o < 299; o++) {
497
- const n = (o + 1) / 299 * t * 3;
498
- 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));
499
458
  }
500
- let u = 1, c = 1, d = 1e-3;
501
- for (let o = 0; o < 500; o++) {
502
- let n = 0, r = 0, i = 0, h = 0, l = 0, p = 0;
503
- for (let U = 0; U < 299; U++) {
504
- const F = a[U], N = Math.pow(F, 2 * c), M = 1 + u * N, w = 1 / M - s[U];
505
- p += w * w;
506
- const C = M * M, A = -N / C, B = F > 0 ? -2 * Math.log(F) * u * N / C : 0;
507
- n += A * w, r += B * w, i += 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;
508
467
  }
509
- const g = i + d, _ = h + d, m = l, b = g * _ - m * m;
510
- if (Math.abs(b) < 1e-20) break;
511
- const y = -(_ * n - m * r) / b, x = -(g * r - m * n) / b, v = Math.max(1e-4, u + y), G = Math.max(1e-4, c + x);
512
- let P = 0;
513
- for (let U = 0; U < 299; U++) {
514
- const F = Math.pow(a[U], 2 * G), N = 1 / (1 + v * F) - s[U];
515
- P += 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;
516
475
  }
517
- if (P < p ? (u = v, c = G, d = Math.max(1e-10, d / 10)) : d = Math.min(1e10, d * 10), Math.abs(y) < 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;
518
477
  }
519
- return { a: u, b: c };
478
+ return { a: u, b: o };
520
479
  }
521
- class ge {
522
- constructor(t = {}) {
523
- R(this, "_nComponents");
524
- R(this, "_nNeighbors");
525
- R(this, "_minDist");
526
- R(this, "_spread");
527
- R(this, "_nEpochs");
528
- R(this, "_hnswOpts");
529
- R(this, "_a");
530
- R(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");
531
492
  /** The low-dimensional embedding produced by the last fit() call. */
532
- R(this, "embedding", null);
533
- R(this, "_hnswIndex", null);
534
- R(this, "_nTrain", 0);
535
- 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 ?? {};
536
- const { a: f, b: a } = Z(this._minDist, this._spread);
537
- 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;
538
499
  }
539
500
  /**
540
501
  * Train UMAP on `vectors`.
@@ -542,46 +503,47 @@ class ge {
542
503
  * index so that transform() can project new points later.
543
504
  * Returns `this` for chaining.
544
505
  */
545
- async fit(t, f) {
546
- const a = t.length, s = this._nEpochs ?? (a > 1e4 ? 200 : 500), { M: u = 16, efConstruction: c = 200, efSearch: d = 50 } = this._hnswOpts;
547
- console.time("knn");
548
- const { knn: o, 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, {
549
510
  M: u,
550
- efConstruction: c,
551
- efSearch: d
511
+ efConstruction: o,
512
+ efSearch: f
552
513
  });
553
- this._hnswIndex = n, this._nTrain = a, console.timeEnd("knn"), console.time("fuzzy-set");
554
- const r = V(o.indices, o.distances, this._nNeighbors);
555
- console.timeEnd("fuzzy-set");
556
- const i = 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);
557
518
  for (let l = 0; l < h.length; l++)
558
- h[l] = Math.random() * 20 - 10;
559
- if (console.time("sgd"), J())
519
+ h[l] = this._rng() * 20 - 10;
520
+ if (this._debug && console.time("sgd"), ie())
560
521
  try {
561
- const l = new X();
522
+ const l = new oe();
562
523
  await l.init(), this.embedding = await l.optimize(
563
524
  h,
564
- new Uint32Array(r.rows),
565
- new Uint32Array(r.cols),
525
+ new Uint32Array(d.rows),
526
+ new Uint32Array(d.cols),
566
527
  i,
567
- a,
528
+ r,
568
529
  this._nComponents,
569
- s,
530
+ n,
570
531
  { a: this._a, b: this._b, gamma: 1, negativeSampleRate: 5 },
571
- f
532
+ s,
533
+ this._rng
572
534
  );
573
535
  } catch (l) {
574
- console.warn("WebGPU SGD failed, falling back to CPU:", l), this.embedding = j(h, r, i, 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, {
575
537
  a: this._a,
576
538
  b: this._b
577
- }, f);
539
+ }, s, this._rng);
578
540
  }
579
541
  else
580
- this.embedding = j(h, r, i, a, this._nComponents, s, {
542
+ this.embedding = W(h, d, i, r, this._nComponents, n, {
581
543
  a: this._a,
582
544
  b: this._b
583
- }, f);
584
- return console.timeEnd("sgd"), this;
545
+ }, s, this._rng);
546
+ return this._debug && console.timeEnd("sgd"), this;
585
547
  }
586
548
  /**
587
549
  * Project new (unseen) `vectors` into the embedding space learned by fit().
@@ -594,35 +556,37 @@ class ge {
594
556
  * returned embedding to [0, 1]. The stored training embedding is never
595
557
  * mutated. Defaults to `false`.
596
558
  */
597
- async transform(t, f = !1) {
559
+ async transform(e, s = !1) {
598
560
  if (!this._hnswIndex || !this.embedding)
599
561
  throw new Error("UMAP.transform() must be called after fit()");
600
- 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), o = new Uint32Array(d.rows), n = new Uint32Array(d.cols), r = new Float32Array(a), i = new Float32Array(a * this._nComponents);
601
- for (let p = 0; p < o.length; p++) {
602
- const g = o[p], _ = n[p], m = d.vals[p];
603
- r[g] += m;
604
- for (let b = 0; b < this._nComponents; b++)
605
- i[g * this._nComponents + b] += m * this.embedding[_ * this._nComponents + b];
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];
606
568
  }
607
- for (let p = 0; p < a; p++)
608
- if (r[p] > 0)
609
- for (let g = 0; g < this._nComponents; g++)
610
- i[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];
611
573
  else
612
- for (let g = 0; g < this._nComponents; g++)
613
- i[p * this._nComponents + g] = Math.random() * 20 - 10;
614
- const h = W(d.vals), l = fe(
574
+ for (let _ = 0; _ < this._nComponents; _++)
575
+ i[g * this._nComponents + _] = this._rng() * 20 - 10;
576
+ const h = V(f.vals), l = ce(
615
577
  i,
616
578
  this.embedding,
617
- d,
579
+ f,
618
580
  h,
619
- a,
581
+ r,
620
582
  this._nTrain,
621
583
  this._nComponents,
622
584
  u,
623
- { a: this._a, b: this._b }
585
+ { a: this._a, b: this._b },
586
+ void 0,
587
+ this._rng
624
588
  );
625
- return f ? K(l, a, this._nComponents) : l;
589
+ return s ? K(l, r, this._nComponents) : l;
626
590
  }
627
591
  /**
628
592
  * Convenience method equivalent to `fit(vectors)` followed by
@@ -633,38 +597,38 @@ class ge {
633
597
  * returned embedding to [0, 1]. `this.embedding` is never mutated.
634
598
  * Defaults to `false`.
635
599
  */
636
- async fit_transform(t, f, a = !1) {
637
- 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;
638
602
  }
639
603
  }
640
- function K(e, t, f) {
641
- const a = new Float32Array(e.length);
642
- for (let s = 0; s < f; s++) {
643
- let u = 1 / 0, c = -1 / 0;
644
- for (let o = 0; o < t; o++) {
645
- const n = e[o * f + s];
646
- 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);
647
611
  }
648
- const d = c - u;
649
- for (let o = 0; o < t; o++)
650
- a[o * f + s] = d > 0 ? (e[o * 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;
651
615
  }
652
- return a;
616
+ return r;
653
617
  }
654
- function W(e, t) {
655
- let f = -1 / 0;
656
- for (let s = 0; s < e.length; s++)
657
- e[s] > f && (f = e[s]);
658
- const a = new Float32Array(e.length);
659
- for (let s = 0; s < e.length; s++) {
660
- const u = e[s] / f;
661
- 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;
662
626
  }
663
- return a;
627
+ return r;
664
628
  }
665
629
  export {
666
- ge as UMAP,
667
- he as checkWebGPUAvailable,
668
- pe as fit,
669
- J as isWebGPUAvailable
630
+ ue as UMAP,
631
+ pe as checkWebGPUAvailable,
632
+ _e as fit,
633
+ ie as isWebGPUAvailable
670
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.15",
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",