UNPKG

clustering-tfjs

Version:

High-performance TypeScript clustering algorithms (K-Means, Spectral, Agglomerative) with TensorFlow.js acceleration and scikit-learn compatibility

97 lines (96 loc) 3.7 kB
"use strict"; /** * Minimal TypeScript implementation of the original MT19937 32-bit variant * used by NumPy's legacy `RandomState` (and therefore by scikit-learn). * * This port only exposes the functionality required by the k-means++ seeding * routine: * • Generation of 32-bit unsigned integers (\[0, 2**32)) * • High-precision uniform floats in the half-open interval \[0, 1) * * The algorithm closely follows the reference implementation described in * Matsumoto & Nishimura (1998) and the public domain C code. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MT19937 = void 0; class MT19937 { constructor(seed) { /** State vector – 624 32-bit unsigned ints. */ this.mt = new Uint32Array(MT19937.N); /** Current index within the state vector. */ this.index = MT19937.N; this.init(seed >>> 0); // ensure unsigned 32-bit } /* --------------------------------------------------------------------- */ /* Public helpers */ /* --------------------------------------------------------------------- */ /** Returns next 32-bit unsigned int in \[0, 2**32). */ nextUint32() { if (this.index >= MT19937.N) { this.twist(); } let y = this.mt[this.index++]; // Tempering (same bit-shifts as NumPy's implementation) y ^= y >>> 11; y ^= (y << 7) & 0x9d2c5680; y ^= (y << 15) & 0xefc60000; y ^= y >>> 18; return y >>> 0; // ensure unsigned } /** * Returns a 53-bit precision float in the interval \[0, 1) identical to * NumPy's `random_sample` implementation. */ nextFloat() { const a = this.nextUint32() >>> 5; // Upper 27 bits const b = this.nextUint32() >>> 6; // Upper 26 bits return (a * 67108864 + b) * 1.1102230246251565e-16; // 1 / 2**53 } /** Uniform integer in \[0, max). Mirrors NumPy's rejection sampling to * eliminate modulo bias so that sequences match exactly. */ nextInt(max) { if (!Number.isInteger(max) || max <= 0 || max > 0xffffffff) { throw new Error('max must be a 32-bit positive integer'); } const bound = max >>> 0; const threshold = (0x100000000 - bound) % bound; // 2**32 == 0x100000000 // eslint-disable-next-line no-constant-condition while (true) { const r = this.nextUint32(); if (r >= threshold) { return r % bound; } } } /* --------------------------------------------------------------------- */ /* Internals */ /* --------------------------------------------------------------------- */ init(seed) { this.mt[0] = seed >>> 0; for (let i = 1; i < MT19937.N; i++) { const prev = this.mt[i - 1] >>> 0; this.mt[i] = ((1812433253 * (prev ^ (prev >>> 30)) + i) & 0xffffffff) >>> 0; } this.index = MT19937.N; } twist() { const { N, M, MATRIX_A, UPPER_MASK, LOWER_MASK } = MT19937; for (let i = 0; i < N; i++) { const x = (this.mt[i] & UPPER_MASK) | (this.mt[(i + 1) % N] & LOWER_MASK); let xa = x >>> 1; if (x % 2 !== 0) { xa ^= MATRIX_A; } this.mt[i] = this.mt[(i + M) % N] ^ xa; } this.index = 0; } } exports.MT19937 = MT19937; MT19937.N = 624; MT19937.M = 397; MT19937.MATRIX_A = 0x9908b0df; MT19937.UPPER_MASK = 0x80000000; MT19937.LOWER_MASK = 0x7fffffff;