use crate::photonic::stabilizer::{Pauli, PauliProduct};
use crate::photonic::{FusionProgram, NodeId, ResourceState};
use crate::{Error, Result};
#[derive(Clone, Debug)]
pub struct SyndromeGraph {
vertices: usize,
edges: Vec<(usize, usize)>,
cuts: Vec<[bool; 3]>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MatchingDecode {
Correction(Vec<bool>),
BudgetExceeded,
Inconsistent,
}
impl SyndromeGraph {
pub(super) fn from_edges(
vertices: usize,
edges: Vec<(usize, usize)>,
cuts: Vec<[bool; 3]>,
) -> Result<Self> {
if vertices == 0
|| edges.len() != cuts.len()
|| edges
.iter()
.any(|&(a, b)| a >= vertices || b >= vertices || a == b)
{
return Err(Error::Shape("invalid syndrome graph incidence".into()));
}
Ok(Self {
vertices,
edges,
cuts,
})
}
pub fn vertices(&self) -> usize {
self.vertices
}
pub fn edges(&self) -> &[(usize, usize)] {
&self.edges
}
pub fn cuts(&self) -> &[[bool; 3]] {
&self.cuts
}
pub fn erasure_logicals(&self, erased: &[bool]) -> Result<[bool; 3]> {
if erased.len() != self.edges.len() {
return Err(Error::Shape("erased edge count".into()));
}
let mut parent: Vec<_> = (0..self.vertices).collect();
let mut potential = vec![[false; 3]; self.vertices];
let mut ambiguous = [false; 3];
fn find(parent: &[usize], potential: &[[bool; 3]], mut q: usize) -> (usize, [bool; 3]) {
let mut p = [false; 3];
while parent[q] != q {
for j in 0..3 {
p[j] ^= potential[q][j];
}
q = parent[q];
}
(q, p)
}
for (e, &lost) in erased.iter().enumerate() {
if lost {
let (a, b) = self.edges[e];
let (ra, pa) = find(&parent, &potential, a);
let (rb, pb) = find(&parent, &potential, b);
let mut delta = [false; 3];
for j in 0..3 {
delta[j] = pa[j] ^ pb[j] ^ self.cuts[e][j];
}
if ra == rb {
for j in 0..3 {
ambiguous[j] |= delta[j];
}
} else {
parent[ra] = rb;
potential[ra] = delta;
}
}
}
Ok(ambiguous)
}
pub fn syndrome(&self, errors: &[bool]) -> Result<Vec<bool>> {
if errors.len() != self.edges.len() {
return Err(Error::Shape("syndrome edge count".into()));
}
let mut s = vec![false; self.vertices];
for (&e, &(a, b)) in errors.iter().zip(&self.edges) {
if e {
s[a] ^= true;
s[b] ^= true;
}
}
Ok(s)
}
pub fn logical_parities(&self, errors: &[bool]) -> Result<[bool; 3]> {
if errors.len() != self.edges.len() {
return Err(Error::Shape("logical edge count".into()));
}
let mut l = [false; 3];
for (&e, cut) in errors.iter().zip(&self.cuts) {
if e {
for j in 0..3 {
l[j] ^= cut[j];
}
}
}
Ok(l)
}
pub fn checks(&self, max_entries: usize) -> Result<Vec<Vec<bool>>> {
let required = self
.vertices
.checked_mul(self.edges.len())
.ok_or(Error::Overflow)?;
if required > max_entries {
return Err(Error::ResourceLimit {
resource: "syndrome matrix entries",
required,
limit: max_entries,
});
}
let mut h = vec![vec![false; self.edges.len()]; self.vertices];
for (i, &(a, b)) in self.edges.iter().enumerate() {
h[a][i] ^= true;
h[b][i] ^= true;
}
Ok(h)
}
pub fn decode(
&self,
syndrome: &[bool],
erased: &[bool],
max_defects: usize,
) -> Result<MatchingDecode> {
if syndrome.len() != self.vertices || erased.len() != self.edges.len() {
return Err(Error::Shape("matching input shape".into()));
}
if max_defects > 20 {
return Err(Error::InvalidValue(
"matching defect budget exceeds 20".into(),
));
}
let defects: Vec<_> = syndrome
.iter()
.enumerate()
.filter_map(|(i, &b)| b.then_some(i))
.collect();
let n = defects.len();
if !n.is_multiple_of(2) {
return Ok(MatchingDecode::Inconsistent);
}
if n > max_defects {
return Ok(MatchingDecode::BudgetExceeded);
}
if n == 0 {
return Ok(MatchingDecode::Correction(vec![false; self.edges.len()]));
}
let mut paths = vec![vec![vec![]; n]; n];
let mut costs = vec![vec![usize::MAX; n]; n];
for (i, &source) in defects.iter().enumerate() {
let mut distance = vec![usize::MAX; self.vertices];
let mut previous = vec![None; self.vertices];
let mut visited = vec![false; self.vertices];
distance[source] = 0;
for _ in 0..self.vertices {
let Some(u) = (0..self.vertices)
.filter(|&v| !visited[v] && distance[v] != usize::MAX)
.min_by_key(|&v| (distance[v], v))
else {
break;
};
visited[u] = true;
for (e, &(a, b)) in self.edges.iter().enumerate() {
let next = if a == u {
Some(b)
} else if b == u {
Some(a)
} else {
None
};
if let Some(v) = next {
let d = distance[u] + usize::from(!erased[e]);
if d < distance[v] {
distance[v] = d;
previous[v] = Some((u, e));
}
}
}
}
for j in i + 1..n {
let mut v = defects[j];
costs[i][j] = distance[v];
while v != source {
let Some((u, e)) = previous[v] else {
return Ok(MatchingDecode::Inconsistent);
};
paths[i][j].push(e);
v = u;
}
}
}
let states = 1usize << n;
let mut best = vec![usize::MAX; states];
let mut choice = vec![0usize; states];
best[0] = 0;
for mask in 1..states {
if !mask.count_ones().is_multiple_of(2) {
continue;
}
let i = mask.trailing_zeros() as usize;
let rest = mask ^ (1 << i);
for (j, &cost) in costs[i].iter().enumerate().skip(i + 1) {
if rest & (1 << j) != 0 {
let sub = rest ^ (1 << j);
if let Some(value) = best[sub].checked_add(cost)
&& value < best[mask]
{
best[mask] = value;
choice[mask] = j;
}
}
}
}
if best[states - 1] == usize::MAX {
return Ok(MatchingDecode::Inconsistent);
}
let mut correction = vec![false; self.edges.len()];
let mut mask = states - 1;
while mask != 0 {
let i = mask.trailing_zeros() as usize;
let j = choice[mask];
for &e in &paths[i][j] {
correction[e] ^= true;
}
mask ^= (1 << i) | (1 << j);
}
Ok(MatchingDecode::Correction(correction))
}
}
#[derive(Clone, Debug)]
pub struct FourStarTorus {
distance: usize,
program: FusionProgram,
primal: SyndromeGraph,
dual: SyndromeGraph,
}
impl FourStarTorus {
pub fn new(
distance: usize,
source: NodeId,
analyzer: NodeId,
max_photons: usize,
) -> Result<Self> {
if distance < 2 {
return Err(Error::InvalidDimensions(
"four-star periodic distance must be >=2".into(),
));
}
let cells = distance.checked_pow(3).ok_or(Error::Overflow)?;
let photons = cells.checked_mul(24).ok_or(Error::Overflow)?;
let max_photons = max_photons.min(1 << 20);
if photons > max_photons {
return Err(Error::ResourceLimit {
resource: "four-star photons",
required: photons,
limit: max_photons,
});
}
let mut program = FusionProgram::new();
let edge_state = ResourceState::four_star().with_local_hadamards(&[0, 1, 2, 3])?;
let mut edge_ports = Vec::with_capacity(cells * 3);
let mut face_ports = Vec::with_capacity(cells * 3);
for _ in 0..cells * 3 {
edge_ports.push(program.add_resource(edge_state.clone(), source)?);
}
for _ in 0..cells * 3 {
face_ports.push(program.add_resource(ResourceState::four_star(), source)?);
}
let mut used = vec![0; cells * 3];
let mut primal = SyndromeGraph {
vertices: cells,
edges: vec![],
cuts: vec![],
};
let mut dual = primal.clone();
for cell in 0..cells {
let c = coords(cell, distance);
for normal in 0..3 {
let a = (normal + 1) % 3;
let b = (normal + 2) % 3;
let incident = [
(c, a),
(c, b),
(shift(c, b, 1, distance), a),
(shift(c, a, 1, distance), b),
];
for (port, (base, axis)) in incident.into_iter().enumerate() {
let edge = idx(base, distance) * 3 + axis;
let q = edge_ports[edge][used[edge]];
used[edge] += 1;
program.fuse(face_ports[cell * 3 + normal][port], q, analyzer)?;
primal.edges.push((
cell,
idx(shift(c, normal, distance - 1, distance), distance),
));
let mut cut = [false; 3];
cut[normal] = c[normal] == 0;
primal.cuts.push(cut);
dual.edges.push((
idx(base, distance),
idx(shift(base, axis, 1, distance), distance),
));
let mut cut = [false; 3];
cut[axis] = base[axis] == distance - 1;
dual.cuts.push(cut);
}
}
}
debug_assert!(used.iter().all(|&n| n == 4));
Ok(Self {
distance,
program,
primal,
dual,
})
}
pub fn distance(&self) -> usize {
self.distance
}
pub fn program(&self) -> &FusionProgram {
&self.program
}
pub fn primal(&self) -> &SyndromeGraph {
&self.primal
}
pub fn dual(&self) -> &SyndromeGraph {
&self.dual
}
pub fn observables(&self) -> Result<Vec<PauliProduct>> {
self.program
.fusions()
.iter()
.flat_map(|f| {
[Pauli::X, Pauli::Z]
.map(|p| PauliProduct::new(f.photons.iter().map(|q| (q.0, p)).collect()))
})
.collect()
}
}
fn coords(i: usize, l: usize) -> [usize; 3] {
[i / (l * l), (i / l) % l, i % l]
}
fn idx(c: [usize; 3], l: usize) -> usize {
(c[0] * l + c[1]) * l + c[2]
}
fn shift(mut c: [usize; 3], axis: usize, step: usize, l: usize) -> [usize; 3] {
c[axis] = (c[axis] + step) % l;
c
}