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