Skip to main content

quantrs2_anneal/meta_learning/
features.rs

1//! Feature Extraction for Meta-Learning Optimization
2//!
3//! This module contains all feature extraction types and implementations used
4//! by the meta-learning optimization system.
5
6use super::config::{
7    DimensionalityReduction, FeatureExtractionConfig, FeatureNormalization, FeatureSelectionMethod,
8};
9use crate::applications::ApplicationResult;
10use crate::ising::IsingModel;
11use std::collections::HashMap;
12use std::time::Instant;
13
14/// Problem feature representation
15#[derive(Debug, Clone)]
16pub struct ProblemFeatures {
17    /// Problem size
18    pub size: usize,
19    /// Problem density
20    pub density: f64,
21    /// Graph-based features
22    pub graph_features: GraphFeatures,
23    /// Statistical features
24    pub statistical_features: StatisticalFeatures,
25    /// Spectral features
26    pub spectral_features: SpectralFeatures,
27    /// Domain-specific features
28    pub domain_features: HashMap<String, f64>,
29}
30
31/// Graph-based features
32#[derive(Debug, Clone)]
33pub struct GraphFeatures {
34    /// Number of vertices
35    pub num_vertices: usize,
36    /// Number of edges
37    pub num_edges: usize,
38    /// Average degree
39    pub avg_degree: f64,
40    /// Clustering coefficient
41    pub clustering_coefficient: f64,
42    /// Path length statistics
43    pub path_length_stats: PathLengthStats,
44    /// Centrality measures
45    pub centrality_measures: CentralityMeasures,
46}
47
48/// Path length statistics
49#[derive(Debug, Clone)]
50pub struct PathLengthStats {
51    /// Average shortest path length
52    pub avg_shortest_path: f64,
53    /// Diameter
54    pub diameter: usize,
55    /// Radius
56    pub radius: usize,
57    /// Eccentricity distribution
58    pub eccentricity_stats: DistributionStats,
59}
60
61/// Centrality measures
62#[derive(Debug, Clone)]
63pub struct CentralityMeasures {
64    /// Degree centrality stats
65    pub degree_centrality: DistributionStats,
66    /// Betweenness centrality stats
67    pub betweenness_centrality: DistributionStats,
68    /// Closeness centrality stats
69    pub closeness_centrality: DistributionStats,
70    /// Eigenvector centrality stats
71    pub eigenvector_centrality: DistributionStats,
72}
73
74/// Distribution statistics
75#[derive(Debug, Clone)]
76pub struct DistributionStats {
77    /// Mean
78    pub mean: f64,
79    /// Standard deviation
80    pub std_dev: f64,
81    /// Minimum
82    pub min: f64,
83    /// Maximum
84    pub max: f64,
85    /// Skewness
86    pub skewness: f64,
87    /// Kurtosis
88    pub kurtosis: f64,
89}
90
91/// Statistical features
92#[derive(Debug, Clone)]
93pub struct StatisticalFeatures {
94    /// Bias statistics
95    pub bias_stats: DistributionStats,
96    /// Coupling statistics
97    pub coupling_stats: DistributionStats,
98    /// Energy landscape features
99    pub energy_landscape: EnergyLandscapeFeatures,
100    /// Correlation features
101    pub correlation_features: CorrelationFeatures,
102}
103
104/// Energy landscape features
105#[derive(Debug, Clone)]
106pub struct EnergyLandscapeFeatures {
107    /// Number of local minima estimate
108    pub local_minima_estimate: usize,
109    /// Energy barrier estimates
110    pub energy_barriers: Vec<f64>,
111    /// Landscape ruggedness
112    pub ruggedness: f64,
113    /// Basin size distribution
114    pub basin_sizes: DistributionStats,
115}
116
117/// Correlation features
118#[derive(Debug, Clone)]
119pub struct CorrelationFeatures {
120    /// Autocorrelation function
121    pub autocorrelation: Vec<f64>,
122    /// Cross-correlation features
123    pub cross_correlation: HashMap<String, f64>,
124    /// Mutual information
125    pub mutual_information: f64,
126}
127
128/// Spectral features
129#[derive(Debug, Clone)]
130pub struct SpectralFeatures {
131    /// Eigenvalue statistics
132    pub eigenvalue_stats: DistributionStats,
133    /// Spectral gap
134    pub spectral_gap: f64,
135    /// Spectral radius
136    pub spectral_radius: f64,
137    /// Trace
138    pub trace: f64,
139    /// Condition number
140    pub condition_number: f64,
141}
142
143/// Feature extraction system
144#[derive(Debug)]
145pub struct FeatureExtractor {
146    /// Configuration
147    pub config: FeatureExtractionConfig,
148    /// Feature transformers
149    pub transformers: Vec<FeatureTransformer>,
150    /// Feature selectors
151    pub selectors: Vec<FeatureSelector>,
152    /// Dimensionality reducers
153    pub reducers: Vec<DimensionalityReducer>,
154}
155
156/// Feature transformer
157#[derive(Debug)]
158pub struct FeatureTransformer {
159    /// Transformer type
160    pub transformer_type: TransformerType,
161    /// Parameters
162    pub parameters: HashMap<String, f64>,
163    /// Fitted state
164    pub is_fitted: bool,
165}
166
167/// Transformer types
168#[derive(Debug, Clone, PartialEq, Eq)]
169pub enum TransformerType {
170    /// Polynomial features
171    Polynomial,
172    /// Interaction features
173    Interaction,
174    /// Logarithmic transform
175    Logarithmic,
176    /// Box-Cox transform
177    BoxCox,
178    /// Custom transform
179    Custom(String),
180}
181
182/// Feature selector
183#[derive(Debug)]
184pub struct FeatureSelector {
185    /// Selection method
186    pub method: FeatureSelectionMethod,
187    /// Selected features
188    pub selected_features: Vec<usize>,
189    /// Feature importance scores
190    pub importance_scores: Vec<f64>,
191}
192
193/// Dimensionality reducer
194#[derive(Debug)]
195pub struct DimensionalityReducer {
196    /// Reduction method
197    pub method: DimensionalityReduction,
198    /// Target dimensions
199    pub target_dims: usize,
200    /// Transformation matrix
201    pub transformation_matrix: Option<Vec<Vec<f64>>>,
202    /// Explained variance
203    pub explained_variance: Vec<f64>,
204}
205
206impl FeatureExtractor {
207    #[must_use]
208    pub const fn new(config: FeatureExtractionConfig) -> Self {
209        Self {
210            config,
211            transformers: Vec::new(),
212            selectors: Vec::new(),
213            reducers: Vec::new(),
214        }
215    }
216
217    pub fn extract_features(&mut self, problem: &IsingModel) -> ApplicationResult<ProblemFeatures> {
218        let graph_features = if self.config.enable_graph_features {
219            self.extract_graph_features(problem)?
220        } else {
221            GraphFeatures::default()
222        };
223
224        let statistical_features = if self.config.enable_statistical_features {
225            self.extract_statistical_features(problem)?
226        } else {
227            StatisticalFeatures::default()
228        };
229
230        let spectral_features = if self.config.enable_spectral_features {
231            self.extract_spectral_features(problem)?
232        } else {
233            SpectralFeatures::default()
234        };
235
236        Ok(ProblemFeatures {
237            size: problem.num_qubits,
238            density: self.calculate_density(problem),
239            graph_features,
240            statistical_features,
241            spectral_features,
242            domain_features: HashMap::new(),
243        })
244    }
245
246    fn extract_graph_features(&self, problem: &IsingModel) -> ApplicationResult<GraphFeatures> {
247        let num_vertices = problem.num_qubits;
248        let mut num_edges = 0;
249
250        // Count edges (non-zero couplings)
251        for i in 0..problem.num_qubits {
252            for j in (i + 1)..problem.num_qubits {
253                if problem.get_coupling(i, j).unwrap_or(0.0).abs() > 1e-10 {
254                    num_edges += 1;
255                }
256            }
257        }
258
259        let avg_degree = if num_vertices > 0 {
260            2.0 * num_edges as f64 / num_vertices as f64
261        } else {
262            0.0
263        };
264
265        Ok(GraphFeatures {
266            num_vertices,
267            num_edges,
268            avg_degree,
269            clustering_coefficient: 0.1, // Simplified
270            path_length_stats: PathLengthStats {
271                avg_shortest_path: avg_degree.ln().max(1.0),
272                diameter: num_vertices / 2,
273                radius: num_vertices / 4,
274                eccentricity_stats: DistributionStats::default(),
275            },
276            centrality_measures: CentralityMeasures {
277                degree_centrality: DistributionStats::default(),
278                betweenness_centrality: DistributionStats::default(),
279                closeness_centrality: DistributionStats::default(),
280                eigenvector_centrality: DistributionStats::default(),
281            },
282        })
283    }
284
285    fn extract_statistical_features(
286        &self,
287        problem: &IsingModel,
288    ) -> ApplicationResult<StatisticalFeatures> {
289        let mut bias_values = Vec::new();
290        let mut coupling_values = Vec::new();
291
292        // Collect bias values
293        for i in 0..problem.num_qubits {
294            bias_values.push(problem.get_bias(i).unwrap_or(0.0));
295        }
296
297        // Collect coupling values
298        for i in 0..problem.num_qubits {
299            for j in (i + 1)..problem.num_qubits {
300                let coupling = problem.get_coupling(i, j).unwrap_or(0.0);
301                if coupling.abs() > 1e-10 {
302                    coupling_values.push(coupling);
303                }
304            }
305        }
306
307        Ok(StatisticalFeatures {
308            bias_stats: self.calculate_distribution_stats(&bias_values),
309            coupling_stats: self.calculate_distribution_stats(&coupling_values),
310            energy_landscape: EnergyLandscapeFeatures {
311                local_minima_estimate: (problem.num_qubits as f64).sqrt() as usize,
312                energy_barriers: vec![1.0, 2.0, 3.0],
313                ruggedness: 0.5,
314                basin_sizes: DistributionStats::default(),
315            },
316            correlation_features: CorrelationFeatures {
317                autocorrelation: vec![1.0, 0.8, 0.6, 0.4, 0.2],
318                cross_correlation: HashMap::new(),
319                mutual_information: 0.3,
320            },
321        })
322    }
323
324    fn extract_spectral_features(
325        &self,
326        problem: &IsingModel,
327    ) -> ApplicationResult<SpectralFeatures> {
328        // Simplified spectral analysis
329        let n = problem.num_qubits as f64;
330        let spectral_gap_estimate = 1.0 / n.sqrt();
331
332        Ok(SpectralFeatures {
333            eigenvalue_stats: DistributionStats {
334                mean: 0.0,
335                std_dev: 1.0,
336                min: -n,
337                max: n,
338                skewness: 0.0,
339                kurtosis: 3.0,
340            },
341            spectral_gap: spectral_gap_estimate,
342            spectral_radius: n,
343            trace: 0.0,
344            condition_number: n,
345        })
346    }
347
348    fn calculate_density(&self, problem: &IsingModel) -> f64 {
349        let mut num_edges = 0;
350        let max_edges = problem.num_qubits * (problem.num_qubits - 1) / 2;
351
352        for i in 0..problem.num_qubits {
353            for j in (i + 1)..problem.num_qubits {
354                if problem.get_coupling(i, j).unwrap_or(0.0).abs() > 1e-10 {
355                    num_edges += 1;
356                }
357            }
358        }
359
360        if max_edges > 0 {
361            f64::from(num_edges) / max_edges as f64
362        } else {
363            0.0
364        }
365    }
366
367    fn calculate_distribution_stats(&self, values: &[f64]) -> DistributionStats {
368        if values.is_empty() {
369            return DistributionStats::default();
370        }
371
372        let mean = values.iter().sum::<f64>() / values.len() as f64;
373        let variance = values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / values.len() as f64;
374        let std_dev = variance.sqrt();
375        let min = values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
376        let max = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
377
378        DistributionStats {
379            mean,
380            std_dev,
381            min,
382            max,
383            skewness: 0.0, // Simplified
384            kurtosis: 3.0, // Simplified
385        }
386    }
387}
388
389impl Default for DistributionStats {
390    fn default() -> Self {
391        Self {
392            mean: 0.0,
393            std_dev: 1.0,
394            min: 0.0,
395            max: 1.0,
396            skewness: 0.0,
397            kurtosis: 3.0,
398        }
399    }
400}
401
402impl Default for GraphFeatures {
403    fn default() -> Self {
404        Self {
405            num_vertices: 0,
406            num_edges: 0,
407            avg_degree: 0.0,
408            clustering_coefficient: 0.0,
409            path_length_stats: PathLengthStats {
410                avg_shortest_path: 0.0,
411                diameter: 0,
412                radius: 0,
413                eccentricity_stats: DistributionStats::default(),
414            },
415            centrality_measures: CentralityMeasures {
416                degree_centrality: DistributionStats::default(),
417                betweenness_centrality: DistributionStats::default(),
418                closeness_centrality: DistributionStats::default(),
419                eigenvector_centrality: DistributionStats::default(),
420            },
421        }
422    }
423}
424
425impl Default for StatisticalFeatures {
426    fn default() -> Self {
427        Self {
428            bias_stats: DistributionStats::default(),
429            coupling_stats: DistributionStats::default(),
430            energy_landscape: EnergyLandscapeFeatures {
431                local_minima_estimate: 0,
432                energy_barriers: Vec::new(),
433                ruggedness: 0.0,
434                basin_sizes: DistributionStats::default(),
435            },
436            correlation_features: CorrelationFeatures {
437                autocorrelation: Vec::new(),
438                cross_correlation: HashMap::new(),
439                mutual_information: 0.0,
440            },
441        }
442    }
443}
444
445impl Default for SpectralFeatures {
446    fn default() -> Self {
447        Self {
448            eigenvalue_stats: DistributionStats::default(),
449            spectral_gap: 0.0,
450            spectral_radius: 0.0,
451            trace: 0.0,
452            condition_number: 1.0,
453        }
454    }
455}