module ToySolver.SAT.PBO.Context
( Context (..)
, getBestValue
, getBestModel
, isOptimum
, isFinished
, getSearchUpperBound
, setFinished
, SimpleContext
, newSimpleContext
, setOnUpdateBestSolution
, setOnUpdateLowerBound
, setLogger
, Normalized
, normalize
) where
import Control.Monad
import Control.Concurrent.STM
import Data.IORef
import Data.Maybe
import qualified ToySolver.SAT.Types as SAT
class Context a where
getObjectiveFunction :: a -> SAT.PBLinSum
isUnsat :: a -> STM Bool
getBestSolution :: a -> STM (Maybe (SAT.Model, Integer))
getLowerBound :: a -> STM Integer
setUnsat :: a -> IO ()
addSolution :: a -> SAT.Model -> IO ()
addLowerBound :: a -> Integer -> IO ()
logMessage :: a -> String -> IO ()
getBestValue :: Context a => a -> STM (Maybe Integer)
getBestValue cxt = liftM (fmap snd) $ getBestSolution cxt
getBestModel :: Context a => a -> STM (Maybe SAT.Model)
getBestModel cxt = liftM (fmap fst) $ getBestSolution cxt
isOptimum :: Context a => a -> STM Bool
isOptimum cxt = do
ub <- getBestValue cxt
lb <- getLowerBound cxt
return $ ub == Just lb
isFinished :: Context a => a -> STM Bool
isFinished cxt = do
b1 <- isUnsat cxt
b2 <- isOptimum cxt
return $ b1 || b2
getSearchUpperBound :: Context a => a -> STM Integer
getSearchUpperBound ctx = do
ret <- getBestValue ctx
case ret of
Just val -> return $ val 1
Nothing -> return $ SAT.pbUpperBound $ getObjectiveFunction ctx
setFinished :: Context a => a -> IO ()
setFinished cxt = do
join $ atomically $ do
ret <- getBestValue cxt
case ret of
Just val -> return $ addLowerBound cxt val
Nothing -> return $ setUnsat cxt
data SimpleContext
= SimpleContext
{ scGetObjectiveFunction :: SAT.PBLinSum
, scUnsatRef :: TVar Bool
, scBestSolutionRef :: TVar (Maybe (SAT.Model, Integer))
, scLowerBoundRef :: TVar Integer
, scOnUpdateBestSolutionRef :: IORef (SAT.Model -> Integer -> IO ())
, scOnUpdateLowerBoundRef :: IORef (Integer -> IO ())
, scLoggerRef :: IORef (String -> IO ())
}
instance Context SimpleContext where
getObjectiveFunction = scGetObjectiveFunction
isUnsat sc = readTVar (scUnsatRef sc)
getBestSolution sc = readTVar (scBestSolutionRef sc)
getLowerBound sc = readTVar (scLowerBoundRef sc)
setUnsat sc = do
atomically $ do
sol <- getBestSolution sc
unless (isNothing sol) $ error "setUnsat: already has a solution"
writeTVar (scUnsatRef sc) True
addSolution sc m = do
let !val = SAT.evalPBLinSum m (getObjectiveFunction sc)
join $ atomically $ do
unsat <- isUnsat sc
when unsat $ error "addSolution: already marked as unsatisfiable"
sol0 <- getBestValue sc
case sol0 of
Just val0 | val0 <= val -> return $ return ()
_ -> do
writeTVar (scBestSolutionRef sc) (Just (m, val))
return $ do
h <- readIORef (scOnUpdateBestSolutionRef sc)
h m val
addLowerBound sc lb = do
join $ atomically $ do
lb0 <- getLowerBound sc
if lb <= lb0 then
return $ return ()
else do
writeTVar (scLowerBoundRef sc) lb
return $ do
h <- readIORef (scOnUpdateLowerBoundRef sc)
h lb
logMessage sc msg = do
h <- readIORef (scLoggerRef sc)
h msg
newSimpleContext :: SAT.PBLinSum -> IO SimpleContext
newSimpleContext obj = do
unsatRef <- newTVarIO False
bestsolRef <- newTVarIO Nothing
lbRef <- newTVarIO $! SAT.pbLowerBound obj
onUpdateBestSolRef <- newIORef $ \_ _ -> return ()
onUpdateLBRef <- newIORef $ \_ -> return ()
loggerRef <- newIORef $ \_ -> return ()
return $
SimpleContext
{ scGetObjectiveFunction = obj
, scUnsatRef = unsatRef
, scBestSolutionRef = bestsolRef
, scLowerBoundRef = lbRef
, scOnUpdateBestSolutionRef = onUpdateBestSolRef
, scOnUpdateLowerBoundRef = onUpdateLBRef
, scLoggerRef = loggerRef
}
setOnUpdateBestSolution :: SimpleContext -> (SAT.Model -> Integer -> IO ()) -> IO ()
setOnUpdateBestSolution sc h = writeIORef (scOnUpdateBestSolutionRef sc) h
setOnUpdateLowerBound :: SimpleContext -> (Integer -> IO ()) -> IO ()
setOnUpdateLowerBound sc h = writeIORef (scOnUpdateLowerBoundRef sc) h
setLogger :: SimpleContext -> (String -> IO ()) -> IO ()
setLogger sc h = writeIORef (scLoggerRef sc) h
data Normalized a
= Normalized
{ nBase :: a
, nObjectiveFunction :: SAT.PBLinSum
, nOffset :: Integer
}
instance Context a => Context (Normalized a) where
getObjectiveFunction = nObjectiveFunction
isUnsat cxt = isUnsat (nBase cxt)
getBestSolution cxt = do
sol <- getBestSolution (nBase cxt)
case sol of
Nothing -> return Nothing
Just (m, val) -> return $ Just (m, val nOffset cxt)
getLowerBound cxt = do
lb <- getLowerBound (nBase cxt)
return $ lb nOffset cxt
setUnsat cxt = setUnsat $ nBase cxt
addSolution cxt m = do
addSolution (nBase cxt) m
addLowerBound cxt lb = do
addLowerBound (nBase cxt) (lb + nOffset cxt)
logMessage cxt msg = logMessage (nBase cxt) msg
normalize :: Context a => a -> Normalized a
normalize cxt = Normalized cxt obj' offset
where
obj = getObjectiveFunction cxt
(obj',offset) = SAT.normalizePBLinSum (obj, 0)