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