{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Data.SBV.Provers.Prover (
SMTSolver(..), SMTConfig(..), Predicate, Provable(..), Goal
, ThmResult(..), SatResult(..), AllSatResult(..), SafeResult(..), OptimizeResult(..), SMTResult(..)
, SExecutable(..), isSafe
, runSMT, runSMTWith
, SatModel(..), Modelable(..), displayModels, extractModels
, getModelDictionaries, getModelValues, getModelUninterpretedValues
, boolector, cvc4, yices, z3, mathSAT, abc, defaultSMTCfg
) where
import Control.Monad (when, unless)
import Control.DeepSeq (rnf, NFData(..))
import Control.Concurrent.Async (async, waitAny, asyncThreadId, Async)
import Control.Exception (finally, throwTo, AsyncException(ThreadKilled))
import System.IO.Unsafe (unsafeInterleaveIO)
import System.Directory (getCurrentDirectory)
import Data.Time (getZonedTime, NominalDiffTime, UTCTime, getCurrentTime, diffUTCTime)
import Data.List (intercalate, isPrefixOf)
import Data.SBV.Core.Data
import Data.SBV.Core.Symbolic
import Data.SBV.SMT.SMT
import Data.SBV.Utils.TDiff
import qualified Data.SBV.Control as Control
import qualified Data.SBV.Control.Query as Control
import qualified Data.SBV.Control.Utils as Control
import GHC.Stack
import qualified Data.SBV.Provers.Boolector as Boolector
import qualified Data.SBV.Provers.CVC4 as CVC4
import qualified Data.SBV.Provers.Yices as Yices
import qualified Data.SBV.Provers.Z3 as Z3
import qualified Data.SBV.Provers.MathSAT as MathSAT
import qualified Data.SBV.Provers.ABC as ABC
mkConfig :: SMTSolver -> SMTLibVersion -> [Control.SMTOption] -> SMTConfig
mkConfig s smtVersion startOpts = SMTConfig { verbose = False
, timing = NoTiming
, printBase = 10
, printRealPrec = 16
, transcript = Nothing
, solver = s
, smtLibVersion = smtVersion
, satCmd = "(check-sat)"
, allSatMaxModelCount = Nothing
, isNonModelVar = const False
, roundingMode = RoundNearestTiesToEven
, solverSetOptions = startOpts
, ignoreExitCode = False
, redirectVerbose = Nothing
}
allOnStdOut :: Control.SMTOption
allOnStdOut = Control.OptionKeyword ":diagnostic-output-channel" [show "stdout"]
boolector :: SMTConfig
boolector = mkConfig Boolector.boolector SMTLib2 []
cvc4 :: SMTConfig
cvc4 = mkConfig CVC4.cvc4 SMTLib2 [allOnStdOut]
yices :: SMTConfig
yices = mkConfig Yices.yices SMTLib2 []
z3 :: SMTConfig
z3 = mkConfig Z3.z3 SMTLib2 [ Control.OptionKeyword ":smtlib2_compliant" ["true"]
, allOnStdOut
]
mathSAT :: SMTConfig
mathSAT = mkConfig MathSAT.mathSAT SMTLib2 [allOnStdOut]
abc :: SMTConfig
abc = mkConfig ABC.abc SMTLib2 [allOnStdOut]
defaultSMTCfg :: SMTConfig
defaultSMTCfg = z3
type Predicate = Symbolic SBool
type Goal = Symbolic ()
class Provable a where
forAll_ :: a -> Predicate
forAll :: [String] -> a -> Predicate
forSome_ :: a -> Predicate
forSome :: [String] -> a -> Predicate
prove :: a -> IO ThmResult
prove = proveWith defaultSMTCfg
proveWith :: SMTConfig -> a -> IO ThmResult
proveWith = runWithQuery False $ ThmResult <$> Control.getSMTResult
sat :: a -> IO SatResult
sat = satWith defaultSMTCfg
satWith :: SMTConfig -> a -> IO SatResult
satWith = runWithQuery True $ SatResult <$> Control.getSMTResult
allSat :: a -> IO AllSatResult
allSat = allSatWith defaultSMTCfg
allSatWith :: SMTConfig -> a -> IO AllSatResult
allSatWith = runWithQuery True $ AllSatResult <$> Control.getAllSatResult
optimize :: OptimizeStyle -> a -> IO OptimizeResult
optimize = optimizeWith defaultSMTCfg
optimizeWith :: SMTConfig -> OptimizeStyle -> a -> IO OptimizeResult
optimizeWith config style = runWithQuery True opt config
where opt = do objectives <- Control.getObjectives
qinps <- Control.getQuantifiedInputs
when (null objectives) $
error $ unlines [ ""
, "*** Data.SBV: Unsupported call to optimize when no objectives are present."
, "*** Use \"sat\" for plain satisfaction"
]
unless (supportsOptimization (capabilities (solver config))) $
error $ unlines [ ""
, "*** Data.SBV: The backend solver " ++ show (name (solver config)) ++ "does not support optimization goals."
, "*** Please use a solver that has support, such as z3"
]
let needsUniversalOpt = let universals = [s | (ALL, (s, _)) <- qinps]
check (x, y) nm = [nm | any (`elem` universals) [x, y]]
isUniversal (Maximize nm xy) = check xy nm
isUniversal (Minimize nm xy) = check xy nm
isUniversal (AssertSoft nm xy _) = check xy nm
in concatMap isUniversal objectives
unless (null needsUniversalOpt) $
error $ unlines [ ""
, "*** Data.SBV: Problem needs optimization of universally quantified metric(s):"
, "***"
, "*** " ++ unwords needsUniversalOpt
, "***"
, "*** Optimization is only meaningful existentially quantified values."
]
let optimizerDirectives = concatMap minmax objectives ++ priority style
where mkEq (x, y) = "(assert (= " ++ show x ++ " " ++ show y ++ "))"
minmax (Minimize _ xy@(_, v)) = [mkEq xy, "(minimize " ++ show v ++ ")"]
minmax (Maximize _ xy@(_, v)) = [mkEq xy, "(maximize " ++ show v ++ ")"]
minmax (AssertSoft nm xy@(_, v) mbp) = [mkEq xy, "(assert-soft " ++ show v ++ penalize mbp ++ ")"]
where penalize DefaultPenalty = ""
penalize (Penalty w mbGrp)
| w <= 0 = error $ unlines [ "SBV.AssertSoft: Goal " ++ show nm ++ " is assigned a non-positive penalty: " ++ shw
, "All soft goals must have > 0 penalties associated."
]
| True = " :weight " ++ shw ++ maybe "" group mbGrp
where shw = show (fromRational w :: Double)
group g = " :id " ++ g
priority Lexicographic = []
priority Independent = ["(set-option :opt.priority box)"]
priority (Pareto _) = ["(set-option :opt.priority pareto)"]
mapM_ (Control.send True) optimizerDirectives
case style of
Lexicographic -> LexicographicResult <$> Control.getLexicographicOptResults
Independent -> IndependentResult <$> Control.getIndependentOptResults (map objectiveName objectives)
Pareto mbN -> ParetoResult <$> Control.getParetoOptResults mbN
isVacuous :: a -> IO Bool
isVacuous = isVacuousWith defaultSMTCfg
isVacuousWith :: SMTConfig -> a -> IO Bool
isVacuousWith cfg a =
fst <$> runSymbolic (SMTMode ISetup True cfg) (forSome_ a >> Control.query check)
where check = do cs <- Control.checkSat
case cs of
Control.Unsat -> return True
Control.Sat -> return False
Control.Unk -> error "SBV: isVacuous: Solver returned unknown!"
isTheorem :: a -> IO Bool
isTheorem = isTheoremWith defaultSMTCfg
isTheoremWith :: SMTConfig -> a -> IO Bool
isTheoremWith cfg p = do r <- proveWith cfg p
case r of
ThmResult Unsatisfiable{} -> return True
ThmResult Satisfiable{} -> return False
_ -> error $ "SBV.isTheorem: Received:\n" ++ show r
isSatisfiable :: a -> IO Bool
isSatisfiable = isSatisfiableWith defaultSMTCfg
isSatisfiableWith :: SMTConfig -> a -> IO Bool
isSatisfiableWith cfg p = do r <- satWith cfg p
case r of
SatResult Satisfiable{} -> return True
SatResult Unsatisfiable{} -> return False
_ -> error $ "SBV.isSatisfiable: Received: " ++ show r
proveWithAll :: [SMTConfig] -> a -> IO [(Solver, NominalDiffTime, ThmResult)]
proveWithAll = (`sbvWithAll` proveWith)
proveWithAny :: [SMTConfig] -> a -> IO (Solver, NominalDiffTime, ThmResult)
proveWithAny = (`sbvWithAny` proveWith)
satWithAll :: [SMTConfig] -> a -> IO [(Solver, NominalDiffTime, SatResult)]
satWithAll = (`sbvWithAll` satWith)
satWithAny :: [SMTConfig] -> a -> IO (Solver, NominalDiffTime, SatResult)
satWithAny = (`sbvWithAny` satWith)
generateSMTBenchmark :: Bool -> a -> IO String
generateSMTBenchmark isSat a = do
t <- getZonedTime
let comments = ["Automatically created by SBV on " ++ show t]
cfg = defaultSMTCfg { smtLibVersion = SMTLib2 }
(_, res) <- runSymbolic (SMTMode ISetup isSat cfg) $ (if isSat then forSome_ else forAll_) a >>= output
let SMTProblem{smtLibPgm} = Control.runProofOn cfg isSat comments res
out = show (smtLibPgm cfg)
return $ out ++ "\n(check-sat)\n"
instance Provable Predicate where
forAll_ = id
forAll [] = id
forAll xs = error $ "SBV.forAll: Extra unmapped name(s) in predicate construction: " ++ intercalate ", " xs
forSome_ = id
forSome [] = id
forSome xs = error $ "SBV.forSome: Extra unmapped name(s) in predicate construction: " ++ intercalate ", " xs
instance Provable SBool where
forAll_ = return
forAll _ = return
forSome_ = return
forSome _ = return
instance (SymWord a, Provable p) => Provable (SBV a -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ k a
forAll (s:ss) k = forall s >>= \a -> forAll ss $ k a
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ k a
forSome (s:ss) k = exists s >>= \a -> forSome ss $ k a
forSome [] k = forSome_ k
instance (HasKind a, HasKind b, Provable p) => Provable (SArray a b -> p) where
forAll_ k = declNewSArray (\t -> "array_" ++ show t) >>= \a -> forAll_ $ k a
forAll (s:ss) k = declNewSArray (const s) >>= \a -> forAll ss $ k a
forAll [] k = forAll_ k
forSome_ _ = error "SBV.forSome: Existential arrays are not currently supported."
forSome _ _ = error "SBV.forSome: Existential arrays are not currently supported."
instance (HasKind a, HasKind b, Provable p) => Provable (SFunArray a b -> p) where
forAll_ k = declNewSFunArray Nothing >>= \a -> forAll_ $ k a
forAll (_:ss) k = declNewSFunArray Nothing >>= \a -> forAll ss $ k a
forAll [] k = forAll_ k
forSome_ _ = error "SBV.forSome: Existential arrays are not currently supported."
forSome _ _ = error "SBV.forSome: Existential arrays are not currently supported."
instance (SymWord a, SymWord b, Provable p) => Provable ((SBV a, SBV b) -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ \b -> k (a, b)
forAll (s:ss) k = forall s >>= \a -> forAll ss $ \b -> k (a, b)
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ \b -> k (a, b)
forSome (s:ss) k = exists s >>= \a -> forSome ss $ \b -> k (a, b)
forSome [] k = forSome_ k
instance (SymWord a, SymWord b, SymWord c, Provable p) => Provable ((SBV a, SBV b, SBV c) -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ \b c -> k (a, b, c)
forAll (s:ss) k = forall s >>= \a -> forAll ss $ \b c -> k (a, b, c)
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ \b c -> k (a, b, c)
forSome (s:ss) k = exists s >>= \a -> forSome ss $ \b c -> k (a, b, c)
forSome [] k = forSome_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, Provable p) => Provable ((SBV a, SBV b, SBV c, SBV d) -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ \b c d -> k (a, b, c, d)
forAll (s:ss) k = forall s >>= \a -> forAll ss $ \b c d -> k (a, b, c, d)
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ \b c d -> k (a, b, c, d)
forSome (s:ss) k = exists s >>= \a -> forSome ss $ \b c d -> k (a, b, c, d)
forSome [] k = forSome_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, Provable p) => Provable ((SBV a, SBV b, SBV c, SBV d, SBV e) -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ \b c d e -> k (a, b, c, d, e)
forAll (s:ss) k = forall s >>= \a -> forAll ss $ \b c d e -> k (a, b, c, d, e)
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ \b c d e -> k (a, b, c, d, e)
forSome (s:ss) k = exists s >>= \a -> forSome ss $ \b c d e -> k (a, b, c, d, e)
forSome [] k = forSome_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, Provable p) => Provable ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f) -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ \b c d e f -> k (a, b, c, d, e, f)
forAll (s:ss) k = forall s >>= \a -> forAll ss $ \b c d e f -> k (a, b, c, d, e, f)
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ \b c d e f -> k (a, b, c, d, e, f)
forSome (s:ss) k = exists s >>= \a -> forSome ss $ \b c d e f -> k (a, b, c, d, e, f)
forSome [] k = forSome_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SymWord g, Provable p) => Provable ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f, SBV g) -> p) where
forAll_ k = forall_ >>= \a -> forAll_ $ \b c d e f g -> k (a, b, c, d, e, f, g)
forAll (s:ss) k = forall s >>= \a -> forAll ss $ \b c d e f g -> k (a, b, c, d, e, f, g)
forAll [] k = forAll_ k
forSome_ k = exists_ >>= \a -> forSome_ $ \b c d e f g -> k (a, b, c, d, e, f, g)
forSome (s:ss) k = exists s >>= \a -> forSome ss $ \b c d e f g -> k (a, b, c, d, e, f, g)
forSome [] k = forSome_ k
runSMT :: Symbolic a -> IO a
runSMT = runSMTWith defaultSMTCfg
runSMTWith :: SMTConfig -> Symbolic a -> IO a
runSMTWith cfg a = fst <$> runSymbolic (SMTMode ISetup True cfg) a
runWithQuery :: Provable a => Bool -> Query b -> SMTConfig -> a -> IO b
runWithQuery isSAT q cfg a = fst <$> runSymbolic (SMTMode ISetup isSAT cfg) comp
where comp = do _ <- (if isSAT then forSome_ else forAll_) a >>= output
Control.query q
isSafe :: SafeResult -> Bool
isSafe (SafeResult (_, _, result)) = case result of
Unsatisfiable{} -> True
Satisfiable{} -> False
SatExtField{} -> False
Unknown{} -> False
ProofError{} -> False
runInThread :: NFData b => UTCTime -> (SMTConfig -> IO b) -> SMTConfig -> IO (Async (Solver, NominalDiffTime, b))
runInThread beginTime action config = async $ do
result <- action config
endTime <- rnf result `seq` getCurrentTime
return (name (solver config), endTime `diffUTCTime` beginTime, result)
sbvWithAny :: NFData b => [SMTConfig] -> (SMTConfig -> a -> IO b) -> a -> IO (Solver, NominalDiffTime, b)
sbvWithAny [] _ _ = error "SBV.withAny: No solvers given!"
sbvWithAny solvers what a = do beginTime <- getCurrentTime
snd `fmap` (mapM (runInThread beginTime (`what` a)) solvers >>= waitAnyFastCancel)
where
waitAnyFastCancel asyncs = waitAny asyncs `finally` mapM_ cancelFast asyncs
cancelFast other = throwTo (asyncThreadId other) ThreadKilled
sbvWithAll :: NFData b => [SMTConfig] -> (SMTConfig -> a -> IO b) -> a -> IO [(Solver, NominalDiffTime, b)]
sbvWithAll solvers what a = do beginTime <- getCurrentTime
mapM (runInThread beginTime (`what` a)) solvers >>= (unsafeInterleaveIO . go)
where go [] = return []
go as = do (d, r) <- waitAny as
rs <- unsafeInterleaveIO $ go (filter (/= d) as)
return (r : rs)
class SExecutable a where
sName_ :: a -> Symbolic ()
sName :: [String] -> a -> Symbolic ()
safe :: a -> IO [SafeResult]
safe = safeWith defaultSMTCfg
safeWith :: SMTConfig -> a -> IO [SafeResult]
safeWith cfg a = do cwd <- (++ "/") <$> getCurrentDirectory
let mkRelative path
| cwd `isPrefixOf` path = drop (length cwd) path
| True = path
fst <$> runSymbolic (SMTMode ISetup True cfg) (sName_ a >> check mkRelative)
where check mkRelative = Control.query $ Control.getSBVAssertions >>= mapM (verify mkRelative)
verify :: (FilePath -> FilePath) -> (String, Maybe CallStack, SW) -> Query SafeResult
verify mkRelative (msg, cs, cond) = do
let locInfo ps = let loc (f, sl) = concat [mkRelative (srcLocFile sl), ":", show (srcLocStartLine sl), ":", show (srcLocStartCol sl), ":", f]
in intercalate ",\n " (map loc ps)
location = (locInfo . getCallStack) `fmap` cs
result <- do Control.push 1
Control.send True $ "(assert " ++ show cond ++ ")"
r <- Control.getSMTResult
Control.pop 1
return r
return $ SafeResult (location, msg, result)
instance NFData a => SExecutable (Symbolic a) where
sName_ a = a >>= \r -> rnf r `seq` return ()
sName [] = sName_
sName xs = error $ "SBV.SExecutable.sName: Extra unmapped name(s): " ++ intercalate ", " xs
instance SExecutable (SBV a) where
sName_ v = sName_ (output v)
sName xs v = sName xs (output v)
instance SExecutable () where
sName_ () = sName_ (output ())
sName xs () = sName xs (output ())
instance SExecutable [SBV a] where
sName_ vs = sName_ (output vs)
sName xs vs = sName xs (output vs)
instance (NFData a, SymWord a, NFData b, SymWord b) => SExecutable (SBV a, SBV b) where
sName_ (a, b) = sName_ (output a >> output b)
sName _ = sName_
instance (NFData a, SymWord a, NFData b, SymWord b, NFData c, SymWord c) => SExecutable (SBV a, SBV b, SBV c) where
sName_ (a, b, c) = sName_ (output a >> output b >> output c)
sName _ = sName_
instance (NFData a, SymWord a, NFData b, SymWord b, NFData c, SymWord c, NFData d, SymWord d) => SExecutable (SBV a, SBV b, SBV c, SBV d) where
sName_ (a, b, c, d) = sName_ (output a >> output b >> output c >> output c >> output d)
sName _ = sName_
instance (NFData a, SymWord a, NFData b, SymWord b, NFData c, SymWord c, NFData d, SymWord d, NFData e, SymWord e) => SExecutable (SBV a, SBV b, SBV c, SBV d, SBV e) where
sName_ (a, b, c, d, e) = sName_ (output a >> output b >> output c >> output d >> output e)
sName _ = sName_
instance (NFData a, SymWord a, NFData b, SymWord b, NFData c, SymWord c, NFData d, SymWord d, NFData e, SymWord e, NFData f, SymWord f) => SExecutable (SBV a, SBV b, SBV c, SBV d, SBV e, SBV f) where
sName_ (a, b, c, d, e, f) = sName_ (output a >> output b >> output c >> output d >> output e >> output f)
sName _ = sName_
instance (NFData a, SymWord a, NFData b, SymWord b, NFData c, SymWord c, NFData d, SymWord d, NFData e, SymWord e, NFData f, SymWord f, NFData g, SymWord g) => SExecutable (SBV a, SBV b, SBV c, SBV d, SBV e, SBV f, SBV g) where
sName_ (a, b, c, d, e, f, g) = sName_ (output a >> output b >> output c >> output d >> output e >> output f >> output g)
sName _ = sName_
instance (SymWord a, SExecutable p) => SExecutable (SBV a -> p) where
sName_ k = exists_ >>= \a -> sName_ $ k a
sName (s:ss) k = exists s >>= \a -> sName ss $ k a
sName [] k = sName_ k
instance (SymWord a, SymWord b, SExecutable p) => SExecutable ((SBV a, SBV b) -> p) where
sName_ k = exists_ >>= \a -> sName_ $ \b -> k (a, b)
sName (s:ss) k = exists s >>= \a -> sName ss $ \b -> k (a, b)
sName [] k = sName_ k
instance (SymWord a, SymWord b, SymWord c, SExecutable p) => SExecutable ((SBV a, SBV b, SBV c) -> p) where
sName_ k = exists_ >>= \a -> sName_ $ \b c -> k (a, b, c)
sName (s:ss) k = exists s >>= \a -> sName ss $ \b c -> k (a, b, c)
sName [] k = sName_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SExecutable p) => SExecutable ((SBV a, SBV b, SBV c, SBV d) -> p) where
sName_ k = exists_ >>= \a -> sName_ $ \b c d -> k (a, b, c, d)
sName (s:ss) k = exists s >>= \a -> sName ss $ \b c d -> k (a, b, c, d)
sName [] k = sName_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SExecutable p) => SExecutable ((SBV a, SBV b, SBV c, SBV d, SBV e) -> p) where
sName_ k = exists_ >>= \a -> sName_ $ \b c d e -> k (a, b, c, d, e)
sName (s:ss) k = exists s >>= \a -> sName ss $ \b c d e -> k (a, b, c, d, e)
sName [] k = sName_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SExecutable p) => SExecutable ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f) -> p) where
sName_ k = exists_ >>= \a -> sName_ $ \b c d e f -> k (a, b, c, d, e, f)
sName (s:ss) k = exists s >>= \a -> sName ss $ \b c d e f -> k (a, b, c, d, e, f)
sName [] k = sName_ k
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SymWord g, SExecutable p) => SExecutable ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f, SBV g) -> p) where
sName_ k = exists_ >>= \a -> sName_ $ \b c d e f g -> k (a, b, c, d, e, f, g)
sName (s:ss) k = exists s >>= \a -> sName ss $ \b c d e f g -> k (a, b, c, d, e, f, g)
sName [] k = sName_ k
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}