#[allow(dead_code)]
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::numeric::Float;
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use crate::error::{OptimError, Result};
#[derive(Debug, Clone)]
pub struct MutationConfig<T: Float + Debug + Send + Sync + 'static> {
pub mutation_probability: T,
pub max_mutations_per_arch: usize,
pub mutation_strength: T,
pub available_operators: Vec<MutationType>,
pub operator_weights: HashMap<MutationType, T>,
pub adaptive_params: Option<AdaptiveMutationParams<T>>,
pub preserve_constraints: bool,
pub track_history: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MutationType {
AddOperation,
RemoveOperation,
ReplaceOperation,
ModifyParameters,
AddConnection,
RemoveConnection,
ModifyConnection,
ChangeDepth,
ChangeWidth,
DuplicateBlock,
RemoveBlock,
SwapOperations,
InsertSkip,
RemoveSkip,
ChangeActivation,
ChangeNormalization,
SplitOperation,
MergeOperations,
RandomizeSection,
MacroMutation,
}
#[derive(Debug, Clone)]
pub struct AdaptiveMutationParams<T: Float + Debug + Send + Sync + 'static> {
pub adaptation_rate: T,
pub success_threshold: T,
pub failure_penalty: T,
pub window_size: usize,
pub min_mutation_rate: T,
pub max_mutation_rate: T,
}
#[derive(Debug)]
pub struct ArchitectureMutator<T: Float + Debug + Send + Sync + 'static> {
config: MutationConfig<T>,
operation_pool: OperationPool<T>,
operators: HashMap<MutationType, Box<dyn MutationOperator<T>>>,
mutation_history: MutationHistory<T>,
performance_feedback: PerformanceFeedback<T>,
constraint_validator: Option<Box<dyn ConstraintValidator<T>>>,
}
#[derive(Debug, Clone)]
pub struct OperationPool<T: Float + Debug + Send + Sync + 'static> {
pub operation_types: Vec<OperationType>,
pub operation_templates: HashMap<String, OperationTemplate<T>>,
pub compatibility_matrix: Array2<bool>,
pub operation_frequencies: HashMap<String, T>,
}
#[derive(Debug, Clone)]
pub struct OperationType {
pub id: String,
pub name: String,
pub category: OperationCategory,
pub parameter_ranges: HashMap<String, ParameterRange>,
pub cost_estimate: CostEstimate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationCategory {
Convolution, Dense, Normalization, Activation,
Pooling, Attention, Recurrent, Custom,
}
#[derive(Debug, Clone)]
pub enum ParameterRange {
Integer { min: i64, max: i64, default: i64 },
Float { min: f64, max: f64, default: f64 },
Discrete { choices: Vec<String>, default: String },
Boolean { default: bool },
}
#[derive(Debug, Clone)]
pub struct CostEstimate {
pub param_multiplier: f64,
pub flops_multiplier: f64,
pub memory_multiplier: f64,
pub latency_multiplier: f64,
}
#[derive(Debug, Clone)]
pub struct OperationTemplate<T: Float + Debug + Send + Sync + 'static> {
pub id: String,
pub operation_type: String,
pub default_parameters: HashMap<String, T>,
pub constraints: Vec<ParameterConstraint<T>>,
pub compatibility: CompatibilityInfo,
}
#[derive(Debug, Clone)]
pub enum ParameterConstraint<T: Float + Debug + Send + Sync + 'static> {
Range { param: String, min: T, max: T },
Dependency { param: String, depends_on: String, condition: String },
MutualExclusion { params: Vec<String> },
Custom { name: String, description: String },
}
#[derive(Debug, Clone)]
pub struct CompatibilityInfo {
pub input_types: Vec<String>,
pub output_types: Vec<String>,
pub shape_requirements: Vec<ShapeRequirement>,
pub incompatible_with: Vec<String>,
}
#[derive(Debug, Clone)]
pub enum ShapeRequirement {
Exact(Vec<usize>),
MinDims(usize),
MaxDims(usize),
DivisibleBy { dim: usize, factor: usize },
Relationship { relation: String, factor: f64 },
}
pub trait MutationOperator<T: Float + Debug + Send + Sync + 'static> {
fn mutate(&self, architecture: &ArchitectureGenotype<T>) -> Result<ArchitectureGenotype<T>>;
fn is_applicable(&self, architecture: &ArchitectureGenotype<T>) -> bool;
fn get_metadata(&self) -> MutationMetadata;
fn estimate_impact(&self, architecture: &ArchitectureGenotype<T>) -> MutationImpact<T>;
}
#[derive(Debug, Clone)]
pub struct ArchitectureGenotype<T: Float + Debug + Send + Sync + 'static> {
pub id: String,
pub operations: Vec<OperationGene<T>>,
pub connections: Vec<ConnectionGene<T>>,
pub global_parameters: HashMap<String, T>,
pub metadata: GenotypeMetadata,
pub is_valid: bool,
}
#[derive(Debug, Clone)]
pub struct OperationGene<T: Float + Debug + Send + Sync + 'static> {
pub id: String,
pub operation_type: String,
pub parameters: HashMap<String, T>,
pub position: Position,
pub metadata: GeneMetadata,
}
#[derive(Debug, Clone)]
pub struct ConnectionGene<T: Float + Debug + Send + Sync + 'static> {
pub id: String,
pub source: String,
pub target: String,
pub connection_type: ConnectionType,
pub weight: T,
pub metadata: GeneMetadata,
}
#[derive(Debug, Clone, Copy)]
pub enum ConnectionType {
Standard, Skip, Residual, Dense, Attention,
}
#[derive(Debug, Clone)]
pub struct Position {
pub layer: usize,
pub index: usize,
pub depth: usize,
}
#[derive(Debug, Clone)]
pub struct GenotypeMetadata {
pub created_at: u64,
pub parents: Vec<String>,
pub generation: usize,
pub mutations_applied: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct GeneMetadata {
pub age: usize,
pub mutation_count: usize,
pub last_mutated: u64,
pub performance_contribution: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct MutationMetadata {
pub operator_name: String,
pub mutation_type: MutationType,
pub difficulty: MutationDifficulty,
pub expected_impact: ImpactLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MutationDifficulty {
Easy, Medium, Hard, Expert,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ImpactLevel {
Minimal, Low, Medium, High, Critical,
}
#[derive(Debug, Clone)]
pub struct MutationImpact<T: Float + Debug + Send + Sync + 'static> {
pub performance_delta: T,
pub complexity_delta: T,
pub resource_delta: HashMap<String, T>,
pub confidence: T,
pub risk_level: RiskLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RiskLevel {
VeryLow, Low, Medium, High, VeryHigh,
}
#[derive(Debug)]
pub struct MutationHistory<T: Float + Debug + Send + Sync + 'static> {
pub mutation_log: Vec<MutationRecord<T>>,
pub operator_stats: HashMap<MutationType, OperatorStatistics<T>>,
pub rollback_info: HashMap<String, ArchitectureGenotype<T>>,
pub performance_tracking: Vec<PerformanceRecord<T>>,
}
#[derive(Debug, Clone)]
pub struct MutationRecord<T: Float + Debug + Send + Sync + 'static> {
pub id: String,
pub mutation_type: MutationType,
pub source_architecture: String,
pub result_architecture: String,
pub parameters: HashMap<String, String>,
pub performance_before: Option<T>,
pub performance_after: Option<T>,
pub success: Option<bool>,
pub timestamp: u64,
}
#[derive(Debug, Clone)]
pub struct OperatorStatistics<T: Float + Debug + Send + Sync + 'static> {
pub applications: usize,
pub successes: usize,
pub avg_improvement: T,
pub success_rate: T,
pub avg_impact: T,
pub reliability_score: T,
}
#[derive(Debug)]
pub struct PerformanceFeedback<T: Float + Debug + Send + Sync + 'static> {
pub recent_changes: VecDeque<T>,
pub moving_average: T,
pub trend: PerformanceTrend,
pub stagnation_counter: usize,
pub best_performance: T,
}
#[derive(Debug, Clone, Copy)]
pub enum PerformanceTrend {
Improving, Declining, Stable, Volatile,
}
#[derive(Debug, Clone)]
pub struct PerformanceRecord<T: Float + Debug + Send + Sync + 'static> {
pub architecture_id: String,
pub performance: T,
pub timestamp: u64,
pub context: HashMap<String, String>,
}
pub trait ConstraintValidator<T: Float + Debug + Send + Sync + 'static> {
fn validate(&self, architecture: &ArchitectureGenotype<T>) -> Result<bool>;
fn get_violations(&self, architecture: &ArchitectureGenotype<T>) -> Vec<String>;
fn suggest_fixes(&self, architecture: &ArchitectureGenotype<T>) -> Vec<String>;
}
#[derive(Debug)]
pub struct AddOperationMutator<T: Float + Debug + Send + Sync + 'static> {
operation_pool: OperationPool<T>,
insertion_strategy: InsertionStrategy,
max_additions: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum InsertionStrategy {
Random,
Optimal,
Append,
Prepend,
Compatible,
}
#[derive(Debug)]
pub struct RemoveOperationMutator<T: Float + Debug + Send + Sync + 'static> {
min_operations: usize,
removal_strategy: RemovalStrategy,
preserve_critical: bool,
critical_operations: HashSet<String>,
}
#[derive(Debug, Clone, Copy)]
pub enum RemovalStrategy {
Random,
LeastImportant,
Redundant,
PerformanceBased,
}
#[derive(Debug)]
pub struct ReplaceOperationMutator<T: Float + Debug + Send + Sync + 'static> {
operation_pool: OperationPool<T>,
replacement_strategy: ReplacementStrategy,
check_compatibility: bool,
}
#[derive(Debug, Clone, Copy)]
pub enum ReplacementStrategy {
RandomCompatible,
Similar,
Performance,
Efficiency,
}
#[derive(Debug)]
pub struct ParameterMutator<T: Float + Debug + Send + Sync + 'static> {
strength: T,
distribution: ParameterDistribution<T>,
selection_strategy: ParameterSelectionStrategy,
}
#[derive(Debug, Clone)]
pub enum ParameterDistribution<T: Float + Debug + Send + Sync + 'static> {
Gaussian { std: T },
Uniform { range: T },
Exponential { factor: T },
Discrete { steps: Vec<T> },
}
#[derive(Debug, Clone, Copy)]
pub enum ParameterSelectionStrategy {
Random,
MostSensitive,
All,
ImportanceBased,
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> ArchitectureMutator<T> {
pub fn new(config: MutationConfig<T>) -> Result<Self> {
let mut mutator = Self {
config: config.clone(),
operation_pool: OperationPool::new()?,
operators: HashMap::new(),
mutation_history: MutationHistory::new(),
performance_feedback: PerformanceFeedback::new(),
constraint_validator: None,
};
mutator.initialize_operators()?;
Ok(mutator)
}
fn initialize_operators(&mut self) -> Result<()> {
if self.config.available_operators.contains(&MutationType::AddOperation) {
let add_mutator = AddOperationMutator {
operation_pool: self.operation_pool.clone(),
insertion_strategy: InsertionStrategy::Random,
max_additions: 3,
};
self.operators.insert(MutationType::AddOperation, Box::new(add_mutator));
}
if self.config.available_operators.contains(&MutationType::RemoveOperation) {
let remove_mutator = RemoveOperationMutator {
min_operations: 1,
removal_strategy: RemovalStrategy::Random,
preserve_critical: true,
critical_operations: HashSet::new(),
};
self.operators.insert(MutationType::RemoveOperation, Box::new(remove_mutator));
}
if self.config.available_operators.contains(&MutationType::ReplaceOperation) {
let replace_mutator = ReplaceOperationMutator {
operation_pool: self.operation_pool.clone(),
replacement_strategy: ReplacementStrategy::RandomCompatible,
check_compatibility: true,
};
self.operators.insert(MutationType::ReplaceOperation, Box::new(replace_mutator));
}
if self.config.available_operators.contains(&MutationType::ModifyParameters) {
let param_mutator = ParameterMutator {
strength: self.config.mutation_strength,
distribution: ParameterDistribution::Gaussian { std: scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero()) },
selection_strategy: ParameterSelectionStrategy::Random,
};
self.operators.insert(MutationType::ModifyParameters, Box::new(param_mutator));
}
Ok(())
}
pub fn mutate(&mut self, architecture: &ArchitectureGenotype<T>) -> Result<ArchitectureGenotype<T>> {
let mutation_type = self.select_mutation_operator(architecture)?;
if let Some(operator) = self.operators.get(&mutation_type) {
let mutated_architecture = operator.mutate(architecture)?;
let is_valid = if let Some(ref validator) = self.constraint_validator {
validator.validate(&mutated_architecture)?
} else {
true
};
let mut final_architecture = mutated_architecture;
final_architecture.is_valid = is_valid;
if self.config.track_history {
self.record_mutation(mutation_type, architecture, &final_architecture)?;
}
if self.config.adaptive_params.is_some() {
self.update_adaptive_parameters(mutation_type, is_valid)?;
}
Ok(final_architecture)
} else {
Err(OptimError::UnsupportedOperation(
format!("Mutation operator {:?} not available", mutation_type)
))
}
}
fn select_mutation_operator(&self, architecture: &ArchitectureGenotype<T>) -> Result<MutationType> {
use scirs2_core::random::Rng;
let mut rng = scirs2_core::random::thread_rng();
let applicable_ops: Vec<MutationType> = self.config.available_operators
.iter()
.filter(|&&op_type| {
if let Some(operator) = self.operators.get(&op_type) {
operator.is_applicable(architecture)
} else {
false
}
})
.copied()
.collect();
if applicable_ops.is_empty() {
return Err(OptimError::SearchFailed(
"No applicable mutation operators".to_string()
));
}
if !self.config.operator_weights.is_empty() {
let total_weight: T = applicable_ops.iter()
.map(|op| self.config.operator_weights.get(op).unwrap_or(&T::one()))
.cloned()
.fold(T::zero(), |acc, w| acc + w);
let mut cumulative_weight = T::zero();
let random_weight = T::from(rng.random::<f64>()).expect("unwrap failed") * total_weight;
for &op_type in &applicable_ops {
let weight = self.config.operator_weights.get(&op_type).unwrap_or(&T::one());
cumulative_weight = cumulative_weight + *weight;
if random_weight <= cumulative_weight {
return Ok(op_type);
}
}
}
let selected_idx = rng.gen_range(0..applicable_ops.len());
Ok(applicable_ops[selected_idx])
}
fn record_mutation(&mut self, mutation_type: MutationType,
source: &ArchitectureGenotype<T>,
result: &ArchitectureGenotype<T>) -> Result<()> {
let record = MutationRecord {
id: format!("mut_{}", scirs2_core::random::random::<u64>()),
mutation_type,
source_architecture: source.id.clone(),
result_architecture: result.id.clone(),
parameters: HashMap::new(),
performance_before: None,
performance_after: None,
success: Some(result.is_valid),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
};
self.mutation_history.mutation_log.push(record);
let stats = self.mutation_history.operator_stats
.entry(mutation_type)
.or_insert_with(|| OperatorStatistics {
applications: 0,
successes: 0,
avg_improvement: T::zero(),
success_rate: T::zero(),
avg_impact: T::zero(),
reliability_score: T::zero(),
});
stats.applications += 1;
if result.is_valid {
stats.successes += 1;
}
stats.success_rate = scirs2_core::numeric::NumCast::from(stats.successes as f64 / stats.applications as f64).unwrap_or_else(|| T::zero());
Ok(())
}
fn update_adaptive_parameters(&mut self, mutation_type: MutationType, success: bool) -> Result<()> {
if let Some(ref params) = self.config.adaptive_params {
let current_weight = self.config.operator_weights
.get(&mutation_type)
.copied()
.unwrap_or(T::one());
let new_weight = if success {
current_weight * (T::one() + params.adaptation_rate)
} else {
current_weight * (T::one() - params.failure_penalty)
};
let clamped_weight = new_weight.max(scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero())).min(scirs2_core::numeric::NumCast::from(10.0).unwrap_or_else(|| T::zero()));
self.config.operator_weights.insert(mutation_type, clamped_weight);
}
Ok(())
}
pub fn mutate_multiple(&mut self, architecture: &ArchitectureGenotype<T>,
num_mutations: usize) -> Result<ArchitectureGenotype<T>> {
let mut current_arch = architecture.clone();
for _ in 0..num_mutations.min(self.config.max_mutations_per_arch) {
current_arch = self.mutate(¤t_arch)?;
if !current_arch.is_valid {
break;
}
}
Ok(current_arch)
}
pub fn get_statistics(&self) -> &HashMap<MutationType, OperatorStatistics<T>> {
&self.mutation_history.operator_stats
}
pub fn get_mutation_history(&self) -> &[MutationRecord<T>] {
&self.mutation_history.mutation_log
}
pub fn set_constraint_validator(&mut self, validator: Box<dyn ConstraintValidator<T>>) {
self.constraint_validator = Some(validator);
}
pub fn update_performance_feedback(&mut self, architecture_id: &str, performance: T) -> Result<()> {
self.performance_feedback.recent_changes.push_back(performance);
let window_size = if let Some(ref params) = self.config.adaptive_params {
params.window_size
} else {
10
};
while self.performance_feedback.recent_changes.len() > window_size {
self.performance_feedback.recent_changes.pop_front();
}
let sum: T = self.performance_feedback.recent_changes.iter().cloned().sum();
let count = T::from(self.performance_feedback.recent_changes.len() as f64).expect("unwrap failed");
self.performance_feedback.moving_average = sum / count;
self.performance_feedback.best_performance =
self.performance_feedback.best_performance.max(performance);
Ok(())
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> MutationOperator<T> for AddOperationMutator<T> {
fn mutate(&self, architecture: &ArchitectureGenotype<T>) -> Result<ArchitectureGenotype<T>> {
use scirs2_core::random::Rng;
let mut rng = scirs2_core::random::thread_rng();
let mut mutated = architecture.clone();
mutated.id = format!("{}_{}", architecture.id, "add_op");
let op_types: Vec<&OperationType> = self.operation_pool.operation_types.iter().collect();
if op_types.is_empty() {
return Err(OptimError::SearchFailed("No operations available to add".to_string()));
}
let selected_op = &op_types[rng.gen_range(0..op_types.len())];
let new_op = OperationGene {
id: format!("op_{}", scirs2_core::random::random::<u64>()),
operation_type: selected_op.id.clone(),
parameters: HashMap::new(), position: Position {
layer: rng.gen_range(0..mutated.operations.len() + 1),
index: 0,
depth: 0,
},
metadata: GeneMetadata {
age: 0,
mutation_count: 0,
last_mutated: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
performance_contribution: None,
},
};
match self.insertion_strategy {
InsertionStrategy::Random => {
let pos = rng.gen_range(0..=mutated.operations.len());
mutated.operations.insert(pos, new_op);
}
InsertionStrategy::Append => {
mutated.operations.push(new_op);
}
_ => {
mutated.operations.push(new_op); }
}
Ok(mutated)
}
fn is_applicable(&self, architecture: &ArchitectureGenotype<T>) -> bool {
architecture.operations.len() < 100 }
fn get_metadata(&self) -> MutationMetadata {
MutationMetadata {
operator_name: "AddOperation".to_string(),
mutation_type: MutationType::AddOperation,
difficulty: MutationDifficulty::Easy,
expected_impact: ImpactLevel::Medium,
}
}
fn estimate_impact(&self, _architecture: &ArchitectureGenotype<T>) -> MutationImpact<T> {
MutationImpact {
performance_delta: scirs2_core::numeric::NumCast::from(0.05).unwrap_or_else(|| T::zero()), complexity_delta: scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero()), resource_delta: {
let mut resources = HashMap::new();
resources.insert("memory".to_string(), scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero()));
resources.insert("flops".to_string(), scirs2_core::numeric::NumCast::from(0.15).unwrap_or_else(|| T::zero()));
resources
},
confidence: scirs2_core::numeric::NumCast::from(0.7).unwrap_or_else(|| T::zero()),
risk_level: RiskLevel::Low,
}
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> MutationOperator<T> for RemoveOperationMutator<T> {
fn mutate(&self, architecture: &ArchitectureGenotype<T>) -> Result<ArchitectureGenotype<T>> {
use scirs2_core::random::Rng;
let mut rng = scirs2_core::random::thread_rng();
if architecture.operations.len() <= self.min_operations {
return Err(OptimError::InvalidInput(
"Cannot remove operation: minimum operations reached".to_string()
));
}
let mut mutated = architecture.clone();
mutated.id = format!("{}_{}", architecture.id, "remove_op");
let removal_idx = match self.removal_strategy {
RemovalStrategy::Random => {
rng.gen_range(0..mutated.operations.len())
}
_ => {
rng.gen_range(0..mutated.operations.len())
}
};
let op_to_remove = &mutated.operations[removal_idx];
if self.preserve_critical && self.critical_operations.contains(&op_to_remove.id) {
return Err(OptimError::InvalidInput(
"Cannot remove critical operation".to_string()
));
}
mutated.operations.remove(removal_idx);
mutated.connections.retain(|conn| {
conn.source != op_to_remove.id && conn.target != op_to_remove.id
});
Ok(mutated)
}
fn is_applicable(&self, architecture: &ArchitectureGenotype<T>) -> bool {
architecture.operations.len() > self.min_operations
}
fn get_metadata(&self) -> MutationMetadata {
MutationMetadata {
operator_name: "RemoveOperation".to_string(),
mutation_type: MutationType::RemoveOperation,
difficulty: MutationDifficulty::Medium,
expected_impact: ImpactLevel::High,
}
}
fn estimate_impact(&self, _architecture: &ArchitectureGenotype<T>) -> MutationImpact<T> {
MutationImpact {
performance_delta: scirs2_core::numeric::NumCast::from(-0.1).unwrap_or_else(|| T::zero()), complexity_delta: scirs2_core::numeric::NumCast::from(-0.2).unwrap_or_else(|| T::zero()), resource_delta: {
let mut resources = HashMap::new();
resources.insert("memory".to_string(), scirs2_core::numeric::NumCast::from(-0.1).unwrap_or_else(|| T::zero()));
resources.insert("flops".to_string(), scirs2_core::numeric::NumCast::from(-0.15).unwrap_or_else(|| T::zero()));
resources
},
confidence: scirs2_core::numeric::NumCast::from(0.6).unwrap_or_else(|| T::zero()),
risk_level: RiskLevel::Medium,
}
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> MutationOperator<T> for ParameterMutator<T> {
fn mutate(&self, architecture: &ArchitectureGenotype<T>) -> Result<ArchitectureGenotype<T>> {
use scirs2_core::random::Rng;
let mut rng = scirs2_core::random::thread_rng();
let mut mutated = architecture.clone();
mutated.id = format!("{}_{}", architecture.id, "param_mut");
if mutated.operations.is_empty() {
return Ok(mutated);
}
let op_idx = rng.gen_range(0..mutated.operations.len());
let operation = &mut mutated.operations[op_idx];
let param_keys: Vec<String> = operation.parameters.keys().cloned().collect();
if param_keys.is_empty() {
return Ok(mutated);
}
let param_key = ¶m_keys[rng.gen_range(0..param_keys.len())];
if let Some(current_value) = operation.parameters.get(param_key) {
let new_value = match &self.distribution {
ParameterDistribution::Gaussian { std } => {
let noise = T::from(rng.random::<f64>() - 0.5).expect("unwrap failed") * *std * scirs2_core::numeric::NumCast::from(2.0).unwrap_or_else(|| T::zero());
*current_value + noise
}
ParameterDistribution::Uniform { range } => {
let noise = T::from(rng.random::<f64>() - 0.5).expect("unwrap failed") * *range * scirs2_core::numeric::NumCast::from(2.0).unwrap_or_else(|| T::zero());
*current_value + noise
}
_ => *current_value, };
operation.parameters.insert(param_key.clone(), new_value);
operation.metadata.mutation_count += 1;
operation.metadata.last_mutated = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
}
Ok(mutated)
}
fn is_applicable(&self, architecture: &ArchitectureGenotype<T>) -> bool {
!architecture.operations.is_empty() &&
architecture.operations.iter().any(|op| !op.parameters.is_empty())
}
fn get_metadata(&self) -> MutationMetadata {
MutationMetadata {
operator_name: "ParameterMutation".to_string(),
mutation_type: MutationType::ModifyParameters,
difficulty: MutationDifficulty::Easy,
expected_impact: ImpactLevel::Low,
}
}
fn estimate_impact(&self, _architecture: &ArchitectureGenotype<T>) -> MutationImpact<T> {
MutationImpact {
performance_delta: T::zero(), complexity_delta: T::zero(), resource_delta: HashMap::new(),
confidence: scirs2_core::numeric::NumCast::from(0.8).unwrap_or_else(|| T::zero()),
risk_level: RiskLevel::VeryLow,
}
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> OperationPool<T> {
fn new() -> Result<Self> {
Ok(Self {
operation_types: vec![
OperationType {
id: "conv2d".to_string(),
name: "2D Convolution".to_string(),
category: OperationCategory::Convolution,
parameter_ranges: HashMap::new(),
cost_estimate: CostEstimate {
param_multiplier: 1.0,
flops_multiplier: 2.0,
memory_multiplier: 1.5,
latency_multiplier: 1.0,
},
},
OperationType {
id: "dense".to_string(),
name: "Dense Layer".to_string(),
category: OperationCategory::Dense,
parameter_ranges: HashMap::new(),
cost_estimate: CostEstimate {
param_multiplier: 1.5,
flops_multiplier: 1.0,
memory_multiplier: 1.0,
latency_multiplier: 0.8,
},
},
],
operation_templates: HashMap::new(),
compatibility_matrix: Array2::eye(2),
operation_frequencies: HashMap::new(),
})
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> MutationHistory<T> {
fn new() -> Self {
Self {
mutation_log: Vec::new(),
operator_stats: HashMap::new(),
rollback_info: HashMap::new(),
performance_tracking: Vec::new(),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> PerformanceFeedback<T> {
fn new() -> Self {
Self {
recent_changes: VecDeque::new(),
moving_average: T::zero(),
trend: PerformanceTrend::Stable,
stagnation_counter: 0,
best_performance: scirs2_core::numeric::NumCast::from(f64::NEG_INFINITY).unwrap_or_else(|| T::zero()),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> Default for MutationConfig<T> {
fn default() -> Self {
let mut operator_weights = HashMap::new();
operator_weights.insert(MutationType::AddOperation, scirs2_core::numeric::NumCast::from(0.3).unwrap_or_else(|| T::zero()));
operator_weights.insert(MutationType::RemoveOperation, scirs2_core::numeric::NumCast::from(0.2).unwrap_or_else(|| T::zero()));
operator_weights.insert(MutationType::ReplaceOperation, scirs2_core::numeric::NumCast::from(0.25).unwrap_or_else(|| T::zero()));
operator_weights.insert(MutationType::ModifyParameters, scirs2_core::numeric::NumCast::from(0.25).unwrap_or_else(|| T::zero()));
Self {
mutation_probability: scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero()),
max_mutations_per_arch: 3,
mutation_strength: scirs2_core::numeric::NumCast::from(0.5).unwrap_or_else(|| T::zero()),
available_operators: vec![
MutationType::AddOperation,
MutationType::RemoveOperation,
MutationType::ReplaceOperation,
MutationType::ModifyParameters,
],
operator_weights,
adaptive_params: Some(AdaptiveMutationParams {
adaptation_rate: scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero()),
success_threshold: scirs2_core::numeric::NumCast::from(0.6).unwrap_or_else(|| T::zero()),
failure_penalty: scirs2_core::numeric::NumCast::from(0.1).unwrap_or_else(|| T::zero()),
window_size: 10,
min_mutation_rate: scirs2_core::numeric::NumCast::from(0.01).unwrap_or_else(|| T::zero()),
max_mutation_rate: scirs2_core::numeric::NumCast::from(0.5).unwrap_or_else(|| T::zero()),
}),
preserve_constraints: true,
track_history: true,
}
}
}