1use crate::abac::{BitModel, Decoder, Encoder, UintModel};
31use crate::bitio::{ByteReader, ByteWriter, DecodeError};
32use crate::codec::Decoded;
33
34pub const MAGIC: &[u8; 4] = b"TCI1";
36
37pub(crate) const ESC: u64 = 1 << 24;
39const LVL: usize = 4;
40const ACT: usize = 9;
41const PREV: usize = 3;
42const TUNE: [(f64, f64); 4] = [(1.0, 1.0), (1.25, 1.5), (1.5, 2.0), (2.0, 4.0)];
44
45struct Models {
46 zero: Vec<BitModel>,
47 sign: Vec<BitModel>,
48 mag: Vec<UintModel>,
49 prev: usize,
50}
51
52impl Models {
53 fn new() -> Self {
54 Self {
55 zero: vec![BitModel::default(); LVL * ACT * PREV],
56 sign: vec![BitModel::default(); LVL],
57 mag: vec![UintModel::default(); LVL * 4],
58 prev: 0,
59 }
60 }
61}
62
63#[derive(Clone, Copy)]
65struct Ctx {
66 lvl: usize,
67 act: usize,
68}
69
70fn levels(dims: &[usize]) -> u32 {
71 let m = dims.iter().copied().max().unwrap_or(1);
72 usize::BITS - (m.max(1) - 1).leading_zeros()
73}
74
75fn level_bound(b: f64, lv: u32, tune: (f64, f64)) -> f64 {
76 b / tune.0.powi(lv as i32 - 1).min(tune.1)
77}
78
79fn act_bucket(d: Option<f64>, step: f64) -> usize {
81 match d {
82 None => ACT - 1,
83 Some(d) => {
84 let u = (d / step) as u64;
85 ((u64::BITS - u.leading_zeros()) as usize).min(ACT - 2)
86 }
87 }
88}
89
90fn pass(
94 dims: &[usize],
95 s: usize,
96 k: usize,
97 cubic: bool,
98 r: &mut [f32],
99 mut f: impl FnMut(usize, f64, Option<f64>) -> f32,
100) {
101 let nd = dims.len();
102 if dims[k] <= s {
103 return;
104 }
105 let mut strides = vec![1usize; nd];
106 for j in (0..nd.saturating_sub(1)).rev() {
107 strides[j] = strides[j + 1] * dims[j + 1];
108 }
109 let (start, step): (Vec<usize>, Vec<usize>) = (0..nd)
110 .map(|j| match j.cmp(&k) {
111 std::cmp::Ordering::Less => (0, s),
112 std::cmp::Ordering::Equal => (s, 2 * s),
113 std::cmp::Ordering::Greater => (0, 2 * s),
114 })
115 .unzip();
116 let st = strides[k];
117 let n = dims[k];
118 let mut c = start.clone();
119 loop {
120 let i: usize = c.iter().zip(&strides).map(|(a, b)| a * b).sum();
121 let ck = c[k];
122 let a = r[i - s * st] as f64;
123 let (pred, diff) = if ck + s < n {
124 let b = r[i + s * st] as f64;
125 let p = if cubic {
126 match (ck >= 3 * s, ck + 3 * s < n) {
127 (true, true) => {
128 (-(r[i - 3 * s * st] as f64) + 9.0 * a + 9.0 * b - r[i + 3 * s * st] as f64)
129 / 16.0
130 }
131 (false, true) => (3.0 * a + 6.0 * b - r[i + 3 * s * st] as f64) / 8.0,
132 (true, false) => (-(r[i - 3 * s * st] as f64) + 6.0 * a + 3.0 * b) / 8.0,
133 (false, false) => (a + b) / 2.0,
134 }
135 } else {
136 (a + b) / 2.0
137 };
138 (p, Some((b - a).abs()))
139 } else {
140 (a, None)
141 };
142 r[i] = f(i, pred, diff);
143 let mut j = nd;
145 loop {
146 if j == 0 {
147 return;
148 }
149 j -= 1;
150 c[j] += step[j];
151 if c[j] < dims[j] {
152 break;
153 }
154 c[j] = start[j];
155 }
156 }
157}
158
159fn zctx(m: &Models, cx: Ctx) -> usize {
160 (cx.lvl * ACT + cx.act) * PREV + m.prev
161}
162
163fn mctx(cx: Ctx) -> usize {
164 cx.lvl * 4 + cx.act.min(3)
165}
166
167fn put(e: &mut Encoder, m: &mut Models, cx: Ctx, q: i64, raw: Option<f32>) {
168 let z = zctx(m, cx);
169 if q == 0 && raw.is_none() {
170 e.encode(false, &mut m.zero[z]);
171 m.prev = 0;
172 return;
173 }
174 e.encode(true, &mut m.zero[z]);
175 e.encode(q < 0, &mut m.sign[cx.lvl]);
176 match raw {
177 Some(v) => {
178 m.mag[mctx(cx)].encode(e, ESC - 1);
179 e.encode_raw(v.to_bits() as u64, 32);
180 m.prev = 2;
181 }
182 None => {
183 let a = q.unsigned_abs();
184 m.mag[mctx(cx)].encode(e, a - 1);
185 m.prev = if a == 1 { 1 } else { 2 };
186 }
187 }
188}
189
190fn get(d: &mut Decoder, m: &mut Models, cx: Ctx) -> Result<i64, f32> {
192 let z = zctx(m, cx);
193 if !d.decode(&mut m.zero[z]) {
194 m.prev = 0;
195 return Ok(0);
196 }
197 let neg = d.decode(&mut m.sign[cx.lvl]);
198 let a = m.mag[mctx(cx)].decode(d) + 1;
199 if a >= ESC {
200 m.prev = 2;
201 return Err(f32::from_bits(d.decode_raw(32) as u32));
202 }
203 m.prev = if a == 1 { 1 } else { 2 };
204 Ok(if neg { -(a as i64) } else { a as i64 })
205}
206
207#[inline]
209pub(crate) fn quant(x: f32, pred: f64, step: f64, b: f64) -> (i64, f32, bool) {
210 let qf = ((x as f64 - pred) / step).round();
211 if qf.abs() < (ESC - 1) as f64 {
212 let q = qf as i64;
213 let rec = (pred + q as f64 * step) as f32;
214 if ((x as f64) - rec as f64).abs() <= b && rec.is_finite() {
215 return (q, rec, false);
216 }
217 }
218 (0, x, true)
219}
220
221fn encode_with(x: &[f32], dims: &[usize], b: f64, tune: (f64, f64)) -> Vec<u8> {
222 encode_sse(x, dims, b, tune).0
223}
224
225fn encode_sse(x: &[f32], dims: &[usize], b: f64, tune: (f64, f64)) -> (Vec<u8>, f64) {
227 let nl = levels(dims);
228 let mut r = vec![0f32; x.len()];
229 let mut m = Models::new();
230 let mut e = Encoder::new();
231 let mut kinds = 0u64;
232 let step0 = 2.0 * level_bound(b, nl.max(1), tune);
234 let (q, rec, esc) = quant(x[0], 0.0, step0, b);
235 put(
236 &mut e,
237 &mut m,
238 Ctx {
239 lvl: LVL - 1,
240 act: ACT - 1,
241 },
242 q,
243 esc.then_some(x[0]),
244 );
245 r[0] = rec;
246 for lv in (1..=nl).rev() {
247 let s = 1usize << (lv - 1);
248 let step = 2.0 * level_bound(b, lv, tune);
249 let lvl = (lv as usize - 1).min(LVL - 1);
250 let mut cost = [0f64; 2];
252 for (ci, cubic) in [false, true].into_iter().enumerate() {
253 for k in 0..dims.len() {
254 pass(dims, s, k, cubic, &mut r, |i, p, _| {
255 let (q, rec, esc) = quant(x[i], p, step, b);
256 cost[ci] += if esc {
257 40.0
258 } else if q == 0 {
259 0.0
260 } else {
261 2.0 + 2.0 * (q.unsigned_abs() as f64).log2()
262 };
263 rec
264 });
265 }
266 }
267 let cubic = cost[1] <= cost[0];
268 kinds |= (cubic as u64) << lv;
269 for k in 0..dims.len() {
270 pass(dims, s, k, cubic, &mut r, |i, p, d| {
271 let (q, rec, esc) = quant(x[i], p, step, b);
272 put(
273 &mut e,
274 &mut m,
275 Ctx {
276 lvl,
277 act: act_bucket(d, step),
278 },
279 q,
280 esc.then_some(x[i]),
281 );
282 rec
283 });
284 }
285 }
286 let mut w = ByteWriter::new();
287 w.bytes(MAGIC);
288 w.u8(dims.len() as u8);
289 for &d in dims {
290 w.varint(d as u64);
291 }
292 w.f64(b);
293 w.u8(TUNE.iter().position(|&t| t == tune).unwrap() as u8);
294 w.varint(kinds);
295 w.bytes(&e.finish());
296 let sse = x
297 .iter()
298 .zip(&r)
299 .map(|(&a, &b)| (a as f64 - b as f64).powi(2))
300 .sum();
301 (w.buf, sse)
302}
303
304pub fn encode_f32(x: &[f32], dims: &[usize], b: f64) -> Vec<u8> {
307 assert_eq!(
308 x.len(),
309 dims.iter().product::<usize>(),
310 "dims do not match data"
311 );
312 assert!(!dims.is_empty() && dims.len() <= 255 && b > 0.0);
313 TUNE.iter()
314 .map(|&t| encode_with(x, dims, b, t))
315 .min_by_key(|v| v.len())
316 .unwrap()
317}
318
319pub fn encode_rmse_f32(x: &[f32], dims: &[usize], rmse: f64) -> Vec<u8> {
326 assert_eq!(
327 x.len(),
328 dims.iter().product::<usize>(),
329 "dims do not match data"
330 );
331 assert!(!dims.is_empty() && dims.len() <= 255 && rmse > 0.0);
332 TUNE.iter()
333 .map(|&t| rate_controlled(x, dims, rmse, t))
334 .min_by_key(|v| v.len())
335 .unwrap()
336}
337
338pub fn encode_psnr_f32(x: &[f32], dims: &[usize], psnr: f64) -> Vec<u8> {
340 let (lo, hi) = x
341 .iter()
342 .fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), &v| {
343 (a.min(v as f64), b.max(v as f64))
344 });
345 let range = if hi > lo { hi - lo } else { 1.0 };
346 encode_rmse_f32(x, dims, range / 10f64.powf(psnr / 20.0))
347}
348
349fn rate_controlled(x: &[f32], dims: &[usize], rmse: f64, tune: (f64, f64)) -> Vec<u8> {
350 rate_control(x.len(), rmse, |b| encode_sse(x, dims, b, tune))
351}
352
353pub(crate) fn rate_control(
358 n: usize,
359 rmse: f64,
360 mut enc: impl FnMut(f64) -> (Vec<u8>, f64),
361) -> Vec<u8> {
362 let budget = rmse * rmse * n as f64;
363 let mut b = rmse * 3f64.sqrt();
365 let mut ok: Option<(f64, f64, Vec<u8>)> = None;
367 let mut bad: Option<(f64, f64)> = None;
368 for _ in 0..12 {
369 let (s, sse) = enc(b);
370 let (lb, ls) = (b.ln(), sse.max(1e-300).ln());
371 if sse <= budget {
372 let close = sse >= budget * 0.99;
373 if ok.as_ref().is_none_or(|o| lb > o.0) {
374 ok = Some((lb, ls, s));
375 }
376 if close {
377 break;
378 }
379 } else if bad.is_none_or(|o| lb < o.0) {
380 bad = Some((lb, ls));
381 }
382 let lt = budget.ln();
383 b = match (&ok, bad) {
384 (Some(o), Some(f)) => {
385 let t = if f.1 > o.1 {
387 ((lt - o.1) / (f.1 - o.1)).clamp(0.1, 0.9)
388 } else {
389 0.5
390 };
391 (o.0 + t * (f.0 - o.0)).exp()
392 }
393 (Some(o), None) => (o.0 + ((lt - o.1) / 2.0).clamp(0.05, 2.0)).exp(),
395 (None, Some(f)) => (f.0 + ((lt - f.1) / 2.0).clamp(-2.0, -0.05)).exp(),
396 (None, None) => unreachable!(),
397 };
398 if !b.is_finite() || b <= 0.0 {
399 break;
400 }
401 }
402 match ok {
404 Some(o) => o.2,
405 None => {
406 let mut b = bad.map_or(rmse, |f| f.0.exp());
407 loop {
408 b *= 0.25;
409 let (s, sse) = enc(b);
410 if sse <= budget {
411 return s;
412 }
413 }
414 }
415 }
416}
417
418pub const FAST_TUNE: usize = 1;
421
422pub fn encode_fast_f32(x: &[f32], dims: &[usize], b: f64) -> Vec<u8> {
424 assert_eq!(
425 x.len(),
426 dims.iter().product::<usize>(),
427 "dims do not match data"
428 );
429 assert!(!dims.is_empty() && dims.len() <= 255 && b > 0.0);
430 let i = std::env::var("TC_FAST_TUNE")
431 .ok()
432 .and_then(|v| v.parse().ok())
433 .unwrap_or(FAST_TUNE);
434 encode_with(x, dims, b, TUNE[i])
435}
436
437pub fn decode(bytes: &[u8]) -> Result<Decoded, DecodeError> {
439 let mut rd = ByteReader::new(bytes);
440 if rd.take(4)? != MAGIC {
441 return Err(DecodeError::Invalid("magic"));
442 }
443 let nd = rd.u8()? as usize;
444 let dims = (0..nd)
445 .map(|_| rd.varint().map(|v| v as usize))
446 .collect::<Result<Vec<_>, _>>()?;
447 if nd == 0
448 || dims.contains(&0)
449 || dims
450 .iter()
451 .try_fold(1usize, |a, &d| a.checked_mul(d))
452 .is_none_or(|n| n as u64 > 1 << 34)
453 {
454 return Err(DecodeError::Invalid("dims"));
455 }
456 let b = rd.f64()?;
457 let tune = *TUNE
458 .get(rd.u8()? as usize)
459 .ok_or(DecodeError::Invalid("tune"))?;
460 let kinds = rd.varint()?;
461 if b.is_nan() || b <= 0.0 {
462 return Err(DecodeError::Invalid("bound"));
463 }
464 let payload = &bytes[rd.position()..];
465 let mut d = Decoder::new(payload);
466 let mut m = Models::new();
467 let n: usize = dims.iter().product();
468 let mut r = vec![0f32; n];
469 let nl = levels(&dims);
470 let val = |q: Result<i64, f32>, p: f64, step: f64| match q {
471 Ok(q) => (p + q as f64 * step) as f32,
472 Err(v) => v,
473 };
474 let step0 = 2.0 * level_bound(b, nl.max(1), tune);
475 r[0] = val(
476 get(
477 &mut d,
478 &mut m,
479 Ctx {
480 lvl: LVL - 1,
481 act: ACT - 1,
482 },
483 ),
484 0.0,
485 step0,
486 );
487 for lv in (1..=nl).rev() {
488 let s = 1usize << (lv - 1);
489 let step = 2.0 * level_bound(b, lv, tune);
490 let lvl = (lv as usize - 1).min(LVL - 1);
491 let cubic = (kinds >> lv) & 1 == 1;
492 for k in 0..nd {
493 pass(&dims, s, k, cubic, &mut r, |_, p, dd| {
494 val(
495 get(
496 &mut d,
497 &mut m,
498 Ctx {
499 lvl,
500 act: act_bucket(dd, step),
501 },
502 ),
503 p,
504 step,
505 )
506 });
507 }
508 }
509 if d.overrun() {
510 return Err(DecodeError::Invalid("truncated"));
511 }
512 Ok(Decoded::F32 { dims, data: r })
513}
514
515#[cfg(test)]
516mod tests {
517 use super::*;
518
519 #[test]
520 fn psnr_targets_are_met() {
521 for d in [vec![40, 33, 29], vec![3000]] {
522 let x = field(&d);
523 let (lo, hi) = x
524 .iter()
525 .fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), &v| {
526 (a.min(v as f64), b.max(v as f64))
527 });
528 let mut last = 0;
529 for p in [40.0, 60.0, 80.0, 100.0] {
530 let s = encode_psnr_f32(&x, &d, p);
531 let Decoded::F32 { data: y, .. } = decode(&s).unwrap() else {
532 panic!("f32")
533 };
534 let mse = x
535 .iter()
536 .zip(&y)
537 .map(|(&a, &b)| (a as f64 - b as f64).powi(2))
538 .sum::<f64>()
539 / x.len() as f64;
540 let got = 20.0 * ((hi - lo) / mse.sqrt()).log10();
541 assert!(got >= p, "{d:?} {p} dB: got {got:.3}");
542 assert!(got < p + 3.0, "{d:?} {p} dB: got {got:.3}");
544 assert!(s.len() > last);
545 last = s.len();
546 }
547 }
548 }
549
550 fn field(d: &[usize]) -> Vec<f32> {
551 let n: usize = d.iter().product();
552 (0..n)
553 .map(|i| {
554 let t = i as f32 / n as f32;
555 (t * 40.0).sin() * 3.0
556 + (i % d[d.len() - 1]) as f32 * 0.01
557 + if i % 997 == 0 { 50.0 } else { 0.0 }
558 })
559 .collect()
560 }
561
562 #[test]
563 fn round_trips_within_bound() {
564 for d in [
565 vec![1000],
566 vec![33, 70],
567 vec![17, 9, 31],
568 vec![1, 1, 5],
569 vec![1],
570 ] {
571 let x = field(&d);
572 for b in [1e-1, 1e-3, 1e-6] {
573 let bytes = encode_f32(&x, &d, b);
574 let Decoded::F32 { data, dims } = decode(&bytes).unwrap() else {
575 panic!()
576 };
577 assert_eq!(dims, d);
578 for (a, c) in x.iter().zip(&data) {
579 assert!(((a - c) as f64).abs() <= b, "{d:?} b={b} {a} {c}");
580 }
581 }
582 }
583 }
584
585 #[test]
586 fn smooth_is_tiny() {
587 let d = [64, 64, 64];
588 let x: Vec<f32> = (0..d.iter().product::<usize>())
589 .map(|i| ((i / 4096) as f32 * 0.05).sin() + ((i % 64) as f32 * 0.03).cos())
590 .collect();
591 let bytes = encode_f32(&x, &d, 1e-2);
592 assert!(bytes.len() < 2000, "{}", bytes.len());
593 }
594}