use std::fmt;
use std::str::FromStr;
use super::{BitTable, Error, Result};
use crate::batch_sampler::BatchSampler;
use crate::circuit::{self, Control, Instr};
use crate::dem::Dem;
use crate::m2d::M2d;
#[derive(Clone, Debug, PartialEq)]
pub struct Circuit {
pub(crate) inner: circuit::Circuit,
num_qubits: usize,
num_measurements: usize,
num_detectors: usize,
num_observables: usize,
num_sweep_bits: usize,
}
fn max_sweep_bit(instrs: &[Instr]) -> usize {
instrs
.iter()
.map(|i| match i {
Instr::SweepX(pairs) => pairs.iter().map(|&(k, _)| k as usize + 1).max().unwrap_or(0),
Instr::Feedback { control: Control::Sweep(k), .. } => *k as usize + 1,
Instr::Repeat { body, .. } | Instr::Gate { body, .. } => max_sweep_bit(body),
_ => 0,
})
.max()
.unwrap_or(0)
}
impl Circuit {
pub fn parse(text: &str) -> Result<Circuit> {
Circuit::from_engine(circuit::Circuit::parse(text)?)
}
pub(crate) fn from_engine(inner: circuit::Circuit) -> Result<Circuit> {
let shape = BatchSampler::new(&inner)?;
Ok(Circuit {
num_qubits: inner.instrs.iter().flat_map(Instr::qubits).map(|q| q as usize + 1).max().unwrap_or(0),
num_measurements: shape.num_measurements,
num_detectors: shape.num_detectors,
num_observables: shape.num_observables,
num_sweep_bits: max_sweep_bit(&inner.instrs),
inner,
})
}
pub fn num_qubits(&self) -> usize {
self.num_qubits
}
pub fn num_measurements(&self) -> usize {
self.num_measurements
}
pub fn num_detectors(&self) -> usize {
self.num_detectors
}
pub fn num_observables(&self) -> usize {
self.num_observables
}
pub fn num_sweep_bits(&self) -> usize {
self.num_sweep_bits
}
pub fn detector_error_model(&self, options: &DemOptions) -> Result<DetectorErrorModel> {
let dem = Dem::from_circuit_with(&self.inner, options.decompose_errors, options.approximate_disjoint_errors)?;
Ok(DetectorErrorModel { inner: dem, pieces: options.decompose_errors })
}
pub fn detector_sampler(&self, seed: u64) -> Result<DetectorSampler> {
Ok(DetectorSampler { sampler: BatchSampler::new(&self.inner)?, seed, next: 0 })
}
pub fn measurement_converter(&self) -> Result<MeasurementConverter> {
Ok(MeasurementConverter { m2d: M2d::new(&self.inner)? })
}
}
impl FromStr for Circuit {
type Err = Error;
fn from_str(text: &str) -> Result<Circuit> {
Circuit::parse(text)
}
}
impl fmt::Display for Circuit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.inner.to_stim())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct DemOptions {
decompose_errors: bool,
approximate_disjoint_errors: Option<f64>,
}
impl DemOptions {
pub fn new() -> DemOptions {
DemOptions::default()
}
pub fn decompose_errors(mut self, yes: bool) -> DemOptions {
self.decompose_errors = yes;
self
}
pub fn approximate_disjoint_errors(mut self, threshold: Option<f64>) -> DemOptions {
self.approximate_disjoint_errors = threshold;
self
}
}
#[derive(Clone, Debug)]
pub struct DetectorErrorModel {
pub(crate) inner: Dem,
pieces: bool,
}
impl DetectorErrorModel {
pub fn parse(text: &str) -> Result<DetectorErrorModel> {
Ok(DetectorErrorModel { inner: Dem::parse(text)?, pieces: true })
}
pub fn num_detectors(&self) -> usize {
self.inner.num_detectors
}
pub fn num_observables(&self) -> usize {
self.inner.num_observables
}
pub fn num_errors(&self) -> usize {
self.inner.mechanisms.len()
}
}
impl FromStr for DetectorErrorModel {
type Err = Error;
fn from_str(text: &str) -> Result<DetectorErrorModel> {
DetectorErrorModel::parse(text)
}
}
impl fmt::Display for DetectorErrorModel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.inner.to_stim(self.pieces))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Samples {
pub detectors: BitTable,
pub observables: BitTable,
}
pub struct DetectorSampler {
sampler: BatchSampler,
seed: u64,
next: u64,
}
impl DetectorSampler {
pub fn num_detectors(&self) -> usize {
self.sampler.num_detectors
}
pub fn num_observables(&self) -> usize {
self.sampler.num_observables
}
pub fn sample(&mut self, shots: usize, threads: usize) -> Samples {
let (d, o) = self.sampler.sample_seeded(self.seed, self.next, shots, threads);
self.next += shots.div_ceil(64) as u64;
Samples {
detectors: BitTable::from_packed(shots, self.num_detectors(), d).expect("the sampler writes whole rows"),
observables: BitTable::from_packed(shots, self.num_observables(), o).expect("the sampler writes whole rows"),
}
}
}
pub struct MeasurementConverter {
m2d: M2d,
}
impl MeasurementConverter {
pub fn num_measurements(&self) -> usize {
self.m2d.num_measurements
}
pub fn num_sweep_bits(&self) -> usize {
self.m2d.num_sweep_bits
}
pub fn convert(&self, measurements: &BitTable, sweep_bits: Option<&BitTable>) -> Result<Samples> {
let shots = measurements.num_rows();
if measurements.num_bits() != self.num_measurements() {
return Err(Error::new(format!("{} measurement bits per shot, not {}", measurements.num_bits(), self.num_measurements())));
}
let zeros;
let sweeps = match sweep_bits {
Some(s) => {
if s.num_rows() != shots || s.num_bits() != self.num_sweep_bits() {
return Err(Error::new(format!(
"sweep bits are {} shots of {}, not {shots} of {}",
s.num_rows(),
s.num_bits(),
self.num_sweep_bits()
)));
}
s
}
None => {
zeros = BitTable::zeros(shots, self.num_sweep_bits());
&zeros
}
};
let (d, o) = self.m2d.convert_b8(measurements.as_bytes(), sweeps.as_bytes(), shots)?;
Ok(Samples {
detectors: BitTable::from_packed(shots, self.m2d.num_detectors, d)?,
observables: BitTable::from_packed(shots, self.m2d.num_observables, o)?,
})
}
}