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