1use scirs2_core::RngExt;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::fmt;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Architecture {
15 pub id: String,
17
18 pub components: Vec<OptimizerComponent>,
20
21 pub connections: Vec<Connection>,
23
24 pub hyperparameters: HashMap<String, f64>,
26
27 pub performance: Option<PerformanceMetrics>,
29
30 pub generation: u32,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct OptimizerComponent {
37 pub id: String,
39
40 pub component_type: ComponentType,
42
43 pub hyperparameters: HashMap<String, f64>,
45
46 pub position: ComponentPosition,
48
49 pub enabled: bool,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
55pub enum ComponentType {
56 SGD,
58 Adam,
60 AdamW,
62 RMSprop,
64 AdaGrad,
66 AdaDelta,
68 Momentum,
70 Nesterov,
72 LRScheduler,
74 GradientClipping,
76 BatchNorm,
78 Dropout,
80 LAMB,
82 LARS,
84 Lion,
86 RAdam,
88 Lookahead,
90 SAM,
92 LBFGS,
94 SparseAdam,
96 GroupedAdam,
98 MAML,
100 L1Regularizer,
102 L2Regularizer,
104 ElasticNetRegularizer,
106 DropoutRegularizer,
108 WeightDecay,
110 AdaptiveLR,
112 AdaptiveMomentum,
114 AdaptiveRegularization,
116 LSTMOptimizer,
118 TransformerOptimizer,
120 AttentionOptimizer,
122 MetaSGD,
124 ConstantLR,
126 ExponentialLR,
128 StepLR,
130 CosineAnnealingLR,
132 OneCycleLR,
134 CyclicLR,
136 Reptile,
138 Custom,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct ComponentPosition {
145 pub layer: u32,
147 pub index: u32,
149 pub x: f32,
151 pub y: f32,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Connection {
157 pub from: String,
159 pub to: String,
161 pub weight: f64,
163 pub connection_type: ConnectionType,
165 pub enabled: bool,
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
171pub enum ConnectionType {
172 Gradient,
174 Parameter,
176 Skip,
178 Residual,
180 Information,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct PerformanceMetrics {
187 pub final_performance: f64,
189 pub convergence_speed: f64,
191 pub stability: f64,
193 pub efficiency: f64,
195 pub memory_usage: f64,
197 pub robustness: f64,
199 pub overall_score: f64,
201}
202
203#[derive(Debug, Clone)]
205pub struct ArchitectureSpace {
206 pub component_types: Vec<ComponentType>,
208
209 pub min_components: usize,
211
212 pub max_components: usize,
214
215 pub max_connections: usize,
217
218 pub allow_cycles: bool,
220
221 pub max_depth: u32,
223
224 pub hyperparameter_ranges: HashMap<ComponentType, HashMap<String, (f64, f64)>>,
226
227 pub component_probabilities: HashMap<ComponentType, f64>,
229
230 pub connection_probability: f64,
232}
233
234impl ArchitectureSpace {
235 pub fn new() -> Self {
237 let component_types = vec![
238 ComponentType::SGD,
239 ComponentType::Adam,
240 ComponentType::AdamW,
241 ComponentType::RMSprop,
242 ComponentType::Momentum,
243 ];
244
245 let mut component_probabilities = HashMap::new();
246 component_probabilities.insert(ComponentType::Adam, 0.3);
247 component_probabilities.insert(ComponentType::AdamW, 0.25);
248 component_probabilities.insert(ComponentType::SGD, 0.2);
249 component_probabilities.insert(ComponentType::RMSprop, 0.15);
250 component_probabilities.insert(ComponentType::Momentum, 0.1);
251
252 let mut hyperparameter_ranges = HashMap::new();
253
254 let mut adam_ranges = HashMap::new();
256 adam_ranges.insert("learning_rate".to_string(), (1e-5, 1e-1));
257 adam_ranges.insert("beta1".to_string(), (0.8, 0.99));
258 adam_ranges.insert("beta2".to_string(), (0.9, 0.9999));
259 adam_ranges.insert("epsilon".to_string(), (1e-8, 1e-6));
260 hyperparameter_ranges.insert(ComponentType::Adam, adam_ranges);
261
262 let mut sgd_ranges = HashMap::new();
264 sgd_ranges.insert("learning_rate".to_string(), (1e-4, 1e-1));
265 sgd_ranges.insert("momentum".to_string(), (0.0, 0.99));
266 hyperparameter_ranges.insert(ComponentType::SGD, sgd_ranges);
267
268 let mut rmsprop_ranges = HashMap::new();
270 rmsprop_ranges.insert("learning_rate".to_string(), (1e-5, 1e-1));
271 rmsprop_ranges.insert("alpha".to_string(), (0.9, 0.999));
272 rmsprop_ranges.insert("epsilon".to_string(), (1e-8, 1e-6));
273 hyperparameter_ranges.insert(ComponentType::RMSprop, rmsprop_ranges);
274
275 Self {
276 component_types,
277 min_components: 1,
278 max_components: 10,
279 max_connections: 20,
280 allow_cycles: false,
281 max_depth: 5,
282 hyperparameter_ranges,
283 component_probabilities,
284 connection_probability: 0.3,
285 }
286 }
287
288 pub fn generate_random_architecture(&self) -> Architecture {
290 let mut rng = scirs2_core::random::Random::default();
291
292 let num_components = rng.gen_range(self.min_components..=self.max_components);
293 let mut components = Vec::new();
294
295 for i in 0..num_components {
296 let component_type = self.component_types[rng.gen_range(0..self.component_types.len())];
297 let mut hyperparameters = HashMap::new();
298
299 if let Some(ranges) = self.hyperparameter_ranges.get(&component_type) {
300 for (param_name, (min_val, max_val)) in ranges {
301 let value = rng.gen_range(*min_val..=*max_val);
302 hyperparameters.insert(param_name.clone(), value);
303 }
304 }
305
306 components.push(OptimizerComponent {
307 id: format!("comp_{}", i),
308 component_type,
309 hyperparameters,
310 position: ComponentPosition {
311 layer: i as u32,
312 index: 0,
313 x: i as f32 * 100.0,
314 y: 0.0,
315 },
316 enabled: true,
317 });
318 }
319
320 let mut connections = Vec::new();
321 let max_connections = self
322 .max_connections
323 .min(num_components * (num_components - 1));
324 let num_connections = rng.gen_range(0..=max_connections);
325
326 for _ in 0..num_connections {
327 if rng.random::<f64>() < self.connection_probability {
328 let from_idx = rng.gen_range(0..num_components);
329 let to_idx = rng.gen_range(0..num_components);
330
331 if !self.allow_cycles {
333 if from_idx < to_idx {
334 connections.push(Connection {
335 from: components[from_idx].id.clone(),
336 to: components[to_idx].id.clone(),
337 weight: rng.gen_range(0.1..=1.0),
338 connection_type: ConnectionType::Gradient,
339 enabled: true,
340 });
341 }
342 } else if from_idx != to_idx {
343 connections.push(Connection {
344 from: components[from_idx].id.clone(),
345 to: components[to_idx].id.clone(),
346 weight: rng.gen_range(0.1..=1.0),
347 connection_type: ConnectionType::Gradient,
348 enabled: true,
349 });
350 }
351 }
352 }
353
354 Architecture {
355 id: format!("arch_{}", rng.random::<u64>()),
356 components,
357 connections,
358 hyperparameters: HashMap::new(),
359 performance: None,
360 generation: 0,
361 }
362 }
363
364 pub fn mutate_architecture(
366 &self,
367 architecture: &Architecture,
368 mutation_rate: f64,
369 ) -> Architecture {
370 let mut rng = scirs2_core::random::Random::default();
371 let mut mutated = architecture.clone();
372 mutated.id = format!("{}_{}_mut", architecture.id, rng.random::<u32>());
373 mutated.generation = architecture.generation + 1;
374 mutated.performance = None; for component in &mut mutated.components {
378 if rng.random::<f64>() < mutation_rate {
379 if let Some(ranges) = self.hyperparameter_ranges.get(&component.component_type) {
380 for (param_name, value) in &mut component.hyperparameters {
381 if let Some((min_val, max_val)) = ranges.get(param_name) {
382 let std_dev = (max_val - min_val) * 0.1;
384 let mutation = rng.random::<f64>() * std_dev - std_dev / 2.0;
385 *value = (*value + mutation).clamp(*min_val, *max_val);
386 }
387 }
388 }
389 }
390 }
391
392 if rng.random::<f64>() < mutation_rate * 0.1 {
394 if mutated.components.len() < self.max_components && rng.random::<bool>() {
395 let component_type =
397 self.component_types[rng.gen_range(0..self.component_types.len())];
398 let mut hyperparameters = HashMap::new();
399
400 if let Some(ranges) = self.hyperparameter_ranges.get(&component_type) {
401 for (param_name, (min_val, max_val)) in ranges {
402 let value = rng.gen_range(*min_val..=*max_val);
403 hyperparameters.insert(param_name.clone(), value);
404 }
405 }
406
407 mutated.components.push(OptimizerComponent {
408 id: format!("comp_{}", mutated.components.len()),
409 component_type,
410 hyperparameters,
411 position: ComponentPosition {
412 layer: mutated.components.len() as u32,
413 index: 0,
414 x: mutated.components.len() as f32 * 100.0,
415 y: 0.0,
416 },
417 enabled: true,
418 });
419 } else if mutated.components.len() > self.min_components {
420 let remove_idx = rng.gen_range(0..mutated.components.len());
422 let removed_id = mutated.components[remove_idx].id.clone();
423 mutated.components.remove(remove_idx);
424
425 mutated
427 .connections
428 .retain(|conn| conn.from != removed_id && conn.to != removed_id);
429 }
430 }
431
432 mutated
433 }
434
435 pub fn crossover_architectures(
437 &self,
438 parent1: &Architecture,
439 parent2: &Architecture,
440 ) -> (Architecture, Architecture) {
441 let mut rng = scirs2_core::random::Random::default();
442
443 let mut child1 = parent1.clone();
444 let mut child2 = parent2.clone();
445
446 child1.id = format!("{}_{}_cross", parent1.id, rng.random::<u32>());
447 child2.id = format!("{}_{}_cross", parent2.id, rng.random::<u32>());
448 child1.generation = parent1.generation.max(parent2.generation) + 1;
449 child2.generation = parent1.generation.max(parent2.generation) + 1;
450 child1.performance = None;
451 child2.performance = None;
452
453 if !parent1.components.is_empty() && !parent2.components.is_empty() {
455 let crossover_point1 = rng.gen_range(0..parent1.components.len());
456 let crossover_point2 = rng.gen_range(0..parent2.components.len());
457
458 let mut new_components1 = parent1.components[..crossover_point1].to_vec();
459 new_components1.extend_from_slice(&parent2.components[crossover_point2..]);
460
461 let mut new_components2 = parent2.components[..crossover_point2].to_vec();
462 new_components2.extend_from_slice(&parent1.components[crossover_point1..]);
463
464 if new_components1.len() > self.max_components {
466 new_components1.truncate(self.max_components);
467 } else if new_components1.len() < self.min_components {
468 while new_components1.len() < self.min_components {
470 let source = if rng.random_bool() {
471 &parent1.components
472 } else {
473 &parent2.components
474 };
475 if !source.is_empty() {
476 let idx = rng.gen_range(0..source.len());
477 new_components1.push(source[idx].clone());
478 } else {
479 break;
480 }
481 }
482 }
483
484 if new_components2.len() > self.max_components {
485 new_components2.truncate(self.max_components);
486 } else if new_components2.len() < self.min_components {
487 while new_components2.len() < self.min_components {
489 let source = if rng.random_bool() {
490 &parent1.components
491 } else {
492 &parent2.components
493 };
494 if !source.is_empty() {
495 let idx = rng.gen_range(0..source.len());
496 new_components2.push(source[idx].clone());
497 } else {
498 break;
499 }
500 }
501 }
502
503 child1.components = new_components1;
504 child2.components = new_components2;
505
506 let child1_ids: std::collections::HashSet<_> =
508 child1.components.iter().map(|c| c.id.clone()).collect();
509 let child2_ids: std::collections::HashSet<_> =
510 child2.components.iter().map(|c| c.id.clone()).collect();
511
512 child1
513 .connections
514 .retain(|conn| child1_ids.contains(&conn.from) && child1_ids.contains(&conn.to));
515
516 child2
517 .connections
518 .retain(|conn| child2_ids.contains(&conn.from) && child2_ids.contains(&conn.to));
519 }
520
521 (child1, child2)
522 }
523
524 pub fn validate_architecture(&self, architecture: &Architecture) -> Result<(), String> {
526 if architecture.components.len() < self.min_components {
527 return Err(format!(
528 "Too few components: {} < {}",
529 architecture.components.len(),
530 self.min_components
531 ));
532 }
533
534 if architecture.components.len() > self.max_components {
535 return Err(format!(
536 "Too many components: {} > {}",
537 architecture.components.len(),
538 self.max_components
539 ));
540 }
541
542 if architecture.connections.len() > self.max_connections {
543 return Err(format!(
544 "Too many connections: {} > {}",
545 architecture.connections.len(),
546 self.max_connections
547 ));
548 }
549
550 if !self.allow_cycles && self.has_cycles(architecture) {
552 return Err("Architecture contains cycles, but cycles are not allowed".to_string());
553 }
554
555 for component in &architecture.components {
557 if let Some(ranges) = self.hyperparameter_ranges.get(&component.component_type) {
558 for (param_name, value) in &component.hyperparameters {
559 if let Some((min_val, max_val)) = ranges.get(param_name) {
560 if *value < *min_val || *value > *max_val {
561 return Err(format!(
562 "Hyperparameter {} = {} is out of range [{}, {}]",
563 param_name, value, min_val, max_val
564 ));
565 }
566 }
567 }
568 }
569 }
570
571 Ok(())
572 }
573
574 fn has_cycles(&self, architecture: &Architecture) -> bool {
576 let mut visited = std::collections::HashSet::new();
577 let mut rec_stack = std::collections::HashSet::new();
578
579 for component in &architecture.components {
580 if !visited.contains(&component.id)
581 && Self::has_cycle_util(architecture, &component.id, &mut visited, &mut rec_stack)
582 {
583 return true;
584 }
585 }
586
587 false
588 }
589
590 fn has_cycle_util(
591 architecture: &Architecture,
592 node: &str,
593 visited: &mut std::collections::HashSet<String>,
594 rec_stack: &mut std::collections::HashSet<String>,
595 ) -> bool {
596 visited.insert(node.to_string());
597 rec_stack.insert(node.to_string());
598
599 for connection in &architecture.connections {
601 if connection.from == node && connection.enabled {
602 if !visited.contains(&connection.to) {
603 if Self::has_cycle_util(architecture, &connection.to, visited, rec_stack) {
604 return true;
605 }
606 } else if rec_stack.contains(&connection.to) {
607 return true;
608 }
609 }
610 }
611
612 rec_stack.remove(node);
613 false
614 }
615}
616
617impl Default for ArchitectureSpace {
618 fn default() -> Self {
619 Self::new()
620 }
621}
622
623impl fmt::Display for Architecture {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 writeln!(f, "Architecture: {}", self.id)?;
626 writeln!(f, " Components: {}", self.components.len())?;
627 writeln!(f, " Connections: {}", self.connections.len())?;
628 writeln!(f, " Generation: {}", self.generation)?;
629
630 if let Some(ref perf) = self.performance {
631 writeln!(f, " Performance: {:.4}", perf.overall_score)?;
632 }
633
634 for component in &self.components {
635 writeln!(
636 f,
637 " - {:?}: {} params",
638 component.component_type,
639 component.hyperparameters.len()
640 )?;
641 }
642
643 Ok(())
644 }
645}
646
647impl fmt::Display for ComponentType {
648 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649 match self {
650 ComponentType::SGD => write!(f, "SGD"),
651 ComponentType::Adam => write!(f, "Adam"),
652 ComponentType::AdamW => write!(f, "AdamW"),
653 ComponentType::RMSprop => write!(f, "RMSprop"),
654 ComponentType::AdaGrad => write!(f, "AdaGrad"),
655 ComponentType::AdaDelta => write!(f, "AdaDelta"),
656 ComponentType::Momentum => write!(f, "Momentum"),
657 ComponentType::Nesterov => write!(f, "Nesterov"),
658 ComponentType::LRScheduler => write!(f, "LRScheduler"),
659 ComponentType::GradientClipping => write!(f, "GradientClipping"),
660 ComponentType::BatchNorm => write!(f, "BatchNorm"),
661 ComponentType::Dropout => write!(f, "Dropout"),
662 ComponentType::LAMB => write!(f, "LAMB"),
663 ComponentType::LARS => write!(f, "LARS"),
664 ComponentType::Lion => write!(f, "Lion"),
665 ComponentType::RAdam => write!(f, "RAdam"),
666 ComponentType::Lookahead => write!(f, "Lookahead"),
667 ComponentType::SAM => write!(f, "SAM"),
668 ComponentType::LBFGS => write!(f, "LBFGS"),
669 ComponentType::SparseAdam => write!(f, "SparseAdam"),
670 ComponentType::GroupedAdam => write!(f, "GroupedAdam"),
671 ComponentType::MAML => write!(f, "MAML"),
672 ComponentType::L1Regularizer => write!(f, "L1Regularizer"),
673 ComponentType::L2Regularizer => write!(f, "L2Regularizer"),
674 ComponentType::ElasticNetRegularizer => write!(f, "ElasticNetRegularizer"),
675 ComponentType::DropoutRegularizer => write!(f, "DropoutRegularizer"),
676 ComponentType::WeightDecay => write!(f, "WeightDecay"),
677 ComponentType::AdaptiveLR => write!(f, "AdaptiveLR"),
678 ComponentType::AdaptiveMomentum => write!(f, "AdaptiveMomentum"),
679 ComponentType::AdaptiveRegularization => write!(f, "AdaptiveRegularization"),
680 ComponentType::LSTMOptimizer => write!(f, "LSTMOptimizer"),
681 ComponentType::TransformerOptimizer => write!(f, "TransformerOptimizer"),
682 ComponentType::AttentionOptimizer => write!(f, "AttentionOptimizer"),
683 ComponentType::MetaSGD => write!(f, "MetaSGD"),
684 ComponentType::ConstantLR => write!(f, "ConstantLR"),
685 ComponentType::ExponentialLR => write!(f, "ExponentialLR"),
686 ComponentType::StepLR => write!(f, "StepLR"),
687 ComponentType::CosineAnnealingLR => write!(f, "CosineAnnealingLR"),
688 ComponentType::OneCycleLR => write!(f, "OneCycleLR"),
689 ComponentType::CyclicLR => write!(f, "CyclicLR"),
690 ComponentType::Reptile => write!(f, "Reptile"),
691 ComponentType::Custom => write!(f, "Custom"),
692 }
693 }
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699
700 #[test]
701 fn test_architecture_space_creation() {
702 let space = ArchitectureSpace::new();
703 assert!(space.min_components <= space.max_components);
704 assert!(!space.component_types.is_empty());
705 assert!(space.connection_probability >= 0.0 && space.connection_probability <= 1.0);
706 }
707
708 #[test]
709 fn test_random_architecture_generation() {
710 let space = ArchitectureSpace::new();
711 let arch = space.generate_random_architecture();
712
713 assert!(arch.components.len() >= space.min_components);
714 assert!(arch.components.len() <= space.max_components);
715 assert!(space.validate_architecture(&arch).is_ok());
716 }
717
718 #[test]
719 fn test_architecture_mutation() {
720 let space = ArchitectureSpace::new();
721 let original = space.generate_random_architecture();
722 let mutated = space.mutate_architecture(&original, 0.5);
723
724 assert_ne!(original.id, mutated.id);
725 assert_eq!(mutated.generation, original.generation + 1);
726
727 if let Err(e) = space.validate_architecture(&mutated) {
729 eprintln!("Validation failed: {}", e);
730 eprintln!("Original components: {}", original.components.len());
731 eprintln!("Mutated components: {}", mutated.components.len());
732 eprintln!("Min components: {}", space.min_components);
733 eprintln!("Max components: {}", space.max_components);
734 }
735
736 assert!(space.validate_architecture(&mutated).is_ok());
737 }
738
739 #[test]
740 fn test_architecture_crossover() {
741 let space = ArchitectureSpace::new();
742 let parent1 = space.generate_random_architecture();
743 let parent2 = space.generate_random_architecture();
744
745 let (child1, child2) = space.crossover_architectures(&parent1, &parent2);
746
747 assert_ne!(child1.id, parent1.id);
748 assert_ne!(child2.id, parent2.id);
749 assert!(space.validate_architecture(&child1).is_ok());
750 assert!(space.validate_architecture(&child2).is_ok());
751 }
752
753 #[test]
754 fn test_architecture_validation() {
755 let space = ArchitectureSpace::new();
756 let valid_arch = space.generate_random_architecture();
757 assert!(space.validate_architecture(&valid_arch).is_ok());
758
759 let mut invalid_arch = valid_arch.clone();
761 for i in 0..space.max_components + 5 {
762 invalid_arch.components.push(OptimizerComponent {
763 id: format!("invalid_{}", i),
764 component_type: ComponentType::Adam,
765 hyperparameters: HashMap::new(),
766 position: ComponentPosition {
767 layer: i as u32,
768 index: 0,
769 x: 0.0,
770 y: 0.0,
771 },
772 enabled: true,
773 });
774 }
775
776 assert!(space.validate_architecture(&invalid_arch).is_err());
777 }
778}