packages feed

satplus-0.1.0.0: SAT/Bool.hs

{-|
Module      : SAT.Bool
Description : Basic boolean functions and constraints
-}
module SAT.Bool where

import SAT
import SAT.Util( unconditionally, usort )
import Data.List( partition, sort )

------------------------------------------------------------------------------
-- * Boolean functions

-- | Return a literal representing the conjunction (''big-and'') of the
-- literals in the argument list. This function may create new literals and
-- add constraints, but tries to avoid doing this when possible.
andl :: Solver -> [Lit] -> IO Lit
andl s xs
  | false `elem` xs = return false
  | xAndNegX        = return false
  | otherwise       = case filter (/= true) xs' of
                        []   -> do return true
                        [x]  -> do return x
                        xs'' -> do y <- newLit s
                                   sequence_ [ addClause s [neg y, x]
                                             | x <- xs''
                                             ]
                                   addClause s (y : map neg xs'')
                                   return y
 where
  xs'       = usort xs
  (xs0,xs1) = partition pos xs'
  xAndNegX  = xs0 `overlap` sort (map neg xs1)

  []     `overlap` _      = False
  _      `overlap` []     = False
  (x:xs) `overlap` (y:ys) =
    case x `compare` y of
      LT -> xs `overlap` (y:ys)
      EQ -> True
      GT -> (x:xs) `overlap` ys

-- | Return a literal representing the disjunction (''big-or'') of the
-- literals in the argument list. This function may create new literals and
-- add constraints, but tries to avoid doing this when possible.
orl :: Solver -> [Lit] -> IO Lit
orl s = fmap neg . andl s . map neg

-- | Return a literal representing the parity (''big-xor'') of the literals
-- in the argument list. This function may create new literals and add
-- constraints, but tries to avoid doing this when possible.
xorl :: Solver -> [Lit] -> IO Lit
xorl s xs =
  case xs'' of
    []  -> do return (bool p)
    [x] -> do return (if p then neg x else x)
    _   -> do y <- newLit s
              parity s (y : xs'') p
              return y
 where
  xs'       = filter (/= false) (sort xs)
  (xs0,xs1) = partition pos (filter (/= true) xs')
  (p,xs'')  = go (odd (length (filter (== true) xs'))) [] xs0 (sort (map neg xs1))

  go p ys []        []        = (p, ys)
  go p ys (x:y:xs0) xs1       | x == y = go p ys xs0 xs1
  go p ys xs0       (x:y:xs1) | x == y = go p ys xs0 xs1
  go p ys []        (x1:xs1)  = go p (neg x1:ys) [] xs1
  go p ys (x0:xs0)  []        = go p (x0:ys) xs0 []
  go p ys (x0:xs0)  (x1:xs1)  =
    case x0 `compare` x1 of
      LT -> go p (x0:ys) xs0 (x1:xs1)
      EQ -> go (not p) ys xs0 xs1
      GT -> go p (neg x1:ys) (x0:xs0) xs1

-- | Return a literal representing the implication @a ==> b@ between two
-- literals @a@ and @b@.
implies :: Solver -> Lit -> Lit -> IO Lit
implies s x y = orl s [neg x, y]

-- | Return a literal representing the equivalence @a \<=\> b@ of two
-- literals @a@ and @b@.
equiv :: Solver -> Lit -> Lit -> IO Lit
equiv s x y = xorl s [neg x, y]

------------------------------------------------------------------------------
-- * Boolean constraints

-- | Add clauses that constrain the list of literals to have at most one
-- element to be True. See also 'atMostOneOr'.
atMostOne :: Solver -> [Lit] -> IO ()
atMostOne = unconditionally atMostOneOr

-- | Add clauses that constrain the list of literals to have the specified
-- parity, as a Bool. The parity of a list says whether the number of True
-- literals is even (False) or odd (True). See also 'parityOr'.
parity :: Solver -> [Lit] -> Bool -> IO ()
parity = unconditionally parityOr

------------------------------------------------------------------------------
-- * Boolean constraints with prefix

-- | Add clauses that constrain the list of literals to have at most one
-- element to be True, under the presence of a /disjunctive prefix/.
-- (See 'SAT.Util.unconditionally' for what /prefix/ means. This function
-- without prefix is called 'atMostOne'.)
atMostOneOr :: Solver -> [Lit] {- ^ prefix -}
                      -> [Lit] {- ^ literal set -}
                      -> IO ()
atMostOneOr s pre xs = go (length xs) xs
 where
  go n xs | n <= 5 =
    do sequence_ [ addClause s (pre ++ [neg x, neg y]) | (x,y) <- pairs xs ]
   where
    pairs (x:xs) = [ (x,y) | y <- xs ] ++ pairs xs
    pairs []     = []

  go n xs =
    do x <- newLit s
       go (k+1)   (x     : take k xs)
       go (n-k+1) (neg x : drop k xs)
   where
    k = n `div` 2

-- | Add clauses that constrain the list of literals to have the specified
-- parity, as a Bool, under the presence of a /disjunctive prefix/.
-- (See 'SAT.Util.unconditionally' for what /prefix/ means. This function
-- without prefix is called 'parity'.)
parityOr :: Solver -> [Lit] {- ^ prefix -}
                   -> [Lit] {- ^ literal set -}
                   -> Bool {- ^ parity -}
                   -> IO ()
parityOr s pre xs p = go pre (length xs) xs p
 where
  go pre _ [] False =
    do return ()

  go pre _ [] True =
    do addClause s pre

  go pre n (x:xs) p | n <= 5 =
    do go (neg x : pre) (n-1) xs (not p)
       go (x     : pre) (n-1) xs p

  go pre n xs p =
    do x <- newLit s
       go pre (k+1) (x : take k xs) p
       go pre (n-k+1) ((if p then neg x else x) : drop k xs) p
   where
    k = n `div` 2

------------------------------------------------------------------------------