quantrs2_device/photonic/
client.rs

1//! Photonic quantum computing client implementation
2//!
3//! This module provides client connectivity for photonic quantum computers,
4//! supporting various photonic platforms and hardware providers.
5
6use crate::{DeviceError, DeviceResult};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::time::Duration;
10use tokio::time::timeout;
11
12/// Client for photonic quantum computing systems
13#[derive(Debug, Clone)]
14pub struct PhotonicClient {
15    /// Base URL for the photonic service
16    pub base_url: String,
17    /// Authentication token
18    pub auth_token: String,
19    /// HTTP client for API requests
20    pub client: reqwest::Client,
21    /// Request timeout
22    pub timeout: Duration,
23    /// Additional headers for requests
24    pub headers: HashMap<String, String>,
25}
26
27impl PhotonicClient {
28    /// Create a new photonic client
29    pub fn new(base_url: String, auth_token: String) -> DeviceResult<Self> {
30        let client = reqwest::Client::builder()
31            .timeout(Duration::from_secs(30))
32            .build()
33            .map_err(|e| DeviceError::Connection(format!("Failed to create HTTP client: {e}")))?;
34
35        Ok(Self {
36            base_url,
37            auth_token,
38            client,
39            timeout: Duration::from_secs(300),
40            headers: HashMap::new(),
41        })
42    }
43
44    /// Create a new photonic client with custom configuration
45    pub fn with_config(
46        base_url: String,
47        auth_token: String,
48        timeout_secs: u64,
49        headers: HashMap<String, String>,
50    ) -> DeviceResult<Self> {
51        let client = reqwest::Client::builder()
52            .timeout(Duration::from_secs(timeout_secs))
53            .build()
54            .map_err(|e| DeviceError::Connection(format!("Failed to create HTTP client: {e}")))?;
55
56        Ok(Self {
57            base_url,
58            auth_token,
59            client,
60            timeout: Duration::from_secs(timeout_secs),
61            headers,
62        })
63    }
64
65    /// Get available photonic devices
66    pub async fn get_devices(&self) -> DeviceResult<Vec<PhotonicDeviceInfo>> {
67        let url = format!("{}/devices", self.base_url);
68        let response = timeout(self.timeout, self.get_request(&url))
69            .await
70            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
71            .map_err(|e| DeviceError::APIError(format!("Failed to get devices: {e}")))?;
72
73        response
74            .json::<Vec<PhotonicDeviceInfo>>()
75            .await
76            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse devices: {e}")))
77    }
78
79    /// Get device information by ID
80    pub async fn get_device(&self, device_id: &str) -> DeviceResult<PhotonicDeviceInfo> {
81        let url = format!("{}/devices/{}", self.base_url, device_id);
82        let response = timeout(self.timeout, self.get_request(&url))
83            .await
84            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
85            .map_err(|e| DeviceError::APIError(format!("Failed to get device: {e}")))?;
86
87        response
88            .json::<PhotonicDeviceInfo>()
89            .await
90            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse device: {e}")))
91    }
92
93    /// Submit a job to a photonic device
94    pub async fn submit_job(&self, job_request: &PhotonicJobRequest) -> DeviceResult<String> {
95        let url = format!("{}/jobs", self.base_url);
96        let response = timeout(self.timeout, self.post_request(&url, job_request))
97            .await
98            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
99            .map_err(|e| DeviceError::JobSubmission(format!("Failed to submit job: {e}")))?;
100
101        let job_response: PhotonicJobResponse = response.json().await.map_err(|e| {
102            DeviceError::Deserialization(format!("Failed to parse job response: {e}"))
103        })?;
104
105        Ok(job_response.job_id)
106    }
107
108    /// Get job status
109    pub async fn get_job_status(&self, job_id: &str) -> DeviceResult<PhotonicJobStatus> {
110        let url = format!("{}/jobs/{}", self.base_url, job_id);
111        let response = timeout(self.timeout, self.get_request(&url))
112            .await
113            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
114            .map_err(|e| DeviceError::APIError(format!("Failed to get job status: {e}")))?;
115
116        response
117            .json::<PhotonicJobStatus>()
118            .await
119            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse job status: {e}")))
120    }
121
122    /// Get job results
123    pub async fn get_job_results(&self, job_id: &str) -> DeviceResult<PhotonicJobResult> {
124        let url = format!("{}/jobs/{}/results", self.base_url, job_id);
125        let response = timeout(self.timeout, self.get_request(&url))
126            .await
127            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
128            .map_err(|e| DeviceError::APIError(format!("Failed to get job results: {e}")))?;
129
130        response
131            .json::<PhotonicJobResult>()
132            .await
133            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse job results: {e}")))
134    }
135
136    /// Cancel a job
137    pub async fn cancel_job(&self, job_id: &str) -> DeviceResult<()> {
138        let url = format!("{}/jobs/{}/cancel", self.base_url, job_id);
139        timeout(self.timeout, self.delete_request(&url))
140            .await
141            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
142            .map_err(|e| DeviceError::APIError(format!("Failed to cancel job: {e}")))?;
143
144        Ok(())
145    }
146
147    /// Perform quadrature measurement
148    pub async fn measure_quadratures(
149        &self,
150        device_id: &str,
151        modes: &[usize],
152        phases: &[f64],
153    ) -> DeviceResult<Vec<(f64, f64)>> {
154        let url = format!(
155            "{}/devices/{}/measure/quadratures",
156            self.base_url, device_id
157        );
158        let request = QuadratureMeasurementRequest {
159            modes: modes.to_vec(),
160            phases: phases.to_vec(),
161        };
162
163        let response = timeout(self.timeout, self.post_request(&url, &request))
164            .await
165            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
166            .map_err(|e| DeviceError::APIError(format!("Failed to measure quadratures: {e}")))?;
167
168        let measurement_response: QuadratureMeasurementResponse =
169            response.json().await.map_err(|e| {
170                DeviceError::Deserialization(format!("Failed to parse measurement: {e}"))
171            })?;
172
173        Ok(measurement_response.quadratures)
174    }
175
176    /// Perform photon number measurement
177    pub async fn measure_photon_numbers(
178        &self,
179        device_id: &str,
180        modes: &[usize],
181    ) -> DeviceResult<Vec<usize>> {
182        let url = format!("{}/devices/{}/measure/photons", self.base_url, device_id);
183        let request = PhotonMeasurementRequest {
184            modes: modes.to_vec(),
185        };
186
187        let response = timeout(self.timeout, self.post_request(&url, &request))
188            .await
189            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
190            .map_err(|e| DeviceError::APIError(format!("Failed to measure photons: {e}")))?;
191
192        let measurement_response: PhotonMeasurementResponse =
193            response.json().await.map_err(|e| {
194                DeviceError::Deserialization(format!("Failed to parse measurement: {e}"))
195            })?;
196
197        Ok(measurement_response.photon_numbers)
198    }
199
200    /// Perform homodyne detection
201    pub async fn homodyne_detection(
202        &self,
203        device_id: &str,
204        mode: usize,
205        phase: f64,
206        shots: usize,
207    ) -> DeviceResult<Vec<f64>> {
208        let url = format!("{}/devices/{}/measure/homodyne", self.base_url, device_id);
209        let request = HomodyneMeasurementRequest { mode, phase, shots };
210
211        let response = timeout(self.timeout, self.post_request(&url, &request))
212            .await
213            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
214            .map_err(|e| DeviceError::APIError(format!("Failed to perform homodyne: {e}")))?;
215
216        let measurement_response: HomodyneMeasurementResponse = response
217            .json()
218            .await
219            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse homodyne: {e}")))?;
220
221        Ok(measurement_response.measurements)
222    }
223
224    /// Perform heterodyne detection
225    pub async fn heterodyne_detection(
226        &self,
227        device_id: &str,
228        mode: usize,
229        shots: usize,
230    ) -> DeviceResult<Vec<(f64, f64)>> {
231        let url = format!("{}/devices/{}/measure/heterodyne", self.base_url, device_id);
232        let request = HeterodyneMeasurementRequest { mode, shots };
233
234        let response = timeout(self.timeout, self.post_request(&url, &request))
235            .await
236            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
237            .map_err(|e| DeviceError::APIError(format!("Failed to perform heterodyne: {e}")))?;
238
239        let measurement_response: HeterodyneMeasurementResponse =
240            response.json().await.map_err(|e| {
241                DeviceError::Deserialization(format!("Failed to parse heterodyne: {e}"))
242            })?;
243
244        Ok(measurement_response.measurements)
245    }
246
247    /// Check device availability
248    pub async fn check_availability(&self) -> DeviceResult<bool> {
249        let url = format!("{}/status", self.base_url);
250        let response = timeout(self.timeout, self.get_request(&url))
251            .await
252            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
253            .map_err(|e| DeviceError::APIError(format!("Failed to check availability: {e}")))?;
254
255        let status: serde_json::Value = response
256            .json()
257            .await
258            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse status: {e}")))?;
259
260        Ok(status
261            .get("available")
262            .and_then(|v| v.as_bool())
263            .unwrap_or(false))
264    }
265
266    /// Check if this is a simulator
267    pub async fn is_simulator(&self) -> DeviceResult<bool> {
268        let url = format!("{}/info", self.base_url);
269        let response = timeout(self.timeout, self.get_request(&url))
270            .await
271            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
272            .map_err(|e| DeviceError::APIError(format!("Failed to get info: {e}")))?;
273
274        let info: serde_json::Value = response
275            .json()
276            .await
277            .map_err(|e| DeviceError::Deserialization(format!("Failed to parse info: {e}")))?;
278
279        Ok(info
280            .get("is_simulator")
281            .and_then(|v| v.as_bool())
282            .unwrap_or(false))
283    }
284
285    /// Get queue time estimate
286    pub async fn get_queue_time(&self) -> DeviceResult<Duration> {
287        let url = format!("{}/queue", self.base_url);
288        let response = timeout(self.timeout, self.get_request(&url))
289            .await
290            .map_err(|_| DeviceError::Timeout("Request timed out".to_string()))?
291            .map_err(|e| DeviceError::APIError(format!("Failed to get queue time: {e}")))?;
292
293        let queue_info: serde_json::Value = response.json().await.map_err(|e| {
294            DeviceError::Deserialization(format!("Failed to parse queue info: {e}"))
295        })?;
296
297        let wait_time_secs = queue_info
298            .get("estimated_wait_time_seconds")
299            .and_then(|v| v.as_u64())
300            .unwrap_or(0);
301
302        Ok(Duration::from_secs(wait_time_secs))
303    }
304
305    /// Execute a photonic circuit
306    pub async fn execute_photonic_circuit(
307        &self,
308        circuit: &str,
309        shots: usize,
310        config: &HashMap<String, serde_json::Value>,
311    ) -> DeviceResult<PhotonicJobResult> {
312        let job_request = PhotonicJobRequest {
313            device_id: "default".to_string(),
314            circuit: circuit.to_string(),
315            shots,
316            config: Some(config.clone()),
317            priority: None,
318            tags: None,
319        };
320
321        let job_id = self.submit_job(&job_request).await?;
322
323        // Poll for completion (simplified)
324        loop {
325            let status = self.get_job_status(&job_id).await?;
326            match status.status.as_str() {
327                "completed" => return self.get_job_results(&job_id).await,
328                "failed" => {
329                    return Err(DeviceError::ExecutionFailed(
330                        status
331                            .error_message
332                            .unwrap_or_else(|| "Job failed".to_string()),
333                    ))
334                }
335                _ => tokio::time::sleep(Duration::from_millis(100)).await,
336            }
337        }
338    }
339
340    /// Calculate quantum correlations between modes
341    pub async fn calculate_correlations(
342        &self,
343        modes: &[(usize, usize)],
344        correlation_type: &str,
345    ) -> DeviceResult<HashMap<String, f64>> {
346        // Simplified implementation - returns mock correlations
347        let mut correlations = HashMap::new();
348        for (i, (mode1, mode2)) in modes.iter().enumerate() {
349            correlations.insert(format!("{mode1}_{mode2}_correlation"), 0.85);
350        }
351        Ok(correlations)
352    }
353
354    /// Estimate state fidelity
355    pub async fn estimate_fidelity(
356        &self,
357        target_state: &str,
358        measurement_data: &super::PhotonicMeasurementData,
359    ) -> DeviceResult<f64> {
360        // Simplified implementation - returns mock fidelity
361        Ok(0.95)
362    }
363
364    /// Perform GET request
365    async fn get_request(&self, url: &str) -> Result<reqwest::Response, reqwest::Error> {
366        let mut request = self.client.get(url).bearer_auth(&self.auth_token);
367
368        for (key, value) in &self.headers {
369            request = request.header(key, value);
370        }
371
372        request.send().await?.error_for_status()
373    }
374
375    /// Perform POST request
376    async fn post_request<T: Serialize>(
377        &self,
378        url: &str,
379        body: &T,
380    ) -> Result<reqwest::Response, reqwest::Error> {
381        let mut request = self
382            .client
383            .post(url)
384            .bearer_auth(&self.auth_token)
385            .json(body);
386
387        for (key, value) in &self.headers {
388            request = request.header(key, value);
389        }
390
391        request.send().await?.error_for_status()
392    }
393
394    /// Perform DELETE request
395    async fn delete_request(&self, url: &str) -> Result<reqwest::Response, reqwest::Error> {
396        let mut request = self.client.delete(url).bearer_auth(&self.auth_token);
397
398        for (key, value) in &self.headers {
399            request = request.header(key, value);
400        }
401
402        request.send().await?.error_for_status()
403    }
404}
405
406/// Information about a photonic device
407#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct PhotonicDeviceInfo {
409    pub id: String,
410    pub name: String,
411    pub provider: String,
412    pub system_type: String,
413    pub mode_count: usize,
414    pub cutoff_dimension: Option<usize>,
415    pub squeezing_range: Option<(f64, f64)>,
416    pub loss_rate: f64,
417    pub detection_efficiency: f64,
418    pub gate_fidelity: f64,
419    pub is_available: bool,
420    pub queue_length: usize,
421    pub estimated_wait_time: Option<Duration>,
422    pub capabilities: Vec<String>,
423    pub properties: HashMap<String, String>,
424}
425
426/// Request to submit a job to a photonic device
427#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct PhotonicJobRequest {
429    pub device_id: String,
430    pub circuit: String, // Serialized circuit
431    pub shots: usize,
432    pub config: Option<HashMap<String, serde_json::Value>>,
433    pub priority: Option<String>,
434    pub tags: Option<HashMap<String, String>>,
435}
436
437/// Response from job submission
438#[derive(Debug, Clone, Serialize, Deserialize)]
439pub struct PhotonicJobResponse {
440    pub job_id: String,
441    pub status: String,
442    pub estimated_execution_time: Option<Duration>,
443    pub queue_position: Option<usize>,
444}
445
446/// Status of a photonic job
447#[derive(Debug, Clone, Serialize, Deserialize)]
448pub struct PhotonicJobStatus {
449    pub job_id: String,
450    pub status: String,
451    pub created_at: String,
452    pub started_at: Option<String>,
453    pub completed_at: Option<String>,
454    pub progress: Option<f64>,
455    pub queue_position: Option<usize>,
456    pub estimated_completion: Option<String>,
457    pub error_message: Option<String>,
458}
459
460/// Results from a photonic job
461#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct PhotonicJobResult {
463    pub job_id: String,
464    pub device_id: String,
465    pub status: String,
466    pub results: HashMap<String, serde_json::Value>,
467    pub metadata: HashMap<String, String>,
468    pub execution_time: Duration,
469    pub shots_completed: usize,
470    pub fidelity_estimate: Option<f64>,
471}
472
473/// Quadrature measurement request
474#[derive(Debug, Clone, Serialize, Deserialize)]
475pub struct QuadratureMeasurementRequest {
476    pub modes: Vec<usize>,
477    pub phases: Vec<f64>,
478}
479
480/// Quadrature measurement response
481#[derive(Debug, Clone, Serialize, Deserialize)]
482pub struct QuadratureMeasurementResponse {
483    pub quadratures: Vec<(f64, f64)>,
484}
485
486/// Photon number measurement request
487#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct PhotonMeasurementRequest {
489    pub modes: Vec<usize>,
490}
491
492/// Photon number measurement response
493#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct PhotonMeasurementResponse {
495    pub photon_numbers: Vec<usize>,
496}
497
498/// Homodyne measurement request
499#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct HomodyneMeasurementRequest {
501    pub mode: usize,
502    pub phase: f64,
503    pub shots: usize,
504}
505
506/// Homodyne measurement response
507#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct HomodyneMeasurementResponse {
509    pub measurements: Vec<f64>,
510}
511
512/// Heterodyne measurement request
513#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct HeterodyneMeasurementRequest {
515    pub mode: usize,
516    pub shots: usize,
517}
518
519/// Heterodyne measurement response
520#[derive(Debug, Clone, Serialize, Deserialize)]
521pub struct HeterodyneMeasurementResponse {
522    pub measurements: Vec<(f64, f64)>,
523}
524
525#[cfg(test)]
526mod tests {
527    use super::*;
528
529    #[test]
530    fn test_photonic_client_creation() {
531        let client = PhotonicClient::new(
532            "https://api.photonic.example.com".to_string(),
533            "test_token".to_string(),
534        );
535        assert!(client.is_ok());
536    }
537
538    #[test]
539    fn test_photonic_client_with_config() {
540        let mut headers = HashMap::new();
541        headers.insert("User-Agent".to_string(), "QuantRS2".to_string());
542
543        let client = PhotonicClient::with_config(
544            "https://api.photonic.example.com".to_string(),
545            "test_token".to_string(),
546            60,
547            headers,
548        );
549        assert!(client.is_ok());
550    }
551
552    #[test]
553    fn test_photonic_device_info_serialization() {
554        let device_info = PhotonicDeviceInfo {
555            id: "photonic_1".to_string(),
556            name: "Test Photonic Device".to_string(),
557            provider: "TestProvider".to_string(),
558            system_type: "ContinuousVariable".to_string(),
559            mode_count: 8,
560            cutoff_dimension: Some(10),
561            squeezing_range: Some((-2.0, 2.0)),
562            loss_rate: 0.01,
563            detection_efficiency: 0.9,
564            gate_fidelity: 0.99,
565            is_available: true,
566            queue_length: 0,
567            estimated_wait_time: None,
568            capabilities: vec!["cv_operations".to_string()],
569            properties: HashMap::new(),
570        };
571
572        let serialized = serde_json::to_string(&device_info);
573        assert!(serialized.is_ok());
574
575        let deserialized: Result<PhotonicDeviceInfo, _> =
576            serde_json::from_str(&serialized.expect("Serialization should succeed"));
577        assert!(deserialized.is_ok());
578    }
579}