├── test_code.mat ├── test_ldpc_decode.m ├── test_ldpc_decode.py ├── ldpc_decode.m └── ldpc_decode.py /test_code.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willemolding/FastPythonLDPC/HEAD/test_code.mat -------------------------------------------------------------------------------- /test_ldpc_decode.m: -------------------------------------------------------------------------------- 1 | load('test_code.mat') 2 | 3 | f1=1./(1+exp(-2*z/sigma^2)); % likelihoods 4 | f0=1-f1; 5 | %prevent f from becoming exactly 0.5 6 | f1(f1==0.5) = 0.5+1e-20; 7 | f0(f0==0.5) = 0.5-1e-20; 8 | 9 | 10 | %decode conventionally 11 | tic 12 | for i = 1:10 13 | [z_hat, success, k] = ldpc_decode(f0,f1,full(H),100); 14 | end 15 | toc 16 | x_hat = z_hat(size(G,2)+1-size(G,1):size(G,2)); 17 | b = x_hat'; 18 | 19 | nErrors = sum(x ~= b) 20 | k 21 | success 22 | -------------------------------------------------------------------------------- /test_ldpc_decode.py: -------------------------------------------------------------------------------- 1 | __author__ = 'willemolding' 2 | 3 | from scipy import sparse, io 4 | import numpy as np 5 | import time 6 | from ldpc_decode import ldpc_decode 7 | 8 | def run_test(): 9 | 10 | data = io.loadmat('test_code.mat') 11 | G = data['G'] 12 | H = data['H'] 13 | x = data['x'] 14 | y = data['y'] 15 | z = data['z'] 16 | sigma = data['sigma'] 17 | 18 | f1 = 1. / (1 + np.exp(-2 * z / sigma ** 2)) # likelihoods 19 | f0 = 1 - f1 20 | f1[f1 == 0.5] = 0.5 + 1e-20 21 | f0[f0 == 0.5] = 0.5 - 1e-20 22 | 23 | t0 = time.time() 24 | for i in range(10): 25 | z_hat, success, k = ldpc_decode(f0, f1, H, 100) 26 | t1 = time.time() 27 | 28 | x_hat = z_hat[G.shape[1] - G.shape[0]:G.shape[1]] 29 | b = x_hat.T 30 | 31 | nErrors = np.sum(x != b) 32 | print "n_errors", nErrors 33 | print "completed in ", t1-t0, "seconds" 34 | print 'iterations', k 35 | print 'success', success 36 | 37 | if __name__ == "__main__": 38 | run_test() 39 | -------------------------------------------------------------------------------- /ldpc_decode.m: -------------------------------------------------------------------------------- 1 | function [x_hat, success, k, prob ] = ldpc_decode(f0,f1,H,max_iter) 2 | % decoding of binary LDPC as in Elec. Letters by MacKay&Neal 13March1997 3 | % For notations see the same reference. 4 | % function [x_hat, success, k] = ldpc_decode(y,f0,f1,H) 5 | % outputs the estimate x_hat of the ENCODED sequence for 6 | % the received vector y with channel likelihoods of '0' and '1's 7 | % in f0 and f1 and parity check matrix H. Success==1 signals 8 | % successful decoding. Maximum number of iterations is set to 100. 9 | % k returns number of iterations until convergence. 10 | % 11 | % Example: 12 | % We assume G is systematic G=[A|I] and, obviously, mod(G*H',2)=0 13 | % sigma = 1; % AWGN noise deviation 14 | % x = (sign(randn(1,size(G,1)))+1)/2; % random bits 15 | % y = mod(x*G,2); % coding 16 | % z = 2*y-1; % BPSK modulation 17 | % z=z + sigma*randn(1,size(G,2)); % AWGN transmission 18 | % 19 | % f1=1./(1+exp(-2*z/sigma^2)); % likelihoods 20 | % f0=1-f1; 21 | % [z_hat, success, k] = ldpc_decode(z,f0,f1,H); 22 | % x_hat = z_hat(size(G,2)+1-size(G,1):size(G,2)); 23 | % x_hat = x_hat'; 24 | 25 | % Copyright (c) 1999 by Igor Kozintsev igor@ifp.uiuc.edu 26 | % $Revision: 1.1 $ $Date: 1999/07/11 $ 27 | % fixed high-SNR decoding 28 | 29 | [m,n] = size(H); if m>n, H=H'; [m,n] = size(H); end 30 | if ~issparse(H) % make H sparse if it is not sparse yet 31 | [ii,jj,sH] = find(H); 32 | H = sparse(ii,jj,sH,m,n); 33 | end 34 | 35 | %initialization 36 | [ii,jj] = find(H); % subscript index to nonzero elements of H 37 | indx = sub2ind(size(H),ii,jj); % linear index to nonzero elements of H 38 | q0 = H * spdiags(f0(:),0,n,n); 39 | sq0 = full(q0(indx)); 40 | sff0 = sq0; 41 | 42 | q1 = H * spdiags(f1(:),0,n,n); 43 | sq1 = full(q1(indx)); 44 | sff1 = sq1; 45 | 46 | %iterations 47 | k=0; 48 | success = 0; 49 | while ((success == 0) & (k < max_iter)), 50 | k = k+1; 51 | 52 | %horizontal step 53 | sdq = sq0 - sq1; sdq(sdq==0) = 1e-20; % if f0 = f1 = .5 54 | dq = sparse(ii,jj,sdq,m,n); 55 | Pdq_v = full(real(exp(sum(spfun('log',dq),2)))); % this is ugly but works :) 56 | Pdq = spdiags(Pdq_v(:),0,m,m) * H; 57 | sPdq = full(Pdq(indx)); 58 | sr0 = (1+sPdq./sdq)./2; sr0(abs(sr0) < 1e-20) = 1e-20; 59 | sr1 = (1-sPdq./sdq)./2; sr1(abs(sr1) < 1e-20) = 1e-20; 60 | r0 = sparse(ii,jj,sr0,m,n); 61 | r1 = sparse(ii,jj,sr1,m,n); 62 | 63 | %vertical step 64 | Pr0_v = full(real(exp(sum(spfun('log',r0),1)))); 65 | Pr0 = H * spdiags(Pr0_v(:),0,n,n); 66 | sPr0 = full(Pr0(indx)); 67 | Q0 = full(sum(sparse(ii,jj,sPr0.*sff0,m,n),1))'; 68 | sq0 = sPr0.*sff0./sr0; 69 | 70 | Pr1_v = full(real(exp(sum(spfun('log',r1),1)))); 71 | Pr1 = H * spdiags(Pr1_v(:),0,n,n); 72 | sPr1 = full(Pr1(indx)); 73 | Q1 = full(sum(sparse(ii,jj,sPr1.*sff1,m,n),1))'; 74 | sq1 = sPr1.*sff1./sr1; 75 | 76 | sqq = sq0+sq1; 77 | sq0 = sq0./sqq; 78 | sq1 = sq1./sqq; 79 | 80 | %tentative decoding 81 | QQ = Q0+Q1; 82 | prob = Q1./QQ; 83 | Q0 = Q0./QQ; 84 | Q1 = Q1./QQ; 85 | 86 | tent = (Q1-Q0); % soft? 87 | x_hat = (sign(tent)+1)/2; % hard bits estimated 88 | if rem(H*x_hat,2) == 0, success = 1; end 89 | end 90 | 91 | -------------------------------------------------------------------------------- /ldpc_decode.py: -------------------------------------------------------------------------------- 1 | # function [x_hat, success, k, prob ] = ldpc_decode(f0,f1,H,max_iter) 2 | # decoding of binary LDPC as in Elec. Letters by MacKay&Neal 13March1997 3 | # For notations see the same reference. 4 | # function [x_hat, success, k] = ldpc_decode(y,f0,f1,H) 5 | # outputs the estimate x_hat of the ENCODED sequence for 6 | # the received vector y with channel likelihoods of '0' and '1's 7 | # in f0 and f1 and parity check matrix H. Success==1 signals 8 | # successful decoding. Maximum number of iterations is set to 100. 9 | # k returns number of iterations until convergence. 10 | # 11 | # Example: 12 | # We assume G is systematic G=[A|I] and, obviously, mod(G*H',2)=0 13 | # sigma = 1; # AWGN noise deviation 14 | # x = (sign(randn(1,size(G,1)))+1)/2; # random bits 15 | # y = mod(x*G,2); # coding 16 | # z = 2*y-1; # BPSK modulation 17 | # z=z + sigma*randn(1,size(G,2)); # AWGN transmission 18 | # 19 | # f1=1./(1+exp(-2*z/sigma^2)); # likelihoods 20 | # f0=1-f1; 21 | # [z_hat, success, k] = ldpc_decode(z,f0,f1,H); 22 | # x_hat = z_hat(size(G,2)+1-size(G,1):size(G,2)); 23 | # x_hat = x_hat'; 24 | 25 | # Copyright (c) 1999 by Igor Kozintsev igor@ifp.uiuc.edu 26 | # $Revision: 1.1 $ $Date: 1999/07/11 $ 27 | # fixed high-SNR decoding 28 | 29 | import numpy as np 30 | from scipy import sparse 31 | 32 | 33 | def ldpc_decode(f0, f1, H, max_iter): 34 | """ 35 | A python port of the ldpc_decode matlab code. 36 | 37 | Parameters: 38 | ---------- 39 | f0 : 1D numpy array 40 | see matlab docstring 41 | f1 : 1D numpy array 42 | see matlab docstring 43 | H : 2D scipy.sparse.csc_matrix 44 | Must be a scipy sparse array of the crc type. This is the same type that matlab used so we remain compatible. 45 | max_iter : integer 46 | maximum number of iterations 47 | 48 | Returns: 49 | -------- 50 | x_hat : 1D numpy array 51 | Error corrected ENCODED sequence 52 | success : bool 53 | indicates successful convergence e.g. parity check passed 54 | k : integer 55 | number of iterations to converge 56 | prob : 57 | """ 58 | 59 | # check the matrix is correctly orientated and transpose it if required 60 | [m, n] = H.shape 61 | if m > n: 62 | H = H.t 63 | [m, n] = H.shape 64 | 65 | # if ~issparse(H) # make H sparse if it is not sparse yet 66 | # [ii, jj, sH] = find(H); 67 | # H = sparse(ii, jj, sH, m, n); 68 | 69 | # initialization 70 | ii, jj = H.nonzero() 71 | 72 | q0 = H.dot(sparse.spdiags(f0, 0, n, n, 'csc')) 73 | sq0 = q0[ii, jj].getA1() 74 | sff0 = sq0 75 | 76 | q1 = H.dot(sparse.spdiags(f1, 0, n, n, 'csc')) 77 | sq1 = q1[ii, jj].getA1() 78 | sff1 = sq1 79 | 80 | # iterations 81 | k = 0 82 | success = 0 83 | while success == 0 and k < max_iter: 84 | k += 1 85 | 86 | # horizontal step 87 | sdq = sq0 - sq1 88 | sdq[sdq == 0] = 1e-20 # if f0 = f1 = .5 89 | dq = sparse.csc_matrix((sdq, (ii, jj)), shape=(m, n)) 90 | 91 | dq.data = np.log(dq.data.astype(np.complex)) 92 | Pdq_v = np.real(np.exp(dq.sum(axis=1))) 93 | 94 | Pdq = sparse.spdiags(Pdq_v.ravel(), 0, m, m, 'csc').dot(H) 95 | sPdq = Pdq[ii, jj].getA1() 96 | 97 | sr0 = (1 + sPdq / sdq) / 2. 98 | sr0[abs(sr0) < 1e-20] = 1e-20 99 | sr1 = (1 - sPdq / sdq) / 2. 100 | sr1[np.abs(sr1) < 1e-20] = 1e-20 101 | r0 = sparse.csc_matrix((sr0, (ii, jj)), shape=(m, n)) 102 | r1 = sparse.csc_matrix((sr1, (ii, jj)), shape=(m, n)) 103 | 104 | # vertical step 105 | r0.data = np.log(r0.data.astype(np.complex)) 106 | Pr0_v = np.real(np.exp(r0.sum(axis=0))) 107 | 108 | Pr0 = H.dot(sparse.spdiags(Pr0_v.ravel(), 0, n, n, 'csc')) 109 | sPr0 = Pr0[ii, jj].getA1() 110 | Q0 = np.array(sparse.csc_matrix((sPr0 * sff0, (ii, jj)), shape=(m, n)).sum(axis=0)).T 111 | 112 | sq0 = sPr0 * sff0 / sr0 113 | 114 | r1.data = np.log(r1.data.astype(np.complex)) 115 | Pr1_v = np.real(np.exp(r1.sum(axis=0))) 116 | 117 | Pr1 = H.dot(sparse.spdiags(Pr1_v.ravel(), 0, n, n, 'csc')) 118 | sPr1 = Pr1[ii, jj].getA1() 119 | 120 | Q1 = np.array(sparse.csc_matrix((sPr1 * sff1, (ii, jj)), shape=(m, n)).sum(axis=0)).T 121 | sq1 = sPr1 * sff1 / sr1 122 | 123 | sqq = sq0 + sq1 124 | sq0 = sq0 / sqq 125 | sq1 = sq1 / sqq 126 | 127 | # tentative decoding 128 | QQ = Q0 + Q1 129 | prob = Q1 / QQ 130 | Q0 = Q0 / QQ 131 | Q1 = Q1 / QQ 132 | 133 | tent = (Q1 - Q0) # soft? 134 | x_hat = (np.sign(tent) + 1) / 2 # hard bits estimated 135 | 136 | if np.all(np.fmod(H.dot(x_hat), 2) == 0): 137 | success = 1 138 | 139 | return x_hat, success, k 140 | 141 | 142 | --------------------------------------------------------------------------------