1use 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#[derive(Debug, Clone)]
15pub struct ProblemFeatures {
16 pub size: usize,
18 pub density: f64,
20 pub graph_features: GraphFeatures,
22 pub statistical_features: StatisticalFeatures,
24 pub spectral_features: SpectralFeatures,
26 pub domain_features: HashMap<String, f64>,
28}
29
30#[derive(Debug, Clone)]
32pub struct FeatureVector {
33 pub values: Vec<f64>,
35 pub names: Vec<String>,
37 pub normalization: Option<NormalizationParams>,
39}
40
41#[derive(Debug, Clone)]
43pub struct NormalizationParams {
44 pub mean: Vec<f64>,
46 pub std: Vec<f64>,
48 pub min: Vec<f64>,
50 pub max: Vec<f64>,
52}
53
54#[derive(Debug, Clone)]
56pub struct DistributionStats {
57 pub mean: f64,
59 pub std_dev: f64,
61 pub min: f64,
63 pub max: f64,
65 pub skewness: f64,
67 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#[derive(Debug, Clone)]
86pub struct GraphFeatures {
87 pub num_vertices: usize,
89 pub num_edges: usize,
91 pub avg_degree: f64,
93 pub clustering_coefficient: f64,
95 pub path_length_stats: PathLengthStats,
97 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#[derive(Debug, Clone)]
126pub struct PathLengthStats {
127 pub avg_shortest_path: f64,
129 pub diameter: usize,
131 pub radius: usize,
133 pub eccentricity_stats: DistributionStats,
135}
136
137#[derive(Debug, Clone)]
139pub struct CentralityMeasures {
140 pub degree_centrality: DistributionStats,
142 pub betweenness_centrality: DistributionStats,
144 pub closeness_centrality: DistributionStats,
146 pub eigenvector_centrality: DistributionStats,
148}
149
150#[derive(Debug, Clone)]
152pub struct StatisticalFeatures {
153 pub bias_stats: DistributionStats,
155 pub coupling_stats: DistributionStats,
157 pub energy_landscape: EnergyLandscapeFeatures,
159 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#[derive(Debug, Clone)]
185pub struct EnergyLandscapeFeatures {
186 pub local_minima_estimate: usize,
188 pub energy_barriers: Vec<f64>,
190 pub ruggedness: f64,
192 pub basin_sizes: DistributionStats,
194}
195
196#[derive(Debug, Clone)]
198pub struct CorrelationFeatures {
199 pub autocorrelation: Vec<f64>,
201 pub cross_correlation: HashMap<String, f64>,
203 pub mutual_information: f64,
205}
206
207#[derive(Debug, Clone)]
209pub struct SpectralFeatures {
210 pub eigenvalue_stats: DistributionStats,
212 pub spectral_gap: f64,
214 pub spectral_radius: f64,
216 pub trace: f64,
218 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
234pub struct FeatureExtractor {
236 pub config: FeatureExtractionConfig,
238 pub transformers: Vec<FeatureTransformer>,
240 pub selectors: Vec<FeatureSelector>,
242 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 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, 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 for i in 0..problem.num_qubits {
334 bias_values.push(problem.get_bias(i).unwrap_or(0.0));
335 }
336
337 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 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, kurtosis: 3.0, }
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 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 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 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 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#[derive(Debug)]
483pub struct FeatureTransformer {
484 pub transformer_type: TransformerType,
486 pub parameters: HashMap<String, f64>,
488 pub is_fitted: bool,
490}
491
492#[derive(Debug, Clone, PartialEq, Eq)]
494pub enum TransformerType {
495 Polynomial,
497 Interaction,
499 Logarithmic,
501 BoxCox,
503 Custom(String),
505}
506
507#[derive(Debug)]
509pub struct FeatureSelector {
510 pub method: FeatureSelectionMethod,
512 pub selected_features: Vec<usize>,
514 pub importance_scores: Vec<f64>,
516}
517
518#[derive(Debug)]
520pub struct DimensionalityReducer {
521 pub method: DimensionalityReduction,
523 pub target_dims: usize,
525 pub transformation_matrix: Option<Vec<Vec<f64>>>,
527 pub explained_variance: Vec<f64>,
529}
530
531pub struct ExperienceDatabase {
533 pub experiences: VecDeque<OptimizationExperience>,
535 pub index: ExperienceIndex,
537 pub similarity_cache: HashMap<String, Vec<(String, f64)>>,
539 pub statistics: DatabaseStatistics,
541}
542
543#[derive(Debug, Clone)]
545pub struct OptimizationExperience {
546 pub id: String,
548 pub problem_features: ProblemFeatures,
550 pub configuration: OptimizationConfiguration,
552 pub results: OptimizationResults,
554 pub timestamp: Instant,
556 pub domain: ProblemDomain,
558 pub success_metrics: SuccessMetrics,
560}
561
562use std::time::Duration;
563
564#[derive(Debug, Clone)]
566pub struct OptimizationConfiguration {
567 pub algorithm: AlgorithmType,
569 pub hyperparameters: HashMap<String, f64>,
571 pub architecture: Option<ArchitectureSpec>,
573 pub resources: ResourceAllocation,
575}
576
577#[derive(Debug, Clone, PartialEq)]
579pub enum AlgorithmType {
580 SimulatedAnnealing,
582 QuantumAnnealing,
584 TabuSearch,
586 GeneticAlgorithm,
588 ParticleSwarm,
590 AntColony,
592 VariableNeighborhood,
594 Hybrid(Vec<Self>),
596}
597
598#[derive(Debug, Clone)]
600pub struct ArchitectureSpec {
601 pub layers: Vec<LayerSpec>,
603 pub connections: ConnectionPattern,
605 pub optimization: OptimizationSettings,
607}
608
609#[derive(Debug, Clone)]
611pub struct LayerSpec {
612 pub layer_type: LayerType,
614 pub input_dim: usize,
616 pub output_dim: usize,
618 pub activation: ActivationFunction,
620 pub dropout: f64,
622 pub parameters: HashMap<String, f64>,
624}
625
626#[derive(Debug, Clone, PartialEq, Eq)]
628pub enum ConnectionPattern {
629 Sequential,
631 SkipConnections,
633 DenseConnections,
635 ResidualConnections,
637 Custom(Vec<(usize, usize)>),
639}
640
641#[derive(Debug, Clone)]
643pub struct OptimizationSettings {
644 pub optimizer: OptimizerType,
646 pub learning_rate: f64,
648 pub batch_size: usize,
650 pub epochs: usize,
652 pub regularization: RegularizationConfig,
654}
655
656#[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#[derive(Debug, Clone)]
670pub struct RegularizationConfig {
671 pub l1_weight: f64,
673 pub l2_weight: f64,
675 pub dropout: f64,
677 pub batch_norm: bool,
679 pub early_stopping: bool,
681}
682
683#[derive(Debug, Clone)]
685pub struct ResourceAllocation {
686 pub cpu: f64,
688 pub memory: usize,
690 pub gpu: f64,
692 pub time: Duration,
694}
695
696#[derive(Debug, Clone)]
698pub struct OptimizationResults {
699 pub objective_values: Vec<f64>,
701 pub execution_time: Duration,
703 pub resource_usage: ResourceUsage,
705 pub convergence: ConvergenceMetrics,
707 pub quality_metrics: QualityMetrics,
709}
710
711#[derive(Debug, Clone)]
713pub struct ResourceUsage {
714 pub peak_cpu: f64,
716 pub peak_memory: usize,
718 pub gpu_utilization: f64,
720 pub energy_consumption: f64,
722}
723
724#[derive(Debug, Clone)]
726pub struct ConvergenceMetrics {
727 pub iterations: usize,
729 pub convergence_rate: f64,
731 pub plateau_detected: bool,
733 pub confidence: f64,
735}
736
737#[derive(Debug, Clone)]
739pub struct QualityMetrics {
740 pub objective_value: f64,
742 pub constraint_violation: f64,
744 pub robustness: f64,
746 pub diversity: f64,
748}
749
750#[derive(Debug, Clone, PartialEq, Eq, Hash)]
752pub enum ProblemDomain {
753 Combinatorial,
755 Portfolio,
757 Scheduling,
759 Graph,
761 MachineLearning,
763 Physics,
765 Chemistry,
767 Custom(String),
769}
770
771#[derive(Debug, Clone)]
773pub struct SuccessMetrics {
774 pub success_score: f64,
776 pub relative_performance: f64,
778 pub user_satisfaction: f64,
780 pub recommendation_confidence: f64,
782}
783
784#[derive(Debug)]
786pub struct ExperienceIndex {
787 pub domain_index: HashMap<ProblemDomain, Vec<String>>,
789 pub size_index: BTreeMap<usize, Vec<String>>,
791 pub performance_index: BTreeMap<String, Vec<String>>,
793 pub feature_index: HashMap<String, Vec<String>>,
795}
796
797#[derive(Debug, Clone)]
799pub struct DatabaseStatistics {
800 pub total_experiences: usize,
802 pub domain_distribution: HashMap<ProblemDomain, usize>,
804 pub avg_performance: f64,
806 pub coverage_stats: CoverageStatistics,
808}
809
810#[derive(Debug, Clone)]
812pub struct CoverageStatistics {
813 pub feature_coverage: f64,
815 pub size_coverage: (usize, usize),
817 pub domain_coverage: f64,
819 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 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 self.index
865 .domain_index
866 .entry(experience.domain.clone())
867 .or_insert_with(Vec::new)
868 .push(experience.id.clone());
869
870 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 if let Some(ids) = self.index.domain_index.get_mut(&experience.domain) {
881 ids.retain(|id| id != &experience.id);
882 }
883
884 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 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 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 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}