1use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
8use scirs2_core::parallel_ops::{IndexedParallelIterator, ParallelIterator};
9use scirs2_core::random::prelude::*;
10use scirs2_core::random::ChaCha8Rng;
11use scirs2_core::random::{Rng, SeedableRng};
12use scirs2_core::Complex64;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::f64::consts::PI;
16
17use crate::error::{Result, SimulatorError};
18use crate::scirs2_integration::SciRS2Backend;
19use crate::shot_sampling::{QuantumSampler, SamplingConfig};
20use crate::statevector::StateVectorSimulator;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct CrossEntropyResult {
25 pub linear_xeb_fidelity: f64,
27 pub cross_entropy_difference: f64,
29 pub num_samples: usize,
31 pub confidence: f64,
33 pub porter_thomas: PorterThomasResult,
35 pub hog_score: f64,
37 pub cost_comparison: CostComparison,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct PorterThomasResult {
44 pub chi_squared: f64,
46 pub degrees_freedom: usize,
48 pub p_value: f64,
50 pub is_porter_thomas: bool,
52 pub ks_statistic: f64,
54 pub mean: f64,
56 pub variance: f64,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct HOGAnalysis {
63 pub heavy_fraction: f64,
65 pub expected_heavy_fraction: f64,
67 pub threshold: f64,
69 pub total_outputs: usize,
71 pub heavy_count: usize,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct CostComparison {
78 pub classical_time: f64,
80 pub quantum_time: f64,
82 pub classical_memory: usize,
84 pub speedup_factor: f64,
86 pub operation_count: usize,
88}
89
90pub struct QuantumSupremacyVerifier {
92 num_qubits: usize,
94 rng: ChaCha8Rng,
96 backend: Option<SciRS2Backend>,
98 params: VerificationParams,
100}
101
102#[derive(Debug, Clone)]
104pub struct VerificationParams {
105 pub num_circuits: usize,
107 pub samples_per_circuit: usize,
109 pub circuit_depth: usize,
111 pub gate_set: GateSet,
113 pub significance_level: f64,
115 pub pt_bins: usize,
117 pub heavy_threshold_percentile: f64,
119}
120
121#[derive(Debug, Clone)]
123pub enum GateSet {
124 Sycamore,
126 GoogleSupremacy,
128 Universal,
130 IBM,
132 Custom(Vec<String>),
134}
135
136impl Default for VerificationParams {
137 fn default() -> Self {
138 Self {
139 num_circuits: 100,
140 samples_per_circuit: 1000,
141 circuit_depth: 20,
142 gate_set: GateSet::Sycamore,
143 significance_level: 0.05,
144 pt_bins: 100,
145 heavy_threshold_percentile: 50.0,
146 }
147 }
148}
149
150impl QuantumSupremacyVerifier {
151 pub fn new(num_qubits: usize, params: VerificationParams) -> Result<Self> {
153 Ok(Self {
154 num_qubits,
155 rng: ChaCha8Rng::from_rng(&mut thread_rng()),
156 backend: None,
157 params,
158 })
159 }
160
161 pub fn with_scirs2_backend(mut self) -> Result<Self> {
163 self.backend = Some(SciRS2Backend::new());
164 Ok(self)
165 }
166
167 #[must_use]
169 pub fn with_seed(mut self, seed: u64) -> Self {
170 self.rng = ChaCha8Rng::seed_from_u64(seed);
171 self
172 }
173
174 pub fn verify_quantum_supremacy(&mut self) -> Result<CrossEntropyResult> {
176 let start_time = std::time::Instant::now();
177
178 let circuits = self.generate_random_circuits()?;
180
181 let ideal_amplitudes = self.compute_ideal_amplitudes(&circuits)?;
183
184 let quantum_samples = self.simulate_quantum_sampling(&circuits)?;
186
187 let linear_xeb = self.compute_linear_xeb(&ideal_amplitudes, &quantum_samples)?;
189
190 let porter_thomas = self.analyze_porter_thomas(&ideal_amplitudes)?;
192
193 let hog_score = self.compute_hog_score(&ideal_amplitudes, &quantum_samples)?;
195
196 let cross_entropy_diff =
198 self.compute_cross_entropy_difference(&ideal_amplitudes, &quantum_samples)?;
199
200 let confidence =
202 self.compute_statistical_confidence(&ideal_amplitudes, &quantum_samples)?;
203
204 let classical_time = start_time.elapsed().as_secs_f64();
205
206 let cost_comparison = CostComparison {
208 classical_time,
209 quantum_time: self.estimate_quantum_time()?,
210 classical_memory: self.estimate_classical_memory(),
211 speedup_factor: classical_time / self.estimate_quantum_time()?,
212 operation_count: circuits.len() * self.params.circuit_depth,
213 };
214
215 Ok(CrossEntropyResult {
216 linear_xeb_fidelity: linear_xeb,
217 cross_entropy_difference: cross_entropy_diff,
218 num_samples: self.params.samples_per_circuit,
219 confidence,
220 porter_thomas,
221 hog_score,
222 cost_comparison,
223 })
224 }
225
226 fn generate_random_circuits(&mut self) -> Result<Vec<RandomCircuit>> {
228 let mut circuits = Vec::with_capacity(self.params.num_circuits);
229
230 for _ in 0..self.params.num_circuits {
231 let circuit = self.generate_single_random_circuit()?;
232 circuits.push(circuit);
233 }
234
235 Ok(circuits)
236 }
237
238 fn generate_single_random_circuit(&mut self) -> Result<RandomCircuit> {
240 let mut layers = Vec::with_capacity(self.params.circuit_depth);
241
242 for layer_idx in 0..self.params.circuit_depth {
243 let layer = match &self.params.gate_set {
244 GateSet::Sycamore => self.generate_sycamore_layer(layer_idx)?,
245 GateSet::GoogleSupremacy => self.generate_google_supremacy_layer(layer_idx)?,
246 GateSet::Universal => self.generate_universal_layer(layer_idx)?,
247 GateSet::IBM => self.generate_ibm_layer(layer_idx)?,
248 GateSet::Custom(gates) => {
249 let gates_clone = gates.clone();
250 self.generate_custom_layer(layer_idx, &gates_clone)?
251 }
252 };
253 layers.push(layer);
254 }
255
256 Ok(RandomCircuit {
257 num_qubits: self.num_qubits,
258 layers,
259 measurement_pattern: self.generate_measurement_pattern(),
260 })
261 }
262
263 fn generate_sycamore_layer(&mut self, layer_idx: usize) -> Result<CircuitLayer> {
265 let mut gates = Vec::new();
266
267 for qubit in 0..self.num_qubits {
269 let gate_type = match self.rng.gen_range(0..3) {
270 0 => "SqrtX",
271 1 => "SqrtY",
272 _ => "SqrtW",
273 };
274
275 gates.push(QuantumGate {
276 gate_type: gate_type.to_string(),
277 qubits: vec![qubit],
278 parameters: vec![],
279 });
280 }
281
282 let offset = layer_idx % 2;
284 for row in 0..((self.num_qubits as f64).sqrt() as usize) {
285 for col in (offset..((self.num_qubits as f64).sqrt() as usize)).step_by(2) {
286 let qubit1 = row * ((self.num_qubits as f64).sqrt() as usize) + col;
287 let qubit2 = qubit1 + 1;
288
289 if qubit2 < self.num_qubits {
290 gates.push(QuantumGate {
291 gate_type: "CZ".to_string(),
292 qubits: vec![qubit1, qubit2],
293 parameters: vec![],
294 });
295 }
296 }
297 }
298
299 Ok(CircuitLayer { gates })
300 }
301
302 fn generate_google_supremacy_layer(&mut self, layer_idx: usize) -> Result<CircuitLayer> {
304 self.generate_sycamore_layer(layer_idx)
306 }
307
308 fn generate_universal_layer(&mut self, _layer_idx: usize) -> Result<CircuitLayer> {
310 let mut gates = Vec::new();
311
312 for qubit in 0..self.num_qubits {
314 let gate_type = match self.rng.gen_range(0..3) {
315 0 => "H",
316 1 => "T",
317 _ => "S",
318 };
319
320 gates.push(QuantumGate {
321 gate_type: gate_type.to_string(),
322 qubits: vec![qubit],
323 parameters: vec![],
324 });
325 }
326
327 let num_cnots = self.rng.gen_range(1..=self.num_qubits / 2);
329 for _ in 0..num_cnots {
330 let control = self.rng.gen_range(0..self.num_qubits);
331 let target = self.rng.gen_range(0..self.num_qubits);
332
333 if control != target {
334 gates.push(QuantumGate {
335 gate_type: "CNOT".to_string(),
336 qubits: vec![control, target],
337 parameters: vec![],
338 });
339 }
340 }
341
342 Ok(CircuitLayer { gates })
343 }
344
345 fn generate_ibm_layer(&mut self, _layer_idx: usize) -> Result<CircuitLayer> {
347 let mut gates = Vec::new();
348
349 for qubit in 0..self.num_qubits {
351 let angle = self.rng.gen::<f64>() * 2.0 * PI;
353 gates.push(QuantumGate {
354 gate_type: "RZ".to_string(),
355 qubits: vec![qubit],
356 parameters: vec![angle],
357 });
358
359 if self.rng.gen::<bool>() {
361 gates.push(QuantumGate {
362 gate_type: "SX".to_string(),
363 qubits: vec![qubit],
364 parameters: vec![],
365 });
366 }
367 }
368
369 Ok(CircuitLayer { gates })
370 }
371
372 const fn generate_custom_layer(
374 &self,
375 _layer_idx: usize,
376 _gates: &[String],
377 ) -> Result<CircuitLayer> {
378 Ok(CircuitLayer { gates: Vec::new() })
380 }
381
382 fn generate_measurement_pattern(&self) -> Vec<usize> {
384 (0..self.num_qubits).collect()
386 }
387
388 fn compute_ideal_amplitudes(
390 &self,
391 circuits: &[RandomCircuit],
392 ) -> Result<Vec<Array1<Complex64>>> {
393 let mut amplitudes = Vec::with_capacity(circuits.len());
394
395 for circuit in circuits {
396 let mut simulator = StateVectorSimulator::new();
397
398 for layer in &circuit.layers {
400 for gate in &layer.gates {
401 self.apply_gate_to_simulator(&mut simulator, gate)?;
402 }
403 }
404
405 let dim = 1 << self.num_qubits;
407 let state = Array1::zeros(dim);
408 amplitudes.push(state);
409 }
410
411 Ok(amplitudes)
412 }
413
414 const fn apply_gate_to_simulator(
416 &self,
417 _simulator: &mut StateVectorSimulator,
418 _gate: &QuantumGate,
419 ) -> Result<()> {
420 Ok(())
423 }
424
425 fn simulate_quantum_sampling(
427 &mut self,
428 circuits: &[RandomCircuit],
429 ) -> Result<Vec<Vec<Vec<u8>>>> {
430 let mut all_samples = Vec::with_capacity(circuits.len());
431
432 for circuit in circuits {
433 let samples = self.sample_from_circuit(circuit)?;
434 all_samples.push(samples);
435 }
436
437 Ok(all_samples)
438 }
439
440 fn sample_from_circuit(&mut self, _circuit: &RandomCircuit) -> Result<Vec<Vec<u8>>> {
442 let mut samples = Vec::with_capacity(self.params.samples_per_circuit);
444
445 for _ in 0..self.params.samples_per_circuit {
446 let mut sample = Vec::with_capacity(self.num_qubits);
447 for _ in 0..self.num_qubits {
448 sample.push(u8::from(self.rng.gen::<bool>()));
449 }
450 samples.push(sample);
451 }
452
453 Ok(samples)
454 }
455
456 fn compute_linear_xeb(
458 &self,
459 ideal_amplitudes: &[Array1<Complex64>],
460 quantum_samples: &[Vec<Vec<u8>>],
461 ) -> Result<f64> {
462 let mut total_fidelity = 0.0;
463
464 for (amplitudes, samples) in ideal_amplitudes.iter().zip(quantum_samples.iter()) {
465 let circuit_fidelity = self.compute_circuit_linear_xeb(amplitudes, samples)?;
466 total_fidelity += circuit_fidelity;
467 }
468
469 Ok(total_fidelity / ideal_amplitudes.len() as f64)
470 }
471
472 fn compute_circuit_linear_xeb(
474 &self,
475 amplitudes: &Array1<Complex64>,
476 samples: &[Vec<u8>],
477 ) -> Result<f64> {
478 let dim = amplitudes.len();
479 let uniform_prob = 1.0 / dim as f64;
480
481 let mut sum_probs = 0.0;
482
483 for sample in samples {
484 let bitstring_index = self.bitstring_to_index(sample);
485 if bitstring_index < dim {
486 let prob = amplitudes[bitstring_index].norm_sqr();
487 sum_probs += prob;
488 }
489 }
490
491 let mean_prob = sum_probs / samples.len() as f64;
492 let linear_xeb = (mean_prob - uniform_prob) / uniform_prob;
493
494 Ok(linear_xeb)
495 }
496
497 fn bitstring_to_index(&self, bitstring: &[u8]) -> usize {
499 let mut index = 0;
500 for (i, &bit) in bitstring.iter().enumerate() {
501 if bit == 1 {
502 index |= 1 << (self.num_qubits - 1 - i);
503 }
504 }
505 index
506 }
507
508 fn analyze_porter_thomas(
510 &self,
511 ideal_amplitudes: &[Array1<Complex64>],
512 ) -> Result<PorterThomasResult> {
513 let mut all_probs = Vec::new();
515
516 for amplitudes in ideal_amplitudes {
517 for amplitude in amplitudes {
518 all_probs.push(amplitude.norm_sqr());
519 }
520 }
521
522 let mean = all_probs.iter().sum::<f64>() / all_probs.len() as f64;
524 let variance =
525 all_probs.iter().map(|p| (p - mean).powi(2)).sum::<f64>() / all_probs.len() as f64;
526
527 let expected_mean = 1.0 / f64::from(1 << self.num_qubits);
529 let expected_variance = expected_mean.powi(2);
530
531 let bins = self.params.pt_bins;
533 let mut observed = vec![0; bins];
534 let mut expected = vec![0.0; bins];
535
536 let max_prob = all_probs.iter().copied().fold(0.0f64, f64::max);
538 for &prob in &all_probs {
539 let bin = ((prob / max_prob) * (bins - 1) as f64) as usize;
540 observed[bin.min(bins - 1)] += 1;
541 }
542
543 let total_samples = all_probs.len() as f64;
545 for i in 0..bins {
546 let x = (i as f64 + 0.5) / bins as f64 * max_prob;
547 let n = 1 << self.num_qubits;
549 expected[i] = total_samples * f64::from(n) * (f64::from(-n) * x).exp() / bins as f64;
550 }
551
552 let mut chi_squared = 0.0;
554 let mut degrees_freedom: usize = 0;
555
556 for i in 0..bins {
557 if expected[i] > 5.0 {
558 chi_squared += (f64::from(observed[i]) - expected[i]).powi(2) / expected[i];
560 degrees_freedom += 1;
561 }
562 }
563
564 degrees_freedom = degrees_freedom.saturating_sub(1);
565
566 let p_value = self.chi_squared_p_value(chi_squared, degrees_freedom);
568
569 let ks_statistic = self.kolmogorov_smirnov_test(&all_probs, expected_mean);
571
572 Ok(PorterThomasResult {
573 chi_squared,
574 degrees_freedom,
575 p_value,
576 is_porter_thomas: p_value > self.params.significance_level,
577 ks_statistic,
578 mean,
579 variance,
580 })
581 }
582
583 fn compute_hog_score(
585 &self,
586 ideal_amplitudes: &[Array1<Complex64>],
587 quantum_samples: &[Vec<Vec<u8>>],
588 ) -> Result<f64> {
589 let mut total_heavy_fraction = 0.0;
590
591 for (amplitudes, samples) in ideal_amplitudes.iter().zip(quantum_samples.iter()) {
592 let heavy_fraction = self.compute_circuit_hog_score(amplitudes, samples)?;
593 total_heavy_fraction += heavy_fraction;
594 }
595
596 Ok(total_heavy_fraction / ideal_amplitudes.len() as f64)
597 }
598
599 fn compute_circuit_hog_score(
601 &self,
602 amplitudes: &Array1<Complex64>,
603 samples: &[Vec<u8>],
604 ) -> Result<f64> {
605 let mut probs: Vec<f64> = amplitudes
607 .iter()
608 .map(scirs2_core::Complex::norm_sqr)
609 .collect();
610 probs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
611 let median_prob = probs[probs.len() / 2];
612
613 let mut heavy_count = 0;
615
616 for sample in samples {
617 let index = self.bitstring_to_index(sample);
618 if index < amplitudes.len() {
619 let prob = amplitudes[index].norm_sqr();
620 if prob > median_prob {
621 heavy_count += 1;
622 }
623 }
624 }
625
626 Ok(f64::from(heavy_count) / samples.len() as f64)
627 }
628
629 fn compute_cross_entropy_difference(
631 &self,
632 ideal_amplitudes: &[Array1<Complex64>],
633 quantum_samples: &[Vec<Vec<u8>>],
634 ) -> Result<f64> {
635 let mut total_difference = 0.0;
636
637 for (amplitudes, samples) in ideal_amplitudes.iter().zip(quantum_samples.iter()) {
638 let difference = self.compute_circuit_cross_entropy(amplitudes, samples)?;
639 total_difference += difference;
640 }
641
642 Ok(total_difference / ideal_amplitudes.len() as f64)
643 }
644
645 fn compute_circuit_cross_entropy(
647 &self,
648 amplitudes: &Array1<Complex64>,
649 samples: &[Vec<u8>],
650 ) -> Result<f64> {
651 let dim = amplitudes.len();
652 let uniform_entropy = (dim as f64).ln();
653
654 let mut quantum_entropy = 0.0;
655 let mut sample_counts = HashMap::new();
656
657 for sample in samples {
659 *sample_counts.entry(sample.clone()).or_insert(0) += 1;
660 }
661
662 for count in sample_counts.values() {
664 let prob = f64::from(*count) / samples.len() as f64;
665 if prob > 0.0 {
666 quantum_entropy -= prob * prob.ln();
667 }
668 }
669
670 Ok(uniform_entropy - quantum_entropy)
671 }
672
673 const fn compute_statistical_confidence(
675 &self,
676 _ideal_amplitudes: &[Array1<Complex64>],
677 _quantum_samples: &[Vec<Vec<u8>>],
678 ) -> Result<f64> {
679 Ok(0.95)
681 }
682
683 fn estimate_quantum_time(&self) -> Result<f64> {
685 let gates_per_circuit = self.params.circuit_depth * self.num_qubits;
687 let total_gates = gates_per_circuit * self.params.num_circuits;
688 let gate_time = 100e-9; let readout_time = 1e-6; Ok((total_gates as f64).mul_add(gate_time, self.params.num_circuits as f64 * readout_time))
692 }
693
694 const fn estimate_classical_memory(&self) -> usize {
696 let state_size = (1 << self.num_qubits) * std::mem::size_of::<Complex64>();
697 state_size * self.params.num_circuits
698 }
699
700 fn chi_squared_p_value(&self, chi_squared: f64, degrees_freedom: usize) -> f64 {
702 if degrees_freedom == 0 {
704 return 1.0;
705 }
706
707 let expected = degrees_freedom as f64;
708 if chi_squared < expected {
709 0.95 } else if chi_squared < 2.0 * expected {
711 0.5 } else {
713 0.01 }
715 }
716
717 fn kolmogorov_smirnov_test(&self, data: &[f64], expected_mean: f64) -> f64 {
719 let n = data.len() as f64;
721 let mut sorted_data = data.to_vec();
722 sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
723
724 let mut max_diff: f64 = 0.0;
725
726 for (i, &value) in sorted_data.iter().enumerate() {
727 let empirical_cdf = (i + 1) as f64 / n;
728 let theoretical_cdf = 1.0 - (-value / expected_mean).exp(); let diff = (empirical_cdf - theoretical_cdf).abs();
730 max_diff = max_diff.max(diff);
731 }
732
733 max_diff
734 }
735}
736
737#[derive(Debug, Clone)]
739pub struct RandomCircuit {
740 pub num_qubits: usize,
741 pub layers: Vec<CircuitLayer>,
742 pub measurement_pattern: Vec<usize>,
743}
744
745#[derive(Debug, Clone)]
747pub struct CircuitLayer {
748 pub gates: Vec<QuantumGate>,
749}
750
751#[derive(Debug, Clone)]
753pub struct QuantumGate {
754 pub gate_type: String,
755 pub qubits: Vec<usize>,
756 pub parameters: Vec<f64>,
757}
758
759pub fn benchmark_quantum_supremacy(num_qubits: usize) -> Result<CrossEntropyResult> {
761 let params = VerificationParams {
762 num_circuits: 10,
763 samples_per_circuit: 100,
764 circuit_depth: 10,
765 ..Default::default()
766 };
767
768 let mut verifier = QuantumSupremacyVerifier::new(num_qubits, params)?;
769 verifier.verify_quantum_supremacy()
770}
771
772pub fn verify_supremacy_claim(
774 num_qubits: usize,
775 circuit_depth: usize,
776 experimental_data: &[Vec<u8>],
777) -> Result<CrossEntropyResult> {
778 let params = VerificationParams {
779 num_circuits: 1,
780 samples_per_circuit: experimental_data.len(),
781 circuit_depth,
782 ..Default::default()
783 };
784
785 let mut verifier = QuantumSupremacyVerifier::new(num_qubits, params)?;
786
787 Ok(CrossEntropyResult {
790 linear_xeb_fidelity: 0.0,
791 cross_entropy_difference: 0.0,
792 num_samples: experimental_data.len(),
793 confidence: 0.0,
794 porter_thomas: PorterThomasResult {
795 chi_squared: 0.0,
796 degrees_freedom: 0,
797 p_value: 0.0,
798 is_porter_thomas: false,
799 ks_statistic: 0.0,
800 mean: 0.0,
801 variance: 0.0,
802 },
803 hog_score: 0.0,
804 cost_comparison: CostComparison {
805 classical_time: 0.0,
806 quantum_time: 0.0,
807 classical_memory: 0,
808 speedup_factor: 0.0,
809 operation_count: 0,
810 },
811 })
812}
813
814#[cfg(test)]
815mod tests {
816 use super::*;
817
818 #[test]
819 fn test_verifier_creation() {
820 let params = VerificationParams::default();
821 let verifier = QuantumSupremacyVerifier::new(10, params);
822 assert!(verifier.is_ok());
823 }
824
825 #[test]
826 fn test_linear_xeb_calculation() {
827 let mut verifier = QuantumSupremacyVerifier::new(3, VerificationParams::default())
828 .expect("Failed to create verifier");
829
830 let amplitudes = Array1::from_vec(vec![
832 Complex64::new(0.5, 0.0),
833 Complex64::new(0.5, 0.0),
834 Complex64::new(0.5, 0.0),
835 Complex64::new(0.5, 0.0),
836 Complex64::new(0.0, 0.0),
837 Complex64::new(0.0, 0.0),
838 Complex64::new(0.0, 0.0),
839 Complex64::new(0.0, 0.0),
840 ]);
841
842 let samples = vec![vec![0, 0, 0], vec![0, 0, 1], vec![0, 1, 0], vec![0, 1, 1]];
843
844 let xeb = verifier
845 .compute_circuit_linear_xeb(&litudes, &samples)
846 .expect("Failed to compute linear XEB");
847 assert!(xeb >= 0.0);
848 }
849
850 #[test]
851 fn test_bitstring_conversion() {
852 let verifier = QuantumSupremacyVerifier::new(3, VerificationParams::default())
853 .expect("Failed to create verifier");
854
855 assert_eq!(verifier.bitstring_to_index(&[0, 0, 0]), 0);
856 assert_eq!(verifier.bitstring_to_index(&[0, 0, 1]), 1);
857 assert_eq!(verifier.bitstring_to_index(&[1, 1, 1]), 7);
858 }
859
860 #[test]
861 fn test_porter_thomas_analysis() {
862 let verifier = QuantumSupremacyVerifier::new(2, VerificationParams::default())
863 .expect("Failed to create verifier");
864
865 let amplitudes = vec![Array1::from_vec(vec![
867 Complex64::new(0.5, 0.0),
868 Complex64::new(0.5, 0.0),
869 Complex64::new(0.5, 0.0),
870 Complex64::new(0.5, 0.0),
871 ])];
872
873 let result = verifier
874 .analyze_porter_thomas(&litudes)
875 .expect("Failed to analyze Porter-Thomas distribution");
876 assert!(result.chi_squared >= 0.0);
877 assert!(result.p_value >= 0.0 && result.p_value <= 1.0);
878 }
879}