1use super::functions::*;
6use std::f64::consts::PI;
7
8#[derive(Debug, Clone)]
10pub enum GateOp {
11 Single { gate: Gate2x2, qubit: usize },
13 Cnot { control: usize, target: usize },
15}
16#[allow(dead_code)]
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum ChannelType {
19 Depolarizing,
20 AmplitudeDamping,
21 PhaseDamping,
22 BitFlip,
23 PhaseFlip,
24 Erasure,
25 Unitary,
26 Identity,
27}
28#[allow(dead_code)]
30#[derive(Debug, Clone)]
31pub struct CliffordGroup {
32 pub num_qubits: usize,
33}
34impl CliffordGroup {
35 #[allow(dead_code)]
36 pub fn new(n: usize) -> Self {
37 Self { num_qubits: n }
38 }
39 #[allow(dead_code)]
40 pub fn normalizes_pauli_group(&self) -> bool {
41 true
42 }
43 #[allow(dead_code)]
44 pub fn is_efficiently_simulable(&self) -> bool {
45 true
46 }
47 #[allow(dead_code)]
48 pub fn gottesman_knill_theorem(&self) -> String {
49 format!(
50 "Clifford circuits on {} qubits can be simulated in poly time (Gottesman-Knill)",
51 self.num_qubits
52 )
53 }
54 #[allow(dead_code)]
55 pub fn universal_with_t_gate(&self) -> bool {
56 true
57 }
58}
59#[allow(dead_code)]
61#[derive(Debug, Clone)]
62pub struct QaoaConfig {
63 pub problem_name: String,
64 pub p_layers: usize,
65}
66impl QaoaConfig {
67 #[allow(dead_code)]
68 pub fn new(problem: &str, p: usize) -> Self {
69 Self {
70 problem_name: problem.to_string(),
71 p_layers: p,
72 }
73 }
74 #[allow(dead_code)]
75 pub fn approximation_ratio_lower_bound(&self) -> f64 {
76 let neg_layers = -(self.p_layers as i64) as f64;
77 0.5 + 0.1924 * (1.0 - (neg_layers * 0.5).exp())
78 }
79 #[allow(dead_code)]
80 pub fn num_circuit_parameters(&self) -> usize {
81 2 * self.p_layers
82 }
83}
84#[allow(dead_code)]
86#[derive(Debug, Clone)]
87pub struct QuantumErrorCode {
88 pub name: String,
89 pub n: usize,
90 pub k: usize,
91 pub d: usize,
92}
93impl QuantumErrorCode {
94 #[allow(dead_code)]
95 pub fn steane_7() -> Self {
96 Self {
97 name: "Steane [[7,1,3]]".to_string(),
98 n: 7,
99 k: 1,
100 d: 3,
101 }
102 }
103 #[allow(dead_code)]
104 pub fn shor_9() -> Self {
105 Self {
106 name: "Shor [[9,1,3]]".to_string(),
107 n: 9,
108 k: 1,
109 d: 3,
110 }
111 }
112 #[allow(dead_code)]
113 pub fn surface_code(d: usize) -> Self {
114 let n = 2 * d * d - 2 * d + 1;
115 Self {
116 name: format!("Surface [[{n},1,{d}]]"),
117 n,
118 k: 1,
119 d,
120 }
121 }
122 #[allow(dead_code)]
123 pub fn encoding_rate(&self) -> f64 {
124 self.k as f64 / self.n as f64
125 }
126 #[allow(dead_code)]
127 pub fn corrects_errors_up_to(&self) -> usize {
128 (self.d - 1) / 2
129 }
130 #[allow(dead_code)]
131 pub fn qec_bound_satisfied(&self) -> bool {
132 true
133 }
134}
135#[allow(dead_code)]
137#[derive(Debug, Clone)]
138pub struct QuantumGate {
139 pub name: String,
140 pub num_qubits: usize,
141 pub is_unitary: bool,
142 pub is_clifford: bool,
143}
144impl QuantumGate {
145 #[allow(dead_code)]
146 pub fn hadamard() -> Self {
147 Self {
148 name: "H".to_string(),
149 num_qubits: 1,
150 is_unitary: true,
151 is_clifford: true,
152 }
153 }
154 #[allow(dead_code)]
155 pub fn pauli_x() -> Self {
156 Self {
157 name: "X".to_string(),
158 num_qubits: 1,
159 is_unitary: true,
160 is_clifford: true,
161 }
162 }
163 #[allow(dead_code)]
164 pub fn pauli_y() -> Self {
165 Self {
166 name: "Y".to_string(),
167 num_qubits: 1,
168 is_unitary: true,
169 is_clifford: true,
170 }
171 }
172 #[allow(dead_code)]
173 pub fn pauli_z() -> Self {
174 Self {
175 name: "Z".to_string(),
176 num_qubits: 1,
177 is_unitary: true,
178 is_clifford: true,
179 }
180 }
181 #[allow(dead_code)]
182 pub fn phase(theta_desc: &str) -> Self {
183 Self {
184 name: format!("P({theta_desc})"),
185 num_qubits: 1,
186 is_unitary: true,
187 is_clifford: false,
188 }
189 }
190 #[allow(dead_code)]
191 pub fn t_gate() -> Self {
192 Self {
193 name: "T".to_string(),
194 num_qubits: 1,
195 is_unitary: true,
196 is_clifford: false,
197 }
198 }
199 #[allow(dead_code)]
200 pub fn cnot() -> Self {
201 Self {
202 name: "CNOT".to_string(),
203 num_qubits: 2,
204 is_unitary: true,
205 is_clifford: true,
206 }
207 }
208 #[allow(dead_code)]
209 pub fn toffoli() -> Self {
210 Self {
211 name: "Toffoli".to_string(),
212 num_qubits: 3,
213 is_unitary: true,
214 is_clifford: false,
215 }
216 }
217 #[allow(dead_code)]
218 pub fn swap() -> Self {
219 Self {
220 name: "SWAP".to_string(),
221 num_qubits: 2,
222 is_unitary: true,
223 is_clifford: true,
224 }
225 }
226 #[allow(dead_code)]
227 pub fn matrix_size(&self) -> usize {
228 1usize << self.num_qubits
229 }
230}
231#[derive(Debug, Clone, Copy)]
233pub struct Complex {
234 pub re: f64,
235 pub im: f64,
236}
237impl Complex {
238 pub fn new(re: f64, im: f64) -> Self {
239 Complex { re, im }
240 }
241 pub fn zero() -> Self {
242 Complex { re: 0.0, im: 0.0 }
243 }
244 pub fn one() -> Self {
245 Complex { re: 1.0, im: 0.0 }
246 }
247 pub fn i() -> Self {
248 Complex { re: 0.0, im: 1.0 }
249 }
250 pub fn abs(&self) -> f64 {
252 (self.re * self.re + self.im * self.im).sqrt()
253 }
254 pub fn abs_sq(&self) -> f64 {
256 self.re * self.re + self.im * self.im
257 }
258 pub fn conj(&self) -> Self {
260 Complex {
261 re: self.re,
262 im: -self.im,
263 }
264 }
265 pub fn add(&self, other: &Self) -> Self {
266 Complex {
267 re: self.re + other.re,
268 im: self.im + other.im,
269 }
270 }
271 pub fn mul(&self, other: &Self) -> Self {
272 Complex {
273 re: self.re * other.re - self.im * other.im,
274 im: self.re * other.im + self.im * other.re,
275 }
276 }
277 pub fn scale(&self, s: f64) -> Self {
278 Complex {
279 re: self.re * s,
280 im: self.im * s,
281 }
282 }
283 pub fn exp(theta: f64) -> Self {
285 Complex {
286 re: theta.cos(),
287 im: theta.sin(),
288 }
289 }
290}
291#[derive(Debug)]
296pub struct QuantumRegister {
297 sv: QuantumStatevector,
298}
299impl QuantumRegister {
300 pub fn new(n_qubits: usize) -> Self {
302 QuantumRegister {
303 sv: QuantumStatevector::new(n_qubits),
304 }
305 }
306 pub fn n_qubits(&self) -> usize {
308 self.sv.n_qubits
309 }
310 pub fn size(&self) -> usize {
312 self.sv.amplitudes.len()
313 }
314 pub fn amplitude(&self, i: usize) -> Complex {
316 self.sv.amplitude(i)
317 }
318 pub fn prob(&self, i: usize) -> f64 {
320 self.sv.prob(i)
321 }
322 pub fn apply_gate(&mut self, gate: &Gate2x2, qubit: usize) {
324 self.sv.apply_single_qubit(gate, qubit);
325 }
326 pub fn apply_cnot(&mut self, control: usize, target: usize) {
328 self.sv.apply_cnot(control, target);
329 }
330 pub fn prepare_uniform_superposition(&mut self) {
332 let h = Gate2x2::hadamard();
333 for q in 0..self.sv.n_qubits {
334 self.sv.apply_single_qubit(&h, q);
335 }
336 }
337 pub fn measure_qubit(&self, qubit: usize, seed: u64) -> u8 {
339 self.sv.measure_qubit(qubit, seed)
340 }
341 pub fn is_normalized(&self) -> bool {
343 self.sv.is_normalized()
344 }
345 pub fn amplitudes(&self) -> &[Complex] {
347 &self.sv.amplitudes
348 }
349}
350#[allow(dead_code)]
352#[derive(Debug, Clone)]
353pub struct QuantumRegisterData {
354 pub num_qubits: usize,
355 pub label: String,
356}
357impl QuantumRegisterData {
358 #[allow(dead_code)]
359 pub fn new(n: usize, label: &str) -> Self {
360 Self {
361 num_qubits: n,
362 label: label.to_string(),
363 }
364 }
365 #[allow(dead_code)]
366 pub fn hilbert_space_dim(&self) -> usize {
367 1usize << self.num_qubits
368 }
369 #[allow(dead_code)]
370 pub fn computational_basis_size(&self) -> usize {
371 self.hilbert_space_dim()
372 }
373}
374pub struct QFTSimulator {
376 pub n_qubits: usize,
377}
378impl QFTSimulator {
379 pub fn new(n_qubits: usize) -> Self {
381 QFTSimulator { n_qubits }
382 }
383 pub fn apply(&self, reg: &mut QuantumRegister) {
388 let n = self.n_qubits;
389 for j in 0..n {
390 reg.apply_gate(&Gate2x2::hadamard(), j);
391 for k in 2..=(n - j) {
392 let theta = 2.0 * PI / (1u64 << k) as f64;
393 let cphase = Gate2x2::phase(theta);
394 let control = j + k - 1;
395 let target = j;
396 let size = reg.size();
397 for i in 0..size {
398 if (i >> control) & 1 == 1 && (i >> target) & 1 == 1 {
399 reg.sv.amplitudes[i] = reg.sv.amplitudes[i].mul(&cphase.matrix[1][1]);
400 }
401 }
402 }
403 }
404 Self::bit_reverse(reg);
405 }
406 pub fn apply_inverse(&self, reg: &mut QuantumRegister) {
408 let n = self.n_qubits;
409 Self::bit_reverse(reg);
410 for j in (0..n).rev() {
411 for k in (2..=(n - j)).rev() {
412 let theta = -2.0 * PI / (1u64 << k) as f64;
413 let cphase = Gate2x2::phase(theta);
414 let control = j + k - 1;
415 let target = j;
416 let size = reg.size();
417 for i in 0..size {
418 if (i >> control) & 1 == 1 && (i >> target) & 1 == 1 {
419 reg.sv.amplitudes[i] = reg.sv.amplitudes[i].mul(&cphase.matrix[1][1]);
420 }
421 }
422 }
423 reg.apply_gate(&Gate2x2::hadamard(), j);
424 }
425 }
426 pub fn transform(&self, input: &[Complex]) -> Vec<Complex> {
429 let n = input.len();
430 let inv_sqrt_n = 1.0 / (n as f64).sqrt();
431 (0..n)
432 .map(|k| {
433 (0..n)
434 .fold(Complex::zero(), |acc, j| {
435 let angle = 2.0 * PI * (j * k) as f64 / n as f64;
436 acc.add(&input[j].mul(&Complex::exp(angle)))
437 })
438 .scale(inv_sqrt_n)
439 })
440 .collect()
441 }
442 fn bit_reverse(reg: &mut QuantumRegister) {
444 let n = reg.n_qubits();
445 let size = reg.size();
446 for i in 0..size {
447 let j = Self::reverse_bits(i, n);
448 if j > i {
449 reg.sv.amplitudes.swap(i, j);
450 }
451 }
452 }
453 fn reverse_bits(mut x: usize, n: usize) -> usize {
454 let mut result = 0;
455 for _ in 0..n {
456 result = (result << 1) | (x & 1);
457 x >>= 1;
458 }
459 result
460 }
461}
462#[derive(Debug, Clone)]
464pub struct Qubit {
465 pub alpha: Complex,
466 pub beta: Complex,
467}
468impl Qubit {
469 pub fn zero() -> Self {
471 Qubit {
472 alpha: Complex::one(),
473 beta: Complex::zero(),
474 }
475 }
476 pub fn one() -> Self {
478 Qubit {
479 alpha: Complex::zero(),
480 beta: Complex::one(),
481 }
482 }
483 pub fn plus() -> Self {
485 let v = 1.0 / 2.0f64.sqrt();
486 Qubit {
487 alpha: Complex::new(v, 0.0),
488 beta: Complex::new(v, 0.0),
489 }
490 }
491 pub fn minus() -> Self {
493 let v = 1.0 / 2.0f64.sqrt();
494 Qubit {
495 alpha: Complex::new(v, 0.0),
496 beta: Complex::new(-v, 0.0),
497 }
498 }
499 pub fn prob_zero(&self) -> f64 {
501 self.alpha.abs_sq()
502 }
503 pub fn prob_one(&self) -> f64 {
505 self.beta.abs_sq()
506 }
507 pub fn is_normalized(&self) -> bool {
509 (self.prob_zero() + self.prob_one() - 1.0).abs() < 1e-10
510 }
511 pub fn measure(&self, seed: u64) -> (u8, Qubit) {
516 let t = (seed
517 .wrapping_mul(6364136223846793005)
518 .wrapping_add(1442695040888963407)
519 >> 33) as f64
520 / u32::MAX as f64;
521 let p0 = self.prob_zero();
522 if t < p0 {
523 (0, Qubit::zero())
524 } else {
525 (1, Qubit::one())
526 }
527 }
528}
529#[derive(Debug)]
531pub struct QuantumStatevector {
532 pub n_qubits: usize,
533 pub amplitudes: Vec<Complex>,
535}
536impl QuantumStatevector {
537 pub fn new(n_qubits: usize) -> Self {
539 let size = 1usize << n_qubits;
540 let mut amplitudes = vec![Complex::zero(); size];
541 amplitudes[0] = Complex::one();
542 QuantumStatevector {
543 n_qubits,
544 amplitudes,
545 }
546 }
547 pub fn amplitude(&self, basis_state: usize) -> Complex {
549 self.amplitudes[basis_state]
550 }
551 pub fn prob(&self, basis_state: usize) -> f64 {
553 self.amplitudes[basis_state].abs_sq()
554 }
555 pub fn apply_single_qubit(&mut self, gate: &Gate2x2, qubit_idx: usize) {
557 let size = self.amplitudes.len();
558 let step = 1usize << qubit_idx;
559 let mut block_start = 0;
560 while block_start < size {
561 for j in block_start..(block_start + step) {
562 let j1 = j + step;
563 let a0 = self.amplitudes[j];
564 let a1 = self.amplitudes[j1];
565 self.amplitudes[j] = gate.matrix[0][0].mul(&a0).add(&gate.matrix[0][1].mul(&a1));
566 self.amplitudes[j1] = gate.matrix[1][0].mul(&a0).add(&gate.matrix[1][1].mul(&a1));
567 }
568 block_start += step * 2;
569 }
570 }
571 pub fn apply_cnot(&mut self, control: usize, target: usize) {
573 let size = self.amplitudes.len();
574 for i in 0..size {
575 if (i >> control) & 1 == 1 {
576 let j = i ^ (1 << target);
577 if j > i {
578 self.amplitudes.swap(i, j);
579 }
580 }
581 }
582 }
583 pub fn measure_qubit(&self, qubit_idx: usize, seed: u64) -> u8 {
585 let prob1: f64 = self
586 .amplitudes
587 .iter()
588 .enumerate()
589 .filter(|(i, _)| (i >> qubit_idx) & 1 == 1)
590 .map(|(_, a)| a.abs_sq())
591 .sum();
592 let t = (seed
593 .wrapping_mul(6364136223846793005)
594 .wrapping_add(1442695040888963407)
595 >> 33) as f64
596 / u32::MAX as f64;
597 if t < prob1 {
598 1
599 } else {
600 0
601 }
602 }
603 pub fn is_normalized(&self) -> bool {
605 let total: f64 = self.amplitudes.iter().map(|a| a.abs_sq()).sum();
606 (total - 1.0).abs() < 1e-9
607 }
608 pub fn n_amplitudes(&self) -> usize {
610 self.amplitudes.len()
611 }
612}
613#[derive(Debug, Clone, Copy, PartialEq, Eq)]
615pub enum Pauli {
616 I,
617 X,
618 Y,
619 Z,
620}
621impl Pauli {
622 pub fn to_gate(self) -> Gate2x2 {
624 match self {
625 Pauli::I => Gate2x2 {
626 name: "I".to_string(),
627 matrix: [
628 [Complex::one(), Complex::zero()],
629 [Complex::zero(), Complex::one()],
630 ],
631 },
632 Pauli::X => Gate2x2::pauli_x(),
633 Pauli::Y => Gate2x2::pauli_y(),
634 Pauli::Z => Gate2x2::pauli_z(),
635 }
636 }
637 pub fn mul(self, other: Pauli) -> Pauli {
639 match (self, other) {
640 (Pauli::I, p) | (p, Pauli::I) => p,
641 (Pauli::X, Pauli::X) => Pauli::I,
642 (Pauli::Y, Pauli::Y) => Pauli::I,
643 (Pauli::Z, Pauli::Z) => Pauli::I,
644 (Pauli::X, Pauli::Y) => Pauli::Z,
645 (Pauli::Y, Pauli::X) => Pauli::Z,
646 (Pauli::Y, Pauli::Z) => Pauli::X,
647 (Pauli::Z, Pauli::Y) => Pauli::X,
648 (Pauli::Z, Pauli::X) => Pauli::Y,
649 (Pauli::X, Pauli::Z) => Pauli::Y,
650 }
651 }
652 pub fn commutes_with(self, other: Pauli) -> bool {
654 matches!(
655 (self, other),
656 (Pauli::I, _)
657 | (_, Pauli::I)
658 | (Pauli::X, Pauli::X)
659 | (Pauli::Y, Pauli::Y)
660 | (Pauli::Z, Pauli::Z)
661 )
662 }
663}
664#[allow(dead_code)]
666#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
667pub enum QuantumComplexityClass {
668 BPP,
669 BQP,
670 QMA,
671 QMAM,
672 PSharpP,
673 PSPACE,
674 EXP,
675}
676impl QuantumComplexityClass {
677 #[allow(dead_code)]
678 pub fn name(&self) -> &'static str {
679 match self {
680 Self::BPP => "BPP",
681 Self::BQP => "BQP",
682 Self::QMA => "QMA",
683 Self::QMAM => "QMAM",
684 Self::PSharpP => "P#P",
685 Self::PSPACE => "PSPACE",
686 Self::EXP => "EXP",
687 }
688 }
689 #[allow(dead_code)]
690 pub fn contains_bpp(&self) -> bool {
691 !matches!(self, Self::BPP)
692 }
693 #[allow(dead_code)]
694 pub fn is_believed_strictly_larger_than_bqp(&self) -> bool {
695 matches!(
696 self,
697 Self::QMA | Self::QMAM | Self::PSharpP | Self::PSPACE | Self::EXP
698 )
699 }
700}
701#[allow(dead_code)]
703#[derive(Debug, Clone)]
704pub struct QuantumWalk {
705 pub graph_name: String,
706 pub walk_type: QuantumWalkType,
707 pub num_steps: usize,
708}
709impl QuantumWalk {
710 #[allow(dead_code)]
711 pub fn new(graph: &str, wt: QuantumWalkType, steps: usize) -> Self {
712 Self {
713 graph_name: graph.to_string(),
714 walk_type: wt,
715 num_steps: steps,
716 }
717 }
718 #[allow(dead_code)]
719 pub fn speedup_over_classical(&self) -> f64 {
720 match self.walk_type {
721 QuantumWalkType::ContinuousTime => 2.0,
722 QuantumWalkType::DiscreteTimeCoin => 2.0,
723 QuantumWalkType::Scattering => 1.5,
724 }
725 }
726 #[allow(dead_code)]
727 pub fn element_distinctness_uses_walk(&self) -> bool {
728 self.graph_name.contains("Johnson")
729 }
730}
731#[allow(dead_code)]
733#[derive(Debug, Clone)]
734pub struct TeleportationProtocol {
735 pub input_qubits: usize,
736 pub classical_bits_needed: usize,
737}
738impl TeleportationProtocol {
739 #[allow(dead_code)]
740 pub fn new(n: usize) -> Self {
741 Self {
742 input_qubits: n,
743 classical_bits_needed: 2 * n,
744 }
745 }
746 #[allow(dead_code)]
747 pub fn requires_entanglement(&self) -> bool {
748 true
749 }
750 #[allow(dead_code)]
751 pub fn no_faster_than_light(&self) -> bool {
752 self.classical_bits_needed > 0
753 }
754 #[allow(dead_code)]
755 pub fn fidelity_with_perfect_channel(&self) -> f64 {
756 1.0
757 }
758}
759#[allow(dead_code)]
761#[derive(Debug, Clone)]
762pub struct PauliGroup {
763 pub num_qubits: usize,
764}
765impl PauliGroup {
766 #[allow(dead_code)]
767 pub fn new(n: usize) -> Self {
768 Self { num_qubits: n }
769 }
770 #[allow(dead_code)]
771 pub fn size(&self) -> usize {
772 4usize.pow(self.num_qubits as u32 + 1)
773 }
774 #[allow(dead_code)]
775 pub fn is_abelian(&self) -> bool {
776 self.num_qubits == 0
777 }
778 #[allow(dead_code)]
779 pub fn stabilizer_group_description(&self) -> String {
780 format!(
781 "Abelian subgroup of P_{} stabilizing a code space",
782 self.num_qubits
783 )
784 }
785}
786#[derive(Debug, Clone)]
790pub struct GroverOracle {
791 pub target: usize,
793}
794impl GroverOracle {
795 pub fn new(target: usize) -> Self {
797 GroverOracle { target }
798 }
799 pub fn apply(&self, reg: &mut QuantumRegister) {
801 if self.target < reg.size() {
802 reg.sv.amplitudes[self.target] = reg.sv.amplitudes[self.target].scale(-1.0);
803 }
804 }
805 pub fn diffusion(reg: &mut QuantumRegister) {
808 let n = reg.size();
809 let mean = reg
810 .sv
811 .amplitudes
812 .iter()
813 .fold(Complex::zero(), |acc, a| acc.add(a))
814 .scale(1.0 / n as f64);
815 for amp in reg.sv.amplitudes.iter_mut() {
816 let two_mean = mean.scale(2.0);
817 *amp = Complex {
818 re: two_mean.re - amp.re,
819 im: two_mean.im - amp.im,
820 };
821 }
822 }
823 pub fn run_grover(&self, n_qubits: usize, iterations: u32) -> QuantumRegister {
825 let mut reg = QuantumRegister::new(n_qubits);
826 reg.prepare_uniform_superposition();
827 for _ in 0..iterations {
828 self.apply(&mut reg);
829 Self::diffusion(&mut reg);
830 }
831 reg
832 }
833}
834#[allow(dead_code)]
836#[derive(Debug, Clone)]
837pub struct QuantumCapacities {
838 pub channel_name: String,
839 pub classical_capacity: f64,
840 pub quantum_capacity: f64,
841 pub entanglement_assisted_capacity: f64,
842 pub private_capacity: f64,
843}
844impl QuantumCapacities {
845 #[allow(dead_code)]
846 pub fn for_qubit_channel(name: &str, cc: f64, qc: f64, ea: f64, pc: f64) -> Self {
847 Self {
848 channel_name: name.to_string(),
849 classical_capacity: cc,
850 quantum_capacity: qc,
851 entanglement_assisted_capacity: ea,
852 private_capacity: pc,
853 }
854 }
855 #[allow(dead_code)]
856 pub fn quantum_is_at_most_private(&self) -> bool {
857 self.quantum_capacity <= self.private_capacity + 1e-10
858 }
859 #[allow(dead_code)]
860 pub fn superdense_coding_factor(&self) -> f64 {
861 if self.classical_capacity > 0.0 {
862 self.entanglement_assisted_capacity / self.classical_capacity
863 } else {
864 0.0
865 }
866 }
867}
868#[derive(Debug, Clone)]
871pub struct PauliString {
872 pub sign: i8,
874 pub paulis: Vec<Pauli>,
876}
877impl PauliString {
878 pub fn new(sign: i8, paulis: Vec<Pauli>) -> Self {
880 PauliString { sign, paulis }
881 }
882 pub fn identity(n: usize) -> Self {
884 PauliString {
885 sign: 1,
886 paulis: vec![Pauli::I; n],
887 }
888 }
889 pub fn n_qubits(&self) -> usize {
891 self.paulis.len()
892 }
893 pub fn mul(&self, other: &PauliString) -> PauliString {
895 assert_eq!(self.paulis.len(), other.paulis.len());
896 let paulis: Vec<Pauli> = self
897 .paulis
898 .iter()
899 .zip(other.paulis.iter())
900 .map(|(&a, &b)| a.mul(b))
901 .collect();
902 PauliString {
903 sign: self.sign * other.sign,
904 paulis,
905 }
906 }
907 pub fn commutes_with(&self, other: &PauliString) -> bool {
912 let anti: usize = self
913 .paulis
914 .iter()
915 .zip(other.paulis.iter())
916 .filter(|(&a, &b)| !a.commutes_with(b))
917 .count();
918 anti % 2 == 0
919 }
920}
921#[allow(dead_code)]
923#[derive(Debug, Clone)]
924pub struct VqeConfig {
925 pub hamiltonian_name: String,
926 pub ansatz_name: String,
927 pub num_layers: usize,
928 pub num_qubits: usize,
929}
930impl VqeConfig {
931 #[allow(dead_code)]
932 pub fn new(ham: &str, ansatz: &str, layers: usize, qubits: usize) -> Self {
933 Self {
934 hamiltonian_name: ham.to_string(),
935 ansatz_name: ansatz.to_string(),
936 num_layers: layers,
937 num_qubits: qubits,
938 }
939 }
940 #[allow(dead_code)]
941 pub fn num_parameters(&self) -> usize {
942 self.num_layers * self.num_qubits * 2
943 }
944 #[allow(dead_code)]
945 pub fn is_hybrid_classical_quantum(&self) -> bool {
946 true
947 }
948 #[allow(dead_code)]
949 pub fn variational_principle(&self) -> String {
950 format!(
951 "E(theta) = <psi(theta)|H|psi(theta)> >= E_ground for {}",
952 self.hamiltonian_name
953 )
954 }
955}
956#[derive(Debug, Clone)]
958pub struct Gate2x2 {
959 pub matrix: [[Complex; 2]; 2],
960 pub name: String,
961}
962impl Gate2x2 {
963 pub fn pauli_x() -> Self {
965 Gate2x2 {
966 name: "X".to_string(),
967 matrix: [
968 [Complex::zero(), Complex::one()],
969 [Complex::one(), Complex::zero()],
970 ],
971 }
972 }
973 pub fn pauli_y() -> Self {
975 Gate2x2 {
976 name: "Y".to_string(),
977 matrix: [
978 [Complex::zero(), Complex::new(0.0, -1.0)],
979 [Complex::new(0.0, 1.0), Complex::zero()],
980 ],
981 }
982 }
983 pub fn pauli_z() -> Self {
985 Gate2x2 {
986 name: "Z".to_string(),
987 matrix: [
988 [Complex::one(), Complex::zero()],
989 [Complex::zero(), Complex::new(-1.0, 0.0)],
990 ],
991 }
992 }
993 pub fn hadamard() -> Self {
995 let v = Complex::new(1.0 / 2.0f64.sqrt(), 0.0);
996 let neg_v = Complex::new(-1.0 / 2.0f64.sqrt(), 0.0);
997 Gate2x2 {
998 name: "H".to_string(),
999 matrix: [[v, v], [v, neg_v]],
1000 }
1001 }
1002 pub fn phase(theta: f64) -> Self {
1004 Gate2x2 {
1005 name: format!("P({theta:.4})"),
1006 matrix: [
1007 [Complex::one(), Complex::zero()],
1008 [Complex::zero(), Complex::exp(theta)],
1009 ],
1010 }
1011 }
1012 pub fn t_gate() -> Self {
1014 let mut g = Self::phase(PI / 4.0);
1015 g.name = "T".to_string();
1016 g
1017 }
1018 pub fn s_gate() -> Self {
1020 let mut g = Self::phase(PI / 2.0);
1021 g.name = "S".to_string();
1022 g
1023 }
1024 pub fn apply(&self, qubit: &Qubit) -> Qubit {
1026 let a = self.matrix[0][0]
1027 .mul(&qubit.alpha)
1028 .add(&self.matrix[0][1].mul(&qubit.beta));
1029 let b = self.matrix[1][0]
1030 .mul(&qubit.alpha)
1031 .add(&self.matrix[1][1].mul(&qubit.beta));
1032 Qubit { alpha: a, beta: b }
1033 }
1034 pub fn compose(&self, other: &Gate2x2) -> Gate2x2 {
1036 let mut m = [[Complex::zero(); 2]; 2];
1037 for i in 0..2 {
1038 for j in 0..2 {
1039 for k in 0..2 {
1040 m[i][j] = m[i][j].add(&self.matrix[i][k].mul(&other.matrix[k][j]));
1041 }
1042 }
1043 }
1044 Gate2x2 {
1045 name: format!("{}·{}", self.name, other.name),
1046 matrix: m,
1047 }
1048 }
1049 pub fn is_unitary(&self) -> bool {
1051 let tol = 1e-9;
1052 for i in 0..2 {
1053 for j in 0..2 {
1054 let mut sum = Complex::zero();
1055 for k in 0..2 {
1056 sum = sum.add(&self.matrix[k][i].conj().mul(&self.matrix[k][j]));
1057 }
1058 let expected_re = if i == j { 1.0 } else { 0.0 };
1059 if (sum.re - expected_re).abs() > tol || sum.im.abs() > tol {
1060 return false;
1061 }
1062 }
1063 }
1064 true
1065 }
1066}
1067#[allow(dead_code)]
1069#[derive(Debug, Clone)]
1070pub struct QuantumCircuitData {
1071 pub num_qubits: usize,
1072 pub gates: Vec<(QuantumGate, Vec<usize>)>,
1073 pub depth: usize,
1074}
1075impl QuantumCircuitData {
1076 #[allow(dead_code)]
1077 pub fn new(n: usize) -> Self {
1078 Self {
1079 num_qubits: n,
1080 gates: Vec::new(),
1081 depth: 0,
1082 }
1083 }
1084 #[allow(dead_code)]
1085 pub fn apply(&mut self, gate: QuantumGate, qubits: Vec<usize>) {
1086 self.depth += 1;
1087 self.gates.push((gate, qubits));
1088 }
1089 #[allow(dead_code)]
1090 pub fn gate_count(&self) -> usize {
1091 self.gates.len()
1092 }
1093 #[allow(dead_code)]
1094 pub fn t_count(&self) -> usize {
1095 self.gates.iter().filter(|(g, _)| g.name == "T").count()
1096 }
1097 #[allow(dead_code)]
1098 pub fn is_clifford(&self) -> bool {
1099 self.gates.iter().all(|(g, _)| g.is_clifford)
1100 }
1101}
1102#[allow(dead_code)]
1104#[derive(Debug, Clone)]
1105pub struct QuantumChannel {
1106 pub name: String,
1107 pub input_dim: usize,
1108 pub output_dim: usize,
1109 pub channel_type: ChannelType,
1110}
1111impl QuantumChannel {
1112 #[allow(dead_code)]
1113 pub fn depolarizing(dim: usize, _p: f64) -> Self {
1114 Self {
1115 name: format!("Depolarizing(dim={dim})"),
1116 input_dim: dim,
1117 output_dim: dim,
1118 channel_type: ChannelType::Depolarizing,
1119 }
1120 }
1121 #[allow(dead_code)]
1122 pub fn amplitude_damping(gamma: f64) -> Self {
1123 let name = format!("AmplitudeDamping(gamma={gamma:.3})");
1124 Self {
1125 name,
1126 input_dim: 2,
1127 output_dim: 2,
1128 channel_type: ChannelType::AmplitudeDamping,
1129 }
1130 }
1131 #[allow(dead_code)]
1132 pub fn is_unital(&self) -> bool {
1133 matches!(
1134 self.channel_type,
1135 ChannelType::Depolarizing
1136 | ChannelType::PhaseDamping
1137 | ChannelType::BitFlip
1138 | ChannelType::PhaseFlip
1139 | ChannelType::Unitary
1140 | ChannelType::Identity
1141 )
1142 }
1143 #[allow(dead_code)]
1144 pub fn is_degradable(&self) -> bool {
1145 matches!(
1146 self.channel_type,
1147 ChannelType::AmplitudeDamping | ChannelType::Unitary
1148 )
1149 }
1150 #[allow(dead_code)]
1151 pub fn quantum_capacity_achievable(&self) -> bool {
1152 !matches!(self.channel_type, ChannelType::Erasure)
1153 }
1154}
1155#[derive(Debug, Clone)]
1160pub struct StabilizerState {
1161 pub n_qubits: usize,
1162 pub generators: Vec<PauliString>,
1164}
1165impl StabilizerState {
1166 pub fn from_generators(n_qubits: usize, generators: Vec<PauliString>) -> Self {
1170 StabilizerState {
1171 n_qubits,
1172 generators,
1173 }
1174 }
1175 pub fn computational_zero(n_qubits: usize) -> Self {
1177 let generators: Vec<PauliString> = (0..n_qubits)
1178 .map(|i| {
1179 let mut paulis = vec![Pauli::I; n_qubits];
1180 paulis[i] = Pauli::Z;
1181 PauliString::new(1, paulis)
1182 })
1183 .collect();
1184 StabilizerState {
1185 n_qubits,
1186 generators,
1187 }
1188 }
1189 pub fn plus_state(n_qubits: usize) -> Self {
1191 let generators: Vec<PauliString> = (0..n_qubits)
1192 .map(|i| {
1193 let mut paulis = vec![Pauli::I; n_qubits];
1194 paulis[i] = Pauli::X;
1195 PauliString::new(1, paulis)
1196 })
1197 .collect();
1198 StabilizerState {
1199 n_qubits,
1200 generators,
1201 }
1202 }
1203 pub fn is_consistent(&self) -> bool {
1205 let n = self.generators.len();
1206 for i in 0..n {
1207 for j in (i + 1)..n {
1208 if !self.generators[i].commutes_with(&self.generators[j]) {
1209 return false;
1210 }
1211 }
1212 }
1213 true
1214 }
1215 pub fn measure_pauli(&self, p: &PauliString) -> Option<i8> {
1221 let all_commute = self.generators.iter().all(|g| p.commutes_with(g));
1222 if !all_commute {
1223 return None;
1224 }
1225 let _product = p.mul(&PauliString::identity(self.n_qubits));
1226 Some(p.sign)
1227 }
1228}
1229#[derive(Debug, Clone, Default)]
1231pub struct QuantumCircuit {
1232 pub n_qubits: usize,
1233 pub ops: Vec<GateOp>,
1234}
1235impl QuantumCircuit {
1236 pub fn new(n_qubits: usize) -> Self {
1238 QuantumCircuit {
1239 n_qubits,
1240 ops: Vec::new(),
1241 }
1242 }
1243 pub fn add_gate(&mut self, gate: Gate2x2, qubit: usize) {
1245 self.ops.push(GateOp::Single { gate, qubit });
1246 }
1247 pub fn add_cnot(&mut self, control: usize, target: usize) {
1249 self.ops.push(GateOp::Cnot { control, target });
1250 }
1251 pub fn depth(&self) -> usize {
1253 self.ops.len()
1254 }
1255 pub fn run(&self) -> QuantumRegister {
1258 let mut reg = QuantumRegister::new(self.n_qubits);
1259 for op in &self.ops {
1260 match op {
1261 GateOp::Single { gate, qubit } => reg.apply_gate(gate, *qubit),
1262 GateOp::Cnot { control, target } => reg.apply_cnot(*control, *target),
1263 }
1264 }
1265 reg
1266 }
1267 pub fn run_and_measure(&self, seed: u64) -> Vec<u8> {
1271 let reg = self.run();
1272 (0..self.n_qubits)
1273 .map(|q| reg.measure_qubit(q, seed.wrapping_add(q as u64 * 1234567891)))
1274 .collect()
1275 }
1276}
1277#[allow(dead_code)]
1278#[derive(Debug, Clone, PartialEq, Eq)]
1279pub enum QuantumWalkType {
1280 ContinuousTime,
1281 DiscreteTimeCoin,
1282 Scattering,
1283}