quantrs2_device/photonic/
device.rs

1//! Photonic quantum device implementation
2//!
3//! This module provides the core implementation of photonic quantum computing devices,
4//! supporting continuous variable, gate-based, and measurement-based quantum computing.
5
6use async_trait::async_trait;
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9use std::time::{Duration, Instant};
10use thiserror::Error;
11
12use quantrs2_circuit::prelude::Circuit;
13use quantrs2_core::qubit::QubitId;
14
15use super::{
16    validate_photonic_config, PhotonicCircuitResult, PhotonicClient, PhotonicDeviceConfig,
17    PhotonicExecutionMetadata, PhotonicMeasurementData, PhotonicQuantumDevice, PhotonicSystemType,
18};
19use crate::{CircuitExecutor, CircuitResult, DeviceError, DeviceResult, QuantumDevice};
20use scirs2_core::random::prelude::*;
21
22/// Photonic quantum device implementation
23#[derive(Debug, Clone)]
24pub struct PhotonicQuantumDeviceImpl {
25    /// Device identifier
26    pub device_id: String,
27    /// Client for hardware communication
28    pub client: PhotonicClient,
29    /// Device configuration
30    pub config: PhotonicDeviceConfig,
31    /// Device capabilities cache
32    capabilities: Arc<RwLock<Option<PhotonicCapabilities>>>,
33    /// Calibration data
34    calibration: Arc<RwLock<PhotonicCalibrationData>>,
35    /// Performance metrics
36    metrics: Arc<RwLock<PhotonicPerformanceMetrics>>,
37}
38
39/// Photonic device capabilities
40#[derive(Debug, Clone)]
41pub struct PhotonicCapabilities {
42    /// Supported system types
43    pub supported_systems: Vec<PhotonicSystemType>,
44    /// Maximum number of modes
45    pub max_modes: usize,
46    /// Maximum cutoff dimension
47    pub max_cutoff: usize,
48    /// Supported gate operations
49    pub supported_gates: Vec<String>,
50    /// Maximum squeezing parameter
51    pub max_squeezing: f64,
52    /// Minimum detection efficiency
53    pub min_detection_efficiency: f64,
54    /// Supported measurement types
55    pub supported_measurements: Vec<String>,
56    /// Hardware-specific features
57    pub hardware_features: HashMap<String, bool>,
58}
59
60/// Photonic calibration data
61#[derive(Debug, Clone)]
62pub struct PhotonicCalibrationData {
63    /// Mode-specific loss rates
64    pub mode_losses: HashMap<usize, f64>,
65    /// Beamsplitter transmittances
66    pub beamsplitter_transmittances: HashMap<(usize, usize), f64>,
67    /// Detector efficiencies
68    pub detector_efficiencies: HashMap<usize, f64>,
69    /// Phase shifter accuracies
70    pub phase_accuracies: HashMap<usize, f64>,
71    /// Squeezing parameters
72    pub squeezing_calibration: HashMap<usize, (f64, f64)>,
73    /// Cross-talk measurements
74    pub crosstalk_matrix: HashMap<(usize, usize), f64>,
75    /// Last calibration time
76    pub last_calibration: Instant,
77    /// Calibration validity duration
78    pub validity_duration: Duration,
79}
80
81/// Performance metrics for photonic devices
82#[derive(Debug, Clone)]
83pub struct PhotonicPerformanceMetrics {
84    /// Total circuits executed
85    pub circuits_executed: u64,
86    /// Average execution time
87    pub avg_execution_time: Duration,
88    /// Success rate
89    pub success_rate: f64,
90    /// Average fidelity
91    pub avg_fidelity: f64,
92    /// Mode utilization statistics
93    pub mode_utilization: HashMap<usize, f64>,
94    /// Gate operation counts
95    pub gate_counts: HashMap<String, u64>,
96    /// Error rates by operation type
97    pub error_rates: HashMap<String, f64>,
98}
99
100impl PhotonicQuantumDeviceImpl {
101    /// Create a new photonic quantum device
102    pub async fn new(
103        device_id: String,
104        client: PhotonicClient,
105        config: PhotonicDeviceConfig,
106    ) -> DeviceResult<Self> {
107        // Validate configuration
108        validate_photonic_config(&config)?;
109
110        let device = Self {
111            device_id,
112            client,
113            config,
114            capabilities: Arc::new(RwLock::new(None)),
115            calibration: Arc::new(RwLock::new(PhotonicCalibrationData::default())),
116            metrics: Arc::new(RwLock::new(PhotonicPerformanceMetrics::default())),
117        };
118
119        // Initialize device
120        device.initialize().await?;
121
122        Ok(device)
123    }
124
125    /// Initialize the device
126    async fn initialize(&self) -> DeviceResult<()> {
127        // Load capabilities
128        let capabilities = self.load_capabilities().await?;
129        *self
130            .capabilities
131            .write()
132            .map_err(|e| DeviceError::LockError(format!("Capabilities lock poisoned: {e}")))? =
133            Some(capabilities);
134
135        // Load calibration data
136        let calibration = self.load_calibration_data().await?;
137        *self
138            .calibration
139            .write()
140            .map_err(|e| DeviceError::LockError(format!("Calibration lock poisoned: {e}")))? =
141            calibration;
142
143        Ok(())
144    }
145
146    /// Load device capabilities
147    async fn load_capabilities(&self) -> DeviceResult<PhotonicCapabilities> {
148        let mut supported_systems = vec![PhotonicSystemType::ContinuousVariable];
149
150        // Check system-specific capabilities
151        match self.config.system_type {
152            PhotonicSystemType::ContinuousVariable => {
153                supported_systems.push(PhotonicSystemType::ContinuousVariable);
154            }
155            PhotonicSystemType::GateBased => {
156                supported_systems.push(PhotonicSystemType::GateBased);
157            }
158            PhotonicSystemType::MeasurementBased => {
159                supported_systems.push(PhotonicSystemType::MeasurementBased);
160            }
161            PhotonicSystemType::Hybrid => {
162                supported_systems.extend(&[
163                    PhotonicSystemType::ContinuousVariable,
164                    PhotonicSystemType::GateBased,
165                    PhotonicSystemType::MeasurementBased,
166                ]);
167            }
168        }
169
170        let supported_gates = vec![
171            "displacement".to_string(),
172            "squeezing".to_string(),
173            "two_mode_squeezing".to_string(),
174            "beamsplitter".to_string(),
175            "phase_rotation".to_string(),
176            "kerr".to_string(),
177            "cross_kerr".to_string(),
178            "homodyne".to_string(),
179            "heterodyne".to_string(),
180        ];
181
182        let supported_measurements = vec![
183            "homodyne".to_string(),
184            "heterodyne".to_string(),
185            "photon_counting".to_string(),
186            "quadrature".to_string(),
187        ];
188
189        let mut hardware_features = HashMap::new();
190        hardware_features.insert("squeezed_light_source".to_string(), true);
191        hardware_features.insert("programmable_beamsplitters".to_string(), true);
192        hardware_features.insert("high_efficiency_detectors".to_string(), true);
193        hardware_features.insert(
194            "real_time_feedback".to_string(),
195            self.config.hardware_acceleration,
196        );
197
198        Ok(PhotonicCapabilities {
199            supported_systems,
200            max_modes: self.config.mode_count * 2, // Allow for expansion
201            max_cutoff: self.config.cutoff_dimension.unwrap_or(20),
202            supported_gates,
203            max_squeezing: 10.0, // dB
204            min_detection_efficiency: 0.8,
205            supported_measurements,
206            hardware_features,
207        })
208    }
209
210    /// Load calibration data
211    async fn load_calibration_data(&self) -> DeviceResult<PhotonicCalibrationData> {
212        let mut mode_losses = HashMap::new();
213        let mut detector_efficiencies = HashMap::new();
214        let mut phase_accuracies = HashMap::new();
215        let mut squeezing_calibration = HashMap::new();
216
217        // Initialize default calibration values
218        for mode in 0..self.config.mode_count {
219            mode_losses.insert(mode, self.config.loss_rate.unwrap_or(0.01));
220            detector_efficiencies.insert(mode, self.config.detection_efficiency.unwrap_or(0.9));
221            phase_accuracies.insert(mode, 0.001); // 0.1% accuracy
222            squeezing_calibration.insert(mode, (0.0, 0.0)); // No squeezing by default
223        }
224
225        Ok(PhotonicCalibrationData {
226            mode_losses,
227            beamsplitter_transmittances: HashMap::new(),
228            detector_efficiencies,
229            phase_accuracies,
230            squeezing_calibration,
231            crosstalk_matrix: HashMap::new(),
232            last_calibration: Instant::now(),
233            validity_duration: Duration::from_secs(3600), // 1 hour
234        })
235    }
236
237    /// Update performance metrics
238    fn update_metrics(&self, execution_time: Duration, success: bool, fidelity: Option<f64>) {
239        let Ok(mut metrics) = self.metrics.write() else {
240            // If lock is poisoned, skip metrics update rather than panic
241            return;
242        };
243
244        metrics.circuits_executed += 1;
245
246        // Update average execution time
247        let total_time =
248            metrics.avg_execution_time * (metrics.circuits_executed - 1) as u32 + execution_time;
249        metrics.avg_execution_time = total_time / metrics.circuits_executed as u32;
250
251        // Update success rate
252        let total_success = metrics.success_rate.mul_add(
253            (metrics.circuits_executed - 1) as f64,
254            if success { 1.0 } else { 0.0 },
255        );
256        metrics.success_rate = total_success / metrics.circuits_executed as f64;
257
258        // Update average fidelity if provided
259        if let Some(fid) = fidelity {
260            let total_fidelity = metrics
261                .avg_fidelity
262                .mul_add((metrics.circuits_executed - 1) as f64, fid);
263            metrics.avg_fidelity = total_fidelity / metrics.circuits_executed as f64;
264        }
265    }
266
267    /// Check if calibration is valid
268    fn is_calibration_valid(&self) -> bool {
269        let Ok(calibration) = self.calibration.read() else {
270            // If lock is poisoned, assume calibration is invalid
271            return false;
272        };
273        calibration.last_calibration.elapsed() < calibration.validity_duration
274    }
275
276    /// Recalibrate device if needed
277    async fn ensure_calibrated(&self) -> DeviceResult<()> {
278        if !self.is_calibration_valid() {
279            let new_calibration = self.load_calibration_data().await?;
280            *self
281                .calibration
282                .write()
283                .map_err(|e| DeviceError::LockError(format!("Calibration lock poisoned: {e}")))? =
284                new_calibration;
285        }
286        Ok(())
287    }
288}
289
290#[async_trait]
291impl QuantumDevice for PhotonicQuantumDeviceImpl {
292    async fn is_available(&self) -> DeviceResult<bool> {
293        // Check client connection and device status
294        self.client.check_availability().await
295    }
296
297    async fn qubit_count(&self) -> DeviceResult<usize> {
298        // For photonic systems, return mode count as effective qubit count
299        Ok(self.config.mode_count)
300    }
301
302    async fn properties(&self) -> DeviceResult<HashMap<String, String>> {
303        let mut properties = HashMap::new();
304
305        properties.insert(
306            "system_type".to_string(),
307            format!("{:?}", self.config.system_type),
308        );
309        properties.insert("mode_count".to_string(), self.config.mode_count.to_string());
310
311        if let Some(cutoff) = self.config.cutoff_dimension {
312            properties.insert("cutoff_dimension".to_string(), cutoff.to_string());
313        }
314
315        if let Some(loss_rate) = self.config.loss_rate {
316            properties.insert("loss_rate".to_string(), loss_rate.to_string());
317        }
318
319        if let Some(efficiency) = self.config.detection_efficiency {
320            properties.insert("detection_efficiency".to_string(), efficiency.to_string());
321        }
322
323        // Add calibration status
324        properties.insert(
325            "calibration_valid".to_string(),
326            self.is_calibration_valid().to_string(),
327        );
328
329        // Add performance metrics
330        let metrics = self
331            .metrics
332            .read()
333            .map_err(|e| DeviceError::LockError(format!("Metrics lock poisoned: {e}")))?;
334        properties.insert(
335            "circuits_executed".to_string(),
336            metrics.circuits_executed.to_string(),
337        );
338        properties.insert("success_rate".to_string(), metrics.success_rate.to_string());
339        properties.insert("avg_fidelity".to_string(), metrics.avg_fidelity.to_string());
340
341        Ok(properties)
342    }
343
344    async fn is_simulator(&self) -> DeviceResult<bool> {
345        // Check if this is a hardware device or simulator
346        self.client.is_simulator().await
347    }
348}
349
350#[async_trait]
351impl CircuitExecutor for PhotonicQuantumDeviceImpl {
352    async fn execute_circuit<const N: usize>(
353        &self,
354        circuit: &Circuit<N>,
355        shots: usize,
356    ) -> DeviceResult<CircuitResult> {
357        let photonic_result = self.execute_photonic_circuit(circuit, shots, None).await?;
358        Ok(photonic_result.circuit_result)
359    }
360
361    async fn execute_circuits<const N: usize>(
362        &self,
363        circuits: Vec<&Circuit<N>>,
364        shots: usize,
365    ) -> DeviceResult<Vec<CircuitResult>> {
366        let mut results = Vec::new();
367
368        for circuit in circuits {
369            let result = self.execute_circuit(circuit, shots).await?;
370            results.push(result);
371        }
372
373        Ok(results)
374    }
375
376    async fn can_execute_circuit<const N: usize>(
377        &self,
378        circuit: &Circuit<N>,
379    ) -> DeviceResult<bool> {
380        // Check if circuit is compatible with photonic system
381
382        // Check mode count
383        if N > self.config.mode_count {
384            return Ok(false);
385        }
386
387        // Check gate compatibility
388        // TODO: Implement gate validation based on photonic capabilities
389
390        Ok(true)
391    }
392
393    async fn estimated_queue_time<const N: usize>(
394        &self,
395        _circuit: &Circuit<N>,
396    ) -> DeviceResult<Duration> {
397        // For photonic systems, execution is typically fast
398        // Queue time depends on system load
399        self.client.get_queue_time().await
400    }
401}
402
403#[async_trait]
404impl PhotonicQuantumDevice for PhotonicQuantumDeviceImpl {
405    async fn system_type(&self) -> DeviceResult<PhotonicSystemType> {
406        Ok(self.config.system_type)
407    }
408
409    async fn mode_count(&self) -> DeviceResult<usize> {
410        Ok(self.config.mode_count)
411    }
412
413    async fn cutoff_dimension(&self) -> DeviceResult<Option<usize>> {
414        Ok(self.config.cutoff_dimension)
415    }
416
417    async fn supports_cv_operations(&self) -> DeviceResult<bool> {
418        let capabilities = self
419            .capabilities
420            .read()
421            .map_err(|e| DeviceError::LockError(format!("Capabilities lock poisoned: {e}")))?;
422        Ok(capabilities.as_ref().map_or(false, |caps| {
423            caps.supported_systems
424                .contains(&PhotonicSystemType::ContinuousVariable)
425        }))
426    }
427
428    async fn supports_gate_based(&self) -> DeviceResult<bool> {
429        let capabilities = self
430            .capabilities
431            .read()
432            .map_err(|e| DeviceError::LockError(format!("Capabilities lock poisoned: {e}")))?;
433        Ok(capabilities.as_ref().map_or(false, |caps| {
434            caps.supported_systems
435                .contains(&PhotonicSystemType::GateBased)
436        }))
437    }
438
439    async fn supports_measurement_based(&self) -> DeviceResult<bool> {
440        let capabilities = self
441            .capabilities
442            .read()
443            .map_err(|e| DeviceError::LockError(format!("Capabilities lock poisoned: {e}")))?;
444        Ok(capabilities.as_ref().map_or(false, |caps| {
445            caps.supported_systems
446                .contains(&PhotonicSystemType::MeasurementBased)
447        }))
448    }
449
450    async fn quadrature_precision(&self) -> DeviceResult<f64> {
451        // Return precision based on calibration data
452        let calibration = self
453            .calibration
454            .read()
455            .map_err(|e| DeviceError::LockError(format!("Calibration lock poisoned: {e}")))?;
456        let len = calibration.phase_accuracies.len();
457        if len == 0 {
458            return Ok(0.0);
459        }
460        let avg_precision =
461            calibration.phase_accuracies.values().copied().sum::<f64>() / len as f64;
462        Ok(avg_precision)
463    }
464
465    async fn detection_efficiency(&self) -> DeviceResult<f64> {
466        Ok(self.config.detection_efficiency.unwrap_or(0.9))
467    }
468
469    async fn execute_photonic_circuit<const N: usize>(
470        &self,
471        circuit: &Circuit<N>,
472        shots: usize,
473        config: Option<PhotonicDeviceConfig>,
474    ) -> DeviceResult<PhotonicCircuitResult> {
475        let start_time = Instant::now();
476
477        // Ensure device is calibrated
478        self.ensure_calibrated().await?;
479
480        // Use provided config or default
481        let exec_config = config.unwrap_or_else(|| self.config.clone());
482
483        // Create a simple circuit representation for API
484        let circuit_str = format!(
485            "{{\"gates\":{},\"qubits\":{}}}",
486            circuit.gates().len(),
487            circuit.num_qubits()
488        );
489
490        // Convert config to JSON
491        let config_json = serde_json::to_value(&exec_config).map_err(|e| {
492            DeviceError::CircuitConversion(format!("Failed to serialize config: {e}"))
493        })?;
494        let mut config_map = std::collections::HashMap::new();
495        if let serde_json::Value::Object(map) = config_json {
496            for (k, v) in map {
497                config_map.insert(k, v);
498            }
499        }
500
501        // Execute circuit using client
502        let circuit_result = self
503            .client
504            .execute_photonic_circuit(&circuit_str, shots, &config_map)
505            .await?;
506
507        // Generate photonic-specific measurement data
508        let photonic_data = self.generate_photonic_measurements(circuit, shots).await?;
509
510        // Create execution metadata
511        let execution_time = start_time.elapsed();
512        let metadata = PhotonicExecutionMetadata {
513            system_type: exec_config.system_type,
514            modes_used: N.min(exec_config.mode_count),
515            execution_time,
516            measured_loss_rate: self.config.loss_rate,
517            thermal_noise: self.config.thermal_photons,
518            gate_sequence: vec![],         // TODO: Extract from circuit
519            optimizations_applied: vec![], // TODO: Track optimizations
520        };
521
522        // Update performance metrics
523        let fidelity = photonic_data.fidelities.get("overall").copied();
524        self.update_metrics(execution_time, true, fidelity);
525
526        // Convert PhotonicJobResult to CircuitResult
527        let circuit_result_converted = CircuitResult {
528            counts: circuit_result
529                .results
530                .get("counts")
531                .and_then(|v| serde_json::from_value(v.clone()).ok())
532                .unwrap_or_else(|| {
533                    let mut counts = HashMap::new();
534                    counts.insert("0".repeat(circuit.num_qubits()), shots);
535                    counts
536                }),
537            shots: circuit_result.shots_completed,
538            metadata: circuit_result.metadata,
539        };
540
541        Ok(PhotonicCircuitResult {
542            circuit_result: circuit_result_converted,
543            photonic_data,
544            execution_metadata: metadata,
545        })
546    }
547
548    async fn measure_quadratures(
549        &self,
550        modes: &[usize],
551        angles: &[f64],
552    ) -> DeviceResult<Vec<(f64, f64)>> {
553        self.client
554            .measure_quadratures(&self.device_id, modes, angles)
555            .await
556    }
557
558    async fn measure_photon_numbers(&self, modes: &[usize]) -> DeviceResult<Vec<usize>> {
559        self.client
560            .measure_photon_numbers(&self.device_id, modes)
561            .await
562    }
563
564    async fn homodyne_detection(
565        &self,
566        mode: usize,
567        phase: f64,
568        shots: usize,
569    ) -> DeviceResult<Vec<f64>> {
570        self.client
571            .homodyne_detection(&self.device_id, mode, phase, shots)
572            .await
573    }
574
575    async fn heterodyne_detection(
576        &self,
577        mode: usize,
578        shots: usize,
579    ) -> DeviceResult<Vec<(f64, f64)>> {
580        self.client
581            .heterodyne_detection(&self.device_id, mode, shots)
582            .await
583    }
584
585    async fn calculate_correlations(
586        &self,
587        modes: &[(usize, usize)],
588        correlation_type: &str,
589    ) -> DeviceResult<HashMap<String, f64>> {
590        self.client
591            .calculate_correlations(modes, correlation_type)
592            .await
593    }
594
595    async fn estimate_fidelity(
596        &self,
597        target_state: &str,
598        measurement_data: &PhotonicMeasurementData,
599    ) -> DeviceResult<f64> {
600        self.client
601            .estimate_fidelity(target_state, measurement_data)
602            .await
603    }
604}
605
606impl PhotonicQuantumDeviceImpl {
607    /// Generate photonic measurement data from circuit execution
608    async fn generate_photonic_measurements<const N: usize>(
609        &self,
610        _circuit: &Circuit<N>,
611        shots: usize,
612    ) -> DeviceResult<PhotonicMeasurementData> {
613        // Simulate photonic measurements
614        let mut quadratures = Vec::new();
615        let mut photon_numbers = Vec::new();
616        let mut homodyne_results = Vec::new();
617        let mut heterodyne_results = Vec::new();
618        let mut correlations = HashMap::new();
619        let mut fidelities = HashMap::new();
620
621        // Generate mock measurements for testing
622        for _ in 0..shots.min(100) {
623            // Limit for demonstration
624            // Random quadrature values
625            quadratures.push((
626                thread_rng().gen::<f64>() - 0.5,
627                thread_rng().gen::<f64>() - 0.5,
628            ));
629
630            // Random photon numbers (small numbers typical for CV systems)
631            photon_numbers.push((thread_rng().gen::<f64>() * 5.0) as usize);
632
633            // Homodyne detection results
634            homodyne_results.push(thread_rng().gen::<f64>() - 0.5);
635
636            // Heterodyne detection results
637            heterodyne_results.push((
638                thread_rng().gen::<f64>() - 0.5,
639                thread_rng().gen::<f64>() - 0.5,
640            ));
641        }
642
643        // Calculate correlations
644        correlations.insert(
645            "g2".to_string(),
646            thread_rng().gen::<f64>().mul_add(0.1, 1.0),
647        );
648        correlations.insert(
649            "visibility".to_string(),
650            thread_rng().gen::<f64>().mul_add(0.09, 0.9),
651        );
652
653        // Estimate fidelities
654        fidelities.insert(
655            "overall".to_string(),
656            thread_rng().gen::<f64>().mul_add(0.04, 0.95),
657        );
658        fidelities.insert(
659            "gate_fidelity".to_string(),
660            self.config.gate_fidelity.unwrap_or(0.99),
661        );
662
663        Ok(PhotonicMeasurementData {
664            quadratures,
665            photon_numbers,
666            homodyne_results,
667            heterodyne_results,
668            correlations,
669            fidelities,
670        })
671    }
672}
673
674impl Default for PhotonicCalibrationData {
675    fn default() -> Self {
676        Self {
677            mode_losses: HashMap::new(),
678            beamsplitter_transmittances: HashMap::new(),
679            detector_efficiencies: HashMap::new(),
680            phase_accuracies: HashMap::new(),
681            squeezing_calibration: HashMap::new(),
682            crosstalk_matrix: HashMap::new(),
683            last_calibration: Instant::now(),
684            validity_duration: Duration::from_secs(3600),
685        }
686    }
687}
688
689impl Default for PhotonicPerformanceMetrics {
690    fn default() -> Self {
691        Self {
692            circuits_executed: 0,
693            avg_execution_time: Duration::from_millis(0),
694            success_rate: 0.0,
695            avg_fidelity: 0.0,
696            mode_utilization: HashMap::new(),
697            gate_counts: HashMap::new(),
698            error_rates: HashMap::new(),
699        }
700    }
701}
702
703#[cfg(test)]
704mod tests {
705    use super::*;
706    use crate::photonic::{PhotonicClient, PhotonicConfig};
707
708    #[tokio::test]
709    async fn test_photonic_device_creation() {
710        let client = PhotonicClient::new(
711            "http://localhost:8080".to_string(),
712            "test_token".to_string(),
713        )
714        .expect("Failed to create photonic client");
715        let config = PhotonicDeviceConfig::default();
716
717        let device =
718            PhotonicQuantumDeviceImpl::new("test_device".to_string(), client, config).await;
719        assert!(device.is_ok());
720    }
721
722    #[tokio::test]
723    async fn test_device_properties() {
724        let client = PhotonicClient::new(
725            "http://localhost:8080".to_string(),
726            "test_token".to_string(),
727        )
728        .expect("Failed to create photonic client");
729        let config = PhotonicDeviceConfig::default();
730        let device = PhotonicQuantumDeviceImpl::new("test_device".to_string(), client, config)
731            .await
732            .expect("Failed to create photonic device");
733
734        let properties = device
735            .properties()
736            .await
737            .expect("Failed to get device properties");
738        assert!(properties.contains_key("system_type"));
739        assert!(properties.contains_key("mode_count"));
740    }
741
742    #[tokio::test]
743    async fn test_capabilities() {
744        let client = PhotonicClient::new(
745            "http://localhost:8080".to_string(),
746            "test_token".to_string(),
747        )
748        .expect("Failed to create photonic client");
749        let config = PhotonicDeviceConfig::default();
750        let device = PhotonicQuantumDeviceImpl::new("test_device".to_string(), client, config)
751            .await
752            .expect("Failed to create photonic device");
753
754        assert!(device
755            .supports_cv_operations()
756            .await
757            .expect("Failed to check CV operations support"));
758        assert_eq!(
759            device.mode_count().await.expect("Failed to get mode count"),
760            8
761        );
762    }
763}