use super::prepared::{PrepareToken, Prepared, PreparedRotation, MAX_LOCAL_SUPPORT};
use super::{Channel, OutputBuffer};
use crate::pauli_string::PauliString;
use crate::pauli_sum::hash::Gf2Hash;
use crate::phase::Phase;
use num_complex::Complex64;
#[derive(Clone, Debug)]
pub struct PauliRotation<const W: usize> {
generator: PauliString<W>,
theta: f64,
}
impl<const W: usize> PauliRotation<W> {
pub fn new(generator: PauliString<W>, theta: f64) -> Self {
Self { generator, theta }
}
pub fn generator(&self) -> PauliString<W> {
self.generator
}
pub fn theta(&self) -> f64 {
self.theta
}
pub fn weight(&self) -> usize {
self.generator.weight() as usize
}
fn apply_with_theta(
&self,
theta: f64,
input_x: &[u64; W],
input_z: &[u64; W],
coeff: Complex64,
out: &mut OutputBuffer<W>,
) {
let input = PauliString::<W> { x: *input_x, z: *input_z };
if input.commutes_with(&self.generator) {
out.push(*input_x, *input_z, coeff);
return;
}
let (sin, cos) = sin_cos(theta);
out.push(*input_x, *input_z, coeff * cos);
let mut product = input;
let phase = product.mul_assign(&self.generator);
let total_phase = Phase::I + phase;
out.push(product.x, product.z, total_phase.apply(coeff) * sin);
}
}
fn sin_cos(theta: f64) -> (f64, f64) {
const SNAP: f64 = 1e-15;
match theta.sin_cos() {
(s, c) if s.abs() < SNAP => (0.0, c.signum()),
(s, c) if c.abs() < SNAP => (s.signum(), 0.0),
sc => sc,
}
}
impl<const W: usize> Channel<W> for PauliRotation<W> {
fn support(&self) -> [u64; W] {
core::array::from_fn(|w| self.generator.x[w] | self.generator.z[w])
}
fn apply(
&self,
input_x: &[u64; W],
input_z: &[u64; W],
coeff: Complex64,
out: &mut OutputBuffer<W>,
) {
self.apply_with_theta(self.theta, input_x, input_z, coeff, out);
}
fn apply_adjoint(
&self,
input_x: &[u64; W],
input_z: &[u64; W],
coeff: Complex64,
out: &mut OutputBuffer<W>,
) {
self.apply_with_theta(-self.theta, input_x, input_z, coeff, out);
}
fn prepare_for_layer(
&self,
hash: &Gf2Hash<W>,
adjoint: bool,
_: PrepareToken,
) -> Option<Prepared<W>> {
if self.weight() <= MAX_LOCAL_SUPPORT {
return Prepared::derive_local(self, hash, adjoint);
}
let (sin, cos) = sin_cos(if adjoint { -self.theta } else { self.theta });
let generator = self.generator;
Some(Prepared::Rotation(PreparedRotation {
generator,
cos,
sin,
bucket_delta_identity: 0,
bucket_delta_generator: hash.bucket_of(&generator.x, &generator.z),
}))
}
}
#[cfg(test)]
mod tests;