1use super::{GpuBackend, GpuBuffer, GpuKernel};
7use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 qubit::QubitId,
10};
11use ndarray::Array2;
12use num_complex::Complex64;
13use std::sync::{Arc, Mutex};
14
15pub struct CpuBuffer {
17 data: Arc<Mutex<Vec<Complex64>>>,
18}
19
20impl CpuBuffer {
21 pub fn new(size: usize) -> Self {
23 Self {
24 data: Arc::new(Mutex::new(vec![Complex64::new(0.0, 0.0); size])),
25 }
26 }
27
28 pub fn data(&self) -> std::sync::MutexGuard<Vec<Complex64>> {
30 self.data.lock().unwrap()
31 }
32}
33
34impl GpuBuffer for CpuBuffer {
35 fn size(&self) -> usize {
36 self.data.lock().unwrap().len() * std::mem::size_of::<Complex64>()
37 }
38
39 fn upload(&mut self, data: &[Complex64]) -> QuantRS2Result<()> {
40 let mut buffer = self.data.lock().unwrap();
41 if buffer.len() != data.len() {
42 return Err(QuantRS2Error::InvalidInput(format!(
43 "Buffer size mismatch: {} != {}",
44 buffer.len(),
45 data.len()
46 )));
47 }
48 buffer.copy_from_slice(data);
49 Ok(())
50 }
51
52 fn download(&self, data: &mut [Complex64]) -> QuantRS2Result<()> {
53 let buffer = self.data.lock().unwrap();
54 if buffer.len() != data.len() {
55 return Err(QuantRS2Error::InvalidInput(format!(
56 "Buffer size mismatch: {} != {}",
57 buffer.len(),
58 data.len()
59 )));
60 }
61 data.copy_from_slice(&buffer);
62 Ok(())
63 }
64
65 fn sync(&self) -> QuantRS2Result<()> {
66 Ok(())
68 }
69
70 fn as_any(&self) -> &dyn std::any::Any {
71 self
72 }
73
74 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
75 self
76 }
77}
78
79pub struct CpuKernel;
81
82impl CpuKernel {
83 fn apply_gate_to_indices(state: &mut [Complex64], gate: &[Complex64], indices: &[usize]) {
85 let gate_size = indices.len();
86 let mut temp = vec![Complex64::new(0.0, 0.0); gate_size];
87
88 for (i, &idx) in indices.iter().enumerate() {
90 temp[i] = state[idx];
91 }
92
93 for (i, &idx) in indices.iter().enumerate() {
95 let mut sum = Complex64::new(0.0, 0.0);
96 for j in 0..gate_size {
97 sum += gate[i * gate_size + j] * temp[j];
98 }
99 state[idx] = sum;
100 }
101 }
102}
103
104impl GpuKernel for CpuKernel {
105 fn apply_single_qubit_gate(
106 &self,
107 state: &mut dyn GpuBuffer,
108 gate_matrix: &[Complex64; 4],
109 qubit: QubitId,
110 n_qubits: usize,
111 ) -> QuantRS2Result<()> {
112 let cpu_buffer = state
113 .as_any_mut()
114 .downcast_mut::<CpuBuffer>()
115 .ok_or_else(|| QuantRS2Error::InvalidInput("Expected CpuBuffer".to_string()))?;
116
117 let mut data = cpu_buffer.data();
118 let qubit_idx = qubit.0 as usize;
119 let stride = 1 << qubit_idx;
120 let pairs = 1 << (n_qubits - 1);
121
122 for i in 0..pairs {
124 let i0 = ((i >> qubit_idx) << (qubit_idx + 1)) | (i & ((1 << qubit_idx) - 1));
125 let i1 = i0 | stride;
126
127 let a = data[i0];
128 let b = data[i1];
129
130 data[i0] = gate_matrix[0] * a + gate_matrix[1] * b;
131 data[i1] = gate_matrix[2] * a + gate_matrix[3] * b;
132 }
133
134 Ok(())
135 }
136
137 fn apply_two_qubit_gate(
138 &self,
139 state: &mut dyn GpuBuffer,
140 gate_matrix: &[Complex64; 16],
141 control: QubitId,
142 target: QubitId,
143 n_qubits: usize,
144 ) -> QuantRS2Result<()> {
145 let cpu_buffer = state
146 .as_any_mut()
147 .downcast_mut::<CpuBuffer>()
148 .ok_or_else(|| QuantRS2Error::InvalidInput("Expected CpuBuffer".to_string()))?;
149
150 let mut data = cpu_buffer.data();
151 let control_idx = control.0 as usize;
152 let target_idx = target.0 as usize;
153
154 let (high_idx, low_idx) = if control_idx > target_idx {
156 (control_idx, target_idx)
157 } else {
158 (target_idx, control_idx)
159 };
160
161 let high_stride = 1 << high_idx;
162 let low_stride = 1 << low_idx;
163
164 let state_size = 1 << n_qubits;
165 let block_size = 1 << (high_idx + 1);
166 let num_blocks = state_size / block_size;
167
168 for block in 0..num_blocks {
170 let block_start = block * block_size;
171
172 for i in 0..(block_size / 4) {
173 let base = block_start
175 + (i & ((1 << low_idx) - 1))
176 + ((i >> low_idx) << (low_idx + 1))
177 + ((i >> (high_idx - 1)) << (high_idx + 1));
178
179 let indices = [
180 base,
181 base + low_stride,
182 base + high_stride,
183 base + low_stride + high_stride,
184 ];
185
186 Self::apply_gate_to_indices(&mut data, gate_matrix, &indices);
187 }
188 }
189
190 Ok(())
191 }
192
193 fn apply_multi_qubit_gate(
194 &self,
195 state: &mut dyn GpuBuffer,
196 gate_matrix: &Array2<Complex64>,
197 qubits: &[QubitId],
198 n_qubits: usize,
199 ) -> QuantRS2Result<()> {
200 let cpu_buffer = state
201 .as_any_mut()
202 .downcast_mut::<CpuBuffer>()
203 .ok_or_else(|| QuantRS2Error::InvalidInput("Expected CpuBuffer".to_string()))?;
204
205 let mut data = cpu_buffer.data();
206 let gate_qubits = qubits.len();
207 let gate_dim = 1 << gate_qubits;
208
209 if gate_matrix.dim() != (gate_dim, gate_dim) {
210 return Err(QuantRS2Error::InvalidInput(format!(
211 "Gate matrix dimension mismatch: {:?} != ({}, {})",
212 gate_matrix.dim(),
213 gate_dim,
214 gate_dim
215 )));
216 }
217
218 let gate_flat: Vec<Complex64> = gate_matrix.iter().cloned().collect();
220
221 let _total_states = 1 << n_qubits;
223 let affected_states = 1 << gate_qubits;
224 let unaffected_qubits = n_qubits - gate_qubits;
225 let iterations = 1 << unaffected_qubits;
226
227 let mut qubit_indices: Vec<usize> = qubits.iter().map(|q| q.0 as usize).collect();
229 qubit_indices.sort_unstable();
230
231 for i in 0..iterations {
233 let mut indices = vec![0; affected_states];
234
235 let mut base = 0;
237 let mut remaining = i;
238 let mut qubit_pos = 0;
239
240 for bit in 0..n_qubits {
241 if qubit_pos < gate_qubits && bit == qubit_indices[qubit_pos] {
242 qubit_pos += 1;
243 } else {
244 if remaining & 1 == 1 {
245 base |= 1 << bit;
246 }
247 remaining >>= 1;
248 }
249 }
250
251 for j in 0..affected_states {
253 indices[j] = base;
254 for (k, &qubit_idx) in qubit_indices.iter().enumerate() {
255 if (j >> k) & 1 == 1 {
256 indices[j] |= 1 << qubit_idx;
257 }
258 }
259 }
260
261 Self::apply_gate_to_indices(&mut data, &gate_flat, &indices);
262 }
263
264 Ok(())
265 }
266
267 fn measure_qubit(
268 &self,
269 state: &dyn GpuBuffer,
270 qubit: QubitId,
271 n_qubits: usize,
272 ) -> QuantRS2Result<(bool, f64)> {
273 let cpu_buffer = state
274 .as_any()
275 .downcast_ref::<CpuBuffer>()
276 .ok_or_else(|| QuantRS2Error::InvalidInput("Expected CpuBuffer".to_string()))?;
277
278 let data = cpu_buffer.data();
279 let qubit_idx = qubit.0 as usize;
280 let _stride = 1 << qubit_idx;
281
282 let mut prob_one = 0.0;
284 for i in 0..(1 << n_qubits) {
285 if (i >> qubit_idx) & 1 == 1 {
286 prob_one += data[i].norm_sqr();
287 }
288 }
289
290 let outcome = rand::random::<f64>() < prob_one;
292
293 Ok((outcome, if outcome { prob_one } else { 1.0 - prob_one }))
294 }
295
296 fn expectation_value(
297 &self,
298 state: &dyn GpuBuffer,
299 observable: &Array2<Complex64>,
300 qubits: &[QubitId],
301 n_qubits: usize,
302 ) -> QuantRS2Result<f64> {
303 let cpu_buffer = state
304 .as_any()
305 .downcast_ref::<CpuBuffer>()
306 .ok_or_else(|| QuantRS2Error::InvalidInput("Expected CpuBuffer".to_string()))?;
307
308 let data = cpu_buffer.data();
309
310 if qubits.len() != 1 || observable.dim() != (2, 2) {
312 return Err(QuantRS2Error::UnsupportedOperation(
313 "Only single-qubit observables supported currently".to_string(),
314 ));
315 }
316
317 let qubit_idx = qubits[0].0 as usize;
318 let stride = 1 << qubit_idx;
319 let pairs = 1 << (n_qubits - 1);
320
321 let mut expectation = Complex64::new(0.0, 0.0);
322
323 for i in 0..pairs {
324 let i0 = ((i >> qubit_idx) << (qubit_idx + 1)) | (i & ((1 << qubit_idx) - 1));
325 let i1 = i0 | stride;
326
327 let a = data[i0];
328 let b = data[i1];
329
330 expectation += a.conj() * (observable[(0, 0)] * a + observable[(0, 1)] * b);
331 expectation += b.conj() * (observable[(1, 0)] * a + observable[(1, 1)] * b);
332 }
333
334 if expectation.im.abs() > 1e-10 {
335 return Err(QuantRS2Error::InvalidInput(
336 "Observable expectation value is not real".to_string(),
337 ));
338 }
339
340 Ok(expectation.re)
341 }
342}
343
344pub struct CpuBackend {
346 kernel: CpuKernel,
347}
348
349impl CpuBackend {
350 pub fn new() -> Self {
352 Self { kernel: CpuKernel }
353 }
354}
355
356impl Default for CpuBackend {
357 fn default() -> Self {
358 Self::new()
359 }
360}
361
362impl GpuBackend for CpuBackend {
363 fn is_available() -> bool {
364 true }
366
367 fn name(&self) -> &str {
368 "CPU"
369 }
370
371 fn device_info(&self) -> String {
372 format!("CPU backend with {} threads", rayon::current_num_threads())
373 }
374
375 fn allocate_state_vector(&self, n_qubits: usize) -> QuantRS2Result<Box<dyn GpuBuffer>> {
376 let size = 1 << n_qubits;
377 Ok(Box::new(CpuBuffer::new(size)))
378 }
379
380 fn allocate_density_matrix(&self, n_qubits: usize) -> QuantRS2Result<Box<dyn GpuBuffer>> {
381 let size = 1 << (2 * n_qubits);
382 Ok(Box::new(CpuBuffer::new(size)))
383 }
384
385 fn kernel(&self) -> &dyn GpuKernel {
386 &self.kernel
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_cpu_buffer() {
396 let mut buffer = CpuBuffer::new(4);
397 let data = vec![
398 Complex64::new(1.0, 0.0),
399 Complex64::new(0.0, 1.0),
400 Complex64::new(-1.0, 0.0),
401 Complex64::new(0.0, -1.0),
402 ];
403
404 buffer.upload(&data).unwrap();
405
406 let mut downloaded = vec![Complex64::new(0.0, 0.0); 4];
407 buffer.download(&mut downloaded).unwrap();
408
409 assert_eq!(data, downloaded);
410 }
411
412 #[test]
413 fn test_cpu_backend() {
414 let backend = CpuBackend::new();
415 assert!(CpuBackend::is_available());
416 assert_eq!(backend.name(), "CPU");
417
418 let buffer = backend.allocate_state_vector(3).unwrap();
420 assert_eq!(buffer.size(), 8 * std::mem::size_of::<Complex64>());
421 }
422}