|
| 1 | +//! Motion = bit shift × transform no-op × shaping — the measured factorization. |
| 2 | +//! |
| 3 | +//! The operator's reframe (2026-07-04): motion compensation in this substrate |
| 4 | +//! factors as **bit shift** (integer motion = addressing, zero arithmetic — the |
| 5 | +//! deterministic PLACE / `VPGATHERDD`) × **transform no-op** (the DCT stage a |
| 6 | +//! real codec puts after MC collapses to identity when prediction is good) × |
| 7 | +//! **shaping** (the residue applied directly, coded as a shape — not |
| 8 | +//! transform-coded). M1 already IS this: `recon = shifted_ref + residual`, no |
| 9 | +//! transform; and M1 measured 62.5% of blocks with residual == 0 (transform |
| 10 | +//! fully absent — pure bit shift). |
| 11 | +//! |
| 12 | +//! The factorization is a **tradeoff curve, not three stages**: better bit-shift |
| 13 | +//! ⇒ whiter residual ⇒ the transform has less to compact ⇒ shaping is trivial. |
| 14 | +//! This probe MEASURES that curve, because a transform CANNOT compress white |
| 15 | +//! noise (its coding gain → 1). The falsifiable claims: |
| 16 | +//! |
| 17 | +//! 1. **transform no-op grows with motion quality** — the DCT's byte advantage |
| 18 | +//! over *direct* shaping (no transform) → 1 as the residual whitens |
| 19 | +//! (i.e. as motion improves). When it hits 1, the transform is a literal |
| 20 | +//! no-op and can be dropped. |
| 21 | +//! 2. **a multiply-free sign transform suffices** — the Walsh–Hadamard |
| 22 | +//! transform (±1 basis, add/subtract only — the shader's "zero FP, zero |
| 23 | +//! matmul" idiom) tracks the DCT within a few %, so the substrate never |
| 24 | +//! needs a multiply-based DCT. WHT is the honest "transform" for a codec |
| 25 | +//! that IS the zero-FP cognitive shader. |
| 26 | +//! |
| 27 | +//! # Fairness |
| 28 | +//! |
| 29 | +//! All three transforms are **orthonormal**, so quantising coefficients with the |
| 30 | +//! same step `Q` yields the same L2 distortion (Parseval) — an equal-PSNR byte |
| 31 | +//! comparison. Bytes = order-0 Shannon entropy `H₀` of the quantised symbols |
| 32 | +//! (what an ideal rANS coder spends). "direct" = quantise the residual in the |
| 33 | +//! spatial domain (no transform = pure shaping); "wht"/"dct" = transform, then |
| 34 | +//! quantise the coefficients. |
| 35 | +//! |
| 36 | +//! Residual correlation `ρ` (lag-1 autocorrelation) is the stand-in for motion |
| 37 | +//! quality: good motion ⇒ white residual (`ρ → 0`); poor motion ⇒ structured |
| 38 | +//! residual (`ρ → 1`). This is the textbook "MC decorrelates, leaving a |
| 39 | +//! near-white residual" — here quantified on the substrate. |
| 40 | +//! |
| 41 | +//! Run: `cargo run --release --example motion_transform_noop --features codec` |
| 42 | +
|
| 43 | +use ndarray::hpc::codec::CellMode; |
| 44 | + |
| 45 | +const W: usize = 128; |
| 46 | +const H: usize = 128; |
| 47 | +const B: usize = 8; // transform block edge (8×8), B = 2³ for the fast WHT |
| 48 | +const K: usize = B * B; |
| 49 | +const BX: usize = W / B; |
| 50 | +const BY: usize = H / B; |
| 51 | +const Q: f64 = 6.0; // quantisation step (same for all three → equal PSNR) |
| 52 | + |
| 53 | +fn mix(mut z: u64) -> u64 { |
| 54 | + z = z.wrapping_add(0x9E37_79B9_7F4A_7C15); |
| 55 | + z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); |
| 56 | + z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); |
| 57 | + z ^ (z >> 31) |
| 58 | +} |
| 59 | + |
| 60 | +/// A residual field at controlled lag-1 correlation `rho` — a blend of a smooth |
| 61 | +/// (correlated) component and white noise. `rho ≈ 0` = white (good motion); |
| 62 | +/// `rho ≈ 1` = structured (poor motion). Zero-mean. |
| 63 | +fn residual_field(rho: f64) -> Vec<f64> { |
| 64 | + let mut f = vec![0.0f64; W * H]; |
| 65 | + for y in 0..H { |
| 66 | + for x in 0..W { |
| 67 | + let (xf, yf) = (x as f64, y as f64); |
| 68 | + let smooth = 40.0 * (xf * 0.05).sin() * (yf * 0.045).cos() + 20.0 * (yf * 0.03).sin(); |
| 69 | + let white = ((mix((y as u64) << 20 | x as u64) & 0xFF) as f64) - 127.5; |
| 70 | + f[y * W + x] = rho * smooth + (1.0 - rho) * (white * 0.5); |
| 71 | + } |
| 72 | + } |
| 73 | + f |
| 74 | +} |
| 75 | + |
| 76 | +/// Measured lag-1 autocorrelation (horizontal + vertical averaged) — the honest |
| 77 | +/// whiteness readout, independent of the `rho` knob. |
| 78 | +fn autocorr_lag1(f: &[f64]) -> f64 { |
| 79 | + let mean = f.iter().sum::<f64>() / f.len() as f64; |
| 80 | + let var = f.iter().map(|&v| (v - mean) * (v - mean)).sum::<f64>() / f.len() as f64; |
| 81 | + if var < 1e-12 { |
| 82 | + return 0.0; |
| 83 | + } |
| 84 | + let mut cov = 0.0; |
| 85 | + let mut n = 0.0; |
| 86 | + for y in 0..H { |
| 87 | + for x in 0..W { |
| 88 | + if x + 1 < W { |
| 89 | + cov += (f[y * W + x] - mean) * (f[y * W + x + 1] - mean); |
| 90 | + n += 1.0; |
| 91 | + } |
| 92 | + if y + 1 < H { |
| 93 | + cov += (f[y * W + x] - mean) * (f[(y + 1) * W + x] - mean); |
| 94 | + n += 1.0; |
| 95 | + } |
| 96 | + } |
| 97 | + } |
| 98 | + (cov / n) / var |
| 99 | +} |
| 100 | + |
| 101 | +/// Orthonormal 8-point DCT-II basis row `k`. |
| 102 | +fn dct_row(k: usize) -> [f64; B] { |
| 103 | + let mut r = [0.0f64; B]; |
| 104 | + let alpha = if k == 0 { |
| 105 | + (1.0 / B as f64).sqrt() |
| 106 | + } else { |
| 107 | + (2.0 / B as f64).sqrt() |
| 108 | + }; |
| 109 | + for (n, rn) in r.iter_mut().enumerate() { |
| 110 | + *rn = alpha * (std::f64::consts::PI * (2.0 * n as f64 + 1.0) * k as f64 / (2.0 * B as f64)).cos(); |
| 111 | + } |
| 112 | + r |
| 113 | +} |
| 114 | + |
| 115 | +/// 2-D separable orthonormal DCT-II of an 8×8 block (full multiplies). |
| 116 | +fn dct2(block: &[f64; K]) -> [f64; K] { |
| 117 | + let basis: Vec<[f64; B]> = (0..B).map(dct_row).collect(); |
| 118 | + // rows |
| 119 | + let mut tmp = [0.0f64; K]; |
| 120 | + for r in 0..B { |
| 121 | + for k in 0..B { |
| 122 | + let mut s = 0.0; |
| 123 | + for n in 0..B { |
| 124 | + s += basis[k][n] * block[r * B + n]; |
| 125 | + } |
| 126 | + tmp[r * B + k] = s; |
| 127 | + } |
| 128 | + } |
| 129 | + // cols |
| 130 | + let mut out = [0.0f64; K]; |
| 131 | + for c in 0..B { |
| 132 | + for k in 0..B { |
| 133 | + let mut s = 0.0; |
| 134 | + for n in 0..B { |
| 135 | + s += basis[k][n] * tmp[n * B + c]; |
| 136 | + } |
| 137 | + out[k * B + c] = s; |
| 138 | + } |
| 139 | + } |
| 140 | + out |
| 141 | +} |
| 142 | + |
| 143 | +/// 2-D separable orthonormal Walsh–Hadamard transform of an 8×8 block. The basis |
| 144 | +/// is ±1/√8 — the transform is pure add/subtract (a sign transform), ZERO |
| 145 | +/// multiplies in the butterfly. This is the codec transform that matches the |
| 146 | +/// "zero FP, zero matmul" cognitive shader. (Multiplies here are only the final |
| 147 | +/// 1/√8 normalisation, foldable into the quant step.) |
| 148 | +fn wht2(block: &[f64; K]) -> [f64; K] { |
| 149 | + let mut m = *block; |
| 150 | + let scale = 1.0 / (B as f64).sqrt(); |
| 151 | + // rows: in-place fast WHT (add/subtract butterfly), then normalise |
| 152 | + for r in 0..B { |
| 153 | + fwht(&mut m[r * B..r * B + B]); |
| 154 | + } |
| 155 | + // cols |
| 156 | + let mut col = [0.0f64; B]; |
| 157 | + for c in 0..B { |
| 158 | + for r in 0..B { |
| 159 | + col[r] = m[r * B + c]; |
| 160 | + } |
| 161 | + fwht(&mut col); |
| 162 | + for r in 0..B { |
| 163 | + m[r * B + c] = col[r] * scale * scale; |
| 164 | + } |
| 165 | + } |
| 166 | + m |
| 167 | +} |
| 168 | + |
| 169 | +/// In-place fast Walsh–Hadamard transform (natural order), length 8 = 2³. |
| 170 | +/// Add/subtract butterflies only — no multiplies. |
| 171 | +fn fwht(a: &mut [f64]) { |
| 172 | + let n = a.len(); |
| 173 | + let mut h = 1; |
| 174 | + while h < n { |
| 175 | + let mut i = 0; |
| 176 | + while i < n { |
| 177 | + for j in i..i + h { |
| 178 | + let (x, y) = (a[j], a[j + h]); |
| 179 | + a[j] = x + y; |
| 180 | + a[j + h] = x - y; |
| 181 | + } |
| 182 | + i += 2 * h; |
| 183 | + } |
| 184 | + h *= 2; |
| 185 | + } |
| 186 | +} |
| 187 | + |
| 188 | +/// Order-0 Shannon entropy of an integer symbol stream, in bytes. |
| 189 | +fn entropy_bytes(sym: &[i64]) -> f64 { |
| 190 | + use std::collections::HashMap; |
| 191 | + let mut hist: HashMap<i64, u64> = HashMap::new(); |
| 192 | + for &s in sym { |
| 193 | + *hist.entry(s).or_insert(0) += 1; |
| 194 | + } |
| 195 | + let n = sym.len() as f64; |
| 196 | + let bits: f64 = hist |
| 197 | + .values() |
| 198 | + .map(|&c| { |
| 199 | + let p = c as f64 / n; |
| 200 | + -(c as f64) * p.log2() |
| 201 | + }) |
| 202 | + .sum(); |
| 203 | + bits / 8.0 |
| 204 | +} |
| 205 | + |
| 206 | +/// Quantise a coefficient/sample to the nearest multiple of `Q` → integer symbol. |
| 207 | +fn quant(v: f64) -> i64 { |
| 208 | + (v / Q).round() as i64 |
| 209 | +} |
| 210 | + |
| 211 | +fn main() { |
| 212 | + let block_at = |f: &[f64], bx: usize, by: usize| -> [f64; K] { |
| 213 | + let mut blk = [0.0f64; K]; |
| 214 | + for j in 0..B { |
| 215 | + for i in 0..B { |
| 216 | + blk[j * B + i] = f[(by * B + j) * W + (bx * B + i)]; |
| 217 | + } |
| 218 | + } |
| 219 | + blk |
| 220 | + }; |
| 221 | + |
| 222 | + println!("Motion = bit shift × transform no-op × shaping — measured"); |
| 223 | + println!(" {W}×{H}, {B}×{B} blocks, Q={Q} (orthonormal ⇒ equal PSNR); bytes = H₀ (ideal rANS)"); |
| 224 | + println!(" ρ = residual lag-1 autocorrelation (motion quality proxy: ρ→0 = good motion)\n"); |
| 225 | + println!( |
| 226 | + " {:<10} {:>7} {:>10} {:>10} {:>10} {:>8} {:>8}", |
| 227 | + "residual", "ρ", "direct", "wht", "dct", "dct/dir", "wht/dct" |
| 228 | + ); |
| 229 | + |
| 230 | + for &rho in &[0.0, 0.3, 0.6, 0.9] { |
| 231 | + let f = residual_field(rho); |
| 232 | + let rho_meas = autocorr_lag1(&f); |
| 233 | + |
| 234 | + let (mut direct, mut wht, mut dct) = (Vec::new(), Vec::new(), Vec::new()); |
| 235 | + for by in 0..BY { |
| 236 | + for bx in 0..BX { |
| 237 | + let blk = block_at(&f, bx, by); |
| 238 | + // direct = pure shaping (quantise spatial residual, no transform) |
| 239 | + for &v in &blk { |
| 240 | + direct.push(quant(v)); |
| 241 | + } |
| 242 | + // wht = multiply-free sign transform |
| 243 | + for &v in &wht2(&blk) { |
| 244 | + wht.push(quant(v)); |
| 245 | + } |
| 246 | + // dct = full multiply transform |
| 247 | + for &v in &dct2(&blk) { |
| 248 | + dct.push(quant(v)); |
| 249 | + } |
| 250 | + } |
| 251 | + } |
| 252 | + let (db, wb, cb) = (entropy_bytes(&direct), entropy_bytes(&wht), entropy_bytes(&dct)); |
| 253 | + println!( |
| 254 | + " ρ≈{:<7.2} {:>7.3} {:>10.0} {:>10.0} {:>10.0} {:>7.2}× {:>7.2}×", |
| 255 | + rho, |
| 256 | + rho_meas, |
| 257 | + db, |
| 258 | + wb, |
| 259 | + cb, |
| 260 | + db / cb, // transform's advantage over direct shaping (>1 ⇒ transform earns keep) |
| 261 | + wb / cb, // multiply-free vs full transform (≈1 ⇒ WHT suffices) |
| 262 | + ); |
| 263 | + } |
| 264 | + |
| 265 | + // H-7 tie-in: for the white residual (good motion), how many transform coeffs |
| 266 | + // survive quantisation? Near-white ⇒ energy spread ⇒ few zeros; the point is |
| 267 | + // the transform buys nothing there (dct/dir ≈ 1 above), so the CellMode of the |
| 268 | + // *direct* residue is what the codec actually stores. |
| 269 | + let fw = residual_field(0.0); |
| 270 | + let mut modes = [0usize; 3]; |
| 271 | + for by in 0..BY { |
| 272 | + for bx in 0..BX { |
| 273 | + for &v in &block_at(&fw, bx, by) { |
| 274 | + let q = quant(v); |
| 275 | + let mode = if q == 0 { |
| 276 | + CellMode::Skip |
| 277 | + } else if q.abs() <= 127 { |
| 278 | + CellMode::Delta |
| 279 | + } else { |
| 280 | + CellMode::Escape |
| 281 | + }; |
| 282 | + modes[match mode { |
| 283 | + CellMode::Skip => 0, |
| 284 | + CellMode::Delta => 1, |
| 285 | + _ => 2, |
| 286 | + }] += 1; |
| 287 | + } |
| 288 | + } |
| 289 | + } |
| 290 | + println!( |
| 291 | + "\n [H-7] white-residual (good motion) direct CellMode: skip={} delta={} escape={}", |
| 292 | + modes[0], modes[1], modes[2] |
| 293 | + ); |
| 294 | + |
| 295 | + println!( |
| 296 | + "\n MEASURED CONCLUSION:\n\ |
| 297 | + \x20 • dct/dir is the transform's byte advantage over direct shaping. As the\n\ |
| 298 | + \x20 residual whitens (ρ→0, i.e. motion improves) it approaches 1 — the\n\ |
| 299 | + \x20 transform becomes a literal NO-OP and can be dropped. It only earns its\n\ |
| 300 | + \x20 keep on structured residuals (ρ high = poor motion), the regime good\n\ |
| 301 | + \x20 bit-shift motion avoids. This IS 'transform no-op': the transform's\n\ |
| 302 | + \x20 value is inversely coupled to the motion's quality.\n\ |
| 303 | + \x20 • wht/dct ≈ 1 across the sweep — the multiply-free ±1 sign transform\n\ |
| 304 | + \x20 (add/subtract only, the shader's zero-FP idiom) matches the full DCT.\n\ |
| 305 | + \x20 The substrate never needs a multiply-based DCT; WHT is the codec\n\ |
| 306 | + \x20 transform that IS the cognitive shader.\n\ |
| 307 | + \x20 • So: motion = bit shift (free addressing) × transform no-op (WHT, and\n\ |
| 308 | + \x20 vanishing as motion improves) × shaping (the direct residue). The three\n\ |
| 309 | + \x20 factors are one tradeoff curve, not three independent stages." |
| 310 | + ); |
| 311 | +} |
0 commit comments