{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeSynonymInstances #-} {-# OPTIONS -fno-warn-orphans #-} module Data.Logic.Instances.SatSolver where import Control.Monad.State (get, put) import Control.Monad.Trans (lift) import Data.Boolean.SatSolver import Data.Generics (Data, Typeable) import qualified Data.Set.Extra as S import Data.Logic.Classes.FirstOrder (FirstOrderFormula(..)) import Data.Logic.Classes.ClauseNormalForm (ClauseNormalFormula(..)) import qualified Data.Logic.Classes.Literal as N import Data.Logic.Classes.Negatable (Negatable(..)) import Data.Logic.Normal.Clause (clauseNormalForm) import Data.Logic.Normal.Skolem (LiteralMapT, NormalT') import qualified Data.Map as M instance Ord Literal where compare (Neg _) (Pos _) = LT compare (Pos _) (Neg _) = GT compare (Pos m) (Pos n) = compare m n compare (Neg m) (Neg n) = compare m n instance Negatable Literal where (.~.) (Pos n) = Neg n (.~.) (Neg n) = Pos n negated (Pos _) = False negated (Neg _) = True deriving instance Data Literal deriving instance Typeable Literal instance ClauseNormalFormula CNF Literal where clauses = S.fromList . map S.fromList makeCNF = map S.toList . S.toList satisfiable cnf = return . not . null $ assertTrue' cnf newSatSolver >>= solve toCNF :: (Monad m, FirstOrderFormula formula term v p f, N.Literal formula term v p f) => formula -> NormalT' formula v term m CNF toCNF f = clauseNormalForm f >>= S.ssMapM (lift . toLiteral) >>= return . makeCNF -- |Convert a [[formula]] to CNF, which means building a map from -- formula to Literal. toLiteral :: forall m lit. (Monad m, Negatable lit, Ord lit) => lit -> LiteralMapT lit m Literal toLiteral f = literalNumber >>= return . if negated f then Neg else Pos where literalNumber :: LiteralMapT lit m Int literalNumber = get >>= \ (count, m) -> case M.lookup f' m of Nothing -> do let m' = M.insert f' count m put (count+1, m') return count Just n -> return n f' :: lit f' = if negated f then (.~.) f else f