1use crate::{DeviceError, DeviceResult};
8use scirs2_core::ndarray::{Array1, Array2};
9use scirs2_core::random::prelude::*;
10use scirs2_core::Complex64;
11use scirs2_stats::distributions; use scirs2_stats::{mean, std}; #[derive(Debug, Clone)]
16pub struct NoiseCharacterizationConfig {
17 pub num_samples: usize,
19 pub confidence_level: f64,
21 pub advanced_analysis: bool,
23 pub min_sample_size: usize,
25}
26
27impl Default for NoiseCharacterizationConfig {
28 fn default() -> Self {
29 Self {
30 num_samples: 10000,
31 confidence_level: 0.95,
32 advanced_analysis: true,
33 min_sample_size: 100,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum NoiseModelType {
41 Depolarizing,
43 AmplitudeDamping,
45 PhaseDamping,
47 Thermal,
49 ReadoutError,
51 Crosstalk,
53}
54
55#[derive(Debug, Clone)]
57pub struct NoiseCharacterizationResult {
58 pub noise_parameters: NoiseParameters,
60 pub confidence_intervals: ConfidenceIntervals,
62 pub fit_quality: FitQuality,
64 pub correlations: CorrelationAnalysis,
66}
67
68#[derive(Debug, Clone)]
70pub struct NoiseParameters {
71 pub mean_error_rate: f64,
73 pub std_error_rate: f64,
75 pub t1_time: Option<f64>,
77 pub t2_time: Option<f64>,
79 pub readout_fidelity: f64,
81 pub model_params: Vec<f64>,
83}
84
85#[derive(Debug, Clone)]
87pub struct ConfidenceIntervals {
88 pub confidence_level: f64,
90 pub error_rate_lower: f64,
92 pub error_rate_upper: f64,
94 pub t1_interval: Option<(f64, f64)>,
96 pub t2_interval: Option<(f64, f64)>,
98}
99
100#[derive(Debug, Clone)]
102pub struct FitQuality {
103 pub chi_squared: f64,
105 pub p_value: f64,
107 pub r_squared: f64,
109 pub rmse: f64,
111}
112
113#[derive(Debug, Clone)]
115pub struct CorrelationAnalysis {
116 pub correlation_matrix: Array2<f64>,
118 pub covariance_matrix: Array2<f64>,
120 pub significant_correlations: Vec<(usize, usize, f64)>,
122}
123
124pub struct NoiseCharacterizer {
126 config: NoiseCharacterizationConfig,
127 rng: StdRng,
128}
129
130impl NoiseCharacterizer {
131 pub fn new(config: NoiseCharacterizationConfig) -> Self {
133 Self {
134 config,
135 rng: StdRng::seed_from_u64(42),
136 }
137 }
138
139 pub fn default() -> Self {
141 Self::new(NoiseCharacterizationConfig::default())
142 }
143
144 pub fn characterize_noise(
154 &mut self,
155 measurement_data: &Array1<f64>,
156 expected_data: &Array1<f64>,
157 noise_model: NoiseModelType,
158 ) -> DeviceResult<NoiseCharacterizationResult> {
159 if measurement_data.len() < self.config.min_sample_size {
161 return Err(DeviceError::InvalidInput(format!(
162 "Insufficient samples: {} < minimum {}",
163 measurement_data.len(),
164 self.config.min_sample_size
165 )));
166 }
167
168 if measurement_data.len() != expected_data.len() {
169 return Err(DeviceError::InvalidInput(
170 "Measurement and expected data must have same length".to_string(),
171 ));
172 }
173
174 let errors: Array1<f64> =
176 (measurement_data - expected_data).mapv(|x| if x.abs() > 0.5 { 1.0 } else { 0.0 });
177
178 let mean_error = mean(&errors.view())?;
179 let std_error = std(&errors.view(), 1, None)?; let noise_params =
183 self.estimate_noise_parameters(measurement_data, expected_data, &errors, noise_model)?;
184
185 let confidence_intervals = self.compute_confidence_intervals(&errors, &noise_params)?;
187
188 let fit_quality =
190 self.assess_fit_quality(measurement_data, expected_data, &noise_params, noise_model)?;
191
192 let correlations = if self.config.advanced_analysis {
194 self.analyze_correlations(measurement_data)?
195 } else {
196 CorrelationAnalysis {
197 correlation_matrix: Array2::zeros((1, 1)),
198 covariance_matrix: Array2::zeros((1, 1)),
199 significant_correlations: Vec::new(),
200 }
201 };
202
203 Ok(NoiseCharacterizationResult {
204 noise_parameters: noise_params,
205 confidence_intervals,
206 fit_quality,
207 correlations,
208 })
209 }
210
211 fn estimate_noise_parameters(
213 &self,
214 measurement_data: &Array1<f64>,
215 expected_data: &Array1<f64>,
216 errors: &Array1<f64>,
217 noise_model: NoiseModelType,
218 ) -> DeviceResult<NoiseParameters> {
219 let mean_error = mean(&errors.view())?;
220 let std_error = std(&errors.view(), 1, None)?;
221
222 let correct_measurements = errors.iter().filter(|&&e| e < 0.5).count();
224 let readout_fidelity = correct_measurements as f64 / errors.len() as f64;
225
226 let (t1_time, t2_time, model_params) = match noise_model {
228 NoiseModelType::Depolarizing => {
229 let p = mean_error;
231 (None, None, vec![p])
232 }
233 NoiseModelType::AmplitudeDamping => {
234 let t1 = self.estimate_t1_time(measurement_data)?;
236 (Some(t1), None, vec![t1])
237 }
238 NoiseModelType::PhaseDamping => {
239 let t2 = self.estimate_t2_time(measurement_data)?;
241 (None, Some(t2), vec![t2])
242 }
243 NoiseModelType::Thermal => {
244 let thermal_prob = mean_error;
246 (None, None, vec![thermal_prob])
247 }
248 NoiseModelType::ReadoutError => {
249 let p_0_given_1 =
251 self.estimate_readout_error(measurement_data, expected_data, 0)?;
252 let p_1_given_0 =
253 self.estimate_readout_error(measurement_data, expected_data, 1)?;
254 (None, None, vec![p_0_given_1, p_1_given_0])
255 }
256 NoiseModelType::Crosstalk => {
257 let crosstalk_strength = std_error;
259 (None, None, vec![crosstalk_strength])
260 }
261 };
262
263 Ok(NoiseParameters {
264 mean_error_rate: mean_error,
265 std_error_rate: std_error,
266 t1_time,
267 t2_time,
268 readout_fidelity,
269 model_params,
270 })
271 }
272
273 fn estimate_t1_time(&self, measurement_data: &Array1<f64>) -> DeviceResult<f64> {
275 let n = measurement_data.len();
278 if n < 10 {
279 return Ok(30.0); }
281
282 let times: Array1<f64> = Array1::from_shape_fn(n, |i| i as f64);
284
285 let log_probs: Array1<f64> = measurement_data.mapv(|p| (p.max(0.01)).ln());
287
288 let mean_t = mean(×.view())?;
290 let mean_log_p = mean(&log_probs.view())?;
291
292 let numerator: f64 = times
293 .iter()
294 .zip(log_probs.iter())
295 .map(|(&t, &lp)| (t - mean_t) * (lp - mean_log_p))
296 .sum();
297
298 let denominator: f64 = times.iter().map(|&t| (t - mean_t).powi(2)).sum();
299
300 if denominator.abs() < 1e-10 {
301 return Ok(30.0);
302 }
303
304 let slope = numerator / denominator;
305 let t1 = -1.0 / slope;
306
307 Ok(t1.clamp(1.0, 1000.0))
309 }
310
311 fn estimate_t2_time(&self, measurement_data: &Array1<f64>) -> DeviceResult<f64> {
313 let t2_estimate = self.estimate_t1_time(measurement_data)? * 0.8;
316 Ok(t2_estimate.clamp(1.0, 500.0))
317 }
318
319 fn estimate_readout_error(
321 &self,
322 measurement_data: &Array1<f64>,
323 expected_data: &Array1<f64>,
324 state: usize,
325 ) -> DeviceResult<f64> {
326 let expected_state = state as f64;
327 let count_expected: usize = expected_data
328 .iter()
329 .filter(|&&e| (e - expected_state).abs() < 0.5)
330 .count();
331
332 if count_expected == 0 {
333 return Ok(0.01); }
335
336 let count_errors: usize = expected_data
337 .iter()
338 .zip(measurement_data.iter())
339 .filter(|(&e, &m)| (e - expected_state).abs() < 0.5 && (m - expected_state).abs() > 0.5)
340 .count();
341
342 Ok((count_errors as f64 / count_expected as f64).clamp(0.0, 0.5))
343 }
344
345 fn compute_confidence_intervals(
347 &self,
348 errors: &Array1<f64>,
349 noise_params: &NoiseParameters,
350 ) -> DeviceResult<ConfidenceIntervals> {
351 let n = errors.len() as f64;
352 let mean_err = noise_params.mean_error_rate;
353 let std_err = noise_params.std_error_rate;
354
355 let z_critical = if self.config.confidence_level >= 0.99 {
357 2.576 } else if self.config.confidence_level >= 0.95 {
359 1.96 } else {
361 1.645 };
363
364 let margin_of_error = z_critical * std_err / n.sqrt();
365
366 Ok(ConfidenceIntervals {
367 confidence_level: self.config.confidence_level,
368 error_rate_lower: (mean_err - margin_of_error).max(0.0),
369 error_rate_upper: (mean_err + margin_of_error).min(1.0),
370 t1_interval: noise_params.t1_time.map(|t1| {
371 let margin = 0.1 * t1; (t1 - margin, t1 + margin)
373 }),
374 t2_interval: noise_params.t2_time.map(|t2| {
375 let margin = 0.1 * t2;
376 (t2 - margin, t2 + margin)
377 }),
378 })
379 }
380
381 fn assess_fit_quality(
383 &self,
384 measurement_data: &Array1<f64>,
385 expected_data: &Array1<f64>,
386 noise_params: &NoiseParameters,
387 noise_model: NoiseModelType,
388 ) -> DeviceResult<FitQuality> {
389 let residuals: Array1<f64> = measurement_data - expected_data;
391
392 let rmse = (residuals.mapv(|r| r * r).mean().unwrap_or(0.0)).sqrt();
394
395 let mean_measured = mean(&measurement_data.view())?;
397 let ss_tot: f64 = measurement_data
398 .iter()
399 .map(|&y| (y - mean_measured).powi(2))
400 .sum();
401
402 let ss_res: f64 = residuals.iter().map(|&r| r.powi(2)).sum();
403
404 let r_squared = if ss_tot > 1e-10 {
405 1.0 - (ss_res / ss_tot)
406 } else {
407 0.0
408 };
409
410 let chi_squared = ss_res / noise_params.std_error_rate.max(0.01).powi(2);
412
413 let p_value = (-chi_squared / (2.0 * measurement_data.len() as f64)).exp();
415
416 Ok(FitQuality {
417 chi_squared,
418 p_value,
419 r_squared: r_squared.clamp(0.0, 1.0),
420 rmse,
421 })
422 }
423
424 fn analyze_correlations(
426 &self,
427 measurement_data: &Array1<f64>,
428 ) -> DeviceResult<CorrelationAnalysis> {
429 let chunk_size = (measurement_data.len() / 5).max(10);
431 let num_chunks = measurement_data.len() / chunk_size;
432
433 if num_chunks < 2 {
434 return Ok(CorrelationAnalysis {
435 correlation_matrix: Array2::eye(1),
436 covariance_matrix: Array2::zeros((1, 1)),
437 significant_correlations: Vec::new(),
438 });
439 }
440
441 let mut data_matrix = Array2::zeros((num_chunks, chunk_size));
443 for i in 0..num_chunks {
444 let start = i * chunk_size;
445 let end = (start + chunk_size).min(measurement_data.len());
446 let chunk_len = end - start;
447 for j in 0..chunk_len {
448 data_matrix[[i, j]] = measurement_data[start + j];
449 }
450 }
451
452 let corr_matrix = self.compute_covariance_matrix(&data_matrix)?;
454
455 let mut significant = Vec::new();
457 for i in 0..num_chunks {
458 for j in (i + 1)..num_chunks {
459 let corr = if i < corr_matrix.nrows() && j < corr_matrix.ncols() {
460 corr_matrix[[i, j]]
461 } else {
462 0.0
463 };
464 if corr.abs() > 0.5 {
465 significant.push((i, j, corr));
466 }
467 }
468 }
469
470 Ok(CorrelationAnalysis {
471 correlation_matrix: corr_matrix.clone(),
472 covariance_matrix: corr_matrix,
473 significant_correlations: significant,
474 })
475 }
476
477 fn compute_covariance_matrix(&self, data: &Array2<f64>) -> DeviceResult<Array2<f64>> {
479 let n = data.nrows();
480 let m = data.ncols();
481
482 if n < 2 {
483 return Ok(Array2::eye(1));
484 }
485
486 let mut means = Array1::zeros(m);
488 for j in 0..m {
489 let col_sum: f64 = (0..n).map(|i| data[[i, j]]).sum();
490 means[j] = col_sum / n as f64;
491 }
492
493 let mut cov = Array2::zeros((n, n));
495 for i in 0..n {
496 for j in 0..n {
497 let mut sum = 0.0;
498 for k in 0..m {
499 sum += (data[[i, k]] - means[k]) * (data[[j, k]] - means[k]);
500 }
501 cov[[i, j]] = sum / (m - 1) as f64;
502 }
503 }
504
505 Ok(cov)
506 }
507
508 pub fn generate_noise_samples(
510 &mut self,
511 noise_model: NoiseModelType,
512 num_samples: usize,
513 noise_strength: f64,
514 ) -> Array1<f64> {
515 match noise_model {
516 NoiseModelType::Depolarizing => {
517 Array1::from_shape_fn(num_samples, |_| {
519 if self.rng.gen::<f64>() < noise_strength {
520 1.0
521 } else {
522 0.0
523 }
524 })
525 }
526 NoiseModelType::AmplitudeDamping | NoiseModelType::PhaseDamping => {
527 Array1::from_shape_fn(num_samples, |i| {
529 let decay = (-(i as f64) / 50.0).exp();
530 let gaussian_noise =
531 self.rng.gen::<f64>() * noise_strength - noise_strength / 2.0;
532 (decay + gaussian_noise).clamp(0.0, 1.0)
533 })
534 }
535 _ => {
536 Array1::from_shape_fn(num_samples, |_| {
538 (self.rng.gen::<f64>() * noise_strength)
539 .abs()
540 .clamp(0.0, 1.0)
541 })
542 }
543 }
544 }
545}
546
547#[cfg(test)]
548mod tests {
549 use super::*;
550
551 #[test]
552 fn test_noise_characterizer_creation() {
553 let config = NoiseCharacterizationConfig::default();
554 let characterizer = NoiseCharacterizer::new(config);
555 assert_eq!(characterizer.config.num_samples, 10000);
556 }
557
558 #[test]
559 fn test_depolarizing_noise_characterization() {
560 let config = NoiseCharacterizationConfig {
561 num_samples: 1000,
562 confidence_level: 0.95,
563 advanced_analysis: false,
564 min_sample_size: 100,
565 };
566
567 let mut characterizer = NoiseCharacterizer::new(config);
568
569 let expected = Array1::zeros(1000);
571 let mut measurement = expected.clone();
572 for i in 0..100 {
573 measurement[i * 10] = 1.0; }
575
576 let result =
577 characterizer.characterize_noise(&measurement, &expected, NoiseModelType::Depolarizing);
578
579 assert!(result.is_ok());
580 let result = result.expect("Characterization failed");
581
582 assert!((result.noise_parameters.mean_error_rate - 0.1).abs() < 0.05);
584 assert!(result.confidence_intervals.error_rate_lower < 0.1);
585 assert!(result.confidence_intervals.error_rate_upper > 0.1);
586 }
587
588 #[test]
589 fn test_amplitude_damping_characterization() {
590 let config = NoiseCharacterizationConfig::default();
591 let mut characterizer = NoiseCharacterizer::new(config);
592
593 let n = 500;
595 let expected = Array1::ones(n);
596 let measurement = Array1::from_shape_fn(n, |i| (-(i as f64) / 50.0).exp());
597
598 let result = characterizer.characterize_noise(
599 &measurement,
600 &expected,
601 NoiseModelType::AmplitudeDamping,
602 );
603
604 assert!(result.is_ok());
605 let result = result.expect("Characterization failed");
606
607 assert!(result.noise_parameters.t1_time.is_some());
609 let t1 = result.noise_parameters.t1_time.expect("No T1 estimate");
610 assert!(t1 > 1.0 && t1 < 1000.0);
611 }
612
613 #[test]
614 fn test_readout_error_characterization() {
615 let config = NoiseCharacterizationConfig::default();
616 let mut characterizer = NoiseCharacterizer::new(config);
617
618 let n = 1000;
620 let mut expected = Array1::zeros(n);
621 let mut measurement = expected.clone();
622
623 for i in 0..50 {
624 expected[i * 20] = 0.0;
625 measurement[i * 20] = 1.0; }
627
628 let result =
629 characterizer.characterize_noise(&measurement, &expected, NoiseModelType::ReadoutError);
630
631 assert!(result.is_ok());
632 let result = result.expect("Characterization failed");
633 assert_eq!(result.noise_parameters.model_params.len(), 2);
634 }
635
636 #[test]
637 fn test_confidence_intervals() {
638 let config = NoiseCharacterizationConfig {
639 confidence_level: 0.95,
640 ..Default::default()
641 };
642 let mut characterizer = NoiseCharacterizer::new(config);
643
644 let expected = Array1::zeros(1000);
645 let measurement = Array1::from_shape_fn(1000, |i| if i % 10 == 0 { 1.0 } else { 0.0 });
646
647 let result =
648 characterizer.characterize_noise(&measurement, &expected, NoiseModelType::Depolarizing);
649
650 assert!(result.is_ok());
651 let result = result.expect("Characterization failed");
652
653 assert_eq!(result.confidence_intervals.confidence_level, 0.95);
654 assert!(result.confidence_intervals.error_rate_lower >= 0.0);
655 assert!(result.confidence_intervals.error_rate_upper <= 1.0);
656 assert!(
657 result.confidence_intervals.error_rate_lower
658 < result.confidence_intervals.error_rate_upper
659 );
660 }
661
662 #[test]
663 fn test_fit_quality_metrics() {
664 let config = NoiseCharacterizationConfig::default();
665 let mut characterizer = NoiseCharacterizer::new(config);
666
667 let expected = Array1::zeros(500);
668 let measurement = expected.clone();
669
670 let result =
671 characterizer.characterize_noise(&measurement, &expected, NoiseModelType::Depolarizing);
672
673 assert!(result.is_ok());
674 let result = result.expect("Characterization failed");
675
676 assert!(result.fit_quality.r_squared >= 0.0);
678 assert!(result.fit_quality.rmse >= 0.0);
679 }
680
681 #[test]
682 fn test_noise_sample_generation() {
683 let config = NoiseCharacterizationConfig::default();
684 let mut characterizer = NoiseCharacterizer::new(config);
685
686 let samples = characterizer.generate_noise_samples(NoiseModelType::Depolarizing, 1000, 0.1);
687
688 assert_eq!(samples.len(), 1000);
689
690 for &s in samples.iter() {
692 assert!((0.0..=1.0).contains(&s));
693 }
694 }
695
696 #[test]
697 fn test_insufficient_samples_error() {
698 let config = NoiseCharacterizationConfig {
699 min_sample_size: 100,
700 ..Default::default()
701 };
702 let mut characterizer = NoiseCharacterizer::new(config);
703
704 let expected = Array1::zeros(50);
705 let measurement = expected.clone();
706
707 let result =
708 characterizer.characterize_noise(&measurement, &expected, NoiseModelType::Depolarizing);
709
710 assert!(result.is_err());
711 }
712}