use super::*;
use crate::photonic::qec::threshold::{DecoderBudget, GraphDecode, GraphStatus};
use crate::photonic::stabilizer::{Pauli, PauliProduct, Tableau};
use crate::{Error, Result};
#[derive(Clone, Debug)]
pub struct SurgeryRecovery {
pub decode: GraphDecode,
pub parity: Option<bool>,
pub corrected_records: Option<Vec<bool>>,
pub correction: Option<PauliProduct>,
patch_qubits: usize,
}
impl SurgeryRecovery {
pub fn apply(&self, state: &mut Tableau, offsets: [usize; 2]) -> Result<()> {
let Some(frame) = &self.correction else {
return Err(Error::Unsupported(
"surgery decoder has no applicable frame".into(),
));
};
if self.decode.status != GraphStatus::Corrected {
return Err(Error::Unsupported(
"surgery decoder status is not corrected".into(),
));
}
let n = self.patch_qubits;
if offsets[0].abs_diff(offsets[1]) < n
|| offsets
.iter()
.any(|o| o.checked_add(n).is_none_or(|end| end > state.qubits()))
{
return Err(Error::Wires(
"invalid surgery recovery patch offsets".into(),
));
}
if frame.terms().iter().any(|(q, _)| *q >= 2 * n) {
return Err(Error::Wires(
"surgery recovery frame outside patches".into(),
));
}
let actual = PauliProduct::new(
frame
.terms()
.iter()
.map(|&(q, p)| (offsets[q / n] + q % n, p))
.collect(),
)?;
super::execution::apply_pauli(state, &actual)
}
}
impl SurgeryDetectorModel {
pub fn recover(
&self,
records: &[bool],
erased_faults: &[bool],
budget: DecoderBudget,
) -> Result<SurgeryRecovery> {
let syndrome = self.detector_values(records)?;
let decode = self
.graph()
.decode_union_find(&syndrome, erased_faults, budget)?;
let mut result = SurgeryRecovery {
decode,
parity: None,
corrected_records: None,
correction: None,
patch_qubits: self.plan().code().qubits(),
};
if result.decode.status != GraphStatus::Corrected {
return Ok(result);
}
let mut corrected = records.to_vec();
let n = 2 * result.patch_qubits;
let mut x = vec![false; n];
let mut z = x.clone();
let mut by_round = vec![vec![]; 2 * self.plan().code().distance()];
for (index, &active) in result.decode.correction.iter().enumerate() {
if !active {
continue;
}
match self.faults()[index] {
SurgeryFault::Data { round, site, pauli } => by_round[round].push((site, pauli)),
SurgeryFault::Readout { record } => corrected[record] ^= true,
}
}
let mut previous = None;
for (index, record) in self.records().iter().enumerate() {
if previous != Some(record.round) {
if let Some(events) = by_round.get(record.round) {
for &(site, pauli) in events {
match pauli {
Pauli::X => x[site] ^= true,
Pauli::Z => z[site] ^= true,
Pauli::Y => {
return Err(Error::Unsupported(
"unexpected non-CSS surgery fault".into(),
));
}
}
}
}
previous = Some(record.round);
}
for &(site, pauli) in record.observable.terms() {
corrected[index] ^= match pauli {
Pauli::X => z[site],
Pauli::Z => x[site],
Pauli::Y => x[site] ^ z[site],
};
}
}
if self.detector_values(&corrected)?.iter().any(|&v| v) {
return Err(Error::InvalidValue(
"surgery decoder failed to close observed syndrome".into(),
));
}
let d = self.plan().code().distance();
let start = d * self.plan().merged_checks().len();
let end = start + self.plan().split_checks().len();
for &(site, pauli) in self
.plan()
.split_correction(&corrected[start..end])?
.terms()
{
match pauli {
Pauli::X => x[site] ^= true,
Pauli::Z => z[site] ^= true,
Pauli::Y => {
x[site] ^= true;
z[site] ^= true;
}
}
}
result.parity = Some(corrected[..d].iter().fold(false, |v, &b| v ^ b));
result.corrected_records = Some(corrected);
result.correction = Some(PauliProduct::new(
(0..n)
.filter_map(|q| match (x[q], z[q]) {
(true, true) => Some((q, Pauli::Y)),
(true, false) => Some((q, Pauli::X)),
(false, true) => Some((q, Pauli::Z)),
_ => None,
})
.collect(),
)?);
Ok(result)
}
}