microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/compiler/qsc_eval/src/backend/noise_tests.rs
564lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | use crate::{ |
| 5 | backend::{Backend, SparseSim}, |
| 6 | noise::PauliNoise, |
| 7 | state::{fmt_complex, format_state_id}, |
| 8 | val, |
| 9 | }; |
| 10 | use expect_test::{Expect, expect}; |
| 11 | use num_bigint::BigUint; |
| 12 | use num_complex::Complex; |
| 13 | use qdk_simulators::noise_config::{NoiseConfig, NoiseTable, encode_pauli}; |
| 14 | use std::fmt::Write; |
| 15 | |
| 16 | #[test] |
| 17 | fn pauli_noise() { |
| 18 | let noise = PauliNoise::from_probabilities(0.0, 0.0, 0.0); |
| 19 | assert!( |
| 20 | noise |
| 21 | .expect("noiseless Pauli noise should be constructable.") |
| 22 | .is_noiseless(), |
| 23 | "Expected noiseless noise." |
| 24 | ); |
| 25 | let noise = PauliNoise::from_probabilities(1e-5, 0.0, 0.0); |
| 26 | assert!( |
| 27 | !noise |
| 28 | .expect("bit flip noise with probability 1e-5 should be constructable.") |
| 29 | .is_noiseless(), |
| 30 | "Expected noise to be noisy." |
| 31 | ); |
| 32 | let noise = PauliNoise::from_probabilities(1.0, 0.0, 0.0); |
| 33 | assert!( |
| 34 | !noise |
| 35 | .expect("bit flip noise with probability 1 should be constructable.") |
| 36 | .is_noiseless(), |
| 37 | "Expected noise to be noisy." |
| 38 | ); |
| 39 | let noise = PauliNoise::from_probabilities(0.01, 0.01, 0.01) |
| 40 | .expect("depolarizing noise with probability 0.01 should be constructable.."); |
| 41 | assert!(!noise.is_noiseless(), "Expected noise to be noisy."); |
| 42 | assert!( |
| 43 | 0.0 <= noise.distribution[0] |
| 44 | && noise.distribution[0] <= noise.distribution[1] |
| 45 | && noise.distribution[1] <= noise.distribution[2] |
| 46 | && noise.distribution[2] <= 1.1, |
| 47 | "Expected non-decreasing noise distribution." |
| 48 | ); |
| 49 | let _ = PauliNoise::from_probabilities(-1e-10, 0.1, 0.1) |
| 50 | .expect_err("pauli noise with probabilities -1e-10, 0.1, 0.1 should result in error."); |
| 51 | let _ = PauliNoise::from_probabilities(1.0 + -1e-10, 0.1, 0.1) |
| 52 | .expect_err("pauli noise with probabilities 1.0+1e-10, 0.1, 0.1 should result in error."); |
| 53 | let _ = PauliNoise::from_probabilities(0.3, 0.4, 0.5) |
| 54 | .expect_err("pauli noise with probabilities 0.3, 0.4, 0.5 should result in error."); |
| 55 | } |
| 56 | |
| 57 | #[test] |
| 58 | fn noisy_simulator() { |
| 59 | let sim = SparseSim::new(); |
| 60 | assert!(sim.is_noiseless(), "Expected noiseless simulator."); |
| 61 | |
| 62 | let noise = PauliNoise::from_probabilities(0.0, 0.0, 0.0) |
| 63 | .expect("noiseless Pauli noise should be constructable."); |
| 64 | let sim = SparseSim::new_with_noise(&noise); |
| 65 | assert!(sim.is_noiseless(), "Expected noiseless simulator."); |
| 66 | |
| 67 | let noise = PauliNoise::from_probabilities(1e-10, 0.0, 0.0) |
| 68 | .expect("1e-10, 0.0, 0.0 Pauli noise should be constructable."); |
| 69 | let sim = SparseSim::new_with_noise(&noise); |
| 70 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 71 | |
| 72 | let noise = PauliNoise::from_probabilities(0.0, 0.0, 1e-10) |
| 73 | .expect("0.0, 0.0, 1e-10 Pauli noise should be constructable."); |
| 74 | let sim = SparseSim::new_with_noise(&noise); |
| 75 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 76 | } |
| 77 | |
| 78 | #[test] |
| 79 | fn noiseless_gate() { |
| 80 | let noise = PauliNoise::from_probabilities(0.0, 0.0, 0.0) |
| 81 | .expect("noiseless Pauli noise should be constructable."); |
| 82 | let mut sim = SparseSim::new_with_noise(&noise); |
| 83 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 84 | for _ in 0..100 { |
| 85 | let _ = sim.x(q); |
| 86 | let res1 = sim |
| 87 | .m(q) |
| 88 | .expect("sparse simulator is infinite") |
| 89 | .unwrap_bool(); |
| 90 | assert!(res1, "Expected True without noise."); |
| 91 | let _ = sim.x(q); |
| 92 | let res2 = sim |
| 93 | .m(q) |
| 94 | .expect("sparse simulator is infinite") |
| 95 | .unwrap_bool(); |
| 96 | assert!(!res2, "Expected False without noise."); |
| 97 | } |
| 98 | assert!( |
| 99 | sim.qubit_release(q).expect("sparse simulator is infinite"), |
| 100 | "Expected correct qubit state on release." |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | #[test] |
| 105 | fn bitflip_measurement() { |
| 106 | let noise = PauliNoise::from_probabilities(1.0, 0.0, 0.0) |
| 107 | .expect("bit flip noise with probability 100% should be constructable."); |
| 108 | let mut sim = SparseSim::new_with_noise(&noise); |
| 109 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 110 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); // Allocation is noiseless even with noise. |
| 111 | for _ in 0..100 { |
| 112 | let res1 = sim |
| 113 | .m(q) |
| 114 | .expect("sparse simulator is infinite") |
| 115 | .unwrap_bool(); |
| 116 | assert!(res1, "Expected True for 100% bit flip noise."); |
| 117 | let res2 = sim |
| 118 | .m(q) |
| 119 | .expect("sparse simulator is infinite") |
| 120 | .unwrap_bool(); |
| 121 | assert!(!res2, "Expected False for 100% bit flip noise."); |
| 122 | } |
| 123 | assert!( |
| 124 | sim.qubit_release(q).expect("sparse simulator is infinite"), |
| 125 | "Expected correct qubit state on release." |
| 126 | ); |
| 127 | } |
| 128 | |
| 129 | #[test] |
| 130 | fn noisy_measurement() { |
| 131 | let noise = PauliNoise::from_probabilities(0.3, 0.0, 0.0) |
| 132 | .expect("bit flip noise with probability 100% should be constructable."); |
| 133 | let mut sim = SparseSim::new_with_noise(&noise); |
| 134 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 135 | sim.set_seed(Some(0)); |
| 136 | let mut true_count = 0; |
| 137 | for _ in 0..1000 { |
| 138 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); // Allocation is noiseless even with noise. |
| 139 | // sim.m sometimes applies X before measuring |
| 140 | if sim |
| 141 | .m(q) |
| 142 | .expect("sparse simulator is infinite") |
| 143 | .unwrap_bool() |
| 144 | { |
| 145 | true_count += 1; |
| 146 | } |
| 147 | sim.qubit_release(q).expect("sparse simulator is infinite"); |
| 148 | } |
| 149 | assert!( |
| 150 | true_count > 200 && true_count < 400, |
| 151 | "Expected about 30% bit flip noise." |
| 152 | ); |
| 153 | } |
| 154 | |
| 155 | pub fn state_to_string(input: &(Vec<(BigUint, Complex<f64>)>, usize)) -> String { |
| 156 | input |
| 157 | .0 |
| 158 | .iter() |
| 159 | .fold(String::new(), |mut output, (id, state)| { |
| 160 | let _ = write!( |
| 161 | output, |
| 162 | "{}: {} ", |
| 163 | format_state_id(id, input.1), |
| 164 | fmt_complex(state) |
| 165 | ); |
| 166 | output |
| 167 | }) |
| 168 | .clone() |
| 169 | } |
| 170 | |
| 171 | fn check_state(sim: &mut SparseSim, expected: &Expect) { |
| 172 | let state = sim |
| 173 | .capture_quantum_state() |
| 174 | .expect("sparse simulator is infinite"); |
| 175 | expected.assert_eq(&state_to_string(&state)); |
| 176 | } |
| 177 | |
| 178 | #[test] |
| 179 | fn noisy_via_x() { |
| 180 | let noise = PauliNoise::from_probabilities(1.0, 0.0, 0.0) |
| 181 | .expect("bit flip noise with probability 100% should be constructable."); |
| 182 | let mut sim = SparseSim::new_with_noise(&noise); |
| 183 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 184 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); // Allocation is noiseless even with noise. |
| 185 | check_state(&mut sim, &expect!["|0⟩: 1.0000+0.0000𝑖 "]); |
| 186 | let _ = sim.x(q); // Followed by X. So, no op. |
| 187 | check_state(&mut sim, &expect!["|0⟩: 1.0000+0.0000𝑖 "]); |
| 188 | let _ = sim.y(q); // Followed by X. |
| 189 | check_state(&mut sim, &expect!["|0⟩: 0.0000+1.0000𝑖 "]); |
| 190 | let _ = sim.z(q); // Followed by X. |
| 191 | check_state(&mut sim, &expect!["|1⟩: 0.0000+1.0000𝑖 "]); |
| 192 | } |
| 193 | |
| 194 | #[test] |
| 195 | fn noisy_via_y() { |
| 196 | let noise = PauliNoise::from_probabilities(0.0, 1.0, 0.0) |
| 197 | .expect("0.0, 1.0, 0.0 Pauli noise should be constructable."); |
| 198 | let mut sim = SparseSim::new_with_noise(&noise); |
| 199 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 200 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); // Allocation is noiseless even with noise. |
| 201 | check_state(&mut sim, &expect!["|0⟩: 1.0000+0.0000𝑖 "]); |
| 202 | let _ = sim.x(q); // Followed by Y. |
| 203 | check_state(&mut sim, &expect!["|0⟩: 0.0000−1.0000𝑖 "]); |
| 204 | let _ = sim.y(q); // Followed by Y. So, no op. |
| 205 | check_state(&mut sim, &expect!["|0⟩: 0.0000−1.0000𝑖 "]); |
| 206 | let _ = sim.z(q); // Followed by Y. |
| 207 | check_state(&mut sim, &expect!["|1⟩: 1.0000+0.0000𝑖 "]); |
| 208 | } |
| 209 | |
| 210 | #[test] |
| 211 | fn noisy_via_z() { |
| 212 | let noise = PauliNoise::from_probabilities(0.0, 0.0, 1.0) |
| 213 | .expect("phase flip noise with probability 100% should be constructable."); |
| 214 | let mut sim = SparseSim::new_with_noise(&noise); |
| 215 | assert!(!sim.is_noiseless(), "Expected noisy simulator."); |
| 216 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); // Allocation is noiseless even with noise. |
| 217 | check_state(&mut sim, &expect!["|0⟩: 1.0000+0.0000𝑖 "]); |
| 218 | let _ = sim.x(q); // Followed by Z. |
| 219 | check_state(&mut sim, &expect!["|1⟩: −1.0000+0.0000𝑖 "]); |
| 220 | let _ = sim.y(q); // Followed by Z. |
| 221 | check_state(&mut sim, &expect!["|0⟩: 0.0000+1.0000𝑖 "]); |
| 222 | let _ = sim.z(q); // Followed by Z. So, no op. |
| 223 | check_state(&mut sim, &expect!["|0⟩: 0.0000+1.0000𝑖 "]); |
| 224 | } |
| 225 | |
| 226 | #[test] |
| 227 | fn measure_without_loss_returns_value() { |
| 228 | let mut sim = SparseSim::new(); |
| 229 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 230 | let res = sim.m(q).expect("sparse simulator is infinite"); |
| 231 | assert!( |
| 232 | matches!(res, val::Result::Val(_)), |
| 233 | "Expected measurement to return a result" |
| 234 | ); |
| 235 | } |
| 236 | |
| 237 | #[test] |
| 238 | fn measure_with_loss_returns_loss() { |
| 239 | let mut sim = SparseSim::new(); |
| 240 | sim.set_loss(1.0); // Set loss probability to 100% |
| 241 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 242 | let res = sim.m(q).expect("sparse simulator is infinite"); |
| 243 | assert_eq!( |
| 244 | res, |
| 245 | val::Result::Loss, |
| 246 | "Expected measurement with loss to return None" |
| 247 | ); |
| 248 | } |
| 249 | |
| 250 | /// Creates a `NoiseConfig` where the given gate's `NoiseTable` has 100% probability |
| 251 | /// of the specified single-qubit Pauli fault, and all other gates are noiseless. |
| 252 | fn noise_config_with_single_qubit_fault( |
| 253 | set_gate: impl FnOnce(&mut NoiseConfig<f64, f64>, NoiseTable<f64>), |
| 254 | pauli: &str, |
| 255 | ) -> NoiseConfig<f64, f64> { |
| 256 | let mut config = NoiseConfig::NOISELESS; |
| 257 | let table = NoiseTable { |
| 258 | qubits: 1, |
| 259 | pauli_strings: vec![encode_pauli(pauli)], |
| 260 | probabilities: vec![1.0], |
| 261 | loss: 0.0, |
| 262 | }; |
| 263 | set_gate(&mut config, table); |
| 264 | config |
| 265 | } |
| 266 | |
| 267 | /// Creates a `NoiseConfig` where the given gate's `NoiseTable` has 100% probability |
| 268 | /// of the specified two-qubit Pauli fault, and all other gates are noiseless. |
| 269 | fn noise_config_with_two_qubit_fault( |
| 270 | set_gate: impl FnOnce(&mut NoiseConfig<f64, f64>, NoiseTable<f64>), |
| 271 | pauli: &str, |
| 272 | ) -> NoiseConfig<f64, f64> { |
| 273 | let mut config = NoiseConfig::NOISELESS; |
| 274 | let table = NoiseTable { |
| 275 | qubits: 2, |
| 276 | pauli_strings: vec![encode_pauli(pauli)], |
| 277 | probabilities: vec![1.0], |
| 278 | loss: 0.0, |
| 279 | }; |
| 280 | set_gate(&mut config, table); |
| 281 | config |
| 282 | } |
| 283 | |
| 284 | // Tests for single-qubit gates with CumulativeNoiseConfig |
| 285 | |
| 286 | #[test] |
| 287 | fn noise_config_x_gate_with_x_fault() { |
| 288 | // X gate followed by 100% X fault = identity (X * X = I) |
| 289 | let config = noise_config_with_single_qubit_fault(|c, t| c.x = t, "X"); |
| 290 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 291 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 292 | let _ = sim.x(q); // X then X fault => |0⟩ |
| 293 | check_state(&mut sim, &expect!["|0⟩: 1.0000+0.0000𝑖 "]); |
| 294 | } |
| 295 | |
| 296 | #[test] |
| 297 | fn noise_config_x_gate_with_z_fault() { |
| 298 | // X gate followed by 100% Z fault = ZX|0⟩ = Z|1⟩ = -|1⟩ |
| 299 | let config = noise_config_with_single_qubit_fault(|c, t| c.x = t, "Z"); |
| 300 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 301 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 302 | let _ = sim.x(q); |
| 303 | check_state(&mut sim, &expect!["|1⟩: −1.0000+0.0000𝑖 "]); |
| 304 | } |
| 305 | |
| 306 | #[test] |
| 307 | fn noise_config_x_gate_with_y_fault() { |
| 308 | // X gate followed by 100% Y fault = YX|0⟩ = Y|1⟩ = -i|0⟩ |
| 309 | let config = noise_config_with_single_qubit_fault(|c, t| c.x = t, "Y"); |
| 310 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 311 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 312 | let _ = sim.x(q); |
| 313 | check_state(&mut sim, &expect!["|0⟩: 0.0000−1.0000𝑖 "]); |
| 314 | } |
| 315 | |
| 316 | #[test] |
| 317 | fn noise_config_h_gate_with_y_fault() { |
| 318 | // H|0⟩ = |+⟩, then Y|+⟩ = i|−⟩ |
| 319 | let config = noise_config_with_single_qubit_fault(|c, t| c.h = t, "Y"); |
| 320 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 321 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 322 | let _ = sim.h(q); |
| 323 | check_state( |
| 324 | &mut sim, |
| 325 | &expect!["|0⟩: 0.0000+0.7071𝑖 |1⟩: 0.0000−0.7071𝑖 "], |
| 326 | ); |
| 327 | } |
| 328 | |
| 329 | #[test] |
| 330 | fn noise_config_h_gate_with_z_fault() { |
| 331 | // H|0⟩ = |+⟩, then Z|+⟩ = |−⟩ |
| 332 | let config = noise_config_with_single_qubit_fault(|c, t| c.h = t, "Z"); |
| 333 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 334 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 335 | let _ = sim.h(q); |
| 336 | check_state( |
| 337 | &mut sim, |
| 338 | &expect!["|0⟩: 0.7071+0.0000𝑖 |1⟩: −0.7071+0.0000𝑖 "], |
| 339 | ); |
| 340 | } |
| 341 | |
| 342 | #[test] |
| 343 | fn noise_config_y_gate_with_y_fault() { |
| 344 | // Y gate followed by 100% Y fault = Y*Y = I |
| 345 | let config = noise_config_with_single_qubit_fault(|c, t| c.y = t, "Y"); |
| 346 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 347 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 348 | let _ = sim.y(q); |
| 349 | check_state(&mut sim, &expect!["|0⟩: 1.0000+0.0000𝑖 "]); |
| 350 | } |
| 351 | |
| 352 | #[test] |
| 353 | fn noise_config_z_gate_with_x_fault() { |
| 354 | // Z|0⟩ = |0⟩, then X|0⟩ = |1⟩ |
| 355 | let config = noise_config_with_single_qubit_fault(|c, t| c.z = t, "X"); |
| 356 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 357 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 358 | let _ = sim.z(q); |
| 359 | check_state(&mut sim, &expect!["|1⟩: 1.0000+0.0000𝑖 "]); |
| 360 | } |
| 361 | |
| 362 | #[test] |
| 363 | fn noise_config_s_gate_with_x_fault() { |
| 364 | // S|0⟩ = |0⟩ (S only adds phase to |1⟩), then X|0⟩ = |1⟩ |
| 365 | let config = noise_config_with_single_qubit_fault(|c, t| c.s = t, "X"); |
| 366 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 367 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 368 | let _ = sim.s(q); |
| 369 | check_state(&mut sim, &expect!["|1⟩: 1.0000+0.0000𝑖 "]); |
| 370 | } |
| 371 | |
| 372 | #[test] |
| 373 | fn noise_config_s_gate_with_y_fault() { |
| 374 | // S|0⟩ = |0⟩, then Y|0⟩ = i|1⟩ |
| 375 | let config = noise_config_with_single_qubit_fault(|c, t| c.s = t, "Y"); |
| 376 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 377 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 378 | let _ = sim.s(q); |
| 379 | check_state(&mut sim, &expect!["|1⟩: 0.0000+1.0000𝑖 "]); |
| 380 | } |
| 381 | |
| 382 | #[test] |
| 383 | fn noise_config_t_gate_with_x_fault() { |
| 384 | // T|0⟩ = |0⟩ (T only adds phase to |1⟩), then X|0⟩ = |1⟩ |
| 385 | let config = noise_config_with_single_qubit_fault(|c, t| c.t = t, "X"); |
| 386 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 387 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 388 | let _ = sim.t(q); |
| 389 | check_state(&mut sim, &expect!["|1⟩: 1.0000+0.0000𝑖 "]); |
| 390 | } |
| 391 | |
| 392 | #[test] |
| 393 | fn noise_config_sadj_gate_with_y_fault() { |
| 394 | // Sadj|0⟩ = |0⟩, then Y|0⟩ = i|1⟩ |
| 395 | let config = noise_config_with_single_qubit_fault(|c, t| c.s_adj = t, "Y"); |
| 396 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 397 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 398 | let _ = sim.sadj(q); |
| 399 | check_state(&mut sim, &expect!["|1⟩: 0.0000+1.0000𝑖 "]); |
| 400 | } |
| 401 | |
| 402 | #[test] |
| 403 | fn noise_config_tadj_gate_with_y_fault() { |
| 404 | // Tadj|0⟩ = |0⟩, then Y|0⟩ = i|1⟩ |
| 405 | let config = noise_config_with_single_qubit_fault(|c, t| c.t_adj = t, "Y"); |
| 406 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 407 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 408 | let _ = sim.tadj(q); |
| 409 | check_state(&mut sim, &expect!["|1⟩: 0.0000+1.0000𝑖 "]); |
| 410 | } |
| 411 | |
| 412 | #[test] |
| 413 | fn noise_config_mz_with_x_fault() { |
| 414 | // Measurement with 100% X fault: qubit in |0⟩, X is applied before measurement, |
| 415 | // so it measures as True (|1⟩). |
| 416 | let config = noise_config_with_single_qubit_fault(|c, t| c.mz = t, "X"); |
| 417 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 418 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 419 | let res = sim |
| 420 | .m(q) |
| 421 | .expect("sparse simulator is infinite") |
| 422 | .unwrap_bool(); |
| 423 | assert!( |
| 424 | res, |
| 425 | "Expected True: X fault flips |0⟩ to |1⟩ before measurement." |
| 426 | ); |
| 427 | } |
| 428 | |
| 429 | #[test] |
| 430 | fn noise_config_mz_with_z_fault() { |
| 431 | // Measurement with 100% Z fault: Z|0⟩ = |0⟩, so measurement is still False. |
| 432 | let config = noise_config_with_single_qubit_fault(|c, t| c.mz = t, "Z"); |
| 433 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 434 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 435 | let res = sim |
| 436 | .m(q) |
| 437 | .expect("sparse simulator is infinite") |
| 438 | .unwrap_bool(); |
| 439 | assert!( |
| 440 | !res, |
| 441 | "Expected False: Z fault on |0⟩ doesn't change measurement outcome." |
| 442 | ); |
| 443 | } |
| 444 | |
| 445 | // Tests for two-qubit gates with CumulativeNoiseConfig |
| 446 | |
| 447 | #[test] |
| 448 | fn noise_config_cx_gate_with_xi_fault() { |
| 449 | // CX(ctl, tgt) on |00⟩ = |00⟩, then XI fault: X on control, I on target => |10⟩ |
| 450 | let config = noise_config_with_two_qubit_fault(|c, t| c.cx = t, "XI"); |
| 451 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 452 | let ctl = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 453 | let tgt = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 454 | let _ = sim.cx(ctl, tgt); |
| 455 | check_state(&mut sim, &expect!["|10⟩: 1.0000+0.0000𝑖 "]); |
| 456 | } |
| 457 | |
| 458 | #[test] |
| 459 | fn noise_config_cx_gate_with_ix_fault() { |
| 460 | // CX(ctl, tgt) on |00⟩ = |00⟩, then IX fault: I on control, X on target => |01⟩ |
| 461 | let config = noise_config_with_two_qubit_fault(|c, t| c.cx = t, "IX"); |
| 462 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 463 | let ctl = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 464 | let tgt = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 465 | let _ = sim.cx(ctl, tgt); |
| 466 | check_state(&mut sim, &expect!["|01⟩: 1.0000+0.0000𝑖 "]); |
| 467 | } |
| 468 | |
| 469 | #[test] |
| 470 | fn noise_config_cx_gate_with_xx_fault() { |
| 471 | // CX(ctl, tgt) on |00⟩ = |00⟩, then XX fault: X on both => |11⟩ |
| 472 | let config = noise_config_with_two_qubit_fault(|c, t| c.cx = t, "XX"); |
| 473 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 474 | let ctl = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 475 | let tgt = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 476 | let _ = sim.cx(ctl, tgt); |
| 477 | check_state(&mut sim, &expect!["|11⟩: 1.0000+0.0000𝑖 "]); |
| 478 | } |
| 479 | |
| 480 | #[test] |
| 481 | fn noise_config_cz_gate_with_xy_fault() { |
| 482 | // CZ on |00⟩ = |00⟩, then XY fault: X on first, Y on second |
| 483 | // X|0⟩ = |1⟩, Y|0⟩ = i|1⟩ => |1i1⟩ |
| 484 | let config = noise_config_with_two_qubit_fault(|c, t| c.cz = t, "XY"); |
| 485 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 486 | let q0 = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 487 | let q1 = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 488 | let _ = sim.cz(q0, q1); |
| 489 | check_state(&mut sim, &expect!["|11⟩: 0.0000+1.0000𝑖 "]); |
| 490 | } |
| 491 | |
| 492 | #[test] |
| 493 | fn noise_config_swap_gate_with_xx_fault() { |
| 494 | // Prepare |10⟩, SWAP => |01⟩, then XX fault => |10⟩ again |
| 495 | let config = noise_config_with_two_qubit_fault(|c, t| c.swap = t, "XX"); |
| 496 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 497 | let q0 = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 498 | let q1 = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 499 | let _ = sim.x(q0); // |10⟩ (x gate has no noise configured) |
| 500 | let _ = sim.swap(q0, q1); // SWAP => |01⟩, then XX => |10⟩ |
| 501 | check_state(&mut sim, &expect!["|10⟩: 1.0000+0.0000𝑖 "]); |
| 502 | } |
| 503 | |
| 504 | // Test that noise is only applied to the configured gate, not others |
| 505 | |
| 506 | #[test] |
| 507 | fn noise_config_only_affects_configured_gate() { |
| 508 | // Configure X fault only on H gate; X gate should be noiseless. |
| 509 | let config = noise_config_with_single_qubit_fault(|c, t| c.h = t, "X"); |
| 510 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 511 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 512 | // X gate has no noise configured, so X|0⟩ = |1⟩ without any fault |
| 513 | let _ = sim.x(q); |
| 514 | check_state(&mut sim, &expect!["|1⟩: 1.0000+0.0000𝑖 "]); |
| 515 | // Now apply H (which has 100% X fault): H|1⟩ = |−⟩, then X|−⟩ = −|−⟩ |
| 516 | let _ = sim.h(q); |
| 517 | check_state( |
| 518 | &mut sim, |
| 519 | &expect!["|0⟩: −0.7071+0.0000𝑖 |1⟩: 0.7071+0.0000𝑖 "], |
| 520 | ); |
| 521 | } |
| 522 | |
| 523 | // Test loss via noise config |
| 524 | |
| 525 | #[test] |
| 526 | fn noise_config_mz_with_loss() { |
| 527 | let mut config = NoiseConfig::NOISELESS; |
| 528 | config.mz = NoiseTable { |
| 529 | qubits: 1, |
| 530 | pauli_strings: vec![], |
| 531 | probabilities: vec![], |
| 532 | loss: 1.0, |
| 533 | }; |
| 534 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 535 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 536 | let res = sim.m(q).expect("sparse simulator is infinite"); |
| 537 | assert_eq!( |
| 538 | res, |
| 539 | val::Result::Loss, |
| 540 | "Expected measurement with 100% loss to return Loss" |
| 541 | ); |
| 542 | } |
| 543 | |
| 544 | #[test] |
| 545 | fn noise_config_gate_loss_causes_measurement_loss() { |
| 546 | // Configure 100% loss on X gate. After X, qubit is lost. |
| 547 | // Measurement of a lost qubit should return Loss. |
| 548 | let mut config = NoiseConfig::NOISELESS; |
| 549 | config.x = NoiseTable { |
| 550 | qubits: 1, |
| 551 | pauli_strings: vec![], |
| 552 | probabilities: vec![], |
| 553 | loss: 1.0, |
| 554 | }; |
| 555 | let mut sim = SparseSim::new_with_noise_config(config.into()); |
| 556 | let q = sim.qubit_allocate().expect("sparse simulator is infinite"); |
| 557 | let _ = sim.x(q); |
| 558 | let res = sim.m(q).expect("sparse simulator is infinite"); |
| 559 | assert_eq!( |
| 560 | res, |
| 561 | val::Result::Loss, |
| 562 | "Expected measurement after gate loss to return Loss" |
| 563 | ); |
| 564 | } |
| 565 | |