1use crate::{EncodedTensor, LayerCodebook, LayerType, NeuralError, Result};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct DecoderConfig {
9 pub apply_residuals: bool,
11 pub batch_size: usize,
13 pub use_gpu: bool,
15 pub prefetch: bool,
17}
18
19impl Default for DecoderConfig {
20 fn default() -> Self {
21 Self {
22 apply_residuals: true,
23 batch_size: 1024,
24 use_gpu: false,
25 prefetch: true,
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct DecodedTensor {
33 pub name: String,
35 pub shape: Vec<usize>,
37 pub data: Vec<f32>,
39 pub stats: DecodingStats,
41}
42
43#[derive(Debug, Clone, Default, Serialize, Deserialize)]
45pub struct DecodingStats {
46 pub decode_time_us: u64,
48 pub lookup_time_us: u64,
50 pub residual_time_us: u64,
52 pub vectors_decoded: usize,
54}
55
56#[derive(Debug)]
58pub struct NeuralDecoder {
59 config: DecoderConfig,
60 codebooks: LayerCodebook,
61}
62
63impl NeuralDecoder {
64 pub fn new(config: DecoderConfig, codebooks: LayerCodebook) -> Self {
66 Self { config, codebooks }
67 }
68
69 pub fn decode_tensor(&self, encoded: &EncodedTensor) -> Result<DecodedTensor> {
71 let start = std::time::Instant::now();
72
73 let codebook = self
74 .codebooks
75 .get(encoded.layer_type)
76 .ok_or_else(|| NeuralError::CodebookNotFound(format!("{:?}", encoded.layer_type)))?;
77
78 let lookup_start = std::time::Instant::now();
80 let mut data = codebook.decode_batch(&encoded.indices);
81 let lookup_time = lookup_start.elapsed();
82
83 let residual_start = std::time::Instant::now();
85 if self.config.apply_residuals {
86 if let Some(residuals) = encoded.residuals.as_ref() {
87 self.apply_residuals(&mut data, residuals, encoded.residual_scale);
88 }
89 }
90 let residual_time = residual_start.elapsed();
91
92 let total_time = start.elapsed();
93
94 let stats = DecodingStats {
95 decode_time_us: total_time.as_micros() as u64,
96 lookup_time_us: lookup_time.as_micros() as u64,
97 residual_time_us: residual_time.as_micros() as u64,
98 vectors_decoded: encoded.indices.len(),
99 };
100
101 Ok(DecodedTensor {
102 name: encoded.name.clone(),
103 shape: encoded.shape.clone(),
104 data,
105 stats,
106 })
107 }
108
109 fn apply_residuals(&self, data: &mut [f32], residuals: &[i8], scale: f32) {
111 for (d, &r) in data.iter_mut().zip(residuals.iter()) {
112 *d += r as f32 * scale;
113 }
114 }
115
116 pub fn decode_tensors(&self, encoded: &[EncodedTensor]) -> Result<Vec<DecodedTensor>> {
118 encoded.iter().map(|e| self.decode_tensor(e)).collect()
119 }
120
121 pub fn decode_streaming<'a>(
123 &'a self,
124 encoded: &'a EncodedTensor,
125 chunk_size: usize,
126 ) -> impl Iterator<Item = Result<Vec<f32>>> + 'a {
127 let codebook = self.codebooks.get(encoded.layer_type);
128
129 encoded
130 .indices
131 .chunks(chunk_size)
132 .enumerate()
133 .map(move |(i, chunk)| {
134 let cb = codebook.ok_or_else(|| {
135 NeuralError::CodebookNotFound(format!("{:?}", encoded.layer_type))
136 })?;
137
138 let mut data: Vec<f32> = chunk
139 .iter()
140 .flat_map(|&idx| {
141 cb.get_centroid(idx as usize)
142 .map(|c| c.to_vec())
143 .unwrap_or_else(|| vec![0.0; cb.config.centroid_dim])
144 })
145 .collect();
146
147 if self.config.apply_residuals {
149 if let Some(residuals) = encoded.residuals.as_ref() {
150 let dim = cb.config.centroid_dim;
151 let start = i * chunk_size * dim;
152 let end = (start + data.len()).min(residuals.len());
153 if start < residuals.len() {
154 self.apply_residuals(
155 &mut data[..end - start],
156 &residuals[start..end],
157 encoded.residual_scale,
158 );
159 }
160 }
161 }
162
163 Ok(data)
164 })
165 }
166
167 pub fn config(&self) -> &DecoderConfig {
169 &self.config
170 }
171
172 pub fn estimate_throughput(&self, stats: &DecodingStats) -> f32 {
174 if stats.decode_time_us == 0 {
175 return 0.0;
176 }
177
178 let bytes_decoded = stats.vectors_decoded * 64 * 4; let seconds = stats.decode_time_us as f64 / 1_000_000.0;
180 (bytes_decoded as f64 / seconds / 1e9) as f32
181 }
182}
183
184#[derive(Debug)]
186pub struct GpuDecoder {
187 codebook_buffers: Vec<GpuBuffer>,
189 output_buffer: GpuBuffer,
191}
192
193#[derive(Debug, Default)]
195struct GpuBuffer {
196 size: usize,
197 device_ptr: u64,
198}
199
200impl GpuDecoder {
201 pub fn new(_codebooks: &LayerCodebook) -> Result<Self> {
203 Ok(Self {
209 codebook_buffers: Vec::new(),
210 output_buffer: GpuBuffer::default(),
211 })
212 }
213
214 pub fn decode_gpu(&self, _indices: &[u16], _layer_type: LayerType) -> Result<Vec<f32>> {
216 Err(NeuralError::DecodingError("GPU not available".into()))
218 }
219
220 pub fn gpu_memory_usage(&self) -> usize {
222 let codebook_mem: usize = self.codebook_buffers.iter().map(|b| b.size).sum();
223 codebook_mem + self.output_buffer.size
224 }
225
226 pub fn is_available(&self) -> bool {
228 self.output_buffer.device_ptr != 0
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::{EncoderConfig, NeuralEncoder};
236
237 #[test]
238 fn test_decode_tensor() {
239 let codebooks = LayerCodebook::with_defaults("test");
241 let encoder = NeuralEncoder::new(EncoderConfig::default(), codebooks.clone());
242
243 let original: Vec<f32> = (0..640).map(|i| (i as f32 / 640.0) - 0.5).collect();
244 let shape = vec![10, 64];
245
246 let encoded = encoder
247 .encode_tensor("test.weight", &original, &shape, LayerType::AttentionQK)
248 .unwrap();
249
250 let decoder = NeuralDecoder::new(DecoderConfig::default(), codebooks);
252 let decoded = decoder.decode_tensor(&encoded).unwrap();
253
254 assert_eq!(decoded.name, "test.weight");
255 assert_eq!(decoded.shape, vec![10, 64]);
256 assert_eq!(decoded.data.len(), 640);
257 }
258
259 #[test]
260 fn test_decode_streaming() {
261 let codebooks = LayerCodebook::with_defaults("test");
262 let encoder = NeuralEncoder::new(EncoderConfig::default(), codebooks.clone());
263
264 let original: Vec<f32> = (0..640).map(|i| (i as f32 / 640.0) - 0.5).collect();
265 let encoded = encoder
266 .encode_tensor("test", &original, &[10, 64], LayerType::AttentionQK)
267 .unwrap();
268
269 let decoder = NeuralDecoder::new(DecoderConfig::default(), codebooks);
270
271 let chunks: Vec<_> = decoder
272 .decode_streaming(&encoded, 3)
273 .collect::<Result<Vec<_>>>()
274 .unwrap();
275
276 assert!(!chunks.is_empty());
277 }
278
279 #[test]
280 fn test_residual_application() {
281 let decoder = NeuralDecoder::new(
282 DecoderConfig::default(),
283 LayerCodebook::with_defaults("test"),
284 );
285
286 let mut data = vec![1.0, 2.0, 3.0, 4.0];
287 let residuals = vec![10i8, -10, 20, -20];
288 let scale = 0.01;
289
290 decoder.apply_residuals(&mut data, &residuals, scale);
291
292 assert!((data[0] - 1.1).abs() < 0.001);
293 assert!((data[1] - 1.9).abs() < 0.001);
294 assert!((data[2] - 3.2).abs() < 0.001);
295 assert!((data[3] - 3.8).abs() < 0.001);
296 }
297
298 #[test]
299 fn test_gpu_decoder_construction() {
300 let codebooks = LayerCodebook::with_defaults("test");
301 let decoder = GpuDecoder::new(&codebooks).unwrap();
302
303 let result = decoder.decode_gpu(&[0, 1, 2], LayerType::AttentionQK);
305 assert!(result.is_err());
306 }
307}