clingo-0.2.0.0: examples/Propagator.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PatternGuards #-}
module Main where
import Control.Concurrent.MVar
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
import Clingo.Control
import Clingo.Symbol
import Clingo.Solving
import Clingo.Model
import Clingo.Propagation
import Clingo.Inspection.Symbolic
import Data.Map (Map)
import Data.Maybe
import Data.IntMap (IntMap)
import qualified Data.Map as M
import qualified Data.IntMap as I
import Text.Printf
import qualified Data.Text.IO as T
newtype Hole = Hole Int
deriving (Eq, Show, Ord)
data PigeonData s = PigeonData
{ placements :: Map (Literal s) Hole
, assignments :: IntMap (Map Hole (Literal s))
}
deriving (Show)
assignHole :: Integer -> Hole -> Literal s -> PigeonData s -> PigeonData s
assignHole tid hole lit pd = pd
{ assignments =
I.adjust (M.insert hole lit) (fromIntegral tid) (assignments pd) }
unassignHole :: Integer -> Literal s -> Hole -> PigeonData s -> PigeonData s
unassignHole tid lit hole pd = pd
{ assignments = I.adjust go (fromIntegral tid) (assignments pd) }
where go m | Just l <- M.lookup hole m
, l == lit = M.delete hole m
| otherwise = m
forceMVar :: MonadIO m => MVar a -> a -> m ()
forceMVar mvar x = liftIO $ do
_ <- tryTakeMVar mvar
putMVar mvar x
onModel :: Model s -> IOSym s Continue
onModel m = do
syms <- map prettySymbol
<$> modelSymbols m (selectNone { selectShown = True })
liftIO (putStr "Model: " >> print syms)
return Continue
main :: IO ()
main = withDefaultClingo $ do
addProgram "pigeon" ["h","p"]
"1 { place(P,H) : H = 1..h } 1 :- P = 1..p."
holes <- createNumber 8
pigeons <- createNumber 9
ground [Part "pigeon" [holes, pigeons]] Nothing
propState <- liftIO newEmptyMVar
registerPropagator False (pigeonator propState)
solveRet <- withSolver [] getResult
liftIO (print solveRet)
-- TODO: Propagator with symbol inspection methods?
pigeonator :: MVar (PigeonData s) -> Propagator s
pigeonator mvar = emptyPropagator
{ propInit = Just $ do
-- obtain place/2 atoms
placeSig <- createSignature "place" 2 True
watches <- propSymbolicAtoms
>>= \sa -> fromSymbolicAtomsSig sa placeSig id
-- watch and create initial placements
mapM_ ((addWatch =<<) . solverLiteral . literal) watches
ps <- forM watches $ \atom -> do
let hole = do
arg <- symbolGetArg (symbol atom) 1
Hole . fromIntegral <$> symbolNumber arg
lit <- solverLiteral . literal $ atom
return $ (,) <$> pure lit <*> hole
-- initialize the PigeonData structure for all threads
threads <- fromIntegral <$> countThreads
forceMVar mvar $
let xs = zip (take threads [0..]) (repeat M.empty)
in PigeonData (M.fromList . catMaybes $ ps) (I.fromList xs)
, propPropagate = Just $ \changes -> do
-- get previous assignment
thread <- getThreadId
-- apply and check assignments done by solver
forM_ changes $ \lit -> do
state <- liftIO (readMVar mvar)
let Just holes = fromIntegral thread `I.lookup` assignments state
Just hole = lit `M.lookup` placements state
prev = hole `M.lookup` holes
case prev of
Nothing -> liftIO $
modifyMVar_ mvar (return . assignHole thread hole lit)
Just p -> do
let c = Clause (map negateLiteral [lit, p]) ClauseLearnt
addClause c
propagate
, propUndo = Just $ \changes -> do
thread <- getThreadId
state <- liftIO (readMVar mvar)
let cs' = mapMaybe
(\l -> (,) <$> pure l <*> M.lookup l (placements state))
changes
liftIO $
modifyMVar_ mvar $
return . flip (foldr (uncurry (unassignHole thread))) cs'
}