1#![allow(clippy::needless_range_loop)]
8
9use scirs2_core::Complex64;
10use std::fmt::Debug;
11
12use quantrs2_core::error::QuantRS2Result;
13use quantrs2_core::qubit::QubitId;
14
15#[derive(Debug, Clone)]
17pub enum NoiseChannelType {
18 BitFlip(BitFlipChannel),
19 PhaseFlip(PhaseFlipChannel),
20 Depolarizing(DepolarizingChannel),
21 AmplitudeDamping(AmplitudeDampingChannel),
22 PhaseDamping(PhaseDampingChannel),
23}
24
25impl NoiseChannelType {
26 #[must_use]
28 pub fn name(&self) -> &'static str {
29 match self {
30 Self::BitFlip(ch) => ch.name(),
31 Self::PhaseFlip(ch) => ch.name(),
32 Self::Depolarizing(ch) => ch.name(),
33 Self::AmplitudeDamping(ch) => ch.name(),
34 Self::PhaseDamping(ch) => ch.name(),
35 }
36 }
37
38 pub fn normalize_state(state: &mut [Complex64]) {
40 let mut norm_squared = 0.0;
42 for amp in state.iter() {
43 norm_squared += amp.norm_sqr();
44 }
45
46 if (norm_squared - 1.0).abs() > 1e-10 {
48 let norm = norm_squared.sqrt();
49 for amp in state.iter_mut() {
50 *amp /= Complex64::new(norm, 0.0);
51 }
52 }
53 }
54
55 #[must_use]
57 pub fn qubits(&self) -> Vec<QubitId> {
58 match self {
59 Self::BitFlip(ch) => ch.qubits(),
60 Self::PhaseFlip(ch) => ch.qubits(),
61 Self::Depolarizing(ch) => ch.qubits(),
62 Self::AmplitudeDamping(ch) => ch.qubits(),
63 Self::PhaseDamping(ch) => ch.qubits(),
64 }
65 }
66
67 pub fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
69 match self {
70 Self::BitFlip(ch) => ch.apply_to_statevector(state),
71 Self::PhaseFlip(ch) => ch.apply_to_statevector(state),
72 Self::Depolarizing(ch) => ch.apply_to_statevector(state),
73 Self::AmplitudeDamping(ch) => ch.apply_to_statevector(state),
74 Self::PhaseDamping(ch) => ch.apply_to_statevector(state),
75 }
76 }
77
78 #[must_use]
80 pub fn probability(&self) -> f64 {
81 match self {
82 Self::BitFlip(ch) => ch.probability(),
83 Self::PhaseFlip(ch) => ch.probability(),
84 Self::Depolarizing(ch) => ch.probability(),
85 Self::AmplitudeDamping(ch) => ch.probability(),
86 Self::PhaseDamping(ch) => ch.probability(),
87 }
88 }
89}
90
91pub trait NoiseChannel: Debug + Clone {
93 fn name(&self) -> &'static str;
95
96 fn qubits(&self) -> Vec<QubitId>;
98
99 fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()>;
101
102 fn kraus_operators(&self) -> Vec<Vec<Complex64>>;
104
105 fn probability(&self) -> f64;
107}
108
109#[derive(Debug, Clone)]
111pub struct BitFlipChannel {
112 pub target: QubitId,
114
115 pub probability: f64,
117}
118
119impl NoiseChannel for BitFlipChannel {
120 fn name(&self) -> &'static str {
121 "BitFlip"
122 }
123
124 fn qubits(&self) -> Vec<QubitId> {
125 vec![self.target]
126 }
127
128 fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
129 let target_idx = self.target.id() as usize;
130 let dim = state.len();
131
132 if fastrand::f64() < self.probability {
134 let state_copy = state.to_vec();
136
137 for i in 0..dim {
139 let flipped_i = i ^ (1 << target_idx);
140 state[i] = state_copy[flipped_i];
141 }
142 }
143
144 Ok(())
145 }
146
147 fn kraus_operators(&self) -> Vec<Vec<Complex64>> {
148 let p = self.probability;
151 let sqrt_1_minus_p = (1.0 - p).sqrt();
152 let sqrt_p = p.sqrt();
153
154 let k0 = vec![
156 Complex64::new(sqrt_1_minus_p, 0.0),
157 Complex64::new(0.0, 0.0),
158 Complex64::new(0.0, 0.0),
159 Complex64::new(sqrt_1_minus_p, 0.0),
160 ];
161
162 let k1 = vec![
164 Complex64::new(0.0, 0.0),
165 Complex64::new(sqrt_p, 0.0),
166 Complex64::new(sqrt_p, 0.0),
167 Complex64::new(0.0, 0.0),
168 ];
169
170 vec![k0, k1]
171 }
172
173 fn probability(&self) -> f64 {
174 self.probability
175 }
176}
177
178#[derive(Debug, Clone)]
180pub struct PhaseFlipChannel {
181 pub target: QubitId,
183
184 pub probability: f64,
186}
187
188impl NoiseChannel for PhaseFlipChannel {
189 fn name(&self) -> &'static str {
190 "PhaseFlip"
191 }
192
193 fn qubits(&self) -> Vec<QubitId> {
194 vec![self.target]
195 }
196
197 fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
198 let target_idx = self.target.id() as usize;
199 let dim = state.len();
200
201 if fastrand::f64() < self.probability {
203 for i in 0..dim {
205 if (i >> target_idx) & 1 == 1 {
206 state[i] = -state[i];
208 }
209 }
210 }
211
212 Ok(())
213 }
214
215 fn kraus_operators(&self) -> Vec<Vec<Complex64>> {
216 let p = self.probability;
219 let sqrt_1_minus_p = (1.0 - p).sqrt();
220 let sqrt_p = p.sqrt();
221
222 let k0 = vec![
224 Complex64::new(sqrt_1_minus_p, 0.0),
225 Complex64::new(0.0, 0.0),
226 Complex64::new(0.0, 0.0),
227 Complex64::new(sqrt_1_minus_p, 0.0),
228 ];
229
230 let k1 = vec![
232 Complex64::new(sqrt_p, 0.0),
233 Complex64::new(0.0, 0.0),
234 Complex64::new(0.0, 0.0),
235 Complex64::new(-sqrt_p, 0.0),
236 ];
237
238 vec![k0, k1]
239 }
240
241 fn probability(&self) -> f64 {
242 self.probability
243 }
244}
245
246#[derive(Debug, Clone)]
248pub struct DepolarizingChannel {
249 pub target: QubitId,
251
252 pub probability: f64,
254}
255
256impl NoiseChannel for DepolarizingChannel {
257 fn name(&self) -> &'static str {
258 "Depolarizing"
259 }
260
261 fn qubits(&self) -> Vec<QubitId> {
262 vec![self.target]
263 }
264
265 fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
266 let target_idx = self.target.id() as usize;
267 let dim = state.len();
268
269 if fastrand::f64() < self.probability {
271 let error_type = fastrand::u32(..) % 3;
273
274 let state_copy = state.to_vec();
276
277 match error_type {
278 0 => {
279 for i in 0..dim {
281 let flipped_i = i ^ (1 << target_idx);
282 state[i] = state_copy[flipped_i];
283 }
284 }
285 1 => {
286 for i in 0..dim {
288 let flipped_i = i ^ (1 << target_idx);
289 let phase = if (i >> target_idx) & 1 == 1 {
290 -1.0
291 } else {
292 1.0
293 };
294 state[i] = state_copy[flipped_i] * Complex64::new(0.0, phase);
295 }
296 }
297 2 => {
298 for i in 0..dim {
300 if (i >> target_idx) & 1 == 1 {
301 state[i] = -state_copy[i];
302 } else {
303 state[i] = state_copy[i];
304 }
305 }
306 }
307 _ => unreachable!(),
308 }
309 }
310
311 Ok(())
312 }
313
314 fn kraus_operators(&self) -> Vec<Vec<Complex64>> {
315 let p = self.probability;
321 let sqrt_1_minus_3p_4 = (1.0 - 3.0 * p / 4.0).sqrt();
322 let sqrt_p_4 = (p / 4.0).sqrt();
323
324 let k0 = vec![
326 Complex64::new(sqrt_1_minus_3p_4, 0.0),
327 Complex64::new(0.0, 0.0),
328 Complex64::new(0.0, 0.0),
329 Complex64::new(sqrt_1_minus_3p_4, 0.0),
330 ];
331
332 let k1 = vec![
334 Complex64::new(0.0, 0.0),
335 Complex64::new(sqrt_p_4, 0.0),
336 Complex64::new(sqrt_p_4, 0.0),
337 Complex64::new(0.0, 0.0),
338 ];
339
340 let k2 = vec![
342 Complex64::new(0.0, 0.0),
343 Complex64::new(0.0, -sqrt_p_4),
344 Complex64::new(0.0, sqrt_p_4),
345 Complex64::new(0.0, 0.0),
346 ];
347
348 let k3 = vec![
350 Complex64::new(sqrt_p_4, 0.0),
351 Complex64::new(0.0, 0.0),
352 Complex64::new(0.0, 0.0),
353 Complex64::new(-sqrt_p_4, 0.0),
354 ];
355
356 vec![k0, k1, k2, k3]
357 }
358
359 fn probability(&self) -> f64 {
360 self.probability
361 }
362}
363
364#[derive(Debug, Clone)]
366pub struct AmplitudeDampingChannel {
367 pub target: QubitId,
369
370 pub gamma: f64,
372}
373
374impl NoiseChannel for AmplitudeDampingChannel {
375 fn name(&self) -> &'static str {
376 "AmplitudeDamping"
377 }
378
379 fn qubits(&self) -> Vec<QubitId> {
380 vec![self.target]
381 }
382
383 fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
384 let target_idx = self.target.id() as usize;
385 let dim = state.len();
386
387 let state_copy = state.to_vec();
389
390 for i in 0..dim {
392 if (i >> target_idx) & 1 == 1 {
393 let base_idx = i & !(1 << target_idx); if fastrand::f64() < self.gamma {
398 state[base_idx] += state_copy[i];
400 state[i] = Complex64::new(0.0, 0.0);
401 } else {
402 state[i] = state_copy[i] * Complex64::new((1.0 - self.gamma).sqrt(), 0.0);
404 }
405 }
406 }
407
408 Ok(())
409 }
410
411 fn kraus_operators(&self) -> Vec<Vec<Complex64>> {
412 let gamma = self.gamma;
416 let sqrt_1_minus_gamma = (1.0 - gamma).sqrt();
417 let sqrt_gamma = gamma.sqrt();
418
419 let k0 = vec![
421 Complex64::new(1.0, 0.0),
422 Complex64::new(0.0, 0.0),
423 Complex64::new(0.0, 0.0),
424 Complex64::new(sqrt_1_minus_gamma, 0.0),
425 ];
426
427 let k1 = vec![
429 Complex64::new(0.0, 0.0),
430 Complex64::new(sqrt_gamma, 0.0),
431 Complex64::new(0.0, 0.0),
432 Complex64::new(0.0, 0.0),
433 ];
434
435 vec![k0, k1]
436 }
437
438 fn probability(&self) -> f64 {
439 self.gamma
440 }
441}
442
443#[derive(Debug, Clone)]
445pub struct PhaseDampingChannel {
446 pub target: QubitId,
448
449 pub lambda: f64,
451}
452
453impl NoiseChannel for PhaseDampingChannel {
454 fn name(&self) -> &'static str {
455 "PhaseDamping"
456 }
457
458 fn qubits(&self) -> Vec<QubitId> {
459 vec![self.target]
460 }
461
462 fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
463 let target_idx = self.target.id() as usize;
464 let dim = state.len();
465
466 for i in 0..dim {
468 if (i >> target_idx) & 1 == 1 {
469 if fastrand::f64() < self.lambda {
472 let phase = 2.0 * std::f64::consts::PI * fastrand::f64();
474 state[i] *= Complex64::new(phase.cos(), phase.sin());
475 }
476 }
477 }
478
479 Ok(())
480 }
481
482 fn kraus_operators(&self) -> Vec<Vec<Complex64>> {
483 let lambda = self.lambda;
487 let sqrt_1_minus_lambda = (1.0 - lambda).sqrt();
488 let sqrt_lambda = lambda.sqrt();
489
490 let k0 = vec![
492 Complex64::new(1.0, 0.0),
493 Complex64::new(0.0, 0.0),
494 Complex64::new(0.0, 0.0),
495 Complex64::new(sqrt_1_minus_lambda, 0.0),
496 ];
497
498 let k1 = vec![
500 Complex64::new(0.0, 0.0),
501 Complex64::new(0.0, 0.0),
502 Complex64::new(0.0, 0.0),
503 Complex64::new(sqrt_lambda, 0.0),
504 ];
505
506 vec![k0, k1]
507 }
508
509 fn probability(&self) -> f64 {
510 self.lambda
511 }
512}
513
514#[derive(Debug, Clone)]
516pub struct NoiseModel {
517 pub channels: Vec<NoiseChannelType>,
519
520 pub per_gate: bool,
522}
523
524impl NoiseModel {
525 #[must_use]
527 pub const fn new(per_gate: bool) -> Self {
528 Self {
529 channels: Vec::new(),
530 per_gate,
531 }
532 }
533
534 pub fn add_bit_flip(&mut self, channel: BitFlipChannel) -> &mut Self {
536 self.channels.push(NoiseChannelType::BitFlip(channel));
537 self
538 }
539
540 pub fn add_phase_flip(&mut self, channel: PhaseFlipChannel) -> &mut Self {
542 self.channels.push(NoiseChannelType::PhaseFlip(channel));
543 self
544 }
545
546 pub fn add_depolarizing(&mut self, channel: DepolarizingChannel) -> &mut Self {
548 self.channels.push(NoiseChannelType::Depolarizing(channel));
549 self
550 }
551
552 pub fn add_amplitude_damping(&mut self, channel: AmplitudeDampingChannel) -> &mut Self {
554 self.channels
555 .push(NoiseChannelType::AmplitudeDamping(channel));
556 self
557 }
558
559 pub fn add_phase_damping(&mut self, channel: PhaseDampingChannel) -> &mut Self {
561 self.channels.push(NoiseChannelType::PhaseDamping(channel));
562 self
563 }
564
565 pub fn apply_to_statevector(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
567 for channel in &self.channels {
568 channel.apply_to_statevector(state)?;
569 }
570
571 NoiseChannelType::normalize_state(state);
573
574 Ok(())
575 }
576
577 #[must_use]
579 pub fn num_channels(&self) -> usize {
580 self.channels.len()
581 }
582}
583
584impl Default for NoiseModel {
585 fn default() -> Self {
586 Self::new(true)
587 }
588}
589
590pub struct NoiseModelBuilder {
592 model: NoiseModel,
593}
594
595impl NoiseModelBuilder {
596 #[must_use]
598 pub const fn new(per_gate: bool) -> Self {
599 Self {
600 model: NoiseModel::new(per_gate),
601 }
602 }
603
604 #[must_use]
606 pub fn with_depolarizing_noise(mut self, qubits: &[QubitId], probability: f64) -> Self {
607 for &qubit in qubits {
608 self.model.add_depolarizing(DepolarizingChannel {
609 target: qubit,
610 probability,
611 });
612 }
613 self
614 }
615
616 #[must_use]
618 pub fn with_bit_flip_noise(mut self, qubits: &[QubitId], probability: f64) -> Self {
619 for &qubit in qubits {
620 self.model.add_bit_flip(BitFlipChannel {
621 target: qubit,
622 probability,
623 });
624 }
625 self
626 }
627
628 #[must_use]
630 pub fn with_phase_flip_noise(mut self, qubits: &[QubitId], probability: f64) -> Self {
631 for &qubit in qubits {
632 self.model.add_phase_flip(PhaseFlipChannel {
633 target: qubit,
634 probability,
635 });
636 }
637 self
638 }
639
640 #[must_use]
642 pub fn with_amplitude_damping(mut self, qubits: &[QubitId], gamma: f64) -> Self {
643 for &qubit in qubits {
644 self.model.add_amplitude_damping(AmplitudeDampingChannel {
645 target: qubit,
646 gamma,
647 });
648 }
649 self
650 }
651
652 #[must_use]
654 pub fn with_phase_damping(mut self, qubits: &[QubitId], lambda: f64) -> Self {
655 for &qubit in qubits {
656 self.model.add_phase_damping(PhaseDampingChannel {
657 target: qubit,
658 lambda,
659 });
660 }
661 self
662 }
663
664 #[must_use]
666 pub fn build(self) -> NoiseModel {
667 self.model
668 }
669}