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 quantrs2_core::{error::QuantRS2Result, qubit::QubitId};
34use scirs2_core::ndarray::{Array1, Array2};
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 const 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)
163 .map_err(|e| DeviceError::APIError(format!("Statistical analysis error: {e:?}")))?;
164
165 let std_val = std(&data_view, 1, None)
166 .map_err(|e| DeviceError::APIError(format!("Statistical analysis error: {e:?}")))?;
167
168 let var_val = var(&data_view, 1, None)
169 .map_err(|e| DeviceError::APIError(format!("Statistical analysis error: {e:?}")))?;
170
171 let noise_stats = NoiseStatistics {
172 mean: mean_val,
173 std_dev: std_val,
174 variance: var_val,
175 distribution_type: DistributionType::Normal, parameters: vec![mean_val, std_val],
177 };
178
179 analysis_results.insert(noise_type.clone(), noise_stats);
180 }
181
182 Ok(StatisticalNoiseAnalysis {
183 noise_statistics: analysis_results,
184 correlation_analysis: self.perform_correlation_analysis(noise_data)?,
185 temporal_analysis: None, })
187 }
188
189 fn simple_noise_analysis(
191 &self,
192 noise_data: &HashMap<String, Array1<f64>>,
193 ) -> DeviceResult<StatisticalNoiseAnalysis> {
194 let mut analysis_results = HashMap::new();
195
196 for (noise_type, data) in noise_data {
197 let mean_val = data.mean().unwrap_or(0.01);
198 let std_val = data.std(1.0).max(0.001);
199
200 let noise_stats = NoiseStatistics {
201 mean: mean_val,
202 std_dev: std_val,
203 variance: std_val * std_val,
204 distribution_type: DistributionType::Normal,
205 parameters: vec![mean_val, std_val],
206 };
207
208 analysis_results.insert(noise_type.clone(), noise_stats);
209 }
210
211 Ok(StatisticalNoiseAnalysis {
212 noise_statistics: analysis_results,
213 correlation_analysis: CorrelationAnalysis {
214 correlationmatrix: scirs2_core::ndarray::Array2::eye(noise_data.len()),
215 correlation_strength: 0.1,
216 },
217 temporal_analysis: None,
218 })
219 }
220
221 fn perform_correlation_analysis(
223 &self,
224 noise_data: &HashMap<String, Array1<f64>>,
225 ) -> DeviceResult<CorrelationAnalysis> {
226 use scirs2_core::ndarray::Array2;
227
228 let n_sources = noise_data.len();
229 let mut correlationmatrix = Array2::eye(n_sources);
230
231 let sources: Vec<_> = noise_data.keys().collect();
233
234 for (i, source1) in sources.iter().enumerate() {
235 for (j, source2) in sources.iter().enumerate() {
236 if i != j {
237 let data1 = &noise_data[*source1];
238 let data2 = &noise_data[*source2];
239
240 let corr = if data1.len() == data2.len() && data1.len() > 1 {
242 let mean1 = data1.mean().unwrap_or(0.0);
243 let mean2 = data2.mean().unwrap_or(0.0);
244
245 let cov = data1
246 .iter()
247 .zip(data2.iter())
248 .map(|(x, y)| (x - mean1) * (y - mean2))
249 .sum::<f64>()
250 / (data1.len() - 1) as f64;
251
252 let std1 = data1.std(1.0).max(1e-10);
253 let std2 = data2.std(1.0).max(1e-10);
254
255 cov / (std1 * std2)
256 } else {
257 0.0
258 };
259
260 correlationmatrix[[i, j]] = corr;
261 }
262 }
263 }
264
265 let avg_correlation = correlationmatrix.iter()
266 .filter(|&&x| x != 1.0) .map(|&x| x.abs())
268 .sum::<f64>()
269 / ((n_sources * n_sources - n_sources) as f64).max(1.0);
270
271 Ok(CorrelationAnalysis {
272 correlationmatrix,
273 correlation_strength: avg_correlation,
274 })
275 }
276
277 fn build_calibration_noise_model(
279 &self,
280 calibration: &DeviceCalibration,
281 analysis: &StatisticalNoiseAnalysis,
282 ) -> DeviceResult<CalibrationNoiseModel> {
283 use crate::noise_model::{GateNoiseParams, QubitNoiseParams, ReadoutNoiseParams};
284 use std::collections::HashMap;
285
286 let mut qubit_noise = HashMap::new();
288 for i in 0..calibration.topology.num_qubits {
289 let qubit_id = QubitId(i as u32);
290
291 let t1_key = format!("t1_{i}");
293 let t2_key = format!("t2_{i}");
294
295 let t1_stats = analysis.noise_statistics.get(&t1_key);
296 let t2_stats = analysis.noise_statistics.get(&t2_key);
297
298 let qubit_params = QubitNoiseParams {
299 gamma_1: t1_stats.map_or(1.0 / 50000.0, |s| 1.0 / s.mean), gamma_phi: t2_stats.map_or(1.0 / 25000.0, |s| 1.0 / s.mean), thermal_population: 0.01,
302 frequency_drift: 0.001,
303 flicker_noise: 0.0005,
304 };
305
306 qubit_noise.insert(qubit_id, qubit_params);
307 }
308
309 let mut gate_noise = HashMap::new();
311 for gate_name in calibration.single_qubit_gates.keys() {
312 let noise_stats = analysis.noise_statistics.values().next();
313
314 let gate_params = GateNoiseParams {
315 depolarizing_rate: noise_stats.map_or(0.001, |s| s.mean),
316 incoherent_error: noise_stats.map_or(0.0005, |s| s.std_dev),
317 amplitude_noise: 0.0001,
318 coherent_error: 0.0002,
319 duration: 100.0, phase_noise: 0.0001,
321 };
322
323 gate_noise.insert(gate_name.clone(), gate_params);
324 }
325
326 let mut readout_noise = HashMap::new();
328 for i in 0..calibration.topology.num_qubits {
329 let qubit_id = QubitId(i as u32);
330 let readout_key = format!("readout_{i}");
331
332 let readout_stats = analysis.noise_statistics.get(&readout_key);
333
334 let error_01 = readout_stats.map_or(0.02, |s| s.mean);
335 let error_10 = readout_stats.map_or(0.02, |s| s.std_dev);
336 let readout_params = ReadoutNoiseParams {
337 assignment_matrix: [[1.0 - error_01, error_01], [error_10, 1.0 - error_10]],
338 readout_excitation: 0.001,
339 readout_relaxation: 0.001,
340 };
341
342 readout_noise.insert(qubit_id, readout_params);
343 }
344
345 Ok(CalibrationNoiseModel {
346 device_id: self.device_id.clone(),
347 qubit_noise,
348 gate_noise,
349 two_qubit_noise: HashMap::new(),
350 readout_noise,
351 crosstalk: CrosstalkNoise {
352 crosstalk_matrix: {
353 let matrix = &analysis.correlation_analysis.correlationmatrix;
354 let rows = matrix.nrows();
355 let cols = matrix.ncols();
356 let mut vec_matrix = Vec::with_capacity(rows);
357 for i in 0..rows {
358 let mut row = Vec::with_capacity(cols);
359 for j in 0..cols {
360 row.push(matrix[[i, j]]);
361 }
362 vec_matrix.push(row);
363 }
364 vec_matrix
365 },
366 threshold: 0.1,
367 single_qubit_crosstalk: 0.001,
368 two_qubit_crosstalk: 0.005,
369 },
370 temperature: 15.0, })
372 }
373
374 pub fn update_config(&mut self, config: SciRS2NoiseConfig) {
376 self.config = config;
377 }
378
379 pub const fn config(&self) -> &SciRS2NoiseConfig {
381 &self.config
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::calibration::create_ideal_calibration;
389
390 #[test]
391 fn test_noise_modeler_creation() {
392 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
393 assert_eq!(modeler.device_id, "test_device");
394 assert!(modeler.config.enable_ml_modeling);
395 }
396
397 #[test]
398 fn test_noise_data_extraction() {
399 let calibration = create_ideal_calibration("test".to_string(), 4);
400 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
401
402 let noise_data = modeler
403 .extract_noise_data(&calibration)
404 .expect("noise data extraction should succeed");
405 assert!(!noise_data.is_empty());
406
407 for i in 0..4 {
409 let key = format!("readout_{}", i);
410 assert!(noise_data.contains_key(&key));
411 }
412 }
413
414 #[test]
415 fn test_simple_noise_analysis() {
416 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
417 let mut noise_data = HashMap::new();
418
419 noise_data.insert(
421 "test_noise".to_string(),
422 Array1::from_vec(vec![0.01, 0.02, 0.015]),
423 );
424
425 let analysis = modeler
426 .simple_noise_analysis(&noise_data)
427 .expect("simple noise analysis should succeed");
428 assert!(analysis.noise_statistics.contains_key("test_noise"));
429 assert!(analysis.correlation_analysis.correlation_strength >= 0.0);
430 }
431
432 #[test]
433 fn test_full_noise_modeling() {
434 let calibration = create_ideal_calibration("test".to_string(), 2);
435 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
436
437 let noise_model = modeler
438 .model_noise(&calibration)
439 .expect("noise modeling should succeed");
440 assert_eq!(noise_model.device_id, "test_device");
441 assert_eq!(noise_model.qubit_noise.len(), 2);
442 assert!(!noise_model.gate_noise.is_empty());
443 assert_eq!(noise_model.readout_noise.len(), 2);
444 }
445
446 #[test]
447 fn test_correlation_analysis() {
448 let modeler = SciRS2NoiseModeler::new("test_device".to_string());
449 let mut noise_data = HashMap::new();
450
451 noise_data.insert(
453 "noise1".to_string(),
454 Array1::from_vec(vec![0.01, 0.02, 0.03]),
455 );
456 noise_data.insert(
457 "noise2".to_string(),
458 Array1::from_vec(vec![0.02, 0.04, 0.06]),
459 );
460
461 let correlation = modeler
462 .perform_correlation_analysis(&noise_data)
463 .expect("correlation analysis should succeed");
464 assert_eq!(correlation.correlationmatrix.shape(), [2, 2]);
465 assert_eq!(correlation.correlationmatrix[[0, 0]], 1.0);
466 assert_eq!(correlation.correlationmatrix[[1, 1]], 1.0);
467
468 assert!(correlation.correlationmatrix[[0, 1]].abs() > 0.5);
470 }
471}