feat(xor): add xor reasoning with gaussian elimination
This commit is contained in:
1 parent
4c9f61e79c
commit
64876af98f
1 file changed
+126
+126
@@ -0,0 +1,126 @@
|
||||
module Sift.Xor
|
||||
( XorClause(..)
|
||||
, xorClause
|
||||
, xorVarsOf
|
||||
, xorSatisfies
|
||||
, systemSatisfies
|
||||
, xorToCnf
|
||||
, systemToCnf
|
||||
, GaussResult(..)
|
||||
, gauss
|
||||
, gaussSolve
|
||||
) where
|
||||
|
||||
import Sift.Types
|
||||
import Data.List (foldl', sortOn)
|
||||
import Data.IntSet (IntSet)
|
||||
import qualified Data.IntSet as IntSet
|
||||
import qualified Data.Map.Strict as Map
|
||||
|
||||
data XorClause = XorClause
|
||||
{ xorVars :: IntSet
|
||||
, xorParity :: Bool
|
||||
} deriving (Eq, Ord, Show)
|
||||
|
||||
xorClause :: [Var] -> Bool -> XorClause
|
||||
xorClause vs parity =
|
||||
let raw = map varId vs
|
||||
folded = foldl' (\acc v -> if IntSet.member v acc
|
||||
then IntSet.delete v acc
|
||||
else IntSet.insert v acc)
|
||||
IntSet.empty raw
|
||||
in XorClause folded parity
|
||||
|
||||
xorVarsOf :: XorClause -> [Var]
|
||||
xorVarsOf = map Var . IntSet.toList . xorVars
|
||||
|
||||
xorSatisfies :: Assignment -> XorClause -> Bool
|
||||
xorSatisfies asn (XorClause vs parity) =
|
||||
IntSet.foldr (\v acc -> acc /= Map.findWithDefault False (Var v) asn) False vs == parity
|
||||
|
||||
systemSatisfies :: Assignment -> [XorClause] -> Bool
|
||||
systemSatisfies asn = all (xorSatisfies asn)
|
||||
|
||||
xorToCnf :: XorClause -> [Clause]
|
||||
xorToCnf (XorClause vs parity) =
|
||||
[ clauseFor signs
|
||||
| signs <- assignmentsOver n
|
||||
, violates signs ]
|
||||
where
|
||||
varList = map Var (IntSet.toList vs)
|
||||
n = length varList
|
||||
violates signs = odd (length (filter id signs)) /= parity
|
||||
clauseFor signs =
|
||||
[ if s then negLit v else posLit v
|
||||
| (v, s) <- zip varList signs ]
|
||||
|
||||
assignmentsOver :: Int -> [[Bool]]
|
||||
assignmentsOver 0 = [[]]
|
||||
assignmentsOver k = [ b : rest | b <- [False, True], rest <- assignmentsOver (k - 1) ]
|
||||
|
||||
systemToCnf :: [XorClause] -> CNF
|
||||
systemToCnf = CNF . concatMap xorToCnf
|
||||
|
||||
data Row = Row
|
||||
{ rowVars :: IntSet
|
||||
, rowRhs :: Bool
|
||||
} deriving (Eq, Show)
|
||||
|
||||
xorRows :: Row -> Row -> Row
|
||||
xorRows a b = Row (symDiff (rowVars a) (rowVars b)) (rowRhs a /= rowRhs b)
|
||||
|
||||
symDiff :: IntSet -> IntSet -> IntSet
|
||||
symDiff a b = IntSet.union (IntSet.difference a b) (IntSet.difference b a)
|
||||
|
||||
data GaussResult
|
||||
= GaussUnsat
|
||||
| GaussSat [(Int, Row)]
|
||||
deriving (Eq, Show)
|
||||
|
||||
gauss :: [XorClause] -> GaussResult
|
||||
gauss system = go [] (map toRow system)
|
||||
where
|
||||
toRow (XorClause vs parity) = Row vs parity
|
||||
go pivots [] = GaussSat pivots
|
||||
go pivots (r : rest) =
|
||||
let reduced = reduce pivots r
|
||||
in if IntSet.null (rowVars reduced)
|
||||
then if rowRhs reduced then GaussUnsat else go pivots rest
|
||||
else
|
||||
let pivotVar = IntSet.findMin (rowVars reduced)
|
||||
pivots' = insertPivot pivotVar reduced pivots
|
||||
in go pivots' rest
|
||||
|
||||
reduce :: [(Int, Row)] -> Row -> Row
|
||||
reduce pivots row =
|
||||
foldl' (\acc (pv, prow) ->
|
||||
if IntSet.member pv (rowVars acc) then xorRows acc prow else acc)
|
||||
row
|
||||
(sortOn fst pivots)
|
||||
|
||||
insertPivot :: Int -> Row -> [(Int, Row)] -> [(Int, Row)]
|
||||
insertPivot pivotVar row pivots =
|
||||
(pivotVar, row) : map back pivots
|
||||
where
|
||||
back (pv, prow) =
|
||||
if IntSet.member pivotVar (rowVars prow)
|
||||
then (pv, xorRows prow row)
|
||||
else (pv, prow)
|
||||
|
||||
gaussSolve :: [XorClause] -> Maybe Assignment
|
||||
gaussSolve system =
|
||||
case gauss system of
|
||||
GaussUnsat -> Nothing
|
||||
GaussSat pivots ->
|
||||
let allVarsSet = IntSet.unions (map xorVars system)
|
||||
pivotVars = IntSet.fromList (map fst pivots)
|
||||
freeVars = IntSet.difference allVarsSet pivotVars
|
||||
base = Map.fromList [ (Var v, False) | v <- IntSet.toList freeVars ]
|
||||
solved = foldl' assignPivot base pivots
|
||||
in Just solved
|
||||
where
|
||||
assignPivot acc (pv, row) =
|
||||
let others = IntSet.delete pv (rowVars row)
|
||||
othersXor = IntSet.foldr (\v a -> a /= Map.findWithDefault False (Var v) acc) False others
|
||||
val = othersXor /= rowRhs row
|
||||
in Map.insert (Var pv) val acc
|
||||
Reference in new issue
Block a user