quantrs2_sim/
optimized_simple.rs1use scirs2_core::Complex64;
7
8use crate::utils::flip_bit;
9
10pub struct OptimizedStateVector {
12 state: Vec<Complex64>,
14 num_qubits: usize,
16}
17
18impl OptimizedStateVector {
19 #[must_use]
21 pub fn new(num_qubits: usize) -> Self {
22 let dim = 1 << num_qubits;
23 let mut state = vec![Complex64::new(0.0, 0.0); dim];
24 state[0] = Complex64::new(1.0, 0.0); Self { state, num_qubits }
27 }
28
29 #[must_use]
31 pub fn state(&self) -> &[Complex64] {
32 &self.state
33 }
34
35 pub fn state_mut(&mut self) -> &mut [Complex64] {
37 &mut self.state
38 }
39
40 #[must_use]
42 pub const fn num_qubits(&self) -> usize {
43 self.num_qubits
44 }
45
46 #[must_use]
48 pub const fn dimension(&self) -> usize {
49 1 << self.num_qubits
50 }
51
52 pub fn apply_single_qubit_gate(&mut self, matrix: &[Complex64], target: usize) {
59 assert!(
60 (target < self.num_qubits),
61 "Target qubit index out of range"
62 );
63
64 let dim = self.state.len();
65 let mut new_state = vec![Complex64::new(0.0, 0.0); dim];
66
67 for i in 0..dim {
69 let bit_val = (i >> target) & 1;
70
71 if bit_val == 0 {
73 let paired_idx = flip_bit(i, target);
74
75 let a0 = self.state[i]; let a1 = self.state[paired_idx]; new_state[i] = matrix[0] * a0 + matrix[1] * a1;
84 new_state[paired_idx] = matrix[2] * a0 + matrix[3] * a1;
85 }
86 }
87
88 self.state = new_state;
89 }
90
91 pub fn apply_cnot(&mut self, control: usize, target: usize) {
98 assert!(
99 !(control >= self.num_qubits || target >= self.num_qubits),
100 "Qubit indices out of range"
101 );
102
103 assert!(
104 (control != target),
105 "Control and target qubits must be different"
106 );
107
108 let dim = self.state.len();
109 let mut new_state = vec![Complex64::new(0.0, 0.0); dim];
110
111 for (i, val) in new_state.iter_mut().enumerate().take(dim) {
113 let control_bit = (i >> control) & 1;
114
115 if control_bit == 0 {
116 *val = self.state[i];
118 } else {
119 let flipped_idx = flip_bit(i, target);
121 *val = self.state[flipped_idx];
122 }
123 }
124
125 self.state = new_state;
126 }
127
128 pub fn apply_two_qubit_gate(&mut self, matrix: &[Complex64], qubit1: usize, qubit2: usize) {
136 assert!(
137 !(qubit1 >= self.num_qubits || qubit2 >= self.num_qubits),
138 "Qubit indices out of range"
139 );
140
141 assert!((qubit1 != qubit2), "Qubit indices must be different");
142
143 let dim = self.state.len();
144 let mut new_state = vec![Complex64::new(0.0, 0.0); dim];
145
146 for (i, val) in new_state.iter_mut().enumerate().take(dim) {
148 let bit1 = (i >> qubit1) & 1;
150 let bit2 = (i >> qubit2) & 1;
151 let subspace_idx = (bit1 << 1) | bit2;
152
153 let bits00 = i & !(1 << qubit1) & !(1 << qubit2);
155 let bits01 = bits00 | (1 << qubit2);
156 let bits10 = bits00 | (1 << qubit1);
157 let bits11 = bits10 | (1 << qubit2);
158
159 *val = matrix[subspace_idx * 4] * self.state[bits00]
161 + matrix[subspace_idx * 4 + 1] * self.state[bits01]
162 + matrix[subspace_idx * 4 + 2] * self.state[bits10]
163 + matrix[subspace_idx * 4 + 3] * self.state[bits11];
164 }
165
166 self.state = new_state;
167 }
168
169 #[must_use]
171 pub fn probability(&self, bit_string: &[u8]) -> f64 {
172 assert!(
173 (bit_string.len() == self.num_qubits),
174 "Bit string length must match number of qubits"
175 );
176
177 let mut idx = 0;
179 for (i, &bit) in bit_string.iter().enumerate() {
180 if bit != 0 {
181 idx |= 1 << i;
182 }
183 }
184
185 self.state[idx].norm_sqr()
187 }
188
189 #[must_use]
191 pub fn probabilities(&self) -> Vec<f64> {
192 self.state
193 .iter()
194 .map(scirs2_core::Complex::norm_sqr)
195 .collect()
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use std::f64::consts::FRAC_1_SQRT_2;
203
204 #[test]
205 fn test_optimized_state_vector_init() {
206 let sv = OptimizedStateVector::new(2);
207 assert_eq!(sv.num_qubits(), 2);
208 assert_eq!(sv.dimension(), 4);
209
210 assert_eq!(sv.state()[0], Complex64::new(1.0, 0.0));
212 assert_eq!(sv.state()[1], Complex64::new(0.0, 0.0));
213 assert_eq!(sv.state()[2], Complex64::new(0.0, 0.0));
214 assert_eq!(sv.state()[3], Complex64::new(0.0, 0.0));
215 }
216
217 #[test]
218 fn test_hadamard_gate() {
219 let h_matrix = [
221 Complex64::new(FRAC_1_SQRT_2, 0.0),
222 Complex64::new(FRAC_1_SQRT_2, 0.0),
223 Complex64::new(FRAC_1_SQRT_2, 0.0),
224 Complex64::new(-FRAC_1_SQRT_2, 0.0),
225 ];
226
227 let mut sv = OptimizedStateVector::new(2);
229 println!("Initial state: {:?}", sv.state());
230 sv.apply_single_qubit_gate(&h_matrix, 1); println!("After H on qubit 1: {:?}", sv.state());
234
235 assert_eq!(sv.state()[0], Complex64::new(FRAC_1_SQRT_2, 0.0));
237 assert_eq!(sv.state()[1], Complex64::new(0.0, 0.0));
238 assert_eq!(sv.state()[2], Complex64::new(FRAC_1_SQRT_2, 0.0));
239 assert_eq!(sv.state()[3], Complex64::new(0.0, 0.0));
240
241 sv.apply_single_qubit_gate(&h_matrix, 0);
243
244 println!("After both H gates: {:?}", sv.state());
246
247 assert!((sv.state()[0] - Complex64::new(0.5, 0.0)).norm() < 1e-10);
254 assert!((sv.state()[1] - Complex64::new(0.5, 0.0)).norm() < 1e-10);
255 assert!((sv.state()[2] - Complex64::new(0.5, 0.0)).norm() < 1e-10);
256 assert!((sv.state()[3] - Complex64::new(0.5, 0.0)).norm() < 1e-10);
257 }
258
259 #[test]
260 fn test_cnot_gate() {
261 let mut sv = OptimizedStateVector::new(2);
263
264 let h_matrix = [
266 Complex64::new(FRAC_1_SQRT_2, 0.0),
267 Complex64::new(FRAC_1_SQRT_2, 0.0),
268 Complex64::new(FRAC_1_SQRT_2, 0.0),
269 Complex64::new(-FRAC_1_SQRT_2, 0.0),
270 ];
271 sv.apply_single_qubit_gate(&h_matrix, 0);
272
273 sv.apply_cnot(0, 1);
275
276 assert_eq!(sv.state()[0], Complex64::new(FRAC_1_SQRT_2, 0.0));
278 assert_eq!(sv.state()[1], Complex64::new(0.0, 0.0));
279 assert_eq!(sv.state()[2], Complex64::new(0.0, 0.0));
280 assert_eq!(sv.state()[3], Complex64::new(FRAC_1_SQRT_2, 0.0));
281 }
282}