GF2 Gaussian elimination
This commit is contained in:
@@ -23,6 +23,7 @@ library
|
|||||||
Factorization
|
Factorization
|
||||||
ModularSquareRoot
|
ModularSquareRoot
|
||||||
ModularArithmeticUtils
|
ModularArithmeticUtils
|
||||||
|
LinearAlgebra.GF2
|
||||||
other-modules:
|
other-modules:
|
||||||
Primes.FermatPrimeTest
|
Primes.FermatPrimeTest
|
||||||
Primes.MillerRabin
|
Primes.MillerRabin
|
||||||
@@ -56,6 +57,8 @@ test-suite haskell-math-test
|
|||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
|
other-modules:
|
||||||
|
GF2test
|
||||||
build-depends:
|
build-depends:
|
||||||
base ^>=4.18.2.1,
|
base ^>=4.18.2.1,
|
||||||
haskell-math
|
haskell-math
|
||||||
|
|||||||
96
src/LinearAlgebra/GF2.hs
Normal file
96
src/LinearAlgebra/GF2.hs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
module LinearAlgebra.GF2
|
||||||
|
( -- * Types
|
||||||
|
BitVec
|
||||||
|
, Mask
|
||||||
|
-- Conversions
|
||||||
|
, fromBools
|
||||||
|
, toBools
|
||||||
|
-- Gaussian elimination
|
||||||
|
, gaussianEliminationMask -- :: nCols -> [BitVec] -> Maybe Mask
|
||||||
|
, gaussianEliminationIndices -- :: nCols -> [BitVec] -> Maybe [Int]
|
||||||
|
, maskToIndicesInt -- :: Mask -> [Int]
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Bits
|
||||||
|
import Data.List (findIndex, foldl')
|
||||||
|
import Data.Maybe (listToMaybe, fromMaybe)
|
||||||
|
|
||||||
|
-- vector over GF(2) represented as an Integer bitmask. The bit i corresponding to the column i.
|
||||||
|
type BitVec = Integer
|
||||||
|
|
||||||
|
-- Mask to select rows. bit i at 1 means original row i included.
|
||||||
|
type Mask = Integer
|
||||||
|
|
||||||
|
-- Conversions
|
||||||
|
|
||||||
|
-- Convert a list of Bool to a BitVec.
|
||||||
|
-- Example: fromBools [True, False, True] == 0b101 == 5
|
||||||
|
fromBools :: [Bool] -> BitVec
|
||||||
|
fromBools bs = foldl' (\acc (i, b) -> if b then setBit acc i else acc) 0 (zip [0..] bs)
|
||||||
|
|
||||||
|
-- Convert a BitVec to a list of Bool of length nCols.
|
||||||
|
toBools :: Int -> BitVec -> [Bool]
|
||||||
|
toBools nCols v = [ testBit v i | i <- [0 .. nCols - 1] ]
|
||||||
|
|
||||||
|
-- Gaussian elimination over GF(2) using Integers
|
||||||
|
-- Gaussian elimination on a list of BitVec rows (each row is an Integer bitmask of length nCols) and return a mask (as Integer) whose set bits indicate which input rows XOR to produce a vector full of 0 (or Nothing if not enough rows to find a combination).
|
||||||
|
gaussianEliminationMask :: Int -> [BitVec] -> Maybe Mask
|
||||||
|
gaussianEliminationMask nCols rows
|
||||||
|
| null rows = Nothing
|
||||||
|
| otherwise = findZeroMask (eliminate 0 augmented)
|
||||||
|
where
|
||||||
|
m :: Int
|
||||||
|
m = length rows
|
||||||
|
|
||||||
|
-- initial mask = 1 << i
|
||||||
|
augmented :: [(BitVec, Mask)]
|
||||||
|
augmented = zip rows (map (\i -> bit i) [0..m-1])
|
||||||
|
|
||||||
|
-- Top level elimination
|
||||||
|
eliminate :: Int -> [(BitVec, Mask)] -> [(BitVec, Mask)]
|
||||||
|
eliminate col mat
|
||||||
|
| col >= nCols = mat
|
||||||
|
| otherwise =
|
||||||
|
case findPivot col mat of
|
||||||
|
Nothing -> eliminate (col + 1) mat
|
||||||
|
Just pivotIdx ->
|
||||||
|
let (pivotRow, rest) = removeAt pivotIdx mat
|
||||||
|
(pvVec, pvMask) = pivotRow
|
||||||
|
rest' = map (xorIfHasBit pvVec pvMask col) rest
|
||||||
|
in eliminate (col + 1) (pivotRow : rest')
|
||||||
|
|
||||||
|
findPivot :: Int -> [(BitVec, Mask)] -> Maybe Int
|
||||||
|
findPivot col mat = findIndex (\(v,_) -> testBit v col) mat
|
||||||
|
|
||||||
|
xorIfHasBit :: BitVec -> Mask -> Int -> (BitVec, Mask) -> (BitVec, Mask)
|
||||||
|
xorIfHasBit pvVec pvMask col (v, mask)
|
||||||
|
| testBit v col = (v `xor` pvVec, mask `xor` pvMask)
|
||||||
|
| otherwise = (v, mask)
|
||||||
|
|
||||||
|
findZeroMask :: [(BitVec, Mask)] -> Maybe Mask
|
||||||
|
findZeroMask mat = fmap snd (listToMaybe (filter (\(v,_) -> v == 0) mat))
|
||||||
|
|
||||||
|
|
||||||
|
-- wrapper
|
||||||
|
gaussianEliminationIndices :: Int -> [BitVec] -> Maybe [Int]
|
||||||
|
gaussianEliminationIndices nCols rows = fmap maskToIndicesInt (gaussianEliminationMask nCols rows)
|
||||||
|
|
||||||
|
-- helpers
|
||||||
|
|
||||||
|
maskToIndicesInt :: Mask -> [Int]
|
||||||
|
maskToIndicesInt mask = go 0 mask []
|
||||||
|
where
|
||||||
|
go :: Int -> Mask -> [Int] -> [Int]
|
||||||
|
go _ 0 acc = reverse acc
|
||||||
|
go i m acc
|
||||||
|
| testBit m 0 = go (i+1) (shiftR m 1) (i:acc)
|
||||||
|
| otherwise = go (i+1) (shiftR m 1) acc
|
||||||
|
|
||||||
|
-- remove element at index i from list
|
||||||
|
-- Returns (element, list-without-element)
|
||||||
|
removeAt :: Int -> [a] -> (a, [a])
|
||||||
|
removeAt i xs =
|
||||||
|
let (front, rest) = splitAt i xs
|
||||||
|
in case rest of
|
||||||
|
(y:ys) -> (y, front ++ ys)
|
||||||
|
[] -> error "removeAt: index out of bounds"
|
||||||
29
test/GF2test.hs
Normal file
29
test/GF2test.hs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
module GF2test (run) where
|
||||||
|
|
||||||
|
import LinearAlgebra.GF2
|
||||||
|
( fromBools
|
||||||
|
, gaussianEliminationIndices
|
||||||
|
)
|
||||||
|
|
||||||
|
run :: IO ()
|
||||||
|
run = do
|
||||||
|
putStrLn "GF2 Gaussian elimination test"
|
||||||
|
|
||||||
|
let rowsBools =
|
||||||
|
[ [True, False, True]
|
||||||
|
, [False, True, True]
|
||||||
|
, [True, True, False]
|
||||||
|
, [True, True, True]
|
||||||
|
]
|
||||||
|
|
||||||
|
nCols = length (head rowsBools)
|
||||||
|
rowsBits = map fromBools rowsBools
|
||||||
|
|
||||||
|
let maybeIndices = gaussianEliminationIndices nCols rowsBits
|
||||||
|
|
||||||
|
case maybeIndices of
|
||||||
|
Nothing -> putStrLn "No combination found."
|
||||||
|
Just idxs -> putStrLn $ "Rows: " ++ show idxs
|
||||||
|
|
||||||
|
putStrLn "GF2 Test Done."
|
||||||
|
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
|
import GF2test (run)
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = putStrLn "Test suite not yet implemented."
|
main = do
|
||||||
|
putStrLn "Running all tests"
|
||||||
|
GF2test.run
|
||||||
|
putStrLn "All tests done."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user