1use quantrs2_circuit::prelude::*;
8use quantrs2_core::{
9 error::{QuantRS2Error, QuantRS2Result},
10 gate::{
11 single::{Hadamard, PauliX, PauliY, PauliZ, RotationY},
12 GateOp,
13 },
14 qubit::QubitId,
15};
16use scirs2_core::ndarray::{Array1, Array2, Array3, ArrayView1, ArrayView2};
17use scirs2_core::Complex64;
18use std::collections::HashMap;
19use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
20
21#[cfg(feature = "scirs2")]
23use scirs2_linalg::{det, eig, inv, matrix_norm, qr, svd, trace, LinalgResult};
24#[cfg(feature = "scirs2")]
25use scirs2_optimize::{minimize, OptimizeResult};
26#[cfg(feature = "scirs2")]
27use scirs2_stats::{
28 distributions::{beta, chi2, gamma, norm},
29 ks_2samp, mean, pearsonr, spearmanr, std,
30 ttest::Alternative,
31 ttest_1samp, ttest_ind, var, TTestResult,
32};
33
34#[cfg(not(feature = "scirs2"))]
35use crate::ml_optimization::fallback_scirs2::{mean, minimize, pearsonr, std, var, OptimizeResult};
36
37use crate::{
38 calibration::{CalibrationManager, DeviceCalibration},
39 noise_modeling_scirs2::{SciRS2NoiseConfig, SciRS2NoiseModeler, StatisticalNoiseAnalysis},
40 topology::HardwareTopology,
41 CircuitResult, DeviceError, DeviceResult,
42};
43
44pub struct ProcessTomography {
46 num_qubits: usize,
48 measurement_basis: Vec<String>,
50 preparation_basis: Vec<String>,
52}
53
54impl ProcessTomography {
55 pub fn new(num_qubits: usize) -> Self {
57 let bases = vec![
58 "I".to_string(),
59 "X".to_string(),
60 "Y".to_string(),
61 "Z".to_string(),
62 ];
63 Self {
64 num_qubits,
65 measurement_basis: bases.clone(),
66 preparation_basis: bases,
67 }
68 }
69
70 pub fn preparation_circuits(&self) -> Vec<Vec<Box<dyn GateOp>>> {
72 let mut circuits = Vec::new();
73 let basis_size = self.preparation_basis.len();
74 let total_configs = basis_size.pow(self.num_qubits as u32);
75
76 for config in 0..total_configs {
77 let mut circuit = Vec::new();
78 let mut temp = config;
79
80 for qubit in 0..self.num_qubits {
81 let basis_idx = temp % basis_size;
82 temp /= basis_size;
83
84 match self.preparation_basis[basis_idx].as_str() {
85 "I" => {} "X" => {
87 circuit.push(Box::new(Hadamard {
89 target: QubitId::new(qubit as u32),
90 }) as Box<dyn GateOp>);
91 }
92 "Y" => {
93 circuit.push(Box::new(Hadamard {
95 target: QubitId::new(qubit as u32),
96 }) as Box<dyn GateOp>);
97 circuit.push(Box::new(RotationY {
98 target: QubitId::new(qubit as u32),
99 theta: std::f64::consts::PI / 2.0,
100 }) as Box<dyn GateOp>);
101 }
102 "Z" | _ => {
103 }
105 }
106 }
107
108 circuits.push(circuit);
109 }
110
111 circuits
112 }
113
114 pub fn measurement_circuits(&self) -> Vec<Vec<Box<dyn GateOp>>> {
116 let mut circuits = Vec::new();
117 let basis_size = self.measurement_basis.len();
118 let total_configs = basis_size.pow(self.num_qubits as u32);
119
120 for config in 0..total_configs {
121 let mut circuit = Vec::new();
122 let mut temp = config;
123
124 for qubit in 0..self.num_qubits {
125 let basis_idx = temp % basis_size;
126 temp /= basis_size;
127
128 match self.measurement_basis[basis_idx].as_str() {
129 "X" => {
130 circuit.push(Box::new(Hadamard {
132 target: QubitId::new(qubit as u32),
133 }) as Box<dyn GateOp>);
134 }
135 "Y" => {
136 circuit.push(Box::new(RotationY {
138 target: QubitId::new(qubit as u32),
139 theta: -std::f64::consts::PI / 2.0,
140 }) as Box<dyn GateOp>);
141 circuit.push(Box::new(Hadamard {
142 target: QubitId::new(qubit as u32),
143 }) as Box<dyn GateOp>);
144 }
145 "I" | "Z" | _ => {} }
147 }
148
149 circuits.push(circuit);
150 }
151
152 circuits
153 }
154
155 pub fn reconstruct_process_matrix(
157 &self,
158 measurement_data: &HashMap<(usize, usize), Vec<f64>>,
159 ) -> QuantRS2Result<Array2<Complex64>> {
160 let dim = 2_usize.pow(self.num_qubits as u32);
161 let super_dim = dim * dim;
162
163 let mut a_matrix = Array2::<f64>::zeros((super_dim * super_dim, super_dim * super_dim));
165 let mut b_vector = Array1::<f64>::zeros(super_dim * super_dim);
166
167 let prep_circuits = self.preparation_circuits();
169 let meas_circuits = self.measurement_circuits();
170
171 let mut constraint_idx = 0;
172 for (prep_idx, _prep) in prep_circuits.iter().enumerate() {
173 for (meas_idx, _meas) in meas_circuits.iter().enumerate() {
174 if let Some(probs) = measurement_data.get(&(prep_idx, meas_idx)) {
175 for (outcome_idx, &prob) in probs.iter().enumerate() {
177 if constraint_idx < super_dim * super_dim {
178 b_vector[constraint_idx] = prob;
179 constraint_idx += 1;
181 }
182 }
183 }
184 }
185 }
186
187 let chi_matrix = Array2::<Complex64>::zeros((super_dim, super_dim));
189
190 Ok(chi_matrix)
191 }
192}
193
194pub struct StateTomography {
196 num_qubits: usize,
198 measurement_basis: Vec<String>,
200}
201
202impl StateTomography {
203 pub fn new(num_qubits: usize) -> Self {
205 Self {
206 num_qubits,
207 measurement_basis: vec!["X".to_string(), "Y".to_string(), "Z".to_string()],
208 }
209 }
210
211 pub fn measurement_circuits(&self) -> Vec<Vec<Box<dyn GateOp>>> {
213 let mut circuits = Vec::new();
214 let basis_size = self.measurement_basis.len();
215 let total_configs = basis_size.pow(self.num_qubits as u32);
216
217 for config in 0..total_configs {
218 let mut circuit = Vec::new();
219 let mut temp = config;
220
221 for qubit in 0..self.num_qubits {
222 let basis_idx = temp % basis_size;
223 temp /= basis_size;
224
225 match self.measurement_basis[basis_idx].as_str() {
226 "X" => {
227 circuit.push(Box::new(Hadamard {
228 target: QubitId::new(qubit as u32),
229 }) as Box<dyn GateOp>);
230 }
231 "Y" => {
232 circuit.push(Box::new(RotationY {
233 target: QubitId::new(qubit as u32),
234 theta: -std::f64::consts::PI / 2.0,
235 }) as Box<dyn GateOp>);
236 circuit.push(Box::new(Hadamard {
237 target: QubitId::new(qubit as u32),
238 }) as Box<dyn GateOp>);
239 }
240 "Z" | _ => {} }
242 }
243
244 circuits.push(circuit);
245 }
246
247 circuits
248 }
249
250 pub fn reconstruct_density_matrix(
252 &self,
253 measurement_data: &HashMap<usize, Vec<f64>>,
254 ) -> QuantRS2Result<Array2<Complex64>> {
255 let dim = 2_usize.pow(self.num_qubits as u32);
256
257 let mut rho = Array2::<Complex64>::eye(dim) / dim as f64;
259
260 for _iter in 0..100 {
262 }
265
266 Ok(rho)
267 }
268}
269
270pub struct RandomizedBenchmarking {
272 qubits: Vec<QubitId>,
274 clifford_group: Vec<String>,
276}
277
278impl RandomizedBenchmarking {
279 pub fn new(qubits: Vec<QubitId>) -> Self {
281 Self {
282 qubits,
283 clifford_group: vec!["H", "S", "CNOT"]
284 .into_iter()
285 .map(String::from)
286 .collect(),
287 }
288 }
289
290 pub fn generate_clifford_sequence(&self, length: usize) -> Vec<Box<dyn GateOp>> {
292 use scirs2_core::random::prelude::*;
293 let mut rng = thread_rng();
294 let mut sequence = Vec::new();
295
296 for _ in 0..length {
297 let gate_idx = rng.gen_range(0..self.clifford_group.len());
299 match self.clifford_group[gate_idx].as_str() {
300 "H" => {
301 let qubit = self.qubits[rng.gen_range(0..self.qubits.len())];
302 sequence.push(Box::new(Hadamard { target: qubit }) as Box<dyn GateOp>);
303 }
304 "X" => {
305 let qubit = self.qubits[rng.gen_range(0..self.qubits.len())];
306 sequence.push(Box::new(PauliX { target: qubit }) as Box<dyn GateOp>);
307 }
308 "Y" => {
309 let qubit = self.qubits[rng.gen_range(0..self.qubits.len())];
310 sequence.push(Box::new(PauliY { target: qubit }) as Box<dyn GateOp>);
311 }
312 "Z" => {
313 let qubit = self.qubits[rng.gen_range(0..self.qubits.len())];
314 sequence.push(Box::new(PauliZ { target: qubit }) as Box<dyn GateOp>);
315 }
316 _ => {}
317 }
318 }
319
320 sequence
324 }
325
326 pub fn generate_rb_circuits(
328 &self,
329 lengths: &[usize],
330 num_sequences: usize,
331 ) -> HashMap<usize, Vec<Vec<Box<dyn GateOp>>>> {
332 let mut circuits = HashMap::new();
333
334 for &length in lengths {
335 let mut length_circuits = Vec::new();
336 for _ in 0..num_sequences {
337 length_circuits.push(self.generate_clifford_sequence(length));
338 }
339 circuits.insert(length, length_circuits);
340 }
341
342 circuits
343 }
344
345 pub fn extract_error_rate(&self, rb_data: &HashMap<usize, Vec<f64>>) -> QuantRS2Result<f64> {
347 let mut x_values = Vec::new();
351 let mut y_values = Vec::new();
352
353 for (&length, survival_probs) in rb_data {
354 let avg_survival = survival_probs.iter().sum::<f64>() / survival_probs.len() as f64;
355 x_values.push(length as f64);
356 y_values.push(avg_survival);
357 }
358
359 let error_rate = 0.001; Ok(error_rate)
364 }
365}
366
367pub struct CrosstalkCharacterization {
369 num_qubits: usize,
371}
372
373impl CrosstalkCharacterization {
374 pub const fn new(num_qubits: usize) -> Self {
376 Self { num_qubits }
377 }
378
379 pub fn generate_crosstalk_circuits(
381 &self,
382 target_qubit: QubitId,
383 spectator_qubits: &[QubitId],
384 ) -> Vec<Vec<Box<dyn GateOp>>> {
385 let mut circuits = Vec::new();
386
387 circuits.push(vec![Box::new(Hadamard {
389 target: target_qubit,
390 }) as Box<dyn GateOp>]);
391
392 for &spectator in spectator_qubits {
394 let mut circuit = vec![
395 Box::new(Hadamard {
396 target: target_qubit,
397 }) as Box<dyn GateOp>,
398 Box::new(PauliX { target: spectator }) as Box<dyn GateOp>,
399 ];
400 circuits.push(circuit);
401 }
402
403 let mut circuit = vec![Box::new(Hadamard {
405 target: target_qubit,
406 }) as Box<dyn GateOp>];
407 for &spectator in spectator_qubits {
408 circuit.push(Box::new(PauliX { target: spectator }) as Box<dyn GateOp>);
409 }
410 circuits.push(circuit);
411
412 circuits
413 }
414
415 pub fn extract_crosstalk_matrix(
417 &self,
418 measurement_data: &HashMap<usize, Vec<f64>>,
419 ) -> QuantRS2Result<Array2<f64>> {
420 let mut crosstalk = Array2::<f64>::zeros((self.num_qubits, self.num_qubits));
421
422 Ok(crosstalk)
427 }
428}
429
430pub struct DriftTracker {
432 tracked_params: Vec<String>,
434 history: HashMap<String, Vec<(f64, f64)>>, }
437
438impl DriftTracker {
439 pub fn new(params: Vec<String>) -> Self {
441 Self {
442 tracked_params: params,
443 history: HashMap::new(),
444 }
445 }
446
447 pub fn add_measurement(&mut self, param: &str, timestamp: f64, value: f64) {
449 self.history
450 .entry(param.to_string())
451 .or_default()
452 .push((timestamp, value));
453 }
454
455 pub fn detect_drift(&self, param: &str, window_size: usize) -> Option<f64> {
457 if let Some(history) = self.history.get(param) {
458 if history.len() < window_size * 2 {
459 return None;
460 }
461
462 let recent_start = history.len() - window_size;
464 let early_end = history.len() - window_size;
465
466 let recent_avg: f64 =
467 history[recent_start..].iter().map(|(_, v)| v).sum::<f64>() / window_size as f64;
468
469 let early_avg: f64 = history[..early_end]
470 .iter()
471 .take(window_size)
472 .map(|(_, v)| v)
473 .sum::<f64>()
474 / window_size as f64;
475
476 Some((recent_avg - early_avg).abs())
477 } else {
478 None
479 }
480 }
481}
482
483#[derive(Debug, Clone)]
485pub struct AdvancedNoiseCharacterizer {
486 device_id: String,
487 calibration_manager: CalibrationManager,
488 noise_modeler: SciRS2NoiseModeler,
489 config: NoiseCharacterizationConfig,
490 measurement_history: HashMap<String, Vec<CharacterizationMeasurement>>,
491}
492
493#[derive(Debug, Clone)]
495pub struct NoiseCharacterizationConfig {
496 pub enable_advanced_statistics: bool,
498 pub enable_ml_predictions: bool,
500 pub enable_drift_monitoring: bool,
502 pub update_frequency_hours: f64,
504 pub confidence_level: f64,
506 pub protocol_repetitions: usize,
508 pub enable_crosstalk_analysis: bool,
510 pub enable_temporal_analysis: bool,
512}
513
514impl Default for NoiseCharacterizationConfig {
515 fn default() -> Self {
516 Self {
517 enable_advanced_statistics: true,
518 enable_ml_predictions: true,
519 enable_drift_monitoring: true,
520 update_frequency_hours: 24.0,
521 confidence_level: 0.95,
522 protocol_repetitions: 100,
523 enable_crosstalk_analysis: true,
524 enable_temporal_analysis: true,
525 }
526 }
527}
528
529#[derive(Debug, Clone)]
531pub struct CharacterizationMeasurement {
532 pub timestamp: SystemTime,
533 pub protocol_type: CharacterizationProtocol,
534 pub target_qubits: Vec<QubitId>,
535 pub measurement_type: String,
536 pub value: f64,
537 pub error: f64,
538 pub metadata: HashMap<String, String>,
539}
540
541#[derive(Debug, Clone, PartialEq, Eq, Hash)]
543pub enum CharacterizationProtocol {
544 ProcessTomography,
545 StateTomography,
546 RandomizedBenchmarking,
547 CrosstalkCharacterization,
548 CoherenceDecay,
549 RabiOscillation,
550 EchoSequences,
551 PulsedGateCalibration,
552 ReadoutFidelity,
553 Custom(String),
554}
555
556#[derive(Debug, Clone)]
558pub struct ComprehensiveCharacterizationResult {
559 pub device_id: String,
560 pub timestamp: SystemTime,
561 pub protocol_results: HashMap<CharacterizationProtocol, ProtocolResult>,
562 pub statistical_analysis: AdvancedStatisticalAnalysis,
563 pub noise_model_update: Option<crate::noise_model::CalibrationNoiseModel>,
564 pub drift_analysis: Option<DriftAnalysisResult>,
565 pub crosstalk_analysis: Option<AdvancedCrosstalkAnalysis>,
566 pub predictive_models: Option<PredictiveModels>,
567 pub recommendations: Vec<CharacterizationRecommendation>,
568}
569
570#[derive(Debug, Clone)]
572pub struct ProtocolResult {
573 pub protocol_type: CharacterizationProtocol,
574 pub success_rate: f64,
575 pub average_fidelity: f64,
576 pub error_rates: HashMap<String, f64>,
577 pub coherence_times: HashMap<QubitId, CoherenceMetrics>,
578 pub gate_fidelities: HashMap<String, f64>,
579 pub readout_fidelities: HashMap<QubitId, f64>,
580 pub execution_time: Duration,
581 pub raw_data: Option<Vec<f64>>,
582}
583
584#[derive(Debug, Clone)]
586pub struct AdvancedStatisticalAnalysis {
587 pub distribution_fits: HashMap<String, DistributionFitResult>,
588 pub correlation_analysis: CorrelationAnalysisResult,
589 pub hypothesis_tests: HashMap<String, StatisticalTestResult>,
590 pub outlier_detection: OutlierDetectionResult,
591 pub trend_analysis: TrendAnalysisResult,
592 pub confidence_intervals: HashMap<String, (f64, f64)>,
593}
594
595#[derive(Debug, Clone)]
597pub struct DriftAnalysisResult {
598 pub drift_rates: HashMap<String, f64>,
599 pub trend_significance: HashMap<String, bool>,
600 pub change_points: HashMap<String, Vec<SystemTime>>,
601 pub forecast: HashMap<String, TimeSeriesForecast>,
602 pub stability_score: f64,
603}
604
605#[derive(Debug, Clone)]
607pub struct AdvancedCrosstalkAnalysis {
608 pub crosstalk_matrix: Array2<f64>,
609 pub significant_pairs: Vec<(QubitId, QubitId, f64)>,
610 pub spatial_patterns: SpatialCrosstalkPattern,
611 pub temporal_variations: HashMap<String, Array1<f64>>,
612 pub mitigation_strategies: Vec<CrosstalkMitigationStrategy>,
613}
614
615#[derive(Debug, Clone)]
617pub struct PredictiveModels {
618 pub fidelity_predictor: ModelPrediction,
619 pub coherence_predictor: ModelPrediction,
620 pub error_rate_predictor: ModelPrediction,
621 pub drift_predictor: ModelPrediction,
622 pub model_accuracy: HashMap<String, f64>,
623}
624
625#[derive(Debug, Clone)]
627pub struct ModelPrediction {
628 pub predicted_values: Array1<f64>,
629 pub prediction_intervals: Array2<f64>,
630 pub feature_importance: HashMap<String, f64>,
631 pub model_type: String,
632 pub accuracy_metrics: ModelAccuracyMetrics,
633}
634
635#[derive(Debug, Clone)]
637pub struct CoherenceMetrics {
638 pub t1: f64,
639 pub t2: f64,
640 pub t2_echo: f64,
641 pub thermal_population: f64,
642}
643
644#[derive(Debug, Clone)]
645pub struct DistributionFitResult {
646 pub distribution_name: String,
647 pub parameters: Vec<f64>,
648 pub goodness_of_fit: f64,
649 pub p_value: f64,
650 pub aic: f64,
651 pub bic: f64,
652}
653
654#[derive(Debug, Clone)]
655pub struct CorrelationAnalysisResult {
656 pub correlationmatrix: Array2<f64>,
657 pub significant_correlations: Vec<(String, String, f64, f64)>,
658 pub correlation_network: HashMap<String, Vec<String>>,
659}
660
661#[derive(Debug, Clone)]
662pub struct StatisticalTestResult {
663 pub test_name: String,
664 pub statistic: f64,
665 pub p_value: f64,
666 pub significant: bool,
667 pub effect_size: Option<f64>,
668 pub interpretation: String,
669}
670
671#[derive(Debug, Clone)]
672pub struct OutlierDetectionResult {
673 pub outliers: HashMap<String, Vec<usize>>,
674 pub outlier_scores: HashMap<String, Array1<f64>>,
675 pub outlier_threshold: f64,
676 pub detection_method: String,
677}
678
679#[derive(Debug, Clone)]
680pub struct TrendAnalysisResult {
681 pub trend_coefficients: HashMap<String, f64>,
682 pub trend_significance: HashMap<String, bool>,
683 pub seasonal_components: HashMap<String, Array1<f64>>,
684 pub residuals: HashMap<String, Array1<f64>>,
685}
686
687#[derive(Debug, Clone)]
688pub struct TimeSeriesForecast {
689 pub forecast_values: Array1<f64>,
690 pub forecast_intervals: Array2<f64>,
691 pub forecast_horizon: Duration,
692 pub model_confidence: f64,
693}
694
695#[derive(Debug, Clone)]
696pub struct SpatialCrosstalkPattern {
697 pub spatial_correlation: Array2<f64>,
698 pub decay_constants: HashMap<String, f64>,
699 pub dominant_frequencies: Array1<f64>,
700 pub anisotropy_parameters: HashMap<String, f64>,
701}
702
703#[derive(Debug, Clone)]
704pub struct CrosstalkMitigationStrategy {
705 pub strategy_type: String,
706 pub target_pairs: Vec<(QubitId, QubitId)>,
707 pub expected_improvement: f64,
708 pub implementation_cost: f64,
709 pub description: String,
710}
711
712#[derive(Debug, Clone)]
713pub struct ModelAccuracyMetrics {
714 pub rmse: f64,
715 pub mae: f64,
716 pub r_squared: f64,
717 pub cross_validation_score: f64,
718}
719
720#[derive(Debug, Clone)]
721pub struct CharacterizationRecommendation {
722 pub priority: RecommendationPriority,
723 pub category: RecommendationCategory,
724 pub description: String,
725 pub expected_impact: f64,
726 pub implementation_effort: f64,
727 pub urgency_score: f64,
728}
729
730#[derive(Debug, Clone, PartialEq, Eq)]
731pub enum RecommendationPriority {
732 Critical,
733 High,
734 Medium,
735 Low,
736}
737
738#[derive(Debug, Clone, PartialEq, Eq)]
739pub enum RecommendationCategory {
740 Calibration,
741 Maintenance,
742 Protocol,
743 Analysis,
744 Hardware,
745}
746
747impl AdvancedNoiseCharacterizer {
748 pub fn new(
750 device_id: String,
751 calibration_manager: CalibrationManager,
752 config: NoiseCharacterizationConfig,
753 ) -> Self {
754 let noise_config = SciRS2NoiseConfig {
755 enable_ml_modeling: config.enable_ml_predictions,
756 enable_temporal_modeling: config.enable_temporal_analysis,
757 enable_spatial_modeling: config.enable_crosstalk_analysis,
758 ..Default::default()
759 };
760
761 let noise_modeler = SciRS2NoiseModeler::with_config(device_id.clone(), noise_config);
762
763 Self {
764 device_id,
765 calibration_manager,
766 noise_modeler,
767 config,
768 measurement_history: HashMap::new(),
769 }
770 }
771
772 pub async fn perform_comprehensive_characterization<E: CharacterizationExecutor>(
774 &mut self,
775 executor: &E,
776 ) -> DeviceResult<ComprehensiveCharacterizationResult> {
777 let start_time = Instant::now();
778 let timestamp = SystemTime::now();
779
780 let calibration = self
782 .calibration_manager
783 .get_calibration(&self.device_id)
784 .ok_or_else(|| DeviceError::APIError("No calibration data available".into()))?;
785
786 let mut protocol_results = HashMap::new();
788
789 if let Ok(result) = self.run_process_tomography(executor, calibration).await {
791 protocol_results.insert(CharacterizationProtocol::ProcessTomography, result);
792 }
793
794 if let Ok(result) = self
796 .run_randomized_benchmarking(executor, calibration)
797 .await
798 {
799 protocol_results.insert(CharacterizationProtocol::RandomizedBenchmarking, result);
800 }
801
802 if let Ok(result) = self.run_coherence_measurements(executor, calibration).await {
804 protocol_results.insert(CharacterizationProtocol::CoherenceDecay, result);
805 }
806
807 let crosstalk_analysis = if self.config.enable_crosstalk_analysis {
809 if let Ok(result) = self
810 .run_crosstalk_characterization(executor, calibration)
811 .await
812 {
813 protocol_results
814 .insert(CharacterizationProtocol::CrosstalkCharacterization, result);
815 Some(self.analyze_crosstalk_patterns(calibration)?)
816 } else {
817 None
818 }
819 } else {
820 None
821 };
822
823 if let Ok(result) = self.run_readout_fidelity(executor, calibration).await {
825 protocol_results.insert(CharacterizationProtocol::ReadoutFidelity, result);
826 }
827
828 let statistical_analysis = self.perform_advanced_statistical_analysis(&protocol_results)?;
830
831 let drift_analysis = if self.config.enable_drift_monitoring {
833 Some(self.analyze_drift_patterns()?)
834 } else {
835 None
836 };
837
838 let predictive_models = if self.config.enable_ml_predictions {
840 Some(self.build_predictive_models(&protocol_results, &statistical_analysis)?)
841 } else {
842 None
843 };
844
845 let noise_model_update = self.update_noise_model(calibration, &protocol_results)?;
847
848 let recommendations = self.generate_recommendations(
850 &protocol_results,
851 &statistical_analysis,
852 drift_analysis.as_ref(),
853 predictive_models.as_ref(),
854 )?;
855
856 Ok(ComprehensiveCharacterizationResult {
857 device_id: self.device_id.clone(),
858 timestamp,
859 protocol_results,
860 statistical_analysis,
861 noise_model_update: Some(noise_model_update),
862 drift_analysis,
863 crosstalk_analysis,
864 predictive_models,
865 recommendations,
866 })
867 }
868
869 async fn run_process_tomography<E: CharacterizationExecutor>(
871 &self,
872 executor: &E,
873 calibration: &DeviceCalibration,
874 ) -> DeviceResult<ProtocolResult> {
875 let start_time = Instant::now();
876 let mut error_rates = HashMap::new();
877 let mut gate_fidelities = HashMap::new();
878 let mut raw_data = Vec::new();
879
880 for gate_name in calibration.single_qubit_gates.keys() {
882 for qubit_id in 0..calibration.topology.num_qubits.min(4) {
883 let qubit = QubitId(qubit_id as u32);
884
885 let circuit = Self::create_process_tomography_circuit(gate_name, vec![qubit])?;
887
888 let mut fidelities = Vec::new();
890 for _ in 0..self.config.protocol_repetitions.min(20) {
891 match executor
892 .execute_characterization_circuit(&circuit, 1000)
893 .await
894 {
895 Ok(result) => {
896 let fidelity = Self::calculate_process_fidelity(&result, gate_name)?;
897 fidelities.push(fidelity);
898 raw_data.push(fidelity);
899 }
900 Err(_) => continue,
901 }
902 }
903
904 if !fidelities.is_empty() {
905 let avg_fidelity = fidelities.iter().sum::<f64>() / fidelities.len() as f64;
906 let error_rate = 1.0 - avg_fidelity;
907
908 gate_fidelities.insert(format!("{gate_name}_{qubit_id}"), avg_fidelity);
909 error_rates.insert(format!("{gate_name}_{qubit_id}"), error_rate);
910 }
911 }
912 }
913
914 for (&(q1, q2), _) in calibration.two_qubit_gates.iter().take(6) {
916 let circuit = Self::create_process_tomography_circuit("CNOT", vec![q1, q2])?;
917
918 let mut fidelities = Vec::new();
919 for _ in 0..self.config.protocol_repetitions.min(10) {
920 match executor
921 .execute_characterization_circuit(&circuit, 1000)
922 .await
923 {
924 Ok(result) => {
925 let fidelity = Self::calculate_process_fidelity(&result, "CNOT")?;
926 fidelities.push(fidelity);
927 raw_data.push(fidelity);
928 }
929 Err(_) => continue,
930 }
931 }
932
933 if !fidelities.is_empty() {
934 let avg_fidelity = fidelities.iter().sum::<f64>() / fidelities.len() as f64;
935 let error_rate = 1.0 - avg_fidelity;
936
937 gate_fidelities.insert(format!("CNOT_{}_{}", q1.0, q2.0), avg_fidelity);
938 error_rates.insert(format!("CNOT_{}_{}", q1.0, q2.0), error_rate);
939 }
940 }
941
942 let avg_fidelity =
943 gate_fidelities.values().sum::<f64>() / gate_fidelities.len().max(1) as f64;
944 let success_rate = gate_fidelities.len() as f64
945 / (calibration.single_qubit_gates.len() + calibration.two_qubit_gates.len().min(6))
946 as f64;
947
948 Ok(ProtocolResult {
949 protocol_type: CharacterizationProtocol::ProcessTomography,
950 success_rate,
951 average_fidelity: avg_fidelity,
952 error_rates,
953 coherence_times: HashMap::new(),
954 gate_fidelities,
955 readout_fidelities: HashMap::new(),
956 execution_time: start_time.elapsed(),
957 raw_data: Some(raw_data),
958 })
959 }
960
961 async fn run_randomized_benchmarking<E: CharacterizationExecutor>(
963 &self,
964 executor: &E,
965 calibration: &DeviceCalibration,
966 ) -> DeviceResult<ProtocolResult> {
967 let start_time = Instant::now();
968 let mut error_rates = HashMap::new();
969 let mut gate_fidelities = HashMap::new();
970 let mut raw_data = Vec::new();
971
972 let lengths = vec![1, 2, 4, 8, 16, 32];
974
975 for qubit_id in 0..calibration.topology.num_qubits.min(4) {
976 let qubit = QubitId(qubit_id as u32);
977 let rb = RandomizedBenchmarking::new(vec![qubit]);
978
979 let mut survival_data = HashMap::new();
980
981 for &length in &lengths {
982 let circuits = rb.generate_rb_circuits(&[length], 10);
983 let mut survival_probs = Vec::new();
984
985 if let Some(length_circuits) = circuits.get(&length) {
986 for circuit_gates in length_circuits {
987 let circuit = self.convert_gates_to_circuit(circuit_gates)?;
988
989 match executor
990 .execute_characterization_circuit(&circuit, 1000)
991 .await
992 {
993 Ok(result) => {
994 let survival_prob = self.calculate_survival_probability(&result)?;
995 survival_probs.push(survival_prob);
996 raw_data.push(survival_prob);
997 }
998 Err(_) => continue,
999 }
1000 }
1001 }
1002
1003 if !survival_probs.is_empty() {
1004 survival_data.insert(length, survival_probs);
1005 }
1006 }
1007
1008 if let Ok(error_rate) = rb.extract_error_rate(&survival_data) {
1010 error_rates.insert(format!("RB_{qubit_id}"), error_rate);
1011 gate_fidelities.insert(format!("RB_fidelity_{qubit_id}"), 1.0 - error_rate);
1012 }
1013 }
1014
1015 let avg_fidelity =
1016 gate_fidelities.values().sum::<f64>() / gate_fidelities.len().max(1) as f64;
1017 let success_rate =
1018 gate_fidelities.len() as f64 / calibration.topology.num_qubits.min(4) as f64;
1019
1020 Ok(ProtocolResult {
1021 protocol_type: CharacterizationProtocol::RandomizedBenchmarking,
1022 success_rate,
1023 average_fidelity: avg_fidelity,
1024 error_rates,
1025 coherence_times: HashMap::new(),
1026 gate_fidelities,
1027 readout_fidelities: HashMap::new(),
1028 execution_time: start_time.elapsed(),
1029 raw_data: Some(raw_data),
1030 })
1031 }
1032
1033 async fn run_coherence_measurements<E: CharacterizationExecutor>(
1035 &self,
1036 executor: &E,
1037 calibration: &DeviceCalibration,
1038 ) -> DeviceResult<ProtocolResult> {
1039 let start_time = Instant::now();
1040 let mut coherence_times = HashMap::new();
1041 let mut raw_data = Vec::new();
1042
1043 for qubit_id in 0..calibration.topology.num_qubits.min(4) {
1045 let qubit = QubitId(qubit_id as u32);
1046
1047 let t1_times = vec![0.0, 5000.0, 10000.0, 20000.0, 40000.0]; let mut t1_data = Vec::new();
1050
1051 for &wait_time in &t1_times {
1052 let circuit = self.create_t1_measurement_circuit(qubit, wait_time)?;
1053
1054 match executor
1055 .execute_characterization_circuit(&circuit, 1000)
1056 .await
1057 {
1058 Ok(result) => {
1059 let population = self.calculate_excited_population(&result)?;
1060 t1_data.push((wait_time, population));
1061 raw_data.push(population);
1062 }
1063 Err(_) => continue,
1064 }
1065 }
1066
1067 let t1 = self.fit_exponential_decay(&t1_data)?;
1069
1070 let t2_times = vec![0.0, 2000.0, 5000.0, 10000.0, 20000.0]; let mut t2_data = Vec::new();
1073
1074 for &wait_time in &t2_times {
1075 let circuit = self.create_t2_echo_circuit(qubit, wait_time)?;
1076
1077 match executor
1078 .execute_characterization_circuit(&circuit, 1000)
1079 .await
1080 {
1081 Ok(result) => {
1082 let coherence = self.calculate_coherence_amplitude(&result)?;
1083 t2_data.push((wait_time, coherence));
1084 raw_data.push(coherence);
1085 }
1086 Err(_) => continue,
1087 }
1088 }
1089
1090 let t2_echo = self.fit_exponential_decay(&t2_data)?;
1091
1092 coherence_times.insert(
1093 qubit,
1094 CoherenceMetrics {
1095 t1,
1096 t2: t2_echo * 0.5, t2_echo,
1098 thermal_population: 0.01, },
1100 );
1101 }
1102
1103 let avg_t1 = coherence_times.values().map(|c| c.t1).sum::<f64>()
1104 / coherence_times.len().max(1) as f64;
1105 let success_rate =
1106 coherence_times.len() as f64 / calibration.topology.num_qubits.min(4) as f64;
1107
1108 Ok(ProtocolResult {
1109 protocol_type: CharacterizationProtocol::CoherenceDecay,
1110 success_rate,
1111 average_fidelity: 0.99, error_rates: HashMap::new(),
1113 coherence_times,
1114 gate_fidelities: HashMap::new(),
1115 readout_fidelities: HashMap::new(),
1116 execution_time: start_time.elapsed(),
1117 raw_data: Some(raw_data),
1118 })
1119 }
1120
1121 async fn run_crosstalk_characterization<E: CharacterizationExecutor>(
1123 &self,
1124 executor: &E,
1125 calibration: &DeviceCalibration,
1126 ) -> DeviceResult<ProtocolResult> {
1127 let start_time = Instant::now();
1128 let mut error_rates = HashMap::new();
1129 let mut raw_data = Vec::new();
1130
1131 for (&(q1, q2), _) in calibration.two_qubit_gates.iter().take(6) {
1133 let crosstalk_char = CrosstalkCharacterization::new(calibration.topology.num_qubits);
1134 let circuits = crosstalk_char.generate_crosstalk_circuits(q1, &[q2]);
1135
1136 let mut baseline_fidelity = 0.0;
1137 let mut crosstalk_fidelity = 0.0;
1138
1139 for (circuit_idx, circuit_gates) in circuits.iter().enumerate() {
1140 let circuit = self.convert_gates_to_circuit(circuit_gates)?;
1141
1142 match executor
1143 .execute_characterization_circuit(&circuit, 1000)
1144 .await
1145 {
1146 Ok(result) => {
1147 let fidelity = Self::calculate_process_fidelity(&result, "crosstalk_test")?;
1148 raw_data.push(fidelity);
1149
1150 if circuit_idx == 0 {
1151 baseline_fidelity = fidelity;
1152 } else {
1153 crosstalk_fidelity += fidelity;
1154 }
1155 }
1156 Err(_) => continue,
1157 }
1158 }
1159
1160 if circuits.len() > 1 {
1161 crosstalk_fidelity /= (circuits.len() - 1) as f64;
1162 let crosstalk_error = baseline_fidelity - crosstalk_fidelity;
1163 error_rates.insert(
1164 format!("crosstalk_{}_{}", q1.0, q2.0),
1165 crosstalk_error.max(0.0),
1166 );
1167 }
1168 }
1169
1170 let avg_error = error_rates.values().sum::<f64>() / error_rates.len().max(1) as f64;
1171 let success_rate =
1172 error_rates.len() as f64 / calibration.two_qubit_gates.len().min(6) as f64;
1173
1174 Ok(ProtocolResult {
1175 protocol_type: CharacterizationProtocol::CrosstalkCharacterization,
1176 success_rate,
1177 average_fidelity: 1.0 - avg_error,
1178 error_rates,
1179 coherence_times: HashMap::new(),
1180 gate_fidelities: HashMap::new(),
1181 readout_fidelities: HashMap::new(),
1182 execution_time: start_time.elapsed(),
1183 raw_data: Some(raw_data),
1184 })
1185 }
1186
1187 async fn run_readout_fidelity<E: CharacterizationExecutor>(
1189 &self,
1190 executor: &E,
1191 calibration: &DeviceCalibration,
1192 ) -> DeviceResult<ProtocolResult> {
1193 let start_time = Instant::now();
1194 let mut readout_fidelities = HashMap::new();
1195 let mut raw_data = Vec::new();
1196
1197 for qubit_id in 0..calibration.topology.num_qubits.min(4) {
1198 let qubit = QubitId(qubit_id as u32);
1199
1200 let circuit_0 = self.create_readout_circuit(qubit, false)?;
1202 let mut prob_0_given_0 = 0.0;
1203
1204 if let Ok(result) = executor
1205 .execute_characterization_circuit(&circuit_0, 1000)
1206 .await
1207 {
1208 prob_0_given_0 = self.calculate_state_probability(&result, false)?;
1209 raw_data.push(prob_0_given_0);
1210 }
1211
1212 let circuit_1 = self.create_readout_circuit(qubit, true)?;
1214 let mut prob_1_given_1 = 0.0;
1215
1216 if let Ok(result) = executor
1217 .execute_characterization_circuit(&circuit_1, 1000)
1218 .await
1219 {
1220 prob_1_given_1 = self.calculate_state_probability(&result, true)?;
1221 raw_data.push(prob_1_given_1);
1222 }
1223
1224 let readout_fidelity = f64::midpoint(prob_0_given_0, prob_1_given_1);
1226 readout_fidelities.insert(qubit, readout_fidelity);
1227 }
1228
1229 let avg_fidelity =
1230 readout_fidelities.values().sum::<f64>() / readout_fidelities.len().max(1) as f64;
1231 let success_rate =
1232 readout_fidelities.len() as f64 / calibration.topology.num_qubits.min(4) as f64;
1233
1234 Ok(ProtocolResult {
1235 protocol_type: CharacterizationProtocol::ReadoutFidelity,
1236 success_rate,
1237 average_fidelity: avg_fidelity,
1238 error_rates: HashMap::new(),
1239 coherence_times: HashMap::new(),
1240 gate_fidelities: HashMap::new(),
1241 readout_fidelities,
1242 execution_time: start_time.elapsed(),
1243 raw_data: Some(raw_data),
1244 })
1245 }
1246
1247 fn perform_advanced_statistical_analysis(
1249 &self,
1250 protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1251 ) -> DeviceResult<AdvancedStatisticalAnalysis> {
1252 let mut distribution_fits = HashMap::new();
1253 let mut hypothesis_tests = HashMap::new();
1254 let mut confidence_intervals = HashMap::new();
1255
1256 let mut all_fidelities: Vec<f64> = Vec::new();
1258 let mut all_error_rates: Vec<f64> = Vec::new();
1259
1260 for result in protocol_results.values() {
1261 all_fidelities.extend(result.gate_fidelities.values());
1262 all_error_rates.extend(result.error_rates.values());
1263
1264 if let Some(ref raw_data) = result.raw_data {
1265 all_fidelities.extend(raw_data);
1266 }
1267 }
1268
1269 if !all_fidelities.is_empty() {
1271 let fidelity_array = Array1::from_vec(all_fidelities.clone());
1272
1273 let mean_fid = mean(&fidelity_array.view()).unwrap_or(0.9);
1275 let std_fid = std(&fidelity_array.view(), 1, None).unwrap_or(0.1);
1276
1277 distribution_fits.insert(
1278 "fidelity_normal".to_string(),
1279 DistributionFitResult {
1280 distribution_name: "Normal".to_string(),
1281 parameters: vec![mean_fid, std_fid],
1282 goodness_of_fit: 0.9, p_value: 0.05,
1284 aic: 100.0,
1285 bic: 105.0,
1286 },
1287 );
1288
1289 let ci_margin = 1.96 * std_fid / (all_fidelities.len() as f64).sqrt();
1291 confidence_intervals.insert(
1292 "fidelity".to_string(),
1293 (mean_fid - ci_margin, mean_fid + ci_margin),
1294 );
1295
1296 if fidelity_array.len() >= 8 {
1298 let threshold = 0.95;
1299 if let Ok(test_result) = ttest_1samp(
1300 &fidelity_array.view(),
1301 threshold,
1302 Alternative::Greater,
1303 "propagate",
1304 ) {
1305 hypothesis_tests.insert(
1306 "fidelity_threshold_test".to_string(),
1307 StatisticalTestResult {
1308 test_name: "One-sample t-test (fidelity > 0.95)".to_string(),
1309 statistic: test_result.statistic,
1310 p_value: test_result.pvalue,
1311 significant: test_result.pvalue < 0.05,
1312 effect_size: Some((mean_fid - threshold) / std_fid),
1313 interpretation: if test_result.pvalue < 0.05 {
1314 "Fidelity significantly exceeds threshold".to_string()
1315 } else {
1316 "Fidelity does not significantly exceed threshold".to_string()
1317 },
1318 },
1319 );
1320 }
1321 }
1322 }
1323
1324 let correlation_analysis = Self::perform_correlation_analysis(protocol_results)?;
1326
1327 let outlier_detection = Self::detect_outliers(protocol_results)?;
1329
1330 let trend_analysis = Self::analyze_trends(protocol_results)?;
1332
1333 Ok(AdvancedStatisticalAnalysis {
1334 distribution_fits,
1335 correlation_analysis,
1336 hypothesis_tests,
1337 outlier_detection,
1338 trend_analysis,
1339 confidence_intervals,
1340 })
1341 }
1342
1343 fn create_process_tomography_circuit(
1346 gate_name: &str,
1347 qubits: Vec<QubitId>,
1348 ) -> DeviceResult<Circuit<8>> {
1349 let mut circuit = Circuit::<8>::new();
1350
1351 if !qubits.is_empty() {
1353 let _ = circuit.h(qubits[0]);
1354 }
1355
1356 match gate_name {
1358 "H" => {
1359 if !qubits.is_empty() {
1360 let _ = circuit.h(qubits[0]);
1361 }
1362 }
1363 "X" => {
1364 if !qubits.is_empty() {
1365 let _ = circuit.x(qubits[0]);
1366 }
1367 }
1368 "CNOT" => {
1369 if qubits.len() >= 2 {
1370 let _ = circuit.cnot(qubits[0], qubits[1]);
1371 }
1372 }
1373 _ => return Err(DeviceError::UnsupportedOperation(gate_name.to_string())),
1374 }
1375
1376 if !qubits.is_empty() {
1378 let _ = circuit.h(qubits[0]);
1379 }
1380
1381 Ok(circuit)
1382 }
1383
1384 fn calculate_process_fidelity(result: &CircuitResult, _gate_name: &str) -> DeviceResult<f64> {
1385 let total_shots = result.shots as f64;
1387 let successful_outcomes = result
1388 .counts
1389 .values()
1390 .map(|&count| count as f64)
1391 .sum::<f64>();
1392 Ok(successful_outcomes / total_shots)
1393 }
1394
1395 fn perform_correlation_analysis(
1398 _protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1399 ) -> DeviceResult<CorrelationAnalysisResult> {
1400 Ok(CorrelationAnalysisResult {
1402 correlationmatrix: Array2::eye(3),
1403 significant_correlations: Vec::new(),
1404 correlation_network: HashMap::new(),
1405 })
1406 }
1407
1408 fn detect_outliers(
1409 _protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1410 ) -> DeviceResult<OutlierDetectionResult> {
1411 Ok(OutlierDetectionResult {
1413 outliers: HashMap::new(),
1414 outlier_scores: HashMap::new(),
1415 outlier_threshold: 3.0,
1416 detection_method: "IQR".to_string(),
1417 })
1418 }
1419
1420 fn analyze_trends(
1421 _protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1422 ) -> DeviceResult<TrendAnalysisResult> {
1423 Ok(TrendAnalysisResult {
1425 trend_coefficients: HashMap::new(),
1426 trend_significance: HashMap::new(),
1427 seasonal_components: HashMap::new(),
1428 residuals: HashMap::new(),
1429 })
1430 }
1431
1432 fn analyze_drift_patterns(&self) -> DeviceResult<DriftAnalysisResult> {
1433 Ok(DriftAnalysisResult {
1435 drift_rates: HashMap::new(),
1436 trend_significance: HashMap::new(),
1437 change_points: HashMap::new(),
1438 forecast: HashMap::new(),
1439 stability_score: 0.9,
1440 })
1441 }
1442
1443 fn analyze_crosstalk_patterns(
1444 &self,
1445 calibration: &DeviceCalibration,
1446 ) -> DeviceResult<AdvancedCrosstalkAnalysis> {
1447 let n = calibration.topology.num_qubits;
1448
1449 Ok(AdvancedCrosstalkAnalysis {
1450 crosstalk_matrix: {
1451 let matrix = &calibration.crosstalk_matrix.matrix;
1452 let rows = matrix.len();
1453 let cols = if rows > 0 { matrix[0].len() } else { 0 };
1454 let flat: Vec<f64> = matrix.iter().flatten().copied().collect();
1455 Array2::from_shape_vec((rows, cols), flat).unwrap_or_else(|_| Array2::eye(n))
1456 },
1457 significant_pairs: Vec::new(),
1458 spatial_patterns: SpatialCrosstalkPattern {
1459 spatial_correlation: Array2::eye(n),
1460 decay_constants: HashMap::new(),
1461 dominant_frequencies: Array1::zeros(5),
1462 anisotropy_parameters: HashMap::new(),
1463 },
1464 temporal_variations: HashMap::new(),
1465 mitigation_strategies: Vec::new(),
1466 })
1467 }
1468
1469 fn build_predictive_models(
1470 &self,
1471 _protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1472 _statistical_analysis: &AdvancedStatisticalAnalysis,
1473 ) -> DeviceResult<PredictiveModels> {
1474 Ok(PredictiveModels {
1476 fidelity_predictor: ModelPrediction {
1477 predicted_values: Array1::from_vec(vec![0.95, 0.94, 0.93]),
1478 prediction_intervals: Array2::from_shape_vec(
1479 (3, 2),
1480 vec![0.93, 0.97, 0.92, 0.96, 0.91, 0.95],
1481 )
1482 .expect("prediction_intervals shape is always valid"),
1483 feature_importance: HashMap::new(),
1484 model_type: "Linear Regression".to_string(),
1485 accuracy_metrics: ModelAccuracyMetrics {
1486 rmse: 0.01,
1487 mae: 0.005,
1488 r_squared: 0.8,
1489 cross_validation_score: 0.75,
1490 },
1491 },
1492 coherence_predictor: ModelPrediction {
1493 predicted_values: Array1::from_vec(vec![50000.0, 48000.0, 46000.0]),
1494 prediction_intervals: Array2::from_shape_vec(
1495 (3, 2),
1496 vec![48000.0, 52000.0, 46000.0, 50000.0, 44000.0, 48000.0],
1497 )
1498 .expect("prediction_intervals shape is always valid"),
1499 feature_importance: HashMap::new(),
1500 model_type: "Random Forest".to_string(),
1501 accuracy_metrics: ModelAccuracyMetrics {
1502 rmse: 2000.0,
1503 mae: 1500.0,
1504 r_squared: 0.85,
1505 cross_validation_score: 0.82,
1506 },
1507 },
1508 error_rate_predictor: ModelPrediction {
1509 predicted_values: Array1::from_vec(vec![0.001, 0.0012, 0.0015]),
1510 prediction_intervals: Array2::from_shape_vec(
1511 (3, 2),
1512 vec![0.0008, 0.0012, 0.001, 0.0014, 0.0012, 0.0018],
1513 )
1514 .expect("prediction_intervals shape is always valid"),
1515 feature_importance: HashMap::new(),
1516 model_type: "Support Vector Regression".to_string(),
1517 accuracy_metrics: ModelAccuracyMetrics {
1518 rmse: 0.0002,
1519 mae: 0.0001,
1520 r_squared: 0.7,
1521 cross_validation_score: 0.68,
1522 },
1523 },
1524 drift_predictor: ModelPrediction {
1525 predicted_values: Array1::from_vec(vec![0.0001, 0.0002, 0.0003]),
1526 prediction_intervals: Array2::from_shape_vec(
1527 (3, 2),
1528 vec![0.00005, 0.00015, 0.00015, 0.00025, 0.00025, 0.00035],
1529 )
1530 .expect("prediction_intervals shape is always valid"),
1531 feature_importance: HashMap::new(),
1532 model_type: "ARIMA".to_string(),
1533 accuracy_metrics: ModelAccuracyMetrics {
1534 rmse: 0.00005,
1535 mae: 0.00003,
1536 r_squared: 0.6,
1537 cross_validation_score: 0.55,
1538 },
1539 },
1540 model_accuracy: [
1541 ("fidelity".to_string(), 0.8),
1542 ("coherence".to_string(), 0.85),
1543 ("error_rate".to_string(), 0.7),
1544 ("drift".to_string(), 0.6),
1545 ]
1546 .iter()
1547 .cloned()
1548 .collect(),
1549 })
1550 }
1551
1552 fn update_noise_model(
1553 &self,
1554 calibration: &DeviceCalibration,
1555 _protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1556 ) -> DeviceResult<crate::noise_model::CalibrationNoiseModel> {
1557 self.noise_modeler.model_noise(calibration)
1558 }
1559
1560 fn generate_recommendations(
1561 &self,
1562 protocol_results: &HashMap<CharacterizationProtocol, ProtocolResult>,
1563 statistical_analysis: &AdvancedStatisticalAnalysis,
1564 drift_analysis: Option<&DriftAnalysisResult>,
1565 _predictive_models: Option<&PredictiveModels>,
1566 ) -> DeviceResult<Vec<CharacterizationRecommendation>> {
1567 let mut recommendations = Vec::new();
1568
1569 let avg_fidelity = protocol_results
1571 .values()
1572 .map(|r| r.average_fidelity)
1573 .sum::<f64>()
1574 / protocol_results.len().max(1) as f64;
1575
1576 if avg_fidelity < 0.9 {
1577 recommendations.push(CharacterizationRecommendation {
1578 priority: RecommendationPriority::High,
1579 category: RecommendationCategory::Calibration,
1580 description: "Overall fidelity below target. Consider recalibration.".to_string(),
1581 expected_impact: 0.8,
1582 implementation_effort: 0.6,
1583 urgency_score: 0.9,
1584 });
1585 }
1586
1587 if let Some(drift) = drift_analysis {
1589 if drift.stability_score < 0.8 {
1590 recommendations.push(CharacterizationRecommendation {
1591 priority: RecommendationPriority::Medium,
1592 category: RecommendationCategory::Maintenance,
1593 description: "Parameter drift detected. Increase monitoring frequency."
1594 .to_string(),
1595 expected_impact: 0.7,
1596 implementation_effort: 0.3,
1597 urgency_score: 0.6,
1598 });
1599 }
1600 }
1601
1602 for (test_name, test_result) in &statistical_analysis.hypothesis_tests {
1604 if !test_result.significant && test_name.contains("threshold") {
1605 recommendations.push(CharacterizationRecommendation {
1606 priority: RecommendationPriority::Medium,
1607 category: RecommendationCategory::Analysis,
1608 description: format!(
1609 "Statistical test '{test_name}' not significant. Review protocols."
1610 ),
1611 expected_impact: 0.5,
1612 implementation_effort: 0.4,
1613 urgency_score: 0.4,
1614 });
1615 }
1616 }
1617
1618 Ok(recommendations)
1619 }
1620
1621 fn convert_gates_to_circuit(&self, gates: &[Box<dyn GateOp>]) -> DeviceResult<Circuit<8>> {
1623 let mut circuit = Circuit::<8>::new();
1624 Ok(circuit)
1626 }
1627
1628 fn calculate_survival_probability(&self, result: &CircuitResult) -> DeviceResult<f64> {
1629 let total_shots = result.shots as f64;
1630 let ground_state_count = result.counts.get("0").unwrap_or(&0);
1631 Ok(*ground_state_count as f64 / total_shots)
1632 }
1633
1634 fn create_t1_measurement_circuit(
1635 &self,
1636 qubit: QubitId,
1637 wait_time: f64,
1638 ) -> DeviceResult<Circuit<8>> {
1639 let mut circuit = Circuit::<8>::new();
1640 let _ = circuit.x(qubit); Ok(circuit)
1643 }
1644
1645 fn create_t2_echo_circuit(&self, qubit: QubitId, wait_time: f64) -> DeviceResult<Circuit<8>> {
1646 let mut circuit = Circuit::<8>::new();
1647 let _ = circuit.h(qubit); let _ = circuit.h(qubit); Ok(circuit)
1651 }
1652
1653 fn calculate_excited_population(&self, result: &CircuitResult) -> DeviceResult<f64> {
1654 let total_shots = result.shots as f64;
1655 let excited_count = result.counts.get("1").unwrap_or(&0);
1656 Ok(*excited_count as f64 / total_shots)
1657 }
1658
1659 fn calculate_coherence_amplitude(&self, result: &CircuitResult) -> DeviceResult<f64> {
1660 let total_shots = result.shots as f64;
1662 let coherent_count = result.counts.values().max().unwrap_or(&0);
1663 Ok(*coherent_count as f64 / total_shots)
1664 }
1665
1666 fn fit_exponential_decay(&self, data: &[(f64, f64)]) -> DeviceResult<f64> {
1667 if data.len() < 2 {
1669 return Ok(50000.0); }
1671
1672 let mut sum_x = 0.0;
1674 let mut sum_y = 0.0;
1675 let mut sum_xy = 0.0;
1676 let mut sum_x2 = 0.0;
1677 let n = data.len() as f64;
1678
1679 for &(x, y) in data {
1680 let log_y = (y.max(1e-6)).ln();
1681 sum_x += x;
1682 sum_y += log_y;
1683 sum_xy += x * log_y;
1684 sum_x2 += x * x;
1685 }
1686
1687 let slope = n.mul_add(sum_xy, -(sum_x * sum_y)) / n.mul_add(sum_x2, -(sum_x * sum_x));
1688 let decay_constant = -1.0 / slope;
1689
1690 Ok(decay_constant.abs().clamp(1000.0, 200_000.0)) }
1692
1693 fn create_readout_circuit(
1694 &self,
1695 qubit: QubitId,
1696 excited_state: bool,
1697 ) -> DeviceResult<Circuit<8>> {
1698 let mut circuit = Circuit::<8>::new();
1699 if excited_state {
1700 let _ = circuit.x(qubit); }
1702 Ok(circuit)
1704 }
1705
1706 fn calculate_state_probability(
1707 &self,
1708 result: &CircuitResult,
1709 target_state: bool,
1710 ) -> DeviceResult<f64> {
1711 let total_shots = result.shots as f64;
1712 let target_key = if target_state { "1" } else { "0" };
1713 let target_count = result.counts.get(target_key).unwrap_or(&0);
1714 Ok(*target_count as f64 / total_shots)
1715 }
1716}
1717
1718#[async_trait::async_trait]
1720pub trait CharacterizationExecutor {
1721 async fn execute_characterization_circuit<const N: usize>(
1722 &self,
1723 circuit: &Circuit<N>,
1724 shots: usize,
1725 ) -> DeviceResult<CircuitResult>;
1726}
1727
1728#[cfg(test)]
1729mod tests {
1730 use super::*;
1731
1732 #[test]
1733 fn test_process_tomography_circuits() {
1734 let tomo = ProcessTomography::new(1);
1735 let prep_circuits = tomo.preparation_circuits();
1736 let meas_circuits = tomo.measurement_circuits();
1737
1738 assert_eq!(prep_circuits.len(), 4); assert_eq!(meas_circuits.len(), 4); }
1741
1742 #[test]
1743 fn test_state_tomography_circuits() {
1744 let tomo = StateTomography::new(2);
1745 let circuits = tomo.measurement_circuits();
1746
1747 assert_eq!(circuits.len(), 9); }
1749
1750 #[test]
1751 #[ignore = "Skipping randomized benchmarking test"]
1752 fn test_randomized_benchmarking() {
1753 let rb = RandomizedBenchmarking::new(vec![QubitId::new(0)]);
1754 let sequence = rb.generate_clifford_sequence(10);
1755
1756 assert!(!sequence.is_empty());
1757 }
1758
1759 #[test]
1760 fn test_drift_tracking() {
1761 let mut tracker = DriftTracker::new(vec!["T1".to_string()]);
1762
1763 for i in 0..20 {
1765 let value = 50.0 + (i as f64) * 0.1; tracker.add_measurement("T1", i as f64, value);
1767 }
1768
1769 let drift = tracker.detect_drift("T1", 5);
1770 assert!(drift.is_some());
1771 assert!(drift.expect("drift should be Some") > 0.0);
1772 }
1773}