1use crate::{
7 error::{QuantRS2Error, QuantRS2Result},
8 gate::GateOp,
9 qubit::QubitId,
10};
11use ndarray::{Array1, Array2};
12use num_complex::Complex64;
13use std::sync::Arc;
14
15pub mod cpu_backend;
16#[cfg(feature = "cuda")]
17pub mod cuda_backend;
18#[cfg(feature = "metal")]
19pub mod metal_backend;
20#[cfg(feature = "vulkan")]
21pub mod vulkan_backend;
22
23pub trait GpuBuffer: Send + Sync {
25 fn size(&self) -> usize;
27
28 fn upload(&mut self, data: &[Complex64]) -> QuantRS2Result<()>;
30
31 fn download(&self, data: &mut [Complex64]) -> QuantRS2Result<()>;
33
34 fn sync(&self) -> QuantRS2Result<()>;
36
37 fn as_any(&self) -> &dyn std::any::Any;
39
40 fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
42}
43
44pub trait GpuKernel: Send + Sync {
46 fn apply_single_qubit_gate(
48 &self,
49 state: &mut dyn GpuBuffer,
50 gate_matrix: &[Complex64; 4],
51 qubit: QubitId,
52 n_qubits: usize,
53 ) -> QuantRS2Result<()>;
54
55 fn apply_two_qubit_gate(
57 &self,
58 state: &mut dyn GpuBuffer,
59 gate_matrix: &[Complex64; 16],
60 control: QubitId,
61 target: QubitId,
62 n_qubits: usize,
63 ) -> QuantRS2Result<()>;
64
65 fn apply_multi_qubit_gate(
67 &self,
68 state: &mut dyn GpuBuffer,
69 gate_matrix: &Array2<Complex64>,
70 qubits: &[QubitId],
71 n_qubits: usize,
72 ) -> QuantRS2Result<()>;
73
74 fn measure_qubit(
76 &self,
77 state: &dyn GpuBuffer,
78 qubit: QubitId,
79 n_qubits: usize,
80 ) -> QuantRS2Result<(bool, f64)>;
81
82 fn expectation_value(
84 &self,
85 state: &dyn GpuBuffer,
86 observable: &Array2<Complex64>,
87 qubits: &[QubitId],
88 n_qubits: usize,
89 ) -> QuantRS2Result<f64>;
90}
91
92pub trait GpuBackend: Send + Sync {
94 fn is_available() -> bool
96 where
97 Self: Sized;
98
99 fn name(&self) -> &str;
101
102 fn device_info(&self) -> String;
104
105 fn allocate_state_vector(&self, n_qubits: usize) -> QuantRS2Result<Box<dyn GpuBuffer>>;
107
108 fn allocate_density_matrix(&self, n_qubits: usize) -> QuantRS2Result<Box<dyn GpuBuffer>>;
110
111 fn kernel(&self) -> &dyn GpuKernel;
113
114 fn apply_gate(
116 &self,
117 state: &mut dyn GpuBuffer,
118 gate: &dyn GateOp,
119 qubits: &[QubitId],
120 n_qubits: usize,
121 ) -> QuantRS2Result<()> {
122 match qubits.len() {
123 1 => {
124 let matrix = gate.matrix()?;
125 let gate_array: [Complex64; 4] = [matrix[0], matrix[1], matrix[2], matrix[3]];
126 self.kernel()
127 .apply_single_qubit_gate(state, &gate_array, qubits[0], n_qubits)
128 }
129 2 => {
130 let matrix = gate.matrix()?;
131 let mut gate_array = [Complex64::new(0.0, 0.0); 16];
132 for (i, &val) in matrix.iter().enumerate() {
133 gate_array[i] = val;
134 }
135 self.kernel().apply_two_qubit_gate(
136 state,
137 &gate_array,
138 qubits[0],
139 qubits[1],
140 n_qubits,
141 )
142 }
143 _ => {
144 let matrix_vec = gate.matrix()?;
145 let size = (1 << qubits.len(), 1 << qubits.len());
146 let matrix = Array2::from_shape_vec(size, matrix_vec)?;
147 self.kernel()
148 .apply_multi_qubit_gate(state, &matrix, qubits, n_qubits)
149 }
150 }
151 }
152
153 fn measure(
155 &self,
156 state: &mut dyn GpuBuffer,
157 qubit: QubitId,
158 n_qubits: usize,
159 ) -> QuantRS2Result<bool> {
160 let (outcome, _prob) = self.kernel().measure_qubit(state, qubit, n_qubits)?;
161 Ok(outcome)
162 }
163
164 fn get_probability(
166 &self,
167 state: &dyn GpuBuffer,
168 qubit: QubitId,
169 n_qubits: usize,
170 ) -> QuantRS2Result<f64> {
171 let (_outcome, prob) = self.kernel().measure_qubit(state, qubit, n_qubits)?;
172 Ok(prob)
173 }
174}
175
176pub struct GpuStateVector {
178 backend: Arc<dyn GpuBackend>,
180 buffer: Box<dyn GpuBuffer>,
182 n_qubits: usize,
184}
185
186impl GpuStateVector {
187 pub fn new(backend: Arc<dyn GpuBackend>, n_qubits: usize) -> QuantRS2Result<Self> {
189 let buffer = backend.allocate_state_vector(n_qubits)?;
190 Ok(Self {
191 backend,
192 buffer,
193 n_qubits,
194 })
195 }
196
197 pub fn initialize_zero_state(&mut self) -> QuantRS2Result<()> {
199 let size = 1 << self.n_qubits;
200 let mut data = vec![Complex64::new(0.0, 0.0); size];
201 data[0] = Complex64::new(1.0, 0.0);
202 self.buffer.upload(&data)
203 }
204
205 pub fn apply_gate(&mut self, gate: &dyn GateOp, qubits: &[QubitId]) -> QuantRS2Result<()> {
207 self.backend
208 .apply_gate(self.buffer.as_mut(), gate, qubits, self.n_qubits)
209 }
210
211 pub fn measure(&mut self, qubit: QubitId) -> QuantRS2Result<bool> {
213 self.backend
214 .measure(self.buffer.as_mut(), qubit, self.n_qubits)
215 }
216
217 pub fn to_array(&self) -> QuantRS2Result<Array1<Complex64>> {
219 let size = 1 << self.n_qubits;
220 let mut data = vec![Complex64::new(0.0, 0.0); size];
221 self.buffer.download(&mut data)?;
222 Ok(Array1::from_vec(data))
223 }
224
225 pub fn get_probabilities(&self) -> QuantRS2Result<Vec<f64>> {
227 let state = self.to_array()?;
228 Ok(state.iter().map(|c| c.norm_sqr()).collect())
229 }
230}
231
232pub struct GpuBackendFactory;
234
235impl GpuBackendFactory {
236 pub fn create_best_available() -> QuantRS2Result<Arc<dyn GpuBackend>> {
238 #[cfg(feature = "cuda")]
240 if cuda_backend::CudaBackend::is_available() {
241 return Ok(Arc::new(cuda_backend::CudaBackend::new()?));
242 }
243
244 #[cfg(feature = "metal")]
245 if metal_backend::MetalBackend::is_available() {
246 return Ok(Arc::new(metal_backend::MetalBackend::new()?));
247 }
248
249 #[cfg(feature = "vulkan")]
250 if vulkan_backend::VulkanBackend::is_available() {
251 return Ok(Arc::new(vulkan_backend::VulkanBackend::new()?));
252 }
253
254 Ok(Arc::new(cpu_backend::CpuBackend::new()))
256 }
257
258 pub fn create_backend(backend_type: &str) -> QuantRS2Result<Arc<dyn GpuBackend>> {
260 match backend_type.to_lowercase().as_str() {
261 #[cfg(feature = "cuda")]
262 "cuda" => Ok(Arc::new(cuda_backend::CudaBackend::new()?)),
263
264 #[cfg(feature = "metal")]
265 "metal" => Ok(Arc::new(metal_backend::MetalBackend::new()?)),
266
267 #[cfg(feature = "vulkan")]
268 "vulkan" => Ok(Arc::new(vulkan_backend::VulkanBackend::new()?)),
269
270 "cpu" => Ok(Arc::new(cpu_backend::CpuBackend::new())),
271
272 _ => Err(QuantRS2Error::InvalidInput(format!(
273 "Unknown backend type: {}",
274 backend_type
275 ))),
276 }
277 }
278
279 pub fn available_backends() -> Vec<&'static str> {
281 let mut backends = vec!["cpu"];
282
283 #[cfg(feature = "cuda")]
284 if cuda_backend::CudaBackend::is_available() {
285 backends.push("cuda");
286 }
287
288 #[cfg(feature = "metal")]
289 if metal_backend::MetalBackend::is_available() {
290 backends.push("metal");
291 }
292
293 #[cfg(feature = "vulkan")]
294 if vulkan_backend::VulkanBackend::is_available() {
295 backends.push("vulkan");
296 }
297
298 backends
299 }
300}
301
302#[derive(Debug, Clone)]
304pub struct GpuConfig {
305 pub backend: Option<String>,
307 pub max_memory: Option<usize>,
309 pub num_threads: Option<usize>,
311 pub enable_profiling: bool,
313}
314
315impl Default for GpuConfig {
316 fn default() -> Self {
317 Self {
318 backend: None,
319 max_memory: None,
320 num_threads: None,
321 enable_profiling: false,
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329 use crate::gate::single::Hadamard;
330
331 #[test]
332 fn test_gpu_backend_factory() {
333 let backends = GpuBackendFactory::available_backends();
334 assert!(backends.contains(&"cpu"));
335
336 let backend = GpuBackendFactory::create_backend("cpu").unwrap();
338 assert_eq!(backend.name(), "CPU");
339 }
340
341 #[test]
342 fn test_gpu_state_vector() {
343 let backend = GpuBackendFactory::create_best_available().unwrap();
344 let mut state = GpuStateVector::new(backend, 2).unwrap();
345
346 state.initialize_zero_state().unwrap();
348
349 let h_gate = Hadamard { target: QubitId(0) };
351 state.apply_gate(&h_gate, &[QubitId(0)]).unwrap();
352
353 let probs = state.get_probabilities().unwrap();
355 assert_eq!(probs.len(), 4);
356
357 assert!((probs[0] - 0.5).abs() < 1e-10); assert!((probs[1] - 0.5).abs() < 1e-10); assert!((probs[2] - 0.0).abs() < 1e-10); assert!((probs[3] - 0.0).abs() < 1e-10); }
364}