use super::DualRail;
use crate::{ComplexTensor, DenseSimulator, DensityMatrix, Error, Matrix, Result};
use burn::tensor::Tensor;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Parity {
Even,
Odd,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FusionParities {
pub xx: Option<Parity>,
pub zz: Option<Parity>,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum FusionOutcome {
Success {
xx: Parity,
},
Failure {
bit: bool,
},
Loss,
}
impl FusionOutcome {
pub fn parities(self) -> FusionParities {
match self {
Self::Success { xx } => FusionParities {
xx: Some(xx),
zz: Some(Parity::Odd),
},
Self::Failure { .. } => FusionParities {
xx: None,
zz: Some(Parity::Even),
},
Self::Loss => FusionParities { xx: None, zz: None },
}
}
}
#[derive(Clone, Debug)]
pub struct FusionBranch {
pub(crate) outcomes: Vec<FusionOutcome>,
pub(crate) probability: Tensor<1>,
pub(crate) state: Option<DensityMatrix>,
}
impl FusionBranch {
pub fn outcomes(&self) -> &[FusionOutcome] {
&self.outcomes
}
pub fn probability(&self) -> Tensor<1> {
self.probability.clone()
}
pub fn unnormalized_state(&self) -> Option<&DensityMatrix> {
self.state.as_ref()
}
pub fn conditional_state(&self, min_probability: f64) -> Result<Option<DensityMatrix>> {
crate::error::tolerance(min_probability)?;
if crate::complex::read(self.probability())?
.iter()
.any(|p| !p.is_finite() || *p <= min_probability)
{
return Err(Error::InvalidValue(
"cannot condition on an impossible or too-small fusion branch".into(),
));
}
self.state
.as_ref()
.map(|state| {
let b = state.batch_size();
let probability = self.probability().reshape([b, 1, 1]);
let elements = crate::error::checked_product(state.entries().dims())?;
let limits = crate::ResourceLimits {
max_state_elements: elements,
..Default::default()
};
DensityMatrix::from_entries(
state.register().clone(),
ComplexTensor::new(
state.entries().real() / probability.clone(),
state.entries().imag() / probability,
)?,
limits,
)
})
.transpose()
}
}
pub struct StandardFusion;
impl StandardFusion {
pub fn branches(
engine: &DenseSimulator,
state: &DensityMatrix,
sites: [usize; 2],
) -> Result<Vec<FusionBranch>> {
DualRail::validate(state.register())?;
state.register().local_size(&sites)?;
let keep: Vec<_> = (0..state.register().wires())
.filter(|w| !sites.contains(w))
.collect();
let mut branches = Vec::with_capacity(5);
for outcome in [
FusionOutcome::Success { xx: Parity::Even },
FusionOutcome::Success { xx: Parity::Odd },
FusionOutcome::Failure { bit: false },
FusionOutcome::Failure { bit: true },
FusionOutcome::Loss,
] {
let projected = state.left_right(engine, &sites, &projector(outcome)?)?;
let probability = projected.trace();
let reduced = if keep.is_empty() {
None
} else {
Some(projected.reduced(&keep, engine.limits)?)
};
branches.push(FusionBranch {
outcomes: vec![outcome],
probability,
state: reduced,
});
}
Ok(branches)
}
}
fn projector(outcome: FusionOutcome) -> Result<Matrix> {
let mut data = vec![0.; 81];
match outcome {
FusionOutcome::Success { xx } => {
data[10] = 0.5;
data[3 * 9 + 3] = 0.5;
let sign = if xx == Parity::Even { 1. } else { -1. };
data[12] = sign * 0.5;
data[3 * 9 + 1] = sign * 0.5;
}
FusionOutcome::Failure { bit } => {
let index = if bit { 4 } else { 0 };
data[index * 9 + index] = 1.;
}
FusionOutcome::Loss => {
for a in 0..3 {
for b in 0..3 {
if a == 2 || b == 2 {
let index = a * 3 + b;
data[index * 9 + index] = 1.;
}
}
}
}
}
Matrix::real(9, data)
}