cabal
only doing this because Data.Set is not in the stdlib
This commit is contained in:
parent
30941456a2
commit
dff5b9f365
19 changed files with 101 additions and 16 deletions
|
@ -1,355 +0,0 @@
|
|||
module Logic.Statement.Laws where
|
||||
|
||||
import Logic.Parse (eof, mkInput)
|
||||
import Logic.Statement (Statement(..))
|
||||
import Logic.Statement.Parse (stmt)
|
||||
import Logic.Statement.Serialize (serialize, SerializeFormat(Plain))
|
||||
import Logic.Graph (bfs, verifyPath, VerifyPathError)
|
||||
|
||||
import Data.Either (fromRight, rights)
|
||||
import Data.Maybe (fromJust, listToMaybe)
|
||||
|
||||
data Law = Law
|
||||
{ lawName :: String
|
||||
, lawLhs :: Statement
|
||||
, lawRhs :: Statement
|
||||
}
|
||||
|
||||
instance Eq Law where
|
||||
law1 == law2 =
|
||||
lawLhs law1 == lawLhs law2
|
||||
&& lawRhs law1 == lawRhs law2
|
||||
|
||||
instance Show Law where
|
||||
show law =
|
||||
"Law{"
|
||||
<> lawName law
|
||||
<> ": "
|
||||
<> serialize Plain (lawLhs law)
|
||||
<> " <=> "
|
||||
<> serialize Plain (lawRhs law)
|
||||
<> "}"
|
||||
|
||||
mkLaw :: String -> String -> String -> Law
|
||||
mkLaw name lhs rhs = Law name (fromString lhs) (fromString rhs)
|
||||
where
|
||||
fromString :: String -> Statement
|
||||
fromString string = fromRight undefined $ eof stmt $ mkInput string
|
||||
|
||||
laws :: [Law]
|
||||
laws =
|
||||
[ mkLaw "dbl_neg" "A" "!!A"
|
||||
, mkLaw "and_comm" "(A&B)" "(B&A)"
|
||||
, mkLaw "or_comm" "(A|B)" "(B|A)"
|
||||
, mkLaw "and_assoc" "(A&(B&C))" "((A&B)&C)"
|
||||
, mkLaw "or_assoc" "(A|(B|C))" "((A|B)|C)"
|
||||
, mkLaw "and_or_distrib" "(A&(B|C))" "((A&B)|(A&C))"
|
||||
, mkLaw "or_and_distrib" "(A|(B&C))" "((A|B)&(A|C))"
|
||||
, mkLaw "and_symm_eq" "A" "(A&A)"
|
||||
, mkLaw "or_symm_eq" "A" "(A|A)"
|
||||
, mkLaw "not_and_distrib" "!(A&B)" "(!A|!B)"
|
||||
, mkLaw "not_or_distrib" "!(A|B)" "(!A&!B)"
|
||||
, mkLaw "implies_or" "(A->B)" "(!A|B)"
|
||||
, mkLaw "implies_and" "(A->B)" "!(A&!B)"
|
||||
, mkLaw "or_contr_eq" "A" "(A|(B&!B))"
|
||||
, mkLaw "and_or_cancel" "A" "(A&(A|B))"
|
||||
, mkLaw "or_and_cancel" "A" "(A|(A&B))"
|
||||
, mkLaw "iff_and" "(A<->B)" "((A->B)&(B->A))"
|
||||
, mkLaw "iff_or" "(A<->B)" "((A&B)|(!A&!B))"
|
||||
]
|
||||
|
||||
{-
|
||||
ghci> import Logic.Statement.Eval (bucket, Bucket(Tautology))
|
||||
ghci> all (== Tautology) $ map (\law -> bucket $ Iff (lawLhs law) (lawRhs law)) laws
|
||||
True
|
||||
-}
|
||||
|
||||
lookupLaw :: String -> Maybe Law
|
||||
lookupLaw name = listToMaybe $ filter (\law -> lawName law == name) laws
|
||||
|
||||
match
|
||||
:: Statement
|
||||
-- ^ pattern
|
||||
-> Statement
|
||||
-- ^ statement to search within
|
||||
-> Maybe [(String, Statement)]
|
||||
-- ^ mapping from pattern-statement atoms to search-statement parts
|
||||
match = aux []
|
||||
where
|
||||
aux
|
||||
:: [(String, Statement)]
|
||||
-> Statement
|
||||
-> Statement
|
||||
-> Maybe [(String, Statement)]
|
||||
aux mapping (Atom key) s = add mapping (key, s)
|
||||
aux mapping (Not p) (Not s) = aux mapping p s
|
||||
aux mapping (And p1 p2) (And s1 s2) = binary mapping (p1, s1) (p2, s2)
|
||||
aux mapping (Or p1 p2) (Or s1 s2) = binary mapping (p1, s1) (p2, s2)
|
||||
aux mapping (Implies p1 p2) (Implies s1 s2) = binary mapping (p1, s1) (p2, s2)
|
||||
aux mapping (Iff p1 p2) (Iff s1 s2) = binary mapping (p1, s1) (p2, s2)
|
||||
aux mapping pattern s = Nothing
|
||||
|
||||
add
|
||||
:: [(String, Statement)]
|
||||
-> (String, Statement)
|
||||
-> Maybe [(String, Statement)]
|
||||
add mapping entry@(key, s') =
|
||||
case lookup key mapping of
|
||||
Nothing -> Just (entry:mapping)
|
||||
Just existing
|
||||
| existing == s' -> Just mapping
|
||||
| otherwise -> Nothing
|
||||
|
||||
binary
|
||||
:: [(String, Statement)]
|
||||
-> (Statement, Statement)
|
||||
-> (Statement, Statement)
|
||||
-> Maybe [(String, Statement)]
|
||||
binary mapping (p1, s1) (p2, s2) = do
|
||||
mapping' <- aux mapping p1 s1
|
||||
aux mapping' p2 s2
|
||||
|
||||
{-
|
||||
ghci> f x = fromRight undefined $ eof stmt $ mkInput x
|
||||
ghci> match (f "(A&B)") (f "(p&(q|r))")
|
||||
Just [("B",Or (Atom "q") (Atom "r")),("A",Atom "p")]
|
||||
ghci> match (f "((A&B)|A)") (f "(p&(q|r))")
|
||||
Nothing
|
||||
ghci> match (f "((A&B)|A)") (f "((p&(q|r))|p)")
|
||||
Just [("B",Or (Atom "q") (Atom "r")),("A",Atom "p")]
|
||||
ghci> match (f "((A&B)|A)") (f "((p&(q|r))|q)")
|
||||
Nothing
|
||||
ghci> l = fromJust $ lookupLaw "and_or_distrib"
|
||||
ghci> l
|
||||
Law {lawName = "and_or_distrib", lawLhs = And (Atom "A") (Or (Atom "B") (Atom "C")), lawRhs = Or (And (Atom "A") (Atom "B")) (And (Atom "A") (Atom "C"))}
|
||||
ghci> match (lawLhs l) (f "(p&(q|r))")
|
||||
Just [("C",Atom "r"),("B",Atom "q"),("A",Atom "p")]
|
||||
-}
|
||||
|
||||
data SwapError
|
||||
= IndeterminateSwap
|
||||
-- ^ An atom in p2 doesn't exist in p1.
|
||||
-- Strictly: an atom in p2 doesn't exist in the result from `match`
|
||||
-- (matters only if `match` is implemented incorrectly).
|
||||
-- Theoretically if for atoms we used terms of a type instead of strings, we
|
||||
-- could avoid needing this error, but I think we still wouldn't be able
|
||||
-- to return a mapping from atom-type-1 to atom-type-2 in a type safe way.
|
||||
| NonMatchingPattern
|
||||
deriving Show
|
||||
|
||||
swap :: Statement -> Statement -> Statement -> Either SwapError Statement
|
||||
swap p1 p2 s = do
|
||||
mapping <- maybe (Left NonMatchingPattern) Right $ match p1 s
|
||||
maybe (Left IndeterminateSwap) Right $ aux mapping p2
|
||||
where
|
||||
aux :: [(String, Statement)] -> Statement -> Maybe Statement
|
||||
aux mapping (Atom key) = lookup key mapping
|
||||
aux mapping (Not p) = Not <$> aux mapping p
|
||||
aux mapping (And p1 p2) = And <$> aux mapping p1 <*> aux mapping p2
|
||||
aux mapping (Or p1 p2) = Or <$> aux mapping p1 <*> aux mapping p2
|
||||
aux mapping (Implies p1 p2) = Implies <$> aux mapping p1 <*> aux mapping p2
|
||||
aux mapping (Iff p1 p2) = Iff <$> aux mapping p1 <*> aux mapping p2
|
||||
|
||||
{-
|
||||
ghci> f x = fromRight undefined $ eof stmt $ mkInput x
|
||||
ghci> l = fromJust $ lookupLaw "and_or_distrib"
|
||||
ghci> l
|
||||
Law {lawName = "and_or_distrib", lawLhs = And (Atom "A") (Or (Atom "B") (Atom "C")), lawRhs = Or (And (Atom "A") (Atom "B")) (And (Atom "A") (Atom "C"))}
|
||||
ghci>
|
||||
ghci> match (f "(A&B)") (f "(p&(q|r))")
|
||||
Just [("B",Or (Atom "q") (Atom "r")),("A",Atom "p")]
|
||||
ghci> swap (f "(A&B)") (f "A") (f "(p&(q|r))")
|
||||
Right (Atom "p")
|
||||
ghci> swap (f "(A&B)") (f "B") (f "(p&(q|r))")
|
||||
Right (Or (Atom "q") (Atom "r"))
|
||||
ghci> swap (f "(A&B)") (f "C") (f "(p&(q|r))")
|
||||
Left Indeterminate
|
||||
ghci> swap (f "((A&B)->C)") (f "A") (f "(p&(q|r))")
|
||||
Left NonMatchingPattern
|
||||
ghci>
|
||||
ghci> x = f "(p&(q|r))"
|
||||
ghci> x
|
||||
And (Atom "p") (Or (Atom "q") (Atom "r"))
|
||||
ghci> serialize Plain x
|
||||
"(p&(q|r))"
|
||||
ghci> serialize Plain $ fromRight undefined $ swap (lawLhs l) (lawLhs l) x
|
||||
"(p&(q|r))"
|
||||
ghci> serialize Plain $ fromRight undefined $ swap (lawLhs l) (lawRhs l) x
|
||||
"((p&q)|(p&r))"
|
||||
ghci>
|
||||
ghci> x = f "(p&(!q|r))"
|
||||
ghci> serialize Plain x
|
||||
"(p&(!q|r))"
|
||||
ghci> serialize Plain $ fromRight undefined $ swap (lawLhs l) (lawLhs l) x
|
||||
"(p&(!q|r))"
|
||||
ghci> serialize Plain $ fromRight undefined $ swap (lawLhs l) (lawRhs l) x
|
||||
"((p&!q)|(p&r))"
|
||||
-}
|
||||
|
||||
data ReplaceError
|
||||
= IndeterminateReplace
|
||||
deriving Show
|
||||
|
||||
replace :: Statement -> Statement -> Statement -> Either ReplaceError [Statement]
|
||||
replace p1 p2 = firstLeft . aux
|
||||
where
|
||||
aux :: Statement -> [Either ReplaceError Statement]
|
||||
aux s =
|
||||
case swap p1 p2 s of
|
||||
Left IndeterminateSwap -> [Left IndeterminateReplace]
|
||||
-- ^ terminate here because in `replace` we stop at the first Left
|
||||
Left NonMatchingPattern -> deeper s
|
||||
Right s' -> (Right s'):(deeper s)
|
||||
|
||||
deeper :: Statement -> [Either ReplaceError Statement]
|
||||
deeper (Atom key) = []
|
||||
deeper (Not s) = do
|
||||
e <- aux s
|
||||
return $ Not <$> e
|
||||
deeper (And s1 s2) = binary And s1 s2
|
||||
deeper (Or s1 s2) = binary Or s1 s2
|
||||
deeper (Implies s1 s2) = binary Implies s1 s2
|
||||
deeper (Iff s1 s2) = binary Iff s1 s2
|
||||
|
||||
binary constructor s1 s2 =
|
||||
[constructor <$> e1 <*> (Right s2) | e1 <- aux s1] ++
|
||||
[constructor <$> (Right s1) <*> e2 | e2 <- aux s2]
|
||||
|
||||
firstLeft :: [Either a b] -> Either a [b]
|
||||
firstLeft [] = Right []
|
||||
firstLeft ((Left a):_) = Left a
|
||||
firstLeft ((Right b):xs) = (b:) <$> firstLeft xs
|
||||
|
||||
data Direction
|
||||
= Forward
|
||||
| Reverse
|
||||
deriving (Eq, Show)
|
||||
|
||||
data LawsGraphEdge = LawsGraphEdge
|
||||
{ lgeDirection :: Direction
|
||||
, lgeIndex :: Integer
|
||||
, lgeLaw :: Law
|
||||
} deriving Eq
|
||||
|
||||
instance Show LawsGraphEdge where
|
||||
show edge =
|
||||
"LawsGraphEdge{"
|
||||
<> (
|
||||
case lgeDirection edge of
|
||||
Forward -> "> "
|
||||
Reverse -> "< "
|
||||
)
|
||||
<> lawName (lgeLaw edge)
|
||||
<> " "
|
||||
<> show (lgeIndex edge)
|
||||
<> "}"
|
||||
|
||||
bfsLaws :: Statement -> Statement -> Maybe [LawsGraphEdge]
|
||||
bfsLaws = bfs getLawsGraphEdges
|
||||
|
||||
getLawsGraphEdges :: Statement -> [(Statement, LawsGraphEdge)]
|
||||
getLawsGraphEdges s = concat $ rights $ map aux laws
|
||||
-- ^ `rights` here because we can't apply
|
||||
-- e.g. or_contr_eq forwards without inventing B
|
||||
-- and the `Left` case is `Left IndeterminateReplace`
|
||||
where
|
||||
aux :: Law -> Either ReplaceError [(Statement, LawsGraphEdge)]
|
||||
aux law = do
|
||||
forward <- edges Forward law
|
||||
reverse <- edges Reverse law
|
||||
return $ forward ++ reverse
|
||||
|
||||
replaceds :: Direction -> Law -> Either ReplaceError [Statement]
|
||||
replaceds direction law =
|
||||
let
|
||||
(pattern1, pattern2) =
|
||||
case direction of
|
||||
Forward -> (lawLhs law, lawRhs law)
|
||||
Reverse -> (lawRhs law, lawLhs law)
|
||||
in replace pattern1 pattern2 s
|
||||
|
||||
mkEdges :: Direction -> Law -> [Statement] -> [(Statement, LawsGraphEdge)]
|
||||
mkEdges direction law statements = do
|
||||
(index, s') <- zip [0..] statements
|
||||
return (s', LawsGraphEdge direction index law)
|
||||
|
||||
edges :: Direction -> Law -> Either ReplaceError [(Statement, LawsGraphEdge)]
|
||||
edges direction law = mkEdges direction law <$> replaceds direction law
|
||||
|
||||
{-
|
||||
ghci> fromString x = fromRight undefined $ eof stmt $ mkInput x
|
||||
ghci> niceEdges = map (\edge -> (if lgeReverse edge then "< " else "> ") <> lawName (lgeLaw edge) <> " " <> show (lgeIndex edge))
|
||||
ghci>
|
||||
ghci> niceEdges <$> bfsLaws (fromString "(p|!q)") (fromString "(p|!q)")
|
||||
Just []
|
||||
ghci> niceEdges <$> bfsLaws (fromString "!!(p|!q)") (fromString "(p|!q)")
|
||||
Just ["> dbl_neg 0"]
|
||||
ghci> niceEdges <$> bfsLaws (fromString "(!!p|!q)") (fromString "(p|!q)")
|
||||
Just ["> dbl_neg 1"]
|
||||
ghci> niceEdges <$> bfsLaws (fromString "(p|!!!q)") (fromString "(p|!q)")
|
||||
Just ["> dbl_neg 2"]
|
||||
ghci> niceEdges <$> bfsLaws (fromString "(p|p)") (fromString "p")
|
||||
Just ["> or_symm_eq 0"]
|
||||
ghci> niceEdges <$> bfsLaws (fromString "p") (fromString "(p|p)")
|
||||
Just ["< or_symm_eq 0"]
|
||||
ghci>
|
||||
ghci> niceEdges <$> bfsLaws (fromString "!(!p&(q|q))") (fromString "(p|!q)")
|
||||
Just ["> dbl_neg 1","> or_symm_eq 5","< not_and_distrib 0"]
|
||||
ghci> niceEdges <$> bfsLaws (fromString "!(!(p&p)&(q|q))") (fromString "(p|!q)")
|
||||
Just ["> dbl_neg 1","> and_symm_eq 3","> or_symm_eq 7","< not_and_distrib 0"]
|
||||
ghci>
|
||||
ghci> import Data.Time.POSIX (getPOSIXTime)
|
||||
ghci> time f = getPOSIXTime >>= \t0 -> f >> getPOSIXTime >>= \t1 -> return $ t1 - t0
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "p") (fromString "p")
|
||||
Just []
|
||||
0.000087114s
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "!!p") (fromString "p")
|
||||
Just ["> dbl_neg 0"]
|
||||
0.000201159s
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "!!!!p") (fromString "p")
|
||||
Just ["> dbl_neg 0","> dbl_neg 0"]
|
||||
0.000444047s
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "!!!!!!p") (fromString "p")
|
||||
Just ["> dbl_neg 0","> dbl_neg 0","> dbl_neg 0"]
|
||||
0.001260947s
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "!!!!!!!!p") (fromString "p")
|
||||
Just ["> dbl_neg 0","> dbl_neg 0","> dbl_neg 0","> dbl_neg 0"]
|
||||
0.021864298s
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "!!!!!!!!!!p") (fromString "p")
|
||||
Just ["> dbl_neg 0","> dbl_neg 0","> dbl_neg 0","> dbl_neg 0","> dbl_neg 0"]
|
||||
3.244101767s
|
||||
ghci> time $ putStrLn $ show $ niceEdges <$> bfsLaws (fromString "!!!!!!!!!!!!p") (fromString "p")
|
||||
Just ["> dbl_neg 0","> dbl_neg 0","> dbl_neg 0","> dbl_neg 0","> dbl_neg 0","> dbl_neg 0"]
|
||||
3066.211460539s
|
||||
-}
|
||||
|
||||
data LawsGraphPath = LawsGraphPath
|
||||
{ lgpStart :: Statement
|
||||
, lgpGoal :: Statement
|
||||
, lgpEdges :: [LawsGraphEdge]
|
||||
} deriving Show
|
||||
|
||||
verifyLawsPath
|
||||
:: LawsGraphPath
|
||||
-> Either (VerifyPathError Statement LawsGraphEdge) [Statement]
|
||||
verifyLawsPath path =
|
||||
verifyPath getLawsGraphEdges (lgpGoal path) (lgpStart path) (lgpEdges path)
|
||||
|
||||
lawsPathExample1 :: LawsGraphPath
|
||||
lawsPathExample1 = LawsGraphPath start goal edges
|
||||
where
|
||||
start = Implies (Atom "p") (Atom "q")
|
||||
goal = Implies (Not (Atom "q")) (Not (Atom "p"))
|
||||
edges =
|
||||
[ LawsGraphEdge Forward 1 $ law "dbl_neg"
|
||||
, LawsGraphEdge Forward 0 $ law "implies_and"
|
||||
, LawsGraphEdge Forward 0 $ law "and_comm"
|
||||
, LawsGraphEdge Reverse 0 $ law "implies_and"
|
||||
]
|
||||
law = fromJust . lookupLaw
|
||||
|
||||
{-
|
||||
ghci> import Logic.Statement.Serialize (serialize, SerializeFormat(..))
|
||||
ghci> map (serialize Plain) <$> verifyLawsPath lawsPathExample1
|
||||
Right ["(p->q)","(!!p->q)","!(!!p&!q)","!(!q&!!p)","(!q->!p)"]
|
||||
-}
|
Loading…
Add table
Add a link
Reference in a new issue