use std::fmt;
use crate::circuit::{QResult, QResultConst};
use crate::error::{QuantrError, QuantrErrorConst};
use crate::states::Qubit;
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct ProductState {
pub(crate) qubits: Vec<Qubit>,
}
impl ProductState {
pub fn new(product_state: &[Qubit]) -> QResultConst<ProductState> {
if product_state.is_empty() {
return Err(QuantrErrorConst {
message: "The slice of qubits is empty, it needs to at least have one element.",
});
}
Ok(ProductState {
qubits: product_state.to_vec(),
})
}
pub fn get(&self, i: usize) -> Option<&Qubit> {
self.qubits.get(i)
}
pub fn get_qubits(&self) -> &[Qubit] {
self.qubits.as_slice()
}
pub fn get_mut_qubits(&mut self) -> &mut [Qubit] {
self.qubits.as_mut_slice()
}
pub(crate) fn new_unchecked(product_state: &[Qubit]) -> ProductState {
ProductState {
qubits: product_state.to_vec(),
}
}
pub(crate) fn insert_qubits(&mut self, qubits: &[Qubit], pos: &[usize]) {
for (enum_i, &i) in pos.iter().enumerate() {
if self.qubits[i] != qubits[enum_i] {
self.qubits[i] = match self.qubits[i] {
Qubit::Zero => Qubit::One,
Qubit::One => Qubit::Zero,
};
}
}
}
pub fn num_qubits(&self) -> usize {
self.qubits.len()
}
pub fn invert_digit(&mut self, place_num: usize) -> QResult<&mut ProductState> {
if place_num >= self.num_qubits() {
return Err(QuantrError { message: format!("The position of the binary digit, {}, is out of bounds. The product dimension is {}, and so the position must be strictly less.", place_num, self.num_qubits()) });
}
let old_qubit: Qubit = self.qubits[place_num];
self.qubits[place_num] = if old_qubit == Qubit::Zero {
Qubit::One
} else {
Qubit::Zero
};
Ok(self)
}
pub fn kronecker_prod(mut self, other: Qubit) -> ProductState {
self.qubits.push(other);
self
}
pub(crate) fn get_unchecked(&self, qubit_number: usize) -> Qubit {
self.qubits[qubit_number]
}
pub(super) fn comp_basis(&self) -> usize {
self.qubits
.iter()
.rev()
.enumerate()
.map(|(pos, i)| match i {
Qubit::Zero => 0u32,
Qubit::One => 1 << pos,
})
.sum::<u32>() as usize
}
pub(super) fn binary_basis(index: usize, basis_size: usize) -> ProductState {
let binary_index: Vec<Qubit> = (0..basis_size)
.rev()
.map(|n| match (index >> n) & 1 == 1 {
false => Qubit::Zero,
true => Qubit::One,
})
.collect();
ProductState::new_unchecked(binary_index.as_slice())
}
}
impl fmt::Display for ProductState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let binary_string = self
.qubits
.iter()
.map(|q| match q {
Qubit::Zero => "0",
Qubit::One => "1",
})
.collect::<String>();
write!(f, "{}", binary_string)
}
}
impl From<Qubit> for ProductState {
fn from(value: Qubit) -> Self {
ProductState::new_unchecked(&[value])
}
}
pub struct ProductStateIter<'a> {
state: &'a ProductState,
index: usize,
}
impl<'a> Iterator for ProductStateIter<'a> {
type Item = Qubit;
fn next(&mut self) -> Option<Self::Item> {
if let Some(qubit) = self.state.qubits.get(self.index).copied() {
self.index += 1;
Some(qubit)
} else {
self.index = 0;
None
}
}
}
impl<'a> IntoIterator for &'a ProductState {
type Item = Qubit;
type IntoIter = ProductStateIter<'a>;
fn into_iter(self) -> Self::IntoIter {
ProductStateIter {
state: &self,
index: 0,
}
}
}
#[cfg(test)]
mod tests {
use crate::states::{ProductState, Qubit, SuperPosition};
use crate::{complex_re, Complex, COMPLEX_ZERO};
use super::ProductStateIter;
#[test]
fn iterates_through_qubits() {
let state: ProductState =
ProductState::new(&[Qubit::One, Qubit::Zero, Qubit::One]).unwrap();
let mut iter_state: ProductStateIter = state.into_iter();
assert_eq!(iter_state.next(), Some(Qubit::One));
assert_eq!(iter_state.next(), Some(Qubit::Zero));
assert_eq!(iter_state.next(), Some(Qubit::One));
assert_eq!(iter_state.next(), None);
}
#[test]
fn converts_from_integer_to_product_state() {
assert_eq!(
ProductState::new_unchecked(&[Qubit::One, Qubit::One, Qubit::Zero]),
ProductState::binary_basis(6, 3)
)
}
#[test]
fn inverting_binary_digit() {
let mut inverted = ProductState::new_unchecked(&[Qubit::One, Qubit::One, Qubit::Zero]);
inverted.invert_digit(2).unwrap();
assert_eq!(
ProductState::new_unchecked(&[Qubit::One, Qubit::One, Qubit::One]),
inverted
)
}
#[test]
fn insert_qubits_in_state() {
let mut prod = ProductState::new_unchecked(&[Qubit::One, Qubit::One, Qubit::One]);
prod.insert_qubits(&[Qubit::Zero, Qubit::Zero], &[0, 2]);
assert_eq!(
ProductState::new_unchecked(&[Qubit::Zero, Qubit::One, Qubit::Zero]).qubits,
prod.qubits
);
}
#[test]
fn converts_from_binary_to_comp_basis() {
assert_eq!(
ProductState::new_unchecked(&[Qubit::One, Qubit::Zero, Qubit::One]).comp_basis(),
5usize
);
assert_eq!(
ProductState::new_unchecked(&[Qubit::One, Qubit::One, Qubit::One]).comp_basis(),
7usize
);
assert_eq!(
ProductState::new_unchecked(&[Qubit::One, Qubit::Zero]).comp_basis(),
2usize
);
assert_eq!(
ProductState::new_unchecked(&[Qubit::One, Qubit::Zero, Qubit::One, Qubit::One])
.comp_basis(),
11usize
);
}
#[test]
fn converts_productstate_to_superpos() {
assert_eq!(
&mut SuperPosition::from(ProductState::new_unchecked(&[Qubit::One, Qubit::Zero])),
SuperPosition::new_unchecked(2)
.set_amplitudes(&[COMPLEX_ZERO, COMPLEX_ZERO, complex_re!(1f64), COMPLEX_ZERO])
.unwrap()
)
}
}