use std::{fmt, hash::Hash};
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum QubitError {
#[error("qubit index must be non-negative, got {0}")]
NegativeIndex(i128),
#[error("qubit index {0} exceeds u32::MAX")]
IndexTooLarge(u128),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Qubit(u32);
impl fmt::Display for Qubit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Q{}", self.0)
}
}
impl Qubit {
#[inline]
pub const fn new(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn id(&self) -> u32 {
self.0
}
#[inline]
pub const fn index(&self) -> usize {
self.0 as usize
}
}
macro_rules! impl_qubit_from_unsigned {
($($t:ty),*) => {
$(
impl From<$t> for Qubit {
fn from(idx: $t) -> Self {
Self(idx as u32)
}
}
)*
};
}
macro_rules! impl_qubit_try_from_signed {
($($t:ty),*) => {
$(
impl TryFrom<$t> for Qubit {
type Error = QubitError;
fn try_from(idx: $t) -> Result<Self, Self::Error> {
if idx < 0 {
Err(QubitError::NegativeIndex(idx as i128))
} else if idx as u128 > u32::MAX as u128 {
Err(QubitError::IndexTooLarge(idx as u128))
} else {
Ok(Self(idx as u32))
}
}
}
)*
};
}
impl_qubit_from_unsigned!(u32, u16, u8);
impl_qubit_try_from_signed!(i8, i16, i32, isize);
macro_rules! impl_qubit_try_from_unsigned {
($($t:ty),*) => {
$(
impl TryFrom<$t> for Qubit {
type Error = QubitError;
fn try_from(idx: $t) -> Result<Self, Self::Error> {
if idx as u128 > u32::MAX as u128 {
Err(QubitError::IndexTooLarge(idx as u128))
} else {
Ok(Self(idx as u32))
}
}
}
)*
};
}
impl_qubit_try_from_unsigned!(usize);
#[test]
fn test_qubit_creation_and_display() {
let q0 = Qubit(0);
let q1 = Qubit(1);
assert_eq!(q0.id(), 0);
assert_eq!(q1.id(), 1);
assert_ne!(q0, q1);
assert_eq!(format!("{}", q0), "Q0");
assert_eq!(format!("{}", q1), "Q1");
let q0_1 = Qubit(0);
assert_eq!(q0, q0_1);
}
#[test]
fn test_qubit_try_from_usize() {
assert!(Qubit::try_from(0usize).is_ok());
assert!(Qubit::try_from(100usize).is_ok());
if usize::BITS >= 32 {
assert!(Qubit::try_from(u32::MAX as usize).is_ok());
}
if usize::BITS > 32 {
let overflow_idx = (u32::MAX as usize) + 1;
let result = Qubit::try_from(overflow_idx);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
QubitError::IndexTooLarge(overflow_idx as u128)
);
}
}
#[test]
fn test_qubit_try_from_isize() {
assert!(Qubit::try_from(0isize).is_ok());
assert!(Qubit::try_from(100isize).is_ok());
if isize::BITS > 32 {
assert!(Qubit::try_from(u32::MAX as isize).is_ok());
} else {
assert!(Qubit::try_from(isize::MAX).is_ok());
}
let result = Qubit::try_from(-1isize);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), QubitError::NegativeIndex(-1));
if isize::BITS > 32 {
let overflow_idx = (u32::MAX as isize) + 1;
let result = Qubit::try_from(overflow_idx);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
QubitError::IndexTooLarge(overflow_idx as u128)
);
}
}
#[test]
fn test_qubit_from_small_types() {
let _: Qubit = 0u8.into();
let _: Qubit = 100u8.into();
let _: Qubit = 0u16.into();
let _: Qubit = 1000u16.into();
let _: Qubit = 0u32.into();
let _: Qubit = 100000u32.into();
assert_eq!(Qubit::try_from(0i8), Ok(Qubit::new(0)));
assert_eq!(Qubit::try_from(100i8), Ok(Qubit::new(100)));
assert_eq!(Qubit::try_from(1000i16), Ok(Qubit::new(1000)));
assert_eq!(Qubit::try_from(100000i32), Ok(Qubit::new(100000)));
assert_eq!(Qubit::try_from(-1i8), Err(QubitError::NegativeIndex(-1)));
assert_eq!(Qubit::try_from(-1i16), Err(QubitError::NegativeIndex(-1)));
assert_eq!(Qubit::try_from(-1i32), Err(QubitError::NegativeIndex(-1)));
}