quantrs2_device/continuous_variable/
gaussian_states.rs1use super::{CVDeviceConfig, CVEntanglementMeasures, CVModeState, Complex};
7use crate::{DeviceError, DeviceResult};
8use scirs2_core::random::prelude::*;
9use scirs2_core::random::{Distribution, RandNormal};
10type Normal<T> = RandNormal<T>;
12use serde::{Deserialize, Serialize};
13use std::f64::consts::PI;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct GaussianState {
18 pub num_modes: usize,
20 pub mean_vector: Vec<f64>,
22 pub covariancematrix: Vec<Vec<f64>>,
24 symplectic_matrix: Vec<Vec<f64>>,
26}
27
28impl GaussianState {
29 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 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 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 for (i, displacement) in displacements.iter().enumerate() {
62 state.mean_vector[2 * i] = displacement.real * (2.0_f64).sqrt(); state.mean_vector[2 * i + 1] = displacement.imag * (2.0_f64).sqrt();
64 }
66
67 Ok(state)
68 }
69
70 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 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 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 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 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 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 let s11 = cosh_r - sinh_r * cos_phi; let s12 = -sinh_r * sin_phi; let s21 = -sinh_r * sin_phi; let s22 = cosh_r + sinh_r * cos_phi; let i = 2 * mode;
144 let j = 2 * mode + 1;
145
146 let old_covar = self.covariancematrix.clone();
147
148 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 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 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 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 let indices = [2 * mode1, 2 * mode1 + 1, 2 * mode2, 2 * mode2 + 1];
227 let old_covar = self.covariancematrix.clone();
228
229 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 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 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 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 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 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 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 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 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 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 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 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 let cos_phi = phase.cos();
388 let sin_phi = phase.sin();
389
390 let mean_result =
392 cos_phi * self.mean_vector[2 * mode] + sin_phi * self.mean_vector[2 * mode + 1];
393
394 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 let noise_variance = self.calculate_measurement_noise(config);
405 let total_variance = variance + noise_variance;
406
407 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 self.condition_on_homodyne_measurement(mode, phase, result)?;
417
418 Ok(result)
419 }
420
421 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 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 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 self.condition_on_heterodyne_measurement(mode, result)?;
463
464 Ok(result)
465 }
466
467 fn calculate_measurement_noise(&self, config: &CVDeviceConfig) -> f64 {
469 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; electronic_noise + efficiency_loss + thermal_noise
475 }
476
477 pub fn condition_on_homodyne_measurement(
479 &mut self,
480 mode: usize,
481 phase: f64,
482 result: f64,
483 ) -> DeviceResult<()> {
484 let cos_phi = phase.cos();
488 let sin_phi = phase.sin();
489
490 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 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; 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 pub fn condition_on_heterodyne_measurement(
507 &mut self,
508 mode: usize,
509 _result: Complex,
510 ) -> DeviceResult<()> {
511 self.reset_mode_to_vacuum(mode)
513 }
514
515 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 self.mean_vector[2 * mode] = 0.0;
526 self.mean_vector[2 * mode + 1] = 0.0;
527
528 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 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 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 let (squeezing_parameter, squeezing_phase) = self.calculate_squeezing(mode);
566
567 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 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 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 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 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 pub fn calculate_purity(&self) -> f64 {
627 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 pub fn calculate_entanglement_entropy(&self) -> f64 {
637 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 pub fn calculate_entanglement_measures(&self) -> CVEntanglementMeasures {
658 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 fn calculate_epr_correlation(&self) -> f64 {
671 if self.num_modes < 2 {
672 return 0.0;
673 }
674
675 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#[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 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); assert!(state.mean_vector[1] > 0.0); }
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 assert!(state.covariancematrix[0][0] < 0.5); assert!(state.covariancematrix[1][1] > 0.5); }
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 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}