use std::fmt::Debug;
pub mod architecture;
pub mod search;
pub mod evaluation;
pub mod space;
pub mod population;
pub mod history;
use std::collections::HashMap;
use scirs2_core::numeric::Float;
pub use architecture::*;
pub use search::*;
pub use evaluation::*;
pub use space::*;
pub use population::*;
pub use history::*;
pub struct NeuralArchitectureSearch<T: Float + Debug + Send + Sync + 'static> {
config: NASConfig,
search_space: ArchitectureSearchSpace,
search_strategy: SearchStrategy<T>,
evaluator: ArchitectureEvaluator,
population_manager: PopulationManager<T>,
performance_predictor: Option<PerformancePredictor>,
search_history: SearchHistory<T>,
resource_manager: ResourceManager,
multi_objective_optimizer: Option<MultiObjectiveOptimizer<T>>,
}
#[derive(Debug, Clone, Default)]
pub struct NASConfig {
pub search_strategy: SearchStrategyType,
pub max_iterations: usize,
pub population_size: usize,
pub elite_size: usize,
pub mutation_rate: f64,
pub crossover_rate: f64,
pub early_stopping_patience: usize,
pub evaluation_budget: usize,
pub objective_weights: Vec<f64>,
pub enable_performance_prediction: bool,
pub progressive_search: bool,
pub constraints: SearchConstraints,
pub parallelization_level: usize,
pub enable_transfer_learning: bool,
pub warm_start_architectures: Vec<String>,
}
pub struct ResourceManager {
pub compute_budget: f64,
pub compute_used: f64,
pub memory_budget: f64,
pub memory_used: f64,
pub time_budget: f64,
pub time_used: f64,
pub utilization_history: Vec<ResourceUtilization>,
}
#[derive(Debug, Clone)]
pub struct ResourceUtilization {
pub timestamp: std::time::Instant,
pub compute_util: f64,
pub memory_util: f64,
pub operation: String,
}
pub struct MultiObjectiveOptimizer<T: Float + Debug + Send + Sync + 'static> {
pub objectives: Vec<ObjectiveFunction<T>>,
pub pareto_front: Vec<ArchitectureCandidate>,
pub algorithm: MultiObjectiveAlgorithm,
pub hypervolume_history: Vec<f64>,
}
pub struct ObjectiveFunction<T: Float + Debug + Send + Sync + 'static> {
pub name: String,
pub function: Box<dyn Fn(&ArchitectureCandidate) -> T>,
pub weight: f64,
pub maximize: bool,
}
impl<T: Float + Debug + Default + std::fmt::Debug + Clone + From<f64 + Send + Sync> + Into<f64>> NeuralArchitectureSearch<T> {
pub fn new(config: NASConfig) -> Self {
let search_space = ArchitectureSearchSpace::default();
let search_strategy = SearchStrategy::new(config.search_strategy, SearchConfig {
budget: config.evaluation_budget,
population_size: config.population_size,
max_iterations: config.max_iterations,
convergence_threshold: 0.001,
early_stopping_patience: config.early_stopping_patience,
random_seed: None,
strategy_params: HashMap::new(),
});
let evaluator = ArchitectureEvaluator {
config: EvaluationConfig::default(),
predictor: if config.enable_performance_prediction {
Some(PerformancePredictor {
predictor_type: PredictorType::NeuralNetwork,
training_data: Vec::new(),
accuracy: 0.0,
})
} else {
None
},
evaluation_history: Vec::new(),
};
let population_manager = PopulationManager::new(PopulationConfig {
size: config.population_size,
elite_size: config.elite_size,
maintain_diversity: true,
min_diversity: 0.1,
max_generations: config.max_iterations,
});
let search_history = SearchHistory::new(HistoryConfig::default());
Self {
config,
search_space,
search_strategy,
evaluator,
population_manager,
performance_predictor: None,
search_history,
resource_manager: ResourceManager::new(),
multi_objective_optimizer: None,
}
}
pub fn search(&mut self) -> Result<Vec<ArchitectureCandidate>, NASError> {
self.initialize_search()?;
for iteration in 0..self.config.max_iterations {
if !self.resource_manager.has_resources() {
break;
}
let candidate = self.search_strategy.generate_candidate()?;
let performance = self.evaluate_candidate(&candidate)?;
self.search_strategy.update_with_result(&candidate, performance);
self.search_history.add_record(
candidate,
performance,
iteration,
format!("{:?}", self.config.search_strategy),
);
if self.search_history.stats.convergence.converged {
break;
}
if self.should_early_stop() {
break;
}
}
Ok(self.search_history.best_architectures.clone())
}
fn initialize_search(&mut self) -> Result<(), NASError> {
if matches!(self.config.search_strategy, SearchStrategyType::Evolutionary) {
self.population_manager.initialize(&self.search_space);
}
if self.config.enable_transfer_learning && !self.config.warm_start_architectures.is_empty() {
self.warm_start()?;
}
Ok(())
}
fn evaluate_candidate(&mut self, candidate: &ArchitectureCandidate) -> Result<T, NASError> {
let start_time = std::time::Instant::now();
if let Some(ref predictor) = self.evaluator.predictor {
if self.config.enable_performance_prediction && predictor.accuracy > 0.8 {
return self.predict_performance(candidate);
}
}
let performance = self.full_evaluation(candidate)?;
let eval_record = EvaluationRecord {
architecture_id: candidate.id.clone(),
performance: PerformanceMetrics {
optimization_performance: performance.into(),
convergence_speed: 0.8,
generalization: 0.7,
robustness: 0.6,
transfer_performance: 0.5,
multitask_performance: 0.4,
stability: 0.9,
},
resource_usage: ResourceUsage::default(),
evaluation_time: start_time.elapsed(),
evaluation_type: EvaluationType::Full,
};
self.evaluator.evaluation_history.push(eval_record);
self.resource_manager.update_usage(start_time.elapsed().as_secs_f64(), "evaluation");
Ok(performance)
}
fn predict_performance(&self, _candidate: &ArchitectureCandidate) -> Result<T, NASError> {
Ok(scirs2_core::numeric::NumCast::from(0.7).unwrap_or_else(|| T::zero())) }
fn full_evaluation(&self, candidate: &ArchitectureCandidate) -> Result<T, NASError> {
let param_count = candidate.architecture.parameter_count();
let layer_count = candidate.architecture.layers.len();
let capacity_score = (param_count as f64 / 1000000.0).min(1.0); let efficiency_score = 1.0 / (layer_count as f64 / 10.0 + 1.0);
let performance = 0.3 * capacity_score + 0.7 * efficiency_score;
Ok(scirs2_core::numeric::NumCast::from(performance).unwrap_or_else(|| T::zero()))
}
fn should_early_stop(&self) -> bool {
if self.search_history.stats.convergence.converged {
return true;
}
if !self.resource_manager.has_resources() {
return true;
}
let stagnation_limit = self.config.early_stopping_patience;
if self.search_history.stats.convergence.stagnation_count >= stagnation_limit {
return true;
}
false
}
fn warm_start(&mut self) -> Result<(), NASError> {
for arch_id in &self.config.warm_start_architectures {
let arch = self.search_space.sample_random(); let candidate = ArchitectureCandidate::new(arch_id.clone(), arch);
let performance = self.evaluate_candidate(&candidate)?;
self.search_history.add_record(
candidate,
performance,
0,
"warm_start".to_string(),
);
}
Ok(())
}
pub fn get_statistics(&self) -> SearchSummary<T> {
self.search_history.get_summary()
}
pub fn get_best_architectures(&self, n: usize) -> Vec<&ArchitectureCandidate> {
self.search_history.best_architectures.iter().take(n).collect()
}
}
impl ResourceManager {
pub fn new() -> Self {
Self {
compute_budget: 1000.0, compute_used: 0.0,
memory_budget: 32.0, memory_used: 0.0,
time_budget: 3600.0, time_used: 0.0,
utilization_history: Vec::new(),
}
}
pub fn has_resources(&self) -> bool {
self.compute_used < self.compute_budget
&& self.memory_used < self.memory_budget
&& self.time_used < self.time_budget
}
pub fn update_usage(&mut self, time_delta: f64, operation: &str) {
self.time_used += time_delta;
self.compute_used += time_delta * 0.1; self.memory_used += 0.1;
self.utilization_history.push(ResourceUtilization {
timestamp: std::time::Instant::now(),
compute_util: self.compute_used / self.compute_budget,
memory_util: self.memory_used / self.memory_budget,
operation: operation.to_string(),
});
}
}
#[derive(Debug)]
pub enum NASError {
SearchError(SearchError),
EvaluationError(String),
ResourceError(String),
ConfigurationError(String),
InitializationError(String),
}
impl From<SearchError> for NASError {
fn from(err: SearchError) -> Self {
NASError::SearchError(err)
}
}
impl std::fmt::Display for NASError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NASError::SearchError(err) => write!(f, "Search error: {}", err),
NASError::EvaluationError(msg) => write!(f, "Evaluation error: {}", msg),
NASError::ResourceError(msg) => write!(f, "Resource error: {}", msg),
NASError::ConfigurationError(msg) => write!(f, "Configuration error: {}", msg),
NASError::InitializationError(msg) => write!(f, "Initialization error: {}", msg),
}
}
}
impl std::error::Error for NASError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nas_creation() {
let config = NASConfig::default();
let nas: NeuralArchitectureSearch<f64> = NeuralArchitectureSearch::new(config);
assert_eq!(nas.config.search_strategy, SearchStrategyType::Random);
assert!(!nas.search_history.records.is_empty() == false);
}
#[test]
fn test_resource_manager() {
let mut manager = ResourceManager::new();
assert!(manager.has_resources());
manager.update_usage(100.0, "test");
assert!(manager.time_used > 0.0);
}
#[test]
fn test_search_initialization() {
let config = NASConfig {
search_strategy: SearchStrategyType::Evolutionary,
population_size: 20,
..Default::default()
};
let mut nas: NeuralArchitectureSearch<f64> = NeuralArchitectureSearch::new(config);
let result = nas.initialize_search();
assert!(result.is_ok());
}
}