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 {mode} exceeds available modes"
110            )));
111        }
112
113        // Update mean vector
114        self.mean_vector[2 * mode] += displacement.real * (2.0_f64).sqrt();
115        self.mean_vector[2 * mode + 1] += displacement.imag * (2.0_f64).sqrt();
116
117        Ok(())
118    }
119
120    /// Apply squeezing operation to a mode
121    pub fn apply_squeezing(&mut self, mode: usize, r: f64, phi: f64) -> DeviceResult<()> {
122        if mode >= self.num_modes {
123            return Err(DeviceError::InvalidInput(format!(
124                "Mode {mode} exceeds available modes"
125            )));
126        }
127
128        let cos_phi = phi.cos();
129        let sin_phi = phi.sin();
130        let cosh_r = r.cosh();
131        let sinh_r = r.sinh();
132
133        // Squeezing transformation matrix
134        // Standard squeezing: S = [[e^(-r), 0], [0, e^r]] for φ=0
135        let s11 = sinh_r.mul_add(-cos_phi, cosh_r); // For φ=0: e^(-r) (squeeze position)
136        let s12 = -sinh_r * sin_phi; // For φ=0: 0
137        let s21 = -sinh_r * sin_phi; // For φ=0: 0
138        let s22 = sinh_r.mul_add(cos_phi, cosh_r); // For φ=0: e^r (anti-squeeze momentum)
139
140        // Apply to covariance matrix
141        let i = 2 * mode;
142        let j = 2 * mode + 1;
143
144        let old_covar = self.covariancematrix.clone();
145
146        // Transform covariance matrix: S * V * S^T
147        for a in 0..2 * self.num_modes {
148            for b in 0..2 * self.num_modes {
149                if (a == i || a == j) && (b == i || b == j) {
150                    let mut new_val = 0.0;
151
152                    for k in &[i, j] {
153                        for l in &[i, j] {
154                            let s_ak = if a == i {
155                                if *k == i {
156                                    s11
157                                } else {
158                                    s12
159                                }
160                            } else if *k == i {
161                                s21
162                            } else {
163                                s22
164                            };
165
166                            let s_bl = if b == i {
167                                if *l == i {
168                                    s11
169                                } else {
170                                    s12
171                                }
172                            } else if *l == i {
173                                s21
174                            } else {
175                                s22
176                            };
177
178                            new_val += s_ak * old_covar[*k][*l] * s_bl;
179                        }
180                    }
181
182                    self.covariancematrix[a][b] = new_val;
183                } else if a == i || a == j {
184                    // Mixed terms
185                    let s_a = if a == i { [s11, s12] } else { [s21, s22] };
186                    self.covariancematrix[a][b] =
187                        s_a[0].mul_add(old_covar[i][b], s_a[1] * old_covar[j][b]);
188                } else if b == i || b == j {
189                    // Mixed terms (transpose)
190                    let s_b = if b == i { [s11, s21] } else { [s12, s22] };
191                    self.covariancematrix[a][b] =
192                        old_covar[a][i].mul_add(s_b[0], old_covar[a][j] * s_b[1]);
193                }
194            }
195        }
196
197        Ok(())
198    }
199
200    /// Apply two-mode squeezing operation
201    pub fn apply_two_mode_squeezing(
202        &mut self,
203        mode1: usize,
204        mode2: usize,
205        r: f64,
206        phi: f64,
207    ) -> DeviceResult<()> {
208        if mode1 >= self.num_modes || mode2 >= self.num_modes {
209            return Err(DeviceError::InvalidInput(
210                "One or both modes exceed available modes".to_string(),
211            ));
212        }
213
214        let cos_phi = phi.cos();
215        let sin_phi = phi.sin();
216        let cosh_r = r.cosh();
217        let sinh_r = r.sinh();
218
219        // Two-mode squeezing transformation
220        let indices = [2 * mode1, 2 * mode1 + 1, 2 * mode2, 2 * mode2 + 1];
221        let old_covar = self.covariancematrix.clone();
222
223        // Build 4x4 transformation matrix
224        let mut transform = [[0.0; 4]; 4];
225        transform[0][0] = cosh_r;
226        transform[0][2] = sinh_r * cos_phi;
227        transform[0][3] = sinh_r * sin_phi;
228        transform[1][1] = cosh_r;
229        transform[1][2] = sinh_r * sin_phi;
230        transform[1][3] = -sinh_r * cos_phi;
231        transform[2][0] = sinh_r * cos_phi;
232        transform[2][1] = sinh_r * sin_phi;
233        transform[2][2] = cosh_r;
234        transform[3][0] = sinh_r * sin_phi;
235        transform[3][1] = -sinh_r * cos_phi;
236        transform[3][3] = cosh_r;
237
238        // Apply transformation to relevant block of covariance matrix
239        for i in 0..4 {
240            for j in 0..4 {
241                let mut new_val = 0.0;
242                for k in 0..4 {
243                    for l in 0..4 {
244                        new_val +=
245                            transform[i][k] * old_covar[indices[k]][indices[l]] * transform[j][l];
246                    }
247                }
248                self.covariancematrix[indices[i]][indices[j]] = new_val;
249            }
250        }
251
252        Ok(())
253    }
254
255    /// Apply beamsplitter operation
256    pub fn apply_beamsplitter(
257        &mut self,
258        mode1: usize,
259        mode2: usize,
260        transmittance: f64,
261        phase: f64,
262    ) -> DeviceResult<()> {
263        if mode1 >= self.num_modes || mode2 >= self.num_modes {
264            return Err(DeviceError::InvalidInput(
265                "One or both modes exceed available modes".to_string(),
266            ));
267        }
268
269        let t = transmittance.sqrt();
270        let r = (1.0 - transmittance).sqrt();
271        let cos_phi = phase.cos();
272        let sin_phi = phase.sin();
273
274        // Beamsplitter transformation matrix
275        let indices = [2 * mode1, 2 * mode1 + 1, 2 * mode2, 2 * mode2 + 1];
276        let old_mean = self.mean_vector.clone();
277        let old_covar = self.covariancematrix.clone();
278
279        // Transform mean vector
280        let mean1_x = old_mean[2 * mode1];
281        let mean1_p = old_mean[2 * mode1 + 1];
282        let mean2_x = old_mean[2 * mode2];
283        let mean2_p = old_mean[2 * mode2 + 1];
284
285        self.mean_vector[2 * mode1] =
286            (r * sin_phi).mul_add(-mean2_p, t.mul_add(mean1_x, r * cos_phi * mean2_x));
287        self.mean_vector[2 * mode1 + 1] =
288            (r * cos_phi).mul_add(mean2_p, t.mul_add(mean1_p, r * sin_phi * mean2_x));
289        self.mean_vector[2 * mode2] = t.mul_add(
290            mean2_x,
291            (-r * cos_phi).mul_add(mean1_x, r * sin_phi * mean1_p),
292        );
293        self.mean_vector[2 * mode2 + 1] = t.mul_add(
294            mean2_p,
295            (r * sin_phi).mul_add(mean1_x, r * cos_phi * mean1_p),
296        );
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 {mode} exceeds available modes"
335            )));
336        }
337
338        let cos_phi = phi.cos();
339        let sin_phi = phi.sin();
340
341        // Rotate mean vector
342        let mean_x = self.mean_vector[2 * mode];
343        let mean_p = self.mean_vector[2 * mode + 1];
344
345        self.mean_vector[2 * mode] = cos_phi.mul_add(mean_x, sin_phi * mean_p);
346        self.mean_vector[2 * mode + 1] = (-sin_phi).mul_add(mean_x, cos_phi * mean_p);
347
348        // Rotation transformation matrix
349        let indices = [2 * mode, 2 * mode + 1];
350        let old_covar = self.covariancematrix.clone();
351
352        let transform = [[cos_phi, sin_phi], [-sin_phi, cos_phi]];
353
354        // Apply rotation to covariance matrix
355        for i in 0..2 {
356            for j in 0..2 {
357                let mut new_val = 0.0;
358                for k in 0..2 {
359                    for l in 0..2 {
360                        new_val +=
361                            transform[i][k] * old_covar[indices[k]][indices[l]] * transform[j][l];
362                    }
363                }
364                self.covariancematrix[indices[i]][indices[j]] = new_val;
365            }
366        }
367
368        Ok(())
369    }
370
371    /// Perform homodyne measurement
372    pub fn homodyne_measurement(
373        &mut self,
374        mode: usize,
375        phase: f64,
376        config: &CVDeviceConfig,
377    ) -> DeviceResult<f64> {
378        if mode >= self.num_modes {
379            return Err(DeviceError::InvalidInput(format!(
380                "Mode {mode} exceeds available modes"
381            )));
382        }
383
384        // Measurement operator: x*cos(phi) + p*sin(phi)
385        let cos_phi = phase.cos();
386        let sin_phi = phase.sin();
387
388        // Mean value
389        let mean_result = cos_phi.mul_add(
390            self.mean_vector[2 * mode],
391            sin_phi * self.mean_vector[2 * mode + 1],
392        );
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 = (2.0 * cos_phi * sin_phi).mul_add(
400            cov_xp,
401            (cos_phi * cos_phi).mul_add(var_x, sin_phi * sin_phi * var_p),
402        );
403
404        // Add noise effects
405        let noise_variance = self.calculate_measurement_noise(config);
406        let total_variance = variance + noise_variance;
407
408        // Sample from Gaussian distribution
409        let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
410        let noise: f64 = Normal::new(0.0, total_variance.sqrt())
411            .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {e}")))?
412            .sample(&mut rng);
413
414        let result = mean_result + noise;
415
416        // Condition the state on the measurement result
417        self.condition_on_homodyne_measurement(mode, phase, result)?;
418
419        Ok(result)
420    }
421
422    /// Perform heterodyne measurement
423    pub fn heterodyne_measurement(
424        &mut self,
425        mode: usize,
426        config: &CVDeviceConfig,
427    ) -> DeviceResult<Complex> {
428        if mode >= self.num_modes {
429            return Err(DeviceError::InvalidInput(format!(
430                "Mode {mode} exceeds available modes"
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_p.mul_add(Complex::i().real, result_x) / (2.0_f64).sqrt(),
458            result_x.mul_add(-Complex::i().imag, result_p) / (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).mul_add(
496            self.covariancematrix[2 * mode][2 * mode],
497            sin_phi * sin_phi * self.covariancematrix[2 * mode + 1][2 * mode + 1],
498        );
499
500        let reduction_factor = 0.1; // Measurement significantly reduces uncertainty
501        self.covariancematrix[2 * mode][2 * mode] *= reduction_factor;
502        self.covariancematrix[2 * mode + 1][2 * mode + 1] *= reduction_factor;
503
504        Ok(())
505    }
506
507    /// Condition state on heterodyne measurement result
508    pub fn condition_on_heterodyne_measurement(
509        &mut self,
510        mode: usize,
511        _result: Complex,
512    ) -> DeviceResult<()> {
513        // Heterodyne measurement destroys the mode - reset to vacuum
514        self.reset_mode_to_vacuum(mode)
515    }
516
517    /// Reset a mode to vacuum state
518    pub fn reset_mode_to_vacuum(&mut self, mode: usize) -> DeviceResult<()> {
519        if mode >= self.num_modes {
520            return Err(DeviceError::InvalidInput(format!(
521                "Mode {mode} exceeds available modes"
522            )));
523        }
524
525        // Reset mean
526        self.mean_vector[2 * mode] = 0.0;
527        self.mean_vector[2 * mode + 1] = 0.0;
528
529        // Reset covariance to vacuum values
530        self.covariancematrix[2 * mode][2 * mode] = 0.5;
531        self.covariancematrix[2 * mode + 1][2 * mode + 1] = 0.5;
532        self.covariancematrix[2 * mode][2 * mode + 1] = 0.0;
533        self.covariancematrix[2 * mode + 1][2 * mode] = 0.0;
534
535        // Remove correlations with other modes
536        for i in 0..2 * self.num_modes {
537            if i != 2 * mode && i != 2 * mode + 1 {
538                self.covariancematrix[2 * mode][i] = 0.0;
539                self.covariancematrix[2 * mode + 1][i] = 0.0;
540                self.covariancematrix[i][2 * mode] = 0.0;
541                self.covariancematrix[i][2 * mode + 1] = 0.0;
542            }
543        }
544
545        Ok(())
546    }
547
548    /// Get mode state information
549    pub fn get_mode_state(&self, mode: usize) -> DeviceResult<CVModeState> {
550        if mode >= self.num_modes {
551            return Err(DeviceError::InvalidInput(format!(
552                "Mode {mode} exceeds available modes"
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).mul_add(var_x - var_p, 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 = cov_xp.mul_add(-cov_xp, var_x * var_p);
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 = cov_xp.mul_add(-cov_xp, var_x * var_p);
646            if det > 0.25 {
647                let eigenvalue = det.sqrt();
648                entropy += (eigenvalue + 0.5).mul_add(
649                    (eigenvalue + 0.5).ln(),
650                    -((eigenvalue - 0.5) * (eigenvalue - 0.5).ln()),
651                );
652            }
653        }
654
655        entropy
656    }
657
658    /// Calculate entanglement measures
659    pub fn calculate_entanglement_measures(&self) -> CVEntanglementMeasures {
660        // Simplified calculations for demonstration
661        let entropy = self.calculate_entanglement_entropy();
662
663        CVEntanglementMeasures {
664            logarithmic_negativity: entropy * 0.5,
665            entanglement_of_formation: entropy * 0.7,
666            mutual_information: entropy * 1.2,
667            epr_correlation: self.calculate_epr_correlation(),
668        }
669    }
670
671    /// Calculate EPR correlation
672    fn calculate_epr_correlation(&self) -> f64 {
673        if self.num_modes < 2 {
674            return 0.0;
675        }
676
677        // Calculate correlation between first two modes
678        let cov_x1x2 = self.covariancematrix[0][2];
679        let cov_p1p2 = self.covariancematrix[1][3];
680
681        f64::midpoint(cov_x1x2.abs(), cov_p1p2.abs())
682    }
683}
684
685// rand_distr imports are already available at the top of the file
686
687#[cfg(test)]
688mod tests {
689    use super::*;
690
691    #[test]
692    fn test_vacuum_state_creation() {
693        let state = GaussianState::vacuum_state(2);
694        assert_eq!(state.num_modes, 2);
695        assert_eq!(state.mean_vector.len(), 4);
696        assert_eq!(state.covariancematrix.len(), 4);
697
698        // Check vacuum variances
699        assert!((state.covariancematrix[0][0] - 0.5).abs() < 1e-10);
700        assert!((state.covariancematrix[1][1] - 0.5).abs() < 1e-10);
701    }
702
703    #[test]
704    fn test_coherent_state_creation() {
705        let displacements = vec![Complex::new(1.0, 0.5), Complex::new(0.0, 1.0)];
706        let state = GaussianState::coherent_state(2, displacements)
707            .expect("Coherent state creation should succeed");
708
709        assert!(state.mean_vector[0] > 0.0); // x quadrature of mode 0
710        assert!(state.mean_vector[1] > 0.0); // p quadrature of mode 0
711    }
712
713    #[test]
714    fn test_displacement_operation() {
715        let mut state = GaussianState::vacuum_state(1);
716        let displacement = Complex::new(2.0, 1.0);
717
718        state
719            .apply_displacement(0, displacement)
720            .expect("Displacement operation should succeed");
721
722        assert!((state.mean_vector[0] - 2.0 * (2.0_f64).sqrt()).abs() < 1e-10);
723        assert!((state.mean_vector[1] - 1.0 * (2.0_f64).sqrt()).abs() < 1e-10);
724    }
725
726    #[test]
727    fn test_squeezing_operation() {
728        let mut state = GaussianState::vacuum_state(1);
729
730        state
731            .apply_squeezing(0, 1.0, 0.0)
732            .expect("Squeezing operation should succeed");
733
734        // Check that one quadrature is squeezed
735        assert!(state.covariancematrix[0][0] < 0.5); // x should be squeezed
736        assert!(state.covariancematrix[1][1] > 0.5); // p should be antisqueezed
737    }
738
739    #[test]
740    fn test_beamsplitter_operation() {
741        let mut state =
742            GaussianState::coherent_state(2, vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)])
743                .expect("Coherent state creation should succeed");
744
745        let initial_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        state
751            .apply_beamsplitter(0, 1, 0.5, 0.0)
752            .expect("Beamsplitter operation should succeed");
753
754        let final_energy = state.mean_vector[0].powi(2)
755            + state.mean_vector[1].powi(2)
756            + state.mean_vector[2].powi(2)
757            + state.mean_vector[3].powi(2);
758
759        // Energy should be conserved
760        assert!((initial_energy - final_energy).abs() < 1e-10);
761    }
762
763    #[test]
764    fn test_mode_state_calculation() {
765        let state = GaussianState::squeezed_vacuum_state(1, vec![1.0], vec![0.0])
766            .expect("Squeezed vacuum state creation should succeed");
767
768        let mode_state = state
769            .get_mode_state(0)
770            .expect("Getting mode state should succeed");
771        assert!(mode_state.squeezing_parameter > 0.0);
772        assert!((mode_state.squeezing_phase).abs() < 1e-10);
773    }
774}