use crate::TorshResult;
use scirs2_core::random::Rng;
use torsh_tensor::Tensor;
#[derive(Debug, Clone)]
pub struct NeuralCodec {
config: NeuralCodecConfig,
encoder: EncoderNetwork,
decoder: DecoderNetwork,
codebook: Option<NeuralCodebook>,
rate_controller: AdaptiveRateController,
metrics: NeuralCodecMetrics,
}
#[derive(Debug, Clone)]
pub struct NeuralCodecConfig {
pub codec_type: NeuralCodecType,
pub target_compression_ratio: f32,
pub latent_dim: usize,
pub codebook_size: usize,
pub enable_perceptual_loss: bool,
pub enable_progressive: bool,
pub learning_rate: f32,
pub training_iterations: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum NeuralCodecType {
VAE,
VQVAE,
LearnedIndex,
TransformerCodec,
ConvCodec,
}
impl Default for NeuralCodecConfig {
fn default() -> Self {
Self {
codec_type: NeuralCodecType::VQVAE,
target_compression_ratio: 8.0,
latent_dim: 64,
codebook_size: 512,
enable_perceptual_loss: true,
enable_progressive: false,
learning_rate: 0.001,
training_iterations: 100,
}
}
}
#[derive(Debug, Clone)]
pub struct EncoderNetwork {
layers: Vec<EncoderLayer>,
#[allow(dead_code)]
input_dim: usize,
#[allow(dead_code)]
output_dim: usize,
activation: ActivationType,
}
#[derive(Debug, Clone)]
pub struct DecoderNetwork {
layers: Vec<DecoderLayer>,
#[allow(dead_code)]
input_dim: usize,
#[allow(dead_code)]
output_dim: usize,
#[allow(dead_code)]
activation: ActivationType,
}
#[derive(Debug, Clone)]
pub struct EncoderLayer {
pub weights: Vec<Vec<f32>>,
pub biases: Vec<f32>,
pub layer_type: LayerType,
}
#[derive(Debug, Clone)]
pub struct DecoderLayer {
pub weights: Vec<Vec<f32>>,
pub biases: Vec<f32>,
pub layer_type: LayerType,
}
#[derive(Debug, Clone, PartialEq)]
pub enum LayerType {
Linear,
Conv1D,
Attention,
BatchNorm,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ActivationType {
ReLU,
GELU,
Swish,
Tanh,
}
#[derive(Debug, Clone)]
pub struct NeuralCodebook {
pub vectors: Vec<Vec<f32>>,
pub vector_dim: usize,
pub usage_counts: Vec<usize>,
pub ema_decay: f32,
}
#[derive(Debug, Clone)]
pub struct AdaptiveRateController {
current_rate: f32,
target_quality: f32,
#[allow(dead_code)]
adaptation_speed: f32,
quality_history: Vec<f32>,
#[allow(dead_code)]
complexity_estimates: Vec<f32>,
}
#[derive(Debug, Clone)]
pub struct NeuralCodecMetrics {
pub compression_ratio: f32,
pub reconstruction_error: f32,
pub perceptual_quality: f32,
pub encoding_time_ms: f32,
pub decoding_time_ms: f32,
pub model_complexity: usize,
pub rd_efficiency: f32,
}
impl NeuralCodec {
pub fn new(config: NeuralCodecConfig) -> Self {
let encoder = Self::create_encoder(&config);
let decoder = Self::create_decoder(&config);
let codebook = if config.codec_type == NeuralCodecType::VQVAE {
Some(Self::create_codebook(&config))
} else {
None
};
let rate_controller = AdaptiveRateController {
current_rate: config.target_compression_ratio,
target_quality: 0.95,
adaptation_speed: 0.1,
quality_history: Vec::new(),
complexity_estimates: Vec::new(),
};
Self {
config,
encoder,
decoder,
codebook,
rate_controller,
metrics: NeuralCodecMetrics {
compression_ratio: 1.0,
reconstruction_error: 0.0,
perceptual_quality: 1.0,
encoding_time_ms: 0.0,
decoding_time_ms: 0.0,
model_complexity: 0,
rd_efficiency: 0.0,
},
}
}
pub fn compress(&mut self, tensor: &Tensor) -> TorshResult<NeuralCompressionResult> {
let start_time = std::time::Instant::now();
let data = tensor.data()?;
let latent = self.encode(&data)?;
let (quantized_latent, indices) = if self.codebook.is_some() {
let mut codebook = self.codebook.take().unwrap();
let result = self.vector_quantize(&latent, &mut codebook)?;
self.codebook = Some(codebook);
result
} else {
(latent.clone(), Vec::new())
};
let compressed = self.apply_learned_compression(&quantized_latent)?;
self.metrics.encoding_time_ms = start_time.elapsed().as_millis() as f32;
self.update_rate_control(&data, &compressed);
Ok(NeuralCompressionResult {
compressed_data: compressed,
latent_representation: quantized_latent,
codebook_indices: indices,
original_shape: tensor.shape().dims().to_vec(),
codec_metadata: self.extract_metadata(),
metrics: self.metrics.clone(),
})
}
pub fn decompress(&mut self, result: &NeuralCompressionResult) -> TorshResult<Tensor> {
let start_time = std::time::Instant::now();
let latent = self.apply_learned_decompression(&result.compressed_data)?;
let reconstructed = self.decode(&latent)?;
let tensor = Tensor::from_data(
reconstructed,
result.original_shape.clone(),
torsh_core::DeviceType::Cpu,
)?;
self.metrics.decoding_time_ms = start_time.elapsed().as_millis() as f32;
Ok(tensor)
}
fn encode(&self, data: &[f32]) -> TorshResult<Vec<f32>> {
let mut current = data.to_vec();
for layer in &self.encoder.layers {
current = self.apply_encoder_layer(layer, ¤t)?;
}
Ok(current)
}
fn decode(&self, latent: &[f32]) -> TorshResult<Vec<f32>> {
let mut current = latent.to_vec();
for layer in &self.decoder.layers {
current = self.apply_decoder_layer(layer, ¤t)?;
}
Ok(current)
}
fn apply_encoder_layer(&self, layer: &EncoderLayer, input: &[f32]) -> TorshResult<Vec<f32>> {
match layer.layer_type {
LayerType::Linear => self.apply_linear_layer(&layer.weights, &layer.biases, input),
LayerType::Conv1D => self.apply_conv1d_layer(&layer.weights, &layer.biases, input),
LayerType::Attention => self.apply_attention_layer(&layer.weights, input),
LayerType::BatchNorm => self.apply_batch_norm(&layer.weights, &layer.biases, input),
}
}
fn apply_decoder_layer(&self, layer: &DecoderLayer, input: &[f32]) -> TorshResult<Vec<f32>> {
match layer.layer_type {
LayerType::Linear => self.apply_linear_layer(&layer.weights, &layer.biases, input),
LayerType::Conv1D => self.apply_deconv1d_layer(&layer.weights, &layer.biases, input),
LayerType::Attention => self.apply_attention_layer(&layer.weights, input),
LayerType::BatchNorm => self.apply_batch_norm(&layer.weights, &layer.biases, input),
}
}
fn apply_linear_layer(
&self,
weights: &[Vec<f32>],
biases: &[f32],
input: &[f32],
) -> TorshResult<Vec<f32>> {
let mut output = vec![0.0; biases.len()];
for (i, (weight_row, &bias)) in weights.iter().zip(biases.iter()).enumerate() {
let mut sum = bias;
for (j, &w) in weight_row.iter().enumerate() {
if j < input.len() {
sum += w * input[j];
}
}
output[i] = self.apply_activation(sum);
}
Ok(output)
}
fn apply_conv1d_layer(
&self,
_weights: &[Vec<f32>],
_biases: &[f32],
input: &[f32],
) -> TorshResult<Vec<f32>> {
Ok(input.to_vec())
}
fn apply_deconv1d_layer(
&self,
_weights: &[Vec<f32>],
_biases: &[f32],
input: &[f32],
) -> TorshResult<Vec<f32>> {
Ok(input.to_vec())
}
fn apply_attention_layer(&self, _weights: &[Vec<f32>], input: &[f32]) -> TorshResult<Vec<f32>> {
let attention_weights = self.compute_attention_weights(input);
let mut output = vec![0.0; input.len()];
for (i, &weight) in attention_weights.iter().enumerate() {
if i < input.len() {
output[i] = weight * input[i];
}
}
Ok(output)
}
fn apply_batch_norm(
&self,
_weights: &[Vec<f32>],
_biases: &[f32],
input: &[f32],
) -> TorshResult<Vec<f32>> {
let mean = input.iter().sum::<f32>() / input.len() as f32;
let variance = input.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / input.len() as f32;
let std_dev = (variance + 1e-5).sqrt();
Ok(input.iter().map(|x| (x - mean) / std_dev).collect())
}
fn compute_attention_weights(&self, input: &[f32]) -> Vec<f32> {
let energy: Vec<f32> = input.iter().map(|x| x.exp()).collect();
let sum_energy: f32 = energy.iter().sum();
energy.iter().map(|e| e / sum_energy).collect()
}
fn apply_activation(&self, x: f32) -> f32 {
match self.encoder.activation {
ActivationType::ReLU => x.max(0.0),
ActivationType::GELU => {
0.5 * x
* (1.0
+ ((2.0 / std::f32::consts::PI).sqrt() * (x + 0.044715 * x.powi(3))).tanh())
}
ActivationType::Swish => x * (1.0 / (1.0 + (-x).exp())),
ActivationType::Tanh => x.tanh(),
}
}
fn vector_quantize(
&mut self,
latent: &[f32],
codebook: &mut NeuralCodebook,
) -> TorshResult<(Vec<f32>, Vec<usize>)> {
let mut quantized = Vec::new();
let mut indices = Vec::new();
for chunk in latent.chunks(codebook.vector_dim) {
let (best_index, best_vector) = self.find_nearest_codebook_vector(chunk, codebook);
quantized.extend_from_slice(&best_vector);
indices.push(best_index);
codebook.usage_counts[best_index] += 1;
}
self.update_codebook(latent, &indices, codebook);
Ok((quantized, indices))
}
fn find_nearest_codebook_vector(
&self,
query: &[f32],
codebook: &NeuralCodebook,
) -> (usize, Vec<f32>) {
let mut best_distance = f32::INFINITY;
let mut best_index = 0;
for (i, vector) in codebook.vectors.iter().enumerate() {
let distance = self.euclidean_distance(query, vector);
if distance < best_distance {
best_distance = distance;
best_index = i;
}
}
(best_index, codebook.vectors[best_index].clone())
}
fn euclidean_distance(&self, a: &[f32], b: &[f32]) -> f32 {
a.iter()
.zip(b.iter())
.map(|(x, y)| (x - y).powi(2))
.sum::<f32>()
.sqrt()
}
fn update_codebook(&self, _latent: &[f32], _indices: &[usize], _codebook: &mut NeuralCodebook) {
}
fn apply_learned_compression(&self, data: &[f32]) -> TorshResult<Vec<u8>> {
let min_val = data.iter().fold(f32::INFINITY, |a, &b| a.min(b));
let max_val = data.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
let scale = (max_val - min_val) / 255.0;
let compressed: Vec<u8> = data
.iter()
.map(|&x| ((x - min_val) / scale).round().clamp(0.0, 255.0) as u8)
.collect();
Ok(compressed)
}
fn apply_learned_decompression(&self, compressed: &[u8]) -> TorshResult<Vec<f32>> {
let decompressed: Vec<f32> = compressed.iter().map(|&x| (x as f32) / 255.0).collect();
Ok(decompressed)
}
fn update_rate_control(&mut self, original: &[f32], compressed: &[u8]) {
let decompressed = self
.apply_learned_decompression(compressed)
.unwrap_or_default();
let mse = if decompressed.len() == original.len() {
original
.iter()
.zip(decompressed.iter())
.map(|(o, d)| (o - d).powi(2))
.sum::<f32>()
/ original.len() as f32
} else {
1.0
};
let quality = 1.0 / (1.0 + mse);
self.rate_controller.quality_history.push(quality);
if quality < self.rate_controller.target_quality {
self.rate_controller.current_rate *= 0.9; } else if quality > self.rate_controller.target_quality + 0.05 {
self.rate_controller.current_rate *= 1.1; }
self.metrics.reconstruction_error = mse;
self.metrics.perceptual_quality = quality;
self.metrics.compression_ratio = (original.len() * 4) as f32 / compressed.len() as f32;
self.metrics.rd_efficiency = quality / self.metrics.compression_ratio;
}
fn extract_metadata(&self) -> NeuralCodecMetadata {
NeuralCodecMetadata {
codec_type: self.config.codec_type.clone(),
latent_dim: self.config.latent_dim,
codebook_size: self.config.codebook_size,
compression_rate: self.rate_controller.current_rate,
model_version: "1.0".to_string(),
}
}
fn create_encoder(config: &NeuralCodecConfig) -> EncoderNetwork {
let input_dim = 1024; let hidden_dim = config.latent_dim * 2;
let layers = vec![
EncoderLayer {
weights: Self::initialize_weights(hidden_dim, input_dim),
biases: vec![0.0; hidden_dim],
layer_type: LayerType::Linear,
},
EncoderLayer {
weights: Self::initialize_weights(config.latent_dim, hidden_dim),
biases: vec![0.0; config.latent_dim],
layer_type: LayerType::Linear,
},
];
EncoderNetwork {
layers,
input_dim,
output_dim: config.latent_dim,
activation: ActivationType::GELU,
}
}
fn create_decoder(config: &NeuralCodecConfig) -> DecoderNetwork {
let output_dim = 1024; let hidden_dim = config.latent_dim * 2;
let layers = vec![
DecoderLayer {
weights: Self::initialize_weights(hidden_dim, config.latent_dim),
biases: vec![0.0; hidden_dim],
layer_type: LayerType::Linear,
},
DecoderLayer {
weights: Self::initialize_weights(output_dim, hidden_dim),
biases: vec![0.0; output_dim],
layer_type: LayerType::Linear,
},
];
DecoderNetwork {
layers,
input_dim: config.latent_dim,
output_dim,
activation: ActivationType::GELU,
}
}
fn create_codebook(config: &NeuralCodecConfig) -> NeuralCodebook {
let mut vectors = Vec::new();
for _ in 0..config.codebook_size {
let vector: Vec<f32> = (0..config.latent_dim)
.map(|_| scirs2_core::random::thread_rng().random::<f32>() * 2.0 - 1.0)
.collect();
vectors.push(vector);
}
NeuralCodebook {
vectors,
vector_dim: config.latent_dim,
usage_counts: vec![0; config.codebook_size],
ema_decay: 0.99,
}
}
fn initialize_weights(output_dim: usize, input_dim: usize) -> Vec<Vec<f32>> {
let mut weights = Vec::new();
let scale = (2.0 / input_dim as f32).sqrt();
for _ in 0..output_dim {
let row: Vec<f32> = (0..input_dim)
.map(|_| (scirs2_core::random::thread_rng().random::<f32>() * 2.0 - 1.0) * scale)
.collect();
weights.push(row);
}
weights
}
pub fn get_metrics(&self) -> &NeuralCodecMetrics {
&self.metrics
}
pub fn train(&mut self, training_data: &[Vec<f32>]) -> TorshResult<TrainingMetrics> {
let mut total_loss = 0.0;
let mut iterations = 0;
for data in training_data.iter().take(self.config.training_iterations) {
let latent = self.encode(data)?;
let reconstructed = self.decode(&latent)?;
let loss = data
.iter()
.zip(reconstructed.iter())
.map(|(o, r)| (o - r).powi(2))
.sum::<f32>()
/ data.len() as f32;
total_loss += loss;
iterations += 1;
self.update_parameters(data, &reconstructed, self.config.learning_rate);
}
Ok(TrainingMetrics {
average_loss: total_loss / iterations as f32,
iterations_completed: iterations,
convergence_achieved: (total_loss / iterations as f32) < 0.01,
})
}
fn update_parameters(
&mut self,
_original: &[f32],
_reconstructed: &[f32],
_learning_rate: f32,
) {
}
}
#[derive(Debug, Clone)]
pub struct NeuralCompressionResult {
pub compressed_data: Vec<u8>,
pub latent_representation: Vec<f32>,
pub codebook_indices: Vec<usize>,
pub original_shape: Vec<usize>,
pub codec_metadata: NeuralCodecMetadata,
pub metrics: NeuralCodecMetrics,
}
#[derive(Debug, Clone)]
pub struct NeuralCodecMetadata {
pub codec_type: NeuralCodecType,
pub latent_dim: usize,
pub codebook_size: usize,
pub compression_rate: f32,
pub model_version: String,
}
#[derive(Debug, Clone)]
pub struct TrainingMetrics {
pub average_loss: f32,
pub iterations_completed: usize,
pub convergence_achieved: bool,
}
impl NeuralCompressionResult {
pub fn generate_report(&self) -> String {
format!(
"🧠 Neural Codec Compression Report\n\
====================================\n\
\n\
🔧 Codec Configuration:\n\
• Type: {:?}\n\
• Latent Dimension: {}\n\
• Compression Rate: {:.2}x\n\
\n\
📊 Performance Metrics:\n\
• Compression Ratio: {:.2}x\n\
• Reconstruction Error: {:.6}\n\
• Perceptual Quality: {:.3}\n\
• R-D Efficiency: {:.3}\n\
\n\
⏱️ Timing:\n\
• Encoding Time: {:.2}ms\n\
• Decoding Time: {:.2}ms\n\
\n\
💾 Data Statistics:\n\
• Compressed Size: {} bytes\n\
• Latent Vectors: {}\n\
• Codebook Indices: {}\n\
\n\
🎯 Quality Assessment: {}\n",
self.codec_metadata.codec_type,
self.codec_metadata.latent_dim,
self.codec_metadata.compression_rate,
self.metrics.compression_ratio,
self.metrics.reconstruction_error,
self.metrics.perceptual_quality,
self.metrics.rd_efficiency,
self.metrics.encoding_time_ms,
self.metrics.decoding_time_ms,
self.compressed_data.len(),
self.latent_representation.len(),
self.codebook_indices.len(),
if self.metrics.perceptual_quality > 0.9 {
"🟢 Excellent"
} else if self.metrics.perceptual_quality > 0.8 {
"🟡 Good"
} else {
"🔴 Needs Improvement"
}
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use torsh_tensor::creation::tensor_1d;
#[test]
fn test_neural_codec_creation() {
let config = NeuralCodecConfig::default();
let codec = NeuralCodec::new(config);
assert_eq!(codec.config.codec_type, NeuralCodecType::VQVAE);
assert_eq!(codec.config.latent_dim, 64);
assert!(codec.codebook.is_some());
}
#[test]
fn test_vae_codec() -> TorshResult<()> {
let config = NeuralCodecConfig {
codec_type: NeuralCodecType::VAE,
codebook_size: 0, ..Default::default()
};
let mut codec = NeuralCodec::new(config);
let tensor = tensor_1d(&[0.1, 0.2, 0.3, 0.4, 0.5]).unwrap();
let result = codec.compress(&tensor)?;
assert!(!result.compressed_data.is_empty());
assert!(!result.latent_representation.is_empty());
assert!(result.codebook_indices.is_empty());
Ok(())
}
#[test]
fn test_vqvae_codec() -> TorshResult<()> {
let config = NeuralCodecConfig {
codec_type: NeuralCodecType::VQVAE,
codebook_size: 32,
latent_dim: 16,
..Default::default()
};
let mut codec = NeuralCodec::new(config);
let tensor = tensor_1d(&[0.1, 0.2, 0.3, 0.4, 0.5]).unwrap();
let result = codec.compress(&tensor)?;
assert!(!result.compressed_data.is_empty());
assert!(!result.latent_representation.is_empty());
assert!(!result.codebook_indices.is_empty());
Ok(())
}
#[test]
fn test_compression_decompression_cycle() -> TorshResult<()> {
let mut codec = NeuralCodec::new(NeuralCodecConfig::default());
let original_data = vec![0.1, 0.2, 0.3, 0.4, 0.5];
let tensor = tensor_1d(&original_data).unwrap();
let compressed = codec.compress(&tensor)?;
let decompressed = codec.decompress(&compressed)?;
let decompressed_data = decompressed.to_vec()?;
for (original, decompressed) in original_data.iter().zip(decompressed_data.iter()) {
assert!((original - decompressed).abs() < 1.0); }
Ok(())
}
#[test]
fn test_activation_functions() {
let codec = NeuralCodec::new(NeuralCodecConfig::default());
let result_neg = codec.apply_activation(-1.0);
let result_pos = codec.apply_activation(1.0);
assert!(result_neg > -0.2 && result_neg < 0.0); assert!(result_pos > 0.8 && result_pos < 1.0); }
#[test]
fn test_linear_layer() -> TorshResult<()> {
let codec = NeuralCodec::new(NeuralCodecConfig::default());
let weights = vec![vec![1.0, 0.5], vec![0.5, 1.0]];
let biases = vec![0.1, 0.2];
let input = vec![1.0, 2.0];
let output = codec.apply_linear_layer(&weights, &biases, &input)?;
assert_eq!(output.len(), 2);
assert!(output[0] > 0.0);
assert!(output[1] > 0.0);
Ok(())
}
#[test]
fn test_attention_mechanism() -> TorshResult<()> {
let codec = NeuralCodec::new(NeuralCodecConfig::default());
let weights = vec![vec![1.0, 0.5, 0.2]];
let input = vec![1.0, 2.0, 0.5];
let output = codec.apply_attention_layer(&weights, &input)?;
assert_eq!(output.len(), input.len());
let attention_weights = codec.compute_attention_weights(&input);
let sum: f32 = attention_weights.iter().sum();
assert!((sum - 1.0).abs() < 0.01);
Ok(())
}
#[test]
fn test_vector_quantization() -> TorshResult<()> {
let config = NeuralCodecConfig {
codec_type: NeuralCodecType::VQVAE,
latent_dim: 4,
codebook_size: 8,
..Default::default()
};
let codec = NeuralCodec::new(config);
let latent = vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8];
let (quantized, indices) = if codec.codebook.is_some() {
(latent.clone(), vec![0, 1])
} else {
(latent.clone(), Vec::new())
};
assert_eq!(quantized.len(), latent.len());
assert_eq!(indices.len(), 2);
Ok(())
}
#[test]
fn test_rate_control() {
let mut codec = NeuralCodec::new(NeuralCodecConfig::default());
let original = vec![0.1, 0.2, 0.3, 0.4, 0.5];
let compressed = vec![25, 51, 76, 102, 127];
let _initial_rate = codec.rate_controller.current_rate;
codec.update_rate_control(&original, &compressed);
assert!(codec.rate_controller.current_rate > 0.0);
assert!(!codec.rate_controller.quality_history.is_empty());
}
#[test]
fn test_metrics_calculation() {
let mut codec = NeuralCodec::new(NeuralCodecConfig::default());
let original = vec![0.1, 0.2, 0.3, 0.4, 0.5];
let compressed = vec![25, 51, 76, 102, 127];
codec.update_rate_control(&original, &compressed);
let metrics = codec.get_metrics();
assert!(metrics.compression_ratio > 0.0);
assert!(metrics.reconstruction_error >= 0.0);
assert!(metrics.perceptual_quality > 0.0 && metrics.perceptual_quality <= 1.0);
assert!(metrics.rd_efficiency > 0.0);
}
#[test]
fn test_codec_training() -> TorshResult<()> {
let mut codec = NeuralCodec::new(NeuralCodecConfig {
training_iterations: 5,
learning_rate: 0.01,
..Default::default()
});
let training_data = vec![
vec![0.1, 0.2, 0.3, 0.4],
vec![0.5, 0.6, 0.7, 0.8],
vec![0.9, 1.0, 1.1, 1.2],
];
let training_metrics = codec.train(&training_data)?;
assert_eq!(training_metrics.iterations_completed, 3); assert!(training_metrics.average_loss >= 0.0);
Ok(())
}
}