Skip to main content

quantrs2_anneal/meta_learning/
portfolio.rs

1//! Algorithm Portfolio Management for Meta-Learning Optimization
2//!
3//! This module contains all Algorithm Portfolio Management types and implementations
4//! used by the meta-learning optimization system.
5
6use super::config::{
7    ActivationFunction, AlgorithmSelectionStrategy, AlgorithmType, ArchitectureSpec,
8    ConnectionPattern, DiversityCriteria, DiversityMethod, LayerSpec, LayerType,
9    OptimizationConfiguration, OptimizationSettings, OptimizerType, RegularizationConfig,
10    ResourceAllocation,
11};
12use super::features::ProblemFeatures;
13use crate::applications::ApplicationResult;
14use std::collections::{HashMap, VecDeque};
15use std::time::{Duration, Instant};
16
17/// Algorithm portfolio manager
18pub struct AlgorithmPortfolio {
19    /// Available algorithms
20    pub algorithms: HashMap<String, Algorithm>,
21    /// Portfolio composition
22    pub composition: PortfolioComposition,
23    /// Selection strategy
24    pub selection_strategy: AlgorithmSelectionStrategy,
25    /// Performance history
26    pub performance_history: HashMap<String, VecDeque<PerformanceRecord>>,
27    /// Diversity analyzer
28    pub diversity_analyzer: DiversityAnalyzer,
29}
30
31/// Algorithm representation
32#[derive(Debug)]
33pub struct Algorithm {
34    /// Algorithm identifier
35    pub id: String,
36    /// Algorithm type
37    pub algorithm_type: AlgorithmType,
38    /// Default configuration
39    pub default_config: OptimizationConfiguration,
40    /// Performance statistics
41    pub performance_stats: AlgorithmPerformanceStats,
42    /// Applicability conditions
43    pub applicability: ApplicabilityConditions,
44}
45
46/// Portfolio composition
47#[derive(Debug, Clone)]
48pub struct PortfolioComposition {
49    /// Algorithm weights
50    pub weights: HashMap<String, f64>,
51    /// Selection probabilities
52    pub selection_probabilities: HashMap<String, f64>,
53    /// Last update time
54    pub last_update: Instant,
55    /// Composition quality
56    pub quality_score: f64,
57}
58
59/// Performance record
60#[derive(Debug, Clone)]
61pub struct PerformanceRecord {
62    /// Timestamp
63    pub timestamp: Instant,
64    /// Problem characteristics
65    pub problem_features: ProblemFeatures,
66    /// Performance achieved
67    pub performance: f64,
68    /// Resource usage
69    pub resource_usage: ResourceUsage,
70    /// Context information
71    pub context: HashMap<String, String>,
72}
73
74/// Resource usage tracking
75#[derive(Debug, Clone)]
76pub struct ResourceUsage {
77    /// Peak CPU usage
78    pub peak_cpu: f64,
79    /// Peak memory usage (MB)
80    pub peak_memory: usize,
81    /// GPU utilization
82    pub gpu_utilization: f64,
83    /// Energy consumption
84    pub energy_consumption: f64,
85}
86
87/// Algorithm performance statistics
88#[derive(Debug, Clone)]
89pub struct AlgorithmPerformanceStats {
90    /// Mean performance
91    pub mean_performance: f64,
92    /// Performance variance
93    pub performance_variance: f64,
94    /// Success rate
95    pub success_rate: f64,
96    /// Average runtime
97    pub avg_runtime: Duration,
98    /// Scalability factor
99    pub scalability_factor: f64,
100}
101
102/// Applicability conditions
103#[derive(Debug, Clone)]
104pub struct ApplicabilityConditions {
105    /// Problem size range
106    pub size_range: (usize, usize),
107    /// Suitable domains
108    pub suitable_domains: Vec<ProblemDomain>,
109    /// Required resources
110    pub required_resources: ResourceRequirements,
111    /// Performance guarantees
112    pub performance_guarantees: Vec<PerformanceGuarantee>,
113}
114
115/// Problem domains
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum ProblemDomain {
118    /// Combinatorial optimization
119    Combinatorial,
120    /// Portfolio optimization
121    Portfolio,
122    /// Scheduling
123    Scheduling,
124    /// Graph problems
125    Graph,
126    /// Machine learning
127    MachineLearning,
128    /// Physics simulation
129    Physics,
130    /// Chemistry
131    Chemistry,
132    /// Custom domain
133    Custom(String),
134}
135
136/// Resource requirements for algorithms
137#[derive(Debug, Clone, PartialEq)]
138pub struct ResourceRequirements {
139    /// Memory requirements (MB)
140    pub memory: usize,
141    /// Computational requirements (FLOPS)
142    pub computation: f64,
143    /// Training time estimate
144    pub training_time: Duration,
145    /// Model size (parameters)
146    pub model_size: usize,
147}
148
149/// Performance guarantee
150#[derive(Debug, Clone)]
151pub struct PerformanceGuarantee {
152    /// Guarantee type
153    pub guarantee_type: GuaranteeType,
154    /// Confidence level
155    pub confidence: f64,
156    /// Conditions
157    pub conditions: Vec<String>,
158}
159
160/// Types of performance guarantees
161#[derive(Debug, Clone, PartialEq)]
162pub enum GuaranteeType {
163    /// Minimum performance level
164    MinimumPerformance(f64),
165    /// Maximum runtime
166    MaximumRuntime(Duration),
167    /// Resource bounds
168    ResourceBounds(ResourceRequirements),
169    /// Quality bounds
170    QualityBounds(f64, f64),
171}
172
173/// Diversity analyzer
174#[derive(Debug)]
175pub struct DiversityAnalyzer {
176    /// Diversity metrics
177    pub metrics: Vec<DiversityMetric>,
178    /// Analysis methods
179    pub methods: Vec<DiversityMethod>,
180    /// Current diversity score
181    pub current_diversity: f64,
182    /// Target diversity
183    pub target_diversity: f64,
184}
185
186/// Diversity metrics
187#[derive(Debug, Clone, PartialEq, Eq)]
188pub enum DiversityMetric {
189    /// Algorithm diversity
190    AlgorithmDiversity,
191    /// Performance diversity
192    PerformanceDiversity,
193    /// Feature diversity
194    FeatureDiversity,
195    /// Error diversity
196    ErrorDiversity,
197    /// Prediction diversity
198    PredictionDiversity,
199}
200
201impl AlgorithmPortfolio {
202    #[must_use]
203    pub fn new(config: super::config::PortfolioManagementConfig) -> Self {
204        Self {
205            algorithms: HashMap::new(),
206            composition: PortfolioComposition {
207                weights: HashMap::new(),
208                selection_probabilities: HashMap::new(),
209                last_update: Instant::now(),
210                quality_score: 0.8,
211            },
212            selection_strategy: config.selection_strategy,
213            performance_history: HashMap::new(),
214            diversity_analyzer: DiversityAnalyzer {
215                metrics: vec![DiversityMetric::AlgorithmDiversity],
216                methods: vec![DiversityMethod::KullbackLeibler],
217                current_diversity: 0.7,
218                target_diversity: 0.8,
219            },
220        }
221    }
222
223    /// Select algorithm for given problem features
224    pub fn select_algorithm(&self, features: &ProblemFeatures) -> ApplicationResult<String> {
225        // Simple algorithm selection based on problem size
226        let algorithm_id = if features.size < 100 {
227            "simulated_annealing"
228        } else if features.size < 500 {
229            "quantum_annealing"
230        } else {
231            "hybrid_approach"
232        };
233
234        Ok(algorithm_id.to_string())
235    }
236
237    /// Update portfolio based on performance feedback
238    pub fn update_portfolio(
239        &mut self,
240        algorithm_id: &str,
241        performance: f64,
242        features: &ProblemFeatures,
243    ) {
244        // Record performance
245        let record = PerformanceRecord {
246            timestamp: Instant::now(),
247            problem_features: features.clone(),
248            performance,
249            resource_usage: ResourceUsage {
250                peak_cpu: 0.8,
251                peak_memory: 512,
252                gpu_utilization: 0.0,
253                energy_consumption: 100.0,
254            },
255            context: HashMap::new(),
256        };
257
258        self.performance_history
259            .entry(algorithm_id.to_string())
260            .or_insert_with(VecDeque::new)
261            .push_back(record);
262
263        // Limit history size
264        if let Some(history) = self.performance_history.get_mut(algorithm_id) {
265            if history.len() > 1000 {
266                history.pop_front();
267            }
268        }
269
270        // Update composition weights based on performance
271        self.update_composition_weights();
272    }
273
274    /// Update composition weights based on performance history
275    fn update_composition_weights(&mut self) {
276        for (algorithm_id, history) in &self.performance_history {
277            if !history.is_empty() {
278                let avg_performance: f64 =
279                    history.iter().map(|record| record.performance).sum::<f64>()
280                        / history.len() as f64;
281
282                self.composition
283                    .weights
284                    .insert(algorithm_id.clone(), avg_performance);
285            }
286        }
287
288        // Normalize weights
289        let total_weight: f64 = self.composition.weights.values().sum();
290        if total_weight > 0.0 {
291            for weight in self.composition.weights.values_mut() {
292                *weight /= total_weight;
293            }
294        }
295
296        self.composition.last_update = Instant::now();
297    }
298
299    /// Get portfolio statistics
300    pub fn get_statistics(&self) -> PortfolioStatistics {
301        let total_algorithms = self.algorithms.len();
302        let active_algorithms = self.composition.weights.len();
303        let avg_performance = if self.performance_history.is_empty() {
304            0.0
305        } else {
306            let total_records: usize = self
307                .performance_history
308                .values()
309                .map(std::collections::VecDeque::len)
310                .sum();
311
312            if total_records > 0 {
313                let total_performance: f64 = self
314                    .performance_history
315                    .values()
316                    .flat_map(|history| history.iter())
317                    .map(|record| record.performance)
318                    .sum();
319                total_performance / total_records as f64
320            } else {
321                0.0
322            }
323        };
324
325        PortfolioStatistics {
326            total_algorithms,
327            active_algorithms,
328            avg_performance,
329            diversity_score: self.diversity_analyzer.current_diversity,
330            last_update: self.composition.last_update,
331        }
332    }
333}
334
335/// Portfolio statistics
336#[derive(Debug, Clone)]
337pub struct PortfolioStatistics {
338    /// Total number of algorithms
339    pub total_algorithms: usize,
340    /// Number of active algorithms
341    pub active_algorithms: usize,
342    /// Average performance
343    pub avg_performance: f64,
344    /// Diversity score
345    pub diversity_score: f64,
346    /// Last update timestamp
347    pub last_update: Instant,
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353    use crate::meta_learning::config::*;
354
355    #[test]
356    fn test_portfolio_creation() {
357        let config = PortfolioManagementConfig::default();
358        let portfolio = AlgorithmPortfolio::new(config);
359
360        assert_eq!(portfolio.algorithms.len(), 0);
361        assert!(portfolio.composition.quality_score > 0.0);
362    }
363
364    #[test]
365    fn test_algorithm_selection() {
366        let config = PortfolioManagementConfig::default();
367        let portfolio = AlgorithmPortfolio::new(config);
368
369        let features = ProblemFeatures {
370            size: 50,
371            density: 0.3,
372            graph_features: crate::meta_learning::features::GraphFeatures::default(),
373            statistical_features: crate::meta_learning::features::StatisticalFeatures::default(),
374            spectral_features: crate::meta_learning::features::SpectralFeatures::default(),
375            domain_features: HashMap::new(),
376        };
377
378        let algorithm_id = portfolio.select_algorithm(&features);
379        assert!(algorithm_id.is_ok());
380        assert!(!algorithm_id
381            .expect("Algorithm selection should succeed")
382            .is_empty());
383    }
384
385    #[test]
386    fn test_portfolio_update() {
387        let config = PortfolioManagementConfig::default();
388        let mut portfolio = AlgorithmPortfolio::new(config);
389
390        let features = ProblemFeatures {
391            size: 100,
392            density: 0.5,
393            graph_features: crate::meta_learning::features::GraphFeatures::default(),
394            statistical_features: crate::meta_learning::features::StatisticalFeatures::default(),
395            spectral_features: crate::meta_learning::features::SpectralFeatures::default(),
396            domain_features: HashMap::new(),
397        };
398
399        portfolio.update_portfolio("test_algorithm", 0.9, &features);
400
401        assert!(portfolio.performance_history.contains_key("test_algorithm"));
402        assert_eq!(portfolio.performance_history["test_algorithm"].len(), 1);
403    }
404
405    #[test]
406    fn test_portfolio_statistics() {
407        let config = PortfolioManagementConfig::default();
408        let portfolio = AlgorithmPortfolio::new(config);
409
410        let stats = portfolio.get_statistics();
411        assert_eq!(stats.total_algorithms, 0);
412        assert_eq!(stats.active_algorithms, 0);
413    }
414}