summaryrefslogtreecommitdiff
path: root/gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py
diff options
context:
space:
mode:
Diffstat (limited to 'gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py')
-rw-r--r--gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py1276
1 files changed, 639 insertions, 637 deletions
diff --git a/gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py b/gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py
index 9563d66afb..7d5e00536b 100644
--- a/gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py
+++ b/gr-fec/python/fec/LDPC/Generate_LDPC_matrix_functions.py
@@ -21,698 +21,700 @@ from numpy.random import shuffle, randint
# verbose = 1 #######################################################
def read_alist_file(filename):
- """
- This function reads in an alist file and creates the
- corresponding parity check matrix H. The format of alist
- files is described at:
- http://www.inference.phy.cam.ac.uk/mackay/codes/alist.html
- """
-
- with open(filename, 'r') as myfile:
- data = myfile.readlines()
- numCols, numRows = parse_alist_header(data[0])
- H = zeros((numRows, numCols))
- # The locations of 1s starts in the 5th line of the file
- for lineNumber in np.arange(4, 4 + numCols):
- indices = data[lineNumber].split()
- for index in indices:
- H[int(index) - 1, lineNumber - 4] = 1
- # The subsequent lines in the file list the indices for where
- # the 1s are in the rows, but this is redundant
- # information.
-
- return H
+ """
+ This function reads in an alist file and creates the
+ corresponding parity check matrix H. The format of alist
+ files is described at:
+ http://www.inference.phy.cam.ac.uk/mackay/codes/alist.html
+ """
+
+ with open(filename, 'r') as myfile:
+ data = myfile.readlines()
+ numCols, numRows = parse_alist_header(data[0])
+ H = zeros((numRows, numCols))
+ # The locations of 1s starts in the 5th line of the file
+ for lineNumber in np.arange(4, 4 + numCols):
+ indices = data[lineNumber].split()
+ for index in indices:
+ H[int(index) - 1, lineNumber - 4] = 1
+ # The subsequent lines in the file list the indices for where
+ # the 1s are in the rows, but this is redundant
+ # information.
+
+ return H
def parse_alist_header(header):
- size = header.split()
- return int(size[0]), int(size[1])
+ size = header.split()
+ return int(size[0]), int(size[1])
def write_alist_file(filename, H, verbose=0):
- """
- This function writes an alist file for the parity check
- matrix. The format of alist files is described at:
- http://www.inference.phy.cam.ac.uk/mackay/codes/alist.html
- """
- with open(filename, 'w') as myfile:
-
- numRows = H.shape[0]
- numCols = H.shape[1]
-
- tempstring = repr(numCols) + ' ' + repr(numRows) + '\n'
- myfile.write(tempstring)
-
- tempstring1 = ''
- tempstring2 = ''
- maxRowWeight = 0
- for rowNum in np.arange(numRows):
- nonzeros = array(H[rowNum, :].nonzero())
- rowWeight = nonzeros.shape[1]
- if rowWeight > maxRowWeight:
- maxRowWeight = rowWeight
- tempstring1 = tempstring1 + repr(rowWeight) + ' '
- for tempArray in nonzeros:
- for index in tempArray:
- tempstring2 = tempstring2 + repr(index + 1) + ' '
- tempstring2 = tempstring2 + '\n'
- tempstring1 = tempstring1 + '\n'
-
- tempstring3 = ''
- tempstring4 = ''
- maxColWeight = 0
- for colNum in np.arange(numCols):
- nonzeros = array(H[:, colNum].nonzero())
- colWeight = nonzeros.shape[1]
- if colWeight > maxColWeight:
- maxColWeight = colWeight
- tempstring3 = tempstring3 + repr(colWeight) + ' '
- for tempArray in nonzeros:
- for index in tempArray:
- tempstring4 = tempstring4 + repr(index + 1) + ' '
- tempstring4 = tempstring4 + '\n'
- tempstring3 = tempstring3 + '\n'
-
- tempstring = repr(maxColWeight) + ' ' + repr(maxRowWeight) + '\n'
- # write out max column and row weights
- myfile.write(tempstring)
- # write out all of the column weights
- myfile.write(tempstring3)
- # write out all of the row weights
- myfile.write(tempstring1)
- # write out the nonzero indices for each column
- myfile.write(tempstring4)
- # write out the nonzero indices for each row
- myfile.write(tempstring2)
+ """
+ This function writes an alist file for the parity check
+ matrix. The format of alist files is described at:
+ http://www.inference.phy.cam.ac.uk/mackay/codes/alist.html
+ """
+ with open(filename, 'w') as myfile:
+
+ numRows = H.shape[0]
+ numCols = H.shape[1]
+
+ tempstring = repr(numCols) + ' ' + repr(numRows) + '\n'
+ myfile.write(tempstring)
+
+ tempstring1 = ''
+ tempstring2 = ''
+ maxRowWeight = 0
+ for rowNum in np.arange(numRows):
+ nonzeros = array(H[rowNum, :].nonzero())
+ rowWeight = nonzeros.shape[1]
+ if rowWeight > maxRowWeight:
+ maxRowWeight = rowWeight
+ tempstring1 = tempstring1 + repr(rowWeight) + ' '
+ for tempArray in nonzeros:
+ for index in tempArray:
+ tempstring2 = tempstring2 + repr(index + 1) + ' '
+ tempstring2 = tempstring2 + '\n'
+ tempstring1 = tempstring1 + '\n'
+
+ tempstring3 = ''
+ tempstring4 = ''
+ maxColWeight = 0
+ for colNum in np.arange(numCols):
+ nonzeros = array(H[:, colNum].nonzero())
+ colWeight = nonzeros.shape[1]
+ if colWeight > maxColWeight:
+ maxColWeight = colWeight
+ tempstring3 = tempstring3 + repr(colWeight) + ' '
+ for tempArray in nonzeros:
+ for index in tempArray:
+ tempstring4 = tempstring4 + repr(index + 1) + ' '
+ tempstring4 = tempstring4 + '\n'
+ tempstring3 = tempstring3 + '\n'
+
+ tempstring = repr(maxColWeight) + ' ' + repr(maxRowWeight) + '\n'
+ # write out max column and row weights
+ myfile.write(tempstring)
+ # write out all of the column weights
+ myfile.write(tempstring3)
+ # write out all of the row weights
+ myfile.write(tempstring1)
+ # write out the nonzero indices for each column
+ myfile.write(tempstring4)
+ # write out the nonzero indices for each row
+ myfile.write(tempstring2)
class LDPC_matrix(object):
- """ Class for a LDPC parity check matrix """
-
- def __init__(self, alist_filename=None,
- n_p_q=None,
- H_matrix=None):
- if (alist_filename != None):
- self.H = read_alist_file(alist_filename)
- elif (n_p_q != None):
- self.H = self.regular_LDPC_code_contructor(n_p_q)
- elif (H_matrix != None):
- self.H = H_matrix
- else:
- print('Error: provide either an alist filename, ', end='')
- print('parameters for constructing regular LDPC parity, ', end='')
- print('check matrix, or a numpy array.')
+ """ Class for a LDPC parity check matrix """
+
+ def __init__(self, alist_filename=None,
+ n_p_q=None,
+ H_matrix=None):
+ if (alist_filename != None):
+ self.H = read_alist_file(alist_filename)
+ elif (n_p_q != None):
+ self.H = self.regular_LDPC_code_contructor(n_p_q)
+ elif (H_matrix != None):
+ self.H = H_matrix
+ else:
+ print('Error: provide either an alist filename, ', end='')
+ print('parameters for constructing regular LDPC parity, ', end='')
+ print('check matrix, or a numpy array.')
+
+ self.rank = linalg.matrix_rank(self.H)
+ self.numRows = self.H.shape[0]
+ self.n = self.H.shape[1]
+ self.k = self.n - self.numRows
+
+ def regular_LDPC_code_contructor(self, n_p_q):
+ """
+ This function constructs a LDPC parity check matrix
+ H. The algorithm follows Gallager's approach where we create
+ p submatrices and stack them together. Reference: Turbo
+ Coding for Satellite and Wireless Communications, section
+ 9,3.
+
+ Note: the matrices computed from this algorithm will never
+ have full rank. (Reference Gallager's Dissertation.) They
+ will have rank = (number of rows - p + 1). To convert it
+ to full rank, use the function get_full_rank_H_matrix
+ """
+
+ n = n_p_q[0] # codeword length
+ p = n_p_q[1] # column weight
+ q = n_p_q[2] # row weight
+ # TODO: There should probably be other guidelines for n/p/q,
+ # but I have not found any specifics in the literature....
+
+ # For this algorithm, n/p must be an integer, because the
+ # number of rows in each submatrix must be a whole number.
+ ratioTest = (n * 1.0) / q
+ if ratioTest % 1 != 0:
+ print('\nError in regular_LDPC_code_contructor: The ', end='')
+ print('ratio of inputs n/q must be a whole number.\n')
+ return
+
+ # First submatrix first:
+ m = (n * p) / q # number of rows in H matrix
+ submatrix1 = zeros((m / p, n))
+ for row in np.arange(m / p):
+ range1 = row * q
+ range2 = (row + 1) * q
+ submatrix1[row, range1:range2] = 1
+ H = submatrix1
+
+ # Create the other submatrices and vertically stack them on.
+ submatrixNum = 2
+ newColumnOrder = np.arange(n)
+ while submatrixNum <= p:
+ submatrix = zeros((m / p, n))
+ shuffle(newColumnOrder)
+
+ for columnNum in np.arange(n):
+ submatrix[:, columnNum] = \
+ submatrix1[:, newColumnOrder[columnNum]]
+
+ H = vstack((H, submatrix))
+ submatrixNum = submatrixNum + 1
+
+ # Double check the row weight and column weights.
+ size = H.shape
+ rows = size[0]
+ cols = size[1]
+
+ # Check the row weights.
+ for rowNum in np.arange(rows):
+ nonzeros = array(H[rowNum, :].nonzero())
+ if nonzeros.shape[1] != q:
+ print('Row', rowNum, 'has incorrect weight!')
+ return
+
+ # Check the column weights
+ for columnNum in np.arange(cols):
+ nonzeros = array(H[:, columnNum].nonzero())
+ if nonzeros.shape[1] != p:
+ print('Row', columnNum, 'has incorrect weight!')
+ return
+
+ return H
- self.rank = linalg.matrix_rank(self.H)
- self.numRows = self.H.shape[0]
- self.n = self.H.shape[1]
- self.k = self.n - self.numRows
- def regular_LDPC_code_contructor(self, n_p_q):
+def greedy_upper_triangulation(H, verbose=0):
"""
- This function constructs a LDPC parity check matrix
- H. The algorithm follows Gallager's approach where we create
- p submatrices and stack them together. Reference: Turbo
- Coding for Satellite and Wireless Communications, section
- 9,3.
-
- Note: the matrices computed from this algorithm will never
- have full rank. (Reference Gallager's Dissertation.) They
- will have rank = (number of rows - p + 1). To convert it
- to full rank, use the function get_full_rank_H_matrix
+ This function performs row/column permutations to bring
+ H into approximate upper triangular form via greedy
+ upper triangulation method outlined in Modern Coding
+ Theory Appendix 1, Section A.2
"""
-
- n = n_p_q[0] # codeword length
- p = n_p_q[1] # column weight
- q = n_p_q[2] # row weight
- # TODO: There should probably be other guidelines for n/p/q,
- # but I have not found any specifics in the literature....
-
- # For this algorithm, n/p must be an integer, because the
- # number of rows in each submatrix must be a whole number.
- ratioTest = (n * 1.0) / q
- if ratioTest % 1 != 0:
- print('\nError in regular_LDPC_code_contructor: The ', end='')
- print('ratio of inputs n/q must be a whole number.\n')
- return
-
- # First submatrix first:
- m = (n * p) / q # number of rows in H matrix
- submatrix1 = zeros((m / p, n))
- for row in np.arange(m / p):
- range1 = row * q
- range2 = (row + 1) * q
- submatrix1[row, range1:range2] = 1
- H = submatrix1
-
- # Create the other submatrices and vertically stack them on.
- submatrixNum = 2
- newColumnOrder = np.arange(n)
- while submatrixNum <= p:
- submatrix = zeros((m / p, n))
- shuffle(newColumnOrder)
-
- for columnNum in np.arange(n):
- submatrix[:, columnNum] = \
- submatrix1[:, newColumnOrder[columnNum]]
-
- H = vstack((H, submatrix))
- submatrixNum = submatrixNum + 1
-
- # Double check the row weight and column weights.
- size = H.shape
- rows = size[0]
- cols = size[1]
-
- # Check the row weights.
- for rowNum in np.arange(rows):
- nonzeros = array(H[rowNum, :].nonzero())
- if nonzeros.shape[1] != q:
- print('Row', rowNum, 'has incorrect weight!')
- return
-
- # Check the column weights
- for columnNum in np.arange(cols):
- nonzeros = array(H[:, columnNum].nonzero())
- if nonzeros.shape[1] != p:
- print('Row', columnNum, 'has incorrect weight!')
+ H_t = H.copy()
+
+ # Per email from Dr. Urbanke, author of this textbook, this
+ # algorithm requires H to be full rank
+ if linalg.matrix_rank(H_t) != H_t.shape[0]:
+ print('Rank of H:', linalg.matrix_rank(tempArray))
+ print('H has', H_t.shape[0], 'rows')
+ print('Error: H must be full rank.')
return
- return H
-
+ size = H_t.shape
+ n = size[1]
+ k = n - size[0]
+ g = t = 0
+
+ while t != (n - k - g):
+ H_residual = H_t[t:n - k - g, t:n]
+ size = H_residual.shape
+ numRows = size[0]
+ numCols = size[1]
+
+ minResidualDegrees = zeros((1, numCols), dtype=int)
+
+ for colNum in np.arange(numCols):
+ nonZeroElements = array(H_residual[:, colNum].nonzero())
+ minResidualDegrees[0, colNum] = nonZeroElements.shape[1]
+
+ # Find the minimum nonzero residual degree
+ nonZeroElementIndices = minResidualDegrees.nonzero()
+ nonZeroElements = minResidualDegrees[nonZeroElementIndices[0],
+ nonZeroElementIndices[1]]
+ minimumResidualDegree = nonZeroElements.min()
+
+ # Get indices of all of the columns in H_t that have degree
+ # equal to the min positive residual degree, then pick a
+ # random column c.
+ indices = (minResidualDegrees == minimumResidualDegree) \
+ .nonzero()[1]
+ indices = indices + t
+ if indices.shape[0] == 1:
+ columnC = indices[0]
+ else:
+ randomIndex = randint(0, indices.shape[0], (1, 1))[0][0]
+ columnC = indices[randomIndex]
-def greedy_upper_triangulation(H, verbose=0):
- """
- This function performs row/column permutations to bring
- H into approximate upper triangular form via greedy
- upper triangulation method outlined in Modern Coding
- Theory Appendix 1, Section A.2
- """
- H_t = H.copy()
-
- # Per email from Dr. Urbanke, author of this textbook, this
- # algorithm requires H to be full rank
- if linalg.matrix_rank(H_t) != H_t.shape[0]:
- print('Rank of H:', linalg.matrix_rank(tempArray))
- print('H has', H_t.shape[0], 'rows')
- print('Error: H must be full rank.')
- return
-
- size = H_t.shape
- n = size[1]
- k = n - size[0]
- g = t = 0
-
- while t != (n - k - g):
- H_residual = H_t[t:n - k - g, t:n]
- size = H_residual.shape
- numRows = size[0]
- numCols = size[1]
-
- minResidualDegrees = zeros((1, numCols), dtype=int)
-
- for colNum in np.arange(numCols):
- nonZeroElements = array(H_residual[:, colNum].nonzero())
- minResidualDegrees[0, colNum] = nonZeroElements.shape[1]
-
- # Find the minimum nonzero residual degree
- nonZeroElementIndices = minResidualDegrees.nonzero()
- nonZeroElements = minResidualDegrees[nonZeroElementIndices[0],
- nonZeroElementIndices[1]]
- minimumResidualDegree = nonZeroElements.min()
-
- # Get indices of all of the columns in H_t that have degree
- # equal to the min positive residual degree, then pick a
- # random column c.
- indices = (minResidualDegrees == minimumResidualDegree) \
- .nonzero()[1]
- indices = indices + t
- if indices.shape[0] == 1:
- columnC = indices[0]
- else:
- randomIndex = randint(0, indices.shape[0], (1, 1))[0][0]
- columnC = indices[randomIndex]
-
- Htemp = H_t.copy()
-
- if minimumResidualDegree == 1:
- # This is the 'extend' case
- rowThatContainsNonZero = H_residual[:, columnC - t].nonzero()[0][0]
-
- # Swap column c with column t. (Book says t+1 but we
- # index from 0, not 1.)
- Htemp[:, columnC] = H_t[:, t]
- Htemp[:, t] = H_t[:, columnC]
- H_t = Htemp.copy()
- Htemp = H_t.copy()
- # Swap row r with row t. (Book says t+1 but we index from
- # 0, not 1.)
- Htemp[rowThatContainsNonZero + t, :] = H_t[t, :]
- Htemp[t, :] = H_t[rowThatContainsNonZero + t, :]
- H_t = Htemp.copy()
- Htemp = H_t.copy()
- else:
- # This is the 'choose' case.
- rowsThatContainNonZeros = H_residual[:, columnC - t] \
- .nonzero()[0]
-
- # Swap column c with column t. (Book says t+1 but we
- # index from 0, not 1.)
- Htemp[:, columnC] = H_t[:, t]
- Htemp[:, t] = H_t[:, columnC]
- H_t = Htemp.copy()
- Htemp = H_t.copy()
-
- # Swap row r1 with row t
- r1 = rowsThatContainNonZeros[0]
- Htemp[r1 + t, :] = H_t[t, :]
- Htemp[t, :] = H_t[r1 + t, :]
- numRowsLeft = rowsThatContainNonZeros.shape[0] - 1
- H_t = Htemp.copy()
- Htemp = H_t.copy()
-
- # Move the other rows that contain nonZero entries to the
- # bottom of the matrix. We can't just swap them,
- # otherwise we will be pulling up rows that we pushed
- # down before. So, use a rotation method.
- for index in np.arange(1, numRowsLeft + 1):
- rowInH_residual = rowsThatContainNonZeros[index]
- rowInH_t = rowInH_residual + t - index + 1
- m = n - k
- # Move the row with the nonzero element to the
- # bottom; don't update H_t.
- Htemp[m - 1, :] = H_t[rowInH_t, :]
- # Now rotate the bottom rows up.
- sub_index = 1
- while sub_index < (m - rowInH_t):
- Htemp[m - sub_index - 1, :] = H_t[m - sub_index, :]
- sub_index = sub_index + 1
- H_t = Htemp.copy()
Htemp = H_t.copy()
- # Save temp H as new H_t.
- H_t = Htemp.copy()
- Htemp = H_t.copy()
- g = g + (minimumResidualDegree - 1)
-
- t = t + 1
-
- if g == 0:
- if verbose:
- print('Error: gap is 0.')
- return
-
- # We need to ensure phi is nonsingular.
- T = H_t[0:t, 0:t]
- E = H_t[t:t + g, 0:t]
- A = H_t[0:t, t:t + g]
- C = H_t[t:t + g, t:t + g]
- D = H_t[t:t + g, t + g:n]
-
- invTmod2array = inv_mod2(T)
- temp1 = dot(E, invTmod2array) % 2
- temp2 = dot(temp1, A) % 2
- phi = (C - temp2) % 2
- if phi.any():
- try:
- # Try to take the inverse of phi.
- invPhi = inv_mod2(phi)
- except linalg.linalg.LinAlgError:
- # Phi is singular
- if verbose > 1:
- print('Initial phi is singular')
- else:
- # Phi is nonsingular, so we need to use this version of H.
- if verbose > 1:
- print('Initial phi is nonsingular')
- return [H_t, g, t]
- else:
- if verbose:
- print('Initial phi is all zeros:\n', phi)
+ if minimumResidualDegree == 1:
+ # This is the 'extend' case
+ rowThatContainsNonZero = H_residual[:, columnC - t].nonzero()[0][0]
+
+ # Swap column c with column t. (Book says t+1 but we
+ # index from 0, not 1.)
+ Htemp[:, columnC] = H_t[:, t]
+ Htemp[:, t] = H_t[:, columnC]
+ H_t = Htemp.copy()
+ Htemp = H_t.copy()
+ # Swap row r with row t. (Book says t+1 but we index from
+ # 0, not 1.)
+ Htemp[rowThatContainsNonZero + t, :] = H_t[t, :]
+ Htemp[t, :] = H_t[rowThatContainsNonZero + t, :]
+ H_t = Htemp.copy()
+ Htemp = H_t.copy()
+ else:
+ # This is the 'choose' case.
+ rowsThatContainNonZeros = H_residual[:, columnC - t] \
+ .nonzero()[0]
+
+ # Swap column c with column t. (Book says t+1 but we
+ # index from 0, not 1.)
+ Htemp[:, columnC] = H_t[:, t]
+ Htemp[:, t] = H_t[:, columnC]
+ H_t = Htemp.copy()
+ Htemp = H_t.copy()
+
+ # Swap row r1 with row t
+ r1 = rowsThatContainNonZeros[0]
+ Htemp[r1 + t, :] = H_t[t, :]
+ Htemp[t, :] = H_t[r1 + t, :]
+ numRowsLeft = rowsThatContainNonZeros.shape[0] - 1
+ H_t = Htemp.copy()
+ Htemp = H_t.copy()
+
+ # Move the other rows that contain nonZero entries to the
+ # bottom of the matrix. We can't just swap them,
+ # otherwise we will be pulling up rows that we pushed
+ # down before. So, use a rotation method.
+ for index in np.arange(1, numRowsLeft + 1):
+ rowInH_residual = rowsThatContainNonZeros[index]
+ rowInH_t = rowInH_residual + t - index + 1
+ m = n - k
+ # Move the row with the nonzero element to the
+ # bottom; don't update H_t.
+ Htemp[m - 1, :] = H_t[rowInH_t, :]
+ # Now rotate the bottom rows up.
+ sub_index = 1
+ while sub_index < (m - rowInH_t):
+ Htemp[m - sub_index - 1, :] = H_t[m - sub_index, :]
+ sub_index = sub_index + 1
+ H_t = Htemp.copy()
+ Htemp = H_t.copy()
+
+ # Save temp H as new H_t.
+ H_t = Htemp.copy()
+ Htemp = H_t.copy()
+ g = g + (minimumResidualDegree - 1)
+
+ t = t + 1
+
+ if g == 0:
+ if verbose:
+ print('Error: gap is 0.')
+ return
- # If the C and D submatrices are all zeros, there is no point in
- # shuffling them around in an attempt to find a good phi.
- if not (C.any() or D.any()):
- if verbose:
- print('C and D are all zeros. There is no hope in', )
- print('finding a nonsingular phi matrix. ')
- return
-
- # We can't look at every row/column permutation possibility
- # because there would be (n-t)! column shuffles and g! row
- # shuffles. g has gotten up to 12 in tests, so 12! would still
- # take quite some time. Instead, we will just pick an arbitrary
- # number of max iterations to perform, then break.
- maxIterations = 300
- iterationCount = 0
- columnsToShuffle = np.arange(t, n)
- rowsToShuffle = np.arange(t, t + g)
-
- while iterationCount < maxIterations:
- if verbose > 1:
- print('iterationCount:', iterationCount)
- tempH = H_t.copy()
-
- shuffle(columnsToShuffle)
- shuffle(rowsToShuffle)
- index = 0
- for newDestinationColumnNumber in np.arange(t, n):
- oldColumnNumber = columnsToShuffle[index]
- tempH[:, newDestinationColumnNumber] = \
- H_t[:, oldColumnNumber]
- index += 1
-
- tempH2 = tempH.copy()
- index = 0
- for newDesinationRowNumber in np.arange(t, t + g):
- oldRowNumber = rowsToShuffle[index]
- tempH[newDesinationRowNumber, :] = tempH2[oldRowNumber, :]
- index += 1
-
- # Now test this new H matrix.
- H_t = tempH.copy()
+ # We need to ensure phi is nonsingular.
T = H_t[0:t, 0:t]
E = H_t[t:t + g, 0:t]
A = H_t[0:t, t:t + g]
C = H_t[t:t + g, t:t + g]
+ D = H_t[t:t + g, t + g:n]
+
invTmod2array = inv_mod2(T)
temp1 = dot(E, invTmod2array) % 2
temp2 = dot(temp1, A) % 2
phi = (C - temp2) % 2
if phi.any():
- try:
- # Try to take the inverse of phi.
- invPhi = inv_mod2(phi)
- except linalg.linalg.LinAlgError:
- # Phi is singular
- if verbose > 1:
- print('Phi is still singular')
- else:
- # Phi is nonsingular, so we're done.
- if verbose:
- print('Found a nonsingular phi on', )
- print('iterationCount = ', iterationCount)
- return [H_t, g, t]
+ try:
+ # Try to take the inverse of phi.
+ invPhi = inv_mod2(phi)
+ except linalg.linalg.LinAlgError:
+ # Phi is singular
+ if verbose > 1:
+ print('Initial phi is singular')
+ else:
+ # Phi is nonsingular, so we need to use this version of H.
+ if verbose > 1:
+ print('Initial phi is nonsingular')
+ return [H_t, g, t]
else:
- if verbose > 1:
- print('phi is all zeros')
-
- iterationCount += 1
-
- # If we've reached this point, then we haven't found a
- # version of H that has a nonsingular phi.
- if verbose:
- print('--- Error: nonsingular phi matrix not found.')
+ if verbose:
+ print('Initial phi is all zeros:\n', phi)
+ # If the C and D submatrices are all zeros, there is no point in
+ # shuffling them around in an attempt to find a good phi.
+ if not (C.any() or D.any()):
+ if verbose:
+ print('C and D are all zeros. There is no hope in', )
+ print('finding a nonsingular phi matrix. ')
+ return
-def inv_mod2(squareMatrix, verbose=0):
- """
- Calculates the mod 2 inverse of a matrix.
- """
- A = squareMatrix.copy()
- t = A.shape[0]
-
- # Special case for one element array [1]
- if A.size == 1 and A[0] == 1:
- return array([1])
-
- Ainverse = inv(A)
- B = det(A) * Ainverse
- C = B % 2
-
- # Encountered lots of rounding errors with this function.
- # Previously tried floor, C.astype(int), and casting with (int)
- # and none of that works correctly, so doing it the tedious way.
-
- test = dot(A, C) % 2
- tempTest = zeros_like(test)
- for colNum in np.arange(test.shape[1]):
- for rowNum in np.arange(test.shape[0]):
- value = test[rowNum, colNum]
- if (abs(1 - value)) < 0.01:
- # this is a 1
- tempTest[rowNum, colNum] = 1
- elif (abs(2 - value)) < 0.01:
- # there shouldn't be any 2s after B % 2, but I'm
- # seeing them!
- tempTest[rowNum, colNum] = 0
- elif (abs(0 - value)) < 0.01:
- # this is a 0
- tempTest[rowNum, colNum] = 0
- else:
+ # We can't look at every row/column permutation possibility
+ # because there would be (n-t)! column shuffles and g! row
+ # shuffles. g has gotten up to 12 in tests, so 12! would still
+ # take quite some time. Instead, we will just pick an arbitrary
+ # number of max iterations to perform, then break.
+ maxIterations = 300
+ iterationCount = 0
+ columnsToShuffle = np.arange(t, n)
+ rowsToShuffle = np.arange(t, t + g)
+
+ while iterationCount < maxIterations:
if verbose > 1:
- print('In inv_mod2. Rounding error on this', )
- print('value? Mod 2 has already been done.', )
- print('value:', value)
+ print('iterationCount:', iterationCount)
+ tempH = H_t.copy()
+
+ shuffle(columnsToShuffle)
+ shuffle(rowsToShuffle)
+ index = 0
+ for newDestinationColumnNumber in np.arange(t, n):
+ oldColumnNumber = columnsToShuffle[index]
+ tempH[:, newDestinationColumnNumber] = \
+ H_t[:, oldColumnNumber]
+ index += 1
+
+ tempH2 = tempH.copy()
+ index = 0
+ for newDesinationRowNumber in np.arange(t, t + g):
+ oldRowNumber = rowsToShuffle[index]
+ tempH[newDesinationRowNumber, :] = tempH2[oldRowNumber, :]
+ index += 1
+
+ # Now test this new H matrix.
+ H_t = tempH.copy()
+ T = H_t[0:t, 0:t]
+ E = H_t[t:t + g, 0:t]
+ A = H_t[0:t, t:t + g]
+ C = H_t[t:t + g, t:t + g]
+ invTmod2array = inv_mod2(T)
+ temp1 = dot(E, invTmod2array) % 2
+ temp2 = dot(temp1, A) % 2
+ phi = (C - temp2) % 2
+ if phi.any():
+ try:
+ # Try to take the inverse of phi.
+ invPhi = inv_mod2(phi)
+ except linalg.linalg.LinAlgError:
+ # Phi is singular
+ if verbose > 1:
+ print('Phi is still singular')
+ else:
+ # Phi is nonsingular, so we're done.
+ if verbose:
+ print('Found a nonsingular phi on', )
+ print('iterationCount = ', iterationCount)
+ return [H_t, g, t]
+ else:
+ if verbose > 1:
+ print('phi is all zeros')
+
+ iterationCount += 1
+
+ # If we've reached this point, then we haven't found a
+ # version of H that has a nonsingular phi.
+ if verbose:
+ print('--- Error: nonsingular phi matrix not found.')
- test = tempTest.copy()
- if (test - eye(t, t) % 2).any():
- if verbose:
- print('Error in inv_mod2: did not find inverse.')
- # TODO is this the most appropriate error to raise?
- raise linalg.linalg.LinAlgError
- else:
- return C
+def inv_mod2(squareMatrix, verbose=0):
+ """
+ Calculates the mod 2 inverse of a matrix.
+ """
+ A = squareMatrix.copy()
+ t = A.shape[0]
+
+ # Special case for one element array [1]
+ if A.size == 1 and A[0] == 1:
+ return array([1])
+
+ Ainverse = inv(A)
+ B = det(A) * Ainverse
+ C = B % 2
+
+ # Encountered lots of rounding errors with this function.
+ # Previously tried floor, C.astype(int), and casting with (int)
+ # and none of that works correctly, so doing it the tedious way.
+
+ test = dot(A, C) % 2
+ tempTest = zeros_like(test)
+ for colNum in np.arange(test.shape[1]):
+ for rowNum in np.arange(test.shape[0]):
+ value = test[rowNum, colNum]
+ if (abs(1 - value)) < 0.01:
+ # this is a 1
+ tempTest[rowNum, colNum] = 1
+ elif (abs(2 - value)) < 0.01:
+ # there shouldn't be any 2s after B % 2, but I'm
+ # seeing them!
+ tempTest[rowNum, colNum] = 0
+ elif (abs(0 - value)) < 0.01:
+ # this is a 0
+ tempTest[rowNum, colNum] = 0
+ else:
+ if verbose > 1:
+ print('In inv_mod2. Rounding error on this', )
+ print('value? Mod 2 has already been done.', )
+ print('value:', value)
+
+ test = tempTest.copy()
+
+ if (test - eye(t, t) % 2).any():
+ if verbose:
+ print('Error in inv_mod2: did not find inverse.')
+ # TODO is this the most appropriate error to raise?
+ raise linalg.linalg.LinAlgError
+ else:
+ return C
def swap_columns(a, b, arrayIn):
- """
- Swaps two columns in a matrix.
- """
- arrayOut = arrayIn.copy()
- arrayOut[:, a] = arrayIn[:, b]
- arrayOut[:, b] = arrayIn[:, a]
- return arrayOut
+ """
+ Swaps two columns in a matrix.
+ """
+ arrayOut = arrayIn.copy()
+ arrayOut[:, a] = arrayIn[:, b]
+ arrayOut[:, b] = arrayIn[:, a]
+ return arrayOut
def move_row_to_bottom(i, arrayIn):
- """"
- Moves a specified row (just one) to the bottom of the matrix,
- then rotates the rows at the bottom up.
-
- For example, if we had a matrix with 5 rows, and we wanted to
- push row 2 to the bottom, then the resulting row order would be:
- 1,3,4,5,2
- """
- arrayOut = arrayIn.copy()
- numRows = arrayOut.shape[0]
- # Push the specified row to the bottom.
- arrayOut[numRows - 1] = arrayIn[i, :]
- # Now rotate the bottom rows up.
- index = 2
- while (numRows - index) >= i:
- arrayOut[numRows - index, :] = arrayIn[numRows - index + 1]
- index = index + 1
- return arrayOut
+ """"
+ Moves a specified row (just one) to the bottom of the matrix,
+ then rotates the rows at the bottom up.
+
+ For example, if we had a matrix with 5 rows, and we wanted to
+ push row 2 to the bottom, then the resulting row order would be:
+ 1,3,4,5,2
+ """
+ arrayOut = arrayIn.copy()
+ numRows = arrayOut.shape[0]
+ # Push the specified row to the bottom.
+ arrayOut[numRows - 1] = arrayIn[i, :]
+ # Now rotate the bottom rows up.
+ index = 2
+ while (numRows - index) >= i:
+ arrayOut[numRows - index, :] = arrayIn[numRows - index + 1]
+ index = index + 1
+ return arrayOut
def get_full_rank_H_matrix(H, verbose=False):
- """
- This function accepts a parity check matrix H and, if it is not
- already full rank, will determine which rows are dependent and
- remove them. The updated matrix will be returned.
- """
- tempArray = H.copy()
- if linalg.matrix_rank(tempArray) == tempArray.shape[0]:
- if verbose:
- print('Returning H; it is already full rank.')
- return tempArray
+ """
+ This function accepts a parity check matrix H and, if it is not
+ already full rank, will determine which rows are dependent and
+ remove them. The updated matrix will be returned.
+ """
+ tempArray = H.copy()
+ if linalg.matrix_rank(tempArray) == tempArray.shape[0]:
+ if verbose:
+ print('Returning H; it is already full rank.')
+ return tempArray
+
+ numRows = tempArray.shape[0]
+ numColumns = tempArray.shape[1]
+ limit = numRows
+ rank = 0
+ i = 0
- numRows = tempArray.shape[0]
- numColumns = tempArray.shape[1]
- limit = numRows
- rank = 0
- i = 0
+ # Create an array to save the column permutations.
+ columnOrder = np.arange(numColumns).reshape(1, numColumns)
- # Create an array to save the column permutations.
- columnOrder = np.arange(numColumns).reshape(1, numColumns)
+ # Create an array to save the row permutations. We just need
+ # this to know which dependent rows to delete.
+ rowOrder = np.arange(numRows).reshape(numRows, 1)
- # Create an array to save the row permutations. We just need
- # this to know which dependent rows to delete.
- rowOrder = np.arange(numRows).reshape(numRows, 1)
+ while i < limit:
+ if verbose:
+ print('In get_full_rank_H_matrix; i:', i)
+ # Flag indicating that the row contains a non-zero entry
+ found = False
+ for j in np.arange(i, numColumns):
+ if tempArray[i, j] == 1:
+ # Encountered a non-zero entry at (i, j)
+ found = True
+ # Increment rank by 1
+ rank = rank + 1
+ # Make the entry at (i,i) be 1
+ tempArray = swap_columns(j, i, tempArray)
+ # Keep track of the column swapping
+ columnOrder = swap_columns(j, i, columnOrder)
+ break
+ if found == True:
+ for k in np.arange(0, numRows):
+ if k == i:
+ continue
+ # Checking for 1's
+ if tempArray[k, i] == 1:
+ # Add row i to row k
+ tempArray[k, :] = tempArray[k, :] + tempArray[i, :]
+ # Addition is mod2
+ tempArray = tempArray.copy() % 2
+ # All the entries above & below (i, i) are now 0
+ i = i + 1
+ if found == False:
+ # Push the row of 0s to the bottom, and move the bottom
+ # rows up (sort of a rotation thing).
+ tempArray = move_row_to_bottom(i, tempArray)
+ # Decrease limit since we just found a row of 0s
+ limit -= 1
+ # Keep track of row swapping
+ rowOrder = move_row_to_bottom(i, rowOrder)
+
+ # Don't need the dependent rows
+ finalRowOrder = rowOrder[0:i]
+
+ # Reorder H, per the permutations taken above .
+ # First, put rows in order, omitting the dependent rows.
+ newNumberOfRowsForH = finalRowOrder.shape[0]
+ newH = zeros((newNumberOfRowsForH, numColumns))
+ for index in np.arange(newNumberOfRowsForH):
+ newH[index, :] = H[finalRowOrder[index], :]
+
+ # Next, put the columns in order.
+ tempHarray = newH.copy()
+ for index in np.arange(numColumns):
+ newH[:, index] = tempHarray[:, columnOrder[0, index]]
- while i < limit:
if verbose:
- print('In get_full_rank_H_matrix; i:', i)
- # Flag indicating that the row contains a non-zero entry
- found = False
- for j in np.arange(i, numColumns):
- if tempArray[i, j] == 1:
- # Encountered a non-zero entry at (i, j)
- found = True
- # Increment rank by 1
- rank = rank + 1
- # Make the entry at (i,i) be 1
- tempArray = swap_columns(j, i, tempArray)
- # Keep track of the column swapping
- columnOrder = swap_columns(j, i, columnOrder)
- break
- if found == True:
- for k in np.arange(0, numRows):
- if k == i: continue
- # Checking for 1's
- if tempArray[k, i] == 1:
- # Add row i to row k
- tempArray[k, :] = tempArray[k, :] + tempArray[i, :]
- # Addition is mod2
- tempArray = tempArray.copy() % 2
- # All the entries above & below (i, i) are now 0
- i = i + 1
- if found == False:
- # Push the row of 0s to the bottom, and move the bottom
- # rows up (sort of a rotation thing).
- tempArray = move_row_to_bottom(i, tempArray)
- # Decrease limit since we just found a row of 0s
- limit -= 1
- # Keep track of row swapping
- rowOrder = move_row_to_bottom(i, rowOrder)
-
- # Don't need the dependent rows
- finalRowOrder = rowOrder[0:i]
-
- # Reorder H, per the permutations taken above .
- # First, put rows in order, omitting the dependent rows.
- newNumberOfRowsForH = finalRowOrder.shape[0]
- newH = zeros((newNumberOfRowsForH, numColumns))
- for index in np.arange(newNumberOfRowsForH):
- newH[index, :] = H[finalRowOrder[index], :]
-
- # Next, put the columns in order.
- tempHarray = newH.copy()
- for index in np.arange(numColumns):
- newH[:, index] = tempHarray[:, columnOrder[0, index]]
-
- if verbose:
- print('original H.shape:', H.shape)
- print('newH.shape:', newH.shape)
-
- return newH
+ print('original H.shape:', H.shape)
+ print('newH.shape:', newH.shape)
+
+ return newH
def get_best_matrix(H, numIterations=100, verbose=0):
- """
- This function will run the Greedy Upper Triangulation algorithm
- for numIterations times, looking for the lowest possible gap.
- The submatrices returned are those needed for real-time encoding.
- """
-
- hadFirstJoy = 0
- index = 1
- while index <= numIterations:
- if verbose:
- print('--- In get_best_matrix, iteration:', index)
- index += 1
- try:
- ret = greedy_upper_triangulation(H, verbose)
- except ValueError as e:
- if verbose > 1:
- print('greedy_upper_triangulation error: ', e)
+ """
+ This function will run the Greedy Upper Triangulation algorithm
+ for numIterations times, looking for the lowest possible gap.
+ The submatrices returned are those needed for real-time encoding.
+ """
+
+ hadFirstJoy = 0
+ index = 1
+ while index <= numIterations:
+ if verbose:
+ print('--- In get_best_matrix, iteration:', index)
+ index += 1
+ try:
+ ret = greedy_upper_triangulation(H, verbose)
+ except ValueError as e:
+ if verbose > 1:
+ print('greedy_upper_triangulation error: ', e)
+ else:
+ if ret:
+ [betterH, gap, t] = ret
+ else:
+ continue
+
+ if not hadFirstJoy:
+ hadFirstJoy = 1
+ bestGap = gap
+ bestH = betterH.copy()
+ bestT = t
+ elif gap < bestGap:
+ bestGap = gap
+ bestH = betterH.copy()
+ bestT = t
+
+ if hadFirstJoy:
+ return [bestH, bestGap]
else:
- if ret:
- [betterH, gap, t] = ret
- else:
- continue
-
- if not hadFirstJoy:
- hadFirstJoy = 1
- bestGap = gap
- bestH = betterH.copy()
- bestT = t
- elif gap < bestGap:
- bestGap = gap
- bestH = betterH.copy()
- bestT = t
-
- if hadFirstJoy:
- return [bestH, bestGap]
- else:
- if verbose:
- print('Error: Could not find appropriate H form', )
- print('for encoding.')
- return
+ if verbose:
+ print('Error: Could not find appropriate H form', )
+ print('for encoding.')
+ return
def getSystematicGmatrix(GenMatrix):
- """
- This function finds the systematic form of the generator
- matrix GenMatrix. This form is G = [I P] where I is an identity
- matrix and P is the parity submatrix. If the GenMatrix matrix
- provided is not full rank, then dependent rows will be deleted.
-
- This function does not convert parity check (H) matrices to the
- generator matrix format. Use the function getSystematicGmatrixFromH
- for that purpose.
- """
- tempArray = GenMatrix.copy()
- numRows = tempArray.shape[0]
- numColumns = tempArray.shape[1]
- limit = numRows
- rank = 0
- i = 0
- while i < limit:
- # Flag indicating that the row contains a non-zero entry
- found = False
- for j in np.arange(i, numColumns):
- if tempArray[i, j] == 1:
- # Encountered a non-zero entry at (i, j)
- found = True
- # Increment rank by 1
- rank = rank + 1
- # make the entry at (i,i) be 1
- tempArray = swap_columns(j, i, tempArray)
- break
- if found == True:
- for k in np.arange(0, numRows):
- if k == i: continue
- # Checking for 1's
- if tempArray[k, i] == 1:
- # add row i to row k
- tempArray[k, :] = tempArray[k, :] + tempArray[i, :]
- # Addition is mod2
- tempArray = tempArray.copy() % 2
- # All the entries above & below (i, i) are now 0
- i = i + 1
- if found == False:
- # push the row of 0s to the bottom, and move the bottom
- # rows up (sort of a rotation thing)
- tempArray = move_row_to_bottom(i, tempArray)
- # decrease limit since we just found a row of 0s
- limit -= 1
- # the rows below i are the dependent rows, which we discard
- G = tempArray[0:i, :]
- return G
+ """
+ This function finds the systematic form of the generator
+ matrix GenMatrix. This form is G = [I P] where I is an identity
+ matrix and P is the parity submatrix. If the GenMatrix matrix
+ provided is not full rank, then dependent rows will be deleted.
+
+ This function does not convert parity check (H) matrices to the
+ generator matrix format. Use the function getSystematicGmatrixFromH
+ for that purpose.
+ """
+ tempArray = GenMatrix.copy()
+ numRows = tempArray.shape[0]
+ numColumns = tempArray.shape[1]
+ limit = numRows
+ rank = 0
+ i = 0
+ while i < limit:
+ # Flag indicating that the row contains a non-zero entry
+ found = False
+ for j in np.arange(i, numColumns):
+ if tempArray[i, j] == 1:
+ # Encountered a non-zero entry at (i, j)
+ found = True
+ # Increment rank by 1
+ rank = rank + 1
+ # make the entry at (i,i) be 1
+ tempArray = swap_columns(j, i, tempArray)
+ break
+ if found == True:
+ for k in np.arange(0, numRows):
+ if k == i:
+ continue
+ # Checking for 1's
+ if tempArray[k, i] == 1:
+ # add row i to row k
+ tempArray[k, :] = tempArray[k, :] + tempArray[i, :]
+ # Addition is mod2
+ tempArray = tempArray.copy() % 2
+ # All the entries above & below (i, i) are now 0
+ i = i + 1
+ if found == False:
+ # push the row of 0s to the bottom, and move the bottom
+ # rows up (sort of a rotation thing)
+ tempArray = move_row_to_bottom(i, tempArray)
+ # decrease limit since we just found a row of 0s
+ limit -= 1
+ # the rows below i are the dependent rows, which we discard
+ G = tempArray[0:i, :]
+ return G
def getSystematicGmatrixFromH(H, verbose=False):
- """
- If given a parity check matrix H, this function returns a
- generator matrix G in the systematic form: G = [I P]
- where: I is an identity matrix, size k x k
- P is the parity submatrix, size k x (n-k)
- If the H matrix provided is not full rank, then dependent rows
- will be deleted first.
- """
- if verbose:
- print('received H with size: ', H.shape)
-
- # First, put the H matrix into the form H = [I|m] where:
- # I is (n-k) x (n-k) identity matrix
- # m is (n-k) x k
- # This part is just copying the algorithm from getSystematicGmatrix
- tempArray = getSystematicGmatrix(H)
-
- # Next, swap I and m columns so the matrix takes the forms [m|I].
- n = H.shape[1]
- k = n - H.shape[0]
- I_temp = tempArray[:, 0:(n - k)]
- m = tempArray[:, (n - k):n]
- newH = concatenate((m, I_temp), axis=1)
-
- # Now the submatrix m is the transpose of the parity submatrix,
- # i.e. H is in the form H = [P'|I]. So G is just [I|P]
- k = m.shape[1]
- G = concatenate((identity(k), m.T), axis=1)
- if verbose:
- print('returning G with size: ', G.shape)
- return G
+ """
+ If given a parity check matrix H, this function returns a
+ generator matrix G in the systematic form: G = [I P]
+ where: I is an identity matrix, size k x k
+ P is the parity submatrix, size k x (n-k)
+ If the H matrix provided is not full rank, then dependent rows
+ will be deleted first.
+ """
+ if verbose:
+ print('received H with size: ', H.shape)
+
+ # First, put the H matrix into the form H = [I|m] where:
+ # I is (n-k) x (n-k) identity matrix
+ # m is (n-k) x k
+ # This part is just copying the algorithm from getSystematicGmatrix
+ tempArray = getSystematicGmatrix(H)
+
+ # Next, swap I and m columns so the matrix takes the forms [m|I].
+ n = H.shape[1]
+ k = n - H.shape[0]
+ I_temp = tempArray[:, 0:(n - k)]
+ m = tempArray[:, (n - k):n]
+ newH = concatenate((m, I_temp), axis=1)
+
+ # Now the submatrix m is the transpose of the parity submatrix,
+ # i.e. H is in the form H = [P'|I]. So G is just [I|P]
+ k = m.shape[1]
+ G = concatenate((identity(k), m.T), axis=1)
+ if verbose:
+ print('returning G with size: ', G.shape)
+ return G