{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.SBV.Core.Symbolic
( NodeId(..)
, SW(..), swKind, trueSW, falseSW
, Op(..), PBOp(..), FPOp(..)
, Quantifier(..), needsExistentials
, RoundingMode(..)
, SBVType(..), newUninterpreted, addAxiom
, SVal(..)
, svMkSymVar
, ArrayContext(..), ArrayInfo
, svToSW, svToSymSW, forceSWArg
, SBVExpr(..), newExpr, isCodeGenMode
, Cached, cache, uncache
, ArrayIndex, uncacheAI
, NamedSymVar
, getSValPathCondition, extendSValPathCondition
, getTableIndex
, SBVPgm(..), Symbolic, runSymbolic, State(..), withNewIncState, IncState(..), incrementInternalCounter
, inSMTMode, SBVRunMode(..), IStage(..), Result(..)
, registerKind, registerLabel
, addAssertion, addNewSMTOption, imposeConstraint, internalConstraint, internalVariable
, SMTLibPgm(..), SMTLibVersion(..), smtLibVersionExtension
, SolverCapabilities(..)
, extractSymbolicSimulationState
, OptimizeStyle(..), Objective(..), Penalty(..), objectiveName, addSValOptGoal
, Query(..), QueryState(..)
, SMTScript(..), Solver(..), SMTSolver(..), SMTResult(..), SMTModel(..), SMTConfig(..), SMTEngine
, outputSVal
, SArr(..), readSArr, writeSArr, mergeSArr, newSArr, eqSArr
) where
import Control.DeepSeq (NFData(..))
import Control.Monad (when, unless)
import Control.Monad.Reader (MonadReader, ReaderT, ask, runReaderT)
import Control.Monad.State.Lazy (MonadState, StateT(..))
import Control.Monad.Trans (MonadIO, liftIO)
import Data.Char (isAlpha, isAlphaNum, toLower)
import Data.IORef (IORef, newIORef, readIORef)
import Data.List (intercalate, sortBy)
import Data.Maybe (isJust, fromJust, fromMaybe)
import Data.Time (getCurrentTime, UTCTime)
import GHC.Stack
import qualified Data.IORef as R (modifyIORef')
import qualified Data.Generics as G (Data(..))
import qualified Data.IntMap as IMap (IntMap, empty, size, toAscList, lookup, insert, insertWith)
import qualified Data.Map as Map (Map, empty, toList, size, insert, lookup)
import qualified Data.Set as Set (Set, empty, toList, insert, member)
import qualified Data.Foldable as F (toList)
import qualified Data.Sequence as S (Seq, empty, (|>))
import System.Mem.StableName
import Data.SBV.Core.Kind
import Data.SBV.Core.Concrete
import Data.SBV.SMT.SMTLibNames
import Data.SBV.Utils.TDiff(Timing)
import Data.SBV.Control.Types
newtype NodeId = NodeId Int deriving (Eq, Ord)
data SW = SW !Kind !NodeId deriving (Eq, Ord)
instance HasKind SW where
kindOf (SW k _) = k
instance Show SW where
show (SW _ (NodeId n))
| n < 0 = "s_" ++ show (abs n)
| True = 's' : show n
swKind :: SW -> Kind
swKind (SW k _) = k
forceSWArg :: SW -> IO ()
forceSWArg (SW k n) = k `seq` n `seq` return ()
falseSW :: SW
falseSW = SW KBool $ NodeId (-2)
trueSW :: SW
trueSW = SW KBool $ NodeId (-1)
data Op = Plus
| Times
| Minus
| UNeg
| Abs
| Quot
| Rem
| Equal
| NotEqual
| LessThan
| GreaterThan
| LessEq
| GreaterEq
| Ite
| And
| Or
| XOr
| Not
| Shl Int
| Shr Int
| Rol Int
| Ror Int
| Extract Int Int
| Join
| LkUp (Int, Kind, Kind, Int) !SW !SW
| ArrEq Int Int
| ArrRead Int
| KindCast Kind Kind
| Uninterpreted String
| Label String
| IEEEFP FPOp
| PseudoBoolean PBOp
deriving (Eq, Ord)
data FPOp = FP_Cast Kind Kind SW
| FP_Reinterpret Kind Kind
| FP_Abs
| FP_Neg
| FP_Add
| FP_Sub
| FP_Mul
| FP_Div
| FP_FMA
| FP_Sqrt
| FP_Rem
| FP_RoundToIntegral
| FP_Min
| FP_Max
| FP_ObjEqual
| FP_IsNormal
| FP_IsSubnormal
| FP_IsZero
| FP_IsInfinite
| FP_IsNaN
| FP_IsNegative
| FP_IsPositive
deriving (Eq, Ord)
instance Show FPOp where
show (FP_Cast f t r) = "(FP_Cast: " ++ show f ++ " -> " ++ show t ++ ", using RM [" ++ show r ++ "])"
show (FP_Reinterpret f t) = case (f, t) of
(KBounded False 32, KFloat) -> "(_ to_fp 8 24)"
(KBounded False 64, KDouble) -> "(_ to_fp 11 53)"
_ -> error $ "SBV.FP_Reinterpret: Unexpected conversion: " ++ show f ++ " to " ++ show t
show FP_Abs = "fp.abs"
show FP_Neg = "fp.neg"
show FP_Add = "fp.add"
show FP_Sub = "fp.sub"
show FP_Mul = "fp.mul"
show FP_Div = "fp.div"
show FP_FMA = "fp.fma"
show FP_Sqrt = "fp.sqrt"
show FP_Rem = "fp.rem"
show FP_RoundToIntegral = "fp.roundToIntegral"
show FP_Min = "fp.min"
show FP_Max = "fp.max"
show FP_ObjEqual = "="
show FP_IsNormal = "fp.isNormal"
show FP_IsSubnormal = "fp.isSubnormal"
show FP_IsZero = "fp.isZero"
show FP_IsInfinite = "fp.isInfinite"
show FP_IsNaN = "fp.isNaN"
show FP_IsNegative = "fp.isNegative"
show FP_IsPositive = "fp.isPositive"
data PBOp = PB_AtMost Int
| PB_AtLeast Int
| PB_Exactly Int
| PB_Le [Int] Int
| PB_Ge [Int] Int
| PB_Eq [Int] Int
deriving (Eq, Ord, Show)
instance Show Op where
show (Shl i) = "<<" ++ show i
show (Shr i) = ">>" ++ show i
show (Rol i) = "<<<" ++ show i
show (Ror i) = ">>>" ++ show i
show (Extract i j) = "choose [" ++ show i ++ ":" ++ show j ++ "]"
show (LkUp (ti, at, rt, l) i e)
= "lookup(" ++ tinfo ++ ", " ++ show i ++ ", " ++ show e ++ ")"
where tinfo = "table" ++ show ti ++ "(" ++ show at ++ " -> " ++ show rt ++ ", " ++ show l ++ ")"
show (ArrEq i j) = "array_" ++ show i ++ " == array_" ++ show j
show (ArrRead i) = "select array_" ++ show i
show (KindCast fr to) = "cast_" ++ show fr ++ "_" ++ show to
show (Uninterpreted i) = "[uninterpreted] " ++ i
show (Label s) = "[label] " ++ s
show (IEEEFP w) = show w
show (PseudoBoolean p) = show p
show op
| Just s <- op `lookup` syms = s
| True = error "impossible happened; can't find op!"
where syms = [ (Plus, "+"), (Times, "*"), (Minus, "-"), (UNeg, "-"), (Abs, "abs")
, (Quot, "quot")
, (Rem, "rem")
, (Equal, "=="), (NotEqual, "/=")
, (LessThan, "<"), (GreaterThan, ">"), (LessEq, "<="), (GreaterEq, ">=")
, (Ite, "if_then_else")
, (And, "&"), (Or, "|"), (XOr, "^"), (Not, "~")
, (Join, "#")
]
data Quantifier = ALL | EX deriving Eq
needsExistentials :: [Quantifier] -> Bool
needsExistentials = (EX `elem`)
newtype SBVType = SBVType [Kind]
deriving (Eq, Ord)
instance Show SBVType where
show (SBVType []) = error "SBV: internal error, empty SBVType"
show (SBVType xs) = intercalate " -> " $ map show xs
data SBVExpr = SBVApp !Op ![SW]
deriving (Eq, Ord)
reorder :: SBVExpr -> SBVExpr
reorder s = case s of
SBVApp op [a, b] | isCommutative op && a > b -> SBVApp op [b, a]
_ -> s
where isCommutative :: Op -> Bool
isCommutative o = o `elem` [Plus, Times, Equal, NotEqual, And, Or, XOr]
instance Show SBVExpr where
show (SBVApp Ite [t, a, b]) = unwords ["if", show t, "then", show a, "else", show b]
show (SBVApp (Shl i) [a]) = unwords [show a, "<<", show i]
show (SBVApp (Shr i) [a]) = unwords [show a, ">>", show i]
show (SBVApp (Rol i) [a]) = unwords [show a, "<<<", show i]
show (SBVApp (Ror i) [a]) = unwords [show a, ">>>", show i]
show (SBVApp (PseudoBoolean pb) args) = unwords (show pb : map show args)
show (SBVApp op [a, b]) = unwords [show a, show op, show b]
show (SBVApp op args) = unwords (show op : map show args)
newtype SBVPgm = SBVPgm {pgmAssignments :: S.Seq (SW, SBVExpr)}
type NamedSymVar = (SW, String)
data OptimizeStyle = Lexicographic
| Independent
| Pareto (Maybe Int)
deriving (Eq, Show)
data Penalty = DefaultPenalty
| Penalty Rational (Maybe String)
deriving Show
data Objective a = Minimize String a
| Maximize String a
| AssertSoft String a Penalty
deriving (Show, Functor)
objectiveName :: Objective a -> String
objectiveName (Minimize s _) = s
objectiveName (Maximize s _) = s
objectiveName (AssertSoft s _ _) = s
data QueryState = QueryState { queryAsk :: Maybe Int -> String -> IO String
, querySend :: Maybe Int -> String -> IO ()
, queryRetrieveResponse :: Maybe Int -> IO String
, queryConfig :: SMTConfig
, queryTerminate :: IO ()
, queryTimeOutValue :: Maybe Int
, queryAssertionStackDepth :: Int
}
newtype Query a = Query (StateT State IO a)
deriving (Applicative, Functor, Monad, MonadIO, MonadState State)
instance NFData OptimizeStyle where
rnf x = x `seq` ()
instance NFData Penalty where
rnf DefaultPenalty = ()
rnf (Penalty p mbs) = rnf p `seq` rnf mbs `seq` ()
instance NFData a => NFData (Objective a) where
rnf (Minimize s a) = rnf s `seq` rnf a `seq` ()
rnf (Maximize s a) = rnf s `seq` rnf a `seq` ()
rnf (AssertSoft s a p) = rnf s `seq` rnf a `seq` rnf p `seq` ()
data Result = Result { reskinds :: Set.Set Kind
, resTraces :: [(String, CW)]
, resUISegs :: [(String, [String])]
, resInputs :: [(Quantifier, NamedSymVar)]
, resConsts :: [(SW, CW)]
, resTables :: [((Int, Kind, Kind), [SW])]
, resArrays :: [(Int, ArrayInfo)]
, resUIConsts :: [(String, SBVType)]
, resAxioms :: [(String, [String])]
, resAsgns :: SBVPgm
, resConstraints :: [(Maybe String, SW)]
, resAssertions :: [(String, Maybe CallStack, SW)]
, resOutputs :: [SW]
}
instance Show Result where
show Result{resConsts=cs, resOutputs=[r]}
| Just c <- r `lookup` cs
= show c
show (Result kinds _ cgs is cs ts as uis axs xs cstrs asserts os) = intercalate "\n" $
(if null usorts then [] else "SORTS" : map (" " ++) usorts)
++ ["INPUTS"]
++ map shn is
++ ["CONSTANTS"]
++ map shc cs
++ ["TABLES"]
++ map sht ts
++ ["ARRAYS"]
++ map sha as
++ ["UNINTERPRETED CONSTANTS"]
++ map shui uis
++ ["USER GIVEN CODE SEGMENTS"]
++ concatMap shcg cgs
++ ["AXIOMS"]
++ map shax axs
++ ["DEFINE"]
++ map (\(s, e) -> " " ++ shs s ++ " = " ++ show e) (F.toList (pgmAssignments xs))
++ ["CONSTRAINTS"]
++ map ((" " ++) . shCstr) cstrs
++ ["ASSERTIONS"]
++ map ((" "++) . shAssert) asserts
++ ["OUTPUTS"]
++ sh2 os
where sh2 :: Show a => [a] -> [String]
sh2 = map ((" "++) . show)
usorts = [sh s t | KUserSort s t <- Set.toList kinds]
where sh s (Left _) = s
sh s (Right es) = s ++ " (" ++ intercalate ", " es ++ ")"
shs sw = show sw ++ " :: " ++ show (swKind sw)
sht ((i, at, rt), es) = " Table " ++ show i ++ " : " ++ show at ++ "->" ++ show rt ++ " = " ++ show es
shc (sw, cw) = " " ++ show sw ++ " = " ++ show cw
shcg (s, ss) = ("Variable: " ++ s) : map (" " ++) ss
shn (q, (sw, nm)) = " " ++ ni ++ " :: " ++ show (swKind sw) ++ ex ++ alias
where ni = show sw
ex | q == ALL = ""
| True = ", existential"
alias | ni == nm = ""
| True = ", aliasing " ++ show nm
sha (i, (nm, (ai, bi), ctx)) = " " ++ ni ++ " :: " ++ show ai ++ " -> " ++ show bi ++ alias
++ "\n Context: " ++ show ctx
where ni = "array_" ++ show i
alias | ni == nm = ""
| True = ", aliasing " ++ show nm
shui (nm, t) = " [uninterpreted] " ++ nm ++ " :: " ++ show t
shax (nm, ss) = " -- user defined axiom: " ++ nm ++ "\n " ++ intercalate "\n " ss
shCstr (Nothing, c) = show c
shCstr (Just nm, c) = nm ++ ": " ++ show c
shAssert (nm, stk, p) = " -- assertion: " ++ nm ++ " " ++ maybe "[No location]"
#if MIN_VERSION_base(4,9,0)
prettyCallStack
#else
showCallStack
#endif
stk ++ ": " ++ show p
data ArrayContext = ArrayFree
| ArrayMutate Int SW SW
| ArrayMerge SW Int Int
instance Show ArrayContext where
show ArrayFree = " initialized with random elements"
show (ArrayMutate i a b) = " cloned from array_" ++ show i ++ " with " ++ show a ++ " :: " ++ show (swKind a) ++ " |-> " ++ show b ++ " :: " ++ show (swKind b)
show (ArrayMerge s i j) = " merged arrays " ++ show i ++ " and " ++ show j ++ " on condition " ++ show s
type ExprMap = Map.Map SBVExpr SW
type CnstMap = Map.Map (Bool, CW) SW
type KindSet = Set.Set Kind
type TableMap = Map.Map (Kind, Kind, [SW]) Int
type ArrayInfo = (String, (Kind, Kind), ArrayContext)
type ArrayMap = IMap.IntMap ArrayInfo
type UIMap = Map.Map String SBVType
type CgMap = Map.Map String [String]
type Cache a = IMap.IntMap [(StableName (State -> IO a), a)]
data IStage = ISetup
| IRun
data SBVRunMode = SMTMode IStage Bool SMTConfig
| CodeGen
| Concrete
instance Show SBVRunMode where
show (SMTMode ISetup True _) = "Satisfiability setup"
show (SMTMode IRun True _) = "Satisfiability"
show (SMTMode ISetup False _) = "Proof setup"
show (SMTMode IRun False _) = "Proof"
show CodeGen = "Code generation"
show Concrete = "Concrete evaluation"
isCodeGenMode :: State -> IO Bool
isCodeGenMode State{runMode} = do rm <- readIORef runMode
return $ case rm of
Concrete{} -> False
SMTMode{} -> False
CodeGen -> True
data IncState = IncState { rNewInps :: IORef [NamedSymVar]
, rNewKinds :: IORef KindSet
, rNewConsts :: IORef CnstMap
, rNewAsgns :: IORef SBVPgm
}
newIncState :: IO IncState
newIncState = do
is <- newIORef []
ks <- newIORef Set.empty
nc <- newIORef Map.empty
pgm <- newIORef (SBVPgm S.empty)
return IncState { rNewInps = is
, rNewKinds = ks
, rNewConsts = nc
, rNewAsgns = pgm
}
withNewIncState :: State -> (State -> IO a) -> IO (IncState, a)
withNewIncState st cont = do
is <- newIncState
R.modifyIORef' (rIncState st) (const is)
r <- cont st
finalIncState <- readIORef (rIncState st)
return (finalIncState, r)
data State = State { pathCond :: SVal
, startTime :: UTCTime
, runMode :: IORef SBVRunMode
, rIncState :: IORef IncState
, rCInfo :: IORef [(String, CW)]
, rctr :: IORef Int
, rUsedKinds :: IORef KindSet
, rUsedLbls :: IORef (Set.Set String)
, rinps :: IORef [(Quantifier, NamedSymVar)]
, rConstraints :: IORef [(Maybe String, SW)]
, routs :: IORef [SW]
, rtblMap :: IORef TableMap
, spgm :: IORef SBVPgm
, rconstMap :: IORef CnstMap
, rexprMap :: IORef ExprMap
, rArrayMap :: IORef ArrayMap
, rUIMap :: IORef UIMap
, rCgMap :: IORef CgMap
, raxioms :: IORef [(String, [String])]
, rSMTOptions :: IORef [SMTOption]
, rOptGoals :: IORef [Objective (SW, SW)]
, rAsserts :: IORef [(String, Maybe CallStack, SW)]
, rSWCache :: IORef (Cache SW)
, rAICache :: IORef (Cache Int)
, queryState :: IORef (Maybe QueryState)
}
instance NFData State where
rnf State{} = ()
getSValPathCondition :: State -> SVal
getSValPathCondition = pathCond
extendSValPathCondition :: State -> (SVal -> SVal) -> State
extendSValPathCondition st f = st{pathCond = f (pathCond st)}
inSMTMode :: State -> IO Bool
inSMTMode State{runMode} = do rm <- readIORef runMode
return $ case rm of
CodeGen -> False
Concrete{} -> False
SMTMode{} -> True
data SVal = SVal !Kind !(Either CW (Cached SW))
instance HasKind SVal where
kindOf (SVal k _) = k
instance Show SVal where
show (SVal KBool (Left c)) = showCW False c
show (SVal k (Left c)) = showCW False c ++ " :: " ++ show k
show (SVal k (Right _)) = "<symbolic> :: " ++ show k
instance Eq SVal where
SVal _ (Left a) == SVal _ (Left b) = a == b
a == b = error $ "Comparing symbolic bit-vectors; Use (.==) instead. Received: " ++ show (a, b)
SVal _ (Left a) /= SVal _ (Left b) = a /= b
a /= b = error $ "Comparing symbolic bit-vectors; Use (./=) instead. Received: " ++ show (a, b)
noInteractive :: [String] -> a
noInteractive ss = error $ unlines $ ""
: "*** Data.SBV: Unsupported interactive/query mode feature."
: map ("*** " ++) ss
++ ["*** Data.SBV: Please report this as a feature request!"]
modifyState :: State -> (State -> IORef a) -> (a -> a) -> IO () -> IO ()
modifyState st@State{runMode} field update interactiveUpdate = do
R.modifyIORef' (field st) update
rm <- readIORef runMode
case rm of
SMTMode IRun _ _ -> interactiveUpdate
_ -> return ()
modifyIncState :: State -> (IncState -> IORef a) -> (a -> a) -> IO ()
modifyIncState State{rIncState} field update = do
incState <- readIORef rIncState
R.modifyIORef' (field incState) update
incrementInternalCounter :: State -> IO Int
incrementInternalCounter st = do ctr <- readIORef (rctr st)
modifyState st rctr (+1) (return ())
return ctr
newUninterpreted :: State -> String -> SBVType -> Maybe [String] -> IO ()
newUninterpreted st nm t mbCode
| null nm || not enclosed && (not (isAlpha (head nm)) || not (all validChar (tail nm)))
= error $ "Bad uninterpreted constant name: " ++ show nm ++ ". Must be a valid identifier."
| True = do
uiMap <- readIORef (rUIMap st)
case nm `Map.lookup` uiMap of
Just t' -> when (t /= t') $ error $ "Uninterpreted constant " ++ show nm ++ " used at incompatible types\n"
++ " Current type : " ++ show t ++ "\n"
++ " Previously used at: " ++ show t'
Nothing -> do modifyState st rUIMap (Map.insert nm t) $ noInteractive [ "Uninterpreted function introduction:"
, " Named: " ++ nm
, " Type : " ++ show t
]
when (isJust mbCode) $ modifyState st rCgMap (Map.insert nm (fromJust mbCode)) (return ())
where validChar x = isAlphaNum x || x `elem` "_"
enclosed = head nm == '|' && last nm == '|' && length nm > 2 && not (any (`elem` "|\\") (tail (init nm)))
addAssertion :: State -> Maybe CallStack -> String -> SW -> IO ()
addAssertion st cs msg cond = modifyState st rAsserts ((msg, cs, cond):)
$ noInteractive [ "Named assertions (sAssert):"
, " Tag: " ++ msg
, " Loc: " ++ maybe "Unknown" show cs
]
internalVariable :: State -> Kind -> IO SW
internalVariable st k = do (sw, nm) <- newSW st k
rm <- readIORef (runMode st)
let q = case rm of
SMTMode _ True _ -> EX
SMTMode _ False _ -> ALL
CodeGen -> ALL
Concrete{} -> ALL
modifyState st rinps ((q, (sw, "__internal_sbv_" ++ nm)):)
$ noInteractive [ "Internal variable creation:"
, " Named: " ++ nm
]
return sw
{-# INLINE internalVariable #-}
newSW :: State -> Kind -> IO (SW, String)
newSW st k = do ctr <- incrementInternalCounter st
let sw = SW k (NodeId ctr)
registerKind st k
return (sw, 's' : show ctr)
{-# INLINE newSW #-}
registerKind :: State -> Kind -> IO ()
registerKind st k
| KUserSort sortName _ <- k, map toLower sortName `elem` smtLibReservedNames
= error $ "SBV: " ++ show sortName ++ " is a reserved sort; please use a different name."
| True
= do ks <- readIORef (rUsedKinds st)
unless (k `Set.member` ks) $ modifyState st rUsedKinds (Set.insert k)
$ modifyIncState st rNewKinds (Set.insert k)
registerLabel :: State -> String -> IO ()
registerLabel st nm
| map toLower nm `elem` smtLibReservedNames
= error $ "SBV: " ++ show nm ++ " is a reserved string; please use a different name."
| '|' `elem` nm
= error $ "SBV: " ++ show nm ++ " contains the character `|', which is not allowed!"
| '\\' `elem` nm
= error $ "SBV: " ++ show nm ++ " contains the character `\', which is not allowed!"
| True
= do old <- readIORef $ rUsedLbls st
if nm `Set.member` old
then error $ "SBV: " ++ show nm ++ " is used as a label multiple times. Please do not use duplicate names!"
else modifyState st rUsedLbls (Set.insert nm) (return ())
newConst :: State -> CW -> IO SW
newConst st c = do
constMap <- readIORef (rconstMap st)
let key = (isNeg0 (cwVal c), c)
case key `Map.lookup` constMap of
Just sw -> return sw
Nothing -> do let k = kindOf c
(sw, _) <- newSW st k
let ins = Map.insert key sw
modifyState st rconstMap ins $ modifyIncState st rNewConsts ins
return sw
where isNeg0 (CWFloat f) = isNegativeZero f
isNeg0 (CWDouble d) = isNegativeZero d
isNeg0 _ = False
{-# INLINE newConst #-}
getTableIndex :: State -> Kind -> Kind -> [SW] -> IO Int
getTableIndex st at rt elts = do
let key = (at, rt, elts)
tblMap <- readIORef (rtblMap st)
case key `Map.lookup` tblMap of
Just i -> return i
_ -> do let i = Map.size tblMap
modifyState st rtblMap (Map.insert key i)
$ noInteractive [ "Creation of a new table:"
, " Index kind: " ++ show at
, " Value kind: " ++ show rt
, " Elements : " ++ unwords (map show elts)
]
return i
newExpr :: State -> Kind -> SBVExpr -> IO SW
newExpr st k app = do
let e = reorder app
exprMap <- readIORef (rexprMap st)
case e `Map.lookup` exprMap of
Just sw -> return sw
Nothing -> do (sw, _) <- newSW st k
let append (SBVPgm xs) = SBVPgm (xs S.|> (sw, e))
modifyState st spgm append $ modifyIncState st rNewAsgns append
modifyState st rexprMap (Map.insert e sw) (return ())
return sw
{-# INLINE newExpr #-}
svToSW :: State -> SVal -> IO SW
svToSW st (SVal _ (Left c)) = newConst st c
svToSW st (SVal _ (Right f)) = uncache f st
svToSymSW :: SVal -> Symbolic SW
svToSymSW sbv = do st <- ask
liftIO $ svToSW st sbv
newtype Symbolic a = Symbolic (ReaderT State IO a)
deriving (Applicative, Functor, Monad, MonadIO, MonadReader State)
svMkSymVar :: Maybe Quantifier -> Kind -> Maybe String -> State -> IO SVal
svMkSymVar mbQ k mbNm st = do
rm <- readIORef (runMode st)
let varInfo = case mbNm of
Nothing -> ", of type " ++ show k
Just nm -> ", while defining " ++ nm ++ " :: " ++ show k
disallow what = error $ "Data.SBV: Unsupported: " ++ what ++ varInfo ++ " in mode: " ++ show rm
noUI cont
| isUninterpreted k = disallow "Uninterpreted sorts"
| True = cont
mkS q = do (sw, internalName) <- newSW st k
let nm = fromMaybe internalName mbNm
introduceUserName st nm k q sw
mkC = do cw <- randomCW k
do registerKind st k
modifyState st rCInfo ((fromMaybe "_" mbNm, cw):) (return ())
return $ SVal k (Left cw)
case (mbQ, rm) of
(Just q, SMTMode{} ) -> mkS q
(Nothing, SMTMode _ isSAT _) -> mkS (if isSAT then EX else ALL)
(Just EX, CodeGen{}) -> disallow "Existentially quantified variables"
(_ , CodeGen) -> noUI $ mkS ALL
(Just EX, Concrete{}) -> disallow "Existentially quantified variables"
(_ , Concrete{}) -> noUI mkC
introduceUserName :: State -> String -> Kind -> Quantifier -> SW -> IO SVal
introduceUserName st nm k q sw = do is <- readIORef (rinps st)
if nm `elem` [n | (_, (_, n)) <- is]
then error $ "SBV: Repeated user given name: " ++ show nm ++ ". Please use unique names."
else do let newInp olds = case q of
EX -> (sw, nm) : olds
ALL -> noInteractive [ "Adding a new universally quantified: "
, " Name : " ++ show nm
, " Kind : " ++ show k
, " Quantifier: Universal"
, " Node : " ++ show sw
, "Only existential variables are supported in query mode."
]
modifyState st rinps ((q, (sw, nm)):)
$ modifyIncState st rNewInps newInp
return $ SVal k $ Right $ cache (const (return sw))
addAxiom :: String -> [String] -> Symbolic ()
addAxiom nm ax = do
st <- ask
liftIO $ modifyState st raxioms ((nm, ax) :)
$ noInteractive [ "Adding a new axiom:"
, " Named: " ++ show nm
, " Axiom: " ++ unlines ax
]
runSymbolic :: SBVRunMode -> Symbolic a -> IO (a, Result)
runSymbolic currentRunMode (Symbolic c) = do
currTime <- getCurrentTime
rm <- newIORef currentRunMode
ctr <- newIORef (-2)
cInfo <- newIORef []
pgm <- newIORef (SBVPgm S.empty)
emap <- newIORef Map.empty
cmap <- newIORef Map.empty
inps <- newIORef []
outs <- newIORef []
tables <- newIORef Map.empty
arrays <- newIORef IMap.empty
uis <- newIORef Map.empty
cgs <- newIORef Map.empty
axioms <- newIORef []
swCache <- newIORef IMap.empty
aiCache <- newIORef IMap.empty
usedKinds <- newIORef Set.empty
usedLbls <- newIORef Set.empty
cstrs <- newIORef []
smtOpts <- newIORef []
optGoals <- newIORef []
asserts <- newIORef []
istate <- newIORef =<< newIncState
qstate <- newIORef Nothing
let st = State { runMode = rm
, startTime = currTime
, pathCond = SVal KBool (Left trueCW)
, rIncState = istate
, rCInfo = cInfo
, rctr = ctr
, rUsedKinds = usedKinds
, rUsedLbls = usedLbls
, rinps = inps
, routs = outs
, rtblMap = tables
, spgm = pgm
, rconstMap = cmap
, rArrayMap = arrays
, rexprMap = emap
, rUIMap = uis
, rCgMap = cgs
, raxioms = axioms
, rSWCache = swCache
, rAICache = aiCache
, rConstraints = cstrs
, rSMTOptions = smtOpts
, rOptGoals = optGoals
, rAsserts = asserts
, queryState = qstate
}
_ <- newConst st falseCW
_ <- newConst st trueCW
r <- runReaderT c st
res <- extractSymbolicSimulationState st
qs <- readIORef qstate
case qs of
Nothing -> return ()
Just QueryState{queryTerminate} -> queryTerminate
return (r, res)
extractSymbolicSimulationState :: State -> IO Result
extractSymbolicSimulationState st@State{ spgm=pgm, rinps=inps, routs=outs, rtblMap=tables, rArrayMap=arrays, rUIMap=uis, raxioms=axioms
, rAsserts=asserts, rUsedKinds=usedKinds, rCgMap=cgs, rCInfo=cInfo, rConstraints=cstrs
} = do
SBVPgm rpgm <- readIORef pgm
inpsO <- reverse <$> readIORef inps
outsO <- reverse <$> readIORef outs
let swap (a, b) = (b, a)
swapc ((_, a), b) = (b, a)
cmp (a, _) (b, _) = a `compare` b
arrange (i, (at, rt, es)) = ((i, at, rt), es)
cnsts <- (sortBy cmp . map swapc . Map.toList) <$> readIORef (rconstMap st)
tbls <- (map arrange . sortBy cmp . map swap . Map.toList) <$> readIORef tables
arrs <- IMap.toAscList <$> readIORef arrays
unint <- Map.toList <$> readIORef uis
axs <- reverse <$> readIORef axioms
knds <- readIORef usedKinds
cgMap <- Map.toList <$> readIORef cgs
traceVals <- reverse <$> readIORef cInfo
extraCstrs <- reverse <$> readIORef cstrs
assertions <- reverse <$> readIORef asserts
return $ Result knds traceVals cgMap inpsO cnsts tbls arrs unint axs (SBVPgm rpgm) extraCstrs assertions outsO
addNewSMTOption :: SMTOption -> Symbolic ()
addNewSMTOption o = do st <- ask
liftIO $ modifyState st rSMTOptions (o:) (return ())
imposeConstraint :: Maybe String -> SVal -> Symbolic ()
imposeConstraint mbNm c = do st <- ask
rm <- liftIO $ readIORef (runMode st)
case rm of
CodeGen -> error "SBV: constraints are not allowed in code-generation"
_ -> do () <- case mbNm of
Nothing -> return ()
Just nm -> liftIO $ registerLabel st nm
liftIO $ internalConstraint st mbNm c
internalConstraint :: State -> Maybe String -> SVal -> IO ()
internalConstraint st mbNm b = do v <- svToSW st b
modifyState st rConstraints ((mbNm, v):)
$ noInteractive [ "Adding an internal constraint:"
, " Named: " ++ fromMaybe "<unnamed>" mbNm
]
addSValOptGoal :: Objective SVal -> Symbolic ()
addSValOptGoal obj = do st <- ask
let mkGoal nm orig = liftIO $ do origSW <- svToSW st orig
track <- svMkSymVar (Just EX) (kindOf orig) (Just nm) st
trackSW <- svToSW st track
return (origSW, trackSW)
let walk (Minimize nm v) = Minimize nm <$> mkGoal nm v
walk (Maximize nm v) = Maximize nm <$> mkGoal nm v
walk (AssertSoft nm v mbP) = flip (AssertSoft nm) mbP <$> mkGoal nm v
obj' <- walk obj
liftIO $ modifyState st rOptGoals (obj' :)
$ noInteractive [ "Adding an optimization objective:"
, " Objective: " ++ show obj
]
outputSVal :: SVal -> Symbolic ()
outputSVal (SVal _ (Left c)) = do
st <- ask
sw <- liftIO $ newConst st c
liftIO $ modifyState st routs (sw:) (return ())
outputSVal (SVal _ (Right f)) = do
st <- ask
sw <- liftIO $ uncache f st
liftIO $ modifyState st routs (sw:) (return ())
data SArr = SArr (Kind, Kind) (Cached ArrayIndex)
readSArr :: SArr -> SVal -> SVal
readSArr (SArr (_, bk) f) a = SVal bk $ Right $ cache r
where r st = do arr <- uncacheAI f st
i <- svToSW st a
newExpr st bk (SBVApp (ArrRead arr) [i])
writeSArr :: SArr -> SVal -> SVal -> SArr
writeSArr (SArr ainfo f) a b = SArr ainfo $ cache g
where g st = do arr <- uncacheAI f st
addr <- svToSW st a
val <- svToSW st b
amap <- readIORef (rArrayMap st)
let j = IMap.size amap
j `seq` modifyState st rArrayMap (IMap.insert j ("array_" ++ show j, ainfo, ArrayMutate arr addr val))
(noInteractive [ "An array update:"
, " Array info: " ++ show ainfo
])
return j
mergeSArr :: SVal -> SArr -> SArr -> SArr
mergeSArr t (SArr ainfo a) (SArr _ b) = SArr ainfo $ cache h
where h st = do ai <- uncacheAI a st
bi <- uncacheAI b st
ts <- svToSW st t
amap <- readIORef (rArrayMap st)
let k = IMap.size amap
k `seq` modifyState st rArrayMap (IMap.insert k ("array_" ++ show k, ainfo, ArrayMerge ts ai bi))
(noInteractive [ "An array merge:"
, " Array info: " ++ show ainfo
])
return k
newSArr :: (Kind, Kind) -> (Int -> String) -> Symbolic SArr
newSArr ainfo mkNm = do
st <- ask
amap <- liftIO $ readIORef $ rArrayMap st
let i = IMap.size amap
nm = mkNm i
actx <- liftIO $ return ArrayFree
liftIO $ modifyState st rArrayMap (IMap.insert i (nm, ainfo, actx))
$ noInteractive [ "A new array creation:"
, " Array info: " ++ show ainfo
, " Named : " ++ show nm
]
return $ SArr ainfo $ cache $ const $ return i
eqSArr :: SArr -> SArr -> SVal
eqSArr (SArr _ a) (SArr _ b) = SVal KBool $ Right $ cache c
where c st = do ai <- uncacheAI a st
bi <- uncacheAI b st
newExpr st KBool (SBVApp (ArrEq ai bi) [])
newtype Cached a = Cached (State -> IO a)
cache :: (State -> IO a) -> Cached a
cache = Cached
uncache :: Cached SW -> State -> IO SW
uncache = uncacheGen rSWCache
type ArrayIndex = Int
uncacheAI :: Cached ArrayIndex -> State -> IO ArrayIndex
uncacheAI = uncacheGen rAICache
uncacheGen :: (State -> IORef (Cache a)) -> Cached a -> State -> IO a
uncacheGen getCache (Cached f) st = do
let rCache = getCache st
stored <- readIORef rCache
sn <- f `seq` makeStableName f
let h = hashStableName sn
case maybe Nothing (sn `lookup`) (h `IMap.lookup` stored) of
Just r -> return r
Nothing -> do r <- f st
r `seq` R.modifyIORef' rCache (IMap.insertWith (++) h [(sn, r)])
return r
data SMTLibVersion = SMTLib2
deriving (Bounded, Enum, Eq, Show)
smtLibVersionExtension :: SMTLibVersion -> String
smtLibVersionExtension SMTLib2 = "smt2"
data SMTLibPgm = SMTLibPgm SMTLibVersion [String]
instance NFData SMTLibVersion where rnf a = a `seq` ()
instance NFData SMTLibPgm where rnf (SMTLibPgm v p) = rnf v `seq` rnf p `seq` ()
instance Show SMTLibPgm where
show (SMTLibPgm _ pre) = intercalate "\n" pre
instance NFData CW where
rnf (CW x y) = x `seq` y `seq` ()
instance NFData GeneralizedCW where
rnf (ExtendedCW e) = e `seq` ()
rnf (RegularCW c) = c `seq` ()
#if MIN_VERSION_base(4,9,0)
#else
instance NFData CallStack where
rnf _ = ()
#endif
instance NFData Result where
rnf (Result kindInfo qcInfo cgs inps consts tbls arrs uis axs pgm cstr asserts outs)
= rnf kindInfo `seq` rnf qcInfo `seq` rnf cgs `seq` rnf inps
`seq` rnf consts `seq` rnf tbls `seq` rnf arrs
`seq` rnf uis `seq` rnf axs `seq` rnf pgm
`seq` rnf cstr `seq` rnf asserts `seq` rnf outs
instance NFData Kind where rnf a = seq a ()
instance NFData ArrayContext where rnf a = seq a ()
instance NFData SW where rnf a = seq a ()
instance NFData SBVExpr where rnf a = seq a ()
instance NFData Quantifier where rnf a = seq a ()
instance NFData SBVType where rnf a = seq a ()
instance NFData SBVPgm where rnf a = seq a ()
instance NFData (Cached a) where rnf (Cached f) = f `seq` ()
instance NFData SVal where rnf (SVal x y) = rnf x `seq` rnf y `seq` ()
instance NFData SMTResult where
rnf Unsatisfiable{} = ()
rnf (Satisfiable _ xs) = rnf xs `seq` ()
rnf (SatExtField _ xs) = rnf xs `seq` ()
rnf (Unknown _ xs) = rnf xs `seq` ()
rnf (ProofError _ xs) = rnf xs `seq` ()
instance NFData SMTModel where
rnf (SMTModel objs assocs) = rnf objs `seq` rnf assocs `seq` ()
instance NFData SMTScript where
rnf (SMTScript b m) = rnf b `seq` rnf m `seq` ()
data SolverCapabilities = SolverCapabilities {
supportsQuantifiers :: Bool
, supportsUninterpretedSorts :: Bool
, supportsUnboundedInts :: Bool
, supportsReals :: Bool
, supportsApproxReals :: Bool
, supportsIEEE754 :: Bool
, supportsOptimization :: Bool
, supportsPseudoBooleans :: Bool
, supportsCustomQueries :: Bool
, supportsGlobalDecls :: Bool
}
data RoundingMode = RoundNearestTiesToEven
| RoundNearestTiesToAway
| RoundTowardPositive
| RoundTowardNegative
| RoundTowardZero
deriving (Eq, Ord, Show, Read, G.Data, Bounded, Enum)
instance HasKind RoundingMode
data SMTConfig = SMTConfig {
verbose :: Bool
, timing :: Timing
, printBase :: Int
, printRealPrec :: Int
, satCmd :: String
, allSatMaxModelCount :: Maybe Int
, isNonModelVar :: String -> Bool
, transcript :: Maybe FilePath
, smtLibVersion :: SMTLibVersion
, solver :: SMTSolver
, roundingMode :: RoundingMode
, solverSetOptions :: [SMTOption]
, ignoreExitCode :: Bool
, redirectVerbose :: Maybe FilePath
}
instance NFData SMTConfig where
rnf SMTConfig{} = ()
data SMTModel = SMTModel {
modelObjectives :: [(String, GeneralizedCW)]
, modelAssocs :: [(String, CW)]
}
deriving Show
data SMTResult = Unsatisfiable SMTConfig
| Satisfiable SMTConfig SMTModel
| SatExtField SMTConfig SMTModel
| Unknown SMTConfig String
| ProofError SMTConfig [String]
data SMTScript = SMTScript {
scriptBody :: String
, scriptModel :: [String]
}
type SMTEngine = forall res.
SMTConfig
-> State
-> String
-> (State -> IO res)
-> IO res
data Solver = Z3
| Yices
| Boolector
| CVC4
| MathSAT
| ABC
deriving (Show, Enum, Bounded)
data SMTSolver = SMTSolver {
name :: Solver
, executable :: String
, options :: SMTConfig -> [String]
, engine :: SMTEngine
, capabilities :: SolverCapabilities
}
{-# ANN type FPOp ("HLint: ignore Use camelCase" :: String) #-}
{-# ANN type PBOp ("HLint: ignore Use camelCase" :: String) #-}