umap-gpu 0.2.8 → 0.2.10

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