use super::either::Either;
use super::noise_model::*;
use super::reproducible_rand::Xoroshiro128StarStar;
use super::simulator::*;
use super::types::*;
use super::util_macros::*;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::sync::{Arc, Mutex};
use ErrorType::*;
#[cfg_attr(feature = "python_binding", cfg_eval)]
#[cfg_attr(feature = "python_binding", pyclass)]
#[derive(Debug, Serialize, Deserialize)]
pub struct SimulatorCompact {
pub error_sources: Vec<ErrorSource>,
#[serde(skip)]
pub rng: Xoroshiro128StarStar,
#[serde(skip)]
errors: BTreeMap<Position, ErrorType>,
#[serde(skip)]
corrections: BTreeMap<Position, ErrorType>,
#[serde(skip)]
defects: BTreeSet<Position>,
#[serde(skip)]
simulator: Option<Simulator>,
}
impl Clone for SimulatorCompact {
fn clone(&self) -> Self {
Self {
error_sources: self.error_sources.clone(),
rng: Xoroshiro128StarStar::new(),
errors: BTreeMap::new(),
corrections: BTreeMap::new(),
defects: BTreeSet::new(),
simulator: self.simulator.clone(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ErrorSource {
Pauli {
p: f64,
defects: Vec<Position>,
errors: Vec<(Position, ErrorType)>,
correction: Vec<(Position, ErrorType)>,
},
}
impl ErrorSource {
pub fn get_error_t(&self) -> usize {
match self {
Self::Pauli { errors, .. } => {
let t = errors[0].0.t;
debug_assert!(
errors.iter().all(|(position, _)| position.t == t),
"an error source cannot happen at multiple time point..."
);
t
}
}
}
pub fn get_correction_t(&self) -> usize {
match self {
Self::Pauli { correction, .. } => {
let t = correction[0].0.t;
debug_assert!(
correction.iter().all(|(position, _)| position.t == t),
"an correction cannot happen at multiple time point..."
);
t
}
}
}
pub fn shift_error_t(&mut self, delta_t: usize) {
match self {
Self::Pauli { defects, errors, .. } => {
for position in defects.iter_mut() {
position.t += delta_t;
}
for (position, _) in errors.iter_mut() {
position.t += delta_t;
}
}
}
}
pub fn shift_correction_t(&mut self, delta_height: usize) {
match self {
Self::Pauli { correction, .. } => {
for (position, _) in correction.iter_mut() {
position.t += delta_height;
}
}
}
}
}
#[cfg(feature = "python_binding")]
bind_trait_simulator_generics! {SimulatorCompact}
impl SimulatorGenerics for SimulatorCompact {
fn set_rng(&mut self, rng: Xoroshiro128StarStar) {
self.rng = rng;
}
fn generate_random_errors(&mut self, _noise_model: &NoiseModel) -> (usize, usize) {
self.clear();
let mut rng = self.rng.clone(); let mut error_count = 0;
for error_source in self.error_sources.iter() {
match error_source {
ErrorSource::Pauli {
p,
errors,
defects,
correction,
} => {
let random_value = rng.next_f64();
if random_value < *p {
for (position, error) in errors.iter() {
if let Some(existing_error) = self.errors.get_mut(position) {
if *existing_error != I {
error_count -= 1;
}
*existing_error = existing_error.multiply(error);
if *existing_error != I {
error_count += 1;
}
} else {
self.errors.insert(position.clone(), *error);
if *error != I {
error_count += 1;
}
}
}
for (position, correct_pauli) in correction.iter() {
if let Some(existing_correct_pauli) = self.corrections.get_mut(position) {
*existing_correct_pauli = existing_correct_pauli.multiply(correct_pauli);
} else {
self.corrections.insert(position.clone(), *correct_pauli);
}
}
for position in defects.iter() {
if self.defects.contains(position) {
self.defects.remove(position);
} else {
self.defects.insert(position.clone());
}
}
}
}
}
}
self.rng = rng; (error_count, 0) }
fn generate_sparse_detected_erasures(&self) -> SparseErasures {
SparseErasures::new() }
fn generate_sparse_error_pattern(&self) -> SparseErrorPattern {
SparseErrorPattern::new_map(self.errors.clone())
}
fn generate_sparse_measurement(&self) -> SparseMeasurement {
SparseMeasurement::new_set(self.defects.clone())
}
fn validate_correction(&mut self, correction: &SparseCorrection) -> (bool, bool) {
assert!(
self.simulator.is_some(),
"a simulator must be provided to validate a correction"
);
let simulator = self.simulator.as_mut().unwrap();
let top_t = simulator.height - 1;
simulator_iter_mut_real!(simulator, position, node, t => top_t, { node.propagated = I;
});
for (position, correct_pauli) in self.corrections.iter() {
let mut position = position.clone();
position.t = top_t; let node: &mut SimulatorNode = simulator.get_node_mut_unwrap(&position);
node.propagated = node.propagated.multiply(correct_pauli);
}
let mut shifted_correction = SparseCorrection::new();
for (position, correct_pauli) in correction.iter() {
let mut position = position.clone();
position.t = top_t; shifted_correction.add(position, *correct_pauli);
}
simulator.validate_correction(&shifted_correction)
}
}
impl SimulatorCompact {
pub fn from_simulator(mut simulator: Simulator, noise_model: Arc<NoiseModel>, parallel: usize) -> Self {
let mut simulator_compact = Self {
error_sources: vec![],
rng: Xoroshiro128StarStar::new(),
errors: BTreeMap::new(),
corrections: BTreeMap::new(),
defects: BTreeSet::new(),
simulator: None,
};
if parallel <= 1 {
let height = simulator.height;
simulator_compact.build_error_sources_region(&mut simulator, noise_model, 0, height);
} else {
let mut handlers = Vec::new();
let mut instances = Vec::new();
let interval = simulator.height / parallel;
for parallel_idx in 0..parallel {
let instance = Arc::new(Mutex::new(simulator_compact.clone()));
let mut simulator = simulator.clone();
instances.push(Arc::clone(&instance));
let t_start = parallel_idx * interval; let mut t_end = (parallel_idx + 1) * interval; if parallel_idx == parallel - 1 {
t_end = simulator.height; }
let noise_model = Arc::clone(&noise_model);
handlers.push(std::thread::spawn(move || {
let mut instance = instance.lock().unwrap();
instance.build_error_sources_region(&mut simulator, noise_model, t_start, t_end);
}));
}
for handler in handlers.drain(..) {
handler.join().unwrap();
}
for instance in instances.iter() {
let mut instance = instance.lock().unwrap();
simulator_compact.error_sources.append(&mut instance.error_sources);
}
}
simulator_compact.simulator = Some(simulator);
simulator_compact
}
fn build_error_sources_region(
&mut self,
simulator: &mut Simulator,
noise_model: Arc<NoiseModel>,
t_start: usize,
t_end: usize,
) {
let mut all_possible_errors: Vec<Either<ErrorType, CorrelatedPauliErrorType>> = Vec::new();
for error_type in ErrorType::all_possible_errors().drain(..) {
all_possible_errors.push(Either::Left(error_type));
}
for correlated_error_type in CorrelatedPauliErrorType::all_possible_errors().drain(..) {
all_possible_errors.push(Either::Right(correlated_error_type));
}
simulator.clear_all_errors();
simulator_iter!(simulator, position, {
if position.t < t_start || position.t >= t_end {
continue;
}
let noise_model_node = noise_model.get_node_unwrap(position);
let possible_erasure_error =
noise_model_node.erasure_error_rate > 0. || noise_model_node.correlated_erasure_error_rates.is_some() || {
let node = simulator.get_node_unwrap(position);
if let Some(gate_peer) = node.gate_peer.as_ref() {
let peer_noise_model_node = noise_model.get_node_unwrap(gate_peer);
if let Some(correlated_erasure_error_rates) = &peer_noise_model_node.correlated_erasure_error_rates {
correlated_erasure_error_rates.error_probability() > 0.
} else {
false
}
} else {
false
}
};
assert!(!possible_erasure_error, "not implemented");
for error in all_possible_errors.iter() {
let p = match error {
Either::Left(error_type) => noise_model_node.pauli_error_rates.error_rate(error_type),
Either::Right(error_type) => match &noise_model_node.correlated_pauli_error_rates {
Some(correlated_pauli_error_rates) => correlated_pauli_error_rates.error_rate(error_type),
None => 0.,
},
}; if p > 0. {
let mut sparse_errors = SparseErrorPattern::new();
match error {
Either::Left(error_type) => {
sparse_errors.add(position.clone(), *error_type);
}
Either::Right(error_type) => {
sparse_errors.add(position.clone(), error_type.my_error());
let node = simulator.get_node_unwrap(position);
let gate_peer = node
.gate_peer
.as_ref()
.expect("correlated error must corresponds to a two-qubit gate");
sparse_errors.add((**gate_peer).clone(), error_type.peer_error());
}
}
let sparse_errors = Arc::new(sparse_errors); let (sparse_correction, sparse_measurement_real, _sparse_measurement_virtual) =
simulator.fast_measurement_given_few_errors(&sparse_errors);
let sparse_measurement_real = sparse_measurement_real.to_vec();
if sparse_measurement_real.is_empty() {
continue;
}
self.error_sources.push(ErrorSource::Pauli {
p,
defects: sparse_measurement_real,
correction: sparse_correction.to_vec(),
errors: sparse_errors.to_vec(),
})
}
}
});
}
pub fn clear(&mut self) {
self.errors.clear();
self.corrections.clear();
self.defects.clear();
}
pub fn assert_eq(&self, other: &Self) -> Result<(), String> {
if self.error_sources.len() != other.error_sources.len() {
return Err(format!(
"the length differs {} != {}",
self.error_sources.len(),
other.error_sources.len()
));
}
for index in 0..self.error_sources.len() {
if self.error_sources[index] != other.error_sources[index] {
return Err(format!(
"the {}-th error source differs: {:?} != {:?}",
index, self.error_sources[index], other.error_sources[index]
));
}
}
debug_assert_eq!(self, other, "up to this step, they should be equal");
Ok(())
}
}
impl PartialEq for SimulatorCompact {
fn eq(&self, other: &Self) -> bool {
self.error_sources == other.error_sources
}
}
#[cfg_attr(feature = "python_binding", cfg_eval)]
#[cfg_attr(feature = "python_binding", pyclass)]
#[derive(Debug, Serialize, Deserialize)]
pub struct SimulatorCompactCompressed {
pub extender: SimulatorCompactExtender,
pub noisy_measurements: usize,
}
impl Clone for SimulatorCompactCompressed {
fn clone(&self) -> Self {
Self {
extender: self.extender.clone(),
noisy_measurements: self.noisy_measurements,
}
}
}
impl SimulatorCompactCompressed {
pub fn new(extender: SimulatorCompactExtender, noisy_measurements: usize) -> Self {
Self {
extender,
noisy_measurements,
}
}
pub fn clear(&mut self) {
self.extender.base.clear();
}
}
#[cfg(feature = "python_binding")]
bind_trait_simulator_generics! {SimulatorCompactCompressed}
impl SimulatorGenerics for SimulatorCompactCompressed {
fn set_rng(&mut self, rng: Xoroshiro128StarStar) {
self.extender.base.rng = rng;
}
fn generate_random_errors(&mut self, _noise_model: &NoiseModel) -> (usize, usize) {
self.clear();
let mut rng = self.extender.base.rng.clone(); let mut error_count = 0;
let mut base_errors = self.extender.base.errors.clone();
let mut base_corrections: BTreeMap<Position, ErrorType> = self.extender.base.corrections.clone();
let mut base_defects = self.extender.base.defects.clone();
for (error_source, delta_t) in self.extender.iter(self.noisy_measurements) {
match &error_source {
ErrorSource::Pauli {
p,
errors,
defects,
correction,
} => {
let random_value = rng.next_f64();
if random_value < *p {
for (position, error) in errors.iter() {
let mut position = position.clone();
position.t += delta_t;
if let Some(existing_error) = base_errors.get_mut(&position) {
if *existing_error != I {
error_count -= 1;
}
*existing_error = existing_error.multiply(error);
if *existing_error != I {
error_count += 1;
}
} else {
base_errors.insert(position.clone(), *error);
if *error != I {
error_count += 1;
}
}
}
for (position, correct_pauli) in correction.iter() {
if let Some(existing_correct_pauli) = base_corrections.get_mut(position) {
*existing_correct_pauli = existing_correct_pauli.multiply(correct_pauli);
} else {
base_corrections.insert(position.clone(), *correct_pauli);
}
}
for position in defects.iter() {
let mut position = position.clone();
position.t += delta_t;
if base_defects.contains(&position) {
base_defects.remove(&position);
} else {
base_defects.insert(position);
}
}
}
}
}
}
self.extender.base.errors = base_errors;
self.extender.base.corrections = base_corrections;
self.extender.base.defects = base_defects;
self.extender.base.rng = rng; (error_count, 0) }
fn generate_sparse_detected_erasures(&self) -> SparseErasures {
self.extender.base.generate_sparse_detected_erasures()
}
fn generate_sparse_error_pattern(&self) -> SparseErrorPattern {
self.extender.base.generate_sparse_error_pattern()
}
fn generate_sparse_measurement(&self) -> SparseMeasurement {
self.extender.base.generate_sparse_measurement()
}
fn validate_correction(&mut self, correction: &SparseCorrection) -> (bool, bool) {
self.extender.base.validate_correction(correction)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SimulatorCompactExtender {
pub base: SimulatorCompact,
pub noisy_measurements: usize,
pub repeat_region: (usize, usize),
pub measurement_cycle: usize,
}
pub struct SimulatorCompactExtenderIter<'a> {
extender: &'a SimulatorCompactExtender,
noisy_measurements: usize,
error_source_index: usize,
}
impl<'a> Iterator for SimulatorCompactExtenderIter<'a> {
type Item = (&'a ErrorSource, usize);
fn next(&mut self) -> Option<Self::Item> {
let error_source_index = self.error_source_index;
self.error_source_index += 1;
let extender = &self.extender;
let base = &extender.base;
if self.noisy_measurements == extender.noisy_measurements {
if error_source_index < base.error_sources.len() {
return Some((&base.error_sources[error_source_index], 0));
} else {
return None;
}
}
let (repeat_start, repeat_end) = extender.repeat_region;
let repeat: usize = self.noisy_measurements - extender.noisy_measurements;
if error_source_index < repeat_end {
return Some((&base.error_sources[error_source_index], 0));
}
if error_source_index >= repeat_end && error_source_index < repeat_end + repeat * (repeat_end - repeat_start) {
let i = (error_source_index - repeat_start) / (repeat_end - repeat_start);
debug_assert!(i >= 1 && i <= repeat);
let index: usize = (error_source_index - repeat_start) % (repeat_end - repeat_start) + repeat_start;
return Some((&base.error_sources[index], i * extender.measurement_cycle));
}
if error_source_index < base.error_sources.len() + repeat * (repeat_end - repeat_start) {
let index = error_source_index - repeat * (repeat_end - repeat_start);
return Some((&base.error_sources[index], repeat * extender.measurement_cycle));
}
None
}
}
impl SimulatorCompactExtender {
pub fn new(first: SimulatorCompact, second: SimulatorCompact, noisy_measurements: usize) -> Self {
assert!(second.error_sources.len() > first.error_sources.len(), "must differ");
let error_sources_differ = second.error_sources.len() - first.error_sources.len();
#[derive(Debug)]
struct RangeT {
t: usize,
start: usize, end: usize, }
let mut first_t_ranges: Vec<RangeT> = vec![];
let mut second_t_ranges: Vec<RangeT> = vec![];
for (t_ranges, simulator_compact) in [(&mut first_t_ranges, &first), (&mut second_t_ranges, &second)] {
let mut last_t = simulator_compact.error_sources[0].get_error_t();
let mut start = 0;
for (index, error_source) in simulator_compact.error_sources.iter().enumerate() {
let t = error_source.get_error_t();
assert!(t >= last_t, "t must be monotonically increasing");
if t != last_t {
t_ranges.push(RangeT { t, start, end: index });
start = index;
last_t = t;
}
}
t_ranges.push(RangeT {
t: last_t,
start,
end: simulator_compact.error_sources.len(),
});
}
let measurement_cycle = second_t_ranges.last().unwrap().t - first_t_ranges.last().unwrap().t;
assert!(measurement_cycle > 0);
let repeat_t_start = (first_t_ranges.last().unwrap().t / measurement_cycle + 1) / 2 * measurement_cycle;
let mut repeat_start = usize::MAX;
let mut repeat_end = 0;
for range in first_t_ranges.iter() {
if range.t >= repeat_t_start && range.t < repeat_t_start + measurement_cycle {
repeat_start = repeat_start.min(range.start);
repeat_end = repeat_end.max(range.end);
}
}
assert!(
repeat_start != usize::MAX,
"no error sources found in t range of [{}, {})",
repeat_t_start,
repeat_t_start + measurement_cycle
);
assert_eq!(
repeat_end - repeat_start,
error_sources_differ,
"the repeat region should have the differed number of error sources"
);
let extender: SimulatorCompactExtender = Self {
base: first,
noisy_measurements,
repeat_region: (repeat_start, repeat_end),
measurement_cycle,
};
second.assert_eq(&extender.generate(noisy_measurements + 1)).unwrap();
extender
}
pub fn generate(&self, noisy_measurements: usize) -> SimulatorCompact {
let mut simulator_compact = self.base.clone();
simulator_compact.error_sources = self
.iter(noisy_measurements)
.map(|(error_source, delta_t)| {
let mut error_source = error_source.clone();
error_source.shift_error_t(delta_t);
error_source.shift_correction_t((noisy_measurements - self.noisy_measurements) * self.measurement_cycle);
error_source
})
.collect();
simulator_compact
}
pub fn iter(&self, noisy_measurements: usize) -> SimulatorCompactExtenderIter {
assert!(noisy_measurements >= self.noisy_measurements);
SimulatorCompactExtenderIter {
extender: self,
noisy_measurements,
error_source_index: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::code_builder::*;
use crate::noise_model_builder::*;
#[test]
fn simulator_compact_extender() {
let di = 3;
let dj = 3;
let p = 0.001;
let build_simulator = |noisy_measurements: usize| -> (Simulator, NoiseModel, SimulatorCompact) {
let mut simulator = Simulator::new(CodeType::RotatedPlanarCode, CodeSize::new(noisy_measurements, di, dj));
let mut noise_model = NoiseModel::new(&simulator);
NoiseModelBuilder::StimNoiseModel.apply(&mut simulator, &mut noise_model, &json!({}), p, 0.5, 0.);
code_builder_sanity_check(&simulator).unwrap();
noise_model_sanity_check(&simulator, &noise_model).unwrap();
let simulator_compact = SimulatorCompact::from_simulator(simulator.clone(), Arc::new(noise_model.clone()), 1);
(simulator, noise_model, simulator_compact)
};
let noisy_measurements = 4;
let (_, _, first) = build_simulator(noisy_measurements);
let (_, _, second) = build_simulator(noisy_measurements + 1);
let extender = SimulatorCompactExtender::new(first, second, noisy_measurements);
println!("extender built successfully");
let test_noisy_measurement = 7;
let generated = extender.generate(test_noisy_measurement);
let (_, _, ground_truth) = build_simulator(test_noisy_measurement);
generated.assert_eq(&ground_truth).unwrap();
}
}