use super::{probability, Circuit, Error, Result, Window, WindowMode, WindowOptions};
use crate::memory::{CodeKind, NoiseModel};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Basis {
X,
Z,
}
impl Basis {
pub(crate) fn engine(self) -> crate::circuit::Basis {
match self {
Basis::X => crate::circuit::Basis::X,
Basis::Z => crate::circuit::Basis::Z,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SurfaceCode {
Rotated,
Xzzx,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum Noise {
Sd6 {
p: f64,
},
Biased {
p: f64,
eta: f64,
},
}
pub fn memory_circuit(code: SurfaceCode, distance: usize, rounds: usize, noise: Noise, basis: Basis) -> Result<Circuit> {
let kind = match code {
SurfaceCode::Rotated => CodeKind::Rotated,
SurfaceCode::Xzzx => CodeKind::Xzzx,
};
let noise = match noise {
Noise::Sd6 { p } => NoiseModel::Sd6 { p },
Noise::Biased { p, eta } => NoiseModel::Current { p, eta },
};
Circuit::from_engine(crate::memory::generate(kind, distance, rounds, noise, basis.engine())?)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum GrossOperator {
F,
Gh,
FTimesGh,
}
impl GrossOperator {
fn name(self) -> &'static str {
match self {
GrossOperator::F => "f",
GrossOperator::Gh => "gh",
GrossOperator::FTimesGh => "f+gh",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Automorphism {
pub shift: (usize, usize),
pub dual: bool,
pub action: Vec<Vec<usize>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Gauging {
pub support: Vec<usize>,
pub edges: Vec<usize>,
pub extra_edges: Vec<(usize, usize)>,
pub incidence: Vec<Vec<usize>>,
pub gauss: Vec<Vec<usize>>,
pub flux: Vec<Vec<usize>>,
pub hx: Vec<Vec<usize>>,
pub hz: Vec<Vec<usize>>,
pub ticks: usize,
pub worst_cut: (usize, usize),
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct StreamResult {
pub failures: usize,
pub shots: usize,
pub window_seconds: Vec<Vec<f64>>,
pub windows: Vec<Window>,
pub seconds: f64,
}
#[allow(clippy::too_many_arguments)]
pub fn stream_memory(
code: SurfaceCode,
distance: usize,
rounds: usize,
p: f64,
windows: WindowOptions,
shots: usize,
seed: u64,
threads: usize,
) -> Result<StreamResult> {
probability(p, "p")?;
if rounds == 0 {
return Err(Error::new("rounds must be at least 1"));
}
let kind = match code {
SurfaceCode::Rotated => CodeKind::Rotated,
SurfaceCode::Xzzx => CodeKind::Xzzx,
};
let (commit, buffer, mode, correlations) = windows.parts();
let mode = match mode {
WindowMode::Sliding => crate::window::Mode::Sliding,
WindowMode::Parallel => crate::window::Mode::Parallel,
};
let batches = shots.max(1).div_ceil(64);
let out = crate::batch::stream_shots(kind, distance, p, rounds, commit, buffer, mode, correlations, batches, seed, threads)?;
stream_result(out)
}
pub fn stream_circuit(circuit: &Circuit, windows: WindowOptions, shots: usize, seed: u64, threads: usize) -> Result<StreamResult> {
let (commit, buffer, mode, correlations) = windows.parts();
let mode = match mode {
WindowMode::Sliding => crate::window::Mode::Sliding,
WindowMode::Parallel => crate::window::Mode::Parallel,
};
let batches = shots.max(1).div_ceil(64);
let out = crate::batch::stream_circuit(&circuit.inner, commit, buffer, mode, correlations, batches, seed, threads)?;
stream_result(out)
}
fn stream_result(out: crate::batch::StreamOutcome) -> Result<StreamResult> {
if out.unexplained > 0 {
return Err(Error::new(format!(
"internal error in stabilizer_qec: window commits left {} defects unexplained; please report it",
out.unexplained
)));
}
let windows: Vec<Window> = out.windows.into_iter().map(Window::from_info).collect();
let per = windows.len().max(1);
Ok(StreamResult {
failures: out.failures,
shots: out.shots,
window_seconds: out.times.chunks(per).map(<[f64]>::to_vec).collect(),
windows,
seconds: out.seconds,
})
}
#[derive(Clone, Debug)]
pub struct BivariateBicycleCode {
inner: crate::bb::BbCode,
gross: bool,
}
impl BivariateBicycleCode {
pub fn gross() -> BivariateBicycleCode {
BivariateBicycleCode::wrap(crate::bb::BbCode::gross())
}
pub fn bb72() -> BivariateBicycleCode {
BivariateBicycleCode::wrap(crate::bb::BbCode::bb72())
}
pub fn bb90() -> BivariateBicycleCode {
BivariateBicycleCode::wrap(crate::bb::BbCode::bb90())
}
pub fn bb108() -> BivariateBicycleCode {
BivariateBicycleCode::wrap(crate::bb::BbCode::bb108())
}
pub fn bb288() -> BivariateBicycleCode {
BivariateBicycleCode::wrap(crate::bb::BbCode::bb288())
}
pub fn named(name: &str) -> Result<BivariateBicycleCode> {
Ok(BivariateBicycleCode::wrap(crate::bb::BbCode::named(name)?))
}
pub fn from_polynomials(l: usize, m: usize, a: [(usize, usize); 3], b: [(usize, usize); 3]) -> Result<BivariateBicycleCode> {
Ok(BivariateBicycleCode::wrap(crate::bb::BbCode::new(l, m, a, b)?))
}
fn wrap(inner: crate::bb::BbCode) -> BivariateBicycleCode {
let gross = inner == crate::bb::BbCode::gross();
BivariateBicycleCode { inner, gross }
}
pub fn n(&self) -> usize {
2 * self.inner.l * self.inner.m
}
pub fn k(&self) -> usize {
self.inner.logicals().0.rows
}
pub fn check_matrices(&self) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
let rows = |m: &crate::gf2::BitMatrix| (0..m.rows).map(|r| m.row_ones(r)).collect();
(rows(&self.inner.hx()), rows(&self.inner.hz()))
}
pub fn logicals(&self) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
let (lx, lz) = self.inner.logicals();
let rows = |m: &crate::gf2::BitMatrix| (0..m.rows).map(|r| m.row_ones(r)).collect();
(rows(&lx), rows(&lz))
}
pub fn memory_circuit(&self, cycles: usize, p: f64, basis: Basis) -> Result<Circuit> {
crate::bb::check_cycles(cycles, cycles)?;
probability(p, "p")?;
let c = match basis {
Basis::Z => crate::circuit::Circuit::parse(&self.inner.memory_z(cycles, p))?,
Basis::X => crate::bb_circuit::memory(&self.inner, basis.engine(), cycles, p),
};
Circuit::from_engine(c)
}
pub fn automorphisms(&self) -> Result<Vec<Automorphism>> {
use crate::bb_auto::Automorphism as Engine;
let mut out = Vec::with_capacity(2 * self.inner.l * self.inner.m);
for a in 0..self.inner.l {
for b in 0..self.inner.m {
for dual in [false, true] {
let m = self.inner.logical_action(Engine { shift: (a, b), dual })?;
out.push(Automorphism { shift: (a, b), dual, action: (0..m.rows).map(|r| m.row_ones(r)).collect() });
}
}
}
Ok(out)
}
pub fn gauging(&self, operator: GrossOperator, expanded: bool) -> Result<Gauging> {
if !self.gross {
return Err(Error::new("logical measurements are built for the gross code only"));
}
let support = crate::bb::gross_operator(operator.name())?;
let g = if expanded {
crate::bb_gauge::Gauging::expanded(&self.inner, &support)
} else {
crate::bb_gauge::Gauging::new(&self.inner, &support)
}?;
let (hx, hz) = g.deformed(&self.inner);
let rows = |m: &crate::gf2::BitMatrix| (0..m.rows).map(|r| m.row_ones(r)).collect();
let ticks = crate::bb_circuit::merged_cycle(&self.inner, &g).ticks.len();
let (boundary, side) = g.cheeger();
Ok(Gauging {
support: g.support.clone(),
edges: g.edges.clone(),
extra_edges: g.extra.clone(),
incidence: g.incidence.clone(),
gauss: g.gauss.clone(),
flux: g.flux.clone(),
hx: rows(&hx),
hz: rows(&hz),
ticks,
worst_cut: (boundary, side.len()),
})
}
#[allow(clippy::too_many_arguments)]
pub fn logical_measurement_circuit(
&self,
operator: GrossOperator,
basis: Basis,
pre: usize,
merged: usize,
post: usize,
p: f64,
expanded: bool,
) -> Result<Circuit> {
if !self.gross {
return Err(Error::new("logical measurements are built for the gross code only"));
}
crate::bb::check_cycles(pre.saturating_add(merged).saturating_add(post).max(1), merged)?;
probability(p, "p")?;
let support = crate::bb::gross_operator(operator.name())?;
let g = if expanded {
crate::bb_gauge::Gauging::expanded(&self.inner, &support)
} else {
crate::bb_gauge::Gauging::new(&self.inner, &support)
}?;
Circuit::from_engine(crate::bb_circuit::logical_measurement(&self.inner, &g, basis.engine(), pre, merged, post, p)?)
}
}
#[derive(Clone, Debug)]
pub struct CssCode {
inner: crate::css::CssCode,
}
impl CssCode {
pub fn new(n: usize, hx: Vec<Vec<usize>>, hz: Vec<Vec<usize>>) -> Result<CssCode> {
Ok(CssCode { inner: crate::css::CssCode::new(n, hx, hz)? })
}
pub fn hypergraph_product(h1: &[Vec<usize>], n1: usize, h2: &[Vec<usize>], n2: usize) -> Result<CssCode> {
Ok(CssCode { inner: crate::css::CssCode::hypergraph_product(h1, n1, h2, n2)? })
}
pub fn color_code(distance: usize) -> Result<CssCode> {
Ok(CssCode { inner: crate::css::CssCode::color_code(distance)? })
}
pub fn n(&self) -> usize {
self.inner.n
}
pub fn k(&self) -> usize {
self.inner.k()
}
pub fn check_matrices(&self) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
(self.inner.hx.clone(), self.inner.hz.clone())
}
pub fn logicals(&self) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
self.inner.logicals()
}
pub fn memory_circuit(&self, rounds: usize, p: f64, basis: Basis) -> Result<Circuit> {
Circuit::parse(&self.inner.memory(rounds, p, basis == Basis::X)?)
}
pub fn memory_circuit_with_colors(&self, rounds: usize, p: f64, basis: Basis) -> Result<Circuit> {
Circuit::parse(&self.inner.memory_annotated(rounds, p, basis == Basis::X, true)?)
}
}