quantrs2_device/continuous_variable/
measurements.rs

1//! Continuous variable quantum measurements
2//!
3//! This module implements various measurement schemes for CV quantum systems,
4//! including homodyne, heterodyne, photon number, and parity measurements.
5
6use super::{CVDeviceConfig, Complex, GaussianState};
7use crate::{DeviceError, DeviceResult};
8use scirs2_core::random::prelude::*;
9use scirs2_core::random::{Distribution, Poisson, RandNormal};
10use serde::{Deserialize, Serialize};
11use std::f64::consts::PI;
12
13// Alias for backward compatibility
14type Normal<T> = RandNormal<T>;
15
16/// Types of CV measurements
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum CVMeasurementScheme {
19    /// Homodyne detection at specific phase
20    Homodyne { phase: f64 },
21    /// Heterodyne detection (simultaneous x and p)
22    Heterodyne,
23    /// Photon number measurement
24    PhotonNumber,
25    /// Parity measurement (even/odd photon number)
26    Parity,
27    /// Bell measurement for entangled modes
28    Bell { basis: BellBasis },
29    /// Fock state projection
30    FockProjection { n: usize },
31    /// Coherent state projection
32    CoherentProjection { alpha: Complex },
33}
34
35/// Bell measurement bases for CV systems
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
37pub enum BellBasis {
38    /// X-X correlation measurement
39    XBasis,
40    /// P-P correlation measurement
41    PBasis,
42    /// Mixed X-P correlation
43    MixedBasis { phase: f64 },
44}
45
46/// CV measurement result with statistics
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct CVMeasurementResult {
49    /// The measurement outcome
50    pub outcome: CVMeasurementOutcome,
51    /// Measurement fidelity/confidence
52    pub fidelity: f64,
53    /// Standard deviation of the measurement
54    pub standard_deviation: f64,
55    /// Number of samples used
56    pub sample_count: usize,
57    /// Timestamp
58    pub timestamp: f64,
59}
60
61/// Different types of measurement outcomes
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub enum CVMeasurementOutcome {
64    /// Real-valued outcome (homodyne)
65    Real(f64),
66    /// Complex-valued outcome (heterodyne)
67    Complex(Complex),
68    /// Integer outcome (photon number)
69    Integer(i32),
70    /// Boolean outcome (parity)
71    Boolean(bool),
72    /// Bell correlation outcome
73    BellCorrelation { correlation: f64, phase: f64 },
74}
75
76/// Configuration for CV measurements
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct CVMeasurementConfig {
79    /// Number of measurement samples
80    pub num_samples: usize,
81    /// Integration time (seconds)
82    pub integration_time: f64,
83    /// Local oscillator power (mW)
84    pub lo_power_mw: f64,
85    /// Detection bandwidth (Hz)
86    pub bandwidth_hz: f64,
87    /// Phase lock stability (rad RMS)
88    pub phase_stability: f64,
89    /// Enable post-processing
90    pub enable_post_processing: bool,
91    /// Calibration data
92    pub calibration: MeasurementCalibration,
93}
94
95/// Calibration data for CV measurements
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct MeasurementCalibration {
98    /// Detection efficiency per mode
99    pub efficiency: Vec<f64>,
100    /// Electronic noise level (dB)
101    pub electronic_noise_db: f64,
102    /// Dark count rate (Hz)
103    pub dark_count_rate: f64,
104    /// Gain calibration factors
105    pub gain_factors: Vec<f64>,
106    /// Phase calibration offsets
107    pub phase_offsets: Vec<f64>,
108}
109
110impl Default for CVMeasurementConfig {
111    fn default() -> Self {
112        Self {
113            num_samples: 10000,
114            integration_time: 0.001, // 1 ms
115            lo_power_mw: 10.0,
116            bandwidth_hz: 10e6,
117            phase_stability: 0.01,
118            enable_post_processing: true,
119            calibration: MeasurementCalibration::default(),
120        }
121    }
122}
123
124impl Default for MeasurementCalibration {
125    fn default() -> Self {
126        Self {
127            efficiency: vec![0.95; 10], // Default for up to 10 modes
128            electronic_noise_db: -90.0,
129            dark_count_rate: 100.0,
130            gain_factors: vec![1.0; 10],
131            phase_offsets: vec![0.0; 10],
132        }
133    }
134}
135
136/// CV measurement engine
137pub struct CVMeasurementEngine {
138    /// Measurement configuration
139    config: CVMeasurementConfig,
140    /// Current measurement results
141    measurement_history: Vec<CVMeasurementResult>,
142    /// Calibration state
143    is_calibrated: bool,
144}
145
146impl CVMeasurementEngine {
147    /// Create a new measurement engine
148    pub const fn new(config: CVMeasurementConfig) -> Self {
149        Self {
150            config,
151            measurement_history: Vec::new(),
152            is_calibrated: false,
153        }
154    }
155
156    /// Calibrate the measurement system
157    pub async fn calibrate(&mut self) -> DeviceResult<()> {
158        println!("Calibrating CV measurement system...");
159
160        // Simulate calibration process
161        tokio::time::sleep(std::time::Duration::from_millis(500)).await;
162
163        // Update calibration factors
164        for i in 0..self.config.calibration.efficiency.len() {
165            self.config.calibration.efficiency[i] =
166                0.03f64.mul_add(thread_rng().gen::<f64>(), 0.95);
167            self.config.calibration.gain_factors[i] =
168                0.1f64.mul_add(thread_rng().gen::<f64>() - 0.5, 1.0);
169            self.config.calibration.phase_offsets[i] = 0.1 * (thread_rng().gen::<f64>() - 0.5);
170        }
171
172        self.is_calibrated = true;
173        println!("Calibration complete");
174        Ok(())
175    }
176
177    /// Perform homodyne measurement
178    pub async fn homodyne_measurement(
179        &mut self,
180        state: &mut GaussianState,
181        mode: usize,
182        phase: f64,
183    ) -> DeviceResult<CVMeasurementResult> {
184        if !self.is_calibrated {
185            return Err(DeviceError::DeviceNotInitialized(
186                "Measurement system not calibrated".to_string(),
187            ));
188        }
189
190        if mode >= state.num_modes {
191            return Err(DeviceError::InvalidInput(format!(
192                "Mode {mode} exceeds available modes"
193            )));
194        }
195
196        // Apply phase offset calibration
197        let calibrated_phase = phase
198            + self
199                .config
200                .calibration
201                .phase_offsets
202                .get(mode)
203                .unwrap_or(&0.0);
204
205        // Get the theoretical mean and variance
206        let cos_phi = calibrated_phase.cos();
207        let sin_phi = calibrated_phase.sin();
208
209        let mean_x = state.mean_vector[2 * mode];
210        let mean_p = state.mean_vector[2 * mode + 1];
211        let theoretical_mean = cos_phi * mean_x + sin_phi * mean_p;
212
213        let var_x = state.covariancematrix[2 * mode][2 * mode];
214        let var_p = state.covariancematrix[2 * mode + 1][2 * mode + 1];
215        let cov_xp = state.covariancematrix[2 * mode][2 * mode + 1];
216
217        let theoretical_variance = (2.0 * cos_phi * sin_phi).mul_add(
218            cov_xp,
219            cos_phi.powi(2).mul_add(var_x, sin_phi.powi(2) * var_p),
220        );
221
222        // Add noise effects
223        let efficiency = self
224            .config
225            .calibration
226            .efficiency
227            .get(mode)
228            .unwrap_or(&0.95);
229        let gain = self
230            .config
231            .calibration
232            .gain_factors
233            .get(mode)
234            .unwrap_or(&1.0);
235
236        let noise_variance = self.calculate_detection_noise(mode);
237        let total_variance = theoretical_variance / efficiency + noise_variance;
238
239        // Perform multiple samples
240        let mut samples = Vec::new();
241        let distribution = Normal::new(theoretical_mean, total_variance.sqrt())
242            .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {e}")))?;
243
244        let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
245        for _ in 0..self.config.num_samples {
246            let sample = distribution.sample(&mut rng) * gain;
247            samples.push(sample);
248        }
249
250        // Calculate statistics
251        let mean_outcome = samples.iter().sum::<f64>() / samples.len() as f64;
252        let variance = samples
253            .iter()
254            .map(|x| (x - mean_outcome).powi(2))
255            .sum::<f64>()
256            / samples.len() as f64;
257        let std_dev = variance.sqrt();
258
259        // Estimate fidelity
260        let fidelity =
261            self.estimate_measurement_fidelity(&samples, theoretical_mean, total_variance);
262
263        let result = CVMeasurementResult {
264            outcome: CVMeasurementOutcome::Real(mean_outcome),
265            fidelity,
266            standard_deviation: std_dev,
267            sample_count: samples.len(),
268            timestamp: std::time::SystemTime::now()
269                .duration_since(std::time::UNIX_EPOCH)
270                .unwrap_or_default()
271                .as_secs_f64(),
272        };
273
274        // Condition the state on the measurement
275        state.condition_on_homodyne_measurement(mode, calibrated_phase, mean_outcome)?;
276
277        self.measurement_history.push(result.clone());
278        Ok(result)
279    }
280
281    /// Perform heterodyne measurement
282    pub async fn heterodyne_measurement(
283        &mut self,
284        state: &mut GaussianState,
285        mode: usize,
286    ) -> DeviceResult<CVMeasurementResult> {
287        if !self.is_calibrated {
288            return Err(DeviceError::DeviceNotInitialized(
289                "Measurement system not calibrated".to_string(),
290            ));
291        }
292
293        if mode >= state.num_modes {
294            return Err(DeviceError::InvalidInput(format!(
295                "Mode {mode} exceeds available modes"
296            )));
297        }
298
299        let efficiency = self
300            .config
301            .calibration
302            .efficiency
303            .get(mode)
304            .unwrap_or(&0.95);
305        let gain = self
306            .config
307            .calibration
308            .gain_factors
309            .get(mode)
310            .unwrap_or(&1.0);
311
312        // Get state parameters
313        let mean_x = state.mean_vector[2 * mode];
314        let mean_p = state.mean_vector[2 * mode + 1];
315        let var_x = state.covariancematrix[2 * mode][2 * mode];
316        let var_p = state.covariancematrix[2 * mode + 1][2 * mode + 1];
317
318        // Add noise
319        let noise_variance = self.calculate_detection_noise(mode);
320
321        // Sample both quadratures
322        let mut x_samples = Vec::new();
323        let mut p_samples = Vec::new();
324
325        let x_distribution =
326            Normal::new(mean_x, (var_x / efficiency + noise_variance / 2.0).sqrt())
327                .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {e}")))?;
328        let p_distribution =
329            Normal::new(mean_p, (var_p / efficiency + noise_variance / 2.0).sqrt())
330                .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {e}")))?;
331
332        let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
333        for _ in 0..self.config.num_samples {
334            x_samples.push(x_distribution.sample(&mut rng) * gain);
335            p_samples.push(p_distribution.sample(&mut rng) * gain);
336        }
337
338        // Calculate complex outcome
339        let mean_x_outcome = x_samples.iter().sum::<f64>() / x_samples.len() as f64;
340        let mean_p_outcome = p_samples.iter().sum::<f64>() / p_samples.len() as f64;
341
342        let complex_outcome = Complex::new(
343            mean_p_outcome.mul_add(Complex::i().real, mean_x_outcome) / (2.0_f64).sqrt(),
344            mean_x_outcome.mul_add(-Complex::i().imag, mean_p_outcome) / (2.0_f64).sqrt(),
345        );
346
347        // Calculate standard deviation
348        let x_var = x_samples
349            .iter()
350            .map(|x| (x - mean_x_outcome).powi(2))
351            .sum::<f64>()
352            / x_samples.len() as f64;
353        let p_var = p_samples
354            .iter()
355            .map(|p| (p - mean_p_outcome).powi(2))
356            .sum::<f64>()
357            / p_samples.len() as f64;
358        let std_dev = (x_var + p_var).sqrt();
359
360        // Estimate fidelity
361        let theoretical_variance = (var_x + var_p) / efficiency + noise_variance;
362        let fidelity = 1.0 / (1.0 + theoretical_variance);
363
364        let result = CVMeasurementResult {
365            outcome: CVMeasurementOutcome::Complex(complex_outcome),
366            fidelity,
367            standard_deviation: std_dev,
368            sample_count: self.config.num_samples,
369            timestamp: std::time::SystemTime::now()
370                .duration_since(std::time::UNIX_EPOCH)
371                .unwrap_or_default()
372                .as_secs_f64(),
373        };
374
375        // Condition state (heterodyne destroys the mode)
376        state.condition_on_heterodyne_measurement(mode, complex_outcome)?;
377
378        self.measurement_history.push(result.clone());
379        Ok(result)
380    }
381
382    /// Perform photon number measurement (simplified for Gaussian states)
383    pub async fn photon_number_measurement(
384        &mut self,
385        state: &GaussianState,
386        mode: usize,
387    ) -> DeviceResult<CVMeasurementResult> {
388        if !self.is_calibrated {
389            return Err(DeviceError::DeviceNotInitialized(
390                "Measurement system not calibrated".to_string(),
391            ));
392        }
393
394        if mode >= state.num_modes {
395            return Err(DeviceError::InvalidInput(format!(
396                "Mode {mode} exceeds available modes"
397            )));
398        }
399
400        // For Gaussian states, estimate photon number from second moments
401        let mean_x = state.mean_vector[2 * mode];
402        let mean_p = state.mean_vector[2 * mode + 1];
403        let var_x = state.covariancematrix[2 * mode][2 * mode];
404        let var_p = state.covariancematrix[2 * mode + 1][2 * mode + 1];
405
406        // Average photon number for Gaussian state
407        let mean_n = 0.5 * (mean_p.mul_add(mean_p, mean_x.powi(2)) / 2.0 + (var_x + var_p) - 1.0);
408
409        // For thermal/squeezed states, photon number follows geometric/negative binomial distribution
410        // Simplified: just add Poissonian noise around the mean
411        let efficiency = self
412            .config
413            .calibration
414            .efficiency
415            .get(mode)
416            .unwrap_or(&0.95);
417        let dark_counts = self.config.calibration.dark_count_rate * self.config.integration_time;
418
419        let detected_n = mean_n * efficiency + dark_counts;
420
421        // Sample from Poisson-like distribution
422        let mut samples = Vec::new();
423        let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
424        for _ in 0..self.config.num_samples {
425            let sample = if detected_n > 0.0 {
426                let poisson = Poisson::new(detected_n)
427                    .map_err(|e| DeviceError::InvalidInput(format!("Poisson error: {e}")))?;
428                poisson.sample(&mut rng) as f64
429            } else {
430                0.0
431            };
432            samples.push(sample);
433        }
434
435        let mean_outcome = samples.iter().sum::<f64>() / samples.len() as f64;
436        let variance = samples
437            .iter()
438            .map(|x| (x - mean_outcome).powi(2))
439            .sum::<f64>()
440            / samples.len() as f64;
441
442        let rounded_outcome = mean_outcome.round() as i32;
443        let fidelity = 1.0 / (1.0 + variance / (mean_outcome + 1.0));
444
445        let result = CVMeasurementResult {
446            outcome: CVMeasurementOutcome::Integer(rounded_outcome),
447            fidelity,
448            standard_deviation: variance.sqrt(),
449            sample_count: samples.len(),
450            timestamp: std::time::SystemTime::now()
451                .duration_since(std::time::UNIX_EPOCH)
452                .unwrap_or_default()
453                .as_secs_f64(),
454        };
455
456        self.measurement_history.push(result.clone());
457        Ok(result)
458    }
459
460    /// Perform parity measurement
461    pub async fn parity_measurement(
462        &mut self,
463        state: &GaussianState,
464        mode: usize,
465    ) -> DeviceResult<CVMeasurementResult> {
466        // Parity measurement for Gaussian states
467        let photon_result = self.photon_number_measurement(state, mode).await?;
468
469        let parity = match photon_result.outcome {
470            CVMeasurementOutcome::Integer(n) => n % 2 == 0,
471            _ => {
472                return Err(DeviceError::InvalidInput(
473                    "Invalid photon number result".to_string(),
474                ))
475            }
476        };
477
478        let result = CVMeasurementResult {
479            outcome: CVMeasurementOutcome::Boolean(parity),
480            fidelity: photon_result.fidelity,
481            standard_deviation: if parity { 0.0 } else { 1.0 },
482            sample_count: photon_result.sample_count,
483            timestamp: std::time::SystemTime::now()
484                .duration_since(std::time::UNIX_EPOCH)
485                .unwrap_or_default()
486                .as_secs_f64(),
487        };
488
489        self.measurement_history.push(result.clone());
490        Ok(result)
491    }
492
493    /// Perform Bell measurement on two modes
494    pub async fn bell_measurement(
495        &mut self,
496        state: &mut GaussianState,
497        mode1: usize,
498        mode2: usize,
499        basis: BellBasis,
500    ) -> DeviceResult<CVMeasurementResult> {
501        if mode1 >= state.num_modes || mode2 >= state.num_modes {
502            return Err(DeviceError::InvalidInput(
503                "One or both modes exceed available modes".to_string(),
504            ));
505        }
506
507        let (phase1, phase2) = match basis {
508            BellBasis::XBasis => (0.0, 0.0),
509            BellBasis::PBasis => (PI / 2.0, PI / 2.0),
510            BellBasis::MixedBasis { phase } => (0.0, phase),
511        };
512
513        // Measure both modes
514        let result1 = self.homodyne_measurement(state, mode1, phase1).await?;
515        let result2 = self.homodyne_measurement(state, mode2, phase2).await?;
516
517        // Calculate correlation
518        let (val1, val2) = match (result1.outcome, result2.outcome) {
519            (CVMeasurementOutcome::Real(v1), CVMeasurementOutcome::Real(v2)) => (v1, v2),
520            _ => {
521                return Err(DeviceError::InvalidInput(
522                    "Invalid homodyne results".to_string(),
523                ))
524            }
525        };
526
527        let correlation = val1 * val2;
528        let avg_phase = f64::midpoint(phase1, phase2);
529
530        let result = CVMeasurementResult {
531            outcome: CVMeasurementOutcome::BellCorrelation {
532                correlation,
533                phase: avg_phase,
534            },
535            fidelity: f64::midpoint(result1.fidelity, result2.fidelity),
536            standard_deviation: result1.standard_deviation.hypot(result2.standard_deviation),
537            sample_count: result1.sample_count.min(result2.sample_count),
538            timestamp: std::time::SystemTime::now()
539                .duration_since(std::time::UNIX_EPOCH)
540                .unwrap_or_default()
541                .as_secs_f64(),
542        };
543
544        self.measurement_history.push(result.clone());
545        Ok(result)
546    }
547
548    /// Calculate detection noise
549    fn calculate_detection_noise(&self, mode: usize) -> f64 {
550        let electronic_noise = 10.0_f64.powf(self.config.calibration.electronic_noise_db / 10.0);
551        let efficiency = self
552            .config
553            .calibration
554            .efficiency
555            .get(mode)
556            .unwrap_or(&0.95);
557        let phase_noise = self.config.phase_stability.powi(2);
558
559        electronic_noise + (1.0 - efficiency) / efficiency + phase_noise
560    }
561
562    /// Estimate measurement fidelity based on sample statistics
563    fn estimate_measurement_fidelity(
564        &self,
565        samples: &[f64],
566        theoretical_mean: f64,
567        theoretical_variance: f64,
568    ) -> f64 {
569        let sample_mean = samples.iter().sum::<f64>() / samples.len() as f64;
570        let sample_variance = samples
571            .iter()
572            .map(|x| (x - sample_mean).powi(2))
573            .sum::<f64>()
574            / samples.len() as f64;
575
576        let mean_error = (sample_mean - theoretical_mean).abs();
577        let variance_error = (sample_variance - theoretical_variance).abs();
578
579        // Simple fidelity estimate
580        let fidelity = 1.0 / (1.0 + mean_error + variance_error);
581        fidelity.clamp(0.0, 1.0)
582    }
583
584    /// Get measurement history
585    pub fn get_measurement_history(&self) -> &[CVMeasurementResult] {
586        &self.measurement_history
587    }
588
589    /// Clear measurement history
590    pub fn clear_history(&mut self) {
591        self.measurement_history.clear();
592    }
593
594    /// Get measurement statistics
595    pub fn get_measurement_statistics(&self) -> MeasurementStatistics {
596        if self.measurement_history.is_empty() {
597            return MeasurementStatistics::default();
598        }
599
600        let total_measurements = self.measurement_history.len();
601        let avg_fidelity = self
602            .measurement_history
603            .iter()
604            .map(|r| r.fidelity)
605            .sum::<f64>()
606            / total_measurements as f64;
607
608        let avg_std_dev = self
609            .measurement_history
610            .iter()
611            .map(|r| r.standard_deviation)
612            .sum::<f64>()
613            / total_measurements as f64;
614
615        let total_samples = self
616            .measurement_history
617            .iter()
618            .map(|r| r.sample_count)
619            .sum::<usize>();
620
621        MeasurementStatistics {
622            total_measurements,
623            average_fidelity: avg_fidelity,
624            average_standard_deviation: avg_std_dev,
625            total_samples,
626            is_calibrated: self.is_calibrated,
627        }
628    }
629}
630
631/// Statistics for measurement performance
632#[derive(Debug, Clone, Serialize, Deserialize)]
633pub struct MeasurementStatistics {
634    pub total_measurements: usize,
635    pub average_fidelity: f64,
636    pub average_standard_deviation: f64,
637    pub total_samples: usize,
638    pub is_calibrated: bool,
639}
640
641impl Default for MeasurementStatistics {
642    fn default() -> Self {
643        Self {
644            total_measurements: 0,
645            average_fidelity: 0.0,
646            average_standard_deviation: 0.0,
647            total_samples: 0,
648            is_calibrated: false,
649        }
650    }
651}
652
653#[cfg(test)]
654mod tests {
655    use super::*;
656
657    #[tokio::test]
658    async fn test_measurement_engine_creation() {
659        let config = CVMeasurementConfig::default();
660        let engine = CVMeasurementEngine::new(config);
661        assert!(!engine.is_calibrated);
662        assert_eq!(engine.measurement_history.len(), 0);
663    }
664
665    #[tokio::test]
666    async fn test_calibration() {
667        let config = CVMeasurementConfig::default();
668        let mut engine = CVMeasurementEngine::new(config);
669
670        engine
671            .calibrate()
672            .await
673            .expect("Engine calibration should succeed");
674        assert!(engine.is_calibrated);
675    }
676
677    #[tokio::test]
678    async fn test_homodyne_measurement() {
679        let config = CVMeasurementConfig::default();
680        let mut engine = CVMeasurementEngine::new(config);
681        engine
682            .calibrate()
683            .await
684            .expect("Engine calibration should succeed");
685
686        let mut state =
687            GaussianState::coherent_state(2, vec![Complex::new(2.0, 0.0), Complex::new(0.0, 0.0)])
688                .expect("Coherent state creation should succeed");
689
690        let result = engine
691            .homodyne_measurement(&mut state, 0, 0.0)
692            .await
693            .expect("Homodyne measurement should succeed");
694
695        match result.outcome {
696            CVMeasurementOutcome::Real(value) => {
697                assert!(value > 0.0); // Should measure positive x quadrature
698            }
699            _ => panic!("Expected real outcome"),
700        }
701
702        assert!(result.fidelity > 0.0);
703        assert_eq!(engine.measurement_history.len(), 1);
704    }
705
706    #[tokio::test]
707    async fn test_heterodyne_measurement() {
708        let config = CVMeasurementConfig::default();
709        let mut engine = CVMeasurementEngine::new(config);
710        engine
711            .calibrate()
712            .await
713            .expect("Engine calibration should succeed");
714
715        let mut state = GaussianState::coherent_state(1, vec![Complex::new(1.0, 0.5)])
716            .expect("Coherent state creation should succeed");
717
718        let result = engine
719            .heterodyne_measurement(&mut state, 0)
720            .await
721            .expect("Heterodyne measurement should succeed");
722
723        match result.outcome {
724            CVMeasurementOutcome::Complex(z) => {
725                assert!(z.magnitude() > 0.0);
726            }
727            _ => panic!("Expected complex outcome"),
728        }
729    }
730
731    #[tokio::test]
732    async fn test_photon_number_measurement() {
733        let config = CVMeasurementConfig::default();
734        let mut engine = CVMeasurementEngine::new(config);
735        engine
736            .calibrate()
737            .await
738            .expect("Engine calibration should succeed");
739
740        let state = GaussianState::coherent_state(1, vec![Complex::new(2.0, 0.0)])
741            .expect("Coherent state creation should succeed");
742
743        let result = engine
744            .photon_number_measurement(&state, 0)
745            .await
746            .expect("Photon number measurement should succeed");
747
748        match result.outcome {
749            CVMeasurementOutcome::Integer(n) => {
750                assert!(n >= 0); // Photon number should be non-negative
751            }
752            _ => panic!("Expected integer outcome"),
753        }
754    }
755
756    #[tokio::test]
757    async fn test_parity_measurement() {
758        let config = CVMeasurementConfig::default();
759        let mut engine = CVMeasurementEngine::new(config);
760        engine
761            .calibrate()
762            .await
763            .expect("Engine calibration should succeed");
764
765        let state = GaussianState::vacuum_state(1);
766
767        let result = engine
768            .parity_measurement(&state, 0)
769            .await
770            .expect("Parity measurement should succeed");
771
772        match result.outcome {
773            CVMeasurementOutcome::Boolean(parity) => {
774                // Vacuum state should have even parity (0 photons)
775                assert!(parity);
776            }
777            _ => panic!("Expected boolean outcome"),
778        }
779    }
780
781    #[test]
782    fn test_measurement_config_defaults() {
783        let config = CVMeasurementConfig::default();
784        assert_eq!(config.num_samples, 10000);
785        assert_eq!(config.integration_time, 0.001);
786        assert!(config.enable_post_processing);
787    }
788
789    #[test]
790    fn test_measurement_statistics() {
791        let config = CVMeasurementConfig::default();
792        let engine = CVMeasurementEngine::new(config);
793
794        let stats = engine.get_measurement_statistics();
795        assert_eq!(stats.total_measurements, 0);
796        assert!(!stats.is_calibrated);
797    }
798}