use std::collections::{BTreeMap, HashMap, VecDeque};
use std::time::Instant;
use super::config::{
ActivationFunction, DimensionalityReduction, FeatureExtractionConfig, FeatureSelectionMethod,
LayerType,
};
use crate::applications::ApplicationResult;
use crate::ising::IsingModel;
#[derive(Debug, Clone)]
pub struct ProblemFeatures {
pub size: usize,
pub density: f64,
pub graph_features: GraphFeatures,
pub statistical_features: StatisticalFeatures,
pub spectral_features: SpectralFeatures,
pub domain_features: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct FeatureVector {
pub values: Vec<f64>,
pub names: Vec<String>,
pub normalization: Option<NormalizationParams>,
}
#[derive(Debug, Clone)]
pub struct NormalizationParams {
pub mean: Vec<f64>,
pub std: Vec<f64>,
pub min: Vec<f64>,
pub max: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct DistributionStats {
pub mean: f64,
pub std_dev: f64,
pub min: f64,
pub max: f64,
pub skewness: f64,
pub kurtosis: f64,
}
impl Default for DistributionStats {
fn default() -> Self {
Self {
mean: 0.0,
std_dev: 1.0,
min: 0.0,
max: 1.0,
skewness: 0.0,
kurtosis: 3.0,
}
}
}
#[derive(Debug, Clone)]
pub struct GraphFeatures {
pub num_vertices: usize,
pub num_edges: usize,
pub avg_degree: f64,
pub clustering_coefficient: f64,
pub path_length_stats: PathLengthStats,
pub centrality_measures: CentralityMeasures,
}
impl Default for GraphFeatures {
fn default() -> Self {
Self {
num_vertices: 0,
num_edges: 0,
avg_degree: 0.0,
clustering_coefficient: 0.0,
path_length_stats: PathLengthStats {
avg_shortest_path: 0.0,
diameter: 0,
radius: 0,
eccentricity_stats: DistributionStats::default(),
},
centrality_measures: CentralityMeasures {
degree_centrality: DistributionStats::default(),
betweenness_centrality: DistributionStats::default(),
closeness_centrality: DistributionStats::default(),
eigenvector_centrality: DistributionStats::default(),
},
}
}
}
#[derive(Debug, Clone)]
pub struct PathLengthStats {
pub avg_shortest_path: f64,
pub diameter: usize,
pub radius: usize,
pub eccentricity_stats: DistributionStats,
}
#[derive(Debug, Clone)]
pub struct CentralityMeasures {
pub degree_centrality: DistributionStats,
pub betweenness_centrality: DistributionStats,
pub closeness_centrality: DistributionStats,
pub eigenvector_centrality: DistributionStats,
}
#[derive(Debug, Clone)]
pub struct StatisticalFeatures {
pub bias_stats: DistributionStats,
pub coupling_stats: DistributionStats,
pub energy_landscape: EnergyLandscapeFeatures,
pub correlation_features: CorrelationFeatures,
}
impl Default for StatisticalFeatures {
fn default() -> Self {
Self {
bias_stats: DistributionStats::default(),
coupling_stats: DistributionStats::default(),
energy_landscape: EnergyLandscapeFeatures {
local_minima_estimate: 0,
energy_barriers: Vec::new(),
ruggedness: 0.0,
basin_sizes: DistributionStats::default(),
},
correlation_features: CorrelationFeatures {
autocorrelation: Vec::new(),
cross_correlation: HashMap::new(),
mutual_information: 0.0,
},
}
}
}
#[derive(Debug, Clone)]
pub struct EnergyLandscapeFeatures {
pub local_minima_estimate: usize,
pub energy_barriers: Vec<f64>,
pub ruggedness: f64,
pub basin_sizes: DistributionStats,
}
#[derive(Debug, Clone)]
pub struct CorrelationFeatures {
pub autocorrelation: Vec<f64>,
pub cross_correlation: HashMap<String, f64>,
pub mutual_information: f64,
}
#[derive(Debug, Clone)]
pub struct SpectralFeatures {
pub eigenvalue_stats: DistributionStats,
pub spectral_gap: f64,
pub spectral_radius: f64,
pub trace: f64,
pub condition_number: f64,
}
impl Default for SpectralFeatures {
fn default() -> Self {
Self {
eigenvalue_stats: DistributionStats::default(),
spectral_gap: 0.0,
spectral_radius: 0.0,
trace: 0.0,
condition_number: 1.0,
}
}
}
pub struct FeatureExtractor {
pub config: FeatureExtractionConfig,
pub transformers: Vec<FeatureTransformer>,
pub selectors: Vec<FeatureSelector>,
pub reducers: Vec<DimensionalityReducer>,
}
impl FeatureExtractor {
#[must_use]
pub const fn new(config: FeatureExtractionConfig) -> Self {
Self {
config,
transformers: Vec::new(),
selectors: Vec::new(),
reducers: Vec::new(),
}
}
pub fn extract_features(&mut self, problem: &IsingModel) -> ApplicationResult<ProblemFeatures> {
let graph_features = if self.config.enable_graph_features {
self.extract_graph_features(problem)?
} else {
GraphFeatures::default()
};
let statistical_features = if self.config.enable_statistical_features {
self.extract_statistical_features(problem)?
} else {
StatisticalFeatures::default()
};
let spectral_features = if self.config.enable_spectral_features {
self.extract_spectral_features(problem)?
} else {
SpectralFeatures::default()
};
Ok(ProblemFeatures {
size: problem.num_qubits,
density: self.calculate_density(problem),
graph_features,
statistical_features,
spectral_features,
domain_features: HashMap::new(),
})
}
fn extract_graph_features(&self, problem: &IsingModel) -> ApplicationResult<GraphFeatures> {
let num_vertices = problem.num_qubits;
let mut num_edges = 0;
for i in 0..problem.num_qubits {
for j in (i + 1)..problem.num_qubits {
if problem.get_coupling(i, j).unwrap_or(0.0).abs() > 1e-10 {
num_edges += 1;
}
}
}
let avg_degree = if num_vertices > 0 {
2.0 * num_edges as f64 / num_vertices as f64
} else {
0.0
};
Ok(GraphFeatures {
num_vertices,
num_edges,
avg_degree,
clustering_coefficient: 0.1, path_length_stats: PathLengthStats {
avg_shortest_path: avg_degree.ln().max(1.0),
diameter: num_vertices / 2,
radius: num_vertices / 4,
eccentricity_stats: DistributionStats::default(),
},
centrality_measures: CentralityMeasures {
degree_centrality: DistributionStats::default(),
betweenness_centrality: DistributionStats::default(),
closeness_centrality: DistributionStats::default(),
eigenvector_centrality: DistributionStats::default(),
},
})
}
fn extract_statistical_features(
&self,
problem: &IsingModel,
) -> ApplicationResult<StatisticalFeatures> {
let mut bias_values = Vec::new();
let mut coupling_values = Vec::new();
for i in 0..problem.num_qubits {
bias_values.push(problem.get_bias(i).unwrap_or(0.0));
}
for i in 0..problem.num_qubits {
for j in (i + 1)..problem.num_qubits {
let coupling = problem.get_coupling(i, j).unwrap_or(0.0);
if coupling.abs() > 1e-10 {
coupling_values.push(coupling);
}
}
}
Ok(StatisticalFeatures {
bias_stats: self.calculate_distribution_stats(&bias_values),
coupling_stats: self.calculate_distribution_stats(&coupling_values),
energy_landscape: EnergyLandscapeFeatures {
local_minima_estimate: (problem.num_qubits as f64).sqrt() as usize,
energy_barriers: vec![1.0, 2.0, 3.0],
ruggedness: 0.5,
basin_sizes: DistributionStats::default(),
},
correlation_features: CorrelationFeatures {
autocorrelation: vec![1.0, 0.8, 0.6, 0.4, 0.2],
cross_correlation: HashMap::new(),
mutual_information: 0.3,
},
})
}
fn extract_spectral_features(
&self,
problem: &IsingModel,
) -> ApplicationResult<SpectralFeatures> {
let n = problem.num_qubits as f64;
let spectral_gap_estimate = 1.0 / n.sqrt();
Ok(SpectralFeatures {
eigenvalue_stats: DistributionStats {
mean: 0.0,
std_dev: 1.0,
min: -n,
max: n,
skewness: 0.0,
kurtosis: 3.0,
},
spectral_gap: spectral_gap_estimate,
spectral_radius: n,
trace: 0.0,
condition_number: n,
})
}
fn calculate_density(&self, problem: &IsingModel) -> f64 {
let mut num_edges = 0;
let max_edges = problem.num_qubits * (problem.num_qubits - 1) / 2;
for i in 0..problem.num_qubits {
for j in (i + 1)..problem.num_qubits {
if problem.get_coupling(i, j).unwrap_or(0.0).abs() > 1e-10 {
num_edges += 1;
}
}
}
if max_edges > 0 {
f64::from(num_edges) / max_edges as f64
} else {
0.0
}
}
fn calculate_distribution_stats(&self, values: &[f64]) -> DistributionStats {
if values.is_empty() {
return DistributionStats::default();
}
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / values.len() as f64;
let std_dev = variance.sqrt();
let min = values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
let max = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
DistributionStats {
mean,
std_dev,
min,
max,
skewness: 0.0, kurtosis: 3.0, }
}
#[must_use]
pub fn vectorize_features(&self, features: &ProblemFeatures) -> FeatureVector {
let mut values = Vec::new();
let mut names = Vec::new();
values.push(features.size as f64);
names.push("size".to_string());
values.push(features.density);
names.push("density".to_string());
if self.config.enable_graph_features {
values.push(features.graph_features.num_vertices as f64);
names.push("num_vertices".to_string());
values.push(features.graph_features.num_edges as f64);
names.push("num_edges".to_string());
values.push(features.graph_features.avg_degree);
names.push("avg_degree".to_string());
values.push(features.graph_features.clustering_coefficient);
names.push("clustering_coefficient".to_string());
}
if self.config.enable_statistical_features {
values.push(features.statistical_features.bias_stats.mean);
names.push("bias_mean".to_string());
values.push(features.statistical_features.bias_stats.std_dev);
names.push("bias_std".to_string());
values.push(features.statistical_features.coupling_stats.mean);
names.push("coupling_mean".to_string());
values.push(features.statistical_features.coupling_stats.std_dev);
names.push("coupling_std".to_string());
}
if self.config.enable_spectral_features {
values.push(features.spectral_features.spectral_gap);
names.push("spectral_gap".to_string());
values.push(features.spectral_features.spectral_radius);
names.push("spectral_radius".to_string());
values.push(features.spectral_features.condition_number);
names.push("condition_number".to_string());
}
FeatureVector {
values,
names,
normalization: None,
}
}
}
#[derive(Debug)]
pub struct FeatureTransformer {
pub transformer_type: TransformerType,
pub parameters: HashMap<String, f64>,
pub is_fitted: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransformerType {
Polynomial,
Interaction,
Logarithmic,
BoxCox,
Custom(String),
}
#[derive(Debug)]
pub struct FeatureSelector {
pub method: FeatureSelectionMethod,
pub selected_features: Vec<usize>,
pub importance_scores: Vec<f64>,
}
#[derive(Debug)]
pub struct DimensionalityReducer {
pub method: DimensionalityReduction,
pub target_dims: usize,
pub transformation_matrix: Option<Vec<Vec<f64>>>,
pub explained_variance: Vec<f64>,
}
pub struct ExperienceDatabase {
pub experiences: VecDeque<OptimizationExperience>,
pub index: ExperienceIndex,
pub similarity_cache: HashMap<String, Vec<(String, f64)>>,
pub statistics: DatabaseStatistics,
}
#[derive(Debug, Clone)]
pub struct OptimizationExperience {
pub id: String,
pub problem_features: ProblemFeatures,
pub configuration: OptimizationConfiguration,
pub results: OptimizationResults,
pub timestamp: Instant,
pub domain: ProblemDomain,
pub success_metrics: SuccessMetrics,
}
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct OptimizationConfiguration {
pub algorithm: AlgorithmType,
pub hyperparameters: HashMap<String, f64>,
pub architecture: Option<ArchitectureSpec>,
pub resources: ResourceAllocation,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AlgorithmType {
SimulatedAnnealing,
QuantumAnnealing,
TabuSearch,
GeneticAlgorithm,
ParticleSwarm,
AntColony,
VariableNeighborhood,
Hybrid(Vec<Self>),
}
#[derive(Debug, Clone)]
pub struct ArchitectureSpec {
pub layers: Vec<LayerSpec>,
pub connections: ConnectionPattern,
pub optimization: OptimizationSettings,
}
#[derive(Debug, Clone)]
pub struct LayerSpec {
pub layer_type: LayerType,
pub input_dim: usize,
pub output_dim: usize,
pub activation: ActivationFunction,
pub dropout: f64,
pub parameters: HashMap<String, f64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConnectionPattern {
Sequential,
SkipConnections,
DenseConnections,
ResidualConnections,
Custom(Vec<(usize, usize)>),
}
#[derive(Debug, Clone)]
pub struct OptimizationSettings {
pub optimizer: OptimizerType,
pub learning_rate: f64,
pub batch_size: usize,
pub epochs: usize,
pub regularization: RegularizationConfig,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OptimizerType {
SGD,
Adam,
AdamW,
RMSprop,
Adagrad,
Adadelta,
LBFGS,
}
#[derive(Debug, Clone)]
pub struct RegularizationConfig {
pub l1_weight: f64,
pub l2_weight: f64,
pub dropout: f64,
pub batch_norm: bool,
pub early_stopping: bool,
}
#[derive(Debug, Clone)]
pub struct ResourceAllocation {
pub cpu: f64,
pub memory: usize,
pub gpu: f64,
pub time: Duration,
}
#[derive(Debug, Clone)]
pub struct OptimizationResults {
pub objective_values: Vec<f64>,
pub execution_time: Duration,
pub resource_usage: ResourceUsage,
pub convergence: ConvergenceMetrics,
pub quality_metrics: QualityMetrics,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage {
pub peak_cpu: f64,
pub peak_memory: usize,
pub gpu_utilization: f64,
pub energy_consumption: f64,
}
#[derive(Debug, Clone)]
pub struct ConvergenceMetrics {
pub iterations: usize,
pub convergence_rate: f64,
pub plateau_detected: bool,
pub confidence: f64,
}
#[derive(Debug, Clone)]
pub struct QualityMetrics {
pub objective_value: f64,
pub constraint_violation: f64,
pub robustness: f64,
pub diversity: f64,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProblemDomain {
Combinatorial,
Portfolio,
Scheduling,
Graph,
MachineLearning,
Physics,
Chemistry,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct SuccessMetrics {
pub success_score: f64,
pub relative_performance: f64,
pub user_satisfaction: f64,
pub recommendation_confidence: f64,
}
#[derive(Debug)]
pub struct ExperienceIndex {
pub domain_index: HashMap<ProblemDomain, Vec<String>>,
pub size_index: BTreeMap<usize, Vec<String>>,
pub performance_index: BTreeMap<String, Vec<String>>,
pub feature_index: HashMap<String, Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct DatabaseStatistics {
pub total_experiences: usize,
pub domain_distribution: HashMap<ProblemDomain, usize>,
pub avg_performance: f64,
pub coverage_stats: CoverageStatistics,
}
#[derive(Debug, Clone)]
pub struct CoverageStatistics {
pub feature_coverage: f64,
pub size_coverage: (usize, usize),
pub domain_coverage: f64,
pub performance_range: (f64, f64),
}
impl ExperienceDatabase {
#[must_use]
pub fn new() -> Self {
Self {
experiences: VecDeque::new(),
index: ExperienceIndex {
domain_index: HashMap::new(),
size_index: BTreeMap::new(),
performance_index: BTreeMap::new(),
feature_index: HashMap::new(),
},
similarity_cache: HashMap::new(),
statistics: DatabaseStatistics {
total_experiences: 0,
domain_distribution: HashMap::new(),
avg_performance: 0.0,
coverage_stats: CoverageStatistics {
feature_coverage: 0.0,
size_coverage: (0, 0),
domain_coverage: 0.0,
performance_range: (0.0, 1.0),
},
},
}
}
pub fn add_experience(&mut self, experience: OptimizationExperience) {
self.experiences.push_back(experience.clone());
self.update_index(&experience);
self.update_statistics();
if self.experiences.len() > 10_000 {
if let Some(removed) = self.experiences.pop_front() {
self.remove_from_index(&removed);
}
}
}
fn update_index(&mut self, experience: &OptimizationExperience) {
self.index
.domain_index
.entry(experience.domain.clone())
.or_insert_with(Vec::new)
.push(experience.id.clone());
self.index
.size_index
.entry(experience.problem_features.size)
.or_insert_with(Vec::new)
.push(experience.id.clone());
}
fn remove_from_index(&mut self, experience: &OptimizationExperience) {
if let Some(ids) = self.index.domain_index.get_mut(&experience.domain) {
ids.retain(|id| id != &experience.id);
}
if let Some(ids) = self
.index
.size_index
.get_mut(&experience.problem_features.size)
{
ids.retain(|id| id != &experience.id);
}
}
fn update_statistics(&mut self) {
self.statistics.total_experiences = self.experiences.len();
if !self.experiences.is_empty() {
let total_performance: f64 = self
.experiences
.iter()
.map(|exp| exp.results.quality_metrics.objective_value)
.sum();
self.statistics.avg_performance = total_performance / self.experiences.len() as f64;
}
self.statistics.domain_distribution.clear();
for experience in &self.experiences {
*self
.statistics
.domain_distribution
.entry(experience.domain.clone())
.or_insert(0) += 1;
}
}
pub fn find_similar_experiences(
&self,
features: &ProblemFeatures,
limit: usize,
) -> ApplicationResult<Vec<OptimizationExperience>> {
let mut similarities = Vec::new();
for experience in &self.experiences {
let similarity = self.calculate_similarity(features, &experience.problem_features);
similarities.push((experience.clone(), similarity));
}
similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
Ok(similarities
.into_iter()
.take(limit)
.map(|(exp, _)| exp)
.collect())
}
fn calculate_similarity(
&self,
features1: &ProblemFeatures,
features2: &ProblemFeatures,
) -> f64 {
let size_diff = (features1.size as f64 - features2.size as f64).abs()
/ features1.size.max(features2.size) as f64;
let density_diff = (features1.density - features2.density).abs();
let size_similarity = 1.0 - size_diff;
let density_similarity = 1.0 - density_diff;
f64::midpoint(size_similarity, density_similarity)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature_extractor_creation() {
let config = FeatureExtractionConfig::default();
let extractor = FeatureExtractor::new(config);
assert!(extractor.config.enable_graph_features);
}
#[test]
fn test_distribution_stats() {
let stats = DistributionStats::default();
assert_eq!(stats.mean, 0.0);
assert_eq!(stats.std_dev, 1.0);
}
#[test]
fn test_experience_database() {
let db = ExperienceDatabase::new();
assert_eq!(db.statistics.total_experiences, 0);
}
}