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