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.
- package/dist/index.js +271 -270
- package/package.json +26 -2
package/dist/index.js
CHANGED
|
@@ -1,95 +1,90 @@
|
|
|
1
1
|
var H = Object.defineProperty;
|
|
2
|
-
var V = (
|
|
3
|
-
var
|
|
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(
|
|
6
|
-
const { M:
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
for (let
|
|
10
|
-
const
|
|
11
|
-
|
|
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:
|
|
13
|
+
return { indices: o, distances: i };
|
|
14
14
|
}
|
|
15
|
-
async function
|
|
16
|
-
const { M:
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
for (let
|
|
20
|
-
const u =
|
|
21
|
-
|
|
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:
|
|
24
|
-
searchKnn(
|
|
25
|
-
const g = [],
|
|
26
|
-
for (const w of
|
|
27
|
-
const
|
|
28
|
-
g.push(
|
|
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(
|
|
35
|
-
const
|
|
36
|
-
for (let
|
|
37
|
-
for (let
|
|
38
|
-
const
|
|
39
|
-
|
|
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(
|
|
41
|
+
return { ...X(d, c, a, n, s), nVertices: n };
|
|
42
42
|
}
|
|
43
|
-
function
|
|
44
|
-
const
|
|
45
|
-
for (let
|
|
46
|
-
for (let
|
|
47
|
-
const
|
|
48
|
-
f.push(
|
|
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(
|
|
52
|
+
cols: new Float32Array(d),
|
|
53
53
|
vals: new Float32Array(c),
|
|
54
|
-
nVertices:
|
|
54
|
+
nVertices: s
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
function L(
|
|
58
|
-
const
|
|
59
|
-
for (let f = 0; f <
|
|
60
|
-
const
|
|
61
|
-
p[f] =
|
|
62
|
-
let c = 0,
|
|
63
|
-
const
|
|
64
|
-
for (let
|
|
65
|
-
let
|
|
66
|
-
for (let u = 1; u <
|
|
67
|
-
|
|
68
|
-
if (Math.abs(
|
|
69
|
-
|
|
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
|
-
|
|
71
|
+
n[f] = o;
|
|
72
72
|
}
|
|
73
|
-
return { sigmas:
|
|
73
|
+
return { sigmas: n, rhos: p };
|
|
74
74
|
}
|
|
75
|
-
function X(
|
|
76
|
-
const p = /* @__PURE__ */ new Map()
|
|
77
|
-
|
|
78
|
-
p.set(
|
|
79
|
-
|
|
80
|
-
for (
|
|
81
|
-
|
|
82
|
-
|
|
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(
|
|
91
|
-
cols: new Float32Array(
|
|
92
|
-
vals: new Float32Array(
|
|
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
|
|
154
|
-
|
|
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
|
-
|
|
201
|
-
|
|
201
|
+
G(this, "device");
|
|
202
|
+
G(this, "pipeline");
|
|
202
203
|
}
|
|
203
204
|
async init() {
|
|
204
|
-
const
|
|
205
|
-
if (!
|
|
206
|
-
this.device = await
|
|
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(
|
|
228
|
-
const { device:
|
|
229
|
-
|
|
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
|
-
),
|
|
232
|
-
for (let
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
for (let
|
|
236
|
-
|
|
237
|
-
const x = this.makeBuffer(
|
|
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
|
|
242
|
-
const F = 1 -
|
|
243
|
-
|
|
244
|
-
const S =
|
|
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:
|
|
249
|
+
{ binding: 1, resource: { buffer: l } },
|
|
249
250
|
{ binding: 2, resource: { buffer: u } },
|
|
250
|
-
{ binding: 3, resource: { buffer:
|
|
251
|
+
{ binding: 3, resource: { buffer: h } },
|
|
251
252
|
{ binding: 4, resource: { buffer: w } },
|
|
252
|
-
{ binding: 5, resource: { buffer:
|
|
253
|
+
{ binding: 5, resource: { buffer: y } },
|
|
253
254
|
{ binding: 6, resource: { buffer: B } },
|
|
254
255
|
{ binding: 7, resource: { buffer: x } }
|
|
255
256
|
]
|
|
256
|
-
}),
|
|
257
|
-
U.setPipeline(this.pipeline), U.setBindGroup(0, S), U.dispatchWorkgroups(Math.ceil(
|
|
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
|
|
260
|
-
size:
|
|
260
|
+
const E = o.createBuffer({
|
|
261
|
+
size: t.byteLength,
|
|
261
262
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
262
|
-
}), R =
|
|
263
|
-
R.copyBufferToBuffer(
|
|
264
|
-
const
|
|
265
|
-
return
|
|
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(
|
|
268
|
-
const
|
|
269
|
-
size:
|
|
270
|
-
usage:
|
|
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
|
|
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
|
|
277
|
-
return Math.max(-4, Math.min(4,
|
|
277
|
+
function q(e) {
|
|
278
|
+
return Math.max(-4, Math.min(4, e));
|
|
278
279
|
}
|
|
279
|
-
function
|
|
280
|
-
const { a: c, b:
|
|
281
|
-
for (let w = 0; w <
|
|
282
|
-
|
|
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
|
-
|
|
285
|
-
const
|
|
286
|
-
for (let
|
|
287
|
-
if (g[
|
|
288
|
-
const
|
|
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
|
|
291
|
-
const F =
|
|
291
|
+
for (let _ = 0; _ < n; _++) {
|
|
292
|
+
const F = e[v * n + _] - e[x * n + _];
|
|
292
293
|
B += F * F;
|
|
293
294
|
}
|
|
294
|
-
const
|
|
295
|
-
for (let
|
|
296
|
-
const F =
|
|
297
|
-
|
|
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[
|
|
300
|
-
const
|
|
301
|
-
for (let
|
|
302
|
-
const F = Math.floor(Math.random() *
|
|
303
|
-
if (F ===
|
|
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 <
|
|
306
|
-
const
|
|
307
|
-
A +=
|
|
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
|
|
310
|
-
for (let S = 0; S <
|
|
311
|
-
const
|
|
312
|
-
|
|
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
|
-
|
|
316
|
+
m[y] += r[y] / i;
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
|
-
return
|
|
319
|
+
return e;
|
|
319
320
|
}
|
|
320
|
-
function $(
|
|
321
|
-
const { a:
|
|
322
|
-
for (let
|
|
323
|
-
y
|
|
324
|
-
for (let
|
|
325
|
-
const
|
|
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] >
|
|
328
|
-
const B = 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
|
|
332
|
-
R +=
|
|
332
|
+
const M = e[B * f + A] - t[E * f + A];
|
|
333
|
+
R += M * M;
|
|
333
334
|
}
|
|
334
|
-
const
|
|
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
|
|
337
|
-
|
|
337
|
+
const M = e[B * f + A] - t[E * f + A];
|
|
338
|
+
e[B * f + A] += v * q(_ * M);
|
|
338
339
|
}
|
|
339
|
-
w[x] +=
|
|
340
|
-
const F =
|
|
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
|
|
343
|
-
if (
|
|
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
|
|
347
|
-
N +=
|
|
347
|
+
const P = e[B * f + U] - t[M * f + U];
|
|
348
|
+
N += P * P;
|
|
348
349
|
}
|
|
349
|
-
const S = Math.pow(N,
|
|
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
|
|
352
|
-
|
|
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
|
-
|
|
356
|
+
b[x] += s[x] / l;
|
|
356
357
|
}
|
|
357
358
|
}
|
|
358
|
-
return
|
|
359
|
+
return e;
|
|
359
360
|
}
|
|
360
361
|
function K() {
|
|
361
362
|
return typeof navigator < "u" && !!navigator.gpu;
|
|
362
363
|
}
|
|
363
|
-
async function ae(
|
|
364
|
+
async function ae(e, t = {}, r) {
|
|
364
365
|
const {
|
|
365
|
-
nComponents:
|
|
366
|
-
nNeighbors:
|
|
366
|
+
nComponents: s = 2,
|
|
367
|
+
nNeighbors: n = 15,
|
|
367
368
|
minDist: p = 0.1,
|
|
368
369
|
spread: f = 1,
|
|
369
|
-
hnsw:
|
|
370
|
-
} =
|
|
370
|
+
hnsw: d = {}
|
|
371
|
+
} = t, c = t.nEpochs ?? (e.length > 1e4 ? 200 : 500);
|
|
371
372
|
console.time("knn");
|
|
372
|
-
const { indices:
|
|
373
|
-
M:
|
|
374
|
-
efConstruction:
|
|
375
|
-
efSearch:
|
|
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
|
|
379
|
+
const i = D(a, o, n);
|
|
379
380
|
console.timeEnd("fuzzy-set");
|
|
380
|
-
const { a:
|
|
381
|
-
for (let
|
|
382
|
-
|
|
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
|
|
388
|
-
await
|
|
389
|
-
|
|
390
|
-
new Uint32Array(
|
|
391
|
-
new Uint32Array(
|
|
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
|
-
|
|
395
|
+
s,
|
|
395
396
|
c,
|
|
396
|
-
{ a:
|
|
397
|
-
|
|
397
|
+
{ a: h, b: l, gamma: 1, negativeSampleRate: 5 },
|
|
398
|
+
r
|
|
398
399
|
);
|
|
399
|
-
} catch (
|
|
400
|
-
console.warn("WebGPU SGD failed, falling back to CPU:",
|
|
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 =
|
|
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(
|
|
407
|
-
if (Math.abs(
|
|
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(
|
|
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(
|
|
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
|
|
414
|
-
return { a: te(
|
|
414
|
+
const r = ee(e, t);
|
|
415
|
+
return { a: te(e, t, r), b: r };
|
|
415
416
|
}
|
|
416
|
-
function ee(
|
|
417
|
-
return 1 / (
|
|
417
|
+
function ee(e, t) {
|
|
418
|
+
return 1 / (t * 1.2);
|
|
418
419
|
}
|
|
419
|
-
function te(
|
|
420
|
-
return
|
|
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(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
this._nComponents =
|
|
437
|
-
const { a:
|
|
438
|
-
this._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(
|
|
447
|
-
const
|
|
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:
|
|
450
|
+
const { knn: c, index: a } = await Q(t, this._nNeighbors, {
|
|
450
451
|
M: p,
|
|
451
452
|
efConstruction: f,
|
|
452
|
-
efSearch:
|
|
453
|
+
efSearch: d
|
|
453
454
|
});
|
|
454
|
-
this._hnswIndex =
|
|
455
|
-
const
|
|
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
|
|
458
|
-
for (let
|
|
459
|
-
l
|
|
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
|
|
463
|
-
await
|
|
464
|
-
|
|
465
|
-
new Uint32Array(
|
|
466
|
-
new Uint32Array(
|
|
467
|
-
|
|
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
|
-
|
|
473
|
+
r
|
|
473
474
|
);
|
|
474
|
-
} catch (
|
|
475
|
-
console.warn("WebGPU SGD failed, falling back to CPU:",
|
|
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
|
-
},
|
|
479
|
+
}, r);
|
|
479
480
|
}
|
|
480
481
|
else
|
|
481
|
-
this.embedding =
|
|
482
|
+
this.embedding = z(h, o, i, s, this._nComponents, n, {
|
|
482
483
|
a: this._a,
|
|
483
484
|
b: this._b
|
|
484
|
-
},
|
|
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(
|
|
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
|
|
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],
|
|
504
|
-
|
|
505
|
-
for (let
|
|
506
|
-
|
|
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 <
|
|
509
|
-
if (
|
|
509
|
+
for (let u = 0; u < s; u++)
|
|
510
|
+
if (o[u] > 0)
|
|
510
511
|
for (let g = 0; g < this._nComponents; g++)
|
|
511
|
-
|
|
512
|
+
i[u * this._nComponents + g] /= o[u];
|
|
512
513
|
else
|
|
513
514
|
for (let g = 0; g < this._nComponents; g++)
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
538
|
-
return await this.fit(
|
|
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(
|
|
542
|
-
const
|
|
543
|
-
for (let
|
|
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 <
|
|
546
|
-
const
|
|
547
|
-
|
|
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
|
|
550
|
-
for (let c = 0; c <
|
|
551
|
-
|
|
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
|
|
554
|
+
return s;
|
|
554
555
|
}
|
|
555
|
-
function I(
|
|
556
|
-
let
|
|
557
|
-
for (let
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
for (let
|
|
561
|
-
const p =
|
|
562
|
-
|
|
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
|
|
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.
|
|
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/
|
|
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",
|