quantrs2_core/
simd_ops.rs1use crate::error::QuantRS2Result;
7use num_complex::Complex64;
8
9pub fn apply_phase_simd(amplitudes: &mut [Complex64], theta: f64) {
15 let phase_factor = Complex64::new(theta.cos(), theta.sin());
16
17 #[cfg(feature = "simd")]
18 {
19 let mut chunks = amplitudes.chunks_exact_mut(4);
21
22 for chunk in &mut chunks {
24 for amp in chunk {
26 *amp *= phase_factor;
27 }
28 }
29
30 let remainder = chunks.into_remainder();
32 for amp in remainder {
33 *amp *= phase_factor;
34 }
35 }
36
37 #[cfg(not(feature = "simd"))]
38 {
39 for amp in amplitudes.iter_mut() {
41 *amp *= phase_factor;
42 }
43 }
44}
45
46pub fn inner_product(state1: &[Complex64], state2: &[Complex64]) -> QuantRS2Result<Complex64> {
50 if state1.len() != state2.len() {
51 return Err(crate::error::QuantRS2Error::InvalidInput(
52 "State vectors must have the same length".to_string(),
53 ));
54 }
55
56 #[cfg(feature = "simd")]
57 {
58 let mut result = Complex64::new(0.0, 0.0);
59
60 let chunks1 = state1.chunks_exact(4);
62 let chunks2 = state2.chunks_exact(4);
63 let remainder1 = chunks1.remainder();
64 let remainder2 = chunks2.remainder();
65
66 for (chunk1, chunk2) in chunks1.zip(chunks2) {
68 for (a, b) in chunk1.iter().zip(chunk2.iter()) {
70 result += a.conj() * b;
71 }
72 }
73
74 for (a, b) in remainder1.iter().zip(remainder2.iter()) {
76 result += a.conj() * b;
77 }
78
79 Ok(result)
80 }
81
82 #[cfg(not(feature = "simd"))]
83 {
84 let result = state1
86 .iter()
87 .zip(state2.iter())
88 .map(|(a, b)| a.conj() * b)
89 .sum();
90
91 Ok(result)
92 }
93}
94
95pub fn normalize_simd(amplitudes: &mut [Complex64]) -> QuantRS2Result<()> {
99 let norm_sqr: f64 = {
100 #[cfg(feature = "simd")]
101 {
102 let mut norm_sqr = 0.0;
104
105 let chunks = amplitudes.chunks_exact(4);
107 let remainder = chunks.remainder();
108
109 for chunk in chunks {
111 for amp in chunk {
113 norm_sqr += amp.norm_sqr();
114 }
115 }
116
117 for amp in remainder {
119 norm_sqr += amp.norm_sqr();
120 }
121
122 norm_sqr
123 }
124
125 #[cfg(not(feature = "simd"))]
126 {
127 amplitudes.iter().map(|c| c.norm_sqr()).sum()
128 }
129 };
130
131 if norm_sqr == 0.0 {
132 return Err(crate::error::QuantRS2Error::InvalidInput(
133 "Cannot normalize zero vector".to_string(),
134 ));
135 }
136
137 let norm = norm_sqr.sqrt();
138
139 #[cfg(feature = "simd")]
141 {
142 let mut chunks = amplitudes.chunks_exact_mut(4);
144
145 for chunk in &mut chunks {
147 for amp in chunk {
148 *amp /= norm;
149 }
150 }
151
152 let remainder = chunks.into_remainder();
154 for amp in remainder {
155 *amp /= norm;
156 }
157 }
158
159 #[cfg(not(feature = "simd"))]
160 {
161 for amp in amplitudes.iter_mut() {
162 *amp /= norm;
163 }
164 }
165
166 Ok(())
167}
168
169pub fn expectation_z_simd(amplitudes: &[Complex64], qubit: usize, _num_qubits: usize) -> f64 {
173 let qubit_mask = 1 << qubit;
174 let mut expectation = 0.0;
175
176 #[cfg(feature = "simd")]
177 {
178 for (i, amp) in amplitudes.iter().enumerate() {
180 let sign = if (i & qubit_mask) == 0 { 1.0 } else { -1.0 };
181 expectation += sign * amp.norm_sqr();
182 }
183 }
184
185 #[cfg(not(feature = "simd"))]
186 {
187 for (i, amp) in amplitudes.iter().enumerate() {
188 let sign = if (i & qubit_mask) == 0 { 1.0 } else { -1.0 };
189 expectation += sign * amp.norm_sqr();
190 }
191 }
192
193 expectation
194}
195
196pub fn hadamard_simd(amplitudes: &mut [Complex64], qubit: usize, _num_qubits: usize) {
200 let qubit_mask = 1 << qubit;
201 let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
202
203 #[cfg(feature = "simd")]
204 {
205 for i in 0..(amplitudes.len() / 2) {
207 let idx0 = (i & !(qubit_mask >> 1)) | ((i & (qubit_mask >> 1)) << 1);
208 let idx1 = idx0 | qubit_mask;
209
210 if idx1 < amplitudes.len() {
211 let a0 = amplitudes[idx0];
212 let a1 = amplitudes[idx1];
213
214 amplitudes[idx0] = (a0 + a1) * sqrt2_inv;
215 amplitudes[idx1] = (a0 - a1) * sqrt2_inv;
216 }
217 }
218 }
219
220 #[cfg(not(feature = "simd"))]
221 {
222 for i in 0..(amplitudes.len() / 2) {
223 let idx0 = (i & !(qubit_mask >> 1)) | ((i & (qubit_mask >> 1)) << 1);
224 let idx1 = idx0 | qubit_mask;
225
226 if idx1 < amplitudes.len() {
227 let a0 = amplitudes[idx0];
228 let a1 = amplitudes[idx1];
229
230 amplitudes[idx0] = (a0 + a1) * sqrt2_inv;
231 amplitudes[idx1] = (a0 - a1) * sqrt2_inv;
232 }
233 }
234 }
235}
236
237pub fn controlled_phase_simd(
241 amplitudes: &mut [Complex64],
242 control_qubit: usize,
243 target_qubit: usize,
244 theta: f64,
245) -> QuantRS2Result<()> {
246 let num_qubits = (amplitudes.len() as f64).log2() as usize;
247
248 if control_qubit >= num_qubits || target_qubit >= num_qubits {
249 return Err(crate::error::QuantRS2Error::InvalidInput(
250 "Qubit index out of range".to_string(),
251 ));
252 }
253
254 let phase_factor = Complex64::new(theta.cos(), theta.sin());
255 let control_mask = 1 << control_qubit;
256 let target_mask = 1 << target_qubit;
257
258 for (idx, amp) in amplitudes.iter_mut().enumerate() {
260 if (idx & control_mask) != 0 && (idx & target_mask) != 0 {
261 *amp *= phase_factor;
262 }
263 }
264
265 Ok(())
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_normalize_simd() {
274 let mut state = vec![
275 Complex64::new(1.0, 0.0),
276 Complex64::new(0.0, 1.0),
277 Complex64::new(1.0, 0.0),
278 Complex64::new(0.0, -1.0),
279 ];
280
281 normalize_simd(&mut state).unwrap();
282
283 let norm_sqr: f64 = state.iter().map(|c| c.norm_sqr()).sum();
284 assert!((norm_sqr - 1.0).abs() < 1e-10);
285 }
286
287 #[test]
288 fn test_inner_product() {
289 let state1 = vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)];
290 let state2 = vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)];
291
292 let result = inner_product(&state1, &state2).unwrap();
293 assert_eq!(result, Complex64::new(0.0, 0.0));
294 }
295
296 #[test]
297 fn test_expectation_z() {
298 let state0 = vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)];
300 let exp0 = expectation_z_simd(&state0, 0, 1);
301 assert!((exp0 - 1.0).abs() < 1e-10);
302
303 let state1 = vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)];
305 let exp1 = expectation_z_simd(&state1, 0, 1);
306 assert!((exp1 + 1.0).abs() < 1e-10);
307
308 let sqrt2_inv = 1.0 / std::f64::consts::SQRT_2;
310 let state_plus = vec![
311 Complex64::new(sqrt2_inv, 0.0),
312 Complex64::new(sqrt2_inv, 0.0),
313 ];
314 let exp_plus = expectation_z_simd(&state_plus, 0, 1);
315 assert!(exp_plus.abs() < 1e-10);
316 }
317
318 #[test]
319 fn test_hadamard_simd() {
320 let mut state = vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)];
322 hadamard_simd(&mut state, 0, 1);
323
324 let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
325 assert!((state[0].re - sqrt2_inv).abs() < 1e-10);
326 assert!((state[1].re - sqrt2_inv).abs() < 1e-10);
327 assert!(state[0].im.abs() < 1e-10);
328 assert!(state[1].im.abs() < 1e-10);
329
330 hadamard_simd(&mut state, 0, 1);
332 assert!((state[0].re - 1.0).abs() < 1e-10);
333 assert!(state[1].re.abs() < 1e-10);
334 }
335
336 #[test]
337 fn test_phase_simd() {
338 let mut state = vec![Complex64::new(0.5, 0.5), Complex64::new(0.5, -0.5)];
339
340 let theta = std::f64::consts::PI / 4.0;
341 apply_phase_simd(&mut state, theta);
342
343 let norm_before = 0.5_f64.powi(2) + 0.5_f64.powi(2);
345 let norm_after = state[0].norm_sqr();
346 assert!((norm_before - norm_after).abs() < 1e-10);
347 }
348}