2023-06-04 17:10:14 +02:00
|
|
|
module Main where
|
|
|
|
|
2023-06-07 17:36:14 +02:00
|
|
|
import FOLSyntax ( Formula(Conj, Neg, Impl), Term(..) )
|
2023-06-08 12:53:19 +02:00
|
|
|
import Parser ( parseFormula, parseFormulaE )
|
|
|
|
import Resolution ( CNF, doResolution, doResolutionIO, showCNF )
|
2023-06-07 17:36:14 +02:00
|
|
|
import Normalforms
|
|
|
|
( makeNNF,
|
|
|
|
renameBinders,
|
|
|
|
makePNF,
|
|
|
|
makeSkolem,
|
|
|
|
makeCNF,
|
|
|
|
makeClauseSet )
|
2023-06-08 12:53:19 +02:00
|
|
|
import Control.Monad (void)
|
2023-06-04 17:10:14 +02:00
|
|
|
|
|
|
|
{-
|
|
|
|
To prove a formula we:
|
|
|
|
1. Construct `Neg phi`
|
|
|
|
2. Transform `Neg phi` to a clause list
|
|
|
|
3. doResolution on clause list
|
|
|
|
-}
|
2023-06-07 17:36:14 +02:00
|
|
|
proveFormula :: Formula -> Either () CNF
|
|
|
|
proveFormula form = doResolution . makeClauseSet . makeCNF . makeSkolem . makePNF . renameBinders . makeNNF $ Neg form
|
2023-06-04 17:10:14 +02:00
|
|
|
|
2023-06-08 12:53:19 +02:00
|
|
|
proveFormulaIO :: Formula -> IO ()
|
|
|
|
proveFormulaIO form = do
|
|
|
|
let clauseSet = makeClauseSet . makeCNF . makeSkolem . makePNF . renameBinders . makeNNF $ Neg form
|
|
|
|
void $ doResolutionIO clauseSet
|
|
|
|
|
2023-06-04 17:10:14 +02:00
|
|
|
-- unification examples
|
|
|
|
terma1 :: Term
|
|
|
|
terma1 = Fun "f" [Var "x", Fun "g" [Var "y"]]
|
|
|
|
termb1 :: Term
|
|
|
|
termb1 = Fun "f" [Fun "g" [Var "z"], Var "z"]
|
|
|
|
terma2 :: Term
|
|
|
|
terma2 = Fun "f" [Var "x", Fun "g" [Var "x"], Fun "h" [Var "y"]]
|
|
|
|
termb2 :: Term
|
|
|
|
termb2 = Fun "f" [Fun "k" [Var "y"], Fun "g" [Var "z"], Var "z"]
|
|
|
|
terma3 :: Term
|
|
|
|
terma3 = Fun "f" [Var "x", Fun "g" [Var "x"]]
|
|
|
|
termb3 :: Term
|
|
|
|
termb3 = Fun "f" [Var "z", Var "z"]
|
|
|
|
|
|
|
|
-- NNF example from gloin
|
|
|
|
formula1 :: Formula
|
2023-06-07 17:36:14 +02:00
|
|
|
formula1 = parseFormula "!((A () \\/ !B()) /\\ (C ()))"
|
2023-06-04 17:10:14 +02:00
|
|
|
|
|
|
|
-- PNF and skolem example from gloin
|
2023-06-07 17:36:14 +02:00
|
|
|
formula2 :: Formula
|
|
|
|
formula2 = parseFormula "forall x. (forall y. L(y,x)) -> exists y.M(x,y)"
|
2023-06-04 17:10:14 +02:00
|
|
|
|
|
|
|
-- Resolution example from gloin script
|
|
|
|
formula3 :: Formula
|
2023-06-07 17:36:14 +02:00
|
|
|
formula3 = parseFormula "P(a()) /\\ (forall x. P(x) -> P(f(x))) -> (exists x. P(f(f(x)))) "
|
2023-06-04 17:10:14 +02:00
|
|
|
|
2023-06-07 17:36:14 +02:00
|
|
|
-- Resolution example from gloin exercises (sheet 11, ex 5) [already in CNF]
|
2023-06-04 17:10:14 +02:00
|
|
|
formula4 :: Formula
|
2023-06-07 17:36:14 +02:00
|
|
|
formula4 = parseFormula "((S(f(x), y)) \\/ (S(y, z)) \\/ (P(y))) /\\ (!(S(f(f(x)), x))) /\\ (!P(f(z)))"
|
2023-06-04 17:10:14 +02:00
|
|
|
|
|
|
|
-- now a big example, sheet 11, exercise 6: Drogenschmuggel, this doesn't work yet but I'm sure its just the exercise thats wrong...
|
|
|
|
formula5 :: Formula
|
2023-06-07 17:36:14 +02:00
|
|
|
formula5 = Impl (Conj phi1 (Conj phi2 phi3)) psi
|
|
|
|
where
|
|
|
|
phi1 = parseFormula "forall x.E(x) /\\ !I(x) -> exists y.Z(y) /\\ S(y,x)"
|
|
|
|
phi2 = parseFormula "exists x. (D(x) /\\ E(x)) /\\ forall y. S(y,x) -> D(y)"
|
|
|
|
phi3 = parseFormula "forall x.I(x) -> !D(x)"
|
|
|
|
psi = parseFormula "exists x. Z(x) /\\ D(x)"
|
2023-06-07 11:04:00 +02:00
|
|
|
|
|
|
|
-- aerzte und quacksalber v2
|
2023-06-07 17:36:14 +02:00
|
|
|
formula6 :: Formula
|
|
|
|
formula6 = Impl (Conj psi1 psi2) psi3
|
2023-06-07 11:04:00 +02:00
|
|
|
where
|
2023-06-07 17:36:14 +02:00
|
|
|
psi1 = parseFormula "forall x. D(x) -> exists y. P(y) /\\ L(y,x)"
|
|
|
|
psi2 = parseFormula "forall x. P(x) -> forall y. Q(y) -> !L(x, y)"
|
|
|
|
psi3 = parseFormula "forall x. D(x) -> !Q(x)"
|
2023-06-07 11:04:00 +02:00
|
|
|
|
2023-06-08 12:53:19 +02:00
|
|
|
testing :: IO ()
|
|
|
|
testing = do
|
2023-06-04 17:10:14 +02:00
|
|
|
putStrLn $ "Now making NNF of formula: " ++ show formula1
|
|
|
|
print $ makeNNF formula1
|
|
|
|
putStrLn $ "Now making PNF of formula: " ++ show formula2
|
2023-06-07 17:36:14 +02:00
|
|
|
print . makePNF . renameBinders $ makeNNF formula2
|
2023-06-04 17:10:14 +02:00
|
|
|
putStrLn $ "Now making Skolemform of formula: " ++ show formula2
|
2023-06-07 17:36:14 +02:00
|
|
|
print . makeSkolem . makePNF . renameBinders $ makeNNF formula2
|
2023-06-04 17:10:14 +02:00
|
|
|
putStrLn $ "Now proving formula by resolution: " ++ show formula3
|
|
|
|
case proveFormula formula3 of
|
|
|
|
Left _ -> putStrLn "Success!"
|
|
|
|
Right _ -> return ()
|
|
|
|
putStrLn $ "Now Proving formula by resolution: " ++ show formula4
|
2023-06-07 17:36:14 +02:00
|
|
|
case doResolution $ makeClauseSet formula4 of
|
2023-06-07 11:04:00 +02:00
|
|
|
Left _ -> putStrLn "Success!"
|
|
|
|
Right _ -> return ()
|
2023-06-07 17:36:14 +02:00
|
|
|
putStrLn $ "Now Proving formula by resolution: " ++ show formula5
|
|
|
|
case proveFormula formula5 of
|
2023-06-07 11:04:00 +02:00
|
|
|
Left _ -> putStrLn "Success!"
|
|
|
|
Right _ -> return ()
|
2023-06-07 17:36:14 +02:00
|
|
|
putStrLn $ "Now Proving formula by resolution: " ++ show formula6
|
2023-06-08 11:30:41 +02:00
|
|
|
case proveFormula (Neg formula6) of
|
2023-06-04 17:10:14 +02:00
|
|
|
Left _ -> putStrLn "Success!"
|
2023-06-08 12:53:19 +02:00
|
|
|
Right _ -> return ()
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
|
|
|
showIntroText
|
|
|
|
go
|
|
|
|
where
|
|
|
|
go = do
|
|
|
|
putStrLn "\nYou can now enter a formula that should be proven or enter the name of a predefined formula to proof them."
|
|
|
|
putStrLn "Type 'help' to see the information again."
|
|
|
|
-- TODO use haskeline...
|
|
|
|
line <- getLine
|
|
|
|
case line of
|
|
|
|
"script" -> fullProof formula3
|
|
|
|
"doctors" -> fullProof formula6
|
|
|
|
"drugs" -> fullProof formula5
|
|
|
|
"help" -> showIntroText
|
|
|
|
str -> case parseFormulaE str of
|
|
|
|
Left err -> putStrLn err
|
|
|
|
Right formula -> fullProof formula
|
|
|
|
go
|
|
|
|
fullProof f = do
|
|
|
|
putStr "\n\n\n"
|
|
|
|
putStrLn $ "1. Negation of formula:\n" ++ show (Neg f)
|
|
|
|
let f' = makeNNF (Neg f)
|
|
|
|
putStrLn $ "2. Negation normalform:\n" ++ show f'
|
|
|
|
let f'' = makePNF $ renameBinders f'
|
|
|
|
putStrLn $ "3. Prenex normalform (with renamed binders):\n" ++ show f''
|
|
|
|
let f''' = makeSkolem f''
|
|
|
|
putStrLn $ "4. Skolemization:\n" ++ show f'''
|
|
|
|
let g = makeClauseSet . makeCNF $ f'''
|
|
|
|
putStrLn $ "5. Clause set:\n" ++ showCNF g
|
|
|
|
putStrLn "6. Do resolution:\n"
|
|
|
|
_ <- doResolutionIO g
|
|
|
|
putStrLn "7. Negation of formula is unsatisfiable, so the formula is valid!"
|
|
|
|
showIntroText = do
|
|
|
|
putStrLn "This is a simple program implementing a resolution algorithm on first order logic (FOL)."
|
|
|
|
putStrLn "Caution: resoluton on FOL is only a semi-decider, if you try to prove an unprovable formula the algorithm will diverge!"
|
|
|
|
putStrLn "\nProcedure:"
|
|
|
|
putStrLn "To proof a formula the program takes the following steps:"
|
|
|
|
putStrLn "1. Negate the formula"
|
|
|
|
putStrLn "2. Transform it to negation normalform"
|
|
|
|
putStrLn "3. Transform it to prenex normalform"
|
|
|
|
putStrLn "4. Skolemize formula"
|
|
|
|
putStrLn "5. Transform it to a clause set"
|
|
|
|
putStrLn "6. Exhaustively use the reduction rule, until the empty clause is found."
|
|
|
|
putStrLn "7. If an empty clause was found the negation of the formula is unsatisfiable, making the the formula valid!"
|
|
|
|
putStrLn "\nPrelude:"
|
|
|
|
putStrLn "There are some predefined formulas that can be called for a proof:"
|
|
|
|
putStrLn $ "script = " ++ show formula3 ++ " [a small resolution example taken from lecture notes]"
|
|
|
|
-- putStrLn $ "exercises = " ++ show formula4 ++ " [another small resolution example taken from exercises]" TODO this is already negated...
|
|
|
|
putStrLn $ "doctors = " ++ show formula6 ++ " [a little more complex example from exercises]"
|
|
|
|
putStrLn $ "drugs = " ++ show formula5 ++ " [most complex example from exercises]"
|