use std::{
fmt::Display,
ops::{Mul, MulAssign},
};
use rand::distr::{Distribution, StandardUniform};
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub enum Pauli {
#[default]
I,
X,
Z,
Y,
}
impl Pauli {
pub fn anticommuting(&self) -> Option<(Self, Self)> {
match self {
Self::I => None,
Self::X => Some((Self::Z, Self::Y)),
Self::Z => Some((Self::X, Self::Y)),
Self::Y => Some((Self::X, Self::Z)),
}
}
}
impl Display for Pauli {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Distribution<Pauli> for StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Pauli {
let i = rng.random_range(0..=3);
match i {
0 => Pauli::I,
1 => Pauli::Z,
2 => Pauli::X,
3 => Pauli::Y,
_ => unreachable!("RNG number out of range"),
}
}
}
impl TryFrom<&char> for Pauli {
type Error = String;
fn try_from(value: &char) -> Result<Self, Self::Error> {
match value.to_ascii_lowercase() {
'i' => Ok(Pauli::I),
'x' => Ok(Pauli::X),
'z' => Ok(Pauli::Z),
'y' => Ok(Pauli::Y),
c => Err(format!("Cannot convert {c} to Pauli")),
}
}
}
impl TryFrom<usize> for Pauli {
type Error = String;
fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
0 => Ok(Pauli::I),
1 => Ok(Pauli::X),
2 => Ok(Pauli::Z),
3 => Ok(Pauli::Y),
_ => Err(format!("Cannot convert {value} to Pauli")),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub struct AutomorphismData {
x: u8,
y: u8,
}
impl AutomorphismData {
pub fn new(x: u8, y: u8) -> Self {
Self { x: x % 6, y: y % 6 }
}
pub fn get_x(&self) -> u8 {
self.x
}
pub fn get_y(&self) -> u8 {
self.y
}
pub fn nr_generators(&self) -> u64 {
match (self.x, self.y) {
(0, 0) => 0,
(1, 0) | (0, 1) | (5, 0) | (0, 5) => 1,
(3, 3) | (0, 3) | (3, 0) => 2,
(3, _) | (_, 3) => 1,
_ => 2,
}
}
pub fn inv(&self) -> Self {
AutomorphismData::new(6 - self.x, 6 - self.y)
}
pub fn is_id(&self) -> bool {
self.x == 0 && self.y == 0
}
}
impl Mul for AutomorphismData {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.x + rhs.x, self.y + rhs.y)
}
}
impl MulAssign for AutomorphismData {
fn mul_assign(&mut self, rhs: Self) {
*self = Self::new(self.x + rhs.x, self.y + rhs.y);
}
}
impl Distribution<AutomorphismData> for StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> AutomorphismData {
let x = rng.random_range(0..=5);
let y = rng.random_range(0..=5);
AutomorphismData::new(x, y)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub struct ParallelMeasureData {
p: Pauli,
}
impl ParallelMeasureData {
pub fn new(p: Pauli) -> Option<Self> {
match p {
Pauli::X | Pauli::Z => Some(ParallelMeasureData { p }),
_ => None,
}
}
pub fn get_basis(&self) -> Pauli {
self.p
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub struct TwoBases {
p1: Pauli,
p7: Pauli,
}
impl TwoBases {
pub fn new(p1: Pauli, p7: Pauli) -> Option<Self> {
match (p1, p7) {
(Pauli::I, Pauli::I) => None,
_ => Some(TwoBases { p1, p7 }),
}
}
pub fn get_basis_1(&self) -> Pauli {
self.p1
}
pub fn get_basis_7(&self) -> Pauli {
self.p7
}
}
impl Distribution<TwoBases> for StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> TwoBases {
let mut out = None;
while out.is_none() {
let p1 = StandardUniform.sample(rng);
let p7 = StandardUniform.sample(rng);
out = TwoBases::new(p1, p7);
}
out.unwrap()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub struct TGateData {
basis: Pauli,
pub primed: bool, pub adjoint: bool, }
impl TGateData {
pub fn new(basis: Pauli, primed: bool, adjoint: bool) -> Option<Self> {
match basis {
Pauli::I => None,
Pauli::X | Pauli::Z | Pauli::Y => Some(TGateData {
basis,
primed,
adjoint,
}),
}
}
pub fn get_basis(&self) -> Pauli {
self.basis
}
}
impl Distribution<TGateData> for StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> TGateData {
let p = if rng.random() { Pauli::X } else { Pauli::Z };
TGateData::new(p, rng.random(), rng.random()).unwrap()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum BicycleISA {
SyndromeCycle, CSSInitZero, CSSInitPlus, DestructiveZ, DestructiveX,
Automorphism(AutomorphismData),
Measure(TwoBases),
JointMeasure(TwoBases),
ParallelMeasure(ParallelMeasureData),
JointBellInit, JointTransversalCX,
InitT, TGate(TGateData), }
impl Display for BicycleISA {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BicycleISA::SyndromeCycle => write!(f, "sc"),
BicycleISA::CSSInitZero => write!(f, "init0"),
BicycleISA::CSSInitPlus => write!(f, "init+"),
BicycleISA::DestructiveZ => write!(f, "measZ"),
BicycleISA::DestructiveX => write!(f, "measX"),
BicycleISA::Automorphism(data) => write!(f, "aut({},{})", data.get_x(), data.get_y()),
BicycleISA::Measure(bases) => {
write!(f, "meas({},{})", bases.get_basis_1(), bases.get_basis_7())
}
BicycleISA::JointMeasure(bases) => {
write!(f, "jMeas({},{})", bases.get_basis_1(), bases.get_basis_7())
}
BicycleISA::ParallelMeasure(basis) => write!(f, "pMeas({})", basis.get_basis()),
BicycleISA::JointBellInit => write!(f, "jBell"),
BicycleISA::JointTransversalCX => write!(f, "jCnot"),
BicycleISA::InitT => write!(f, "initT"),
BicycleISA::TGate(basis) => {
let prime = if basis.primed { "'" } else { "" };
let dagger = if basis.adjoint { "†" } else { "" };
write!(f, "T({}", basis.get_basis())?;
write!(f, "{prime}")?;
write!(f, "{dagger}")?;
write!(f, ")")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_two_bases() {
assert_eq!(None, TwoBases::new(Pauli::I, Pauli::I));
assert_eq!(
Some(TwoBases {
p1: Pauli::X,
p7: Pauli::Z
}),
TwoBases::new(Pauli::X, Pauli::Z)
);
}
#[test]
fn number_required_generators() {
let generator_exponents = [
(1, 0), (0, 1), (3, 5), (1, 3), (3, 4), (2, 3), ];
let generators: Vec<_> = generator_exponents
.into_iter()
.flat_map(|(x_exp, y_exp)| {
let el = AutomorphismData::new(x_exp, y_exp);
[el, el.inv()]
})
.collect();
for x_exponent in 0..6 {
for y_exponent in 0..6 {
let el = AutomorphismData::new(x_exponent, y_exponent);
let n = el.nr_generators();
if el.is_id() {
assert!(n == 0);
}
else if generators.contains(&el) {
if n != 1 {
println!("Fail 1: {:?}", &el);
}
assert!(n == 1)
} else {
if n != 2 {
println!("Fail 2: {:?}", &el);
}
assert!(n == 2)
}
}
}
}
}