use std::collections::HashMap;
use scirs2_core::ndarray::Array2;
use super::components::*;
#[derive(Debug, Clone)]
pub struct ArchitectureSpec {
pub layers: Vec<LayerSpec>,
pub connections: Array2<bool>,
pub global_config: GlobalArchitectureConfig,
pub specialized_components: Vec<SpecializedComponent>,
}
#[derive(Debug, Clone)]
pub struct LayerSpec {
pub layer_type: LayerType,
pub dimensions: LayerDimensions,
pub activation: ActivationType,
pub normalization: NormalizationType,
pub parameters: HashMap<String, f64>,
pub skip_connections: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct LayerDimensions {
pub input_dim: usize,
pub output_dim: usize,
pub hidden_dims: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct GlobalArchitectureConfig {
pub depth: usize,
pub width: usize,
pub global_skip_connections: bool,
pub attention_pattern: AttentionPattern,
pub memory_management: MemoryManagementStrategy,
}
#[derive(Debug, Clone)]
pub struct AttentionPattern {
pub attention_type: AttentionType,
pub num_heads: usize,
pub attention_span: usize,
pub sparse_config: Option<SparseAttentionConfig>,
}
#[derive(Debug, Clone)]
pub struct SparseAttentionConfig {
pub sparsity_pattern: SparsityPattern,
pub sparsity_ratio: f64,
pub block_size: usize,
}
#[derive(Debug, Clone)]
pub struct MemoryManagement {
pub memory_type: MemoryType,
pub memory_capacity: usize,
pub access_pattern: MemoryAccessPattern,
pub compression_enabled: bool,
}
#[derive(Debug, Clone)]
pub struct SpecializedComponent {
pub component_type: OptimizerComponent,
pub parameters: HashMap<String, f64>,
pub integration_points: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct ArchitectureCandidate {
pub id: String,
pub architecture: ArchitectureSpec,
pub performance: PerformanceMetrics,
pub resource_usage: ResourceUsage,
pub generation_info: GenerationInfo,
pub validation_results: Option<ValidationResults>,
}
#[derive(Debug, Clone)]
pub struct PerformanceMetrics {
pub optimization_performance: f64,
pub convergence_speed: f64,
pub generalization: f64,
pub robustness: f64,
pub transfer_performance: f64,
pub multitask_performance: f64,
pub stability: f64,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage {
pub parameter_count: usize,
pub memory_usage: usize,
pub computational_cost: u64,
pub inference_time_us: u64,
pub training_time_us: u64,
pub energy_consumption: f64,
}
#[derive(Debug, Clone)]
pub struct GenerationInfo {
pub generation: usize,
pub parents: Vec<String>,
pub mutations: Vec<MutationRecord>,
pub created_at: std::time::Instant,
pub creation_method: CreationMethod,
}
#[derive(Debug, Clone)]
pub struct MutationRecord {
pub mutation_type: String,
pub affected_layers: Vec<usize>,
pub parameters: HashMap<String, f64>,
pub timestamp: std::time::Instant,
}
#[derive(Debug, Clone, Copy)]
pub enum CreationMethod {
Random,
Evolutionary,
BayesianOptimization,
ReinforcementLearning,
Progressive,
Manual,
Transfer,
}
#[derive(Debug, Clone)]
pub struct ValidationResults {
pub accuracy: f64,
pub cv_scores: Vec<f64>,
pub p_value: f64,
pub confidence_interval: (f64, f64),
pub details: HashMap<String, f64>,
}
impl Default for LayerDimensions {
fn default() -> Self {
Self {
input_dim: 128,
output_dim: 128,
hidden_dims: vec![],
}
}
}
impl Default for AttentionPattern {
fn default() -> Self {
Self {
attention_type: AttentionType::None,
num_heads: 1,
attention_span: 0,
sparse_config: None,
}
}
}
impl Default for GlobalArchitectureConfig {
fn default() -> Self {
Self {
depth: 3,
width: 128,
global_skip_connections: false,
attention_pattern: AttentionPattern::default(),
memory_management: MemoryManagementStrategy::Standard,
}
}
}
impl Default for MemoryManagement {
fn default() -> Self {
Self {
memory_type: MemoryType::None,
memory_capacity: 0,
access_pattern: MemoryAccessPattern::Sequential,
compression_enabled: false,
}
}
}
impl Default for PerformanceMetrics {
fn default() -> Self {
Self {
optimization_performance: 0.0,
convergence_speed: 0.0,
generalization: 0.0,
robustness: 0.0,
transfer_performance: 0.0,
multitask_performance: 0.0,
stability: 0.0,
}
}
}
impl Default for ResourceUsage {
fn default() -> Self {
Self {
parameter_count: 0,
memory_usage: 0,
computational_cost: 0,
inference_time_us: 0,
training_time_us: 0,
energy_consumption: 0.0,
}
}
}
impl Default for CreationMethod {
fn default() -> Self {
CreationMethod::Random
}
}
impl ArchitectureSpec {
pub fn new(
layers: Vec<LayerSpec>,
global_config: GlobalArchitectureConfig,
) -> Self {
let num_layers = layers.len();
let connections = Array2::from_elem((num_layers, num_layers), false);
Self {
layers,
connections,
global_config,
specialized_components: Vec::new(),
}
}
pub fn parameter_count(&self) -> usize {
self.layers
.iter()
.map(|layer| layer.estimate_parameter_count())
.sum::<usize>()
+ self
.specialized_components
.iter()
.map(|comp| comp.estimate_parameter_count())
.sum::<usize>()
}
pub fn memory_usage_estimate(&self) -> usize {
let layer_memory: usize = self
.layers
.iter()
.map(|layer| layer.estimate_memory_usage())
.sum();
let connection_memory = self.connections.len() * std::mem::size_of::<bool>();
layer_memory + connection_memory
}
pub fn validate(&self) -> Result<(), SpecificationError> {
if self.layers.is_empty() {
return Err(SpecificationError::InvalidConfiguration(
"Architecture must have at least one layer".to_string(),
));
}
let expected_size = self.layers.len();
if self.connections.shape() != [expected_size, expected_size] {
return Err(SpecificationError::InvalidConfiguration(
"Connection matrix size does not match layer count".to_string(),
));
}
for (i, layer) in self.layers.iter().enumerate() {
layer.validate().map_err(|e| {
SpecificationError::InvalidLayer(i, format!("{}", e))
})?;
}
self.validate_dimension_compatibility()?;
Ok(())
}
fn validate_dimension_compatibility(&self) -> Result<(), SpecificationError> {
for i in 0..self.layers.len() - 1 {
let current_layer = &self.layers[i];
let next_layer = &self.layers[i + 1];
if current_layer.dimensions.output_dim != next_layer.dimensions.input_dim {
return Err(SpecificationError::DimensionMismatch(
i,
i + 1,
current_layer.dimensions.output_dim,
next_layer.dimensions.input_dim,
));
}
}
Ok(())
}
pub fn add_specialized_component(&mut self, component: SpecializedComponent) {
self.specialized_components.push(component);
}
pub fn get_layers_by_type(&self, layer_type: LayerType) -> Vec<&LayerSpec> {
self.layers
.iter()
.filter(|layer| layer.layer_type == layer_type)
.collect()
}
}
impl LayerSpec {
pub fn new(
layer_type: LayerType,
dimensions: LayerDimensions,
activation: ActivationType,
) -> Self {
Self {
layer_type,
dimensions,
activation,
normalization: NormalizationType::None,
parameters: HashMap::new(),
skip_connections: Vec::new(),
}
}
pub fn estimate_parameter_count(&self) -> usize {
let base_params = self.dimensions.input_dim * self.dimensions.output_dim;
let bias_params = self.dimensions.output_dim;
let multiplier = self.layer_type.parameter_multiplier();
((base_params + bias_params) as f64 * multiplier) as usize
}
pub fn estimate_memory_usage(&self) -> usize {
let param_memory = self.estimate_parameter_count() * std::mem::size_of::<f32>();
let activation_memory = self.dimensions.output_dim * std::mem::size_of::<f32>();
param_memory + activation_memory
}
pub fn validate(&self) -> Result<(), SpecificationError> {
if self.dimensions.input_dim == 0 {
return Err(SpecificationError::InvalidConfiguration(
"Input dimension cannot be zero".to_string(),
));
}
if self.dimensions.output_dim == 0 {
return Err(SpecificationError::InvalidConfiguration(
"Output dimension cannot be zero".to_string(),
));
}
if !are_components_compatible(
self.layer_type,
self.activation,
AttentionType::None,
) {
return Err(SpecificationError::IncompatibleComponents(
format!("{:?}", self.layer_type),
format!("{:?}", self.activation),
));
}
Ok(())
}
pub fn add_parameter(&mut self, name: String, value: f64) {
self.parameters.insert(name, value);
}
pub fn get_parameter(&self, name: &str) -> Option<f64> {
self.parameters.get(name).copied()
}
}
impl SpecializedComponent {
pub fn new(component_type: OptimizerComponent) -> Self {
Self {
component_type,
parameters: HashMap::new(),
integration_points: Vec::new(),
}
}
pub fn estimate_parameter_count(&self) -> usize {
match self.component_type {
OptimizerComponent::MomentumTracker => 1,
OptimizerComponent::AdaptiveLearningRate => 2,
OptimizerComponent::GradientClipping => 1,
OptimizerComponent::NoiseInjection => 2,
OptimizerComponent::CurvatureEstimation => 10,
OptimizerComponent::SecondOrderInfo => 20,
OptimizerComponent::MetaGradients => 5,
}
}
pub fn add_integration_point(&mut self, layer_index: usize) {
if !self.integration_points.contains(&layer_index) {
self.integration_points.push(layer_index);
}
}
}
impl ArchitectureCandidate {
pub fn new(id: String, architecture: ArchitectureSpec) -> Self {
Self {
id,
architecture,
performance: PerformanceMetrics::default(),
resource_usage: ResourceUsage::default(),
generation_info: GenerationInfo {
generation: 0,
parents: Vec::new(),
mutations: Vec::new(),
created_at: std::time::Instant::now(),
creation_method: CreationMethod::default(),
},
validation_results: None,
}
}
pub fn fitness_score(&self, weights: &[f64]) -> f64 {
let performance_scores = vec![
self.performance.optimization_performance,
self.performance.convergence_speed,
self.performance.generalization,
self.performance.robustness,
self.performance.stability,
];
performance_scores
.iter()
.zip(weights.iter())
.map(|(score, weight)| score * weight)
.sum()
}
pub fn meets_constraints(&self, constraints: &super::constraints::SearchConstraints) -> bool {
self.resource_usage.parameter_count <= constraints.max_parameters
&& self.resource_usage.memory_usage <= constraints.max_memory_mb * 1024 * 1024
&& self.resource_usage.inference_time_us <= constraints.max_inference_time_ms * 1000
&& self.performance.optimization_performance >= constraints.min_accuracy
}
}
#[derive(Debug, Clone)]
pub enum SpecificationError {
InvalidConfiguration(String),
InvalidLayer(usize, String),
DimensionMismatch(usize, usize, usize, usize),
IncompatibleComponents(String, String),
ValidationFailed(String),
}
impl std::fmt::Display for SpecificationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SpecificationError::InvalidConfiguration(msg) => {
write!(f, "Invalid configuration: {}", msg)
}
SpecificationError::InvalidLayer(idx, msg) => {
write!(f, "Invalid layer {}: {}", idx, msg)
}
SpecificationError::DimensionMismatch(i, j, dim1, dim2) => {
write!(
f,
"Dimension mismatch between layers {} and {}: {} != {}",
i, j, dim1, dim2
)
}
SpecificationError::IncompatibleComponents(comp1, comp2) => {
write!(f, "Incompatible components: {} and {}", comp1, comp2)
}
SpecificationError::ValidationFailed(msg) => {
write!(f, "Validation failed: {}", msg)
}
}
}
}
impl std::error::Error for SpecificationError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_layer_spec_creation() {
let dimensions = LayerDimensions {
input_dim: 256,
output_dim: 128,
hidden_dims: vec![],
};
let layer = LayerSpec::new(LayerType::Linear, dimensions, ActivationType::ReLU);
assert_eq!(layer.layer_type, LayerType::Linear);
assert_eq!(layer.activation, ActivationType::ReLU);
}
#[test]
fn test_architecture_validation() {
let layer1 = LayerSpec::new(
LayerType::Linear,
LayerDimensions {
input_dim: 256,
output_dim: 128,
hidden_dims: vec![],
},
ActivationType::ReLU,
);
let layer2 = LayerSpec::new(
LayerType::Linear,
LayerDimensions {
input_dim: 128,
output_dim: 64,
hidden_dims: vec![],
},
ActivationType::ReLU,
);
let arch = ArchitectureSpec::new(
vec![layer1, layer2],
GlobalArchitectureConfig::default(),
);
assert!(arch.validate().is_ok());
}
#[test]
fn test_parameter_count_estimation() {
let layer = LayerSpec::new(
LayerType::Linear,
LayerDimensions {
input_dim: 100,
output_dim: 50,
hidden_dims: vec![],
},
ActivationType::ReLU,
);
let param_count = layer.estimate_parameter_count();
assert!(param_count > 0);
}
#[test]
fn test_architecture_candidate_fitness() {
let arch = ArchitectureSpec::new(vec![], GlobalArchitectureConfig::default());
let mut candidate = ArchitectureCandidate::new("test".to_string(), arch);
candidate.performance.optimization_performance = 0.8;
candidate.performance.convergence_speed = 0.7;
let weights = vec![0.5, 0.3, 0.2, 0.0, 0.0];
let fitness = candidate.fitness_score(&weights);
assert!(fitness > 0.0);
}
}