module ToySolver.SAT.MUS.CAMUS
( module ToySolver.SAT.MUS.Types
, Options (..)
, defaultOptions
, allMCSAssumptions
, allMUSAssumptions
, enumMCSAssumptions
, camus
) where
import Control.Monad
import Data.Array.IArray
import Data.Default.Class
import qualified Data.IntSet as IS
import Data.List
import Data.IORef
import Data.Set (Set)
import qualified Data.Set as Set
import qualified ToySolver.Combinatorial.HittingSet.Simple as HittingSet
import qualified ToySolver.SAT as SAT
import ToySolver.SAT.Types
import ToySolver.SAT.MUS.Types
data Options
= Options
{ optLogger :: String -> IO ()
, optOnMCSFound :: MCS -> IO ()
, optOnMUSFound :: MUS -> IO ()
, optKnownMCSes :: [MCS]
, optKnownMUSes :: [MUS]
, optKnownCSes :: [CS]
}
instance Default Options where
def = defaultOptions
defaultOptions :: Options
defaultOptions =
Options
{ optLogger = \_ -> return ()
, optOnMCSFound = \_ -> return ()
, optOnMUSFound = \_ -> return ()
, optKnownMCSes = []
, optKnownMUSes = []
, optKnownCSes = []
}
enumMCSAssumptions :: SAT.Solver -> [Lit] -> Options -> IO ()
enumMCSAssumptions solver sels opt = do
candRef <- newIORef [(IS.size cs, cs) | cs <- optKnownCSes opt]
loop candRef 1
where
log :: String -> IO ()
log = optLogger opt
mcsFound :: MCS -> IO ()
mcsFound mcs = do
optOnMCSFound opt mcs
SAT.addClause solver (IS.toList 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 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.getModel solver
let mcs = IS.fromList [sel | sel <- sels, not (evalLit m sel)]
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
{ optOnMCSFound = \mcs -> do
modifyIORef ref (mcs:)
optOnMCSFound opt mcs
}
enumMCSAssumptions solver sels opt2
readIORef ref
allMUSAssumptions :: SAT.Solver -> [Lit] -> Options -> IO [MUS]
allMUSAssumptions solver sels opt = do
(muses, _mcses) <- camus solver sels opt
return $ muses
camus :: SAT.Solver -> [Lit] -> Options -> IO ([MUS], [MCS])
camus solver sels opt = do
log "CAMUS: MCS enumeration begins"
mcses <- allMCSAssumptions solver sels opt
log "CAMUS: MCS enumeration done"
let muses = HittingSet.enumMinimalHittingSets $ Set.fromList mcses
mapM_ (optOnMUSFound opt) muses
return (muses, mcses)
where
log :: String -> IO ()
log = optLogger opt