macro_rules! const_pauli {
($($name:ident,)*) => {$(
#[doc = stringify!($name)]
const $name: Self;
)*};
}
macro_rules! new_pauli {
($(($name:ident, $gate:ident),)*) => {$(
/// Create a new
#[doc = stringify!($gate)]
fn $name() -> Self where Self: Sized {
Self::$gate
}
)*};
}
macro_rules! plus {
($(($name:ident, $left:ident, $right:ident),)*) => {$(
/// Add `other`'s
#[doc = stringify!($right)]
#[doc = stringify!($left)]
fn $name(&mut self, other: &Self);
)*};
}
pub trait Pauli {
const_pauli!(I, X, Y, Z,);
new_pauli!((new_i, I), (new_x, X), (new_y, Y), (new_z, Z),);
fn new_product(z: bool, x: bool) -> Self;
fn multiply(&mut self, other: Self)
where
Self: Sized,
{
#[allow(deprecated)]
self.add(other);
}
#[deprecated(since = "0.4.2", note = "use `multiply` instead")]
fn add(&mut self, other: Self);
#[inline(always)]
fn id(&mut self) {}
fn s(&mut self);
fn h(&mut self);
fn sh(&mut self) {
self.h();
self.s();
}
fn hs(&mut self) {
self.s();
self.h();
}
fn shs(&mut self) {
self.s();
self.h();
self.s();
}
plus!((xpx, X, X), (xpz, X, Z), (zpx, Z, X), (zpz, Z, Z),);
fn get_x(&self) -> bool;
fn get_z(&self) -> bool;
fn set_x(&mut self, x: bool);
fn set_z(&mut self, z: bool);
fn tableau_encoding(&self) -> u8;
}
mod dense;
pub use dense::PauliDense;
mod enumlike;
pub use enumlike::PauliEnum;
mod tuple;
pub use tuple::PauliTuple;
impl From<PauliEnum> for PauliDense {
fn from(pauli: PauliEnum) -> Self {
unsafe { Self::from_unchecked(pauli.discriminant()) }
}
}
impl From<PauliTuple> for PauliDense {
fn from(pauli: PauliTuple) -> Self {
Self::new_product(pauli.get_z(), pauli.get_x())
}
}
impl From<PauliDense> for PauliEnum {
fn from(pauli: PauliDense) -> Self {
pauli.storage().try_into().unwrap_or_else(|e| panic!("{e}"))
}
}
impl From<PauliTuple> for PauliEnum {
fn from(pauli: PauliTuple) -> Self {
Self::new_product(pauli.get_z(), pauli.get_x())
}
}
impl From<PauliDense> for PauliTuple {
fn from(pauli: PauliDense) -> Self {
Self::new_product(pauli.get_z(), pauli.get_x())
}
}
impl From<PauliEnum> for PauliTuple {
fn from(pauli: PauliEnum) -> Self {
Self::new_product(pauli.get_z(), pauli.get_x())
}
}
pub mod stack;
#[doc(inline)]
pub use stack::PauliStack;
pub mod tableau_encoding {
pub const I: u8 = 0;
pub const X: u8 = 2;
pub const Y: u8 = 3;
pub const Z: u8 = 1;
}
#[cfg(test)]
mod tests {
use std::fmt;
use coverage_helper::test;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PauliForceDefault(PauliEnum);
#[rustfmt::skip]
impl Pauli for PauliForceDefault {
const I: Self = Self(PauliEnum::I);
const X: Self = Self(PauliEnum::X);
const Y: Self = Self(PauliEnum::Y);
const Z: Self = Self(PauliEnum::Z);
fn new_product(z: bool, x: bool) -> Self { Self(PauliEnum::new_product(z, x)) }
fn multiply(&mut self, other: Self) { self.0.multiply(other.0) }
fn add(&mut self, other: Self) { self.multiply(other) }
fn s(&mut self) { self.0.s() }
fn h(&mut self) { self.0.h() }
fn xpx(&mut self, other: &Self) { self.0.xpx(&other.0) }
fn xpz(&mut self, other: &Self) { self.0.xpz(&other.0) }
fn zpx(&mut self, other: &Self) { self.0.zpx(&other.0) }
fn zpz(&mut self, other: &Self) { self.0.zpz(&other.0) }
fn get_x(&self) -> bool { self.0.get_x() }
fn get_z(&self) -> bool { self.0.get_z() }
fn set_x(&mut self, x: bool) { self.0.set_x(x) }
fn set_z(&mut self, z: bool) { self.0.set_z(z) }
fn tableau_encoding(&self) -> u8 { self.0.tableau_encoding() }
}
use super::*;
trait PauliAssert: Pauli + fmt::Debug + PartialEq + Copy {}
impl PauliAssert for PauliDense {}
impl PauliAssert for PauliEnum {}
impl PauliAssert for PauliTuple {}
impl PauliAssert for PauliForceDefault {}
macro_rules! check {
() => {
check::<PauliDense>();
check::<PauliEnum>();
check::<PauliTuple>();
check::<PauliForceDefault>();
};
(combinations) => {
check::<PauliDense, PauliEnum>();
check::<PauliDense, PauliTuple>();
check::<PauliEnum, PauliDense>();
check::<PauliEnum, PauliTuple>();
};
}
#[test]
fn consistency() {
fn check<T: PauliAssert>() {
let mapping = [
(T::I, &T::new_i as &dyn Fn() -> T, (false, false), tableau_encoding::I),
(T::Z, &T::new_z as &dyn Fn() -> T, (true, false), tableau_encoding::Z),
(T::X, &T::new_x as &dyn Fn() -> T, (false, true), tableau_encoding::X),
(T::Y, &T::new_y as &dyn Fn() -> T, (true, true), tableau_encoding::Y),
];
for (t_const, fun, (prod_z, prod_x), tableau_const) in mapping {
assert_eq!(t_const, fun());
assert_eq!(t_const, T::new_product(prod_z, prod_x));
assert_eq!(t_const.tableau_encoding(), tableau_const);
}
}
check!();
}
#[test]
fn conversions() {
fn check<A, B>()
where
A: PauliAssert + From<B>,
B: PauliAssert + From<A>,
{
for (a, b) in
[A::I, A::Z, A::X, A::Y].into_iter().zip([B::I, B::Z, B::X, B::Y])
{
assert_eq!(A::from(b), a);
assert_eq!(B::from(a), b);
}
}
check!(combinations);
}
#[test]
fn multiplication() {
fn check<T: PauliAssert>() {
let mapping = [
(T::I, T::I, T::I),
(T::I, T::Z, T::Z),
(T::I, T::X, T::X),
(T::I, T::Y, T::Y),
(T::Z, T::I, T::Z),
(T::Z, T::Z, T::I),
(T::Z, T::X, T::Y),
(T::Z, T::Y, T::X),
(T::X, T::I, T::X),
(T::X, T::Z, T::Y),
(T::X, T::X, T::I),
(T::X, T::Y, T::Z),
(T::Y, T::I, T::Y),
(T::Y, T::Z, T::X),
(T::Y, T::X, T::Z),
(T::Y, T::Y, T::I),
];
for (mut this, other, expected) in mapping {
this.multiply(other);
assert_eq!(this, expected);
}
}
check!();
}
#[test]
fn cliffords() {
fn check<T: PauliAssert>() {
#[rustfmt::skip]
let mapping = [
(&T::id as &dyn Fn(&mut T), [T::I, T::Z, T::X, T::Y]),
(&T::s as &dyn Fn(&mut T), [T::I, T::Z, T::Y, T::X]),
(&T::h as &dyn Fn(&mut T), [T::I, T::X, T::Z, T::Y]),
(&T::sh as &dyn Fn(&mut T), [T::I, T::Y, T::Z, T::X]),
(&T::hs as &dyn Fn(&mut T), [T::I, T::X, T::Y, T::Z]),
(&T::shs as &dyn Fn(&mut T), [T::I, T::Y, T::X, T::Z]),
];
for (fun, outputs) in mapping {
for (expected, mut input) in
outputs.into_iter().zip([T::I, T::Z, T::X, T::Y])
{
fun(&mut input);
assert_eq!(input, expected)
}
}
}
check!();
}
#[test]
fn get() {
fn check<T: PauliAssert>() {
#[rustfmt::skip]
let f = [
(T::I, false, false),
(T::Z, true, false),
(T::X, false, true),
(T::Y, true, true),
];
for (input, get_z, get_x) in f {
assert_eq!(input.get_z(), get_z);
assert_eq!(input.get_x(), get_x);
}
}
check!();
}
#[test]
fn set() {
fn check<T: PauliAssert>() {
let mapping = [
(
&T::set_z as &dyn Fn(&mut T, bool),
([(T::I, T::Z), (T::I, T::Z), (T::X, T::Y), (T::X, T::Y)]),
),
(
&T::set_x as &dyn Fn(&mut T, bool),
([(T::I, T::X), (T::Z, T::Y), (T::I, T::X), (T::Z, T::Y)]),
),
];
for (fun, outputs) in mapping.into_iter() {
for ((expected_false, expected_true), mut input) in
outputs.into_iter().zip([T::I, T::Z, T::X, T::Y])
{
let mut clone = input;
fun(&mut clone, false);
assert_eq!(clone, expected_false);
fun(&mut input, true);
assert_eq!(input, expected_true);
}
}
}
check!();
}
#[test]
fn partial_add() {
fn check<T: PauliAssert>() {
let funs = [
&T::zpz as &dyn Fn(&mut T, &T),
&T::zpx as &dyn Fn(&mut T, &T),
&T::xpz as &dyn Fn(&mut T, &T),
&T::xpx as &dyn Fn(&mut T, &T),
];
let mapping = [
(T::I, T::I, [T::I, T::I, T::I, T::I]),
(T::I, T::Z, [T::Z, T::I, T::X, T::I]),
(T::I, T::X, [T::I, T::Z, T::I, T::X]),
(T::I, T::Y, [T::Z, T::Z, T::X, T::X]),
(T::Z, T::I, [T::Z, T::Z, T::Z, T::Z]),
(T::Z, T::Z, [T::I, T::Z, T::Y, T::Z]),
(T::Z, T::X, [T::Z, T::I, T::Z, T::Y]),
(T::Z, T::Y, [T::I, T::I, T::Y, T::Y]),
(T::X, T::I, [T::X, T::X, T::X, T::X]),
(T::X, T::Z, [T::Y, T::X, T::I, T::X]),
(T::X, T::X, [T::X, T::Y, T::X, T::I]),
(T::X, T::Y, [T::Y, T::Y, T::I, T::I]),
(T::Y, T::I, [T::Y, T::Y, T::Y, T::Y]),
(T::Y, T::Z, [T::X, T::Y, T::Z, T::Y]),
(T::Y, T::X, [T::Y, T::X, T::Y, T::Z]),
(T::Y, T::Y, [T::X, T::X, T::Z, T::Z]),
];
for (this, other, outputs) in mapping {
for (fun, expected) in funs.iter().zip(outputs) {
let mut this = this;
fun(&mut this, &other);
assert_eq!(this, expected,);
}
}
}
check!();
}
}