umap-gpu 0.2.6 → 0.2.9
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/fallback/cpu-sgd.d.ts +2 -2
- package/dist/gpu/sgd.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +307 -282
- package/dist/umap.d.ts +20 -4
- package/package.json +33 -3
|
@@ -9,7 +9,7 @@ export interface CPUSgdParams {
|
|
|
9
9
|
* CPU fallback SGD optimizer for environments without WebGPU.
|
|
10
10
|
* Mirrors the GPU shader logic: per-edge attraction + negative-sample repulsion.
|
|
11
11
|
*/
|
|
12
|
-
export declare function cpuSgd(embedding: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: CPUSgdParams): Float32Array;
|
|
12
|
+
export declare function cpuSgd(embedding: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: CPUSgdParams, onProgress?: (epoch: number, nEpochs: number) => void): Float32Array;
|
|
13
13
|
/**
|
|
14
14
|
* CPU SGD for UMAP.transform(): optimizes only the new-point embeddings.
|
|
15
15
|
* The training embedding is read-only; attraction pulls new points toward
|
|
@@ -26,4 +26,4 @@ export declare function cpuSgd(embedding: Float32Array, graph: FuzzyGraph, epoch
|
|
|
26
26
|
* @param nEpochs - Number of optimization epochs
|
|
27
27
|
* @param params - UMAP curve parameters
|
|
28
28
|
*/
|
|
29
|
-
export declare function cpuSgdTransform(embeddingNew: Float32Array, embeddingTrain: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nNew: number, nTrain: number, nComponents: number, nEpochs: number, params: CPUSgdParams): Float32Array;
|
|
29
|
+
export declare function cpuSgdTransform(embeddingNew: Float32Array, embeddingTrain: Float32Array, graph: FuzzyGraph, epochsPerSample: Float32Array, nNew: number, nTrain: number, nComponents: number, nEpochs: number, params: CPUSgdParams, onProgress?: (epoch: number, nEpochs: number) => void): Float32Array;
|
package/dist/gpu/sgd.d.ts
CHANGED
|
@@ -25,6 +25,6 @@ export declare class GPUSgd {
|
|
|
25
25
|
* @param params - UMAP curve parameters and repulsion settings
|
|
26
26
|
* @returns Optimized embedding as Float32Array
|
|
27
27
|
*/
|
|
28
|
-
optimize(embedding: Float32Array, head: Uint32Array, tail: Uint32Array, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: SGDParams): Promise<Float32Array>;
|
|
28
|
+
optimize(embedding: Float32Array, head: Uint32Array, tail: Uint32Array, epochsPerSample: Float32Array, nVertices: number, nComponents: number, nEpochs: number, params: SGDParams, onProgress?: (epoch: number, nEpochs: number) => void): Promise<Float32Array>;
|
|
29
29
|
private makeBuffer;
|
|
30
30
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { fit, UMAP } from './umap';
|
|
2
|
-
export type { UMAPOptions } from './umap';
|
|
2
|
+
export type { UMAPOptions, ProgressCallback } from './umap';
|
|
3
3
|
export type { KNNResult, HNSWOptions, HNSWSearchableIndex } from './hnsw-knn';
|
|
4
4
|
export type { FuzzyGraph } from './fuzzy-set';
|
|
5
5
|
export { isWebGPUAvailable } from './gpu/device';
|
package/dist/index.js
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { loadHnswlib as
|
|
5
|
-
async function
|
|
6
|
-
const { M:
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
for (let
|
|
10
|
-
const d =
|
|
11
|
-
|
|
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);
|
|
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));
|
|
12
12
|
}
|
|
13
|
-
return { indices:
|
|
13
|
+
return { indices: n, distances: r };
|
|
14
14
|
}
|
|
15
|
-
async function
|
|
16
|
-
const { M:
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
for (let d = 0; d <
|
|
20
|
-
const
|
|
21
|
-
|
|
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: _ }) => _));
|
|
22
22
|
}
|
|
23
|
-
return { knn: { indices:
|
|
24
|
-
searchKnn(d,
|
|
25
|
-
const g = [],
|
|
26
|
-
for (const
|
|
27
|
-
const
|
|
28
|
-
g.push(
|
|
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));
|
|
29
29
|
}
|
|
30
|
-
return { indices: g, distances:
|
|
30
|
+
return { indices: g, distances: _ };
|
|
31
31
|
}
|
|
32
32
|
} };
|
|
33
33
|
}
|
|
34
|
-
function
|
|
35
|
-
const
|
|
36
|
-
for (let
|
|
37
|
-
for (let
|
|
38
|
-
const d = e[
|
|
39
|
-
|
|
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);
|
|
40
40
|
}
|
|
41
|
-
return { ...
|
|
41
|
+
return { ...X(h, c, o, s, a), nVertices: s };
|
|
42
42
|
}
|
|
43
|
-
function
|
|
44
|
-
const
|
|
45
|
-
for (let
|
|
46
|
-
for (let
|
|
47
|
-
const
|
|
48
|
-
|
|
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);
|
|
49
49
|
}
|
|
50
50
|
return {
|
|
51
|
-
rows: new Float32Array(
|
|
52
|
-
cols: new Float32Array(
|
|
53
|
-
vals: new Float32Array(
|
|
54
|
-
nVertices:
|
|
51
|
+
rows: new Float32Array(f),
|
|
52
|
+
cols: new Float32Array(h),
|
|
53
|
+
vals: new Float32Array(c),
|
|
54
|
+
nVertices: a
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
const
|
|
59
|
-
for (let
|
|
60
|
-
const
|
|
61
|
-
p[
|
|
62
|
-
let
|
|
63
|
-
const
|
|
64
|
-
for (let
|
|
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
65
|
let d = 0;
|
|
66
|
-
for (let
|
|
67
|
-
d += Math.exp(-Math.max(0,
|
|
68
|
-
if (Math.abs(d -
|
|
69
|
-
d >
|
|
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);
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
s[f] = n;
|
|
72
72
|
}
|
|
73
|
-
return { sigmas:
|
|
73
|
+
return { sigmas: s, rhos: p };
|
|
74
74
|
}
|
|
75
|
-
function
|
|
76
|
-
const p = /* @__PURE__ */ new Map(),
|
|
77
|
-
const d =
|
|
78
|
-
p.set(d, (p.get(d) ?? 0) +
|
|
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
79
|
};
|
|
80
|
-
for (let
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
for (const [
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
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
87
|
);
|
|
88
88
|
}
|
|
89
89
|
return {
|
|
90
|
-
rows: new Float32Array(
|
|
91
|
-
cols: new Float32Array(
|
|
92
|
-
vals: new Float32Array(
|
|
90
|
+
rows: new Float32Array(h),
|
|
91
|
+
cols: new Float32Array(c),
|
|
92
|
+
vals: new Float32Array(o)
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
-
const
|
|
95
|
+
const Z = `// UMAP SGD compute shader — processes one graph edge per GPU thread.
|
|
96
96
|
// Applies attraction forces between connected nodes and repulsion forces
|
|
97
97
|
// against negative samples.
|
|
98
98
|
|
|
@@ -195,10 +195,10 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
|
195
195
|
epochs_per_sample[edge_idx] / f32(params.negative_sample_rate);
|
|
196
196
|
}
|
|
197
197
|
`;
|
|
198
|
-
class
|
|
198
|
+
class W {
|
|
199
199
|
constructor() {
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
E(this, "device");
|
|
201
|
+
E(this, "pipeline");
|
|
202
202
|
}
|
|
203
203
|
async init() {
|
|
204
204
|
const e = await navigator.gpu.requestAdapter();
|
|
@@ -206,7 +206,7 @@ class D {
|
|
|
206
206
|
this.device = await e.requestDevice(), this.pipeline = this.device.createComputePipeline({
|
|
207
207
|
layout: "auto",
|
|
208
208
|
compute: {
|
|
209
|
-
module: this.device.createShaderModule({ code:
|
|
209
|
+
module: this.device.createShaderModule({ code: Z }),
|
|
210
210
|
entryPoint: "main"
|
|
211
211
|
}
|
|
212
212
|
});
|
|
@@ -224,216 +224,218 @@ class D {
|
|
|
224
224
|
* @param params - UMAP curve parameters and repulsion settings
|
|
225
225
|
* @returns Optimized embedding as Float32Array
|
|
226
226
|
*/
|
|
227
|
-
async optimize(e,
|
|
228
|
-
const { device:
|
|
227
|
+
async optimize(e, i, a, s, p, f, h, c, o) {
|
|
228
|
+
const { device: n } = this, r = i.length, l = this.makeBuffer(
|
|
229
229
|
e,
|
|
230
230
|
GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
231
|
-
),
|
|
232
|
-
for (let m = 0; m <
|
|
233
|
-
y[m] =
|
|
234
|
-
const
|
|
235
|
-
for (let m = 0; m <
|
|
236
|
-
|
|
237
|
-
const
|
|
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({
|
|
238
238
|
size: 40,
|
|
239
239
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
240
240
|
});
|
|
241
|
-
for (let m = 0; m <
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
const
|
|
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({
|
|
245
245
|
layout: this.pipeline.getBindGroupLayout(0),
|
|
246
246
|
entries: [
|
|
247
|
-
{ binding: 0, resource: { buffer:
|
|
248
|
-
{ binding: 1, resource: { buffer:
|
|
249
|
-
{ binding: 2, resource: { buffer:
|
|
250
|
-
{ binding: 3, resource: { buffer:
|
|
251
|
-
{ binding: 4, resource: { buffer:
|
|
252
|
-
{ binding: 5, resource: { buffer:
|
|
253
|
-
{ binding: 6, resource: { buffer:
|
|
254
|
-
{ binding: 7, resource: { buffer:
|
|
247
|
+
{ binding: 0, resource: { buffer: g } },
|
|
248
|
+
{ binding: 1, resource: { buffer: d } },
|
|
249
|
+
{ binding: 2, resource: { buffer: u } },
|
|
250
|
+
{ binding: 3, resource: { buffer: l } },
|
|
251
|
+
{ binding: 4, resource: { buffer: w } },
|
|
252
|
+
{ binding: 5, resource: { buffer: b } },
|
|
253
|
+
{ binding: 6, resource: { buffer: B } },
|
|
254
|
+
{ binding: 7, resource: { buffer: x } }
|
|
255
255
|
]
|
|
256
|
-
}),
|
|
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
258
|
}
|
|
259
|
-
const
|
|
259
|
+
const G = n.createBuffer({
|
|
260
260
|
size: e.byteLength,
|
|
261
261
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
262
|
-
}),
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
return
|
|
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;
|
|
266
266
|
}
|
|
267
|
-
makeBuffer(e,
|
|
268
|
-
const
|
|
267
|
+
makeBuffer(e, i) {
|
|
268
|
+
const a = this.device.createBuffer({
|
|
269
269
|
size: e.byteLength,
|
|
270
|
-
usage:
|
|
270
|
+
usage: i,
|
|
271
271
|
mappedAtCreation: !0
|
|
272
272
|
});
|
|
273
|
-
return e instanceof Float32Array ? new Float32Array(
|
|
273
|
+
return e instanceof Float32Array ? new Float32Array(a.getMappedRange()).set(e) : new Uint32Array(a.getMappedRange()).set(e), a.unmap(), a;
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
|
-
function
|
|
277
|
-
return Math.max(-4, Math.min(4,
|
|
276
|
+
function P(t) {
|
|
277
|
+
return Math.max(-4, Math.min(4, t));
|
|
278
278
|
}
|
|
279
|
-
function
|
|
280
|
-
const { a:
|
|
281
|
-
for (let
|
|
282
|
-
|
|
283
|
-
for (let
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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;
|
|
283
|
+
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];
|
|
289
|
+
let B = 0;
|
|
290
|
+
for (let m = 0; m < s; m++) {
|
|
291
|
+
const F = t[M * s + m] - t[x * s + m];
|
|
292
|
+
B += F * F;
|
|
292
293
|
}
|
|
293
|
-
const
|
|
294
|
-
for (let m = 0; m <
|
|
295
|
-
const
|
|
296
|
-
|
|
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;
|
|
297
298
|
}
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
for (let m = 0; m <
|
|
301
|
-
const
|
|
302
|
-
if (
|
|
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;
|
|
303
304
|
let A = 0;
|
|
304
|
-
for (let
|
|
305
|
-
const
|
|
306
|
-
A +=
|
|
305
|
+
for (let S = 0; S < s; S++) {
|
|
306
|
+
const k = t[M * s + S] - t[F * s + S];
|
|
307
|
+
A += k * k;
|
|
307
308
|
}
|
|
308
|
-
const
|
|
309
|
-
for (let
|
|
310
|
-
const
|
|
311
|
-
|
|
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;
|
|
312
313
|
}
|
|
313
314
|
}
|
|
314
|
-
|
|
315
|
+
_[b] += i[b] / r;
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
|
-
return
|
|
318
|
+
return t;
|
|
318
319
|
}
|
|
319
|
-
function
|
|
320
|
-
const { a:
|
|
321
|
-
for (let
|
|
322
|
-
y[
|
|
323
|
-
for (let
|
|
324
|
-
const
|
|
325
|
-
for (let
|
|
326
|
-
if (
|
|
327
|
-
const
|
|
328
|
-
let
|
|
329
|
-
for (let A = 0; A <
|
|
330
|
-
const
|
|
331
|
-
|
|
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;
|
|
326
|
+
for (let x = 0; x < u; x++) {
|
|
327
|
+
if (w[x] > b) continue;
|
|
328
|
+
const B = g[x], G = _[x];
|
|
329
|
+
let R = 0;
|
|
330
|
+
for (let A = 0; A < f; A++) {
|
|
331
|
+
const v = t[B * f + A] - e[G * f + A];
|
|
332
|
+
R += v * v;
|
|
332
333
|
}
|
|
333
|
-
const
|
|
334
|
-
for (let A = 0; A <
|
|
335
|
-
const
|
|
336
|
-
|
|
334
|
+
const O = Math.pow(R, r), m = -2 * n * r * (R > 0 ? O / R : 0) / (n * O + 1);
|
|
335
|
+
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
338
|
}
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
for (let A = 0; A <
|
|
341
|
-
const
|
|
342
|
-
if (
|
|
343
|
-
let
|
|
344
|
-
for (let
|
|
345
|
-
const
|
|
346
|
-
|
|
339
|
+
w[x] += a[x];
|
|
340
|
+
const F = y[x] > 0 ? Math.floor(a[x] / y[x]) : 0;
|
|
341
|
+
for (let A = 0; A < F; A++) {
|
|
342
|
+
const v = Math.floor(Math.random() * p);
|
|
343
|
+
if (v === G) continue;
|
|
344
|
+
let N = 0;
|
|
345
|
+
for (let U = 0; U < f; U++) {
|
|
346
|
+
const z = t[B * f + U] - e[v * f + U];
|
|
347
|
+
N += z * z;
|
|
347
348
|
}
|
|
348
|
-
const
|
|
349
|
-
for (let
|
|
350
|
-
const
|
|
351
|
-
|
|
349
|
+
const S = Math.pow(N, r), k = 2 * l * r / ((1e-3 + N) * (n * S + 1));
|
|
350
|
+
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
353
|
}
|
|
353
354
|
}
|
|
354
|
-
y[
|
|
355
|
+
y[x] += a[x] / d;
|
|
355
356
|
}
|
|
356
357
|
}
|
|
357
|
-
return
|
|
358
|
+
return t;
|
|
358
359
|
}
|
|
359
|
-
function
|
|
360
|
+
function K() {
|
|
360
361
|
return typeof navigator < "u" && !!navigator.gpu;
|
|
361
362
|
}
|
|
362
|
-
async function
|
|
363
|
+
async function ae(t, e = {}, i) {
|
|
363
364
|
const {
|
|
364
365
|
nComponents: a = 2,
|
|
365
|
-
nNeighbors:
|
|
366
|
-
minDist:
|
|
367
|
-
spread:
|
|
368
|
-
hnsw:
|
|
369
|
-
} = e,
|
|
366
|
+
nNeighbors: s = 15,
|
|
367
|
+
minDist: p = 0.1,
|
|
368
|
+
spread: f = 1,
|
|
369
|
+
hnsw: h = {}
|
|
370
|
+
} = e, c = e.nEpochs ?? (t.length > 1e4 ? 200 : 500);
|
|
370
371
|
console.time("knn");
|
|
371
|
-
const { indices:
|
|
372
|
-
M:
|
|
373
|
-
efConstruction:
|
|
374
|
-
efSearch:
|
|
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
|
|
375
376
|
});
|
|
376
377
|
console.timeEnd("knn"), console.time("fuzzy-set");
|
|
377
|
-
const
|
|
378
|
+
const r = D(o, n, s);
|
|
378
379
|
console.timeEnd("fuzzy-set");
|
|
379
|
-
const { a:
|
|
380
|
-
for (let y = 0; y <
|
|
381
|
-
|
|
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
383
|
console.time("sgd");
|
|
383
|
-
let
|
|
384
|
-
if (
|
|
384
|
+
let w;
|
|
385
|
+
if (K())
|
|
385
386
|
try {
|
|
386
|
-
const y = new
|
|
387
|
-
await y.init(),
|
|
388
|
-
g,
|
|
389
|
-
new Uint32Array(t.rows),
|
|
390
|
-
new Uint32Array(t.cols),
|
|
391
|
-
d,
|
|
387
|
+
const y = new W();
|
|
388
|
+
await y.init(), w = await y.optimize(
|
|
392
389
|
_,
|
|
390
|
+
new Uint32Array(r.rows),
|
|
391
|
+
new Uint32Array(r.cols),
|
|
392
|
+
u,
|
|
393
|
+
g,
|
|
393
394
|
a,
|
|
394
|
-
|
|
395
|
-
{ a:
|
|
395
|
+
c,
|
|
396
|
+
{ a: l, b: d, gamma: 1, negativeSampleRate: 5 },
|
|
397
|
+
i
|
|
396
398
|
);
|
|
397
399
|
} catch (y) {
|
|
398
|
-
console.warn("WebGPU SGD failed, falling back to CPU:", y),
|
|
400
|
+
console.warn("WebGPU SGD failed, falling back to CPU:", y), w = q(_, r, u, g, a, c, { a: l, b: d }, i);
|
|
399
401
|
}
|
|
400
402
|
else
|
|
401
|
-
|
|
402
|
-
return console.timeEnd("sgd"),
|
|
403
|
+
w = q(_, r, u, g, a, c, { a: l, b: d }, i);
|
|
404
|
+
return console.timeEnd("sgd"), w;
|
|
403
405
|
}
|
|
404
|
-
function
|
|
405
|
-
if (Math.abs(e - 1) < 1e-6 && Math.abs(
|
|
406
|
+
function j(t, e) {
|
|
407
|
+
if (Math.abs(e - 1) < 1e-6 && Math.abs(t - 0.1) < 1e-6)
|
|
406
408
|
return { a: 1.9292, b: 0.7915 };
|
|
407
|
-
if (Math.abs(e - 1) < 1e-6 && Math.abs(
|
|
409
|
+
if (Math.abs(e - 1) < 1e-6 && Math.abs(t - 0) < 1e-6)
|
|
408
410
|
return { a: 1.8956, b: 0.8006 };
|
|
409
|
-
if (Math.abs(e - 1) < 1e-6 && Math.abs(
|
|
411
|
+
if (Math.abs(e - 1) < 1e-6 && Math.abs(t - 0.5) < 1e-6)
|
|
410
412
|
return { a: 1.5769, b: 0.8951 };
|
|
411
|
-
const
|
|
412
|
-
return { a:
|
|
413
|
+
const i = ee(t, e);
|
|
414
|
+
return { a: te(t, e, i), b: i };
|
|
413
415
|
}
|
|
414
|
-
function
|
|
416
|
+
function ee(t, e) {
|
|
415
417
|
return 1 / (e * 1.2);
|
|
416
418
|
}
|
|
417
|
-
function
|
|
418
|
-
return
|
|
419
|
+
function te(t, e, i) {
|
|
420
|
+
return t < 1e-6 ? 1.8956 : (1 / (1 + 1e-3) - 1) / -Math.pow(t, 2 * i);
|
|
419
421
|
}
|
|
420
|
-
class
|
|
422
|
+
class re {
|
|
421
423
|
constructor(e = {}) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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");
|
|
430
432
|
/** The low-dimensional embedding produced by the last fit() call. */
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
433
|
+
E(this, "embedding", null);
|
|
434
|
+
E(this, "_hnswIndex", null);
|
|
435
|
+
E(this, "_nTrain", 0);
|
|
434
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 ?? {};
|
|
435
|
-
const { a, b:
|
|
436
|
-
this._a =
|
|
437
|
+
const { a: i, b: a } = j(this._minDist, this._spread);
|
|
438
|
+
this._a = i, this._b = a;
|
|
437
439
|
}
|
|
438
440
|
/**
|
|
439
441
|
* Train UMAP on `vectors`.
|
|
@@ -441,44 +443,45 @@ class se {
|
|
|
441
443
|
* index so that transform() can project new points later.
|
|
442
444
|
* Returns `this` for chaining.
|
|
443
445
|
*/
|
|
444
|
-
async fit(e) {
|
|
445
|
-
const a = e.length,
|
|
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;
|
|
446
448
|
console.time("knn");
|
|
447
|
-
const { knn:
|
|
448
|
-
M:
|
|
449
|
-
efConstruction:
|
|
450
|
-
efSearch:
|
|
449
|
+
const { knn: c, index: o } = await J(e, this._nNeighbors, {
|
|
450
|
+
M: p,
|
|
451
|
+
efConstruction: f,
|
|
452
|
+
efSearch: h
|
|
451
453
|
});
|
|
452
|
-
this._hnswIndex =
|
|
453
|
-
const
|
|
454
|
+
this._hnswIndex = o, this._nTrain = a, console.timeEnd("knn"), console.time("fuzzy-set");
|
|
455
|
+
const n = D(c.indices, c.distances, this._nNeighbors);
|
|
454
456
|
console.timeEnd("fuzzy-set");
|
|
455
|
-
const
|
|
456
|
-
for (let
|
|
457
|
-
|
|
458
|
-
if (console.time("sgd"),
|
|
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
461
|
try {
|
|
460
|
-
const
|
|
461
|
-
await
|
|
462
|
-
|
|
463
|
-
new Uint32Array(
|
|
464
|
-
new Uint32Array(
|
|
465
|
-
|
|
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,
|
|
466
468
|
a,
|
|
467
469
|
this._nComponents,
|
|
468
|
-
|
|
469
|
-
{ a: this._a, b: this._b, gamma: 1, negativeSampleRate: 5 }
|
|
470
|
+
s,
|
|
471
|
+
{ a: this._a, b: this._b, gamma: 1, negativeSampleRate: 5 },
|
|
472
|
+
i
|
|
470
473
|
);
|
|
471
|
-
} catch (
|
|
472
|
-
console.warn("WebGPU SGD failed, falling back to CPU:",
|
|
474
|
+
} catch (d) {
|
|
475
|
+
console.warn("WebGPU SGD failed, falling back to CPU:", d), this.embedding = q(l, n, r, a, this._nComponents, s, {
|
|
473
476
|
a: this._a,
|
|
474
477
|
b: this._b
|
|
475
|
-
});
|
|
478
|
+
}, i);
|
|
476
479
|
}
|
|
477
480
|
else
|
|
478
|
-
this.embedding =
|
|
481
|
+
this.embedding = q(l, n, r, a, this._nComponents, s, {
|
|
479
482
|
a: this._a,
|
|
480
483
|
b: this._b
|
|
481
|
-
});
|
|
484
|
+
}, i);
|
|
482
485
|
return console.timeEnd("sgd"), this;
|
|
483
486
|
}
|
|
484
487
|
/**
|
|
@@ -487,59 +490,81 @@ class se {
|
|
|
487
490
|
*
|
|
488
491
|
* The training embedding is kept fixed; only the new-point positions are
|
|
489
492
|
* optimised. Returns a Float32Array of shape [vectors.length × nComponents].
|
|
493
|
+
*
|
|
494
|
+
* @param normalize - When `true`, min-max normalise each dimension of the
|
|
495
|
+
* returned embedding to [0, 1]. The stored training embedding is never
|
|
496
|
+
* mutated. Defaults to `false`.
|
|
490
497
|
*/
|
|
491
|
-
async transform(e) {
|
|
498
|
+
async transform(e, i = !1) {
|
|
492
499
|
if (!this._hnswIndex || !this.embedding)
|
|
493
500
|
throw new Error("UMAP.transform() must be called after fit()");
|
|
494
|
-
const a = e.length,
|
|
495
|
-
for (let
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
for (let
|
|
499
|
-
|
|
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
|
+
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];
|
|
500
507
|
}
|
|
501
|
-
for (let
|
|
502
|
-
if (
|
|
503
|
-
for (let
|
|
504
|
-
|
|
508
|
+
for (let u = 0; u < a; u++)
|
|
509
|
+
if (n[u] > 0)
|
|
510
|
+
for (let g = 0; g < this._nComponents; g++)
|
|
511
|
+
r[u * this._nComponents + g] /= n[u];
|
|
505
512
|
else
|
|
506
|
-
for (let
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
t,
|
|
513
|
+
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,
|
|
511
517
|
this.embedding,
|
|
512
|
-
|
|
513
|
-
|
|
518
|
+
h,
|
|
519
|
+
l,
|
|
514
520
|
a,
|
|
515
521
|
this._nTrain,
|
|
516
522
|
this._nComponents,
|
|
517
|
-
|
|
523
|
+
p,
|
|
518
524
|
{ a: this._a, b: this._b }
|
|
519
525
|
);
|
|
526
|
+
return i ? T(d, a, this._nComponents) : d;
|
|
520
527
|
}
|
|
521
528
|
/**
|
|
522
529
|
* Convenience method equivalent to `fit(vectors)` followed by
|
|
523
530
|
* `transform(vectors)` — but more efficient because the training embedding
|
|
524
531
|
* is returned directly without a second optimization pass.
|
|
532
|
+
*
|
|
533
|
+
* @param normalize - When `true`, min-max normalise each dimension of the
|
|
534
|
+
* returned embedding to [0, 1]. `this.embedding` is never mutated.
|
|
535
|
+
* Defaults to `false`.
|
|
525
536
|
*/
|
|
526
|
-
async fit_transform(e) {
|
|
527
|
-
return await this.fit(e), this.embedding;
|
|
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
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function T(t, e, i) {
|
|
542
|
+
const a = new Float32Array(t.length);
|
|
543
|
+
for (let s = 0; s < i; s++) {
|
|
544
|
+
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);
|
|
548
|
+
}
|
|
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;
|
|
528
552
|
}
|
|
553
|
+
return a;
|
|
529
554
|
}
|
|
530
|
-
function
|
|
531
|
-
let
|
|
532
|
-
for (let
|
|
533
|
-
|
|
534
|
-
const
|
|
535
|
-
for (let
|
|
536
|
-
const p =
|
|
537
|
-
|
|
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;
|
|
538
563
|
}
|
|
539
|
-
return
|
|
564
|
+
return a;
|
|
540
565
|
}
|
|
541
566
|
export {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
567
|
+
re as UMAP,
|
|
568
|
+
ae as fit,
|
|
569
|
+
K as isWebGPUAvailable
|
|
545
570
|
};
|
package/dist/umap.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export interface UMAPOptions {
|
|
|
16
16
|
efSearch?: number;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Called after each completed SGD epoch (or every 10 epochs on the GPU path,
|
|
21
|
+
* piggybacking on the existing GPU synchronisation point to avoid extra stalls).
|
|
22
|
+
*
|
|
23
|
+
* @param epoch - Zero-based index of the epoch that just finished.
|
|
24
|
+
* @param nEpochs - Total number of epochs.
|
|
25
|
+
*/
|
|
26
|
+
export type ProgressCallback = (epoch: number, nEpochs: number) => void;
|
|
19
27
|
/**
|
|
20
28
|
* Fit UMAP to the given high-dimensional vectors and return a low-dimensional embedding.
|
|
21
29
|
*
|
|
@@ -24,7 +32,7 @@ export interface UMAPOptions {
|
|
|
24
32
|
* 2. Fuzzy simplicial set construction (graph weights)
|
|
25
33
|
* 3. SGD optimization (WebGPU accelerated, with CPU fallback)
|
|
26
34
|
*/
|
|
27
|
-
export declare function fit(vectors: number[][], opts?: UMAPOptions): Promise<Float32Array>;
|
|
35
|
+
export declare function fit(vectors: number[][], opts?: UMAPOptions, onProgress?: ProgressCallback): Promise<Float32Array>;
|
|
28
36
|
/**
|
|
29
37
|
* Compute the a, b parameters for the UMAP curve 1/(1 + a*d^(2b)).
|
|
30
38
|
*
|
|
@@ -74,21 +82,29 @@ export declare class UMAP {
|
|
|
74
82
|
* index so that transform() can project new points later.
|
|
75
83
|
* Returns `this` for chaining.
|
|
76
84
|
*/
|
|
77
|
-
fit(vectors: number[][]): Promise<this>;
|
|
85
|
+
fit(vectors: number[][], onProgress?: ProgressCallback): Promise<this>;
|
|
78
86
|
/**
|
|
79
87
|
* Project new (unseen) `vectors` into the embedding space learned by fit().
|
|
80
88
|
* Must be called after fit().
|
|
81
89
|
*
|
|
82
90
|
* The training embedding is kept fixed; only the new-point positions are
|
|
83
91
|
* optimised. Returns a Float32Array of shape [vectors.length × nComponents].
|
|
92
|
+
*
|
|
93
|
+
* @param normalize - When `true`, min-max normalise each dimension of the
|
|
94
|
+
* returned embedding to [0, 1]. The stored training embedding is never
|
|
95
|
+
* mutated. Defaults to `false`.
|
|
84
96
|
*/
|
|
85
|
-
transform(vectors: number[][]): Promise<Float32Array>;
|
|
97
|
+
transform(vectors: number[][], normalize?: boolean): Promise<Float32Array>;
|
|
86
98
|
/**
|
|
87
99
|
* Convenience method equivalent to `fit(vectors)` followed by
|
|
88
100
|
* `transform(vectors)` — but more efficient because the training embedding
|
|
89
101
|
* is returned directly without a second optimization pass.
|
|
102
|
+
*
|
|
103
|
+
* @param normalize - When `true`, min-max normalise each dimension of the
|
|
104
|
+
* returned embedding to [0, 1]. `this.embedding` is never mutated.
|
|
105
|
+
* Defaults to `false`.
|
|
90
106
|
*/
|
|
91
|
-
fit_transform(vectors: number[][]): Promise<Float32Array>;
|
|
107
|
+
fit_transform(vectors: number[][], onProgress?: ProgressCallback, normalize?: boolean): Promise<Float32Array>;
|
|
92
108
|
}
|
|
93
109
|
/**
|
|
94
110
|
* Compute per-edge epoch sampling periods based on edge weights.
|
package/package.json
CHANGED
|
@@ -1,30 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "umap-gpu",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
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",
|
|
18
42
|
"test": "vitest run",
|
|
19
|
-
"prepublishOnly": "
|
|
43
|
+
"prepublishOnly": "bun test && bun run build",
|
|
44
|
+
"docs:dev": "vitepress dev docs",
|
|
45
|
+
"docs:build": "vitepress build docs",
|
|
46
|
+
"docs:generate": "bun run build && bunx api-extractor run && bun run docs:build"
|
|
20
47
|
},
|
|
21
48
|
"dependencies": {
|
|
22
49
|
"hnswlib-wasm": "^0.8.2"
|
|
23
50
|
},
|
|
24
51
|
"devDependencies": {
|
|
52
|
+
"@microsoft/api-extractor": "^7.57.6",
|
|
25
53
|
"@webgpu/types": "^0.1.40",
|
|
26
54
|
"typescript": "^5.4.0",
|
|
27
55
|
"vite": "^5.0.0",
|
|
56
|
+
"vitepress": "^1.6.4",
|
|
57
|
+
"vitepress-plugin-llms": "^1.11.0",
|
|
28
58
|
"vitest": "^4.0.18"
|
|
29
59
|
},
|
|
30
60
|
"license": "MIT"
|