From bd8606e1e7108975225abfe02363d3afcbbbddae Mon Sep 17 00:00:00 2001
From: danieljankowski <daniel.jankowski@rub.de>
Date: Wed, 4 Sep 2019 13:18:15 +0200
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20added=20permutationmatrix=20for?=
 =?UTF-8?q?=20lineardependency=20resolver?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 gaussian.go                       |  38 +++++-----
 gaussian_test.go                  | 113 ++++++++++++++++++++++++++----
 resolver/lineardependency.go      |  13 ++--
 resolver/lineardependency_test.go |   3 +-
 4 files changed, 126 insertions(+), 41 deletions(-)

diff --git a/gaussian.go b/gaussian.go
index 477fdac..37c3a8e 100644
--- a/gaussian.go
+++ b/gaussian.go
@@ -35,8 +35,6 @@ func (f *F2) GaussianElimination() {
 				f.Rows[rr].Xor(f.Rows[rr], f.Rows[pivotBit])
 			}
 		}
-
-		//TODO: detect linear dependency
 	}
 
 	// do the same thing backwards to get the identity matrix
@@ -103,7 +101,7 @@ func (f *F2) PartialGaussianElimination(startRow, startCol, stopRow, stopCol int
 	f.partialDiagonalize(startRow, startCol, stopRow, stopCol, nil)
 }
 
-func (f *F2) partialDiagonalize(startRow, startCol, stopRow, stopCol int, permutationMatrix *F2) *F2 {
+func (f *F2) partialDiagonalize(startRow, startCol, stopRow, stopCol int, gaussMatrix *F2) *F2 {
 	// iterate backwards through the pivot bits
 	for pivotBit := stopCol; pivotBit >= startCol; pivotBit-- {
 		// choose each row from the top row to the one with the pivot bit
@@ -125,19 +123,19 @@ func (f *F2) partialDiagonalize(startRow, startCol, stopRow, stopCol int, permut
 				f.Rows[startRow+pivotBit-startCol],
 			)
 
-			if permutationMatrix == nil {
+			if gaussMatrix == nil {
 				continue
 			}
 
 			// eliminate the 1
-			permutationMatrix.Rows[rowCounter].Xor(
-				permutationMatrix.Rows[rowCounter],
-				permutationMatrix.Rows[startRow+pivotBit-startCol],
+			gaussMatrix.Rows[rowCounter].Xor(
+				gaussMatrix.Rows[rowCounter],
+				gaussMatrix.Rows[startRow+pivotBit-startCol],
 			)
 		}
 	}
 
-	return permutationMatrix
+	return gaussMatrix
 }
 
 // PartialGaussianWithLinearChecking performs a partial gaussian elimination
@@ -152,10 +150,11 @@ func (f *F2) PartialGaussianWithLinearChecking(
 	startCol int,
 	stopRow int,
 	stopCol int,
-	linearCheck func(*F2, *F2, int, int, int, int, int) (*F2, error),
-) (*F2, error) {
+	linearCheck func(*F2, *F2, *F2, int, int, int, int, int) (*F2, *F2, error),
+) (*F2, *F2, error) {
 	// initialize the permutation matrix
-	permutationMatrix := NewF2(f.N, f.N).SetToIdentity()
+	gaussMatrix := NewF2(f.N, f.N).SetToIdentity()
+	permutationMatrix := NewF2(f.M, f.M).SetToIdentity()
 
 	// initialize the error vector
 	var err error
@@ -177,7 +176,7 @@ func (f *F2) PartialGaussianWithLinearChecking(
 			if startRow+pivotBit-startCol != rowCounter {
 				// ...swap it with first one
 				f.SwapRows(startRow+pivotBit-startCol, rowCounter)
-				permutationMatrix.SwapRows(startRow+pivotBit-startCol, rowCounter)
+				gaussMatrix.SwapRows(startRow+pivotBit-startCol, rowCounter)
 			}
 
 			// iterate through all other rows except the first one
@@ -191,9 +190,9 @@ func (f *F2) PartialGaussianWithLinearChecking(
 					f.Rows[rr],
 					f.Rows[startRow+pivotBit-startCol],
 				)
-				permutationMatrix.Rows[rr].Xor(
-					permutationMatrix.Rows[rr],
-					permutationMatrix.Rows[startRow+pivotBit-startCol],
+				gaussMatrix.Rows[rr].Xor(
+					gaussMatrix.Rows[rr],
+					gaussMatrix.Rows[startRow+pivotBit-startCol],
 				)
 			}
 
@@ -211,8 +210,9 @@ func (f *F2) PartialGaussianWithLinearChecking(
 		}
 
 		// detect linear dependencies and try to resolve them
-		permutationMatrix, err = linearCheck(
+		gaussMatrix, permutationMatrix, err = linearCheck(
 			f,
+			gaussMatrix,
 			permutationMatrix,
 			startRow,
 			startCol,
@@ -223,7 +223,7 @@ func (f *F2) PartialGaussianWithLinearChecking(
 
 		// check the error
 		if err != nil {
-			return nil, err
+			return nil, nil, err
 		}
 
 		// process the same row again
@@ -231,9 +231,9 @@ func (f *F2) PartialGaussianWithLinearChecking(
 	}
 
 	// do the same thing backwards to get the identity matrix
-	permutationMatrix = f.partialDiagonalize(startRow, startCol, stopRow, stopCol, permutationMatrix)
+	gaussMatrix = f.partialDiagonalize(startRow, startCol, stopRow, stopCol, gaussMatrix)
 
-	return permutationMatrix, nil
+	return gaussMatrix, permutationMatrix, nil
 }
 
 // CheckGaussian checks if the given range in the matrix is the identity matrix
diff --git a/gaussian_test.go b/gaussian_test.go
index 14842ea..bc55fe8 100644
--- a/gaussian_test.go
+++ b/gaussian_test.go
@@ -116,7 +116,7 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 		startCol       int
 		stopRow        int
 		stopCol        int
-		linearCheck    func(*F2, *F2, int, int, int, int, int) (*F2, error)
+		linearCheck    func(*F2, *F2, *F2, int, int, int, int, int) (*F2, *F2, error)
 		expectedResult *F2
 		expectedError  bool
 	}{
@@ -132,7 +132,7 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 			startCol: 1,
 			stopRow:  2,
 			stopCol:  3,
-			linearCheck: func(f, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, error) {
+			linearCheck: func(f, gaussMatrix, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, *F2, error) {
 				// create a bitmask for the row check
 				bitmask := big.NewInt(0).SetBit(big.NewInt(0), stopCol-startCol+1, 1)
 				bitmask = bitmask.Sub(bitmask, big.NewInt(1))
@@ -161,7 +161,7 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 
 					// swap the rows
 					f.SwapRows(pivotBit-1, index)
-					permMatrix.SwapRows(pivotBit-1, index)
+					gaussMatrix.SwapRows(pivotBit-1, index)
 
 					foundValidRow = true
 
@@ -170,7 +170,7 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 				}
 
 				if !foundValidRow {
-					return nil, fmt.Errorf("cannot resolve linear dependency")
+					return nil, nil, fmt.Errorf("cannot resolve linear dependency")
 				}
 
 				for i := startCol; i < pivotBit; i++ {
@@ -185,13 +185,13 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 						f.Rows[startRow+i-startCol],
 					)
 
-					permMatrix.Rows[pivotBit-startCol].Xor(
-						permMatrix.Rows[pivotBit-startCol],
-						permMatrix.Rows[startRow+i-startCol],
+					gaussMatrix.Rows[pivotBit-startCol].Xor(
+						gaussMatrix.Rows[pivotBit-startCol],
+						gaussMatrix.Rows[startRow+i-startCol],
 					)
 				}
 
-				return permMatrix, nil
+				return gaussMatrix, permMatrix, nil
 			},
 			expectedResult: NewF2(4, 4).Set([]*big.Int{
 				big.NewInt(3),
@@ -213,8 +213,8 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 			startCol: 1,
 			stopRow:  2,
 			stopCol:  3,
-			linearCheck: func(f, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, error) {
-				return nil, fmt.Errorf("testfoo")
+			linearCheck: func(f, gaussMatrix, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, *F2, error) {
+				return nil, nil, fmt.Errorf("testfoo")
 			},
 			expectedResult: NewF2(4, 4).Set([]*big.Int{
 				big.NewInt(3),
@@ -236,8 +236,8 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 			startCol: 1,
 			stopRow:  2,
 			stopCol:  3,
-			linearCheck: func(f, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, error) {
-				return nil, fmt.Errorf("testfoo")
+			linearCheck: func(f, gaussMatrix, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, *F2, error) {
+				return nil, nil, fmt.Errorf("testfoo")
 			},
 			expectedResult: NewF2(4, 4).Set([]*big.Int{
 				big.NewInt(3),
@@ -247,6 +247,85 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 			}),
 			expectedError: true,
 		},
+		{
+			description: "3x4 with one swap",
+			matrix: NewF2(3, 4).Set([]*big.Int{
+				big.NewInt(3),
+				big.NewInt(15),
+				big.NewInt(5),
+			}),
+			startRow: 1,
+			startCol: 2,
+			stopRow:  2,
+			stopCol:  3,
+			linearCheck: func(f, gaussMatrix, permMatrix *F2, startRow, startCol, stopRow, stopCol, pivotBit int) (*F2, *F2, error) {
+				// create a bitmask for the row check
+				bitmask := big.NewInt(0).SetBit(big.NewInt(0), stopCol-startCol+1, 1)
+				bitmask = bitmask.Sub(bitmask, big.NewInt(1))
+				bitmask = bitmask.Lsh(bitmask, uint(startCol))
+
+				foundValidRow := false
+
+				// iterate through the rows
+				for index, row := range f.Rows {
+					// skip all rows, that are processed by the gaussian elimination
+					if index >= startRow && index <= stopRow {
+						continue
+					}
+
+					// get the bits to check
+					bitsToCheck := big.NewInt(0).And(
+						bitmask,
+						row,
+					)
+
+					// if the bits are 0...
+					if bitsToCheck.Cmp(big.NewInt(0)) == 0 {
+						// ...skip the row
+						continue
+					}
+
+					// swap the rows
+					f.SwapRows(pivotBit-1, index)
+					gaussMatrix.SwapRows(pivotBit-1, index)
+
+					foundValidRow = true
+
+					// exit the loop
+					break
+				}
+
+				if !foundValidRow {
+					return nil, nil, fmt.Errorf("cannot resolve linear dependency")
+				}
+
+				for i := startCol; i < pivotBit; i++ {
+					if f.Rows[pivotBit-startCol].Bit(i) == uint(0) {
+						continue
+					}
+
+					fmt.Printf("%d xor %d\n", pivotBit-startCol, startRow+i-startCol)
+
+					f.Rows[pivotBit-startCol].Xor(
+						f.Rows[pivotBit-startCol],
+						f.Rows[startRow+i-startCol],
+					)
+
+					gaussMatrix.Rows[pivotBit-startCol].Xor(
+						gaussMatrix.Rows[pivotBit-startCol],
+						gaussMatrix.Rows[startRow+i-startCol],
+					)
+				}
+
+				return gaussMatrix, permMatrix, nil
+			},
+			expectedResult: NewF2(3, 4).Set([]*big.Int{
+				big.NewInt(3),
+				big.NewInt(5),
+				big.NewInt(10),
+			}),
+			expectedError: false,
+		},
 	}
 
 	for _, test := range tests {
@@ -257,7 +336,7 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 			test.matrix.Rows,
 		)
 
-		permMatrix, err := test.matrix.PartialGaussianWithLinearChecking(
+		gaussMatrix, _, err := test.matrix.PartialGaussianWithLinearChecking(
 			test.startRow,
 			test.startCol,
 			test.stopRow,
@@ -265,6 +344,7 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 			test.linearCheck,
 		)
 
+		fmt.Printf(":: processed matrix\n")
 		test.matrix.PrettyPrint()
 
 		assert.Equalf(t, test.expectedError, err != nil, test.description)
@@ -276,10 +356,13 @@ func TestPartialGaussianWithLinearChecking(t *testing.T) {
 		assert.Truef(t, test.expectedResult.IsEqual(test.matrix), test.description)
 
 		// apply the transformation matrix on the origin matrix
-		permMatrix = permMatrix.MulMatrix(savedMatrix)
+		gaussMatrix = gaussMatrix.MulMatrix(savedMatrix)
+
+		fmt.Printf(":: gaussMatrix * savedMatrix\n")
+		gaussMatrix.PrintSlim()
 
 		// verify the result is correct
-		assert.Truef(t, permMatrix.IsEqual(test.matrix), test.description)
+		assert.Truef(t, gaussMatrix.IsEqual(test.matrix), test.description)
 	}
 }
 
diff --git a/resolver/lineardependency.go b/resolver/lineardependency.go
index 7a7b0de..e44d173 100644
--- a/resolver/lineardependency.go
+++ b/resolver/lineardependency.go
@@ -13,13 +13,14 @@ import (
 // using the function PartialGaussianWithLinearChecking as linearCheck-function.
 func LinearDependenciesInGauss(
 	f *gomatrix.F2,
+	gaussMatrix *gomatrix.F2,
 	permutationMatrix *gomatrix.F2,
 	startRow int,
 	startCol int,
 	stopRow int,
 	stopCol int,
 	pivotBit int,
-) (*gomatrix.F2, error) {
+) (*gomatrix.F2, *gomatrix.F2, error) {
 	// resolve the linear dependency
 	permutationMatrix, err := resolveWithOptimizedAlgorithm(
 		f,
@@ -34,7 +35,7 @@ func LinearDependenciesInGauss(
 	// if an error occured...
 	if err != nil {
 		// ...return it
-		return nil, err
+		return nil, nil, err
 	}
 
 	// apply the previous operations on the new row, with iterating through
@@ -52,14 +53,14 @@ func LinearDependenciesInGauss(
 			f.Rows[startRow+i-startCol],
 		)
 
-		permutationMatrix.Rows[startRow+pivotBit-startCol].Xor(
-			permutationMatrix.Rows[startRow+pivotBit-startCol],
-			permutationMatrix.Rows[startRow+i-startCol],
+		gaussMatrix.Rows[startRow+pivotBit-startCol].Xor(
+			gaussMatrix.Rows[startRow+pivotBit-startCol],
+			gaussMatrix.Rows[startRow+i-startCol],
 		)
 	}
 
 	// return success
-	return permutationMatrix, nil
+	return gaussMatrix, permutationMatrix, nil
 }
 
 // resolveWithOptimizedAlgorithm tries to resolve the dependency with finding
diff --git a/resolver/lineardependency_test.go b/resolver/lineardependency_test.go
index e782ffe..dfb3aa6 100644
--- a/resolver/lineardependency_test.go
+++ b/resolver/lineardependency_test.go
@@ -131,9 +131,10 @@ func TestLinearDependenciesInGauss(t *testing.T) {
 
 	for _, test := range tests {
 		fmt.Printf("%s\n", test.description)
-		_, err := LinearDependenciesInGauss(
+		_, _, err := LinearDependenciesInGauss(
 			test.matrix,
 			gomatrix.NewF2(test.matrix.N, test.matrix.N).SetToIdentity(),
+			gomatrix.NewF2(test.matrix.M, test.matrix.M).SetToIdentity(),
 			test.startRow,
 			test.startCol,
 			test.stopRow,
-- 
GitLab