oxicuda_quantum/algorithm/
teleportation.rs1use num_complex::Complex;
26
27use crate::error::{QuantumError, QuantumResult};
28use crate::gates::controlled::apply_cnot;
29use crate::gates::hadamard::gate_h;
30use crate::gates::pauli::{gate_x, gate_z};
31use crate::handle::LcgRng;
32use crate::midcircuit::measurement::{ClassicalRegister, apply_if, measure_and_collapse};
33use crate::statevec::apply_1q::apply_1q_inplace;
34use crate::statevec::state::StateVector;
35
36type Complex32 = Complex<f32>;
37
38#[derive(Debug, Clone)]
40pub struct TeleportResult {
41 pub classical_bits: (bool, bool),
43 pub bob_amplitudes: (Complex32, Complex32),
47 pub fidelity: f32,
49}
50
51pub fn teleport(
63 alpha: Complex32,
64 beta: Complex32,
65 rng: &mut LcgRng,
66) -> QuantumResult<TeleportResult> {
67 let norm = (alpha.norm_sqr() + beta.norm_sqr()).sqrt();
68 if norm < 1e-12 {
69 return Err(QuantumError::InvalidParameter {
70 name: "input state amplitudes are both zero".into(),
71 });
72 }
73 let a = alpha / norm;
74 let b = beta / norm;
75
76 let mut amps = vec![Complex32::new(0.0, 0.0); 8];
79 amps[0b000] = a; amps[0b001] = b; let mut sv = StateVector { amps, n_qubits: 3 };
82
83 apply_1q_inplace(&mut sv, 1, &gate_h())?;
85 apply_cnot(&mut sv, 1, 2)?;
86
87 apply_cnot(&mut sv, 0, 1)?;
89 apply_1q_inplace(&mut sv, 0, &gate_h())?;
90
91 let mut creg = ClassicalRegister::new(2);
93 let m0 = measure_and_collapse(&mut sv, 0, rng, &mut creg, 0)?;
94 let m1 = measure_and_collapse(&mut sv, 1, rng, &mut creg, 1)?;
95
96 apply_if(&mut sv, &creg, &[(1, true)], &gate_x(), 2)?;
98 apply_if(&mut sv, &creg, &[(0, true)], &gate_z(), 2)?;
99
100 let base = (usize::from(m0)) | (usize::from(m1) << 1);
103 let bob0 = sv.amps[base]; let bob1 = sv.amps[base | (1 << 2)]; let overlap = a.conj() * bob0 + b.conj() * bob1;
108 let fidelity = overlap.norm_sqr();
109
110 Ok(TeleportResult {
111 classical_bits: (m0, m1),
112 bob_amplitudes: (bob0, bob1),
113 fidelity,
114 })
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use std::f32::consts::FRAC_1_SQRT_2;
121
122 #[test]
123 fn teleports_zero_state() {
124 let mut rng = LcgRng::new(1);
126 let res = teleport(Complex32::new(1.0, 0.0), Complex32::new(0.0, 0.0), &mut rng)
127 .expect("teleportation of |0⟩ state with normalized input");
128 assert!(res.fidelity > 0.999, "fidelity={}", res.fidelity);
129 assert!((res.bob_amplitudes.0.norm() - 1.0).abs() < 1e-4);
130 assert!(res.bob_amplitudes.1.norm() < 1e-4);
131 }
132
133 #[test]
134 fn teleports_one_state() {
135 let mut rng = LcgRng::new(2);
137 let res = teleport(Complex32::new(0.0, 0.0), Complex32::new(1.0, 0.0), &mut rng)
138 .expect("non-zero |1⟩ state is a valid teleportation input");
139 assert!(res.fidelity > 0.999, "fidelity={}", res.fidelity);
140 assert!(res.bob_amplitudes.0.norm() < 1e-4);
141 assert!((res.bob_amplitudes.1.norm() - 1.0).abs() < 1e-4);
142 }
143
144 #[test]
145 fn teleports_plus_state() {
146 let mut rng = LcgRng::new(3);
148 let res = teleport(
149 Complex32::new(FRAC_1_SQRT_2, 0.0),
150 Complex32::new(FRAC_1_SQRT_2, 0.0),
151 &mut rng,
152 )
153 .expect("normalized |+⟩ state is a valid teleportation input");
154 assert!(res.fidelity > 0.999, "fidelity={}", res.fidelity);
155 }
156
157 #[test]
158 fn teleports_complex_superposition() {
159 let mut rng = LcgRng::new(4);
161 let res = teleport(Complex32::new(0.6, 0.0), Complex32::new(0.0, 0.8), &mut rng)
162 .expect("non-zero complex superposition is a valid teleportation input");
163 assert!(res.fidelity > 0.999, "fidelity={}", res.fidelity);
164 }
165
166 #[test]
167 fn fidelity_high_across_many_seeds() {
168 for seed in 0..32u64 {
170 let mut rng = LcgRng::new(seed);
171 let res = teleport(Complex32::new(0.6, 0.0), Complex32::new(0.8, 0.0), &mut rng)
172 .expect("non-zero (0.6, 0.8) real state is a valid teleportation input");
173 assert!(res.fidelity > 0.99, "seed={seed} fidelity={}", res.fidelity);
174 }
175 }
176
177 #[test]
178 fn unnormalized_input_is_accepted() {
179 let mut rng = LcgRng::new(5);
181 let res = teleport(Complex32::new(2.0, 0.0), Complex32::new(0.0, 0.0), &mut rng)
182 .expect("unnormalized (2, 0) is non-zero and renormalized internally");
183 assert!(res.fidelity > 0.999, "fidelity={}", res.fidelity);
184 }
185
186 #[test]
187 fn zero_input_errors() {
188 let mut rng = LcgRng::new(6);
189 assert!(teleport(Complex32::new(0.0, 0.0), Complex32::new(0.0, 0.0), &mut rng).is_err());
190 }
191
192 #[test]
193 fn classical_bits_are_booleans() {
194 let mut rng = LcgRng::new(7);
196 let res = teleport(
197 Complex32::new(0.5, 0.5),
198 Complex32::new(0.5, -0.5),
199 &mut rng,
200 )
201 .expect("complex superposition with non-zero norm is a valid teleportation input");
202 let (m0, m1) = res.classical_bits;
203 let _ = m0 & m1;
205 assert!(res.fidelity > 0.99, "fidelity={}", res.fidelity);
206 }
207
208 #[test]
209 fn bob_amplitudes_match_input_up_to_branch() {
210 let mut rng = LcgRng::new(11);
212 let a = Complex32::new(0.6, 0.0);
213 let b = Complex32::new(0.0, 0.8);
214 let res = teleport(a, b, &mut rng)
215 .expect("non-zero (0.6, 0.8i) state is a valid teleportation input");
216 assert!(
217 (res.bob_amplitudes.0 - a).norm() < 1e-4,
218 "bob0={:?}",
219 res.bob_amplitudes.0
220 );
221 assert!(
222 (res.bob_amplitudes.1 - b).norm() < 1e-4,
223 "bob1={:?}",
224 res.bob_amplitudes.1
225 );
226 }
227
228 #[test]
229 fn fidelity_is_finite_and_in_range() {
230 let mut rng = LcgRng::new(13);
231 let res = teleport(Complex32::new(0.6, 0.2), Complex32::new(0.3, 0.7), &mut rng)
232 .expect("non-zero complex state (0.6+0.2i, 0.3+0.7i) is a valid teleportation input");
233 assert!(res.fidelity.is_finite());
234 assert!((0.0..=1.0 + 1e-4).contains(&res.fidelity));
235 }
236}