use std::collections::HashMap;
#[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>,
}
impl NASConfig {
pub fn new() -> Self {
Self {
search_strategy: SearchStrategyType::Evolutionary,
max_iterations: 100,
population_size: 50,
elite_size: 10,
mutation_rate: 0.1,
crossover_rate: 0.8,
early_stopping_patience: 20,
evaluation_budget: 1000,
objective_weights: vec![1.0, 0.5, 0.3], enable_performance_prediction: true,
progressive_search: false,
constraints: SearchConstraints::default(),
parallelization_level: 4,
enable_transfer_learning: false,
warm_start_architectures: Vec::new(),
}
}
pub fn quick_search() -> Self {
Self {
max_iterations: 20,
population_size: 10,
elite_size: 3,
evaluation_budget: 100,
early_stopping_patience: 10,
..Self::new()
}
}
pub fn thorough_search() -> Self {
Self {
max_iterations: 500,
population_size: 100,
elite_size: 20,
evaluation_budget: 5000,
early_stopping_patience: 50,
parallelization_level: 8,
..Self::new()
}
}
pub fn validate(&self) -> Result<(), String> {
if self.max_iterations == 0 {
return Err("max_iterations must be greater than 0".to_string());
}
if self.population_size == 0 {
return Err("population_size must be greater than 0".to_string());
}
if self.elite_size > self.population_size {
return Err("elite_size cannot be larger than population_size".to_string());
}
if self.mutation_rate < 0.0 || self.mutation_rate > 1.0 {
return Err("mutation_rate must be between 0.0 and 1.0".to_string());
}
if self.crossover_rate < 0.0 || self.crossover_rate > 1.0 {
return Err("crossover_rate must be between 0.0 and 1.0".to_string());
}
if self.evaluation_budget == 0 {
return Err("evaluation_budget must be greater than 0".to_string());
}
if self.objective_weights.is_empty() {
return Err("objective_weights cannot be empty".to_string());
}
if self.parallelization_level == 0 {
return Err("parallelization_level must be greater than 0".to_string());
}
self.constraints.validate()?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SearchStrategyType {
Random,
Evolutionary,
Bayesian,
ReinforcementLearning,
GradientBased,
Progressive,
Hybrid,
}
impl Default for SearchStrategyType {
fn default() -> Self {
SearchStrategyType::Evolutionary
}
}
#[derive(Debug, Clone)]
pub struct SearchConstraints {
pub max_layers: usize,
pub min_layers: usize,
pub max_params_per_layer: usize,
pub allowed_layer_types: Vec<LayerType>,
pub max_memory_mb: usize,
pub max_flops: u64,
pub min_accuracy: f64,
pub max_training_time_secs: u64,
}
impl Default for SearchConstraints {
fn default() -> Self {
Self {
max_layers: 20,
min_layers: 2,
max_params_per_layer: 1000000,
allowed_layer_types: vec![
LayerType::Dense,
LayerType::Conv2D,
LayerType::LSTM,
LayerType::Attention,
LayerType::Residual,
],
max_memory_mb: 1024,
max_flops: 1_000_000_000,
min_accuracy: 0.8,
max_training_time_secs: 3600,
}
}
}
impl SearchConstraints {
pub fn validate(&self) -> Result<(), String> {
if self.min_layers > self.max_layers {
return Err("min_layers cannot be greater than max_layers".to_string());
}
if self.max_layers == 0 {
return Err("max_layers must be greater than 0".to_string());
}
if self.max_params_per_layer == 0 {
return Err("max_params_per_layer must be greater than 0".to_string());
}
if self.allowed_layer_types.is_empty() {
return Err("allowed_layer_types cannot be empty".to_string());
}
if self.min_accuracy < 0.0 || self.min_accuracy > 1.0 {
return Err("min_accuracy must be between 0.0 and 1.0".to_string());
}
Ok(())
}
pub fn satisfies(&self, architecture: &ArchitectureSpec) -> bool {
if architecture.layers.len() < self.min_layers || architecture.layers.len() > self.max_layers {
return false;
}
for layer in &architecture.layers {
if !self.allowed_layer_types.contains(&layer.layer_type) {
return false;
}
if layer.params > self.max_params_per_layer {
return false;
}
}
if architecture.estimated_memory_mb > self.max_memory_mb {
return false;
}
if architecture.estimated_flops > self.max_flops {
return false;
}
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum LayerType {
Dense,
Conv2D,
Conv1D,
LSTM,
GRU,
Attention,
MultiHeadAttention,
Transformer,
Residual,
BatchNorm,
Dropout,
Activation,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ArchitectureSpec {
pub layers: Vec<LayerSpec>,
pub estimated_memory_mb: usize,
pub estimated_flops: u64,
pub estimated_params: usize,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LayerSpec {
pub layer_type: LayerType,
pub params: usize,
pub config: HashMap<String, serde_json::Value>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nas_config_creation() {
let config = NASConfig::new();
assert!(config.validate().is_ok());
assert_eq!(config.search_strategy, SearchStrategyType::Evolutionary);
}
#[test]
fn test_config_validation() {
let mut config = NASConfig::new();
config.population_size = 0;
assert!(config.validate().is_err());
config.population_size = 10;
config.elite_size = 20;
assert!(config.validate().is_err());
}
#[test]
fn test_search_constraints() {
let constraints = SearchConstraints::default();
assert!(constraints.validate().is_ok());
let mut invalid_constraints = constraints.clone();
invalid_constraints.min_layers = 10;
invalid_constraints.max_layers = 5;
assert!(invalid_constraints.validate().is_err());
}
#[test]
fn test_quick_search_config() {
let config = NASConfig::quick_search();
assert!(config.validate().is_ok());
assert_eq!(config.max_iterations, 20);
assert_eq!(config.population_size, 10);
}
}