use crate::{Error, Result};
use serde::Serialize;
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub struct FusionErasureDistribution {
probabilities: [f64; 4],
}
impl FusionErasureDistribution {
pub fn probabilities(&self) -> [f64; 4] {
self.probabilities
}
pub fn marginals(&self) -> [f64; 2] {
[
self.probabilities[1] + self.probabilities[3],
self.probabilities[2] + self.probabilities[3],
]
}
pub fn sample(&self, uniform: f64) -> Result<[bool; 2]> {
if !uniform.is_finite() || !(0.0..1.0).contains(&uniform) {
return Err(Error::InvalidValue(
"fusion erasure variate outside [0,1)".into(),
));
}
let mut cumulative = 0.;
for (mask, probability) in self.probabilities.iter().enumerate() {
cumulative += probability;
if uniform < cumulative || mask == 3 {
return Ok([mask & 1 != 0, mask & 2 != 0]);
}
}
unreachable!("the last interval includes residual floating-point roundoff")
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub struct OpticalFusionLoss {
photon_loss: f64,
boost_level: u8,
shor_encoded: bool,
}
impl OpticalFusionLoss {
pub fn new(photon_loss: f64, boost_level: u8, shor_encoded: bool) -> Result<Self> {
if !photon_loss.is_finite() || !(0.0..=1.0).contains(&photon_loss) {
return Err(Error::InvalidValue(
"invalid optical photon-loss probability".into(),
));
}
if boost_level > 8 {
return Err(Error::InvalidValue(
"fusion boost level exceeds eight".into(),
));
}
Ok(Self {
photon_loss,
boost_level,
shor_encoded,
})
}
pub fn photon_loss(&self) -> f64 {
self.photon_loss
}
pub fn failure_probability(&self) -> f64 {
1. / f64::from(1u16 << (self.boost_level + 1))
}
pub fn physical_fusions(&self) -> usize {
if self.shor_encoded { 4 } else { 1 }
}
pub fn signal_photons(&self) -> usize {
2 * self.physical_fusions()
}
pub fn boosting_photons(&self) -> usize {
((1usize << (self.boost_level + 1)) - 2) * self.physical_fusions()
}
pub fn physical_distribution(&self) -> FusionErasureDistribution {
let photons = 1u16 << (self.boost_level + 1);
let survival = (1. - self.photon_loss).powi(i32::from(photons));
let failure = self.failure_probability();
FusionErasureDistribution {
probabilities: [
survival * (1. - failure),
survival * failure / 2.,
survival * failure / 2.,
1. - survival,
],
}
}
pub fn distribution(&self) -> FusionErasureDistribution {
let physical = self.physical_distribution();
if !self.shor_encoded {
return physical;
}
let mut probabilities = [0.; 4];
for pattern in 0usize..256 {
let masks: [usize; 4] = std::array::from_fn(|q| (pattern >> (2 * q)) & 3);
let weight: f64 = masks.iter().map(|&m| physical.probabilities[m]).product();
let x: [bool; 4] = masks.map(|m| m & 1 == 0);
let z: [bool; 4] = masks.map(|m| m & 2 == 0);
let known_x = (x[0] && x[2]) || (x[1] && x[3]);
let known_z = (z[0] || z[2]) && (z[1] || z[3]);
let standard = usize::from(!known_x) + 2 * usize::from(!known_z);
let dual = usize::from(!known_z) + 2 * usize::from(!known_x);
probabilities[standard] += weight / 2.;
probabilities[dual] += weight / 2.;
}
FusionErasureDistribution { probabilities }
}
pub fn photon_loss_at_marginal(&self, target: f64) -> Result<Option<f64>> {
if !target.is_finite() || !(0.0..=1.0).contains(&target) {
return Err(Error::InvalidValue(
"invalid marginal erasure target".into(),
));
}
let marginal = |loss: f64| {
let physical = 1.
- (1. - self.failure_probability() / 2.)
* (1. - loss).powi(i32::from(1u16 << (self.boost_level + 1)));
if self.shor_encoded {
((1. - (1. - physical).powi(2)).powi(2) + 1. - (1. - physical.powi(2)).powi(2)) / 2.
} else {
physical
}
};
if target < marginal(0.) {
return Ok(None);
}
let (mut lower, mut upper) = (0., 1.);
for _ in 0..64 {
let midpoint = (lower + upper) / 2.;
if marginal(midpoint) < target {
lower = midpoint;
} else {
upper = midpoint;
}
}
Ok(Some((lower + upper) / 2.))
}
}