stabilizer_ch_form_rust/form/
discard.rs1use crate::{
2 StabilizerCHForm,
3 error::{Error, Result},
4};
5use ndarray::{Array1, Array2};
6
7impl StabilizerCHForm {
8 pub fn discard(&mut self, qarg: usize) -> Result<()> {
22 if qarg >= self.n {
23 return Err(Error::QubitIndexOutOfBounds(qarg, self.n));
24 }
25
26 self.set_s_v_to_false(qarg)?;
30 self.transform_g(qarg)?;
31 self.transform_m(qarg)?;
32
33 self.n -= 1;
35 self.mat_g = self.remove_row_col_from_matrix(&self.mat_g, qarg);
36 self.mat_f = self.remove_row_col_from_matrix(&self.mat_f, qarg);
37 self.mat_m = self.remove_row_col_from_matrix(&self.mat_m, qarg);
38
39 self.gamma = self.remove_element_from_vector(&self.gamma, qarg);
40 self.vec_v = self.remove_element_from_vector(&self.vec_v, qarg);
41 self.vec_s = self.remove_element_from_vector(&self.vec_s, qarg);
42
43 Ok(())
44 }
45
46 pub fn discarded(&self, qarg: usize) -> Result<StabilizerCHForm> {
58 let mut self_clone = self.clone();
59 self_clone.discard(qarg)?;
60 Ok(self_clone)
61 }
62
63 fn remove_row_col_from_matrix<T: Clone>(&self, matrix: &Array2<T>, index: usize) -> Array2<T> {
65 let mut new_matrix = Array2::from_elem((self.n, self.n), matrix[[0, 0]].clone());
66 let mut new_i = 0;
67 for i in 0..=self.n {
68 if i == index {
69 continue;
70 }
71 let mut new_j = 0;
72 for j in 0..=self.n {
73 if j == index {
74 continue;
75 }
76 new_matrix[[new_i, new_j]] = matrix[[i, j]].clone();
77 new_j += 1;
78 }
79 new_i += 1;
80 }
81 new_matrix
82 }
83
84 fn remove_element_from_vector<T: Clone>(&self, vector: &Array1<T>, index: usize) -> Array1<T> {
86 let mut new_vector = Array1::from_elem(self.n, vector[0].clone());
87 let mut new_i = 0;
88 for i in 0..=self.n {
89 if i == index {
90 continue;
91 }
92 new_vector[new_i] = vector[i].clone();
93 new_i += 1;
94 }
95 new_vector
96 }
97
98 fn set_s_v_to_false(&mut self, qarg: usize) -> Result<()> {
99 if !self.vec_v[qarg] && !self.vec_s[qarg] {
100 return Ok(());
101 }
102
103 let mut clean_index = (0..self.n).find(|&i| i != qarg && !self.vec_v[i] && !self.vec_s[i]);
105 if clean_index.is_none() {
106 let mut candidates = (0..self.n).filter(|&i| !self.vec_v[i] && self.vec_s[i]);
108 if let (Some(f), Some(s)) = (candidates.next(), candidates.next()) {
109 self.right_multiply_cx(f, s)?;
110 self.vec_s[s] = false;
111 clean_index = Some(s);
112 }
113 }
114
115 let target = clean_index.ok_or(Error::CannotDiscardQubit(qarg))?;
117
118 if target == qarg {
120 return Ok(());
121 }
122
123 self.right_multiply_cx(target, qarg)?;
125 self.right_multiply_cx(qarg, target)?;
126 self.right_multiply_cx(target, qarg)?;
127
128 self.vec_v.swap(qarg, target);
129 self.vec_s.swap(qarg, target);
130
131 Ok(())
132 }
133
134 fn transform_g(&mut self, qarg: usize) -> Result<()> {
136 if !self.mat_g[[qarg, qarg]] {
137 if let Some(pivot) = (0..self.n).find(|&i| i != qarg && self.mat_g[[qarg, i]]) {
138 self.right_multiply_cx(qarg, pivot)?;
139 } else {
140 unreachable!("Cannot zero G row due to G matrix state.");
141 }
142 }
143
144 for i in 0..self.n {
146 if i != qarg && self.mat_g[[i, qarg]] {
147 self.left_multiply_cx(qarg, i)?;
148 }
149 }
150
151 for i in 0..self.n {
153 if i != qarg && self.mat_g[[qarg, i]] {
154 if self.vec_v[i] {
155 unreachable!("Cannot zero G column due to v vector state.");
156 }
157 self.right_multiply_cx(i, qarg)?;
158 }
159 }
160 Ok(())
161 }
162
163 fn transform_m(&mut self, qarg: usize) -> Result<()> {
165 for i in 0..self.n {
166 if i != qarg && self.mat_m[[i, qarg]] {
167 self.left_multiply_cx(qarg, i)?;
168 }
169 }
170 if self.mat_m[[qarg, qarg]] {
171 self.left_multiply_sdg(qarg)?;
172 }
173
174 for i in 0..self.n {
175 if i != qarg && self.mat_m[[qarg, i]] {
176 self.right_multiply_cz(qarg, i)?;
177 }
178 }
179
180 Ok(())
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use num_complex::Complex64;
187
188 use crate::circuit::CliffordCircuit;
189 use crate::test_utils::{assert_eq_complex_array1, tensor_statevectors};
190
191 use super::*;
192
193 #[test]
194 fn test_discard() {
195 let mut trials = 0;
196 let mut successes = 0;
197 let num_qubits = 5;
198 while successes < 10 && trials < 1000 {
199 trials += 1;
200 let mut seed = [0u8; 32];
201 let trial_bytes = (trials as u64).to_le_bytes();
202 seed[0..8].copy_from_slice(&trial_bytes);
203 let random_circuit = CliffordCircuit::random_clifford(num_qubits, Some(seed));
204 let mut ch_form = StabilizerCHForm::from_clifford_circuit(&random_circuit).unwrap();
205
206 let qubit_to_discard = num_qubits - 1;
207 if ch_form.project(qubit_to_discard, false).is_err() {
209 continue; }
211 let expected_original_sv = ch_form.to_statevector().unwrap();
212 let zero_state = ndarray::array![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)];
214
215 match ch_form.discard(qubit_to_discard) {
216 Ok(()) => {
217 let sv_after_discard = ch_form.to_statevector().unwrap();
218 let expected_after_discard =
219 tensor_statevectors(&sv_after_discard, &zero_state);
220 assert_eq_complex_array1(&expected_original_sv, &expected_after_discard);
221 successes += 1;
222 }
223 Err(_) => {
224 panic!("Discard failed after successful projection");
226 }
227 }
228 }
229 }
230}