module ToySolver.SAT.Types
(
Var
, VarSet
, VarMap
, validVar
, IModel (..)
, Model
, Lit
, LitSet
, LitMap
, litUndef
, validLit
, literal
, litNot
, litVar
, litPolarity
, evalLit
, Clause
, normalizeClause
, instantiateClause
, clauseSubsume
, evalClause
, clauseToPBLinAtLeast
, AtLeast
, normalizeAtLeast
, instantiateAtLeast
, evalAtLeast
, PBLinTerm
, PBLinSum
, PBLinAtLeast
, PBLinExactly
, normalizePBLinSum
, normalizePBLinAtLeast
, normalizePBLinExactly
, instantiatePBLinAtLeast
, instantiatePBLinExactly
, cutResolve
, cardinalityReduction
, negatePBLinAtLeast
, evalPBLinSum
, evalPBLinAtLeast
, evalPBLinExactly
, pbLowerBound
, pbUpperBound
, pbSubsume
, XORClause
, normalizeXORClause
, instantiateXORClause
, evalXORClause
) where
import Control.Monad
import Control.Exception
import Data.Array.Unboxed
import Data.Ord
import Data.List
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
import ToySolver.Data.LBool
type Var = Int
type VarSet = IntSet
type VarMap = IntMap
validVar :: Var -> Bool
validVar v = v > 0
class IModel a where
evalVar :: a -> Var -> Bool
type Model = UArray Var Bool
instance IModel (UArray Var Bool) where
evalVar m v = m ! v
instance IModel (Array Var Bool) where
evalVar m v = m ! v
instance IModel (Var -> Bool) where
evalVar m v = m v
type Lit = Int
litUndef :: Lit
litUndef = 0
type LitSet = IntSet
type LitMap = IntMap
validLit :: Lit -> Bool
validLit l = l /= 0
literal :: Var
-> Bool
-> Lit
literal v polarity =
assert (validVar v) $ if polarity then v else litNot v
litNot :: Lit -> Lit
litNot l = assert (validLit l) $ negate l
litVar :: Lit -> Var
litVar l = assert (validLit l) $ abs l
litPolarity :: Lit -> Bool
litPolarity l = assert (validLit l) $ l > 0
evalLit :: IModel m => m -> Lit -> Bool
evalLit m l = if l > 0 then evalVar m l else not (evalVar m (abs l))
type Clause = [Lit]
normalizeClause :: Clause -> Maybe Clause
normalizeClause lits = assert (IntSet.size ys `mod` 2 == 0) $
if IntSet.null ys
then Just (IntSet.toList xs)
else Nothing
where
xs = IntSet.fromList lits
ys = xs `IntSet.intersection` (IntSet.map litNot xs)
instantiateClause :: forall m. Monad m => (Lit -> m LBool) -> Clause -> m (Maybe Clause)
instantiateClause evalLitM = loop []
where
loop :: [Lit] -> [Lit] -> m (Maybe Clause)
loop ret [] = return $ Just ret
loop ret (l:ls) = do
val <- evalLitM l
if val==lTrue then
return Nothing
else if val==lFalse then
loop ret ls
else
loop (l : ret) ls
clauseSubsume :: Clause -> Clause -> Bool
clauseSubsume cl1 cl2 = cl1' `IntSet.isSubsetOf` cl2'
where
cl1' = IntSet.fromList cl1
cl2' = IntSet.fromList cl2
evalClause :: IModel m => m -> Clause -> Bool
evalClause m cl = any (evalLit m) cl
clauseToPBLinAtLeast :: Clause -> PBLinAtLeast
clauseToPBLinAtLeast xs = ([(1,l) | l <- xs], 1)
type AtLeast = ([Lit], Int)
normalizeAtLeast :: AtLeast -> AtLeast
normalizeAtLeast (lits,n) = assert (IntSet.size ys `mod` 2 == 0) $
(IntSet.toList lits', n')
where
xs = IntSet.fromList lits
ys = xs `IntSet.intersection` (IntSet.map litNot xs)
lits' = xs `IntSet.difference` ys
n' = n (IntSet.size ys `div` 2)
instantiateAtLeast :: forall m. Monad m => (Lit -> m LBool) -> AtLeast -> m AtLeast
instantiateAtLeast evalLitM (xs,n) = loop ([],n) xs
where
loop :: AtLeast -> [Lit] -> m AtLeast
loop ret [] = return ret
loop (ys,m) (l:ls) = do
val <- evalLitM l
if val == lTrue then
loop (ys, m1) ls
else if val == lFalse then
loop (ys, m) ls
else
loop (l:ys, m) ls
evalAtLeast :: IModel m => m -> AtLeast -> Bool
evalAtLeast m (lits,n) = sum [1 | lit <- lits, evalLit m lit] >= n
type PBLinTerm = (Integer, Lit)
type PBLinSum = [PBLinTerm]
type PBLinAtLeast = (PBLinSum, Integer)
type PBLinExactly = (PBLinSum, Integer)
normalizePBLinSum :: (PBLinSum, Integer) -> (PBLinSum, Integer)
normalizePBLinSum = step2 . step1
where
step1 :: (PBLinSum, Integer) -> (PBLinSum, Integer)
step1 (xs,n) =
case loop (IntMap.empty,n) xs of
(ys,n') -> ([(c,v) | (v,c) <- IntMap.toList ys], n')
where
loop :: (VarMap Integer, Integer) -> PBLinSum -> (VarMap Integer, Integer)
loop (ys,m) [] = (ys,m)
loop (ys,m) ((c,l):zs) =
if litPolarity l
then loop (IntMap.insertWith (+) l c ys, m) zs
else loop (IntMap.insertWith (+) (litNot l) (negate c) ys, m+c) zs
step2 :: (PBLinSum, Integer) -> (PBLinSum, Integer)
step2 (xs,n) = loop ([],n) xs
where
loop (ys,m) [] = (ys,m)
loop (ys,m) (t@(c,l):zs)
| c == 0 = loop (ys,m) zs
| c < 0 = loop ((negate c,litNot l):ys, m+c) zs
| otherwise = loop (t:ys,m) zs
normalizePBLinAtLeast :: PBLinAtLeast -> PBLinAtLeast
normalizePBLinAtLeast a =
case step1 a of
(xs,n)
| n > 0 -> step3 (saturate n xs, n)
| otherwise -> ([], 0)
where
step1 :: PBLinAtLeast -> PBLinAtLeast
step1 (xs,n) =
case normalizePBLinSum (xs,n) of
(ys,m) -> (ys, m)
saturate :: Integer -> PBLinSum -> PBLinSum
saturate n xs = [assert (c>0) (min n c, l) | (c,l) <- xs]
step3 :: PBLinAtLeast -> PBLinAtLeast
step3 ([],n) = ([],n)
step3 (xs,n) = ([(c `div` d, l) | (c,l) <- xs], (n+d1) `div` d)
where
d = foldl1' gcd [c | (c,_) <- xs]
normalizePBLinExactly :: PBLinExactly -> PBLinExactly
normalizePBLinExactly a =
case step1 $ a of
(xs,n)
| n >= 0 -> step2 (xs, n)
| otherwise -> ([], 1)
where
step1 :: PBLinExactly -> PBLinExactly
step1 (xs,n) =
case normalizePBLinSum (xs,n) of
(ys,m) -> (ys, m)
step2 :: PBLinExactly -> PBLinExactly
step2 ([],n) = ([],n)
step2 (xs,n)
| n `mod` d == 0 = ([(c `div` d, l) | (c,l) <- xs], n `div` d)
| otherwise = ([], 1)
where
d = foldl1' gcd [c | (c,_) <- xs]
instantiatePBLinAtLeast :: forall m. Monad m => (Lit -> m LBool) -> PBLinAtLeast -> m PBLinAtLeast
instantiatePBLinAtLeast evalLitM (xs,n) = loop ([],n) xs
where
loop :: PBLinAtLeast -> PBLinSum -> m PBLinAtLeast
loop ret [] = return ret
loop (ys,m) ((c,l):ts) = do
val <- evalLitM l
if val == lTrue then
loop (ys, mc) ts
else if val == lFalse then
loop (ys, m) ts
else
loop ((c,l):ys, m) ts
instantiatePBLinExactly :: Monad m => (Lit -> m LBool) -> PBLinExactly -> m PBLinExactly
instantiatePBLinExactly = instantiatePBLinAtLeast
cutResolve :: PBLinAtLeast -> PBLinAtLeast -> Var -> PBLinAtLeast
cutResolve (lhs1,rhs1) (lhs2,rhs2) v = assert (l1 == litNot l2) $ normalizePBLinAtLeast pb
where
(c1,l1) = head [(c,l) | (c,l) <- lhs1, litVar l == v]
(c2,l2) = head [(c,l) | (c,l) <- lhs2, litVar l == v]
g = gcd c1 c2
s1 = c2 `div` g
s2 = c1 `div` g
pb = ([(s1*c,l) | (c,l) <- lhs1] ++ [(s2*c,l) | (c,l) <- lhs2], s1*rhs1 + s2 * rhs2)
cardinalityReduction :: PBLinAtLeast -> AtLeast
cardinalityReduction (lhs,rhs) = (ls, rhs')
where
rhs' = go1 0 0 (sortBy (flip (comparing fst)) lhs)
go1 !s !k ((a,_):ts)
| s < rhs = go1 (s+a) (k+1) ts
| otherwise = k
go1 _ _ [] = error "ToySolver.SAT.Types.cardinalityReduction: should not happen"
ls = go2 (minimum (rhs : map (subtract 1 . fst) lhs)) (sortBy (comparing fst) lhs)
go2 !guard' ((a,_) : ts)
| a 1 < guard' = go2 (guard' a) ts
| otherwise = map snd ts
go2 _ [] = error "ToySolver.SAT.Types.cardinalityReduction: should not happen"
negatePBLinAtLeast :: PBLinAtLeast -> PBLinAtLeast
negatePBLinAtLeast (xs, rhs) = ([(c,lit) | (c,lit)<-xs] , rhs + 1)
evalPBLinSum :: IModel m => m -> PBLinSum -> Integer
evalPBLinSum m xs = sum [c | (c,lit) <- xs, evalLit m lit]
evalPBLinAtLeast :: IModel m => m -> PBLinAtLeast -> Bool
evalPBLinAtLeast m (lhs,rhs) = evalPBLinSum m lhs >= rhs
evalPBLinExactly :: IModel m => m -> PBLinAtLeast -> Bool
evalPBLinExactly m (lhs,rhs) = evalPBLinSum m lhs == rhs
pbLowerBound :: PBLinSum -> Integer
pbLowerBound xs = sum [if c < 0 then c else 0 | (c,_) <- xs]
pbUpperBound :: PBLinSum -> Integer
pbUpperBound xs = sum [if c > 0 then c else 0 | (c,_) <- xs]
pbSubsume :: PBLinAtLeast -> PBLinAtLeast -> Bool
pbSubsume (lhs1,rhs1) (lhs2,rhs2) =
rhs1 >= rhs2 && and [di >= ci | (ci,li) <- lhs1, let di = IntMap.findWithDefault 0 li lhs2']
where
lhs2' = IntMap.fromList [(l,c) | (c,l) <- lhs2]
type XORClause = ([Lit], Bool)
normalizeXORClause :: XORClause -> XORClause
normalizeXORClause (lits, b) =
case IntMap.keys m of
0:xs -> (xs, not b)
xs -> (xs, b)
where
m = IntMap.filter id $ IntMap.unionsWith xor [f lit | lit <- lits]
xor = (/=)
f 0 = IntMap.singleton 0 True
f lit =
if litPolarity lit
then IntMap.singleton lit True
else IntMap.fromList [(litVar lit, True), (0, True)]
instantiateXORClause :: forall m. Monad m => (Lit -> m LBool) -> XORClause -> m XORClause
instantiateXORClause evalLitM (ls,b) = loop [] b ls
where
loop :: [Lit] -> Bool -> [Lit] -> m XORClause
loop lhs !rhs [] = return (lhs, rhs)
loop lhs !rhs (l:ls) = do
val <- evalLitM l
if val==lTrue then
loop lhs (not rhs) ls
else if val==lFalse then
loop lhs rhs ls
else
loop (l : lhs) rhs ls
evalXORClause :: IModel m => m -> XORClause -> Bool
evalXORClause m (lits, rhs) = foldl' xor False (map f lits) == rhs
where
xor = (/=)
f 0 = True
f lit = evalLit m lit