module ToySolver.SAT.CAMUS
( MUS
, MCS
, Options (..)
, defaultOptions
, allMCSAssumptions
, allMUSAssumptions
, enumMCSAssumptions
) where
import Control.Monad
import Data.Array.IArray
import qualified Data.IntSet as IS
import Data.List
import Data.IORef
import qualified ToySolver.HittingSet as HittingSet
import qualified ToySolver.SAT as SAT
import ToySolver.SAT.Types
type MUS = [Lit]
type MCS = [Lit]
data Options
= Options
{ optLogger :: String -> IO ()
, optCallback :: MCS -> IO ()
, optMCSCandidates :: [MCS]
}
defaultOptions :: Options
defaultOptions =
Options
{ optLogger = \_ -> return ()
, optCallback = \_ -> return ()
, optMCSCandidates = []
}
enumMCSAssumptions :: SAT.Solver -> [Lit] -> Options -> IO ()
enumMCSAssumptions solver sels opt = do
candRef <- newIORef [(IS.size s, s) | mcs <- optMCSCandidates opt, let s = IS.fromList mcs]
loop candRef 1
where
log :: String -> IO ()
log = optLogger opt
mcsFound :: MCS -> IO ()
mcsFound mcs = do
optCallback opt mcs
SAT.addClause solver mcs
loop :: IORef [(Int, LitSet)] -> Int -> IO ()
loop candRef k = do
log $ "CAMUS: k = " ++ show k
cand <- readIORef candRef
ret <- if not (null cand) then return True else SAT.solve solver
when ret $ do
forM_ cand $ \(size,cs) -> do
when (size == k) $ do
mcsFound (IS.toList cs)
writeIORef candRef [(size,cs) | (size,cs) <- cand, size /= k]
vk <- SAT.newVar solver
SAT.addPBAtMostSoft solver vk [(1,sel) | sel <- sels] (fromIntegral k)
let loop2 = do
ret2 <- SAT.solveWith solver [vk]
when ret2 $ do
m <- SAT.model solver
let mcs = [sel | sel <- sels, not (evalLit m sel)]
mcs' = IS.fromList mcs
mcsFound mcs
modifyIORef candRef (filter (\(_,cs) -> not (mcs' `IS.isSubsetOf` cs)))
loop2
loop2
SAT.addClause solver [vk]
loop candRef (k+1)
allMCSAssumptions :: SAT.Solver -> [Lit] -> Options -> IO [MCS]
allMCSAssumptions solver sels opt = do
ref <- newIORef []
let opt2 =
opt
{ optCallback = \mcs -> do
modifyIORef ref (mcs:)
optCallback opt mcs
}
enumMCSAssumptions solver sels opt2
readIORef ref
allMUSAssumptions :: SAT.Solver -> [Lit] -> Options -> IO [MUS]
allMUSAssumptions solver sels opt = do
log "CAMUS: MCS enumeration begins"
mcses <- allMCSAssumptions solver sels opt
log "CAMUS: MCS enumeration done"
return $ HittingSet.minimalHittingSets mcses
where
log :: String -> IO ()
log = optLogger opt