*** thanks for stopping by my corner of the web *** best viewed at 800x600 *** sign my guestbook ***

LDPC Reborn: Density Evolution

Channel Coding

The previous entry ended on an uncomfortable note: turbo codes work spectacularly, and the best available explanation (EXIT charts) is an approximation. The turbo shock of 1993 sent the whole field hunting for other codes that might respond to iterative decoding, and in 1996 MacKay and Neal found one sitting in the library: Gallager’s 1963 sparse-graph codes, reinvented, resimulated, and suddenly competitive with turbo. Then, in 2001, Richardson, Shokrollahi and Urbanke (RSU hereafter) gave LDPC codes the thing turbo codes never got: an exact asymptotic analysis. Their tool, density evolution, does not estimate the decoding threshold; it computes it, to as many digits as you care to run the numerics. The reason LDPC admits an exact theory while turbo does not is structural and worth stating up front. Density evolution needs the decoder’s incoming messages to be independent, which holds when the neighborhood of a node looks like a tree. A sparse random graph is locally tree-like: cycles exist, but they are long, and any fixed-depth view of the graph is cycle-free with probability approaching one as nn grows. The turbo graph is the opposite: two enormous structured cycles (the two trellises) threaded through an interleaver, tree-like nowhere. So turbo got EXIT charts, a Gaussian approximation; LDPC got a theorem. This page builds that theorem, uses it to design codes that landed 0.0045 dB from Shannon’s limit, and then walks the result back into the real world of encoders, standards, and the failure modes the theorem cannot see.

Irregular LDPC Code Ensembles

Gallager’s codes are regular: every variable node has the same degree jj, every check node the same degree kk. The first modern move, due to Luby and coauthors in the late 1990s, was to ask why. Suppose instead you give some variable nodes very high degree. A high-degree variable node collects evidence from many checks at once, so it converges fast and becomes reliable early. Once reliable, it feeds strong messages into every check it touches, and those checks in turn rescue their weaker, low-degree neighbors. An irregular graph is a bootstrapping structure: a well-connected core locks in first and pulls the periphery along behind it. The catch is conservation: the total edge count is pinned by the rate, so every high-degree variable node must be paid for with low-degree ones, and low-degree variable nodes are individually fragile. Where the optimal balance lies is a genuine optimization problem, and solving it is exactly what RSU’s paper does. But first the balance needs a notation.

RSU describe a degree profile by a pair of polynomials, and here is the warning that saves you the most grief on this entire page: these are edge-perspective distributions, not node-perspective ones. Write

λ(x)=i2λixi1,ρ(x)=i2ρixi1,\lambda(x) = \sum_{i \ge 2} \lambda_i\, x^{i-1}, \qquad \rho(x) = \sum_{i \ge 2} \rho_i\, x^{i-1},

where λi\lambda_i is the fraction of edges attached to variable nodes of degree ii, and ρi\rho_i the fraction of edges attached to check nodes of degree ii. Both normalize, λ(1)=ρ(1)=1\lambda(1) = \rho(1) = 1. The trap is to read λi\lambda_i as the fraction of nodes of degree ii; it is not, because a degree-30 node soaks up thirty edges while a degree-2 node takes two, so high-degree nodes are heavily overrepresented in λ\lambda. If you need the node fractions, divide by degree and renormalize: the fraction of variable nodes of degree ii is (λi/i)/iλi/i(\lambda_i/i)\big/\sum_{i'} \lambda_{i'}/i'. Get this wrong and every rate calculation that follows comes out wrong.

Even the exponent is doing work. The polynomials use xi1x^{i-1}, not xix^i, and that is deliberate: a message leaving a node along one edge depends on the other i1i-1 edges at that node. This is the extrinsic principle you met in turbo decoding, and Gallager’s Theorem 4.1 before that, here baked directly into the notation so that the analysis formulas come out clean.

The rate follows from edge counting. Since iλi/i\sum_i \lambda_i/i is proportional to the number of variable nodes per edge, and likewise for checks, the design rate is

  R  =  101ρ(x)dx01λ(x)dx  \boxed{\;R \;=\; 1 - \frac{\int_0^1 \rho(x)\,dx}{\int_0^1 \lambda(x)\,dx}\;}

(the integrals compute exactly those iλi/i\sum_i \lambda_i / i sums). Sanity-check it against the regular (3,6)(3,6) code, the running benchmark for this whole page: λ(x)=x2\lambda(x) = x^2 and ρ(x)=x5\rho(x) = x^5, so 01λ=1/3\int_0^1 \lambda = 1/3 and 01ρ=1/6\int_0^1 \rho = 1/6, giving R=1(1/6)/(1/3)=1/2R = 1 - (1/6)/(1/3) = 1/2, as it must.

Finally, the object of analysis is not a single code but an ensemble, in exactly Gallager’s methodological tradition: fix nn and (λ,ρ)(\lambda, \rho), enumerate the edge sockets on the variable side and the check side, and match them by a uniformly random permutation. Every statement in the rest of this page is a statement about a code drawn at random from this ensemble; the next section explains why that is enough.

Foundations of the Analysis

Density evolution rests on three preparatory results, and it pays to state each one carefully, because each answers a specific objection you should be raising.

Step one: concentration. The obvious objection to ensemble analysis is that you will build one code, not an average. RSU’s concentration theorem answers it. Let ZZ be the number of incorrect messages after \ell iterations, for a code drawn at random from the ensemble and a random channel realization, and let zˉ=E[Z]/(ndˉv)\bar z = \mathbb{E}[Z]/(n \bar d_v) be its normalized expectation. Then there is a constant β>0\beta > 0, depending on (λ,ρ)(\lambda,\rho) and \ell but crucially not on nn, such that for any ε>0\varepsilon > 0

Pr[Zndˉvzˉ>ε]    2eβε2n,\Pr\left[\,\left|\tfrac{Z}{n \bar d_v} - \bar z\right| > \varepsilon\,\right] \;\le\; 2\,e^{-\beta \varepsilon^2 n},

and separately the ensemble average itself converges to the cycle-free (“tree”) value pp_\ell with error at most γ/n\gamma/n. The proof of the first bound is an Azuma-Hoeffding martingale argument (expose the graph’s edges and the noise one item at a time; each exposure moves ZZ by a bounded amount); the second is a direct count showing a depth-22\ell neighborhood contains a cycle with probability O(1/n)O(1/n). Chain the two statements together and you get the license for everything that follows: a random long code performs like the ensemble average, which performs like the tree value that density evolution computes, and each link in the chain carries a quantified error term. Analyzing the ensemble is analyzing your code.

Step two: the tree assumption. Fix the iteration count \ell and let nn \to \infty; then the depth-22\ell neighborhood of a randomly chosen variable node is cycle-free with probability tending to one. On a tree, every incoming message to a node is statistically independent of every other, so the belief-propagation update is exact, and (this is the part that matters for analysis) the distributions of the messages compose by simple convolution. Now, the objection: any finite code has cycles, so the assumption is literally false for every code you will ever build. The resolution is that the tree assumption is never applied to a finite code. It is applied in the limit, where it holds; concentration then bounds how far a finite code can stray from the limit, and the deviation shrinks exponentially in nn. Empirically the limit is an excellent guide: RSU simulate an n=106n = 10^6 code and land 0.13 dB from capacity against a predicted threshold 0.06 dB from capacity. The threshold is the limit; finite length is the gap to it. Beware the shorthand “density evolution assumes a cycle-free code”; what it assumes is the ensemble average as nn \to \infty, which is a different and defensible thing.

You can watch that gap close. Simulating the regular (3,6) ensemble at three block lengths, against its own density-evolution threshold of 1.11 dB:

Bit error rate against Eb/N0 for a regular (3,6) LDPC code at block lengths 252, 1008 and 4032, decoded with normalized min-sum. A vertical dashed line marks the asymptotic density-evolution threshold at 1.11 dB. Each fourfold increase in block length moves the waterfall left, closer to that line, and makes it markedly steeper: the shortest code decays gently across two and a half decibels, while the longest falls off a cliff within half a decibel of the threshold.
Bit error rate against Eb/N0 for a regular (3,6) LDPC code at block lengths 252, 1008 and 4032, decoded with normalized min-sum. A vertical dashed line marks the asymptotic density-evolution threshold at 1.11 dB. Each fourfold increase in block length moves the waterfall left, closer to that line, and makes it markedly steeper: the shortest code decays gently across two and a half decibels, while the longest falls off a cliff within half a decibel of the threshold.

The threshold is a vertical line no finite code reaches, but the approach to it is orderly and visible. Lengthening the code does two things at once: it moves the waterfall toward the threshold, and it sharpens the waterfall into an actual cliff. Both are the concentration theorem made visual, since a longer code deviates less from the ensemble average and its neighborhoods are tree-like to greater depth. Note also what the asymptotic theory buys you in practice: having computed one number, 1.11 dB, from a cycle-free idealization, you know where the waterfall of a code you have not yet built will eventually sit.

Step three: symmetry, and the all-ones codeword. Tracking message distributions would still be hopeless if the answer depended on which codeword was sent. It does not, provided two symmetries hold. The channel must be output-symmetric, p(yx=0)=p(yx=1)p(y \mid x{=}0) = p(-y \mid x{=}1), and the decoder’s update rules must respect that symmetry; RSU’s Theorem 3 proves the belief-propagation updates preserve a precise density symmetry condition, f(x)=f(x)exf(-x) = f(x)\,e^{-x}, through every iteration. Under these conditions the conditional error probability is identical for every transmitted codeword, so you may analyze the most convenient one: the all-ones codeword in ±1\pm 1 notation (equivalently the all-zeros codeword in bit notation). It is worth checking the symmetry condition once by hand for the channel that matters most. For BI-AWGN with unit amplitude and noise variance σ2\sigma^2, the channel LLR is Gaussian with mean μ=2/σ2\mu = 2/\sigma^2 and variance s2=4/σ2s^2 = 4/\sigma^2; note s2=2μs^2 = 2\mu, the consistency condition that will return in the Gaussian approximation. Then

f(x)f(x)=exp ⁣((x+μ)2(xμ)22s2)=exp ⁣(4xμ22μ)=ex,\frac{f(-x)}{f(x)} = \exp\!\left(-\frac{(x+\mu)^2 - (x-\mu)^2}{2 s^2}\right) = \exp\!\left(-\frac{4 x \mu}{2 \cdot 2\mu}\right) = e^{-x},

exactly as required. With all-ones transmitted, the sign convention becomes wonderfully simple: a positive message is correct, a negative message is an error, and all of density evolution reduces to one question asked over and over: how much probability mass does the message density still have on the negative axis?

The Density Evolution Recursion

Now the construction itself. Write P0P_0 for the density of the channel LLRs, QQ_\ell for the density of variable-to-check messages at iteration \ell (with Q0=P0Q_0 = P_0), and RR_\ell for the density of check-to-variable messages. The task is to compute QQ_\ell from Q1Q_{\ell-1}, and it splits into an easy half and a hard half.

The easy half is the variable node. A degree-ii variable node sends out its channel LLR plus the sum of i1i-1 incoming check messages, and on the tree those summands are independent. The density of a sum of independent random variables is the convolution of their densities, so a degree-ii node emits P0R(i1)P_0 \circledast R_\ell^{\circledast(i-1)}, where \circledast is ordinary convolution on R\mathbb{R}. To get the density on a randomly chosen edge, average over the edge-degree distribution, and here you see precisely why λ\lambda was defined per edge rather than per node:

Q  =  P0i2λiR(i1).Q_\ell \;=\; P_0 \circledast \sum_{i \ge 2} \lambda_i\, R_\ell^{\circledast(i-1)}.

The hard half is the check node, because its update rule is multiplicative:

tanhLcv2  =  vtanhLvc2.\tanh\frac{L_{c \to v}}{2} \;=\; \prod_{v'} \tanh\frac{L_{v' \to c}}{2}.

Densities do not compose under products, and you cannot rescue the situation by taking logarithms, because tanh(L/2)\tanh(L/2) ranges over [1,1][-1,1] and the factors can be negative. This is where RSU deploy the one genuinely clever trick in the whole construction: change coordinates so that the product becomes a sum. Split each message mm into a sign and a magnitude,

m    (s,r),s={0m01m<0,r=m,m \;\longmapsto\; (s, r), \qquad s = \begin{cases} 0 & m \ge 0 \\ 1 & m < 0 \end{cases}, \qquad r = |m|,

and handle the two parts separately. The sign part is immediate: the sign of a product is the product of the signs, and in {0,1}\{0,1\} notation a product of signs is an XOR, which is addition in GF(2)\mathrm{GF}(2). The magnitude part is where the logarithm becomes legal: taking absolute values gives tanh(rout/2)=tanh(r/2)\tanh(r_{\text{out}}/2) = \prod_\ell \tanh(r_\ell/2) with every factor now in (0,1](0,1], so apply ln-\ln to both sides and define the magnitude map

γ(r)    lntanhr2  =  lner+1er1,\gamma(r) \;\triangleq\; -\ln\tanh\frac{r}{2} \;=\; \ln\frac{e^r + 1}{e^r - 1},

which maps (0,)(0,\infty) to (0,)(0,\infty) and turns the product into the sum γ(rout)=γ(r)\gamma(r_{\text{out}}) = \sum_\ell \gamma(r_\ell).

The function γ\gamma has a lovely property that halves your implementation work: it is an involution, γ(γ(x))=x\gamma(\gamma(x)) = x, so the transform is its own inverse. The proof takes three lines. Let y=γ(x)y = \gamma(x), so ey=coth(x/2)=ex+1ex1e^y = \coth(x/2) = \frac{e^x + 1}{e^x - 1}. Then

cothy2=ey+1ey1=(ex+1)+(ex1)(ex+1)(ex1)=2ex2=ex,\coth\frac{y}{2} = \frac{e^y + 1}{e^y - 1} = \frac{(e^x+1) + (e^x-1)}{(e^x+1) - (e^x-1)} = \frac{2e^x}{2} = e^x,

so γ(y)=lncoth(y/2)=x\gamma(y) = \ln\coth(y/2) = x. One function serves as both the forward and the inverse transform.

Assemble the pieces. Let Γ\Gamma denote the operator that pushes a density on R\mathbb{R} through m(signm,γ(m))m \mapsto (\operatorname{sign} m,\, \gamma(|m|)), producing a density on the group G=GF(2)×[0,)\mathcal{G} = \mathrm{GF}(2) \times [0,\infty). Under Γ\Gamma, the check-node operation is componentwise addition in G\mathcal{G}, so densities compose by convolution over G\mathcal{G}: XOR-convolution in the sign coordinate, ordinary convolution in the magnitude coordinate. Averaging over the check-degree distribution and transforming back,

R  =  Γ1 ⁣[i2ρi(Γ[Q1])G(i1)],R_\ell \;=\; \Gamma^{-1}\!\left[\sum_{i \ge 2} \rho_i \left(\Gamma[Q_{\ell-1}]\right)^{\circledast_{\mathcal{G}}(i-1)}\right],

and splicing this into the variable-node update gives the complete recursion, the central object of the page:

  Q  =  P0    i2λi(Γ1 ⁣[j2ρj(Γ[Q1])G(j1)]) ⁣(i1),Q0=P0.  \boxed{\;Q_\ell \;=\; P_0 \;\circledast\; \sum_{i\ge2}\lambda_i \left(\Gamma^{-1}\!\left[\sum_{j\ge2} \rho_j \left(\Gamma[Q_{\ell-1}]\right)^{\circledast_{\mathcal{G}}(j-1)}\right]\right)^{\!\circledast(i-1)}, \qquad Q_0 = P_0.\;}

From QQ_\ell, read off the error probability as the mass on the wrong side of zero (recall: under all-ones, negative means error), Pe()=0Q+12Q({0})P_e^{(\ell)} = \int_{-\infty}^{0^-} Q_\ell + \tfrac12 Q_\ell(\{0\}), and define the threshold as the worst channel the decoder survives:

σ  =  sup{σ  :  Pe()0 as }.\sigma^* \;=\; \sup\left\{\sigma \;:\; P_e^{(\ell)} \to 0 \text{ as } \ell \to \infty\right\}.

RSU’s Corollary 1 confirms the two natural notions of success coincide: Pe()0P_e^{(\ell)} \to 0 if and only if QQ_\ell converges to a point mass at ++\infty, so you may monitor whichever is numerically convenient.

The recursion is exact but infinite-dimensional, so in practice you quantize the LLR axis (a uniform grid on roughly [25,25][-25, 25] with 2102^{10} to 2142^{14} bins is plenty; an LLR of 25 is certainty to one part in 101110^{11}, and the quantization must be done in a way that preserves the symmetry condition, or the threshold drifts). Both convolutions then become fast transforms: FFT on the real axis for variable nodes; for check nodes, a two-point Hadamard transform diagonalizes the GF(2)\mathrm{GF}(2) convolution on the sign index while an FFT handles the magnitude axis. In sketch:

Python
def de_iteration(Q, P0, lam, rho):
    # check node: to GF(2) x R+, convolve, back
    G    = Gamma(Q)                      # (sign, gamma(|x|))
    Ghat = hadamard_sign(fft_mag(G))
    R    = Gamma(ifft_mag(inv_hadamard_sign(
               sum(rho[i] * Ghat**(i-1) for i in rho))))   # Gamma inverts itself
    # variable node: ordinary convolution on R
    Rhat = fft(R)
    return ifft(fft(P0) * sum(lam[i] * Rhat**(i-1) for i in lam))

One iteration costs O(BlogB)O(B \log B) for BB bins, milliseconds on anything modern, and finding a threshold is a bisection over the channel parameter with a few hundred DE iterations per probe. In 1963 this computation was inconceivable; in 2001 it was routine, and that gap in arithmetic is a fair summary of why LDPC codes had to wait.

Density Evolution for the Binary Erasure Channel

Everything above collapses beautifully on the binary erasure channel, and running the collapsed version by hand is the single fastest way to make density evolution feel real. On the BEC a message is either known exactly or erased, so the entire density reduces to one number: xx_\ell, the probability that a variable-to-check message is still an erasure at iteration \ell. Trace one round. A check node can resolve its outgoing edge only if all of its other i1i-1 inputs are known, which happens with probability (1x)i1(1-x)^{i-1} for a degree-ii check; averaging over the edge-degree distribution, the check output is erased with probability 1ρ(1x)1 - \rho(1-x). A variable node’s outgoing message is erased only if its own channel observation was erased (probability ϵ\epsilon) and all i1i-1 of its other check inputs are erased; averaging with λ\lambda gives the scalar recursion

  x+1  =  ϵλ(1ρ(1x)),x0=ϵ.  \boxed{\;x_{\ell+1} \;=\; \epsilon \,\cdot\, \lambda\big(1 - \rho(1 - x_\ell)\big), \qquad x_0 = \epsilon.\;}

Notice how the edge-perspective polynomials slot straight into their roles: the whole two-paragraph construction above is three symbols per node type. This is a recursion you can iterate in three lines of code, or on paper.

Do it for the (3,6)(3,6) code: λ(x)=x2\lambda(x) = x^2, ρ(x)=x5\rho(x) = x^5, so x+1=ϵ(1(1x)5)2x_{\ell+1} = \epsilon\,(1 - (1-x_\ell)^5)^2. Iterate from x0=ϵx_0 = \epsilon and watch what happens as you tune ϵ\epsilon. For small ϵ\epsilon the sequence crashes to zero; past a critical value it stalls at a nonzero fixed point. The critical value is the threshold, ϵ0.4294\epsilon^* \approx 0.4294, against a capacity limit of 1R=0.51 - R = 0.5 for rate one half: the regular (3,6)(3,6) code recovers about 86 percent of what is achievable on the BEC. RSU’s Theorem 6 removes even the need to iterate, characterizing the threshold directly through the fixed points: ϵ=infx(0,1]x/λ(1ρ(1x))\epsilon^* = \inf_{x \in (0,1]} x \big/ \lambda(1 - \rho(1-x)), which for (3,6)(3,6) evaluates to the same 0.42944.

BEC density-evolution trajectories for the regular (3,6) code: erasure probability x_l versus iteration for several values of epsilon around the threshold 0.42944. Below threshold the trajectory falls off a cliff to zero; at and above threshold it locks onto a nonzero fixed point. The cliff also moves right as epsilon approaches the threshold from below, meaning near-threshold operation costs many more iterations.
BEC density-evolution trajectories for the regular (3,6) code: erasure probability x_l versus iteration for several values of epsilon around the threshold 0.42944. Below threshold the trajectory falls off a cliff to zero; at and above threshold it locks onto a nonzero fixed point. The cliff also moves right as epsilon approaches the threshold from below, meaning near-threshold operation costs many more iterations.

The figure shows the recursion actually run, and it repays a careful look. Below threshold, xx_\ell dawdles along a plateau and then plunges; this is the waterfall, seen from inside the math. At or above threshold, the trajectory flattens onto a fixed point and decoding fails no matter how long you iterate. And watch the cliff slide rightward as ϵ\epsilon approaches ϵ\epsilon^* from below: the closer to threshold you operate, the more iterations the plunge costs. That is not an artifact; it is the iteration-budget-versus-SNR trade-off that every practical decoder schedule fights, visible already in a three-line scalar recursion.

The Stability Condition

A fixed point at x=0x = 0 is only useful if it attracts, and asking when it does yields the most-used practical result in RSU’s paper. Linearize the BEC recursion around zero. For small xx, expand ρ(1x)=1xρ(1)+O(x2)\rho(1-x) = 1 - x\rho'(1) + O(x^2), so 1ρ(1x)=xρ(1)+O(x2)1 - \rho(1-x) = x\rho'(1) + O(x^2); feed that through λ\lambda, using λ(0)=0\lambda(0) = 0 (there are no degree-1 variable nodes), to get λ(xρ(1)+O(x2))=λ(0)ρ(1)x+O(x2)\lambda(x\rho'(1) + O(x^2)) = \lambda'(0)\rho'(1)\,x + O(x^2). The recursion near zero is therefore x+1=ϵλ(0)ρ(1)x+O(x2)x_{\ell+1} = \epsilon\,\lambda'(0)\rho'(1)\, x_\ell + O(x_\ell^2), and the origin attracts precisely when the multiplier is below one. That is RSU’s Theorem 4, the stability condition on the BEC:

  λ(0)ρ(1)  <  1ϵ.  \boxed{\;\lambda'(0)\,\rho'(1) \;<\; \frac{1}{\epsilon}.\;}

For a general binary-input symmetric channel the message is a density rather than a number, so linearizing requires a scalar surrogate for “how much error remains.” The right surrogate is the Bhattacharyya functional B(f)=f(x)ex/2dx\mathfrak{B}(f) = \int f(x)\,e^{-x/2}\,dx, chosen for two properties: it upper-bounds the error probability, and it is multiplicative under variable-node convolution, B(fg)=B(f)B(g)\mathfrak{B}(f \circledast g) = \mathfrak{B}(f)\,\mathfrak{B}(g), because the integral is a two-sided Laplace transform evaluated at 1/2-1/2 and transforms turn convolutions into products. Running the same linearization with B\mathfrak{B} in place of xx gives the multiplier λ(0)ρ(1)B(P0)\lambda'(0)\rho'(1)\mathfrak{B}(P_0), hence RSU’s Theorem 5: stability on a general channel requires λ(0)ρ(1)<1/B(P0)\lambda'(0)\rho'(1) < 1/\mathfrak{B}(P_0). Check it against the two channels you care about. On the BEC, the channel LLR is ++\infty with probability 1ϵ1-\epsilon and 00 with probability ϵ\epsilon, so B(P0)=(1ϵ)0+ϵ1=ϵ\mathfrak{B}(P_0) = (1-\epsilon)\cdot 0 + \epsilon \cdot 1 = \epsilon, recovering the hand derivation exactly. On BI-AWGN, P0=N(μ,2μ)P_0 = \mathcal{N}(\mu, 2\mu) with μ=2/σ2\mu = 2/\sigma^2, and the integral is the moment generating function at 1/2-1/2: B(P0)=exp(μ/2+2μ/8)=eμ/4=e1/(2σ2)\mathfrak{B}(P_0) = \exp(-\mu/2 + 2\mu/8) = e^{-\mu/4} = e^{-1/(2\sigma^2)}, giving the condition λ(0)ρ(1)<e1/(2σ2)\lambda'(0)\rho'(1) < e^{1/(2\sigma^2)}.

Now read off what the two derivatives actually are. Every term of λ(x)\lambda'(x) except the first carries a factor xi2x^{i-2} that vanishes at zero, so λ(0)=λ2\lambda'(0) = \lambda_2: the fraction of edges on degree-2 variable nodes. And ρ(1)=iρi(i1)\rho'(1) = \sum_i \rho_i (i-1) is the average of dc1d_c - 1 over edges. So the stability condition says, in engineering terms: the amount of degree-2 material you may use is capped, inversely, by the average check degree and by how bad the channel is.

Stop and notice what this is, because it is one of the best moments in the whole story. Gallager’s Theorem 2.5, from 1963, showed that codes built entirely from degree-2 variable nodes are hopeless: degree-2 nodes form chains, chains must close into cycles within O(logn)O(\log n) steps, and a closed cycle is a codeword of logarithmic weight. The stability condition is the same fact reborn in a different mathematical language. In 1963 the failure mode was “logarithmic minimum distance,” proved by counting tree tiers; in 2001 it is “the zero fixed point of density evolution is unstable,” proved by linearization. Same mechanism, same design rule, two formalisms 38 years apart. And the modern version sharpens the old one into a quantitative budget: degree-2 nodes are not forbidden, they are rationed, and RSU observe that capacity-approaching sequences on the BEC push λ2\lambda_2 right up against the bound. The best codes spend the entire budget.

The Gaussian Approximation

Exact density evolution costs an FFT pipeline per iteration; a degree-distribution search needs millions of threshold evaluations; the product of those two facts is a practical bottleneck. Chung, Richardson and Urbanke supplied the workhorse shortcut: assume every message density is a symmetric Gaussian. Symmetry forces the consistency condition s2=2μs^2 = 2\mu you verified earlier, so a symmetric Gaussian is described by its mean alone, and the infinite-dimensional recursion collapses to a scalar one.

The variable-node half of the collapse is free. Means of independent summands add, so a degree-ii variable node outputs mean μv=μ0+(i1)μc\mu_v = \mu_0 + (i-1)\mu_c with μ0=2/σ2\mu_0 = 2/\sigma^2 on BI-AWGN, and the variances add consistently too (2μ0+(i1)2μc=2μv2\mu_0 + (i-1)\,2\mu_c = 2\mu_v), so the Gaussian family is genuinely closed under this step. The check node is where the approximation lives. Take expectations of the tanh rule and define

ϕ(μ)    1E[tanhL2],LN(μ,2μ),\phi(\mu) \;\triangleq\; 1 - \mathbb{E}\big[\tanh\tfrac{L}{2}\big], \qquad L \sim \mathcal{N}(\mu, 2\mu),

a continuous, strictly decreasing function with ϕ(0)=1\phi(0) = 1 and ϕ(μ)0\phi(\mu) \to 0, so its inverse exists. Independence of the check inputs gives 1ϕ(μc)=j(1ϕ(μv,j))1 - \phi(\mu_c) = \prod_j (1 - \phi(\mu_{v,j})), and this step is the approximation: the true check output is not Gaussian, and you re-Gaussianize it by matching this single moment. Averaging over both degree distributions yields the full irregular recursion,

μc()  =  ϕ1 ⁣(1jρj[1iλiϕ(μ0+(i1)μc(1))]j1),\mu_c^{(\ell)} \;=\; \phi^{-1}\!\left(1 - \sum_j \rho_j \Big[\,1 - \sum_i \lambda_i\, \phi\big(\mu_0 + (i-1)\,\mu_c^{(\ell-1)}\big)\Big]^{j-1}\right),

started from μc(0)=0\mu_c^{(0)} = 0. Divergence of μc\mu_c to infinity means success; a finite fixed point means failure; bisection over σ\sigma finds the threshold. The function ϕ\phi has no closed form (tabulate it, or use the standard curve fits), the answers land within a few hundredths of a dB of exact DE, usually on the slightly optimistic side, and each threshold costs microseconds instead of seconds. That speed is what makes large-scale design search feasible.

Calibrate against the benchmark before trusting any implementation: the regular (3,6)(3,6) ensemble on BI-AWGN has exact-DE threshold σ=0.8809\sigma^* = 0.8809, which is Eb/N0=1.11E_b/N_0 = 1.11 dB, about 0.9 dB from the Shannon limit of 0.187 dB for rate one half with binary input. If your Gaussian-approximation code lands near σ0.88\sigma^* \approx 0.88, it works.

Optimization of Degree Distributions

Density evolution evaluates a degree profile; the remaining step is to search over profiles, and RSU’s crucial observation is that half the search is a linear program. Fix ρ\rho and the target channel. Maximizing the rate means maximizing 01λ=iλi/i\int_0^1 \lambda = \sum_i \lambda_i / i, which is linear in the unknowns λi\lambda_i. The constraints are linear too: normalization iλi=1\sum_i \lambda_i = 1, non-negativity, stability λ2<1/(ρ(1)B(P0))\lambda_2 < 1/(\rho'(1)\mathfrak{B}(P_0)), and, the interesting one, convergence. Convergence constraints are linear because the DE map itself is linear in λ\lambda for a fixed input density: the variable-node update is the λ\lambda-weighted sum P0iλiR(i1)P_0 \circledast \sum_i \lambda_i R^{\circledast(i-1)}, so “one iteration strictly reduces the error, starting from this checkpoint density” is one linear inequality per checkpoint. The working algorithm is a cutting-plane loop: solve the LP, verify with full density evolution, and if the recursion stalls at some intermediate error level, add that level as a new linear constraint and re-solve; then perturb ρ\rho by hill climbing (or run differential evolution over both polynomials) and repeat. The profiles that come out are strongly bimodal and tell the bootstrapping story of the opening section in numbers: a large mass of degree-2 and degree-3 variable nodes pressed hard against the stability bound, a small population of very high-degree nodes (degrees 15 to 30) to form the reliable core, and check degrees concentrated on one or two values.

The results, all at rate one half on BI-AWGN, are the headline of the paper and arguably of the decade:

CodeGap to Shannon
(3,6)(3,6) regular, the best regular ensembleabout 0.9 dB
RSU’s best irregular ensemble (threshold)0.06 dB
The same code simulated at n=106n = 10^6, BER 10610^{-6}0.13 dB
Chung, Forney, Richardson and Urbanke (2001)0.0045 dB

Read the table bottom-up for the punchline. Chung and coauthors, pushing the same machinery with degree-thousands profiles, produced an ensemble whose threshold sits 0.0045 dB from the Shannon limit: for engineering purposes, capacity, reached by explicit design rather than existence proof. And RSU’s Figure 2 carries the sentence the field noticed most: their n=106n = 10^6 irregular LDPC code, at 0.13 dB from capacity, “even surpasses the performance of turbo codes.” Eight years after the 1993 shock, the rediscovered 1963 code, armed with an exact theory the newcomer never had, retook the lead.

Practical Code Construction

Density evolution hands you an optimal (λ,ρ)(\lambda, \rho), but a literally random graph with that profile is unusable: storing an arbitrary edge list for n=106n = 10^6 and routing its wires through silicon is a non-starter. Real designs impose structure that preserves the degree profile while making the randomness cheap to describe:

TechniqueIdeaWhere used
Quasi-cyclic (QC)Build HH from Z×ZZ \times Z circulant permutation blocks; store only shift valuesWi-Fi, WiMAX, 5G NR
ProtographSmall template graph, lifted by ZZ copies with permuted edges; DE runs on the tiny template5G NR, CCSDS
PEGAdd edges greedily, always maximizing the girth createdshort-block designs
Staircase (IRA/RA) parity partDual-diagonal structure in the parity columns gives linear-time encodingDVB-S2, 5G NR

The staircase row addresses a pitfall subtle enough to deserve its own sentence: a sparse HH does not give you cheap encoding. Decoding complexity lives in HH, but encoding runs through the generator matrix, and Gaussian elimination on a sparse HH produces a dense GG, making naive encoding O(n2)O(n^2). Two standard fixes exist. Richardson and Urbanke’s companion paper in the same 2001 journal issue shows how approximate lower-triangulation of HH yields O(n)O(n) encoding plus a small dense correction; alternatively, design the staircase structure into the parity part of HH from the start, so parity bits fall out of a simple recursion. The second fix is why 5G looks the way it does: 5G NR specifies two QC protograph base graphs, BG1 (long blocks, rates 1/3 to 8/9) and BG2 (short blocks, rates 1/5 to 2/3), each with a staircase parity core, 51 lifting sizes for fine-grained length flexibility, and built-in punctured columns for rate matching. The decoding hardware story (why min-sum plus layered scheduling plus QC barrel shifters make LDPC the throughput king, and why interconnect rather than arithmetic is the real bottleneck) deserves its own treatment, and it gets one: the final page of this series takes the three code families into silicon together.

Trapping Sets and Error Floors

Honesty about limits, to close the technical story. Density evolution is an asymptotic, cycle-free theory, and there is a failure mode that lives exactly in its blind spot. A trapping set (a,b)(a, b) is a set of aa variable nodes whose induced subgraph has only bb odd-degree check nodes. If the channel corrupts roughly this set, the few unsatisfied checks cannot outvote the internally consistent (but wrong) mutual reinforcement of the aa variables, and the decoder locks into the wrong state; the stable-fixed-point variants, absorbing sets, are the worst offenders. These are finite-length cycle structures, precisely the objects the tree assumption erased, so density evolution says nothing about them. The practical consequence is the error floor: a code whose waterfall sits gorgeously close to capacity can flatten out at a BER of 10710^{-7} and refuse to improve, and no threshold computation would ever have warned you. Threshold and floor are governed by different mathematics and are, to first order, independent properties; a better threshold does not mean a better code, and a serious design optimizes both, using girth- and trapping-set-aware construction (PEG, the ACE metric), post-processing decoders, or an outer algebraic cleanup code (DVB-S2 wraps its LDPC in BCH for exactly this reason). One related operational point: on a graph with cycles, belief propagation can converge to something that is not a codeword at all, so practical decoders cap the iteration count and use the syndrome test Hx^T=0H\hat{\mathbf{x}}^\mathsf{T} = \mathbf{0} as the stopping rule. That test is also a quiet luxury: an LDPC decoder knows when it has won, which turbo decoding never quite did.

Check Yourself

  1. Why is analyzing transmission of the all-ones codeword legitimate, when a real transmitter sends arbitrary codewords?
  2. Write the BEC density-evolution recursion and explain what each factor is doing.
  3. Why does the check-node side of density evolution need the Γ\Gamma transform, while the variable-node side is an ordinary convolution?
  4. What is the stability condition, what does λ(0)\lambda'(0) actually measure, and what 1963 result is the condition a descendant of?
  5. Your new code has a threshold 0.08 dB from capacity but its BER curve flattens at 10710^{-7}. What is wrong, and can density evolution help you diagnose it?
  6. In the trajectory figure, why does the cliff move to the right as ϵ\epsilon approaches the threshold, and what does that cost in practice?
Answers
  1. Because the channel is output-symmetric and RSU’s Theorem 3 shows the decoder updates preserve the density symmetry condition f(x)=f(x)exf(-x) = f(x)e^{-x} at every iteration, the conditional error probability is the same for every transmitted codeword. You may therefore analyze the most convenient one. Under all-ones (in ±1\pm1 notation), sign carries all the information: positive messages are correct, negative are errors.

  2. x+1=ϵλ(1ρ(1x))x_{\ell+1} = \epsilon\,\lambda(1 - \rho(1 - x_\ell)). Inside out: 1x1 - x_\ell is the probability an incoming variable-to-check message is known; ρ(1x)\rho(1 - x_\ell) is the probability that all the other inputs of a randomly chosen check edge are known, so the check can resolve; 1ρ(1x)1 - \rho(1-x_\ell) is the probability the check’s outgoing message is still erased; λ()\lambda(\cdot) is the probability that all the other check inputs of a variable node are erased; and the leading ϵ\epsilon requires the node’s own channel observation to be erased too. The xi1x^{i-1} exponents in λ\lambda and ρ\rho encode “the other i1i-1 edges,” the extrinsic principle.

  3. A variable node combines independent pieces of evidence about the same bit, and independent log-likelihood evidence adds; the density of a sum of independent variables is the convolution of their densities, so no trick is needed. A check node constrains the XOR of its bits, and by Gallager’s parity lemma the soft bits tanh(L/2)\tanh(L/2) multiply across a check. Densities do not compose under products, and the factors can be negative, so no direct logarithm works. The fix is the coordinate change m(signm,γ(m))m \mapsto (\operatorname{sign} m, \gamma(|m|)) with γ(r)=lntanh(r/2)\gamma(r) = -\ln\tanh(r/2): signs add in GF(2)\mathrm{GF}(2), transformed magnitudes add in R+\mathbb{R}^+, so the check-node operation becomes convolution over GF(2)×[0,)\mathrm{GF}(2) \times [0,\infty). As a bonus, γ\gamma is an involution, so the same function inverts the transform.

  4. λ(0)ρ(1)<1/ϵ\lambda'(0)\rho'(1) < 1/\epsilon on the BEC, and λ(0)ρ(1)<1/B(P0)\lambda'(0)\rho'(1) < 1/\mathfrak{B}(P_0) on a general symmetric channel, where B(P0)=P0(x)ex/2dx\mathfrak{B}(P_0) = \int P_0(x)e^{-x/2}dx (equal to ϵ\epsilon on the BEC and e1/(2σ2)e^{-1/(2\sigma^2)} on BI-AWGN). λ(0)=λ2\lambda'(0) = \lambda_2 is the fraction of edges on degree-2 variable nodes, so the condition caps the degree-2 budget. It is the density-evolution descendant of Gallager’s 1963 Theorem 2.5: degree-2 nodes form chains, chains close into short cycles, and cycles create low-weight codewords. Gallager proved it as a logarithmic bound on minimum distance by counting tree tiers; RSU prove it as instability of the zero fixed point by linearization. Same mechanism, two languages.

  5. An error floor caused by trapping or absorbing sets: small sets of variable nodes whose induced subgraph leaves too few unsatisfied checks to overturn a self-consistent wrong belief. Density evolution cannot help at all, because it is asymptotic and cycle-free while trapping sets are finite-length cycle structures. Diagnose by trapping-set enumeration or importance-sampled simulation; fix with girth- or ACE-aware construction, a post-processing decoder, or an outer BCH code. Threshold and floor are independent design targets.

  6. Near the threshold, the recursion passes close to the emerging fixed point, where each iteration shrinks xx_\ell by only a tiny factor; the trajectory crawls along a plateau before the collapse. In practice this means operating near threshold costs many more decoder iterations, so a real decoder with a fixed iteration budget effectively gives up some of the theoretical threshold; the iteration budget is part of the code’s operating point.

Where This Leaves Us

The arc that began with Gallager’s forgotten thesis is now complete. Sparse graphs make message passing cheap; local tree-likeness makes it nearly exact; concentration, the tree limit, and symmetry make the ensemble analyzable; the Γ\Gamma transform makes the analysis computable; and linear programming over degree distributions turns the analysis into a design tool that produced codes 0.0045 dB from Shannon’s limit and pushed past turbo. The same machinery also knows its own boundaries: everything asymptotic and cycle-free, nothing about the trapping sets and error floors where finite codes actually fail.

Step back, though, and turbo and LDPC share both a virtue and an unfinished question. Each gets its power by injecting randomness somewhere (turbo through the interleaver, LDPC through the random graph ensemble), and for neither is there a general proof of reaching capacity: LDPC thresholds can be driven as close to the limit as computation allows, but “0.0045 dB by numerical design” is an achievement, not a theorem, and Gallager himself proved that bounded check degrees keep capacity strictly out of reach. The next entry is about the code that closed this gap from an entirely different direction: polar codes, a deterministic, recursive construction with no randomness anywhere, and the first practical family provably achieving capacity.

← Why Turbo Codes WorkIndexPolar Codes: Capacity by Construction →