quantrs2_ml/anomaly_detection/
core.rs1use 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
12pub trait AnomalyDetectorTrait {
14 fn fit(&mut self, data: &Array2<f64>) -> Result<()>;
16
17 fn detect(&self, data: &Array2<f64>) -> Result<AnomalyResult>;
19
20 fn update(&mut self, data: &Array2<f64>, labels: Option<&Array1<i32>>) -> Result<()>;
22
23 fn get_config(&self) -> String;
25
26 fn get_type(&self) -> String;
28}
29
30pub struct QuantumAnomalyDetector {
32 config: QuantumAnomalyConfig,
34
35 primary_detector: Box<dyn AnomalyDetectorTrait>,
37
38 ensemble_detectors: Vec<Box<dyn AnomalyDetectorTrait>>,
40
41 preprocessor: DataPreprocessor,
43
44 realtime_buffer: Option<VecDeque<Array1<f64>>>,
46
47 training_stats: Option<TrainingStats>,
49
50 circuit_cache: HashMap<String, Circuit<16>>,
52
53 performance_monitor: PerformanceMonitor,
55}
56
57#[derive(Debug)]
59pub struct PerformanceMonitor {
60 latencies: VecDeque<f64>,
62
63 memory_usage: VecDeque<f64>,
65
66 accuracy_history: VecDeque<f64>,
68
69 quantum_error_rates: VecDeque<f64>,
71
72 pub detection_latency: Vec<f64>,
74
75 pub throughput: Vec<f64>,
77
78 pub accuracy_scores: Vec<f64>,
80
81 pub false_positive_rate: Vec<f64>,
83
84 pub resource_usage: Vec<f64>,
86}
87
88impl QuantumAnomalyDetector {
89 pub fn new(config: QuantumAnomalyConfig) -> Result<Self> {
91 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 let primary_detector = Self::create_detector(&config.primary_method, &config)?;
106
107 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 let preprocessor = DataPreprocessor::new(config.preprocessing.clone());
116
117 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 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 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 pub fn fit(&mut self, data: &Array2<f64>) -> Result<()> {
186 let start_time = std::time::Instant::now();
187
188 let processed_data = self.preprocessor.fit_transform(data)?;
190
191 self.primary_detector.fit(&processed_data)?;
193
194 for detector in &mut self.ensemble_detectors {
196 detector.fit(&processed_data)?;
197 }
198
199 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 pub fn detect(&self, data: &Array2<f64>) -> Result<AnomalyResult> {
218 let start_time = std::time::Instant::now();
219
220 let processed_data = self.preprocessor.transform(data)?;
222
223 let primary_result = self.primary_detector.detect(&processed_data)?;
225
226 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 let final_result = self.combine_results(primary_result, ensemble_results)?;
235
236 let detection_time = start_time.elapsed().as_secs_f64();
238 Ok(final_result)
241 }
242
243 pub fn update(&mut self, data: &Array2<f64>, labels: Option<&Array1<i32>>) -> Result<()> {
245 let processed_data = self.preprocessor.transform(data)?;
247
248 self.primary_detector.update(&processed_data, labels)?;
250
251 for detector in &mut self.ensemble_detectors {
253 detector.update(&processed_data, labels)?;
254 }
255
256 Ok(())
257 }
258
259 pub fn get_config(&self) -> &QuantumAnomalyConfig {
261 &self.config
262 }
263
264 pub fn get_training_stats(&self) -> Option<&TrainingStats> {
266 self.training_stats.as_ref()
267 }
268
269 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)); 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 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 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 pub fn record_latency(&mut self, latency: f64) {
325 self.latencies.push_back(latency);
326 self.detection_latency.push(latency);
327
328 if self.latencies.len() > 1000 {
330 self.latencies.pop_front();
331 }
332 }
333
334 pub fn record_memory_usage(&mut self, usage: f64) {
336 self.memory_usage.push_back(usage);
337 self.resource_usage.push(usage);
338
339 if self.memory_usage.len() > 1000 {
341 self.memory_usage.pop_front();
342 }
343 }
344
345 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 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}