quickspec-2.2: src/QuickSpec/Internal/Explore.hs
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE FlexibleContexts, PatternGuards, CPP #-}
module QuickSpec.Internal.Explore where
import QuickSpec.Internal.Explore.Polymorphic
import QuickSpec.Internal.Testing
import QuickSpec.Internal.Pruning
import QuickSpec.Internal.Term
import QuickSpec.Internal.Type
import QuickSpec.Internal.Utils
import QuickSpec.Internal.Prop
import QuickSpec.Internal.Terminal
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict
import Text.Printf
#if! MIN_VERSION_base(4,9,0)
import Data.Semigroup(Semigroup(..))
#endif
import Data.List
newtype Enumerator a = Enumerator { enumerate :: Int -> [[a]] -> [a] }
-- N.B. order matters!
-- Later enumerators get to see terms which were generated by earlier ones.
instance Semigroup (Enumerator a) where
e1 <> e2 = Enumerator $ \n tss ->
let us = enumerate e1 n tss
vs = enumerate e2 n (appendAt n us tss)
in us ++ vs
instance Monoid (Enumerator a) where
mempty = Enumerator (\_ _ -> [])
mappend = (<>)
mapEnumerator :: ([a] -> [a]) -> Enumerator a -> Enumerator a
mapEnumerator f e =
Enumerator $ \n tss ->
f (enumerate e n tss)
filterEnumerator :: (a -> Bool) -> Enumerator a -> Enumerator a
filterEnumerator p e =
mapEnumerator (filter p) e
enumerateConstants :: Sized a => [a] -> Enumerator a
enumerateConstants ts = Enumerator (\n _ -> [t | t <- ts, size t == n])
enumerateApplications :: Apply a => Enumerator a
enumerateApplications = Enumerator $ \n tss ->
[ unPoly v
| i <- [0..n],
t <- tss !! i,
u <- tss !! (n-i),
Just v <- [tryApply (poly t) (poly u)] ]
filterUniverse :: Typed f => Universe -> Enumerator (Term f) -> Enumerator (Term f)
filterUniverse univ e =
filterEnumerator (`usefulForUniverse` univ) e
sortTerms :: Ord b => (a -> b) -> Enumerator a -> Enumerator a
sortTerms measure e =
mapEnumerator (sortBy' measure) e
quickSpec ::
(Ord fun, Ord norm, Sized fun, Typed fun, Ord result, PrettyTerm fun,
MonadPruner (Term fun) norm m, MonadTester testcase (Term fun) m, MonadTerminal m) =>
(Prop (Term fun) -> m ()) ->
(Term fun -> testcase -> Maybe result) ->
Int -> Int -> (Type -> VariableUse) -> Universe -> Enumerator (Term fun) -> m ()
quickSpec present eval maxSize maxCommutativeSize use univ enum = do
let
state0 = initialState use univ (\t -> size t <= maxCommutativeSize) eval
loop m n _ | m > n = return ()
loop m n tss = do
putStatus (printf "enumerating terms of size %d" m)
let
ts = enumerate (filterUniverse univ enum) m tss
total = length ts
consider (i, t) = do
putStatus (printf "testing terms of size %d: %d/%d" m i total)
res <- explore t
putStatus (printf "testing terms of size %d: %d/%d" m i total)
lift $ mapM_ present (result_props res)
case res of
Accepted _ -> return True
Rejected _ -> return False
us <- map snd <$> filterM consider (zip [1 :: Int ..] ts)
clearStatus
loop (m+1) n (appendAt m us tss)
evalStateT (loop 0 maxSize (repeat [])) state0
----------------------------------------------------------------------
-- Functions that are not really to do with theory exploration,
-- but are useful for printing the output nicely.
----------------------------------------------------------------------
pPrintSignature :: (Pretty a, Typed a) => [a] -> Doc
pPrintSignature funs =
text "== Functions ==" $$
vcat (map pPrintDecl decls)
where
decls = [ (prettyShow f, pPrintType (typ f)) | f <- funs ]
maxWidth = maximum (0:map (length . fst) decls)
pad xs = nest (maxWidth - length xs) (text xs)
pPrintDecl (name, ty) =
pad name <+> text "::" <+> ty
-- Put an equation that defines the function f into the form f lhs = rhs.
-- An equation defines f if:
-- * it is of the form f lhs = rhs (or vice versa).
-- * f is not a background function.
-- * lhs only contains background functions.
-- * rhs does not contain f.
-- * all vars in rhs appear in lhs
prettyDefinition :: Eq fun => [fun] -> Prop (Term fun) -> Prop (Term fun)
prettyDefinition cons (lhs :=>: t :=: u)
| Just (f, ts) <- defines u,
f `notElem` funs t,
null (usort (vars t) \\ vars ts) =
lhs :=>: u :=: t
-- In the case where t defines f, the equation is already oriented correctly
| otherwise = lhs :=>: t :=: u
where
defines (Fun f :@: ts)
| f `elem` cons,
all (`notElem` cons) (funs ts) = Just (f, ts)
defines _ = Nothing
-- Transform x+(y+z) = y+(x+z) into associativity, if + is commutative
prettyAC :: (Eq f, Eq norm) => (Term f -> norm) -> Prop (Term f) -> Prop (Term f)
prettyAC norm (lhs :=>: Fun f :@: [Var x, Fun f1 :@: [Var y, Var z]] :=: Fun f2 :@: [Var y1, Fun f3 :@: [Var x1, Var z1]])
| f == f1, f1 == f2, f2 == f3,
x == x1, y == y1, z == z1,
x /= y, y /= z, x /= z,
norm (Fun f :@: [Var x, Var y]) == norm (Fun f :@: [Var y, Var x]) =
lhs :=>: Fun f :@: [Fun f :@: [Var x, Var y], Var z] :=: Fun f :@: [Var x, Fun f :@: [Var y, Var z]]
prettyAC _ prop = prop
-- Add a type signature when printing the equation x = y.
disambiguatePropType :: Prop (Term fun) -> Doc
disambiguatePropType (_ :=>: (Var x) :=: Var _) =
text "::" <+> pPrintType (typ x)
disambiguatePropType _ = pPrintEmpty