1pub mod monitoring;
4pub mod statistical;
5pub mod structural;
6
7use scirs2_core::ndarray::{Array1, Array2, Array4};
8use scirs2_core::Complex64;
9use std::collections::HashMap;
10
11use super::core::SciRS2ProcessTomographer;
12use super::results::*;
13use crate::DeviceResult;
14
15pub use monitoring::*;
16pub use statistical::*;
17pub use structural::*;
18
19#[cfg(feature = "scirs2")]
21use scirs2_stats::{
22 corrcoef, mean, pearsonr, shapiro_wilk, spearmanr, std, ttest::Alternative, ttest_1samp,
23 ttest_ind, var, TTestResult,
24};
25
26#[cfg(not(feature = "scirs2"))]
27use super::super::fallback::*;
28
29impl SciRS2ProcessTomographer {
31 pub fn perform_statistical_analysis(
33 &self,
34 process_matrix: &Array4<Complex64>,
35 experimental_data: &ExperimentalData,
36 ) -> DeviceResult<(
37 HashMap<String, StatisticalTest>,
38 DistributionAnalysis,
39 CorrelationAnalysis,
40 )> {
41 let statistical_tests =
42 self.perform_statistical_tests(process_matrix, experimental_data)?;
43 let distribution_analysis = self.analyze_distributions(process_matrix)?;
44 let correlation_analysis = self.analyze_correlations(process_matrix)?;
45
46 Ok((
47 statistical_tests,
48 distribution_analysis,
49 correlation_analysis,
50 ))
51 }
52
53 fn perform_statistical_tests(
55 &self,
56 process_matrix: &Array4<Complex64>,
57 experimental_data: &ExperimentalData,
58 ) -> DeviceResult<HashMap<String, StatisticalTest>> {
59 let mut tests = HashMap::new();
60
61 let dim = process_matrix.dim().0;
63 let mut real_parts = Vec::new();
64 let mut imag_parts = Vec::new();
65
66 for i in 0..dim {
67 for j in 0..dim {
68 for k in 0..dim {
69 for l in 0..dim {
70 real_parts.push(process_matrix[[i, j, k, l]].re);
71 imag_parts.push(process_matrix[[i, j, k, l]].im);
72 }
73 }
74 }
75 }
76
77 #[cfg(feature = "scirs2")]
79 {
80 let real_array = scirs2_core::ndarray::Array1::from_vec(real_parts.clone());
81 if let Ok((statistic, pvalue)) = shapiro_wilk(&real_array.view()) {
82 tests.insert(
83 "shapiro_wilk_real".to_string(),
84 StatisticalTest {
85 statistic,
86 p_value: pvalue,
87 critical_value: 0.95,
88 is_significant: pvalue < 0.05,
89 effect_size: Some(1.0 - statistic),
90 },
91 );
92 }
93
94 let imag_array = scirs2_core::ndarray::Array1::from_vec(imag_parts.clone());
95 if let Ok((statistic, pvalue)) = shapiro_wilk(&imag_array.view()) {
96 tests.insert(
97 "shapiro_wilk_imag".to_string(),
98 StatisticalTest {
99 statistic,
100 p_value: pvalue,
101 critical_value: 0.95,
102 is_significant: pvalue < 0.05,
103 effect_size: Some(1.0 - statistic),
104 },
105 );
106 }
107 }
108
109 #[cfg(not(feature = "scirs2"))]
110 {
111 tests.insert(
112 "shapiro_wilk_real".to_string(),
113 StatisticalTest {
114 statistic: 0.95,
115 p_value: 0.1,
116 critical_value: 0.95,
117 is_significant: false,
118 effect_size: Some(0.05),
119 },
120 );
121 }
122
123 #[cfg(feature = "scirs2")]
125 {
126 let real_array = scirs2_core::ndarray::Array1::from_vec(real_parts);
127 let imag_array = scirs2_core::ndarray::Array1::from_vec(imag_parts);
128
129 if let Ok(ttest_result) = ttest_ind(
130 &real_array.view(),
131 &imag_array.view(),
132 true,
133 Alternative::TwoSided,
134 "",
135 ) {
136 tests.insert(
137 "ttest_real_vs_imag".to_string(),
138 StatisticalTest {
139 statistic: ttest_result.statistic,
140 p_value: ttest_result.pvalue,
141 critical_value: 1.96,
142 is_significant: ttest_result.pvalue < 0.05,
143 effect_size: Some(ttest_result.statistic.abs() / 10.0),
144 },
145 );
146 }
147 }
148
149 Ok(tests)
150 }
151
152 fn analyze_distributions(
154 &self,
155 process_matrix: &Array4<Complex64>,
156 ) -> DeviceResult<DistributionAnalysis> {
157 let dim = process_matrix.dim().0;
158 let mut element_distributions = HashMap::new();
159
160 let real_parts: Vec<f64> = (0..dim)
162 .flat_map(|i| {
163 (0..dim).flat_map(move |j| {
164 (0..dim)
165 .flat_map(move |k| (0..dim).map(move |l| process_matrix[[i, j, k, l]].re))
166 })
167 })
168 .collect();
169
170 let real_distribution = self.fit_distribution(&real_parts, "real_parts")?;
171 element_distributions.insert("real_parts".to_string(), real_distribution);
172
173 let imag_parts: Vec<f64> = (0..dim)
175 .flat_map(|i| {
176 (0..dim).flat_map(move |j| {
177 (0..dim)
178 .flat_map(move |k| (0..dim).map(move |l| process_matrix[[i, j, k, l]].im))
179 })
180 })
181 .collect();
182
183 let imag_distribution = self.fit_distribution(&imag_parts, "imag_parts")?;
184 element_distributions.insert("imag_parts".to_string(), imag_distribution);
185
186 let magnitudes: Vec<f64> = (0..dim)
188 .flat_map(|i| {
189 (0..dim).flat_map(move |j| {
190 (0..dim).flat_map(move |k| {
191 (0..dim).map(move |l| process_matrix[[i, j, k, l]].norm())
192 })
193 })
194 })
195 .collect();
196
197 let magnitude_distribution = self.fit_distribution(&magnitudes, "magnitudes")?;
198 element_distributions.insert("magnitudes".to_string(), magnitude_distribution);
199
200 let skewness = self.calculate_skewness(&real_parts);
202 let kurtosis = self.calculate_kurtosis(&real_parts);
203 let entropy = self.calculate_entropy(&magnitudes);
204
205 let global_properties = GlobalDistributionProperties {
206 skewness,
207 kurtosis,
208 entropy,
209 };
210
211 Ok(DistributionAnalysis {
212 element_distributions,
213 global_properties,
214 })
215 }
216
217 fn analyze_correlations(
219 &self,
220 process_matrix: &Array4<Complex64>,
221 ) -> DeviceResult<CorrelationAnalysis> {
222 let dim = process_matrix.dim().0;
223 let mut element_correlations = HashMap::new();
224
225 let real_parts: Vec<f64> = (0..dim)
227 .flat_map(|i| {
228 (0..dim).flat_map(move |j| {
229 (0..dim)
230 .flat_map(move |k| (0..dim).map(move |l| process_matrix[[i, j, k, l]].re))
231 })
232 })
233 .collect();
234
235 let imag_parts: Vec<f64> = (0..dim)
236 .flat_map(|i| {
237 (0..dim).flat_map(move |j| {
238 (0..dim)
239 .flat_map(move |k| (0..dim).map(move |l| process_matrix[[i, j, k, l]].im))
240 })
241 })
242 .collect();
243
244 let magnitudes: Vec<f64> = (0..dim)
245 .flat_map(|i| {
246 (0..dim).flat_map(move |j| {
247 (0..dim).flat_map(move |k| {
248 (0..dim).map(move |l| process_matrix[[i, j, k, l]].norm())
249 })
250 })
251 })
252 .collect();
253
254 #[cfg(feature = "scirs2")]
256 {
257 let real_array = scirs2_core::ndarray::Array1::from_vec(real_parts);
258 let imag_array = scirs2_core::ndarray::Array1::from_vec(imag_parts);
259 let mag_array = scirs2_core::ndarray::Array1::from_vec(magnitudes);
260
261 if let Ok((corr, p_val)) = pearsonr(&real_array.view(), &imag_array.view(), "two-sided")
262 {
263 element_correlations.insert("real_imag_correlation".to_string(), corr);
264 }
265
266 if let Ok((corr, p_val)) = pearsonr(&real_array.view(), &mag_array.view(), "two-sided")
267 {
268 element_correlations.insert("real_magnitude_correlation".to_string(), corr);
269 }
270
271 if let Ok((corr, p_val)) = pearsonr(&imag_array.view(), &mag_array.view(), "two-sided")
272 {
273 element_correlations.insert("imag_magnitude_correlation".to_string(), corr);
274 }
275 }
276
277 #[cfg(not(feature = "scirs2"))]
278 {
279 element_correlations.insert("real_imag_correlation".to_string(), 0.1);
280 element_correlations.insert("real_magnitude_correlation".to_string(), 0.8);
281 element_correlations.insert("imag_magnitude_correlation".to_string(), 0.2);
282 }
283
284 let n_components = 3;
286 let principal_components = Array2::eye(n_components);
287
288 let correlation_network = CorrelationNetwork {
290 adjacency_matrix: Array2::eye(3),
291 centrality_measures: HashMap::new(),
292 };
293
294 Ok(CorrelationAnalysis {
295 element_correlations,
296 principal_components,
297 correlation_network,
298 })
299 }
300
301 pub fn analyze_process_structure(
303 &self,
304 process_matrix: &Array4<Complex64>,
305 ) -> DeviceResult<ProcessStructureAnalysis> {
306 let kraus_decomposition = self.perform_kraus_decomposition(process_matrix)?;
307 let noise_decomposition = self.analyze_noise_decomposition(process_matrix)?;
308 let coherence_analysis = self.analyze_coherence(process_matrix)?;
309 let symmetry_analysis = self.analyze_symmetries(process_matrix)?;
310 let process_graph = self.construct_process_graph(process_matrix)?;
311
312 Ok(ProcessStructureAnalysis {
313 kraus_decomposition,
314 noise_decomposition,
315 coherence_analysis,
316 symmetry_analysis,
317 process_graph,
318 })
319 }
320
321 pub fn convert_to_pauli_transfer(
323 &self,
324 process_matrix: &Array4<Complex64>,
325 ) -> DeviceResult<Array2<f64>> {
326 let dim = process_matrix.dim().0;
327 let pauli_dim = dim * dim;
328 let mut pauli_transfer = Array2::zeros((pauli_dim, pauli_dim));
329
330 for i in 0..pauli_dim.min(dim) {
333 for j in 0..pauli_dim.min(dim) {
334 if i < dim && j < dim {
335 pauli_transfer[[i, j]] = process_matrix[[i, j, i, j]].re;
336 }
337 }
338 }
339
340 Ok(pauli_transfer)
341 }
342
343 pub fn quantify_uncertainty(
345 &self,
346 process_matrix: &Array4<Complex64>,
347 experimental_data: &ExperimentalData,
348 ) -> DeviceResult<ProcessUncertaintyQuantification> {
349 let dim = process_matrix.dim().0;
350
351 let mut confidence_intervals = Array4::from_elem((dim, dim, dim, dim), (0.0, 0.0));
353 let mut bootstrap_uncertainty = Array4::zeros((dim, dim, dim, dim));
354
355 for i in 0..dim {
356 for j in 0..dim {
357 for k in 0..dim {
358 for l in 0..dim {
359 let element_value = process_matrix[[i, j, k, l]].norm();
360 let uncertainty = element_value * 0.1; confidence_intervals[[i, j, k, l]] = (
362 1.96f64.mul_add(-uncertainty, element_value),
363 1.96f64.mul_add(uncertainty, element_value),
364 );
365 bootstrap_uncertainty[[i, j, k, l]] = uncertainty;
366 }
367 }
368 }
369 }
370
371 let fisher_info_dim = dim * dim;
373 let fisher_information = Array2::eye(fisher_info_dim);
374
375 Ok(ProcessUncertaintyQuantification {
376 confidence_intervals,
377 bootstrap_uncertainty,
378 fisher_information,
379 })
380 }
381
382 pub fn compare_with_known_processes(
384 &self,
385 process_matrix: &Array4<Complex64>,
386 ) -> DeviceResult<ProcessComparisons> {
387 let mut standard_process_fidelities = HashMap::new();
388 let mut process_distances = HashMap::new();
389 let mut model_selection_scores = HashMap::new();
390
391 let identity_fidelity = self.calculate_process_fidelity(process_matrix)?;
393 standard_process_fidelities.insert("identity".to_string(), identity_fidelity);
394 process_distances.insert("identity".to_string(), 1.0 - identity_fidelity);
395
396 let pauli_channels = ["pauli_x", "pauli_y", "pauli_z"];
398 for channel in &pauli_channels {
399 let fidelity = 0.5; standard_process_fidelities.insert(channel.to_string(), fidelity);
401 process_distances.insert(channel.to_string(), 1.0 - fidelity);
402 model_selection_scores.insert(channel.to_string(), fidelity);
403 }
404
405 Ok(ProcessComparisons {
406 standard_process_fidelities,
407 process_distances,
408 model_selection_scores,
409 })
410 }
411}