Skip to main content

scirs2_ndimage/
adaptive_image_optimizer.rs

1//! # Adaptive Advanced Optimizer - Dynamic Performance Tuning
2//!
3//! This module provides an adaptive optimization system that continuously monitors
4//! and improves Advanced mode performance based on runtime characteristics.
5//! It combines machine learning, hardware profiling, and adaptive algorithms
6//! to achieve optimal performance for any workload.
7
8use scirs2_core::ndarray::{Array1, Dimension};
9use scirs2_core::numeric::Float;
10use std::collections::{HashMap, VecDeque};
11use std::sync::{Arc, Mutex, RwLock};
12use std::time::{Duration, Instant};
13
14use crate::advanced_fusion_algorithms::AdvancedConfig;
15use crate::error::{NdimageError, NdimageResult};
16
17/// Adaptive optimization system for Advanced mode operations
18#[derive(Debug)]
19pub struct AdaptiveAdvancedOptimizer {
20    /// Performance history database
21    performancehistory: Arc<RwLock<HashMap<String, VecDeque<PerformanceSnapshot>>>>,
22    /// Machine learning model for performance prediction
23    ml_predictor: Arc<Mutex<PerformancePredictionModel>>,
24    /// Hardware characteristics profiler
25    hardware_profiler: Arc<Mutex<HardwareProfiler>>,
26    /// Adaptive parameter controller
27    parameter_controller: Arc<Mutex<ParameterController>>,
28    /// Real-time monitoring system
29    monitor: Arc<Mutex<RealTimeMonitor>>,
30    /// Configuration
31    config: AdaptiveOptimizerConfig,
32}
33
34#[derive(Debug, Clone)]
35pub struct AdaptiveOptimizerConfig {
36    /// Learning rate for adaptive adjustments
37    pub learning_rate: f64,
38    /// History window size for trend analysis
39    pub history_window_size: usize,
40    /// Minimum improvement threshold for parameter changes
41    pub improvement_threshold: f64,
42    /// Maximum parameter adjustment per iteration
43    pub max_adjustment_rate: f64,
44    /// Enable predictive optimization
45    pub enable_prediction: bool,
46    /// Monitoring sampling rate (Hz)
47    pub monitoring_rate: f64,
48}
49
50impl Default for AdaptiveOptimizerConfig {
51    fn default() -> Self {
52        Self {
53            learning_rate: 0.01,
54            history_window_size: 100,
55            improvement_threshold: 0.05, // 5% improvement threshold
56            max_adjustment_rate: 0.1,    // 10% max adjustment
57            enable_prediction: true,
58            monitoring_rate: 1000.0, // 1kHz monitoring
59        }
60    }
61}
62
63/// Performance snapshot for trend analysis
64#[derive(Debug, Clone)]
65pub struct PerformanceSnapshot {
66    /// Timestamp of measurement
67    pub timestamp: Instant,
68    /// Operation type identifier
69    pub operation_type: String,
70    /// Input data characteristics
71    pub data_characteristics: DataCharacteristics,
72    /// Performance metrics
73    pub metrics: PerformanceMetrics,
74    /// Configuration used
75    pub config_used: AdvancedConfig,
76}
77
78#[derive(Debug, Clone)]
79pub struct DataCharacteristics {
80    /// Data dimensions
81    pub dimensions: Vec<usize>,
82    /// Data type size
83    pub element_size: usize,
84    /// Estimated complexity
85    pub complexity_score: f64,
86    /// Memory access pattern
87    pub access_pattern: AccessPattern,
88}
89
90#[derive(Debug, Clone)]
91pub enum AccessPattern {
92    Sequential,
93    Random,
94    Strided { stride: usize },
95    Blocked { block_size: usize },
96}
97
98#[derive(Debug, Clone)]
99pub struct PerformanceMetrics {
100    /// Execution time
101    pub execution_time: Duration,
102    /// Memory throughput (bytes/sec)
103    pub memory_throughput: f64,
104    /// FLOPS achieved
105    pub flops: f64,
106    /// Cache hit ratio
107    pub cache_hit_ratio: f64,
108    /// CPU utilization
109    pub cpu_utilization: f64,
110    /// Power consumption (watts)
111    pub power_consumption: f64,
112}
113
114/// Machine learning model for performance prediction
115#[derive(Debug)]
116pub struct PerformancePredictionModel {
117    /// Feature weights for linear model
118    feature_weights: Array1<f64>,
119    /// Bias term
120    bias: f64,
121    /// Model accuracy history
122    accuracyhistory: VecDeque<f64>,
123    /// Training data
124    training_data: Vec<(Array1<f64>, f64)>,
125}
126
127impl PerformancePredictionModel {
128    pub fn new() -> Self {
129        Self {
130            feature_weights: Array1::zeros(16), // 16 features initially
131            bias: 0.0,
132            accuracyhistory: VecDeque::new(),
133            training_data: Vec::new(),
134        }
135    }
136
137    /// Predict performance based on data characteristics
138    pub fn predict_performance(&self, features: &Array1<f64>) -> f64 {
139        let prediction = features.dot(&self.feature_weights) + self.bias;
140        prediction.max(0.0) // Ensure non-negative performance
141    }
142
143    /// Update model with new training data
144    pub fn update_model(&mut self, features: Array1<f64>, target: f64) -> NdimageResult<()> {
145        self.training_data.push((features, target));
146
147        // Keep only recent training data to adapt to changing conditions
148        if self.training_data.len() > 1000 {
149            self.training_data.remove(0);
150        }
151
152        // Retrain model using gradient descent
153        self.retrain_model()?;
154
155        Ok(())
156    }
157
158    fn retrain_model(&mut self) -> NdimageResult<()> {
159        if self.training_data.is_empty() {
160            return Ok(());
161        }
162
163        let learning_rate = 0.001;
164        let epochs = 10;
165
166        for _ in 0..epochs {
167            for (features, target) in &self.training_data {
168                let prediction = self.predict_performance(features);
169                let error = prediction - target;
170
171                // Update weights using gradient descent
172                for (i, weight) in self.feature_weights.iter_mut().enumerate() {
173                    *weight -= learning_rate * error * features[i];
174                }
175                self.bias -= learning_rate * error;
176            }
177        }
178
179        Ok(())
180    }
181}
182
183/// Hardware characteristics profiler
184#[derive(Debug)]
185pub struct HardwareProfiler {
186    /// CPU cache sizes (L1, L2, L3)
187    cache_sizes: Vec<usize>,
188    /// Memory bandwidth
189    memory_bandwidth: f64,
190    /// CPU frequency
191    cpu_frequency: f64,
192    /// Number of cores
193    num_cores: usize,
194    /// SIMD capabilities
195    simd_capabilities: SIMDCapabilities,
196}
197
198#[derive(Debug)]
199pub struct SIMDCapabilities {
200    pub avx512: bool,
201    pub avx2: bool,
202    pub sse4: bool,
203    pub vector_width: usize,
204}
205
206impl HardwareProfiler {
207    pub fn new() -> Self {
208        Self {
209            cache_sizes: vec![32768, 262144, 8388608], // Default L1, L2, L3 sizes
210            memory_bandwidth: 25.6e9,                  // 25.6 GB/s default
211            cpu_frequency: 3.0e9,                      // 3 GHz default
212            num_cores: num_cpus::get(),
213            simd_capabilities: Self::detect_simd_capabilities(),
214        }
215    }
216
217    fn detect_simd_capabilities() -> SIMDCapabilities {
218        // In a real implementation, this would use CPU feature detection
219        SIMDCapabilities {
220            avx512: false, // Conservative default
221            avx2: true,    // Assume AVX2 support
222            sse4: true,
223            vector_width: 256, // 256-bit vectors for AVX2
224        }
225    }
226
227    /// Estimate optimal parameters based on hardware characteristics
228    pub fn suggest_optimal_parameters(&self, data_size: usize) -> OptimalParameters {
229        let cache_friendly_tile_size = (self.cache_sizes[0] / 8).min(1024); // L1 cache friendly
230        let parallel_threshold = data_size / self.num_cores;
231
232        OptimalParameters {
233            tile_size: cache_friendly_tile_size,
234            parallel_threshold,
235            simd_enabled: self.simd_capabilities.avx2,
236            vector_width: self.simd_capabilities.vector_width,
237        }
238    }
239}
240
241#[derive(Debug, Clone)]
242pub struct OptimalParameters {
243    pub tile_size: usize,
244    pub parallel_threshold: usize,
245    pub simd_enabled: bool,
246    pub vector_width: usize,
247}
248
249/// Parameter controller for adaptive adjustments
250#[derive(Debug)]
251pub struct ParameterController {
252    /// Current parameter values
253    current_parameters: HashMap<String, f64>,
254    /// Parameter bounds
255    parameter_bounds: HashMap<String, (f64, f64)>,
256    /// Adjustment history
257    adjustmenthistory: VecDeque<ParameterAdjustment>,
258}
259
260#[derive(Debug, Clone)]
261pub struct ParameterAdjustment {
262    pub parameter_name: String,
263    pub old_value: f64,
264    pub new_value: f64,
265    pub performance_impact: f64,
266    pub timestamp: Instant,
267}
268
269impl ParameterController {
270    pub fn new() -> Self {
271        let mut bounds = HashMap::new();
272        bounds.insert("quantum_coherence_time".to_string(), (0.1, 10.0));
273        bounds.insert("consciousness_depth".to_string(), (1.0, 32.0));
274        bounds.insert("meta_learning_rate".to_string(), (0.001, 0.1));
275        bounds.insert("advanced_dimensions".to_string(), (4.0, 64.0));
276        bounds.insert("temporal_window".to_string(), (8.0, 256.0));
277
278        Self {
279            current_parameters: HashMap::new(),
280            parameter_bounds: bounds,
281            adjustmenthistory: VecDeque::new(),
282        }
283    }
284
285    /// Adjust parameters based on performance feedback
286    pub fn adjust_parameters(
287        &mut self,
288        performance_impact: f64,
289        config: &AdaptiveOptimizerConfig,
290    ) -> NdimageResult<HashMap<String, f64>> {
291        let mut adjustments = HashMap::new();
292
293        for (param_name, &current_value) in &self.current_parameters {
294            if let Some(&(min_val, max_val)) = self.parameter_bounds.get(param_name) {
295                // Calculate adjustment based on performance feedback
296                let adjustment_factor = if performance_impact > config.improvement_threshold {
297                    1.0 + config.learning_rate
298                } else if performance_impact < -config.improvement_threshold {
299                    1.0 - config.learning_rate
300                } else {
301                    1.0 // No adjustment needed
302                };
303
304                let new_value = (current_value * adjustment_factor).clamp(min_val, max_val);
305
306                // Apply maximum adjustment rate limit
307                let max_change = current_value * config.max_adjustment_rate;
308                let limited_new_value = if new_value > current_value {
309                    (current_value + max_change).min(new_value)
310                } else {
311                    (current_value - max_change).max(new_value)
312                };
313
314                if (limited_new_value - current_value).abs() > 1e-6 {
315                    adjustments.insert(param_name.clone(), limited_new_value);
316
317                    // Record adjustment
318                    self.adjustmenthistory.push_back(ParameterAdjustment {
319                        parameter_name: param_name.clone(),
320                        old_value: current_value,
321                        new_value: limited_new_value,
322                        performance_impact,
323                        timestamp: Instant::now(),
324                    });
325                }
326            }
327        }
328
329        // Limit history size
330        if self.adjustmenthistory.len() > 1000 {
331            self.adjustmenthistory.pop_front();
332        }
333
334        Ok(adjustments)
335    }
336}
337
338/// Real-time monitoring system
339#[derive(Debug)]
340pub struct RealTimeMonitor {
341    /// Current operation being monitored
342    current_operation: Option<String>,
343    /// Monitoring start time
344    start_time: Option<Instant>,
345    /// Real-time metrics
346    metrics: PerformanceMetrics,
347    /// Monitoring active flag
348    active: bool,
349}
350
351impl RealTimeMonitor {
352    pub fn new() -> Self {
353        Self {
354            current_operation: None,
355            start_time: None,
356            metrics: PerformanceMetrics {
357                execution_time: Duration::from_secs(0),
358                memory_throughput: 0.0,
359                flops: 0.0,
360                cache_hit_ratio: 0.0,
361                cpu_utilization: 0.0,
362                power_consumption: 0.0,
363            },
364            active: false,
365        }
366    }
367
368    /// Start monitoring an operation
369    pub fn start_monitoring(&mut self, operation_name: String) {
370        self.current_operation = Some(operation_name);
371        self.start_time = Some(Instant::now());
372        self.active = true;
373    }
374
375    /// Stop monitoring and return metrics
376    pub fn stop_monitoring(&mut self) -> Option<PerformanceMetrics> {
377        if !self.active {
378            return None;
379        }
380
381        if let Some(start_time) = self.start_time {
382            self.metrics.execution_time = start_time.elapsed();
383        }
384
385        self.active = false;
386        self.current_operation = None;
387        self.start_time = None;
388
389        Some(self.metrics.clone())
390    }
391}
392
393impl AdaptiveAdvancedOptimizer {
394    /// Create a new adaptive optimizer
395    pub fn new(config: AdaptiveOptimizerConfig) -> Self {
396        Self {
397            performancehistory: Arc::new(RwLock::new(HashMap::new())),
398            ml_predictor: Arc::new(Mutex::new(PerformancePredictionModel::new())),
399            hardware_profiler: Arc::new(Mutex::new(HardwareProfiler::new())),
400            parameter_controller: Arc::new(Mutex::new(ParameterController::new())),
401            monitor: Arc::new(Mutex::new(RealTimeMonitor::new())),
402            config,
403        }
404    }
405
406    /// Optimize configuration for a specific operation
407    pub fn optimize_configuration(
408        &self,
409        operation_type: &str,
410        data_characteristics: &DataCharacteristics,
411        base_config: &AdvancedConfig,
412    ) -> NdimageResult<AdvancedConfig> {
413        // Get hardware-optimized parameters
414        let hardware_profiler = self.hardware_profiler.lock().map_err(|_| {
415            NdimageError::ComputationError("Failed to acquire hardware profiler lock".into())
416        })?;
417
418        let data_size = data_characteristics.dimensions.iter().product::<usize>();
419        let optimal_params = hardware_profiler.suggest_optimal_parameters(data_size);
420
421        // Get ML prediction for performance
422        let mut optimized_config = base_config.clone();
423
424        if self.config.enable_prediction {
425            let ml_predictor = self.ml_predictor.lock().map_err(|_| {
426                NdimageError::ComputationError("Failed to acquire ML predictor lock".into())
427            })?;
428
429            let features = self.extractfeatures(data_characteristics, &optimal_params);
430            let predicted_performance = ml_predictor.predict_performance(&features);
431
432            // Adjust configuration based on prediction
433            if predicted_performance < 0.5 {
434                // Poor predicted performance, use conservative settings
435                optimized_config.advanced_dimensions = optimized_config.advanced_dimensions.min(8);
436                optimized_config.consciousness_depth = optimized_config.consciousness_depth.min(4);
437            } else {
438                // Good predicted performance, use aggressive settings
439                optimized_config.advanced_dimensions = optimized_config.advanced_dimensions.max(16);
440                optimized_config.consciousness_depth = optimized_config.consciousness_depth.max(8);
441            }
442        }
443
444        // Apply hardware-specific optimizations
445        optimized_config.temporal_window = optimal_params
446            .tile_size
447            .min(optimized_config.temporal_window);
448
449        Ok(optimized_config)
450    }
451
452    /// Record performance results for learning
453    pub fn record_performance(
454        &self,
455        operation_type: String,
456        data_characteristics: DataCharacteristics,
457        config_used: AdvancedConfig,
458        metrics: PerformanceMetrics,
459    ) -> NdimageResult<()> {
460        let snapshot = PerformanceSnapshot {
461            timestamp: Instant::now(),
462            operation_type: operation_type.clone(),
463            data_characteristics: data_characteristics.clone(),
464            metrics: metrics.clone(),
465            config_used,
466        };
467
468        // Add to performance history
469        let mut history = self.performancehistory.write().map_err(|_| {
470            NdimageError::ComputationError("Failed to acquire performance history lock".into())
471        })?;
472
473        let operationhistory = history.entry(operation_type).or_insert_with(VecDeque::new);
474        operationhistory.push_back(snapshot);
475
476        // Limit history size
477        if operationhistory.len() > self.config.history_window_size {
478            operationhistory.pop_front();
479        }
480
481        // Update ML model
482        if self.config.enable_prediction {
483            let mut ml_predictor = self.ml_predictor.lock().map_err(|_| {
484                NdimageError::ComputationError("Failed to acquire ML predictor lock".into())
485            })?;
486
487            let hardware_profiler = self.hardware_profiler.lock().map_err(|_| {
488                NdimageError::ComputationError("Failed to acquire hardware profiler lock".into())
489            })?;
490
491            let data_size = data_characteristics.dimensions.iter().product::<usize>();
492            let optimal_params = hardware_profiler.suggest_optimal_parameters(data_size);
493            let features = self.extractfeatures(&data_characteristics, &optimal_params);
494
495            // Use execution time as performance target (lower is better, so invert)
496            let performance_score = 1.0 / (metrics.execution_time.as_secs_f64() + 1e-6);
497            ml_predictor.update_model(features, performance_score)?;
498        }
499
500        Ok(())
501    }
502
503    /// Extract features for machine learning
504    fn extractfeatures(
505        &self,
506        data_chars: &DataCharacteristics,
507        optimal_params: &OptimalParameters,
508    ) -> Array1<f64> {
509        let mut features = Array1::zeros(16);
510
511        // Data characteristics features
512        features[0] = data_chars.dimensions.len() as f64; // Number of dimensions
513        features[1] = data_chars.dimensions.iter().product::<usize>() as f64; // Total size
514        features[2] = data_chars.element_size as f64;
515        features[3] = data_chars.complexity_score;
516
517        // Access pattern features
518        features[4] = match data_chars.access_pattern {
519            AccessPattern::Sequential => 1.0,
520            AccessPattern::Random => 2.0,
521            AccessPattern::Strided { .. } => 3.0,
522            AccessPattern::Blocked { .. } => 4.0,
523        };
524
525        // Hardware features
526        features[5] = optimal_params.tile_size as f64;
527        features[6] = optimal_params.parallel_threshold as f64;
528        features[7] = if optimal_params.simd_enabled {
529            1.0
530        } else {
531            0.0
532        };
533        features[8] = optimal_params.vector_width as f64;
534
535        // Shape-based features
536        if !data_chars.dimensions.is_empty() {
537            features[9] = data_chars.dimensions[0] as f64;
538            if data_chars.dimensions.len() > 1 {
539                features[10] = data_chars.dimensions[1] as f64;
540            }
541            if data_chars.dimensions.len() > 2 {
542                features[11] = data_chars.dimensions[2] as f64;
543            }
544        }
545
546        // Derived features
547        features[12] = features[1].log2(); // Log of total size
548        features[13] = data_chars.dimensions.len() as f64 * features[2]; // Dimension * element size
549        features[14] = features[1] / features[5]; // Size / tile size ratio
550        features[15] = 1.0; // Bias feature
551
552        features
553    }
554
555    /// Get performance analysis for an operation type
556    pub fn get_performance_analysis(
557        &self,
558        operation_type: &str,
559    ) -> NdimageResult<PerformanceAnalysis> {
560        let history = self.performancehistory.read().map_err(|_| {
561            NdimageError::ComputationError("Failed to acquire performance history lock".into())
562        })?;
563
564        let snapshots = history.get(operation_type).ok_or_else(|| {
565            NdimageError::ComputationError(format!(
566                "No performance history for operation: {}",
567                operation_type
568            ))
569        })?;
570
571        if snapshots.is_empty() {
572            return Err(NdimageError::ComputationError(
573                "No performance data available".into(),
574            ));
575        }
576
577        let mut execution_times: Vec<f64> = snapshots
578            .iter()
579            .map(|s| s.metrics.execution_time.as_secs_f64())
580            .collect();
581
582        execution_times.sort_by(|a, b| a.partial_cmp(b).expect("Operation failed"));
583
584        let mean_time = execution_times.iter().sum::<f64>() / execution_times.len() as f64;
585        let median_time = execution_times[execution_times.len() / 2];
586        let min_time = execution_times[0];
587        let max_time = execution_times[execution_times.len() - 1];
588
589        // Calculate trend (simple linear regression)
590        let trend = self.calculate_performance_trend(snapshots);
591
592        Ok(PerformanceAnalysis {
593            operation_type: operation_type.to_string(),
594            total_samples: snapshots.len(),
595            mean_execution_time: Duration::from_secs_f64(mean_time),
596            median_execution_time: Duration::from_secs_f64(median_time),
597            min_execution_time: Duration::from_secs_f64(min_time),
598            max_execution_time: Duration::from_secs_f64(max_time),
599            performance_trend: trend,
600            optimization_opportunities: self.identify_optimization_opportunities(snapshots),
601        })
602    }
603
604    fn calculate_performance_trend(&self, snapshots: &VecDeque<PerformanceSnapshot>) -> f64 {
605        if snapshots.len() < 2 {
606            return 0.0;
607        }
608
609        let x_values: Vec<f64> = (0..snapshots.len()).map(|i| i as f64).collect();
610        let y_values: Vec<f64> = snapshots
611            .iter()
612            .map(|s| s.metrics.execution_time.as_secs_f64())
613            .collect();
614
615        // Simple linear regression slope
616        let n = x_values.len() as f64;
617        let sum_x = x_values.iter().sum::<f64>();
618        let sum_y = y_values.iter().sum::<f64>();
619        let sum_xy = x_values
620            .iter()
621            .zip(y_values.iter())
622            .map(|(x, y)| x * y)
623            .sum::<f64>();
624        let sum_x2 = x_values.iter().map(|x| x * x).sum::<f64>();
625
626        (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x)
627    }
628
629    fn identify_optimization_opportunities(
630        &self,
631        snapshots: &VecDeque<PerformanceSnapshot>,
632    ) -> Vec<String> {
633        let mut opportunities = Vec::new();
634
635        // Analyze cache hit ratios
636        let avg_cache_hit_ratio = snapshots
637            .iter()
638            .map(|s| s.metrics.cache_hit_ratio)
639            .sum::<f64>()
640            / snapshots.len() as f64;
641
642        if avg_cache_hit_ratio < 0.8 {
643            opportunities
644                .push("Consider adjusting tile sizes for better cache locality".to_string());
645        }
646
647        // Analyze CPU utilization
648        let avg_cpu_utilization = snapshots
649            .iter()
650            .map(|s| s.metrics.cpu_utilization)
651            .sum::<f64>()
652            / snapshots.len() as f64;
653
654        if avg_cpu_utilization < 0.6 {
655            opportunities.push("CPU underutilized - consider increasing parallelism".to_string());
656        }
657
658        // Analyze memory throughput
659        let max_memory_throughput = snapshots
660            .iter()
661            .map(|s| s.metrics.memory_throughput)
662            .fold(0.0, f64::max);
663
664        let avg_memory_throughput = snapshots
665            .iter()
666            .map(|s| s.metrics.memory_throughput)
667            .sum::<f64>()
668            / snapshots.len() as f64;
669
670        if avg_memory_throughput < max_memory_throughput * 0.5 {
671            opportunities
672                .push("Memory bandwidth underutilized - optimize data access patterns".to_string());
673        }
674
675        opportunities
676    }
677}
678
679#[derive(Debug)]
680pub struct PerformanceAnalysis {
681    pub operation_type: String,
682    pub total_samples: usize,
683    pub mean_execution_time: Duration,
684    pub median_execution_time: Duration,
685    pub min_execution_time: Duration,
686    pub max_execution_time: Duration,
687    pub performance_trend: f64,
688    pub optimization_opportunities: Vec<String>,
689}
690
691#[cfg(test)]
692mod tests {
693    use super::*;
694
695    #[test]
696    fn test_adaptive_optimizer_creation() {
697        let config = AdaptiveOptimizerConfig::default();
698        let optimizer = AdaptiveAdvancedOptimizer::new(config);
699
700        // Test basic creation
701        assert!(optimizer
702            .performancehistory
703            .read()
704            .expect("Operation failed")
705            .is_empty());
706    }
707
708    #[test]
709    fn test_performance_prediction() {
710        let mut model = PerformancePredictionModel::new();
711
712        let features = Array1::ones(16);
713        let prediction = model.predict_performance(&features);
714        assert!(prediction >= 0.0);
715
716        // Test model update
717        let result = model.update_model(features.clone(), 1.0);
718        assert!(result.is_ok());
719    }
720
721    #[test]
722    fn test_hardware_profiler() {
723        let profiler = HardwareProfiler::new();
724        let params = profiler.suggest_optimal_parameters(1000000);
725
726        assert!(params.tile_size > 0);
727        assert!(params.parallel_threshold > 0);
728    }
729
730    #[test]
731    fn test_parameter_controller() {
732        let mut controller = ParameterController::new();
733        let config = AdaptiveOptimizerConfig::default();
734
735        // Initialize some parameters
736        controller
737            .current_parameters
738            .insert("test_param".to_string(), 1.0);
739        controller
740            .parameter_bounds
741            .insert("test_param".to_string(), (0.1, 10.0));
742
743        let adjustments = controller.adjust_parameters(0.1, &config);
744        assert!(adjustments.is_ok());
745    }
746}