ldpc_toolbox/
decoder.rs

1//! LDPC belief propagation decoders.
2//!
3//! This module provides several implementations of a LDPC decoders using belief
4//! propagation (the sum-product algorithm). The implementations differ in
5//! details about their numerical algorithms, data types and message passing
6//! schedules.
7
8use crate::sparse::SparseMatrix;
9
10pub mod arithmetic;
11pub mod factory;
12pub mod flooding;
13pub mod horizontal_layered;
14
15/// Generic LDPC decoder.
16///
17/// This trait is used to form LDPC decoder trait objects, abstracting over the
18/// internal implementation decoder.
19pub trait LdpcDecoder: std::fmt::Debug + Send {
20    /// Decodes a codeword.
21    ///
22    /// The parameters are the LLRs for the received codeword and the maximum
23    /// number of iterations to perform. If decoding is successful, the function
24    /// returns an `Ok` containing the (hard decision) on the decoded codeword
25    /// and the number of iterations used in decoding. If decoding is not
26    /// successful, the function returns an `Err` containing the hard decision
27    /// on the final decoder LLRs (which still has some bit errors) and the
28    /// number of iterations used in decoding (which is equal to
29    /// `max_iterations`).
30    fn decode(
31        &mut self,
32        llrs: &[f64],
33        max_iterations: usize,
34    ) -> Result<DecoderOutput, DecoderOutput>;
35}
36
37/// LDPC decoder output.
38#[derive(Debug, Clone, Eq, PartialEq, Hash)]
39pub struct DecoderOutput {
40    /// Decoded codeword.
41    ///
42    /// Contains the hard decision bits of the decoded codeword.
43    pub codeword: Vec<u8>,
44    /// Number of iterations.
45    ///
46    /// Number of iterations used in decoding.
47    pub iterations: usize,
48}
49
50/// LDPC decoder message.
51///
52/// This represents a message used by the flooding belief propagation
53/// decoder. It is used for messages received by the nodes.
54#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
55pub struct Message<T> {
56    /// Message source.
57    ///
58    /// Contains the index of the variable node or check node that sent the
59    /// message.
60    pub source: usize,
61    /// Value.
62    ///
63    /// Contains the value of the message.
64    pub value: T,
65}
66
67/// LDPC decoder outgoing message.
68///
69/// This represents a message used by the flooding belief propagation
70/// decoder. It is used for messages sent by the nodes.
71#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
72pub struct SentMessage<T> {
73    /// Message destination.
74    ///
75    /// Contains the index of the variable node or check node to which the
76    /// message is addressed.
77    pub dest: usize,
78    /// Value.
79    ///
80    /// Contains the value of the message.
81    pub value: T,
82}
83
84#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
85struct Messages<T> {
86    per_destination: Box<[Box<[Message<T>]>]>,
87}
88
89impl<T: Default> Messages<T> {
90    fn from_iter<I, J, B>(iter: I) -> Messages<T>
91    where
92        I: Iterator<Item = J>,
93        J: Iterator<Item = B>,
94        B: core::borrow::Borrow<usize>,
95    {
96        Messages {
97            per_destination: iter
98                .map(|i| {
99                    i.map(|j| Message {
100                        source: *j.borrow(),
101                        value: T::default(),
102                    })
103                    .collect::<Vec<_>>()
104                    .into_boxed_slice()
105                })
106                .collect::<Vec<_>>()
107                .into_boxed_slice(),
108        }
109    }
110
111    fn send(&mut self, source: usize, destination: usize, value: T) {
112        let message = self.per_destination[destination]
113            .iter_mut()
114            .find(|m| m.source == source)
115            .expect("message for source not found");
116        message.value = value;
117    }
118}
119
120#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
121struct SentMessages<T> {
122    per_source: Box<[Box<[SentMessage<T>]>]>,
123}
124
125impl<T: Default> SentMessages<T> {
126    fn from_iter<I, J, B>(iter: I) -> SentMessages<T>
127    where
128        I: Iterator<Item = J>,
129        J: Iterator<Item = B>,
130        B: core::borrow::Borrow<usize>,
131    {
132        SentMessages {
133            per_source: iter
134                .map(|i| {
135                    i.map(|j| SentMessage {
136                        dest: *j.borrow(),
137                        value: T::default(),
138                    })
139                    .collect::<Vec<_>>()
140                    .into_boxed_slice()
141                })
142                .collect::<Vec<_>>()
143                .into_boxed_slice(),
144        }
145    }
146
147    #[allow(dead_code)]
148    fn send(&mut self, source: usize, destination: usize, value: T) {
149        let message = self.per_source[source]
150            .iter_mut()
151            .find(|m| m.dest == destination)
152            .expect("message for destination not found");
153        message.value = value;
154    }
155}
156
157fn check_llrs<T, F>(h: &SparseMatrix, llrs: &[T], hard_decision: F) -> bool
158where
159    T: Copy,
160    F: Fn(T) -> bool,
161{
162    // Check if hard decision on LLRs satisfies the parity check equations
163    !(0..h.num_rows()).any(|r| h.iter_row(r).filter(|&&c| hard_decision(llrs[c])).count() % 2 == 1)
164}
165
166fn hard_decisions<T, F>(llrs: &[T], hard_decision: F) -> Vec<u8>
167where
168    T: Copy,
169    F: Fn(T) -> bool,
170{
171    llrs.iter()
172        .map(|&llr| u8::from(hard_decision(llr)))
173        .collect()
174}