A C implementation of LDPC over GF(q)
Seishi Takamura @ Stanford Univ./NTT Corporation
Since June 9, 2006.
Introduction
This page is on LDPC over GF(q>=2) software,
- GF2_LDPC: binary LDPC decoder and
- GFq_LDPC_NTT: LDPC over GF(q) q=4,8,16,32,64,128,256 decoder
absolutely with no warranty.
The source codes are written in plain C. You do not need any support applications
other than a C compiler.
I've tested with VC/gcc on Cygwin and gcc on Linux.
With use of several tricks such as
- fixed point arithmetic,
- multiplications in log-domain,
- powered by NTT (Number-Theoretic Transform i.e., FFT over GF), and
- code optimization,
it speeded up by 30 times for GF(32) and more than 1,000 times for GF(256)
compared to straightforward implementation.
This program can be used not only for multi-level signals but for
(concatenated) binary sources i.e., for m-tuple signal simply GF(2m)
does that.
About the speed, GF2_LDPC is fastest, even though taking into account
that GFq_LDPC_NTT decodes multiple bits (i.e., GF(8) LDPC is more than
three (=log28) times slower than binary LDPC. The advantage
of GFq_LDPC_NTT is its potential of better error correction performance
(you can easily find a lot of articles on this topic).
Comments are very much welcome. Please give any comments to
.
Download
The package contains C sources and some LDPC matrix files.
LDPC-v0.1.zip (649kB)
Initial version (June 9, 2006).
LDPC-v0.2.zip (649kB)
Fixed a bug in GF2_LDPC.c for irregular matrix case
(such as 3584x444).
Special thanks: Mr. David Elkouss (Feb. 5, 2008)
Compilation and demonstration
For binary decoding demo, compile and run
% gcc GF2_LDPC.c -lm
% ./a.out 0.08 4986.93xb.329.alist
This performs binary LDPC decoding of rate (m/n=)0.5
over BSC channel of 8% crossover probability.
For non-binary, macro-variable Log2Q is set to 5 (i.e., LDPC over GF(32)).
Please override the definition with compiler's command line option.
For GF(8) decoding demo, compile and run
% gcc -DLog2Q=3 GFq_LDPC_NTT.c -lm
% ./a.out bsc 0.147 q8.sp.6000.4000.3000.1
For GF(4) demo, compile and run
% gcc -DLog2Q=2 GFq_LDPC_NTT.c -lm
% ./a.out bsc 0.145 q4.sp.9000.6000.4500.1
I do not have LDPC matrices for other than GF order of 4 and 8. (your contribution
appreciated)
If you wish, you can specify maximum sum-product iteration using "-iter
num" option as the first argument e.g.,
% ./a.out -iter 50 bsc 0.145 q4.sp.9000.6000.4500.1
Internally, the demo does
- read m, n values and m*n LDPC matrix H from an alist-format file
- create a q-ary random vector x of length n (source signal)
- obtain s = Hx in GF(q) of length m (syndrome signal)
- add BSC(for q>=2) or Laplacian(for q>2) noise onto x to yield y
- do sum-product loop using y and s to obtain x^ (recovered signal)
- if Hx^ equals s, then stop, else continue from step 5
- do next experiment from step 4 (x is unchanged, only additive noise is changed)
Execution snapshot looks as follows, where HamDist stands for Hamming
distance and synd(x) means Hx.
% ./GFq_LDPC_NTT bsc 0.1 ../LDPC/q8.sp.6000.4000.3000.1
Warning! GF order(32) does not match with matrix file(8)
I'll use it anyways.
GF(32) experiment 1:
m/n=0.666667, BSC channel capacity(rate) = 0.468996 (bits)
start: HamDist(x,y)=3000 HamDist(synd(x),synd(y))=7199
1: HamDist(x,x^)=2535 HamDist(s,synd(x^))=4020
2: HamDist(x,x^)=1756 HamDist(s,synd(x^))=2619
3: HamDist(x,x^)=1075 HamDist(s,synd(x^))=1608
4: HamDist(x,x^)=514 HamDist(s,synd(x^))=794
5: HamDist(x,x^)=171 HamDist(s,synd(x^))=288
6: HamDist(x,x^)=36 HamDist(s,synd(x^))=53
7: HamDist(x,x^)=8 HamDist(s,synd(x^))=11
8: HamDist(x,x^)=0 HamDist(s,synd(x^))=0
converged.
GF(32) experiment 2:
m/n=0.666667, BSC channel capacity(rate) = 0.468996 (bits)
start: HamDist(x,y)=3000 HamDist(synd(x),synd(y))=7137
1: HamDist(x,x^)=2606 HamDist(s,synd(x^))=3887
2: HamDist(x,x^)=1818 HamDist(s,synd(x^))=2539
3: HamDist(x,x^)=1136 HamDist(s,synd(x^))=1556
4: HamDist(x,x^)=627 HamDist(s,synd(x^))=917
5: HamDist(x,x^)=247 HamDist(s,synd(x^))=379
6: HamDist(x,x^)=75 HamDist(s,synd(x^))=130
7: HamDist(x,x^)=2 HamDist(s,synd(x^))=6
8: HamDist(x,x^)=0 HamDist(s,synd(x^))=0
converged.
GF(32) experiment 3:
m/n=0.666667, BSC channel capacity(rate) = 0.468996 (bits)
start: HamDist(x,y)=3000 HamDist(synd(x),synd(y))=7191
1: HamDist(x,x^)=2661 HamDist(s,synd(x^))=4008
2: HamDist(x,x^)=1850 HamDist(s,synd(x^))=2649
3: HamDist(x,x^)=1104 HamDist(s,synd(x^))=1611
4: HamDist(x,x^)=569 HamDist(s,synd(x^))=861
5: HamDist(x,x^)=223 HamDist(s,synd(x^))=420
6: HamDist(x,x^)=59 HamDist(s,synd(x^))=103
7: HamDist(x,x^)=7 HamDist(s,synd(x^))=11
8: HamDist(x,x^)=2 HamDist(s,synd(x^))=6
9: HamDist(x,x^)=0 HamDist(s,synd(x^))=0
converged.
Notes
GF2_LDPC, a binary (GF(2)) LDPC decoder, is based on the
IEEE paper of A.D. Liveris et al.: "Compression of binary sources
with side information at the decoder using LDPC codes".
Low density parity matrix is in 'alist' format, see Prof. MacKay's
description
for details.
GFq_LDPC_NTT, a non-binary (GF(2m)) LDPC decoder, is based on
Dr. Davey's
dissertation, "Error-correction using Low-Density Parity-Check Codes".
Please be noted that
there's a typo in the first line of page 26 of the dissertation:
Ramn is the a'th coordinate
should be
Ramn is the (zm - Hmna)'th coordinate
which is already incorporated in my source code.
The original alist format is aimed for binary LDPC, however, its extension to GF(q>2) is
found in
GFQmatrices directory.
Also the matrix examples (in fact these are the only GF(q>2) matrices I
know of) can be found in the same directory.
API
Binary LDPC (GF2_LDPC)
- void initdec(char *s)
- s: alist file name
- -- Reads in a low density parity check matrix H in alist format and
sets up necessary variables such as m, n etc.
- void bsc(int x[], int y[], double p, int q0[])
- -- lets y[] be noisy version of x[], with crossover probability of p,
and sets the values of q0[] (see description below)
- int dec(int q0[], int s[], int loop_max, int x[])
- q0[0<=i<n]: log likelihood ratio, equals log(Prob(x[i]=0|y[i]) / Prob(x[i]=1|y[i]))
- s[m]: syndrome
- loop_max: maximum loop iteration
- x[n]: original input signal
- return value: 0=converged, -1=not converged
- -- sum-product decoder. Please be noted that information of y[] is already incorporated in q0[]
Non-binary LDPC (GFq_LDPC_NTT)
- void initdec(char *s)
- s: matrix file name
- -- Reads in a low density parity check matrix H in extended alist format and
sets up necessary variables such as m, n etc.
- void bsc(int x[], int y[], double p, int **logfna)
- -- lets each bit of y[] be noisy version of corresponding bit of x[], with crossover probability of p,
and sets the values of logfna[][] (see below)
- void lap(int x[], int y[], double stddev, int **logfna)
- -- lets y[] be noisy version of x[], with Laplacian noise with standard deviation of stddev,
and sets the values of logfna[][] (see below)
- int dec(int **logfna, int z[], int loop_max, int x[])
- logfna[0<=i<n][0<=a<q]: prior, equals log(Prob(x[i]=a|y[i])
- z[m]: syndrome
- loop_max: maximum loop iteration
- x[n]: original input signal (not used for sum-product calculation, just for reference)
- -- sum-product decoder
Tweaks and modifications
You might perhaps want to tweak the following fixed-point-number-related
definitions in the source code.
#define INT 6 // int part
#define DECI 14 // fraction part
Only up to GF(256) is supported so far. To raise this limit, just add
new logq[] and expq[] arrays appropriately (your contribution
appreciated).
The case for GF(prime_number) can be simply implemented. Just take
modulo of the prime number after normal
addition/subtraction/multiplication operations.
Bugs
You can compile GFq_LDPC_NTT.c as a binary LDPC decoder by compiling
% gcc -DLog2Q=1 GFq_LDPC_NTT.c -lm
but this does not execute well. Please use GF2_LDPC.c for this purpose.
LDPC decoding convergence (i.e., successful/unsuccessful decoding) is
now automatically determined by watching the transition of Hamming
distance between given syndrome and temporarily decoded signal's
syndrome. Though this practically works, it is not very intelligent.