pub(crate) mod prepared;
pub(crate) mod ptm;
pub(crate) mod rotation;
pub use ptm::PtmChannel;
pub use rotation::PauliRotation;
pub(crate) use prepared::Prepare;
use crate::pauli_string::qubit_mask;
use crate::pauli_sum::hash::Gf2Hash;
use num_complex::Complex64;
use prepared::{PrepareToken, Prepared};
pub struct OutputBuffer<const W: usize> {
x: Vec<[u64; W]>,
z: Vec<[u64; W]>,
coeff: Vec<Complex64>,
}
impl<const W: usize> OutputBuffer<W> {
pub fn push(&mut self, x: [u64; W], z: [u64; W], coeff: Complex64) {
self.x.push(x);
self.z.push(z);
self.coeff.push(coeff);
}
pub(crate) fn new() -> Self {
Self { x: Vec::new(), z: Vec::new(), coeff: Vec::new() }
}
pub(crate) fn clear(&mut self) {
self.x.clear();
self.z.clear();
self.coeff.clear();
}
pub(crate) fn terms(
&self,
) -> impl ExactSizeIterator<Item = ([u64; W], [u64; W], Complex64)> + '_ {
self.x.iter().zip(&self.z).zip(&self.coeff).map(|((&x, &z), &coeff)| (x, z, coeff))
}
}
pub fn support_mask<const W: usize>(qubits: &[usize]) -> [u64; W] {
qubit_mask(qubits.iter().copied(), 64 * W)
}
fn qubit_loc(q: usize) -> (usize, usize, u64) {
let word = q / 64;
let bit = q % 64;
(word, bit, 1u64 << bit)
}
fn read_pauli<const W: usize>(x: &[u64; W], z: &[u64; W], word: usize, bit: usize) -> usize {
let x_bit = (x[word] >> bit) & 1;
let z_bit = (z[word] >> bit) & 1;
(x_bit | (z_bit << 1)) as usize
}
fn write_pauli<const W: usize>(
x: &mut [u64; W],
z: &mut [u64; W],
word: usize,
bit: usize,
mask: u64,
p: usize,
) {
let x_bit = (p & 1) as u64;
let z_bit = ((p >> 1) & 1) as u64;
x[word] = (x[word] & !mask) | (x_bit << bit);
z[word] = (z[word] & !mask) | (z_bit << bit);
}
pub trait Channel<const W: usize>: Send + Sync {
fn support(&self) -> [u64; W];
fn debug_name(&self) -> &'static str {
let full = core::any::type_name::<Self>();
let head = match full.find('<') {
Some(i) => &full[..i],
None => full,
};
match head.rfind("::") {
Some(i) => &head[i + 2..],
None => head,
}
}
fn apply(
&self,
input_x: &[u64; W],
input_z: &[u64; W],
coeff: Complex64,
out: &mut OutputBuffer<W>,
);
fn apply_adjoint(
&self,
input_x: &[u64; W],
input_z: &[u64; W],
coeff: Complex64,
out: &mut OutputBuffer<W>,
);
#[doc(hidden)]
fn prepare_for_layer(
&self,
hash: &Gf2Hash<W>,
adjoint: bool,
_: PrepareToken,
) -> Option<Prepared<W>> {
Prepared::derive_local(self, hash, adjoint)
}
}
#[cfg(test)]
mod tests;