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 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","names":["i","iSwap: number","SPECS: Record<string, MetricSpec>","NAMED_METRICS: readonly string[]","identity: CorrectionFn","state","forest: FlatTree[]","rows: Int32Array[]","state: State","handlers: Record<string, Handler>","out: number[]","deltas: Float32Array | null"],"sources":["../src/knn/heap.ts","../src/types.ts","../src/metrics.ts","../src/knn/internal-metrics.ts","../src/rng.ts","../src/knn/nndescent.ts","../src/knn/rpforest.ts","../src/layout/sgd.ts","../src/backends/cpu/worker.ts"],"sourcesContent":["/**\n * pynndescent heap structure (utils.py:130-218, 352-533): per-row max-heap on\n * distance rooted at column 0 (root = current worst). Rows: indices int32 (-1 empty),\n * distances float32 (+inf empty), flags uint8.\n */\n\nexport interface NeighborHeap {\n indices: Int32Array; // n * size\n distances: Float32Array; // n * size\n flags: Uint8Array; // n * size\n n: number;\n size: number;\n}\n\nexport function makeHeap(n: number, size: number): NeighborHeap {\n const indices = new Int32Array(n * size).fill(-1);\n const distances = new Float32Array(n * size).fill(Infinity);\n const flags = new Uint8Array(n * size);\n return { indices, distances, flags, n, size };\n}\n\n/**\n * checked_flagged_heap_push (utils.py:471-533). Operates on one row given by base\n * offset. Rejects p >= root (ties dropped) and duplicates. Returns 1 if pushed.\n */\nexport function checkedFlaggedHeapPush(\n heap: NeighborHeap,\n row: number,\n p: number,\n n: number,\n f: number,\n): number {\n const size = heap.size;\n const base = row * size;\n const priorities = heap.distances;\n const indices = heap.indices;\n const flags = heap.flags;\n if (p >= priorities[base]!) return 0;\n for (let i = 0; i < size; i++) {\n if (indices[base + i] === n) return 0;\n }\n // sift down from root\n let i = 0;\n for (;;) {\n const ic1 = 2 * i + 1;\n const ic2 = ic1 + 1;\n let iSwap: number;\n if (ic1 >= size) break;\n if (ic2 >= size) {\n if (priorities[base + ic1]! > p) iSwap = ic1;\n else break;\n } else if (priorities[base + ic1]! >= priorities[base + ic2]!) {\n if (p < priorities[base + ic1]!) iSwap = ic1;\n else break;\n } else {\n if (p < priorities[base + ic2]!) iSwap = ic2;\n else break;\n }\n priorities[base + i] = priorities[base + iSwap]!;\n indices[base + i] = indices[base + iSwap]!;\n flags[base + i] = flags[base + iSwap]!;\n i = iSwap;\n }\n priorities[base + i] = p;\n indices[base + i] = n;\n flags[base + i] = f;\n return 1;\n}\n\n/** checked_heap_push (utils.py:409-468) — no flags. */\nexport function checkedHeapPush(\n priorities: Float32Array,\n indices: Int32Array,\n base: number,\n size: number,\n p: number,\n n: number,\n): number {\n if (p >= priorities[base]!) return 0;\n for (let i = 0; i < size; i++) {\n if (indices[base + i] === n) return 0;\n }\n let i = 0;\n for (;;) {\n const ic1 = 2 * i + 1;\n const ic2 = ic1 + 1;\n let iSwap: number;\n if (ic1 >= size) break;\n if (ic2 >= size) {\n if (priorities[base + ic1]! > p) iSwap = ic1;\n else break;\n } else if (priorities[base + ic1]! >= priorities[base + ic2]!) {\n if (p < priorities[base + ic1]!) iSwap = ic1;\n else break;\n } else {\n if (p < priorities[base + ic2]!) iSwap = ic2;\n else break;\n }\n priorities[base + i] = priorities[base + iSwap]!;\n indices[base + i] = indices[base + iSwap]!;\n i = iSwap;\n }\n priorities[base + i] = p;\n indices[base + i] = n;\n return 1;\n}\n\n/** simple_heap_push (utils.py:352-406) — no duplicate scan (query uses visited mask). */\nexport function simpleHeapPush(\n priorities: Float32Array,\n indices: Int32Array,\n size: number,\n p: number,\n n: number,\n): number {\n if (p >= priorities[0]!) return 0;\n let i = 0;\n for (;;) {\n const ic1 = 2 * i + 1;\n const ic2 = ic1 + 1;\n let iSwap: number;\n if (ic1 >= size) break;\n if (ic2 >= size) {\n if (priorities[ic1]! > p) iSwap = ic1;\n else break;\n } else if (priorities[ic1]! >= priorities[ic2]!) {\n if (p < priorities[ic1]!) iSwap = ic1;\n else break;\n } else {\n if (p < priorities[ic2]!) iSwap = ic2;\n else break;\n }\n priorities[i] = priorities[iSwap]!;\n indices[i] = indices[iSwap]!;\n i = iSwap;\n }\n priorities[i] = p;\n indices[i] = n;\n return 1;\n}\n\nfunction siftdownRange(\n distances: Float32Array,\n indices: Int32Array,\n base: number,\n len: number,\n elt: number,\n): void {\n // utils.py:165-186 — restore max-heap over [base, base+len)\n let e = elt;\n while (e * 2 + 1 < len) {\n const leftChild = e * 2 + 1;\n const rightChild = leftChild + 1;\n let swap = e;\n if (distances[base + swap]! < distances[base + leftChild]!) swap = leftChild;\n if (rightChild < len && distances[base + swap]! < distances[base + rightChild]!) {\n swap = rightChild;\n }\n if (swap === e) break;\n const td = distances[base + e]!;\n distances[base + e] = distances[base + swap]!;\n distances[base + swap] = td;\n const ti = indices[base + e]!;\n indices[base + e] = indices[base + swap]!;\n indices[base + swap] = ti;\n e = swap;\n }\n}\n\n/** deheap_sort (utils.py:189-218): per-row heapsort → ascending, (-1, inf) tail. */\nexport function deheapSort(heap: NeighborHeap): void {\n const { indices, distances, n, size } = heap;\n for (let r = 0; r < n; r++) {\n const base = r * size;\n for (let j = size - 1; j > 0; j--) {\n const td = distances[base]!;\n distances[base] = distances[base + j]!;\n distances[base + j] = td;\n const ti = indices[base]!;\n indices[base] = indices[base + j]!;\n indices[base + j] = ti;\n siftdownRange(distances, indices, base, j, 0);\n }\n }\n}\n","/**\n * Public data types and typed errors (BRIEF §4.1/§4.2).\n */\n\nexport type DenseInput =\n | number[][]\n | { data: Float32Array | Float64Array; rows: number; cols: number };\n\nexport interface CSRInput {\n data: Float32Array;\n indices: Int32Array;\n indptr: Int32Array;\n rows: number;\n cols: number;\n}\n\n/** CPU-only custom metric. */\nexport type MetricFn = (a: Float32Array, b: Float32Array) => number;\n\nexport interface Embedding {\n data: Float32Array;\n rows: number;\n cols: number;\n}\n\n/** -1 = unlabeled (semi-supervised). */\nexport type Labels = Int32Array | number[];\n/** Continuous supervision target. */\nexport type Target = Float32Array | number[];\n\nexport interface Logger {\n info(msg: string): void;\n warn(msg: string): void;\n}\n\nexport const silentLogger: Logger = { info() {}, warn() {} };\n\nexport const consoleLogger: Logger = {\n info(msg: string) {\n console.log(`[umap-web] ${msg}`);\n },\n warn(msg: string) {\n console.warn(`[umap-web] ${msg}`);\n },\n};\n\n// ---------------------------------------------------------------------------\n// errors\n// ---------------------------------------------------------------------------\n\nexport class UmapError extends Error {\n override name = 'UmapError';\n}\n\nexport class UmapValidationError extends UmapError {\n override name = 'UmapValidationError';\n}\n\nexport class UmapBackendError extends UmapError {\n override name = 'UmapBackendError';\n}\n\n/** Surfaced through the logger on convergence problems (spectral fallback etc.). */\nexport class UmapConvergenceWarning extends UmapError {\n override name = 'UmapConvergenceWarning';\n}\n\nexport function abortError(): DOMException {\n return new DOMException('The operation was aborted.', 'AbortError');\n}\n\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw (signal.reason as Error | undefined) ?? abortError();\n }\n}\n\n// ---------------------------------------------------------------------------\n// internal dense matrix view\n// ---------------------------------------------------------------------------\n\nexport interface Matrix {\n data: Float32Array;\n rows: number;\n cols: number;\n}\n\nexport interface KnnGraph {\n indices: Int32Array; // n * k, -1 = missing\n distances: Float32Array; // n * k\n k: number;\n}\n","/**\n * Named distance metrics — faithful ports of `umap.distances` (vendored 0.5.9.post2).\n * Every formula, branch, and edge case mirrors the Python source (see\n * reference/notes/umap-internals.md and DECISIONS.md D-004 for the requirement set).\n *\n * Computation is float64 (JS Number); inputs are typically Float32Array views.\n */\n\nimport { UmapValidationError } from './types.js';\n\nexport type Vec = Float32Array | Float64Array;\nexport type MetricImpl = (x: Vec, y: Vec) => number;\n\nexport interface MetricOptions {\n /** minkowski / wminkowski exponent */\n p?: number;\n /** seuclidean variance vector */\n V?: ArrayLike<number>;\n /** wminkowski weight vector */\n w?: ArrayLike<number>;\n /** mahalanobis inverse covariance (row-major cols×cols) */\n VInv?: ArrayLike<number>;\n}\n\nexport function euclidean(x: Vec, y: Vec): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n const d = x[i]! - y[i]!;\n result += d * d;\n }\n return Math.sqrt(result);\n}\n\n/** Squared euclidean — used internally by the optimizer (rdist). */\nexport function sqeuclidean(x: Vec, y: Vec): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n const d = x[i]! - y[i]!;\n result += d * d;\n }\n return result;\n}\n\nexport function standardisedEuclidean(x: Vec, y: Vec, sigma: ArrayLike<number>): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n const d = x[i]! - y[i]!;\n result += (d * d) / (sigma[i] as number);\n }\n return Math.sqrt(result);\n}\n\nexport function manhattan(x: Vec, y: Vec): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n result += Math.abs(x[i]! - y[i]!);\n }\n return result;\n}\n\nexport function chebyshev(x: Vec, y: Vec): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n const v = Math.abs(x[i]! - y[i]!);\n if (v > result) result = v;\n }\n return result;\n}\n\nexport function minkowski(x: Vec, y: Vec, p: number): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n result += Math.abs(x[i]! - y[i]!) ** p;\n }\n return result ** (1.0 / p);\n}\n\nexport function weightedMinkowski(x: Vec, y: Vec, w: ArrayLike<number>, p: number): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n result += (w[i] as number) * Math.abs(x[i]! - y[i]!) ** p;\n }\n return result ** (1.0 / p);\n}\n\nexport function mahalanobis(x: Vec, y: Vec, vinv: ArrayLike<number>): number {\n const n = x.length;\n let result = 0;\n const diff = new Float64Array(n);\n for (let i = 0; i < n; i++) diff[i] = x[i]! - y[i]!;\n for (let i = 0; i < n; i++) {\n let tmp = 0;\n for (let j = 0; j < n; j++) {\n tmp += (vinv[i * n + j] as number) * diff[j]!;\n }\n result += tmp * diff[i]!;\n }\n return Math.sqrt(result);\n}\n\nexport function hamming(x: Vec, y: Vec): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n if (x[i] !== y[i]) result += 1;\n }\n return result / x.length;\n}\n\nexport function canberra(x: Vec, y: Vec): number {\n let result = 0;\n for (let i = 0; i < x.length; i++) {\n const denominator = Math.abs(x[i]!) + Math.abs(y[i]!);\n if (denominator > 0) result += Math.abs(x[i]! - y[i]!) / denominator;\n }\n return result;\n}\n\nexport function brayCurtis(x: Vec, y: Vec): number {\n let numerator = 0;\n let denominator = 0;\n for (let i = 0; i < x.length; i++) {\n numerator += Math.abs(x[i]! - y[i]!);\n denominator += Math.abs(x[i]! + y[i]!);\n }\n return denominator > 0 ? numerator / denominator : 0;\n}\n\nexport function jaccard(x: Vec, y: Vec): number {\n let numNonZero = 0;\n let numEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n numNonZero += xt || yt ? 1 : 0;\n numEqual += xt && yt ? 1 : 0;\n }\n return numNonZero === 0 ? 0 : (numNonZero - numEqual) / numNonZero;\n}\n\nexport function matching(x: Vec, y: Vec): number {\n let numNotEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n if (xt !== yt) numNotEqual += 1;\n }\n return numNotEqual / x.length;\n}\n\nexport function dice(x: Vec, y: Vec): number {\n let numTrueTrue = 0;\n let numNotEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n numTrueTrue += xt && yt ? 1 : 0;\n numNotEqual += xt !== yt ? 1 : 0;\n }\n return numNotEqual === 0 ? 0 : numNotEqual / (2.0 * numTrueTrue + numNotEqual);\n}\n\nexport function kulsinski(x: Vec, y: Vec): number {\n let numTrueTrue = 0;\n let numNotEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n numTrueTrue += xt && yt ? 1 : 0;\n numNotEqual += xt !== yt ? 1 : 0;\n }\n return numNotEqual === 0 ? 0 : (numNotEqual - numTrueTrue + x.length) / (numNotEqual + x.length);\n}\n\nexport function rogersTanimoto(x: Vec, y: Vec): number {\n let numNotEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n if (xt !== yt) numNotEqual += 1;\n }\n return (2.0 * numNotEqual) / (x.length + numNotEqual);\n}\n\nexport function russellrao(x: Vec, y: Vec): number {\n let numTrueTrue = 0;\n let xNonZero = 0;\n let yNonZero = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n numTrueTrue += xt && yt ? 1 : 0;\n xNonZero += xt ? 1 : 0;\n yNonZero += yt ? 1 : 0;\n }\n if (numTrueTrue === xNonZero && numTrueTrue === yNonZero) return 0;\n return (x.length - numTrueTrue) / x.length;\n}\n\nexport function sokalMichener(x: Vec, y: Vec): number {\n return rogersTanimoto(x, y); // identical formula in source\n}\n\nexport function sokalSneath(x: Vec, y: Vec): number {\n let numTrueTrue = 0;\n let numNotEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n numTrueTrue += xt && yt ? 1 : 0;\n numNotEqual += xt !== yt ? 1 : 0;\n }\n return numNotEqual === 0 ? 0 : numNotEqual / (0.5 * numTrueTrue + numNotEqual);\n}\n\nexport function yule(x: Vec, y: Vec): number {\n let tt = 0;\n let tf = 0;\n let ft = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n tt += xt && yt ? 1 : 0;\n tf += xt && !yt ? 1 : 0;\n ft += !xt && yt ? 1 : 0;\n }\n const ff = x.length - tt - tf - ft;\n if (tf === 0 || ft === 0) return 0;\n return (2.0 * tf * ft) / (tt * ff + tf * ft);\n}\n\nexport function cosine(x: Vec, y: Vec): number {\n let result = 0;\n let normX = 0;\n let normY = 0;\n for (let i = 0; i < x.length; i++) {\n result += x[i]! * y[i]!;\n normX += x[i]! * x[i]!;\n normY += y[i]! * y[i]!;\n }\n if (normX === 0 && normY === 0) return 0;\n if (normX === 0 || normY === 0) return 1;\n return 1.0 - result / Math.sqrt(normX * normY);\n}\n\nexport function correlation(x: Vec, y: Vec): number {\n let muX = 0;\n let muY = 0;\n let normX = 0;\n let normY = 0;\n let dot = 0;\n const n = x.length;\n for (let i = 0; i < n; i++) {\n muX += x[i]!;\n muY += y[i]!;\n }\n muX /= n;\n muY /= n;\n for (let i = 0; i < n; i++) {\n const sx = x[i]! - muX;\n const sy = y[i]! - muY;\n normX += sx * sx;\n normY += sy * sy;\n dot += sx * sy;\n }\n if (normX === 0 && normY === 0) return 0;\n if (dot === 0) return 1;\n return 1.0 - dot / Math.sqrt(normX * normY);\n}\n\nexport function hellinger(x: Vec, y: Vec): number {\n let result = 0;\n let l1x = 0;\n let l1y = 0;\n for (let i = 0; i < x.length; i++) {\n result += Math.sqrt(x[i]! * y[i]!);\n l1x += x[i]!;\n l1y += y[i]!;\n }\n if (l1x === 0 && l1y === 0) return 0;\n if (l1x === 0 || l1y === 0) return 1;\n return Math.sqrt(1 - result / Math.sqrt(l1x * l1y));\n}\n\n// --- ll_dirichlet helpers (distances.py:678-712), exact truncated Stirling forms ---\n\nfunction approxLogGamma(x: number): number {\n if (x === 1) return 0;\n return x * Math.log(x) - x + 0.5 * Math.log((2.0 * Math.PI) / x) + 1.0 / (x * 12.0);\n}\n\nfunction logBeta(x: number, y: number): number {\n const a = Math.min(x, y);\n const b = Math.max(x, y);\n if (b < 5) {\n let value = -Math.log(b);\n for (let i = 1; i < Math.trunc(a); i++) {\n value += Math.log(i) - Math.log(b + i);\n }\n return value;\n }\n return approxLogGamma(x) + approxLogGamma(y) - approxLogGamma(x + y);\n}\n\nfunction logSingleBeta(x: number): number {\n return Math.log(2.0) * (-2.0 * x + 0.5) + 0.5 * Math.log((2.0 * Math.PI) / x) + 0.125 / x;\n}\n\nexport function llDirichlet(data1: Vec, data2: Vec): number {\n let n1 = 0;\n let n2 = 0;\n for (let i = 0; i < data1.length; i++) n1 += data1[i]!;\n for (let i = 0; i < data2.length; i++) n2 += data2[i]!;\n let logB = 0;\n let selfDenom1 = 0;\n let selfDenom2 = 0;\n for (let i = 0; i < data1.length; i++) {\n const d1 = data1[i]!;\n const d2 = data2[i]!;\n if (d1 * d2 > 0.9) {\n logB += logBeta(d1, d2);\n selfDenom1 += logSingleBeta(d1);\n selfDenom2 += logSingleBeta(d2);\n } else {\n if (d1 > 0.9) selfDenom1 += logSingleBeta(d1);\n if (d2 > 0.9) selfDenom2 += logSingleBeta(d2);\n }\n }\n return Math.sqrt(\n (1.0 / n2) * (logB - logBeta(n1, n2) - (selfDenom2 - logSingleBeta(n2))) +\n (1.0 / n1) * (logB - logBeta(n2, n1) - (selfDenom1 - logSingleBeta(n1))),\n );\n}\n\n/**\n * Symmetrized KL divergence. NOTE: the Python source mutates its inputs (adds z,\n * normalizes in place); we copy to preserve the same numerics without side effects.\n */\nexport function symmetricKl(x: Vec, y: Vec, z = 1e-11): number {\n const n = x.length;\n let xSum = 0;\n let ySum = 0;\n const xs = new Float64Array(n);\n const ys = new Float64Array(n);\n for (let i = 0; i < n; i++) {\n xs[i] = x[i]! + z;\n xSum += xs[i]!;\n ys[i] = y[i]! + z;\n ySum += ys[i]!;\n }\n for (let i = 0; i < n; i++) {\n xs[i] = xs[i]! / xSum;\n ys[i] = ys[i]! / ySum;\n }\n let kl1 = 0;\n let kl2 = 0;\n for (let i = 0; i < n; i++) {\n kl1 += xs[i]! * Math.log(xs[i]! / ys[i]!);\n kl2 += ys[i]! * Math.log(ys[i]! / xs[i]!);\n }\n return (kl1 + kl2) / 2;\n}\n\nexport function haversine(x: Vec, y: Vec): number {\n if (x.length !== 2) {\n throw new UmapValidationError('haversine is only defined for 2 dimensional data');\n }\n const sinLat = Math.sin(0.5 * (x[0]! - y[0]!));\n const sinLong = Math.sin(0.5 * (x[1]! - y[1]!));\n const result = Math.sqrt(sinLat * sinLat + Math.cos(x[0]!) * Math.cos(y[0]!) * sinLong * sinLong);\n return 2.0 * Math.asin(result);\n}\n\n// ---------------------------------------------------------------------------\n// registry\n// ---------------------------------------------------------------------------\n\ninterface MetricSpec {\n /** canonical name */\n canonical: string;\n /** which options it requires */\n needs?: ('p' | 'V' | 'w' | 'VInv')[];\n resolve: (opts: MetricOptions) => MetricImpl;\n}\n\nfunction simple(fn: MetricImpl, canonical: string): MetricSpec {\n return { canonical, resolve: () => fn };\n}\n\nconst SPECS: Record<string, MetricSpec> = {\n euclidean: simple(euclidean, 'euclidean'),\n l2: simple(euclidean, 'euclidean'),\n sqeuclidean: simple(sqeuclidean, 'sqeuclidean'),\n manhattan: simple(manhattan, 'manhattan'),\n taxicab: simple(manhattan, 'manhattan'),\n l1: simple(manhattan, 'manhattan'),\n chebyshev: simple(chebyshev, 'chebyshev'),\n linfinity: simple(chebyshev, 'chebyshev'),\n linfty: simple(chebyshev, 'chebyshev'),\n linf: simple(chebyshev, 'chebyshev'),\n minkowski: {\n canonical: 'minkowski',\n needs: ['p'],\n resolve: (o) => {\n const p = o.p ?? 2;\n return (x, y) => minkowski(x, y, p);\n },\n },\n seuclidean: {\n canonical: 'seuclidean',\n needs: ['V'],\n resolve: (o) => {\n if (!o.V) throw new UmapValidationError(\"metric 'seuclidean' requires metricOptions.V\");\n const V = o.V;\n return (x, y) => standardisedEuclidean(x, y, V);\n },\n },\n standardised_euclidean: {\n canonical: 'seuclidean',\n needs: ['V'],\n resolve: (o) => {\n if (!o.V) {\n throw new UmapValidationError(\"metric 'standardised_euclidean' requires metricOptions.V\");\n }\n const V = o.V;\n return (x, y) => standardisedEuclidean(x, y, V);\n },\n },\n wminkowski: {\n canonical: 'wminkowski',\n needs: ['w', 'p'],\n resolve: (o) => {\n if (!o.w) throw new UmapValidationError(\"metric 'wminkowski' requires metricOptions.w\");\n const w = o.w;\n const p = o.p ?? 2;\n return (x, y) => weightedMinkowski(x, y, w, p);\n },\n },\n weighted_minkowski: {\n canonical: 'wminkowski',\n needs: ['w', 'p'],\n resolve: (o) => {\n if (!o.w) {\n throw new UmapValidationError(\"metric 'weighted_minkowski' requires metricOptions.w\");\n }\n const w = o.w;\n const p = o.p ?? 2;\n return (x, y) => weightedMinkowski(x, y, w, p);\n },\n },\n mahalanobis: {\n canonical: 'mahalanobis',\n needs: ['VInv'],\n resolve: (o) => {\n if (!o.VInv) {\n throw new UmapValidationError(\"metric 'mahalanobis' requires metricOptions.VInv\");\n }\n const VInv = o.VInv;\n return (x, y) => mahalanobis(x, y, VInv);\n },\n },\n canberra: simple(canberra, 'canberra'),\n braycurtis: simple(brayCurtis, 'braycurtis'),\n cosine: simple(cosine, 'cosine'),\n correlation: simple(correlation, 'correlation'),\n haversine: simple(haversine, 'haversine'),\n hamming: simple(hamming, 'hamming'),\n jaccard: simple(jaccard, 'jaccard'),\n dice: simple(dice, 'dice'),\n matching: simple(matching, 'matching'),\n kulsinski: simple(kulsinski, 'kulsinski'),\n rogerstanimoto: simple(rogersTanimoto, 'rogerstanimoto'),\n russellrao: simple(russellrao, 'russellrao'),\n sokalmichener: simple(sokalMichener, 'sokalmichener'),\n sokalsneath: simple(sokalSneath, 'sokalsneath'),\n yule: simple(yule, 'yule'),\n hellinger: simple(hellinger, 'hellinger'),\n ll_dirichlet: simple(llDirichlet, 'll_dirichlet'),\n symmetric_kl: simple(symmetricKl, 'symmetric_kl'),\n};\n\nexport const NAMED_METRICS: readonly string[] = Object.keys(SPECS);\n\n/** Metrics that auto-enable angular RP-forest (umap_.py:1938-1946). */\nexport const ANGULAR_METRICS = new Set([\n 'cosine',\n 'correlation',\n 'dice',\n 'jaccard',\n 'll_dirichlet',\n 'hellinger',\n]);\n\nexport function isNamedMetric(name: string): boolean {\n return name in SPECS;\n}\n\nexport function canonicalMetricName(name: string): string {\n const spec = SPECS[name];\n if (!spec) throw new UmapValidationError(`unknown metric '${name}'`);\n return spec.canonical;\n}\n\nexport function resolveMetric(name: string, options: MetricOptions = {}): MetricImpl {\n const spec = SPECS[name];\n if (!spec) {\n throw new UmapValidationError(\n `unknown metric '${name}' — named metrics: ${NAMED_METRICS.join(', ')}`,\n );\n }\n return spec.resolve(options);\n}\n\n/**\n * Size/positivity checks for metricOptions once the data dimension is known —\n * a wrong-sized V/w/VInv would otherwise read undefined and silently poison\n * every distance with NaN.\n */\nexport function validateMetricOptionSizes(name: string, options: MetricOptions, dim: number): void {\n const spec = SPECS[name];\n if (!spec) return; // unknown names throw in resolveMetric with a clearer message\n const canonical = spec.canonical;\n if ((canonical === 'minkowski' || canonical === 'wminkowski') && options.p !== undefined) {\n if (!Number.isFinite(options.p) || options.p <= 0) {\n throw new UmapValidationError('metricOptions.p must be a finite number > 0');\n }\n }\n if (canonical === 'seuclidean' && options.V) {\n if (options.V.length !== dim) {\n throw new UmapValidationError(\n `metricOptions.V has ${options.V.length} entries; the data has ${dim} features`,\n );\n }\n for (let i = 0; i < dim; i++) {\n const v = options.V[i] as number;\n if (!Number.isFinite(v) || v <= 0) {\n throw new UmapValidationError(`metricOptions.V must be positive (index ${i} is ${v})`);\n }\n }\n }\n if (canonical === 'wminkowski' && options.w) {\n if (options.w.length !== dim) {\n throw new UmapValidationError(\n `metricOptions.w has ${options.w.length} entries; the data has ${dim} features`,\n );\n }\n }\n if (canonical === 'mahalanobis' && options.VInv) {\n if (options.VInv.length !== dim * dim) {\n throw new UmapValidationError(\n `metricOptions.VInv has ${options.VInv.length} entries; expected ${dim}×${dim} = ${dim * dim}`,\n );\n }\n }\n}\n","/**\n * pynndescent internal (\"alternative\") distance spaces + corrections (notes §8).\n * All build/search ranking happens in these spaces; corrections are applied only to\n * final outputs (neighbor_graph, query).\n */\n\nimport { type MetricImpl, type MetricOptions, resolveMetric } from '../metrics.js';\n\nexport const FLOAT32_MAX = 3.4028234663852886e38;\nexport const FLOAT32_EPS = 1.1920928955078125e-7;\n\nexport function squaredEuclidean(x: Float32Array, y: Float32Array): number {\n let r = 0;\n const n = x.length;\n let i = 0;\n // 4-way unroll (hot path of NN-descent)\n const n4 = n - (n % 4);\n for (; i < n4; i += 4) {\n const d0 = x[i]! - y[i]!;\n const d1 = x[i + 1]! - y[i + 1]!;\n const d2 = x[i + 2]! - y[i + 2]!;\n const d3 = x[i + 3]! - y[i + 3]!;\n r += d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3;\n }\n for (; i < n; i++) {\n const d = x[i]! - y[i]!;\n r += d * d;\n }\n return r;\n}\n\n/** alternative_cosine (distances.py:583-630). */\nexport function alternativeCosine(x: Float32Array, y: Float32Array): number {\n let dot = 0;\n let nx = 0;\n let ny = 0;\n for (let i = 0; i < x.length; i++) {\n dot += x[i]! * y[i]!;\n nx += x[i]! * x[i]!;\n ny += y[i]! * y[i]!;\n }\n if (nx === 0 && ny === 0) return 0;\n if (nx === 0 || ny === 0) return FLOAT32_MAX;\n if (dot <= 0) return FLOAT32_MAX;\n return Math.log2(Math.sqrt(nx * ny) / dot);\n}\n\n/** alternative_hellinger (distances.py:1387-1416). */\nexport function alternativeHellinger(x: Float32Array, y: Float32Array): number {\n let result = 0;\n let l1x = 0;\n let l1y = 0;\n for (let i = 0; i < x.length; i++) {\n result += Math.sqrt(x[i]! * y[i]!);\n l1x += x[i]!;\n l1y += y[i]!;\n }\n if (l1x === 0 && l1y === 0) return 0;\n if (l1x === 0 || l1y === 0) return FLOAT32_MAX;\n if (result <= 0) return FLOAT32_MAX;\n return Math.log2(Math.sqrt(l1x * l1y) / result);\n}\n\n/** alternative_jaccard (distances.py:259-336 family). */\nexport function alternativeJaccard(x: Float32Array, y: Float32Array): number {\n let numNonZero = 0;\n let numEqual = 0;\n for (let i = 0; i < x.length; i++) {\n const xt = x[i] !== 0;\n const yt = y[i] !== 0;\n numNonZero += xt || yt ? 1 : 0;\n numEqual += xt && yt ? 1 : 0;\n }\n if (numNonZero === 0) return 0;\n if (numEqual === 0) return FLOAT32_MAX;\n return -Math.log2(numEqual / numNonZero);\n}\n\nexport type CorrectionFn = (d: number) => number;\n\nexport interface InternalMetric {\n /** distance used for ALL internal ranking/thresholds */\n dist: MetricImpl;\n /** correction applied to final output distances (identity if none) */\n correction: CorrectionFn;\n /** whether angular RP trees are used (pynndescent decides by metric name) */\n angularTrees: boolean;\n}\n\nconst identity: CorrectionFn = (d) => d;\n\n/** Metric names that switch NNDescent to angular trees (pynndescent_.py:1059-1077). */\nconst ANGULAR_TREE_METRICS = new Set([\n 'cosine',\n 'dot',\n 'correlation',\n 'dice',\n 'jaccard',\n 'hellinger',\n 'hamming',\n]);\n\n/**\n * Resolve the internal metric + correction for NN-descent\n * (fast_distance_alternatives, distances.py:2170-2188).\n */\nexport function resolveInternalMetric(name: string, options: MetricOptions): InternalMetric {\n const angularTrees = ANGULAR_TREE_METRICS.has(name);\n switch (name) {\n case 'euclidean':\n case 'l2':\n return {\n dist: squaredEuclidean as MetricImpl,\n correction: (d) => Math.sqrt(d),\n angularTrees,\n };\n case 'cosine':\n return {\n dist: alternativeCosine as MetricImpl,\n correction: (d) => 1.0 - 2.0 ** -d,\n angularTrees,\n };\n case 'hellinger':\n return {\n dist: alternativeHellinger as MetricImpl,\n correction: (d) => Math.sqrt(1.0 - 2.0 ** -d),\n angularTrees,\n };\n case 'jaccard':\n return {\n dist: alternativeJaccard as MetricImpl,\n correction: (d) => 1.0 - 2.0 ** -d,\n angularTrees,\n };\n default:\n return { dist: resolveMetric(name, options), correction: identity, angularTrees };\n }\n}\n\n// ---------------------------------------------------------------------------\n// sparse (CSR row) variants — used by sparse NN-descent (notes §9)\n// ---------------------------------------------------------------------------\n\nexport type SparseMetricImpl = (\n ind1: Int32Array,\n data1: Float32Array,\n ind2: Int32Array,\n data2: Float32Array,\n) => number;\n\n/** sparse_squared_euclidean (sparse.py:383-408). */\nexport function sparseSquaredEuclidean(\n ind1: Int32Array,\n data1: Float32Array,\n ind2: Int32Array,\n data2: Float32Array,\n): number {\n let r = 0;\n let i1 = 0;\n let i2 = 0;\n while (i1 < ind1.length && i2 < ind2.length) {\n const c1 = ind1[i1]!;\n const c2 = ind2[i2]!;\n if (c1 === c2) {\n const d = data1[i1]! - data2[i2]!;\n r += d * d;\n i1++;\n i2++;\n } else if (c1 < c2) {\n r += data1[i1]! * data1[i1]!;\n i1++;\n } else {\n r += data2[i2]! * data2[i2]!;\n i2++;\n }\n }\n while (i1 < ind1.length) {\n r += data1[i1]! * data1[i1]!;\n i1++;\n }\n while (i2 < ind2.length) {\n r += data2[i2]! * data2[i2]!;\n i2++;\n }\n return r;\n}\n\n/** sparse_alternative_cosine (sparse.py:632-659). */\nexport function sparseAlternativeCosine(\n ind1: Int32Array,\n data1: Float32Array,\n ind2: Int32Array,\n data2: Float32Array,\n): number {\n let dot = 0;\n let i1 = 0;\n let i2 = 0;\n while (i1 < ind1.length && i2 < ind2.length) {\n const c1 = ind1[i1]!;\n const c2 = ind2[i2]!;\n if (c1 === c2) {\n dot += data1[i1]! * data2[i2]!;\n i1++;\n i2++;\n } else if (c1 < c2) i1++;\n else i2++;\n }\n let nx = 0;\n for (let i = 0; i < data1.length; i++) nx += data1[i]! * data1[i]!;\n let ny = 0;\n for (let i = 0; i < data2.length; i++) ny += data2[i]! * data2[i]!;\n if (nx === 0 && ny === 0) return 0;\n if (nx === 0 || ny === 0) return FLOAT32_MAX;\n if (dot <= 0) return FLOAT32_MAX;\n return Math.log2(Math.sqrt(nx * ny) / dot);\n}\n\n/** sparse_correct_alternative_cosine clamps near-zero/negative to 0 (sparse.py:662-667). */\nexport function sparseCorrectAlternativeCosine(d: number): number {\n if (Math.abs(d) < 1e-7 || d < 0) return 0.0;\n return 1.0 - 2.0 ** -d;\n}\n\nexport interface SparseInternalMetric {\n dist: SparseMetricImpl;\n correction: CorrectionFn;\n angularTrees: boolean;\n}\n\nexport function resolveSparseInternalMetric(name: string): SparseInternalMetric {\n switch (name) {\n case 'euclidean':\n case 'l2':\n return {\n dist: sparseSquaredEuclidean,\n correction: (d) => Math.sqrt(d),\n angularTrees: false,\n };\n case 'cosine':\n return {\n dist: sparseAlternativeCosine,\n correction: sparseCorrectAlternativeCosine,\n angularTrees: true,\n };\n default:\n throw new Error(`sparse metric '${name}' is not supported (euclidean, cosine)`);\n }\n}\n","/**\n * Seeded PRNG — PCG32 (O'Neill, pcg-random.org), the reproducible generator of §5.5.\n *\n * Test vectors in test/unit/rng.test.ts were generated from the canonical C reference\n * (pcg_basic.c) compiled locally; the first draw for seed(42, 54) is 0xa15c02b7,\n * matching the published pcg32-demo output.\n *\n * 64-bit state is held as two u32 words; the LCG multiply uses exact 16-bit limb\n * arithmetic (all intermediates < 2^53).\n */\n\nconst MULT_HI = 0x5851f42d >>> 0;\nconst MULT_LO = 0x4c957f2d >>> 0;\n\nexport class Pcg32 {\n private sHi = 0;\n private sLo = 0;\n private iHi = 0;\n private iLo = 0;\n private spareNormal: number | null = null;\n\n constructor(seedHi: number, seedLo: number, seqHi: number, seqLo: number) {\n // inc = (initseq << 1) | 1 (64-bit)\n this.iHi = ((seqHi << 1) | (seqLo >>> 31)) >>> 0;\n this.iLo = ((seqLo << 1) | 1) >>> 0;\n this.sHi = 0;\n this.sLo = 0;\n this.nextU32();\n // state += initstate\n const lo = this.sLo + (seedLo >>> 0);\n this.sLo = lo >>> 0;\n this.sHi = (this.sHi + (seedHi >>> 0) + (lo > 0xffffffff ? 1 : 0)) >>> 0;\n this.nextU32();\n }\n\n /** Derive from a JS number seed (integer-valued; uses full 53-bit range). */\n static fromSeed(seed: number, stream = 54): Pcg32 {\n const s = Math.abs(Math.floor(seed));\n const hi = Math.floor(s / 0x100000000) >>> 0;\n const lo = s >>> 0;\n return new Pcg32(hi, lo, 0, stream >>> 0);\n }\n\n nextU32(): number {\n const oHi = this.sHi;\n const oLo = this.sLo;\n\n // state = oldstate * MULT + inc (mod 2^64), 16-bit limbs\n const a0 = oLo & 0xffff;\n const a1 = oLo >>> 16;\n const a2 = oHi & 0xffff;\n const a3 = oHi >>> 16;\n const b0 = MULT_LO & 0xffff;\n const b1 = MULT_LO >>> 16;\n const b2 = MULT_HI & 0xffff;\n const b3 = MULT_HI >>> 16;\n\n const p0 = a0 * b0;\n const p1 = a1 * b0 + a0 * b1;\n const p2 = a2 * b0 + a1 * b1 + a0 * b2;\n const p3 = a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3;\n // Assemble 64-bit result limb-by-limb (16-bit limbs l0..l3), then add inc.\n let l0 = p0 & 0xffff;\n let acc = Math.floor(p0 / 0x10000) + p1;\n let l1 = acc & 0xffff;\n acc = Math.floor(acc / 0x10000) + p2;\n let l2 = acc & 0xffff;\n acc = Math.floor(acc / 0x10000) + p3;\n let l3 = acc & 0xffff;\n // + inc (64-bit add)\n let s = l0 + (this.iLo & 0xffff);\n l0 = s & 0xffff;\n s = l1 + (this.iLo >>> 16) + (s >>> 16);\n l1 = s & 0xffff;\n s = l2 + (this.iHi & 0xffff) + (s >>> 16);\n l2 = s & 0xffff;\n s = l3 + (this.iHi >>> 16) + (s >>> 16);\n l3 = s & 0xffff;\n\n this.sLo = ((l1 << 16) | l0) >>> 0;\n this.sHi = ((l3 << 16) | l2) >>> 0;\n\n // xorshifted = ((old >> 18) ^ old) >> 27 (u32); rot = old >> 59\n const xHi = (oHi ^ (oHi >>> 18)) >>> 0;\n const xLo = (oLo ^ ((oHi << 14) | (oLo >>> 18))) >>> 0;\n const xorshifted = ((xHi << 5) | (xLo >>> 27)) >>> 0;\n const rot = oHi >>> 27;\n return ((xorshifted >>> rot) | (xorshifted << (-rot & 31))) >>> 0;\n }\n\n /** Uniform float in [0, 1). */\n nextFloat(): number {\n return this.nextU32() * 2.3283064365386963e-10; // 2^-32\n }\n\n /** Uniform float in [lo, hi). */\n uniform(lo: number, hi: number): number {\n return lo + (hi - lo) * this.nextFloat();\n }\n\n /** Integer in [0, n) — modulo method (matches umap's structural usage). */\n randint(n: number): number {\n return this.nextU32() % n;\n }\n\n /** Integer in [lo, hi) like numpy randint. */\n randintRange(lo: number, hi: number): number {\n return lo + (this.nextU32() % (hi - lo));\n }\n\n /** Standard normal via Box–Muller (cached spare). */\n normal(): number {\n if (this.spareNormal !== null) {\n const v = this.spareNormal;\n this.spareNormal = null;\n return v;\n }\n let u = 0;\n let v = 0;\n do {\n u = this.nextFloat();\n } while (u <= 1e-12);\n v = this.nextFloat();\n const r = Math.sqrt(-2.0 * Math.log(u));\n const theta = 2.0 * Math.PI * v;\n this.spareNormal = r * Math.sin(theta);\n return r * Math.cos(theta);\n }\n\n /** In-place Fisher–Yates shuffle. */\n shuffle(arr: Int32Array | Uint32Array | Float32Array): void {\n for (let i = arr.length - 1; i > 0; i--) {\n const j = this.randint(i + 1);\n const t = arr[i]!;\n arr[i] = arr[j]!;\n arr[j] = t;\n }\n }\n\n /**\n * Seed a 3-word tau state like Python's\n * `random_state.randint(INT32_MIN, INT32_MAX, 3)` (values in int32 range).\n */\n tauState(): Int32Array {\n const st = new Int32Array(3);\n for (let i = 0; i < 3; i++) st[i] = this.nextU32() | 0;\n return st;\n }\n\n /** Snapshot the full generator state (backend-fallback rng restoration). */\n clone(): Pcg32 {\n const c = new Pcg32(0, 0, 0, 0);\n c.sHi = this.sHi;\n c.sLo = this.sLo;\n c.iHi = this.iHi;\n c.iLo = this.iLo;\n c.spareNormal = this.spareNormal;\n return c;\n }\n\n /** Restore state from a clone() snapshot (un-consumes draws after the snapshot). */\n restoreFrom(snapshot: Pcg32): void {\n this.sHi = snapshot.sHi;\n this.sLo = snapshot.sLo;\n this.iHi = snapshot.iHi;\n this.iLo = snapshot.iLo;\n this.spareNormal = snapshot.spareNormal;\n }\n}\n\n/**\n * taus88 combined Tausworthe step, structurally identical to umap's `tau_rand_int`\n * but on u32 words (Python runs it on int64 with 32-bit truncations). Used for the\n * SGD negative-sampling stream; parity with Python is statistical, not bitwise (§5.5).\n * Returns a signed int32.\n */\nexport function tauRandInt(state: Int32Array): number {\n const s0 = state[0]! >>> 0;\n const s1 = state[1]! >>> 0;\n const s2 = state[2]! >>> 0;\n state[0] = (((s0 & 0xfffffffe) << 12) ^ ((((s0 << 13) >>> 0) ^ s0) >>> 19)) | 0;\n state[1] = (((s1 & 0xfffffff8) << 4) ^ ((((s1 << 2) >>> 0) ^ s1) >>> 25)) | 0;\n state[2] = (((s2 & 0xfffffff0) << 17) ^ ((((s2 << 3) >>> 0) ^ s2) >>> 11)) | 0;\n return (state[0]! ^ state[1]! ^ state[2]!) | 0;\n}\n\n/** Floor-mod (Python `%` semantics) of a signed int by a positive int. */\nexport function floorMod(a: number, n: number): number {\n const r = a % n;\n return r < 0 ? r + n : r;\n}\n\n/** tau_rand: uniform in [0,1] from tauRandInt (umap utils.py:66-80). */\nexport function tauRand(state: Int32Array): number {\n return Math.abs(tauRandInt(state) / 0x7fffffff);\n}\n","/**\n * NN-descent (pynndescent_.py @ 0.6.0, notes §3-4). Single-threaded deterministic\n * structure (seeded umap forces n_jobs=1). Updates are applied directly rather than\n * via per-thread buffers — same candidate sets and acceptance rules, so the result\n * quality is equivalent (recall-gated, not bitwise; §5.5).\n */\n\nimport { floorMod, tauRand, tauRandInt } from '../rng.js';\nimport {\n checkedFlaggedHeapPush,\n checkedHeapPush,\n deheapSort,\n makeHeap,\n type NeighborHeap,\n} from './heap.js';\n\nexport type DistFn = (i: number, j: number) => number;\n\nexport interface NnDescentOptions {\n maxCandidates?: number;\n nIters?: number;\n delta?: number;\n /** dense leaf init: d < max(tp,tq); sparse: d < tp or d < tq (notes §9) */\n leafAcceptance?: 'max' | 'either';\n /** per-iteration progress hook (the library never writes to the console) */\n onIteration?: (iter: number, c: number) => void;\n}\n\n/** init_rp_tree: all pairs within each leaf. */\nexport function initRpTree(\n nLeaves: number,\n leafCols: number,\n leafArray: Int32Array,\n heap: NeighborHeap,\n dist: DistFn,\n leafAcceptance: 'max' | 'either' = 'max',\n): void {\n const k = heap.size;\n const distances = heap.distances;\n const either = leafAcceptance === 'either';\n for (let leaf = 0; leaf < nLeaves; leaf++) {\n const base = leaf * leafCols;\n for (let a = 0; a < leafCols; a++) {\n const p = leafArray[base + a]!;\n if (p < 0) break;\n for (let b = a + 1; b < leafCols; b++) {\n const q = leafArray[base + b]!;\n if (q < 0) break;\n const d = dist(p, q);\n // dense: d < max(th_p, th_q) (pynndescent_.py:103-104); sparse: either (sparse_nndescent.py:67)\n const accept = either\n ? d < distances[p * k]! || d < distances[q * k]!\n : d < Math.max(distances[p * k]!, distances[q * k]!);\n if (accept) {\n checkedFlaggedHeapPush(heap, p, d, q, 1);\n checkedFlaggedHeapPush(heap, q, d, p, 1);\n }\n }\n }\n }\n}\n\n/** init_random: one attempt per missing slot (pynndescent_.py:188-203). */\nexport function initRandom(\n n: number,\n heap: NeighborHeap,\n dist: DistFn,\n rngState: Int32Array,\n): void {\n const k = heap.size;\n for (let i = 0; i < n; i++) {\n if (heap.indices[i * k]! < 0) {\n let filled = 0;\n for (let j = 0; j < k; j++) if (heap.indices[i * k + j]! >= 0) filled++;\n const missing = k - filled;\n for (let j = 0; j < missing; j++) {\n const idx = Math.abs(tauRandInt(rngState)) % n;\n const d = dist(idx, i);\n checkedFlaggedHeapPush(heap, i, d, idx, 1);\n }\n }\n }\n}\n\n/**\n * new_build_candidates (utils.py:221-320): sample up to maxCandidates new/old\n * candidates per vertex via random priorities; clear \"new\" flags for sampled entries.\n * The master rng state is NOT advanced (per-thread copies in Python; thread 0 here).\n */\nfunction buildCandidates(\n heap: NeighborHeap,\n maxCandidates: number,\n masterRng: Int32Array,\n): { newIdx: Int32Array; oldIdx: Int32Array } {\n const n = heap.n;\n const k = heap.size;\n const newIdx = new Int32Array(n * maxCandidates).fill(-1);\n const newPri = new Float32Array(n * maxCandidates).fill(Infinity);\n const oldIdx = new Int32Array(n * maxCandidates).fill(-1);\n const oldPri = new Float32Array(n * maxCandidates).fill(Infinity);\n\n const localRng = Int32Array.from(masterRng); // copy — master not advanced\n\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < k; j++) {\n const idx = heap.indices[i * k + j]!;\n if (idx < 0) continue;\n const d = tauRand(localRng);\n const isNew = heap.flags[i * k + j]! === 1;\n if (isNew) {\n checkedHeapPush(newPri, newIdx, i * maxCandidates, maxCandidates, d, idx);\n checkedHeapPush(newPri, newIdx, idx * maxCandidates, maxCandidates, d, i);\n } else {\n checkedHeapPush(oldPri, oldIdx, i * maxCandidates, maxCandidates, d, idx);\n checkedHeapPush(oldPri, oldIdx, idx * maxCandidates, maxCandidates, d, i);\n }\n }\n }\n // flag reset pass: sampled \"new\" entries become old\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < k; j++) {\n const idx = heap.indices[i * k + j]!;\n if (idx < 0 || heap.flags[i * k + j] !== 1) continue;\n const cbase = i * maxCandidates;\n for (let c = 0; c < maxCandidates; c++) {\n if (newIdx[cbase + c] === idx) {\n heap.flags[i * k + j] = 0;\n break;\n }\n }\n }\n }\n return { newIdx, oldIdx };\n}\n\n/** Local joins over candidate lists; returns number of successful pushes. */\nfunction processCandidates(\n heap: NeighborHeap,\n newIdx: Int32Array,\n oldIdx: Int32Array,\n maxCandidates: number,\n dist: DistFn,\n): number {\n const n = heap.n;\n const k = heap.size;\n const distances = heap.distances;\n let c = 0;\n for (let i = 0; i < n; i++) {\n const base = i * maxCandidates;\n for (let j = 0; j < maxCandidates; j++) {\n const p = newIdx[base + j]!;\n if (p < 0) continue;\n // new-new pairs, k from j (INCLUDES k == j ⇒ (p,p,0) self-edge; umap depends on it)\n for (let t = j; t < maxCandidates; t++) {\n const q = newIdx[base + t]!;\n if (q < 0) continue;\n const d = dist(p, q);\n if (d <= Math.max(distances[p * k]!, distances[q * k]!)) {\n c += checkedFlaggedHeapPush(heap, p, d, q, 1);\n c += checkedFlaggedHeapPush(heap, q, d, p, 1);\n }\n }\n // new-old pairs\n for (let t = 0; t < maxCandidates; t++) {\n const q = oldIdx[base + t]!;\n if (q < 0) continue;\n const d = dist(p, q);\n if (d <= Math.max(distances[p * k]!, distances[q * k]!)) {\n c += checkedFlaggedHeapPush(heap, p, d, q, 1);\n c += checkedFlaggedHeapPush(heap, q, d, p, 1);\n }\n }\n }\n }\n return c;\n}\n\n/**\n * Full NN-descent. `leafArray` from the RP forest (or null to skip tree init).\n * Returns the heap AFTER deheap_sort: ascending distances, (-1, inf) tails.\n * Distances are in the INTERNAL metric space (corrections applied by the caller).\n */\nexport function nnDescent(\n n: number,\n nNeighbors: number,\n dist: DistFn,\n rngState: Int32Array,\n leafArray: { data: Int32Array; rows: number; cols: number } | null,\n {\n maxCandidates = 60,\n nIters = 10,\n delta = 0.001,\n leafAcceptance = 'max',\n onIteration,\n }: NnDescentOptions = {},\n): NeighborHeap {\n const heap = makeHeap(n, nNeighbors);\n if (leafArray) {\n initRpTree(leafArray.rows, leafArray.cols, leafArray.data, heap, dist, leafAcceptance);\n }\n initRandom(n, heap, dist, rngState);\n\n for (let iter = 0; iter < nIters; iter++) {\n const { newIdx, oldIdx } = buildCandidates(heap, maxCandidates, rngState);\n const c = processCandidates(heap, newIdx, oldIdx, maxCandidates, dist);\n onIteration?.(iter, c);\n if (c <= delta * nNeighbors * n) break;\n }\n deheapSort(heap);\n return heap;\n}\n\n/** umap's NNDescent parameter rules (umap_.py:327-328). */\nexport function umapNnDescentParams(n: number): { nTrees: number; nIters: number } {\n return {\n nTrees: Math.min(64, 5 + Math.round(Math.sqrt(n) / 20.0)),\n nIters: Math.max(5, Math.round(Math.log2(n))),\n };\n}\n\n/** Random-fill count used by query init. */\nexport function missingSlots(heap: NeighborHeap, row: number): number {\n let filled = 0;\n for (let j = 0; j < heap.size; j++) if (heap.indices[row * heap.size + j]! >= 0) filled++;\n return heap.size - filled;\n}\n\nexport { floorMod };\n","/**\n * Random-projection forest (pynndescent rp_trees.py @ 0.6.0; notes §2).\n * Dense euclidean + angular splits, flat preorder trees, leaf arrays for descent\n * init, and the 0.6.0 graph-informed hub tree used as the search tree.\n */\n\nimport { floorMod, tauRandInt } from '../rng.js';\nimport type { Matrix } from '../types.js';\n\nexport const RP_EPS = 1e-8;\nexport const MIN_SPLIT_BALANCE = 0.1;\n\nexport interface FlatTree {\n /** nNodes × dim hyperplane vectors (dense) */\n hyperplanes: Float32Array;\n offsets: Float32Array;\n /** nNodes × 2; internal: child node ids (>0); leaf: (-leafStart, -leafEnd) */\n children: Int32Array;\n /** concatenated leaf point indices (a permutation of all points) */\n indices: Int32Array;\n leafSize: number;\n nNodes: number;\n dim: number;\n}\n\ninterface BuildNode {\n left: BuildNode | null;\n right: BuildNode | null;\n hyperplane: Float32Array | null;\n offset: number;\n points: Int32Array | null;\n}\n\nexport function defaultLeafSize(nNeighbors: number): number {\n return Math.max(60, Math.min(256, 5 * nNeighbors));\n}\n\ninterface SplitResult {\n left: Int32Array;\n right: Int32Array;\n hyperplane: Float32Array;\n offset: number;\n}\n\nfunction partition(indices: Int32Array, side: Uint8Array, nLeft: number, nRight: number) {\n const left = new Int32Array(nLeft);\n const right = new Int32Array(nRight);\n let il = 0;\n let ir = 0;\n for (let i = 0; i < indices.length; i++) {\n if (side[i] === 0) left[il++] = indices[i]!;\n else right[ir++] = indices[i]!;\n }\n return { left, right };\n}\n\nfunction euclideanSplit(\n data: Float32Array,\n dim: number,\n indices: Int32Array,\n rngState: Int32Array,\n): SplitResult {\n const n = indices.length;\n const leftIndex = floorMod(tauRandInt(rngState), n);\n let rightIndex = floorMod(tauRandInt(rngState), n);\n if (leftIndex === rightIndex) rightIndex += 1;\n rightIndex = floorMod(rightIndex, n);\n const l = indices[leftIndex]!;\n const r = indices[rightIndex]!;\n\n const hyperplane = new Float32Array(dim);\n let offset = 0;\n for (let d = 0; d < dim; d++) {\n const hv = data[l * dim + d]! - data[r * dim + d]!;\n hyperplane[d] = hv;\n offset -= (hv * (data[l * dim + d]! + data[r * dim + d]!)) / 2.0;\n }\n\n const side = new Uint8Array(n);\n let nLeft = 0;\n let nRight = 0;\n for (let i = 0; i < n; i++) {\n let margin = offset;\n const p = indices[i]!;\n for (let d = 0; d < dim; d++) margin += hyperplane[d]! * data[p * dim + d]!;\n if (Math.abs(margin) < RP_EPS) {\n side[i] = Math.abs(tauRandInt(rngState)) % 2;\n } else if (margin > 0) {\n side[i] = 0;\n } else {\n side[i] = 1;\n }\n if (side[i] === 0) nLeft++;\n else nRight++;\n }\n if (nLeft === 0 || nRight === 0) {\n nLeft = 0;\n nRight = 0;\n for (let i = 0; i < n; i++) {\n side[i] = floorMod(tauRandInt(rngState), 2);\n if (side[i] === 0) nLeft++;\n else nRight++;\n }\n }\n const { left, right } = partition(indices, side, nLeft, nRight);\n return { left, right, hyperplane, offset };\n}\n\nfunction angularSplit(\n data: Float32Array,\n dim: number,\n indices: Int32Array,\n rngState: Int32Array,\n): SplitResult {\n const n = indices.length;\n const leftIndex = floorMod(tauRandInt(rngState), n);\n let rightIndex = floorMod(tauRandInt(rngState), n);\n if (leftIndex === rightIndex) rightIndex += 1;\n rightIndex = floorMod(rightIndex, n);\n const l = indices[leftIndex]!;\n const r = indices[rightIndex]!;\n\n let leftNorm = 0;\n let rightNorm = 0;\n for (let d = 0; d < dim; d++) {\n leftNorm += data[l * dim + d]! * data[l * dim + d]!;\n rightNorm += data[r * dim + d]! * data[r * dim + d]!;\n }\n leftNorm = Math.sqrt(leftNorm);\n rightNorm = Math.sqrt(rightNorm);\n if (Math.abs(leftNorm) < RP_EPS) leftNorm = 1.0;\n if (Math.abs(rightNorm) < RP_EPS) rightNorm = 1.0;\n\n const hyperplane = new Float32Array(dim);\n let hnorm = 0;\n for (let d = 0; d < dim; d++) {\n const hv = data[l * dim + d]! / leftNorm - data[r * dim + d]! / rightNorm;\n hyperplane[d] = hv;\n hnorm += hv * hv;\n }\n hnorm = Math.sqrt(hnorm);\n if (Math.abs(hnorm) < RP_EPS) hnorm = 1.0;\n for (let d = 0; d < dim; d++) hyperplane[d] = hyperplane[d]! / hnorm;\n\n const side = new Uint8Array(n);\n let nLeft = 0;\n let nRight = 0;\n for (let i = 0; i < n; i++) {\n let margin = 0;\n const p = indices[i]!;\n for (let d = 0; d < dim; d++) margin += hyperplane[d]! * data[p * dim + d]!;\n if (Math.abs(margin) < RP_EPS) {\n side[i] = floorMod(tauRandInt(rngState), 2);\n } else if (margin > 0) {\n side[i] = 0;\n } else {\n side[i] = 1;\n }\n if (side[i] === 0) nLeft++;\n else nRight++;\n }\n if (nLeft === 0 || nRight === 0) {\n nLeft = 0;\n nRight = 0;\n for (let i = 0; i < n; i++) {\n side[i] = floorMod(tauRandInt(rngState), 2);\n if (side[i] === 0) nLeft++;\n else nRight++;\n }\n }\n const { left, right } = partition(indices, side, nLeft, nRight);\n return { left, right, hyperplane, offset: 0.0 };\n}\n\nfunction buildTree(\n data: Float32Array,\n dim: number,\n indices: Int32Array,\n leafSize: number,\n maxDepth: number,\n angular: boolean,\n rngState: Int32Array,\n): BuildNode {\n if (indices.length > leafSize && maxDepth > 0) {\n const { left, right, hyperplane, offset } = angular\n ? angularSplit(data, dim, indices, rngState)\n : euclideanSplit(data, dim, indices, rngState);\n return {\n left: buildTree(data, dim, left, leafSize, maxDepth - 1, angular, rngState),\n right: buildTree(data, dim, right, leafSize, maxDepth - 1, angular, rngState),\n hyperplane,\n offset,\n points: null,\n };\n }\n return { left: null, right: null, hyperplane: null, offset: 0, points: indices };\n}\n\n/** Flatten to preorder (convert_tree_format, rp_trees.py:3019-3049). */\nexport function flattenTree(root: BuildNode, nPoints: number, dim: number): FlatTree {\n let nNodes = 0;\n let maxLeaf = 0;\n const count = (node: BuildNode): void => {\n nNodes++;\n if (node.points) {\n if (node.points.length > maxLeaf) maxLeaf = node.points.length;\n } else {\n count(node.left!);\n count(node.right!);\n }\n };\n count(root);\n const hyperplanes = new Float32Array(nNodes * dim);\n const offsets = new Float32Array(nNodes);\n const children = new Int32Array(nNodes * 2).fill(-1);\n const indices = new Int32Array(nPoints).fill(-1);\n let nodeNum = 0;\n let leafStart = 0;\n const walk = (node: BuildNode): void => {\n const my = nodeNum;\n if (node.points) {\n children[my * 2] = -leafStart;\n children[my * 2 + 1] = -(leafStart + node.points.length);\n indices.set(node.points, leafStart);\n leafStart += node.points.length;\n return;\n }\n hyperplanes.set(node.hyperplane!, my * dim);\n offsets[my] = node.offset;\n nodeNum++;\n children[my * 2] = nodeNum;\n walk(node.left!);\n nodeNum++;\n children[my * 2 + 1] = nodeNum;\n walk(node.right!);\n };\n walk(root);\n return { hyperplanes, offsets, children, indices, leafSize: maxLeaf, nNodes, dim };\n}\n\nexport interface MakeForestOptions {\n leafSize?: number;\n angular?: boolean;\n maxDepth?: number;\n}\n\n/** make_forest: per-tree tau states drawn from the master PRNG. */\nexport function makeForest(\n X: Matrix,\n nNeighbors: number,\n nTrees: number,\n treeStates: Int32Array[], // one Int32Array(3) per tree\n { leafSize, angular = false, maxDepth = 200 }: MakeForestOptions = {},\n): FlatTree[] {\n const ls = leafSize ?? defaultLeafSize(nNeighbors);\n const all = new Int32Array(X.rows);\n for (let i = 0; i < X.rows; i++) all[i] = i;\n const forest: FlatTree[] = [];\n for (let t = 0; t < nTrees; t++) {\n const root = buildTree(X.data, X.cols, all.slice(), ls, maxDepth, angular, treeStates[t]!);\n forest.push(flattenTree(root, X.rows, X.cols));\n }\n return forest;\n}\n\n/** rptree_leaf_array: rows = leaves, padded with -1 to the forest-wide max leaf size. */\nexport function leafArrayFromForest(forest: FlatTree[]): {\n data: Int32Array;\n rows: number;\n cols: number;\n} {\n if (forest.length === 0) return { data: new Int32Array([-1]), rows: 1, cols: 1 };\n let maxLeafSize = 0;\n for (const t of forest) maxLeafSize = Math.max(maxLeafSize, t.leafSize);\n const rows: Int32Array[] = [];\n for (const t of forest) {\n for (let node = 0; node < t.nNodes; node++) {\n const c0 = t.children[node * 2]!;\n if (c0 <= 0) {\n const start = -c0;\n const end = -t.children[node * 2 + 1]!;\n const row = new Int32Array(maxLeafSize).fill(-1);\n row.set(t.indices.subarray(start, end));\n rows.push(row);\n }\n }\n }\n const out = new Int32Array(rows.length * maxLeafSize);\n rows.forEach((r, i) => {\n out.set(r, i * maxLeafSize);\n });\n return { data: out, rows: rows.length, cols: maxLeafSize };\n}\n\n/** search_flat_tree descent → [leafStart, leafEnd) into tree.indices. */\nexport function searchFlatTree(\n tree: FlatTree,\n point: Float32Array,\n rngState: Int32Array,\n): [number, number] {\n let node = 0;\n while (tree.children[node * 2]! > 0) {\n // select_side (rp_trees.py:2662-2677)\n let margin = tree.offsets[node]!;\n const base = node * tree.dim;\n for (let d = 0; d < tree.dim; d++) margin += tree.hyperplanes[base + d]! * point[d]!;\n let side: number;\n if (Math.abs(margin) < RP_EPS) side = Math.abs(tauRandInt(rngState)) % 2;\n else side = margin > 0 ? 0 : 1;\n node = tree.children[node * 2 + side]!;\n }\n return [-tree.children[node * 2]!, -tree.children[node * 2 + 1]!];\n}\n\n// ---------------------------------------------------------------------------\n// 0.6.0 hub tree (search tree built from the finished kNN graph; notes §6.1)\n// ---------------------------------------------------------------------------\n\ninterface HubSplitResult {\n hyperplane: Float32Array;\n offset: number;\n side: Uint8Array;\n nLeft: number;\n nRight: number;\n}\n\nfunction hubSplit(\n data: Float32Array,\n dim: number,\n indices: Int32Array,\n hubA: number,\n hubB: number,\n angular: boolean,\n): HubSplitResult {\n const n = indices.length;\n const hyperplane = new Float32Array(dim);\n let offset = 0;\n if (angular) {\n let an = 0;\n let bn = 0;\n for (let d = 0; d < dim; d++) {\n an += data[hubA * dim + d]! * data[hubA * dim + d]!;\n bn += data[hubB * dim + d]! * data[hubB * dim + d]!;\n }\n an = Math.sqrt(an);\n bn = Math.sqrt(bn);\n if (Math.abs(an) < RP_EPS) an = 1;\n if (Math.abs(bn) < RP_EPS) bn = 1;\n let hn = 0;\n for (let d = 0; d < dim; d++) {\n const hv = data[hubA * dim + d]! / an - data[hubB * dim + d]! / bn;\n hyperplane[d] = hv;\n hn += hv * hv;\n }\n hn = Math.sqrt(hn);\n if (Math.abs(hn) < RP_EPS) hn = 1;\n for (let d = 0; d < dim; d++) hyperplane[d] = hyperplane[d]! / hn;\n } else {\n for (let d = 0; d < dim; d++) {\n const hv = data[hubA * dim + d]! - data[hubB * dim + d]!;\n hyperplane[d] = hv;\n offset -= (hv * (data[hubA * dim + d]! + data[hubB * dim + d]!)) / 2.0;\n }\n }\n const side = new Uint8Array(n);\n let nLeft = 0;\n let nRight = 0;\n for (let i = 0; i < n; i++) {\n let margin = offset;\n const p = indices[i]!;\n for (let d = 0; d < dim; d++) margin += hyperplane[d]! * data[p * dim + d]!;\n let s: number;\n if (margin > RP_EPS) s = 0;\n else if (margin < -RP_EPS) s = 1;\n else s = i % 2; // deterministic alternation on ties — no rng in hub splits\n side[i] = s;\n if (s === 0) nLeft++;\n else nRight++;\n }\n return { hyperplane, offset, side, nLeft, nRight };\n}\n\n/** make_hub_tree (rp_trees.py:1232-1312) — top-3-degree hub pairs, best balance. */\nexport function makeHubTree(\n X: Matrix,\n knnIndices: Int32Array,\n leafSize = 30,\n angular = false,\n maxDepth = 200,\n): FlatTree {\n const n = X.rows;\n const degrees = new Int32Array(n);\n for (let t = 0; t < knnIndices.length; t++) {\n const j = knnIndices[t]!;\n if (j >= 0) degrees[j]!++;\n }\n\n const topHubs = (indices: Int32Array): number[] => {\n // top-3 by in-degree, strict > so earlier-seen wins ties\n const top: number[] = [];\n const topDeg: number[] = [];\n for (let i = 0; i < indices.length; i++) {\n const p = indices[i]!;\n const dg = degrees[p]!;\n let ins = -1;\n for (let s = 0; s < Math.min(3, top.length + 1); s++) {\n if (s >= top.length || dg > topDeg[s]!) {\n ins = s;\n break;\n }\n }\n if (ins >= 0) {\n top.splice(ins, 0, p);\n topDeg.splice(ins, 0, dg);\n if (top.length > 3) {\n top.pop();\n topDeg.pop();\n }\n }\n }\n return top;\n };\n\n const build = (indices: Int32Array, depth: number): BuildNode => {\n if (indices.length <= leafSize || depth <= 0) {\n return { left: null, right: null, hyperplane: null, offset: 0, points: indices };\n }\n const hubs = topHubs(indices);\n let best: HubSplitResult | null = null;\n let bestBalance = -1;\n for (let a = 0; a < hubs.length; a++) {\n for (let b = a + 1; b < hubs.length; b++) {\n const res = hubSplit(X.data, X.cols, indices, hubs[a]!, hubs[b]!, angular);\n if (res.nLeft === 0 || res.nRight === 0) continue;\n const balance = Math.min(res.nLeft, res.nRight) / indices.length;\n if (balance > bestBalance) {\n bestBalance = balance;\n best = res;\n }\n }\n }\n if (best === null || bestBalance < MIN_SPLIT_BALANCE) {\n return { left: null, right: null, hyperplane: null, offset: 0, points: indices };\n }\n const { left, right } = partition(indices, best.side, best.nLeft, best.nRight);\n return {\n left: build(left, depth - 1),\n right: build(right, depth - 1),\n hyperplane: best.hyperplane,\n offset: best.offset,\n points: null,\n };\n };\n\n const all = new Int32Array(n);\n for (let i = 0; i < n; i++) all[i] = i;\n return flattenTree(build(all, maxDepth), n, X.cols);\n}\n","/**\n * SGD layout optimization — faithful port of layouts.py (notes §9-11):\n * optimize_layout_euclidean (+ densMAP terms) and optimize_layout_generic.\n * Single-thread reference implementation; the worker backend shards the same\n * epoch kernels (Hogwild) at M6.\n */\n\nimport { tauRandInt } from '../rng.js';\nimport { throwIfAborted } from '../types.js';\nimport type { OutputMetricGradFn } from './outputMetrics.js';\n\nexport interface EpochCallbackInfo {\n epoch: number;\n nEpochs: number;\n phase: 'layout';\n embedding?: Float32Array;\n}\n\nexport interface DensmapParams {\n lambda: number;\n frac: number;\n varShift: number;\n /** per-edge membership weights (graph.data), aligned with head/tail */\n mu: Float32Array;\n /** per-vertex membership sums */\n muSum: Float32Array;\n /** standardized log original radii */\n R: Float32Array;\n}\n\nexport interface OptimizeLayoutOptions {\n gamma?: number; // repulsion_strength\n initialAlpha?: number;\n negativeSampleRate?: number;\n moveOther?: boolean;\n densmap?: DensmapParams | null;\n onEpoch?: (info: EpochCallbackInfo) => void;\n signal?: AbortSignal;\n /** snapshot embedding into onEpoch every k epochs (0 = never) */\n snapshotEvery?: number;\n}\n\nexport function clip(val: number): number {\n if (val > 4.0) return 4.0;\n if (val < -4.0) return -4.0;\n return val;\n}\n\n/** Per-vertex tau streams: base rng_state + bit-pattern of the vertex's first coord. */\nexport function perVertexRngStates(\n n: number,\n rngState: Int32Array,\n embedding: Float32Array,\n dim: number,\n): Int32Array {\n const states = new Int32Array(n * 3);\n const buf = new DataView(new ArrayBuffer(8));\n for (let j = 0; j < n; j++) {\n buf.setFloat64(0, embedding[j * dim]!, true);\n const lo = buf.getInt32(0, true);\n const hi = buf.getInt32(4, true);\n // int64 add of the bit pattern folds into 32-bit words (statistical equivalence)\n states[j * 3] = (rngState[0]! + lo) | 0;\n states[j * 3 + 1] = (rngState[1]! + hi) | 0;\n states[j * 3 + 2] = (rngState[2]! + (lo ^ hi)) | 0;\n }\n return states;\n}\n\n/**\n * optimize_layout_euclidean. Mutates headEmbedding (and tailEmbedding when\n * moveOther and they alias). Returns the final embedding.\n */\nexport function optimizeLayoutEuclidean(\n headEmbedding: Float32Array,\n tailEmbedding: Float32Array,\n dim: number,\n head: Int32Array,\n tail: Int32Array,\n nEpochs: number,\n nVertices: number,\n epochsPerSample: Float64Array,\n a: number,\n b: number,\n rngState: Int32Array,\n {\n gamma = 1.0,\n initialAlpha = 1.0,\n negativeSampleRate = 5,\n moveOther = true,\n densmap = null,\n onEpoch,\n signal,\n snapshotEvery = 0,\n }: OptimizeLayoutOptions = {},\n): Float32Array {\n const nEdges = epochsPerSample.length;\n let alpha = initialAlpha;\n\n const epochsPerNegativeSample = new Float64Array(nEdges);\n for (let i = 0; i < nEdges; i++) {\n epochsPerNegativeSample[i] = epochsPerSample[i]! / negativeSampleRate;\n }\n const epochOfNextNegativeSample = epochsPerNegativeSample.slice();\n const epochOfNextSample = epochsPerSample.slice();\n\n const nHead = headEmbedding.length / dim;\n const vertexRng = perVertexRngStates(nHead, rngState, headEmbedding, dim);\n\n // densMAP state\n let densReSum: Float32Array | null = null;\n let densPhiSum: Float32Array | null = null;\n let densMuTot = 0;\n if (densmap) {\n densReSum = new Float32Array(nVertices);\n densPhiSum = new Float32Array(nVertices);\n for (let i = 0; i < densmap.muSum.length; i++) densMuTot += densmap.muSum[i]!;\n densMuTot /= 2;\n }\n\n const rngRow = new Int32Array(3);\n\n for (let n = 0; n < nEpochs; n++) {\n throwIfAborted(signal);\n\n let densmapFlag = false;\n let densReStd = 0;\n let densReMean = 0;\n let densReCov = 0;\n if (densmap && densmap.lambda > 0 && (n + 1) / nEpochs > 1 - densmap.frac) {\n densmapFlag = true;\n // epoch init: recompute embedding local radii\n densReSum!.fill(0);\n densPhiSum!.fill(0);\n for (let i = 0; i < nEdges; i++) {\n const j = head[i]!;\n const k = tail[i]!;\n let d2 = 0;\n for (let d = 0; d < dim; d++) {\n const diff = headEmbedding[j * dim + d]! - tailEmbedding[k * dim + d]!;\n d2 += diff * diff;\n }\n const phi = 1.0 / (1.0 + a * d2 ** b);\n densReSum![j] = densReSum![j]! + phi * d2;\n densReSum![k] = densReSum![k]! + phi * d2;\n densPhiSum![j] = densPhiSum![j]! + phi;\n densPhiSum![k] = densPhiSum![k]! + phi;\n }\n let mean = 0;\n for (let i = 0; i < nVertices; i++) {\n densReSum![i] = Math.log(1e-8 + densReSum![i]! / densPhiSum![i]!);\n mean += densReSum![i]!;\n }\n mean /= nVertices;\n let variance = 0;\n let cov = 0;\n for (let i = 0; i < nVertices; i++) {\n variance += (densReSum![i]! - mean) ** 2;\n cov += densReSum![i]! * densmap.R[i]!;\n }\n variance /= nVertices;\n densReStd = Math.sqrt(variance + densmap.varShift);\n densReMean = mean;\n densReCov = cov / (nVertices - 1);\n }\n\n if (dim === 2 && !densmap) {\n // hot-path specialization (identical arithmetic, unrolled). Vertex j's\n // coords live in f32-rounded locals across the edge iteration —\n // Math.fround after every update reproduces the per-step Float32Array\n // rounding bit-exactly while avoiding the load/store traffic.\n const fround = Math.fround;\n const bm1 = b - 1.0;\n for (let i = 0; i < nEdges; i++) {\n if (epochOfNextSample[i]! > n) continue;\n const j = head[i]!;\n let k = tail[i]!;\n const jBase = j * 2;\n let kBase = k * 2;\n let cj0 = headEmbedding[jBase]!;\n let cj1 = headEmbedding[jBase + 1]!;\n let dx = cj0 - tailEmbedding[kBase]!;\n let dy = cj1 - tailEmbedding[kBase + 1]!;\n const d2 = dx * dx + dy * dy;\n if (d2 > 0) {\n // pow(d2, b-1) kept separate from pow(d2, b): numba op order, bit-exact\n const gc = (-2.0 * a * b * d2 ** bm1) / (a * d2 ** b + 1.0);\n const g0 = clip(gc * dx);\n const g1 = clip(gc * dy);\n cj0 = fround(cj0 + g0 * alpha);\n cj1 = fround(cj1 + g1 * alpha);\n if (moveOther) {\n tailEmbedding[kBase] = tailEmbedding[kBase]! - g0 * alpha;\n tailEmbedding[kBase + 1] = tailEmbedding[kBase + 1]! - g1 * alpha;\n }\n }\n epochOfNextSample[i] = epochOfNextSample[i]! + epochsPerSample[i]!;\n const nNegSamples = Math.trunc(\n (n - epochOfNextNegativeSample[i]!) / epochsPerNegativeSample[i]!,\n );\n // taus88 inlined on integer locals (bit-identical to tauRandInt)\n let s0 = vertexRng[j * 3]! | 0;\n let s1 = vertexRng[j * 3 + 1]! | 0;\n let s2 = vertexRng[j * 3 + 2]! | 0;\n for (let s = 0; s < nNegSamples; s++) {\n const u0 = s0 >>> 0;\n const u1 = s1 >>> 0;\n const u2 = s2 >>> 0;\n s0 = (((u0 & 0xfffffffe) << 12) ^ ((((u0 << 13) >>> 0) ^ u0) >>> 19)) | 0;\n s1 = (((u1 & 0xfffffff8) << 4) ^ ((((u1 << 2) >>> 0) ^ u1) >>> 25)) | 0;\n s2 = (((u2 & 0xfffffff0) << 17) ^ ((((u2 << 3) >>> 0) ^ u2) >>> 11)) | 0;\n const r = (s0 ^ s1 ^ s2) | 0;\n const rm = r % nVertices;\n k = rm + (rm < 0 ? nVertices : 0);\n if (j === k) continue;\n kBase = k * 2;\n dx = cj0 - tailEmbedding[kBase]!;\n dy = cj1 - tailEmbedding[kBase + 1]!;\n const dn = dx * dx + dy * dy;\n if (dn > 0) {\n const rc = (2.0 * gamma * b) / ((0.001 + dn) * (a * dn ** b + 1));\n cj0 = fround(cj0 + clip(rc * dx) * alpha);\n cj1 = fround(cj1 + clip(rc * dy) * alpha);\n }\n }\n headEmbedding[jBase] = cj0;\n headEmbedding[jBase + 1] = cj1;\n vertexRng[j * 3] = s0;\n vertexRng[j * 3 + 1] = s1;\n vertexRng[j * 3 + 2] = s2;\n epochOfNextNegativeSample[i] =\n epochOfNextNegativeSample[i]! + nNegSamples * epochsPerNegativeSample[i]!;\n }\n alpha = initialAlpha * (1.0 - n / nEpochs);\n if (onEpoch) {\n const info: EpochCallbackInfo = { epoch: n, nEpochs, phase: 'layout' };\n if (snapshotEvery > 0 && (n % snapshotEvery === 0 || n === nEpochs - 1)) {\n info.embedding = headEmbedding;\n }\n onEpoch(info);\n }\n continue;\n }\n\n for (let i = 0; i < nEdges; i++) {\n if (epochOfNextSample[i]! > n) continue;\n const j = head[i]!;\n let k = tail[i]!;\n const jBase = j * dim;\n let kBase = k * dim;\n\n let distSquared = 0;\n for (let d = 0; d < dim; d++) {\n const diff = headEmbedding[jBase + d]! - tailEmbedding[kBase + d]!;\n distSquared += diff * diff;\n }\n\n let gradCorCoeff = 0;\n if (densmapFlag) {\n const phi = 1.0 / (1.0 + a * distSquared ** b);\n const dphiTerm = (a * b * distSquared ** (b - 1)) / (1.0 + a * distSquared ** b);\n const qjk = phi / densPhiSum![k]!;\n const qkj = phi / densPhiSum![j]!;\n const drk = qjk * ((1.0 - b * (1 - phi)) / Math.exp(densReSum![k]!) + dphiTerm);\n const drj = qkj * ((1.0 - b * (1 - phi)) / Math.exp(densReSum![j]!) + dphiTerm);\n const reStdSq = densReStd * densReStd;\n const weightK = densmap!.R[k]! - (densReCov * (densReSum![k]! - densReMean)) / reStdSq;\n const weightJ = densmap!.R[j]! - (densReCov * (densReSum![j]! - densReMean)) / reStdSq;\n gradCorCoeff =\n (((densmap!.lambda * densMuTot) / (densmap!.mu[i]! * densReStd)) *\n (weightK * drk + weightJ * drj)) /\n nVertices;\n }\n\n let gradCoeff: number;\n if (distSquared > 0) {\n // pow(d2, b-1) kept separate from pow(d2, b): numba op order, bit-exact\n gradCoeff = (-2.0 * a * b * distSquared ** (b - 1.0)) / (a * distSquared ** b + 1.0);\n } else {\n gradCoeff = 0;\n }\n for (let d = 0; d < dim; d++) {\n const diff = headEmbedding[jBase + d]! - tailEmbedding[kBase + d]!;\n let gradD = clip(gradCoeff * diff);\n if (densmapFlag) gradD += clip(2 * gradCorCoeff * diff);\n headEmbedding[jBase + d] = headEmbedding[jBase + d]! + gradD * alpha;\n if (moveOther) {\n tailEmbedding[kBase + d] = tailEmbedding[kBase + d]! - gradD * alpha;\n }\n }\n\n epochOfNextSample[i] = epochOfNextSample[i]! + epochsPerSample[i]!;\n\n const nNegSamples = Math.trunc(\n (n - epochOfNextNegativeSample[i]!) / epochsPerNegativeSample[i]!,\n );\n\n rngRow[0] = vertexRng[j * 3]!;\n rngRow[1] = vertexRng[j * 3 + 1]!;\n rngRow[2] = vertexRng[j * 3 + 2]!;\n for (let p = 0; p < nNegSamples; p++) {\n const r = tauRandInt(rngRow);\n k = (r % nVertices) + (r % nVertices < 0 ? nVertices : 0);\n kBase = k * dim;\n distSquared = 0;\n for (let d = 0; d < dim; d++) {\n const diff = headEmbedding[jBase + d]! - tailEmbedding[kBase + d]!;\n distSquared += diff * diff;\n }\n let repCoeff: number;\n if (distSquared > 0) {\n repCoeff = (2.0 * gamma * b) / ((0.001 + distSquared) * (a * distSquared ** b + 1));\n } else if (j === k) {\n continue;\n } else {\n repCoeff = 0;\n }\n for (let d = 0; d < dim; d++) {\n let gradD: number;\n if (repCoeff > 0) {\n const diff = headEmbedding[jBase + d]! - tailEmbedding[kBase + d]!;\n gradD = clip(repCoeff * diff);\n } else {\n gradD = 0; // 0.5.9 semantics: no push at coincident points (notes §9.4)\n }\n headEmbedding[jBase + d] = headEmbedding[jBase + d]! + gradD * alpha;\n }\n }\n vertexRng[j * 3] = rngRow[0]!;\n vertexRng[j * 3 + 1] = rngRow[1]!;\n vertexRng[j * 3 + 2] = rngRow[2]!;\n\n epochOfNextNegativeSample[i] =\n epochOfNextNegativeSample[i]! + nNegSamples * epochsPerNegativeSample[i]!;\n }\n\n alpha = initialAlpha * (1.0 - n / nEpochs);\n if (onEpoch) {\n const info: EpochCallbackInfo = { epoch: n, nEpochs, phase: 'layout' };\n if (snapshotEvery > 0 && (n % snapshotEvery === 0 || n === nEpochs - 1)) {\n info.embedding = headEmbedding;\n }\n onEpoch(info);\n }\n }\n return headEmbedding;\n}\n\n/**\n * optimize_layout_inverse (layouts.py:663-732; notes §13): optimizes DATA-space\n * positions of inverse-transformed points against the raw training data.\n * Negative samples use the SHARED rng state (not per-vertex).\n */\nexport function optimizeLayoutInverse(\n headEmbedding: Float32Array, // query points in DATA space (mutated)\n tailEmbedding: Float32Array, // raw training data (fixed)\n dim: number,\n head: Int32Array,\n tail: Int32Array,\n weight: Float32Array,\n sigmas: Float32Array,\n rhos: Float32Array,\n nEpochs: number,\n nVertices: number,\n epochsPerSample: Float64Array,\n rngState: Int32Array,\n outputMetric: OutputMetricGradFn,\n { gamma = 1.0, initialAlpha = 1.0, negativeSampleRate = 5, signal }: OptimizeLayoutOptions = {},\n): Float32Array {\n const nEdges = epochsPerSample.length;\n let alpha = initialAlpha;\n const epochsPerNegativeSample = new Float64Array(nEdges);\n for (let i = 0; i < nEdges; i++) {\n epochsPerNegativeSample[i] = epochsPerSample[i]! / negativeSampleRate;\n }\n const epochOfNextNegativeSample = epochsPerNegativeSample.slice();\n const epochOfNextSample = epochsPerSample.slice();\n const grad = new Float64Array(dim);\n const cur = new Float32Array(dim);\n const oth = new Float32Array(dim);\n\n for (let n = 0; n < nEpochs; n++) {\n throwIfAborted(signal);\n for (let i = 0; i < nEdges; i++) {\n if (epochOfNextSample[i]! > n) continue;\n const j = head[i]!;\n let k = tail[i]!;\n const jBase = j * dim;\n let kBase = k * dim;\n for (let d = 0; d < dim; d++) {\n cur[d] = headEmbedding[jBase + d]!;\n oth[d] = tailEmbedding[kBase + d]!;\n }\n outputMetric(cur, oth, grad);\n const wL = weight[i]!;\n const gradCoeff = -(1 / (wL * sigmas[k]! + 1e-6));\n for (let d = 0; d < dim; d++) {\n const gradD = clip(gradCoeff * grad[d]!);\n headEmbedding[jBase + d] = headEmbedding[jBase + d]! + gradD * alpha;\n }\n epochOfNextSample[i] = epochOfNextSample[i]! + epochsPerSample[i]!;\n const nNegSamples = Math.trunc(\n (n - epochOfNextNegativeSample[i]!) / epochsPerNegativeSample[i]!,\n );\n for (let p = 0; p < nNegSamples; p++) {\n const r = tauRandInt(rngState);\n k = (r % nVertices) + (r % nVertices < 0 ? nVertices : 0);\n kBase = k * dim;\n for (let d = 0; d < dim; d++) {\n cur[d] = headEmbedding[jBase + d]!;\n oth[d] = tailEmbedding[kBase + d]!;\n }\n const distOutput = outputMetric(cur, oth, grad);\n const wH = Math.exp(-Math.max(distOutput - rhos[k]!, 1e-6) / (sigmas[k]! + 1e-6));\n // sigma=inf (fastmath fold, D-006) makes (1−wH)·σ ill-defined (0·∞). The pinned\n // wheel produces huge finite coefficients that the ±4 clip saturates; we take\n // the same clip-saturating limit (denominator → 1e-6). See DECISIONS D-010.\n let denom = (1 - wH) * sigmas[k]! + 1e-6;\n if (!Number.isFinite(denom)) denom = 1e-6;\n const gradCoeffNeg = -gamma * ((0 - wH) / denom);\n for (let d = 0; d < dim; d++) {\n const gradD = clip(gradCoeffNeg * grad[d]!);\n headEmbedding[jBase + d] = headEmbedding[jBase + d]! + gradD * alpha;\n }\n }\n epochOfNextNegativeSample[i] =\n epochOfNextNegativeSample[i]! + nNegSamples * epochsPerNegativeSample[i]!;\n }\n alpha = initialAlpha * (1.0 - n / nEpochs);\n }\n return headEmbedding;\n}\n\n/** optimize_layout_generic — non-euclidean output metrics (notes §10). */\nexport function optimizeLayoutGeneric(\n headEmbedding: Float32Array,\n tailEmbedding: Float32Array,\n dim: number,\n head: Int32Array,\n tail: Int32Array,\n nEpochs: number,\n nVertices: number,\n epochsPerSample: Float64Array,\n a: number,\n b: number,\n rngState: Int32Array,\n outputMetric: OutputMetricGradFn,\n {\n gamma = 1.0,\n initialAlpha = 1.0,\n negativeSampleRate = 5,\n moveOther = true,\n onEpoch,\n signal,\n snapshotEvery = 0,\n }: OptimizeLayoutOptions = {},\n): Float32Array {\n const nEdges = epochsPerSample.length;\n let alpha = initialAlpha;\n\n const epochsPerNegativeSample = new Float64Array(nEdges);\n for (let i = 0; i < nEdges; i++) {\n epochsPerNegativeSample[i] = epochsPerSample[i]! / negativeSampleRate;\n }\n const epochOfNextNegativeSample = epochsPerNegativeSample.slice();\n const epochOfNextSample = epochsPerSample.slice();\n\n const nHead = headEmbedding.length / dim;\n const vertexRng = perVertexRngStates(nHead, rngState, headEmbedding, dim);\n const grad = new Float64Array(dim);\n const revGrad = new Float64Array(dim);\n const rngRow = new Int32Array(3);\n const cur = new Float32Array(dim);\n const oth = new Float32Array(dim);\n\n for (let n = 0; n < nEpochs; n++) {\n throwIfAborted(signal);\n for (let i = 0; i < nEdges; i++) {\n if (epochOfNextSample[i]! > n) continue;\n const j = head[i]!;\n let k = tail[i]!;\n const jBase = j * dim;\n let kBase = k * dim;\n\n for (let d = 0; d < dim; d++) {\n cur[d] = headEmbedding[jBase + d]!;\n oth[d] = tailEmbedding[kBase + d]!;\n }\n const distOutput = outputMetric(cur, oth, grad);\n outputMetric(oth, cur, revGrad);\n\n const wL = distOutput > 0 ? (1 + a * distOutput ** (2 * b)) ** -1 : 1.0;\n const gradCoeff = (2 * b * (wL - 1)) / (distOutput + 1e-6);\n\n for (let d = 0; d < dim; d++) {\n const gradD = clip(gradCoeff * grad[d]!);\n headEmbedding[jBase + d] = headEmbedding[jBase + d]! + gradD * alpha;\n if (moveOther) {\n const revD = clip(gradCoeff * revGrad[d]!);\n tailEmbedding[kBase + d] = tailEmbedding[kBase + d]! + revD * alpha;\n }\n }\n\n epochOfNextSample[i] = epochOfNextSample[i]! + epochsPerSample[i]!;\n const nNegSamples = Math.trunc(\n (n - epochOfNextNegativeSample[i]!) / epochsPerNegativeSample[i]!,\n );\n\n rngRow[0] = vertexRng[j * 3]!;\n rngRow[1] = vertexRng[j * 3 + 1]!;\n rngRow[2] = vertexRng[j * 3 + 2]!;\n for (let p = 0; p < nNegSamples; p++) {\n const r = tauRandInt(rngRow);\n k = (r % nVertices) + (r % nVertices < 0 ? nVertices : 0);\n kBase = k * dim;\n for (let d = 0; d < dim; d++) {\n cur[d] = headEmbedding[jBase + d]!;\n oth[d] = tailEmbedding[kBase + d]!;\n }\n const dNeg = outputMetric(cur, oth, grad);\n let wLNeg: number;\n if (dNeg > 0) {\n wLNeg = (1 + a * dNeg ** (2 * b)) ** -1;\n } else if (j === k) {\n continue;\n } else {\n wLNeg = 1.0;\n }\n const repCoeff = (gamma * 2 * b * wLNeg) / (dNeg + 1e-6);\n for (let d = 0; d < dim; d++) {\n const gradD = clip(repCoeff * grad[d]!);\n headEmbedding[jBase + d] = headEmbedding[jBase + d]! + gradD * alpha;\n }\n }\n vertexRng[j * 3] = rngRow[0]!;\n vertexRng[j * 3 + 1] = rngRow[1]!;\n vertexRng[j * 3 + 2] = rngRow[2]!;\n\n epochOfNextNegativeSample[i] =\n epochOfNextNegativeSample[i]! + nNegSamples * epochsPerNegativeSample[i]!;\n }\n alpha = initialAlpha * (1.0 - n / nEpochs);\n if (onEpoch) {\n const info: EpochCallbackInfo = { epoch: n, nEpochs, phase: 'layout' };\n if (snapshotEvery > 0 && (n % snapshotEvery === 0 || n === nEpochs - 1)) {\n info.embedding = headEmbedding;\n }\n onEpoch(info);\n }\n }\n return headEmbedding;\n}\n","/**\n * CPU worker entry (§5.3) — executes tree builds, NN-descent distance passes, and\n * Hogwild layout epoch shards. Data arrives once (SAB views when available,\n * copied buffers otherwise) via the `init` message.\n */\n\nimport { makeHeap } from '../../knn/heap.js';\nimport { type InternalMetric, resolveInternalMetric } from '../../knn/internal-metrics.js';\nimport { initRpTree } from '../../knn/nndescent.js';\nimport { defaultLeafSize, leafArrayFromForest, makeForest } from '../../knn/rpforest.js';\nimport { clip, perVertexRngStates } from '../../layout/sgd.js';\nimport type { MetricOptions } from '../../metrics.js';\nimport { tauRandInt } from '../../rng.js';\n\ninterface InitArgs {\n data: Float32Array; // possibly a SAB view\n rows: number;\n cols: number;\n metricName: string;\n metricOptions: MetricOptions;\n}\n\ninterface State {\n data: Float32Array;\n rows: number;\n cols: number;\n internal: InternalMetric;\n // layout state (set by layoutInit)\n embedding?: Float32Array;\n tailEmbedding?: Float32Array;\n head?: Int32Array;\n tail?: Int32Array;\n epochsPerSample?: Float64Array;\n epochOfNextSample?: Float64Array;\n epochsPerNegativeSample?: Float64Array;\n epochOfNextNegativeSample?: Float64Array;\n vertexRng?: Int32Array;\n layoutParams?: {\n a: number;\n b: number;\n gamma: number;\n initialAlpha: number;\n negativeSampleRate: number;\n dim: number;\n nVertices: number;\n moveOther: boolean;\n edgeStart: number;\n edgeEnd: number;\n sharedEmbedding: boolean;\n };\n}\n\nconst state: State = {} as State;\n\ntype Handler = (args: never) => { result: unknown; transfers?: Transferable[] };\n\nconst handlers: Record<string, Handler> = {\n init(args: InitArgs) {\n state.data = args.data;\n state.rows = args.rows;\n state.cols = args.cols;\n state.internal = resolveInternalMetric(args.metricName, args.metricOptions);\n return { result: true };\n },\n\n /** Build a slice of the RP forest; return its leaf array. */\n buildTrees(args: { treeStates: Int32Array[]; nNeighbors: number; angular: boolean }) {\n const forest = makeForest(\n { data: state.data, rows: state.rows, cols: state.cols },\n args.nNeighbors,\n args.treeStates.length,\n args.treeStates.map((s) => Int32Array.from(s)),\n { leafSize: defaultLeafSize(args.nNeighbors), angular: args.angular },\n );\n const leaves = leafArrayFromForest(forest);\n return {\n result: { data: leaves.data, rows: leaves.rows, cols: leaves.cols },\n transfers: [leaves.data.buffer],\n };\n },\n\n /**\n * Leaf-init distance pass over a leaf-row range: emits (p, q, d) triples that\n * pass the shared threshold snapshot.\n */\n leafUpdates(args: {\n leafArray: Int32Array;\n cols: number;\n rowStart: number;\n rowEnd: number;\n thresholds: Float32Array;\n }) {\n const dist = state.internal.dist;\n const dim = state.cols;\n const data = state.data;\n const out: number[] = [];\n for (let leaf = args.rowStart; leaf < args.rowEnd; leaf++) {\n const base = leaf * args.cols;\n for (let a = 0; a < args.cols; a++) {\n const p = args.leafArray[base + a]!;\n if (p < 0) break;\n for (let b = a + 1; b < args.cols; b++) {\n const q = args.leafArray[base + b]!;\n if (q < 0) break;\n const d = dist(\n data.subarray(p * dim, (p + 1) * dim),\n data.subarray(q * dim, (q + 1) * dim),\n );\n if (d < Math.max(args.thresholds[p]!, args.thresholds[q]!)) {\n out.push(p, q, d);\n }\n }\n }\n }\n const triples = Float64Array.from(out);\n return { result: triples, transfers: [triples.buffer] };\n },\n\n /** NN-descent join pass over a vertex range of the candidate arrays. */\n joinUpdates(args: {\n newIdx: Int32Array;\n oldIdx: Int32Array;\n maxCandidates: number;\n vStart: number;\n vEnd: number;\n thresholds: Float32Array;\n }) {\n const dist = state.internal.dist;\n const dim = state.cols;\n const data = state.data;\n const mc = args.maxCandidates;\n const out: number[] = [];\n for (let i = args.vStart; i < args.vEnd; i++) {\n const base = i * mc;\n for (let j = 0; j < mc; j++) {\n const p = args.newIdx[base + j]!;\n if (p < 0) continue;\n const pRow = data.subarray(p * dim, (p + 1) * dim);\n for (let t = j; t < mc; t++) {\n const q = args.newIdx[base + t]!;\n if (q < 0) continue;\n const d = dist(pRow, data.subarray(q * dim, (q + 1) * dim));\n if (d <= Math.max(args.thresholds[p]!, args.thresholds[q]!)) out.push(p, q, d);\n }\n for (let t = 0; t < mc; t++) {\n const q = args.oldIdx[base + t]!;\n if (q < 0) continue;\n const d = dist(pRow, data.subarray(q * dim, (q + 1) * dim));\n if (d <= Math.max(args.thresholds[p]!, args.thresholds[q]!)) out.push(p, q, d);\n }\n }\n }\n const triples = Float64Array.from(out);\n return { result: triples, transfers: [triples.buffer] };\n },\n\n /** One leaf-init pass entirely in the worker (small data path helper). */\n leafInitHeap(args: { leafArray: Int32Array; rows: number; cols: number; nNeighbors: number }) {\n const heap = makeHeap(state.rows, args.nNeighbors);\n const dist = (i: number, j: number) =>\n state.internal.dist(\n state.data.subarray(i * state.cols, (i + 1) * state.cols),\n state.data.subarray(j * state.cols, (j + 1) * state.cols),\n );\n initRpTree(args.rows, args.cols, args.leafArray, heap, dist);\n return {\n result: { indices: heap.indices, distances: heap.distances, flags: heap.flags },\n transfers: [heap.indices.buffer, heap.distances.buffer, heap.flags.buffer],\n };\n },\n\n /** Install layout state (SAB views or copies). */\n layoutInit(args: {\n embedding: Float32Array;\n tailEmbedding: Float32Array | null;\n head: Int32Array;\n tail: Int32Array;\n epochsPerSample: Float64Array;\n rngState: Int32Array;\n params: NonNullable<State['layoutParams']>;\n }) {\n state.embedding = args.embedding;\n state.tailEmbedding = args.tailEmbedding ?? args.embedding;\n state.head = args.head;\n state.tail = args.tail;\n state.epochsPerSample = args.epochsPerSample;\n const nEdges = args.epochsPerSample.length;\n const eons = new Float64Array(nEdges);\n const epns = new Float64Array(nEdges);\n for (let i = 0; i < nEdges; i++) {\n eons[i] = args.epochsPerSample[i]!;\n epns[i] = args.epochsPerSample[i]! / args.params.negativeSampleRate;\n }\n state.epochOfNextSample = eons;\n state.epochsPerNegativeSample = epns;\n state.epochOfNextNegativeSample = epns.slice();\n state.layoutParams = args.params;\n const nHead = args.embedding.length / args.params.dim;\n state.vertexRng = perVertexRngStates(nHead, args.rngState, args.embedding, args.params.dim);\n return { result: true };\n },\n\n /**\n * One Hogwild epoch over this worker's edge shard. With shared embedding the\n * writes go straight to the SAB; otherwise returns per-vertex deltas.\n */\n layoutEpoch(args: { epoch: number; alpha: number; embedding?: Float32Array }) {\n const p = state.layoutParams!;\n const head = state.head!;\n const tail = state.tail!;\n const eps = state.epochsPerSample!;\n const eons = state.epochOfNextSample!;\n const epns = state.epochsPerNegativeSample!;\n const eonns = state.epochOfNextNegativeSample!;\n const vertexRng = state.vertexRng!;\n const dim = p.dim;\n const { a, b, gamma, negativeSampleRate } = p;\n void negativeSampleRate;\n const alpha = args.alpha;\n const n = args.epoch;\n\n let emb = state.embedding!;\n let deltas: Float32Array | null = null;\n let tailEmb = state.tailEmbedding!;\n if (!p.sharedEmbedding) {\n // transfer mode: fresh snapshot in, deltas out\n emb = args.embedding!;\n tailEmb = p.moveOther ? emb : state.tailEmbedding!;\n deltas = new Float32Array(emb.length);\n }\n const rngRow = new Int32Array(3);\n\n const write = (idx: number, v: number) => {\n if (deltas) deltas[idx] = deltas[idx]! + v;\n else emb[idx] = emb[idx]! + v;\n };\n\n for (let i = p.edgeStart; i < p.edgeEnd; i++) {\n if (eons[i]! > n) continue;\n const j = head[i]!;\n let k = tail[i]!;\n const jBase = j * dim;\n let kBase = k * dim;\n let d2 = 0;\n for (let d = 0; d < dim; d++) {\n const diff = emb[jBase + d]! - tailEmb[kBase + d]!;\n d2 += diff * diff;\n }\n let gradCoeff = 0;\n if (d2 > 0) {\n gradCoeff = (-2.0 * a * b * d2 ** (b - 1.0)) / (a * d2 ** b + 1.0);\n }\n for (let d = 0; d < dim; d++) {\n const diff = emb[jBase + d]! - tailEmb[kBase + d]!;\n const gradD = clip(gradCoeff * diff);\n write(jBase + d, gradD * alpha);\n if (p.moveOther) write(kBase + d, -gradD * alpha);\n }\n eons[i] = eons[i]! + eps[i]!;\n const nNeg = Math.trunc((n - eonns[i]!) / epns[i]!);\n rngRow[0] = vertexRng[j * 3]!;\n rngRow[1] = vertexRng[j * 3 + 1]!;\n rngRow[2] = vertexRng[j * 3 + 2]!;\n for (let s = 0; s < nNeg; s++) {\n const r = tauRandInt(rngRow);\n k = (r % p.nVertices) + (r % p.nVertices < 0 ? p.nVertices : 0);\n kBase = k * dim;\n let dn = 0;\n for (let d = 0; d < dim; d++) {\n const diff = emb[jBase + d]! - tailEmb[kBase + d]!;\n dn += diff * diff;\n }\n let repCoeff = 0;\n if (dn > 0) {\n repCoeff = (2.0 * gamma * b) / ((0.001 + dn) * (a * dn ** b + 1));\n } else if (j === k) {\n continue;\n }\n for (let d = 0; d < dim; d++) {\n if (repCoeff > 0) {\n const diff = emb[jBase + d]! - tailEmb[kBase + d]!;\n write(jBase + d, clip(repCoeff * diff) * alpha);\n }\n }\n }\n vertexRng[j * 3] = rngRow[0]!;\n vertexRng[j * 3 + 1] = rngRow[1]!;\n vertexRng[j * 3 + 2] = rngRow[2]!;\n eonns[i] = eonns[i]! + nNeg * epns[i]!;\n }\n if (deltas) return { result: deltas, transfers: [deltas.buffer] };\n return { result: null };\n },\n};\n\ninterface RpcMessage {\n id: number;\n method: string;\n args: never;\n}\n\nfunction handle(msg: RpcMessage, post: (m: unknown, t?: Transferable[]) => void): void {\n const h = handlers[msg.method];\n if (!h) {\n post({ id: msg.id, error: `unknown method ${msg.method}` });\n return;\n }\n try {\n const { result, transfers } = h(msg.args);\n post({ id: msg.id, result }, transfers);\n } catch (e) {\n // keep the worker-side stack — the main thread rethrows this as the message\n post({ id: msg.id, error: e instanceof Error ? (e.stack ?? e.message) : String(e) });\n }\n}\n\nconst IS_NODE = typeof process !== 'undefined' && !!process.versions?.node;\nif (IS_NODE) {\n // async bootstrap, NOT top-level await: consumer bundlers (Vite) wrap this\n // file in an iife worker format where TLA is a build error. parentPort\n // queues messages until the 'message' listener attaches, so nothing races.\n void (async () => {\n // variable specifier + ignore comments: browser bundlers must not try to\n // resolve node builtins (evaluated only under IS_NODE)\n const specifier = 'node:worker_threads';\n const { parentPort } = (await import(\n /* @vite-ignore */ /* webpackIgnore: true */ specifier\n )) as typeof import('node:worker_threads');\n parentPort!.on('message', (msg: RpcMessage) =>\n handle(msg, (m, t) => parentPort!.postMessage(m, (t ?? []) as never)),\n );\n })();\n} else {\n const scope = globalThis as unknown as {\n onmessage: ((e: MessageEvent) => void) | null;\n postMessage: (m: unknown, opts?: { transfer?: Transferable[] }) => void;\n };\n scope.onmessage = (e) =>\n handle(e.data as RpcMessage, (m, t) => scope.postMessage(m, { transfer: t ?? [] }));\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "umap-web",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Standalone TypeScript implementation of UMAP for browsers and Node.js \u2014 memory-efficient CPU backend and WebGPU backend for large datasets. Functional parity with umap-learn.",
|
|
5
|
+
"license": "BSD-3-Clause",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/jeyabbalas/umap-web.git",
|
|
11
|
+
"directory": "packages/umap-web"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/jeyabbalas/umap-web#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/jeyabbalas/umap-web/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.19"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"NOTICE",
|
|
28
|
+
"CHANGELOG.md"
|
|
29
|
+
],
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./core": {
|
|
36
|
+
"types": "./dist/core.d.ts",
|
|
37
|
+
"default": "./dist/core.js"
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json",
|
|
40
|
+
"./worker": {
|
|
41
|
+
"types": "./dist/worker.d.ts",
|
|
42
|
+
"default": "./dist/worker.js"
|
|
43
|
+
},
|
|
44
|
+
"./webgpu": {
|
|
45
|
+
"types": "./dist/webgpu.d.ts",
|
|
46
|
+
"default": "./dist/webgpu.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"umap",
|
|
51
|
+
"dimensionality-reduction",
|
|
52
|
+
"embedding",
|
|
53
|
+
"webgpu",
|
|
54
|
+
"machine-learning"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsdown && node ../../scripts/check-worker-selfcontained.mjs",
|
|
58
|
+
"prepack": "npm run build",
|
|
59
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"test:browser": "playwright test",
|
|
62
|
+
"docs": "typedoc",
|
|
63
|
+
"release-check": "publint && attw --pack . --profile esm-only"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@arethetypeswrong/cli": "^0.18.4",
|
|
67
|
+
"@playwright/test": "^1.61.1",
|
|
68
|
+
"@types/node": "^24.10.1",
|
|
69
|
+
"@webgpu/types": "^0.1.69",
|
|
70
|
+
"publint": "^0.3.21",
|
|
71
|
+
"tsdown": "^0.15.7",
|
|
72
|
+
"typedoc": "^0.28.20",
|
|
73
|
+
"typescript": "^5.9.3",
|
|
74
|
+
"vitest": "^3.2.4"
|
|
75
|
+
}
|
|
76
|
+
}
|