use std::hash::{DefaultHasher, Hash, Hasher};
use std::sync::Arc;
use hashbrown::HashMap;
use crate::bin::error_model_type::RemoteCheckModel;
use crate::bin::{ErrorModel, ErrorModelType, ProbabilityModifier};
use crate::controller::jit_controller::hash_error_model_type_structural;
use crate::misc::relative_program::{RelativeMapping, RelativeProgram};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ProbabilityModifierBits {
pub probabilities: Vec<u64>,
pub sparse_indices: Vec<u64>,
pub sparse_probabilities: Vec<u64>,
}
impl From<&ProbabilityModifier> for ProbabilityModifierBits {
fn from(pm: &ProbabilityModifier) -> Self {
Self {
probabilities: pm.probabilities.iter().map(|p| p.to_bits()).collect(),
sparse_indices: pm.sparse_indices.clone(),
sparse_probabilities: pm.sparse_probabilities.iter().map(|p| p.to_bits()).collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ErrorModelFingerprint {
pub etype_digest: u64,
pub pm: Option<ProbabilityModifierBits>,
pub remote_check_models: Arc<Vec<Option<RemoteCheckModel>>>,
}
impl ErrorModelFingerprint {
pub fn new(
instance: &ErrorModel,
modified_remote_check_models: Arc<Vec<Option<RemoteCheckModel>>>,
etype_digest: u64,
) -> Self {
let pm = instance
.modifier
.as_ref()
.and_then(|m| m.probability_modifier.as_ref())
.map(ProbabilityModifierBits::from);
Self {
etype_digest,
pm,
remote_check_models: modified_remote_check_models,
}
}
}
pub fn etype_digest(emt: &ErrorModelType) -> u64 {
let mut hasher = DefaultHasher::new();
hash_error_model_type_structural(emt, &mut hasher);
hasher.finish()
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DecoderCacheKey {
pub relative_program: RelativeProgram,
pub error_model_fingerprints: Vec<ErrorModelFingerprint>,
pub committing_local_cids: Vec<u32>,
}
pub trait FingerprintSource {
fn instance(&self) -> &ErrorModel;
fn modified_remote_check_models(&self) -> &Arc<Vec<Option<RemoteCheckModel>>>;
}
pub fn build_modifier_fingerprints<E: FingerprintSource>(
mapping: &RelativeMapping,
error_models: &HashMap<u64, E>,
error_model_types: &HashMap<u64, Arc<ErrorModelType>>,
) -> Vec<ErrorModelFingerprint> {
let mut digest_cache: HashMap<u64, u64> = HashMap::with_capacity(error_model_types.len());
mapping
.global_eid_of
.iter()
.map(|eid| {
let em = error_models.get(eid).expect("error_model present for eid in window");
let etype = em.instance().etype;
let digest = *digest_cache.entry(etype).or_insert_with(|| {
let emt = error_model_types
.get(&etype)
.expect("error_model_type present for etype referenced by error_model");
etype_digest(emt)
});
ErrorModelFingerprint::new(em.instance(), em.modified_remote_check_models().clone(), digest)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bin::ProbabilityModifier;
use crate::bin::error_model::ErrorModelModifier;
use crate::bin::error_model_type::{Error, RemoteCheck, RemoteCheckModel, remote_check_model};
use crate::controller::jit_controller::ErrorModelTypeKey;
fn pm(probabilities: Vec<f64>) -> ProbabilityModifier {
ProbabilityModifier {
probabilities,
sparse_indices: vec![],
sparse_probabilities: vec![],
}
}
fn pm_sparse(sparse_indices: Vec<u64>, sparse_probabilities: Vec<f64>) -> ProbabilityModifier {
ProbabilityModifier {
probabilities: vec![],
sparse_indices,
sparse_probabilities,
}
}
fn error(probability: f64, check_index: u64) -> Error {
Error {
checks: vec![RemoteCheck {
remote_check_model: None,
check_index,
}],
probability,
..Default::default()
}
}
fn emt(etype: u64, errors: Vec<Error>) -> ErrorModelType {
ErrorModelType {
etype,
ctype: 1,
errors,
remote_check_models: vec![],
..Default::default()
}
}
fn em_with_modifier(eid: u64, etype: u64, modifier: Option<ProbabilityModifier>) -> ErrorModel {
ErrorModel {
eid,
etype,
cid: 1,
modifier: modifier.map(|p| ErrorModelModifier {
probability_modifier: Some(p),
reroute_remote_check_models: vec![],
}),
..Default::default()
}
}
fn remote_check(check_bias: u64, output: u64) -> RemoteCheckModel {
RemoteCheckModel {
previous_remote_check_model: None,
port: Some(remote_check_model::Port::Output(output)),
expecting_ctype: 0,
check_bias,
absolute_cid: None,
..Default::default()
}
}
fn fingerprint(instance: &ErrorModel, rcm: &[Option<RemoteCheckModel>], emt: &ErrorModelType) -> ErrorModelFingerprint {
ErrorModelFingerprint::new(instance, Arc::new(rcm.to_vec()), etype_digest(emt))
}
#[test]
fn probability_modifier_bits_distinguishes_different_dense_values() {
let a = ProbabilityModifierBits::from(&pm(vec![0.1, 0.2]));
let b = ProbabilityModifierBits::from(&pm(vec![0.1, 0.3]));
assert_ne!(a, b);
}
#[test]
fn probability_modifier_bits_distinguishes_different_sparse_values() {
let a = ProbabilityModifierBits::from(&pm_sparse(vec![0, 2], vec![0.1, 0.4]));
let b = ProbabilityModifierBits::from(&pm_sparse(vec![0, 2], vec![0.1, 0.5]));
assert_ne!(a, b);
}
#[test]
fn probability_modifier_bits_distinguishes_different_sparse_indices() {
let a = ProbabilityModifierBits::from(&pm_sparse(vec![0, 2], vec![0.1, 0.2]));
let b = ProbabilityModifierBits::from(&pm_sparse(vec![0, 3], vec![0.1, 0.2]));
assert_ne!(a, b);
}
#[test]
fn probability_modifier_bits_distinguishes_positive_and_negative_zero() {
assert_eq!(0.0_f64, -0.0_f64);
let a = ProbabilityModifierBits::from(&pm(vec![0.0]));
let b = ProbabilityModifierBits::from(&pm(vec![-0.0]));
assert_ne!(a, b);
}
#[test]
fn probability_modifier_bits_distinguishes_different_nan_payloads() {
let nan1 = f64::from_bits(0x7ff8_0000_0000_0001);
let nan2 = f64::from_bits(0x7ff8_0000_0000_0002);
assert!(nan1.is_nan() && nan2.is_nan());
let a = ProbabilityModifierBits::from(&pm(vec![nan1]));
let b = ProbabilityModifierBits::from(&pm(vec![nan2]));
assert_ne!(a, b);
}
#[test]
fn probability_modifier_bits_equal_for_identical_inputs() {
let a = ProbabilityModifierBits::from(&pm(vec![0.1, 0.2]));
let b = ProbabilityModifierBits::from(&pm(vec![0.1, 0.2]));
assert_eq!(a, b);
}
#[test]
fn fingerprint_differs_when_probability_modifier_differs() {
let etype = emt(1, vec![error(0.1, 0)]);
let em1 = em_with_modifier(1, 1, Some(pm(vec![0.1])));
let em2 = em_with_modifier(1, 1, Some(pm(vec![0.2])));
let f1 = fingerprint(&em1, &[], &etype);
let f2 = fingerprint(&em2, &[], &etype);
assert_ne!(f1, f2);
}
#[test]
fn fingerprint_differs_when_modifier_present_vs_absent() {
let etype = emt(1, vec![error(0.1, 0)]);
let em_no_mod = em_with_modifier(1, 1, None);
let em_with_mod = em_with_modifier(1, 1, Some(pm(vec![0.1])));
let f1 = fingerprint(&em_no_mod, &[], &etype);
let f2 = fingerprint(&em_with_mod, &[], &etype);
assert_ne!(f1, f2);
}
#[test]
fn fingerprint_differs_when_check_bias_differs() {
let etype = emt(1, vec![error(0.1, 0)]);
let em = em_with_modifier(1, 1, None);
let rcm_a = vec![Some(remote_check( 0, 0))];
let rcm_b = vec![Some(remote_check( 5, 0))];
let f1 = fingerprint(&em, &rcm_a, &etype);
let f2 = fingerprint(&em, &rcm_b, &etype);
assert_ne!(f1, f2);
}
#[test]
fn fingerprint_differs_when_remote_check_model_present_vs_absent() {
let etype = emt(1, vec![error(0.1, 0)]);
let em = em_with_modifier(1, 1, None);
let rcm_none = vec![None];
let rcm_some = vec![Some(remote_check(0, 0))];
let f1 = fingerprint(&em, &rcm_none, &etype);
let f2 = fingerprint(&em, &rcm_some, &etype);
assert_ne!(f1, f2);
}
#[test]
fn fingerprint_differs_when_same_etype_id_but_different_errors() {
let etype_v1 = emt(1, vec![error(0.1, 0)]);
let etype_v2 = emt(1, vec![error(0.1, 0), error(0.2, 1)]);
let em = em_with_modifier(1, 1, None);
let f1 = fingerprint(&em, &[], &etype_v1);
let f2 = fingerprint(&em, &[], &etype_v2);
assert_ne!(f1, f2);
assert_ne!(f1.etype_digest, f2.etype_digest);
}
#[test]
fn fingerprint_differs_when_same_etype_id_but_different_probability() {
let etype_v1 = emt(1, vec![error(0.1, 0)]);
let etype_v2 = emt(1, vec![error(0.2, 0)]);
let em = em_with_modifier(1, 1, None);
let f1 = fingerprint(&em, &[], &etype_v1);
let f2 = fingerprint(&em, &[], &etype_v2);
assert_ne!(f1.etype_digest, f2.etype_digest);
}
#[test]
fn fingerprint_equal_for_identical_inputs() {
let etype = emt(1, vec![error(0.1, 0)]);
let em = em_with_modifier(1, 1, Some(pm(vec![0.1])));
let rcm = vec![Some(remote_check(2, 0))];
let f1 = fingerprint(&em, &rcm, &etype);
let f2 = fingerprint(&em, &rcm, &etype);
assert_eq!(f1, f2);
}
#[test]
fn etype_digest_matches_error_model_type_key_hash() {
let et = emt(1, vec![error(0.1, 0), error(0.2, 1)]);
let digest = etype_digest(&et);
let mut hasher = DefaultHasher::new();
ErrorModelTypeKey(et.clone()).hash(&mut hasher);
assert_eq!(digest, hasher.finish());
}
fn empty_relative_program() -> RelativeProgram {
RelativeProgram {
local_gadgets: vec![],
count_checks: 0,
}
}
fn fp_with_etype_digest(d: u64) -> ErrorModelFingerprint {
ErrorModelFingerprint {
etype_digest: d,
pm: None,
remote_check_models: Arc::new(vec![]),
}
}
#[test]
fn cache_key_differs_when_fingerprints_differ() {
let r = empty_relative_program();
let k1 = DecoderCacheKey {
relative_program: r.clone(),
error_model_fingerprints: vec![fp_with_etype_digest(1)],
committing_local_cids: vec![],
};
let k2 = DecoderCacheKey {
relative_program: r,
error_model_fingerprints: vec![fp_with_etype_digest(2)],
committing_local_cids: vec![],
};
assert_ne!(k1, k2);
let mut map: HashMap<DecoderCacheKey, &'static str> = HashMap::new();
map.insert(k1.clone(), "first");
assert_eq!(map.get(&k2), None);
assert_eq!(map.get(&k1), Some(&"first"));
}
#[test]
fn cache_key_differs_when_committing_local_cids_differ() {
let r = empty_relative_program();
let k1 = DecoderCacheKey {
relative_program: r.clone(),
error_model_fingerprints: vec![],
committing_local_cids: vec![0, 1],
};
let k2 = DecoderCacheKey {
relative_program: r,
error_model_fingerprints: vec![],
committing_local_cids: vec![0, 2],
};
assert_ne!(k1, k2);
let mut map: HashMap<DecoderCacheKey, &'static str> = HashMap::new();
map.insert(k1.clone(), "first");
assert_eq!(map.get(&k2), None);
}
#[test]
fn cache_key_differs_when_only_commit_region_differs() {
let r = empty_relative_program();
let fp = fp_with_etype_digest(42);
let k1 = DecoderCacheKey {
relative_program: r.clone(),
error_model_fingerprints: vec![fp.clone()],
committing_local_cids: vec![0, 1, 2],
};
let k2 = DecoderCacheKey {
relative_program: r,
error_model_fingerprints: vec![fp],
committing_local_cids: vec![0, 1],
};
assert_ne!(k1, k2);
}
#[test]
fn cache_key_committing_local_cids_order_matters() {
let r = empty_relative_program();
let k1 = DecoderCacheKey {
relative_program: r.clone(),
error_model_fingerprints: vec![],
committing_local_cids: vec![0, 1, 2],
};
let k2 = DecoderCacheKey {
relative_program: r,
error_model_fingerprints: vec![],
committing_local_cids: vec![2, 1, 0],
};
assert_ne!(k1, k2);
}
#[test]
fn cache_key_equal_for_identical_inputs() {
let r = empty_relative_program();
let fp = fp_with_etype_digest(7);
let k1 = DecoderCacheKey {
relative_program: r.clone(),
error_model_fingerprints: vec![fp.clone()],
committing_local_cids: vec![0, 1],
};
let k2 = DecoderCacheKey {
relative_program: r,
error_model_fingerprints: vec![fp],
committing_local_cids: vec![0, 1],
};
assert_eq!(k1, k2);
let mut map: HashMap<DecoderCacheKey, &'static str> = HashMap::new();
map.insert(k1, "first");
assert_eq!(map.get(&k2), Some(&"first"));
}
#[test]
fn cache_key_distinguishes_fingerprint_position() {
let r = empty_relative_program();
let fp_a = fp_with_etype_digest(1);
let fp_b = fp_with_etype_digest(2);
let k1 = DecoderCacheKey {
relative_program: r.clone(),
error_model_fingerprints: vec![fp_a.clone(), fp_b.clone()],
committing_local_cids: vec![],
};
let k2 = DecoderCacheKey {
relative_program: r,
error_model_fingerprints: vec![fp_b, fp_a],
committing_local_cids: vec![],
};
assert_ne!(k1, k2);
}
}