module Data.Logic.ATP.DefCNF
( NumAtom(ma, ai)
, defcnfs
, defcnf1
, defcnf2
, defcnf3
, Atom(N)
, testDefCNF
) where
import Data.Function (on)
import Data.List as List
import Data.Logic.ATP.Formulas as P
import Data.Logic.ATP.Lit ((.~.), (¬), convertLiteral, IsLiteral, JustLiteral, LFormula)
import Data.Logic.ATP.Pretty (assertEqual', HasFixity, Pretty(pPrint), prettyShow, text)
import Data.Logic.ATP.Prop (cnf', foldPropositional, IsPropositional(foldPropositional'), JustPropositional,
list_conj, list_disj, nenf, PFormula, Prop(P), simpcnf,
(∨), (∧), (.<=>.), (.&.), (.|.), BinOp(..))
import Data.Map.Strict as Map hiding (fromList)
import Data.Set as Set
import Test.HUnit
test01 :: Test
test01 =
let p :: PFormula Prop
q :: PFormula Prop
r :: PFormula Prop
[p, q, r] = (List.map (atomic . P) ["p", "q", "r"]) in
TestCase $ assertEqual' "cnf test (p. 74)"
((p∨q∨r)∧(p∨(¬)q∨(¬)r)∧(q∨(¬)p∨(¬)r)∧(r∨(¬)p∨(¬)q))
(cnf' (p .<=>. (q .<=>. r)) :: PFormula Prop)
class NumAtom atom where
ma :: Integer -> atom
ai :: atom -> Integer
data Atom = N String Integer deriving (Eq, Ord, Show)
instance Pretty Atom where
pPrint (N s n) = text (s ++ if n == 0 then "" else show n)
instance NumAtom Atom where
ma = N "p_"
ai (N _ n) = n
instance HasFixity Atom
instance IsAtom Atom
mkprop :: forall pf. (IsPropositional pf, NumAtom (AtomOf pf)) => Integer -> (pf, Integer)
mkprop n = (atomic (ma n :: AtomOf pf), n + 1)
maincnf :: (IsPropositional pf, Ord pf, NumAtom (AtomOf pf)) => (pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer)
maincnf trip@(fm, _defs, _n) =
foldPropositional' ho co ne tf at fm
where
ho _ = trip
co p (:&:) q = defstep (.&.) (p,q) trip
co p (:|:) q = defstep (.|.) (p,q) trip
co p (:<=>:) q = defstep (.<=>.) (p,q) trip
co _ (:=>:) _ = trip
ne _ = trip
tf _ = trip
at _ = trip
defstep :: (IsPropositional pf, NumAtom (AtomOf pf), Ord pf) =>
(pf -> pf -> pf) -> (pf, pf) -> (pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer)
defstep op (p,q) (_fm, defs, n) =
let (fm1,defs1,n1) = maincnf (p,defs,n) in
let (fm2,defs2,n2) = maincnf (q,defs1,n1) in
let fm' = op fm1 fm2 in
case Map.lookup fm' defs2 of
Just _ -> (fm', defs2, n2)
Nothing -> let (v,n3) = mkprop n2 in (v, Map.insert v (v .<=>. fm') defs2,n3)
max_varindex :: NumAtom atom => atom -> Integer -> Integer
max_varindex atom n = max n (ai atom)
defcnf1 :: forall pf. (IsPropositional pf, JustPropositional pf, NumAtom (AtomOf pf), Ord pf) => pf -> pf
defcnf1 = list_conj . Set.map (list_disj . Set.map (convertLiteral id)) . (mk_defcnf id maincnf :: pf -> Set (Set (LFormula (AtomOf pf))))
mk_defcnf :: forall pf lit.
(IsPropositional pf, JustPropositional pf,
IsLiteral lit, JustLiteral lit, Ord lit,
NumAtom (AtomOf pf)) =>
(AtomOf pf -> AtomOf lit)
-> ((pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer))
-> pf -> Set (Set lit)
mk_defcnf ca fn fm =
let (fm' :: pf) = nenf fm in
let n = 1 + overatoms max_varindex fm' 0 in
let (fm'',defs,_) = fn (fm',Map.empty,n) in
let (deflist :: [pf]) = Map.elems defs in
Set.unions (List.map (simpcnf ca :: pf -> Set (Set lit)) (fm'' : deflist))
testfm :: PFormula Atom
testfm = let (p, q, r, s) = (atomic (N "p" 0), atomic (N "q" 0), atomic (N "r" 0), atomic (N "s" 0)) in
(p .|. (q .&. ((.~.) r))) .&. s
test02 :: Test
test02 =
let input = strings (mk_defcnf id maincnf testfm :: Set (Set (LFormula Atom)))
expected = [["p_3"],
["p_2","¬p"],
["p_2","¬p_1"],
["p_2","¬p_3"],
["q","¬p_1"],
["s","¬p_3"],
["¬p_1","¬r"],
["p","p_1","¬p_2"],
["p_1","r","¬q"],
["p_3","¬p_2","¬s"]] in
TestCase $ assertEqual "defcnf1 (p. 77)" expected input
strings :: Pretty a => Set (Set a) -> [[String]]
strings ss = sortBy (compare `on` length) . sort . Set.toList $ Set.map (sort . Set.toList . Set.map prettyShow) ss
defcnf2 :: (IsPropositional pf, JustPropositional pf, Ord pf, NumAtom (AtomOf pf)) => pf -> pf
defcnf2 fm = list_conj (Set.map (list_disj . Set.map (convertLiteral id)) (defcnfs fm))
defcnfs :: (IsPropositional pf, JustPropositional pf, Ord pf, NumAtom (AtomOf pf)) => pf -> Set (Set (LFormula (AtomOf pf)))
defcnfs fm = mk_defcnf id andcnf fm
andcnf :: (IsPropositional pf, JustPropositional pf, Ord pf, NumAtom (AtomOf pf)) => (pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer)
andcnf trip@(fm,_defs,_n) =
foldPropositional co (\ _ -> orcnf trip) (\ _ -> orcnf trip) (\ _ -> orcnf trip) fm
where
co p (:&:) q = subcnf andcnf (.&.) p q trip
co _ _ _ = orcnf trip
orcnf :: (IsPropositional pf, JustPropositional pf, Ord pf, NumAtom (AtomOf pf)) => (pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer)
orcnf trip@(fm,_defs,_n) =
foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
where
co p (:|:) q = subcnf orcnf (.|.) p q trip
co _ _ _ = maincnf trip
subcnf :: (IsPropositional pf, NumAtom (AtomOf pf)) =>
((pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer))
-> (pf -> pf -> pf)
-> pf
-> pf
-> (pf, Map pf pf, Integer)
-> (pf, Map pf pf, Integer)
subcnf sfn op p q (_fm,defs,n) =
let (fm1,defs1,n1) = sfn (p,defs,n) in
let (fm2,defs2,n2) = sfn (q,defs1,n1) in
(op fm1 fm2, defs2, n2)
defcnf3 :: forall pf. (JustPropositional pf, Ord pf, NumAtom (AtomOf pf)) => pf -> pf
defcnf3 = list_conj . Set.map (list_disj . Set.map (convertLiteral id)) . (mk_defcnf id andcnf3 :: pf -> Set (Set (LFormula (AtomOf pf))))
andcnf3 :: (IsPropositional pf, JustPropositional pf, Ord pf, NumAtom (AtomOf pf)) => (pf, Map pf pf, Integer) -> (pf, Map pf pf, Integer)
andcnf3 trip@(fm,_defs,_n) =
foldPropositional co (\ _ -> maincnf trip) (\ _ -> maincnf trip) (\ _ -> maincnf trip) fm
where
co p (:&:) q = subcnf andcnf3 (.&.) p q trip
co _ _ _ = maincnf trip
test03 :: Test
test03 =
let input = strings (mk_defcnf id andcnf3 testfm :: Set (Set (LFormula Atom)))
expected = [["p_2"],
["s"],
["p_2","¬p"],
["p_2","¬p_1"],
["q","¬p_1"],
["¬p_1","¬r"],
["p","p_1","¬p_2"],
["p_1","r","¬q"]] in
TestCase $ assertEqual "defcnf1 (p. 77)" expected input
testDefCNF :: Test
testDefCNF = TestLabel "DefCNF" (TestList [test01, test02, test03])