Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary Linear Algebra #208

Merged
merged 59 commits into from
Aug 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
913e86e
Matrix multiplication
federico-bongiorno Jun 18, 2020
f92995a
Removing rows
federico-bongiorno Jun 18, 2020
eeaf18a
First step of linear algebra algorithm
federico-bongiorno Jun 19, 2020
17ceaec
Initial implementation of linear algebra algorithm
federico-bongiorno Jun 23, 2020
6e75169
Using bitvec package instead
federico-bongiorno Jun 25, 2020
767a154
Modified extended Euclid's algorithm. It now stops before reaching gcd
federico-bongiorno Jun 25, 2020
b6cf046
Working version of linear algebra solver. Gives a solution a third of…
federico-bongiorno Jun 27, 2020
f947dc0
Time and space improvements
federico-bongiorno Jun 28, 2020
01e73a0
Several bug fixes
federico-bongiorno Jun 30, 2020
821caf4
Modified .gitignore
federico-bongiorno Jun 30, 2020
8c2fb50
Proper random vectors
federico-bongiorno Jul 1, 2020
58a0cde
Wrote tests and debuged
federico-bongiorno Jul 4, 2020
59fe10f
Sparse z and x
federico-bongiorno Jul 4, 2020
a778369
New implementation of mult
federico-bongiorno Jul 11, 2020
d3187cb
Faster implementation of mult
federico-bongiorno Jul 14, 2020
c961852
Dense binary vectors
federico-bongiorno Jul 14, 2020
c4364ef
Deterministic vector z is chosen
federico-bongiorno Jul 15, 2020
c6150eb
Dense binary vectors of fixed length
federico-bongiorno Jul 15, 2020
f38a4f6
Added cabal file
federico-bongiorno Jul 16, 2020
96c7fbe
Comparison with Gauss. Matrices and sparse vectors are of given size
federico-bongiorno Jul 17, 2020
b0daf92
Started joining the two algorithms
federico-bongiorno Jul 21, 2020
bce57d6
Debugging for memory issue
federico-bongiorno Jul 22, 2020
45f3409
Slight modification to generateInterval
federico-bongiorno Jul 22, 2020
8756b20
Performing sieve in blocks
federico-bongiorno Jul 23, 2020
04d840b
Draft of sieve finding smooth numbers
federico-bongiorno May 27, 2020
ab221e8
Changed from storing factorisations in lists to vectors
federico-bongiorno May 28, 2020
4caabd9
Improved comments
federico-bongiorno May 28, 2020
3663d25
Deleted smoothSieve from master
federico-bongiorno Jun 1, 2020
f63cf16
Changed arithmoi cabal
federico-bongiorno Jun 1, 2020
19a2e0e
Modified test-suite
federico-bongiorno Jul 24, 2020
e2d34f1
Fixed a bug
federico-bongiorno Jul 24, 2020
2e1e84b
Testing
federico-bongiorno Jul 24, 2020
c3fc628
Testing
federico-bongiorno Jul 24, 2020
c55de8a
Testing
federico-bongiorno Jul 24, 2020
f317d90
Checking how many smooth numbers are found
federico-bongiorno Jul 25, 2020
4ec1d10
Testing
federico-bongiorno Jul 25, 2020
d094801
Speed improvement
federico-bongiorno Jul 25, 2020
742a54e
Solves linear system until a factor is found
federico-bongiorno Jul 25, 2020
617ac42
Log sieving and hlint suggestions
federico-bongiorno Jul 25, 2020
465701a
Draft of sieve finding smooth numbers
federico-bongiorno May 27, 2020
8b1a035
Changed from storing factorisations in lists to vectors
federico-bongiorno May 28, 2020
bea76e3
Improved comments
federico-bongiorno May 28, 2020
d33eb0d
Deleted smoothSieve from master
federico-bongiorno Jun 1, 2020
d6cfe0c
Changed arithmoi cabal
federico-bongiorno Jun 1, 2020
cfd3e45
Log sieving
federico-bongiorno Jul 27, 2020
8fb459f
Removed Log Sieving
federico-bongiorno Jul 28, 2020
27e3fcb
Changed testing
federico-bongiorno Jul 30, 2020
093e583
Changed testing again
federico-bongiorno Jul 30, 2020
f3c93cf
Wrote documentation
federico-bongiorno Jul 30, 2020
7e91ded
Studied a bug where sometimes linearSolve produces the same solution
federico-bongiorno Jul 31, 2020
16a8a81
Corrected testing
federico-bongiorno Jul 31, 2020
5c98663
Minor changes to comments
federico-bongiorno Aug 1, 2020
134f5fb
Removed Data.Set
federico-bongiorno Aug 1, 2020
81721d4
Removed $
federico-bongiorno Aug 5, 2020
277b221
Implementing suggestions
federico-bongiorno Aug 5, 2020
3c765c9
One second
federico-bongiorno Aug 5, 2020
47b29cd
Implemented suggestions
federico-bongiorno Aug 5, 2020
ee4164d
Travis suggestion
federico-bongiorno Aug 5, 2020
ba040c6
Tiny hlint suggestion
federico-bongiorno Aug 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ dist-newstyle
.swp
.stack-work
*.prof
*.hp
*.svg
159 changes: 159 additions & 0 deletions Math/NumberTheory/Primes/Factorisation/LinearAlgebra.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Math.NumberTheory.Primes.Factorisation.LinearAlgebra
( SBVector(..)
, DBVector(..)
, SBMatrix(..)
, dot
, mult
, linearSolve
) where

#if __GLASGOW_HASKELL__ < 803
import Data.Semigroup
#endif
import qualified Data.List as L
import qualified Data.Vector as V
import qualified Data.Vector.Sized as SV
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Sized as SU
import qualified Data.Vector.Unboxed.Mutable.Sized as SMU
import qualified Data.Vector.Generic.Sized.Internal as GSI
import qualified Data.Vector.Generic.Mutable.Sized.Internal as GMSI
import Math.NumberTheory.Utils.FromIntegral
import Control.Monad.ST
import GHC.TypeNats (Nat, KnownNat, natVal)
import Data.Proxy
import System.Random
import Data.Foldable
import Data.Mod.Word
import Data.Maybe
import Data.Bit
import Data.Bits

-- | Sparse Binary Vector of size @k.
newtype SBVector (k :: Nat) = SBVector { unSBVector :: U.Vector (Mod k) }

-- | Dense Binary Vector of size @k@.
newtype DBVector (k :: Nat) = DBVector { unDBVector :: SU.Vector k Bit }
deriving (Eq, Show)

-- | Sparse Binary square Matrix of size @k@. It is formed of columns
-- of sparse binary vectors.
newtype SBMatrix (k :: Nat) = SBMatrix { unSBMatrix :: SV.Vector k (SBVector k) }

-- | Addition of two dense Binary Vectors.
instance KnownNat k => Semigroup (DBVector k) where
DBVector v1 <> DBVector v2 = DBVector $ SU.withVectorUnsafe (zipBits xor (SU.fromSized v1)) v2

-- | Dense Binary Vectors of given length form a group under addition.
instance KnownNat k => Monoid (DBVector k) where
mempty = DBVector $ SU.replicate (Bit False)
mappend = (<>)

-- | Dot product of two dense Binary Vectors of the same size.
dot :: KnownNat k => DBVector k -> DBVector k -> Bit
dot (DBVector v1) (DBVector v2) = Bit $ odd . countBits $ zipBits (.&.) (SU.fromSized v1) (SU.fromSized v2)

-- | Multiplication of a square matrix and a dense vector.
mult :: KnownNat k => SBMatrix k -> DBVector k -> DBVector k
mult matrix vector = runST $ do
vs <- SMU.new
traverse_ (U.mapM_ (flipBit' vs . wordToInt . unMod) . unSBVector . (matrix `index'`)) $ listBits' vector
ws <- SU.unsafeFreeze vs
pure $ DBVector ws

listBits' :: KnownNat k => DBVector k -> [Int]
listBits' (DBVector (GSI.Vector v)) = listBits v

flipBit' :: KnownNat k => SMU.MVector k s Bit -> Int -> ST s ()
flipBit' (GMSI.MVector v) = unsafeFlipBit v

index' :: KnownNat k => SBMatrix k -> Int -> SBVector k
index' (SBMatrix (GSI.Vector v)) = V.unsafeIndex v

intVal :: KnownNat k => a k -> Int
intVal = naturalToInt . natVal

-- | It takes a random seed and a square singular matrix and it returns an
-- elemnent of its kernel. It does not check if the matrix is singular.
linearSolve :: KnownNat k => Int -> SBMatrix k -> DBVector k
linearSolve seed matrix = linearSolveHelper 1 matrix randomVectors 1
where
-- The floating point number is the density of the random vectors.
randomVectors = getRandomDBVectors 0.5 $ mkStdGen seed

linearSolveHelper :: KnownNat k => F2Poly -> SBMatrix k -> [DBVector k] -> Int -> DBVector k
linearSolveHelper _ _ [] _ = error "Not enough random vectors"
linearSolveHelper _ _ [_] _ = error "Not enough random vectors"
linearSolveHelper previousPoly matrix (z : x : otherVecs) counter = case potentialSolution of
-- This is a good solution.
Just solution -> solution
Nothing
-- If the algorithm does not find a solution, try another random vector @x@.
| counter <= 5 -> linearSolveHelper potentialMinPoly matrix (z : otherVecs) (counter + 1)
-- If the algorithm does not find a solution after five iterations,
-- most likely (>90%), it means that it picked a bad random vector @z@
-- in the image of the matrix. This changes @z@.
| otherwise -> linearSolveHelper 1 matrix otherVecs 1
where
potentialSolution = findSolution countOfSingularities matrix almostZeroVector
almostZeroVector = evaluate matrix z $ toF2Poly $ U.drop countOfSingularities vectorMinPoly
countOfSingularities = fromMaybe (U.length vectorMinPoly) $ bitIndex (Bit True) vectorMinPoly
-- Information gathered from the previous random vector @x@ is used in
-- the subsequent iteration.
vectorMinPoly = unF2Poly potentialMinPoly
potentialMinPoly = lcm previousPoly candidateMinPoly
candidateMinPoly = findCandidatePoly matrix z x

-- Infinite lists of random DBVectors.
getRandomDBVectors :: forall k. KnownNat k => Double -> StdGen -> [DBVector k]
getRandomDBVectors density gen = go $ randomRs (0, 1) gen
where
numberOfColumns = intVal (Proxy :: Proxy k)
go :: KnownNat k => [Double] -> [DBVector k]
go list = newVector `seq` newVector : go backOfList
where
newVector = DBVector $ fromJust $ SU.fromList $ map (\d -> Bit (d < density)) frontOfList
(frontOfList, backOfList) = L.splitAt numberOfColumns list

-- The input is a matrix @B@ and random vectors @z@ and @x@. It returns the
-- sequence @x A ^ k z@ as k runs from 0 to the size of the matrix.
generateData :: KnownNat k => SBMatrix k -> DBVector k -> DBVector k -> F2Poly
generateData matrix z x = toF2Poly $ U.fromList $ reverse $ map (x `dot`) matrixPowers
where
matrixPowers = take (((*2) . intVal) matrix) $ L.iterate (matrix `mult`) z

-- This routine finds the generating polynomial of the random sequence computed
-- in @generateData@.
berlekampMassey :: Int -> F2Poly -> F2Poly -> F2Poly
berlekampMassey dim = go 1 0
where
go :: F2Poly -> F2Poly -> F2Poly ->F2Poly -> F2Poly
go oneBefore twoBefore a b
federico-bongiorno marked this conversation as resolved.
Show resolved Hide resolved
| U.length (unF2Poly b) <= dim = oneBefore
| otherwise = go (twoBefore - oneBefore * q) oneBefore b r
where
(q, r) = quotRem a b

findCandidatePoly :: KnownNat k => SBMatrix k -> DBVector k -> DBVector k -> F2Poly
findCandidatePoly matrix z x = berlekampMassey dim errorPoly randomSequence
where
randomSequence = generateData matrix z x
errorPoly = fromInteger (1 `shiftL` (2 * dim)) :: F2Poly
dim = intVal matrix

-- This routine takes a polynomial @p@, a @matrix@ and a vector @w@ and
-- returns @p(A)w@.
evaluate :: KnownNat k => SBMatrix k -> DBVector k -> F2Poly -> DBVector k
evaluate matrix w poly = U.foldr (\coeff acc -> (matrix `mult` acc) <> (if unBit coeff then w else mempty)) mempty $ unF2Poly poly

-- Tries to infer a solution. If it does not succeed, it returns an empty solution.
findSolution :: KnownNat k => Int -> SBMatrix k -> DBVector k -> Maybe (DBVector k)
findSolution len matrix vector = fst <$> find ((== mempty) . snd) (zip vectors (tail vectors))
where
vectors = take (len + 1) $ L.iterate (matrix `mult`) vector
Loading