LDPC codes#
LDPC coding: systematic encoder and min-sum decoder (decision D5).
Same vectorization philosophy as the convolutional module (D4): the
decoder state lives in (batch, n_edges) arrays, message-passing
updates are segmented numpy reductions (ufunc.reduceat) over the
edges of the Tanner graph, and the only Python loop is over decoding
iterations.
- class LDPCEncoder(H: ndarray, *, name: str = 'ldpc_encoder')[source]#
Systematic encoder for an LDPC code given its parity-check matrix.
Signal Model#
The codeword \(c \in \{0,1\}^n\) satisfies \(H c = 0\) over GF(2). Bringing \(H\) to reduced row echelon form partitions the columns into \(r\) pivot (parity) positions and \(k = n - r\) free (information) positions; the information bits \(u\) are copied to the free positions and each parity is
\[c_{p_i} = \bigoplus_{j \in \mathrm{free}} \tilde{H}_{ij} \, u_j\]where \(\tilde{H}\) is the reduced matrix. Linearly dependent rows of \(H\) are detected and ignored, so \(k\) can exceed the design dimension \(n - m\).
Axes: axis -1 – information bits on the last axis, broadcast over the rest;
(..., k)maps to(..., n).- param H:
Parity-check matrix of shape
(m, n)(entries 0/1); must match the decoder.- type H:
np.ndarray
- param name:
Name of the processor. Default
"ldpc_encoder".- type name:
str, keyword-only
References
T. Richardson, R. Urbanke, Modern Coding Theory, Cambridge University Press, 2008, Section 3.2 (encoding via echelon form).
Examples
>>> H = np.array([[1, 1, 0, 1, 1, 0, 0], ... [1, 0, 1, 1, 0, 1, 0], ... [0, 1, 1, 1, 0, 0, 1]]) # (7, 4) Hamming code >>> encoder = LDPCEncoder(H) >>> c = encoder(np.array([1, 0, 1, 1])) >>> print(c) [0 0 1 0 0 1 1] >>> print(H @ c % 2) [0 0 0]
- property rate: float#
Code rate \(k / n\) (true rank, not design rate).
- class LDPCDecoder(H: ndarray, *, n_iter: int = 25, alpha: float = 1.0, output: Literal['info', 'codeword'] = 'info', name: str = 'ldpc_decoder')[source]#
Min-sum belief-propagation decoder for an LDPC code.
Signal Model#
Messages flow on the edges of the Tanner graph of \(H\). With channel log-likelihood ratios \(L_j = \log P(c_j{=}0)/P(c_j{=}1)\) (the
SymbolDemapper(soft=True)convention), the min-sum check update for the message from check \(i\) to variable \(j\) is\[m_{i \to j} = \alpha \prod_{j' \in N(i) \setminus j} \mathrm{sign}(m_{j' \to i}) \min_{j' \in N(i) \setminus j} |m_{j' \to i}|\]and the variable update is \(m_{j \to i} = L_j + \sum_{i' \in N(j) \setminus i} m_{i' \to j}\). Iterations stop early when the hard decision satisfies every parity check. All messages live in
(batch, n_edges)arrays; the check and variable updates are segmented reductions over edges sorted by check and by variable (decision D5) – the only Python loop is over iterations.Axes: axis -1 – channel LLRs on the last axis, broadcast over the rest;
(..., n)maps to(..., k)(output="info") or(..., n)(output="codeword").- param H:
Parity-check matrix of shape
(m, n); must match the encoder. Every row and column must contain at least one 1.- type H:
np.ndarray
- param n_iter:
Maximum number of message-passing iterations. Default 25.
- type n_iter:
int, keyword-only
- param alpha:
Normalization factor of the check update (normalized min-sum). Default 1.0 (plain min-sum); 0.75 is a common choice that recovers most of the sum-product gap.
- type alpha:
float, keyword-only
- param output:
Return the information bits at the systematic positions (default) or the full hard-decision codeword.
- type output:
{“info”, “codeword”}, keyword-only
- param name:
Name of the processor. Default
"ldpc_decoder".- type name:
str, keyword-only
- raises ShapeError:
If the last axis of the input does not match the code length.
References
R. G. Gallager, “Low-density parity-check codes,” IRE Trans. Inf. Theory 8(1), 1962; J. Chen, M. Fossorier, “Near optimum universal belief propagation based decoding of low-density parity check codes,” IEEE Trans. Commun. 50(3), 2002 (normalized min-sum).
Examples
>>> H = np.array([[1, 1, 0, 1, 1, 0, 0], ... [1, 0, 1, 1, 0, 1, 0], ... [0, 1, 1, 1, 0, 0, 1]]) # (7, 4) Hamming code >>> c = LDPCEncoder(H)(np.array([1, 0, 1, 1])) >>> llr = 4.0 * (1.0 - 2.0 * c) # log P(0)/P(1), positive = bit 0 >>> llr[1] = -1.0 # one weak channel error >>> decoder = LDPCDecoder(H) >>> print(decoder(llr)) [1 0 1 1]
- make_gallager_parity_check(n: int, d_v: int = 3, d_c: int = 6, seed: int | None = None) ndarray[source]#
Regular parity-check matrix from the Gallager ensemble.
Signal Model#
The \((d_v, d_c)\)-regular matrix stacks \(d_v\) bands of \(n / d_c\) rows. The first band places its ones in consecutive groups of \(d_c\) columns; every other band is a random column permutation of it, so each column has weight \(d_v\) and each row weight \(d_c\). The design rate is \(R = 1 - d_v / d_c\) (the true rate is slightly higher when the stacked rows are linearly dependent, which the encoder detects).
- param n:
Code length (number of columns). Must be a multiple of
d_c.- type n:
int
- param d_v:
Column (variable node) weight. Default 3.
- type d_v:
int, optional
- param d_c:
Row (check node) weight. Default 6.
- type d_c:
int, optional
- param seed:
Seed for the band permutations.
- type seed:
int, optional
- returns:
np.ndarray – Parity-check matrix of shape
(n d_v / d_c, n), dtype uint8.
References
R. G. Gallager, “Low-density parity-check codes,” IRE Trans. Inf. Theory 8(1), 1962.
Examples
>>> H = make_gallager_parity_check(12, d_v=3, d_c=6, seed=0) >>> print(H.shape, int(H.sum(axis=0)[0]), int(H.sum(axis=1)[0])) (6, 12) 3 6