quantrs2_ml/anomaly_detection/
core.rs

1//! Core quantum anomaly detection functionality
2
3use crate::error::{MLError, Result};
4use ndarray::{Array1, Array2};
5use quantrs2_circuit::builder::Circuit;
6use std::collections::{HashMap, VecDeque};
7
8use super::config::*;
9use super::metrics::*;
10use super::preprocessing::*;
11
12/// Trait for anomaly detection methods
13pub trait AnomalyDetectorTrait {
14    /// Train the detector on normal data
15    fn fit(&mut self, data: &Array2<f64>) -> Result<()>;
16
17    /// Detect anomalies in data
18    fn detect(&self, data: &Array2<f64>) -> Result<AnomalyResult>;
19
20    /// Update detector with new data (online learning)
21    fn update(&mut self, data: &Array2<f64>, labels: Option<&Array1<i32>>) -> Result<()>;
22
23    /// Get detector configuration
24    fn get_config(&self) -> String;
25
26    /// Get detector type
27    fn get_type(&self) -> String;
28}
29
30/// Main quantum anomaly detector
31pub struct QuantumAnomalyDetector {
32    /// Configuration
33    config: QuantumAnomalyConfig,
34
35    /// Primary detection model
36    primary_detector: Box<dyn AnomalyDetectorTrait>,
37
38    /// Ensemble detectors
39    ensemble_detectors: Vec<Box<dyn AnomalyDetectorTrait>>,
40
41    /// Preprocessing pipeline
42    preprocessor: DataPreprocessor,
43
44    /// Real-time buffer for streaming detection
45    realtime_buffer: Option<VecDeque<Array1<f64>>>,
46
47    /// Training statistics
48    training_stats: Option<TrainingStats>,
49
50    /// Quantum circuits cache
51    circuit_cache: HashMap<String, Circuit<16>>,
52
53    /// Performance monitoring
54    performance_monitor: PerformanceMonitor,
55}
56
57/// Performance monitoring
58#[derive(Debug)]
59pub struct PerformanceMonitor {
60    /// Detection latencies
61    latencies: VecDeque<f64>,
62
63    /// Memory usage history
64    memory_usage: VecDeque<f64>,
65
66    /// Accuracy history (if ground truth available)
67    accuracy_history: VecDeque<f64>,
68
69    /// Quantum error rates
70    quantum_error_rates: VecDeque<f64>,
71
72    /// Detection latencies for test compatibility
73    pub detection_latency: Vec<f64>,
74
75    /// Throughput measurements
76    pub throughput: Vec<f64>,
77
78    /// Accuracy scores
79    pub accuracy_scores: Vec<f64>,
80
81    /// False positive rates
82    pub false_positive_rate: Vec<f64>,
83
84    /// Resource usage metrics
85    pub resource_usage: Vec<f64>,
86}
87
88impl QuantumAnomalyDetector {
89    /// Create a new quantum anomaly detector
90    pub fn new(config: QuantumAnomalyConfig) -> Result<Self> {
91        // Validate configuration
92        if config.num_qubits == 0 {
93            return Err(MLError::InvalidConfiguration(
94                "Number of qubits must be greater than 0".to_string(),
95            ));
96        }
97
98        if config.contamination < 0.0 || config.contamination > 1.0 {
99            return Err(MLError::InvalidConfiguration(
100                "Contamination must be between 0 and 1".to_string(),
101            ));
102        }
103
104        // Create primary detector
105        let primary_detector = Self::create_detector(&config.primary_method, &config)?;
106
107        // Create ensemble detectors
108        let mut ensemble_detectors = Vec::new();
109        for method in &config.ensemble_methods {
110            let detector = Self::create_detector(method, &config)?;
111            ensemble_detectors.push(detector);
112        }
113
114        // Create preprocessor
115        let preprocessor = DataPreprocessor::new(config.preprocessing.clone());
116
117        // Initialize real-time buffer if configured
118        let realtime_buffer = if let Some(realtime_config) = &config.realtime_config {
119            Some(VecDeque::with_capacity(realtime_config.buffer_size))
120        } else {
121            None
122        };
123
124        // Initialize performance monitor
125        let performance_monitor = PerformanceMonitor {
126            latencies: VecDeque::new(),
127            memory_usage: VecDeque::new(),
128            accuracy_history: VecDeque::new(),
129            quantum_error_rates: VecDeque::new(),
130            detection_latency: Vec::new(),
131            throughput: Vec::new(),
132            accuracy_scores: Vec::new(),
133            false_positive_rate: Vec::new(),
134            resource_usage: Vec::new(),
135        };
136
137        Ok(Self {
138            config,
139            primary_detector,
140            ensemble_detectors,
141            preprocessor,
142            realtime_buffer,
143            training_stats: None,
144            circuit_cache: HashMap::new(),
145            performance_monitor,
146        })
147    }
148
149    /// Create a detector based on the method configuration
150    fn create_detector(
151        method: &AnomalyDetectionMethod,
152        config: &QuantumAnomalyConfig,
153    ) -> Result<Box<dyn AnomalyDetectorTrait>> {
154        use super::algorithms::*;
155
156        match method {
157            AnomalyDetectionMethod::QuantumIsolationForest { .. } => {
158                Ok(Box::new(QuantumIsolationForest::new(config.clone())?))
159            }
160            AnomalyDetectionMethod::QuantumAutoencoder { .. } => {
161                Ok(Box::new(QuantumAutoencoder::new(config.clone())?))
162            }
163            AnomalyDetectionMethod::QuantumOneClassSVM { .. } => {
164                Ok(Box::new(QuantumOneClassSVM::new(config.clone())?))
165            }
166            AnomalyDetectionMethod::QuantumLOF { .. } => {
167                Ok(Box::new(QuantumLOF::new(config.clone())?))
168            }
169            AnomalyDetectionMethod::QuantumDBSCAN { .. } => {
170                Ok(Box::new(QuantumDBSCAN::new(config.clone())?))
171            }
172            AnomalyDetectionMethod::QuantumKMeansDetection { .. } => {
173                Ok(Box::new(QuantumKMeansDetection::new(config.clone())?))
174            }
175            AnomalyDetectionMethod::QuantumNoveltyDetection { .. } => {
176                Ok(Box::new(QuantumNoveltyDetection::new(config.clone())?))
177            }
178            AnomalyDetectionMethod::QuantumEnsemble { .. } => {
179                Ok(Box::new(QuantumEnsemble::new(config.clone())?))
180            }
181        }
182    }
183
184    /// Train the anomaly detector
185    pub fn fit(&mut self, data: &Array2<f64>) -> Result<()> {
186        let start_time = std::time::Instant::now();
187
188        // Preprocess data
189        let processed_data = self.preprocessor.fit_transform(data)?;
190
191        // Train primary detector
192        self.primary_detector.fit(&processed_data)?;
193
194        // Train ensemble detectors
195        for detector in &mut self.ensemble_detectors {
196            detector.fit(&processed_data)?;
197        }
198
199        // Update training statistics
200        let training_time = start_time.elapsed().as_secs_f64();
201        self.training_stats = Some(TrainingStats {
202            training_time,
203            n_training_samples: data.nrows(),
204            feature_stats: self.compute_feature_stats(data)?,
205            circuit_stats: CircuitStats {
206                avg_depth: 0.0,
207                avg_gates: 0.0,
208                avg_execution_time: 0.0,
209                success_rate: 1.0,
210            },
211        });
212
213        Ok(())
214    }
215
216    /// Detect anomalies in new data
217    pub fn detect(&self, data: &Array2<f64>) -> Result<AnomalyResult> {
218        let start_time = std::time::Instant::now();
219
220        // Preprocess data
221        let processed_data = self.preprocessor.transform(data)?;
222
223        // Get primary detection results
224        let primary_result = self.primary_detector.detect(&processed_data)?;
225
226        // Get ensemble results if available
227        let mut ensemble_results = Vec::new();
228        for detector in &self.ensemble_detectors {
229            let result = detector.detect(&processed_data)?;
230            ensemble_results.push(result);
231        }
232
233        // Combine results
234        let final_result = self.combine_results(primary_result, ensemble_results)?;
235
236        // Update performance monitoring
237        let detection_time = start_time.elapsed().as_secs_f64();
238        // Performance monitoring update would go here
239
240        Ok(final_result)
241    }
242
243    /// Update detector with new data (online learning)
244    pub fn update(&mut self, data: &Array2<f64>, labels: Option<&Array1<i32>>) -> Result<()> {
245        // Preprocess data
246        let processed_data = self.preprocessor.transform(data)?;
247
248        // Update primary detector
249        self.primary_detector.update(&processed_data, labels)?;
250
251        // Update ensemble detectors
252        for detector in &mut self.ensemble_detectors {
253            detector.update(&processed_data, labels)?;
254        }
255
256        Ok(())
257    }
258
259    /// Get detector configuration
260    pub fn get_config(&self) -> &QuantumAnomalyConfig {
261        &self.config
262    }
263
264    /// Get training statistics
265    pub fn get_training_stats(&self) -> Option<&TrainingStats> {
266        self.training_stats.as_ref()
267    }
268
269    // Private helper methods
270
271    fn compute_feature_stats(&self, data: &Array2<f64>) -> Result<Array2<f64>> {
272        let n_features = data.ncols();
273        let mut stats = Array2::zeros((n_features, 4)); // mean, std, min, max
274
275        for i in 0..n_features {
276            let col = data.column(i);
277            let mean = col.mean().unwrap_or(0.0);
278            let std = col.var(0.0).sqrt();
279            let min = col.iter().fold(f64::INFINITY, |a, &b| a.min(b));
280            let max = col.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
281
282            stats[[i, 0]] = mean;
283            stats[[i, 1]] = std;
284            stats[[i, 2]] = min;
285            stats[[i, 3]] = max;
286        }
287
288        Ok(stats)
289    }
290
291    fn combine_results(
292        &self,
293        mut primary: AnomalyResult,
294        ensemble: Vec<AnomalyResult>,
295    ) -> Result<AnomalyResult> {
296        // Combine method results from all detectors
297        for ensemble_result in ensemble {
298            for (method_name, method_result) in ensemble_result.method_results {
299                primary.method_results.insert(method_name, method_result);
300            }
301        }
302
303        Ok(primary)
304    }
305}
306
307impl PerformanceMonitor {
308    /// Create new performance monitor
309    pub fn new() -> Self {
310        Self {
311            latencies: VecDeque::new(),
312            memory_usage: VecDeque::new(),
313            accuracy_history: VecDeque::new(),
314            quantum_error_rates: VecDeque::new(),
315            detection_latency: Vec::new(),
316            throughput: Vec::new(),
317            accuracy_scores: Vec::new(),
318            false_positive_rate: Vec::new(),
319            resource_usage: Vec::new(),
320        }
321    }
322
323    /// Record detection latency
324    pub fn record_latency(&mut self, latency: f64) {
325        self.latencies.push_back(latency);
326        self.detection_latency.push(latency);
327
328        // Keep only recent entries
329        if self.latencies.len() > 1000 {
330            self.latencies.pop_front();
331        }
332    }
333
334    /// Record memory usage
335    pub fn record_memory_usage(&mut self, usage: f64) {
336        self.memory_usage.push_back(usage);
337        self.resource_usage.push(usage);
338
339        // Keep only recent entries
340        if self.memory_usage.len() > 1000 {
341            self.memory_usage.pop_front();
342        }
343    }
344
345    /// Get average latency
346    pub fn get_average_latency(&self) -> f64 {
347        if self.latencies.is_empty() {
348            0.0
349        } else {
350            self.latencies.iter().sum::<f64>() / self.latencies.len() as f64
351        }
352    }
353
354    /// Get peak memory usage
355    pub fn get_peak_memory_usage(&self) -> f64 {
356        self.memory_usage.iter().fold(0.0, |a, &b| a.max(b))
357    }
358}
359
360impl Default for PerformanceMonitor {
361    fn default() -> Self {
362        Self::new()
363    }
364}