umap-web 0.2.0
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/CHANGELOG.md +98 -0
- package/LICENSE +29 -0
- package/NOTICE +19 -0
- package/README.md +205 -0
- package/dist/chunk-Bp6m_JJh.js +13 -0
- package/dist/core-BPpH5_PW.js +2816 -0
- package/dist/core-BPpH5_PW.js.map +1 -0
- package/dist/core-IHdw-3yY.d.ts +373 -0
- package/dist/core.d.ts +3 -0
- package/dist/core.js +4 -0
- package/dist/index.d.ts +261 -0
- package/dist/index.js +2991 -0
- package/dist/index.js.map +1 -0
- package/dist/init-DLiEtGpx.js +802 -0
- package/dist/init-DLiEtGpx.js.map +1 -0
- package/dist/sparse-BxSeVhO4.d.ts +150 -0
- package/dist/webgpu.d.ts +111 -0
- package/dist/webgpu.js +955 -0
- package/dist/webgpu.js.map +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +1095 -0
- package/dist/worker.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
//#region src/types.ts
|
|
2
|
+
const silentLogger = {
|
|
3
|
+
info() {},
|
|
4
|
+
warn() {}
|
|
5
|
+
};
|
|
6
|
+
const consoleLogger = {
|
|
7
|
+
info(msg) {
|
|
8
|
+
console.log(`[umap-web] ${msg}`);
|
|
9
|
+
},
|
|
10
|
+
warn(msg) {
|
|
11
|
+
console.warn(`[umap-web] ${msg}`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var UmapError = class extends Error {
|
|
15
|
+
name = "UmapError";
|
|
16
|
+
};
|
|
17
|
+
var UmapValidationError = class extends UmapError {
|
|
18
|
+
name = "UmapValidationError";
|
|
19
|
+
};
|
|
20
|
+
var UmapBackendError = class extends UmapError {
|
|
21
|
+
name = "UmapBackendError";
|
|
22
|
+
};
|
|
23
|
+
/** Surfaced through the logger on convergence problems (spectral fallback etc.). */
|
|
24
|
+
var UmapConvergenceWarning = class extends UmapError {
|
|
25
|
+
name = "UmapConvergenceWarning";
|
|
26
|
+
};
|
|
27
|
+
function abortError() {
|
|
28
|
+
return new DOMException("The operation was aborted.", "AbortError");
|
|
29
|
+
}
|
|
30
|
+
function throwIfAborted(signal) {
|
|
31
|
+
if (signal?.aborted) throw signal.reason ?? abortError();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/linalg.ts
|
|
36
|
+
/** Cyclic Jacobi for a symmetric n×n matrix (row-major input, not modified). */
|
|
37
|
+
function symmetricEigen(A, n, maxSweeps = 100) {
|
|
38
|
+
const a = A.slice();
|
|
39
|
+
const v = new Float64Array(n * n);
|
|
40
|
+
for (let i = 0; i < n; i++) v[i * n + i] = 1;
|
|
41
|
+
for (let sweep = 0; sweep < maxSweeps; sweep++) {
|
|
42
|
+
let off = 0;
|
|
43
|
+
for (let p = 0; p < n - 1; p++) for (let q = p + 1; q < n; q++) off += a[p * n + q] * a[p * n + q];
|
|
44
|
+
if (off < 1e-30) break;
|
|
45
|
+
for (let p = 0; p < n - 1; p++) for (let q = p + 1; q < n; q++) {
|
|
46
|
+
const apq = a[p * n + q];
|
|
47
|
+
if (Math.abs(apq) < 1e-300) continue;
|
|
48
|
+
const app = a[p * n + p];
|
|
49
|
+
const tau = (a[q * n + q] - app) / (2 * apq);
|
|
50
|
+
const t = Math.sign(tau) / (Math.abs(tau) + Math.sqrt(1 + tau * tau)) || 1 / (tau + Math.sqrt(1 + tau * tau));
|
|
51
|
+
const c = 1 / Math.sqrt(1 + t * t);
|
|
52
|
+
const s = t * c;
|
|
53
|
+
for (let i = 0; i < n; i++) {
|
|
54
|
+
const aip = a[i * n + p];
|
|
55
|
+
const aiq = a[i * n + q];
|
|
56
|
+
a[i * n + p] = c * aip - s * aiq;
|
|
57
|
+
a[i * n + q] = s * aip + c * aiq;
|
|
58
|
+
}
|
|
59
|
+
for (let i = 0; i < n; i++) {
|
|
60
|
+
const api = a[p * n + i];
|
|
61
|
+
const aqi = a[q * n + i];
|
|
62
|
+
a[p * n + i] = c * api - s * aqi;
|
|
63
|
+
a[q * n + i] = s * api + c * aqi;
|
|
64
|
+
}
|
|
65
|
+
for (let i = 0; i < n; i++) {
|
|
66
|
+
const vip = v[i * n + p];
|
|
67
|
+
const viq = v[i * n + q];
|
|
68
|
+
v[i * n + p] = c * vip - s * viq;
|
|
69
|
+
v[i * n + q] = s * vip + c * viq;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const values = new Float64Array(n);
|
|
74
|
+
for (let i = 0; i < n; i++) values[i] = a[i * n + i];
|
|
75
|
+
const order = Array.from({ length: n }, (_, i) => i).sort((x, y) => values[x] - values[y]);
|
|
76
|
+
const outVal = new Float64Array(n);
|
|
77
|
+
const outVec = new Float64Array(n * n);
|
|
78
|
+
for (let c = 0; c < n; c++) {
|
|
79
|
+
const src = order[c];
|
|
80
|
+
outVal[c] = values[src];
|
|
81
|
+
for (let r = 0; r < n; r++) outVec[c * n + r] = v[r * n + src];
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
values: outVal,
|
|
85
|
+
vectors: outVec
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Modified Gram–Schmidt over columns stored contiguously (col-major: X[c*n..c*n+n)).
|
|
90
|
+
* Columns with norm below dropTol (relative to their original norm) are dropped.
|
|
91
|
+
* Returns the number of retained columns (retained columns are compacted in place).
|
|
92
|
+
*/
|
|
93
|
+
function mgsOrthonormalize(X, n, cols, dropTol = 1e-10) {
|
|
94
|
+
let kept = 0;
|
|
95
|
+
for (let c = 0; c < cols; c++) {
|
|
96
|
+
const src = c * n;
|
|
97
|
+
if (kept !== c) X.copyWithin(kept * n, src, src + n);
|
|
98
|
+
const base = kept * n;
|
|
99
|
+
let norm0 = 0;
|
|
100
|
+
for (let r = 0; r < n; r++) norm0 += X[base + r] * X[base + r];
|
|
101
|
+
norm0 = Math.sqrt(norm0);
|
|
102
|
+
for (let pass = 0; pass < 2; pass++) for (let p = 0; p < kept; p++) {
|
|
103
|
+
const pb = p * n;
|
|
104
|
+
let dot = 0;
|
|
105
|
+
for (let r = 0; r < n; r++) dot += X[pb + r] * X[base + r];
|
|
106
|
+
for (let r = 0; r < n; r++) X[base + r] = X[base + r] - dot * X[pb + r];
|
|
107
|
+
}
|
|
108
|
+
let norm = 0;
|
|
109
|
+
for (let r = 0; r < n; r++) norm += X[base + r] * X[base + r];
|
|
110
|
+
norm = Math.sqrt(norm);
|
|
111
|
+
if (norm > dropTol * Math.max(norm0, 1e-300) && norm > 1e-300) {
|
|
112
|
+
for (let r = 0; r < n; r++) X[base + r] = X[base + r] / norm;
|
|
113
|
+
kept++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return kept;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/init/pca.ts
|
|
121
|
+
function pcaScores(X, dim, rng, center = true) {
|
|
122
|
+
const n = X.rows;
|
|
123
|
+
const d = X.cols;
|
|
124
|
+
const l = Math.min(d, dim + 7);
|
|
125
|
+
const mean = new Float64Array(d);
|
|
126
|
+
if (center) {
|
|
127
|
+
for (let i = 0; i < n; i++) for (let j = 0; j < d; j++) mean[j] = mean[j] + X.data[i * d + j];
|
|
128
|
+
for (let j = 0; j < d; j++) mean[j] = mean[j] / n;
|
|
129
|
+
}
|
|
130
|
+
const omega = new Float64Array(d * l);
|
|
131
|
+
for (let i = 0; i < omega.length; i++) omega[i] = rng.normal();
|
|
132
|
+
let Q = new Float64Array(n * l);
|
|
133
|
+
for (let i = 0; i < n; i++) for (let c = 0; c < l; c++) {
|
|
134
|
+
let acc = 0;
|
|
135
|
+
for (let j = 0; j < d; j++) acc += (X.data[i * d + j] - mean[j]) * omega[j * l + c];
|
|
136
|
+
Q[c * n + i] = acc;
|
|
137
|
+
}
|
|
138
|
+
orthonormalizeCols(Q, n, l);
|
|
139
|
+
for (let power = 0; power < 2; power++) {
|
|
140
|
+
const Z = new Float64Array(d * l);
|
|
141
|
+
for (let c = 0; c < l; c++) for (let i = 0; i < n; i++) {
|
|
142
|
+
const q = Q[c * n + i];
|
|
143
|
+
if (q === 0) continue;
|
|
144
|
+
for (let j = 0; j < d; j++) Z[c * d + j] = Z[c * d + j] + (X.data[i * d + j] - mean[j]) * q;
|
|
145
|
+
}
|
|
146
|
+
const Q2 = new Float64Array(n * l);
|
|
147
|
+
for (let c = 0; c < l; c++) for (let i = 0; i < n; i++) {
|
|
148
|
+
let acc = 0;
|
|
149
|
+
for (let j = 0; j < d; j++) acc += (X.data[i * d + j] - mean[j]) * Z[c * d + j];
|
|
150
|
+
Q2[c * n + i] = acc;
|
|
151
|
+
}
|
|
152
|
+
Q = Q2;
|
|
153
|
+
orthonormalizeCols(Q, n, l);
|
|
154
|
+
}
|
|
155
|
+
const B = new Float64Array(l * d);
|
|
156
|
+
for (let c = 0; c < l; c++) for (let i = 0; i < n; i++) {
|
|
157
|
+
const q = Q[c * n + i];
|
|
158
|
+
if (q === 0) continue;
|
|
159
|
+
for (let j = 0; j < d; j++) B[c * d + j] = B[c * d + j] + q * (X.data[i * d + j] - mean[j]);
|
|
160
|
+
}
|
|
161
|
+
const BBt = new Float64Array(l * l);
|
|
162
|
+
for (let a = 0; a < l; a++) for (let b = a; b < l; b++) {
|
|
163
|
+
let acc = 0;
|
|
164
|
+
for (let j = 0; j < d; j++) acc += B[a * d + j] * B[b * d + j];
|
|
165
|
+
BBt[a * l + b] = acc;
|
|
166
|
+
BBt[b * l + a] = acc;
|
|
167
|
+
}
|
|
168
|
+
const eig = symmetricEigen(BBt, l);
|
|
169
|
+
const scores = new Float64Array(n * dim);
|
|
170
|
+
for (let c = 0; c < dim; c++) {
|
|
171
|
+
const col = l - 1 - c;
|
|
172
|
+
const sigma = Math.sqrt(Math.max(eig.values[col], 0));
|
|
173
|
+
for (let i = 0; i < n; i++) {
|
|
174
|
+
let acc = 0;
|
|
175
|
+
for (let a = 0; a < l; a++) acc += Q[a * n + i] * eig.vectors[col * l + a];
|
|
176
|
+
scores[i * dim + c] = acc * sigma;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return scores;
|
|
180
|
+
}
|
|
181
|
+
function orthonormalizeCols(Q, n, cols) {
|
|
182
|
+
for (let c = 0; c < cols; c++) {
|
|
183
|
+
const base = c * n;
|
|
184
|
+
for (let pass = 0; pass < 2; pass++) for (let p = 0; p < c; p++) {
|
|
185
|
+
const pb = p * n;
|
|
186
|
+
let dot = 0;
|
|
187
|
+
for (let r = 0; r < n; r++) dot += Q[pb + r] * Q[base + r];
|
|
188
|
+
for (let r = 0; r < n; r++) Q[base + r] = Q[base + r] - dot * Q[pb + r];
|
|
189
|
+
}
|
|
190
|
+
let norm = 0;
|
|
191
|
+
for (let r = 0; r < n; r++) norm += Q[base + r] * Q[base + r];
|
|
192
|
+
norm = Math.sqrt(norm);
|
|
193
|
+
if (norm > 1e-300) for (let r = 0; r < n; r++) Q[base + r] = Q[base + r] / norm;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/init/spectral.ts
|
|
199
|
+
/** Connected components via BFS over an undirected CSR graph. */
|
|
200
|
+
function connectedComponents(graph) {
|
|
201
|
+
const n = graph.rows;
|
|
202
|
+
const labels = new Int32Array(n).fill(-1);
|
|
203
|
+
const queue = new Int32Array(n);
|
|
204
|
+
let count = 0;
|
|
205
|
+
for (let s = 0; s < n; s++) {
|
|
206
|
+
if (labels[s] !== -1) continue;
|
|
207
|
+
labels[s] = count;
|
|
208
|
+
let head = 0;
|
|
209
|
+
let tail = 0;
|
|
210
|
+
queue[tail++] = s;
|
|
211
|
+
while (head < tail) {
|
|
212
|
+
const u = queue[head++];
|
|
213
|
+
for (let p = graph.indptr[u]; p < graph.indptr[u + 1]; p++) {
|
|
214
|
+
const v = graph.indices[p];
|
|
215
|
+
if (labels[v] === -1) {
|
|
216
|
+
labels[v] = count;
|
|
217
|
+
queue[tail++] = v;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
count++;
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
count,
|
|
225
|
+
labels
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Y = L S for a block S of `cols` column vectors, where
|
|
230
|
+
* L = I − D^{-1/2} A D^{-1/2}. `scaled` holds the pre-scaled off-diagonal
|
|
231
|
+
* values A_ij·dinvsqrt[j] (computed once per solve); dinvsqrt[i] is applied
|
|
232
|
+
* after each row sum — the exact fp op order of the unfused loop, so results
|
|
233
|
+
* are bit-identical to the reference implementation path, per column. All
|
|
234
|
+
* columns share one pass over the CSR arrays (the SpMV is memory-bound; the
|
|
235
|
+
* blocked walk reads `scaled`/`indices` once instead of once per column).
|
|
236
|
+
*/
|
|
237
|
+
function laplacianMatVecBlock(graph, scaled, dinvsqrt, S, cols, Y) {
|
|
238
|
+
const n = graph.rows;
|
|
239
|
+
const indptr = graph.indptr;
|
|
240
|
+
const indices = graph.indices;
|
|
241
|
+
if (cols === 3) {
|
|
242
|
+
for (let i = 0; i < n; i++) {
|
|
243
|
+
let a0 = 0;
|
|
244
|
+
let a1 = 0;
|
|
245
|
+
let a2 = 0;
|
|
246
|
+
const end = indptr[i + 1];
|
|
247
|
+
for (let p = indptr[i]; p < end; p++) {
|
|
248
|
+
const v = scaled[p];
|
|
249
|
+
const j = indices[p];
|
|
250
|
+
a0 += v * S[j];
|
|
251
|
+
a1 += v * S[n + j];
|
|
252
|
+
a2 += v * S[2 * n + j];
|
|
253
|
+
}
|
|
254
|
+
const di = dinvsqrt[i];
|
|
255
|
+
Y[i] = S[i] - di * a0;
|
|
256
|
+
Y[n + i] = S[n + i] - di * a1;
|
|
257
|
+
Y[2 * n + i] = S[2 * n + i] - di * a2;
|
|
258
|
+
}
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const acc = new Float64Array(cols);
|
|
262
|
+
for (let i = 0; i < n; i++) {
|
|
263
|
+
acc.fill(0);
|
|
264
|
+
const end = indptr[i + 1];
|
|
265
|
+
for (let p = indptr[i]; p < end; p++) {
|
|
266
|
+
const v = scaled[p];
|
|
267
|
+
const j = indices[p];
|
|
268
|
+
for (let c = 0; c < cols; c++) acc[c] += v * S[c * n + j];
|
|
269
|
+
}
|
|
270
|
+
const di = dinvsqrt[i];
|
|
271
|
+
for (let c = 0; c < cols; c++) Y[c * n + i] = S[c * n + i] - di * acc[c];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function scaleLaplacian(graph, dinvsqrt) {
|
|
275
|
+
const scaled = new Float64Array(graph.data.length);
|
|
276
|
+
for (let i = 0; i < graph.rows; i++) for (let p = graph.indptr[i]; p < graph.indptr[i + 1]; p++) scaled[p] = graph.data[p] * dinvsqrt[graph.indices[p]];
|
|
277
|
+
return scaled;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* LOBPCG for the m smallest eigenpairs of the normalized Laplacian of `graph`,
|
|
281
|
+
* with diagonal (Jacobi) preconditioner. X0 is col-major n×m.
|
|
282
|
+
*/
|
|
283
|
+
function lobpcgSmallest(graph, dinvsqrt, X0, m, tol = 1e-4, maxIter = 200) {
|
|
284
|
+
const n = graph.rows;
|
|
285
|
+
const diag = new Float64Array(n).fill(1);
|
|
286
|
+
for (let i = 0; i < n; i++) for (let p = graph.indptr[i]; p < graph.indptr[i + 1]; p++) if (graph.indices[p] === i) diag[i] = 1 - graph.data[p] * dinvsqrt[i] * dinvsqrt[i];
|
|
287
|
+
const precond = new Float64Array(n);
|
|
288
|
+
for (let i = 0; i < n; i++) precond[i] = Math.abs(diag[i]) > 1e-12 ? 1 / diag[i] : 1;
|
|
289
|
+
const scaled = scaleLaplacian(graph, dinvsqrt);
|
|
290
|
+
let X = X0.slice();
|
|
291
|
+
let P = null;
|
|
292
|
+
let pCols = 0;
|
|
293
|
+
const values = new Float64Array(m);
|
|
294
|
+
const residuals = new Float64Array(m).fill(Infinity);
|
|
295
|
+
let converged = false;
|
|
296
|
+
let iter = 0;
|
|
297
|
+
const LXbuf = new Float64Array(n * m);
|
|
298
|
+
const Rbuf = new Float64Array(n * m);
|
|
299
|
+
const Wbuf = new Float64Array(n * m);
|
|
300
|
+
const Sbuf = new Float64Array(n * 3 * m);
|
|
301
|
+
const LSbuf = new Float64Array(n * 3 * m);
|
|
302
|
+
const xtlx = new Float64Array(m);
|
|
303
|
+
let newX = new Float64Array(n * m);
|
|
304
|
+
let newP = new Float64Array(n * m);
|
|
305
|
+
const applyL = (S, cols, out) => {
|
|
306
|
+
laplacianMatVecBlock(graph, scaled, dinvsqrt, S, cols, out);
|
|
307
|
+
return out;
|
|
308
|
+
};
|
|
309
|
+
for (iter = 0; iter < maxIter; iter++) {
|
|
310
|
+
mgsOrthonormalize(X, n, m);
|
|
311
|
+
const LX$1 = applyL(X, m, LXbuf);
|
|
312
|
+
for (let c = 0; c < m; c++) {
|
|
313
|
+
let dot = 0;
|
|
314
|
+
for (let r = 0; r < n; r++) dot += X[c * n + r] * LX$1[c * n + r];
|
|
315
|
+
xtlx[c] = dot;
|
|
316
|
+
}
|
|
317
|
+
const R = Rbuf;
|
|
318
|
+
let worst = 0;
|
|
319
|
+
for (let c = 0; c < m; c++) {
|
|
320
|
+
let rn = 0;
|
|
321
|
+
for (let r = 0; r < n; r++) {
|
|
322
|
+
const v = LX$1[c * n + r] - xtlx[c] * X[c * n + r];
|
|
323
|
+
R[c * n + r] = v;
|
|
324
|
+
rn += v * v;
|
|
325
|
+
}
|
|
326
|
+
residuals[c] = Math.sqrt(rn);
|
|
327
|
+
if (residuals[c] > worst) worst = residuals[c];
|
|
328
|
+
values[c] = xtlx[c];
|
|
329
|
+
}
|
|
330
|
+
if (worst <= tol) {
|
|
331
|
+
converged = true;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
const W = Wbuf;
|
|
335
|
+
for (let c = 0; c < m; c++) for (let r = 0; r < n; r++) W[c * n + r] = precond[r] * R[c * n + r];
|
|
336
|
+
const totalCols = m + m + pCols;
|
|
337
|
+
const S = Sbuf;
|
|
338
|
+
S.set(X.subarray(0, n * m), 0);
|
|
339
|
+
S.set(W, n * m);
|
|
340
|
+
if (P && pCols > 0) S.set(P.subarray(0, n * pCols), n * 2 * m);
|
|
341
|
+
const q = mgsOrthonormalize(S, n, totalCols);
|
|
342
|
+
if (q < m) break;
|
|
343
|
+
const LS = applyL(S, q, LSbuf);
|
|
344
|
+
const A$1 = new Float64Array(q * q);
|
|
345
|
+
for (let a = 0; a < q; a++) for (let b = a; b < q; b++) {
|
|
346
|
+
let dot = 0;
|
|
347
|
+
for (let r = 0; r < n; r++) dot += S[a * n + r] * LS[b * n + r];
|
|
348
|
+
A$1[a * q + b] = dot;
|
|
349
|
+
A$1[b * q + a] = dot;
|
|
350
|
+
}
|
|
351
|
+
const eig$1 = symmetricEigen(A$1, q);
|
|
352
|
+
newX.fill(0);
|
|
353
|
+
newP.fill(0);
|
|
354
|
+
for (let c = 0; c < m; c++) for (let a = 0; a < q; a++) {
|
|
355
|
+
const w = eig$1.vectors[c * q + a];
|
|
356
|
+
if (w === 0) continue;
|
|
357
|
+
const sBase = a * n;
|
|
358
|
+
const xBase = c * n;
|
|
359
|
+
for (let r = 0; r < n; r++) newX[xBase + r] = newX[xBase + r] + w * S[sBase + r];
|
|
360
|
+
if (a >= m) for (let r = 0; r < n; r++) newP[xBase + r] = newP[xBase + r] + w * S[sBase + r];
|
|
361
|
+
}
|
|
362
|
+
const oldX = X;
|
|
363
|
+
const oldP = P ?? new Float64Array(n * m);
|
|
364
|
+
X = newX;
|
|
365
|
+
P = newP;
|
|
366
|
+
newX = oldX;
|
|
367
|
+
newP = oldP;
|
|
368
|
+
pCols = m;
|
|
369
|
+
}
|
|
370
|
+
mgsOrthonormalize(X, n, m);
|
|
371
|
+
const LX = applyL(X, m, LXbuf);
|
|
372
|
+
const A = new Float64Array(m * m);
|
|
373
|
+
for (let a = 0; a < m; a++) for (let b = a; b < m; b++) {
|
|
374
|
+
let dot = 0;
|
|
375
|
+
for (let r = 0; r < n; r++) dot += X[a * n + r] * LX[b * n + r];
|
|
376
|
+
A[a * m + b] = dot;
|
|
377
|
+
A[b * m + a] = dot;
|
|
378
|
+
}
|
|
379
|
+
const eig = symmetricEigen(A, m);
|
|
380
|
+
const outVec = new Float64Array(n * m);
|
|
381
|
+
for (let c = 0; c < m; c++) for (let a = 0; a < m; a++) {
|
|
382
|
+
const w = eig.vectors[c * m + a];
|
|
383
|
+
if (w === 0) continue;
|
|
384
|
+
for (let r = 0; r < n; r++) outVec[c * n + r] = outVec[c * n + r] + w * X[a * n + r];
|
|
385
|
+
}
|
|
386
|
+
const LV = applyL(outVec, m, LXbuf);
|
|
387
|
+
for (let c = 0; c < m; c++) {
|
|
388
|
+
let rn = 0;
|
|
389
|
+
for (let r = 0; r < n; r++) {
|
|
390
|
+
const v = LV[c * n + r] - eig.values[c] * outVec[c * n + r];
|
|
391
|
+
rn += v * v;
|
|
392
|
+
}
|
|
393
|
+
residuals[c] = Math.sqrt(rn);
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
values: eig.values,
|
|
397
|
+
vectors: outVec,
|
|
398
|
+
converged,
|
|
399
|
+
iterations: iter,
|
|
400
|
+
residuals
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* lobpcgSmallest with an injected (possibly asynchronous) L·S operator — used
|
|
405
|
+
* by the WebGPU backend to offload the SpMV. A verbatim duplicate of the sync
|
|
406
|
+
* driver above (kept separate so the validated CPU path stays untouched);
|
|
407
|
+
* only the applyL calls are awaited.
|
|
408
|
+
*/
|
|
409
|
+
async function lobpcgSmallestAsync(graph, dinvsqrt, X0, m, tol, maxIter, applyLAsync) {
|
|
410
|
+
const n = graph.rows;
|
|
411
|
+
const diag = new Float64Array(n).fill(1);
|
|
412
|
+
for (let i = 0; i < n; i++) for (let p = graph.indptr[i]; p < graph.indptr[i + 1]; p++) if (graph.indices[p] === i) diag[i] = 1 - graph.data[p] * dinvsqrt[i] * dinvsqrt[i];
|
|
413
|
+
const precond = new Float64Array(n);
|
|
414
|
+
for (let i = 0; i < n; i++) precond[i] = Math.abs(diag[i]) > 1e-12 ? 1 / diag[i] : 1;
|
|
415
|
+
let X = X0.slice();
|
|
416
|
+
let P = null;
|
|
417
|
+
let pCols = 0;
|
|
418
|
+
const values = new Float64Array(m);
|
|
419
|
+
const residuals = new Float64Array(m).fill(Infinity);
|
|
420
|
+
let converged = false;
|
|
421
|
+
let iter = 0;
|
|
422
|
+
const LXbuf = new Float64Array(n * m);
|
|
423
|
+
const Rbuf = new Float64Array(n * m);
|
|
424
|
+
const Wbuf = new Float64Array(n * m);
|
|
425
|
+
const Sbuf = new Float64Array(n * 3 * m);
|
|
426
|
+
const LSbuf = new Float64Array(n * 3 * m);
|
|
427
|
+
const xtlx = new Float64Array(m);
|
|
428
|
+
let newX = new Float64Array(n * m);
|
|
429
|
+
let newP = new Float64Array(n * m);
|
|
430
|
+
for (iter = 0; iter < maxIter; iter++) {
|
|
431
|
+
mgsOrthonormalize(X, n, m);
|
|
432
|
+
const LX$1 = await applyLAsync(X, m, LXbuf);
|
|
433
|
+
for (let c = 0; c < m; c++) {
|
|
434
|
+
let dot = 0;
|
|
435
|
+
for (let r = 0; r < n; r++) dot += X[c * n + r] * LX$1[c * n + r];
|
|
436
|
+
xtlx[c] = dot;
|
|
437
|
+
}
|
|
438
|
+
const R = Rbuf;
|
|
439
|
+
let worst = 0;
|
|
440
|
+
for (let c = 0; c < m; c++) {
|
|
441
|
+
let rn = 0;
|
|
442
|
+
for (let r = 0; r < n; r++) {
|
|
443
|
+
const v = LX$1[c * n + r] - xtlx[c] * X[c * n + r];
|
|
444
|
+
R[c * n + r] = v;
|
|
445
|
+
rn += v * v;
|
|
446
|
+
}
|
|
447
|
+
residuals[c] = Math.sqrt(rn);
|
|
448
|
+
if (residuals[c] > worst) worst = residuals[c];
|
|
449
|
+
values[c] = xtlx[c];
|
|
450
|
+
}
|
|
451
|
+
if (worst <= tol) {
|
|
452
|
+
converged = true;
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
const W = Wbuf;
|
|
456
|
+
for (let c = 0; c < m; c++) for (let r = 0; r < n; r++) W[c * n + r] = precond[r] * R[c * n + r];
|
|
457
|
+
const totalCols = m + m + pCols;
|
|
458
|
+
const S = Sbuf;
|
|
459
|
+
S.set(X.subarray(0, n * m), 0);
|
|
460
|
+
S.set(W, n * m);
|
|
461
|
+
if (P && pCols > 0) S.set(P.subarray(0, n * pCols), n * 2 * m);
|
|
462
|
+
const q = mgsOrthonormalize(S, n, totalCols);
|
|
463
|
+
if (q < m) break;
|
|
464
|
+
const LS = await applyLAsync(S, q, LSbuf);
|
|
465
|
+
const A$1 = new Float64Array(q * q);
|
|
466
|
+
for (let a = 0; a < q; a++) for (let b = a; b < q; b++) {
|
|
467
|
+
let dot = 0;
|
|
468
|
+
for (let r = 0; r < n; r++) dot += S[a * n + r] * LS[b * n + r];
|
|
469
|
+
A$1[a * q + b] = dot;
|
|
470
|
+
A$1[b * q + a] = dot;
|
|
471
|
+
}
|
|
472
|
+
const eig$1 = symmetricEigen(A$1, q);
|
|
473
|
+
newX.fill(0);
|
|
474
|
+
newP.fill(0);
|
|
475
|
+
for (let c = 0; c < m; c++) for (let a = 0; a < q; a++) {
|
|
476
|
+
const w = eig$1.vectors[c * q + a];
|
|
477
|
+
if (w === 0) continue;
|
|
478
|
+
const sBase = a * n;
|
|
479
|
+
const xBase = c * n;
|
|
480
|
+
for (let r = 0; r < n; r++) newX[xBase + r] = newX[xBase + r] + w * S[sBase + r];
|
|
481
|
+
if (a >= m) for (let r = 0; r < n; r++) newP[xBase + r] = newP[xBase + r] + w * S[sBase + r];
|
|
482
|
+
}
|
|
483
|
+
const oldX = X;
|
|
484
|
+
const oldP = P ?? new Float64Array(n * m);
|
|
485
|
+
X = newX;
|
|
486
|
+
P = newP;
|
|
487
|
+
newX = oldX;
|
|
488
|
+
newP = oldP;
|
|
489
|
+
pCols = m;
|
|
490
|
+
}
|
|
491
|
+
mgsOrthonormalize(X, n, m);
|
|
492
|
+
const LX = await applyLAsync(X, m, LXbuf);
|
|
493
|
+
const A = new Float64Array(m * m);
|
|
494
|
+
for (let a = 0; a < m; a++) for (let b = a; b < m; b++) {
|
|
495
|
+
let dot = 0;
|
|
496
|
+
for (let r = 0; r < n; r++) dot += X[a * n + r] * LX[b * n + r];
|
|
497
|
+
A[a * m + b] = dot;
|
|
498
|
+
A[b * m + a] = dot;
|
|
499
|
+
}
|
|
500
|
+
const eig = symmetricEigen(A, m);
|
|
501
|
+
const outVec = new Float64Array(n * m);
|
|
502
|
+
for (let c = 0; c < m; c++) for (let a = 0; a < m; a++) {
|
|
503
|
+
const w = eig.vectors[c * m + a];
|
|
504
|
+
if (w === 0) continue;
|
|
505
|
+
for (let r = 0; r < n; r++) outVec[c * n + r] = outVec[c * n + r] + w * X[a * n + r];
|
|
506
|
+
}
|
|
507
|
+
const LV = await applyLAsync(outVec, m, LXbuf);
|
|
508
|
+
for (let c = 0; c < m; c++) {
|
|
509
|
+
let rn = 0;
|
|
510
|
+
for (let r = 0; r < n; r++) {
|
|
511
|
+
const v = LV[c * n + r] - eig.values[c] * outVec[c * n + r];
|
|
512
|
+
rn += v * v;
|
|
513
|
+
}
|
|
514
|
+
residuals[c] = Math.sqrt(rn);
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
values: eig.values,
|
|
518
|
+
vectors: outVec,
|
|
519
|
+
converged,
|
|
520
|
+
iterations: iter,
|
|
521
|
+
residuals
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/** Degree vector d_i = Σ_j A_ij and its inverse sqrt. */
|
|
525
|
+
function degreeInvSqrt(graph) {
|
|
526
|
+
const n = graph.rows;
|
|
527
|
+
const deg = new Float64Array(n);
|
|
528
|
+
for (let i = 0; i < n; i++) for (let p = graph.indptr[i]; p < graph.indptr[i + 1]; p++) deg[i] = deg[i] + graph.data[p];
|
|
529
|
+
const dinvsqrt = new Float64Array(n);
|
|
530
|
+
for (let i = 0; i < n; i++) dinvsqrt[i] = deg[i] > 0 ? 1 / Math.sqrt(deg[i]) : 0;
|
|
531
|
+
return {
|
|
532
|
+
deg,
|
|
533
|
+
dinvsqrt
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
/** Single-component spectral layout: k+1 smallest eigenvectors, drop trivial. */
|
|
537
|
+
function spectralLayoutSingle(graph, dim, rng, logger) {
|
|
538
|
+
const n = graph.rows;
|
|
539
|
+
const m = dim + 1;
|
|
540
|
+
const { deg, dinvsqrt } = degreeInvSqrt(graph);
|
|
541
|
+
const X0 = new Float64Array(n * m);
|
|
542
|
+
for (let c = 0; c < m; c++) for (let r = 0; r < n; r++) X0[c * n + r] = rng.normal();
|
|
543
|
+
let norm = 0;
|
|
544
|
+
for (let r = 0; r < n; r++) norm += deg[r];
|
|
545
|
+
norm = Math.sqrt(norm);
|
|
546
|
+
for (let r = 0; r < n; r++) X0[r] = Math.sqrt(deg[r]) / norm;
|
|
547
|
+
const res = lobpcgSmallest(graph, dinvsqrt, X0, m, 1e-4, 5 * Math.min(n, 60));
|
|
548
|
+
if (!res.converged) {
|
|
549
|
+
logger.warn("UmapConvergenceWarning: Spectral initialisation failed! The eigenvector solver failed. This is likely due to too small an eigengap. Consider adding some noise or jitter to your data. Falling back to random initialisation!");
|
|
550
|
+
const emb = new Float64Array(n * dim);
|
|
551
|
+
for (let i = 0; i < emb.length; i++) emb[i] = rng.uniform(-10, 10);
|
|
552
|
+
return {
|
|
553
|
+
embedding: emb,
|
|
554
|
+
usedFallback: true
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
const embedding = new Float64Array(n * dim);
|
|
558
|
+
for (let d = 0; d < dim; d++) {
|
|
559
|
+
const c = d + 1;
|
|
560
|
+
for (let r = 0; r < n; r++) embedding[r * dim + d] = res.vectors[c * n + r];
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
embedding,
|
|
564
|
+
usedFallback: false
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
/** exp(−d²) affinity spectral embedding of component centroids (spectral.py:18-142). */
|
|
568
|
+
function componentLayout(data, nComponents, labels, dim, rng) {
|
|
569
|
+
const meta = new Float64Array(nComponents * dim);
|
|
570
|
+
if (!data) {
|
|
571
|
+
for (let i = 0; i < meta.length; i++) meta[i] = rng.nextFloat() * 10;
|
|
572
|
+
return meta;
|
|
573
|
+
}
|
|
574
|
+
const d = data.cols;
|
|
575
|
+
const centroids = new Float64Array(nComponents * d);
|
|
576
|
+
const counts = new Int32Array(nComponents);
|
|
577
|
+
for (let i = 0; i < data.rows; i++) {
|
|
578
|
+
const c = labels[i];
|
|
579
|
+
counts[c]++;
|
|
580
|
+
for (let j = 0; j < d; j++) centroids[c * d + j] = centroids[c * d + j] + data.data[i * d + j];
|
|
581
|
+
}
|
|
582
|
+
for (let c = 0; c < nComponents; c++) for (let j = 0; j < d; j++) centroids[c * d + j] = centroids[c * d + j] / counts[c];
|
|
583
|
+
const aff = new Float64Array(nComponents * nComponents);
|
|
584
|
+
for (let a = 0; a < nComponents; a++) for (let b = 0; b < nComponents; b++) {
|
|
585
|
+
let s = 0;
|
|
586
|
+
for (let j = 0; j < d; j++) {
|
|
587
|
+
const diff = centroids[a * d + j] - centroids[b * d + j];
|
|
588
|
+
s += diff * diff;
|
|
589
|
+
}
|
|
590
|
+
aff[a * nComponents + b] = Math.exp(-s);
|
|
591
|
+
}
|
|
592
|
+
const n = nComponents;
|
|
593
|
+
const deg = new Float64Array(n);
|
|
594
|
+
for (let a = 0; a < n; a++) for (let b = 0; b < n; b++) deg[a] = deg[a] + aff[a * n + b];
|
|
595
|
+
const L = new Float64Array(n * n);
|
|
596
|
+
for (let a = 0; a < n; a++) for (let b = 0; b < n; b++) {
|
|
597
|
+
const norm = Math.sqrt(deg[a] * deg[b]);
|
|
598
|
+
L[a * n + b] = (a === b ? 1 : 0) - (norm > 0 ? aff[a * n + b] / norm : 0);
|
|
599
|
+
}
|
|
600
|
+
const eig = symmetricEigen(L, n);
|
|
601
|
+
let mx = 0;
|
|
602
|
+
for (let c = 0; c < dim; c++) {
|
|
603
|
+
const col = Math.min(c + 1, n - 1);
|
|
604
|
+
for (let a = 0; a < n; a++) {
|
|
605
|
+
const v = eig.vectors[col * n + a] / Math.sqrt(deg[a]);
|
|
606
|
+
meta[a * dim + c] = v;
|
|
607
|
+
if (Math.abs(v) > mx) mx = Math.abs(v);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
let realMax = -Infinity;
|
|
611
|
+
for (let i = 0; i < meta.length; i++) if (meta[i] > realMax) realMax = meta[i];
|
|
612
|
+
if (realMax !== 0 && Number.isFinite(realMax)) for (let i = 0; i < meta.length; i++) meta[i] = meta[i] / realMax;
|
|
613
|
+
return meta;
|
|
614
|
+
}
|
|
615
|
+
/** multi_component_layout (spectral.py:145-260). */
|
|
616
|
+
function multiComponentLayout(data, graph, nComponents, labels, dim, rng, logger) {
|
|
617
|
+
const n = graph.rows;
|
|
618
|
+
const result = new Float64Array(n * dim);
|
|
619
|
+
let meta;
|
|
620
|
+
if (nComponents > 2 * dim) meta = componentLayout(data, nComponents, labels, dim, rng);
|
|
621
|
+
else {
|
|
622
|
+
const k = Math.ceil(nComponents / 2);
|
|
623
|
+
meta = new Float64Array(nComponents * dim);
|
|
624
|
+
for (let c = 0; c < nComponents; c++) if (c < k) {
|
|
625
|
+
if (c < dim) meta[c * dim + c] = 1;
|
|
626
|
+
} else {
|
|
627
|
+
const idx = c - k;
|
|
628
|
+
if (idx < dim) meta[c * dim + idx] = -1;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
let anyFallback = false;
|
|
632
|
+
for (let label = 0; label < nComponents; label++) {
|
|
633
|
+
const verts = [];
|
|
634
|
+
for (let i = 0; i < n; i++) if (labels[i] === label) verts.push(i);
|
|
635
|
+
const size = verts.length;
|
|
636
|
+
let minDist = Infinity;
|
|
637
|
+
for (let other = 0; other < nComponents; other++) {
|
|
638
|
+
if (other === label) continue;
|
|
639
|
+
let s = 0;
|
|
640
|
+
for (let c = 0; c < dim; c++) {
|
|
641
|
+
const diff = meta[label * dim + c] - meta[other * dim + c];
|
|
642
|
+
s += diff * diff;
|
|
643
|
+
}
|
|
644
|
+
const dd = Math.sqrt(s);
|
|
645
|
+
if (dd > 0 && dd < minDist) minDist = dd;
|
|
646
|
+
}
|
|
647
|
+
const dataRange = (Number.isFinite(minDist) ? minDist : 1) / 2;
|
|
648
|
+
if (size < 2 * dim || size <= dim + 1) {
|
|
649
|
+
for (const [vi, v] of verts.entries()) for (let c = 0; c < dim; c++) result[v * dim + c] = rng.uniform(-dataRange, dataRange) + meta[label * dim + c];
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
const remap = new Int32Array(n).fill(-1);
|
|
653
|
+
verts.forEach((v, i) => {
|
|
654
|
+
remap[v] = i;
|
|
655
|
+
});
|
|
656
|
+
const subIndptr = new Int32Array(size + 1);
|
|
657
|
+
let nnz = 0;
|
|
658
|
+
for (const v of verts) for (let p = graph.indptr[v]; p < graph.indptr[v + 1]; p++) if (remap[graph.indices[p]] >= 0) nnz++;
|
|
659
|
+
const subIndices = new Int32Array(nnz);
|
|
660
|
+
const subData = new Float64Array(nnz);
|
|
661
|
+
let w = 0;
|
|
662
|
+
verts.forEach((v, i) => {
|
|
663
|
+
for (let p = graph.indptr[v]; p < graph.indptr[v + 1]; p++) {
|
|
664
|
+
const t = remap[graph.indices[p]];
|
|
665
|
+
if (t >= 0) {
|
|
666
|
+
subIndices[w] = t;
|
|
667
|
+
subData[w] = graph.data[p];
|
|
668
|
+
w++;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
subIndptr[i + 1] = w;
|
|
672
|
+
});
|
|
673
|
+
const compRes = spectralLayoutSingle({
|
|
674
|
+
indptr: subIndptr,
|
|
675
|
+
indices: subIndices,
|
|
676
|
+
data: subData,
|
|
677
|
+
rows: size,
|
|
678
|
+
cols: size
|
|
679
|
+
}, dim, rng, logger);
|
|
680
|
+
anyFallback ||= compRes.usedFallback;
|
|
681
|
+
let mx = 0;
|
|
682
|
+
for (let i = 0; i < compRes.embedding.length; i++) {
|
|
683
|
+
const v = Math.abs(compRes.embedding[i]);
|
|
684
|
+
if (v > mx) mx = v;
|
|
685
|
+
}
|
|
686
|
+
const expansion = mx > 0 ? dataRange / mx : 1;
|
|
687
|
+
verts.forEach((v, i) => {
|
|
688
|
+
for (let c = 0; c < dim; c++) result[v * dim + c] = compRes.embedding[i * dim + c] * expansion + meta[label * dim + c];
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
embedding: result,
|
|
693
|
+
usedFallback: anyFallback
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* spectral_layout: normalized-Laplacian eigenmaps of the fuzzy graph with
|
|
698
|
+
* multi-component handling. Returns n×dim float64 (caller scales/noises/casts).
|
|
699
|
+
*/
|
|
700
|
+
function spectralLayout(data, graph, dim, rng, logger = silentLogger) {
|
|
701
|
+
const { count, labels } = connectedComponents(graph);
|
|
702
|
+
if (count > 1) return multiComponentLayout(data, graph, count, labels, dim, rng, logger);
|
|
703
|
+
return spectralLayoutSingle(graph, dim, rng, logger);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
//#endregion
|
|
707
|
+
//#region src/init/index.ts
|
|
708
|
+
/** noisy_scale_coords (umap_.py:928-935): scale abs-max to maxCoord, add N(0, noise). */
|
|
709
|
+
function noisyScaleCoords(coords, rng, maxCoord = 10, noise = 1e-4) {
|
|
710
|
+
let mx = 0;
|
|
711
|
+
for (let i = 0; i < coords.length; i++) {
|
|
712
|
+
const v = Math.abs(coords[i]);
|
|
713
|
+
if (v > mx) mx = v;
|
|
714
|
+
}
|
|
715
|
+
const expansion = mx > 0 ? maxCoord / mx : 1;
|
|
716
|
+
for (let i = 0; i < coords.length; i++) coords[i] = Math.fround(coords[i] * expansion) + rng.normal() * noise;
|
|
717
|
+
}
|
|
718
|
+
/** Final rescale applied to EVERY init (umap_.py:1188-1192): per-dim min-max → [0,10]. */
|
|
719
|
+
function finalInitRescale(embedding, n, dim) {
|
|
720
|
+
const out = new Float32Array(n * dim);
|
|
721
|
+
for (let c = 0; c < dim; c++) {
|
|
722
|
+
let mn = Infinity;
|
|
723
|
+
let mx = -Infinity;
|
|
724
|
+
for (let i = 0; i < n; i++) {
|
|
725
|
+
const v = embedding[i * dim + c];
|
|
726
|
+
if (v < mn) mn = v;
|
|
727
|
+
if (v > mx) mx = v;
|
|
728
|
+
}
|
|
729
|
+
const range = mx - mn;
|
|
730
|
+
for (let i = 0; i < n; i++) out[i * dim + c] = range > 0 ? 10 * (embedding[i * dim + c] - mn) / range : 0;
|
|
731
|
+
}
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Produce the initial embedding for `graph` (pruned fuzzy graph) and `data`.
|
|
736
|
+
* Data may be null when init doesn't need it (random / spectral without
|
|
737
|
+
* multi-component meta layout data).
|
|
738
|
+
*/
|
|
739
|
+
function initializeEmbedding(init, data, graph, dim, rng, { logger = silentLogger } = {}) {
|
|
740
|
+
const n = graph.rows;
|
|
741
|
+
let emb;
|
|
742
|
+
let spectralFallback = false;
|
|
743
|
+
if (typeof init === "object") {
|
|
744
|
+
const e = init;
|
|
745
|
+
if (e.rows !== n || e.cols !== dim) throw new UmapValidationError(`init embedding must be ${n}x${dim}, got ${e.rows}x${e.cols}`);
|
|
746
|
+
emb = new Float64Array(n * dim);
|
|
747
|
+
const seen = /* @__PURE__ */ new Set();
|
|
748
|
+
let hasDup = false;
|
|
749
|
+
for (let i = 0; i < n; i++) {
|
|
750
|
+
const key = Array.from(e.data.subarray(i * dim, (i + 1) * dim)).join(",");
|
|
751
|
+
if (seen.has(key)) {
|
|
752
|
+
hasDup = true;
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
seen.add(key);
|
|
756
|
+
}
|
|
757
|
+
for (let i = 0; i < e.data.length; i++) emb[i] = e.data[i];
|
|
758
|
+
if (hasDup) {
|
|
759
|
+
const sample = Math.min(n, 512);
|
|
760
|
+
let acc = 0;
|
|
761
|
+
let cnt = 0;
|
|
762
|
+
for (let i = 0; i < sample; i++) {
|
|
763
|
+
let best = Infinity;
|
|
764
|
+
for (let j = 0; j < n; j++) {
|
|
765
|
+
if (j === i) continue;
|
|
766
|
+
let s = 0;
|
|
767
|
+
for (let c = 0; c < dim; c++) {
|
|
768
|
+
const d = emb[i * dim + c] - emb[j * dim + c];
|
|
769
|
+
s += d * d;
|
|
770
|
+
}
|
|
771
|
+
if (s > 0 && s < best) best = s;
|
|
772
|
+
}
|
|
773
|
+
if (Number.isFinite(best)) {
|
|
774
|
+
acc += Math.sqrt(best);
|
|
775
|
+
cnt++;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const nndist = cnt > 0 ? acc / cnt : .001;
|
|
779
|
+
for (let i = 0; i < emb.length; i++) emb[i] = emb[i] + rng.normal() * .001 * nndist;
|
|
780
|
+
}
|
|
781
|
+
} else if (init === "random") {
|
|
782
|
+
emb = new Float64Array(n * dim);
|
|
783
|
+
for (let i = 0; i < emb.length; i++) emb[i] = rng.uniform(-10, 10);
|
|
784
|
+
} else if (init === "pca") {
|
|
785
|
+
if (!data) throw new UmapValidationError("init 'pca' requires dense data");
|
|
786
|
+
emb = pcaScores(data, dim, rng);
|
|
787
|
+
noisyScaleCoords(emb, rng, 10, 1e-4);
|
|
788
|
+
} else if (init === "spectral") {
|
|
789
|
+
const res = spectralLayout(data, graph, dim, rng, logger);
|
|
790
|
+
emb = res.embedding;
|
|
791
|
+
spectralFallback = res.usedFallback;
|
|
792
|
+
if (!res.usedFallback) noisyScaleCoords(emb, rng, 10, 1e-4);
|
|
793
|
+
} else throw new UmapValidationError(`unknown init '${String(init)}'`);
|
|
794
|
+
return {
|
|
795
|
+
embedding: finalInitRescale(emb, n, dim),
|
|
796
|
+
spectralFallback
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
//#endregion
|
|
801
|
+
export { silentLogger as _, degreeInvSqrt as a, spectralLayout as c, symmetricEigen as d, UmapBackendError as f, consoleLogger as g, UmapValidationError as h, connectedComponents as i, pcaScores as l, UmapError as m, initializeEmbedding as n, lobpcgSmallest as o, UmapConvergenceWarning as p, noisyScaleCoords as r, lobpcgSmallestAsync as s, finalInitRescale as t, mgsOrthonormalize as u, throwIfAborted as v };
|
|
802
|
+
//# sourceMappingURL=init-DLiEtGpx.js.map
|