ideas-0.5.8: src/Domain/Logic/Formula.hs
-----------------------------------------------------------------------------
-- Copyright 2009, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Domain.Logic.Formula where
import Common.Uniplate (Uniplate(..), universe)
import Common.Rewriting
import Common.Utils
import Data.List
import Data.Maybe
infixr 2 :<->:
infixr 3 :->:
infixr 4 :||:
infixr 5 :&&:
-- | The data type Logic is the abstract syntax for the domain
-- | of logic expressions.
data Logic a = Var a
| Logic a :->: Logic a -- implication
| Logic a :<->: Logic a -- equivalence
| Logic a :&&: Logic a -- and (conjunction)
| Logic a :||: Logic a -- or (disjunction)
| Not (Logic a) -- not
| T -- true
| F -- false
deriving (Show, Eq, Ord)
-- | For simple use, we assume the variables to be strings
type SLogic = Logic String
instance Functor Logic where
fmap f = foldLogic (Var . f, (:->:), (:<->:), (:&&:), (:||:), Not, T, F)
-- | The type LogicAlg is the algebra for the data type Logic
-- | Used in the fold for Logic.
type LogicAlg b a = (b -> a, a -> a -> a, a -> a -> a, a -> a -> a, a -> a -> a, a -> a, a, a)
-- | foldLogic is the standard fold for Logic.
foldLogic :: LogicAlg b a -> Logic b -> a
foldLogic (var, impl, equiv, and, or, not, true, false) = rec
where
rec logic =
case logic of
Var x -> var x
p :->: q -> rec p `impl` rec q
p :<->: q -> rec p `equiv` rec q
p :&&: q -> rec p `and` rec q
p :||: q -> rec p `or` rec q
Not p -> not (rec p)
T -> true
F -> false
-- | evalLogic takes a function that gives a logic value to a variable,
-- | and a Logic expression, and evaluates the boolean expression.
evalLogic :: (a -> Bool) -> Logic a -> Bool
evalLogic env = foldLogic (env, impl, (==), (&&), (||), not, True, False)
where
impl p q = not p || q
-- | eqLogic determines whether or not two Logic expression are logically
-- | equal, by evaluating the logic expressions on all valuations.
eqLogic :: Eq a => Logic a -> Logic a -> Bool
eqLogic p q = all (\f -> evalLogic f p == evalLogic f q) fs
where
xs = varsLogic p `union` varsLogic q
fs = map (flip elem) (subsets xs)
-- | Functions noNot, noOr, and noAnd determine whether or not a Logic
-- | expression contains a not, or, and and constructor, respectively.
noNot, noOr, noAnd :: Logic a -> Bool
noNot = foldLogic (const True, (&&), (&&), (&&), (&&), const False, True, True)
noOr = foldLogic (const True, (&&), (&&), (&&), \_ _ -> False, id, True, True)
noAnd = foldLogic (const True, (&&), (&&), \_ _ -> False, (&&), id, True, True)
-- | A Logic expression is atomic if it is a variable or a constant True or False.
isAtomic :: Logic a -> Bool
isAtomic logic =
case logic of
Var _ -> True
Not (Var _) -> True
T -> True
F -> True
_ -> False
-- | Functions isDNF, and isCNF determine whether or not a Logix expression
-- | is in disjunctive normal form, or conjunctive normal form, respectively.
isDNF, isCNF :: Logic a -> Bool
isDNF = all isAtomic . concatMap conjunctions . disjunctions
isCNF = all isAtomic . concatMap disjunctions . conjunctions
-- | Function disjunctions returns all Logic expressions separated by an or
-- | operator at the top level.
disjunctions :: Logic a -> [Logic a]
disjunctions = collectWithOperator orOperator
-- | Function conjunctions returns all Logic expressions separated by an and
-- | operator at the top level.
conjunctions :: Logic a -> [Logic a]
conjunctions = collectWithOperator andOperator
-- | Count the number of implicationsations :: Logic -> Int
countImplications :: Logic a -> Int
countImplications p = length [ () | _ :->: _ <- universe p ]
-- | Count the number of equivalences
countEquivalences :: Logic a -> Int
countEquivalences p = length [ () | _ :<->: _ <- universe p ]
-- | Count the number of binary operators
countBinaryOperators :: Logic a -> Int
countBinaryOperators = foldLogic (const 0, binop, binop, binop, binop, id, 0, 0)
where binop x y = x + y + 1
-- | Count the number of double negations
countDoubleNegations :: Logic a -> Int
countDoubleNegations p = length [ () | Not (Not _) <- universe p ]
-- | Function varsLogic returns the variables that appear in a Logic expression.
varsLogic :: Eq a => Logic a -> [a]
varsLogic p = nub [ s | Var s <- universe p ]
instance Uniplate (Logic a) where
uniplate p =
case p of
p :->: q -> ([p, q], \[a, b] -> a :->: b)
p :<->: q -> ([p, q], \[a, b] -> a :<->: b)
p :&&: q -> ([p, q], \[a, b] -> a :&&: b)
p :||: q -> ([p, q], \[a, b] -> a :||: b)
Not p -> ([p], \[a] -> Not a)
_ -> ([], \[] -> p)
instance Eq a => ShallowEq (Logic a) where
shallowEq expr1 expr2 =
case (expr1, expr2) of
(Var a, Var b) -> a==b
(_ :->: _ , _ :->: _ ) -> True
(_ :<->: _, _ :<->: _) -> True
(_ :&&: _ , _ :&&: _ ) -> True
(_ :||: _ , _ :||: _ ) -> True
(Not _ , Not _ ) -> True
(T , T ) -> True
(F , F ) -> True
_ -> False
instance MetaVar a => MetaVar (Logic a) where
isMetaVar (Var a) = isMetaVar a
isMetaVar _ = Nothing
metaVar = Var . metaVar
logicOperators :: Operators (Logic a)
logicOperators = [andOperator, orOperator]
-- The "and" operator is also commutative, but not (yet) in the equational theory
andOperator :: Operator (Logic a)
andOperator = associativeOperator (:&&:) isAnd
where
isAnd (p :&&: q) = Just (p, q)
isAnd _ = Nothing
-- The "or" operator is also commutative, but not (yet) in the equational theory
orOperator :: Operator (Logic a)
orOperator = associativeOperator (:||:) isOr
where
isOr (p :||: q) = Just (p, q)
isOr _ = Nothing