twee-0.1: executable/Main.hs
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, CPP, GeneralizedNewtypeDeriving, TypeFamilies, RecordWildCards, FlexibleContexts, UndecidableInstances, NondecreasingIndentation #-}
#include "errors.h"
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
import Control.Monad
import Control.Monad.Trans.State.Strict
import Data.Char
import Data.Either
import Twee hiding (info)
import Twee.Base hiding (char, lookup, (<>))
import Twee.Rule
import Twee.Utils
import Twee.Queue
import Data.Ord
import qualified Twee.Indexes as Indexes
import qualified Data.Map.Strict as Map
import qualified Twee.KBO as KBO
import qualified Twee.LPO as LPO
import qualified Data.Set as Set
import Data.Reflection
import qualified Data.IntMap as IntMap
import Data.IntMap(IntMap)
import Data.List.Split
import Data.List
import Data.Maybe
import Jukebox.Options
import Jukebox.Toolbox
import Jukebox.Name
import qualified Jukebox.Form as Jukebox
import Jukebox.Form hiding ((:=:), Var, Symbolic(..), Term)
import qualified Twee.Label as Label
parseInitialState :: OptionParser (Twee f)
parseInitialState =
go <$> maxSize <*> general
<*> groundJoin <*> conn <*> set <*> setGoals <*> tracing <*> moreTracing <*> lweight <*> rweight <*> splits <*> cpSetSize <*> mixFIFO <*> mixPrio <*> skipComposite <*> interreduce <*> unsafeInterreduce <*> cancel <*> cancelSize <*> cancelConsts <*> atomicCancellation
where
go maxSize general groundJoin conn set setGoals tracing moreTracing lweight rweight splits cpSetSize mixFIFO mixPrio skipComposite interreduce unsafeInterreduce cancel cancelSize cancelConsts atomicCancellation =
(initialState mixFIFO mixPrio) {
maxSize = maxSize,
cpSplits = splits,
minimumCPSetSize = cpSetSize,
useGeneralSuperpositions = general,
useGroundJoining = groundJoin,
useConnectedness = conn,
useSetJoining = set,
useSetJoiningForGoals = setGoals,
useCancellation = cancel,
maxCancellationSize = cancelSize,
atomicCancellation = atomicCancellation,
unifyConstantsInCancellation = cancelConsts,
useInterreduction = interreduce,
useUnsafeInterreduction = unsafeInterreduce,
skipCompositeSuperpositions = skipComposite,
tracing = tracing,
moreTracing = moreTracing,
lhsWeight = lweight,
rhsWeight = rweight }
maxSize = flag "max-size" ["Maximum critical pair size"] Nothing (Just <$> argNum)
general = not <$> bool "no-general-superpositions" ["Disable considering only general superpositions"]
groundJoin = not <$> bool "no-ground-join" ["Disable ground joinability testing"]
conn = not <$> bool "no-connectedness" ["Disable connectedness testing"]
set = bool "set-join" ["Join by computing set of normal forms"]
setGoals = not <$> bool "no-set-join-goals" ["Disable joining goals by computing set of normal forms"]
tracing = not <$> bool "no-tracing" ["Disable tracing output"]
moreTracing = bool "more-tracing" ["Produce even more tracing output"]
lweight = flag "lhs-weight" ["Weight given to LHS of critical pair (default 2)"] 2 argNum
rweight = flag "rhs-weight" ["Weight given to RHS of critical pair (default 1)"] 1 argNum
splits = flag "split" ["Split CP sets into this many pieces on selection (default 20)"] 20 argNum
cpSetSize = flag "cp-set-minimum" ["Decay CP sets into single CPs when they get this small (default 20)"] 20 argNum
mixFIFO = flag "mix-fifo" ["Take this many CPs at a time from FIFO (default 0)"] 0 argNum
mixPrio = flag "mix-prio" ["Take this many CPs at a time from priority queue (default 10)"] 10 argNum
interreduce = bool "interreduce" ["Enable interreduction of left hand sides"]
unsafeInterreduce = not <$> bool "safe-interreduce" ["Disable some incomplete interreductions"]
cancel = not <$> bool "no-cancellation" ["Disable cancellation"]
cancelSize = flag "max-cancellation-size" ["Maximum size of cancellation laws"] Nothing (Just <$> argNum)
cancelConsts = bool "unify-consts-in-cancellation" ["Allow unification with a constant in cancellation"]
skipComposite = not <$> bool "composite-superpositions" ["Generate composite superpositions"]
atomicCancellation = not <$> bool "compound-cancellation" ["Allow cancellation laws to have non-atomic RHS"]
data Order = KBO | LPO
parseOrder :: OptionParser Order
parseOrder =
f <$>
bool "lpo" ["Use lexicographic path ordering instead of KBO"]
where
f False = KBO
f True = LPO
parsePrecedence :: OptionParser [String]
parsePrecedence =
fmap (splitOn ",")
(flag "precedence" ["List of functions in descending order of precedence"] [] (arg "<function>" "expected a function name" Just))
data Constant =
Constant {
conIndex :: Int,
conArity :: Int,
conSize :: Int,
conName :: String }
| Builtin Builtin
data Builtin = CFalse | CTrue | CEquals deriving (Eq, Ord)
instance Eq Constant where
x == y = x `compare` y == EQ
instance Ord Constant where
compare Constant{conIndex = x} Constant{conIndex = y} = compare x y
compare Constant{} Builtin{} = LT
compare Builtin{} Constant{} = GT
compare (Builtin x) (Builtin y) = compare x y
instance Sized Constant where
size Constant{conSize = n} = fromIntegral n
size Builtin{} = 0
instance Arity Constant where
arity Constant{conSize = n} = n
arity (Builtin CEquals) = 2
arity (Builtin _) = 0
instance Pretty Constant where
pPrint Constant{conName = name} = text name
pPrint (Builtin CEquals) = text "$equals"
pPrint (Builtin CTrue) = text "$true"
pPrint (Builtin CFalse) = text "$false"
instance PrettyTerm Constant where
termStyle con@Constant{}
| not (any isAlphaNum (conName con)) =
case conArity con of
1 -> prefix
2 -> infixStyle 5
_ -> uncurried
termStyle _ = uncurried
instance Given (IntMap Constant) => Numbered Constant where
fromInt 0 = Builtin CFalse
fromInt 1 = Builtin CTrue
fromInt 2 = Builtin CEquals
fromInt n = IntMap.findWithDefault __ (n-3) given
toInt Constant{conIndex = n} = n+3
toInt (Builtin CFalse) = 0
toInt (Builtin CTrue) = 1
toInt (Builtin CEquals) = 2
instance (Given Order, Given (IntMap Constant)) => Ordered (Extended Constant) where
lessEq =
case given of
KBO -> KBO.lessEq
LPO -> LPO.lessEq
lessIn =
case given of
KBO -> KBO.lessIn
LPO -> LPO.lessIn
instance Label.Labelled Jukebox.Function where
cache = functionCache
{-# NOINLINE functionCache #-}
functionCache :: Label.Cache Jukebox.Function
functionCache = Label.mkCache
instance Numbered Jukebox.Function where
fromInt n = fromMaybe __ (Label.find n)
toInt = Label.label
toTwee :: Problem Clause -> ([Equation Jukebox.Function], [Term Jukebox.Function])
toTwee prob = (lefts eqs, goals)
where
eq Input{what = Clause (Bind _ [Pos (t Jukebox.:=: u)])} =
Left (tm t :=: tm u)
eq Input{what = Clause (Bind _ [Neg (t Jukebox.:=: u)])} =
Right (tm t :=: tm u)
eq _ = ERROR("Problem is not unit equality")
eqs = map eq prob
goals =
case rights eqs of
[] -> []
[t :=: u] -> [t, u]
_ -> ERROR("Problem is not unit equality")
tm (Jukebox.Var (Unique x _ _ ::: _)) =
build (var (MkVar (fromIntegral x)))
tm (f :@: ts) =
app f (map tm ts)
addNarrowing ::
Given (IntMap Constant) =>
([Equation (Extended Constant)], [Term (Extended Constant)]) ->
([Equation (Extended Constant)], [Term (Extended Constant)])
addNarrowing (axioms, goals)
| length goals < 2 = (axioms, [app false [], app true []])
where
false = Function (Builtin CFalse)
true = Function (Builtin CTrue)
addNarrowing (axioms, goals)
| length goals >= 2 && all isGround goals = (axioms, goals)
addNarrowing (axioms, [t, u])
| otherwise = (axioms ++ equalities, [app false [], app true []])
where
false = Function (Builtin CFalse)
true = Function (Builtin CTrue)
equals = Function (Builtin CEquals)
equalities =
[app equals [build (var (MkVar 0)), build (var (MkVar 0))] :=: app true [],
app equals [t, u] :=: app false []]
addNarrowing _ =
ERROR("Don't know how to handle several non-ground goals")
runTwee :: Twee (Extended Constant) -> Order -> [String] -> Problem Clause -> IO Answer
runTwee state order precedence obligs = do
let (axioms0, goals0) = toTwee obligs
prec c = (isNothing (elemIndex (base c) precedence),
fmap negate (elemIndex (base c) precedence),
negate (occ (toFun c) (axioms0, goals0)))
fs0 = map fromFun (usort (funs (axioms0, goals0)))
fs1 = sortBy (comparing prec) fs0
fs2 = zipWith (\i (c ::: (FunType args _)) -> Constant i (length args) 1 (show c)) [1..] fs1
m = IntMap.fromList [(conIndex f, f) | f <- fs2]
m' = Map.fromList (zip fs1 (map Function fs2))
give m $ give order $ do
let replace = build . mapFun (toFun . flip (Map.findWithDefault __) m' . fromFun)
axioms1 = [replace t :=: replace u | t :=: u <- axioms0]
goals1 = map replace goals0
(axioms2, goals2) = addNarrowing (axioms1, goals1)
putStrLn "Axioms:"
mapM_ prettyPrint axioms2
putStrLn "\nGoals:"
mapM_ prettyPrint goals2
putStrLn "\nGo!"
let
identical xs = not (Set.null (foldr1 Set.intersection xs))
loop = do
res <- complete1
goals <- gets goals
when (res && (length goals <= 1 || not (identical goals))) loop
s =
flip execState (addGoals (map Set.singleton goals2) state) $ do
mapM_ newEquation axioms2
loop
rs = map (critical . modelled . peel) (Indexes.elems (labelledRules s))
putStrLn "\nFinal rules:"
mapM_ prettyPrint rs
putStrLn ""
putStrLn (report s)
putStrLn "Normalised goal terms:"
forM_ goals2 $ \t ->
prettyPrint (Rule Oriented t (result (normalise s t)))
return $
case () of
_ | identical (goals s) -> Unsatisfiable
| isJust (maxSize s) -> NoAnswer GaveUp
| otherwise -> Satisfiable
main = do
let twee = Tool "twee" "twee - the Wonderful Equation Engine" "1" "Proves equations."
join . parseCommandLine twee . tool twee $
greetingBox twee =>>
allFilesBox <*>
(parseProblemBox =>>=
toFofBox =>>=
clausifyBox =>>=
allObligsBox <*>
(runTwee <$> parseInitialState <*> parseOrder <*> parsePrecedence))