use rand::Rng;
use rayon::prelude::*;
use std::collections::HashSet;
use crate::unitaries::Unitary;
use crate::{
config::Config,
consts::{H, X, Y, Z},
core::State,
math::{Amplitude, Float, SQRT_ONE_HALF},
};
const LOW_QUBIT_THRESHOLD: u8 = 15;
struct SendPtr<T>(*mut T);
unsafe impl<T: Send> Send for SendPtr<T> {}
unsafe impl<T: Send> Sync for SendPtr<T> {}
impl<T> SendPtr<T> {
fn get(self) -> *mut T {
self.0
}
}
impl<T> Clone for SendPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for SendPtr<T> {}
#[derive(Clone)]
pub enum Gate {
H,
M,
X,
Y,
Z,
P(Float),
RX(Float),
RY(Float),
RZ(Float),
SWAP(usize, usize),
U(Float, Float, Float),
Unitary(Unitary),
BitFlipNoise(Float),
}
impl Gate {
pub fn inverse(self) -> Self {
match self {
Self::H | Self::X | Self::Y | Self::Z | Self::SWAP(_, _) => self,
Self::P(theta) => Self::P(-theta),
Self::RX(theta) => Self::RX(-theta),
Self::RY(theta) => Self::RY(-theta),
Self::RZ(theta) => Self::RZ(-theta),
Self::U(theta, phi, lambda) => Self::U(-theta, -lambda, -phi),
Self::M | Self::BitFlipNoise(_) => unimplemented!(),
Self::Unitary(mut unitary) => {
unitary.conj_t();
Self::Unitary(unitary)
}
}
}
pub fn to_matrix(&self) -> [Amplitude; 4] {
match self {
Self::H => H,
Self::X => X,
Self::Y => Y,
Self::Z => Z,
Self::P(theta) => [
Amplitude { re: 1.0, im: 0.0 },
Amplitude { re: 0.0, im: 0.0 },
Amplitude { re: 0.0, im: 0.0 },
Amplitude {
re: theta.cos(),
im: theta.sin(),
},
],
Self::RX(theta) => {
let theta = theta / 2.0;
[
Amplitude {
re: theta.cos(),
im: 0.0,
},
Amplitude {
re: 0.0,
im: -theta.sin(),
},
Amplitude {
re: 0.0,
im: -theta.sin(),
},
Amplitude {
re: theta.cos(),
im: 0.0,
},
]
}
Self::RY(theta) => {
let theta = theta / 2.0;
[
Amplitude {
re: theta.cos(),
im: 0.0,
},
Amplitude {
re: -theta.sin(),
im: 0.0,
},
Amplitude {
re: theta.sin(),
im: 0.0,
},
Amplitude {
re: theta.cos(),
im: 0.0,
},
]
}
Self::RZ(theta) => {
let theta = theta / 2.0;
[
Amplitude {
re: theta.cos(),
im: -theta.sin(),
},
Amplitude { re: 0.0, im: 0.0 },
Amplitude { re: 0.0, im: 0.0 },
Amplitude {
re: theta.cos(),
im: theta.sin(),
},
]
}
Self::U(theta, phi, lambda) => {
let theta = theta / 2.0;
[
Amplitude {
re: theta.cos(),
im: 0.0,
},
Amplitude {
re: -lambda.cos() * theta.sin(),
im: -lambda.sin() * theta.sin(),
},
Amplitude {
re: phi.cos() * theta.sin(),
im: phi.sin() * theta.sin(),
},
Amplitude {
re: (phi + lambda).cos() * theta.cos(),
im: (phi + lambda).sin() * theta.cos(),
},
]
}
_ => unimplemented!(),
}
}
}
pub fn apply(gate: Gate, state: &mut State, target: usize) {
match gate {
Gate::H => h_apply(state, target),
Gate::X => x_apply(state, target),
Gate::Y => y_apply(state, target),
Gate::Z => z_apply(state, target),
Gate::P(theta) => p_apply(state, target, theta),
Gate::RX(theta) => rx_apply(state, target, theta),
Gate::RY(theta) => ry_apply(state, target, theta),
Gate::RZ(theta) => rz_apply(state, target, theta),
Gate::SWAP(t0, t1) => swap_apply(state, t0, t1),
Gate::U(theta, phi, lambda) => u_apply(state, target, theta, phi, lambda),
Gate::BitFlipNoise(prob) => {
bit_flip_noise_apply(state, prob, target);
}
_ => unimplemented!(),
}
}
pub fn c_apply(gate: Gate, state: &mut State, control: usize, target: usize) {
match gate {
Gate::H => h_c_apply(state, control, target),
Gate::X => x_c_apply(state, control, target),
Gate::Y => y_c_apply(state, control, target),
Gate::P(theta) => p_c_apply(state, control, target, theta),
Gate::RX(theta) => rx_c_apply(state, control, target, theta),
Gate::RY(theta) => ry_c_apply(state, control, target, theta),
Gate::RZ(theta) => rz_c_apply(state, control, target, theta),
Gate::U(theta, phi, lambda) => u_c_apply(state, theta, phi, lambda, control, target),
_ => todo!(),
}
}
pub fn cc_apply(gate: Gate, state: &mut State, control0: usize, control1: usize, target: usize) {
match gate {
Gate::X => x_cc_apply(state, control0, control1, target),
_ => todo!(),
}
}
pub fn mc_apply(
gate: Gate,
state: &mut State,
controls: &[usize],
zeros: Option<HashSet<usize>>,
target: usize,
) {
debug_assert!(usize::from(state.n) > controls.len());
let mut mask: u64 = 0;
if let Some(z) = zeros {
for control in controls.iter() {
if z.contains(control) {
continue;
}
mask |= 1 << control
}
} else {
for control in controls.iter() {
mask |= 1 << control
}
}
match gate {
Gate::X => x_mc_apply(state, mask, target),
Gate::P(theta) => p_mc_apply(state, mask, target, theta),
Gate::RX(theta) => rx_mc_apply(state, mask, target, theta),
Gate::RY(theta) => ry_mc_apply(state, mask, target, theta),
_ => todo!(),
}
}
fn x_apply_target_0(state_re: SendPtr<Float>, state_im: SendPtr<Float>, s0: usize) {
unsafe {
std::ptr::swap(state_re.get().add(s0), state_re.get().add(s0 + 1));
std::ptr::swap(state_im.get().add(s0), state_im.get().add(s0 + 1));
}
}
fn x_apply_target(state_re: SendPtr<Float>, state_im: SendPtr<Float>, s0: usize, s1: usize) {
unsafe {
std::ptr::swap(state_re.get().add(s0), state_re.get().add(s1));
std::ptr::swap(state_im.get().add(s0), state_im.get().add(s1));
}
}
fn x_proc_chunk(state_re: SendPtr<Float>, state_im: SendPtr<Float>, chunk: usize, target: usize) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
x_apply_target(state_re, state_im, s0, s1)
}
}
pub(crate) fn x_apply(state: &mut State, target: usize) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
if target == 0 {
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..state.len()).step_by(2).for_each(|s0| {
x_apply_target_0(state_re, state_im, s0);
});
} else {
(0..state.len()).into_par_iter().step_by(2).for_each(|s0| {
x_apply_target_0(state_re, state_im, s0);
});
}
} else {
let end = state.len() >> 1;
let chunks = end >> target;
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|chunk| {
x_proc_chunk(state_re, state_im, chunk, target);
});
} else {
(0..chunks).into_par_iter().for_each(|chunk| {
x_proc_chunk(state_re, state_im, chunk, target);
});
}
}
}
fn x_c_apply(state: &mut State, control: usize, target: usize) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let end = state.len() >> 2;
let dist = 1 << target;
let marks = (target.min(control), target.max(control));
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..end).for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
x_apply_target(state_re, state_im, s0, s1);
});
} else {
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
x_apply_target(state_re, state_im, s0, s1);
});
}
}
fn x_cc_apply(state: &mut State, control0: usize, control1: usize, target: usize) {
let mut i = 0;
let dist = 1 << target;
while i < state.len() {
let c0c1_set = (((i >> control0) & 1) != 0) && (((i >> control1) & 1) != 0);
if c0c1_set {
let s0 = i;
let s1 = i + dist;
unsafe {
let temp0 = *state.reals.get_unchecked(s0);
*state.reals.get_unchecked_mut(s0) = *state.reals.get_unchecked(s1);
*state.reals.get_unchecked_mut(s1) = temp0;
let temp1 = *state.imags.get_unchecked(s0);
*state.imags.get_unchecked_mut(s0) = *state.imags.get_unchecked(s1);
*state.imags.get_unchecked_mut(s1) = temp1;
};
i += dist;
}
i += 1;
}
}
fn x_mc_apply(state: &mut State, mask: u64, target: usize) {
let mut i = 0;
let dist = 1 << target;
while i < state.len() {
if (i as u64 & mask) == mask {
let s0 = i;
let s1 = i + dist;
unsafe {
let temp0 = *state.reals.get_unchecked(s0);
*state.reals.get_unchecked_mut(s0) = *state.reals.get_unchecked(s1);
*state.reals.get_unchecked_mut(s1) = temp0;
let temp1 = *state.imags.get_unchecked(s0);
*state.imags.get_unchecked_mut(s0) = *state.imags.get_unchecked(s1);
*state.imags.get_unchecked_mut(s1) = temp1;
};
i += dist;
}
i += 1;
}
}
fn y_proc_chunk(state_re: SendPtr<Float>, state_im: SendPtr<Float>, chunk: usize, target: usize) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
unsafe {
std::ptr::swap(state_re.get().add(s0), state_re.get().add(s1));
std::ptr::swap(state_im.get().add(s0), state_im.get().add(s1));
std::ptr::swap(state_re.get().add(s0), state_im.get().add(s0));
std::ptr::swap(state_re.get().add(s1), state_im.get().add(s1));
*state_im.get().add(s0) = -(*state_im.get().add(s0));
*state_re.get().add(s1) = -(*state_re.get().add(s1));
}
}
}
pub(crate) fn y_apply(state: &mut State, target: usize) {
let end = state.len() >> 1;
let chunks = end >> target;
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|chunk| {
y_proc_chunk(state_re, state_im, chunk, target);
});
} else {
(0..chunks).into_par_iter().for_each(|chunk| {
y_proc_chunk(state_re, state_im, chunk, target);
});
}
}
fn y_c_apply_to_range(state: &mut State, control: usize, target: usize) {
let dist = 1 << target;
let marks = (target.min(control), target.max(control));
let end = state.len() >> 2;
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
for i in 0..end {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
unsafe {
std::ptr::swap(state_re.get().add(s0), state_re.get().add(s1));
std::ptr::swap(state_im.get().add(s0), state_im.get().add(s1));
std::ptr::swap(state_re.get().add(s0), state_im.get().add(s0));
std::ptr::swap(state_re.get().add(s1), state_im.get().add(s1));
*state_im.get().add(s0) = -(*state_im.get().add(s0));
*state_re.get().add(s1) = -(*state_re.get().add(s1));
}
}
} else {
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
unsafe {
std::ptr::swap(state_re.get().add(s0), state_re.get().add(s1));
std::ptr::swap(state_im.get().add(s0), state_im.get().add(s1));
std::ptr::swap(state_re.get().add(s0), state_im.get().add(s0));
std::ptr::swap(state_re.get().add(s1), state_im.get().add(s1));
*state_im.get().add(s0) = -(*state_im.get().add(s0));
*state_re.get().add(s1) = -(*state_re.get().add(s1));
}
});
}
}
fn y_c_apply(state: &mut State, control: usize, target: usize) {
y_c_apply_to_range(state, control, target);
}
fn h_apply_strat2(state: &mut State, chunk: usize, target: usize) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
let (a, b, c, d) = unsafe {
let a = *state.reals.get_unchecked(s0);
let b = *state.imags.get_unchecked(s0);
let c = *state.reals.get_unchecked(s1);
let d = *state.imags.get_unchecked(s1);
(a, b, c, d)
};
let a1 = SQRT_ONE_HALF * a;
let b1 = SQRT_ONE_HALF * b;
let c1 = SQRT_ONE_HALF * c;
let d1 = SQRT_ONE_HALF * d;
unsafe {
*state.reals.get_unchecked_mut(s0) = a1 + c1;
*state.imags.get_unchecked_mut(s0) = b1 + d1;
*state.reals.get_unchecked_mut(s1) = a1 - c1;
*state.imags.get_unchecked_mut(s1) = b1 - d1;
}
}
}
fn h_apply_strat2_par(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
chunk: usize,
target: usize,
) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
let (a, b, c, d) = unsafe {
let a = *state_re.get().add(s0);
let b = *state_im.get().add(s0);
let c = *state_re.get().add(s1);
let d = *state_im.get().add(s1);
(a, b, c, d)
};
let a1 = SQRT_ONE_HALF * a;
let b1 = SQRT_ONE_HALF * b;
let c1 = SQRT_ONE_HALF * c;
let d1 = SQRT_ONE_HALF * d;
unsafe {
*state_re.get().add(s0) = a1 + c1;
*state_im.get().add(s0) = b1 + c1;
*state_re.get().add(s1) = a1 - c1;
*state_im.get().add(s1) = b1 - d1;
}
}
}
fn h_apply(state: &mut State, target: usize) {
let end = state.len() >> 1;
let chunks = end >> target;
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|c| {
h_apply_strat2(state, c, target);
});
} else {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
(0..chunks).into_par_iter().for_each(|c| {
h_apply_strat2_par(state_re, state_im, c, target);
});
}
}
fn h_c_apply(state: &mut State, control: usize, target: usize) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let end = state.len() >> 2;
let dist = 1 << target;
let marks = (target.min(control), target.max(control));
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..end).for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
let (a, b, c, d) = unsafe {
let a = *state.reals.get_unchecked(s0);
let b = *state.imags.get_unchecked(s0);
let c = *state.reals.get_unchecked(s1);
let d = *state.imags.get_unchecked(s1);
(a, b, c, d)
};
let a1 = SQRT_ONE_HALF * a;
let b1 = SQRT_ONE_HALF * b;
let c1 = SQRT_ONE_HALF * c;
let d1 = SQRT_ONE_HALF * d;
unsafe {
*state.reals.get_unchecked_mut(s0) = a1 + c1;
*state.imags.get_unchecked_mut(s0) = b1 + d1;
*state.reals.get_unchecked_mut(s1) = a1 - c1;
*state.imags.get_unchecked_mut(s1) = b1 - d1;
}
});
} else {
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
let (a, b, c, d) = unsafe {
let a = *state_re.get().add(s0);
let b = *state_im.get().add(s0);
let c = *state_re.get().add(s1);
let d = *state_im.get().add(s1);
(a, b, c, d)
};
let a1 = SQRT_ONE_HALF * a;
let b1 = SQRT_ONE_HALF * b;
let c1 = SQRT_ONE_HALF * c;
let d1 = SQRT_ONE_HALF * d;
unsafe {
*state_re.get().add(s0) = a1 + c1;
*state_im.get().add(s0) = b1 + d1;
*state_re.get().add(s1) = a1 - c1;
*state_im.get().add(s1) = b1 - d1;
}
});
}
}
fn rx_apply_target_0(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
l: usize,
cos: Float,
neg_sin: Float,
) {
unsafe {
let a = *state_re.get().add(l);
let b = *state_im.get().add(l);
let c = *state_re.get().add(l + 1);
let d = *state_im.get().add(l + 1);
*state_re.get().add(l) = a * cos - d * neg_sin;
*state_im.get().add(l) = b * cos + c * neg_sin;
*state_re.get().add(l + 1) = b * -neg_sin + c * cos;
*state_im.get().add(l + 1) = d * cos + a * neg_sin;
}
}
fn rx_apply_target(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
s0: usize,
s1: usize,
cos: Float,
neg_sin: Float,
) {
unsafe {
let a = *state_re.get().add(s0);
let b = *state_im.get().add(s0);
let c = *state_re.get().add(s1);
let d = *state_im.get().add(s1);
*state_re.get().add(s0) = a * cos - d * neg_sin;
*state_im.get().add(s0) = b * cos + c * neg_sin;
*state_re.get().add(s1) = b * -neg_sin + c * cos;
*state_im.get().add(s1) = d * cos + a * neg_sin;
}
}
fn rx_proc_chunk(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
chunk: usize,
target: usize,
cos: Float,
neg_sin: Float,
) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
rx_apply_target(state_re, state_im, s0, s1, cos, neg_sin);
}
}
fn rx_apply(state: &mut State, target: usize, angle: Float) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let theta = angle * 0.5;
let ct = Float::cos(theta);
let nst = -Float::sin(theta);
if target == 0 {
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..state.len()).step_by(2).for_each(|l| {
rx_apply_target_0(state_re, state_im, l, ct, nst);
})
} else {
(0..state.len()).into_par_iter().step_by(2).for_each(|l| {
rx_apply_target_0(state_re, state_im, l, ct, nst);
})
}
} else {
let end = state.len() >> 1;
let chunks = end >> target;
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|chunk| {
rx_proc_chunk(state_re, state_im, chunk, target, ct, nst);
});
} else {
(0..chunks).into_par_iter().for_each(|chunk| {
rx_proc_chunk(state_re, state_im, chunk, target, ct, nst);
});
}
}
}
fn rx_c_apply(state: &mut State, control: usize, target: usize, angle: Float) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let theta = angle * 0.5;
let ct = Float::cos(theta);
let nst = -Float::sin(theta);
let end = state.len() >> 2;
let dist = 1 << target;
let marks = (target.min(control), target.max(control));
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..end).for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
rx_apply_target(state_re, state_im, s0, s1, ct, nst);
});
} else {
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
rx_apply_target(state_re, state_im, s0, s1, ct, nst);
});
}
}
fn rx_mc_apply(state: &mut State, mask: u64, target: usize, angle: Float) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let theta = angle * 0.5;
let ct = Float::cos(theta);
let nst = -Float::sin(theta);
let mut i = 0;
let dist = 1 << target;
while i < state.len() {
if (i as u64 & mask) == mask {
let s0 = i;
let s1 = i + dist;
rx_apply_target(state_re, state_im, s0, s1, ct, nst);
i += dist;
}
i += 1;
}
}
fn p_proc_chunk(state: &mut State, chunk: usize, target: usize, cos: Float, sin: Float) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for s1 in (base + dist)..(base + 2 * dist) {
let z_re = state.reals[s1];
let z_im = state.imags[s1];
state.reals[s1] = z_re.mul_add(cos, -z_im * sin);
state.imags[s1] = z_im.mul_add(cos, z_re * sin);
}
}
fn p_proc_chunk_par(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
chunk: usize,
target: usize,
cos: Float,
sin: Float,
) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for s1 in (base + dist)..(base + 2 * dist) {
unsafe {
let z_re = *state_re.get().add(s1);
let z_im = *state_im.get().add(s1);
*state_re.get().add(s1) = z_re.mul_add(cos, -z_im * sin);
*state_im.get().add(s1) = z_im.mul_add(cos, z_re * sin);
}
}
}
fn p_apply(state: &mut State, target: usize, angle: Float) {
let (sin, cos) = Float::sin_cos(angle);
let end = state.len() >> 1;
let chunks = end >> target;
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|chunk| {
p_proc_chunk(state, chunk, target, cos, sin);
});
} else {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
(0..chunks).into_par_iter().for_each(|chunk| {
p_proc_chunk_par(state_re, state_im, chunk, target, cos, sin);
});
}
}
fn p_c_apply(state: &mut State, control: usize, target: usize, angle: Float) {
let (sin, cos) = Float::sin_cos(angle);
let end = state.len() >> 2;
let marks = (target.min(control), target.max(control));
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
for i in 0..end {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let z_re = state.reals[s1];
let z_im = state.imags[s1];
state.reals[s1] = z_re.mul_add(cos, -z_im * sin);
state.imags[s1] = z_im.mul_add(cos, z_re * sin);
}
} else {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
unsafe {
let z_re = *state_re.get().add(s1);
let z_im = *state_im.get().add(s1);
*state_re.get().add(s1) = z_re.mul_add(cos, -z_im * sin);
*state_im.get().add(s1) = z_im.mul_add(cos, z_re * sin);
}
});
}
}
fn p_mc_apply(state: &mut State, mask: u64, target: usize, theta: Float) {
let (sin, cos) = Float::sin_cos(theta);
let mut i = 0;
let dist = 1 << target;
while i < state.len() {
if (i as u64 & mask) == mask {
let s1 = i + dist;
let z_re = state.reals[s1];
let z_im = state.imags[s1];
state.reals[s1] = z_re.mul_add(cos, -z_im * sin);
state.imags[s1] = z_im.mul_add(cos, z_re * sin);
i += dist;
}
i += 1;
}
}
fn rz_apply_strategy1(state: &mut State, target: usize, diag_matrix: &[Amplitude; 2]) {
let chunk_size = 1 << target;
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
state
.reals
.chunks_exact_mut(chunk_size)
.zip(state.imags.chunks_exact_mut(chunk_size))
.enumerate()
.for_each(|(i, (c0, c1))| {
c0.iter_mut().zip(c1.iter_mut()).for_each(|(a, b)| {
let m = diag_matrix[i & 1];
let c = *a;
let d = *b;
*a = c.mul_add(m.re, -d * m.im);
*b = c.mul_add(m.im, d * m.re);
});
});
} else {
state
.reals
.par_chunks_exact_mut(chunk_size)
.zip(state.imags.par_chunks_exact_mut(chunk_size))
.enumerate()
.with_min_len(1 << 11)
.for_each(|(i, (c0, c1))| {
c0.iter_mut().zip(c1.iter_mut()).for_each(|(a, b)| {
let m = diag_matrix[i & 1];
let c = *a;
let d = *b;
*a = c.mul_add(m.re, -d * m.im);
*b = c.mul_add(m.im, d * m.re);
});
});
}
}
fn rz_apply(state: &mut State, target: usize, angle: Float) {
let theta = angle * 0.5;
let (s, c) = Float::sin_cos(theta);
let d0 = Amplitude { re: c, im: -s };
let d1 = Amplitude { re: c, im: s };
let diag_matrix = [d0, d1];
rz_apply_strategy1(state, target, &diag_matrix);
}
fn rz_c_apply(state: &mut State, control: usize, target: usize, angle: Float) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let theta = angle * 0.5;
let (s, c) = Float::sin_cos(theta);
let d0 = Amplitude { re: c, im: -s };
let d1 = Amplitude { re: c, im: s };
let diag_matrix = [d0, d1];
let end = state.len() >> 2;
let dist = 1 << target;
let marks = (target.min(control), target.max(control));
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..end).for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
let m = diag_matrix[0];
unsafe {
let a = state_re.get().add(s0);
let b = state_im.get().add(s0);
let c = *a;
let d = *b;
*a = c.mul_add(m.re, -d * m.im);
*b = c.mul_add(m.im, d * m.re);
}
let m = diag_matrix[1];
unsafe {
let a = state_re.get().add(s1);
let b = state_im.get().add(s1);
let c = *a;
let d = *b;
*a = c.mul_add(m.re, -d * m.im);
*b = c.mul_add(m.im, d * m.re);
}
});
} else {
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
let m = diag_matrix[0];
unsafe {
let a = state_re.get().add(s0);
let b = state_im.get().add(s0);
let c = *a;
let d = *b;
*a = c.mul_add(m.re, -d * m.im);
*b = c.mul_add(m.im, d * m.re);
}
let m = diag_matrix[1];
unsafe {
let a = state_re.get().add(s1);
let b = state_im.get().add(s1);
let c = *a;
let d = *b;
*a = c.mul_add(m.re, -d * m.im);
*b = c.mul_add(m.im, d * m.re);
}
});
}
}
fn z_proc_chunk(state: &mut State, chunk: usize, target: usize) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in base + dist..base + 2 * dist {
unsafe {
*state.reals.get_unchecked_mut(i) = -state.reals.get_unchecked(i);
*state.imags.get_unchecked_mut(i) = -state.imags.get_unchecked(i);
};
}
}
fn z_proc_chunk_par(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
chunk: usize,
target: usize,
) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in base + dist..base + 2 * dist {
unsafe {
*state_re.get().add(i) = -(*state_re.get().add(i));
*state_im.get().add(i) = -(*state_im.get().add(i));
};
}
}
pub(crate) fn z_apply(state: &mut State, target: usize) {
let chunks = state.len() >> (target + 1);
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|c| z_proc_chunk(state, c, target));
} else {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
(0..chunks)
.into_par_iter()
.for_each(|chunk| z_proc_chunk_par(state_re, state_im, chunk, target));
}
}
fn ry_apply_strategy2(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
chunk: usize,
target: usize,
sin: Float,
cos: Float,
) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
let (a, b, c, d) = unsafe {
let a = *state_re.get().add(s0);
let b = *state_im.get().add(s0);
let c = *state_re.get().add(s1);
let d = *state_im.get().add(s1);
(a, b, c, d)
};
unsafe {
*state_re.get().add(s0) = a * cos - c * sin;
*state_im.get().add(s0) = b * cos - d * sin;
*state_re.get().add(s1) = a * sin + c * cos;
*state_im.get().add(s1) = b * sin + d * cos;
}
}
}
fn ry_apply(state: &mut State, target: usize, angle: Float) {
let theta = angle * 0.5;
let (sin, cos) = Float::sin_cos(theta);
let end = state.len() >> 1;
let chunks = end >> target;
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|chunk| {
ry_apply_strategy2(state_re, state_im, chunk, target, sin, cos);
});
} else {
(0..chunks).into_par_iter().for_each(|chunk| {
ry_apply_strategy2(state_re, state_im, chunk, target, sin, cos);
});
}
}
fn ry_c_apply_strategy3(state: &mut State, control: usize, target: usize, angle: Float) {
let theta = angle * 0.5;
let (sin, cos) = Float::sin_cos(theta);
let dist = 1 << target;
let marks = (target.min(control), target.max(control));
let end = state.len() >> 2;
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
for i in 0..end {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
let (a, b, c, d) = unsafe {
let a = *state_re.get().add(s0);
let b = *state_im.get().add(s0);
let c = *state_re.get().add(s1);
let d = *state_im.get().add(s1);
(a, b, c, d)
};
unsafe {
*state_re.get().add(s0) = a * cos - c * sin;
*state_im.get().add(s0) = b * cos - d * sin;
*state_re.get().add(s1) = a * sin + c * cos;
*state_im.get().add(s1) = b * sin + d * cos;
}
}
} else {
(0..end).into_par_iter().for_each(|i| {
let x = i + (1 << (marks.1 - 1)) + ((i >> (marks.1 - 1)) << (marks.1 - 1));
let s1 = x + (1 << marks.0) + ((x >> marks.0) << marks.0);
let s0 = s1 - dist;
let (a, b, c, d) = unsafe {
let a = *state_re.get().add(s0);
let b = *state_im.get().add(s0);
let c = *state_re.get().add(s1);
let d = *state_im.get().add(s1);
(a, b, c, d)
};
unsafe {
*state_re.get().add(s0) = a * cos - c * sin;
*state_im.get().add(s0) = b * cos - d * sin;
*state_re.get().add(s1) = a * sin + c * cos;
*state_im.get().add(s1) = b * sin + d * cos;
}
});
}
}
fn ry_c_apply(state: &mut State, control: usize, target: usize, angle: Float) {
ry_c_apply_strategy3(state, control, target, angle);
}
fn ry_mc_apply(state: &mut State, mask: u64, target: usize, angle: Float) {
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let theta = angle * 0.5;
let ct = Float::cos(theta);
let nst = -Float::sin(theta);
let mut i = 0;
let dist = 1 << target;
while i < state.len() {
if (i as u64 & mask) == mask {
let s0 = i;
let s1 = i + dist;
rx_apply_target(state_re, state_im, s0, s1, ct, nst);
i += dist;
}
i += 1;
}
}
fn u_apply_target(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
g: &[Amplitude; 4],
s0: usize,
s1: usize,
) {
let (c, d, m, n) = unsafe {
let z0_re = *state_re.get().add(s0);
let z0_im = *state_im.get().add(s0);
let z1_re = *state_re.get().add(s1);
let z1_im = *state_im.get().add(s1);
(z0_re, z0_im, z1_re, z1_im)
};
let a = g[0].re;
let k = g[1].re;
let l = g[1].im;
let q = g[2].re;
let r = g[2].im;
let s = g[3].re;
let t = g[3].im;
let t0 = a.mul_add(c, k.mul_add(m, -l * n));
let t1 = a.mul_add(d, k.mul_add(n, l * m));
let t2 = q.mul_add(c, (-r).mul_add(d, s.mul_add(m, -t * n)));
let t3 = q.mul_add(d, r.mul_add(c, s.mul_add(n, t * m)));
unsafe {
*state_re.get().add(s0) = t0;
*state_im.get().add(s0) = t1;
*state_re.get().add(s1) = t2;
*state_im.get().add(s1) = t3;
}
}
fn u_apply_strategy2(
state_re: SendPtr<Float>,
state_im: SendPtr<Float>,
g: &[Amplitude; 4],
chunk: usize,
target: usize,
) {
let dist = 1 << target;
let base = (2 * chunk) << target;
for i in 0..dist {
let s0 = base + i;
let s1 = s0 + dist;
u_apply_target(state_re, state_im, g, s0, s1);
}
}
fn u_apply(state: &mut State, target: usize, theta: Float, phi: Float, lambda: Float) {
let (st, ct) = Float::sin_cos(theta * 0.5);
let (sl, cl) = Float::sin_cos(lambda);
let (spl, cpl) = Float::sin_cos(phi + lambda);
let (sp, cp) = Float::sin_cos(phi);
let c = Amplitude { re: ct, im: 0.0 };
let ncs = Amplitude {
re: -cl * st,
im: -sl * st,
};
let es = Amplitude {
re: cp * st,
im: sp * st,
};
let ec = Amplitude {
re: cpl * ct,
im: spl * ct,
};
let g = [c, ncs, es, ec];
let chunks = state.len() >> (target + 1);
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
if Config::global().threads < 2 || state.n < LOW_QUBIT_THRESHOLD {
(0..chunks).for_each(|chunk| {
u_apply_strategy2(state_re, state_im, &g, chunk, target);
});
} else {
(0..chunks).into_par_iter().for_each(|chunk| {
u_apply_strategy2(state_re, state_im, &g, chunk, target);
});
}
}
fn u_c_apply(
state: &mut State,
theta: Float,
phi: Float,
lambda: Float,
control: usize,
target: usize,
) {
let (st, ct) = Float::sin_cos(theta * 0.5);
let (sl, cl) = Float::sin_cos(lambda);
let (spl, cpl) = Float::sin_cos(phi + lambda);
let (sp, cp) = Float::sin_cos(phi);
let c = Amplitude { re: ct, im: 0.0 };
let ncs = Amplitude {
re: -cl * st,
im: -sl * st,
};
let es = Amplitude {
re: cp * st,
im: sp * st,
};
let ec = Amplitude {
re: cpl * ct,
im: spl * ct,
};
let g = [c, ncs, es, ec];
let state_re = SendPtr(state.reals.as_mut_ptr());
let state_im = SendPtr(state.imags.as_mut_ptr());
let distance = 1 << target;
let mask = (1 << control) | (1 << target);
for i in 0..state.len() {
if i & mask == mask {
let s1 = i;
let s0 = s1 - distance;
u_apply_target(state_re, state_im, &g, s0, s1);
}
}
}
fn bit_flip_noise_apply(state: &mut State, prob: Float, target: usize) -> bool {
let mut rng = rand::thread_rng();
let epsilon: Float = rng.gen();
if epsilon <= prob {
x_apply(state, target);
return true;
}
false
}
fn swap_apply(state: &mut State, t0: usize, t1: usize) {
assert!(usize::from(state.n) > t0 && usize::from(state.n) > t1);
for i in 0..state.len() {
if ((i >> t0) & 1) == 0 && ((i >> t1) & 1) == 1 {
let j = i + (1 << t0) - (1 << t1);
state.reals.swap(i, j);
state.imags.swap(i, j);
}
}
}
fn unitary_col_vec_mul(
unitary: &Unitary,
vec_reals: &[Float],
vec_imags: &[Float],
) -> (Vec<Float>, Vec<Float>) {
assert!(
unitary.height == unitary.width
&& vec_reals.len() == unitary.height
&& vec_imags.len() == unitary.height
);
let chunk_size = unitary.width;
let mut reals = Vec::with_capacity(vec_reals.len());
let mut imags = Vec::with_capacity(vec_imags.len());
unitary
.reals
.chunks_exact(chunk_size)
.zip(unitary.imags.chunks_exact(chunk_size))
.for_each(|(row_reals, row_imags)| {
let mut dot_prod_re = 0.0;
let mut dot_prod_im = 0.0;
row_reals
.iter()
.zip(row_imags.iter())
.zip(vec_reals.iter())
.zip(vec_imags.iter())
.for_each(|(((a, b), s_re), s_im)| {
dot_prod_re += *a * s_re - *b * s_im;
dot_prod_im += *a * s_im + *b * s_re;
});
reals.push(dot_prod_re);
imags.push(dot_prod_im);
});
(reals, imags)
}
pub fn transform_u(state: &mut State, u: &Unitary, t: usize) {
assert_eq!(u.height, u.width);
let m: usize = usize::try_from(u.height.ilog2()).unwrap();
let n: usize = usize::try_from(u.width.ilog2()).unwrap();
let mut vec_reals = vec![0.0; 1 << m];
let mut vec_imags = vec![0.0; 1 << m];
for suffix in 0..(1 << t) {
for prefix in 0..(1 << (n - m - t)) {
for target in 0..(1 << m) {
let k = prefix * (1 << (t + m)) + target * (1 << t) + suffix;
vec_reals[target] = state.reals[k];
vec_imags[target] = state.imags[k];
}
let (vec_reals_out, vec_imags_out) = unitary_col_vec_mul(u, &vec_reals, &vec_imags);
for target in 0..(1 << m) {
let k = prefix * (1 << (t + m)) + target * (1 << t) + suffix;
state.reals[k] = vec_reals_out[target];
state.imags[k] = vec_imags_out[target];
}
}
}
}
pub fn c_transform_u(state: &mut State, u: &Unitary, c: usize, t: usize) {
assert_eq!(u.height, u.width);
let m: usize = usize::try_from(u.height.ilog2()).unwrap();
let n: usize = usize::try_from(u.width.ilog2()).unwrap();
let mut vec_reals = vec![0.0; 1 << m];
let mut vec_imags = vec![0.0; 1 << m];
let mut targets = Vec::new();
for suffix in 0..(1 << t) {
for prefix in 0..(1 << (n - m - t)) {
targets.clear();
for idx in 0..(1 << m) {
let k = prefix * (1 << (t + m)) + idx * (1 << t) + suffix;
if ((k >> c) & 1) == 1 {
vec_reals[idx] = state.reals[k];
vec_imags[idx] = state.imags[k];
targets.push(k)
}
}
let (vec_reals_out, vec_imags_out) = unitary_col_vec_mul(u, &vec_reals, &vec_imags);
for idx in 0..(1 << m) {
let k = prefix * (1 << (t + m)) + idx * (1 << t) + suffix;
if ((k >> c) & 1) == 1 {
state.reals[k] = vec_reals_out[idx];
state.imags[k] = vec_imags_out[idx];
}
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{
core::iqft,
math::{pow2f, PI},
utils::{assert_float_closeness, gen_random_state, mat_mul_2x2, swap},
};
use super::*;
fn qcbm_functional(n: usize) -> State {
let mut state = State::new(n);
let pairs: Vec<_> = (0..n).map(|i| (i, (i + 1) % n)).collect();
for i in 0..n {
apply(Gate::RX(1.0), &mut state, i);
apply(Gate::RZ(1.0), &mut state, i);
}
for (p0, p1) in pairs.iter().take(n - 1) {
c_apply(Gate::X, &mut state, *p0, *p1);
}
for _ in 0..9 {
for i in 0..n {
apply(Gate::RZ(1.0), &mut state, i);
apply(Gate::RX(1.0), &mut state, i);
apply(Gate::RZ(1.0), &mut state, i);
}
for (p0, p1) in pairs.iter().take(n - 1) {
c_apply(Gate::X, &mut state, *p0, *p1);
}
}
for i in 0..n {
apply(Gate::RZ(1.0), &mut state, i);
apply(Gate::RX(1.0), &mut state, i);
}
state
}
#[test]
fn h_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::H, &mut state, i);
}
for i in 0..(1 << n) {
assert_float_closeness(state.reals[i], 0.35355339059327384, 1e-10);
assert_float_closeness(state.imags[i], 0.0, 1e-10);
}
}
#[test]
fn x_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::X, &mut state, i);
}
assert_float_closeness(state.reals[0], 0.0, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[7], 1.0, 1e-10);
assert_float_closeness(state.imags[7], 0.0, 1e-10);
}
#[test]
fn x_gate_20_qubits() {
let n = 20;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::X, &mut state, i);
}
assert_float_closeness(state.reals[0], 0.0, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[(1 << n) - 1], 1.0, 1e-10);
assert_float_closeness(state.imags[(1 << n) - 1], 0.0, 1e-10);
}
#[test]
fn y_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::Y, &mut state, i);
}
assert_float_closeness(state.reals[0], 0.0, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[7], 0.0, 1e-10);
assert_float_closeness(state.imags[7], -1.0, 1e-10);
}
#[test]
fn y_gate_20_qubits() {
let n = 20;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::Y, &mut state, i);
}
assert_float_closeness(state.reals[0], 0.0, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[(1 << n) - 1], 1.0, 1e-10);
assert_float_closeness(state.imags[(1 << n) - 1], 0.0, 1e-10);
}
#[test]
fn z_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::Z, &mut state, i);
}
for i in 0..(1 << n) {
if i == 0 {
assert_float_closeness(state.reals[i], 1.0, 1e-10);
assert_float_closeness(state.imags[i], 0.0, 1e-10);
} else {
assert_float_closeness(state.reals[i], 0.0, 1e-10);
assert_float_closeness(state.imags[i], 0.0, 1e-10);
}
}
}
#[test]
fn p_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::P(PI), &mut state, i);
}
for i in 0..(1 << n) {
if i == 0 {
assert_float_closeness(state.reals[i], 1.0, 1e-10);
assert_float_closeness(state.imags[i], 0.0, 1e-10);
} else {
assert_float_closeness(state.reals[i], 0.0, 1e-10);
assert_float_closeness(state.imags[i], 0.0, 1e-10);
}
}
}
#[test]
fn rx_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::RX(1.0), &mut state, i);
}
assert_float_closeness(state.reals[0], 0.6758712218347053, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[1], 0.0, 1e-10);
assert_float_closeness(state.imags[1], -0.3692301313020644, 1e-10);
assert_float_closeness(state.reals[2], 0.0, 1e-10);
assert_float_closeness(state.imags[2], -0.3692301313020644, 1e-10);
assert_float_closeness(state.reals[3], -0.20171134005566746, 1e-10);
assert_float_closeness(state.imags[3], 0.0, 1e-10);
assert_float_closeness(state.reals[4], 0.0, 1e-10);
assert_float_closeness(state.imags[4], -0.3692301313020644, 1e-10);
assert_float_closeness(state.reals[5], -0.20171134005566746, 1e-10);
assert_float_closeness(state.imags[5], 0.0, 1e-10);
assert_float_closeness(state.reals[6], -0.20171134005566746, 1e-10);
assert_float_closeness(state.imags[6], 0.0, 1e-10);
assert_float_closeness(state.reals[7], 0.0, 1e-10);
assert_float_closeness(state.imags[7], 0.11019540730213864, 1e-10);
}
#[test]
fn ry_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::RY(1.0), &mut state, i);
}
assert_float_closeness(state.reals[0], 0.6758712218347053, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[1], 0.3692301313020644, 1e-10);
assert_float_closeness(state.imags[1], 0.0, 1e-10);
assert_float_closeness(state.reals[2], 0.3692301313020644, 1e-10);
assert_float_closeness(state.imags[2], 0.0, 1e-10);
assert_float_closeness(state.reals[3], 0.20171134005566746, 1e-10);
assert_float_closeness(state.imags[3], 0.0, 1e-10);
assert_float_closeness(state.reals[4], 0.3692301313020644, 1e-10);
assert_float_closeness(state.imags[4], 0.0, 1e-10);
assert_float_closeness(state.reals[5], 0.20171134005566746, 1e-10);
assert_float_closeness(state.imags[5], 0.0, 1e-10);
assert_float_closeness(state.reals[6], 0.20171134005566746, 1e-10);
assert_float_closeness(state.imags[6], 0.0, 1e-10);
assert_float_closeness(state.reals[7], 0.11019540730213864, 1e-10);
assert_float_closeness(state.imags[7], 0.0, 1e-10);
}
#[test]
fn rz_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::RZ(1.0), &mut state, i);
}
assert_float_closeness(state.reals[0], 0.07073720166770296, 1e-10);
assert_float_closeness(state.imags[0], -0.9974949866040546, 1e-10);
assert_float_closeness(state.reals[1], 0.0, 1e-10);
assert_float_closeness(state.imags[1], 0.0, 1e-10);
assert_float_closeness(state.reals[2], 0.0, 1e-10);
assert_float_closeness(state.imags[2], 0.0, 1e-10);
assert_float_closeness(state.reals[3], 0.0, 1e-10);
assert_float_closeness(state.imags[3], 0.0, 1e-10);
assert_float_closeness(state.reals[4], 0.0, 1e-10);
assert_float_closeness(state.imags[4], 0.0, 1e-10);
assert_float_closeness(state.reals[5], 0.0, 1e-10);
assert_float_closeness(state.imags[5], 0.0, 1e-10);
assert_float_closeness(state.reals[6], 0.0, 1e-10);
assert_float_closeness(state.imags[6], 0.0, 1e-10);
assert_float_closeness(state.reals[7], 0.0, 1e-10);
assert_float_closeness(state.imags[7], 0.0, 1e-10);
}
#[test]
fn value_encoding_3_qubits() {
let v = 2.4;
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::H, &mut state, i);
}
for i in 0..n {
apply(Gate::P(2.0 * PI / (pow2f(i + 1)) * v), &mut state, i);
}
let targets: Vec<usize> = (0..n).rev().collect();
iqft(&mut state, &targets);
}
#[test]
fn value_encoding_20_qubits() {
let v = 2.4;
let n = 20;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::H, &mut state, i);
}
for i in 0..n {
apply(Gate::P(2.0 * PI / (pow2f(i + 1)) * v), &mut state, i);
}
let targets: Vec<usize> = (0..n).rev().collect();
iqft(&mut state, &targets);
}
#[test]
fn qcbm_3_qubits() {
let state = qcbm_functional(3);
assert_float_closeness(state.reals[0], 0.18037770683997864, 1e-10);
assert_float_closeness(state.imags[0], -0.17626993141958947, 1e-10);
assert_float_closeness(state.reals[7], 0.014503954556966365, 1e-10);
assert_float_closeness(state.imags[7], -0.11198008105074927, 1e-10);
}
#[test]
fn qcbm_20_qubits() {
let state = qcbm_functional(20);
assert_float_closeness(state.reals[0], -0.0022221321676945643, 1e-10);
assert_float_closeness(state.imags[0], 0.001743068112560825, 1e-10);
assert_float_closeness(state.reals[7], -0.0031017461877124453, 1e-10);
assert_float_closeness(state.imags[7], -0.0034043237120339686, 1e-10);
assert_float_closeness(state.reals[12], 0.0005494086136357235, 1e-10);
assert_float_closeness(state.imags[12], -0.00009827749580581964, 1e-10);
}
#[test]
fn u_gate_3_qubits() {
let n = 3;
let mut state = State::new(n);
for i in 0..n {
apply(Gate::U(1.0, 1.0, 1.0), &mut state, i);
}
assert_float_closeness(state.reals[0], 0.6758712218347053, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[1], 0.19949589133850137, 1e-10);
assert_float_closeness(state.imags[1], 0.3106964422074971, 1e-10);
assert_float_closeness(state.reals[2], 0.19949589133850137, 1e-10);
assert_float_closeness(state.imags[2], 0.3106964422074971, 1e-10);
assert_float_closeness(state.reals[3], -0.08394153605985091, 1e-10);
assert_float_closeness(state.imags[3], 0.18341560247417849, 1e-10);
assert_float_closeness(state.reals[2], 0.19949589133850137, 1e-10);
assert_float_closeness(state.imags[2], 0.3106964422074971, 1e-10);
assert_float_closeness(state.reals[3], -0.08394153605985091, 1e-10);
assert_float_closeness(state.imags[3], 0.18341560247417849, 1e-10);
assert_float_closeness(state.reals[3], -0.08394153605985091, 1e-10);
assert_float_closeness(state.imags[3], 0.18341560247417849, 1e-10);
assert_float_closeness(state.reals[7], -0.1090926263889472, 1e-10);
assert_float_closeness(state.imags[7], 0.015550776766638148, 1e-10);
}
#[test]
fn u_gate_1_qubit() {
let mut state = State::new(1);
let lambda = 1.0;
let theta = 2.0;
let phi = 3.0;
apply(Gate::U(theta, phi, lambda), &mut state, 0);
assert_float_closeness(state.reals[0], 0.5403023058681398, 1e-10);
assert_float_closeness(state.imags[0], 0.0, 1e-10);
assert_float_closeness(state.reals[1], -0.833049961066805, 1e-10);
assert_float_closeness(state.imags[1], 0.11874839215823475, 1e-10);
let mut state = State::new(1);
apply(Gate::X, &mut state, 0);
apply(Gate::U(theta, phi, lambda), &mut state, 0);
assert_float_closeness(state.reals[0], -0.4546487134128409, 1e-10);
assert_float_closeness(state.imags[0], -0.7080734182735712, 1e-10);
assert_float_closeness(state.reals[1], -0.35316515556860967, 1e-10);
assert_float_closeness(state.imags[1], -0.4089021333016357, 1e-10);
}
#[test]
fn swap_9_qubits() {
const N: usize = 9;
let mut state_0 = gen_random_state(N);
let mut state_1 = state_0.clone();
let (t0, t1) = (0, 1);
swap(&mut state_0, t0, t1);
swap_apply(&mut state_1, t0, t1);
assert_eq!(state_0.n, state_1.n);
assert_eq!(state_0.reals, state_1.reals);
assert_eq!(state_0.imags, state_1.imags);
}
#[test]
fn swap_all_qubits() {
const N: usize = 3;
let mut state_0 = gen_random_state(N);
let mut state_1 = state_0.clone();
for i in 0..(N >> 1) {
swap(&mut state_0, i, N - 1 - i);
swap_apply(&mut state_1, i, N - 1 - i);
}
assert_eq!(state_0.n, state_1.n);
assert_eq!(state_0.reals, state_1.reals);
assert_eq!(state_0.imags, state_1.imags);
}
#[test]
fn h_inverse() {
let h = Gate::H.to_matrix();
let h_inv = Gate::H.inverse().to_matrix();
let identity = mat_mul_2x2(h, h_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn x_inverse() {
let x = Gate::X.to_matrix();
let x_inv = Gate::X.inverse().to_matrix();
let identity = mat_mul_2x2(x, x_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn y_inverse() {
let y = Gate::Y.to_matrix();
let y_inv = Gate::Y.inverse().to_matrix();
let identity = mat_mul_2x2(y, y_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn z_inverse() {
let z = Gate::Z.to_matrix();
let z_inv = Gate::Z.inverse().to_matrix();
let identity = mat_mul_2x2(z, z_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn p_inverse() {
let p = Gate::P(2.03).to_matrix();
let p_inv = Gate::P(2.03).inverse().to_matrix();
let identity = mat_mul_2x2(p, p_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn rx_inverse() {
let rx = Gate::RX(2.03).to_matrix();
let rx_inv = Gate::RX(2.03).inverse().to_matrix();
let identity = mat_mul_2x2(rx, rx_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn rz_inverse() {
let rz = Gate::RZ(3.03).to_matrix();
let rz_inv = Gate::RZ(3.03).inverse().to_matrix();
let identity = mat_mul_2x2(rz, rz_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn ry_inverse() {
let ry = Gate::RY(3.03).to_matrix();
let ry_inv = Gate::RY(3.03).inverse().to_matrix();
let identity = mat_mul_2x2(ry, ry_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn u_inverse() {
let u = Gate::U(1.0, 2.0, 3.0).to_matrix();
let u_inv = Gate::U(1.0, 2.0, 3.0).inverse().to_matrix();
let identity = mat_mul_2x2(u, u_inv);
assert_float_closeness(identity[0].re, 1.0, 0.001);
assert_float_closeness(identity[0].im, 0.0, 0.001);
assert_float_closeness(identity[1].re, 0.0, 0.001);
assert_float_closeness(identity[1].im, 0.0, 0.001);
assert_float_closeness(identity[2].re, 0.0, 0.001);
assert_float_closeness(identity[2].im, 0.0, 0.001);
assert_float_closeness(identity[3].re, 1.0, 0.001);
assert_float_closeness(identity[3].im, 0.0, 0.001);
}
#[test]
fn unitary_inverse() {}
#[test]
#[should_panic]
fn m_inverse() {
let _m = Gate::M;
let _m_inv = Gate::M.inverse();
}
#[test]
#[should_panic]
fn swap_inverse() {
let _swap = Gate::SWAP(0, 1);
let _swap_inv = Gate::SWAP(0, 1).inverse().to_matrix();
}
#[test]
fn unitary_column_vector_multiplication() {
let u1 = Unitary {
height: 2,
width: 2,
reals: vec![1.0, 0.0, 0.0, 1.0],
imags: vec![0.0, 0.0, 0.0, 0.0],
};
let vec_reals1 = vec![2.0, 3.0];
let vec_imags1 = vec![4.0, 5.0];
let (result_reals1, result_imags1) = unitary_col_vec_mul(&u1, &vec_reals1, &vec_imags1);
assert_eq!(result_reals1, vec![2.0, 3.0]);
assert_eq!(result_imags1, vec![4.0, 5.0]);
let u2 = Unitary {
height: 2,
width: 2,
reals: vec![0.0, 1.0, 1.0, 0.0],
imags: vec![0.0, 0.0, 0.0, 0.0],
};
let vec_reals2 = vec![2.0, 3.0];
let vec_imags2 = vec![4.0, 5.0];
let (result_reals2, result_imags2) = unitary_col_vec_mul(&u2, &vec_reals2, &vec_imags2);
assert_eq!(result_reals2, vec![3.0, 2.0]);
assert_eq!(result_imags2, vec![5.0, 4.0]);
}
#[test]
fn ch() {
let n = 3;
let mut state = State::new(n);
for t in 0..n {
apply(Gate::H, &mut state, t);
}
state
.reals
.iter()
.zip(state.imags.iter())
.for_each(|(z_re, z_im)| {
assert_float_closeness(*z_re, 0.353553391, 0.0001);
assert_float_closeness(*z_im, 0.0, 0.0001);
});
c_apply(Gate::H, &mut state, 0, 1);
let mut i = 0;
while i < state.len() - 3 {
assert_float_closeness(state.reals[i], 0.353553391, 0.0001);
assert_float_closeness(state.reals[i + 1], 0.5, 0.0001);
assert_float_closeness(state.reals[i + 2], 0.353553391, 0.0001);
assert_float_closeness(state.reals[i + 3], 0.0, 0.0001);
assert_float_closeness(state.imags[i], 0.0, 0.0001);
assert_float_closeness(state.imags[i + 1], 0.0, 0.0001);
assert_float_closeness(state.imags[i + 2], 0.0, 0.0001);
assert_float_closeness(state.imags[i + 3], 0.0, 0.0001);
i += 4;
}
}
#[test]
fn crz() {
let n = 3;
let mut state = State::new(n);
for t in 0..n {
apply(Gate::H, &mut state, t);
}
state
.reals
.iter()
.zip(state.imags.iter())
.for_each(|(z_re, z_im)| {
assert_float_closeness(*z_re, 0.353553391, 0.0001);
assert_float_closeness(*z_im, 0.0, 0.0001);
});
c_apply(Gate::RZ(PI / 2.0), &mut state, 0, 1);
let mut i = 0;
while i < state.len() - 3 {
assert_float_closeness(state.reals[i], 0.353553391, 0.0001);
assert_float_closeness(state.imags[i], 0.0, 0.0001);
assert_float_closeness(state.reals[i + 1], 0.25, 0.0001);
assert_float_closeness(state.imags[i + 1], -0.25, 0.0001);
assert_float_closeness(state.reals[i + 2], 0.353553391, 0.0001);
assert_float_closeness(state.imags[i + 2], 0.0, 0.0001);
assert_float_closeness(state.reals[i + 3], 0.25, 0.0001);
assert_float_closeness(state.imags[i + 3], 0.25, 0.0001);
i += 4;
}
}
#[test]
fn bit_flip_noise() {
const N: usize = 1;
let state0 = gen_random_state(N);
let mut state1 = state0.clone();
apply(Gate::BitFlipNoise(0.0), &mut state1, 0);
assert_eq!(
state0.reals, state1.reals,
"BitFlipNoise with prob=0.0 should have no effect"
);
assert_eq!(
state0.imags, state1.imags,
"BitFlipNoise with prob=0.0 should have no effect"
);
apply(Gate::BitFlipNoise(1.0), &mut state1, 0);
assert_float_closeness(state1.reals[0], state0.reals[1], 0.00001);
assert_float_closeness(state1.reals[1], state0.reals[0], 0.00001);
assert_float_closeness(state1.imags[0], state0.imags[1], 0.00001);
assert_float_closeness(state1.imags[1], state0.imags[0], 0.00001);
}
#[test]
fn controlled_u() {
const N: usize = 3;
let mut state = State::new(N);
for target in 0..N {
h_apply(&mut state, target);
}
u_c_apply(&mut state, 1.0, 2.0, 3.0, 0, 1);
let expected_reals = [
0.35355339, 0.47807852, 0.35355339, 0.01747458, 0.35355339, 0.47807852, 0.35355339,
0.01747458,
];
let expected_imags = [
0.0,
-0.0239202,
0.0,
-0.14339942,
0.0,
-0.0239202,
0.0,
-0.14339942,
];
state
.reals
.iter()
.zip(expected_reals.iter())
.for_each(|(actual, expected)| {
assert_float_closeness(*actual, *expected, 0.00001);
});
state
.imags
.iter()
.zip(expected_imags.iter())
.for_each(|(actual, expected)| {
assert_float_closeness(*actual, *expected, 0.00001);
});
}
}