1use crate::{DeviceError, DeviceResult};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::time::Duration;
10use tokio::time::timeout;
11
12#[derive(Debug, Clone)]
14pub struct PhotonicClient {
15 pub base_url: String,
17 pub auth_token: String,
19 pub client: reqwest::Client,
21 pub timeout: Duration,
23 pub headers: HashMap<String, String>,
25}
26
27impl PhotonicClient {
28 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub async fn calculate_correlations(
342 &self,
343 modes: &[(usize, usize)],
344 correlation_type: &str,
345 ) -> DeviceResult<HashMap<String, f64>> {
346 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 pub async fn estimate_fidelity(
356 &self,
357 target_state: &str,
358 measurement_data: &super::PhotonicMeasurementData,
359 ) -> DeviceResult<f64> {
360 Ok(0.95)
362 }
363
364 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 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 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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct PhotonicJobRequest {
429 pub device_id: String,
430 pub circuit: String, 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#[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#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
475pub struct QuadratureMeasurementRequest {
476 pub modes: Vec<usize>,
477 pub phases: Vec<f64>,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize)]
482pub struct QuadratureMeasurementResponse {
483 pub quadratures: Vec<(f64, f64)>,
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct PhotonMeasurementRequest {
489 pub modes: Vec<usize>,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct PhotonMeasurementResponse {
495 pub photon_numbers: Vec<usize>,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct HomodyneMeasurementRequest {
501 pub mode: usize,
502 pub phase: f64,
503 pub shots: usize,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct HomodyneMeasurementResponse {
509 pub measurements: Vec<f64>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct HeterodyneMeasurementRequest {
515 pub mode: usize,
516 pub shots: usize,
517}
518
519#[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}