use num_complex::Complex64;
use crate::pauli_sum::PauliSum;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PauliAxis {
X,
Y,
Z,
}
impl PauliAxis {
const fn bits(self) -> (bool, bool) {
match self {
PauliAxis::X => (true, false),
PauliAxis::Y => (true, true),
PauliAxis::Z => (false, true),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProductBasis<const W: usize> {
axis_x: [u64; W],
axis_z: [u64; W],
negative: [u64; W],
}
impl<const W: usize> ProductBasis<W> {
pub fn uniform(axis: PauliAxis) -> Self {
let (axis_x, axis_z) = axis.bits();
let all = |b: bool| if b { [!0u64; W] } else { [0u64; W] };
Self { axis_x: all(axis_x), axis_z: all(axis_z), negative: [0u64; W] }
}
pub fn from_axes<I>(axes: I) -> Self
where
I: IntoIterator<Item = (PauliAxis, bool)>,
{
let mut out = Self { axis_x: [0u64; W], axis_z: [0u64; W], negative: [0u64; W] };
for (q, (axis, minus)) in axes.into_iter().enumerate() {
assert!(q < 64 * W, "ProductBasis::from_axes: qubit {q} exceeds the {W}-word width",);
let word = q / 64;
let bit = 1u64 << (q % 64);
let (axis_x, axis_z) = axis.bits();
if axis_x {
out.axis_x[word] |= bit;
}
if axis_z {
out.axis_z[word] |= bit;
}
if minus {
out.negative[word] |= bit;
}
}
out
}
}
impl<const W: usize> PauliSum<W> {
pub fn expectation_product(&self, basis: &ProductBasis<W>) -> Complex64 {
self.sum_over_terms(|x, z, coeff| {
let mut mismatch = 0u64;
let mut sign_bits = 0u32;
for w in 0..W {
let support = x[w] | z[w];
mismatch |=
(x[w] ^ (support & basis.axis_x[w])) | (z[w] ^ (support & basis.axis_z[w]));
sign_bits += (support & basis.negative[w]).count_ones();
}
match (mismatch == 0, sign_bits & 1 == 0) {
(false, _) => Complex64::new(0.0, 0.0),
(true, true) => coeff,
(true, false) => -coeff,
}
})
}
}
#[cfg(test)]
mod tests;