quantrs2_device/continuous_variable/
cv_gates.rs

1//! Continuous variable quantum gates
2//!
3//! This module implements the standard gate set for continuous variable quantum computing,
4//! including Gaussian operations and some non-Gaussian operations.
5
6use super::{Complex, GaussianState};
7use crate::{DeviceError, DeviceResult};
8use serde::{Deserialize, Serialize};
9use std::f64::consts::PI;
10
11/// Types of CV gates
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub enum CVGateType {
14    /// Displacement gate D(α)
15    Displacement { amplitude: Complex },
16    /// Squeezing gate S(r, φ)
17    Squeezing { parameter: f64, phase: f64 },
18    /// Two-mode squeezing gate S₂(r, φ)
19    TwoModeSqueezing { parameter: f64, phase: f64 },
20    /// Beamsplitter gate BS(θ, φ)
21    Beamsplitter { transmittance: f64, phase: f64 },
22    /// Phase rotation gate R(φ)
23    PhaseRotation { phase: f64 },
24    /// Controlled displacement gate CD(α)
25    ControlledDisplacement { amplitude: Complex },
26    /// Controlled phase gate CP(s)
27    ControlledPhase { parameter: f64 },
28    /// Cross-Kerr gate CK(κ)
29    CrossKerr { parameter: f64 },
30    /// Cubic phase gate V(γ)
31    CubicPhase { parameter: f64 },
32    /// Position measurement M_x(s)
33    PositionMeasurement { result: f64 },
34    /// Momentum measurement M_p(s)
35    MomentumMeasurement { result: f64 },
36}
37
38/// CV gate parameters for parameterized operations
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct CVGateParams {
41    /// Real parameters (amplitudes, phases, etc.)
42    pub real_params: Vec<f64>,
43    /// Complex parameters (displacements, etc.)
44    pub complex_params: Vec<Complex>,
45    /// Target modes
46    pub target_modes: Vec<usize>,
47    /// Control modes (if applicable)
48    pub control_modes: Vec<usize>,
49}
50
51/// Sequence of CV gates forming a quantum program
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct CVGateSequence {
54    /// Gates in the sequence
55    pub gates: Vec<(CVGateType, CVGateParams)>,
56    /// Total number of modes required
57    pub num_modes: usize,
58}
59
60impl CVGateSequence {
61    /// Create a new empty gate sequence
62    pub const fn new(num_modes: usize) -> Self {
63        Self {
64            gates: Vec::new(),
65            num_modes,
66        }
67    }
68
69    /// Add a displacement gate
70    pub fn displacement(&mut self, mode: usize, amplitude: Complex) -> DeviceResult<()> {
71        if mode >= self.num_modes {
72            return Err(DeviceError::InvalidInput(format!(
73                "Mode {mode} exceeds sequence capacity"
74            )));
75        }
76
77        self.gates.push((
78            CVGateType::Displacement { amplitude },
79            CVGateParams {
80                real_params: vec![amplitude.real, amplitude.imag],
81                complex_params: vec![amplitude],
82                target_modes: vec![mode],
83                control_modes: vec![],
84            },
85        ));
86
87        Ok(())
88    }
89
90    /// Add a squeezing gate
91    pub fn squeezing(&mut self, mode: usize, parameter: f64, phase: f64) -> DeviceResult<()> {
92        if mode >= self.num_modes {
93            return Err(DeviceError::InvalidInput(format!(
94                "Mode {mode} exceeds sequence capacity"
95            )));
96        }
97
98        self.gates.push((
99            CVGateType::Squeezing { parameter, phase },
100            CVGateParams {
101                real_params: vec![parameter, phase],
102                complex_params: vec![],
103                target_modes: vec![mode],
104                control_modes: vec![],
105            },
106        ));
107
108        Ok(())
109    }
110
111    /// Add a two-mode squeezing gate
112    pub fn two_mode_squeezing(
113        &mut self,
114        mode1: usize,
115        mode2: usize,
116        parameter: f64,
117        phase: f64,
118    ) -> DeviceResult<()> {
119        if mode1 >= self.num_modes || mode2 >= self.num_modes {
120            return Err(DeviceError::InvalidInput(
121                "One or both modes exceed sequence capacity".to_string(),
122            ));
123        }
124
125        self.gates.push((
126            CVGateType::TwoModeSqueezing { parameter, phase },
127            CVGateParams {
128                real_params: vec![parameter, phase],
129                complex_params: vec![],
130                target_modes: vec![mode1, mode2],
131                control_modes: vec![],
132            },
133        ));
134
135        Ok(())
136    }
137
138    /// Add a beamsplitter gate
139    pub fn beamsplitter(
140        &mut self,
141        mode1: usize,
142        mode2: usize,
143        transmittance: f64,
144        phase: f64,
145    ) -> DeviceResult<()> {
146        if mode1 >= self.num_modes || mode2 >= self.num_modes {
147            return Err(DeviceError::InvalidInput(
148                "One or both modes exceed sequence capacity".to_string(),
149            ));
150        }
151
152        if !(0.0..=1.0).contains(&transmittance) {
153            return Err(DeviceError::InvalidInput(
154                "Transmittance must be between 0 and 1".to_string(),
155            ));
156        }
157
158        self.gates.push((
159            CVGateType::Beamsplitter {
160                transmittance,
161                phase,
162            },
163            CVGateParams {
164                real_params: vec![transmittance, phase],
165                complex_params: vec![],
166                target_modes: vec![mode1, mode2],
167                control_modes: vec![],
168            },
169        ));
170
171        Ok(())
172    }
173
174    /// Add a phase rotation gate
175    pub fn phase_rotation(&mut self, mode: usize, phase: f64) -> DeviceResult<()> {
176        if mode >= self.num_modes {
177            return Err(DeviceError::InvalidInput(format!(
178                "Mode {mode} exceeds sequence capacity"
179            )));
180        }
181
182        self.gates.push((
183            CVGateType::PhaseRotation { phase },
184            CVGateParams {
185                real_params: vec![phase],
186                complex_params: vec![],
187                target_modes: vec![mode],
188                control_modes: vec![],
189            },
190        ));
191
192        Ok(())
193    }
194
195    /// Add a controlled displacement gate
196    pub fn controlled_displacement(
197        &mut self,
198        control_mode: usize,
199        target_mode: usize,
200        amplitude: Complex,
201    ) -> DeviceResult<()> {
202        if control_mode >= self.num_modes || target_mode >= self.num_modes {
203            return Err(DeviceError::InvalidInput(
204                "Control or target mode exceeds sequence capacity".to_string(),
205            ));
206        }
207
208        self.gates.push((
209            CVGateType::ControlledDisplacement { amplitude },
210            CVGateParams {
211                real_params: vec![amplitude.real, amplitude.imag],
212                complex_params: vec![amplitude],
213                target_modes: vec![target_mode],
214                control_modes: vec![control_mode],
215            },
216        ));
217
218        Ok(())
219    }
220
221    /// Add a controlled phase gate
222    pub fn controlled_phase(
223        &mut self,
224        control_mode: usize,
225        target_mode: usize,
226        parameter: f64,
227    ) -> DeviceResult<()> {
228        if control_mode >= self.num_modes || target_mode >= self.num_modes {
229            return Err(DeviceError::InvalidInput(
230                "Control or target mode exceeds sequence capacity".to_string(),
231            ));
232        }
233
234        self.gates.push((
235            CVGateType::ControlledPhase { parameter },
236            CVGateParams {
237                real_params: vec![parameter],
238                complex_params: vec![],
239                target_modes: vec![target_mode],
240                control_modes: vec![control_mode],
241            },
242        ));
243
244        Ok(())
245    }
246
247    /// Add a cross-Kerr gate
248    pub fn cross_kerr(&mut self, mode1: usize, mode2: usize, parameter: f64) -> DeviceResult<()> {
249        if mode1 >= self.num_modes || mode2 >= self.num_modes {
250            return Err(DeviceError::InvalidInput(
251                "One or both modes exceed sequence capacity".to_string(),
252            ));
253        }
254
255        self.gates.push((
256            CVGateType::CrossKerr { parameter },
257            CVGateParams {
258                real_params: vec![parameter],
259                complex_params: vec![],
260                target_modes: vec![mode1, mode2],
261                control_modes: vec![],
262            },
263        ));
264
265        Ok(())
266    }
267
268    /// Add a cubic phase gate (non-Gaussian)
269    pub fn cubic_phase(&mut self, mode: usize, parameter: f64) -> DeviceResult<()> {
270        if mode >= self.num_modes {
271            return Err(DeviceError::InvalidInput(format!(
272                "Mode {mode} exceeds sequence capacity"
273            )));
274        }
275
276        self.gates.push((
277            CVGateType::CubicPhase { parameter },
278            CVGateParams {
279                real_params: vec![parameter],
280                complex_params: vec![],
281                target_modes: vec![mode],
282                control_modes: vec![],
283            },
284        ));
285
286        Ok(())
287    }
288
289    /// Get the number of gates in the sequence
290    pub fn gate_count(&self) -> usize {
291        self.gates.len()
292    }
293
294    /// Get the depth of the sequence (maximum number of gates on any mode)
295    pub fn depth(&self) -> usize {
296        let mut mode_depths = vec![0; self.num_modes];
297
298        for (_, params) in &self.gates {
299            for &mode in &params.target_modes {
300                mode_depths[mode] += 1;
301            }
302            for &mode in &params.control_modes {
303                mode_depths[mode] += 1;
304            }
305        }
306
307        *mode_depths.iter().max().unwrap_or(&0)
308    }
309
310    /// Check if the sequence contains only Gaussian operations
311    pub fn is_gaussian(&self) -> bool {
312        for (gate_type, _) in &self.gates {
313            match gate_type {
314                CVGateType::CubicPhase { .. }
315                | CVGateType::PositionMeasurement { .. }
316                | CVGateType::MomentumMeasurement { .. } => return false,
317                _ => continue,
318            }
319        }
320        true
321    }
322
323    /// Execute the gate sequence on a Gaussian state
324    pub fn execute_on_state(&self, state: &mut GaussianState) -> DeviceResult<()> {
325        if state.num_modes != self.num_modes {
326            return Err(DeviceError::InvalidInput(
327                "State mode count doesn't match sequence requirements".to_string(),
328            ));
329        }
330
331        for (gate_type, params) in &self.gates {
332            self.execute_single_gate(gate_type, params, state)?;
333        }
334
335        Ok(())
336    }
337
338    /// Execute a single gate on the state
339    fn execute_single_gate(
340        &self,
341        gate_type: &CVGateType,
342        params: &CVGateParams,
343        state: &mut GaussianState,
344    ) -> DeviceResult<()> {
345        match gate_type {
346            CVGateType::Displacement { amplitude } => {
347                if params.target_modes.len() != 1 {
348                    return Err(DeviceError::InvalidInput(
349                        "Displacement gate requires exactly one target mode".to_string(),
350                    ));
351                }
352                state.apply_displacement(params.target_modes[0], *amplitude)?;
353            }
354
355            CVGateType::Squeezing { parameter, phase } => {
356                if params.target_modes.len() != 1 {
357                    return Err(DeviceError::InvalidInput(
358                        "Squeezing gate requires exactly one target mode".to_string(),
359                    ));
360                }
361                state.apply_squeezing(params.target_modes[0], *parameter, *phase)?;
362            }
363
364            CVGateType::TwoModeSqueezing { parameter, phase } => {
365                if params.target_modes.len() != 2 {
366                    return Err(DeviceError::InvalidInput(
367                        "Two-mode squeezing gate requires exactly two target modes".to_string(),
368                    ));
369                }
370                state.apply_two_mode_squeezing(
371                    params.target_modes[0],
372                    params.target_modes[1],
373                    *parameter,
374                    *phase,
375                )?;
376            }
377
378            CVGateType::Beamsplitter {
379                transmittance,
380                phase,
381            } => {
382                if params.target_modes.len() != 2 {
383                    return Err(DeviceError::InvalidInput(
384                        "Beamsplitter gate requires exactly two target modes".to_string(),
385                    ));
386                }
387                state.apply_beamsplitter(
388                    params.target_modes[0],
389                    params.target_modes[1],
390                    *transmittance,
391                    *phase,
392                )?;
393            }
394
395            CVGateType::PhaseRotation { phase } => {
396                if params.target_modes.len() != 1 {
397                    return Err(DeviceError::InvalidInput(
398                        "Phase rotation gate requires exactly one target mode".to_string(),
399                    ));
400                }
401                state.apply_phase_rotation(params.target_modes[0], *phase)?;
402            }
403
404            CVGateType::ControlledDisplacement { amplitude } => {
405                if params.control_modes.len() != 1 || params.target_modes.len() != 1 {
406                    return Err(DeviceError::InvalidInput(
407                        "Controlled displacement requires one control and one target mode"
408                            .to_string(),
409                    ));
410                }
411                self.apply_controlled_displacement(
412                    params.control_modes[0],
413                    params.target_modes[0],
414                    *amplitude,
415                    state,
416                )?;
417            }
418
419            CVGateType::ControlledPhase { parameter } => {
420                if params.control_modes.len() != 1 || params.target_modes.len() != 1 {
421                    return Err(DeviceError::InvalidInput(
422                        "Controlled phase requires one control and one target mode".to_string(),
423                    ));
424                }
425                self.apply_controlled_phase(
426                    params.control_modes[0],
427                    params.target_modes[0],
428                    *parameter,
429                    state,
430                )?;
431            }
432
433            CVGateType::CrossKerr { parameter } => {
434                if params.target_modes.len() != 2 {
435                    return Err(DeviceError::InvalidInput(
436                        "Cross-Kerr gate requires exactly two target modes".to_string(),
437                    ));
438                }
439                self.apply_cross_kerr(
440                    params.target_modes[0],
441                    params.target_modes[1],
442                    *parameter,
443                    state,
444                )?;
445            }
446
447            CVGateType::CubicPhase { parameter } => {
448                return Err(DeviceError::UnsupportedOperation(
449                    "Cubic phase gate is non-Gaussian and not supported for Gaussian states"
450                        .to_string(),
451                ));
452            }
453
454            CVGateType::PositionMeasurement { .. } | CVGateType::MomentumMeasurement { .. } => {
455                return Err(DeviceError::UnsupportedOperation(
456                    "Measurements should be performed separately from gate sequences".to_string(),
457                ));
458            }
459        }
460
461        Ok(())
462    }
463
464    /// Apply controlled displacement (simplified implementation)
465    fn apply_controlled_displacement(
466        &self,
467        control_mode: usize,
468        target_mode: usize,
469        amplitude: Complex,
470        state: &mut GaussianState,
471    ) -> DeviceResult<()> {
472        // Simplified implementation - would need full multimode transformation
473        // For now, apply displacement scaled by control mode amplitude
474        let control_amplitude = Complex::new(
475            state.mean_vector[2 * control_mode] / (2.0_f64).sqrt(),
476            state.mean_vector[2 * control_mode + 1] / (2.0_f64).sqrt(),
477        );
478
479        let scaled_amplitude = amplitude * control_amplitude.magnitude();
480        state.apply_displacement(target_mode, scaled_amplitude)?;
481
482        Ok(())
483    }
484
485    /// Apply controlled phase (simplified implementation)
486    fn apply_controlled_phase(
487        &self,
488        control_mode: usize,
489        target_mode: usize,
490        parameter: f64,
491        state: &mut GaussianState,
492    ) -> DeviceResult<()> {
493        // Simplified implementation
494        let control_photon_number = self.estimate_photon_number(control_mode, state);
495        let phase = parameter * control_photon_number;
496        state.apply_phase_rotation(target_mode, phase)?;
497
498        Ok(())
499    }
500
501    /// Apply cross-Kerr interaction
502    fn apply_cross_kerr(
503        &self,
504        mode1: usize,
505        mode2: usize,
506        parameter: f64,
507        state: &mut GaussianState,
508    ) -> DeviceResult<()> {
509        // Simplified cross-Kerr implementation
510        // Cross-Kerr induces phase shifts proportional to photon numbers
511        let n1 = self.estimate_photon_number(mode1, state);
512        let n2 = self.estimate_photon_number(mode2, state);
513
514        state.apply_phase_rotation(mode1, parameter * n2)?;
515        state.apply_phase_rotation(mode2, parameter * n1)?;
516
517        Ok(())
518    }
519
520    /// Estimate photon number for a mode (for simplified implementations)
521    fn estimate_photon_number(&self, mode: usize, state: &GaussianState) -> f64 {
522        let mean_x = state.mean_vector[2 * mode];
523        let mean_p = state.mean_vector[2 * mode + 1];
524        let var_x = state.covariancematrix[2 * mode][2 * mode];
525        let var_p = state.covariancematrix[2 * mode + 1][2 * mode + 1];
526
527        // Average photon number approximation
528        0.5 * (mean_p.mul_add(mean_p, mean_x.powi(2)) / 2.0 + (var_x + var_p) - 1.0)
529    }
530}
531
532/// Common CV gate implementations
533pub struct CVGateLibrary;
534
535impl CVGateLibrary {
536    /// Create a displacement gate
537    pub fn displacement(amplitude: Complex) -> (CVGateType, CVGateParams) {
538        (
539            CVGateType::Displacement { amplitude },
540            CVGateParams {
541                real_params: vec![amplitude.real, amplitude.imag],
542                complex_params: vec![amplitude],
543                target_modes: vec![],
544                control_modes: vec![],
545            },
546        )
547    }
548
549    /// Create a squeezing gate
550    pub fn squeezing(parameter: f64, phase: f64) -> (CVGateType, CVGateParams) {
551        (
552            CVGateType::Squeezing { parameter, phase },
553            CVGateParams {
554                real_params: vec![parameter, phase],
555                complex_params: vec![],
556                target_modes: vec![],
557                control_modes: vec![],
558            },
559        )
560    }
561
562    /// Create a 50:50 beamsplitter
563    pub fn balanced_beamsplitter() -> (CVGateType, CVGateParams) {
564        (
565            CVGateType::Beamsplitter {
566                transmittance: 0.5,
567                phase: 0.0,
568            },
569            CVGateParams {
570                real_params: vec![0.5, 0.0],
571                complex_params: vec![],
572                target_modes: vec![],
573                control_modes: vec![],
574            },
575        )
576    }
577
578    /// Create a Hadamard-like operation for CV (Fourier transform)
579    pub fn fourier_transform() -> CVGateSequence {
580        let mut sequence = CVGateSequence::new(1);
581        sequence
582            .phase_rotation(0, PI / 2.0)
583            .expect("Phase rotation on mode 0 should always succeed for single-mode sequence");
584        sequence
585    }
586
587    /// Create a CNOT-like operation for CV
588    pub fn cv_cnot() -> CVGateSequence {
589        let mut sequence = CVGateSequence::new(2);
590        // Simplified CV CNOT using beamsplitter and phase rotations
591        sequence
592            .beamsplitter(0, 1, 0.5, 0.0)
593            .expect("Beamsplitter on modes 0,1 should always succeed for two-mode sequence");
594        sequence
595            .phase_rotation(1, PI)
596            .expect("Phase rotation on mode 1 should always succeed for two-mode sequence");
597        sequence
598            .beamsplitter(0, 1, 0.5, 0.0)
599            .expect("Beamsplitter on modes 0,1 should always succeed for two-mode sequence");
600        sequence
601    }
602
603    /// Create an EPR pair generation sequence
604    pub fn epr_pair_generation(squeezing_param: f64) -> CVGateSequence {
605        let mut sequence = CVGateSequence::new(2);
606        sequence
607            .two_mode_squeezing(0, 1, squeezing_param, 0.0)
608            .expect("Two-mode squeezing on modes 0,1 should always succeed for two-mode sequence");
609        sequence
610    }
611
612    /// Create a GKP (Gottesman-Kitaev-Preskill) state preparation sequence
613    pub fn gkp_state_preparation() -> CVGateSequence {
614        let mut sequence = CVGateSequence::new(1);
615        // Simplified GKP preparation using multiple squeezing operations
616        for i in 0..10 {
617            let phase = 2.0 * PI * i as f64 / 10.0;
618            sequence
619                .squeezing(0, 0.5, phase)
620                .expect("Squeezing on mode 0 should always succeed for single-mode sequence");
621        }
622        sequence
623    }
624}
625
626#[cfg(test)]
627mod tests {
628    use super::*;
629
630    #[test]
631    fn test_gate_sequence_creation() {
632        let mut sequence = CVGateSequence::new(3);
633        assert_eq!(sequence.num_modes, 3);
634        assert_eq!(sequence.gate_count(), 0);
635    }
636
637    #[test]
638    fn test_displacement_gate_addition() {
639        let mut sequence = CVGateSequence::new(2);
640        let amplitude = Complex::new(1.0, 0.5);
641
642        sequence
643            .displacement(0, amplitude)
644            .expect("Failed to add displacement gate");
645        assert_eq!(sequence.gate_count(), 1);
646
647        match &sequence.gates[0].0 {
648            CVGateType::Displacement { amplitude: a } => {
649                assert_eq!(*a, amplitude);
650            }
651            _ => panic!("Expected displacement gate"),
652        }
653    }
654
655    #[test]
656    fn test_beamsplitter_gate_addition() {
657        let mut sequence = CVGateSequence::new(2);
658
659        sequence
660            .beamsplitter(0, 1, 0.7, PI / 4.0)
661            .expect("Failed to add beamsplitter gate");
662        assert_eq!(sequence.gate_count(), 1);
663
664        match &sequence.gates[0].0 {
665            CVGateType::Beamsplitter {
666                transmittance,
667                phase,
668            } => {
669                assert_eq!(*transmittance, 0.7);
670                assert_eq!(*phase, PI / 4.0);
671            }
672            _ => panic!("Expected beamsplitter gate"),
673        }
674    }
675
676    #[test]
677    fn test_gaussian_property() {
678        let mut sequence = CVGateSequence::new(2);
679
680        sequence
681            .displacement(0, Complex::new(1.0, 0.0))
682            .expect("Failed to add displacement gate");
683        sequence
684            .squeezing(1, 0.5, 0.0)
685            .expect("Failed to add squeezing gate");
686        sequence
687            .beamsplitter(0, 1, 0.5, 0.0)
688            .expect("Failed to add beamsplitter gate");
689
690        assert!(sequence.is_gaussian());
691
692        sequence
693            .cubic_phase(0, 0.1)
694            .expect("Failed to add cubic phase gate");
695        assert!(!sequence.is_gaussian());
696    }
697
698    #[test]
699    fn test_sequence_depth_calculation() {
700        let mut sequence = CVGateSequence::new(3);
701
702        sequence
703            .displacement(0, Complex::new(1.0, 0.0))
704            .expect("Failed to add first displacement gate");
705        sequence
706            .displacement(0, Complex::new(0.5, 0.0))
707            .expect("Failed to add second displacement gate");
708        sequence
709            .squeezing(1, 0.5, 0.0)
710            .expect("Failed to add squeezing gate");
711        sequence
712            .beamsplitter(0, 2, 0.5, 0.0)
713            .expect("Failed to add beamsplitter gate");
714
715        assert_eq!(sequence.depth(), 3); // Mode 0 has 3 operations
716    }
717
718    #[test]
719    fn test_gate_execution_on_state() {
720        let mut sequence = CVGateSequence::new(2);
721        sequence
722            .displacement(0, Complex::new(1.0, 0.0))
723            .expect("Failed to add displacement gate");
724        sequence
725            .squeezing(1, 0.5, 0.0)
726            .expect("Failed to add squeezing gate");
727
728        let mut state = GaussianState::vacuum_state(2);
729        sequence
730            .execute_on_state(&mut state)
731            .expect("Failed to execute gate sequence on state");
732
733        // Check that the displacement was applied
734        assert!(state.mean_vector[0] > 0.0);
735
736        // Check that squeezing was applied
737        // For mode 1 (index 2 for x-quadrature), squeezing should reduce variance
738        // Expected: 0.5 * exp(-2*r) = 0.5 * exp(-1) ≈ 0.184
739        assert!(state.covariancematrix[2][2] < 0.5);
740    }
741
742    #[test]
743    fn test_epr_pair_generation() {
744        let sequence = CVGateLibrary::epr_pair_generation(1.0);
745        assert_eq!(sequence.gate_count(), 1);
746
747        let mut state = GaussianState::vacuum_state(2);
748        sequence
749            .execute_on_state(&mut state)
750            .expect("Failed to execute EPR pair generation sequence");
751
752        // EPR state should have correlations between modes
753        let entanglement = state.calculate_entanglement_measures();
754        assert!(entanglement.epr_correlation > 0.0);
755    }
756
757    #[test]
758    fn test_balanced_beamsplitter() {
759        let (gate_type, params) = CVGateLibrary::balanced_beamsplitter();
760
761        match gate_type {
762            CVGateType::Beamsplitter {
763                transmittance,
764                phase,
765            } => {
766                assert_eq!(transmittance, 0.5);
767                assert_eq!(phase, 0.0);
768            }
769            _ => panic!("Expected balanced beamsplitter"),
770        }
771    }
772
773    #[test]
774    fn test_invalid_mode_error() {
775        let mut sequence = CVGateSequence::new(2);
776
777        let result = sequence.displacement(3, Complex::new(1.0, 0.0));
778        assert!(result.is_err());
779
780        let result = sequence.beamsplitter(0, 3, 0.5, 0.0);
781        assert!(result.is_err());
782    }
783}