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