packages feed

toysolver-0.9.0: src/ToySolver/SAT/Formula.hs

{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  ToySolver.SAT.Formula
-- Copyright   :  (c) Masahiro Sakai 2012-2021
-- License     :  BSD-style
--
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  provisional
-- Portability :  non-portable
--
-----------------------------------------------------------------------------
module ToySolver.SAT.Formula
  (
  -- * Boolean formula type
    Formula (Atom, And, Or, Not, Equiv, Imply, ITE)
  , fold
  , evalFormula
  , simplify
  ) where

import Control.Monad
import Control.Monad.ST
import qualified Data.Aeson as J
import Data.Aeson ((.=))
import Data.Hashable
import qualified Data.HashTable.Class as H
import qualified Data.HashTable.ST.Cuckoo as C
import Data.Interned
import GHC.Generics
import ToySolver.Data.Boolean
import qualified ToySolver.Data.BoolExpr as BoolExpr
import qualified ToySolver.SAT.Types as SAT
import ToySolver.SAT.Internal.JSON

-- Should this module be merged into ToySolver.SAT.Types module?

-- ------------------------------------------------------------------------

-- | Arbitrary formula not restricted to CNF
data Formula = Formula {-# UNPACK #-} !Id UFormula

instance Eq Formula where
  Formula i _ == Formula j _ = i == j

instance Show Formula where
  showsPrec d x = showsPrec d (toBoolExpr x)

instance Read Formula where
  readsPrec d s = [(fromBoolExpr b, rest) | (b, rest) <- readsPrec d s]

instance Hashable Formula where
  hashWithSalt s (Formula i _) = hashWithSalt s i

data UFormula
  = UAtom SAT.Lit
  | UAnd [Formula]
  | UOr [Formula]
  | UNot Formula
  | UImply Formula Formula
  | UEquiv Formula Formula
  | UITE Formula Formula Formula

instance Interned Formula where
  type Uninterned Formula = UFormula
  data Description Formula
    = DAtom SAT.Lit
    | DAnd [Id]
    | DOr [Id]
    | DNot Id
    | DImply Id Id
    | DEquiv Id Id
    | DITE Id Id Id
    deriving (Eq, Generic)
  describe (UAtom a) = DAtom a
  describe (UAnd xs) = DAnd [i | Formula i _ <- xs]
  describe (UOr xs) = DOr [i | Formula i _ <- xs]
  describe (UNot (Formula i _)) = DNot i
  describe (UImply (Formula i _) (Formula j _)) = DImply i j
  describe (UEquiv (Formula i _) (Formula j _)) = DEquiv i j
  describe (UITE (Formula i _) (Formula j _) (Formula k _)) = DITE i j k
  identify = Formula
  cache = formulaCache

instance Hashable (Description Formula)

instance Uninternable Formula where
  unintern (Formula _ uformula) = uformula

formulaCache :: Cache Formula
formulaCache = mkCache
{-# NOINLINE formulaCache #-}

-- ------------------------------------------------------------------------

pattern Atom :: SAT.Lit -> Formula
pattern Atom l <- (unintern -> UAtom l) where
  Atom l = intern (UAtom l)

pattern Not :: Formula -> Formula
pattern Not p <- (unintern -> UNot p) where
  Not p = intern (UNot p)

pattern And :: [Formula] -> Formula
pattern And ps <- (unintern -> UAnd ps) where
  And ps = intern (UAnd ps)

pattern Or :: [Formula] -> Formula
pattern Or ps <- (unintern -> UOr ps) where
  Or ps = intern (UOr ps)

pattern Equiv :: Formula -> Formula -> Formula
pattern Equiv p q <- (unintern -> UEquiv p q) where
  Equiv p q = intern (UEquiv p q)

pattern Imply :: Formula -> Formula -> Formula
pattern Imply p q <- (unintern -> UImply p q) where
  Imply p q = intern (UImply p q)

pattern ITE :: Formula -> Formula -> Formula -> Formula
pattern ITE p q r <- (unintern -> UITE p q r) where
  ITE p q r = intern (UITE p q r)

{-# COMPLETE Atom, Not, And, Or, Equiv, Imply, ITE #-}

-- ------------------------------------------------------------------------

instance Complement Formula where
  notB = intern . UNot

instance MonotoneBoolean Formula where
  andB = intern . UAnd
  orB  = intern . UOr

instance IfThenElse Formula Formula where
  ite c t e = intern (UITE c t e)

instance Boolean Formula where
  (.=>.) p q = intern (UImply p q)
  (.<=>.) p q = intern (UEquiv p q)

-- ------------------------------------------------------------------------

fold :: Boolean b => (SAT.Lit -> b) -> Formula -> b
fold f formula = runST $ do
  h <- C.newSized 256
  let g x = do
        m <- H.lookup h x
        case m of
          Just y -> return y
          Nothing -> do
            y <-
              case x of
                Atom lit -> return (f lit)
                And ps -> andB <$> mapM g ps
                Or ps -> orB <$> mapM g ps
                Not p -> notB <$> g p
                Imply p q -> (.=>.) <$> g p <*> g q
                Equiv p q -> (.<=>.) <$> g p <*> g q
                ITE p q r -> ite <$> g p <*> g q <*> g r
            H.insert h x y
            return y
  g formula

evalFormula :: SAT.IModel m => m -> Formula -> Bool
evalFormula m = fold (SAT.evalLit m)

toBoolExpr :: Formula -> BoolExpr.BoolExpr SAT.Lit
toBoolExpr = fold BoolExpr.Atom

fromBoolExpr :: BoolExpr.BoolExpr SAT.Lit -> Formula
fromBoolExpr = BoolExpr.fold Atom

-- ------------------------------------------------------------------------

simplify :: Formula -> Formula
simplify = runSimplify . fold (Simplify . Atom)

newtype Simplify = Simplify{ runSimplify :: Formula }

instance Complement Simplify where
  notB (Simplify (Not x)) = Simplify x
  notB (Simplify x) = Simplify (Not x)

instance MonotoneBoolean (Simplify) where
  orB xs
    | any isTrue ys = Simplify true
    | otherwise = Simplify $ Or ys
    where
      ys = concat [f x | Simplify x <- xs]
      f (Or zs) = zs
      f z = [z]
  andB xs
    | any isFalse ys = Simplify false
    | otherwise = Simplify $ And ys
    where
      ys = concat [f x | Simplify x <- xs]
      f (And zs) = zs
      f z = [z]

instance IfThenElse Simplify Simplify where
  ite (Simplify c) (Simplify t) (Simplify e)
    | isTrue c  = Simplify t
    | isFalse c = Simplify e
    | otherwise = Simplify (ITE c t e)

instance Boolean Simplify where
  Simplify x .=>. Simplify y
    | isFalse x = true
    | isTrue y  = true
    | isTrue x  = Simplify y
    | isFalse y = notB (Simplify x)
    | otherwise = Simplify (x .=>. y)

isTrue :: Formula -> Bool
isTrue (And []) = True
isTrue _ = False

isFalse :: Formula -> Bool
isFalse (Or []) = True
isFalse _ = False

-- ------------------------------------------------------------------------

newtype JSON = JSON{ getJSON :: J.Value }

instance Complement JSON where
  notB (JSON x) = JSON $ jNot x

instance MonotoneBoolean JSON where
  andB xs = JSON $ J.object
    [ "type" .= ("operator" :: J.Value)
    , "name" .= ("and" :: J.Value)
    , "operands" .= [x | JSON x <- xs]
    ]
  orB xs = JSON $ J.object
    [ "type" .= ("operator" :: J.Value)
    , "name" .= ("or" :: J.Value)
    , "operands" .= [x | JSON x <- xs]
    ]

instance IfThenElse JSON JSON where
  ite (JSON c) (JSON t) (JSON e) = JSON $ J.object
    [ "type" .= ("operator" :: J.Value)
    , "name" .= ("ite" :: J.Value)
    , "operands" .= [c, t, e]
    ]

instance Boolean JSON where
  (.=>.) (JSON p) (JSON q) = JSON $ J.object
    [ "type" .= ("operator" :: J.Value)
    , "name" .= ("=>" :: J.Value)
    , "operands" .= [p, q]
    ]
  (.<=>.) (JSON p) (JSON q) = JSON $ J.object
    [ "type" .= ("operator" :: J.Value)
    , "name" .= ("<=>" :: J.Value)
    , "operands" .= [p, q]
    ]

instance J.ToJSON Formula where
  toJSON = getJSON . fold (JSON . jLit)

instance J.FromJSON Formula where
  parseJSON x = msum
    [ Atom <$> parseVar x
    , withNot (\y -> Not <$> J.parseJSON y) x
    , withAnd (\xs -> And <$> mapM J.parseJSON xs) x
    , withOr (\xs -> Or <$> mapM J.parseJSON xs) x
    , withITE (\c t e -> ITE <$> J.parseJSON c <*> J.parseJSON t <*> J.parseJSON e) x
    , withImply (\a b -> Imply <$> J.parseJSON a <*> J.parseJSON b) x
    , withEquiv (\a b -> Equiv <$> J.parseJSON a <*> J.parseJSON b) x
    ]

-- ------------------------------------------------------------------------