use super::code_builder::*;
#[cfg(feature = "python_binding")]
use super::pyo3::prelude::*;
use super::simulator::*;
use super::types::*;
use super::util_macros::*;
use crate::visualize::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "python_binding", pyclass)]
pub struct NoiseModel {
pub nodes: Vec<Vec<Vec<Option<Arc<NoiseModelNode>>>>>,
pub additional_noise: Vec<AdditionalNoise>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "python_binding", pyclass)]
pub struct AdditionalNoise {
#[serde(rename = "p")]
pub probability: f64,
#[serde(rename = "ee")]
pub erasures: SparseErasures,
#[serde(rename = "pe")]
pub pauli_errors: SparseErrorPattern,
}
impl QecpVisualizer for NoiseModel {
fn component_info(&self, abbrev: bool) -> (String, serde_json::Value) {
let name = "noise_model";
let info = json!({
"nodes": (0..self.nodes.len()).map(|t| {
(0..self.nodes[t].len()).map(|i| {
(0..self.nodes[t][i].len()).map(|j| {
let position = &pos!(t, i, j);
if self.is_node_exist(position) {
let node = self.get_node_unwrap(position);
Some(json!({
if abbrev { "p" } else { "position" }: position, if abbrev { "pp" } else { "pauli_error_rates" }: node.pauli_error_rates,
if abbrev { "pe" } else { "erasure_error_rate" }: node.erasure_error_rate,
if abbrev { "corr_pp" } else { "correlated_pauli_error_rates" }: node.correlated_pauli_error_rates,
if abbrev { "corr_pe" } else { "correlated_erasure_error_rates" }: node.correlated_erasure_error_rates,
}))
} else {
None
}
}).collect::<Vec<Option<serde_json::Value>>>()
}).collect::<Vec<Vec<Option<serde_json::Value>>>>()
}).collect::<Vec<Vec<Vec<Option<serde_json::Value>>>>>(),
"additional_noise": self.additional_noise,
});
(name.to_string(), info)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "python_binding", pyclass)]
pub struct NoiseModelNode {
#[serde(rename = "pp")]
pub pauli_error_rates: PauliErrorRates,
#[serde(rename = "pe")]
pub erasure_error_rate: f64,
#[serde(rename = "corr_pp")]
pub correlated_pauli_error_rates: Option<CorrelatedPauliErrorRates>,
#[serde(rename = "corr_pe")]
pub correlated_erasure_error_rates: Option<CorrelatedErasureErrorRates>,
}
impl Default for NoiseModelNode {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(feature = "python_binding", cfg_eval)]
#[cfg_attr(feature = "python_binding", pymethods)]
impl NoiseModelNode {
#[cfg_attr(feature = "python_binding", new)]
pub fn new() -> Self {
Self {
pauli_error_rates: PauliErrorRates::default(),
erasure_error_rate: 0.,
correlated_pauli_error_rates: None,
correlated_erasure_error_rates: None,
}
}
pub fn is_noiseless(&self) -> bool {
if self.pauli_error_rates.error_probability() > 0. {
return false;
}
if self.erasure_error_rate > 0. {
return false;
}
if self.correlated_pauli_error_rates.is_some()
&& self.correlated_pauli_error_rates.as_ref().unwrap().error_probability() > 0.
{
return false;
}
if self.correlated_erasure_error_rates.is_some()
&& self.correlated_erasure_error_rates.as_ref().unwrap().error_probability() > 0.
{
return false;
}
true
}
}
#[cfg_attr(feature = "python_binding", cfg_eval)]
#[cfg_attr(feature = "python_binding", pymethods)]
impl NoiseModel {
#[cfg_attr(feature = "python_binding", new)]
pub fn new(simulator: &Simulator) -> Self {
assert!(simulator.volume() > 0, "cannot build noise model out of zero-sized simulator");
let default_noise_model_node = Arc::new(NoiseModelNode::new());
Self {
nodes: (0..simulator.height)
.map(|t| {
(0..simulator.vertical)
.map(|i| {
(0..simulator.horizontal)
.map(|j| {
if simulator.is_node_exist(&pos!(t, i, j)) {
Some(default_noise_model_node.clone())
} else {
None
}
})
.collect()
})
.collect()
})
.collect(),
additional_noise: vec![],
}
}
}
impl NoiseModel {
#[inline]
pub fn is_valid_position(&self, position: &Position) -> bool {
position.t < self.nodes.len()
&& position.i < self.nodes[position.t].len()
&& position.j < self.nodes[position.t][position.i].len()
}
#[inline]
pub fn is_node_exist(&self, position: &Position) -> bool {
self.is_valid_position(position) && self.get_node(position).is_some()
}
#[inline]
pub fn get_node(&'_ self, position: &Position) -> &'_ Option<Arc<NoiseModelNode>> {
&self.nodes[position.t][position.i][position.j]
}
pub fn get_node_unwrap(&'_ self, position: &Position) -> &'_ NoiseModelNode {
self.nodes[position.t][position.i][position.j].as_ref().unwrap()
}
pub fn get_node_unwrap_arc(&'_ self, position: &Position) -> Arc<NoiseModelNode> {
self.nodes[position.t][position.i][position.j].as_ref().unwrap().clone()
}
pub fn set_node(&mut self, position: &Position, node: Option<Arc<NoiseModelNode>>) {
self.nodes[position.t][position.i][position.j] = node;
}
}
pub fn noise_model_sanity_check(simulator: &Simulator, noise_model: &NoiseModel) -> Result<(), String> {
let CodeSize { noisy_measurements, .. } = simulator.code_size;
let expected_height = simulator.measurement_cycles * (noisy_measurements + 1) + 1;
if simulator.height != expected_height {
return Err(format!(
"height {} is not expected {}, don't know where is perfect measurement",
simulator.height, expected_height
));
}
for t in simulator.height - simulator.measurement_cycles..simulator.height {
simulator_iter!(simulator, position, _node, t => t, {
let noise_model_node = noise_model.get_node_unwrap(position);
if !noise_model_node.is_noiseless() {
return Err(format!("detected noisy position {} within final perfect measurement", position))
}
});
}
simulator_iter_virtual!(simulator, position, _node, {
let noise_model_node = noise_model.get_node_unwrap(position);
if !noise_model_node.is_noiseless() {
return Err(format!("detected noisy position {} which is virtual node", position));
}
});
simulator_iter!(simulator, position, node, {
let noise_model_node = noise_model.get_node_unwrap(position);
if node.is_virtual {
if noise_model_node.pauli_error_rates.error_probability() > 0. {
return Err(format!(
"virtual position at {} have non-zero pauli_error_rates: {:?}",
position, noise_model_node.pauli_error_rates
));
}
if noise_model_node.erasure_error_rate > 0. {
return Err(format!(
"virtual position at {} have non-zero erasure_error_rate: {}",
position, noise_model_node.erasure_error_rate
));
}
if let Some(correlated_pauli_error_rates) = &noise_model_node.correlated_pauli_error_rates {
if correlated_pauli_error_rates.error_probability() > 0. {
return Err(format!(
"virtual position at {} have non-zero correlated_pauli_error_rates: {:?}",
position, correlated_pauli_error_rates
));
}
}
if let Some(correlated_erasure_error_rates) = &noise_model_node.correlated_erasure_error_rates {
if correlated_erasure_error_rates.error_probability() > 0. {
return Err(format!(
"virtual position at {} have non-zero correlated_erasure_error_rates: {:?}",
position, correlated_erasure_error_rates
));
}
}
}
if node.is_peer_virtual {
if let Some(correlated_pauli_error_rates) = &noise_model_node.correlated_pauli_error_rates {
if correlated_pauli_error_rates.error_probability() > 0. {
return Err(format!(
"position at {} have virtual peer but non-zero correlated_pauli_error_rates: {:?}",
position, correlated_pauli_error_rates
));
}
}
if let Some(correlated_erasure_error_rates) = &noise_model_node.correlated_erasure_error_rates {
if correlated_erasure_error_rates.error_probability() > 0. {
return Err(format!(
"position at {} have virtual peer but non-zero correlated_erasure_error_rates: {:?}",
position, correlated_erasure_error_rates
));
}
}
}
});
Ok(())
}
#[cfg(feature = "python_binding")]
#[pyfunction]
pub(crate) fn register(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<NoiseModel>()?;
m.add_class::<NoiseModelNode>()?;
m.add_class::<AdditionalNoise>()?;
Ok(())
}