moonlight-linalg-0.1.0.0: src-carrier/Moonlight/LinAlg/Internal/Discrete.hs
module Moonlight.LinAlg.Internal.Discrete
( GF2 (..),
gf2Zero,
gf2One,
gf2ToBool,
gf2FromBool,
rankPackedRows,
PackedBitMatrix (..),
matrixRowWords,
packedBitMatrixFromRowMajor,
packedBitMatrixFromXorEntries,
)
where
import Data.Bits (Bits (bit, xor, (.|.)), zeroBits)
import qualified Data.IntMap.Strict as IntMap
import Data.Kind (Type)
import qualified Data.Vector.Unboxed as U
import Data.Word (Word64)
import Moonlight.Algebra.Pure.Ring
( EuclideanDomain (..),
GCDDomain (..),
IntegralDomain (..),
)
import Moonlight.Core
( AdditiveGroup (..),
AdditiveMonoid (..),
CommutativeRing,
Field (..),
MultiplicativeMonoid (..),
Ring,
Semiring,
)
import qualified Moonlight.LinAlg.Internal.GF2.Xor as GF2Xor
import Prelude
type GF2 :: Type
data GF2 = GF2Zero | GF2One
deriving stock (Eq, Ord, Show)
instance Num GF2 where
(+) = add
(*) = mul
negate = id
abs = id
signum value =
case value of
GF2Zero -> GF2Zero
GF2One -> GF2One
fromInteger integerValue =
if odd integerValue
then GF2One
else GF2Zero
gf2Zero :: GF2
gf2Zero = GF2Zero
gf2One :: GF2
gf2One = GF2One
gf2FromBool :: Bool -> GF2
gf2FromBool flag = if flag then GF2One else GF2Zero
gf2ToBool :: GF2 -> Bool
gf2ToBool value = case value of
GF2Zero -> False
GF2One -> True
type PackedBitMatrix :: Type
data PackedBitMatrix = PackedBitMatrix
{ packedRows :: Int,
packedCols :: Int,
packedWordsPerRow :: Int,
packedWords :: U.Vector Word64
}
deriving stock (Eq, Show)
wordWidth :: Int -> Int
wordWidth count
| count <= 0 = 0
| otherwise =
count `Prelude.div` 64
+ if count `mod` 64 == 0 then 0 else 1
packRowMajorBits :: Int -> Int -> [GF2] -> U.Vector Word64
packRowMajorBits rowCount columnCount values =
let wordsPerRowValue = wordWidth columnCount
packedWordCount = rowCount * wordsPerRowValue
updateWord mapValue (linearIndex, bitValue) =
if bitValue == GF2Zero || columnCount <= 0
then mapValue
else
let rowIndex = linearIndex `Prelude.div` columnCount
columnIndex = linearIndex `mod` columnCount
wordIndex = (rowIndex * wordsPerRowValue) + (columnIndex `Prelude.div` 64)
bitIndex = columnIndex `mod` 64
bitMask = bit bitIndex :: Word64
in IntMap.insertWith (.|.) wordIndex bitMask mapValue
packedMap = foldl' updateWord IntMap.empty (zip [0 :: Int ..] values)
in U.generate packedWordCount (\wordIndex -> IntMap.findWithDefault zeroBits wordIndex packedMap)
packedBitMatrixFromRowMajor :: Int -> Int -> [GF2] -> PackedBitMatrix
packedBitMatrixFromRowMajor rowCount columnCount values =
PackedBitMatrix
{ packedRows = rowCount,
packedCols = columnCount,
packedWordsPerRow = wordWidth columnCount,
packedWords = packRowMajorBits rowCount columnCount values
}
packedBitMatrixFromXorEntries :: Int -> Int -> [(Int, Int)] -> PackedBitMatrix
packedBitMatrixFromXorEntries rowCount columnCount entries =
let wordsPerRowValue = wordWidth columnCount
packedWordCount = rowCount * wordsPerRowValue
updateWord mapValue (rowIndex, columnIndex) =
let wordIndex = (rowIndex * wordsPerRowValue) + (columnIndex `Prelude.div` 64)
bitIndex = columnIndex `mod` 64
bitMask = bit bitIndex :: Word64
in IntMap.insertWith xor wordIndex bitMask mapValue
packedMap = foldl' updateWord IntMap.empty entries
in PackedBitMatrix
{ packedRows = rowCount,
packedCols = columnCount,
packedWordsPerRow = wordsPerRowValue,
packedWords = U.generate packedWordCount (\wordIndex -> IntMap.findWithDefault zeroBits wordIndex packedMap)
}
matrixRowWords :: PackedBitMatrix -> Int -> U.Vector Word64
matrixRowWords matrixValue rowIndex =
let wordsPerRowValue = packedWordsPerRow matrixValue
startIndex = rowIndex * wordsPerRowValue
in U.take wordsPerRowValue (U.drop startIndex (packedWords matrixValue))
rankPackedRows :: Int -> [U.Vector Word64] -> Int
rankPackedRows =
GF2Xor.rankPackedRowsByReduction
instance AdditiveMonoid GF2 where
zero = GF2Zero
add left right =
case (left, right) of
(GF2Zero, value) -> value
(value, GF2Zero) -> value
(GF2One, GF2One) -> GF2Zero
instance AdditiveGroup GF2 where
neg = id
instance MultiplicativeMonoid GF2 where
one = GF2One
mul left right =
case (left, right) of
(GF2One, GF2One) -> GF2One
_ -> GF2Zero
instance Ring GF2
instance Field GF2 where
tryInv value = case value of
GF2Zero -> Nothing
GF2One -> Just GF2One
instance Semiring GF2
instance CommutativeRing GF2
instance IntegralDomain GF2 where
isZero value = value == GF2Zero
unitInverse value = case value of
GF2Zero -> Nothing
GF2One -> Just GF2One
instance GCDDomain GF2 where
gcdDomain left right
| left == GF2Zero && right == GF2Zero = GF2Zero
| otherwise = GF2One
extendedGcdDomain left right
| left == GF2Zero && right == GF2Zero = (GF2Zero, GF2Zero, GF2Zero)
| left == GF2One = (GF2One, GF2One, GF2Zero)
| otherwise = (GF2One, GF2Zero, GF2One)
instance EuclideanDomain GF2 where
type Degree GF2 = Int
divideWithRemainder numerator _ = (numerator, GF2Zero)
degree value =
case value of
GF2Zero -> 0
GF2One -> 0