1use crate::states::State;
5use ndarray::Array1;
6use num_complex::Complex64;
7use rand::Rng;
8
9pub trait NoiseModel {
11 fn apply(&self, state: &mut State);
13
14 fn name(&self) -> String;
16}
17
18pub struct NoiseParams {
20 pub error_probability: f64,
22
23 pub target_qubits: Option<Vec<usize>>,
25}
26
27impl NoiseParams {
28 pub fn new(error_probability: f64) -> Self {
29 Self {
30 error_probability,
31 target_qubits: None,
32 }
33 }
34
35 pub fn with_target_qubits(mut self, qubits: Vec<usize>) -> Self {
36 self.target_qubits = Some(qubits);
37 self
38 }
39}
40
41pub struct DepolarizingNoise {
43 params: NoiseParams,
44}
45
46impl DepolarizingNoise {
47 pub fn new(error_probability: f64) -> Self {
48 Self {
49 params: NoiseParams::new(error_probability),
50 }
51 }
52
53 pub fn with_target_qubits(self, qubits: Vec<usize>) -> Self {
54 Self {
55 params: self.params.with_target_qubits(qubits),
56 }
57 }
58}
59
60impl NoiseModel for DepolarizingNoise {
61 fn apply(&self, state: &mut State) {
62 let mut rng = rand::thread_rng();
63
64 let target_qubits = match &self.params.target_qubits {
66 Some(qubits) => qubits.clone(),
67 None => (0..state.num_qubits()).collect(),
68 };
69
70 for &qubit in &target_qubits {
71 if rng.gen::<f64>() < self.params.error_probability {
72 let error_type = rng.gen_range(0..3);
74 match error_type {
75 0 => apply_x_error(state, qubit), 1 => apply_y_error(state, qubit), 2 => apply_z_error(state, qubit), _ => unreachable!(),
79 }
80 }
81 }
82 }
83
84 fn name(&self) -> String {
85 format!("Depolarizing(p={})", self.params.error_probability)
86 }
87}
88
89pub struct AmplitudeDampingNoise {
91 params: NoiseParams,
92}
93
94impl AmplitudeDampingNoise {
95 pub fn new(error_probability: f64) -> Self {
96 Self {
97 params: NoiseParams::new(error_probability),
98 }
99 }
100
101 pub fn with_target_qubits(self, qubits: Vec<usize>) -> Self {
102 Self {
103 params: self.params.with_target_qubits(qubits),
104 }
105 }
106}
107
108impl NoiseModel for AmplitudeDampingNoise {
109 fn apply(&self, state: &mut State) {
110 let dimension = state.vector().len();
111
112 let target_qubits = match &self.params.target_qubits {
114 Some(qubits) => qubits.clone(),
115 None => (0..state.num_qubits()).collect(),
116 };
117
118 let mut new_vector = Array1::<Complex64>::zeros(dimension);
120 let state_vec = state.vector();
121
122 for &qubit in &target_qubits {
123 let gamma = self.params.error_probability;
124 let sqrt_gamma = gamma.sqrt();
125
126 for i in 0..dimension {
131 if (i & (1 << qubit)) == 0 {
132 new_vector[i] += state_vec[i];
134 } else {
135 new_vector[i] += state_vec[i] * Complex64::new((1.0 - gamma).sqrt(), 0.0);
137
138 let j = i & !(1 << qubit); new_vector[j] += state_vec[i] * Complex64::new(sqrt_gamma, 0.0);
141 }
142 }
143 }
144
145 *state.vector_mut() = new_vector;
146 state.normalize();
147 }
148
149 fn name(&self) -> String {
150 format!("AmplitudeDamping(p={})", self.params.error_probability)
151 }
152}
153
154pub struct PhaseDampingNoise {
156 params: NoiseParams,
157}
158
159impl PhaseDampingNoise {
160 pub fn new(error_probability: f64) -> Self {
161 Self {
162 params: NoiseParams::new(error_probability),
163 }
164 }
165
166 pub fn with_target_qubits(self, qubits: Vec<usize>) -> Self {
167 Self {
168 params: self.params.with_target_qubits(qubits),
169 }
170 }
171}
172
173impl NoiseModel for PhaseDampingNoise {
174 fn apply(&self, state: &mut State) {
175 let dimension = state.vector().len();
176
177 let target_qubits = match &self.params.target_qubits {
179 Some(qubits) => qubits.clone(),
180 None => (0..state.num_qubits()).collect(),
181 };
182
183 let mut new_vector = ndarray::Array1::zeros(dimension);
185 let state_vec = state.vector();
186
187 for &qubit in &target_qubits {
188 let lambda = self.params.error_probability;
193 let sqrt_1_minus_lambda = (1.0 - lambda).sqrt();
194
195 for i in 0..dimension {
196 new_vector[i] = state_vec[i];
198
199 if (i & (1 << qubit)) != 0 {
201 new_vector[i] *= Complex64::new(sqrt_1_minus_lambda, 0.0);
203 }
204 }
205 }
206
207 *state.vector_mut() = new_vector;
209 state.normalize();
210 }
211
212 fn name(&self) -> String {
213 format!("PhaseDamping(p={})", self.params.error_probability)
214 }
215}
216
217pub struct ThermalRelaxationNoise {
219 t1: f64,
221
222 t2: f64,
224
225 gate_time: f64,
227
228 target_qubits: Option<Vec<usize>>,
230}
231
232impl ThermalRelaxationNoise {
233 pub fn new(t1: f64, t2: f64, gate_time: f64) -> Self {
234 let t2 = t2.min(2.0 * t1);
236
237 Self {
238 t1,
239 t2,
240 gate_time,
241 target_qubits: None,
242 }
243 }
244
245 pub fn with_target_qubits(mut self, qubits: Vec<usize>) -> Self {
246 self.target_qubits = Some(qubits);
247 self
248 }
249}
250
251impl NoiseModel for ThermalRelaxationNoise {
252 fn apply(&self, state: &mut State) {
253 let p_amplitude = 1.0 - (-self.gate_time / self.t1).exp();
256
257 let pure_dephasing_rate = if self.t2 <= 2.0 * self.t1 {
260 1.0 / self.t2 - 1.0 / (2.0 * self.t1)
261 } else {
262 0.0
264 };
265
266 let p_phase = if pure_dephasing_rate > 0.0 {
268 1.0 - (-self.gate_time * pure_dephasing_rate).exp()
269 } else {
270 0.0
271 };
272
273 let target_qubits = self
275 .target_qubits
276 .clone()
277 .unwrap_or_else(|| (0..state.num_qubits()).collect());
278
279 if p_amplitude > 0.0 {
281 let amplitude_noise =
282 AmplitudeDampingNoise::new(p_amplitude).with_target_qubits(target_qubits.clone());
283 amplitude_noise.apply(state);
284 }
285
286 if p_phase > 0.0 {
288 let phase_noise = PhaseDampingNoise::new(p_phase).with_target_qubits(target_qubits);
289 phase_noise.apply(state);
290 }
291 }
292
293 fn name(&self) -> String {
294 format!(
295 "ThermalRelaxation(T1={}, T2={}, gate_time={})",
296 self.t1, self.t2, self.gate_time
297 )
298 }
299}
300
301pub struct CompositeNoise {
303 noise_models: Vec<Box<dyn NoiseModel>>,
304}
305
306impl CompositeNoise {
307 pub fn new() -> Self {
308 Self {
309 noise_models: Vec::new(),
310 }
311 }
312
313 pub fn add_noise<N: NoiseModel + 'static>(&mut self, noise_model: N) -> &mut Self {
314 self.noise_models.push(Box::new(noise_model));
315 self
316 }
317}
318
319impl NoiseModel for CompositeNoise {
320 fn apply(&self, state: &mut State) {
321 for noise_model in &self.noise_models {
323 let mut temp_state = state.clone();
325 noise_model.apply(&mut temp_state);
326 *state = temp_state;
327 }
328 }
329
330 fn name(&self) -> String {
331 let names: Vec<String> = self.noise_models.iter().map(|model| model.name()).collect();
332 format!("Composite({})", names.join(", "))
333 }
334}
335
336fn apply_x_error(state: &mut State, qubit: usize) {
338 let dimension = state.vector().len();
339 let state_vec = state.vector();
340 let mut new_vector = state_vec.clone();
341
342 for i in 0..dimension {
343 let j = i ^ (1 << qubit); new_vector[i] = state_vec[j];
345 }
346
347 *state.vector_mut() = new_vector;
348}
349
350fn apply_y_error(state: &mut State, qubit: usize) {
351 let dimension = state.vector().len();
352 let state_vec = state.vector();
353 let mut new_vector = state_vec.clone();
354 let imag_i = Complex64::new(0.0, 1.0);
355
356 for i in 0..dimension {
357 let j = i ^ (1 << qubit); if (i & (1 << qubit)) != 0 {
361 new_vector[i] = -imag_i * state_vec[j];
362 } else {
363 new_vector[i] = imag_i * state_vec[j];
364 }
365 }
366
367 *state.vector_mut() = new_vector;
368}
369
370fn apply_z_error(state: &mut State, qubit: usize) {
371 let dimension = state.vector().len();
372 let vector_slice = state.vector_mut().as_slice_mut().unwrap();
373
374 for i in 0..dimension {
375 if (i & (1 << qubit)) != 0 {
377 vector_slice[i] = -vector_slice[i];
378 }
379 }
380}