use crate::EvaluationError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use voirs_sdk::AudioBuffer;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuralConfig {
pub model_architecture: ModelArchitecture,
pub sequence_length: usize,
pub hidden_size: usize,
pub num_attention_heads: usize,
pub num_layers: usize,
pub dropout_prob: f32,
pub enable_adversarial: bool,
pub enable_self_supervised: bool,
pub contrastive_temperature: f32,
}
impl Default for NeuralConfig {
fn default() -> Self {
Self {
model_architecture: ModelArchitecture::Transformer,
sequence_length: 1024,
hidden_size: 512,
num_attention_heads: 8,
num_layers: 6,
dropout_prob: 0.1,
enable_adversarial: false,
enable_self_supervised: true,
contrastive_temperature: 0.07,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ModelArchitecture {
Transformer,
CNN,
RNN,
CNNTransformer,
AttentionSeq2Seq,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuralQualityAssessment {
pub overall_score: f32,
pub confidence: f32,
pub attention_weights: Vec<Vec<f32>>,
pub feature_importance: HashMap<String, f32>,
pub latent_features: Vec<f32>,
pub multi_scale_scores: Vec<f32>,
pub adversarial_robustness: Option<f32>,
pub self_supervised_score: Option<f32>,
pub perceptual_alignment: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelfSupervisedResult {
pub contrastive_loss: f32,
pub representation_quality: f32,
pub masked_prediction_accuracy: f32,
pub temporal_consistency: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdversarialResult {
pub robustness_score: f32,
pub attack_success_rate: f32,
pub perturbation_threshold: f32,
pub num_adversarial_examples: usize,
}
#[derive(Debug, Clone)]
pub struct AttentionModule {
pub query_dim: usize,
pub key_dim: usize,
pub value_dim: usize,
pub num_heads: usize,
}
pub struct NeuralEvaluator {
config: NeuralConfig,
attention_module: AttentionModule,
feature_extractor: FeatureExtractor,
quality_predictor: QualityPredictor,
}
#[derive(Debug, Clone)]
pub struct FeatureExtractor {
pub window_size: usize,
pub hop_size: usize,
pub num_mel_filters: usize,
pub frame_rate: f32,
}
#[derive(Debug, Clone)]
pub struct QualityPredictor {
pub input_dim: usize,
pub hidden_dims: Vec<usize>,
pub output_dim: usize,
pub activation: ActivationType,
}
#[derive(Debug, Clone, Copy)]
pub enum ActivationType {
ReLU,
GELU,
Swish,
Tanh,
}
impl NeuralEvaluator {
pub fn new() -> Self {
Self::with_config(NeuralConfig::default())
}
pub fn with_config(config: NeuralConfig) -> Self {
let attention_module = AttentionModule {
query_dim: config.hidden_size,
key_dim: config.hidden_size,
value_dim: config.hidden_size,
num_heads: config.num_attention_heads,
};
let feature_extractor = FeatureExtractor {
window_size: 2048,
hop_size: 512,
num_mel_filters: 80,
frame_rate: 25.0, };
let quality_predictor = QualityPredictor {
input_dim: config.hidden_size,
hidden_dims: vec![
config.hidden_size,
config.hidden_size / 2,
config.hidden_size / 4,
],
output_dim: 1,
activation: ActivationType::GELU,
};
Self {
config,
attention_module,
feature_extractor,
quality_predictor,
}
}
pub fn evaluate_neural_quality(
&self,
audio: &AudioBuffer,
) -> Result<NeuralQualityAssessment, EvaluationError> {
let features = self.extract_neural_features(audio)?;
let (processed_features, attention_weights) =
self.apply_transformer_processing(&features)?;
let overall_score = self.predict_quality_score(&processed_features)?;
let confidence = self.compute_prediction_confidence(&processed_features)?;
let multi_scale_scores = self.compute_multi_scale_scores(audio)?;
let feature_importance = self.analyze_feature_importance(&features)?;
let self_supervised_score = if self.config.enable_self_supervised {
Some(self.evaluate_self_supervised(audio)?)
} else {
None
};
let adversarial_robustness = if self.config.enable_adversarial {
Some(self.evaluate_adversarial_robustness(audio)?)
} else {
None
};
let perceptual_alignment = self.compute_perceptual_alignment(&processed_features)?;
Ok(NeuralQualityAssessment {
overall_score,
confidence,
attention_weights,
feature_importance,
latent_features: processed_features,
multi_scale_scores,
adversarial_robustness,
self_supervised_score,
perceptual_alignment,
})
}
fn extract_neural_features(&self, audio: &AudioBuffer) -> Result<Vec<f32>, EvaluationError> {
let samples = audio.samples();
if samples.len() < self.feature_extractor.window_size {
return Err(EvaluationError::InvalidInput {
message: "Audio too short for neural feature extraction".to_string(),
});
}
let mut features = Vec::new();
for i in (0..samples.len()).step_by(self.feature_extractor.hop_size) {
if i + self.feature_extractor.window_size <= samples.len() {
let frame = &samples[i..i + self.feature_extractor.window_size];
let mel_features = self.extract_mel_features(frame)?;
let temporal_features = self.extract_temporal_features(frame)?;
let spectral_features = self.extract_spectral_features(frame)?;
features.extend(mel_features);
features.extend(temporal_features);
features.extend(spectral_features);
}
}
Ok(features)
}
fn extract_mel_features(&self, frame: &[f32]) -> Result<Vec<f32>, EvaluationError> {
let mut mel_features = vec![0.0; self.feature_extractor.num_mel_filters];
let windowed: Vec<f32> = frame
.iter()
.enumerate()
.map(|(i, &sample)| {
let window_val = 0.5
* (1.0
- (2.0 * std::f32::consts::PI * i as f32 / (frame.len() - 1) as f32).cos());
sample * window_val
})
.collect();
for k in 0..self.feature_extractor.num_mel_filters {
let mut real = 0.0;
let mut imag = 0.0;
for (i, &sample) in windowed.iter().enumerate() {
let angle =
-2.0 * std::f32::consts::PI * k as f32 * i as f32 / windowed.len() as f32;
real += sample * angle.cos();
imag += sample * angle.sin();
}
let magnitude = (real * real + imag * imag).sqrt();
let mel_freq = Self::hz_to_mel(k as f32 * 22050.0 / windowed.len() as f32);
mel_features[k] = magnitude * mel_freq.ln().max(1e-10);
}
Ok(mel_features)
}
fn hz_to_mel(freq_hz: f32) -> f32 {
2595.0 * (1.0 + freq_hz / 700.0).log10()
}
fn extract_temporal_features(&self, frame: &[f32]) -> Result<Vec<f32>, EvaluationError> {
let mut temporal_features = Vec::new();
let energy = frame.iter().map(|x| x * x).sum::<f32>() / frame.len() as f32;
temporal_features.push(energy.ln().max(-80.0));
let mut zero_crossings = 0;
for i in 1..frame.len() {
if frame[i - 1] * frame[i] < 0.0 {
zero_crossings += 1;
}
}
temporal_features.push(zero_crossings as f32 / frame.len() as f32);
let centroid = self.compute_spectral_centroid(frame)?;
temporal_features.push(centroid);
let rolloff = self.compute_spectral_rolloff(frame)?;
temporal_features.push(rolloff);
Ok(temporal_features)
}
fn extract_spectral_features(&self, frame: &[f32]) -> Result<Vec<f32>, EvaluationError> {
let mut spectral_features = Vec::new();
let flatness = self.compute_spectral_flatness(frame)?;
spectral_features.push(flatness);
let flux = self.compute_spectral_flux(frame)?;
spectral_features.push(flux);
let bandwidth = self.compute_spectral_bandwidth(frame)?;
spectral_features.push(bandwidth);
Ok(spectral_features)
}
fn compute_spectral_centroid(&self, frame: &[f32]) -> Result<f32, EvaluationError> {
let spectrum = self.compute_magnitude_spectrum(frame)?;
let weighted_sum: f32 = spectrum
.iter()
.enumerate()
.map(|(i, &mag)| i as f32 * mag)
.sum();
let total_magnitude: f32 = spectrum.iter().sum();
if total_magnitude > 0.0 {
Ok(weighted_sum / total_magnitude)
} else {
Ok(0.0)
}
}
fn compute_spectral_rolloff(&self, frame: &[f32]) -> Result<f32, EvaluationError> {
let spectrum = self.compute_magnitude_spectrum(frame)?;
let total_energy: f32 = spectrum.iter().map(|x| x * x).sum();
let threshold = 0.85 * total_energy;
let mut cumulative_energy = 0.0;
for (i, &mag) in spectrum.iter().enumerate() {
cumulative_energy += mag * mag;
if cumulative_energy >= threshold {
return Ok(i as f32 / spectrum.len() as f32);
}
}
Ok(1.0)
}
fn compute_spectral_flatness(&self, frame: &[f32]) -> Result<f32, EvaluationError> {
let spectrum = self.compute_magnitude_spectrum(frame)?;
if spectrum.iter().any(|&x| x <= 0.0) {
return Ok(0.0);
}
let geometric_mean = spectrum.iter().map(|x| x.ln()).sum::<f32>() / spectrum.len() as f32;
let arithmetic_mean = spectrum.iter().sum::<f32>() / spectrum.len() as f32;
if arithmetic_mean > 0.0 {
Ok(geometric_mean.exp() / arithmetic_mean)
} else {
Ok(0.0)
}
}
fn compute_spectral_flux(&self, frame: &[f32]) -> Result<f32, EvaluationError> {
let spectrum = self.compute_magnitude_spectrum(frame)?;
let mut flux = 0.0;
for i in 1..spectrum.len() {
let diff = spectrum[i] - spectrum[i - 1];
flux += diff * diff;
}
Ok(flux.sqrt())
}
fn compute_spectral_bandwidth(&self, frame: &[f32]) -> Result<f32, EvaluationError> {
let spectrum = self.compute_magnitude_spectrum(frame)?;
let centroid = self.compute_spectral_centroid(frame)?;
let weighted_deviation: f32 = spectrum
.iter()
.enumerate()
.map(|(i, &mag)| {
let freq_diff = i as f32 - centroid;
freq_diff * freq_diff * mag
})
.sum();
let total_magnitude: f32 = spectrum.iter().sum();
if total_magnitude > 0.0 {
Ok((weighted_deviation / total_magnitude).sqrt())
} else {
Ok(0.0)
}
}
fn compute_magnitude_spectrum(&self, frame: &[f32]) -> Result<Vec<f32>, EvaluationError> {
let n = frame.len();
let mut spectrum = vec![0.0; n / 2];
for k in 0..n / 2 {
let mut real = 0.0;
let mut imag = 0.0;
for (i, &sample) in frame.iter().enumerate() {
let angle = -2.0 * std::f32::consts::PI * k as f32 * i as f32 / n as f32;
real += sample * angle.cos();
imag += sample * angle.sin();
}
spectrum[k] = (real * real + imag * imag).sqrt();
}
Ok(spectrum)
}
fn apply_transformer_processing(
&self,
features: &[f32],
) -> Result<(Vec<f32>, Vec<Vec<f32>>), EvaluationError> {
if features.is_empty() {
return Ok((Vec::new(), Vec::new()));
}
let sequence_length = self.config.sequence_length.min(features.len());
let hidden_size = self.config.hidden_size;
let attention_weights = self.compute_attention_weights(features, sequence_length)?;
let processed_features = self.apply_attention(features, &attention_weights)?;
let output_features = self.apply_feedforward(&processed_features)?;
Ok((output_features, attention_weights))
}
fn compute_attention_weights(
&self,
features: &[f32],
seq_len: usize,
) -> Result<Vec<Vec<f32>>, EvaluationError> {
let num_heads = self.config.num_attention_heads;
let mut attention_weights = vec![vec![0.0; seq_len]; num_heads];
for head in 0..num_heads {
for i in 0..seq_len {
for j in 0..seq_len {
let query_idx = (i * features.len() / seq_len).min(features.len() - 1);
let key_idx = (j * features.len() / seq_len).min(features.len() - 1);
let attention_score = if query_idx < features.len() && key_idx < features.len()
{
let dot_product = features[query_idx] * features[key_idx];
let scale = 1.0 / (self.attention_module.query_dim as f32).sqrt();
(dot_product * scale).exp()
} else {
0.0
};
attention_weights[head][j] = attention_score;
}
let sum: f32 = attention_weights[head].iter().sum();
if sum > 0.0 {
for weight in &mut attention_weights[head] {
*weight /= sum;
}
}
}
}
Ok(attention_weights)
}
fn apply_attention(
&self,
features: &[f32],
attention_weights: &[Vec<f32>],
) -> Result<Vec<f32>, EvaluationError> {
if attention_weights.is_empty() || features.is_empty() {
return Ok(features.to_vec());
}
let seq_len = attention_weights[0].len();
let mut output = vec![0.0; features.len()];
for (i, &feature) in features.iter().enumerate() {
let mut attended_value = 0.0;
for head_weights in attention_weights {
let pos = (i * seq_len / features.len()).min(seq_len - 1);
if pos < head_weights.len() {
attended_value += feature * head_weights[pos];
}
}
output[i] = attended_value / attention_weights.len() as f32;
}
Ok(output)
}
fn apply_feedforward(&self, features: &[f32]) -> Result<Vec<f32>, EvaluationError> {
if features.is_empty() {
return Ok(Vec::new());
}
let mut output = features.to_vec();
for &hidden_dim in &self.quality_predictor.hidden_dims {
let input_size = output.len();
let mut new_output = vec![0.0; hidden_dim];
for i in 0..hidden_dim {
let mut sum = 0.0;
for j in 0..input_size {
let weight = ((i + j) as f32).sin() * 0.1;
sum += output[j] * weight;
}
new_output[i] = self.apply_activation(sum);
}
output = new_output;
}
Ok(output)
}
fn apply_activation(&self, x: f32) -> f32 {
match self.quality_predictor.activation {
ActivationType::ReLU => x.max(0.0),
ActivationType::GELU => {
0.5 * x * (1.0 + (0.797_884_560_8 * (x + 0.044_715 * x * x * x)).tanh())
}
ActivationType::Swish => x / (1.0 + (-x).exp()),
ActivationType::Tanh => x.tanh(),
}
}
fn predict_quality_score(&self, features: &[f32]) -> Result<f32, EvaluationError> {
if features.is_empty() {
return Ok(0.0);
}
let mut score = 0.0;
for (i, &feature) in features.iter().enumerate() {
let weight = (i as f32 / features.len() as f32).sin() * 0.1;
score += feature * weight;
}
let quality_score = 1.0 / (1.0 + (-score).exp());
Ok(quality_score)
}
fn compute_prediction_confidence(&self, features: &[f32]) -> Result<f32, EvaluationError> {
if features.is_empty() {
return Ok(0.0);
}
let mean = features.iter().sum::<f32>() / features.len() as f32;
let variance = features
.iter()
.map(|x| (x - mean) * (x - mean))
.sum::<f32>()
/ features.len() as f32;
let confidence = 1.0 / (1.0 + variance);
Ok(confidence.min(1.0).max(0.0))
}
fn compute_multi_scale_scores(&self, audio: &AudioBuffer) -> Result<Vec<f32>, EvaluationError> {
let samples = audio.samples();
let mut multi_scale_scores = Vec::new();
let window_sizes = vec![2048, 4096, 8192, 16384];
for &window_size in &window_sizes {
if samples.len() >= window_size {
let mut scale_scores = Vec::new();
for i in (0..samples.len()).step_by(window_size / 2) {
if i + window_size <= samples.len() {
let window = &samples[i..i + window_size];
let features = self.extract_neural_features(&AudioBuffer::mono(
window.to_vec(),
audio.sample_rate(),
))?;
let score = self.predict_quality_score(&features)?;
scale_scores.push(score);
}
}
let average_score = if scale_scores.is_empty() {
0.0
} else {
scale_scores.iter().sum::<f32>() / scale_scores.len() as f32
};
multi_scale_scores.push(average_score);
} else {
multi_scale_scores.push(0.0);
}
}
Ok(multi_scale_scores)
}
fn analyze_feature_importance(
&self,
features: &[f32],
) -> Result<HashMap<String, f32>, EvaluationError> {
let mut importance = HashMap::new();
if features.is_empty() {
return Ok(importance);
}
let feature_groups = vec![
("mel_features", 0..self.feature_extractor.num_mel_filters),
(
"temporal_features",
self.feature_extractor.num_mel_filters..self.feature_extractor.num_mel_filters + 4,
),
(
"spectral_features",
self.feature_extractor.num_mel_filters + 4..features.len(),
),
];
for (group_name, range) in feature_groups {
let group_features: Vec<f32> = features.get(range.clone()).unwrap_or(&[]).to_vec();
if !group_features.is_empty() {
let variance = {
let mean = group_features.iter().sum::<f32>() / group_features.len() as f32;
group_features
.iter()
.map(|x| (x - mean) * (x - mean))
.sum::<f32>()
/ group_features.len() as f32
};
importance.insert(group_name.to_string(), variance);
}
}
Ok(importance)
}
fn evaluate_self_supervised(&self, audio: &AudioBuffer) -> Result<f32, EvaluationError> {
let samples = audio.samples();
if samples.len() < 4096 {
return Ok(0.0);
}
let anchor_features = self.extract_neural_features(audio)?;
let positive_samples: Vec<f32> = samples
.iter()
.map(|&x| x * 1.01) .collect();
let positive_audio = AudioBuffer::mono(positive_samples, audio.sample_rate());
let positive_features = self.extract_neural_features(&positive_audio)?;
use scirs2_core::random::Rng;
let mut rng = scirs2_core::random::thread_rng();
let negative_samples: Vec<f32> = (0..samples.len())
.map(|_| rng.random::<f32>() * 0.1 - 0.05)
.collect();
let negative_audio = AudioBuffer::mono(negative_samples, audio.sample_rate());
let negative_features = self.extract_neural_features(&negative_audio)?;
let positive_similarity =
self.compute_feature_similarity(&anchor_features, &positive_features);
let negative_similarity =
self.compute_feature_similarity(&anchor_features, &negative_features);
let contrastive_score =
positive_similarity / (positive_similarity + negative_similarity + 1e-8);
Ok(contrastive_score)
}
fn compute_feature_similarity(&self, features1: &[f32], features2: &[f32]) -> f32 {
if features1.len() != features2.len() || features1.is_empty() {
return 0.0;
}
let dot_product: f32 = features1
.iter()
.zip(features2.iter())
.map(|(a, b)| a * b)
.sum();
let norm1: f32 = features1.iter().map(|x| x * x).sum::<f32>().sqrt();
let norm2: f32 = features2.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm1 > 0.0 && norm2 > 0.0 {
dot_product / (norm1 * norm2)
} else {
0.0
}
}
fn evaluate_adversarial_robustness(&self, audio: &AudioBuffer) -> Result<f32, EvaluationError> {
let samples = audio.samples();
let original_score = self.predict_quality_score(&self.extract_neural_features(audio)?)?;
let mut adversarial_scores = Vec::new();
let noise_levels = vec![0.001, 0.005, 0.01, 0.02];
for &noise_level in &noise_levels {
use scirs2_core::random::Rng;
let mut rng = scirs2_core::random::thread_rng();
let adversarial_samples: Vec<f32> = samples
.iter()
.map(|&x| {
let noise = (rng.random::<f32>() - 0.5) * noise_level;
x + noise
})
.collect();
let adversarial_audio = AudioBuffer::mono(adversarial_samples, audio.sample_rate());
let adversarial_features = self.extract_neural_features(&adversarial_audio)?;
let adversarial_score = self.predict_quality_score(&adversarial_features)?;
adversarial_scores.push(adversarial_score);
}
let score_variance = {
let mean_score =
adversarial_scores.iter().sum::<f32>() / adversarial_scores.len() as f32;
adversarial_scores
.iter()
.map(|score| (score - mean_score) * (score - mean_score))
.sum::<f32>()
/ adversarial_scores.len() as f32
};
let robustness = 1.0 / (1.0 + score_variance * 10.0); Ok(robustness)
}
fn compute_perceptual_alignment(&self, features: &[f32]) -> Result<f32, EvaluationError> {
if features.is_empty() {
return Ok(0.0);
}
let mean = features.iter().sum::<f32>() / features.len() as f32;
let std_dev = {
let variance = features
.iter()
.map(|x| (x - mean) * (x - mean))
.sum::<f32>()
/ features.len() as f32;
variance.sqrt()
};
let ideal_std = 0.5;
let alignment = 1.0 - (std_dev - ideal_std).abs() / ideal_std;
Ok(alignment.max(0.0).min(1.0))
}
pub fn compare_neural_quality(
&self,
reference: &AudioBuffer,
generated: &AudioBuffer,
) -> Result<f32, EvaluationError> {
let ref_assessment = self.evaluate_neural_quality(reference)?;
let gen_assessment = self.evaluate_neural_quality(generated)?;
let score_diff = (ref_assessment.overall_score - gen_assessment.overall_score).abs();
let confidence_diff = (ref_assessment.confidence - gen_assessment.confidence).abs();
let perceptual_diff =
(ref_assessment.perceptual_alignment - gen_assessment.perceptual_alignment).abs();
let feature_similarity = self.compute_feature_similarity(
&ref_assessment.latent_features,
&gen_assessment.latent_features,
);
let multi_scale_similarity =
if ref_assessment.multi_scale_scores.len() == gen_assessment.multi_scale_scores.len() {
let correlations: Vec<f32> = ref_assessment
.multi_scale_scores
.iter()
.zip(gen_assessment.multi_scale_scores.iter())
.map(|(&r, &g)| 1.0 - (r - g).abs())
.collect();
correlations.iter().sum::<f32>() / correlations.len() as f32
} else {
0.5
};
let neural_similarity = 0.3 * (1.0 - score_diff)
+ 0.2 * (1.0 - confidence_diff)
+ 0.2 * (1.0 - perceptual_diff)
+ 0.2 * feature_similarity
+ 0.1 * multi_scale_similarity;
Ok(neural_similarity.max(0.0).min(1.0))
}
}
impl Default for NeuralEvaluator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_neural_evaluator_creation() {
let evaluator = NeuralEvaluator::new();
assert_eq!(
evaluator.config.model_architecture as u8,
ModelArchitecture::Transformer as u8
);
assert_eq!(evaluator.config.hidden_size, 512);
assert_eq!(evaluator.config.num_attention_heads, 8);
}
#[test]
fn test_neural_config_default() {
let config = NeuralConfig::default();
assert_eq!(config.sequence_length, 1024);
assert_eq!(config.num_layers, 6);
assert!(!config.enable_adversarial);
assert!(config.enable_self_supervised);
}
#[test]
fn test_neural_quality_evaluation() {
let evaluator = NeuralEvaluator::new();
let audio = AudioBuffer::mono(vec![0.1; 16000], 16000);
let assessment = evaluator.evaluate_neural_quality(&audio);
assert!(assessment.is_ok());
let result = assessment.unwrap();
assert!(result.overall_score >= 0.0 && result.overall_score <= 1.0);
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
assert!(!result.attention_weights.is_empty());
assert!(!result.feature_importance.is_empty());
assert!(!result.latent_features.is_empty());
assert!(!result.multi_scale_scores.is_empty());
}
#[test]
fn test_feature_extraction() {
let evaluator = NeuralEvaluator::new();
let audio = AudioBuffer::mono(vec![0.1; 16000], 16000);
let features = evaluator.extract_neural_features(&audio);
assert!(features.is_ok());
let result = features.unwrap();
assert!(!result.is_empty());
assert!(result.iter().all(|&x| x.is_finite()));
}
#[test]
fn test_mel_feature_extraction() {
let evaluator = NeuralEvaluator::new();
let frame = vec![0.1; 2048];
let mel_features = evaluator.extract_mel_features(&frame);
assert!(mel_features.is_ok());
let result = mel_features.unwrap();
assert_eq!(result.len(), evaluator.feature_extractor.num_mel_filters);
assert!(result.iter().all(|&x| x.is_finite()));
}
#[test]
fn test_temporal_feature_extraction() {
let evaluator = NeuralEvaluator::new();
let frame = vec![0.1; 2048];
let temporal_features = evaluator.extract_temporal_features(&frame);
assert!(temporal_features.is_ok());
let result = temporal_features.unwrap();
assert_eq!(result.len(), 4); assert!(result.iter().all(|&x| x.is_finite()));
}
#[test]
fn test_spectral_feature_extraction() {
let evaluator = NeuralEvaluator::new();
let frame = vec![0.1; 2048];
let spectral_features = evaluator.extract_spectral_features(&frame);
assert!(spectral_features.is_ok());
let result = spectral_features.unwrap();
assert_eq!(result.len(), 3); assert!(result.iter().all(|&x| x.is_finite()));
}
#[test]
fn test_attention_computation() {
let evaluator = NeuralEvaluator::new();
let features = vec![0.1, 0.2, 0.3, 0.4, 0.5];
let attention_weights = evaluator.compute_attention_weights(&features, 3);
assert!(attention_weights.is_ok());
let result = attention_weights.unwrap();
assert_eq!(result.len(), evaluator.config.num_attention_heads);
for head_weights in &result {
assert_eq!(head_weights.len(), 3);
let sum: f32 = head_weights.iter().sum();
assert!((sum - 1.0).abs() < 0.01); }
}
#[test]
fn test_transformer_processing() {
let evaluator = NeuralEvaluator::new();
let features = vec![0.1; 100];
let result = evaluator.apply_transformer_processing(&features);
assert!(result.is_ok());
let (processed_features, attention_weights) = result.unwrap();
assert!(!processed_features.is_empty());
assert!(!attention_weights.is_empty());
}
#[test]
fn test_quality_prediction() {
let evaluator = NeuralEvaluator::new();
let features = vec![0.5; 10];
let score = evaluator.predict_quality_score(&features);
assert!(score.is_ok());
let result = score.unwrap();
assert!(result >= 0.0 && result <= 1.0);
}
#[test]
fn test_multi_scale_analysis() {
let evaluator = NeuralEvaluator::new();
let audio = AudioBuffer::mono(vec![0.1; 16000], 16000);
let scores = evaluator.compute_multi_scale_scores(&audio);
assert!(scores.is_ok());
let result = scores.unwrap();
assert!(!result.is_empty());
assert!(result.iter().all(|&score| score >= 0.0 && score <= 1.0));
}
#[test]
fn test_feature_importance() {
let evaluator = NeuralEvaluator::new();
let features = vec![0.1; 100];
let importance = evaluator.analyze_feature_importance(&features);
assert!(importance.is_ok());
let result = importance.unwrap();
assert!(!result.is_empty());
assert!(result.values().all(|&val| val >= 0.0));
}
#[test]
fn test_self_supervised_evaluation() {
let evaluator = NeuralEvaluator::new();
let audio = AudioBuffer::mono(vec![0.1; 16000], 16000);
let score = evaluator.evaluate_self_supervised(&audio);
assert!(score.is_ok());
let result = score.unwrap();
assert!(result >= 0.0 && result <= 1.0);
}
#[test]
fn test_adversarial_robustness() {
let evaluator = NeuralEvaluator::new();
let audio = AudioBuffer::mono(vec![0.1; 16000], 16000);
let robustness = evaluator.evaluate_adversarial_robustness(&audio);
assert!(robustness.is_ok());
let result = robustness.unwrap();
assert!(result >= 0.0 && result <= 1.0);
}
#[test]
fn test_neural_comparison() {
let evaluator = NeuralEvaluator::new();
let reference = AudioBuffer::mono(vec![0.1; 16000], 16000);
let generated = AudioBuffer::mono(vec![0.1; 16000], 16000);
let similarity = evaluator.compare_neural_quality(&reference, &generated);
assert!(similarity.is_ok());
let result = similarity.unwrap();
assert!(result >= 0.0 && result <= 1.0);
assert!(result > 0.8); }
#[test]
fn test_feature_similarity() {
let evaluator = NeuralEvaluator::new();
let features1 = vec![1.0, 2.0, 3.0];
let features2 = vec![2.0, 4.0, 6.0];
let similarity = evaluator.compute_feature_similarity(&features1, &features2);
assert!((similarity - 1.0).abs() < 0.001); }
#[test]
fn test_activation_functions() {
let evaluator = NeuralEvaluator::new();
assert!(evaluator.apply_activation(-1.0) < 0.0); assert!(evaluator.apply_activation(1.0) > 0.0);
assert!(evaluator.apply_activation(0.0) >= 0.0);
}
#[test]
fn test_hz_to_mel_conversion() {
let mel_1000 = NeuralEvaluator::hz_to_mel(1000.0);
let mel_2000 = NeuralEvaluator::hz_to_mel(2000.0);
assert!(mel_1000 < mel_2000); assert!(mel_1000 > 0.0);
}
#[test]
fn test_empty_audio_handling() {
let evaluator = NeuralEvaluator::new();
let empty_audio = AudioBuffer::mono(vec![], 16000);
let result = evaluator.evaluate_neural_quality(&empty_audio);
assert!(result.is_err()); }
#[test]
fn test_perceptual_alignment() {
let evaluator = NeuralEvaluator::new();
let features = vec![0.5; 10];
let alignment = evaluator.compute_perceptual_alignment(&features);
assert!(alignment.is_ok());
let result = alignment.unwrap();
assert!(result >= 0.0 && result <= 1.0);
}
}