quantrs2_anneal/meta_learning_optimization/
feature_extraction.rs

1//! Feature extraction system for meta-learning optimization
2
3use std::collections::{BTreeMap, HashMap, VecDeque};
4use std::time::Instant;
5
6use super::config::{
7    ActivationFunction, DimensionalityReduction, FeatureExtractionConfig, FeatureSelectionMethod,
8    LayerType,
9};
10use crate::applications::ApplicationResult;
11use crate::ising::IsingModel;
12
13/// Problem features representation
14#[derive(Debug, Clone)]
15pub struct ProblemFeatures {
16    /// Problem size (number of variables)
17    pub size: usize,
18    /// Graph density
19    pub density: f64,
20    /// Graph-based features
21    pub graph_features: GraphFeatures,
22    /// Statistical features
23    pub statistical_features: StatisticalFeatures,
24    /// Spectral features
25    pub spectral_features: SpectralFeatures,
26    /// Domain-specific features
27    pub domain_features: HashMap<String, f64>,
28}
29
30/// Feature vector representation
31#[derive(Debug, Clone)]
32pub struct FeatureVector {
33    /// Feature values
34    pub values: Vec<f64>,
35    /// Feature names
36    pub names: Vec<String>,
37    /// Normalization parameters
38    pub normalization: Option<NormalizationParams>,
39}
40
41/// Normalization parameters
42#[derive(Debug, Clone)]
43pub struct NormalizationParams {
44    /// Mean values
45    pub mean: Vec<f64>,
46    /// Standard deviation values
47    pub std: Vec<f64>,
48    /// Min values
49    pub min: Vec<f64>,
50    /// Max values
51    pub max: Vec<f64>,
52}
53
54/// Distribution statistics
55#[derive(Debug, Clone)]
56pub struct DistributionStats {
57    /// Mean value
58    pub mean: f64,
59    /// Standard deviation
60    pub std_dev: f64,
61    /// Minimum value
62    pub min: f64,
63    /// Maximum value
64    pub max: f64,
65    /// Skewness
66    pub skewness: f64,
67    /// Kurtosis
68    pub kurtosis: f64,
69}
70
71impl Default for DistributionStats {
72    fn default() -> Self {
73        Self {
74            mean: 0.0,
75            std_dev: 1.0,
76            min: 0.0,
77            max: 1.0,
78            skewness: 0.0,
79            kurtosis: 3.0,
80        }
81    }
82}
83
84/// Graph-based features
85#[derive(Debug, Clone)]
86pub struct GraphFeatures {
87    /// Number of vertices
88    pub num_vertices: usize,
89    /// Number of edges
90    pub num_edges: usize,
91    /// Average degree
92    pub avg_degree: f64,
93    /// Clustering coefficient
94    pub clustering_coefficient: f64,
95    /// Path length statistics
96    pub path_length_stats: PathLengthStats,
97    /// Centrality measures
98    pub centrality_measures: CentralityMeasures,
99}
100
101impl Default for GraphFeatures {
102    fn default() -> Self {
103        Self {
104            num_vertices: 0,
105            num_edges: 0,
106            avg_degree: 0.0,
107            clustering_coefficient: 0.0,
108            path_length_stats: PathLengthStats {
109                avg_shortest_path: 0.0,
110                diameter: 0,
111                radius: 0,
112                eccentricity_stats: DistributionStats::default(),
113            },
114            centrality_measures: CentralityMeasures {
115                degree_centrality: DistributionStats::default(),
116                betweenness_centrality: DistributionStats::default(),
117                closeness_centrality: DistributionStats::default(),
118                eigenvector_centrality: DistributionStats::default(),
119            },
120        }
121    }
122}
123
124/// Path length statistics
125#[derive(Debug, Clone)]
126pub struct PathLengthStats {
127    /// Average shortest path length
128    pub avg_shortest_path: f64,
129    /// Diameter
130    pub diameter: usize,
131    /// Radius
132    pub radius: usize,
133    /// Eccentricity statistics
134    pub eccentricity_stats: DistributionStats,
135}
136
137/// Centrality measures
138#[derive(Debug, Clone)]
139pub struct CentralityMeasures {
140    /// Degree centrality
141    pub degree_centrality: DistributionStats,
142    /// Betweenness centrality
143    pub betweenness_centrality: DistributionStats,
144    /// Closeness centrality
145    pub closeness_centrality: DistributionStats,
146    /// Eigenvector centrality
147    pub eigenvector_centrality: DistributionStats,
148}
149
150/// Statistical features
151#[derive(Debug, Clone)]
152pub struct StatisticalFeatures {
153    /// Bias statistics
154    pub bias_stats: DistributionStats,
155    /// Coupling statistics
156    pub coupling_stats: DistributionStats,
157    /// Energy landscape features
158    pub energy_landscape: EnergyLandscapeFeatures,
159    /// Correlation features
160    pub correlation_features: CorrelationFeatures,
161}
162
163impl Default for StatisticalFeatures {
164    fn default() -> Self {
165        Self {
166            bias_stats: DistributionStats::default(),
167            coupling_stats: DistributionStats::default(),
168            energy_landscape: EnergyLandscapeFeatures {
169                local_minima_estimate: 0,
170                energy_barriers: Vec::new(),
171                ruggedness: 0.0,
172                basin_sizes: DistributionStats::default(),
173            },
174            correlation_features: CorrelationFeatures {
175                autocorrelation: Vec::new(),
176                cross_correlation: HashMap::new(),
177                mutual_information: 0.0,
178            },
179        }
180    }
181}
182
183/// Energy landscape features
184#[derive(Debug, Clone)]
185pub struct EnergyLandscapeFeatures {
186    /// Number of local minima estimate
187    pub local_minima_estimate: usize,
188    /// Energy barrier estimates
189    pub energy_barriers: Vec<f64>,
190    /// Landscape ruggedness
191    pub ruggedness: f64,
192    /// Basin size distribution
193    pub basin_sizes: DistributionStats,
194}
195
196/// Correlation features
197#[derive(Debug, Clone)]
198pub struct CorrelationFeatures {
199    /// Autocorrelation function
200    pub autocorrelation: Vec<f64>,
201    /// Cross-correlation features
202    pub cross_correlation: HashMap<String, f64>,
203    /// Mutual information
204    pub mutual_information: f64,
205}
206
207/// Spectral features
208#[derive(Debug, Clone)]
209pub struct SpectralFeatures {
210    /// Eigenvalue statistics
211    pub eigenvalue_stats: DistributionStats,
212    /// Spectral gap
213    pub spectral_gap: f64,
214    /// Spectral radius
215    pub spectral_radius: f64,
216    /// Trace
217    pub trace: f64,
218    /// Condition number
219    pub condition_number: f64,
220}
221
222impl Default for SpectralFeatures {
223    fn default() -> Self {
224        Self {
225            eigenvalue_stats: DistributionStats::default(),
226            spectral_gap: 0.0,
227            spectral_radius: 0.0,
228            trace: 0.0,
229            condition_number: 1.0,
230        }
231    }
232}
233
234/// Feature extraction system
235pub struct FeatureExtractor {
236    /// Configuration
237    pub config: FeatureExtractionConfig,
238    /// Feature transformers
239    pub transformers: Vec<FeatureTransformer>,
240    /// Feature selectors
241    pub selectors: Vec<FeatureSelector>,
242    /// Dimensionality reducers
243    pub reducers: Vec<DimensionalityReducer>,
244}
245
246impl FeatureExtractor {
247    #[must_use]
248    pub const fn new(config: FeatureExtractionConfig) -> Self {
249        Self {
250            config,
251            transformers: Vec::new(),
252            selectors: Vec::new(),
253            reducers: Vec::new(),
254        }
255    }
256
257    pub fn extract_features(&mut self, problem: &IsingModel) -> ApplicationResult<ProblemFeatures> {
258        let graph_features = if self.config.enable_graph_features {
259            self.extract_graph_features(problem)?
260        } else {
261            GraphFeatures::default()
262        };
263
264        let statistical_features = if self.config.enable_statistical_features {
265            self.extract_statistical_features(problem)?
266        } else {
267            StatisticalFeatures::default()
268        };
269
270        let spectral_features = if self.config.enable_spectral_features {
271            self.extract_spectral_features(problem)?
272        } else {
273            SpectralFeatures::default()
274        };
275
276        Ok(ProblemFeatures {
277            size: problem.num_qubits,
278            density: self.calculate_density(problem),
279            graph_features,
280            statistical_features,
281            spectral_features,
282            domain_features: HashMap::new(),
283        })
284    }
285
286    fn extract_graph_features(&self, problem: &IsingModel) -> ApplicationResult<GraphFeatures> {
287        let num_vertices = problem.num_qubits;
288        let mut num_edges = 0;
289
290        // Count edges (non-zero couplings)
291        for i in 0..problem.num_qubits {
292            for j in (i + 1)..problem.num_qubits {
293                if problem.get_coupling(i, j).unwrap_or(0.0).abs() > 1e-10 {
294                    num_edges += 1;
295                }
296            }
297        }
298
299        let avg_degree = if num_vertices > 0 {
300            2.0 * num_edges as f64 / num_vertices as f64
301        } else {
302            0.0
303        };
304
305        Ok(GraphFeatures {
306            num_vertices,
307            num_edges,
308            avg_degree,
309            clustering_coefficient: 0.1, // Simplified
310            path_length_stats: PathLengthStats {
311                avg_shortest_path: avg_degree.ln().max(1.0),
312                diameter: num_vertices / 2,
313                radius: num_vertices / 4,
314                eccentricity_stats: DistributionStats::default(),
315            },
316            centrality_measures: CentralityMeasures {
317                degree_centrality: DistributionStats::default(),
318                betweenness_centrality: DistributionStats::default(),
319                closeness_centrality: DistributionStats::default(),
320                eigenvector_centrality: DistributionStats::default(),
321            },
322        })
323    }
324
325    fn extract_statistical_features(
326        &self,
327        problem: &IsingModel,
328    ) -> ApplicationResult<StatisticalFeatures> {
329        let mut bias_values = Vec::new();
330        let mut coupling_values = Vec::new();
331
332        // Collect bias values
333        for i in 0..problem.num_qubits {
334            bias_values.push(problem.get_bias(i).unwrap_or(0.0));
335        }
336
337        // Collect coupling values
338        for i in 0..problem.num_qubits {
339            for j in (i + 1)..problem.num_qubits {
340                let coupling = problem.get_coupling(i, j).unwrap_or(0.0);
341                if coupling.abs() > 1e-10 {
342                    coupling_values.push(coupling);
343                }
344            }
345        }
346
347        Ok(StatisticalFeatures {
348            bias_stats: self.calculate_distribution_stats(&bias_values),
349            coupling_stats: self.calculate_distribution_stats(&coupling_values),
350            energy_landscape: EnergyLandscapeFeatures {
351                local_minima_estimate: (problem.num_qubits as f64).sqrt() as usize,
352                energy_barriers: vec![1.0, 2.0, 3.0],
353                ruggedness: 0.5,
354                basin_sizes: DistributionStats::default(),
355            },
356            correlation_features: CorrelationFeatures {
357                autocorrelation: vec![1.0, 0.8, 0.6, 0.4, 0.2],
358                cross_correlation: HashMap::new(),
359                mutual_information: 0.3,
360            },
361        })
362    }
363
364    fn extract_spectral_features(
365        &self,
366        problem: &IsingModel,
367    ) -> ApplicationResult<SpectralFeatures> {
368        // Simplified spectral analysis
369        let n = problem.num_qubits as f64;
370        let spectral_gap_estimate = 1.0 / n.sqrt();
371
372        Ok(SpectralFeatures {
373            eigenvalue_stats: DistributionStats {
374                mean: 0.0,
375                std_dev: 1.0,
376                min: -n,
377                max: n,
378                skewness: 0.0,
379                kurtosis: 3.0,
380            },
381            spectral_gap: spectral_gap_estimate,
382            spectral_radius: n,
383            trace: 0.0,
384            condition_number: n,
385        })
386    }
387
388    fn calculate_density(&self, problem: &IsingModel) -> f64 {
389        let mut num_edges = 0;
390        let max_edges = problem.num_qubits * (problem.num_qubits - 1) / 2;
391
392        for i in 0..problem.num_qubits {
393            for j in (i + 1)..problem.num_qubits {
394                if problem.get_coupling(i, j).unwrap_or(0.0).abs() > 1e-10 {
395                    num_edges += 1;
396                }
397            }
398        }
399
400        if max_edges > 0 {
401            f64::from(num_edges) / max_edges as f64
402        } else {
403            0.0
404        }
405    }
406
407    fn calculate_distribution_stats(&self, values: &[f64]) -> DistributionStats {
408        if values.is_empty() {
409            return DistributionStats::default();
410        }
411
412        let mean = values.iter().sum::<f64>() / values.len() as f64;
413        let variance = values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / values.len() as f64;
414        let std_dev = variance.sqrt();
415        let min = values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
416        let max = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
417
418        DistributionStats {
419            mean,
420            std_dev,
421            min,
422            max,
423            skewness: 0.0, // Simplified
424            kurtosis: 3.0, // Simplified
425        }
426    }
427
428    #[must_use]
429    pub fn vectorize_features(&self, features: &ProblemFeatures) -> FeatureVector {
430        let mut values = Vec::new();
431        let mut names = Vec::new();
432
433        // Basic features
434        values.push(features.size as f64);
435        names.push("size".to_string());
436        values.push(features.density);
437        names.push("density".to_string());
438
439        // Graph features
440        if self.config.enable_graph_features {
441            values.push(features.graph_features.num_vertices as f64);
442            names.push("num_vertices".to_string());
443            values.push(features.graph_features.num_edges as f64);
444            names.push("num_edges".to_string());
445            values.push(features.graph_features.avg_degree);
446            names.push("avg_degree".to_string());
447            values.push(features.graph_features.clustering_coefficient);
448            names.push("clustering_coefficient".to_string());
449        }
450
451        // Statistical features
452        if self.config.enable_statistical_features {
453            values.push(features.statistical_features.bias_stats.mean);
454            names.push("bias_mean".to_string());
455            values.push(features.statistical_features.bias_stats.std_dev);
456            names.push("bias_std".to_string());
457            values.push(features.statistical_features.coupling_stats.mean);
458            names.push("coupling_mean".to_string());
459            values.push(features.statistical_features.coupling_stats.std_dev);
460            names.push("coupling_std".to_string());
461        }
462
463        // Spectral features
464        if self.config.enable_spectral_features {
465            values.push(features.spectral_features.spectral_gap);
466            names.push("spectral_gap".to_string());
467            values.push(features.spectral_features.spectral_radius);
468            names.push("spectral_radius".to_string());
469            values.push(features.spectral_features.condition_number);
470            names.push("condition_number".to_string());
471        }
472
473        FeatureVector {
474            values,
475            names,
476            normalization: None,
477        }
478    }
479}
480
481/// Feature transformer
482#[derive(Debug)]
483pub struct FeatureTransformer {
484    /// Transformer type
485    pub transformer_type: TransformerType,
486    /// Parameters
487    pub parameters: HashMap<String, f64>,
488    /// Fitted state
489    pub is_fitted: bool,
490}
491
492/// Transformer types
493#[derive(Debug, Clone, PartialEq, Eq)]
494pub enum TransformerType {
495    /// Polynomial features
496    Polynomial,
497    /// Interaction features
498    Interaction,
499    /// Logarithmic transform
500    Logarithmic,
501    /// Box-Cox transform
502    BoxCox,
503    /// Custom transform
504    Custom(String),
505}
506
507/// Feature selector
508#[derive(Debug)]
509pub struct FeatureSelector {
510    /// Selection method
511    pub method: FeatureSelectionMethod,
512    /// Selected features
513    pub selected_features: Vec<usize>,
514    /// Feature importance scores
515    pub importance_scores: Vec<f64>,
516}
517
518/// Dimensionality reducer
519#[derive(Debug)]
520pub struct DimensionalityReducer {
521    /// Reduction method
522    pub method: DimensionalityReduction,
523    /// Target dimensions
524    pub target_dims: usize,
525    /// Transformation matrix
526    pub transformation_matrix: Option<Vec<Vec<f64>>>,
527    /// Explained variance
528    pub explained_variance: Vec<f64>,
529}
530
531/// Experience database for storing optimization experiences
532pub struct ExperienceDatabase {
533    /// Stored experiences
534    pub experiences: VecDeque<OptimizationExperience>,
535    /// Index for fast retrieval
536    pub index: ExperienceIndex,
537    /// Similarity cache
538    pub similarity_cache: HashMap<String, Vec<(String, f64)>>,
539    /// Statistics
540    pub statistics: DatabaseStatistics,
541}
542
543/// Optimization experience record
544#[derive(Debug, Clone)]
545pub struct OptimizationExperience {
546    /// Unique experience identifier
547    pub id: String,
548    /// Problem characteristics
549    pub problem_features: ProblemFeatures,
550    /// Configuration used
551    pub configuration: OptimizationConfiguration,
552    /// Results achieved
553    pub results: OptimizationResults,
554    /// Timestamp
555    pub timestamp: Instant,
556    /// Problem domain
557    pub domain: ProblemDomain,
558    /// Success metrics
559    pub success_metrics: SuccessMetrics,
560}
561
562use std::time::Duration;
563
564/// Optimization configuration
565#[derive(Debug, Clone)]
566pub struct OptimizationConfiguration {
567    /// Algorithm used
568    pub algorithm: AlgorithmType,
569    /// Hyperparameters
570    pub hyperparameters: HashMap<String, f64>,
571    /// Architecture specification
572    pub architecture: Option<ArchitectureSpec>,
573    /// Resource allocation
574    pub resources: ResourceAllocation,
575}
576
577/// Algorithm types
578#[derive(Debug, Clone, PartialEq)]
579pub enum AlgorithmType {
580    /// Simulated annealing
581    SimulatedAnnealing,
582    /// Quantum annealing
583    QuantumAnnealing,
584    /// Tabu search
585    TabuSearch,
586    /// Genetic algorithm
587    GeneticAlgorithm,
588    /// Particle swarm optimization
589    ParticleSwarm,
590    /// Ant colony optimization
591    AntColony,
592    /// Variable neighborhood search
593    VariableNeighborhood,
594    /// Hybrid algorithm
595    Hybrid(Vec<Self>),
596}
597
598/// Architecture specification
599#[derive(Debug, Clone)]
600pub struct ArchitectureSpec {
601    /// Layer specifications
602    pub layers: Vec<LayerSpec>,
603    /// Connection pattern
604    pub connections: ConnectionPattern,
605    /// Optimization settings
606    pub optimization: OptimizationSettings,
607}
608
609/// Layer specification
610#[derive(Debug, Clone)]
611pub struct LayerSpec {
612    /// Layer type
613    pub layer_type: LayerType,
614    /// Input dimension
615    pub input_dim: usize,
616    /// Output dimension
617    pub output_dim: usize,
618    /// Activation function
619    pub activation: ActivationFunction,
620    /// Dropout rate
621    pub dropout: f64,
622    /// Additional parameters
623    pub parameters: HashMap<String, f64>,
624}
625
626/// Connection patterns
627#[derive(Debug, Clone, PartialEq, Eq)]
628pub enum ConnectionPattern {
629    /// Sequential connections
630    Sequential,
631    /// Skip connections
632    SkipConnections,
633    /// Dense connections
634    DenseConnections,
635    /// Residual connections
636    ResidualConnections,
637    /// Custom pattern
638    Custom(Vec<(usize, usize)>),
639}
640
641/// Optimization settings
642#[derive(Debug, Clone)]
643pub struct OptimizationSettings {
644    /// Optimizer type
645    pub optimizer: OptimizerType,
646    /// Learning rate
647    pub learning_rate: f64,
648    /// Batch size
649    pub batch_size: usize,
650    /// Number of epochs
651    pub epochs: usize,
652    /// Regularization
653    pub regularization: RegularizationConfig,
654}
655
656/// Optimizer types
657#[derive(Debug, Clone, PartialEq, Eq)]
658pub enum OptimizerType {
659    SGD,
660    Adam,
661    AdamW,
662    RMSprop,
663    Adagrad,
664    Adadelta,
665    LBFGS,
666}
667
668/// Regularization configuration
669#[derive(Debug, Clone)]
670pub struct RegularizationConfig {
671    /// L1 regularization weight
672    pub l1_weight: f64,
673    /// L2 regularization weight
674    pub l2_weight: f64,
675    /// Dropout rate
676    pub dropout: f64,
677    /// Batch normalization
678    pub batch_norm: bool,
679    /// Early stopping
680    pub early_stopping: bool,
681}
682
683/// Resource allocation
684#[derive(Debug, Clone)]
685pub struct ResourceAllocation {
686    /// CPU allocation
687    pub cpu: f64,
688    /// Memory allocation (MB)
689    pub memory: usize,
690    /// GPU allocation
691    pub gpu: f64,
692    /// Time allocation
693    pub time: Duration,
694}
695
696/// Optimization results
697#[derive(Debug, Clone)]
698pub struct OptimizationResults {
699    /// Final objective values
700    pub objective_values: Vec<f64>,
701    /// Execution time
702    pub execution_time: Duration,
703    /// Resource usage
704    pub resource_usage: ResourceUsage,
705    /// Convergence metrics
706    pub convergence: ConvergenceMetrics,
707    /// Solution quality metrics
708    pub quality_metrics: QualityMetrics,
709}
710
711/// Resource usage tracking
712#[derive(Debug, Clone)]
713pub struct ResourceUsage {
714    /// Peak CPU usage
715    pub peak_cpu: f64,
716    /// Peak memory usage (MB)
717    pub peak_memory: usize,
718    /// GPU utilization
719    pub gpu_utilization: f64,
720    /// Energy consumption
721    pub energy_consumption: f64,
722}
723
724/// Convergence metrics
725#[derive(Debug, Clone)]
726pub struct ConvergenceMetrics {
727    /// Number of iterations
728    pub iterations: usize,
729    /// Final convergence rate
730    pub convergence_rate: f64,
731    /// Plateau detection
732    pub plateau_detected: bool,
733    /// Convergence confidence
734    pub confidence: f64,
735}
736
737/// Solution quality metrics
738#[derive(Debug, Clone)]
739pub struct QualityMetrics {
740    /// Objective function value
741    pub objective_value: f64,
742    /// Constraint violation
743    pub constraint_violation: f64,
744    /// Robustness score
745    pub robustness: f64,
746    /// Diversity score
747    pub diversity: f64,
748}
749
750/// Problem domains
751#[derive(Debug, Clone, PartialEq, Eq, Hash)]
752pub enum ProblemDomain {
753    /// Combinatorial optimization
754    Combinatorial,
755    /// Portfolio optimization
756    Portfolio,
757    /// Scheduling
758    Scheduling,
759    /// Graph problems
760    Graph,
761    /// Machine learning
762    MachineLearning,
763    /// Physics simulation
764    Physics,
765    /// Chemistry
766    Chemistry,
767    /// Custom domain
768    Custom(String),
769}
770
771/// Success metrics
772#[derive(Debug, Clone)]
773pub struct SuccessMetrics {
774    /// Overall success score
775    pub success_score: f64,
776    /// Performance relative to baseline
777    pub relative_performance: f64,
778    /// User satisfaction score
779    pub user_satisfaction: f64,
780    /// Recommendation confidence
781    pub recommendation_confidence: f64,
782}
783
784/// Experience indexing system
785#[derive(Debug)]
786pub struct ExperienceIndex {
787    /// Domain-based index
788    pub domain_index: HashMap<ProblemDomain, Vec<String>>,
789    /// Size-based index
790    pub size_index: BTreeMap<usize, Vec<String>>,
791    /// Performance-based index
792    pub performance_index: BTreeMap<String, Vec<String>>,
793    /// Feature-based index
794    pub feature_index: HashMap<String, Vec<String>>,
795}
796
797/// Database statistics
798#[derive(Debug, Clone)]
799pub struct DatabaseStatistics {
800    /// Total experiences
801    pub total_experiences: usize,
802    /// Experiences per domain
803    pub domain_distribution: HashMap<ProblemDomain, usize>,
804    /// Average performance
805    pub avg_performance: f64,
806    /// Coverage statistics
807    pub coverage_stats: CoverageStatistics,
808}
809
810/// Coverage statistics
811#[derive(Debug, Clone)]
812pub struct CoverageStatistics {
813    /// Feature space coverage
814    pub feature_coverage: f64,
815    /// Problem size coverage
816    pub size_coverage: (usize, usize),
817    /// Domain coverage
818    pub domain_coverage: f64,
819    /// Performance range coverage
820    pub performance_range: (f64, f64),
821}
822
823impl ExperienceDatabase {
824    #[must_use]
825    pub fn new() -> Self {
826        Self {
827            experiences: VecDeque::new(),
828            index: ExperienceIndex {
829                domain_index: HashMap::new(),
830                size_index: BTreeMap::new(),
831                performance_index: BTreeMap::new(),
832                feature_index: HashMap::new(),
833            },
834            similarity_cache: HashMap::new(),
835            statistics: DatabaseStatistics {
836                total_experiences: 0,
837                domain_distribution: HashMap::new(),
838                avg_performance: 0.0,
839                coverage_stats: CoverageStatistics {
840                    feature_coverage: 0.0,
841                    size_coverage: (0, 0),
842                    domain_coverage: 0.0,
843                    performance_range: (0.0, 1.0),
844                },
845            },
846        }
847    }
848
849    pub fn add_experience(&mut self, experience: OptimizationExperience) {
850        self.experiences.push_back(experience.clone());
851        self.update_index(&experience);
852        self.update_statistics();
853
854        // Limit buffer size
855        if self.experiences.len() > 10_000 {
856            if let Some(removed) = self.experiences.pop_front() {
857                self.remove_from_index(&removed);
858            }
859        }
860    }
861
862    fn update_index(&mut self, experience: &OptimizationExperience) {
863        // Update domain index
864        self.index
865            .domain_index
866            .entry(experience.domain.clone())
867            .or_insert_with(Vec::new)
868            .push(experience.id.clone());
869
870        // Update size index
871        self.index
872            .size_index
873            .entry(experience.problem_features.size)
874            .or_insert_with(Vec::new)
875            .push(experience.id.clone());
876    }
877
878    fn remove_from_index(&mut self, experience: &OptimizationExperience) {
879        // Remove from domain index
880        if let Some(ids) = self.index.domain_index.get_mut(&experience.domain) {
881            ids.retain(|id| id != &experience.id);
882        }
883
884        // Remove from size index
885        if let Some(ids) = self
886            .index
887            .size_index
888            .get_mut(&experience.problem_features.size)
889        {
890            ids.retain(|id| id != &experience.id);
891        }
892    }
893
894    fn update_statistics(&mut self) {
895        self.statistics.total_experiences = self.experiences.len();
896
897        if !self.experiences.is_empty() {
898            let total_performance: f64 = self
899                .experiences
900                .iter()
901                .map(|exp| exp.results.quality_metrics.objective_value)
902                .sum();
903            self.statistics.avg_performance = total_performance / self.experiences.len() as f64;
904        }
905
906        // Update domain distribution
907        self.statistics.domain_distribution.clear();
908        for experience in &self.experiences {
909            *self
910                .statistics
911                .domain_distribution
912                .entry(experience.domain.clone())
913                .or_insert(0) += 1;
914        }
915    }
916
917    pub fn find_similar_experiences(
918        &self,
919        features: &ProblemFeatures,
920        limit: usize,
921    ) -> ApplicationResult<Vec<OptimizationExperience>> {
922        let mut similarities = Vec::new();
923
924        for experience in &self.experiences {
925            let similarity = self.calculate_similarity(features, &experience.problem_features);
926            similarities.push((experience.clone(), similarity));
927        }
928
929        // Sort by similarity (descending)
930        similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
931
932        Ok(similarities
933            .into_iter()
934            .take(limit)
935            .map(|(exp, _)| exp)
936            .collect())
937    }
938
939    fn calculate_similarity(
940        &self,
941        features1: &ProblemFeatures,
942        features2: &ProblemFeatures,
943    ) -> f64 {
944        // Simple similarity calculation based on size and density
945        let size_diff = (features1.size as f64 - features2.size as f64).abs()
946            / features1.size.max(features2.size) as f64;
947        let density_diff = (features1.density - features2.density).abs();
948
949        let size_similarity = 1.0 - size_diff;
950        let density_similarity = 1.0 - density_diff;
951
952        f64::midpoint(size_similarity, density_similarity)
953    }
954}
955
956#[cfg(test)]
957mod tests {
958    use super::*;
959
960    #[test]
961    fn test_feature_extractor_creation() {
962        let config = FeatureExtractionConfig::default();
963        let extractor = FeatureExtractor::new(config);
964        assert!(extractor.config.enable_graph_features);
965    }
966
967    #[test]
968    fn test_distribution_stats() {
969        let stats = DistributionStats::default();
970        assert_eq!(stats.mean, 0.0);
971        assert_eq!(stats.std_dev, 1.0);
972    }
973
974    #[test]
975    fn test_experience_database() {
976        let db = ExperienceDatabase::new();
977        assert_eq!(db.statistics.total_experiences, 0);
978    }
979}