use super::{Message, SentMessage};
use std::convert::identity;
pub trait DecoderArithmetic: std::fmt::Debug + Send {
type Llr: std::fmt::Debug + Copy + Default + Send;
type CheckMessage: std::fmt::Debug + Copy + Default + Send;
type VarMessage: std::fmt::Debug + Copy + Default + Send;
type VarLlr: std::fmt::Debug + Copy + Default + Send;
fn input_llr_quantize(&self, llr: f64) -> Self::Llr;
fn llr_hard_decision(&self, llr: Self::Llr) -> bool;
fn llr_to_var_message(&self, llr: Self::Llr) -> Self::VarMessage;
fn llr_to_var_llr(&self, llr: Self::Llr) -> Self::VarLlr;
fn var_llr_to_llr(&self, var_llr: Self::VarLlr) -> Self::Llr;
fn send_check_messages<F>(&mut self, var_messages: &[Message<Self::VarMessage>], send: F)
where
F: FnMut(SentMessage<Self::CheckMessage>);
fn send_var_messages<F>(
&mut self,
input_llr: Self::Llr,
check_messages: &[Message<Self::CheckMessage>],
send: F,
) -> Self::Llr
where
F: FnMut(SentMessage<Self::VarMessage>);
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<Self::CheckMessage>],
vars: &mut [Self::VarLlr],
);
}
fn send_var_messages_no_clip<T, F>(input_llr: T, check_messages: &[Message<T>], mut send: F) -> T
where
T: std::iter::Sum + std::ops::Add<Output = T> + std::ops::Sub<Output = T> + Copy,
F: FnMut(SentMessage<T>),
{
let llr: T = input_llr + check_messages.iter().map(|m| m.value).sum::<T>();
for msg in check_messages.iter() {
send(SentMessage {
dest: msg.source,
value: llr - msg.value,
});
}
llr
}
macro_rules! impl_phif {
($ty:ident, $f:ty, $min_x:expr) => {
#[derive(Debug, Clone, Default)]
pub struct $ty {
phis: Vec<$f>,
}
impl $ty {
pub fn new() -> $ty {
<$ty>::default()
}
}
impl $ty {
fn phi(x: $f) -> $f {
let x = x.max($min_x);
-((0.5 * x).tanh().ln())
}
}
impl DecoderArithmetic for $ty {
type Llr = $f;
type CheckMessage = $f;
type VarMessage = $f;
type VarLlr = $f;
fn input_llr_quantize(&self, llr: f64) -> $f {
llr as $f
}
fn llr_hard_decision(&self, llr: $f) -> bool {
llr <= 0.0
}
fn llr_to_var_message(&self, llr: $f) -> $f {
llr
}
fn llr_to_var_llr(&self, llr: $f) -> $f {
llr
}
fn var_llr_to_llr(&self, var_llr: $f) -> $f {
var_llr
}
fn send_check_messages<F>(&mut self, var_messages: &[Message<$f>], mut send: F)
where
F: FnMut(SentMessage<$f>),
{
let mut sign: u32 = 0;
let mut sum = 0.0;
if self.phis.len() < var_messages.len() {
self.phis.resize(var_messages.len(), 0.0);
}
for (msg, phi) in var_messages.iter().zip(self.phis.iter_mut()) {
let x = msg.value;
let phi_x = Self::phi(x.abs());
*phi = phi_x;
sum += phi_x;
if x < 0.0 {
sign ^= 1;
}
}
for (msg, phi) in var_messages.iter().zip(self.phis.iter()) {
let x = msg.value;
let y = Self::phi(sum - phi);
let s = if x < 0.0 { sign ^ 1 } else { sign };
let val = if s == 0 { y } else { -y };
send(SentMessage {
dest: msg.source,
value: val,
});
}
}
fn send_var_messages<F>(
&mut self,
input_llr: $f,
check_messages: &[Message<$f>],
send: F,
) -> $f
where
F: FnMut(SentMessage<$f>),
{
send_var_messages_no_clip(input_llr, check_messages, send)
}
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<$f>],
vars: &mut [$f],
) {
let mut sign: u32 = 0;
let mut sum = 0.0;
if self.phis.len() < check_messages.len() {
self.phis.resize(check_messages.len(), 0.0);
}
for (msg, phi) in check_messages.iter().zip(self.phis.iter_mut()) {
let x = vars[msg.dest] - msg.value;
let phi_x = Self::phi(x.abs());
*phi = phi_x;
sum += phi_x;
if x < 0.0 {
sign ^= 1;
}
}
for (msg, phi) in check_messages.iter_mut().zip(self.phis.iter()) {
let x = vars[msg.dest] - msg.value;
let rcv = Self::phi(sum - phi);
let s = if x < 0.0 { sign ^ 1 } else { sign };
let rcv = if s == 0 { rcv } else { -rcv };
msg.value = rcv;
vars[msg.dest] = x + rcv;
}
}
}
};
}
impl_phif!(Phif64, f64, 1e-30);
impl_phif!(Phif32, f32, 1e-30);
macro_rules! impl_tanhf {
($ty:ident, $f:ty, $tanh_clamp:expr) => {
#[derive(Debug, Clone, Default)]
pub struct $ty {
tanhs: Vec<$f>,
}
impl $ty {
pub fn new() -> $ty {
<$ty>::default()
}
}
impl DecoderArithmetic for $ty {
type Llr = $f;
type CheckMessage = $f;
type VarMessage = $f;
type VarLlr = $f;
fn input_llr_quantize(&self, llr: f64) -> $f {
llr as $f
}
fn llr_hard_decision(&self, llr: $f) -> bool {
llr <= 0.0
}
fn llr_to_var_message(&self, llr: $f) -> $f {
llr
}
fn llr_to_var_llr(&self, llr: $f) -> $f {
llr
}
fn var_llr_to_llr(&self, var_llr: $f) -> $f {
var_llr
}
fn send_check_messages<F>(&mut self, var_messages: &[Message<$f>], mut send: F)
where
F: FnMut(SentMessage<$f>),
{
if self.tanhs.len() < var_messages.len() {
self.tanhs.resize(var_messages.len(), 0.0);
}
for (msg, tanh) in var_messages.iter().zip(self.tanhs.iter_mut()) {
let x = msg.value;
let t = (0.5 * x).clamp(-$tanh_clamp, $tanh_clamp).tanh();
*tanh = t;
}
for exclude_msg in var_messages.iter() {
let product = var_messages
.iter()
.zip(self.tanhs.iter())
.filter_map(|(msg, tanh)| {
if msg.source != exclude_msg.source {
Some(tanh)
} else {
None
}
})
.product::<$f>();
send(SentMessage {
dest: exclude_msg.source,
value: 2.0 * product.atanh(),
})
}
}
fn send_var_messages<F>(
&mut self,
input_llr: $f,
check_messages: &[Message<$f>],
send: F,
) -> $f
where
F: FnMut(SentMessage<$f>),
{
send_var_messages_no_clip(input_llr, check_messages, send)
}
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<$f>],
vars: &mut [$f],
) {
if self.tanhs.len() < check_messages.len() {
self.tanhs.resize(check_messages.len(), 0.0);
}
for (msg, tanh) in check_messages.iter().zip(self.tanhs.iter_mut()) {
let x = vars[msg.dest] - msg.value;
let t = (0.5 * x).clamp(-$tanh_clamp, $tanh_clamp).tanh();
*tanh = t;
}
for j in 0..check_messages.len() {
let exclude_msg = &check_messages[j];
let product = check_messages
.iter()
.zip(self.tanhs.iter())
.filter_map(|(msg, tanh)| {
if msg.dest != exclude_msg.dest {
Some(tanh)
} else {
None
}
})
.product::<$f>();
let rcv = 2.0 * product.atanh();
vars[exclude_msg.dest] += rcv - exclude_msg.value;
check_messages[j].value = rcv;
}
}
}
};
}
impl_tanhf!(Tanhf64, f64, 18.0);
impl_tanhf!(Tanhf32, f32, 9.0);
macro_rules! impl_minstarapproxf {
($ty:ident, $f:ty) => {
#[derive(Debug, Clone, Default)]
pub struct $ty {
minstars: Vec<$f>,
}
impl $ty {
pub fn new() -> $ty {
<$ty>::default()
}
}
impl DecoderArithmetic for $ty {
type Llr = $f;
type CheckMessage = $f;
type VarMessage = $f;
type VarLlr = $f;
fn input_llr_quantize(&self, llr: f64) -> $f {
llr as $f
}
fn llr_hard_decision(&self, llr: $f) -> bool {
llr <= 0.0
}
fn llr_to_var_message(&self, llr: $f) -> $f {
llr
}
fn llr_to_var_llr(&self, llr: $f) -> $f {
llr
}
fn var_llr_to_llr(&self, var_llr: $f) -> $f {
var_llr
}
fn send_check_messages<F>(&mut self, var_messages: &[Message<$f>], mut send: F)
where
F: FnMut(SentMessage<$f>),
{
for exclude_msg in var_messages.iter() {
let mut sign: u32 = 0;
let mut minstar = None;
for msg in var_messages
.iter()
.filter(|msg| msg.source != exclude_msg.source)
{
let x = msg.value;
if x < 0.0 {
sign ^= 1;
}
let x = x.abs();
minstar = Some(match minstar {
None => x,
Some(y) => (x.min(y) - (-(x - y).abs()).exp().ln_1p()).max(0.0),
});
}
let minstar =
minstar.expect("only one variable message connected to check node");
let minstar = if sign == 0 { minstar } else { -minstar };
send(SentMessage {
dest: exclude_msg.source,
value: minstar,
})
}
}
fn send_var_messages<F>(
&mut self,
input_llr: $f,
check_messages: &[Message<$f>],
send: F,
) -> $f
where
F: FnMut(SentMessage<$f>),
{
send_var_messages_no_clip(input_llr, check_messages, send)
}
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<$f>],
vars: &mut [$f],
) {
if self.minstars.len() < check_messages.len() {
self.minstars.resize(check_messages.len(), 0.0);
}
for (exclude_msg, minstar) in check_messages.iter().zip(self.minstars.iter_mut()) {
let mut sign: u32 = 0;
let mut mstar = None;
for msg in check_messages
.iter()
.filter(|msg| msg.dest != exclude_msg.dest)
{
let x = vars[msg.dest] - msg.value;
if x < 0.0 {
sign ^= 1;
}
let x = x.abs();
mstar = Some(match mstar {
None => x,
Some(y) => (x.min(y) - (-(x - y).abs()).exp().ln_1p()).max(0.0),
});
}
let mstar = mstar.expect("only one variable message connected to check node");
*minstar = if sign == 0 { mstar } else { -mstar };
}
for (msg, &minstar) in check_messages.iter_mut().zip(self.minstars.iter()) {
vars[msg.dest] += minstar - msg.value;
msg.value = minstar;
}
}
}
};
}
impl_minstarapproxf!(Minstarapproxf64, f64);
impl_minstarapproxf!(Minstarapproxf32, f32);
macro_rules! impl_8bitquant {
($ty:ident) => {
impl $ty {
const QUANTIZER_C: f64 = 8.0;
pub fn new() -> $ty {
let table = (0..=127)
.map_while(|t| {
let x = (Self::QUANTIZER_C
* (-(t as f64 / Self::QUANTIZER_C)).exp().ln_1p())
.round() as i8;
if x > 0 { Some(x) } else { None }
})
.collect::<Vec<_>>()
.into_boxed_slice();
$ty {
table,
_minstars: Vec::new(),
}
}
fn lookup(table: &[i8], x: i8) -> i8 {
assert!(x >= 0);
table.get(x as usize).copied().unwrap_or(0)
}
fn clip(x: i16) -> i8 {
if x >= 127 {
127
} else if x <= -127 {
-127
} else {
x as i8
}
}
}
};
}
macro_rules! impl_send_var_messages_i8 {
($degree_one_clip:expr, $jones_clip:expr) => {
#[allow(clippy::redundant_closure_call)]
fn send_var_messages<F>(
&mut self,
input_llr: i8,
check_messages: &[Message<i8>],
mut send: F,
) -> i8
where
F: FnMut(SentMessage<i8>),
{
let degree_one = check_messages.len() == 1;
let llr = i16::from($degree_one_clip(input_llr, degree_one))
+ check_messages
.iter()
.map(|m| i16::from(m.value))
.sum::<i16>();
let llr = $jones_clip(llr);
for msg in check_messages.iter() {
send(SentMessage {
dest: msg.source,
value: Self::clip(llr - i16::from(msg.value)),
});
}
Self::clip(llr)
}
};
}
macro_rules! impl_minstarapproxi8 {
($ty:ident, $jones_clip:expr, $check_hardlimit:expr, $degree_one_clip:expr) => {
#[derive(Debug, Clone)]
pub struct $ty {
table: Box<[i8]>,
_minstars: Vec<i8>,
}
impl_8bitquant!($ty);
impl Default for $ty {
fn default() -> $ty {
<$ty>::new()
}
}
impl DecoderArithmetic for $ty {
type Llr = i8;
type CheckMessage = i8;
type VarMessage = i8;
type VarLlr = i16;
fn input_llr_quantize(&self, llr: f64) -> i8 {
let x = Self::QUANTIZER_C * llr;
if x >= 127.0 {
127
} else if x <= -127.0 {
-127
} else {
x.round() as i8
}
}
fn llr_hard_decision(&self, llr: i8) -> bool {
llr <= 0
}
fn llr_to_var_message(&self, llr: i8) -> i8 {
llr
}
fn llr_to_var_llr(&self, llr: i8) -> i16 {
i16::from(llr)
}
fn var_llr_to_llr(&self, var_llr: i16) -> i8 {
Self::clip(var_llr)
}
#[allow(clippy::redundant_closure_call)]
fn send_check_messages<F>(&mut self, var_messages: &[Message<i8>], mut send: F)
where
F: FnMut(SentMessage<i8>),
{
for exclude_msg in var_messages.iter() {
let mut sign: u32 = 0;
let mut minstar = None;
for msg in var_messages
.iter()
.filter(|msg| msg.source != exclude_msg.source)
{
let x = msg.value;
if x < 0 {
sign ^= 1;
}
let x = x.abs();
minstar = Some(match minstar {
None => x,
Some(y) => (x.min(y) - Self::lookup(&self.table, (x - y).abs())).max(0),
});
}
let minstar =
minstar.expect("only one variable message connected to check node");
let minstar = if sign == 0 { minstar } else { -minstar };
let minstar = $check_hardlimit(minstar);
send(SentMessage {
dest: exclude_msg.source,
value: minstar,
})
}
}
impl_send_var_messages_i8!($degree_one_clip, $jones_clip);
#[allow(clippy::redundant_closure_call)]
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<i8>],
vars: &mut [i16],
) {
if self._minstars.len() < check_messages.len() {
self._minstars.resize(check_messages.len(), 0);
}
for (exclude_msg, minstar) in check_messages.iter().zip(self._minstars.iter_mut()) {
let mut sign: u32 = 0;
let mut mstar = None;
for msg in check_messages
.iter()
.filter(|msg| msg.dest != exclude_msg.dest)
{
let x = Self::clip(vars[msg.dest] - i16::from(msg.value));
if x < 0 {
sign ^= 1;
}
let x = x.abs();
mstar = Some(match mstar {
None => x,
Some(y) => (x.min(y) - Self::lookup(&self.table, (x - y).abs())).max(0),
});
}
let mstar = mstar.expect("only one variable message connected to check node");
let mstar = if sign == 0 { mstar } else { -mstar };
let mstar = $check_hardlimit(mstar);
*minstar = mstar;
}
for (msg, &minstar) in check_messages.iter_mut().zip(self._minstars.iter()) {
vars[msg.dest] += i16::from(minstar) - i16::from(msg.value);
msg.value = minstar;
}
}
}
};
}
macro_rules! jones_clip {
() => {
|x| i16::from(Self::clip(x))
};
}
macro_rules! partial_hard_limit {
() => {
|x| {
if x <= -100 {
-127
} else if x >= 100 {
127
} else {
x
}
}
};
}
macro_rules! degree_one_clipping {
() => {
|x, degree_one| {
if degree_one {
if x <= -116 {
-116
} else if x >= 116 {
116
} else {
x
}
} else {
x
}
}
};
}
macro_rules! degree_one_no_clipping {
() => {
|x, _| x
};
}
impl_minstarapproxi8!(
Minstarapproxi8,
identity,
identity,
degree_one_no_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8Jones,
jones_clip!(),
identity,
degree_one_no_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8PartialHardLimit,
identity,
partial_hard_limit!(),
degree_one_no_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8JonesPartialHardLimit,
jones_clip!(),
partial_hard_limit!(),
degree_one_no_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8Deg1Clip,
identity,
identity,
degree_one_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8JonesDeg1Clip,
jones_clip!(),
identity,
degree_one_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8PartialHardLimitDeg1Clip,
identity,
partial_hard_limit!(),
degree_one_clipping!()
);
impl_minstarapproxi8!(
Minstarapproxi8JonesPartialHardLimitDeg1Clip,
jones_clip!(),
partial_hard_limit!(),
degree_one_clipping!()
);
macro_rules! impl_aminstarf {
($ty:ident, $f:ty) => {
#[derive(Debug, Clone, Default)]
pub struct $ty {}
impl $ty {
pub fn new() -> $ty {
<$ty>::default()
}
}
impl DecoderArithmetic for $ty {
type Llr = $f;
type CheckMessage = $f;
type VarMessage = $f;
type VarLlr = $f;
fn input_llr_quantize(&self, llr: f64) -> $f {
llr as $f
}
fn llr_hard_decision(&self, llr: $f) -> bool {
llr <= 0.0
}
fn llr_to_var_message(&self, llr: $f) -> $f {
llr
}
fn llr_to_var_llr(&self, llr: $f) -> $f {
llr
}
fn var_llr_to_llr(&self, var_llr: $f) -> $f {
var_llr
}
fn send_check_messages<F>(&mut self, var_messages: &[Message<$f>], mut send: F)
where
F: FnMut(SentMessage<$f>),
{
let (argmin, msgmin) = var_messages
.iter()
.enumerate()
.min_by(|(_, msg1), (_, msg2)| {
msg1.value.abs().partial_cmp(&msg2.value.abs()).unwrap()
})
.expect("var_messages is empty");
let mut sign: u32 = 0;
let mut delta = None;
for (j, msg) in var_messages.iter().enumerate() {
let x = msg.value;
if x < 0.0 {
sign ^= 1;
}
if j != argmin {
let x = x.abs();
delta = Some(match delta {
None => x,
Some(y) => {
(x.min(y) - (-(x - y).abs()).exp().ln_1p()
+ (-(x + y)).exp().ln_1p())
}
});
}
}
let delta = delta.expect("var_messages_empty");
send(SentMessage {
dest: msgmin.source,
value: if (sign != 0) ^ (msgmin.value < 0.0) {
-delta
} else {
delta
},
});
let vmin = msgmin.value.abs();
let delta = delta.min(vmin) - (-(delta - vmin).abs()).exp().ln_1p()
+ (-(delta + vmin)).exp().ln_1p();
for msg in var_messages
.iter()
.enumerate()
.filter_map(|(j, msg)| if j != argmin { Some(msg) } else { None })
{
send(SentMessage {
dest: msg.source,
value: if (sign != 0) ^ (msg.value < 0.0) {
-delta
} else {
delta
},
});
}
}
fn send_var_messages<F>(
&mut self,
input_llr: $f,
check_messages: &[Message<$f>],
send: F,
) -> $f
where
F: FnMut(SentMessage<$f>),
{
send_var_messages_no_clip(input_llr, check_messages, send)
}
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<$f>],
vars: &mut [$f],
) {
let (argmin, msgmin) = check_messages
.iter()
.map(|msg| vars[msg.dest] - msg.value)
.enumerate()
.min_by(|(_, msg1), (_, msg2)| msg1.abs().partial_cmp(&msg2.abs()).unwrap())
.expect("var_messages is empty");
let mut sign: u32 = 0;
let mut delta = None;
for (j, msg) in check_messages.iter().enumerate() {
let x = vars[msg.dest] - msg.value;
if x < 0.0 {
sign ^= 1;
}
if j != argmin {
let x = x.abs();
delta = Some(match delta {
None => x,
Some(y) => {
(x.min(y) - (-(x - y).abs()).exp().ln_1p()
+ (-(x + y)).exp().ln_1p())
}
});
}
}
let delta = delta.expect("var_messages_empty");
let msgmin_rcv = if (sign != 0) ^ (msgmin < 0.0) {
-delta
} else {
delta
};
let vmin = msgmin.abs();
let delta = delta.min(vmin) - (-(delta - vmin).abs()).exp().ln_1p()
+ (-(delta + vmin)).exp().ln_1p();
for (j, msg) in check_messages.iter_mut().enumerate() {
let x = vars[msg.dest] - msg.value;
let rcv = if j == argmin {
msgmin_rcv
} else {
if (sign != 0) ^ (x < 0.0) {
-delta
} else {
delta
}
};
msg.value = rcv;
vars[msg.dest] = x + rcv;
}
}
}
};
}
impl_aminstarf!(Aminstarf64, f64);
impl_aminstarf!(Aminstarf32, f32);
macro_rules! impl_aminstari8 {
($ty:ident, $jones_clip:expr, $check_hardlimit:expr, $degree_one_clip:expr) => {
#[derive(Debug, Clone)]
pub struct $ty {
table: Box<[i8]>,
_minstars: Vec<i8>,
}
impl_8bitquant!($ty);
impl Default for $ty {
fn default() -> $ty {
<$ty>::new()
}
}
impl DecoderArithmetic for $ty {
type Llr = i8;
type CheckMessage = i8;
type VarMessage = i8;
type VarLlr = i16;
fn input_llr_quantize(&self, llr: f64) -> i8 {
let x = Self::QUANTIZER_C * llr;
if x >= 127.0 {
127
} else if x <= -127.0 {
-127
} else {
x.round() as i8
}
}
fn llr_hard_decision(&self, llr: i8) -> bool {
llr <= 0
}
fn llr_to_var_message(&self, llr: i8) -> i8 {
llr
}
fn llr_to_var_llr(&self, llr: i8) -> i16 {
i16::from(llr)
}
fn var_llr_to_llr(&self, var_llr: i16) -> i8 {
Self::clip(var_llr)
}
#[allow(clippy::redundant_closure_call)]
fn send_check_messages<F>(&mut self, var_messages: &[Message<i8>], mut send: F)
where
F: FnMut(SentMessage<i8>),
{
let (argmin, msgmin) = var_messages
.iter()
.enumerate()
.min_by_key(|(_, msg)| msg.value.abs())
.expect("var_messages is empty");
let mut sign: u32 = 0;
let mut delta = None;
for (j, msg) in var_messages.iter().enumerate() {
let x = msg.value;
if x < 0 {
sign ^= 1;
}
if j != argmin {
let x = x.abs();
delta = Some(match delta {
None => x,
Some(y) => (x.min(y) - Self::lookup(&self.table, (x - y).abs())
+ Self::lookup(&self.table, x.saturating_add(y)))
.max(0),
});
}
}
let delta = delta.expect("var_messages_empty");
let delta_hl = $check_hardlimit(delta);
send(SentMessage {
dest: msgmin.source,
value: if (sign != 0) ^ (msgmin.value < 0) {
-delta_hl
} else {
delta_hl
},
});
let vmin = msgmin.value.abs();
let delta = (delta.min(vmin) - Self::lookup(&self.table, (delta - vmin).abs())
+ Self::lookup(&self.table, delta.saturating_add(vmin)))
.max(0);
let delta_hl = $check_hardlimit(delta);
for msg in var_messages
.iter()
.enumerate()
.filter_map(|(j, msg)| if j != argmin { Some(msg) } else { None })
{
send(SentMessage {
dest: msg.source,
value: if (sign != 0) ^ (msg.value < 0) {
-delta_hl
} else {
delta_hl
},
});
}
}
impl_send_var_messages_i8!($degree_one_clip, $jones_clip);
#[allow(clippy::redundant_closure_call)]
fn update_check_messages_and_vars(
&mut self,
check_messages: &mut [SentMessage<i8>],
vars: &mut [i16],
) {
let (argmin, msgmin) = check_messages
.iter()
.map(|msg| Self::clip(vars[msg.dest] - i16::from(msg.value)))
.enumerate()
.min_by_key(|(_, msg)| msg.abs())
.expect("var_messages is empty");
let mut sign: u32 = 0;
let mut delta = None;
for (j, msg) in check_messages.iter().enumerate() {
let x = Self::clip(vars[msg.dest] - i16::from(msg.value));
if x < 0 {
sign ^= 1;
}
if j != argmin {
let x = x.abs();
delta = Some(match delta {
None => x,
Some(y) => (x.min(y) - Self::lookup(&self.table, (x - y).abs())
+ Self::lookup(&self.table, x.saturating_add(y)))
.max(0),
});
}
}
let delta = delta.expect("var_messages_empty");
let delta_hl = $check_hardlimit(delta);
let msgmin_rcv = if (sign != 0) ^ (msgmin < 0) {
-delta_hl
} else {
delta_hl
};
let vmin = msgmin.abs();
let delta = (delta.min(vmin) - Self::lookup(&self.table, (delta - vmin).abs())
+ Self::lookup(&self.table, delta.saturating_add(vmin)))
.max(0);
let delta_hl = $check_hardlimit(delta);
for (j, msg) in check_messages.iter_mut().enumerate() {
let x = vars[msg.dest] - i16::from(msg.value);
let rcv = if j == argmin {
msgmin_rcv
} else {
if (sign != 0) ^ (x < 0) {
-delta_hl
} else {
delta_hl
}
};
vars[msg.dest] = x + i16::from(rcv);
msg.value = rcv;
}
}
}
};
}
impl_aminstari8!(Aminstari8, identity, identity, degree_one_no_clipping!());
impl_aminstari8!(
Aminstari8Jones,
jones_clip!(),
identity,
degree_one_no_clipping!()
);
impl_aminstari8!(
Aminstari8PartialHardLimit,
identity,
partial_hard_limit!(),
degree_one_no_clipping!()
);
impl_aminstari8!(
Aminstari8JonesPartialHardLimit,
jones_clip!(),
partial_hard_limit!(),
degree_one_no_clipping!()
);
impl_aminstari8!(
Aminstari8Deg1Clip,
identity,
identity,
degree_one_clipping!()
);
impl_aminstari8!(
Aminstari8JonesDeg1Clip,
jones_clip!(),
identity,
degree_one_clipping!()
);
impl_aminstari8!(
Aminstari8PartialHardLimitDeg1Clip,
identity,
partial_hard_limit!(),
degree_one_clipping!()
);
impl_aminstari8!(
Aminstari8JonesPartialHardLimitDeg1Clip,
jones_clip!(),
partial_hard_limit!(),
degree_one_clipping!()
);