use crate::automl::pipeline::QuantumMLPipeline;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct SearchHistory {
trials: Vec<SearchTrial>,
start_time: Option<Instant>,
best_performance: Option<f64>,
trials_without_improvement: usize,
statistics: SearchStatistics,
}
#[derive(Debug, Clone)]
pub struct SearchTrial {
pub trial_id: usize,
pub pipeline_config: PipelineConfig,
pub performance: f64,
pub duration: Duration,
pub resource_usage: TrialResourceUsage,
}
#[derive(Debug, Clone)]
pub struct PipelineConfig {
pub model_type: String,
pub hyperparameters: std::collections::HashMap<String, f64>,
pub architecture_summary: String,
pub preprocessing_steps: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct TrialResourceUsage {
pub memory_mb: f64,
pub quantum_resources: QuantumResourceUsage,
pub training_time: f64,
}
#[derive(Debug, Clone)]
pub struct QuantumResourceUsage {
pub qubits_used: usize,
pub circuit_depth: usize,
pub gate_count: usize,
pub coherence_time_used: f64,
}
#[derive(Debug, Clone)]
pub struct SearchStatistics {
pub total_trials: usize,
pub total_time: Duration,
pub average_performance: f64,
pub performance_std: f64,
pub best_trial_id: Option<usize>,
pub convergence_rate: f64,
}
impl SearchHistory {
pub fn new() -> Self {
Self {
trials: Vec::new(),
start_time: None,
best_performance: None,
trials_without_improvement: 0,
statistics: SearchStatistics::new(),
}
}
pub fn start_search(&mut self) {
self.start_time = Some(Instant::now());
self.trials.clear();
self.best_performance = None;
self.trials_without_improvement = 0;
self.statistics = SearchStatistics::new();
}
pub fn record_trial(
&mut self,
trial_id: usize,
pipeline: &QuantumMLPipeline,
performance: f64,
) {
let trial_start = Instant::now();
let trial = SearchTrial {
trial_id,
pipeline_config: PipelineConfig::from_pipeline(pipeline),
performance,
duration: trial_start.elapsed(),
resource_usage: TrialResourceUsage::from_pipeline(pipeline),
};
let is_improvement = match self.best_performance {
Some(best) => performance > best,
None => true,
};
if is_improvement {
self.best_performance = Some(performance);
self.trials_without_improvement = 0;
self.statistics.best_trial_id = Some(trial_id);
} else {
self.trials_without_improvement += 1;
}
self.trials.push(trial);
self.update_statistics();
}
pub fn elapsed_time(&self) -> Option<f64> {
self.start_time.map(|start| start.elapsed().as_secs_f64())
}
pub fn trials_without_improvement(&self) -> usize {
self.trials_without_improvement
}
pub fn best_performance(&self) -> Option<f64> {
self.best_performance
}
pub fn trials(&self) -> &[SearchTrial] {
&self.trials
}
pub fn statistics(&self) -> &SearchStatistics {
&self.statistics
}
pub fn best_trial(&self) -> Option<&SearchTrial> {
self.statistics
.best_trial_id
.and_then(|id| self.trials.iter().find(|t| t.trial_id == id))
}
pub fn performance_history(&self) -> Vec<f64> {
self.trials.iter().map(|t| t.performance).collect()
}
pub fn convergence_curve(&self) -> Vec<f64> {
let mut best_so_far = f64::NEG_INFINITY;
self.trials
.iter()
.map(|t| {
if t.performance > best_so_far {
best_so_far = t.performance;
}
best_so_far
})
.collect()
}
fn update_statistics(&mut self) {
let performances: Vec<f64> = self.trials.iter().map(|t| t.performance).collect();
self.statistics.total_trials = self.trials.len();
self.statistics.total_time = self
.start_time
.map(|start| start.elapsed())
.unwrap_or(Duration::ZERO);
if !performances.is_empty() {
self.statistics.average_performance =
performances.iter().sum::<f64>() / performances.len() as f64;
let variance = performances
.iter()
.map(|&p| (p - self.statistics.average_performance).powi(2))
.sum::<f64>()
/ performances.len() as f64;
self.statistics.performance_std = variance.sqrt();
if performances.len() >= 10 {
let recent_best = performances
.iter()
.rev()
.take(10)
.fold(f64::NEG_INFINITY, |a, &b| a.max(b));
let early_best = performances
.iter()
.take(10)
.fold(f64::NEG_INFINITY, |a, &b| a.max(b));
self.statistics.convergence_rate = (recent_best - early_best) / 10.0;
}
}
}
}
impl PipelineConfig {
fn from_pipeline(pipeline: &QuantumMLPipeline) -> Self {
Self {
model_type: "QuantumNeuralNetwork".to_string(), hyperparameters: std::collections::HashMap::new(), architecture_summary: "4-qubit variational circuit".to_string(), preprocessing_steps: vec!["StandardScaler".to_string(), "AngleEncoding".to_string()],
}
}
}
impl TrialResourceUsage {
fn from_pipeline(pipeline: &QuantumMLPipeline) -> Self {
Self {
memory_mb: 256.0, quantum_resources: QuantumResourceUsage {
qubits_used: 4, circuit_depth: 6, gate_count: 24, coherence_time_used: 50.0, },
training_time: 120.0, }
}
}
impl SearchStatistics {
fn new() -> Self {
Self {
total_trials: 0,
total_time: Duration::ZERO,
average_performance: 0.0,
performance_std: 0.0,
best_trial_id: None,
convergence_rate: 0.0,
}
}
}
impl Default for SearchHistory {
fn default() -> Self {
Self::new()
}
}