use super::{Program, Step};
use crate::statevec::C;
use crate::circuit::Basis;
pub(crate) trait Source {
fn bern(&mut self, p: f64) -> bool;
fn pick(&mut self, probs: &[f64]) -> Option<usize>;
fn index(&mut self, p: f64, n: u64) -> Option<u64>;
fn coin(&mut self) -> bool;
}
#[derive(Clone, Debug, Default)]
pub struct Shot {
pub records: Vec<bool>,
pub fired: Vec<bool>,
pub anti: Vec<bool>,
pub branch: Vec<bool>,
}
fn apply(x: &mut [bool], z: &mut [bool], q: usize, c: u8) {
x[q] ^= c & 1 != 0;
z[q] ^= c & 2 != 0;
}
impl Program {
pub(crate) fn run_shot(&self, src: &mut dyn Source) -> Shot {
let nq = self.num_qubits;
let (mut cx, mut cz) = (vec![false; nq], vec![false; nq]);
let (mut fx, mut fz) = (vec![false; nq], vec![false; nq]);
for z in cz.iter_mut() {
*z = src.coin();
}
let nl = self.locations.len();
let mut shot = Shot { records: Vec::with_capacity(self.num_records), fired: vec![false; nl], anti: vec![false; nl], branch: self.reference.clone() };
let mut chain = false;
let mut taus: Vec<Vec<C>> = vec![Vec::new(); self.merge_groups.len()];
macro_rules! both {
($q:expr, $c:expr) => {{
let (q, c) = ($q, $c);
apply(&mut cx, &mut cz, q, c);
apply(&mut fx, &mut fz, q, c);
}};
}
for step in &self.steps {
match *step {
Step::H(q) => {
std::mem::swap(&mut cx[q], &mut cz[q]);
std::mem::swap(&mut fx[q], &mut fz[q]);
}
Step::S(q) => {
cz[q] ^= cx[q];
fz[q] ^= fx[q];
}
Step::Pauli(..) => {}
Step::Cx(c, t) => {
if cx[c] {
cx[t] ^= true;
}
if cz[t] {
cz[c] ^= true;
}
if fx[c] {
fx[t] ^= true;
}
if fz[t] {
fz[c] ^= true;
}
}
Step::Cz(a, b) => {
let (ca, cb, fa, fb) = (cx[a], cx[b], fx[a], fx[b]);
cz[b] ^= ca;
cz[a] ^= cb;
fz[b] ^= fa;
fz[a] ^= fb;
}
Step::Measure { q, basis, reset, flip, slot } => {
let (c, f) = match basis {
Basis::Z => (cx[q], fx[q]),
Basis::X => (cz[q], fz[q]),
};
let mut r = c;
if flip > 0.0 && src.bern(flip) {
r = !r;
}
shot.records.push(r);
shot.branch[slot] ^= c ^ f;
match basis {
Basis::Z => cz[q] = src.coin(),
Basis::X => cx[q] = src.coin(),
}
if reset {
match basis {
Basis::Z => {
cx[q] = false;
cz[q] = src.coin();
}
Basis::X => {
cz[q] = false;
cx[q] = src.coin();
}
}
fx[q] = false;
fz[q] = false;
}
}
Step::Reset { q, basis, slot } => {
let (c, f) = match basis {
Basis::Z => (cx[q], fx[q]),
Basis::X => (cz[q], fz[q]),
};
shot.branch[slot] ^= c ^ f;
match basis {
Basis::Z => {
cx[q] = false;
cz[q] = src.coin();
}
Basis::X => {
cz[q] = false;
cx[q] = src.coin();
}
}
fx[q] = false;
fz[q] = false;
}
Step::PauliError { q, pauli, p } => {
if src.bern(p) {
both!(q, pauli);
}
}
Step::Depolarize1 { q, p } => {
if let Some(k) = src.index(p, 3) {
both!(q, 1 + k as u8);
}
}
Step::PauliChannel1 { q, px, py, pz } => {
if let Some(k) = src.pick(&[px, py, pz]) {
both!(q, [1, 3, 2][k]);
}
}
Step::Depolarize2 { a, b, p } => {
if let Some(k) = src.index(p, 15) {
let r = 1 + k as u8;
both!(a, r & 3);
both!(b, r >> 2);
}
}
Step::PauliChannel2 { a, b, ref probs } => {
if let Some(k) = src.pick(probs) {
const CODE: [u8; 4] = [0, 1, 3, 2];
both!(a, CODE[(k + 1) / 4]);
both!(b, CODE[(k + 1) % 4]);
}
}
Step::Correlated { p, ref paulis, chained } => {
let fires = src.bern(p) && !(chained && chain);
chain = if chained { chain || fires } else { fires };
if fires {
for &(q, c) in paulis {
both!(q, c);
}
}
}
Step::Herald { q, probs, slot } => {
let case = src.pick(&probs);
shot.records.push(case.is_some());
let _ = slot;
if let Some(k) = case {
both!(q, [0, 1, 3, 2][k]);
}
}
Step::Pad { flip, .. } => {
let r = flip > 0.0 && src.bern(flip);
shot.records.push(r);
}
Step::Coherent(loc) => {
let l = &self.locations[loc];
shot.anti[loc] = l.paulis.iter().fold(false, |a, &(q, c)| a ^ (c & 1 != 0 && fz[q]) ^ (c & 2 != 0 && fx[q]));
let p = match self.merge.get(loc).copied().flatten() {
None => l.theta.sin().powi(2),
Some(m) => {
taus[m.group].push(self.unfired_toggle(loc, m.mu, shot.anti[loc]));
if m.last { Program::odd_proposal(&taus[m.group]) } else { 0.0 }
}
};
if p != 0.0 && src.bern(p) {
shot.fired[loc] = true;
for &(q, c) in &l.paulis {
both!(q, c);
}
}
}
}
}
for q in 0..nq {
let s = self.end_slot(q);
shot.branch[s] ^= cx[q] ^ fx[q];
}
shot
}
pub fn outcome(&self, records: &[bool]) -> (Vec<bool>, u64) {
let parity = |r: &Vec<usize>| r.iter().fold(false, |a, &m| a ^ records[m]);
let dets = self.detectors.iter().map(parity).collect();
let obs = self.observables.iter().enumerate().fold(0u64, |acc, (k, r)| acc | (parity(r) as u64) << k);
(dets, obs)
}
}
pub(crate) struct Stream<'a>(pub &'a mut crate::surface_code::Xorshift);
impl Source for Stream<'_> {
fn bern(&mut self, p: f64) -> bool {
self.0.next_f64() < p
}
fn pick(&mut self, probs: &[f64]) -> Option<usize> {
let u = self.0.next_f64();
let mut acc = 0.0;
probs.iter().position(|p| {
acc += p;
u < acc
})
}
fn index(&mut self, p: f64, n: u64) -> Option<u64> {
if self.0.next_f64() < p {
Some(self.0.next_u64() % n)
} else {
None
}
}
fn coin(&mut self) -> bool {
self.0.next_u64() & 1 == 1
}
}