use num_complex::Complex64;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum QubitState {
Zero,
One,
Plus,
Minus,
}
#[derive(Debug, Clone)]
pub struct Qubit {
alpha: Complex64,
beta: Complex64,
}
impl Qubit {
pub fn new(state: QubitState) -> Self {
match state {
QubitState::Zero => Self {
alpha: Complex64::new(1.0, 0.0),
beta: Complex64::new(0.0, 0.0),
},
QubitState::One => Self {
alpha: Complex64::new(0.0, 0.0),
beta: Complex64::new(1.0, 0.0),
},
QubitState::Plus => {
let s: _ = 1.0 / 2.0_f64.sqrt();
Self {
alpha: Complex64::new(s, 0.0),
beta: Complex64::new(s, 0.0),
}
}
QubitState::Minus => {
let s: _ = 1.0 / 2.0_f64.sqrt();
Self {
alpha: Complex64::new(s, 0.0),
beta: Complex64::new(-s, 0.0),
}
}
}
}
pub fn from_amplitudes(alpha: Complex64, beta: Complex64) -> Self {
let norm: _ = (alpha.norm_sqr() + beta.norm_sqr()).sqrt();
Self {
alpha: alpha / norm,
beta: beta / norm,
}
}
pub fn amplitudes(&self) -> (Complex64, Complex64) {
(self.alpha, self.beta)
}
pub(crate) fn set_amplitudes(&mut self, alpha: Complex64, beta: Complex64) {
self.alpha = alpha;
self.beta = beta;
}
pub fn measurement_probabilities(&self) -> (f64, f64) {
(self.alpha.norm_sqr(), self.beta.norm_sqr())
}
pub fn measure(&mut self) -> u8 {
let p0: _ = self.alpha.norm_sqr();
let random: f64 = rand::random();
if random < p0 {
self.alpha = Complex64::new(1.0, 0.0);
self.beta = Complex64::new(0.0, 0.0);
0
} else {
self.alpha = Complex64::new(0.0, 0.0);
self.beta = Complex64::new(1.0, 0.0);
1
}
}
pub fn bloch_coordinates(&self) -> (f64, f64) {
let theta: _ = 2.0 * self.alpha.norm().acos();
let phi: _ = (self.beta / self.alpha).arg();
(theta, if phi.is_nan() { 0.0 } else { phi })
}
}
impl Default for Qubit {
fn default() -> Self {
Self::new(QubitState::Zero)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::{HashMap, BTreeMap};
#[test]
fn test_qubit_default_is_zero() {
let q: _ = Qubit::default();
let (alpha, beta) = q.amplitudes();
assert!((alpha.re - 1.0).abs() < 1e-10);
assert!(beta.norm() < 1e-10);
}
}