module-management-0.20.2: testdata/split-expected/Data/Logic/Harrison/Prop.hs
{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies #-}
{-# OPTIONS_GHC -Wall -Wwarn #-}
module Data.Logic.Harrison.Prop
( eval
, atoms
, onAllValuations
, TruthTable
, TruthTableRow
, truthTable
, tautology
, unsatisfiable
, satisfiable
, rawdnf
, purednf
, dnf
, dnf'
, trivial
, psimplify
, nnf
, simpdnf
, simpcnf
, positive
, negative
, negate
, distrib
, list_disj
, list_conj
-- previously unexported
, pSubst
, dual
, nenf
, mkLits
, allSatValuations
, dnf0
, cnf
, cnf'
) where
import Data.Logic.Classes.Combine (binop, BinOp(..), Combinable(..), Combination(..))
import Data.Logic.Classes.Constants (Constants(fromBool, asBool), false, ifElse, true)
import Data.Logic.Classes.Formula (Formula(atomic))
import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
import Data.Logic.Classes.Literal.ToPropositional (toPropositional)
import Data.Logic.Classes.Negate ((.~.))
import Data.Logic.Classes.Propositional (PropositionalFormula(..))
import Data.Logic.Harrison.Formulas.Propositional (atom_union, on_atoms)
import Data.Logic.Harrison.Lib (distrib', fpf, setAny)
import qualified Data.Map as Map (empty, findWithDefault, insert, Map)
import qualified Data.Set as Set (empty, filter, fold, fromList, intersection, isProperSubsetOf, map, minView, null, partition, Set, singleton, toAscList, union)
import Prelude hiding (negate)
-- type Map a = Map.Map a Bool
-- m0 = Map.empty
-- ins :: forall a. Ord a => a -> Bool -> Map a -> Map a
-- ins = Map.insert
-- m ! k = Map.findWithDefault False k m
-- -------------------------------------------------------------------------
-- Parsing of propositional formulas.
-- -------------------------------------------------------------------------
{-
let parse_propvar vs inp =
match inp with
p::oinp when p /= "(" -> Atom(P(p)),oinp
| _ -> failwith "parse_propvar";;
let parse_prop_formula = make_parser
(parse_formula ((fun _ _ -> failwith ""),parse_propvar) []);;
-}
-- -------------------------------------------------------------------------
-- Set this up as default for quotations.
-- -------------------------------------------------------------------------
{-
let default_parser = parse_prop_formula;;
-}
-- -------------------------------------------------------------------------
-- Printer.
-- -------------------------------------------------------------------------
{-
let print_propvar prec p = print_string(pname p);;
let print_prop_formula = print_qformula print_propvar;;
#install_printer print_prop_formula;;
-}
-- -------------------------------------------------------------------------
-- Interpretation of formulas.
-- -------------------------------------------------------------------------
eval :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Map.Map atomic Bool -> Bool
eval fm v =
foldPropositional co id at fm
where
co ((:~:) p) = not (eval p v)
co (BinOp p (:&:) q) = eval p v && eval q v
co (BinOp p (:|:) q) = eval p v || eval q v
co (BinOp p (:=>:) q) = not (eval p v) || eval q v
co (BinOp p (:<=>:) q) = eval p v == eval q v
at x = Map.findWithDefault False x v
{-
START_INTERACTIVE;;
eval <<p /\ q ==> q /\ r>>
(function P"p" -> true | P"q" -> false | P"r" -> true);;
eval <<p /\ q ==> q /\ r>>
(function P"p" -> true | P"q" -> true | P"r" -> false);;
END_INTERACTIVE;;
-}
-- -------------------------------------------------------------------------
-- Return the set of propositional variables in a formula.
-- -------------------------------------------------------------------------
atoms :: Ord atomic => PropositionalFormula formula atomic => formula -> Set.Set atomic
atoms = atom_union Set.singleton
-- -------------------------------------------------------------------------
-- Code to print out truth tables.
-- -------------------------------------------------------------------------
onAllValuations :: (Ord a) =>
(r -> r -> r) -- ^ Combine function for result type
-> (Map.Map a Bool -> r) -- ^ The substitution function
-> Map.Map a Bool -- ^ The default valuation function for atoms not in ps
-> Set.Set a -- ^ The variables to vary
-> r
onAllValuations _ subfn v ps | Set.null ps = subfn v
onAllValuations append subfn v ps =
case Set.minView ps of
Nothing -> error "onAllValuations"
Just (p, ps') ->
append -- Do the valuations of the remaining variables with set to false
(onAllValuations append subfn (Map.insert p False v) ps')
-- Do the valuations of the remaining variables with set to true
(onAllValuations append subfn (Map.insert p True v) ps')
type TruthTableRow = ([Bool], Bool)
type TruthTable a = ([a], [TruthTableRow])
truthTable :: forall formula atom. (PropositionalFormula formula atom, Eq atom, Ord atom) =>
formula -> TruthTable atom
truthTable fm =
(atl, onAllValuations (++) mkRow Map.empty ats)
where
mkRow :: Map.Map atom Bool -- ^ The current variable assignment
-> [TruthTableRow] -- ^ The variable assignments and the formula value
mkRow v = [(map (\ k -> Map.findWithDefault False k v) atl, eval fm v)]
atl = Set.toAscList ats
ats = atoms fm
-- -------------------------------------------------------------------------
-- Recognizing tautologies.
-- -------------------------------------------------------------------------
tautology :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
tautology fm = onAllValuations (&&) (eval fm) Map.empty (atoms fm)
-- -------------------------------------------------------------------------
-- Related concepts.
-- -------------------------------------------------------------------------
unsatisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
unsatisfiable fm = tautology ((.~.) fm)
satisfiable :: (PropositionalFormula formula atomic, Ord atomic) => formula -> Bool
satisfiable = not . unsatisfiable
-- -------------------------------------------------------------------------
-- Substitution operation.
-- -------------------------------------------------------------------------
-- pSubst :: Ord a => Map.Map a (Formula a) -> Formula a -> Formula a
pSubst :: (PropositionalFormula formula atomic, Ord atomic) => Map.Map atomic formula -> formula -> formula
pSubst subfn fm = on_atoms (\ p -> maybe (atomic p) id (fpf subfn p)) fm
-- -------------------------------------------------------------------------
-- Dualization.
-- -------------------------------------------------------------------------
dual :: forall formula atomic. (PropositionalFormula formula atomic) => formula -> formula
dual fm =
foldPropositional co (fromBool . not) at fm
where
co ((:~:) _) = fm
co (BinOp p (:&:) q) = dual p .|. dual q
co (BinOp p (:|:) q) = dual p .&. dual q
co _ = error "dual: Formula involves connectives ==> or <=>";;
at = atomic
-- -------------------------------------------------------------------------
-- Routine simplification.
-- -------------------------------------------------------------------------
psimplify1 :: (PropositionalFormula r a, Eq r) => r -> r
psimplify1 fm =
foldPropositional simplifyCombine (\ _ -> fm) (\ _ -> fm) fm
where
simplifyCombine ((:~:) fm') = foldPropositional simplifyNotCombine (fromBool . not) (\ _ -> fm) fm'
simplifyCombine (BinOp l op r) =
case (asBool l, op, asBool r) of
(Just True, (:&:), _ ) -> r
(Just False, (:&:), _ ) -> false
(_, (:&:), Just True ) -> l
(_, (:&:), Just False) -> false
(Just True, (:|:), _ ) -> true
(Just False, (:|:), _ ) -> r
(_, (:|:), Just True ) -> true
(_, (:|:), Just False) -> l
(Just True, (:=>:), _ ) -> r
(Just False, (:=>:), _ ) -> true
(_, (:=>:), Just True ) -> true
(_, (:=>:), Just False) -> (.~.) l
(Just True, (:<=>:), _ ) -> r
(Just False, (:<=>:), _ ) -> (.~.) r
(_, (:<=>:), Just True ) -> l
(_, (:<=>:), Just False) -> (.~.) l
_ -> fm
simplifyNotCombine ((:~:) p) = p
simplifyNotCombine _ = fm
psimplify :: forall formula atomic. (PropositionalFormula formula atomic, Eq formula) => formula -> formula
psimplify fm =
foldPropositional c (\ _ -> fm) (\ _ -> fm) fm
where
c :: Combination formula -> formula
c ((:~:) p) = psimplify1 ((.~.) (psimplify p))
c (BinOp p op q) = psimplify1 (binop (psimplify p) op (psimplify q))
-- -------------------------------------------------------------------------
-- Some operations on literals.
-- -------------------------------------------------------------------------
negative :: forall lit atom. Literal lit atom => lit -> Bool
negative lit =
foldLiteral neg tf a lit
where
neg _ = True
tf = not
a _ = False
positive :: Literal lit atom => lit -> Bool
positive = not . negative
negate :: PropositionalFormula formula atomic => formula -> formula
negate lit =
foldPropositional c (fromBool . not) a lit
where
c ((:~:) p) = p
c _ = (.~.) lit
a _ = (.~.) lit
-- -------------------------------------------------------------------------
-- Negation normal form.
-- -------------------------------------------------------------------------
nnf' :: PropositionalFormula formula atomic => formula -> formula
nnf' fm =
foldPropositional nnfCombine (\ _ -> fm) (\ _ -> fm) fm
where
nnfCombine ((:~:) p) = foldPropositional nnfNotCombine (fromBool . not) (\ _ -> fm) p
nnfCombine (BinOp p (:=>:) q) = nnf' ((.~.) p) .|. (nnf' q)
nnfCombine (BinOp p (:<=>:) q) = (nnf' p .&. nnf' q) .|. (nnf' ((.~.) p) .&. nnf' ((.~.) q))
nnfCombine (BinOp p (:&:) q) = nnf' p .&. nnf' q
nnfCombine (BinOp p (:|:) q) = nnf' p .|. nnf' q
nnfNotCombine ((:~:) p) = nnf' p
nnfNotCombine (BinOp p (:&:) q) = nnf' ((.~.) p) .|. nnf' ((.~.) q)
nnfNotCombine (BinOp p (:|:) q) = nnf' ((.~.) p) .&. nnf' ((.~.) q)
nnfNotCombine (BinOp p (:=>:) q) = nnf' p .&. nnf' ((.~.) q)
nnfNotCombine (BinOp p (:<=>:) q) = (nnf' p .&. nnf' ((.~.) q)) .|. nnf' ((.~.) p) .&. nnf' q
-- -------------------------------------------------------------------------
-- Roll in simplification.
-- -------------------------------------------------------------------------
nnf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
nnf = nnf' . psimplify
-- -------------------------------------------------------------------------
-- Simple negation-pushing when we don't care to distinguish occurrences.
-- -------------------------------------------------------------------------
nenf' :: PropositionalFormula formula atomic => formula -> formula
nenf' fm =
foldPropositional nenfCombine (\ _ -> fm) (\ _ -> fm) fm
where
nenfCombine ((:~:) p) = foldPropositional nenfNotCombine (\ _ -> fm) (\ _ -> fm) p
nenfCombine (BinOp p (:&:) q) = nenf' p .&. nenf' q
nenfCombine (BinOp p (:|:) q) = nenf' p .|. nenf' q
nenfCombine (BinOp p (:=>:) q) = nenf' ((.~.) p) .|. nenf' q
nenfCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' q
nenfNotCombine ((:~:) p) = p
nenfNotCombine (BinOp p (:&:) q) = nenf' ((.~.) p) .|. nenf' ((.~.) q)
nenfNotCombine (BinOp p (:|:) q) = nenf' ((.~.) p) .&. nenf' ((.~.) q)
nenfNotCombine (BinOp p (:=>:) q) = nenf' p .&. nenf' ((.~.) q)
nenfNotCombine (BinOp p (:<=>:) q) = nenf' p .<=>. nenf' ((.~.) q) -- really? how is this asymmetrical?
nenf :: (PropositionalFormula formula atomic, Eq formula) => formula -> formula
nenf = nenf' . psimplify
{-
# Not (prime 2) ->
<<~(~(((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1))>>
# nenf (Not (prime 2)) ->
<<((out_0 <=> x_0 /\ y_0) /\ ~out_1) /\ ~out_0 /\ out_1>>
> pretty ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N)))
(out0 ⇔ x0 ∧ y0) ∧ ¬out1 ∧ out1 ∧ ¬out0
> pretty (nenf ((.~.)(prime 2 :: Formula (Data.Logic.Harrison.PropExamples.Atom N))))
(out0 ⇔ x0 ∨ y0) ∨ ¬out1 ∨ out1 ∨ ¬out0
-}
-- -------------------------------------------------------------------------
-- Disjunctive normal form (DNF) via truth tables.
-- -------------------------------------------------------------------------
list_conj :: (PropositionalFormula formula atomic, Ord formula) => Set.Set formula -> formula
list_conj l = maybe true (\ (x, xs) -> Set.fold (.&.) x xs) (Set.minView l)
list_disj :: PropositionalFormula formula atomic => Set.Set formula -> formula
list_disj l = maybe false (\ (x, xs) -> Set.fold (.|.) x xs) (Set.minView l)
mkLits :: (PropositionalFormula formula atomic, Ord formula, Ord atomic) =>
Set.Set formula -> Map.Map atomic Bool -> formula
mkLits pvs v = list_conj (Set.map (\ p -> if eval p v then p else (.~.) p) pvs)
allSatValuations :: Ord a => (Map.Map a Bool -> Bool) -> Map.Map a Bool -> Set.Set a -> [Map.Map a Bool]
allSatValuations subfn v pvs =
case Set.minView pvs of
Nothing -> if subfn v then [v] else []
Just (p, ps) -> (allSatValuations subfn (Map.insert p False v) ps) ++
(allSatValuations subfn (Map.insert p True v) ps)
dnf0 :: forall formula atomic. (PropositionalFormula formula atomic, Ord atomic, Ord formula) => formula -> formula
dnf0 fm =
list_disj (Set.fromList (map (mkLits (Set.map atomic pvs)) satvals))
where
satvals = allSatValuations (eval fm) Map.empty pvs
pvs = atoms fm
-- -------------------------------------------------------------------------
-- DNF via distribution.
-- -------------------------------------------------------------------------
distrib :: PropositionalFormula formula atomic => formula -> formula
distrib fm =
foldPropositional c tf a fm
where
c (BinOp p (:&:) s) =
foldPropositional c' tf a s
where c' (BinOp q (:|:) r) = distrib (p .&. q) .|. distrib (p .&. r)
c' _ =
foldPropositional c'' tf a p
where c'' (BinOp q (:|:) r) = distrib (q .&. s) .|. distrib (r .&. s)
c'' _ = fm
c _ = fm
tf _ = fm
a _ = fm
rawdnf :: PropositionalFormula formula atomic => formula -> formula
rawdnf fm =
foldPropositional c tf a fm
where
c (BinOp p (:&:) q) = distrib (rawdnf p .&. rawdnf q)
c (BinOp p (:|:) q) = rawdnf p .|. rawdnf q
c _ = fm
tf _ = fm
a _ = fm
-- -------------------------------------------------------------------------
-- A version using a list representation.
-- -------------------------------------------------------------------------
purednf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
purednf fm =
foldPropositional c tf a fm
where
c (BinOp p (:&:) q) = distrib' (purednf p) (purednf q)
c (BinOp p (:|:) q) = Set.union (purednf p) (purednf q)
c ((:~:) p) = Set.map (Set.map (.~.)) (purednf p)
c _ = error "purednf" -- Set.singleton (Set.singleton fm)
tf x = Set.singleton (Set.singleton (fromBool x))
a x = Set.singleton (Set.singleton (atomic x))
-- -------------------------------------------------------------------------
-- Filtering out trivial disjuncts (in this guise, contradictory).
-- -------------------------------------------------------------------------
trivial :: (Literal lit atom, Ord lit) => Set.Set lit -> Bool
trivial lits =
not . Set.null $ Set.intersection neg (Set.map (.~.) pos)
where (pos, neg) = Set.partition positive lits
-- -------------------------------------------------------------------------
-- With subsumption checking, done very naively (quadratic).
-- -------------------------------------------------------------------------
simpdnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
simpdnf fm =
foldPropositional c tf a fm
where
c :: Combination pf -> Set.Set (Set.Set lit)
c _ = Set.filter (\ d -> not (setAny (\ d' -> Set.isProperSubsetOf d' d) djs)) djs
where djs = Set.filter (not . trivial) (purednf (nnf fm))
tf = ifElse (Set.singleton Set.empty) Set.empty
a :: atom -> Set.Set (Set.Set lit)
a x = Set.singleton (Set.singleton (atomic x))
-- -------------------------------------------------------------------------
-- Mapping back to a formula.
-- -------------------------------------------------------------------------
dnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
dnf = list_disj . Set.map (list_conj . Set.map (toPropositional id))
dnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
dnf' = dnf . (simpdnf :: pf -> Set.Set (Set.Set pf))
-- -------------------------------------------------------------------------
-- Conjunctive normal form (CNF) by essentially the same code.
-- -------------------------------------------------------------------------
purecnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
purecnf fm = Set.map (Set.map (.~.)) (purednf (nnf ((.~.) fm)))
simpcnf :: (PropositionalFormula pf atom, Literal lit atom, Ord lit) => pf -> Set.Set (Set.Set lit)
simpcnf fm =
foldPropositional c tf a fm
where
tf = ifElse Set.empty (Set.singleton Set.empty)
-- Discard any clause that is the proper subset of another clause
c _ = Set.filter keep cjs
keep x = not (setAny (`Set.isProperSubsetOf` x) cjs)
cjs = Set.filter (not . trivial) (purecnf fm)
a x = Set.singleton (Set.singleton (atomic x))
cnf :: forall pf lit atom. (PropositionalFormula pf atom, Literal lit atom, Ord lit) => Set.Set (Set.Set lit) -> pf
cnf = list_conj . Set.map (list_disj . Set.map (toPropositional id))
cnf' :: forall pf atom. (PropositionalFormula pf atom, Literal pf atom) => pf -> pf
cnf' = cnf . (simpcnf :: pf -> Set.Set (Set.Set pf))