1pub mod config;
18pub mod spectral;
19pub mod statistical;
20pub mod temporal;
21
22pub use config::*;
24pub use spectral::*;
25pub use statistical::*;
26pub use temporal::*;
27
28use crate::{
29 calibration::DeviceCalibration,
30 noise_model::{CalibrationNoiseModel, CrosstalkNoise},
31 DeviceError, DeviceResult,
32};
33use scirs2_core::ndarray::{Array1, Array2};
34use quantrs2_core::{error::QuantRS2Result, qubit::QubitId};
35use std::collections::HashMap;
36
37#[derive(Debug, Clone)]
39pub struct StatisticalNoiseAnalysis {
40 pub noise_statistics: HashMap<String, NoiseStatistics>,
41 pub correlation_analysis: CorrelationAnalysis,
42 pub temporal_analysis: Option<TemporalAnalysis>,
43}
44
45#[derive(Debug, Clone)]
47pub struct NoiseStatistics {
48 pub mean: f64,
49 pub std_dev: f64,
50 pub variance: f64,
51 pub distribution_type: DistributionType,
52 pub parameters: Vec<f64>,
53}
54
55#[derive(Debug, Clone)]
57pub struct CorrelationAnalysis {
58 pub correlationmatrix: Array2<f64>,
59 pub correlation_strength: f64,
60}
61
62#[derive(Debug, Clone)]
64pub struct TemporalAnalysis {
65 pub autocorrelation: Array1<f64>,
66 pub power_spectrum: Array1<f64>,
67}
68
69#[derive(Debug, Clone)]
74pub struct SciRS2NoiseModeler {
75 config: SciRS2NoiseConfig,
76 device_id: String,
77}
78
79impl SciRS2NoiseModeler {
80 pub fn new(device_id: String) -> Self {
82 Self {
83 config: SciRS2NoiseConfig::default(),
84 device_id,
85 }
86 }
87
88 pub fn with_config(device_id: String, config: SciRS2NoiseConfig) -> Self {
90 Self { config, device_id }
91 }
92
93 pub fn model_noise(
95 &self,
96 calibration: &DeviceCalibration,
97 ) -> DeviceResult<CalibrationNoiseModel> {
98 use crate::noise_model::{GateNoiseParams, QubitNoiseParams, ReadoutNoiseParams};
99 use scirs2_core::ndarray::Array1;
100 use std::collections::HashMap;
101
102 let noise_data = self.extract_noise_data(calibration)?;
104
105 let statistical_model = if self.config.enable_ml_modeling {
107 self.perform_statistical_analysis(&noise_data)?
108 } else {
109 self.simple_noise_analysis(&noise_data)?
110 };
111
112 self.build_calibration_noise_model(calibration, &statistical_model)
114 }
115
116 fn extract_noise_data(
118 &self,
119 calibration: &DeviceCalibration,
120 ) -> DeviceResult<HashMap<String, Array1<f64>>> {
121 use scirs2_core::ndarray::Array1;
122 let mut noise_data = HashMap::new();
123
124 for (gate_name, gate_cal) in &calibration.single_qubit_gates {
126 for (qubit, qubit_data) in &gate_cal.qubit_data {
127 let key = format!("single_qubit_{}_{}", gate_name, qubit.0);
128 let error_data = vec![qubit_data.error_rate, 1.0 - qubit_data.fidelity];
129 noise_data.insert(key, Array1::from_vec(error_data));
130 }
131 }
132
133 for (qubit_pair, gate_cal) in &calibration.two_qubit_gates {
135 let key = format!("two_qubit_{}_{}", qubit_pair.0 .0, qubit_pair.1 .0);
136 let error_data = vec![gate_cal.error_rate, 1.0 - gate_cal.fidelity];
137 noise_data.insert(key, Array1::from_vec(error_data));
138 }
139
140 for (qubit, readout_cal) in &calibration.readout_calibration.qubit_readout {
142 let key = format!("readout_{}", qubit.0);
143 let error_data = vec![1.0 - readout_cal.p0_given_0, 1.0 - readout_cal.p1_given_1];
144 noise_data.insert(key, Array1::from_vec(error_data));
145 }
146
147 Ok(noise_data)
148 }
149
150 fn perform_statistical_analysis(
152 &self,
153 noise_data: &HashMap<String, Array1<f64>>,
154 ) -> DeviceResult<StatisticalNoiseAnalysis> {
155 use scirs2_stats::{mean, std, var};
156
157 let mut analysis_results = HashMap::new();
158
159 for (noise_type, data) in noise_data {
160 let data_view = data.view();
161
162 let mean_val = mean(&data_view).map_err(|e| {
163 DeviceError::APIError(format!("Statistical analysis error: {:?}", e))
164 })?;
165
166 let std_val = std(&data_view, 1, None).map_err(|e| {
167 DeviceError::APIError(format!("Statistical analysis error: {:?}", e))
168 })?;
169
170 let var_val = var(&data_view, 1, None).map_err(|e| {
171 DeviceError::APIError(format!("Statistical analysis error: {:?}", e))
172 })?;
173
174 let noise_stats = NoiseStatistics {
175 mean: mean_val,
176 std_dev: std_val,
177 variance: var_val,
178 distribution_type: DistributionType::Normal, parameters: vec![mean_val, std_val],
180 };
181
182 analysis_results.insert(noise_type.clone(), noise_stats);
183 }
184
185 Ok(StatisticalNoiseAnalysis {
186 noise_statistics: analysis_results,
187 correlation_analysis: self.perform_correlation_analysis(noise_data)?,
188 temporal_analysis: None, })
190 }
191
192 fn simple_noise_analysis(
194 &self,
195 noise_data: &HashMap<String, Array1<f64>>,
196 ) -> DeviceResult<StatisticalNoiseAnalysis> {
197 let mut analysis_results = HashMap::new();
198
199 for (noise_type, data) in noise_data {
200 let mean_val = data.mean().unwrap_or(0.01);
201 let std_val = data.std(1.0).max(0.001);
202
203 let noise_stats = NoiseStatistics {
204 mean: mean_val,
205 std_dev: std_val,
206 variance: std_val * std_val,
207 distribution_type: DistributionType::Normal,
208 parameters: vec![mean_val, std_val],
209 };
210
211 analysis_results.insert(noise_type.clone(), noise_stats);
212 }
213
214 Ok(StatisticalNoiseAnalysis {
215 noise_statistics: analysis_results,
216 correlation_analysis: CorrelationAnalysis {
217 correlationmatrix: scirs2_core::ndarray::Array2::eye(noise_data.len()),
218 correlation_strength: 0.1,
219 },
220 temporal_analysis: None,
221 })
222 }
223
224 fn perform_correlation_analysis(
226 &self,
227 noise_data: &HashMap<String, Array1<f64>>,
228 ) -> DeviceResult<CorrelationAnalysis> {
229 use scirs2_core::ndarray::Array2;
230
231 let n_sources = noise_data.len();
232 let mut correlationmatrix = Array2::eye(n_sources);
233
234 let sources: Vec<_> = noise_data.keys().collect();
236
237 for (i, source1) in sources.iter().enumerate() {
238 for (j, source2) in sources.iter().enumerate() {
239 if i != j {
240 let data1 = &noise_data[*source1];
241 let data2 = &noise_data[*source2];
242
243 let corr = if data1.len() == data2.len() && data1.len() > 1 {
245 let mean1 = data1.mean().unwrap_or(0.0);
246 let mean2 = data2.mean().unwrap_or(0.0);
247
248 let cov = data1
249 .iter()
250 .zip(data2.iter())
251 .map(|(x, y)| (x - mean1) * (y - mean2))
252 .sum::<f64>()
253 / (data1.len() - 1) as f64;
254
255 let std1 = data1.std(1.0).max(1e-10);
256 let std2 = data2.std(1.0).max(1e-10);
257
258 cov / (std1 * std2)
259 } else {
260 0.0
261 };
262
263 correlationmatrix[[i, j]] = corr;
264 }
265 }
266 }
267
268 let avg_correlation = correlationmatrix.iter()
269 .filter(|&&x| x != 1.0) .map(|&x| x.abs())
271 .sum::<f64>()
272 / ((n_sources * n_sources - n_sources) as f64).max(1.0);
273
274 Ok(CorrelationAnalysis {
275 correlationmatrix,
276 correlation_strength: avg_correlation,
277 })
278 }
279
280 fn build_calibration_noise_model(
282 &self,
283 calibration: &DeviceCalibration,
284 analysis: &StatisticalNoiseAnalysis,
285 ) -> DeviceResult<CalibrationNoiseModel> {
286 use crate::noise_model::{GateNoiseParams, QubitNoiseParams, ReadoutNoiseParams};
287 use std::collections::HashMap;
288
289 let mut qubit_noise = HashMap::new();
291 for i in 0..calibration.topology.num_qubits {
292 let qubit_id = QubitId(i as u32);
293
294 let t1_key = format!("t1_{}", i);
296 let t2_key = format!("t2_{}", i);
297
298 let t1_stats = analysis.noise_statistics.get(&t1_key);
299 let t2_stats = analysis.noise_statistics.get(&t2_key);
300
301 let qubit_params = QubitNoiseParams {
302 gamma_1: t1_stats.map(|s| 1.0 / s.mean).unwrap_or(1.0 / 50000.0), gamma_phi: t2_stats.map(|s| 1.0 / s.mean).unwrap_or(1.0 / 25000.0), thermal_population: 0.01,
305 frequency_drift: 0.001,
306 flicker_noise: 0.0005,
307 };
308
309 qubit_noise.insert(qubit_id, qubit_params);
310 }
311
312 let mut gate_noise = HashMap::new();
314 for (gate_name, _) in &calibration.single_qubit_gates {
315 let noise_stats = analysis.noise_statistics.values().next();
316
317 let gate_params = GateNoiseParams {
318 depolarizing_rate: noise_stats.map(|s| s.mean).unwrap_or(0.001),
319 incoherent_error: noise_stats.map(|s| s.std_dev).unwrap_or(0.0005),
320 amplitude_noise: 0.0001,
321 coherent_error: 0.0002,
322 duration: 100.0, phase_noise: 0.0001,
324 };
325
326 gate_noise.insert(gate_name.clone(), gate_params);
327 }
328
329 let mut readout_noise = HashMap::new();
331 for i in 0..calibration.topology.num_qubits {
332 let qubit_id = QubitId(i as u32);
333 let readout_key = format!("readout_{}", i);
334
335 let readout_stats = analysis.noise_statistics.get(&readout_key);
336
337 let error_01 = readout_stats.map(|s| s.mean).unwrap_or(0.02);
338 let error_10 = readout_stats.map(|s| s.std_dev).unwrap_or(0.02);
339 let readout_params = ReadoutNoiseParams {
340 assignment_matrix: [[1.0 - error_01, error_01], [error_10, 1.0 - error_10]],
341 readout_excitation: 0.001,
342 readout_relaxation: 0.001,
343 };
344
345 readout_noise.insert(qubit_id, readout_params);
346 }
347
348 Ok(CalibrationNoiseModel {
349 device_id: self.device_id.clone(),
350 qubit_noise,
351 gate_noise,
352 two_qubit_noise: HashMap::new(),
353 readout_noise,
354 crosstalk: CrosstalkNoise {
355 crosstalk_matrix: {
356 let matrix = &analysis.correlation_analysis.correlationmatrix;
357 let rows = matrix.nrows();
358 let cols = matrix.ncols();
359 let mut vec_matrix = Vec::with_capacity(rows);
360 for i in 0..rows {
361 let mut row = Vec::with_capacity(cols);
362 for j in 0..cols {
363 row.push(matrix[[i, j]]);
364 }
365 vec_matrix.push(row);
366 }
367 vec_matrix
368 },
369 threshold: 0.1,
370 single_qubit_crosstalk: 0.001,
371 two_qubit_crosstalk: 0.005,
372 },
373 temperature: 15.0, })
375 }
376
377 pub fn update_config(&mut self, config: SciRS2NoiseConfig) {
379 self.config = config;
380 }
381
382 pub fn config(&self) -> &SciRS2NoiseConfig {
384 &self.config
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use crate::calibration::create_ideal_calibration;
392
393 #[test]
394 fn test_noise_modeler_creation() {
395 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
396 assert_eq!(modeler.device_id, "test_device");
397 assert!(modeler.config.enable_ml_modeling);
398 }
399
400 #[test]
401 fn test_noise_data_extraction() {
402 let calibration = create_ideal_calibration("test".to_string(), 4);
403 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
404
405 let noise_data = modeler.extract_noise_data(&calibration).unwrap();
406 assert!(!noise_data.is_empty());
407
408 for i in 0..4 {
410 let key = format!("readout_{}", i);
411 assert!(noise_data.contains_key(&key));
412 }
413 }
414
415 #[test]
416 fn test_simple_noise_analysis() {
417 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
418 let mut noise_data = HashMap::new();
419
420 noise_data.insert(
422 "test_noise".to_string(),
423 Array1::from_vec(vec![0.01, 0.02, 0.015]),
424 );
425
426 let analysis = modeler.simple_noise_analysis(&noise_data).unwrap();
427 assert!(analysis.noise_statistics.contains_key("test_noise"));
428 assert!(analysis.correlation_analysis.correlation_strength >= 0.0);
429 }
430
431 #[test]
432 fn test_full_noise_modeling() {
433 let calibration = create_ideal_calibration("test".to_string(), 2);
434 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
435
436 let noise_model = modeler.model_noise(&calibration).unwrap();
437 assert_eq!(noise_model.device_id, "test_device");
438 assert_eq!(noise_model.qubit_noise.len(), 2);
439 assert!(!noise_model.gate_noise.is_empty());
440 assert_eq!(noise_model.readout_noise.len(), 2);
441 }
442
443 #[test]
444 fn test_correlation_analysis() {
445 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
446 let mut noise_data = HashMap::new();
447
448 noise_data.insert(
450 "noise1".to_string(),
451 Array1::from_vec(vec![0.01, 0.02, 0.03]),
452 );
453 noise_data.insert(
454 "noise2".to_string(),
455 Array1::from_vec(vec![0.02, 0.04, 0.06]),
456 );
457
458 let correlation = modeler.perform_correlation_analysis(&noise_data).unwrap();
459 assert_eq!(correlation.correlationmatrix.shape(), [2, 2]);
460 assert_eq!(correlation.correlationmatrix[[0, 0]], 1.0);
461 assert_eq!(correlation.correlationmatrix[[1, 1]], 1.0);
462
463 assert!(correlation.correlationmatrix[[0, 1]].abs() > 0.5);
465 }
466}