stim 0.1.0

Safe Rust bindings for Stim, a high-performance stabilizer circuit simulator and analyzer
Documentation
use std::collections::BTreeMap;
use std::str::FromStr;

use crate::{
    Circuit, CircuitErrorLocation, CircuitErrorLocationStackFrame, CircuitInstruction, CircuitItem,
    CircuitRepeatBlock, CircuitTargetsInsideInstruction, DemTarget, DemTargetWithCoords,
    ExplainedError, FlippedMeasurement, GateTarget, GateTargetWithCoords, PauliString, Result,
    StimError,
};

pub fn parse_detecting_regions_text(
    text: &str,
) -> Result<BTreeMap<DemTarget, BTreeMap<u64, PauliString>>> {
    let mut result: BTreeMap<DemTarget, BTreeMap<u64, PauliString>> = BTreeMap::new();
    for line in text.lines() {
        if line.is_empty() {
            continue;
        }
        let mut parts = line.splitn(3, '\t');
        let target = parts
            .next()
            .ok_or_else(|| StimError::new("missing detecting-region target"))?;
        let tick = parts
            .next()
            .ok_or_else(|| StimError::new("missing detecting-region tick"))?;
        let pauli = parts
            .next()
            .ok_or_else(|| StimError::new("missing detecting-region pauli string"))?;

        let target = DemTarget::from_text(target)?;
        let tick = tick
            .parse::<u64>()
            .map_err(|_| StimError::new("failed to parse detecting-region tick"))?;
        let pauli = PauliString::from_text(pauli)?;
        result.entry(target).or_default().insert(tick, pauli);
    }
    Ok(result)
}

fn convert_gate_target_with_coords(
    data: stim_cxx::GateTargetWithCoordsData,
) -> GateTargetWithCoords {
    GateTargetWithCoords::new(GateTarget::from_raw_data(data.raw_target), data.coords)
}

fn convert_dem_target_with_coords(
    data: stim_cxx::DemTargetWithCoordsData,
) -> Result<DemTargetWithCoords> {
    Ok(DemTargetWithCoords::new(
        DemTarget::from_text(&data.dem_target)?,
        data.coords,
    ))
}

fn convert_circuit_error_location_stack_frame(
    data: stim_cxx::CircuitErrorLocationStackFrameData,
) -> CircuitErrorLocationStackFrame {
    CircuitErrorLocationStackFrame::new(
        data.instruction_offset,
        data.iteration_index,
        data.instruction_repetitions_arg,
    )
}

fn convert_circuit_targets_inside_instruction(
    data: stim_cxx::CircuitTargetsInsideInstructionData,
) -> Result<CircuitTargetsInsideInstruction> {
    Ok(CircuitTargetsInsideInstruction::new(
        data.gate,
        data.tag,
        data.args,
        usize::try_from(data.target_range_start)
            .map_err(|_| StimError::new("target_range_start overflow"))?,
        usize::try_from(data.target_range_end)
            .map_err(|_| StimError::new("target_range_end overflow"))?,
        data.targets_in_range
            .into_iter()
            .map(convert_gate_target_with_coords)
            .collect(),
    ))
}

fn convert_flipped_measurement(data: stim_cxx::FlippedMeasurementData) -> FlippedMeasurement {
    FlippedMeasurement::new(
        if data.record_index == u64::MAX {
            None
        } else {
            Some(data.record_index)
        },
        data.observable
            .into_iter()
            .map(convert_gate_target_with_coords)
            .collect::<Vec<_>>(),
    )
}

fn convert_circuit_error_location(
    data: stim_cxx::CircuitErrorLocationData,
) -> Result<CircuitErrorLocation> {
    Ok(CircuitErrorLocation::new(
        data.tick_offset,
        data.flipped_pauli_product
            .into_iter()
            .map(convert_gate_target_with_coords)
            .collect::<Vec<_>>(),
        {
            let measurement = convert_flipped_measurement(data.flipped_measurement);
            if measurement.record_index().is_none() && measurement.observable().is_empty() {
                None
            } else {
                Some(measurement)
            }
        },
        convert_circuit_targets_inside_instruction(data.instruction_targets)?,
        data.stack_frames
            .into_iter()
            .map(convert_circuit_error_location_stack_frame)
            .collect::<Vec<_>>(),
        data.noise_tag,
    ))
}

pub fn convert_explained_error(data: stim_cxx::ExplainedErrorData) -> Result<ExplainedError> {
    Ok(ExplainedError::new(
        data.dem_error_terms
            .into_iter()
            .map(convert_dem_target_with_coords)
            .collect::<Result<Vec<_>>>()?,
        data.circuit_error_locations
            .into_iter()
            .map(convert_circuit_error_location)
            .collect::<Result<Vec<_>>>()?,
    ))
}

pub fn split_top_level_circuit_items(text: &str) -> Result<Vec<String>> {
    if text.is_empty() {
        return Ok(Vec::new());
    }

    let mut items = Vec::new();
    let mut current = Vec::new();
    let mut depth = 0isize;

    for line in text.lines() {
        if depth == 0 && current.is_empty() && !line.starts_with("REPEAT") {
            items.push(line.to_string());
            continue;
        }

        depth += line.matches('{').count() as isize;
        current.push(line.to_string());
        depth -= line.matches('}').count() as isize;

        if depth < 0 {
            return Err(StimError::new("unbalanced circuit repeat block braces"));
        }
        if depth == 0 {
            items.push(current.join("\n"));
            current.clear();
        }
    }

    if depth != 0 || !current.is_empty() {
        return Err(StimError::new("unterminated circuit repeat block"));
    }

    Ok(items)
}

pub fn parse_circuit_item(text: &str) -> Result<CircuitItem> {
    if let Some((header, body)) = text.split_once("{\n") {
        let header = header.trim();
        let rest = header.strip_prefix("REPEAT").ok_or_else(|| {
            StimError::new(format!("invalid circuit repeat block header: {header}"))
        })?;
        let (tag, count_text) = if let Some(rest) = rest.strip_prefix('[') {
            let close = rest
                .find(']')
                .ok_or_else(|| StimError::new("unterminated circuit repeat block tag"))?;
            let tag = &rest[..close];
            let count_text = rest[close + 1..].trim();
            (tag.to_string(), count_text.to_string())
        } else {
            (String::new(), rest.trim().to_string())
        };
        let repeat_count = count_text
            .parse::<u64>()
            .map_err(|_| StimError::new(format!("invalid repeat count: {count_text}")))?;
        let inner = body
            .strip_suffix("\n}")
            .ok_or_else(|| StimError::new("invalid circuit repeat block body"))?;
        let inner = inner
            .lines()
            .map(|line| line.strip_prefix("    ").unwrap_or(line))
            .collect::<Vec<_>>()
            .join("\n");
        Ok(CircuitItem::repeat_block(CircuitRepeatBlock::new(
            repeat_count,
            &Circuit::from_str(&inner)?,
            tag,
        )?))
    } else {
        Ok(CircuitItem::instruction(CircuitInstruction::from_str(
            text,
        )?))
    }
}