1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum LatticeKind {
8 Z1,
9 A2,
10}
11
12#[derive(Debug, Clone, PartialEq)]
13pub struct LatticeQuantizationResult {
14 pub kind: LatticeKind,
15 pub codes: Vec<i16>,
16 pub reconstructed: Vec<f32>,
17 pub mse: f32,
18}
19
20fn sanitize_scale(scale: f32) -> f32 {
21 if scale.is_finite() && scale > 0.0 {
22 scale
23 } else {
24 1.0
25 }
26}
27
28fn clamp_to_i16(v: f32) -> i16 {
29 let rounded = v.round();
30 if rounded >= i16::MAX as f32 {
31 i16::MAX
32 } else if rounded <= i16::MIN as f32 {
33 i16::MIN
34 } else {
35 rounded as i16
36 }
37}
38
39fn mse(original: &[f32], reconstructed: &[f32]) -> f32 {
40 let n = original.len();
41 if n == 0 {
42 return 0.0;
43 }
44 let sum: f32 = original
45 .iter()
46 .zip(reconstructed.iter())
47 .map(|(a, b)| (a - b).powi(2))
48 .sum();
49 sum / n as f32
50}
51
52pub fn quantize_z1(values: &[f32], scale: f32) -> LatticeQuantizationResult {
54 let scale = sanitize_scale(scale);
55 let codes: Vec<i16> = values.iter().map(|&v| clamp_to_i16(v * scale)).collect();
56 let reconstructed: Vec<f32> = codes.iter().map(|&c| c as f32 / scale).collect();
57 let mse = mse(values, &reconstructed);
58 LatticeQuantizationResult {
59 kind: LatticeKind::Z1,
60 codes,
61 reconstructed,
62 mse,
63 }
64}
65
66pub fn quantize_a2_pairs(values: &[f32], scale: f32) -> LatticeQuantizationResult {
71 let scale = sanitize_scale(scale);
72 let n = values.len();
73 let mut codes = Vec::with_capacity(n);
74 let mut reconstructed = Vec::with_capacity(n);
75
76 let pairs = n / 2;
77 for i in 0..pairs {
78 let a = values[2 * i];
79 let b = values[2 * i + 1];
80 let ca = clamp_to_i16(a * scale);
81 let cb = clamp_to_i16(b * scale);
82 codes.push(ca);
83 codes.push(cb);
84 reconstructed.push(ca as f32 / scale);
85 reconstructed.push(cb as f32 / scale);
86 }
87
88 if n % 2 == 1 {
90 let v = values[n - 1];
91 let c = clamp_to_i16(v * scale);
92 codes.push(c);
93 reconstructed.push(c as f32 / scale);
94 }
95
96 let mse = mse(values, &reconstructed);
97 LatticeQuantizationResult {
98 kind: LatticeKind::A2,
99 codes,
100 reconstructed,
101 mse,
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn z1_deterministic_and_same_length() {
111 let values = vec![0.1, -0.5, 1.2, -3.7, 0.0];
112 let r1 = quantize_z1(&values, 10.0);
113 let r2 = quantize_z1(&values, 10.0);
114 assert_eq!(r1.codes, r2.codes);
115 assert_eq!(r1.codes.len(), values.len());
116 assert_eq!(r1.reconstructed.len(), values.len());
117 }
118
119 #[test]
120 fn z1_mse_finite() {
121 let values: Vec<f32> = (0..64).map(|i| i as f32 * 0.1 - 3.2).collect();
122 let r = quantize_z1(&values, 4.0);
123 assert!(r.mse.is_finite());
124 }
125
126 #[test]
127 fn a2_preserves_length_even() {
128 let values = vec![1.0, 2.0, 3.0, 4.0];
129 let r = quantize_a2_pairs(&values, 1.0);
130 assert_eq!(r.codes.len(), 4);
131 assert_eq!(r.reconstructed.len(), 4);
132 }
133
134 #[test]
135 fn a2_preserves_length_odd() {
136 let values = vec![1.0, 2.0, 3.0];
137 let r = quantize_a2_pairs(&values, 1.0);
138 assert_eq!(r.codes.len(), 3);
139 assert_eq!(r.reconstructed.len(), 3);
140 }
141
142 #[test]
143 fn nonpositive_scale_falls_back_to_1() {
144 let values = vec![2.0, -2.0];
145 let r0 = quantize_z1(&values, 0.0);
146 let rn = quantize_z1(&values, -5.0);
147 let rf = quantize_z1(&values, f32::NAN);
148 let r1 = quantize_z1(&values, 1.0);
149 assert_eq!(r0.codes, r1.codes);
150 assert_eq!(rn.codes, r1.codes);
151 assert_eq!(rf.codes, r1.codes);
152 }
153
154 #[test]
155 fn large_values_clamp_to_i16_bounds() {
156 let values = vec![1e10_f32, -1e10_f32];
157 let r = quantize_z1(&values, 1.0);
158 assert_eq!(r.codes[0], i16::MAX);
159 assert_eq!(r.codes[1], i16::MIN);
160 }
161}