#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DecodingRegime {
Jbr,
Udr,
}
impl DecodingRegime {
pub fn instantiate(&self, field_size: f64, alpha: f64) -> Box<dyn ProximityGapsRegime> {
match self {
DecodingRegime::Jbr => Box::new(JohnsonBoundRegime::new(field_size, alpha)),
DecodingRegime::Udr => Box::new(UniqueDecodingRegime::new(field_size, alpha)),
}
}
}
pub trait ProximityGapsRegime {
fn identifier(&self) -> &'static str;
fn field_size(&self) -> &f64;
fn decoding_radius(&self, rate: &f64) -> f64;
fn gap(&self, rate: &f64) -> f64;
fn proximity_parameter(&self, rate: &f64) -> f64 {
let pp = self.decoding_radius(rate) - self.gap(rate);
assert!(pp > 0.0, "Proximity parameter must be positive");
pp
}
fn max_list_size(&self, rate: &f64, dimension: u32) -> u64;
fn error_linear(&self, rate: &f64, dimension: u32) -> f64;
fn error_powers(&self, rate: &f64, dimension: u32, batch_size: u64) -> f64 {
self.error_linear(rate, dimension) * (batch_size as f64 - 1.0)
}
fn error_multilinear(&self, rate: &f64, dimension: u32, batch_size: u64) -> f64 {
self.error_linear(rate, dimension) * (batch_size as f64).log2().ceil()
}
}
pub struct UniqueDecodingRegime {
field_size: f64,
alpha: f64,
}
impl UniqueDecodingRegime {
pub fn new(field_size: f64, alpha: f64) -> Self {
Self { field_size, alpha }
}
}
impl ProximityGapsRegime for UniqueDecodingRegime {
fn identifier(&self) -> &'static str {
"UDR"
}
fn field_size(&self) -> &f64 {
&self.field_size
}
fn decoding_radius(&self, rate: &f64) -> f64 {
(1.0 - rate) / 2.0
}
fn gap(&self, rate: &f64) -> f64 {
let minimum_distance = 1.0 - rate;
let base_correction = minimum_distance / 20.0;
let gap = base_correction * (1.0 + self.alpha);
assert!(gap < minimum_distance / 6.0, "Gap must be smaller than δ/6 in UDR");
gap
}
fn max_list_size(&self, _rate: &f64, _dimension: u32) -> u64 {
1
}
fn error_linear(&self, rate: &f64, dimension: u32) -> f64 {
let pp = self.proximity_parameter(rate);
let n = dimension as f64 / rate;
(pp * n + 1.0) / self.field_size
}
}
pub struct JohnsonBoundRegime {
field_size: f64,
alpha: f64,
}
impl JohnsonBoundRegime {
pub fn new(field_size: f64, alpha: f64) -> Self {
Self { field_size, alpha }
}
fn get_mul_from_gap(&self, rate: &f64, gap: f64) -> u64 {
let two_gap = 2.0 * gap;
let m_ceil = (rate.sqrt() / two_gap).ceil() as u64;
m_ceil.max(3)
}
}
impl ProximityGapsRegime for JohnsonBoundRegime {
fn identifier(&self) -> &'static str {
"JBR"
}
fn field_size(&self) -> &f64 {
&self.field_size
}
fn decoding_radius(&self, rate: &f64) -> f64 {
1.0 - rate.sqrt()
}
fn gap(&self, rate: &f64) -> f64 {
let base_correction = 1.0 / 300.0;
let gap = base_correction * (1.0 + self.alpha);
let minimum_distance = 1.0 - rate;
let delta_half = minimum_distance / 2.0;
assert!(gap < self.decoding_radius(rate) - delta_half, "Gap must be smaller than 1 - √(1-δ) - δ/2 in JBR");
gap
}
fn max_list_size(&self, rate: &f64, _dimension: u32) -> u64 {
let two_gap = 2.0 * self.gap(rate);
(1.0 / (two_gap * rate.sqrt())).ceil() as u64
}
fn error_linear(&self, rate: &f64, dimension: u32) -> f64 {
let sqrt_rate = rate.sqrt();
let m = self.get_mul_from_gap(rate, self.gap(rate));
let m_shifted = m as f64 + 0.5;
let n = dimension as f64 / rate;
let numerator = (2.0 * m_shifted.powi(5) + 3.0 * m_shifted * self.gap(rate) * rate) * n;
let denominator = 3.0 * rate * sqrt_rate;
let first_fraction = numerator / denominator;
let second_fraction = m_shifted / sqrt_rate;
(first_fraction + second_fraction) / self.field_size
}
}