umap-gpu 0.2.9 → 0.2.11

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 (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +268 -266
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ # ⚠️ NOT READY YET | WORK IN PROGRESS ⚠️
2
+
1
3
  # umap-gpu
2
4
 
3
5
  UMAP dimensionality reduction with WebGPU-accelerated SGD and HNSW approximate nearest neighbors.
package/dist/index.js CHANGED
@@ -1,95 +1,90 @@
1
1
  var H = Object.defineProperty;
2
- var V = (t, e, i) => e in t ? H(t, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : t[e] = i;
3
- var E = (t, e, i) => V(t, typeof e != "symbol" ? e + "" : e, i);
2
+ var V = (e, n, r) => n in e ? H(e, n, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[n] = r;
3
+ var G = (e, n, r) => V(e, typeof n != "symbol" ? n + "" : n, r);
4
4
  import { loadHnswlib as C } from "hnswlib-wasm";
5
- async function Y(t, e, i = {}) {
6
- const { M: a = 16, efConstruction: s = 200, efSearch: p = 50 } = i, f = await C(), h = t[0].length, c = t.length, o = new f.HierarchicalNSW("l2", h, "");
7
- o.initIndex(c, a, s, 200), o.setEfSearch(Math.max(p, e)), o.addItems(t, !1);
8
- const n = [], r = [];
9
- for (let l = 0; l < c; l++) {
10
- const d = o.searchKnn(t[l], e + 1, void 0), u = d.neighbors.map((g, _) => ({ idx: g, dist: d.distances[_] })).filter(({ idx: g }) => g !== l).slice(0, e);
11
- n.push(u.map(({ idx: g }) => g)), r.push(u.map(({ dist: g }) => g));
5
+ async function Y(e, n, r = {}) {
6
+ const { M: s = 16, efConstruction: t = 200, efSearch: p = 50 } = r, f = await C(), l = e[0].length, c = e.length, a = new f.HierarchicalNSW("l2", l, "");
7
+ a.initIndex(c, s, t, 200), a.setEfSearch(Math.max(p, n)), a.addItems(e, !1);
8
+ const o = [], i = [];
9
+ for (let h = 0; h < c; h++) {
10
+ const d = a.searchKnn(e[h], n + 1, void 0), u = d.neighbors.map((g, m) => ({ idx: g, dist: d.distances[m] })).filter(({ idx: g }) => g !== h).slice(0, n);
11
+ o.push(u.map(({ idx: g }) => g)), i.push(u.map(({ dist: g }) => g));
12
12
  }
13
- return { indices: n, distances: r };
13
+ return { indices: o, distances: i };
14
14
  }
15
- async function J(t, e, i = {}) {
16
- const { M: a = 16, efConstruction: s = 200, efSearch: p = 50 } = i, f = await C(), h = t[0].length, c = t.length, o = new f.HierarchicalNSW("l2", h, "");
17
- o.initIndex(c, a, s, 200), o.setEfSearch(Math.max(p, e)), o.addItems(t, !1);
18
- const n = [], r = [];
15
+ async function Q(e, n, r = {}) {
16
+ const { M: s = 16, efConstruction: t = 200, efSearch: p = 50 } = r, f = await C(), l = e[0].length, c = e.length, a = new f.HierarchicalNSW("l2", l, "");
17
+ a.initIndex(c, s, t, 200), a.setEfSearch(Math.max(p, n)), a.addItems(e, !1);
18
+ const o = [], i = [];
19
19
  for (let d = 0; d < c; d++) {
20
- const u = o.searchKnn(t[d], e + 1, void 0), g = u.neighbors.map((_, w) => ({ idx: _, dist: u.distances[w] })).filter(({ idx: _ }) => _ !== d).slice(0, e);
21
- n.push(g.map(({ idx: _ }) => _)), r.push(g.map(({ dist: _ }) => _));
20
+ const u = a.searchKnn(e[d], n + 1, void 0), g = u.neighbors.map((m, w) => ({ idx: m, dist: u.distances[w] })).filter(({ idx: m }) => m !== d).slice(0, n);
21
+ o.push(g.map(({ idx: m }) => m)), i.push(g.map(({ dist: m }) => m));
22
22
  }
23
- return { knn: { indices: n, distances: r }, index: {
23
+ return { knn: { indices: o, distances: i }, index: {
24
24
  searchKnn(d, u) {
25
- const g = [], _ = [];
25
+ const g = [], m = [];
26
26
  for (const w of d) {
27
- const y = o.searchKnn(w, u, void 0), b = y.neighbors.map((M, x) => ({ idx: M, dist: y.distances[x] })).sort((M, x) => M.dist - x.dist).slice(0, u);
28
- g.push(b.map(({ idx: M }) => M)), _.push(b.map(({ dist: M }) => M));
27
+ const b = a.searchKnn(w, u, void 0), y = b.neighbors.map((v, x) => ({ idx: v, dist: b.distances[x] })).sort((v, x) => v.dist - x.dist).slice(0, u);
28
+ g.push(y.map(({ idx: v }) => v)), m.push(y.map(({ dist: v }) => v));
29
29
  }
30
- return { indices: g, distances: _ };
30
+ return { indices: g, distances: m };
31
31
  }
32
32
  } };
33
33
  }
34
- function D(t, e, i, a = 1) {
35
- const s = t.length, { sigmas: p, rhos: f } = L(e, i), h = [], c = [], o = [];
36
- for (let r = 0; r < s; r++)
37
- for (let l = 0; l < t[r].length; l++) {
38
- const d = e[r][l], u = d <= f[r] ? 1 : Math.exp(-((d - f[r]) / p[r]));
39
- h.push(r), c.push(t[r][l]), o.push(u);
34
+ function D(e, n, r, s = 1) {
35
+ const t = e.length, { sigmas: p, rhos: f } = L(n, r), l = [], c = [], a = [];
36
+ for (let i = 0; i < t; i++)
37
+ for (let h = 0; h < e[i].length; h++) {
38
+ const d = n[i][h], u = d <= f[i] ? 1 : Math.exp(-((d - f[i]) / p[i]));
39
+ l.push(i), c.push(e[i][h]), a.push(u);
40
40
  }
41
- return { ...X(h, c, o, s, a), nVertices: s };
41
+ return { ...X(l, c, a, t, s), nVertices: t };
42
42
  }
43
- function Q(t, e, i) {
44
- const a = t.length, { sigmas: s, rhos: p } = L(e, i), f = [], h = [], c = [];
45
- for (let o = 0; o < a; o++)
46
- for (let n = 0; n < t[o].length; n++) {
47
- const r = e[o][n], l = r <= p[o] ? 1 : Math.exp(-((r - p[o]) / s[o]));
48
- f.push(o), h.push(t[o][n]), c.push(l);
43
+ function J(e, n, r) {
44
+ const s = e.length, { sigmas: t, rhos: p } = L(n, r), f = [], l = [], c = [];
45
+ for (let a = 0; a < s; a++)
46
+ for (let o = 0; o < e[a].length; o++) {
47
+ const i = n[a][o], h = i <= p[a] ? 1 : Math.exp(-((i - p[a]) / t[a]));
48
+ f.push(a), l.push(e[a][o]), c.push(h);
49
49
  }
50
50
  return {
51
51
  rows: new Float32Array(f),
52
- cols: new Float32Array(h),
52
+ cols: new Float32Array(l),
53
53
  vals: new Float32Array(c),
54
- nVertices: a
54
+ nVertices: s
55
55
  };
56
56
  }
57
- function L(t, e) {
58
- const a = t.length, s = new Float32Array(a), p = new Float32Array(a);
59
- for (let f = 0; f < a; f++) {
60
- const h = t[f];
61
- p[f] = h.find((l) => l > 0) ?? 0;
62
- let c = 0, o = 1 / 0, n = 1;
63
- const r = Math.log2(e);
64
- for (let l = 0; l < 64; l++) {
57
+ function L(e, n) {
58
+ const s = e.length, t = new Float32Array(s), p = new Float32Array(s);
59
+ for (let f = 0; f < s; f++) {
60
+ const l = e[f];
61
+ p[f] = l.find((h) => h > 0) ?? 0;
62
+ let c = 0, a = 1 / 0, o = 1;
63
+ const i = Math.log2(n);
64
+ for (let h = 0; h < 64; h++) {
65
65
  let d = 0;
66
- for (let u = 1; u < h.length; u++)
67
- d += Math.exp(-Math.max(0, h[u] - p[f]) / n);
68
- if (Math.abs(d - r) < 1e-5) break;
69
- d > r ? (o = n, n = (c + o) / 2) : (c = n, n = o === 1 / 0 ? n * 2 : (c + o) / 2);
66
+ for (let u = 0; u < l.length; u++)
67
+ d += Math.exp(-Math.max(0, l[u] - p[f]) / o);
68
+ if (Math.abs(d - i) < 1e-5) break;
69
+ d > i ? (a = o, o = (c + a) / 2) : (c = o, o = a === 1 / 0 ? o * 2 : (c + a) / 2);
70
70
  }
71
- s[f] = n;
71
+ t[f] = o;
72
72
  }
73
- return { sigmas: s, rhos: p };
73
+ return { sigmas: t, rhos: p };
74
74
  }
75
- function X(t, e, i, a, s) {
76
- const p = /* @__PURE__ */ new Map(), f = (n, r, l) => {
77
- const d = n * a + r;
78
- p.set(d, (p.get(d) ?? 0) + l);
79
- };
80
- for (let n = 0; n < t.length; n++)
81
- f(t[n], e[n], i[n]), f(e[n], t[n], i[n]);
82
- const h = [], c = [], o = [];
83
- for (const [n, r] of p.entries()) {
84
- const l = Math.floor(n / a), d = n % a;
85
- h.push(l), c.push(d), o.push(
86
- r > 1 ? s * (2 - r) + (1 - s) * (r - 1) : r
87
- );
75
+ function X(e, n, r, s, t) {
76
+ const p = /* @__PURE__ */ new Map();
77
+ for (let a = 0; a < e.length; a++)
78
+ p.set(e[a] * s + n[a], r[a]);
79
+ const f = [], l = [], c = [];
80
+ for (const [a, o] of p) {
81
+ const i = Math.floor(a / s), h = a % s, d = p.get(h * s + i) ?? 0, u = o + d - o * d, g = o * d;
82
+ f.push(i), l.push(h), c.push(t * u + (1 - t) * g);
88
83
  }
89
84
  return {
90
- rows: new Float32Array(h),
91
- cols: new Float32Array(c),
92
- vals: new Float32Array(o)
85
+ rows: new Float32Array(f),
86
+ cols: new Float32Array(l),
87
+ vals: new Float32Array(c)
93
88
  };
94
89
  }
95
90
  const Z = `// UMAP SGD compute shader — processes one graph edge per GPU thread.
@@ -150,13 +145,20 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
150
145
  dist_sq += diff * diff;
151
146
  }
152
147
 
153
- let grad_coeff_attr = -2.0 * params.a * params.b * pow(dist_sq, params.b - 1.0)
154
- / (params.a * pow(dist_sq, params.b) + 1.0);
148
+ let pow_b = pow(dist_sq, params.b);
149
+ // Guard dist_sq == 0: b-1 is negative so pow(0, b-1) = +Inf.
150
+ // Mirror CPU: use pow_b / dist_sq only when dist_sq > 0, else 0.
151
+ let grad_coeff_attr = select(
152
+ -2.0 * params.a * params.b * (pow_b / dist_sq) / (params.a * pow_b + 1.0),
153
+ 0.0,
154
+ dist_sq == 0.0
155
+ );
155
156
 
156
157
  for (var d = 0u; d < nc; d++) {
157
158
  let diff = embedding[i * nc + d] - embedding[j * nc + d];
158
159
  let grad = clip(grad_coeff_attr * diff, -4.0, 4.0);
159
160
  embedding[i * nc + d] += params.alpha * grad;
161
+ embedding[j * nc + d] -= params.alpha * grad;
160
162
  }
161
163
 
162
164
  epoch_of_next_sample[edge_idx] += epochs_per_sample[edge_idx];
@@ -197,13 +199,13 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
197
199
  `;
198
200
  class W {
199
201
  constructor() {
200
- E(this, "device");
201
- E(this, "pipeline");
202
+ G(this, "device");
203
+ G(this, "pipeline");
202
204
  }
203
205
  async init() {
204
- const e = await navigator.gpu.requestAdapter();
205
- if (!e) throw new Error("WebGPU not supported");
206
- this.device = await e.requestDevice(), this.pipeline = this.device.createComputePipeline({
206
+ const n = await navigator.gpu.requestAdapter();
207
+ if (!n) throw new Error("WebGPU not supported");
208
+ this.device = await n.requestDevice(), this.pipeline = this.device.createComputePipeline({
207
209
  layout: "auto",
208
210
  compute: {
209
211
  module: this.device.createShaderModule({ code: Z }),
@@ -224,218 +226,218 @@ class W {
224
226
  * @param params - UMAP curve parameters and repulsion settings
225
227
  * @returns Optimized embedding as Float32Array
226
228
  */
227
- async optimize(e, i, a, s, p, f, h, c, o) {
228
- const { device: n } = this, r = i.length, l = this.makeBuffer(
229
- e,
229
+ async optimize(n, r, s, t, p, f, l, c, a) {
230
+ const { device: o } = this, i = r.length, h = this.makeBuffer(
231
+ n,
230
232
  GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
231
- ), d = this.makeBuffer(i, GPUBufferUsage.STORAGE), u = this.makeBuffer(a, GPUBufferUsage.STORAGE), g = this.makeBuffer(s, GPUBufferUsage.STORAGE), _ = new Float32Array(r).fill(0), w = this.makeBuffer(_, GPUBufferUsage.STORAGE), y = new Float32Array(r);
232
- for (let m = 0; m < r; m++)
233
- y[m] = s[m] / c.negativeSampleRate;
234
- const b = this.makeBuffer(y, GPUBufferUsage.STORAGE), M = new Uint32Array(r);
235
- for (let m = 0; m < r; m++)
236
- M[m] = Math.random() * 4294967295 | 0;
237
- const x = this.makeBuffer(M, GPUBufferUsage.STORAGE), B = n.createBuffer({
233
+ ), d = this.makeBuffer(r, GPUBufferUsage.STORAGE), u = this.makeBuffer(s, GPUBufferUsage.STORAGE), g = this.makeBuffer(t, GPUBufferUsage.STORAGE), m = new Float32Array(i).fill(0), w = this.makeBuffer(m, GPUBufferUsage.STORAGE), b = new Float32Array(i);
234
+ for (let _ = 0; _ < i; _++)
235
+ b[_] = t[_] / c.negativeSampleRate;
236
+ const y = this.makeBuffer(b, GPUBufferUsage.STORAGE), v = new Uint32Array(i);
237
+ for (let _ = 0; _ < i; _++)
238
+ v[_] = Math.random() * 4294967295 | 0;
239
+ const x = this.makeBuffer(v, GPUBufferUsage.STORAGE), B = o.createBuffer({
238
240
  size: 40,
239
241
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
240
242
  });
241
- for (let m = 0; m < h; m++) {
242
- const F = 1 - m / h, A = new ArrayBuffer(40), v = new Uint32Array(A), N = new Float32Array(A);
243
- v[0] = r, v[1] = p, v[2] = f, v[3] = m, v[4] = h, N[5] = F, N[6] = c.a, N[7] = c.b, N[8] = c.gamma, v[9] = c.negativeSampleRate, n.queue.writeBuffer(B, 0, A);
244
- const S = n.createBindGroup({
243
+ for (let _ = 0; _ < l; _++) {
244
+ const F = 1 - _ / l, A = new ArrayBuffer(40), M = new Uint32Array(A), N = new Float32Array(A);
245
+ M[0] = i, M[1] = p, M[2] = f, M[3] = _, M[4] = l, N[5] = F, N[6] = c.a, N[7] = c.b, N[8] = c.gamma, M[9] = c.negativeSampleRate, o.queue.writeBuffer(B, 0, A);
246
+ const S = o.createBindGroup({
245
247
  layout: this.pipeline.getBindGroupLayout(0),
246
248
  entries: [
247
249
  { binding: 0, resource: { buffer: g } },
248
250
  { binding: 1, resource: { buffer: d } },
249
251
  { binding: 2, resource: { buffer: u } },
250
- { binding: 3, resource: { buffer: l } },
252
+ { binding: 3, resource: { buffer: h } },
251
253
  { binding: 4, resource: { buffer: w } },
252
- { binding: 5, resource: { buffer: b } },
254
+ { binding: 5, resource: { buffer: y } },
253
255
  { binding: 6, resource: { buffer: B } },
254
256
  { binding: 7, resource: { buffer: x } }
255
257
  ]
256
- }), k = n.createCommandEncoder(), U = k.beginComputePass();
257
- U.setPipeline(this.pipeline), U.setBindGroup(0, S), U.dispatchWorkgroups(Math.ceil(r / 256)), U.end(), n.queue.submit([k.finish()]), m % 10 === 0 && (await n.queue.onSubmittedWorkDone(), o == null || o(m, h));
258
+ }), O = o.createCommandEncoder(), U = O.beginComputePass();
259
+ U.setPipeline(this.pipeline), U.setBindGroup(0, S), U.dispatchWorkgroups(Math.ceil(i / 256)), U.end(), o.queue.submit([O.finish()]), _ % 10 === 0 && (await o.queue.onSubmittedWorkDone(), a == null || a(_, l));
258
260
  }
259
- const G = n.createBuffer({
260
- size: e.byteLength,
261
+ const E = o.createBuffer({
262
+ size: n.byteLength,
261
263
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
262
- }), R = n.createCommandEncoder();
263
- R.copyBufferToBuffer(l, 0, G, 0, e.byteLength), n.queue.submit([R.finish()]), await G.mapAsync(GPUMapMode.READ);
264
- const O = new Float32Array(G.getMappedRange().slice(0));
265
- return G.unmap(), l.destroy(), d.destroy(), u.destroy(), g.destroy(), w.destroy(), b.destroy(), x.destroy(), B.destroy(), G.destroy(), O;
264
+ }), R = o.createCommandEncoder();
265
+ R.copyBufferToBuffer(h, 0, E, 0, n.byteLength), o.queue.submit([R.finish()]), await E.mapAsync(GPUMapMode.READ);
266
+ const k = new Float32Array(E.getMappedRange().slice(0));
267
+ return E.unmap(), h.destroy(), d.destroy(), u.destroy(), g.destroy(), w.destroy(), y.destroy(), x.destroy(), B.destroy(), E.destroy(), k;
266
268
  }
267
- makeBuffer(e, i) {
268
- const a = this.device.createBuffer({
269
- size: e.byteLength,
270
- usage: i,
269
+ makeBuffer(n, r) {
270
+ const s = this.device.createBuffer({
271
+ size: n.byteLength,
272
+ usage: r,
271
273
  mappedAtCreation: !0
272
274
  });
273
- return e instanceof Float32Array ? new Float32Array(a.getMappedRange()).set(e) : new Uint32Array(a.getMappedRange()).set(e), a.unmap(), a;
275
+ return n instanceof Float32Array ? new Float32Array(s.getMappedRange()).set(n) : new Uint32Array(s.getMappedRange()).set(n), s.unmap(), s;
274
276
  }
275
277
  }
276
- function P(t) {
277
- return Math.max(-4, Math.min(4, t));
278
+ function q(e) {
279
+ return Math.max(-4, Math.min(4, e));
278
280
  }
279
- function q(t, e, i, a, s, p, f, h) {
280
- const { a: c, b: o, gamma: n = 1, negativeSampleRate: r = 5 } = f, l = e.rows.length, d = new Uint32Array(e.rows), u = new Uint32Array(e.cols), g = new Float32Array(l).fill(0), _ = new Float32Array(l);
281
- for (let w = 0; w < l; w++)
282
- _[w] = i[w] / r;
281
+ function z(e, n, r, s, t, p, f, l) {
282
+ const { a: c, b: a, gamma: o = 1, negativeSampleRate: i = 5 } = f, h = n.rows.length, d = new Uint32Array(n.rows), u = new Uint32Array(n.cols), g = new Float32Array(h).fill(0), m = new Float32Array(h);
283
+ for (let w = 0; w < h; w++)
284
+ m[w] = r[w] / i;
283
285
  for (let w = 0; w < p; w++) {
284
- h == null || h(w, p);
285
- const y = 1 - w / p;
286
- for (let b = 0; b < l; b++) {
287
- if (g[b] > w) continue;
288
- const M = d[b], x = u[b];
286
+ l == null || l(w, p);
287
+ const b = 1 - w / p;
288
+ for (let y = 0; y < h; y++) {
289
+ if (g[y] > w) continue;
290
+ const v = d[y], x = u[y];
289
291
  let B = 0;
290
- for (let m = 0; m < s; m++) {
291
- const F = t[M * s + m] - t[x * s + m];
292
+ for (let _ = 0; _ < t; _++) {
293
+ const F = e[v * t + _] - e[x * t + _];
292
294
  B += F * F;
293
295
  }
294
- const G = Math.pow(B, o), R = -2 * c * o * (B > 0 ? G / B : 0) / (c * G + 1);
295
- for (let m = 0; m < s; m++) {
296
- const F = t[M * s + m] - t[x * s + m], A = P(R * F);
297
- t[M * s + m] += y * A;
296
+ const E = Math.pow(B, a), R = -2 * c * a * (B > 0 ? E / B : 0) / (c * E + 1);
297
+ for (let _ = 0; _ < t; _++) {
298
+ const F = e[v * t + _] - e[x * t + _], A = q(R * F);
299
+ e[v * t + _] += b * A, e[x * t + _] -= b * A;
298
300
  }
299
- g[b] += i[b];
300
- const O = _[b] > 0 ? Math.floor(i[b] / _[b]) : 0;
301
- for (let m = 0; m < O; m++) {
302
- const F = Math.floor(Math.random() * a);
303
- if (F === M) continue;
301
+ g[y] += r[y];
302
+ const k = m[y] > 0 ? Math.floor(r[y] / m[y]) : 0;
303
+ for (let _ = 0; _ < k; _++) {
304
+ const F = Math.floor(Math.random() * s);
305
+ if (F === v) continue;
304
306
  let A = 0;
305
- for (let S = 0; S < s; S++) {
306
- const k = t[M * s + S] - t[F * s + S];
307
- A += k * k;
307
+ for (let S = 0; S < t; S++) {
308
+ const O = e[v * t + S] - e[F * t + S];
309
+ A += O * O;
308
310
  }
309
- const v = Math.pow(A, o), N = 2 * n * o / ((1e-3 + A) * (c * v + 1));
310
- for (let S = 0; S < s; S++) {
311
- const k = t[M * s + S] - t[F * s + S], U = P(N * k);
312
- t[M * s + S] += y * U;
311
+ const M = Math.pow(A, a), N = 2 * o * a / ((1e-3 + A) * (c * M + 1));
312
+ for (let S = 0; S < t; S++) {
313
+ const O = e[v * t + S] - e[F * t + S], U = q(N * O);
314
+ e[v * t + S] += b * U;
313
315
  }
314
316
  }
315
- _[b] += i[b] / r;
317
+ m[y] += r[y] / i;
316
318
  }
317
319
  }
318
- return t;
320
+ return e;
319
321
  }
320
- function $(t, e, i, a, s, p, f, h, c, o) {
321
- const { a: n, b: r, gamma: l = 1, negativeSampleRate: d = 5 } = c, u = i.rows.length, g = new Uint32Array(i.rows), _ = new Uint32Array(i.cols), w = new Float32Array(u).fill(0), y = new Float32Array(u);
322
- for (let b = 0; b < u; b++)
323
- y[b] = a[b] / d;
324
- for (let b = 0; b < h; b++) {
325
- const M = 1 - b / h;
322
+ function $(e, n, r, s, t, p, f, l, c, a) {
323
+ const { a: o, b: i, gamma: h = 1, negativeSampleRate: d = 5 } = c, u = r.rows.length, g = new Uint32Array(r.rows), m = new Uint32Array(r.cols), w = new Float32Array(u).fill(0), b = new Float32Array(u);
324
+ for (let y = 0; y < u; y++)
325
+ b[y] = s[y] / d;
326
+ for (let y = 0; y < l; y++) {
327
+ const v = 1 - y / l;
326
328
  for (let x = 0; x < u; x++) {
327
- if (w[x] > b) continue;
328
- const B = g[x], G = _[x];
329
+ if (w[x] > y) continue;
330
+ const B = g[x], E = m[x];
329
331
  let R = 0;
330
332
  for (let A = 0; A < f; A++) {
331
- const v = t[B * f + A] - e[G * f + A];
332
- R += v * v;
333
+ const M = e[B * f + A] - n[E * f + A];
334
+ R += M * M;
333
335
  }
334
- const O = Math.pow(R, r), m = -2 * n * r * (R > 0 ? O / R : 0) / (n * O + 1);
336
+ const k = Math.pow(R, i), _ = -2 * o * i * (R > 0 ? k / R : 0) / (o * k + 1);
335
337
  for (let A = 0; A < f; A++) {
336
- const v = t[B * f + A] - e[G * f + A];
337
- t[B * f + A] += M * P(m * v);
338
+ const M = e[B * f + A] - n[E * f + A];
339
+ e[B * f + A] += v * q(_ * M);
338
340
  }
339
- w[x] += a[x];
340
- const F = y[x] > 0 ? Math.floor(a[x] / y[x]) : 0;
341
+ w[x] += s[x];
342
+ const F = b[x] > 0 ? Math.floor(s[x] / b[x]) : 0;
341
343
  for (let A = 0; A < F; A++) {
342
- const v = Math.floor(Math.random() * p);
343
- if (v === G) continue;
344
+ const M = Math.floor(Math.random() * p);
345
+ if (M === E) continue;
344
346
  let N = 0;
345
347
  for (let U = 0; U < f; U++) {
346
- const z = t[B * f + U] - e[v * f + U];
347
- N += z * z;
348
+ const P = e[B * f + U] - n[M * f + U];
349
+ N += P * P;
348
350
  }
349
- const S = Math.pow(N, r), k = 2 * l * r / ((1e-3 + N) * (n * S + 1));
351
+ const S = Math.pow(N, i), O = 2 * h * i / ((1e-3 + N) * (o * S + 1));
350
352
  for (let U = 0; U < f; U++) {
351
- const z = t[B * f + U] - e[v * f + U];
352
- t[B * f + U] += M * P(k * z);
353
+ const P = e[B * f + U] - n[M * f + U];
354
+ e[B * f + U] += v * q(O * P);
353
355
  }
354
356
  }
355
- y[x] += a[x] / d;
357
+ b[x] += s[x] / d;
356
358
  }
357
359
  }
358
- return t;
360
+ return e;
359
361
  }
360
- function K() {
362
+ function j() {
361
363
  return typeof navigator < "u" && !!navigator.gpu;
362
364
  }
363
- async function ae(t, e = {}, i) {
365
+ async function ae(e, n = {}, r) {
364
366
  const {
365
- nComponents: a = 2,
366
- nNeighbors: s = 15,
367
+ nComponents: s = 2,
368
+ nNeighbors: t = 15,
367
369
  minDist: p = 0.1,
368
370
  spread: f = 1,
369
- hnsw: h = {}
370
- } = e, c = e.nEpochs ?? (t.length > 1e4 ? 200 : 500);
371
+ hnsw: l = {}
372
+ } = n, c = n.nEpochs ?? (e.length > 1e4 ? 200 : 500);
371
373
  console.time("knn");
372
- const { indices: o, distances: n } = await Y(t, s, {
373
- M: h.M ?? 16,
374
- efConstruction: h.efConstruction ?? 200,
375
- efSearch: h.efSearch ?? 50
374
+ const { indices: a, distances: o } = await Y(e, t, {
375
+ M: l.M ?? 16,
376
+ efConstruction: l.efConstruction ?? 200,
377
+ efSearch: l.efSearch ?? 50
376
378
  });
377
379
  console.timeEnd("knn"), console.time("fuzzy-set");
378
- const r = D(o, n, s);
380
+ const i = D(a, o, t);
379
381
  console.timeEnd("fuzzy-set");
380
- const { a: l, b: d } = j(p, f), u = I(r.vals, c), g = t.length, _ = new Float32Array(g * a);
381
- for (let y = 0; y < _.length; y++)
382
- _[y] = Math.random() * 20 - 10;
382
+ const { a: h, b: d } = K(p, f), u = I(i.vals, c), g = e.length, m = new Float32Array(g * s);
383
+ for (let b = 0; b < m.length; b++)
384
+ m[b] = Math.random() * 20 - 10;
383
385
  console.time("sgd");
384
386
  let w;
385
- if (K())
387
+ if (j())
386
388
  try {
387
- const y = new W();
388
- await y.init(), w = await y.optimize(
389
- _,
390
- new Uint32Array(r.rows),
391
- new Uint32Array(r.cols),
389
+ const b = new W();
390
+ await b.init(), w = await b.optimize(
391
+ m,
392
+ new Uint32Array(i.rows),
393
+ new Uint32Array(i.cols),
392
394
  u,
393
395
  g,
394
- a,
396
+ s,
395
397
  c,
396
- { a: l, b: d, gamma: 1, negativeSampleRate: 5 },
397
- i
398
+ { a: h, b: d, gamma: 1, negativeSampleRate: 5 },
399
+ r
398
400
  );
399
- } catch (y) {
400
- console.warn("WebGPU SGD failed, falling back to CPU:", y), w = q(_, r, u, g, a, c, { a: l, b: d }, i);
401
+ } catch (b) {
402
+ console.warn("WebGPU SGD failed, falling back to CPU:", b), w = z(m, i, u, g, s, c, { a: h, b: d }, r);
401
403
  }
402
404
  else
403
- w = q(_, r, u, g, a, c, { a: l, b: d }, i);
405
+ w = z(m, i, u, g, s, c, { a: h, b: d }, r);
404
406
  return console.timeEnd("sgd"), w;
405
407
  }
406
- function j(t, e) {
407
- if (Math.abs(e - 1) < 1e-6 && Math.abs(t - 0.1) < 1e-6)
408
+ function K(e, n) {
409
+ if (Math.abs(n - 1) < 1e-6 && Math.abs(e - 0.1) < 1e-6)
408
410
  return { a: 1.9292, b: 0.7915 };
409
- if (Math.abs(e - 1) < 1e-6 && Math.abs(t - 0) < 1e-6)
411
+ if (Math.abs(n - 1) < 1e-6 && Math.abs(e - 0) < 1e-6)
410
412
  return { a: 1.8956, b: 0.8006 };
411
- if (Math.abs(e - 1) < 1e-6 && Math.abs(t - 0.5) < 1e-6)
413
+ if (Math.abs(n - 1) < 1e-6 && Math.abs(e - 0.5) < 1e-6)
412
414
  return { a: 1.5769, b: 0.8951 };
413
- const i = ee(t, e);
414
- return { a: te(t, e, i), b: i };
415
+ const r = ee(e, n);
416
+ return { a: ne(e, n, r), b: r };
415
417
  }
416
- function ee(t, e) {
417
- return 1 / (e * 1.2);
418
+ function ee(e, n) {
419
+ return 1 / (n * 1.2);
418
420
  }
419
- function te(t, e, i) {
420
- return t < 1e-6 ? 1.8956 : (1 / (1 + 1e-3) - 1) / -Math.pow(t, 2 * i);
421
+ function ne(e, n, r) {
422
+ return e < 1e-6 ? 1.8956 : (1 / (1 + 1e-3) - 1) / -Math.pow(e, 2 * r);
421
423
  }
422
424
  class re {
423
- constructor(e = {}) {
424
- E(this, "_nComponents");
425
- E(this, "_nNeighbors");
426
- E(this, "_minDist");
427
- E(this, "_spread");
428
- E(this, "_nEpochs");
429
- E(this, "_hnswOpts");
430
- E(this, "_a");
431
- E(this, "_b");
425
+ constructor(n = {}) {
426
+ G(this, "_nComponents");
427
+ G(this, "_nNeighbors");
428
+ G(this, "_minDist");
429
+ G(this, "_spread");
430
+ G(this, "_nEpochs");
431
+ G(this, "_hnswOpts");
432
+ G(this, "_a");
433
+ G(this, "_b");
432
434
  /** The low-dimensional embedding produced by the last fit() call. */
433
- E(this, "embedding", null);
434
- E(this, "_hnswIndex", null);
435
- E(this, "_nTrain", 0);
436
- 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 ?? {};
437
- const { a: i, b: a } = j(this._minDist, this._spread);
438
- this._a = i, this._b = a;
435
+ G(this, "embedding", null);
436
+ G(this, "_hnswIndex", null);
437
+ G(this, "_nTrain", 0);
438
+ this._nComponents = n.nComponents ?? 2, this._nNeighbors = n.nNeighbors ?? 15, this._minDist = n.minDist ?? 0.1, this._spread = n.spread ?? 1, this._nEpochs = n.nEpochs, this._hnswOpts = n.hnsw ?? {};
439
+ const { a: r, b: s } = K(this._minDist, this._spread);
440
+ this._a = r, this._b = s;
439
441
  }
440
442
  /**
441
443
  * Train UMAP on `vectors`.
@@ -443,45 +445,45 @@ class re {
443
445
  * index so that transform() can project new points later.
444
446
  * Returns `this` for chaining.
445
447
  */
446
- async fit(e, i) {
447
- const a = e.length, s = this._nEpochs ?? (a > 1e4 ? 200 : 500), { M: p = 16, efConstruction: f = 200, efSearch: h = 50 } = this._hnswOpts;
448
+ async fit(n, r) {
449
+ const s = n.length, t = this._nEpochs ?? (s > 1e4 ? 200 : 500), { M: p = 16, efConstruction: f = 200, efSearch: l = 50 } = this._hnswOpts;
448
450
  console.time("knn");
449
- const { knn: c, index: o } = await J(e, this._nNeighbors, {
451
+ const { knn: c, index: a } = await Q(n, this._nNeighbors, {
450
452
  M: p,
451
453
  efConstruction: f,
452
- efSearch: h
454
+ efSearch: l
453
455
  });
454
- this._hnswIndex = o, this._nTrain = a, console.timeEnd("knn"), console.time("fuzzy-set");
455
- const n = D(c.indices, c.distances, this._nNeighbors);
456
+ this._hnswIndex = a, this._nTrain = s, console.timeEnd("knn"), console.time("fuzzy-set");
457
+ const o = D(c.indices, c.distances, this._nNeighbors);
456
458
  console.timeEnd("fuzzy-set");
457
- const r = I(n.vals, s), l = new Float32Array(a * this._nComponents);
458
- for (let d = 0; d < l.length; d++)
459
- l[d] = Math.random() * 20 - 10;
460
- if (console.time("sgd"), K())
459
+ const i = I(o.vals, t), h = new Float32Array(s * this._nComponents);
460
+ for (let d = 0; d < h.length; d++)
461
+ h[d] = Math.random() * 20 - 10;
462
+ if (console.time("sgd"), j())
461
463
  try {
462
464
  const d = new W();
463
465
  await d.init(), this.embedding = await d.optimize(
464
- l,
465
- new Uint32Array(n.rows),
466
- new Uint32Array(n.cols),
467
- r,
468
- a,
469
- this._nComponents,
466
+ h,
467
+ new Uint32Array(o.rows),
468
+ new Uint32Array(o.cols),
469
+ i,
470
470
  s,
471
+ this._nComponents,
472
+ t,
471
473
  { a: this._a, b: this._b, gamma: 1, negativeSampleRate: 5 },
472
- i
474
+ r
473
475
  );
474
476
  } catch (d) {
475
- console.warn("WebGPU SGD failed, falling back to CPU:", d), this.embedding = q(l, n, r, a, this._nComponents, s, {
477
+ console.warn("WebGPU SGD failed, falling back to CPU:", d), this.embedding = z(h, o, i, s, this._nComponents, t, {
476
478
  a: this._a,
477
479
  b: this._b
478
- }, i);
480
+ }, r);
479
481
  }
480
482
  else
481
- this.embedding = q(l, n, r, a, this._nComponents, s, {
483
+ this.embedding = z(h, o, i, s, this._nComponents, t, {
482
484
  a: this._a,
483
485
  b: this._b
484
- }, i);
486
+ }, r);
485
487
  return console.timeEnd("sgd"), this;
486
488
  }
487
489
  /**
@@ -495,35 +497,35 @@ class re {
495
497
  * returned embedding to [0, 1]. The stored training embedding is never
496
498
  * mutated. Defaults to `false`.
497
499
  */
498
- async transform(e, i = !1) {
500
+ async transform(n, r = !1) {
499
501
  if (!this._hnswIndex || !this.embedding)
500
502
  throw new Error("UMAP.transform() must be called after fit()");
501
- const a = e.length, s = this._nEpochs ?? (this._nTrain > 1e4 ? 200 : 500), p = Math.max(100, Math.floor(s / 4)), f = this._hnswIndex.searchKnn(e, this._nNeighbors), h = Q(f.indices, f.distances, this._nNeighbors), c = new Uint32Array(h.rows), o = new Uint32Array(h.cols), n = new Float32Array(a), r = new Float32Array(a * this._nComponents);
503
+ const s = n.length, t = this._nEpochs ?? (this._nTrain > 1e4 ? 200 : 500), p = Math.max(100, Math.floor(t / 4)), f = this._hnswIndex.searchKnn(n, this._nNeighbors), l = J(f.indices, f.distances, this._nNeighbors), c = new Uint32Array(l.rows), a = new Uint32Array(l.cols), o = new Float32Array(s), i = new Float32Array(s * this._nComponents);
502
504
  for (let u = 0; u < c.length; u++) {
503
- const g = c[u], _ = o[u], w = h.vals[u];
504
- n[g] += w;
505
- for (let y = 0; y < this._nComponents; y++)
506
- r[g * this._nComponents + y] += w * this.embedding[_ * this._nComponents + y];
505
+ const g = c[u], m = a[u], w = l.vals[u];
506
+ o[g] += w;
507
+ for (let b = 0; b < this._nComponents; b++)
508
+ i[g * this._nComponents + b] += w * this.embedding[m * this._nComponents + b];
507
509
  }
508
- for (let u = 0; u < a; u++)
509
- if (n[u] > 0)
510
+ for (let u = 0; u < s; u++)
511
+ if (o[u] > 0)
510
512
  for (let g = 0; g < this._nComponents; g++)
511
- r[u * this._nComponents + g] /= n[u];
513
+ i[u * this._nComponents + g] /= o[u];
512
514
  else
513
515
  for (let g = 0; g < this._nComponents; g++)
514
- r[u * this._nComponents + g] = Math.random() * 20 - 10;
515
- const l = I(h.vals, p), d = $(
516
- r,
516
+ i[u * this._nComponents + g] = Math.random() * 20 - 10;
517
+ const h = I(l.vals, p), d = $(
518
+ i,
517
519
  this.embedding,
518
- h,
519
520
  l,
520
- a,
521
+ h,
522
+ s,
521
523
  this._nTrain,
522
524
  this._nComponents,
523
525
  p,
524
526
  { a: this._a, b: this._b }
525
527
  );
526
- return i ? T(d, a, this._nComponents) : d;
528
+ return r ? T(d, s, this._nComponents) : d;
527
529
  }
528
530
  /**
529
531
  * Convenience method equivalent to `fit(vectors)` followed by
@@ -534,37 +536,37 @@ class re {
534
536
  * returned embedding to [0, 1]. `this.embedding` is never mutated.
535
537
  * Defaults to `false`.
536
538
  */
537
- async fit_transform(e, i, a = !1) {
538
- return await this.fit(e, i), a ? T(this.embedding, e.length, this._nComponents) : this.embedding;
539
+ async fit_transform(n, r, s = !1) {
540
+ return await this.fit(n, r), s ? T(this.embedding, n.length, this._nComponents) : this.embedding;
539
541
  }
540
542
  }
541
- function T(t, e, i) {
542
- const a = new Float32Array(t.length);
543
- for (let s = 0; s < i; s++) {
543
+ function T(e, n, r) {
544
+ const s = new Float32Array(e.length);
545
+ for (let t = 0; t < r; t++) {
544
546
  let p = 1 / 0, f = -1 / 0;
545
- for (let c = 0; c < e; c++) {
546
- const o = t[c * i + s];
547
- o < p && (p = o), o > f && (f = o);
547
+ for (let c = 0; c < n; c++) {
548
+ const a = e[c * r + t];
549
+ a < p && (p = a), a > f && (f = a);
548
550
  }
549
- const h = f - p;
550
- for (let c = 0; c < e; c++)
551
- a[c * i + s] = h > 0 ? (t[c * i + s] - p) / h : 0;
551
+ const l = f - p;
552
+ for (let c = 0; c < n; c++)
553
+ s[c * r + t] = l > 0 ? (e[c * r + t] - p) / l : 0;
552
554
  }
553
- return a;
555
+ return s;
554
556
  }
555
- function I(t, e) {
556
- let i = -1 / 0;
557
- for (let s = 0; s < t.length; s++)
558
- t[s] > i && (i = t[s]);
559
- const a = new Float32Array(t.length);
560
- for (let s = 0; s < t.length; s++) {
561
- const p = t[s] / i;
562
- a[s] = p > 0 ? e / p : -1;
557
+ function I(e, n) {
558
+ let r = -1 / 0;
559
+ for (let t = 0; t < e.length; t++)
560
+ e[t] > r && (r = e[t]);
561
+ const s = new Float32Array(e.length);
562
+ for (let t = 0; t < e.length; t++) {
563
+ const p = e[t] / r;
564
+ s[t] = p > 0 ? n / p : -1;
563
565
  }
564
- return a;
566
+ return s;
565
567
  }
566
568
  export {
567
569
  re as UMAP,
568
570
  ae as fit,
569
- K as isWebGPUAvailable
571
+ j as isWebGPUAvailable
570
572
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umap-gpu",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "UMAP with HNSW kNN and WebGPU-accelerated SGD",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",