mios-1.2.1: SAT/Solver/Mios/ClauseManager.hs
-- | A shrinkable 'VectorFamily' of 'C.Clause'
{-# LANGUAGE
BangPatterns
, DuplicateRecordFields
, FlexibleInstances
, MultiParamTypeClasses
, RecordWildCards
, ViewPatterns
#-}
{-# LANGUAGE Trustworthy #-}
module SAT.Solver.Mios.ClauseManager
(
-- * higher level interface for ClauseVector
ClauseManager (..)
-- -- * vector of clauses
-- , SimpleManager
-- * Manager with an extra Int (used as sort key or blocking literal)
, ClauseExtManager
, pushClauseWithKey
, getKeyVector
, markClause
-- , purifyManager
-- * WatcherList
, WatcherList
, newWatcherList
, getNthWatcher
, garbageCollect
-- , numberOfRegisteredClauses
)
where
import Control.Monad (forM, unless, when)
import qualified Data.IORef as IORef
import qualified Data.List as L
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as MV
import SAT.Solver.Mios.Types
import qualified SAT.Solver.Mios.Clause as C
-- | resizable clause vector
class ClauseManager a where
newManager :: Int -> IO a
numberOfClauses :: a -> IO Int
clearManager :: a -> IO ()
shrinkManager :: a -> Int -> IO ()
getClauseVector :: a -> IO C.ClauseVector
pushClause :: a -> C.Clause -> IO ()
-- removeClause :: a -> C.Clause -> IO ()
-- removeNthClause :: a -> Int -> IO ()
{-
-- | The Clause Container
data SimpleManager = SimpleManager
{
_nActives :: IntSingleton -- number of active clause
, _clauseVector :: IORef.IORef C.ClauseVector -- clause list
}
instance ClauseManager SimpleManager where
{-# SPECIALIZE INLINE newManager :: Int -> IO SimpleManager #-}
newManager initialSize = do
i <- newInt 0
v <- C.newClauseVector initialSize
SimpleManager i <$> IORef.newIORef v
{-# SPECIALIZE INLINE numberOfClauses :: SimpleManager -> IO Int #-}
numberOfClauses SimpleManager{..} = getInt _nActives
{-# SPECIALIZE INLINE clearManager :: SimpleManager -> IO () #-}
clearManager SimpleManager{..} = setInt _nActives 0
{-# SPECIALIZE INLINE shrinkManager :: SimpleManager -> Int -> IO () #-}
shrinkManager SimpleManager{..} k = modifyInt _nActives (subtract k)
{-# SPECIALIZE INLINE getClauseVector :: SimpleManager -> IO C.ClauseVector #-}
getClauseVector SimpleManager{..} = IORef.readIORef _clauseVector
-- | O(1) inserter
{-# SPECIALIZE INLINE pushClause :: SimpleManager -> C.Clause -> IO () #-}
pushClause !SimpleManager{..} !c = do
!n <- getInt _nActives
!v <- IORef.readIORef _clauseVector
if MV.length v - 1 <= n
then do
v' <- MV.unsafeGrow v (max 8 (MV.length v))
-- forM_ [n .. MV.length v' - 1] $ \i -> MV.unsafeWrite v' i C.NullClause
MV.unsafeWrite v' n c
IORef.writeIORef _clauseVector v'
else MV.unsafeWrite v n c
modifyInt _nActives (1 +)
-- | O(1) remove-and-compact function
{-# SPECIALIZE INLINE removeNthClause :: SimpleManager -> Int -> IO () #-}
removeNthClause SimpleManager{..} i = do
!n <- subtract 1 <$> getInt _nActives
!v <- IORef.readIORef _clauseVector
MV.unsafeWrite v i =<< MV.unsafeRead v n
setInt _nActives n
-- | O(n) but lightweight remove-and-compact function
-- __Pre-conditions:__ the clause manager is empty or the clause is stored in it.
{-# SPECIALIZE INLINE removeClause :: SimpleManager -> C.Clause -> IO () #-}
removeClause SimpleManager{..} c = do
-- putStrLn =<< dump "@removeClause| remove " c
-- putStrLn =<< dump "@removeClause| from " m
!n <- subtract 1 <$> getInt _nActives
-- unless (0 <= n) $ error $ "removeClause catches " ++ show n
!v <- IORef.readIORef _clauseVector
let
seekIndex :: Int -> IO Int
seekIndex k = do
c' <- MV.unsafeRead v k
if c' == c then return k else seekIndex $ k + 1
unless (n == -1) $ do
!i <- seekIndex 0
MV.unsafeWrite v i =<< MV.unsafeRead v n
setInt _nActives n
instance VectorFamily SimpleManager C.Clause where
dump mes SimpleManager{..} = do
n <- getInt _nActives
if n == 0
then return $ mes ++ "empty clausemanager"
else do
l <- take n <$> (asList =<< IORef.readIORef _clauseVector)
sts <- mapM (dump ",") (l :: [C.Clause])
return $ mes ++ "[" ++ show n ++ "]" ++ tail (concat sts)
-}
--------------------------------------------------------------------------------
-- | Clause + Blocking Literal
data ClauseExtManager = ClauseExtManager
{
_nActives :: IntSingleton -- number of active clause
, _purged :: BoolSingleton -- whether it needs gc
, _clauseVector :: IORef.IORef C.ClauseVector -- clause list
, _keyVector :: IORef.IORef Vec -- Int list
}
instance ClauseManager ClauseExtManager where
{-# SPECIALIZE INLINE newManager :: Int -> IO ClauseExtManager #-}
newManager initialSize = do
i <- newInt 0
v <- C.newClauseVector initialSize
b <- newVec (MV.length v)
ClauseExtManager i <$> newBool False <*> IORef.newIORef v <*> IORef.newIORef b
{-# SPECIALIZE INLINE numberOfClauses :: ClauseExtManager -> IO Int #-}
numberOfClauses ClauseExtManager{..} = getInt _nActives
{-# SPECIALIZE INLINE clearManager :: ClauseExtManager -> IO () #-}
clearManager ClauseExtManager{..} = setInt _nActives 0
{-# SPECIALIZE INLINE shrinkManager :: ClauseExtManager -> Int -> IO () #-}
shrinkManager ClauseExtManager{..} k = modifyInt _nActives (subtract k)
{-# SPECIALIZE INLINE getClauseVector :: ClauseExtManager -> IO C.ClauseVector #-}
getClauseVector ClauseExtManager{..} = IORef.readIORef _clauseVector
-- | O(1) insertion function
{-# SPECIALIZE INLINE pushClause :: ClauseExtManager -> C.Clause -> IO () #-}
pushClause !ClauseExtManager{..} !c = do
-- checkConsistency m c
!n <- getInt _nActives
!v <- IORef.readIORef _clauseVector
!b <- IORef.readIORef _keyVector
if MV.length v - 1 <= n
then do
let len = max 8 $ MV.length v
v' <- MV.unsafeGrow v len
b' <- vecGrow b len
MV.unsafeWrite v' n c
setNth b' n 0
IORef.writeIORef _clauseVector v'
IORef.writeIORef _keyVector b'
else MV.unsafeWrite v n c >> setNth b n 0
modifyInt _nActives (1 +)
{-
-- | O(n) but lightweight remove-and-compact function
-- __Pre-conditions:__ the clause manager is empty or the clause is stored in it.
{-# SPECIALIZE INLINE removeClause :: ClauseExtManager -> C.Clause -> IO () #-}
removeClause ClauseExtManager{..} c = do
!n <- subtract 1 <$> getInt _nActives
!v <- IORef.readIORef _clauseVector
!b <- IORef.readIORef _keyVector
let
seekIndex :: Int -> IO Int
seekIndex k = do
c' <- MV.unsafeRead v k
if c' == c then return k else seekIndex $ k + 1
unless (n == -1) $ do
!i <- seekIndex 0
MV.unsafeWrite v i =<< MV.unsafeRead v n
setNth b i =<< getNth b n
setInt _nActives n
removeNthClause = error "removeNthClause is not implemented on ClauseExtManager"
-}
-- | sets the expire flag to a clause
{-# INLINE markClause #-}
markClause :: ClauseExtManager -> C.Clause -> IO ()
markClause ClauseExtManager{..} c = do
!n <- getInt _nActives
!v <- IORef.readIORef _clauseVector
let
seekIndex :: Int -> IO ()
seekIndex k = do
c' <- MV.unsafeRead v k
if c' == c then MV.unsafeWrite v k C.NullClause else seekIndex $ k + 1
unless (n == 0) $ do
seekIndex 0
setBool _purged True
{-# INLINE purifyManager #-}
purifyManager :: ClauseExtManager -> IO ()
purifyManager ClauseExtManager{..} = do
diry <- getBool _purged
when diry $ do
n <- getInt _nActives
vec <- IORef.readIORef _clauseVector
keys <- IORef.readIORef _keyVector
let
loop :: Int -> Int -> IO Int
loop ((< n) -> False) n' = return n'
loop i j = do
c <- C.getNthClause vec i
if c /= C.NullClause
then do
unless (i == j) $ do
C.setNthClause vec j c
setNth keys j =<< getNth keys i
loop (i + 1) (j + 1)
else loop (i + 1) j
setInt _nActives =<< loop 0 0
setBool _purged False
-- | returns the associated Int vector
{-# INLINE getKeyVector #-}
getKeyVector :: ClauseExtManager -> IO Vec
getKeyVector ClauseExtManager{..} = IORef.readIORef _keyVector
-- | O(1) inserter
{-# INLINE pushClauseWithKey #-}
pushClauseWithKey :: ClauseExtManager -> C.Clause -> Lit -> IO ()
pushClauseWithKey !ClauseExtManager{..} !c k = do
-- checkConsistency m c
!n <- getInt _nActives
!v <- IORef.readIORef _clauseVector
!b <- IORef.readIORef _keyVector
if MV.length v - 1 <= n
then do
let len = max 8 $ MV.length v
v' <- MV.unsafeGrow v len
b' <- vecGrow b len
MV.unsafeWrite v' n c
setNth b' n k
IORef.writeIORef _clauseVector v'
IORef.writeIORef _keyVector b'
else MV.unsafeWrite v n c >> setNth b n k
modifyInt _nActives (1 +)
instance VectorFamily ClauseExtManager C.Clause where
dump mes ClauseExtManager{..} = do
n <- getInt _nActives
if n == 0
then return $ mes ++ "empty ClauseExtManager"
else do
l <- take n <$> (asList =<< IORef.readIORef _clauseVector)
sts <- mapM (dump ",") (l :: [C.Clause])
return $ mes ++ "[" ++ show n ++ "]" ++ tail (concat sts)
-------------------------------------------------------------------------------- WatcherList
-- | Vector of 'ClauseExtManager'
type WatcherList = V.Vector ClauseExtManager
-- | /n/ is the number of 'Var', /m/ is default size of each watcher list
-- | For /n/ vars, we need [0 .. 2 + 2 * n - 1] slots, namely /2 * (n + 1)/-length vector
newWatcherList :: Int -> Int -> IO WatcherList
newWatcherList n m = V.fromList <$> forM [0 .. int2lit (negate n) + 1] (\_ -> newManager m)
-- | returns the watcher List :: "ClauseManager" for "Literal" /l/
{-# INLINE getNthWatcher #-}
getNthWatcher :: WatcherList -> Lit-> ClauseExtManager
getNthWatcher = V.unsafeIndex
instance VectorFamily WatcherList C.Clause where
dump mes wl = (mes ++) . L.concat <$> forM [1 .. V.length wl - 1] (\i -> dump ("\n" ++ show (lit2int i) ++ "' watchers:") (getNthWatcher wl i))
-- | purges all expirable clauses in 'WatcherList'
{-# INLINE garbageCollect #-}
garbageCollect :: WatcherList -> IO ()
garbageCollect wm = V.mapM_ purifyManager wm
numberOfRegisteredClauses :: WatcherList -> IO Int
numberOfRegisteredClauses ws = sum <$> V.mapM numberOfClauses ws
{-
-------------------------------------------------------------------------------- debugging stuff
checkConsistency :: ClauseManager a => a -> C.Clause -> IO ()
checkConsistency manager c = do
nc <- numberOfClauses manager
vec <- getClauseVector manager
let
loop :: Int -> IO ()
loop i = do
when (i < nc) $ do
c' <- MV.unsafeRead vec i
when (c' == c) $ error "insert a clause to a ClauseMananger twice"
loop $ i + 1
loop 0
checkClauseOrder :: ClauseManager a => a -> IO ()
checkClauseOrder manager = do
putStr "checking..."
nc <- numberOfClauses manager
vec <- getClauseVector manager
let
nthActivity :: Int -> IO Double
nthActivity i = getDouble . C.activity =<< MV.unsafeRead vec i
report :: Int -> Int -> IO ()
report i j = (putStr . (++ ", ") . show =<< nthActivity i) >> when (i < j) (report (i + 1) j)
loop :: Int -> Double -> IO ()
loop i v = do
when (i < nc) $ do
c <- MV.unsafeRead vec i
a <- getDouble (C.activity c)
when (c == C.NullClause) $ error "null is included"
when (v < a) $ report 0 i >> error ("unsorted clause vector: " ++ show (nc, i))
loop (i + 1) a
loop 0 =<< nthActivity 0
putStrLn "done"
-}