/ concept-collection / timeseries-compressibility
Sign in
concept-collection / timeseries-compressibility
timeseries-compressibility / src / entropy / rng.ts
64 lines · 2.0 KBCodeBlameHistory
2 * Seeded RNG for the entropy estimator: xoshiro128** uniforms (128-bit
3 * state, period 2^128 − 1, seeded through splitmix32) with Box–Muller
4 * normals. A single past consumes ~5e7 uniforms, so the 2^32-period
5 * generators used elsewhere in the app are not enough here: their seeds are
6 * mere offsets into one cyclic stream, and long refine-until-stopped runs
7 * would reuse randomness across "independent" pasts. Owned by this module so
8 * that src/entropy stays self-contained — the plan is to maintain it in step
9 * with (and eventually extract it back into) the timeseries-entropy package.
10 */
11export class Rng {
12 private s0: number
13 private s1: number
14 private s2: number
15 private s3: number
16 private spare: number | null = null
18 constructor(seed: number) {
19 // splitmix32 stream fills the state; any seed gives a non-zero state.
20 let z = seed >>> 0
21 const next = () => {
22 z = (z + 0x9e3779b9) >>> 0
23 let t = z
24 t = Math.imul(t ^ (t >>> 16), 0x21f0aaad)
25 t = Math.imul(t ^ (t >>> 15), 0x735a2d97)
26 return (t ^ (t >>> 15)) >>> 0
27 }
28 this.s0 = next()
29 this.s1 = next()
30 this.s2 = next()
31 this.s3 = next()
32 if ((this.s0 | this.s1 | this.s2 | this.s3) === 0) this.s0 = 1
33 }
35 /** Uniform on [0, 1). */
36 uniform(): number {
37 const s1 = this.s1
38 const x = Math.imul(s1, 5)
39 const result = (Math.imul((x << 7) | (x >>> 25), 9) >>> 0) / 4294967296
40 const t = s1 << 9
41 this.s2 ^= this.s0
42 this.s3 ^= s1
43 this.s1 = s1 ^ this.s2
44 this.s0 ^= this.s3
45 this.s2 ^= t
46 this.s3 = (this.s3 << 11) | (this.s3 >>> 21)
47 return result
48 }
50 /** Standard normal. */
51 normal(): number {
52 if (this.spare !== null) {
53 const v = this.spare
54 this.spare = null
55 return v
56 }
57 let u = 0
58 while (u === 0) u = this.uniform()
59 const r = Math.sqrt(-2 * Math.log(u))
60 const theta = 2 * Math.PI * this.uniform()
61 this.spare = r * Math.sin(theta)
62 return r * Math.cos(theta)
63 }
moveopenescclose