use super::SupportedVersion;
use crate::{RoqoqoBackendError, RoqoqoError};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub struct ImperfectReadoutModel {
prob_detect_0_as_1: HashMap<usize, f64>,
prob_detect_1_as_0: HashMap<usize, f64>,
}
impl ImperfectReadoutModel {
pub fn new() -> Self {
Self::default()
}
pub fn new_with_uniform_error(
number_qubits: usize,
prob_detect_0_as_1: f64,
prob_detect_1_as_0: f64,
) -> Result<Self, RoqoqoBackendError> {
check_is_probability(&prob_detect_0_as_1)?;
check_is_probability(&prob_detect_1_as_0)?;
let prob_detect_0_as_1: HashMap<usize, f64> = (0..number_qubits)
.map(|i| (i, prob_detect_0_as_1))
.collect();
let prob_detect_1_as_0: HashMap<usize, f64> = (0..number_qubits)
.map(|i| (i, prob_detect_1_as_0))
.collect();
Ok(Self {
prob_detect_0_as_1,
prob_detect_1_as_0,
})
}
pub fn set_error_probabilites(
mut self,
qubit: usize,
prob_detect_0_as_1: f64,
prob_detect_1_as_0: f64,
) -> Result<Self, RoqoqoBackendError> {
check_is_probability(&prob_detect_0_as_1)?;
check_is_probability(&prob_detect_1_as_0)?;
self.prob_detect_0_as_1.insert(qubit, prob_detect_0_as_1);
self.prob_detect_1_as_0.insert(qubit, prob_detect_1_as_0);
Ok(self)
}
pub fn prob_detect_0_as_1(&self, qubit: &usize) -> f64 {
*self.prob_detect_0_as_1.get(qubit).unwrap_or(&0.0)
}
pub fn prob_detect_1_as_0(&self, qubit: &usize) -> f64 {
*self.prob_detect_1_as_0.get(qubit).unwrap_or(&0.0)
}
}
impl SupportedVersion for ImperfectReadoutModel {
fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
(1, 6, 0)
}
}
fn check_is_probability(prob: &f64) -> Result<(), RoqoqoError> {
if prob < &0.0 {
Err(RoqoqoError::GenericError {
msg: format!("Probabilities need to be > 0, {prob} is not > 0"),
})
} else if prob > &1.0 {
Err(RoqoqoError::GenericError {
msg: format!("Probabilities need to be < 1, {prob} is not < 1"),
})
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "json_schema")]
use jsonschema::Validator;
#[test]
fn test_check_is_probability_valid() {
assert!(check_is_probability(&0.5).is_ok());
assert!(check_is_probability(&0.1).is_ok());
assert!(check_is_probability(&0.9).is_ok());
}
#[test]
fn test_check_is_probability_out_of_range() {
assert!(check_is_probability(&-0.1).is_err());
assert!(check_is_probability(&1.1).is_err());
}
#[test]
fn test_imperfect_readout_model_new() {
let model = ImperfectReadoutModel::new();
assert_eq!(model.prob_detect_0_as_1, HashMap::<usize, f64>::new());
assert_eq!(model.prob_detect_1_as_0, HashMap::<usize, f64>::new());
}
#[test]
fn test_imperfect_readout_model_new_with_uniform_error() {
let model = ImperfectReadoutModel::new_with_uniform_error(2, 0.2, 0.8).unwrap();
assert_eq!(
model.clone().prob_detect_0_as_1,
[(0, 0.2), (1, 0.2)].iter().cloned().collect()
);
assert_eq!(
model.prob_detect_1_as_0,
[(0, 0.8), (1, 0.8)].iter().cloned().collect()
);
}
#[test]
fn test_imperfect_readout_model_set_error_probabilites() {
let mut model = ImperfectReadoutModel::new();
model = model.set_error_probabilites(0, 0.2, 0.8).unwrap();
model = model.set_error_probabilites(1, 0.3, 0.3).unwrap();
assert_eq!(
model.clone().prob_detect_0_as_1,
[(0, 0.2), (1, 0.3)].iter().cloned().collect()
);
assert_eq!(
model.prob_detect_1_as_0,
[(0, 0.8), (1, 0.3)].iter().cloned().collect()
);
}
#[test]
fn test_imperfect_readout_model_prob_detect_0_as_1() {
let model = ImperfectReadoutModel::new_with_uniform_error(2, 0.2, 0.8).unwrap();
assert_eq!(model.prob_detect_0_as_1(&0), 0.2);
assert_eq!(model.prob_detect_0_as_1(&0), 0.2);
assert_eq!(model.prob_detect_0_as_1(&4), 0.0);
}
#[test]
fn test_imperfect_readout_model_prob_detect_1_as_0() {
let model = ImperfectReadoutModel::new_with_uniform_error(2, 0.2, 0.8).unwrap();
assert_eq!(model.prob_detect_1_as_0(&0), 0.8);
assert_eq!(model.prob_detect_1_as_0(&1), 0.8);
assert_eq!(model.prob_detect_1_as_0(&4), 0.0);
}
#[cfg(feature = "json_schema")]
#[test]
fn test_json_schema_feature() {
let model = ImperfectReadoutModel::new_with_uniform_error(2, 0.2, 0.8).unwrap();
let schema = schemars::schema_for!(ImperfectReadoutModel);
let schema_checker =
Validator::new(&serde_json::to_value(&schema).unwrap()).expect("schema is valid");
let value = serde_json::to_value(model).unwrap();
let val = match value {
serde_json::Value::Object(ob) => ob,
_ => panic!(),
};
let value: serde_json::Value = serde_json::to_value(val).unwrap();
let validation = schema_checker.validate(&value);
assert!(validation.is_ok());
}
}