quantrs2_core/networking/
channel.rs1use scirs2_core::ndarray::Array2;
8use scirs2_core::Complex64;
9use std::f64::consts::SQRT_2;
10
11pub trait NoiseChannel: Send + Sync {
16 fn apply(&self, rho: &mut Array2<Complex64>);
18}
19
20fn kraus_act(k: &[[Complex64; 2]; 2], rho: &Array2<Complex64>, out: &mut Array2<Complex64>) {
24 let mut k_rho = [[Complex64::new(0.0, 0.0); 2]; 2];
26 for i in 0..2 {
27 for j in 0..2 {
28 for l in 0..2 {
29 k_rho[i][j] += k[i][l] * rho[[l, j]];
30 }
31 }
32 }
33 for i in 0..2 {
35 for j in 0..2 {
36 for l in 0..2 {
37 out[[i, j]] += k_rho[i][l] * k[j][l].conj();
38 }
39 }
40 }
41}
42
43fn pauli_x() -> [[Complex64; 2]; 2] {
47 [
48 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
49 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
50 ]
51}
52
53fn pauli_y() -> [[Complex64; 2]; 2] {
54 [
55 [Complex64::new(0.0, 0.0), Complex64::new(0.0, -1.0)],
56 [Complex64::new(0.0, 1.0), Complex64::new(0.0, 0.0)],
57 ]
58}
59
60fn pauli_z() -> [[Complex64; 2]; 2] {
61 [
62 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
63 [Complex64::new(0.0, 0.0), Complex64::new(-1.0, 0.0)],
64 ]
65}
66
67#[derive(Debug, Clone)]
75pub struct DepolarizingChannel {
76 pub p: f64,
78}
79
80impl DepolarizingChannel {
81 pub fn new(p: f64) -> Self {
83 Self {
84 p: p.clamp(0.0, 1.0),
85 }
86 }
87}
88
89impl NoiseChannel for DepolarizingChannel {
90 fn apply(&self, rho: &mut Array2<Complex64>) {
91 if self.p == 0.0 {
92 return;
93 }
94 let p = self.p;
95 let identity_weight = Complex64::new(1.0 - p, 0.0);
96 let pauli_weight = Complex64::new(p / 3.0, 0.0);
97
98 let rho_orig = rho.clone();
99
100 rho.mapv_inplace(|v| v * identity_weight);
102
103 let paulis = [pauli_x(), pauli_y(), pauli_z()];
104 for pauli in &paulis {
105 let mut term = Array2::<Complex64>::zeros((2, 2));
106 kraus_act(pauli, &rho_orig, &mut term);
107 for i in 0..2 {
108 for j in 0..2 {
109 rho[[i, j]] += pauli_weight * term[[i, j]];
110 }
111 }
112 }
113 }
114}
115
116#[derive(Debug, Clone)]
122pub struct DephazingChannel {
123 pub p: f64,
125}
126
127impl DephazingChannel {
128 pub fn new(p: f64) -> Self {
130 Self {
131 p: p.clamp(0.0, 1.0),
132 }
133 }
134}
135
136impl NoiseChannel for DephazingChannel {
137 fn apply(&self, rho: &mut Array2<Complex64>) {
138 if self.p == 0.0 {
139 return;
140 }
141 let p = self.p;
142 let z = pauli_z();
143
144 let rho_orig = rho.clone();
145 rho.mapv_inplace(|v| v * Complex64::new(1.0 - p, 0.0));
146
147 let mut z_term = Array2::<Complex64>::zeros((2, 2));
148 kraus_act(&z, &rho_orig, &mut z_term);
149 for i in 0..2 {
150 for j in 0..2 {
151 rho[[i, j]] += Complex64::new(p, 0.0) * z_term[[i, j]];
152 }
153 }
154 }
155}
156
157#[derive(Debug, Clone)]
163pub struct AmplitudeDampingChannel {
164 pub gamma: f64,
166}
167
168impl AmplitudeDampingChannel {
169 pub fn new(gamma: f64) -> Self {
171 Self {
172 gamma: gamma.clamp(0.0, 1.0),
173 }
174 }
175}
176
177impl NoiseChannel for AmplitudeDampingChannel {
178 fn apply(&self, rho: &mut Array2<Complex64>) {
179 if self.gamma == 0.0 {
180 return;
181 }
182 let gamma = self.gamma;
183 let sqrt_1mg = (1.0 - gamma).sqrt();
184 let sqrt_g = gamma.sqrt();
185
186 let k0: [[Complex64; 2]; 2] = [
187 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
188 [Complex64::new(0.0, 0.0), Complex64::new(sqrt_1mg, 0.0)],
189 ];
190 let k1: [[Complex64; 2]; 2] = [
191 [Complex64::new(0.0, 0.0), Complex64::new(sqrt_g, 0.0)],
192 [Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0)],
193 ];
194
195 let rho_orig = rho.clone();
196 rho.mapv_inplace(|_| Complex64::new(0.0, 0.0));
197 kraus_act(&k0, &rho_orig, rho);
198 kraus_act(&k1, &rho_orig, rho);
199 }
200}
201
202pub fn pure_state_density(psi: &[Complex64; 2]) -> Array2<Complex64> {
208 let mut rho = Array2::<Complex64>::zeros((2, 2));
209 for i in 0..2 {
210 for j in 0..2 {
211 rho[[i, j]] = psi[i] * psi[j].conj();
212 }
213 }
214 rho
215}
216
217pub fn fidelity_pure(psi: &[Complex64; 2], rho: &Array2<Complex64>) -> f64 {
219 let mut f = Complex64::new(0.0, 0.0);
221 for i in 0..2 {
222 for j in 0..2 {
223 f += psi[i].conj() * rho[[i, j]] * psi[j];
224 }
225 }
226 f.re.clamp(0.0, 1.0)
227}
228
229pub fn ket_zero() -> [Complex64; 2] {
231 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]
232}
233
234pub fn ket_one() -> [Complex64; 2] {
236 [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)]
237}
238
239pub fn ket_plus() -> [Complex64; 2] {
241 let v = 1.0 / SQRT_2;
242 [Complex64::new(v, 0.0), Complex64::new(v, 0.0)]
243}
244
245pub fn ket_minus() -> [Complex64; 2] {
247 let v = 1.0 / SQRT_2;
248 [Complex64::new(v, 0.0), Complex64::new(-v, 0.0)]
249}
250
251pub fn measure_computational(rho: &Array2<Complex64>, r: f64) -> (bool, Array2<Complex64>) {
256 let p0 = rho[[0, 0]].re.clamp(0.0, 1.0);
258 let outcome = r >= p0;
259 let mut collapsed = Array2::<Complex64>::zeros((2, 2));
260 if !outcome {
261 if p0 > 1e-15 {
263 collapsed[[0, 0]] = Complex64::new(1.0, 0.0);
264 }
265 } else {
266 let p1 = (1.0 - p0).max(0.0);
267 if p1 > 1e-15 {
268 collapsed[[1, 1]] = Complex64::new(1.0, 0.0);
269 }
270 }
271 (outcome, collapsed)
272}
273
274#[cfg(test)]
279mod tests {
280 use super::*;
281 use approx::assert_abs_diff_eq;
282
283 fn identity_rho() -> Array2<Complex64> {
284 let mut rho = Array2::<Complex64>::zeros((2, 2));
285 rho[[0, 0]] = Complex64::new(0.5, 0.0);
286 rho[[1, 1]] = Complex64::new(0.5, 0.0);
287 rho
288 }
289
290 #[test]
291 fn depolarizing_zero_is_identity() {
292 let ch = DepolarizingChannel::new(0.0);
293 let mut rho = pure_state_density(&ket_zero());
294 let orig = rho.clone();
295 ch.apply(&mut rho);
296 assert_abs_diff_eq!(rho[[0, 0]].re, orig[[0, 0]].re, epsilon = 1e-12);
297 assert_abs_diff_eq!(rho[[1, 1]].re, orig[[1, 1]].re, epsilon = 1e-12);
298 }
299
300 #[test]
301 fn depolarizing_fully_mixed_stays_mixed() {
302 let ch = DepolarizingChannel::new(0.75);
303 let mut rho = identity_rho();
304 ch.apply(&mut rho);
305 assert_abs_diff_eq!(rho[[0, 0]].re, 0.5, epsilon = 1e-12);
307 assert_abs_diff_eq!(rho[[1, 1]].re, 0.5, epsilon = 1e-12);
308 }
309
310 #[test]
311 fn amplitude_damping_relaxes_to_ground() {
312 let ch = AmplitudeDampingChannel::new(1.0);
313 let mut rho = pure_state_density(&ket_one());
314 ch.apply(&mut rho);
315 assert_abs_diff_eq!(rho[[0, 0]].re, 1.0, epsilon = 1e-12);
316 assert_abs_diff_eq!(rho[[1, 1]].re, 0.0, epsilon = 1e-12);
317 }
318
319 #[test]
320 fn dephasing_zero_is_identity() {
321 let ch = DephazingChannel::new(0.0);
322 let mut rho = pure_state_density(&ket_plus());
323 let orig = rho.clone();
324 ch.apply(&mut rho);
325 for i in 0..2 {
326 for j in 0..2 {
327 assert_abs_diff_eq!(rho[[i, j]].re, orig[[i, j]].re, epsilon = 1e-12);
328 }
329 }
330 }
331}