quantrs2_device/process_tomography/
mod.rs1pub mod analysis;
7pub mod config;
8pub mod core;
9pub mod fallback;
10pub mod reconstruction;
11pub mod results;
12pub mod utils;
13pub mod validation;
14
15pub use analysis::*;
17pub use config::*;
18pub use core::{ProcessTomographyExecutor, SciRS2ProcessTomographer};
19pub use reconstruction::*;
20pub use results::*;
21pub use utils::*;
22pub use validation::*;
23
24#[cfg(feature = "scirs2")]
26pub use fallback as scirs2_fallback;
27
28#[cfg(not(feature = "scirs2"))]
29pub use fallback::*;
30
31use scirs2_core::ndarray::{Array1, Array2, Array4};
32use scirs2_core::Complex64;
33use quantrs2_circuit::prelude::{
35 Circuit,
36 PerformanceAnalyzer,
37 PerformanceSnapshot,
38 PerformanceSummary,
39 ProfilerConfig as ProfilerConfiguration,
40 ProfilingReport,
42 ProfilingSession,
43 QuantumProfiler,
44};
45use std::collections::HashMap;
46
47use crate::{calibration::CalibrationManager, DeviceResult};
48
49pub fn create_process_tomographer(
51 calibration_manager: CalibrationManager,
52) -> SciRS2ProcessTomographer {
53 let config = SciRS2ProcessTomographyConfig::default();
54 SciRS2ProcessTomographer::new(config, calibration_manager)
55}
56
57pub const fn create_process_tomographer_with_config(
59 config: SciRS2ProcessTomographyConfig,
60 calibration_manager: CalibrationManager,
61) -> SciRS2ProcessTomographer {
62 SciRS2ProcessTomographer::new(config, calibration_manager)
63}
64
65pub async fn quick_process_tomography<const N: usize, E: ProcessTomographyExecutor>(
67 process_circuit: &Circuit<N>,
68 executor: &E,
69 num_qubits: usize,
70) -> DeviceResult<ProcessMetrics> {
71 let calibration_manager = CalibrationManager::new();
72 let mut tomographer = create_process_tomographer(calibration_manager);
73
74 tomographer.generate_input_states(num_qubits)?;
76 tomographer.generate_measurement_operators(num_qubits)?;
77
78 let result = tomographer
80 .perform_process_tomography("quick_tomography", process_circuit, executor)
81 .await?;
82
83 Ok(result.process_metrics)
84}
85
86pub async fn comprehensive_process_characterization<
88 const N: usize,
89 E: ProcessTomographyExecutor,
90>(
91 device_id: &str,
92 process_circuit: &Circuit<N>,
93 executor: &E,
94 num_qubits: usize,
95 config: Option<SciRS2ProcessTomographyConfig>,
96) -> DeviceResult<SciRS2ProcessTomographyResult> {
97 let calibration_manager = CalibrationManager::new();
98 let config = config.unwrap_or_default();
99 let mut tomographer = SciRS2ProcessTomographer::new(config, calibration_manager);
100
101 tomographer.generate_input_states(num_qubits)?;
103 tomographer.generate_measurement_operators(num_qubits)?;
104
105 tomographer
107 .perform_process_tomography(device_id, process_circuit, executor)
108 .await
109}
110
111pub const fn create_process_monitoring_system(
113 reference_metrics: ProcessMetrics,
114 anomaly_threshold: f64,
115 drift_sensitivity: f64,
116) -> (ProcessAnomalyDetector, ProcessDriftDetector) {
117 let anomaly_detector = ProcessAnomalyDetector::new(
118 anomaly_threshold,
119 AnomalyDetectionAlgorithm::StatisticalThreshold,
120 );
121
122 let drift_detector = ProcessDriftDetector::new(
123 reference_metrics,
124 drift_sensitivity,
125 DriftDetectionMethod::StatisticalTest,
126 );
127
128 (anomaly_detector, drift_detector)
129}
130
131pub async fn benchmark_process<const N: usize, E: ProcessTomographyExecutor>(
133 process_circuit: &Circuit<N>,
134 executor: &E,
135 num_qubits: usize,
136 benchmark_channels: &[String],
137) -> DeviceResult<HashMap<String, f64>> {
138 let calibration_manager = CalibrationManager::new();
139 let mut config = SciRS2ProcessTomographyConfig::default();
140 config.validation_config.enable_benchmarking = true;
141 config.validation_config.benchmark_processes = benchmark_channels.to_vec();
142
143 let mut tomographer = SciRS2ProcessTomographer::new(config, calibration_manager);
144
145 tomographer.generate_input_states(num_qubits)?;
147 tomographer.generate_measurement_operators(num_qubits)?;
148
149 let result = tomographer
151 .perform_process_tomography("benchmark", process_circuit, executor)
152 .await?;
153
154 Ok(result.process_comparisons.standard_process_fidelities)
155}
156
157pub fn compare_processes(
159 process1: &Array4<Complex64>,
160 process2: &Array4<Complex64>,
161) -> DeviceResult<ProcessComparisonResult> {
162 let trace_distance = utils::process_utils::trace_distance(process1, process2)?;
164
165 let mut fidelity = 0.0;
167 let mut norm1 = 0.0;
168 let mut norm2 = 0.0;
169
170 let dim = process1.dim();
171 for i in 0..dim.0 {
172 for j in 0..dim.1 {
173 for k in 0..dim.2 {
174 for l in 0..dim.3 {
175 let element1 = process1[[i, j, k, l]];
176 let element2 = process2[[i, j, k, l]];
177
178 fidelity += (element1.conj() * element2).re;
179 norm1 += element1.norm_sqr();
180 norm2 += element2.norm_sqr();
181 }
182 }
183 }
184 }
185
186 let process_fidelity = if norm1 > 1e-12 && norm2 > 1e-12 {
187 fidelity / (norm1 * norm2).sqrt()
188 } else {
189 0.0
190 };
191
192 Ok(ProcessComparisonResult {
193 process_fidelity,
194 trace_distance,
195 diamond_norm_distance: 2.0 * (1.0 - process_fidelity).sqrt(),
196 })
197}
198
199#[derive(Debug, Clone)]
201pub struct ProcessComparisonResult {
202 pub process_fidelity: f64,
203 pub trace_distance: f64,
204 pub diamond_norm_distance: f64,
205}
206
207pub fn validate_process_result(
209 result: &SciRS2ProcessTomographyResult,
210 tolerance: f64,
211) -> ProcessValidationReport {
212 let mut issues = Vec::new();
213 let mut warnings = Vec::new();
214
215 let physical_validity = &result
217 .statistical_analysis
218 .reconstruction_quality
219 .physical_validity;
220
221 if !physical_validity.is_completely_positive {
222 issues.push("Process is not completely positive".to_string());
223 }
224
225 if !physical_validity.is_trace_preserving {
226 issues.push("Process is not trace preserving".to_string());
227 }
228
229 if physical_validity.positivity_measure < 0.9 {
230 warnings.push(format!(
231 "Low positivity measure: {:.3}",
232 physical_validity.positivity_measure
233 ));
234 }
235
236 if physical_validity.trace_preservation_measure < 0.95 {
237 warnings.push(format!(
238 "Poor trace preservation: {:.3}",
239 physical_validity.trace_preservation_measure
240 ));
241 }
242
243 if result.process_metrics.process_fidelity < 0.5 {
245 warnings.push("Low process fidelity detected".to_string());
246 }
247
248 if result.process_metrics.unitarity < 0.1 {
249 warnings.push("Very low unitarity detected".to_string());
250 }
251
252 if result
254 .statistical_analysis
255 .reconstruction_quality
256 .condition_number
257 > 1e10
258 {
259 warnings.push("High condition number indicates numerical instability".to_string());
260 }
261
262 let is_valid = issues.is_empty();
263 let quality_score = calculate_overall_quality_score(result);
264
265 ProcessValidationReport {
266 is_valid,
267 quality_score,
268 issues,
269 warnings,
270 }
271}
272
273#[derive(Debug, Clone)]
275pub struct ProcessValidationReport {
276 pub is_valid: bool,
277 pub quality_score: f64,
278 pub issues: Vec<String>,
279 pub warnings: Vec<String>,
280}
281
282fn calculate_overall_quality_score(result: &SciRS2ProcessTomographyResult) -> f64 {
284 let physical_score = f64::midpoint(
285 result
286 .statistical_analysis
287 .reconstruction_quality
288 .physical_validity
289 .positivity_measure,
290 result
291 .statistical_analysis
292 .reconstruction_quality
293 .physical_validity
294 .trace_preservation_measure,
295 );
296
297 let fidelity_score = result.process_metrics.process_fidelity;
298 let unitarity_score = result.process_metrics.unitarity;
299
300 let numerical_score = 1.0
301 / (1.0
302 + result
303 .statistical_analysis
304 .reconstruction_quality
305 .condition_number
306 / 1e6);
307
308 numerical_score
310 .mul_add(
311 0.2,
312 unitarity_score.mul_add(0.2, fidelity_score.mul_add(0.3, physical_score * 0.3)),
313 )
314 .clamp(0.0, 1.0)
315}
316
317pub fn export_process_results(
319 result: &SciRS2ProcessTomographyResult,
320 format: ExportFormat,
321) -> DeviceResult<String> {
322 match format {
323 ExportFormat::Json => export_as_json(result),
324 ExportFormat::Csv => export_as_csv(result),
325 ExportFormat::Hdf5 => export_as_hdf5(result),
326 ExportFormat::Matlab => export_as_matlab(result),
327 }
328}
329
330#[derive(Debug, Clone)]
332pub enum ExportFormat {
333 Json,
334 Csv,
335 Hdf5,
336 Matlab,
337}
338
339fn export_as_json(result: &SciRS2ProcessTomographyResult) -> DeviceResult<String> {
340 let json_data = format!(
342 r#"{{
343 "device_id": "{}",
344 "process_fidelity": {:.6},
345 "average_gate_fidelity": {:.6},
346 "unitarity": {:.6},
347 "entangling_power": {:.6},
348 "reconstruction_method": "{:?}",
349 "log_likelihood": {:.6}
350}}"#,
351 result.device_id,
352 result.process_metrics.process_fidelity,
353 result.process_metrics.average_gate_fidelity,
354 result.process_metrics.unitarity,
355 result.process_metrics.entangling_power,
356 result.config.reconstruction_method,
357 result
358 .statistical_analysis
359 .reconstruction_quality
360 .log_likelihood
361 );
362
363 Ok(json_data)
364}
365
366fn export_as_csv(result: &SciRS2ProcessTomographyResult) -> DeviceResult<String> {
367 let csv_data = format!(
368 "device_id,process_fidelity,average_gate_fidelity,unitarity,entangling_power,log_likelihood\n{},{:.6},{:.6},{:.6},{:.6},{:.6}",
369 result.device_id,
370 result.process_metrics.process_fidelity,
371 result.process_metrics.average_gate_fidelity,
372 result.process_metrics.unitarity,
373 result.process_metrics.entangling_power,
374 result.statistical_analysis.reconstruction_quality.log_likelihood
375 );
376
377 Ok(csv_data)
378}
379
380fn export_as_hdf5(_result: &SciRS2ProcessTomographyResult) -> DeviceResult<String> {
381 Ok("HDF5 export not yet implemented".to_string())
383}
384
385fn export_as_matlab(_result: &SciRS2ProcessTomographyResult) -> DeviceResult<String> {
386 Ok("MATLAB export not yet implemented".to_string())
388}