quantrs2_device/continuous_variable/
gaussian_states.rs

1//! Gaussian states for continuous variable quantum computing
2//!
3//! This module implements Gaussian states, which are the foundation of CV quantum computing.
4//! Gaussian states can be fully characterized by their first and second moments.
5
6use super::{CVDeviceConfig, CVEntanglementMeasures, CVModeState, Complex};
7use crate::{DeviceError, DeviceResult};
8use scirs2_core::random::prelude::*;
9use scirs2_core::random::{Distribution, RandNormal};
10// Alias for backward compatibility
11type Normal<T> = RandNormal<T>;
12use serde::{Deserialize, Serialize};
13use std::f64::consts::PI;
14
15/// Gaussian state representation using covariance matrix formalism
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct GaussianState {
18    /// Number of modes
19    pub num_modes: usize,
20    /// Mean vector (displacement amplitudes) - [x1, p1, x2, p2, ...]
21    pub mean_vector: Vec<f64>,
22    /// Covariance matrix (2N x 2N where N is number of modes)
23    pub covariancematrix: Vec<Vec<f64>>,
24    /// Symplectic matrix for canonical commutation relations
25    symplectic_matrix: Vec<Vec<f64>>,
26}
27
28impl GaussianState {
29    /// Create vacuum state for N modes
30    pub fn vacuum_state(num_modes: usize) -> Self {
31        let vector_size = 2 * num_modes;
32        let mean_vector = vec![0.0; vector_size];
33
34        // Vacuum covariance matrix: I/2 (identity matrix scaled by 1/2)
35        let mut covariancematrix = vec![vec![0.0; vector_size]; vector_size];
36        for i in 0..vector_size {
37            covariancematrix[i][i] = 0.5;
38        }
39
40        let symplectic_matrix = Self::build_symplectic_matrix(num_modes);
41
42        Self {
43            num_modes,
44            mean_vector,
45            covariancematrix,
46            symplectic_matrix,
47        }
48    }
49
50    /// Create coherent state with given displacement
51    pub fn coherent_state(num_modes: usize, displacements: Vec<Complex>) -> DeviceResult<Self> {
52        if displacements.len() != num_modes {
53            return Err(DeviceError::InvalidInput(
54                "Number of displacements must match number of modes".to_string(),
55            ));
56        }
57
58        let mut state = Self::vacuum_state(num_modes);
59
60        // Set mean vector from displacements
61        for (i, displacement) in displacements.iter().enumerate() {
62            state.mean_vector[2 * i] = displacement.real * (2.0_f64).sqrt(); // x quadrature
63            state.mean_vector[2 * i + 1] = displacement.imag * (2.0_f64).sqrt();
64            // p quadrature
65        }
66
67        Ok(state)
68    }
69
70    /// Create squeezed vacuum state
71    pub fn squeezed_vacuum_state(
72        num_modes: usize,
73        squeezing_params: Vec<f64>,
74        squeezing_phases: Vec<f64>,
75    ) -> DeviceResult<Self> {
76        if squeezing_params.len() != num_modes || squeezing_phases.len() != num_modes {
77            return Err(DeviceError::InvalidInput(
78                "Squeezing parameters and phases must match number of modes".to_string(),
79            ));
80        }
81
82        let mut state = Self::vacuum_state(num_modes);
83
84        // Apply squeezing to each mode
85        for i in 0..num_modes {
86            state.apply_squeezing(i, squeezing_params[i], squeezing_phases[i])?;
87        }
88
89        Ok(state)
90    }
91
92    /// Build symplectic matrix for canonical commutation relations
93    fn build_symplectic_matrix(num_modes: usize) -> Vec<Vec<f64>> {
94        let size = 2 * num_modes;
95        let mut omega = vec![vec![0.0; size]; size];
96
97        for i in 0..num_modes {
98            omega[2 * i][2 * i + 1] = 1.0;
99            omega[2 * i + 1][2 * i] = -1.0;
100        }
101
102        omega
103    }
104
105    /// Apply displacement operation to a mode
106    pub fn apply_displacement(&mut self, mode: usize, displacement: Complex) -> DeviceResult<()> {
107        if mode >= self.num_modes {
108            return Err(DeviceError::InvalidInput(format!(
109                "Mode {} exceeds available modes",
110                mode
111            )));
112        }
113
114        // Update mean vector
115        self.mean_vector[2 * mode] += displacement.real * (2.0_f64).sqrt();
116        self.mean_vector[2 * mode + 1] += displacement.imag * (2.0_f64).sqrt();
117
118        Ok(())
119    }
120
121    /// Apply squeezing operation to a mode
122    pub fn apply_squeezing(&mut self, mode: usize, r: f64, phi: f64) -> DeviceResult<()> {
123        if mode >= self.num_modes {
124            return Err(DeviceError::InvalidInput(format!(
125                "Mode {} exceeds available modes",
126                mode
127            )));
128        }
129
130        let cos_phi = phi.cos();
131        let sin_phi = phi.sin();
132        let cosh_r = r.cosh();
133        let sinh_r = r.sinh();
134
135        // Squeezing transformation matrix
136        // Standard squeezing: S = [[e^(-r), 0], [0, e^r]] for φ=0
137        let s11 = cosh_r - sinh_r * cos_phi; // For φ=0: e^(-r) (squeeze position)
138        let s12 = -sinh_r * sin_phi; // For φ=0: 0
139        let s21 = -sinh_r * sin_phi; // For φ=0: 0
140        let s22 = cosh_r + sinh_r * cos_phi; // For φ=0: e^r (anti-squeeze momentum)
141
142        // Apply to covariance matrix
143        let i = 2 * mode;
144        let j = 2 * mode + 1;
145
146        let old_covar = self.covariancematrix.clone();
147
148        // Transform covariance matrix: S * V * S^T
149        for a in 0..2 * self.num_modes {
150            for b in 0..2 * self.num_modes {
151                if (a == i || a == j) && (b == i || b == j) {
152                    let mut new_val = 0.0;
153
154                    for k in [i, j].iter() {
155                        for l in [i, j].iter() {
156                            let s_ak = if a == i {
157                                if *k == i {
158                                    s11
159                                } else {
160                                    s12
161                                }
162                            } else {
163                                if *k == i {
164                                    s21
165                                } else {
166                                    s22
167                                }
168                            };
169
170                            let s_bl = if b == i {
171                                if *l == i {
172                                    s11
173                                } else {
174                                    s12
175                                }
176                            } else {
177                                if *l == i {
178                                    s21
179                                } else {
180                                    s22
181                                }
182                            };
183
184                            new_val += s_ak * old_covar[*k][*l] * s_bl;
185                        }
186                    }
187
188                    self.covariancematrix[a][b] = new_val;
189                } else if a == i || a == j {
190                    // Mixed terms
191                    let s_a = if a == i { [s11, s12] } else { [s21, s22] };
192                    self.covariancematrix[a][b] =
193                        s_a[0] * old_covar[i][b] + s_a[1] * old_covar[j][b];
194                } else if b == i || b == j {
195                    // Mixed terms (transpose)
196                    let s_b = if b == i { [s11, s21] } else { [s12, s22] };
197                    self.covariancematrix[a][b] =
198                        old_covar[a][i] * s_b[0] + old_covar[a][j] * s_b[1];
199                }
200            }
201        }
202
203        Ok(())
204    }
205
206    /// Apply two-mode squeezing operation
207    pub fn apply_two_mode_squeezing(
208        &mut self,
209        mode1: usize,
210        mode2: usize,
211        r: f64,
212        phi: f64,
213    ) -> DeviceResult<()> {
214        if mode1 >= self.num_modes || mode2 >= self.num_modes {
215            return Err(DeviceError::InvalidInput(
216                "One or both modes exceed available modes".to_string(),
217            ));
218        }
219
220        let cos_phi = phi.cos();
221        let sin_phi = phi.sin();
222        let cosh_r = r.cosh();
223        let sinh_r = r.sinh();
224
225        // Two-mode squeezing transformation
226        let indices = [2 * mode1, 2 * mode1 + 1, 2 * mode2, 2 * mode2 + 1];
227        let old_covar = self.covariancematrix.clone();
228
229        // Build 4x4 transformation matrix
230        let mut transform = [[0.0; 4]; 4];
231        transform[0][0] = cosh_r;
232        transform[0][2] = sinh_r * cos_phi;
233        transform[0][3] = sinh_r * sin_phi;
234        transform[1][1] = cosh_r;
235        transform[1][2] = sinh_r * sin_phi;
236        transform[1][3] = -sinh_r * cos_phi;
237        transform[2][0] = sinh_r * cos_phi;
238        transform[2][1] = sinh_r * sin_phi;
239        transform[2][2] = cosh_r;
240        transform[3][0] = sinh_r * sin_phi;
241        transform[3][1] = -sinh_r * cos_phi;
242        transform[3][3] = cosh_r;
243
244        // Apply transformation to relevant block of covariance matrix
245        for i in 0..4 {
246            for j in 0..4 {
247                let mut new_val = 0.0;
248                for k in 0..4 {
249                    for l in 0..4 {
250                        new_val +=
251                            transform[i][k] * old_covar[indices[k]][indices[l]] * transform[j][l];
252                    }
253                }
254                self.covariancematrix[indices[i]][indices[j]] = new_val;
255            }
256        }
257
258        Ok(())
259    }
260
261    /// Apply beamsplitter operation
262    pub fn apply_beamsplitter(
263        &mut self,
264        mode1: usize,
265        mode2: usize,
266        transmittance: f64,
267        phase: f64,
268    ) -> DeviceResult<()> {
269        if mode1 >= self.num_modes || mode2 >= self.num_modes {
270            return Err(DeviceError::InvalidInput(
271                "One or both modes exceed available modes".to_string(),
272            ));
273        }
274
275        let t = transmittance.sqrt();
276        let r = (1.0 - transmittance).sqrt();
277        let cos_phi = phase.cos();
278        let sin_phi = phase.sin();
279
280        // Beamsplitter transformation matrix
281        let indices = [2 * mode1, 2 * mode1 + 1, 2 * mode2, 2 * mode2 + 1];
282        let old_mean = self.mean_vector.clone();
283        let old_covar = self.covariancematrix.clone();
284
285        // Transform mean vector
286        let mean1_x = old_mean[2 * mode1];
287        let mean1_p = old_mean[2 * mode1 + 1];
288        let mean2_x = old_mean[2 * mode2];
289        let mean2_p = old_mean[2 * mode2 + 1];
290
291        self.mean_vector[2 * mode1] = t * mean1_x + r * cos_phi * mean2_x - r * sin_phi * mean2_p;
292        self.mean_vector[2 * mode1 + 1] =
293            t * mean1_p + r * sin_phi * mean2_x + r * cos_phi * mean2_p;
294        self.mean_vector[2 * mode2] = -r * cos_phi * mean1_x + r * sin_phi * mean1_p + t * mean2_x;
295        self.mean_vector[2 * mode2 + 1] =
296            r * sin_phi * mean1_x + r * cos_phi * mean1_p + t * mean2_p;
297
298        // Build 4x4 transformation matrix
299        let mut transform = [[0.0; 4]; 4];
300        transform[0][0] = t;
301        transform[0][2] = r * cos_phi;
302        transform[0][3] = -r * sin_phi;
303        transform[1][1] = t;
304        transform[1][2] = r * sin_phi;
305        transform[1][3] = r * cos_phi;
306        transform[2][0] = -r * cos_phi;
307        transform[2][1] = r * sin_phi;
308        transform[2][2] = t;
309        transform[3][0] = r * sin_phi;
310        transform[3][1] = r * cos_phi;
311        transform[3][3] = t;
312
313        // Apply transformation to covariance matrix
314        for i in 0..4 {
315            for j in 0..4 {
316                let mut new_val = 0.0;
317                for k in 0..4 {
318                    for l in 0..4 {
319                        new_val +=
320                            transform[i][k] * old_covar[indices[k]][indices[l]] * transform[j][l];
321                    }
322                }
323                self.covariancematrix[indices[i]][indices[j]] = new_val;
324            }
325        }
326
327        Ok(())
328    }
329
330    /// Apply phase rotation
331    pub fn apply_phase_rotation(&mut self, mode: usize, phi: f64) -> DeviceResult<()> {
332        if mode >= self.num_modes {
333            return Err(DeviceError::InvalidInput(format!(
334                "Mode {} exceeds available modes",
335                mode
336            )));
337        }
338
339        let cos_phi = phi.cos();
340        let sin_phi = phi.sin();
341
342        // Rotate mean vector
343        let mean_x = self.mean_vector[2 * mode];
344        let mean_p = self.mean_vector[2 * mode + 1];
345
346        self.mean_vector[2 * mode] = cos_phi * mean_x + sin_phi * mean_p;
347        self.mean_vector[2 * mode + 1] = -sin_phi * mean_x + cos_phi * mean_p;
348
349        // Rotation transformation matrix
350        let indices = [2 * mode, 2 * mode + 1];
351        let old_covar = self.covariancematrix.clone();
352
353        let transform = [[cos_phi, sin_phi], [-sin_phi, cos_phi]];
354
355        // Apply rotation to covariance matrix
356        for i in 0..2 {
357            for j in 0..2 {
358                let mut new_val = 0.0;
359                for k in 0..2 {
360                    for l in 0..2 {
361                        new_val +=
362                            transform[i][k] * old_covar[indices[k]][indices[l]] * transform[j][l];
363                    }
364                }
365                self.covariancematrix[indices[i]][indices[j]] = new_val;
366            }
367        }
368
369        Ok(())
370    }
371
372    /// Perform homodyne measurement
373    pub fn homodyne_measurement(
374        &mut self,
375        mode: usize,
376        phase: f64,
377        config: &CVDeviceConfig,
378    ) -> DeviceResult<f64> {
379        if mode >= self.num_modes {
380            return Err(DeviceError::InvalidInput(format!(
381                "Mode {} exceeds available modes",
382                mode
383            )));
384        }
385
386        // Measurement operator: x*cos(phi) + p*sin(phi)
387        let cos_phi = phase.cos();
388        let sin_phi = phase.sin();
389
390        // Mean value
391        let mean_result =
392            cos_phi * self.mean_vector[2 * mode] + sin_phi * self.mean_vector[2 * mode + 1];
393
394        // Variance
395        let var_x = self.covariancematrix[2 * mode][2 * mode];
396        let var_p = self.covariancematrix[2 * mode + 1][2 * mode + 1];
397        let cov_xp = self.covariancematrix[2 * mode][2 * mode + 1];
398
399        let variance = cos_phi * cos_phi * var_x
400            + sin_phi * sin_phi * var_p
401            + 2.0 * cos_phi * sin_phi * cov_xp;
402
403        // Add noise effects
404        let noise_variance = self.calculate_measurement_noise(config);
405        let total_variance = variance + noise_variance;
406
407        // Sample from Gaussian distribution
408        let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
409        let noise: f64 = Normal::new(0.0, total_variance.sqrt())
410            .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {}", e)))?
411            .sample(&mut rng);
412
413        let result = mean_result + noise;
414
415        // Condition the state on the measurement result
416        self.condition_on_homodyne_measurement(mode, phase, result)?;
417
418        Ok(result)
419    }
420
421    /// Perform heterodyne measurement
422    pub fn heterodyne_measurement(
423        &mut self,
424        mode: usize,
425        config: &CVDeviceConfig,
426    ) -> DeviceResult<Complex> {
427        if mode >= self.num_modes {
428            return Err(DeviceError::InvalidInput(format!(
429                "Mode {} exceeds available modes",
430                mode
431            )));
432        }
433
434        // Heterodyne measures both quadratures simultaneously
435        let mean_x = self.mean_vector[2 * mode];
436        let mean_p = self.mean_vector[2 * mode + 1];
437
438        let var_x = self.covariancematrix[2 * mode][2 * mode];
439        let var_p = self.covariancematrix[2 * mode + 1][2 * mode + 1];
440
441        // Add noise
442        let noise_variance = self.calculate_measurement_noise(config);
443        let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
444
445        let noise_x: f64 = Normal::new(0.0, (var_x + noise_variance / 2.0).sqrt())
446            .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {}", e)))?
447            .sample(&mut rng);
448
449        let noise_p: f64 = Normal::new(0.0, (var_p + noise_variance / 2.0).sqrt())
450            .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {}", e)))?
451            .sample(&mut rng);
452
453        let result_x = mean_x + noise_x;
454        let result_p = mean_p + noise_p;
455
456        let result = Complex::new(
457            (result_x + result_p * Complex::i().real) / (2.0_f64).sqrt(),
458            (result_p - result_x * Complex::i().imag) / (2.0_f64).sqrt(),
459        );
460
461        // Condition state on measurement (destructive measurement)
462        self.condition_on_heterodyne_measurement(mode, result)?;
463
464        Ok(result)
465    }
466
467    /// Calculate measurement noise based on device configuration
468    fn calculate_measurement_noise(&self, config: &CVDeviceConfig) -> f64 {
469        // Include electronic noise, detection efficiency, and thermal noise
470        let electronic_noise = 10.0_f64.powf(config.electronic_noise_db / 10.0);
471        let efficiency_loss = 1.0 / config.detection_efficiency - 1.0;
472        let thermal_noise = 2.0 * config.temperature_k / 0.01; // Relative to 10 mK
473
474        electronic_noise + efficiency_loss + thermal_noise
475    }
476
477    /// Condition state on homodyne measurement result
478    pub fn condition_on_homodyne_measurement(
479        &mut self,
480        mode: usize,
481        phase: f64,
482        result: f64,
483    ) -> DeviceResult<()> {
484        // This is a simplified conditioning - in practice would use full Kalman filter
485        // For now, just reset the measured mode to vacuum and update correlations
486
487        let cos_phi = phase.cos();
488        let sin_phi = phase.sin();
489
490        // Update mean vector
491        self.mean_vector[2 * mode] = result * cos_phi / (2.0_f64).sqrt();
492        self.mean_vector[2 * mode + 1] = result * sin_phi / (2.0_f64).sqrt();
493
494        // Simplified: reduce variance in measured quadrature
495        let measured_var = cos_phi * cos_phi * self.covariancematrix[2 * mode][2 * mode]
496            + sin_phi * sin_phi * self.covariancematrix[2 * mode + 1][2 * mode + 1];
497
498        let reduction_factor = 0.1; // Measurement significantly reduces uncertainty
499        self.covariancematrix[2 * mode][2 * mode] *= reduction_factor;
500        self.covariancematrix[2 * mode + 1][2 * mode + 1] *= reduction_factor;
501
502        Ok(())
503    }
504
505    /// Condition state on heterodyne measurement result
506    pub fn condition_on_heterodyne_measurement(
507        &mut self,
508        mode: usize,
509        _result: Complex,
510    ) -> DeviceResult<()> {
511        // Heterodyne measurement destroys the mode - reset to vacuum
512        self.reset_mode_to_vacuum(mode)
513    }
514
515    /// Reset a mode to vacuum state
516    pub fn reset_mode_to_vacuum(&mut self, mode: usize) -> DeviceResult<()> {
517        if mode >= self.num_modes {
518            return Err(DeviceError::InvalidInput(format!(
519                "Mode {} exceeds available modes",
520                mode
521            )));
522        }
523
524        // Reset mean
525        self.mean_vector[2 * mode] = 0.0;
526        self.mean_vector[2 * mode + 1] = 0.0;
527
528        // Reset covariance to vacuum values
529        self.covariancematrix[2 * mode][2 * mode] = 0.5;
530        self.covariancematrix[2 * mode + 1][2 * mode + 1] = 0.5;
531        self.covariancematrix[2 * mode][2 * mode + 1] = 0.0;
532        self.covariancematrix[2 * mode + 1][2 * mode] = 0.0;
533
534        // Remove correlations with other modes
535        for i in 0..2 * self.num_modes {
536            if i != 2 * mode && i != 2 * mode + 1 {
537                self.covariancematrix[2 * mode][i] = 0.0;
538                self.covariancematrix[2 * mode + 1][i] = 0.0;
539                self.covariancematrix[i][2 * mode] = 0.0;
540                self.covariancematrix[i][2 * mode + 1] = 0.0;
541            }
542        }
543
544        Ok(())
545    }
546
547    /// Get mode state information
548    pub fn get_mode_state(&self, mode: usize) -> DeviceResult<CVModeState> {
549        if mode >= self.num_modes {
550            return Err(DeviceError::InvalidInput(format!(
551                "Mode {} exceeds available modes",
552                mode
553            )));
554        }
555
556        let mean_amplitude = Complex::new(
557            self.mean_vector[2 * mode] / (2.0_f64).sqrt(),
558            self.mean_vector[2 * mode + 1] / (2.0_f64).sqrt(),
559        );
560
561        let var_x = self.covariancematrix[2 * mode][2 * mode];
562        let var_p = self.covariancematrix[2 * mode + 1][2 * mode + 1];
563
564        // Calculate squeezing parameters
565        let (squeezing_parameter, squeezing_phase) = self.calculate_squeezing(mode);
566
567        // Calculate mode purity
568        let purity = self.calculate_mode_purity(mode);
569
570        Ok(CVModeState {
571            mean_amplitude,
572            quadrature_variances: (var_x, var_p),
573            squeezing_parameter,
574            squeezing_phase,
575            purity,
576        })
577    }
578
579    /// Calculate squeezing parameters for a mode
580    fn calculate_squeezing(&self, mode: usize) -> (f64, f64) {
581        let var_x = self.covariancematrix[2 * mode][2 * mode];
582        let var_p = self.covariancematrix[2 * mode + 1][2 * mode + 1];
583        let cov_xp = self.covariancematrix[2 * mode][2 * mode + 1];
584
585        // Find minimum variance quadrature
586        let delta = (var_x - var_p).powi(2) + 4.0 * cov_xp.powi(2);
587        let min_var = 0.5 * (var_x + var_p - delta.sqrt());
588        let max_var = 0.5 * (var_x + var_p + delta.sqrt());
589
590        let squeezing_parameter = if min_var < 0.5 {
591            -0.5 * (2.0 * min_var).ln()
592        } else {
593            0.0
594        };
595
596        let squeezing_phase = if cov_xp.abs() > 1e-10 {
597            0.5 * (2.0 * cov_xp / (var_x - var_p)).atan()
598        } else {
599            0.0
600        };
601
602        (squeezing_parameter, squeezing_phase)
603    }
604
605    /// Calculate mode purity
606    fn calculate_mode_purity(&self, mode: usize) -> f64 {
607        let var_x = self.covariancematrix[2 * mode][2 * mode];
608        let var_p = self.covariancematrix[2 * mode + 1][2 * mode + 1];
609        let cov_xp = self.covariancematrix[2 * mode][2 * mode + 1];
610
611        let det = var_x * var_p - cov_xp.powi(2);
612        1.0 / (4.0 * det)
613    }
614
615    /// Calculate average squeezing across all modes
616    pub fn calculate_average_squeezing(&self) -> f64 {
617        let mut total_squeezing = 0.0;
618        for mode in 0..self.num_modes {
619            let (squeezing, _) = self.calculate_squeezing(mode);
620            total_squeezing += squeezing;
621        }
622        total_squeezing / self.num_modes as f64
623    }
624
625    /// Calculate system purity
626    pub fn calculate_purity(&self) -> f64 {
627        // Simplified purity calculation
628        let mut total_purity = 0.0;
629        for mode in 0..self.num_modes {
630            total_purity += self.calculate_mode_purity(mode);
631        }
632        total_purity / self.num_modes as f64
633    }
634
635    /// Calculate entanglement entropy
636    pub fn calculate_entanglement_entropy(&self) -> f64 {
637        // Simplified calculation based on covariance matrix eigenvalues
638        let mut entropy = 0.0;
639
640        for mode in 0..self.num_modes {
641            let var_x = self.covariancematrix[2 * mode][2 * mode];
642            let var_p = self.covariancematrix[2 * mode + 1][2 * mode + 1];
643            let cov_xp = self.covariancematrix[2 * mode][2 * mode + 1];
644
645            let det = var_x * var_p - cov_xp.powi(2);
646            if det > 0.25 {
647                let eigenvalue = det.sqrt();
648                entropy += (eigenvalue + 0.5) * (eigenvalue + 0.5).ln()
649                    - (eigenvalue - 0.5) * (eigenvalue - 0.5).ln();
650            }
651        }
652
653        entropy
654    }
655
656    /// Calculate entanglement measures
657    pub fn calculate_entanglement_measures(&self) -> CVEntanglementMeasures {
658        // Simplified calculations for demonstration
659        let entropy = self.calculate_entanglement_entropy();
660
661        CVEntanglementMeasures {
662            logarithmic_negativity: entropy * 0.5,
663            entanglement_of_formation: entropy * 0.7,
664            mutual_information: entropy * 1.2,
665            epr_correlation: self.calculate_epr_correlation(),
666        }
667    }
668
669    /// Calculate EPR correlation
670    fn calculate_epr_correlation(&self) -> f64 {
671        if self.num_modes < 2 {
672            return 0.0;
673        }
674
675        // Calculate correlation between first two modes
676        let cov_x1x2 = self.covariancematrix[0][2];
677        let cov_p1p2 = self.covariancematrix[1][3];
678
679        (cov_x1x2.abs() + cov_p1p2.abs()) / 2.0
680    }
681}
682
683// rand_distr imports are already available at the top of the file
684
685#[cfg(test)]
686mod tests {
687    use super::*;
688
689    #[test]
690    fn test_vacuum_state_creation() {
691        let state = GaussianState::vacuum_state(2);
692        assert_eq!(state.num_modes, 2);
693        assert_eq!(state.mean_vector.len(), 4);
694        assert_eq!(state.covariancematrix.len(), 4);
695
696        // Check vacuum variances
697        assert!((state.covariancematrix[0][0] - 0.5).abs() < 1e-10);
698        assert!((state.covariancematrix[1][1] - 0.5).abs() < 1e-10);
699    }
700
701    #[test]
702    fn test_coherent_state_creation() {
703        let displacements = vec![Complex::new(1.0, 0.5), Complex::new(0.0, 1.0)];
704        let state = GaussianState::coherent_state(2, displacements).unwrap();
705
706        assert!(state.mean_vector[0] > 0.0); // x quadrature of mode 0
707        assert!(state.mean_vector[1] > 0.0); // p quadrature of mode 0
708    }
709
710    #[test]
711    fn test_displacement_operation() {
712        let mut state = GaussianState::vacuum_state(1);
713        let displacement = Complex::new(2.0, 1.0);
714
715        state.apply_displacement(0, displacement).unwrap();
716
717        assert!((state.mean_vector[0] - 2.0 * (2.0_f64).sqrt()).abs() < 1e-10);
718        assert!((state.mean_vector[1] - 1.0 * (2.0_f64).sqrt()).abs() < 1e-10);
719    }
720
721    #[test]
722    fn test_squeezing_operation() {
723        let mut state = GaussianState::vacuum_state(1);
724
725        state.apply_squeezing(0, 1.0, 0.0).unwrap();
726
727        // Check that one quadrature is squeezed
728        assert!(state.covariancematrix[0][0] < 0.5); // x should be squeezed
729        assert!(state.covariancematrix[1][1] > 0.5); // p should be antisqueezed
730    }
731
732    #[test]
733    fn test_beamsplitter_operation() {
734        let mut state =
735            GaussianState::coherent_state(2, vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)])
736                .unwrap();
737
738        let initial_energy = state.mean_vector[0].powi(2)
739            + state.mean_vector[1].powi(2)
740            + state.mean_vector[2].powi(2)
741            + state.mean_vector[3].powi(2);
742
743        state.apply_beamsplitter(0, 1, 0.5, 0.0).unwrap();
744
745        let final_energy = state.mean_vector[0].powi(2)
746            + state.mean_vector[1].powi(2)
747            + state.mean_vector[2].powi(2)
748            + state.mean_vector[3].powi(2);
749
750        // Energy should be conserved
751        assert!((initial_energy - final_energy).abs() < 1e-10);
752    }
753
754    #[test]
755    fn test_mode_state_calculation() {
756        let state = GaussianState::squeezed_vacuum_state(1, vec![1.0], vec![0.0]).unwrap();
757
758        let mode_state = state.get_mode_state(0).unwrap();
759        assert!(mode_state.squeezing_parameter > 0.0);
760        assert!((mode_state.squeezing_phase).abs() < 1e-10);
761    }
762}