/ concept-collection / timeseries-compressibility
Sign in
concept-collection / timeseries-compressibility
timeseries-compressibility / src / model / latent.ts
51 lines · 1.6 KBBlameHistoryRaw
1/**
2 * A fixed latent randomness underlying everything: standard normals indexed
3 * by absolute sample position. The pipeline x → h*x → round is evaluated on
4 * demand against these indices, so changing σ or the filter transforms the
5 * *same* underlying data — the display morphs smoothly instead of resampling
6 * — and the compression block (indices 0…N) shares its randomness with the
7 * displayed window.
8 */
9import { GaussianStream } from './random'
11export const LATENT_SEED = 20260729
13export class LatentSource {
14 private xs: number[] = []
15 private xStream: GaussianStream
17 constructor(seed: number) {
18 this.xStream = new GaussianStream(seed)
19 }
21 private ensure(n: number) {
22 while (this.xs.length <= n) {
23 this.xs.push(this.xStream.normal())
24 }
25 }
27 /**
28 * Quantized samples for absolute indices [start, start + count). The kernel
29 * is applied zero-phase (centered on its midpoint), so changing its length
30 * does not shift features along the time axis. Latent indices before 0 read
31 * as zero input.
32 */
33 window(start: number, count: number, kernel: Float64Array, sigma: number): Int16Array {
34 const L = kernel.length
35 const mid = (L - 1) >> 1
36 this.ensure(start + count - 1 + mid)
37 const { xs } = this
38 const out = new Int16Array(count)
39 for (let j = 0; j < count; j++) {
40 const n = start + j
41 let y = 0
42 for (let k = 0; k < L; k++) {
43 const idx = n - k + mid
44 if (idx >= 0) y += kernel[k] * xs[idx]
45 }
46 y *= sigma
47 out[j] = Math.max(-32768, Math.min(32767, Math.round(y)))
48 }
49 return out
50 }
moveopenescclose