quantrs2_device/photonic/
continuous_variable.rs

1//! Continuous Variable Quantum Computing Implementation
2//!
3//! This module implements continuous variable (CV) quantum computing operations,
4//! including Gaussian states, displacement operations, squeezing, and CV gates.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::f64::consts::{PI, SQRT_2};
9use thiserror::Error;
10
11use super::{PhotonicMode, PhotonicSystemType};
12use crate::DeviceResult;
13
14/// Errors specific to continuous variable operations
15#[derive(Error, Debug)]
16pub enum CVError {
17    #[error("Invalid displacement parameter: {0}")]
18    InvalidDisplacement(String),
19    #[error("Invalid squeezing parameter: {0}")]
20    InvalidSqueezing(String),
21    #[error("Mode not found: {0}")]
22    ModeNotFound(usize),
23    #[error("Incompatible CV operation: {0}")]
24    IncompatibleOperation(String),
25    #[error("Matrix dimension mismatch: {0}")]
26    MatrixDimensionMismatch(String),
27}
28
29pub type CVResult<T> = Result<T, CVError>;
30
31/// Complex number representation for CV quantum computing
32#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
33pub struct Complex {
34    pub real: f64,
35    pub imag: f64,
36}
37
38impl Complex {
39    pub const fn new(real: f64, imag: f64) -> Self {
40        Self { real, imag }
41    }
42
43    pub fn magnitude(&self) -> f64 {
44        self.real.hypot(self.imag)
45    }
46
47    pub fn phase(&self) -> f64 {
48        self.imag.atan2(self.real)
49    }
50
51    #[must_use]
52    pub fn conj(&self) -> Self {
53        Self {
54            real: self.real,
55            imag: -self.imag,
56        }
57    }
58}
59
60/// Gaussian state representation in phase space
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct GaussianState {
63    /// Mean vector (displacement vector)
64    pub mean: Vec<f64>,
65    /// Covariance matrix
66    pub covariance: Vec<Vec<f64>>,
67    /// Number of modes
68    pub num_modes: usize,
69}
70
71impl GaussianState {
72    /// Create vacuum state
73    pub fn vacuum(num_modes: usize) -> Self {
74        let mean = vec![0.0; 2 * num_modes];
75        let mut covariance = vec![vec![0.0; 2 * num_modes]; 2 * num_modes];
76
77        // Initialize covariance matrix for vacuum state
78        for i in 0..2 * num_modes {
79            covariance[i][i] = 0.5; // Vacuum noise
80        }
81
82        Self {
83            mean,
84            covariance,
85            num_modes,
86        }
87    }
88
89    /// Create coherent state with displacement alpha
90    pub fn coherent(alpha: Complex, mode: usize, num_modes: usize) -> CVResult<Self> {
91        if mode >= num_modes {
92            return Err(CVError::ModeNotFound(mode));
93        }
94
95        let mut state = Self::vacuum(num_modes);
96
97        // Set displacement in position and momentum
98        state.mean[2 * mode] = alpha.real * SQRT_2;
99        state.mean[2 * mode + 1] = alpha.imag * SQRT_2;
100
101        Ok(state)
102    }
103
104    /// Create squeezed vacuum state
105    pub fn squeezed_vacuum(r: f64, phi: f64, mode: usize, num_modes: usize) -> CVResult<Self> {
106        if mode >= num_modes {
107            return Err(CVError::ModeNotFound(mode));
108        }
109
110        if r.abs() > 10.0 {
111            return Err(CVError::InvalidSqueezing(
112                "Squeezing parameter too large".to_string(),
113            ));
114        }
115
116        let mut state = Self::vacuum(num_modes);
117
118        // Apply squeezing to covariance matrix
119        let cos_2phi = (2.0 * phi).cos();
120        let sin_2phi = (2.0 * phi).sin();
121        let exp_2r = (2.0 * r).exp();
122        let exp_neg_2r = (-2.0 * r).exp();
123
124        let x_idx = 2 * mode;
125        let p_idx = 2 * mode + 1;
126
127        // Update covariance matrix elements using correct squeezing formulas
128        // For phi=0: x-variance = 0.5 * e^(-2r), p-variance = 0.5 * e^(2r)
129        state.covariance[x_idx][x_idx] =
130            0.5 * (exp_2r - exp_neg_2r).mul_add(-cos_2phi, exp_neg_2r + exp_2r) / 2.0;
131        state.covariance[p_idx][p_idx] =
132            0.5 * (exp_2r - exp_neg_2r).mul_add(cos_2phi, exp_2r + exp_neg_2r) / 2.0;
133        state.covariance[x_idx][p_idx] = 0.5 * (exp_2r - exp_neg_2r) * sin_2phi / 2.0;
134        state.covariance[p_idx][x_idx] = state.covariance[x_idx][p_idx];
135
136        Ok(state)
137    }
138
139    /// Create thermal state
140    pub fn thermal(n_bar: f64, mode: usize, num_modes: usize) -> CVResult<Self> {
141        if mode >= num_modes {
142            return Err(CVError::ModeNotFound(mode));
143        }
144
145        if n_bar < 0.0 {
146            return Err(CVError::InvalidDisplacement(
147                "Thermal photon number must be non-negative".to_string(),
148            ));
149        }
150
151        let mut state = Self::vacuum(num_modes);
152
153        // Add thermal noise
154        let thermal_variance = 0.5 * 2.0f64.mul_add(n_bar, 1.0);
155        state.covariance[2 * mode][2 * mode] = thermal_variance;
156        state.covariance[2 * mode + 1][2 * mode + 1] = thermal_variance;
157
158        Ok(state)
159    }
160
161    /// Apply displacement operation
162    pub fn displace(&mut self, alpha: Complex, mode: usize) -> CVResult<()> {
163        if mode >= self.num_modes {
164            return Err(CVError::ModeNotFound(mode));
165        }
166
167        // Add displacement to mean vector
168        self.mean[2 * mode] += alpha.real * SQRT_2;
169        self.mean[2 * mode + 1] += alpha.imag * SQRT_2;
170
171        Ok(())
172    }
173
174    /// Apply squeezing operation
175    pub fn squeeze(&mut self, r: f64, phi: f64, mode: usize) -> CVResult<()> {
176        if mode >= self.num_modes {
177            return Err(CVError::ModeNotFound(mode));
178        }
179
180        if r.abs() > 10.0 {
181            return Err(CVError::InvalidSqueezing(
182                "Squeezing parameter too large".to_string(),
183            ));
184        }
185
186        // Squeezing transformation matrix
187        let cos_phi = phi.cos();
188        let sin_phi = phi.sin();
189        let cosh_r = r.cosh();
190        let sinh_r = r.sinh();
191
192        let s_matrix = [
193            [sinh_r.mul_add(-cos_phi, cosh_r), -sinh_r * sin_phi],
194            [-sinh_r * sin_phi, sinh_r.mul_add(cos_phi, cosh_r)],
195        ];
196
197        // Apply transformation to covariance matrix
198        let x_idx = 2 * mode;
199        let p_idx = 2 * mode + 1;
200
201        let old_cov = [
202            [self.covariance[x_idx][x_idx], self.covariance[x_idx][p_idx]],
203            [self.covariance[p_idx][x_idx], self.covariance[p_idx][p_idx]],
204        ];
205
206        // New covariance = S * old_cov * S^T
207        for i in 0..2 {
208            for j in 0..2 {
209                let mut new_val = 0.0;
210                for k in 0..2 {
211                    for l in 0..2 {
212                        new_val += s_matrix[i][k] * old_cov[k][l] * s_matrix[j][l];
213                    }
214                }
215                let idx_i = x_idx + i;
216                let idx_j = x_idx + j;
217                self.covariance[idx_i][idx_j] = new_val;
218            }
219        }
220
221        Ok(())
222    }
223
224    /// Apply two-mode squeezing operation
225    pub fn two_mode_squeeze(
226        &mut self,
227        r: f64,
228        phi: f64,
229        mode1: usize,
230        mode2: usize,
231    ) -> CVResult<()> {
232        if mode1 >= self.num_modes || mode2 >= self.num_modes {
233            return Err(CVError::ModeNotFound(mode1.max(mode2)));
234        }
235
236        if mode1 == mode2 {
237            return Err(CVError::IncompatibleOperation(
238                "Two-mode squeezing requires different modes".to_string(),
239            ));
240        }
241
242        // Two-mode squeezing transformation
243        let cosh_r = r.cosh();
244        let sinh_r = r.sinh();
245        let cos_phi = phi.cos();
246        let sin_phi = phi.sin();
247
248        // Update covariance matrix for two modes
249        let x1_idx = 2 * mode1;
250        let p1_idx = 2 * mode1 + 1;
251        let x2_idx = 2 * mode2;
252        let p2_idx = 2 * mode2 + 1;
253
254        // Store original values
255        let orig_cov = [
256            [
257                self.covariance[x1_idx][x1_idx],
258                self.covariance[x1_idx][p1_idx],
259                self.covariance[x1_idx][x2_idx],
260                self.covariance[x1_idx][p2_idx],
261            ],
262            [
263                self.covariance[p1_idx][x1_idx],
264                self.covariance[p1_idx][p1_idx],
265                self.covariance[p1_idx][x2_idx],
266                self.covariance[p1_idx][p2_idx],
267            ],
268            [
269                self.covariance[x2_idx][x1_idx],
270                self.covariance[x2_idx][p1_idx],
271                self.covariance[x2_idx][x2_idx],
272                self.covariance[x2_idx][p2_idx],
273            ],
274            [
275                self.covariance[p2_idx][x1_idx],
276                self.covariance[p2_idx][p1_idx],
277                self.covariance[p2_idx][x2_idx],
278                self.covariance[p2_idx][p2_idx],
279            ],
280        ];
281
282        // Two-mode squeezing matrix
283        let s_matrix = [
284            [cosh_r, 0.0, sinh_r * cos_phi, sinh_r * sin_phi],
285            [0.0, cosh_r, sinh_r * sin_phi, -sinh_r * cos_phi],
286            [sinh_r * cos_phi, sinh_r * sin_phi, cosh_r, 0.0],
287            [sinh_r * sin_phi, -sinh_r * cos_phi, 0.0, cosh_r],
288        ];
289
290        // Apply transformation
291        let indices = [x1_idx, p1_idx, x2_idx, p2_idx];
292        for i in 0..4 {
293            for j in 0..4 {
294                let mut new_val = 0.0;
295                for k in 0..4 {
296                    for l in 0..4 {
297                        new_val += s_matrix[i][k] * orig_cov[k][l] * s_matrix[j][l];
298                    }
299                }
300                self.covariance[indices[i]][indices[j]] = new_val;
301            }
302        }
303
304        Ok(())
305    }
306
307    /// Apply beamsplitter operation
308    pub fn beamsplitter(
309        &mut self,
310        theta: f64,
311        phi: f64,
312        mode1: usize,
313        mode2: usize,
314    ) -> CVResult<()> {
315        if mode1 >= self.num_modes || mode2 >= self.num_modes {
316            return Err(CVError::ModeNotFound(mode1.max(mode2)));
317        }
318
319        if mode1 == mode2 {
320            return Err(CVError::IncompatibleOperation(
321                "Beamsplitter requires different modes".to_string(),
322            ));
323        }
324
325        let cos_theta = theta.cos();
326        let sin_theta = theta.sin();
327        let cos_phi = phi.cos();
328        let sin_phi = phi.sin();
329
330        // Beamsplitter transformation matrix
331        let bs_matrix = [
332            [cos_theta, 0.0, sin_theta * cos_phi, sin_theta * sin_phi],
333            [0.0, cos_theta, -sin_theta * sin_phi, sin_theta * cos_phi],
334            [-sin_theta * cos_phi, sin_theta * sin_phi, cos_theta, 0.0],
335            [-sin_theta * sin_phi, -sin_theta * cos_phi, 0.0, cos_theta],
336        ];
337
338        // Apply transformation to both mean and covariance
339        let indices = [2 * mode1, 2 * mode1 + 1, 2 * mode2, 2 * mode2 + 1];
340
341        // Transform mean vector
342        let orig_mean = [
343            self.mean[indices[0]],
344            self.mean[indices[1]],
345            self.mean[indices[2]],
346            self.mean[indices[3]],
347        ];
348
349        for i in 0..4 {
350            let mut new_mean = 0.0;
351            for j in 0..4 {
352                new_mean += bs_matrix[i][j] * orig_mean[j];
353            }
354            self.mean[indices[i]] = new_mean;
355        }
356
357        // Transform covariance matrix
358        let orig_cov = [
359            [
360                self.covariance[indices[0]][indices[0]],
361                self.covariance[indices[0]][indices[1]],
362                self.covariance[indices[0]][indices[2]],
363                self.covariance[indices[0]][indices[3]],
364            ],
365            [
366                self.covariance[indices[1]][indices[0]],
367                self.covariance[indices[1]][indices[1]],
368                self.covariance[indices[1]][indices[2]],
369                self.covariance[indices[1]][indices[3]],
370            ],
371            [
372                self.covariance[indices[2]][indices[0]],
373                self.covariance[indices[2]][indices[1]],
374                self.covariance[indices[2]][indices[2]],
375                self.covariance[indices[2]][indices[3]],
376            ],
377            [
378                self.covariance[indices[3]][indices[0]],
379                self.covariance[indices[3]][indices[1]],
380                self.covariance[indices[3]][indices[2]],
381                self.covariance[indices[3]][indices[3]],
382            ],
383        ];
384
385        for i in 0..4 {
386            for j in 0..4 {
387                let mut new_val = 0.0;
388                for k in 0..4 {
389                    for l in 0..4 {
390                        new_val += bs_matrix[i][k] * orig_cov[k][l] * bs_matrix[j][l];
391                    }
392                }
393                self.covariance[indices[i]][indices[j]] = new_val;
394            }
395        }
396
397        Ok(())
398    }
399
400    /// Apply phase rotation
401    pub fn phase_rotation(&mut self, phi: f64, mode: usize) -> CVResult<()> {
402        if mode >= self.num_modes {
403            return Err(CVError::ModeNotFound(mode));
404        }
405
406        let cos_phi = phi.cos();
407        let sin_phi = phi.sin();
408
409        // Rotation matrix for phase space
410        let rotation_matrix = [[cos_phi, -sin_phi], [sin_phi, cos_phi]];
411
412        let x_idx = 2 * mode;
413        let p_idx = 2 * mode + 1;
414
415        // Transform mean vector
416        let old_x = self.mean[x_idx];
417        let old_p = self.mean[p_idx];
418
419        self.mean[x_idx] = rotation_matrix[0][0].mul_add(old_x, rotation_matrix[0][1] * old_p);
420        self.mean[p_idx] = rotation_matrix[1][0].mul_add(old_x, rotation_matrix[1][1] * old_p);
421
422        // Transform covariance matrix
423        let old_cov = [
424            [self.covariance[x_idx][x_idx], self.covariance[x_idx][p_idx]],
425            [self.covariance[p_idx][x_idx], self.covariance[p_idx][p_idx]],
426        ];
427
428        for i in 0..2 {
429            for j in 0..2 {
430                let mut new_val = 0.0;
431                for k in 0..2 {
432                    for l in 0..2 {
433                        new_val += rotation_matrix[i][k] * old_cov[k][l] * rotation_matrix[j][l];
434                    }
435                }
436                let idx_i = x_idx + i;
437                let idx_j = x_idx + j;
438                self.covariance[idx_i][idx_j] = new_val;
439            }
440        }
441
442        Ok(())
443    }
444
445    /// Calculate fidelity with another Gaussian state
446    pub fn fidelity(&self, other: &Self) -> CVResult<f64> {
447        if self.num_modes != other.num_modes {
448            return Err(CVError::MatrixDimensionMismatch(
449                "States must have same number of modes".to_string(),
450            ));
451        }
452
453        // Simplified fidelity calculation for Gaussian states
454        // This is a basic implementation - full calculation involves matrix operations
455
456        let mut mean_diff_squared = 0.0;
457        for i in 0..self.mean.len() {
458            let diff = self.mean[i] - other.mean[i];
459            mean_diff_squared += diff * diff;
460        }
461
462        // Approximate fidelity based on overlap of means and covariances
463        let overlap = (-0.5 * mean_diff_squared).exp();
464
465        // Include covariance contribution (simplified)
466        let mut cov_diff = 0.0;
467        for i in 0..self.covariance.len() {
468            for j in 0..self.covariance[i].len() {
469                let diff = self.covariance[i][j] - other.covariance[i][j];
470                cov_diff += diff * diff;
471            }
472        }
473
474        let cov_overlap = (-0.1 * cov_diff).exp();
475
476        Ok(overlap * cov_overlap)
477    }
478
479    /// Get average photon number for a mode
480    pub fn average_photon_number(&self, mode: usize) -> CVResult<f64> {
481        if mode >= self.num_modes {
482            return Err(CVError::ModeNotFound(mode));
483        }
484
485        let x_idx = 2 * mode;
486        let p_idx = 2 * mode + 1;
487
488        // Average photon number = (Var(X) + Var(P) + <X>^2 + <P>^2)/2 - 1/2
489        let var_x = self.covariance[x_idx][x_idx];
490        let var_p = self.covariance[p_idx][p_idx];
491        let mean_x_sq = self.mean[x_idx] * self.mean[x_idx];
492        let mean_p_sq = self.mean[p_idx] * self.mean[p_idx];
493
494        let n_avg = (var_x + var_p + mean_x_sq + mean_p_sq) / 2.0 - 0.5;
495
496        Ok(n_avg.max(0.0)) // Ensure non-negative
497    }
498
499    /// Get squeezing parameter for a mode
500    pub fn squeezing_parameter(&self, mode: usize) -> CVResult<f64> {
501        if mode >= self.num_modes {
502            return Err(CVError::ModeNotFound(mode));
503        }
504
505        let x_idx = 2 * mode;
506        let p_idx = 2 * mode + 1;
507
508        let var_x = self.covariance[x_idx][x_idx];
509        let var_p = self.covariance[p_idx][p_idx];
510
511        // Squeezing in dB: -10 * log10(min(Var(X), Var(P)) / 0.5)
512        let min_variance = var_x.min(var_p);
513        let squeezing_db = -10.0 * (min_variance / 0.5).log10();
514
515        Ok(squeezing_db.max(0.0))
516    }
517}
518
519/// CV gate operations
520pub struct CVGateSet;
521
522impl CVGateSet {
523    /// Create displacement operation
524    pub fn displacement(
525        alpha: Complex,
526        mode: usize,
527    ) -> impl Fn(&mut GaussianState) -> CVResult<()> {
528        move |state: &mut GaussianState| state.displace(alpha, mode)
529    }
530
531    /// Create squeezing operation
532    pub fn squeezing(r: f64, phi: f64, mode: usize) -> impl Fn(&mut GaussianState) -> CVResult<()> {
533        move |state: &mut GaussianState| state.squeeze(r, phi, mode)
534    }
535
536    /// Create two-mode squeezing operation
537    pub fn two_mode_squeezing(
538        r: f64,
539        phi: f64,
540        mode1: usize,
541        mode2: usize,
542    ) -> impl Fn(&mut GaussianState) -> CVResult<()> {
543        move |state: &mut GaussianState| state.two_mode_squeeze(r, phi, mode1, mode2)
544    }
545
546    /// Create beamsplitter operation
547    pub fn beamsplitter(
548        theta: f64,
549        phi: f64,
550        mode1: usize,
551        mode2: usize,
552    ) -> impl Fn(&mut GaussianState) -> CVResult<()> {
553        move |state: &mut GaussianState| state.beamsplitter(theta, phi, mode1, mode2)
554    }
555
556    /// Create phase rotation operation
557    pub fn phase_rotation(phi: f64, mode: usize) -> impl Fn(&mut GaussianState) -> CVResult<()> {
558        move |state: &mut GaussianState| state.phase_rotation(phi, mode)
559    }
560}
561
562/// Measurement operations for CV systems
563pub struct CVMeasurements;
564
565impl CVMeasurements {
566    /// Perform homodyne measurement
567    pub fn homodyne(state: &GaussianState, mode: usize, phase: f64) -> CVResult<f64> {
568        if mode >= state.num_modes {
569            return Err(CVError::ModeNotFound(mode));
570        }
571
572        let x_idx = 2 * mode;
573        let p_idx = 2 * mode + 1;
574
575        // Quadrature operator: X_phi = X*cos(phi) + P*sin(phi)
576        let cos_phi = phase.cos();
577        let sin_phi = phase.sin();
578
579        let mean_value = cos_phi.mul_add(state.mean[x_idx], sin_phi * state.mean[p_idx]);
580        let variance = (2.0 * cos_phi * sin_phi).mul_add(
581            state.covariance[x_idx][p_idx],
582            (cos_phi * cos_phi).mul_add(
583                state.covariance[x_idx][x_idx],
584                sin_phi * sin_phi * state.covariance[p_idx][p_idx],
585            ),
586        );
587
588        // Return mean value (in a real implementation, this would be sampled)
589        Ok(mean_value)
590    }
591
592    /// Perform heterodyne measurement
593    pub fn heterodyne(state: &GaussianState, mode: usize) -> CVResult<Complex> {
594        if mode >= state.num_modes {
595            return Err(CVError::ModeNotFound(mode));
596        }
597
598        let x_idx = 2 * mode;
599        let p_idx = 2 * mode + 1;
600
601        // Heterodyne measures both quadratures simultaneously
602        let alpha_real = state.mean[x_idx] / SQRT_2;
603        let alpha_imag = state.mean[p_idx] / SQRT_2;
604
605        Ok(Complex::new(alpha_real, alpha_imag))
606    }
607
608    /// Perform photon number measurement (approximate for CV)
609    pub fn photon_number(state: &GaussianState, mode: usize) -> CVResult<f64> {
610        state.average_photon_number(mode)
611    }
612}
613
614#[cfg(test)]
615mod tests {
616    use super::*;
617
618    #[test]
619    fn test_vacuum_state() {
620        let vacuum = GaussianState::vacuum(2);
621        assert_eq!(vacuum.num_modes, 2);
622        assert_eq!(vacuum.mean.len(), 4);
623        assert_eq!(vacuum.covariance.len(), 4);
624
625        // Check vacuum noise
626        for i in 0..4 {
627            assert_eq!(vacuum.mean[i], 0.0);
628            assert_eq!(vacuum.covariance[i][i], 0.5);
629        }
630    }
631
632    #[test]
633    fn test_coherent_state() {
634        let alpha = Complex::new(1.0, 0.5);
635        let coherent =
636            GaussianState::coherent(alpha, 0, 1).expect("Coherent state creation should succeed");
637
638        assert!((coherent.mean[0] - alpha.real * SQRT_2).abs() < 1e-10);
639        assert!((coherent.mean[1] - alpha.imag * SQRT_2).abs() < 1e-10);
640    }
641
642    #[test]
643    fn test_squeezed_vacuum() {
644        let squeezed = GaussianState::squeezed_vacuum(1.0, 0.0, 0, 1)
645            .expect("Squeezed vacuum creation should succeed");
646
647        // For squeezing in X quadrature (phi=0), Var(X) should be reduced
648        assert!(squeezed.covariance[0][0] < 0.5);
649        assert!(squeezed.covariance[1][1] > 0.5);
650    }
651
652    #[test]
653    fn test_displacement_operation() {
654        let mut state = GaussianState::vacuum(1);
655        let alpha = Complex::new(2.0, 1.0);
656
657        state
658            .displace(alpha, 0)
659            .expect("Displacement operation should succeed");
660
661        assert!((state.mean[0] - alpha.real * SQRT_2).abs() < 1e-10);
662        assert!((state.mean[1] - alpha.imag * SQRT_2).abs() < 1e-10);
663    }
664
665    #[test]
666    fn test_beamsplitter() {
667        let mut state = GaussianState::coherent(Complex::new(1.0, 0.0), 0, 2)
668            .expect("Coherent state creation should succeed");
669
670        // 50:50 beamsplitter
671        state
672            .beamsplitter(PI / 4.0, 0.0, 0, 1)
673            .expect("Beamsplitter operation should succeed");
674
675        // Check that amplitude is distributed between modes
676        assert!(state.mean[0].abs() > 0.0);
677        assert!(state.mean[2].abs() > 0.0);
678    }
679
680    #[test]
681    fn test_average_photon_number() {
682        let alpha = Complex::new(2.0, 0.0);
683        let coherent =
684            GaussianState::coherent(alpha, 0, 1).expect("Coherent state creation should succeed");
685
686        let n_avg = coherent
687            .average_photon_number(0)
688            .expect("Photon number calculation should succeed");
689        let expected = alpha.magnitude() * alpha.magnitude();
690
691        assert!((n_avg - expected).abs() < 1e-10);
692    }
693
694    #[test]
695    fn test_homodyne_measurement() {
696        let alpha = Complex::new(2.0, 1.0);
697        let coherent =
698            GaussianState::coherent(alpha, 0, 1).expect("Coherent state creation should succeed");
699
700        // Measure X quadrature
701        let x_result = CVMeasurements::homodyne(&coherent, 0, 0.0)
702            .expect("X quadrature homodyne measurement should succeed");
703        assert!((x_result - alpha.real * SQRT_2).abs() < 1e-10);
704
705        // Measure P quadrature
706        let p_result = CVMeasurements::homodyne(&coherent, 0, PI / 2.0)
707            .expect("P quadrature homodyne measurement should succeed");
708        assert!((p_result - alpha.imag * SQRT_2).abs() < 1e-10);
709    }
710
711    #[test]
712    fn test_fidelity() {
713        let state1 = GaussianState::vacuum(1);
714        let state2 = GaussianState::vacuum(1);
715
716        let fidelity = state1
717            .fidelity(&state2)
718            .expect("Fidelity calculation should succeed");
719        assert!((fidelity - 1.0).abs() < 1e-6); // Identical states should have fidelity 1
720
721        let coherent = GaussianState::coherent(Complex::new(1.0, 0.0), 0, 1)
722            .expect("Coherent state creation should succeed");
723        let fidelity_diff = state1
724            .fidelity(&coherent)
725            .expect("Fidelity calculation should succeed");
726        assert!(fidelity_diff < 1.0); // Different states should have fidelity < 1
727    }
728}