packages feed

logic-classes-1.7: Data/Boolean.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# OPTIONS -fno-warn-incomplete-patterns #-}
-- |
-- Module      : Data.Boolean
-- Copyright   : Sebastian Fischer
-- License     : BSD3
--
-- Maintainer  : Sebastian Fischer (sebf@informatik.uni-kiel.de)
-- Stability   : experimental
-- Portability : portable
--
-- This library provides a representation of boolean formulas that is
-- used by the solver in "Data.Boolean.SatSolver".
--
-- We also define a function to simplify formulas, a type for
-- conjunctive normalforms, and a function that creates them from
-- boolean formulas.
--
module Data.Boolean (

  Boolean(..),

  Literal(..), literalVar, invLiteral, isPositiveLiteral,

  CNF, Clause, booleanToCNF

  ) where

import Control.Monad ( guard, liftM )
import Data.Generics (Data, Typeable)
import qualified Data.IntMap as IM
import Data.Logic.ATP.Formulas (IsAtom, IsFormula(..))
import Data.Logic.ATP.Lit (IsLiteral(..))
import Data.Logic.ATP.Prop (IsPropositional(..), JustPropositional)
import Data.Logic.ATP.Pretty (HasFixity(..), Pretty(pPrint), text)
import Data.Maybe ( mapMaybe )

-- | Boolean formulas are represented as values of type @Boolean@.
--
data Boolean
  -- | Variables are labeled with an @Int@,
  = Var Int
  -- | @Yes@ represents /true/,
  | Yes
  -- | @No@ represents /false/,
  | No
  -- | @Not@ constructs negated formulas,
  | Not Boolean
  -- | and finally we provide conjunction
  | Boolean :&&: Boolean
  -- | and disjunction of boolean formulas.
  | Boolean :||: Boolean
 deriving Show

-- | Literals are variables that occur either positively or negatively.
--
data Literal = Pos Int | Neg Int deriving (Eq, Show)

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

deriving instance Data Literal
deriving instance Typeable Literal

-- | This function returns the name of the variable in a literal.
--
literalVar :: Literal -> Int
literalVar (Pos n) = n
literalVar (Neg n) = n

-- | This function negates a literal.
--
invLiteral :: Literal -> Literal
invLiteral (Pos n) = Neg n
invLiteral (Neg n) = Pos n

-- | This predicate checks whether the given literal is positive.
--
isPositiveLiteral :: Literal -> Bool
isPositiveLiteral (Pos _) = True
isPositiveLiteral _       = False

-- | Conjunctive normalforms are lists of lists of literals.
--
type CNF     = [Clause]
type Clause  = [Literal]

instance JustPropositional CNF

instance HasFixity Int

instance IsAtom Int

instance IsFormula CNF where
    type AtomOf CNF = Int
    atomic = error "FIXME: IsFormula CNF MyAtom"
    overatoms = error "FIXME: IsFormula CNF MyAtom"
    onatoms = error "FIXME: IsFormula CNF MyAtom"
    asBool = error "FIXME: HasBoolean CNF"
    true = error "FIXME: HasBoolean CNF"
    false = error "FIXME: HasBoolean CNF"
instance Pretty Literal where
    pPrint = text . show
instance IsPropositional CNF where
    foldPropositional' = error "FIXME: IsPropositional CNF MyAtom"
    foldCombination = error "FIXME: IsCombinable CNF"
    _ .|. _ = error "FIXME: IsCombinable CNF"
    _ .&. _ = error "FIXME: IsCombinable CNF"
    _ .=>. _ = error "FIXME: IsCombinable CNF"
    _ .<=>. _ = error "FIXME: IsCombinable CNF"
instance HasFixity CNF where
    precedence _ = error "FIXME: HasFixity CNF"
    associativity _ = error "FIXME: HasFixity CNF"
instance IsLiteral CNF where
    foldLiteral' = error "FIXME: IsLiteral CNF MyAtom"
    naiveNegate = error "FIXME: IsNegatable CNF"
    foldNegation = error "FIXME: IsNegatable CNF"

-- |
-- We convert boolean formulas to conjunctive normal form by pushing
-- negations down to variables and repeatedly applying the
-- distributive laws.
--
booleanToCNF :: Boolean -> CNF
booleanToCNF
  = mapMaybe (simpleClause . map literal . disjunction)
  . conjunction
  . asLongAsPossible distribute
  . asLongAsPossible pushNots
  . asLongAsPossible elim
 where
  elim (Not Yes)      = Just No
  elim (Not No)       = Just Yes
  elim (No  :&&: _)   = Just No
  elim (Yes :&&: x)   = Just x
  elim (_   :&&: No)  = Just No
  elim (x   :&&: Yes) = Just x
  elim (Yes :||: _)   = Just Yes
  elim (No  :||: x)   = Just x
  elim (_   :||: Yes) = Just Yes
  elim (x   :||: No)  = Just x
  elim _              = Nothing

  pushNots (Not (Not x))  = Just x
  pushNots (Not (x:&&:y)) = Just (Not x :||: Not y)
  pushNots (Not (x:||:y)) = Just (Not x :&&: Not y)
  pushNots _              = Nothing

  distribute (x:||:(y:&&:z)) = Just ((x:||:y):&&:(x:||:z))
  distribute ((x:&&:y):||:z) = Just ((x:||:z):&&:(y:||:z))
  distribute _               = Nothing

  literal (Var x)       = Pos x
  literal (Not (Var x)) = Neg x


-- private helper functions

-- remove duplicate literals from clauses and drop clauses that
-- contain one literal both positively and negatively.
--
simpleClause :: Clause -> Maybe Clause
simpleClause = liftM (map lit . IM.toList) . foldl add (Just IM.empty)
 where
  lit (x,True)  = Pos x
  lit (x,False) = Neg x

  add mm l = do
    m <- mm
    let x = literalVar l; kind = isPositiveLiteral l
    maybe (Just (IM.insert x kind m))
          (\b -> guard (b==kind) >> Just m)
          (IM.lookup x m)

conjunction :: Boolean -> [Boolean]
conjunction b = flat b []
 where flat Yes      = id
       flat (x:&&:y) = flat x . flat y
       flat x        = (x:)

disjunction :: Boolean -> [Boolean]
disjunction b = flat b []
 where flat No       = id
       flat (x:||:y) = flat x . flat y
       flat x        = (x:)

asLongAsPossible :: (Boolean -> Maybe Boolean) -> Boolean -> Boolean
asLongAsPossible f = everywhere g
 where g x = maybe x (everywhere g) (f x)

everywhere :: (Boolean -> Boolean) -> Boolean -> Boolean
everywhere f = f . atChildren (everywhere f)

atChildren :: (Boolean -> Boolean) -> Boolean -> Boolean
atChildren f (Not x)  = Not (f x)
atChildren f (x:&&:y) = f x :&&: f y
atChildren f (x:||:y) = f x :||: f y
atChildren _ x        = x