quantrs2_device/neutral_atom/
device.rs1use super::{
2 AtomStateEncoding, NeutralAtomCircuitResult, NeutralAtomClient, NeutralAtomDeviceConfig,
3 NeutralAtomExecutionMetadata, NeutralAtomMeasurementData, NeutralAtomQuantumDevice,
4 NeutralAtomSystemType,
5};
6use crate::{Circuit, CircuitExecutor, CircuitResult, DeviceError, DeviceResult, QuantumDevice};
7use async_trait::async_trait;
8use std::collections::HashMap;
9use std::time::Duration;
10use scirs2_core::random::prelude::*;
11
12#[derive(Debug, Clone)]
13pub struct NeutralAtomDevice {
14 pub client: NeutralAtomClient,
15 pub device_id: String,
16 pub config: NeutralAtomDeviceConfig,
17}
18
19impl NeutralAtomDevice {
20 pub fn new(
21 client: NeutralAtomClient,
22 device_id: String,
23 config: NeutralAtomDeviceConfig,
24 ) -> Self {
25 Self {
26 client,
27 device_id,
28 config,
29 }
30 }
31
32 pub fn with_config(mut self, config: NeutralAtomDeviceConfig) -> Self {
33 self.config = config;
34 self
35 }
36}
37
38#[async_trait]
39impl QuantumDevice for NeutralAtomDevice {
40 async fn is_available(&self) -> DeviceResult<bool> {
41 Ok(true)
43 }
44
45 async fn qubit_count(&self) -> DeviceResult<usize> {
46 Ok(self.config.atom_count)
47 }
48
49 async fn properties(&self) -> DeviceResult<HashMap<String, String>> {
50 let mut props = HashMap::new();
51
52 props.insert("device_id".to_string(), self.device_id.clone());
53 props.insert(
54 "system_type".to_string(),
55 format!("{:?}", self.config.system_type),
56 );
57 props.insert("atom_count".to_string(), self.config.atom_count.to_string());
58 props.insert(
59 "atom_spacing".to_string(),
60 self.config.atom_spacing.to_string(),
61 );
62 props.insert(
63 "state_encoding".to_string(),
64 format!("{:?}", self.config.state_encoding),
65 );
66
67 if let Some(blockade_radius) = self.config.blockade_radius {
68 props.insert("blockade_radius".to_string(), blockade_radius.to_string());
69 }
70
71 if let Some(loading_efficiency) = self.config.loading_efficiency {
72 props.insert(
73 "loading_efficiency".to_string(),
74 loading_efficiency.to_string(),
75 );
76 }
77
78 if let Some(gate_fidelity) = self.config.gate_fidelity {
79 props.insert("gate_fidelity".to_string(), gate_fidelity.to_string());
80 }
81
82 Ok(props)
83 }
84
85 async fn is_simulator(&self) -> DeviceResult<bool> {
86 Ok(self.device_id.to_lowercase().contains("simulator")
87 || self.device_id.to_lowercase().contains("emulator"))
88 }
89}
90
91#[async_trait]
92impl CircuitExecutor for NeutralAtomDevice {
93 async fn execute_circuit<const N: usize>(
94 &self,
95 circuit: &Circuit<N>,
96 shots: usize,
97 ) -> DeviceResult<CircuitResult> {
98 let result = self
99 .execute_neutral_atom_circuit(circuit, shots, None)
100 .await?;
101
102 Ok(result.circuit_result)
103 }
104
105 async fn execute_circuits<const N: usize>(
106 &self,
107 circuits: Vec<&Circuit<N>>,
108 shots: usize,
109 ) -> DeviceResult<Vec<CircuitResult>> {
110 let mut results = Vec::new();
111
112 for circuit in circuits {
113 let result = self.execute_circuit(circuit, shots).await?;
114 results.push(result);
115 }
116
117 Ok(results)
118 }
119
120 async fn can_execute_circuit<const N: usize>(
121 &self,
122 circuit: &Circuit<N>,
123 ) -> DeviceResult<bool> {
124 let required_qubits = N;
126 let available_qubits = self.config.atom_count;
127
128 if required_qubits > available_qubits {
129 return Ok(false);
130 }
131
132 Ok(true)
135 }
136
137 async fn estimated_queue_time<const N: usize>(
138 &self,
139 _circuit: &Circuit<N>,
140 ) -> DeviceResult<std::time::Duration> {
141 Ok(std::time::Duration::from_secs(30))
143 }
144}
145
146#[async_trait]
147impl NeutralAtomQuantumDevice for NeutralAtomDevice {
148 async fn system_type(&self) -> DeviceResult<NeutralAtomSystemType> {
149 Ok(self.config.system_type)
150 }
151
152 async fn atom_count(&self) -> DeviceResult<usize> {
153 Ok(self.config.atom_count)
154 }
155
156 async fn atom_spacing(&self) -> DeviceResult<f64> {
157 Ok(self.config.atom_spacing)
158 }
159
160 async fn state_encoding(&self) -> DeviceResult<AtomStateEncoding> {
161 Ok(self.config.state_encoding)
162 }
163
164 async fn blockade_radius(&self) -> DeviceResult<Option<f64>> {
165 Ok(self.config.blockade_radius)
166 }
167
168 async fn supports_rydberg_gates(&self) -> DeviceResult<bool> {
169 Ok(matches!(
170 self.config.system_type,
171 NeutralAtomSystemType::Rydberg | NeutralAtomSystemType::Hybrid
172 ))
173 }
174
175 async fn supports_tweezer_manipulation(&self) -> DeviceResult<bool> {
176 Ok(matches!(
177 self.config.system_type,
178 NeutralAtomSystemType::OpticalTweezer | NeutralAtomSystemType::Hybrid
179 ))
180 }
181
182 async fn loading_efficiency(&self) -> DeviceResult<f64> {
183 Ok(self.config.loading_efficiency.unwrap_or(0.95))
184 }
185
186 async fn gate_fidelity(&self) -> DeviceResult<f64> {
187 Ok(self.config.gate_fidelity.unwrap_or(0.995))
188 }
189
190 async fn execute_neutral_atom_circuit<const N: usize>(
191 &self,
192 circuit: &Circuit<N>,
193 shots: usize,
194 config: Option<NeutralAtomDeviceConfig>,
195 ) -> DeviceResult<NeutralAtomCircuitResult> {
196 let _job_config = config.unwrap_or_else(|| self.config.clone());
197
198 let measurement_data = NeutralAtomMeasurementData::default();
200 let execution_metadata = NeutralAtomExecutionMetadata {
201 system_type: self.config.system_type,
202 atoms_used: self.config.atom_count,
203 execution_time: Duration::from_millis(100),
204 gate_sequence: vec!["X".to_string(), "CNOT".to_string()],
205 optimizations_applied: vec!["rydberg_blockade".to_string()],
206 temperature: Some(1e-6),
207 laser_power: Some(10.0),
208 };
209
210 let circuit_result = CircuitResult {
211 counts: {
212 let mut counts = HashMap::new();
213 let all_zeros = "0".repeat(N);
215 counts.insert(all_zeros, shots);
216 counts
217 },
218 shots,
219 metadata: {
220 let mut metadata = HashMap::new();
221 metadata.insert("execution_time_ms".to_string(), "100".to_string());
222 metadata.insert("success".to_string(), "true".to_string());
223 metadata.insert(
224 "system_type".to_string(),
225 format!("{:?}", self.config.system_type),
226 );
227 metadata
228 },
229 };
230
231 Ok(NeutralAtomCircuitResult {
232 circuit_result,
233 neutral_atom_data: measurement_data,
234 execution_metadata,
235 })
236 }
237
238 async fn load_atoms(&self, _positions: &[(f64, f64, f64)]) -> DeviceResult<Vec<bool>> {
239 let success_rate = self.config.loading_efficiency.unwrap_or(0.95);
241 let loading_results = (0..self.config.atom_count)
242 .map(|_| thread_rng().gen::<f64>() < success_rate)
243 .collect();
244 Ok(loading_results)
245 }
246
247 async fn move_atoms(
248 &self,
249 _atom_indices: &[usize],
250 _new_positions: &[(f64, f64, f64)],
251 ) -> DeviceResult<()> {
252 if !self.supports_tweezer_manipulation().await? {
254 return Err(DeviceError::UnsupportedOperation(
255 "Tweezer manipulation not supported by this system".to_string(),
256 ));
257 }
258 Ok(())
259 }
260
261 async fn rydberg_excitation(
262 &self,
263 atom_indices: &[usize],
264 _excitation_time: Duration,
265 _laser_power: f64,
266 ) -> DeviceResult<Vec<bool>> {
267 if !self.supports_rydberg_gates().await? {
269 return Err(DeviceError::UnsupportedOperation(
270 "Rydberg excitation not supported by this system".to_string(),
271 ));
272 }
273
274 let success_rate = 0.99;
275 let excitation_results = atom_indices
276 .iter()
277 .map(|_| thread_rng().gen::<f64>() < success_rate)
278 .collect();
279 Ok(excitation_results)
280 }
281
282 async fn global_rydberg_operation(
283 &self,
284 _operation: &str,
285 _parameters: &HashMap<String, f64>,
286 ) -> DeviceResult<()> {
287 if !self.supports_rydberg_gates().await? {
289 return Err(DeviceError::UnsupportedOperation(
290 "Global Rydberg operations not supported by this system".to_string(),
291 ));
292 }
293 Ok(())
294 }
295
296 async fn measure_atom_states(&self, atom_indices: &[usize]) -> DeviceResult<Vec<String>> {
297 let states = atom_indices
299 .iter()
300 .map(|_| {
301 if thread_rng().gen::<f64>() < 0.5 {
302 "ground".to_string()
303 } else {
304 "excited".to_string()
305 }
306 })
307 .collect();
308 Ok(states)
309 }
310
311 async fn calculate_atom_correlations(
312 &self,
313 atom_pairs: &[(usize, usize)],
314 _correlation_type: &str,
315 ) -> DeviceResult<HashMap<String, f64>> {
316 let mut correlations = HashMap::new();
318 for (i, (atom1, atom2)) in atom_pairs.iter().enumerate() {
319 let correlation_key = format!("{}_{}", atom1, atom2);
320 correlations.insert(correlation_key, thread_rng().gen::<f64>() * 2.0 - 1.0);
321 }
322 Ok(correlations)
323 }
324
325 async fn estimate_fidelity(
326 &self,
327 _target_state: &str,
328 _measurement_data: &NeutralAtomMeasurementData,
329 ) -> DeviceResult<f64> {
330 Ok(self.config.gate_fidelity.unwrap_or(0.995))
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use std::time::Duration;
339
340 fn create_test_device() -> NeutralAtomDevice {
341 let client = NeutralAtomClient::new(
342 "https://test-neutral-atom-api.example.com".to_string(),
343 "test-token".to_string(),
344 )
345 .unwrap();
346 let config = NeutralAtomDeviceConfig::default();
347 NeutralAtomDevice::new(client, "test-device-1".to_string(), config)
348 }
349
350 #[tokio::test]
351 async fn test_device_creation() {
352 let device = create_test_device();
353 assert_eq!(device.device_id, "test-device-1");
354 assert_eq!(
355 device.client.base_url,
356 "https://test-neutral-atom-api.example.com"
357 );
358 }
359
360 #[tokio::test]
361 async fn test_device_properties() {
362 let device = create_test_device();
363 let properties = device.properties().await.unwrap();
364
365 assert_eq!(properties.get("device_id").unwrap(), "test-device-1");
366 assert_eq!(properties.get("system_type").unwrap(), "Rydberg");
367 assert_eq!(properties.get("atom_count").unwrap(), "100");
368 }
369
370 #[tokio::test]
371 async fn test_quantum_device_traits() {
372 let device = create_test_device();
373
374 assert!(device.is_available().await.unwrap());
375 assert_eq!(device.qubit_count().await.unwrap(), 100);
376 assert!(!device.is_simulator().await.unwrap());
377 }
378
379 #[tokio::test]
380 async fn test_neutral_atom_capabilities() {
381 let device = create_test_device();
382
383 assert_eq!(
384 device.system_type().await.unwrap(),
385 NeutralAtomSystemType::Rydberg
386 );
387 assert_eq!(device.atom_count().await.unwrap(), 100);
388 assert_eq!(device.atom_spacing().await.unwrap(), 5.0);
389 assert_eq!(
390 device.state_encoding().await.unwrap(),
391 AtomStateEncoding::GroundExcited
392 );
393 assert!(device.supports_rydberg_gates().await.unwrap());
394 assert!(!device.supports_tweezer_manipulation().await.unwrap());
395 }
396
397 #[tokio::test]
398 async fn test_atom_operations() {
399 let device = create_test_device();
400
401 let positions = vec![(0.0, 0.0, 0.0), (5.0, 0.0, 0.0), (10.0, 0.0, 0.0)];
403 let loading_results = device.load_atoms(&positions).await.unwrap();
404 assert_eq!(loading_results.len(), 100); let atom_indices = vec![0, 1, 2];
408 let excitation_results = device
409 .rydberg_excitation(&atom_indices, Duration::from_nanos(1000), 10.0)
410 .await
411 .unwrap();
412 assert_eq!(excitation_results.len(), 3);
413
414 let states = device.measure_atom_states(&atom_indices).await.unwrap();
416 assert_eq!(states.len(), 3);
417 assert!(states.iter().all(|s| s == "ground" || s == "excited"));
418 }
419}