use complex::Complex;
use gate::Gate;
use ket::Ket;
use matrix::Matrix;
#[allow(unused)]
pub fn identity(width: usize) -> Gate {
let m = Matrix::identity(Ket::size(width));
Gate::new(width, m)
}
#[allow(unused, trivial_numeric_casts)]
pub fn hadamard(n: usize) -> Gate {
let sqrt2inv = 2.0f64.sqrt().recip();
let mut m = match n {
0 => Matrix::identity(1),
1 => {
m_real![sqrt2inv, sqrt2inv;
sqrt2inv, -sqrt2inv]
}
2 => {
m_real![0.5, 0.5, 0.5, 0.5;
0.5, -0.5, 0.5, -0.5;
0.5, 0.5, -0.5, -0.5;
0.5, -0.5, -0.5, 0.5]
}
_ => panic!("Cannot compute Hadamard gate of dimension > 3!"),
};
Gate::new(n, m)
}
#[allow(unused)]
pub fn pauli_x() -> Gate {
let m = m_real![0, 1;
1, 0];
Gate::new(1, m)
}
#[allow(unused)]
pub fn pauli_y() -> Gate {
let m = m![Complex::zero(),
-Complex::i();
Complex::i(),
Complex::zero()];
Gate::new(1, m)
}
#[allow(unused)]
pub fn pauli_z() -> Gate {
let m = m_real![1, 0;
0, -1];
Gate::new(1, m)
}
#[allow(unused)]
pub fn phase_shift(phi: f64) -> Gate {
let m = m![Complex::one(),
Complex::zero();
Complex::zero(),
Complex::new_euler(1f64, phi)];
Gate::new(1, m)
}
#[allow(unused)]
pub fn swap() -> Gate {
let m = m_real![1, 0, 0, 0;
0, 0, 1, 0;
0, 1, 0, 0;
0, 0, 0, 1];
Gate::new(2, m)
}
#[allow(unused)]
pub fn sqrt_swap() -> Gate {
let alpha_one = c!(0.5f64, 0.5f64);
let alpha_two = c!(0.5f64, -0.5f64);
let m = m![Complex::one(), Complex::zero(), Complex::zero(), Complex::zero();
Complex::zero(), alpha_one, alpha_two, Complex::zero();
Complex::zero(), alpha_two, alpha_one, Complex::zero();
Complex::zero(), Complex::zero(), Complex::zero(), Complex::one() ];
Gate::new(2, m)
}
#[allow(unused)]
pub fn controlled_not() -> Gate {
let m = m_real![1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 0, 1;
0, 0, 1, 0];
Gate::new(2, m)
}
#[allow(unused)]
pub fn controlled(u: &Matrix) -> Gate {
assert_eq!(2, u.size());
let mut m = m_real![1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0];
m.embed(&u, 2, 2);
Gate::new(2, m)
}
#[allow(unused)]
pub fn controlled_x() -> Gate {
controlled(pauli_x().matrix())
}
#[allow(unused)]
pub fn controlled_y() -> Gate {
controlled(pauli_y().matrix())
}
#[allow(unused)]
pub fn controlled_z() -> Gate {
controlled(pauli_z().matrix())
}
#[allow(unused)]
pub fn toffoli() -> Gate {
let mut m = Matrix::identity(8);
let mut exchange = m_real![0, 1;
1, 0];
m.embed(&exchange, 6, 6);
Gate::new(3, m)
}
#[allow(unused)]
pub fn fredkin() -> Gate {
let mut m = Matrix::identity(8);
let mut exchange = m_real![0, 1;
1, 0];
m.embed(&exchange, 5, 5);
Gate::new(3, m)
}
#[allow(unused)]
pub fn quantum_fourier_transform(n: usize) -> Gate {
let d = Ket::size(n);
let c = (d as f64).sqrt().recip();
let r = Complex::nth_root_of_unity(d as u32);
let mut m = Matrix::new(d);
for i in 0..d {
for j in 0..i + 1 {
let v = c![c, 0f64] * r.pow((i * j) as u32);
m.set(i, j, v);
m.set(j, i, v);
}
}
Gate::new(n, m)
}
macro_rules! test_gate {
($computer:expr, $gate:expr, $from:expr, $to:expr) => {
$computer.initialize($from);
$computer.apply($gate);
$computer.collapse();
assert_eq!($to, $computer.value());
$computer.reset();
};
}
#[test]
fn identity_test() {
use complex::Complex;
let id_gate = identity(3);
let mut ket = Ket::new(8);
ket.elements[5] = c![99f64, 0f64];
let expected = ket.clone();
ket.apply(id_gate);
assert_eq!(expected, ket);
}
#[test]
fn hadamard_test() {
use float_cmp::ApproxEqUlps;
use computer::QuantumComputer;
let mut c = QuantumComputer::new(1);
c.initialize(0);
c.apply(hadamard(1));
assert!(0.5f64.approx_eq_ulps(&c.probabilities()[0], 10));
assert!(0.5f64.approx_eq_ulps(&c.probabilities()[1], 10));
}
#[test]
fn pauli_x_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(1);
test_gate!(c, pauli_x(), 0, 1);
test_gate!(c, pauli_x(), 1, 0);
}
#[test]
fn pauli_y_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(1);
test_gate!(c, pauli_y(), 0, 1);
test_gate!(c, pauli_y(), 1, 0);
}
#[test]
fn pauli_z_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(1);
test_gate!(c, pauli_z(), 0, 0);
test_gate!(c, pauli_z(), 1, 1);
}
#[test]
fn phase_shift_test() {
use computer::QuantumComputer;
let phi = 0.3f64;
let mut c = QuantumComputer::new(1);
test_gate!(c, phase_shift(phi), 0, 0);
test_gate!(c, phase_shift(phi), 1, 1);
}
#[test]
fn swap_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(2);
test_gate!(c, swap(), 0, 0);
test_gate!(c, swap(), 2, 1);
}
#[test]
fn sqrt_swap_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(2);
test_gate!(c, sqrt_swap(), 0, 0);
test_gate!(c, sqrt_swap(), 3, 3);
}
#[test]
fn controlled_not_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(2);
test_gate!(c, controlled_not(), 0, 0);
test_gate!(c, controlled_not(), 1, 1);
test_gate!(c, controlled_not(), 2, 3);
test_gate!(c, controlled_not(), 3, 2);
}
#[test]
fn controlled_test() {
let g = controlled(&m_real![0, 1; 1, 0]);
assert_eq!(controlled_not(), g);
}
#[test]
fn toffoli_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(3);
test_gate!(c, toffoli(), 0, 0);
test_gate!(c, toffoli(), 2, 2);
test_gate!(c, toffoli(), 6, 7);
test_gate!(c, toffoli(), 7, 6);
}
#[test]
fn fredkin_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(3);
test_gate!(c, fredkin(), 0, 0);
test_gate!(c, fredkin(), 5, 6);
test_gate!(c, fredkin(), 6, 5);
test_gate!(c, fredkin(), 7, 7);
}
#[test]
fn quantum_fourier_transform_test() {
let qft = quantum_fourier_transform(2);
assert!(c![0.5f64, 0.0f64].approx_eq(&qft.matrix().get(3, 0)));
assert!(c![0.0f64, -0.5f64].approx_eq(&qft.matrix().get(3, 1)));
assert!(c![-0.5f64, 0.0f64].approx_eq(&qft.matrix().get(3, 2)));
assert!(c![0.0f64, 0.5f64].approx_eq(&qft.matrix().get(3, 3)));
}
#[test]
fn permutation_test() {
use computer::QuantumComputer;
let mut c = QuantumComputer::new(2);
let flipped_controlled_not = controlled_not().permute(vec![2, 3, 0, 1]);
test_gate!(c, flipped_controlled_not, 1, 3);
}