1use 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#[derive(Debug, Clone)]
16pub struct ProblemFeatures {
17 pub size: usize,
19 pub density: f64,
21 pub graph_features: GraphFeatures,
23 pub statistical_features: StatisticalFeatures,
25 pub spectral_features: SpectralFeatures,
27 pub domain_features: HashMap<String, f64>,
29}
30
31#[derive(Debug, Clone)]
33pub struct GraphFeatures {
34 pub num_vertices: usize,
36 pub num_edges: usize,
38 pub avg_degree: f64,
40 pub clustering_coefficient: f64,
42 pub path_length_stats: PathLengthStats,
44 pub centrality_measures: CentralityMeasures,
46}
47
48#[derive(Debug, Clone)]
50pub struct PathLengthStats {
51 pub avg_shortest_path: f64,
53 pub diameter: usize,
55 pub radius: usize,
57 pub eccentricity_stats: DistributionStats,
59}
60
61#[derive(Debug, Clone)]
63pub struct CentralityMeasures {
64 pub degree_centrality: DistributionStats,
66 pub betweenness_centrality: DistributionStats,
68 pub closeness_centrality: DistributionStats,
70 pub eigenvector_centrality: DistributionStats,
72}
73
74#[derive(Debug, Clone)]
76pub struct DistributionStats {
77 pub mean: f64,
79 pub std_dev: f64,
81 pub min: f64,
83 pub max: f64,
85 pub skewness: f64,
87 pub kurtosis: f64,
89}
90
91#[derive(Debug, Clone)]
93pub struct StatisticalFeatures {
94 pub bias_stats: DistributionStats,
96 pub coupling_stats: DistributionStats,
98 pub energy_landscape: EnergyLandscapeFeatures,
100 pub correlation_features: CorrelationFeatures,
102}
103
104#[derive(Debug, Clone)]
106pub struct EnergyLandscapeFeatures {
107 pub local_minima_estimate: usize,
109 pub energy_barriers: Vec<f64>,
111 pub ruggedness: f64,
113 pub basin_sizes: DistributionStats,
115}
116
117#[derive(Debug, Clone)]
119pub struct CorrelationFeatures {
120 pub autocorrelation: Vec<f64>,
122 pub cross_correlation: HashMap<String, f64>,
124 pub mutual_information: f64,
126}
127
128#[derive(Debug, Clone)]
130pub struct SpectralFeatures {
131 pub eigenvalue_stats: DistributionStats,
133 pub spectral_gap: f64,
135 pub spectral_radius: f64,
137 pub trace: f64,
139 pub condition_number: f64,
141}
142
143#[derive(Debug)]
145pub struct FeatureExtractor {
146 pub config: FeatureExtractionConfig,
148 pub transformers: Vec<FeatureTransformer>,
150 pub selectors: Vec<FeatureSelector>,
152 pub reducers: Vec<DimensionalityReducer>,
154}
155
156#[derive(Debug)]
158pub struct FeatureTransformer {
159 pub transformer_type: TransformerType,
161 pub parameters: HashMap<String, f64>,
163 pub is_fitted: bool,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
169pub enum TransformerType {
170 Polynomial,
172 Interaction,
174 Logarithmic,
176 BoxCox,
178 Custom(String),
180}
181
182#[derive(Debug)]
184pub struct FeatureSelector {
185 pub method: FeatureSelectionMethod,
187 pub selected_features: Vec<usize>,
189 pub importance_scores: Vec<f64>,
191}
192
193#[derive(Debug)]
195pub struct DimensionalityReducer {
196 pub method: DimensionalityReduction,
198 pub target_dims: usize,
200 pub transformation_matrix: Option<Vec<Vec<f64>>>,
202 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 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, 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 for i in 0..problem.num_qubits {
294 bias_values.push(problem.get_bias(i).unwrap_or(0.0));
295 }
296
297 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 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, kurtosis: 3.0, }
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}