1use scirs2_core::ndarray::Array1;
8use scirs2_core::random::prelude::*;
9use scirs2_core::random::ChaCha8Rng;
10use scirs2_core::random::{RngExt, SeedableRng};
11use scirs2_core::Complex64;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::f64::consts::PI;
15
16use crate::error::{Result, SimulatorError};
17use crate::scirs2_integration::SciRS2Backend;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct CrossEntropyResult {
22 pub linear_xeb_fidelity: f64,
24 pub cross_entropy_difference: f64,
26 pub num_samples: usize,
28 pub confidence: f64,
30 pub porter_thomas: PorterThomasResult,
32 pub hog_score: f64,
34 pub cost_comparison: CostComparison,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct PorterThomasResult {
41 pub chi_squared: f64,
43 pub degrees_freedom: usize,
45 pub p_value: f64,
47 pub is_porter_thomas: bool,
49 pub ks_statistic: f64,
51 pub mean: f64,
53 pub variance: f64,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct HOGAnalysis {
60 pub heavy_fraction: f64,
62 pub expected_heavy_fraction: f64,
64 pub threshold: f64,
66 pub total_outputs: usize,
68 pub heavy_count: usize,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct CostComparison {
75 pub classical_time: f64,
77 pub quantum_time: f64,
79 pub classical_memory: usize,
81 pub speedup_factor: f64,
83 pub operation_count: usize,
85}
86
87pub struct QuantumSupremacyVerifier {
89 num_qubits: usize,
91 rng: ChaCha8Rng,
93 backend: Option<SciRS2Backend>,
95 params: VerificationParams,
97}
98
99#[derive(Debug, Clone)]
101pub struct VerificationParams {
102 pub num_circuits: usize,
104 pub samples_per_circuit: usize,
106 pub circuit_depth: usize,
108 pub gate_set: GateSet,
110 pub significance_level: f64,
112 pub pt_bins: usize,
114 pub heavy_threshold_percentile: f64,
116}
117
118#[derive(Debug, Clone)]
120pub enum GateSet {
121 Sycamore,
123 GoogleSupremacy,
125 Universal,
127 IBM,
129 Custom(Vec<String>),
131}
132
133impl Default for VerificationParams {
134 fn default() -> Self {
135 Self {
136 num_circuits: 100,
137 samples_per_circuit: 1000,
138 circuit_depth: 20,
139 gate_set: GateSet::Sycamore,
140 significance_level: 0.05,
141 pt_bins: 100,
142 heavy_threshold_percentile: 50.0,
143 }
144 }
145}
146
147impl QuantumSupremacyVerifier {
148 pub fn new(num_qubits: usize, params: VerificationParams) -> Result<Self> {
150 Ok(Self {
151 num_qubits,
152 rng: ChaCha8Rng::from_rng(&mut thread_rng()),
153 backend: None,
154 params,
155 })
156 }
157
158 pub fn with_scirs2_backend(mut self) -> Result<Self> {
160 self.backend = Some(SciRS2Backend::new());
161 Ok(self)
162 }
163
164 #[must_use]
166 pub fn with_seed(mut self, seed: u64) -> Self {
167 self.rng = ChaCha8Rng::seed_from_u64(seed);
168 self
169 }
170
171 pub fn verify_quantum_supremacy(&mut self) -> Result<CrossEntropyResult> {
173 let start_time = std::time::Instant::now();
174
175 let circuits = self.generate_random_circuits()?;
177
178 let ideal_amplitudes = self.compute_ideal_amplitudes(&circuits)?;
180
181 let quantum_samples = self.simulate_quantum_sampling(&ideal_amplitudes)?;
183
184 let linear_xeb = self.compute_linear_xeb(&ideal_amplitudes, &quantum_samples)?;
186
187 let porter_thomas = self.analyze_porter_thomas(&ideal_amplitudes)?;
189
190 let hog_score = self.compute_hog_score(&ideal_amplitudes, &quantum_samples)?;
192
193 let cross_entropy_diff =
195 self.compute_cross_entropy_difference(&ideal_amplitudes, &quantum_samples)?;
196
197 let confidence =
199 self.compute_statistical_confidence(&ideal_amplitudes, &quantum_samples)?;
200
201 let classical_time = start_time.elapsed().as_secs_f64();
202
203 let cost_comparison = CostComparison {
205 classical_time,
206 quantum_time: self.estimate_quantum_time()?,
207 classical_memory: self.estimate_classical_memory(),
208 speedup_factor: classical_time / self.estimate_quantum_time()?,
209 operation_count: circuits.len() * self.params.circuit_depth,
210 };
211
212 Ok(CrossEntropyResult {
213 linear_xeb_fidelity: linear_xeb,
214 cross_entropy_difference: cross_entropy_diff,
215 num_samples: self.params.samples_per_circuit,
216 confidence,
217 porter_thomas,
218 hog_score,
219 cost_comparison,
220 })
221 }
222
223 fn generate_random_circuits(&mut self) -> Result<Vec<RandomCircuit>> {
225 let mut circuits = Vec::with_capacity(self.params.num_circuits);
226
227 for _ in 0..self.params.num_circuits {
228 let circuit = self.generate_single_random_circuit()?;
229 circuits.push(circuit);
230 }
231
232 Ok(circuits)
233 }
234
235 fn generate_single_random_circuit(&mut self) -> Result<RandomCircuit> {
237 let mut layers = Vec::with_capacity(self.params.circuit_depth);
238
239 for layer_idx in 0..self.params.circuit_depth {
240 let layer = match &self.params.gate_set {
241 GateSet::Sycamore => self.generate_sycamore_layer(layer_idx)?,
242 GateSet::GoogleSupremacy => self.generate_google_supremacy_layer(layer_idx)?,
243 GateSet::Universal => self.generate_universal_layer(layer_idx)?,
244 GateSet::IBM => self.generate_ibm_layer(layer_idx)?,
245 GateSet::Custom(gates) => {
246 let gates_clone = gates.clone();
247 self.generate_custom_layer(layer_idx, &gates_clone)?
248 }
249 };
250 layers.push(layer);
251 }
252
253 Ok(RandomCircuit {
254 num_qubits: self.num_qubits,
255 layers,
256 measurement_pattern: self.generate_measurement_pattern(),
257 })
258 }
259
260 fn generate_sycamore_layer(&mut self, layer_idx: usize) -> Result<CircuitLayer> {
262 let mut gates = Vec::new();
263
264 for qubit in 0..self.num_qubits {
266 let gate_type = match self.rng.random_range(0..3) {
267 0 => "SqrtX",
268 1 => "SqrtY",
269 _ => "SqrtW",
270 };
271
272 gates.push(QuantumGate {
273 gate_type: gate_type.to_string(),
274 qubits: vec![qubit],
275 parameters: vec![],
276 });
277 }
278
279 let offset = layer_idx % 2;
281 for row in 0..((self.num_qubits as f64).sqrt() as usize) {
282 for col in (offset..((self.num_qubits as f64).sqrt() as usize)).step_by(2) {
283 let qubit1 = row * ((self.num_qubits as f64).sqrt() as usize) + col;
284 let qubit2 = qubit1 + 1;
285
286 if qubit2 < self.num_qubits {
287 gates.push(QuantumGate {
288 gate_type: "CZ".to_string(),
289 qubits: vec![qubit1, qubit2],
290 parameters: vec![],
291 });
292 }
293 }
294 }
295
296 Ok(CircuitLayer { gates })
297 }
298
299 fn generate_google_supremacy_layer(&mut self, layer_idx: usize) -> Result<CircuitLayer> {
301 self.generate_sycamore_layer(layer_idx)
303 }
304
305 fn generate_universal_layer(&mut self, _layer_idx: usize) -> Result<CircuitLayer> {
307 let mut gates = Vec::new();
308
309 for qubit in 0..self.num_qubits {
311 let gate_type = match self.rng.random_range(0..3) {
312 0 => "H",
313 1 => "T",
314 _ => "S",
315 };
316
317 gates.push(QuantumGate {
318 gate_type: gate_type.to_string(),
319 qubits: vec![qubit],
320 parameters: vec![],
321 });
322 }
323
324 let num_cnots = self.rng.random_range(1..=self.num_qubits / 2);
326 for _ in 0..num_cnots {
327 let control = self.rng.random_range(0..self.num_qubits);
328 let target = self.rng.random_range(0..self.num_qubits);
329
330 if control != target {
331 gates.push(QuantumGate {
332 gate_type: "CNOT".to_string(),
333 qubits: vec![control, target],
334 parameters: vec![],
335 });
336 }
337 }
338
339 Ok(CircuitLayer { gates })
340 }
341
342 fn generate_ibm_layer(&mut self, _layer_idx: usize) -> Result<CircuitLayer> {
344 let mut gates = Vec::new();
345
346 for qubit in 0..self.num_qubits {
348 let angle = self.rng.random::<f64>() * 2.0 * PI;
350 gates.push(QuantumGate {
351 gate_type: "RZ".to_string(),
352 qubits: vec![qubit],
353 parameters: vec![angle],
354 });
355
356 if self.rng.random::<bool>() {
358 gates.push(QuantumGate {
359 gate_type: "SX".to_string(),
360 qubits: vec![qubit],
361 parameters: vec![],
362 });
363 }
364 }
365
366 Ok(CircuitLayer { gates })
367 }
368
369 const fn generate_custom_layer(
371 &self,
372 _layer_idx: usize,
373 _gates: &[String],
374 ) -> Result<CircuitLayer> {
375 Ok(CircuitLayer { gates: Vec::new() })
377 }
378
379 fn generate_measurement_pattern(&self) -> Vec<usize> {
381 (0..self.num_qubits).collect()
383 }
384
385 fn compute_ideal_amplitudes(
387 &self,
388 circuits: &[RandomCircuit],
389 ) -> Result<Vec<Array1<Complex64>>> {
390 let mut amplitudes = Vec::with_capacity(circuits.len());
391
392 for circuit in circuits {
393 let dim = 1usize << self.num_qubits;
394 let mut state = vec![Complex64::new(0.0, 0.0); dim];
396 state[0] = Complex64::new(1.0, 0.0);
397
398 for layer in &circuit.layers {
400 for gate in &layer.gates {
401 self.apply_gate_to_state(&mut state, gate)?;
402 }
403 }
404
405 let final_state = Array1::from_vec(state);
406 amplitudes.push(final_state);
407 }
408
409 Ok(amplitudes)
410 }
411
412 fn apply_gate_to_state(&self, state: &mut Vec<Complex64>, gate: &QuantumGate) -> Result<()> {
416 let n = self.num_qubits;
417 let dim = state.len();
418
419 fn apply_1q(state: &mut [Complex64], m: &[Complex64; 4], target_bit: usize, dim: usize) {
422 for i in 0..dim {
423 if (i >> target_bit) & 1 == 0 {
424 let j = i | (1 << target_bit);
425 let a = state[i];
426 let b = state[j];
427 state[i] = m[0] * a + m[1] * b;
428 state[j] = m[2] * a + m[3] * b;
429 }
430 }
431 }
432
433 fn apply_2q(
437 state: &mut [Complex64],
438 m: &[Complex64; 16],
439 ctrl_bit: usize,
440 tgt_bit: usize,
441 dim: usize,
442 ) {
443 for i in 0..dim {
444 if (i >> ctrl_bit) & 1 == 0 && (i >> tgt_bit) & 1 == 0 {
446 let i00 = i;
447 let i01 = i | (1 << tgt_bit);
448 let i10 = i | (1 << ctrl_bit);
449 let i11 = i | (1 << ctrl_bit) | (1 << tgt_bit);
450 let s00 = state[i00];
451 let s01 = state[i01];
452 let s10 = state[i10];
453 let s11 = state[i11];
454 state[i00] = m[0] * s00 + m[1] * s01 + m[2] * s10 + m[3] * s11;
455 state[i01] = m[4] * s00 + m[5] * s01 + m[6] * s10 + m[7] * s11;
456 state[i10] = m[8] * s00 + m[9] * s01 + m[10] * s10 + m[11] * s11;
457 state[i11] = m[12] * s00 + m[13] * s01 + m[14] * s10 + m[15] * s11;
458 }
459 }
460 }
461
462 match gate.gate_type.as_str() {
463 "H" => {
465 let q = *gate.qubits.first().ok_or_else(|| {
466 SimulatorError::InvalidInput("H gate requires a qubit argument".to_string())
467 })?;
468 if q >= n {
469 return Err(SimulatorError::InvalidInput(format!(
470 "Qubit index {q} out of range ({n} qubits)"
471 )));
472 }
473 let f = std::f64::consts::FRAC_1_SQRT_2;
474 let c = Complex64::new(f, 0.0);
475 let m = [c, c, c, -c];
476 apply_1q(state, &m, q, dim);
477 }
478 "X" | "PauliX" => {
479 let q = *gate.qubits.first().ok_or_else(|| {
480 SimulatorError::InvalidInput("X gate requires a qubit argument".to_string())
481 })?;
482 if q >= n {
483 return Err(SimulatorError::InvalidInput(format!(
484 "Qubit index {q} out of range ({n} qubits)"
485 )));
486 }
487 let m = [
488 Complex64::new(0.0, 0.0),
489 Complex64::new(1.0, 0.0),
490 Complex64::new(1.0, 0.0),
491 Complex64::new(0.0, 0.0),
492 ];
493 apply_1q(state, &m, q, dim);
494 }
495 "Y" | "PauliY" => {
496 let q = *gate.qubits.first().ok_or_else(|| {
497 SimulatorError::InvalidInput("Y gate requires a qubit argument".to_string())
498 })?;
499 if q >= n {
500 return Err(SimulatorError::InvalidInput(format!(
501 "Qubit index {q} out of range ({n} qubits)"
502 )));
503 }
504 let m = [
505 Complex64::new(0.0, 0.0),
506 Complex64::new(0.0, -1.0),
507 Complex64::new(0.0, 1.0),
508 Complex64::new(0.0, 0.0),
509 ];
510 apply_1q(state, &m, q, dim);
511 }
512 "Z" | "PauliZ" => {
513 let q = *gate.qubits.first().ok_or_else(|| {
514 SimulatorError::InvalidInput("Z gate requires a qubit argument".to_string())
515 })?;
516 if q >= n {
517 return Err(SimulatorError::InvalidInput(format!(
518 "Qubit index {q} out of range ({n} qubits)"
519 )));
520 }
521 let m = [
522 Complex64::new(1.0, 0.0),
523 Complex64::new(0.0, 0.0),
524 Complex64::new(0.0, 0.0),
525 Complex64::new(-1.0, 0.0),
526 ];
527 apply_1q(state, &m, q, dim);
528 }
529 "S" => {
530 let q = *gate.qubits.first().ok_or_else(|| {
531 SimulatorError::InvalidInput("S gate requires a qubit argument".to_string())
532 })?;
533 if q >= n {
534 return Err(SimulatorError::InvalidInput(format!(
535 "Qubit index {q} out of range ({n} qubits)"
536 )));
537 }
538 let m = [
539 Complex64::new(1.0, 0.0),
540 Complex64::new(0.0, 0.0),
541 Complex64::new(0.0, 0.0),
542 Complex64::new(0.0, 1.0),
543 ];
544 apply_1q(state, &m, q, dim);
545 }
546 "T" => {
547 let q = *gate.qubits.first().ok_or_else(|| {
548 SimulatorError::InvalidInput("T gate requires a qubit argument".to_string())
549 })?;
550 if q >= n {
551 return Err(SimulatorError::InvalidInput(format!(
552 "Qubit index {q} out of range ({n} qubits)"
553 )));
554 }
555 let f = std::f64::consts::FRAC_1_SQRT_2;
556 let m = [
557 Complex64::new(1.0, 0.0),
558 Complex64::new(0.0, 0.0),
559 Complex64::new(0.0, 0.0),
560 Complex64::new(f, f),
561 ];
562 apply_1q(state, &m, q, dim);
563 }
564 "SX" | "SqrtX" => {
565 let q = *gate.qubits.first().ok_or_else(|| {
567 SimulatorError::InvalidInput("SX gate requires a qubit argument".to_string())
568 })?;
569 if q >= n {
570 return Err(SimulatorError::InvalidInput(format!(
571 "Qubit index {q} out of range ({n} qubits)"
572 )));
573 }
574 let pp = Complex64::new(0.5, 0.5);
575 let pm = Complex64::new(0.5, -0.5);
576 let m = [pp, pm, pm, pp];
577 apply_1q(state, &m, q, dim);
578 }
579 "SqrtY" => {
580 let q = *gate.qubits.first().ok_or_else(|| {
582 SimulatorError::InvalidInput("SqrtY gate requires a qubit argument".to_string())
583 })?;
584 if q >= n {
585 return Err(SimulatorError::InvalidInput(format!(
586 "Qubit index {q} out of range ({n} qubits)"
587 )));
588 }
589 let pp = Complex64::new(0.5, 0.5);
590 let nm = Complex64::new(-0.5, -0.5);
591 let m = [pp, nm, pp, pp];
592 apply_1q(state, &m, q, dim);
593 }
594 "SqrtW" => {
595 let q = *gate.qubits.first().ok_or_else(|| {
599 SimulatorError::InvalidInput("SqrtW gate requires a qubit argument".to_string())
600 })?;
601 if q >= n {
602 return Err(SimulatorError::InvalidInput(format!(
603 "Qubit index {q} out of range ({n} qubits)"
604 )));
605 }
606 let f = 0.5_f64;
609 let m = [
610 Complex64::new(f, f),
611 Complex64::new(f, -f),
612 Complex64::new(f, f),
613 Complex64::new(-f, f),
614 ];
615 apply_1q(state, &m, q, dim);
616 }
617 "RZ" => {
618 let q = *gate.qubits.first().ok_or_else(|| {
619 SimulatorError::InvalidInput("RZ gate requires a qubit argument".to_string())
620 })?;
621 if q >= n {
622 return Err(SimulatorError::InvalidInput(format!(
623 "Qubit index {q} out of range ({n} qubits)"
624 )));
625 }
626 let angle = gate.parameters.first().copied().unwrap_or(0.0);
627 let half = angle / 2.0;
629 let m = [
630 Complex64::new((-half).cos(), (-half).sin()),
631 Complex64::new(0.0, 0.0),
632 Complex64::new(0.0, 0.0),
633 Complex64::new(half.cos(), half.sin()),
634 ];
635 apply_1q(state, &m, q, dim);
636 }
637 "RX" => {
638 let q = *gate.qubits.first().ok_or_else(|| {
639 SimulatorError::InvalidInput("RX gate requires a qubit argument".to_string())
640 })?;
641 if q >= n {
642 return Err(SimulatorError::InvalidInput(format!(
643 "Qubit index {q} out of range ({n} qubits)"
644 )));
645 }
646 let angle = gate.parameters.first().copied().unwrap_or(0.0);
647 let half = angle / 2.0;
648 let c = half.cos();
649 let s = half.sin();
650 let m = [
651 Complex64::new(c, 0.0),
652 Complex64::new(0.0, -s),
653 Complex64::new(0.0, -s),
654 Complex64::new(c, 0.0),
655 ];
656 apply_1q(state, &m, q, dim);
657 }
658 "RY" => {
659 let q = *gate.qubits.first().ok_or_else(|| {
660 SimulatorError::InvalidInput("RY gate requires a qubit argument".to_string())
661 })?;
662 if q >= n {
663 return Err(SimulatorError::InvalidInput(format!(
664 "Qubit index {q} out of range ({n} qubits)"
665 )));
666 }
667 let angle = gate.parameters.first().copied().unwrap_or(0.0);
668 let half = angle / 2.0;
669 let c = half.cos();
670 let s = half.sin();
671 let m = [
672 Complex64::new(c, 0.0),
673 Complex64::new(-s, 0.0),
674 Complex64::new(s, 0.0),
675 Complex64::new(c, 0.0),
676 ];
677 apply_1q(state, &m, q, dim);
678 }
679 "CNOT" | "CX" => {
681 if gate.qubits.len() < 2 {
682 return Err(SimulatorError::InvalidInput(
683 "CNOT gate requires 2 qubit arguments".to_string(),
684 ));
685 }
686 let ctrl = gate.qubits[0];
687 let tgt = gate.qubits[1];
688 if ctrl >= n || tgt >= n {
689 return Err(SimulatorError::InvalidInput(format!(
690 "Qubit index out of range ({n} qubits)"
691 )));
692 }
693 let o = Complex64::new(0.0, 0.0);
695 let l = Complex64::new(1.0, 0.0);
696 let m: [Complex64; 16] = [l, o, o, o, o, l, o, o, o, o, o, l, o, o, l, o];
697 apply_2q(state, &m, ctrl, tgt, dim);
698 }
699 "CZ" => {
700 if gate.qubits.len() < 2 {
701 return Err(SimulatorError::InvalidInput(
702 "CZ gate requires 2 qubit arguments".to_string(),
703 ));
704 }
705 let ctrl = gate.qubits[0];
706 let tgt = gate.qubits[1];
707 if ctrl >= n || tgt >= n {
708 return Err(SimulatorError::InvalidInput(format!(
709 "Qubit index out of range ({n} qubits)"
710 )));
711 }
712 let mask = (1 << ctrl) | (1 << tgt);
714 for i in 0..dim {
715 if (i & mask) == mask {
716 state[i] = -state[i];
717 }
718 }
719 }
720 _ => {}
722 }
723
724 Ok(())
725 }
726
727 fn simulate_quantum_sampling(
734 &mut self,
735 ideal_amplitudes: &[Array1<Complex64>],
736 ) -> Result<Vec<Vec<Vec<u8>>>> {
737 let mut all_samples = Vec::with_capacity(ideal_amplitudes.len());
738
739 for amplitudes in ideal_amplitudes {
740 let samples = self.sample_from_amplitudes(amplitudes)?;
741 all_samples.push(samples);
742 }
743
744 Ok(all_samples)
745 }
746
747 fn sample_from_amplitudes(&mut self, amplitudes: &Array1<Complex64>) -> Result<Vec<Vec<u8>>> {
750 let dim = amplitudes.len();
751 let n = self.num_qubits;
752
753 let mut probs: Vec<f64> = amplitudes.iter().map(|a| a.norm_sqr()).collect();
755
756 let total: f64 = probs.iter().sum();
758 if total <= 0.0 {
759 return Err(SimulatorError::InvalidInput(
760 "State vector has zero norm — cannot sample".to_string(),
761 ));
762 }
763 for p in &mut probs {
764 *p /= total;
765 }
766
767 let mut cdf = Vec::with_capacity(dim);
769 let mut running = 0.0_f64;
770 for p in &probs {
771 running += p;
772 cdf.push(running);
773 }
774
775 let mut samples = Vec::with_capacity(self.params.samples_per_circuit);
777 for _ in 0..self.params.samples_per_circuit {
778 let u: f64 = self.rng.random();
779 let outcome = cdf.partition_point(|&c| c < u).min(dim - 1);
781
782 let mut bitstring = Vec::with_capacity(n);
784 for q in (0..n).rev() {
785 bitstring.push(((outcome >> q) & 1) as u8);
786 }
787 samples.push(bitstring);
788 }
789
790 Ok(samples)
791 }
792
793 fn compute_linear_xeb(
795 &self,
796 ideal_amplitudes: &[Array1<Complex64>],
797 quantum_samples: &[Vec<Vec<u8>>],
798 ) -> Result<f64> {
799 let mut total_fidelity = 0.0;
800
801 for (amplitudes, samples) in ideal_amplitudes.iter().zip(quantum_samples.iter()) {
802 let circuit_fidelity = self.compute_circuit_linear_xeb(amplitudes, samples)?;
803 total_fidelity += circuit_fidelity;
804 }
805
806 Ok(total_fidelity / ideal_amplitudes.len() as f64)
807 }
808
809 fn compute_circuit_linear_xeb(
811 &self,
812 amplitudes: &Array1<Complex64>,
813 samples: &[Vec<u8>],
814 ) -> Result<f64> {
815 let dim = amplitudes.len();
816 let uniform_prob = 1.0 / dim as f64;
817
818 let mut sum_probs = 0.0;
819
820 for sample in samples {
821 let bitstring_index = self.bitstring_to_index(sample);
822 if bitstring_index < dim {
823 let prob = amplitudes[bitstring_index].norm_sqr();
824 sum_probs += prob;
825 }
826 }
827
828 let mean_prob = sum_probs / samples.len() as f64;
829 let linear_xeb = (mean_prob - uniform_prob) / uniform_prob;
830
831 Ok(linear_xeb)
832 }
833
834 fn bitstring_to_index(&self, bitstring: &[u8]) -> usize {
836 let mut index = 0;
837 for (i, &bit) in bitstring.iter().enumerate() {
838 if bit == 1 {
839 index |= 1 << (self.num_qubits - 1 - i);
840 }
841 }
842 index
843 }
844
845 fn analyze_porter_thomas(
847 &self,
848 ideal_amplitudes: &[Array1<Complex64>],
849 ) -> Result<PorterThomasResult> {
850 let mut all_probs = Vec::new();
852
853 for amplitudes in ideal_amplitudes {
854 for amplitude in amplitudes {
855 all_probs.push(amplitude.norm_sqr());
856 }
857 }
858
859 let mean = all_probs.iter().sum::<f64>() / all_probs.len() as f64;
861 let variance =
862 all_probs.iter().map(|p| (p - mean).powi(2)).sum::<f64>() / all_probs.len() as f64;
863
864 let expected_mean = 1.0 / f64::from(1 << self.num_qubits);
866 let expected_variance = expected_mean.powi(2);
867
868 let bins = self.params.pt_bins;
870 let mut observed = vec![0; bins];
871 let mut expected = vec![0.0; bins];
872
873 let max_prob = all_probs.iter().copied().fold(0.0f64, f64::max);
875 for &prob in &all_probs {
876 let bin = ((prob / max_prob) * (bins - 1) as f64) as usize;
877 observed[bin.min(bins - 1)] += 1;
878 }
879
880 let total_samples = all_probs.len() as f64;
882 for i in 0..bins {
883 let x = (i as f64 + 0.5) / bins as f64 * max_prob;
884 let n = 1 << self.num_qubits;
886 expected[i] = total_samples * f64::from(n) * (f64::from(-n) * x).exp() / bins as f64;
887 }
888
889 let mut chi_squared = 0.0;
891 let mut degrees_freedom: usize = 0;
892
893 for i in 0..bins {
894 if expected[i] > 5.0 {
895 chi_squared += (f64::from(observed[i]) - expected[i]).powi(2) / expected[i];
897 degrees_freedom += 1;
898 }
899 }
900
901 degrees_freedom = degrees_freedom.saturating_sub(1);
902
903 let p_value = self.chi_squared_p_value(chi_squared, degrees_freedom);
905
906 let ks_statistic = self.kolmogorov_smirnov_test(&all_probs, expected_mean);
908
909 Ok(PorterThomasResult {
910 chi_squared,
911 degrees_freedom,
912 p_value,
913 is_porter_thomas: p_value > self.params.significance_level,
914 ks_statistic,
915 mean,
916 variance,
917 })
918 }
919
920 fn compute_hog_score(
922 &self,
923 ideal_amplitudes: &[Array1<Complex64>],
924 quantum_samples: &[Vec<Vec<u8>>],
925 ) -> Result<f64> {
926 let mut total_heavy_fraction = 0.0;
927
928 for (amplitudes, samples) in ideal_amplitudes.iter().zip(quantum_samples.iter()) {
929 let heavy_fraction = self.compute_circuit_hog_score(amplitudes, samples)?;
930 total_heavy_fraction += heavy_fraction;
931 }
932
933 Ok(total_heavy_fraction / ideal_amplitudes.len() as f64)
934 }
935
936 fn compute_circuit_hog_score(
938 &self,
939 amplitudes: &Array1<Complex64>,
940 samples: &[Vec<u8>],
941 ) -> Result<f64> {
942 let mut probs: Vec<f64> = amplitudes
944 .iter()
945 .map(scirs2_core::Complex::norm_sqr)
946 .collect();
947 probs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
948 let median_prob = probs[probs.len() / 2];
949
950 let mut heavy_count = 0;
952
953 for sample in samples {
954 let index = self.bitstring_to_index(sample);
955 if index < amplitudes.len() {
956 let prob = amplitudes[index].norm_sqr();
957 if prob > median_prob {
958 heavy_count += 1;
959 }
960 }
961 }
962
963 Ok(f64::from(heavy_count) / samples.len() as f64)
964 }
965
966 fn compute_cross_entropy_difference(
968 &self,
969 ideal_amplitudes: &[Array1<Complex64>],
970 quantum_samples: &[Vec<Vec<u8>>],
971 ) -> Result<f64> {
972 let mut total_difference = 0.0;
973
974 for (amplitudes, samples) in ideal_amplitudes.iter().zip(quantum_samples.iter()) {
975 let difference = self.compute_circuit_cross_entropy(amplitudes, samples)?;
976 total_difference += difference;
977 }
978
979 Ok(total_difference / ideal_amplitudes.len() as f64)
980 }
981
982 fn compute_circuit_cross_entropy(
984 &self,
985 amplitudes: &Array1<Complex64>,
986 samples: &[Vec<u8>],
987 ) -> Result<f64> {
988 let dim = amplitudes.len();
989 let uniform_entropy = (dim as f64).ln();
990
991 let mut quantum_entropy = 0.0;
992 let mut sample_counts = HashMap::new();
993
994 for sample in samples {
996 *sample_counts.entry(sample.clone()).or_insert(0) += 1;
997 }
998
999 for count in sample_counts.values() {
1001 let prob = f64::from(*count) / samples.len() as f64;
1002 if prob > 0.0 {
1003 quantum_entropy -= prob * prob.ln();
1004 }
1005 }
1006
1007 Ok(uniform_entropy - quantum_entropy)
1008 }
1009
1010 const fn compute_statistical_confidence(
1012 &self,
1013 _ideal_amplitudes: &[Array1<Complex64>],
1014 _quantum_samples: &[Vec<Vec<u8>>],
1015 ) -> Result<f64> {
1016 Ok(0.95)
1018 }
1019
1020 fn estimate_quantum_time(&self) -> Result<f64> {
1022 let gates_per_circuit = self.params.circuit_depth * self.num_qubits;
1024 let total_gates = gates_per_circuit * self.params.num_circuits;
1025 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))
1029 }
1030
1031 const fn estimate_classical_memory(&self) -> usize {
1033 let state_size = (1 << self.num_qubits) * std::mem::size_of::<Complex64>();
1034 state_size * self.params.num_circuits
1035 }
1036
1037 fn chi_squared_p_value(&self, chi_squared: f64, degrees_freedom: usize) -> f64 {
1039 if degrees_freedom == 0 {
1041 return 1.0;
1042 }
1043
1044 let expected = degrees_freedom as f64;
1045 if chi_squared < expected {
1046 0.95 } else if chi_squared < 2.0 * expected {
1048 0.5 } else {
1050 0.01 }
1052 }
1053
1054 fn kolmogorov_smirnov_test(&self, data: &[f64], expected_mean: f64) -> f64 {
1056 let n = data.len() as f64;
1058 let mut sorted_data = data.to_vec();
1059 sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1060
1061 let mut max_diff: f64 = 0.0;
1062
1063 for (i, &value) in sorted_data.iter().enumerate() {
1064 let empirical_cdf = (i + 1) as f64 / n;
1065 let theoretical_cdf = 1.0 - (-value / expected_mean).exp(); let diff = (empirical_cdf - theoretical_cdf).abs();
1067 max_diff = max_diff.max(diff);
1068 }
1069
1070 max_diff
1071 }
1072}
1073
1074#[derive(Debug, Clone)]
1076pub struct RandomCircuit {
1077 pub num_qubits: usize,
1078 pub layers: Vec<CircuitLayer>,
1079 pub measurement_pattern: Vec<usize>,
1080}
1081
1082#[derive(Debug, Clone)]
1084pub struct CircuitLayer {
1085 pub gates: Vec<QuantumGate>,
1086}
1087
1088#[derive(Debug, Clone)]
1090pub struct QuantumGate {
1091 pub gate_type: String,
1092 pub qubits: Vec<usize>,
1093 pub parameters: Vec<f64>,
1094}
1095
1096pub fn benchmark_quantum_supremacy(num_qubits: usize) -> Result<CrossEntropyResult> {
1098 let params = VerificationParams {
1099 num_circuits: 10,
1100 samples_per_circuit: 100,
1101 circuit_depth: 10,
1102 ..Default::default()
1103 };
1104
1105 let mut verifier = QuantumSupremacyVerifier::new(num_qubits, params)?;
1106 verifier.verify_quantum_supremacy()
1107}
1108
1109pub fn verify_supremacy_claim(
1111 num_qubits: usize,
1112 circuit_depth: usize,
1113 experimental_data: &[Vec<u8>],
1114) -> Result<CrossEntropyResult> {
1115 let params = VerificationParams {
1116 num_circuits: 1,
1117 samples_per_circuit: experimental_data.len(),
1118 circuit_depth,
1119 ..Default::default()
1120 };
1121
1122 let mut verifier = QuantumSupremacyVerifier::new(num_qubits, params)?;
1123
1124 Ok(CrossEntropyResult {
1127 linear_xeb_fidelity: 0.0,
1128 cross_entropy_difference: 0.0,
1129 num_samples: experimental_data.len(),
1130 confidence: 0.0,
1131 porter_thomas: PorterThomasResult {
1132 chi_squared: 0.0,
1133 degrees_freedom: 0,
1134 p_value: 0.0,
1135 is_porter_thomas: false,
1136 ks_statistic: 0.0,
1137 mean: 0.0,
1138 variance: 0.0,
1139 },
1140 hog_score: 0.0,
1141 cost_comparison: CostComparison {
1142 classical_time: 0.0,
1143 quantum_time: 0.0,
1144 classical_memory: 0,
1145 speedup_factor: 0.0,
1146 operation_count: 0,
1147 },
1148 })
1149}
1150
1151#[cfg(test)]
1152mod tests {
1153 use super::*;
1154
1155 #[test]
1156 fn test_verifier_creation() {
1157 let params = VerificationParams::default();
1158 let verifier = QuantumSupremacyVerifier::new(10, params);
1159 assert!(verifier.is_ok());
1160 }
1161
1162 #[test]
1163 fn test_linear_xeb_calculation() {
1164 let mut verifier = QuantumSupremacyVerifier::new(3, VerificationParams::default())
1165 .expect("Failed to create verifier");
1166
1167 let amplitudes = Array1::from_vec(vec![
1169 Complex64::new(0.5, 0.0),
1170 Complex64::new(0.5, 0.0),
1171 Complex64::new(0.5, 0.0),
1172 Complex64::new(0.5, 0.0),
1173 Complex64::new(0.0, 0.0),
1174 Complex64::new(0.0, 0.0),
1175 Complex64::new(0.0, 0.0),
1176 Complex64::new(0.0, 0.0),
1177 ]);
1178
1179 let samples = vec![vec![0, 0, 0], vec![0, 0, 1], vec![0, 1, 0], vec![0, 1, 1]];
1180
1181 let xeb = verifier
1182 .compute_circuit_linear_xeb(&litudes, &samples)
1183 .expect("Failed to compute linear XEB");
1184 assert!(xeb >= 0.0);
1185 }
1186
1187 #[test]
1188 fn test_bitstring_conversion() {
1189 let verifier = QuantumSupremacyVerifier::new(3, VerificationParams::default())
1190 .expect("Failed to create verifier");
1191
1192 assert_eq!(verifier.bitstring_to_index(&[0, 0, 0]), 0);
1193 assert_eq!(verifier.bitstring_to_index(&[0, 0, 1]), 1);
1194 assert_eq!(verifier.bitstring_to_index(&[1, 1, 1]), 7);
1195 }
1196
1197 #[test]
1198 fn test_porter_thomas_analysis() {
1199 let verifier = QuantumSupremacyVerifier::new(2, VerificationParams::default())
1200 .expect("Failed to create verifier");
1201
1202 let amplitudes = vec![Array1::from_vec(vec![
1204 Complex64::new(0.5, 0.0),
1205 Complex64::new(0.5, 0.0),
1206 Complex64::new(0.5, 0.0),
1207 Complex64::new(0.5, 0.0),
1208 ])];
1209
1210 let result = verifier
1211 .analyze_porter_thomas(&litudes)
1212 .expect("Failed to analyze Porter-Thomas distribution");
1213 assert!(result.chi_squared >= 0.0);
1214 assert!(result.p_value >= 0.0 && result.p_value <= 1.0);
1215 }
1216}