use crate::BraKet::{Bra, Ket};
use crate::ProNil::{Nilpotent, Projector};
use core::cmp::{max, min};
use core::hash::Hash;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use cpx_coords::FloatExt;
use cpx_coords::*;
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BraKet {
Bra,
Ket,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NormPair<T: FloatExt + Hash> {
pub c0: T,
pub c1: Cpx<T>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NormQubit<T: FloatExt + Hash> {
pub bra_ket: BraKet,
pub norm_pair: NormPair<T>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MixPair<T: FloatExt + Hash> {
pub prob: T,
pub norm_pair: NormPair<T>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MixQubit<T: FloatExt + Hash> {
pub bra_ket: BraKet,
pub mix_pair: MixPair<T>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IdxToNorm<T: FloatExt + Hash> {
pub interval: (usize, usize),
pub padding: NormPair<T>,
pub idx_to_norm: BTreeMap<usize, NormPair<T>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SepQubits<T: FloatExt + Hash> {
pub bra_ket: BraKet,
pub idx_to_norm: IdxToNorm<T>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IdxToMixQubits<T: FloatExt + Hash> {
pub bra_ket: BraKet,
pub interval: (usize, usize),
pub padding: MixPair<T>,
pub idx_to_mix: BTreeMap<usize, MixPair<T>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ProNil {
Projector,
Nilpotent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rk1PN<T: FloatExt + Hash> {
pub pro_nil: ProNil,
pub bra_ket: BraKet,
pub norm_pair: NormPair<T>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rk1KB<T: FloatExt + Hash> {
pub ket: NormPair<T>,
pub bra: NormPair<T>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Rank1<T: FloatExt + Hash> {
ProNil(Rk1PN<T>),
Other(Rk1KB<T>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RawMat<T: FloatExt + Hash> {
pub mat: [[Cpx<T>; 2]; 2],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CpxQuaternion<T: FloatExt + Hash> {
pub coefficients: [Cpx<T>; 4],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RealQuaternion<T: FloatExt + Hash> {
pub coefficients: [T; 4],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Det1CpxQuaternion<T: FloatExt + Hash> {
pub x: Cpx<T>,
pub y: Cpx<T>,
pub z: Cpx<T>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Det1RealQuaternion<T: FloatExt + Hash> {
pub x: T,
pub y: T,
pub z: T,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Det1<T: FloatExt + Hash> {
Hermitian(Det1RealQuaternion<T>),
Other(Det1CpxQuaternion<T>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RankOneTwo<T: FloatExt + Hash> {
Rank1(Rank1<T>),
Rank2(Det1<T>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LocalOps<T: FloatExt + Hash> {
pub interval: (usize, usize),
pub padding: RankOneTwo<T>,
pub idx_to_mat: BTreeMap<usize, RankOneTwo<T>>,
}
pub fn key_interval_or<K: Copy + Ord>(map: &BTreeMap<K, impl Sized>, default: (K, K)) -> (K, K) {
match (map.keys().next().copied(), map.keys().next_back().copied()) {
(Some(min), Some(max)) => (min, max),
_ => default,
}
}
pub fn swap_btree_entries<K: Ord + Copy, V>(map: &mut BTreeMap<K, V>, i: K, j: K) {
if i == j {
return;
}
let i_val = map.remove(&i);
let j_val = map.remove(&j);
if let Some(val) = j_val {
map.insert(i, val);
}
if let Some(val) = i_val {
map.insert(j, val);
}
}
impl BraKet {
pub fn dag(&self) -> Self {
match self {
Bra => Ket,
Ket => Bra,
}
}
}
impl<T: FloatExt + Hash> NormPair<T> {
pub fn conj(&self) -> Self {
Self {
c0: self.c0,
c1: self.c1.conj(),
}
}
pub fn orthogonal(&self) -> Self {
let (c1_mag, c1_phase) = self.c1.factor_out();
let new_c0 = c1_mag.re();
match new_c0 {
val if val == T::zero() => Self {
c0: T::zero(),
c1: Cpx::ONE,
},
val if val == T::one() => Self {
c0: T::one(),
c1: Cpx::ZERO,
},
_ => Self {
c0: new_c0,
c1: -c1_phase.conj() * self.c0,
},
}
}
pub fn try_factor_out(&self) -> Option<(Self, T, Cpx<T>)> {
let zero = T::zero();
let one = T::one();
match (self.c0 == zero, matches!(self.c1, Cpx::Zero {})) {
(true, true) => None,
(true, false) => {
let (sqrt_prob, ph) = self.c1.factor_out();
Some((
Self {
c0: zero,
c1: Cpx::ONE,
},
sqrt_prob.re(),
ph,
))
}
(false, true) => {
let c0_sign = if self.c0 > zero {
Cpx::ONE
} else {
Cpx::NEG_ONE
};
Some((
Self {
c0: one,
c1: Cpx::ZERO,
},
self.c0.abs(),
c0_sign,
))
}
(false, false) => {
let norm = self.c0.hypot(self.c1.rad());
let c0_sign = if self.c0 > zero { one } else { -one };
let c0_real = (self.c0 / norm).abs();
let c1_rel = self.c1 / norm * c0_sign;
Some((
Self {
c0: c0_real,
c1: c1_rel,
},
norm,
Cpx::Real { re: c0_sign },
))
}
}
}
}
impl<T: FloatExt + Hash> NormQubit<T> {
pub fn dag(&self) -> Self {
Self {
norm_pair: self.norm_pair.conj(),
bra_ket: self.bra_ket.dag(),
}
}
pub fn to_bra(&self) -> Self {
match self.bra_ket {
Bra => *self,
Ket => self.dag(),
}
}
pub fn to_ket(&self) -> Self {
match self.bra_ket {
Ket => *self,
Bra => self.dag(),
}
}
pub fn orthogonal(&self) -> Self {
Self {
bra_ket: self.bra_ket,
norm_pair: self.norm_pair.orthogonal(),
}
}
pub fn try_factor_out(&self) -> Option<(Self, T, Cpx<T>)> {
self.norm_pair
.try_factor_out()
.map(|(norm_pair, sqrt_prob, phase)| {
(
Self {
norm_pair,
bra_ket: self.bra_ket,
},
sqrt_prob,
phase,
)
})
}
pub fn inner(&self, ket: &Self) -> Cpx<T> {
let bra_pair = self.to_bra().norm_pair;
let ket_pair = ket.to_ket().norm_pair;
(bra_pair.c1 * ket_pair.c1) + (bra_pair.c0 * ket_pair.c0)
}
pub fn outer(&self, bra: &Self) -> Rank1<T> {
let ket = self.to_ket().norm_pair;
let bra = bra.to_bra().norm_pair;
let conj_ket = ket.conj();
if conj_ket == bra {
Rank1::ProNil(Rk1PN {
pro_nil: Projector,
bra_ket: Ket,
norm_pair: ket,
})
} else if conj_ket.orthogonal() == bra {
Rank1::ProNil(Rk1PN {
pro_nil: Nilpotent,
bra_ket: Ket,
norm_pair: ket,
})
} else {
Rank1::Other(Rk1KB { ket, bra })
}
}
}
impl<T: FloatExt + Hash> MixPair<T> {
pub fn conj(&self) -> Self {
Self {
prob: self.prob,
norm_pair: self.norm_pair.conj(),
}
}
pub fn orthogonal(&self) -> Self {
Self {
prob: self.prob,
norm_pair: self.norm_pair.orthogonal(),
}
}
pub fn purity(&self) -> T {
let p = self.prob;
p.powi(2) + (T::one() - p).powi(2)
}
pub fn entropy_vn(&self) -> T {
let zero = T::zero();
let one = T::one();
let p = self.prob;
if (p != one) && (p != zero) {
-p * p.ln() - (one - p) * (one - p).ln()
} else {
zero
}
}
}
impl<T: FloatExt + Hash> MixQubit<T> {
pub fn dag(&self) -> Self {
Self {
bra_ket: self.bra_ket.dag(),
mix_pair: self.mix_pair.conj(),
}
}
pub fn orthogonal(&self) -> Self {
Self {
bra_ket: self.bra_ket,
mix_pair: self.mix_pair.orthogonal(),
}
}
pub fn purity(&self) -> T {
self.mix_pair.purity()
}
pub fn entropy_vn(&self) -> T {
self.mix_pair.entropy_vn()
}
}
impl<T: FloatExt + Hash> IdxToNorm<T> {
pub fn conj(&self) -> Self {
Self {
interval: self.interval,
padding: self.padding.conj(),
idx_to_norm: self
.idx_to_norm
.iter()
.map(|(k, v)| (*k, v.conj()))
.collect(),
}
}
pub fn try_factor_out(&self) -> Option<(Self, T, Cpx<T>)> {
let mut idx_to_norm = BTreeMap::new();
let mut acc_sqrt_prob = T::one();
let mut acc_phase = Cpx::ONE;
for (&k, v) in &self.idx_to_norm {
let (canon, sqrt_prob, phase) = v.try_factor_out()?;
idx_to_norm.insert(k, canon);
acc_sqrt_prob = acc_sqrt_prob * sqrt_prob;
acc_phase *= phase;
}
Some((
Self {
interval: self.interval,
padding: self.padding,
idx_to_norm,
},
acc_sqrt_prob,
acc_phase,
))
}
pub fn indices_keys(&self) -> Vec<usize> {
self.idx_to_norm.keys().copied().collect()
}
pub fn try_interval(&self) -> (Option<usize>, Option<usize>) {
let min = self.idx_to_norm.keys().next().copied();
let max = self.idx_to_norm.keys().next_back().copied();
(min, max)
}
pub fn try_fit_interval(&self) -> Option<Self> {
let (min, max) = self.try_interval();
Some(Self {
interval: (min?, max?),
padding: self.padding,
idx_to_norm: self.idx_to_norm.clone(),
})
}
pub fn extract_padding(&self) -> Self {
let mut idx_to_norm = self.idx_to_norm.clone();
let (start, end) = self.interval;
for i in start..=end {
idx_to_norm.entry(i).or_insert(self.padding);
}
Self {
interval: self.interval,
padding: self.padding,
idx_to_norm,
}
}
pub fn set_interval(&self, interval: (usize, usize)) -> Self {
Self {
interval,
padding: self.padding,
idx_to_norm: self.idx_to_norm.clone(),
}
}
pub fn set_padding(&self, padding: NormPair<T>) -> Self {
Self {
interval: self.interval,
padding,
idx_to_norm: self.idx_to_norm.clone(),
}
}
pub fn discard<I: IntoIterator<Item = usize>>(&self, indices: I) -> Self {
let mut new_map = self.idx_to_norm.clone();
for idx in indices {
new_map.remove(&idx);
}
Self {
interval: self.interval,
padding: self.padding,
idx_to_norm: new_map,
}
}
pub fn extend(&self, idx_to_norm: BTreeMap<usize, NormPair<T>>) -> Self {
let mut new_map = self.idx_to_norm.clone();
for (k, v) in idx_to_norm {
new_map.entry(k).or_insert(v);
}
let interval = key_interval_or(&new_map, self.interval);
Self {
interval,
padding: self.padding,
idx_to_norm: new_map,
}
}
pub fn force_update(&self, idx_to_norm: BTreeMap<usize, NormPair<T>>) -> Self {
let mut new_map = self.idx_to_norm.clone();
for (k, v) in idx_to_norm {
new_map.insert(k, v); }
let interval = key_interval_or(&new_map, self.interval);
Self {
interval,
padding: self.padding,
idx_to_norm: new_map,
}
}
pub fn swap(&self, i: usize, j: usize) -> Self {
if i == j {
return self.clone(); }
let mut new_map = self.idx_to_norm.clone();
swap_btree_entries(&mut new_map, i, j);
let interval = key_interval_or(&new_map, self.interval);
Self {
interval,
padding: self.padding,
idx_to_norm: new_map,
}
}
}
impl<T: FloatExt + Hash> SepQubits<T> {
pub fn dag(&self) -> Self {
Self {
bra_ket: self.bra_ket.dag(),
idx_to_norm: self.idx_to_norm.conj(),
}
}
pub fn to_bra(&self) -> Self {
match self.bra_ket {
Bra => self.clone(),
Ket => self.dag(),
}
}
pub fn to_ket(&self) -> Self {
match self.bra_ket {
Ket => self.clone(),
Bra => self.dag(),
}
}
pub fn try_factor_out(&self) -> Option<(Self, T, Cpx<T>)> {
let (idx_to_norm, sqrt_prob, phase) = self.idx_to_norm.try_factor_out()?;
Some((
Self {
bra_ket: self.bra_ket,
idx_to_norm,
},
sqrt_prob,
phase,
))
}
pub fn inner(&self, ket: &Self) -> Cpx<T> {
let bra = self.to_bra().idx_to_norm;
let ket = ket.to_ket().idx_to_norm;
let (min, max) = {
let (bra_min, bra_max) = bra.interval;
let (ket_min, ket_max) = ket.interval;
(min(bra_min, ket_min), max(bra_max, ket_max))
};
let bra = bra.set_interval((min, max)).extract_padding();
let ket = ket.set_interval((min, max)).extract_padding();
let mut acc = Cpx::ONE;
for idx in min..max {
let NormPair { c0: b0, c1: b1 } = bra.idx_to_norm[&idx];
let NormPair { c0: k0, c1: k1 } = ket.idx_to_norm[&idx];
let inner = b1 * k1 + b0 * k0;
if matches!(inner, Cpx::Zero {}) {
return Cpx::ZERO;
} else if matches!(inner, Cpx::One {}) {
continue;
}
acc *= inner;
}
acc
}
pub fn indices_keys(&self) -> Vec<usize> {
self.idx_to_norm.indices_keys()
}
pub fn try_interval(&self) -> (Option<usize>, Option<usize>) {
let min = self.idx_to_norm.idx_to_norm.keys().next().copied();
let max = self.idx_to_norm.idx_to_norm.keys().next_back().copied();
(min, max)
}
pub fn try_fit_interval(&self) -> Option<Self> {
self.idx_to_norm.try_fit_interval().map(|idx_to_norm| Self {
bra_ket: self.bra_ket,
idx_to_norm,
})
}
pub fn extract_padding(&self) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.extract_padding(),
}
}
pub fn set_interval(&self, interval: (usize, usize)) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.set_interval(interval),
}
}
pub fn set_padding(&self, padding: NormPair<T>) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.set_padding(padding),
}
}
pub fn discard<I: IntoIterator<Item = usize>>(&self, indices: I) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.discard(indices),
}
}
pub fn extend(&self, idx_to_norm: BTreeMap<usize, NormPair<T>>) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.extend(idx_to_norm),
}
}
pub fn force_update(&self, idx_to_norm: BTreeMap<usize, NormPair<T>>) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.force_update(idx_to_norm),
}
}
pub fn swap(&self, i: usize, j: usize) -> Self {
Self {
bra_ket: self.bra_ket,
idx_to_norm: self.idx_to_norm.swap(i, j),
}
}
}
impl<T: FloatExt + Hash> IdxToMixQubits<T> {
pub fn dag(&self) -> Self {
Self {
bra_ket: self.bra_ket.dag(),
interval: self.interval,
padding: self.padding.conj(),
idx_to_mix: self
.idx_to_mix
.iter()
.map(|(k, v)| (*k, v.conj()))
.collect(),
}
}
pub fn indices_keys(&self) -> Vec<usize> {
self.idx_to_mix.keys().copied().collect()
}
pub fn try_interval(&self) -> (Option<usize>, Option<usize>) {
let min = self.idx_to_mix.keys().next().copied();
let max = self.idx_to_mix.keys().next_back().copied();
(min, max)
}
pub fn try_fit_interval(&self) -> Option<Self> {
let (min, max) = self.try_interval();
Some(Self {
bra_ket: self.bra_ket,
interval: (min?, max?),
padding: self.padding,
idx_to_mix: self.idx_to_mix.clone(),
})
}
pub fn extract_padding(&self) -> Self {
let mut idx_to_mix = self.idx_to_mix.clone();
let (start, end) = self.interval;
for i in start..=end {
idx_to_mix.entry(i).or_insert(self.padding);
}
Self {
bra_ket: self.bra_ket,
interval: self.interval,
padding: self.padding,
idx_to_mix,
}
}
pub fn set_interval(&self, interval: (usize, usize)) -> Self {
Self {
bra_ket: self.bra_ket,
interval,
padding: self.padding,
idx_to_mix: self.idx_to_mix.clone(),
}
}
pub fn set_padding(&self, padding: MixPair<T>) -> Self {
Self {
bra_ket: self.bra_ket,
interval: self.interval,
padding,
idx_to_mix: self.idx_to_mix.clone(),
}
}
pub fn discard<I: IntoIterator<Item = usize>>(&self, indices: I) -> Self {
let mut new_map = self.idx_to_mix.clone();
for idx in indices {
new_map.remove(&idx);
}
Self {
bra_ket: self.bra_ket,
interval: self.interval,
padding: self.padding,
idx_to_mix: new_map,
}
}
pub fn extend(&self, idx_to_mix: BTreeMap<usize, MixPair<T>>) -> Self {
let mut new_map = self.idx_to_mix.clone();
for (k, v) in idx_to_mix {
new_map.entry(k).or_insert(v);
}
let interval = key_interval_or(&new_map, self.interval);
Self {
bra_ket: self.bra_ket,
interval,
padding: self.padding,
idx_to_mix: new_map,
}
}
pub fn force_update(&self, idx_to_mix: BTreeMap<usize, MixPair<T>>) -> Self {
let mut new_map = self.idx_to_mix.clone();
for (k, v) in idx_to_mix {
new_map.insert(k, v); }
let interval = key_interval_or(&new_map, self.interval);
Self {
bra_ket: self.bra_ket,
interval,
padding: self.padding,
idx_to_mix: new_map,
}
}
pub fn swap(&self, i: usize, j: usize) -> Self {
if i == j {
return self.clone(); }
let mut new_map = self.idx_to_mix.clone();
swap_btree_entries(&mut new_map, i, j);
let interval = key_interval_or(&new_map, self.interval);
Self {
bra_ket: self.bra_ket,
interval,
padding: self.padding,
idx_to_mix: new_map,
}
}
}
impl<T: FloatExt + Hash> Rk1PN<T> {
pub fn try_factor_out(self) -> Option<(Self, T)> {
self.norm_pair
.try_factor_out()
.map(|(norm, sqrt_prob, ..)| {
(
Self {
bra_ket: self.bra_ket,
norm_pair: norm,
pro_nil: self.pro_nil,
},
sqrt_prob.powi(2),
)
})
}
pub fn dag(self) -> Self {
match self.pro_nil {
Nilpotent => {
let norm_pair = self.norm_pair.orthogonal();
Self {
bra_ket: self.bra_ket,
norm_pair,
pro_nil: Nilpotent,
}
}
Projector => self,
}
}
pub fn trace(self) -> Cpx<T> {
let NormPair { c0, c1 } = self.norm_pair;
let dual = match self.pro_nil {
Projector => self.norm_pair.conj(),
Nilpotent => self.norm_pair.orthogonal().conj(),
};
Cpx::Real { re: c0 * dual.c0 } + c1 * dual.c1
}
}
impl<T: FloatExt + Hash> Rk1KB<T> {
pub fn try_factor_out(self) -> Option<(Self, T, Cpx<T>)> {
let ket_factored = self.ket.try_factor_out();
let bra_factored = self.bra.try_factor_out();
match (ket_factored, bra_factored) {
(Some((ket_norm, sqrt_ket, ph_ket)), Some((bra_norm, sqrt_bra, ph_bra))) => {
let sqrt_prob = sqrt_ket * sqrt_bra;
let phase = ph_ket * ph_bra;
Some((
Self {
ket: ket_norm,
bra: bra_norm,
},
sqrt_prob,
phase,
))
}
_ => None,
}
}
pub fn dag(self) -> Self {
Self {
ket: self.bra.conj(),
bra: self.ket.conj(),
}
}
pub fn trace(self) -> Cpx<T> {
let NormPair { c0: k0, c1: k1 } = self.ket;
let NormPair { c0: b0, c1: b1 } = self.bra;
Cpx::Real { re: k0 * b0 } + k1 * b1
}
}
impl<T: FloatExt + Hash> Rank1<T> {
pub fn try_factor_out(self) -> Option<(Self, T, Option<Cpx<T>>)> {
match self {
Self::ProNil(pn) => pn
.try_factor_out()
.map(|(canon, prob)| (Self::ProNil(canon), prob, None)),
Self::Other(other) => other
.try_factor_out()
.map(|(canon, prob, phase)| (Self::Other(canon), prob, Some(phase))),
}
}
pub fn dag(self) -> Self {
match self {
Rank1::ProNil(pn) => Rank1::ProNil(pn.dag()),
Rank1::Other(other) => Rank1::Other(other.dag()),
}
}
pub fn trace(self) -> Cpx<T> {
match self {
Rank1::ProNil(pn) => pn.trace(),
Rank1::Other(other) => other.trace(),
}
}
}
impl<T: FloatExt + Hash> RawMat<T> {
pub fn from_rank1(rk1: Rank1<T>) -> Self {
let build_outer = |k0: Cpx<T>, k1: Cpx<T>, b0: Cpx<T>, b1: Cpx<T>| RawMat {
mat: [[k0 * b0, k0 * b1], [k1 * b0, k1 * b1]],
};
let (ket, bra) = match rk1 {
Rank1::ProNil(pn) => {
let dual = pn.norm_pair.conj();
let dual_out = match pn.pro_nil {
Projector => dual,
Nilpotent => dual.orthogonal(),
};
match pn.bra_ket {
Ket => (pn.norm_pair, dual_out),
Bra => (dual_out, pn.norm_pair),
}
}
Rank1::Other(other) => (other.ket, other.bra),
};
build_outer(
Cpx::Real { re: ket.c0 },
ket.c1,
Cpx::Real { re: bra.c0 },
bra.c1,
)
}
pub fn from_mixed_qubit(mixed: MixQubit<T>) -> Self {
let prob = mixed.mix_pair.prob;
let comp_prob = T::one() - prob;
let norm = mixed.mix_pair.norm_pair;
let bra_ket = mixed.bra_ket;
let mk_proj = |norm_pair| {
Self::from_rank1(Rank1::ProNil(Rk1PN {
bra_ket,
norm_pair,
pro_nil: Projector,
}))
};
let rho1 = mk_proj(norm) * prob;
let rho2 = mk_proj(norm.orthogonal()) * comp_prob;
rho1 + rho2
}
pub fn from_cpx_quaternion(cq: CpxQuaternion<T>) -> Self {
let [c0, c1, c2, c3] = cq.coefficients;
let mat = [[c0 + c3, c1 - c2 * Cpx::J], [c1 + c2 * Cpx::J, c0 - c3]];
Self { mat }
}
pub fn from_real_quaternion(rq: RealQuaternion<T>) -> Self {
Self::from_cpx_quaternion(CpxQuaternion::from_real_quaternion(rq))
}
pub fn from_det1_cpx_quaternion(d1cq: Det1CpxQuaternion<T>) -> Self {
Self::from_cpx_quaternion(CpxQuaternion::from_det1_cpx_quaternion(d1cq))
}
pub fn from_det1_real_quaternion(d1rq: Det1RealQuaternion<T>) -> Self {
Self::from_real_quaternion(RealQuaternion::from_det1_real_quaternion(d1rq))
}
pub fn dag(self) -> Self {
let m = self.mat;
Self {
mat: [
[m[0][0].conj(), m[1][0].conj()],
[m[0][1].conj(), m[1][1].conj()],
],
}
}
pub fn trace(self) -> Cpx<T> {
let m = self.mat;
m[0][0] + m[1][1]
}
pub fn det(self) -> Cpx<T> {
let m = self.mat;
(m[0][0] * m[1][1]) - (m[0][1] * m[1][0])
}
pub fn is_zero(&self) -> bool {
self.mat.iter().flatten().all(|c| matches!(c, Cpx::Zero {}))
}
pub fn rank(&self) -> u8 {
if self.is_zero() {
0
} else if matches!(self.det(), Cpx::Zero {}) {
1
} else {
2
}
}
}
impl<T: FloatExt + Hash> CpxQuaternion<T> {
pub fn from_raw_mat(raw: RawMat<T>) -> Self {
let half = T::from(0.5).unwrap();
let [[a, b], [c, d]] = raw.mat;
let coefficients = [
(a + d) * half, (b + c) * half, (b - c) * half * Cpx::J, (a - d) * half, ];
Self { coefficients }
}
pub fn from_real_quaternion(rq: RealQuaternion<T>) -> Self {
let mut coefficients = [Cpx::ZERO; 4];
for (i, &real) in rq.coefficients.iter().enumerate() {
coefficients[i] = Cpx::Real { re: real };
}
Self { coefficients }
}
pub fn from_det1_cpx_quaternion(d1cq: Det1CpxQuaternion<T>) -> Self {
let x2 = d1cq.x.powi(2);
let y2 = d1cq.y.powi(2);
let z2 = d1cq.z.powi(2);
let a0 = (x2 + y2 + z2 + Cpx::ONE).powf(T::from(0.5).unwrap());
let coefficients = [a0, d1cq.x, d1cq.y, d1cq.z];
Self { coefficients }
}
pub fn nonzero_count(&self) -> usize {
self.coefficients
.iter()
.filter(|c| !matches!(c, Cpx::Zero {}))
.count()
}
pub fn is_ixyz(&self) -> bool {
self.nonzero_count() == 1
}
pub fn dag(&self) -> Self {
let mut coefficients = self.coefficients;
for elem in &mut coefficients {
*elem = elem.conj();
}
Self { coefficients }
}
pub fn trace(&self) -> Cpx<T> {
self.coefficients[0] * T::from(2.0).unwrap()
}
pub fn det(&self) -> Cpx<T> {
let [a0, a1, a2, a3] = self.coefficients;
a0.powi(2) - a1.powi(2) - a2.powi(2) - a3.powi(2)
}
}
impl<T: FloatExt + Hash> RealQuaternion<T> {
pub fn from_det1_real_quaternion(d1rq: Det1RealQuaternion<T>) -> Self {
let x2 = d1rq.x.powi(2);
let y2 = d1rq.y.powi(2);
let z2 = d1rq.z.powi(2);
let a0 = (x2 + y2 + z2 + T::one()).sqrt();
let coefficients = [a0, d1rq.x, d1rq.y, d1rq.z];
Self { coefficients }
}
pub fn nonzero_count(&self) -> usize {
self.coefficients
.iter()
.filter(|c| **c != T::zero())
.count()
}
pub fn is_ixyz(&self) -> bool {
self.nonzero_count() == 1
}
pub fn trace(&self) -> T {
self.coefficients[0] * T::from(2.0).unwrap()
}
pub fn det(&self) -> T {
let [a0, a1, a2, a3] = self.coefficients;
a0.powi(2) - a1.powi(2) - a2.powi(2) - a3.powi(2)
}
}
impl<T: FloatExt + Hash> Det1CpxQuaternion<T> {
pub fn from_real(d1rq: Det1RealQuaternion<T>) -> Self {
Self {
x: Cpx::Real { re: d1rq.x },
y: Cpx::Real { re: d1rq.y },
z: Cpx::Real { re: d1rq.z },
}
}
pub fn a0(&self) -> Cpx<T> {
(Cpx::ONE + self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).powf(T::from(0.5).unwrap())
}
pub fn dag(&self) -> Self {
let x = self.x.conj();
let y = self.y.conj();
let z = self.z.conj();
Self { x, y, z }
}
pub fn trace(&self) -> Cpx<T> {
self.a0() * T::from(2.0).unwrap()
}
pub fn is_ixyz(self) -> bool {
CpxQuaternion::from_det1_cpx_quaternion(self).nonzero_count() == 1
}
}
impl<T: FloatExt + Hash> Det1RealQuaternion<T> {
pub fn a0(&self) -> T {
(T::one() + self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).powf(T::from(0.5).unwrap())
}
pub fn trace(&self) -> T {
self.a0() * T::from(2.0).unwrap()
}
pub fn is_ixyz(self) -> bool {
RealQuaternion::from_det1_real_quaternion(self).nonzero_count() == 1
}
}
impl<T: FloatExt + Hash> Det1<T> {
pub fn trace(&self) -> Cpx<T> {
match self {
Self::Hermitian(d1rq) => Cpx::Real { re: d1rq.trace() },
Self::Other(d1cq) => d1cq.trace(),
}
}
pub fn dag(&self) -> Self {
match self {
Self::Hermitian(d1rq) => Self::Hermitian(*d1rq),
Self::Other(d1cq) => Self::Other(d1cq.dag()),
}
}
pub fn is_ixyz(self) -> bool {
match self {
Self::Hermitian(d1rq) => d1rq.is_ixyz(),
Self::Other(d1cq) => d1cq.is_ixyz(),
}
}
}
impl<T: FloatExt + Hash> RankOneTwo<T> {
pub fn dag(self) -> Self {
match self {
Self::Rank1(rk1) => Self::Rank1(rk1.dag()),
Self::Rank2(det1) => Self::Rank2(det1.dag()),
}
}
pub fn trace(self) -> Cpx<T> {
match self {
Self::Rank1(rk1) => rk1.trace(),
Self::Rank2(det1) => det1.trace(),
}
}
pub fn det(self) -> Cpx<T> {
match self {
Self::Rank1(..) => Cpx::ZERO,
Self::Rank2(..) => Cpx::ONE,
}
}
pub fn rank(self) -> usize {
match self {
Self::Rank1(..) => 1,
Self::Rank2(..) => 2,
}
}
}
impl<T: FloatExt + Hash> LocalOps<T> {
pub fn indices_keys(&self) -> Vec<usize> {
self.idx_to_mat.keys().copied().collect()
}
pub fn try_interval(&self) -> (Option<usize>, Option<usize>) {
let min = self.idx_to_mat.keys().next().copied();
let max = self.idx_to_mat.keys().next_back().copied();
(min, max)
}
pub fn try_fit_interval(&self) -> Option<Self> {
let (min, max) = self.try_interval();
Some(Self {
interval: (min?, max?),
padding: self.padding,
idx_to_mat: self.idx_to_mat.clone(),
})
}
pub fn extract_padding(&self) -> Self {
let mut idx_to_mat = self.idx_to_mat.clone();
let (start, end) = self.interval;
for i in start..=end {
idx_to_mat.entry(i).or_insert(self.padding);
}
Self {
interval: self.interval,
padding: self.padding,
idx_to_mat,
}
}
pub fn set_interval(&self, interval: (usize, usize)) -> Self {
Self {
interval,
padding: self.padding,
idx_to_mat: self.idx_to_mat.clone(),
}
}
pub fn set_padding(&self, padding: RankOneTwo<T>) -> Self {
Self {
interval: self.interval,
padding,
idx_to_mat: self.idx_to_mat.clone(),
}
}
pub fn discard<I: IntoIterator<Item = usize>>(&self, indices: I) -> Self {
let mut new_map = self.idx_to_mat.clone();
for idx in indices {
new_map.remove(&idx);
}
Self {
interval: self.interval,
padding: self.padding,
idx_to_mat: new_map,
}
}
pub fn extend(&self, idx_to_mat: BTreeMap<usize, RankOneTwo<T>>) -> Self {
let mut new_map = self.idx_to_mat.clone();
for (k, v) in idx_to_mat.iter() {
new_map.entry(*k).or_insert(*v);
}
let interval = key_interval_or(&new_map, self.interval);
Self {
interval,
padding: self.padding,
idx_to_mat: new_map,
}
}
pub fn force_update(&self, idx_to_mat: BTreeMap<usize, RankOneTwo<T>>) -> Self {
let mut new_map = self.idx_to_mat.clone();
for (k, v) in idx_to_mat {
new_map.insert(k, v); }
let interval = key_interval_or(&new_map, self.interval);
Self {
interval,
padding: self.padding,
idx_to_mat: new_map,
}
}
pub fn swap(&self, i: usize, j: usize) -> Self {
if i == j {
return self.clone(); }
let mut new_map = self.idx_to_mat.clone();
swap_btree_entries(&mut new_map, i, j);
let interval = key_interval_or(&new_map, self.interval);
Self {
interval,
padding: self.padding,
idx_to_mat: new_map,
}
}
}
impl<T: FloatExt + Hash> Add for RawMat<T> {
type Output = Self;
fn add(self, other: Self) -> Self {
let mut mat = self.mat;
for (i, row) in mat.iter_mut().enumerate() {
for (j, elem) in row.iter_mut().enumerate() {
*elem += other.mat[i][j];
}
}
Self { mat }
}
}
impl<T: FloatExt + Hash> AddAssign for RawMat<T> {
fn add_assign(&mut self, other: Self) {
for (i, row) in self.mat.iter_mut().enumerate() {
for (j, elem) in row.iter_mut().enumerate() {
*elem += other.mat[i][j];
}
}
}
}
impl<T: FloatExt + Hash> Neg for RawMat<T> {
type Output = Self;
fn neg(self) -> Self {
let mut mat = self.mat;
for row in mat.iter_mut() {
for elem in row.iter_mut() {
*elem = -*elem;
}
}
Self { mat }
}
}
impl<T: FloatExt + Hash> Sub for RawMat<T> {
type Output = Self;
fn sub(self, other: Self) -> Self {
self + (-other)
}
}
impl<T: FloatExt + Hash> SubAssign for RawMat<T> {
fn sub_assign(&mut self, other: Self) {
*self += -other;
}
}
impl<T: FloatExt + Hash> Mul for RawMat<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let [[a, b], [c, d]] = self.mat;
let [[e, f], [g, h]] = rhs.mat;
Self {
mat: [
[a * e + b * g, a * f + b * h],
[c * e + d * g, c * f + d * h],
],
}
}
}
impl<T: FloatExt + Hash> MulAssign for RawMat<T> {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T: FloatExt + Hash> Mul<T> for RawMat<T> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
let mut mat = self.mat;
for row in mat.iter_mut() {
for elem in row.iter_mut() {
*elem *= rhs;
}
}
Self { mat }
}
}
impl<T: FloatExt + Hash> MulAssign<T> for RawMat<T> {
fn mul_assign(&mut self, rhs: T) {
*self = *self * rhs;
}
}
impl<T: FloatExt + Hash> Mul<Cpx<T>> for RawMat<T> {
type Output = Self;
fn mul(self, rhs: Cpx<T>) -> Self::Output {
let mut mat = self.mat;
for row in mat.iter_mut() {
for elem in row.iter_mut() {
*elem *= rhs;
}
}
Self { mat }
}
}
impl<T: FloatExt + Hash> MulAssign<Cpx<T>> for RawMat<T> {
fn mul_assign(&mut self, rhs: Cpx<T>) {
*self = *self * rhs;
}
}
impl<T: FloatExt + Hash> Add for CpxQuaternion<T> {
type Output = Self;
fn add(self, other: Self) -> Self {
let mut coefficients = self.coefficients;
for (i, elem) in coefficients.iter_mut().enumerate() {
*elem += other.coefficients[i];
}
Self { coefficients }
}
}
impl<T: FloatExt + Hash> AddAssign for CpxQuaternion<T> {
fn add_assign(&mut self, other: Self) {
let mut coefficients = self.coefficients;
for (i, elem) in coefficients.iter_mut().enumerate() {
*elem += other.coefficients[i];
}
}
}
impl<T: FloatExt + Hash> Neg for CpxQuaternion<T> {
type Output = Self;
fn neg(self) -> Self::Output {
let mut coefficients = self.coefficients;
for elem in coefficients.iter_mut() {
*elem = -*elem;
}
Self { coefficients }
}
}
impl<T: FloatExt + Hash> Sub for CpxQuaternion<T> {
type Output = Self;
fn sub(self, other: Self) -> Self {
self + (-other)
}
}
impl<T: FloatExt + Hash> SubAssign for CpxQuaternion<T> {
fn sub_assign(&mut self, other: Self) {
*self += -other;
}
}
impl<T: FloatExt + Hash> Mul for CpxQuaternion<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let mat_lhs = RawMat::from_cpx_quaternion(self);
let mat_rhs = RawMat::from_cpx_quaternion(rhs);
let mat_new = mat_lhs * mat_rhs;
Self::from_raw_mat(mat_new)
}
}
impl<T: FloatExt + Hash> MulAssign for CpxQuaternion<T> {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T: FloatExt + Hash> Mul<T> for CpxQuaternion<T> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
let mut coefficients = self.coefficients;
for elem in coefficients.iter_mut() {
*elem *= rhs;
}
Self { coefficients }
}
}
impl<T: FloatExt + Hash> MulAssign<T> for CpxQuaternion<T> {
fn mul_assign(&mut self, rhs: T) {
*self = *self * rhs;
}
}
impl<T: FloatExt + Hash> Mul<Cpx<T>> for CpxQuaternion<T> {
type Output = Self;
fn mul(self, rhs: Cpx<T>) -> Self::Output {
let mut coefficients = self.coefficients;
for elem in coefficients.iter_mut() {
*elem *= rhs;
}
Self { coefficients }
}
}
impl<T: FloatExt + Hash> MulAssign<Cpx<T>> for CpxQuaternion<T> {
fn mul_assign(&mut self, rhs: Cpx<T>) {
*self = *self * rhs;
}
}