use torsh_core::Result as TorshResult;
use torsh_tensor::Tensor;
#[derive(Debug, Clone)]
pub struct TensorCharacteristics {
pub size: usize,
pub condition_number: f32,
pub sparsity: f32,
pub numerical_precision: f32,
pub memory_layout_score: f32,
pub computational_complexity: f32,
}
impl TensorCharacteristics {
pub fn analyze(tensor: &Tensor) -> TorshResult<Self> {
let data = tensor.data()?;
let size = data.len();
let zero_count = data.iter().filter(|&&x| x.abs() < 1e-12).count();
let sparsity = zero_count as f32 / size as f32;
let max_val = data.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b.abs()));
let min_val = data.iter().fold(f32::INFINITY, |a, &b| {
if b.abs() > 1e-12 {
a.min(b.abs())
} else {
a
}
});
let condition_number = if min_val > 0.0 {
max_val / min_val
} else {
f32::INFINITY
};
let mean_val = data.iter().sum::<f32>() / size as f32;
let std_dev =
(data.iter().map(|&x| (x - mean_val).powi(2)).sum::<f32>() / size as f32).sqrt();
let numerical_precision = if std_dev > 0.0 {
mean_val.abs() / std_dev
} else {
1.0
};
let shape_binding = tensor.shape();
let shape = shape_binding.dims();
let memory_layout_score = match shape.len() {
1 => 1.0, 2 => 0.8, 3 => 0.6, _ => 0.4, };
let computational_complexity = size as f32 * shape.len() as f32;
Ok(TensorCharacteristics {
size,
condition_number,
sparsity,
numerical_precision,
memory_layout_score,
computational_complexity,
})
}
}
#[derive(Debug, Clone)]
pub enum OptimizationAlgorithm {
GradientDescent,
MomentumGradientDescent,
Adam,
LBFGS,
ConjugateGradient,
}
pub struct AdaptiveAlgorithmSelector {
performance_history: std::collections::HashMap<String, Vec<f32>>,
#[allow(dead_code)]
adaptation_rate: f32,
}
impl AdaptiveAlgorithmSelector {
pub fn new() -> Self {
Self {
performance_history: std::collections::HashMap::new(),
adaptation_rate: 0.1,
}
}
pub fn select_algorithm(
&self,
characteristics: &TensorCharacteristics,
) -> OptimizationAlgorithm {
if characteristics.sparsity > 0.8 {
return OptimizationAlgorithm::ConjugateGradient;
}
if characteristics.size > 1_000_000 {
return match characteristics.condition_number {
cn if cn > 1000.0 => OptimizationAlgorithm::Adam,
_ => OptimizationAlgorithm::LBFGS,
};
}
if characteristics.condition_number < 100.0 && characteristics.numerical_precision > 0.1 {
return OptimizationAlgorithm::LBFGS;
}
if characteristics.condition_number > 1000.0 {
return OptimizationAlgorithm::Adam;
}
if characteristics.size < 10_000 {
OptimizationAlgorithm::MomentumGradientDescent
} else {
OptimizationAlgorithm::Adam
}
}
pub fn record_performance(&mut self, algorithm: &OptimizationAlgorithm, performance: f32) {
let key = format!("{:?}", algorithm);
self.performance_history
.entry(key)
.or_insert_with(Vec::new)
.push(performance);
if let Some(history) = self
.performance_history
.get_mut(&format!("{:?}", algorithm))
{
if history.len() > 100 {
history.drain(0..history.len() - 100);
}
}
}
pub fn get_algorithm_score(&self, algorithm: &OptimizationAlgorithm) -> f32 {
let key = format!("{:?}", algorithm);
if let Some(history) = self.performance_history.get(&key) {
if !history.is_empty() {
history.iter().sum::<f32>() / history.len() as f32
} else {
0.0
}
} else {
0.0
}
}
pub fn select_algorithm_adaptive(
&self,
characteristics: &TensorCharacteristics,
) -> OptimizationAlgorithm {
let base_selection = self.select_algorithm(characteristics);
let base_score = self.get_algorithm_score(&base_selection);
if base_score < 0.5 && self.performance_history.len() > 3 {
let alternatives = vec![
OptimizationAlgorithm::Adam,
OptimizationAlgorithm::MomentumGradientDescent,
OptimizationAlgorithm::LBFGS,
];
let best_alternative = alternatives.iter().max_by(|a, b| {
self.get_algorithm_score(a)
.partial_cmp(&self.get_algorithm_score(b))
.unwrap_or(std::cmp::Ordering::Equal)
});
if let Some(best) = best_alternative {
if self.get_algorithm_score(best) > base_score + 0.1 {
return best.clone();
}
}
}
base_selection
}
}
impl Default for AdaptiveAlgorithmSelector {
fn default() -> Self {
Self::new()
}
}
pub fn analyze_optimization_problem(
objective_values: &[f32],
gradient_norms: &[f32],
tensor_characteristics: &TensorCharacteristics,
) -> (OptimizationAlgorithm, String) {
let mut recommendations = Vec::new();
if objective_values.len() > 5 {
let recent_improvement = objective_values
.windows(2)
.rev()
.take(5)
.map(|w| w[0] - w[1])
.sum::<f32>()
/ 5.0;
if recent_improvement < 1e-8 {
recommendations.push(
"Problem appears to have slow convergence - consider Adam or momentum methods",
);
}
}
if gradient_norms.len() > 3 {
let gradient_variance = {
let mean = gradient_norms.iter().sum::<f32>() / gradient_norms.len() as f32;
gradient_norms
.iter()
.map(|&x| (x - mean).powi(2))
.sum::<f32>()
/ gradient_norms.len() as f32
};
if gradient_variance > 1.0 {
recommendations.push("High gradient variance detected - Adam optimizer recommended");
}
}
let selector = AdaptiveAlgorithmSelector::new();
let algorithm = selector.select_algorithm(tensor_characteristics);
let recommendation_text = if recommendations.is_empty() {
format!(
"Recommended algorithm: {:?} based on tensor characteristics",
algorithm
)
} else {
format!(
"Recommended algorithm: {:?}. Additional notes: {}",
algorithm,
recommendations.join("; ")
)
};
(algorithm, recommendation_text)
}
pub fn auto_configure_optimization(
characteristics: &TensorCharacteristics,
algorithm: &OptimizationAlgorithm,
) -> TorshResult<String> {
let config = match algorithm {
OptimizationAlgorithm::GradientDescent => {
let lr = if characteristics.condition_number > 100.0 {
0.001 } else {
0.01 };
format!(
"GradientDescentParams {{ learning_rate: {}, max_iter: {}, tolerance: {} }}",
lr, 1000, 1e-6
)
}
OptimizationAlgorithm::MomentumGradientDescent => {
let lr = if characteristics.size > 100_000 {
0.001
} else {
0.01
};
let momentum = if characteristics.condition_number > 100.0 {
0.95
} else {
0.9
};
format!(
"MomentumParams {{ learning_rate: {}, momentum: {}, max_iter: {}, tolerance: {} }}",
lr, momentum, 1000, 1e-6
)
}
OptimizationAlgorithm::Adam => {
let lr = if characteristics.sparsity > 0.5 {
0.001
} else {
0.01
};
format!("AdamParams {{ learning_rate: {}, beta1: 0.9, beta2: 0.999, eps: 1e-8, max_iter: {}, tolerance: {} }}",
lr, 2000, 1e-6)
}
OptimizationAlgorithm::LBFGS => {
let m = if characteristics.size > 10_000 { 5 } else { 10 };
format!(
"BFGSParams {{ memory_size: {}, max_iter: {}, tolerance: {} }}",
m, 500, 1e-6
)
}
OptimizationAlgorithm::ConjugateGradient => {
format!(
"ConjugateGradientParams {{ max_iter: {}, tolerance: {}, restart_frequency: {} }}",
1000,
1e-6,
characteristics.size.min(50)
)
}
};
Ok(config)
}