mirror of
https://github.com/GaloisInc/cryptol.git
synced 2024-11-24 15:08:58 +03:00
Merge changes from SBV release version 3.1
This commit is contained in:
parent
11d5c884b7
commit
db08bfafa9
@ -18,6 +18,7 @@ flag static
|
|||||||
library
|
library
|
||||||
Build-depends: base >= 4.6,
|
Build-depends: base >= 4.6,
|
||||||
array >= 0.4,
|
array >= 0.4,
|
||||||
|
async >= 2.0,
|
||||||
containers >= 0.5,
|
containers >= 0.5,
|
||||||
deepseq >= 1.3,
|
deepseq >= 1.3,
|
||||||
directory >= 1.2,
|
directory >= 1.2,
|
||||||
@ -25,7 +26,7 @@ library
|
|||||||
filepath >= 1.3,
|
filepath >= 1.3,
|
||||||
GraphSCC >= 1.0.4,
|
GraphSCC >= 1.0.4,
|
||||||
monadLib >= 3.7.2,
|
monadLib >= 3.7.2,
|
||||||
mtl >= 2.1,
|
mtl >= 2.2.1,
|
||||||
old-time >= 1.1,
|
old-time >= 1.1,
|
||||||
presburger >= 1.1,
|
presburger >= 1.1,
|
||||||
pretty >= 1.1,
|
pretty >= 1.1,
|
||||||
@ -119,6 +120,7 @@ library
|
|||||||
Data.SBV,
|
Data.SBV,
|
||||||
Data.SBV.Bridge.Boolector,
|
Data.SBV.Bridge.Boolector,
|
||||||
Data.SBV.Bridge.CVC4,
|
Data.SBV.Bridge.CVC4,
|
||||||
|
Data.SBV.Bridge.MathSAT,
|
||||||
Data.SBV.Bridge.Yices,
|
Data.SBV.Bridge.Yices,
|
||||||
Data.SBV.Bridge.Z3,
|
Data.SBV.Bridge.Z3,
|
||||||
Data.SBV.Internals,
|
Data.SBV.Internals,
|
||||||
@ -141,6 +143,7 @@ library
|
|||||||
Data.SBV.Provers.CVC4,
|
Data.SBV.Provers.CVC4,
|
||||||
Data.SBV.Provers.Yices,
|
Data.SBV.Provers.Yices,
|
||||||
Data.SBV.Provers.Z3,
|
Data.SBV.Provers.Z3,
|
||||||
|
Data.SBV.Provers.MathSAT,
|
||||||
Data.SBV.Tools.ExpectedValue,
|
Data.SBV.Tools.ExpectedValue,
|
||||||
Data.SBV.Tools.GenTest,
|
Data.SBV.Tools.GenTest,
|
||||||
Data.SBV.Tools.Optimize,
|
Data.SBV.Tools.Optimize,
|
||||||
|
191
sbv/Data/SBV.hs
191
sbv/Data/SBV.hs
@ -40,7 +40,7 @@
|
|||||||
--
|
--
|
||||||
-- * 'SInt8', 'SInt16', 'SInt32', 'SInt64': Symbolic Ints (signed).
|
-- * 'SInt8', 'SInt16', 'SInt32', 'SInt64': Symbolic Ints (signed).
|
||||||
--
|
--
|
||||||
-- * 'SArray', 'SFunArray': Flat arrays of symbolic values.
|
-- * 'SInteger': Unbounded signed integers.
|
||||||
--
|
--
|
||||||
-- * 'SReal': Algebraic-real numbers
|
-- * 'SReal': Algebraic-real numbers
|
||||||
--
|
--
|
||||||
@ -48,6 +48,8 @@
|
|||||||
--
|
--
|
||||||
-- * 'SDouble': IEEE-754 double-precision floating point values
|
-- * 'SDouble': IEEE-754 double-precision floating point values
|
||||||
--
|
--
|
||||||
|
-- * 'SArray', 'SFunArray': Flat arrays of symbolic values.
|
||||||
|
--
|
||||||
-- * Symbolic polynomials over GF(2^n), polynomial arithmetic, and CRCs.
|
-- * Symbolic polynomials over GF(2^n), polynomial arithmetic, and CRCs.
|
||||||
--
|
--
|
||||||
-- * Uninterpreted constants and functions over symbolic values, with user
|
-- * Uninterpreted constants and functions over symbolic values, with user
|
||||||
@ -92,10 +94,18 @@
|
|||||||
--
|
--
|
||||||
-- * Boolector from Johannes Kepler University: <http://fmv.jku.at/boolector/>
|
-- * Boolector from Johannes Kepler University: <http://fmv.jku.at/boolector/>
|
||||||
--
|
--
|
||||||
|
-- * MathSAT from Fondazione Bruno Kessler and DISI-University of Trento: <http://mathsat.fbk.eu/>
|
||||||
|
--
|
||||||
|
-- SBV also allows calling these solvers in parallel, either getting results from multiple solvers
|
||||||
|
-- or returning the fastest one. (See 'proveWithAll', 'proveWithAny', etc.)
|
||||||
|
--
|
||||||
-- Support for other compliant solvers can be added relatively easily, please
|
-- Support for other compliant solvers can be added relatively easily, please
|
||||||
-- get in touch if there is a solver you'd like to see included.
|
-- get in touch if there is a solver you'd like to see included.
|
||||||
---------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverlappingInstances #-}
|
||||||
|
|
||||||
module Data.SBV (
|
module Data.SBV (
|
||||||
-- * Programming with symbolic values
|
-- * Programming with symbolic values
|
||||||
-- $progIntro
|
-- $progIntro
|
||||||
@ -113,7 +123,7 @@ module Data.SBV (
|
|||||||
, SInteger
|
, SInteger
|
||||||
-- *** IEEE-floating point numbers
|
-- *** IEEE-floating point numbers
|
||||||
-- $floatingPoints
|
-- $floatingPoints
|
||||||
, SFloat, SDouble, RoundingMode(..), nan, infinity, sNaN, sInfinity, fusedMA
|
, SFloat, SDouble, RoundingMode(..), nan, infinity, sNaN, sInfinity, fusedMA, isSNaN, isFPPoint
|
||||||
-- *** Signed algebraic reals
|
-- *** Signed algebraic reals
|
||||||
-- $algReals
|
-- $algReals
|
||||||
, SReal, AlgReal, toSReal
|
, SReal, AlgReal, toSReal
|
||||||
@ -187,6 +197,10 @@ module Data.SBV (
|
|||||||
-- ** Checking constraint vacuity
|
-- ** Checking constraint vacuity
|
||||||
, isVacuous, isVacuousWith
|
, isVacuous, isVacuousWith
|
||||||
|
|
||||||
|
-- * Proving properties using multiple solvers
|
||||||
|
-- $multiIntro
|
||||||
|
, proveWithAll, proveWithAny, satWithAll, satWithAny, allSatWithAll, allSatWithAny
|
||||||
|
|
||||||
-- * Optimization
|
-- * Optimization
|
||||||
-- $optimizeIntro
|
-- $optimizeIntro
|
||||||
, minimize, maximize, optimize
|
, minimize, maximize, optimize
|
||||||
@ -205,9 +219,10 @@ module Data.SBV (
|
|||||||
-- ** Programmable model extraction
|
-- ** Programmable model extraction
|
||||||
-- $programmableExtraction
|
-- $programmableExtraction
|
||||||
, SatModel(..), Modelable(..), displayModels, extractModels
|
, SatModel(..), Modelable(..), displayModels, extractModels
|
||||||
|
, getModelDictionaries, getModelValues, getModelUninterpretedValues
|
||||||
|
|
||||||
-- * SMT Interface: Configurations and solvers
|
-- * SMT Interface: Configurations and solvers
|
||||||
, SMTConfig(..), OptimizeOpts(..), SMTSolver(..), boolector, cvc4, yices, z3, sbvCurrentSolver, defaultSMTCfg, sbvCheckSolverInstallation
|
, SMTConfig(..), SMTLibLogic(..), Logic(..), OptimizeOpts(..), Solver(..), SMTSolver(..), boolector, cvc4, yices, z3, mathSAT, sbvCurrentSolver, defaultSMTCfg, sbvCheckSolverInstallation, sbvAvailableSolvers
|
||||||
|
|
||||||
-- * Symbolic computations
|
-- * Symbolic computations
|
||||||
, Symbolic, output, SymWord(..)
|
, Symbolic, output, SymWord(..)
|
||||||
@ -253,6 +268,10 @@ module Data.SBV (
|
|||||||
, module Data.Ratio
|
, module Data.Ratio
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad (filterM)
|
||||||
|
import Control.Concurrent.Async (async, waitAny, waitAnyCancel)
|
||||||
|
import System.IO.Unsafe (unsafeInterleaveIO) -- only used safely!
|
||||||
|
|
||||||
import Data.SBV.BitVectors.AlgReals
|
import Data.SBV.BitVectors.AlgReals
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.BitVectors.Model
|
import Data.SBV.BitVectors.Model
|
||||||
@ -280,6 +299,140 @@ import Data.Word
|
|||||||
sbvCurrentSolver :: SMTConfig
|
sbvCurrentSolver :: SMTConfig
|
||||||
sbvCurrentSolver = z3
|
sbvCurrentSolver = z3
|
||||||
|
|
||||||
|
-- | Note that the floating point value NaN does not compare equal to itself,
|
||||||
|
-- so we need a special recognizer for that. Haskell provides the isNaN predicate
|
||||||
|
-- with the `RealFrac` class, which unfortunately is not currently implementable for
|
||||||
|
-- symbolic cases. (Requires trigonometric functions etc.) Thus, we provide this
|
||||||
|
-- recognizer separately. Note that the definition simply tests equality against
|
||||||
|
-- itself, which fails for NaN. Who said equality for floating point was reflexive?
|
||||||
|
isSNaN :: (Floating a, SymWord a) => SBV a -> SBool
|
||||||
|
isSNaN x = x ./= x
|
||||||
|
|
||||||
|
-- | We call a FP number FPPoint if it is neither NaN, nor +/- infinity.
|
||||||
|
isFPPoint :: (Floating a, SymWord a) => SBV a -> SBool
|
||||||
|
isFPPoint x = x .== x -- gets rid of NaN's
|
||||||
|
&&& x .< sInfinity -- gets rid of +inf
|
||||||
|
&&& x .> -sInfinity -- gets rid of -inf
|
||||||
|
|
||||||
|
-- | Form the symbolic conjunction of a given list of boolean conditions. Useful in expressing
|
||||||
|
-- problems with constraints, like the following:
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- do [x, y, z] <- sIntegers [\"x\", \"y\", \"z\"]
|
||||||
|
-- solve [x .> 5, y + z .< x]
|
||||||
|
-- @
|
||||||
|
solve :: [SBool] -> Symbolic SBool
|
||||||
|
solve = return . bAnd
|
||||||
|
|
||||||
|
-- | Check whether the given solver is installed and is ready to go. This call does a
|
||||||
|
-- simple call to the solver to ensure all is well.
|
||||||
|
sbvCheckSolverInstallation :: SMTConfig -> IO Bool
|
||||||
|
sbvCheckSolverInstallation cfg = do ThmResult r <- proveWith cfg $ \x -> (x+x) .== ((x*2) :: SWord8)
|
||||||
|
case r of
|
||||||
|
Unsatisfiable _ -> return True
|
||||||
|
_ -> return False
|
||||||
|
|
||||||
|
-- The default configs
|
||||||
|
defaultSolverConfig :: Solver -> SMTConfig
|
||||||
|
defaultSolverConfig Z3 = z3
|
||||||
|
defaultSolverConfig Yices = yices
|
||||||
|
defaultSolverConfig Boolector = boolector
|
||||||
|
defaultSolverConfig CVC4 = cvc4
|
||||||
|
defaultSolverConfig MathSAT = mathSAT
|
||||||
|
|
||||||
|
-- | Return the known available solver configs, installed on your machine.
|
||||||
|
sbvAvailableSolvers :: IO [SMTConfig]
|
||||||
|
sbvAvailableSolvers = filterM sbvCheckSolverInstallation (map defaultSolverConfig [minBound .. maxBound])
|
||||||
|
|
||||||
|
sbvWithAny :: Provable a => [SMTConfig] -> (SMTConfig -> a -> IO b) -> a -> IO (Solver, b)
|
||||||
|
sbvWithAny [] _ _ = error "SBV.withAny: No solvers given!"
|
||||||
|
sbvWithAny solvers what a = snd `fmap` (mapM try solvers >>= waitAnyCancel)
|
||||||
|
where try s = async $ what s a >>= \r -> return (name (solver s), r)
|
||||||
|
|
||||||
|
sbvWithAll :: Provable a => [SMTConfig] -> (SMTConfig -> a -> IO b) -> a -> IO [(Solver, b)]
|
||||||
|
sbvWithAll solvers what a = mapM try solvers >>= (unsafeInterleaveIO . go)
|
||||||
|
where try s = async $ what s a >>= \r -> return (name (solver s), r)
|
||||||
|
go [] = return []
|
||||||
|
go as = do (d, r) <- waitAny as
|
||||||
|
rs <- unsafeInterleaveIO $ go (filter (/= d) as)
|
||||||
|
return (r : rs)
|
||||||
|
|
||||||
|
-- | Prove a property with multiple solvers, running them in separate threads. The
|
||||||
|
-- results will be returned in the order produced.
|
||||||
|
proveWithAll :: Provable a => [SMTConfig] -> a -> IO [(Solver, ThmResult)]
|
||||||
|
proveWithAll = (`sbvWithAll` proveWith)
|
||||||
|
|
||||||
|
-- | Prove a property with multiple solvers, running them in separate threads. Only
|
||||||
|
-- the result of the first one to finish will be returned, remaining threads will be killed.
|
||||||
|
proveWithAny :: Provable a => [SMTConfig] -> a -> IO (Solver, ThmResult)
|
||||||
|
proveWithAny = (`sbvWithAny` proveWith)
|
||||||
|
|
||||||
|
-- | Find a satisfying assignment to a property with multiple solvers, running them in separate threads. The
|
||||||
|
-- results will be returned in the order produced.
|
||||||
|
satWithAll :: Provable a => [SMTConfig] -> a -> IO [(Solver, SatResult)]
|
||||||
|
satWithAll = (`sbvWithAll` satWith)
|
||||||
|
|
||||||
|
-- | Find a satisfying assignment to a property with multiple solvers, running them in separate threads. Only
|
||||||
|
-- the result of the first one to finish will be returned, remaining threads will be killed.
|
||||||
|
satWithAny :: Provable a => [SMTConfig] -> a -> IO (Solver, SatResult)
|
||||||
|
satWithAny = (`sbvWithAny` satWith)
|
||||||
|
|
||||||
|
-- | Find all satisfying assignments to a property with multiple solvers, running them in separate threads. Only
|
||||||
|
-- the result of the first one to finish will be returned, remaining threads will be killed.
|
||||||
|
allSatWithAll :: Provable a => [SMTConfig] -> a -> IO [(Solver, AllSatResult)]
|
||||||
|
allSatWithAll = (`sbvWithAll` allSatWith)
|
||||||
|
|
||||||
|
-- | Find all satisfying assignments to a property with multiple solvers, running them in separate threads. Only
|
||||||
|
-- the result of the first one to finish will be returned, remaining threads will be killed.
|
||||||
|
allSatWithAny :: Provable a => [SMTConfig] -> a -> IO (Solver, AllSatResult)
|
||||||
|
allSatWithAny = (`sbvWithAny` allSatWith)
|
||||||
|
|
||||||
|
-- | Equality as a proof method. Allows for
|
||||||
|
-- very concise construction of equivalence proofs, which is very typical in
|
||||||
|
-- bit-precise proofs.
|
||||||
|
infix 4 ===
|
||||||
|
class Equality a where
|
||||||
|
(===) :: a -> a -> IO ThmResult
|
||||||
|
|
||||||
|
instance (SymWord a, EqSymbolic z) => Equality (SBV a -> z) where
|
||||||
|
k === l = prove $ \a -> k a .== l a
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, EqSymbolic z) => Equality (SBV a -> SBV b -> z) where
|
||||||
|
k === l = prove $ \a b -> k a b .== l a b
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, EqSymbolic z) => Equality ((SBV a, SBV b) -> z) where
|
||||||
|
k === l = prove $ \a b -> k (a, b) .== l (a, b)
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> z) where
|
||||||
|
k === l = prove $ \a b c -> k a b c .== l a b c
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c) -> z) where
|
||||||
|
k === l = prove $ \a b c -> k (a, b, c) .== l (a, b, c)
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> z) where
|
||||||
|
k === l = prove $ \a b c d -> k a b c d .== l a b c d
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d) -> z) where
|
||||||
|
k === l = prove $ \a b c d -> k (a, b, c, d) .== l (a, b, c, d)
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> SBV e -> z) where
|
||||||
|
k === l = prove $ \a b c d e -> k a b c d e .== l a b c d e
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d, SBV e) -> z) where
|
||||||
|
k === l = prove $ \a b c d e -> k (a, b, c, d, e) .== l (a, b, c, d, e)
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> SBV e -> SBV f -> z) where
|
||||||
|
k === l = prove $ \a b c d e f -> k a b c d e f .== l a b c d e f
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f) -> z) where
|
||||||
|
k === l = prove $ \a b c d e f -> k (a, b, c, d, e, f) .== l (a, b, c, d, e, f)
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SymWord g, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> SBV e -> SBV f -> SBV g -> z) where
|
||||||
|
k === l = prove $ \a b c d e f g -> k a b c d e f g .== l a b c d e f g
|
||||||
|
|
||||||
|
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SymWord g, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f, SBV g) -> z) where
|
||||||
|
k === l = prove $ \a b c d e f g -> k (a, b, c, d, e, f, g) .== l (a, b, c, d, e, f, g)
|
||||||
|
|
||||||
-- Haddock section documentation
|
-- Haddock section documentation
|
||||||
{- $progIntro
|
{- $progIntro
|
||||||
The SBV library is really two things:
|
The SBV library is really two things:
|
||||||
@ -309,12 +462,32 @@ design goal is to let SMT solvers be used without any knowledge of how SMT solve
|
|||||||
or how different logics operate. The details are hidden behind the SBV framework, providing
|
or how different logics operate. The details are hidden behind the SBV framework, providing
|
||||||
Haskell programmers with a clean API that is unencumbered by the details of individual solvers.
|
Haskell programmers with a clean API that is unencumbered by the details of individual solvers.
|
||||||
To that end, we use the SMT-Lib standard (<http://goedel.cs.uiowa.edu/smtlib/>)
|
To that end, we use the SMT-Lib standard (<http://goedel.cs.uiowa.edu/smtlib/>)
|
||||||
to communicate with arbitrary SMT solvers. Unfortunately,
|
to communicate with arbitrary SMT solvers.
|
||||||
the SMT-Lib version 1.X does not standardize how models are communicated back from solvers, so
|
-}
|
||||||
there is some work in parsing individual SMT solver output. The 2.X version of the SMT-Lib
|
|
||||||
standard (not yet implemented by SMT solvers widely, unfortunately) will bring new standard features
|
{- $multiIntro
|
||||||
for getting models; at which time the SBV framework can be modified into a truly plug-and-play
|
On a multi-core machine, it might be desirable to try a given property using multiple SMT solvers,
|
||||||
system where arbitrary SMT solvers can be used.
|
using parallel threads. Even with machines with single-cores, threading can be helpful if you
|
||||||
|
want to try out multiple-solvers but do not know which one would work the best
|
||||||
|
for the problem at hand ahead of time.
|
||||||
|
|
||||||
|
The functions in this section allow proving/satisfiability-checking with multiple
|
||||||
|
backends at the same time. Each function comes in two variants, one that
|
||||||
|
returns the results from all solvers, the other that returns the fastest one.
|
||||||
|
|
||||||
|
The @All@ variants, (i.e., 'proveWithAll', 'satWithAll', 'allSatWithAll') run all solvers and
|
||||||
|
return all the results. SBV internally makes sure that the result is lazily generated; so,
|
||||||
|
the order of solvers given does not matter. In other words, the order of results will follow
|
||||||
|
the order of the solvers as they finish, not as given by the user. These variants are useful when you
|
||||||
|
want to make sure multiple-solvers agree (or disagree!) on a given problem.
|
||||||
|
|
||||||
|
The @Any@ variants, (i.e., 'proveWithAny', 'satWithAny', 'allSatWithAny') will run all the solvers
|
||||||
|
in parallel, and return the results of the first one finishing. The other threads will then be killed. These variants
|
||||||
|
are useful when you do not care if the solvers produce the same result, but rather want to get the
|
||||||
|
solution as quickly as possible, taking advantage of modern many-core machines.
|
||||||
|
|
||||||
|
Note that the function 'sbvAvailableSolvers' will return all the installed solvers, which can be
|
||||||
|
used as the first argument to all these functions, if you simply want to try all available solvers on a machine.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{- $optimizeIntro
|
{- $optimizeIntro
|
||||||
|
@ -49,7 +49,7 @@ mkPolyReal (Left (exact, str))
|
|||||||
mkPolyReal (Right (k, coeffs))
|
mkPolyReal (Right (k, coeffs))
|
||||||
= AlgPolyRoot (k, Polynomial (normalize coeffs)) Nothing
|
= AlgPolyRoot (k, Polynomial (normalize coeffs)) Nothing
|
||||||
where normalize :: [(Integer, Integer)] -> [(Integer, Integer)]
|
where normalize :: [(Integer, Integer)] -> [(Integer, Integer)]
|
||||||
normalize = merge . reverse . sortBy (compare `on` snd)
|
normalize = merge . sortBy (flip compare `on` snd)
|
||||||
merge [] = []
|
merge [] = []
|
||||||
merge [x] = [x]
|
merge [x] = [x]
|
||||||
merge ((a, b):r@((c, d):xs))
|
merge ((a, b):r@((c, d):xs))
|
||||||
|
@ -17,31 +17,35 @@
|
|||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE PatternGuards #-}
|
{-# LANGUAGE PatternGuards #-}
|
||||||
{-# LANGUAGE DefaultSignatures #-}
|
{-# LANGUAGE DefaultSignatures #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
|
||||||
module Data.SBV.BitVectors.Data
|
module Data.SBV.BitVectors.Data
|
||||||
( SBool, SWord8, SWord16, SWord32, SWord64
|
( SBool, SWord8, SWord16, SWord32, SWord64
|
||||||
, SInt8, SInt16, SInt32, SInt64, SInteger, SReal, SFloat, SDouble
|
, SInt8, SInt16, SInt32, SInt64, SInteger, SReal, SFloat, SDouble
|
||||||
, nan, infinity, sNaN, sInfinity, RoundingMode(..), smtLibSquareRoot, smtLibFusedMA
|
, nan, infinity, sNaN, sInfinity, RoundingMode(..), smtLibSquareRoot, smtLibFusedMA
|
||||||
, SymWord(..)
|
, SymWord(..)
|
||||||
, CW(..), CWVal(..), cwSameType, cwIsBit, cwToBool, normCW
|
, CW(..), CWVal(..), AlgReal(..), cwSameType, cwIsBit, cwToBool
|
||||||
, mkConstCW ,liftCW2, mapCW, mapCW2
|
, mkConstCW ,liftCW2, mapCW, mapCW2
|
||||||
, SW(..), trueSW, falseSW, trueCW, falseCW
|
, SW(..), trueSW, falseSW, trueCW, falseCW, normCW
|
||||||
, SBV(..), NodeId(..), mkSymSBV, mkSymSBVWithRandom
|
, SBV(..), NodeId(..), mkSymSBV, mkSymSBVWithRandom
|
||||||
, ArrayContext(..), ArrayInfo, SymArray(..), SFunArray(..), mkSFunArray, SArray(..), arrayUIKind
|
, ArrayContext(..), ArrayInfo, SymArray(..), SFunArray(..), mkSFunArray, SArray(..), arrayUIKind
|
||||||
, sbvToSW, sbvToSymSW
|
, sbvToSW, sbvToSymSW, forceSWArg
|
||||||
, SBVExpr(..), newExpr
|
, SBVExpr(..), newExpr
|
||||||
, cache, Cached, uncache, uncacheAI, HasKind(..)
|
, cache, Cached, uncache, uncacheAI, HasKind(..)
|
||||||
, Op(..), NamedSymVar, UnintKind(..), getTableIndex, SBVPgm(..), Symbolic, runSymbolic, runSymbolic', State, inProofMode, SBVRunMode(..), Kind(..), Outputtable(..), Result(..)
|
, Op(..), NamedSymVar, UnintKind(..), getTableIndex, SBVPgm(..), Symbolic, runSymbolic, runSymbolic', State, getPathCondition, extendPathCondition
|
||||||
, readResult, getResult
|
, inProofMode, SBVRunMode(..), Kind(..), Outputtable(..), Result(..)
|
||||||
|
, Logic(..), SMTLibLogic(..)
|
||||||
, getTraceInfo, getConstraints, addConstraint
|
, getTraceInfo, getConstraints, addConstraint
|
||||||
, SBVType(..), newUninterpreted, unintFnUIKind, addAxiom
|
, SBVType(..), newUninterpreted, unintFnUIKind, addAxiom
|
||||||
, Quantifier(..), needsExistentials
|
, Quantifier(..), needsExistentials
|
||||||
, SMTLibPgm(..), SMTLibVersion(..)
|
, SMTLibPgm(..), SMTLibVersion(..)
|
||||||
, SolverCapabilities(..)
|
, SolverCapabilities(..)
|
||||||
|
, extractSymbolicSimulationState, getResult
|
||||||
|
, SMTScript(..), Solver(..), SMTSolver(..), SMTResult(..), SMTModel(..), SMTConfig(..), getSBranchRunConfig
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative (Applicative)
|
|
||||||
import Control.DeepSeq (NFData(..))
|
import Control.DeepSeq (NFData(..))
|
||||||
|
import Control.Applicative (Applicative)
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Control.Monad.Fix (MonadFix)
|
import Control.Monad.Fix (MonadFix)
|
||||||
import Control.Monad.Reader (MonadReader, ReaderT, ask, runReaderT)
|
import Control.Monad.Reader (MonadReader, ReaderT, ask, runReaderT)
|
||||||
@ -60,6 +64,7 @@ import qualified Data.Set as Set (Set, empty, toList, insert)
|
|||||||
import qualified Data.Foldable as F (toList)
|
import qualified Data.Foldable as F (toList)
|
||||||
import qualified Data.Sequence as S (Seq, empty, (|>))
|
import qualified Data.Sequence as S (Seq, empty, (|>))
|
||||||
|
|
||||||
|
import System.Exit (ExitCode(..))
|
||||||
import System.Mem.StableName
|
import System.Mem.StableName
|
||||||
import System.Random
|
import System.Random
|
||||||
|
|
||||||
@ -130,8 +135,8 @@ cwSameType x y = cwKind x == cwKind y
|
|||||||
-- | Is this a bit?
|
-- | Is this a bit?
|
||||||
cwIsBit :: CW -> Bool
|
cwIsBit :: CW -> Bool
|
||||||
cwIsBit x = case cwKind x of
|
cwIsBit x = case cwKind x of
|
||||||
KBounded False 1 -> True
|
KBool -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
-- | Convert a CW to a Haskell boolean (NB. Assumes input is well-kinded)
|
-- | Convert a CW to a Haskell boolean (NB. Assumes input is well-kinded)
|
||||||
cwToBool :: CW -> Bool
|
cwToBool :: CW -> Bool
|
||||||
@ -152,7 +157,8 @@ normCW c@(CW (KBounded signed sz) (CWInteger v)) = c { cwVal = CWInteger norm }
|
|||||||
normCW c = c
|
normCW c = c
|
||||||
|
|
||||||
-- | Kind of symbolic value
|
-- | Kind of symbolic value
|
||||||
data Kind = KBounded Bool Int
|
data Kind = KBool
|
||||||
|
| KBounded Bool Int
|
||||||
| KUnbounded
|
| KUnbounded
|
||||||
| KReal
|
| KReal
|
||||||
| KUninterpreted String
|
| KUninterpreted String
|
||||||
@ -161,7 +167,7 @@ data Kind = KBounded Bool Int
|
|||||||
deriving (Eq, Ord)
|
deriving (Eq, Ord)
|
||||||
|
|
||||||
instance Show Kind where
|
instance Show Kind where
|
||||||
show (KBounded False 1) = "SBool"
|
show KBool = "SBool"
|
||||||
show (KBounded False n) = "SWord" ++ show n
|
show (KBounded False n) = "SWord" ++ show n
|
||||||
show (KBounded True n) = "SInt" ++ show n
|
show (KBounded True n) = "SInt" ++ show n
|
||||||
show KUnbounded = "SInteger"
|
show KUnbounded = "SInteger"
|
||||||
@ -176,6 +182,12 @@ newtype NodeId = NodeId Int deriving (Eq, Ord)
|
|||||||
-- | A symbolic word, tracking it's signedness and size.
|
-- | A symbolic word, tracking it's signedness and size.
|
||||||
data SW = SW Kind NodeId deriving (Eq, Ord)
|
data SW = SW Kind NodeId deriving (Eq, Ord)
|
||||||
|
|
||||||
|
-- | Forcing an argument; this is a necessary evil to make sure all the arguments
|
||||||
|
-- to an uninterpreted function and sBranch test conditions are evaluated before called;
|
||||||
|
-- the semantics of uinterpreted functions is necessarily strict; deviating from Haskell's
|
||||||
|
forceSWArg :: SW -> IO ()
|
||||||
|
forceSWArg (SW k n) = k `seq` n `seq` return ()
|
||||||
|
|
||||||
-- | Quantifiers: forall or exists. Note that we allow
|
-- | Quantifiers: forall or exists. Note that we allow
|
||||||
-- arbitrary nestings.
|
-- arbitrary nestings.
|
||||||
data Quantifier = ALL | EX deriving Eq
|
data Quantifier = ALL | EX deriving Eq
|
||||||
@ -186,19 +198,19 @@ needsExistentials = (EX `elem`)
|
|||||||
|
|
||||||
-- | Constant False as a SW. Note that this value always occupies slot -2.
|
-- | Constant False as a SW. Note that this value always occupies slot -2.
|
||||||
falseSW :: SW
|
falseSW :: SW
|
||||||
falseSW = SW (KBounded False 1) $ NodeId (-2)
|
falseSW = SW KBool $ NodeId (-2)
|
||||||
|
|
||||||
-- | Constant False as a SW. Note that this value always occupies slot -1.
|
-- | Constant False as a SW. Note that this value always occupies slot -1.
|
||||||
trueSW :: SW
|
trueSW :: SW
|
||||||
trueSW = SW (KBounded False 1) $ NodeId (-1)
|
trueSW = SW KBool $ NodeId (-1)
|
||||||
|
|
||||||
-- | Constant False as a CW. We represent it using the integer value 0.
|
-- | Constant False as a CW. We represent it using the integer value 0.
|
||||||
falseCW :: CW
|
falseCW :: CW
|
||||||
falseCW = CW (KBounded False 1) (CWInteger 0)
|
falseCW = CW KBool (CWInteger 0)
|
||||||
|
|
||||||
-- | Constant True as a CW. We represent it using the integer value 1.
|
-- | Constant True as a CW. We represent it using the integer value 1.
|
||||||
trueCW :: CW
|
trueCW :: CW
|
||||||
trueCW = CW (KBounded False 1) (CWInteger 1)
|
trueCW = CW KBool (CWInteger 1)
|
||||||
|
|
||||||
-- | A simple type for SBV computations, used mainly for uninterpreted constants.
|
-- | A simple type for SBV computations, used mainly for uninterpreted constants.
|
||||||
-- We keep track of the signedness/size of the arguments. A non-function will
|
-- We keep track of the signedness/size of the arguments. A non-function will
|
||||||
@ -265,6 +277,7 @@ class HasKind a where
|
|||||||
showType :: a -> String
|
showType :: a -> String
|
||||||
-- defaults
|
-- defaults
|
||||||
hasSign x = case kindOf x of
|
hasSign x = case kindOf x of
|
||||||
|
KBool -> False
|
||||||
KBounded b _ -> b
|
KBounded b _ -> b
|
||||||
KUnbounded -> True
|
KUnbounded -> True
|
||||||
KReal -> True
|
KReal -> True
|
||||||
@ -272,13 +285,14 @@ class HasKind a where
|
|||||||
KDouble -> True
|
KDouble -> True
|
||||||
KUninterpreted{} -> False
|
KUninterpreted{} -> False
|
||||||
intSizeOf x = case kindOf x of
|
intSizeOf x = case kindOf x of
|
||||||
|
KBool -> error "SBV.HasKind.intSizeOf((S)Bool)"
|
||||||
KBounded _ s -> s
|
KBounded _ s -> s
|
||||||
KUnbounded -> error "SBV.HasKind.intSizeOf((S)Integer)"
|
KUnbounded -> error "SBV.HasKind.intSizeOf((S)Integer)"
|
||||||
KReal -> error "SBV.HasKind.intSizeOf((S)Real)"
|
KReal -> error "SBV.HasKind.intSizeOf((S)Real)"
|
||||||
KFloat -> error "SBV.HasKind.intSizeOf((S)Float)"
|
KFloat -> error "SBV.HasKind.intSizeOf((S)Float)"
|
||||||
KDouble -> error "SBV.HasKind.intSizeOf((S)Double)"
|
KDouble -> error "SBV.HasKind.intSizeOf((S)Double)"
|
||||||
KUninterpreted s -> error $ "SBV.HasKind.intSizeOf: Uninterpreted sort: " ++ s
|
KUninterpreted s -> error $ "SBV.HasKind.intSizeOf: Uninterpreted sort: " ++ s
|
||||||
isBoolean x | KBounded False 1 <- kindOf x = True
|
isBoolean x | KBool{} <- kindOf x = True
|
||||||
| True = False
|
| True = False
|
||||||
isBounded x | KBounded{} <- kindOf x = True
|
isBounded x | KBounded{} <- kindOf x = True
|
||||||
| True = False
|
| True = False
|
||||||
@ -298,7 +312,7 @@ class HasKind a where
|
|||||||
default kindOf :: Data a => a -> Kind
|
default kindOf :: Data a => a -> Kind
|
||||||
kindOf = KUninterpreted . tyconUQname . dataTypeName . dataTypeOf
|
kindOf = KUninterpreted . tyconUQname . dataTypeName . dataTypeOf
|
||||||
|
|
||||||
instance HasKind Bool where kindOf _ = KBounded False 1
|
instance HasKind Bool where kindOf _ = KBool
|
||||||
instance HasKind Int8 where kindOf _ = KBounded True 8
|
instance HasKind Int8 where kindOf _ = KBounded True 8
|
||||||
instance HasKind Word8 where kindOf _ = KBounded False 8
|
instance HasKind Word8 where kindOf _ = KBounded False 8
|
||||||
instance HasKind Int16 where kindOf _ = KBounded True 16
|
instance HasKind Int16 where kindOf _ = KBounded True 16
|
||||||
@ -421,7 +435,7 @@ data UnintKind = UFun Int String | UArr Int String -- in each case, arity a
|
|||||||
-- | Result of running a symbolic computation
|
-- | Result of running a symbolic computation
|
||||||
data Result = Result (Set.Set Kind) -- kinds used in the program
|
data Result = Result (Set.Set Kind) -- kinds used in the program
|
||||||
[(String, CW)] -- quick-check counter-example information (if any)
|
[(String, CW)] -- quick-check counter-example information (if any)
|
||||||
[(String, [String])] -- uninterpreted code segments
|
[(String, [String])] -- uninterpeted code segments
|
||||||
[(Quantifier, NamedSymVar)] -- inputs (possibly existential)
|
[(Quantifier, NamedSymVar)] -- inputs (possibly existential)
|
||||||
[(SW, CW)] -- constants
|
[(SW, CW)] -- constants
|
||||||
[((Int, Kind, Kind), [SW])] -- tables (automatically constructed) (tableno, index-type, result-type) elts
|
[((Int, Kind, Kind), [SW])] -- tables (automatically constructed) (tableno, index-type, result-type) elts
|
||||||
@ -540,9 +554,9 @@ arrayUIKind (i, (nm, _, ctx))
|
|||||||
external (ArrayMerge{}) = False
|
external (ArrayMerge{}) = False
|
||||||
|
|
||||||
-- | Different means of running a symbolic piece of code
|
-- | Different means of running a symbolic piece of code
|
||||||
data SBVRunMode = Proof Bool -- ^ Symbolic simulation mode, for proof purposes. Bool is True if it's a sat instance
|
data SBVRunMode = Proof (Bool, Maybe SMTConfig) -- ^ Symbolic simulation mode, for proof purposes. Bool is True if it's a sat instance. SMTConfig is used for 'sBranch' calls.
|
||||||
| CodeGen -- ^ Code generation mode
|
| CodeGen -- ^ Code generation mode
|
||||||
| Concrete StdGen -- ^ Concrete simulation mode. The StdGen is for the pConstrain acceptance in cross runs
|
| Concrete StdGen -- ^ Concrete simulation mode. The StdGen is for the pConstrain acceptance in cross runs
|
||||||
|
|
||||||
-- | Is this a concrete run? (i.e., quick-check or test-generation like)
|
-- | Is this a concrete run? (i.e., quick-check or test-generation like)
|
||||||
isConcreteMode :: SBVRunMode -> Bool
|
isConcreteMode :: SBVRunMode -> Bool
|
||||||
@ -552,6 +566,7 @@ isConcreteMode CodeGen = False
|
|||||||
|
|
||||||
-- | The state of the symbolic interpreter
|
-- | The state of the symbolic interpreter
|
||||||
data State = State { runMode :: SBVRunMode
|
data State = State { runMode :: SBVRunMode
|
||||||
|
, pathCond :: SBool
|
||||||
, rStdGen :: IORef StdGen
|
, rStdGen :: IORef StdGen
|
||||||
, rCInfo :: IORef [(String, CW)]
|
, rCInfo :: IORef [(String, CW)]
|
||||||
, rctr :: IORef Int
|
, rctr :: IORef Int
|
||||||
@ -571,6 +586,14 @@ data State = State { runMode :: SBVRunMode
|
|||||||
, rAICache :: IORef (Cache Int)
|
, rAICache :: IORef (Cache Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Get the current path condition
|
||||||
|
getPathCondition :: State -> SBool
|
||||||
|
getPathCondition = pathCond
|
||||||
|
|
||||||
|
-- | Extend the path condition with the given test value.
|
||||||
|
extendPathCondition :: State -> (SBool -> SBool) -> State
|
||||||
|
extendPathCondition st f = st{pathCond = f (pathCond st)}
|
||||||
|
|
||||||
-- | Are we running in proof mode?
|
-- | Are we running in proof mode?
|
||||||
inProofMode :: State -> Bool
|
inProofMode :: State -> Bool
|
||||||
inProofMode s = case runMode s of
|
inProofMode s = case runMode s of
|
||||||
@ -578,6 +601,12 @@ inProofMode s = case runMode s of
|
|||||||
CodeGen -> False
|
CodeGen -> False
|
||||||
Concrete{} -> False
|
Concrete{} -> False
|
||||||
|
|
||||||
|
-- | If in proof mode, get the underlying configuration (used for 'sBranch')
|
||||||
|
getSBranchRunConfig :: State -> Maybe SMTConfig
|
||||||
|
getSBranchRunConfig st = case runMode st of
|
||||||
|
Proof (_, s) -> s
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
-- | The "Symbolic" value. Either a constant (@Left@) or a symbolic
|
-- | The "Symbolic" value. Either a constant (@Left@) or a symbolic
|
||||||
-- value (@Right Cached@). Note that caching is essential for making
|
-- value (@Right Cached@). Note that caching is essential for making
|
||||||
-- sure sharing is preserved. The parameter 'a' is phantom, but is
|
-- sure sharing is preserved. The parameter 'a' is phantom, but is
|
||||||
@ -636,7 +665,7 @@ infinity = 1/0
|
|||||||
-- | Symbolic variant of Not-A-Number. This value will inhabit both
|
-- | Symbolic variant of Not-A-Number. This value will inhabit both
|
||||||
-- 'SDouble' and 'SFloat'.
|
-- 'SDouble' and 'SFloat'.
|
||||||
sNaN :: (Floating a, SymWord a) => SBV a
|
sNaN :: (Floating a, SymWord a) => SBV a
|
||||||
sNaN = literal nan
|
sNaN = literal nan
|
||||||
|
|
||||||
-- | Symbolic variant of infinity. This value will inhabit both
|
-- | Symbolic variant of infinity. This value will inhabit both
|
||||||
-- 'SDouble' and 'SFloat'.
|
-- 'SDouble' and 'SFloat'.
|
||||||
@ -744,6 +773,7 @@ getTableIndex st at rt elts = do
|
|||||||
|
|
||||||
-- | Create a constant word from an integral
|
-- | Create a constant word from an integral
|
||||||
mkConstCW :: Integral a => Kind -> a -> CW
|
mkConstCW :: Integral a => Kind -> a -> CW
|
||||||
|
mkConstCW KBool a = normCW $ CW KBool (CWInteger (toInteger a))
|
||||||
mkConstCW k@(KBounded{}) a = normCW $ CW k (CWInteger (toInteger a))
|
mkConstCW k@(KBounded{}) a = normCW $ CW k (CWInteger (toInteger a))
|
||||||
mkConstCW KUnbounded a = normCW $ CW KUnbounded (CWInteger (toInteger a))
|
mkConstCW KUnbounded a = normCW $ CW KUnbounded (CWInteger (toInteger a))
|
||||||
mkConstCW KReal a = normCW $ CW KReal (CWAlgReal (fromInteger (toInteger a)))
|
mkConstCW KReal a = normCW $ CW KReal (CWAlgReal (fromInteger (toInteger a)))
|
||||||
@ -783,21 +813,20 @@ newtype Symbolic a = Symbolic (ReaderT State IO a)
|
|||||||
mkSymSBV :: forall a. (Random a, SymWord a) => Maybe Quantifier -> Kind -> Maybe String -> Symbolic (SBV a)
|
mkSymSBV :: forall a. (Random a, SymWord a) => Maybe Quantifier -> Kind -> Maybe String -> Symbolic (SBV a)
|
||||||
mkSymSBV = mkSymSBVWithRandom randomIO
|
mkSymSBV = mkSymSBVWithRandom randomIO
|
||||||
|
|
||||||
mkSymSBVWithRandom :: forall a. SymWord a =>
|
mkSymSBVWithRandom :: forall a. SymWord a => IO (SBV a) -> Maybe Quantifier -> Kind -> Maybe String -> Symbolic (SBV a)
|
||||||
IO (SBV a) -> Maybe Quantifier -> Kind -> Maybe String -> Symbolic (SBV a)
|
mkSymSBVWithRandom rand mbQ k mbNm = do
|
||||||
mkSymSBVWithRandom random mbQ k mbNm = do
|
|
||||||
st <- ask
|
st <- ask
|
||||||
let q = case (mbQ, runMode st) of
|
let q = case (mbQ, runMode st) of
|
||||||
(Just x, _) -> x -- user given, just take it
|
(Just x, _) -> x -- user given, just take it
|
||||||
(Nothing, Concrete{}) -> ALL -- concrete simulation, pick universal
|
(Nothing, Concrete{}) -> ALL -- concrete simulation, pick universal
|
||||||
(Nothing, Proof True) -> EX -- sat mode, pick existential
|
(Nothing, Proof (True, _)) -> EX -- sat mode, pick existential
|
||||||
(Nothing, Proof False) -> ALL -- proof mode, pick universal
|
(Nothing, Proof (False, _)) -> ALL -- proof mode, pick universal
|
||||||
(Nothing, CodeGen) -> ALL -- code generation, pick universal
|
(Nothing, CodeGen) -> ALL -- code generation, pick universal
|
||||||
case runMode st of
|
case runMode st of
|
||||||
Concrete _ | q == EX -> case mbNm of
|
Concrete _ | q == EX -> case mbNm of
|
||||||
Nothing -> error $ "Cannot quick-check in the presence of existential variables, type: " ++ showType (undefined :: SBV a)
|
Nothing -> error $ "Cannot quick-check in the presence of existential variables, type: " ++ showType (undefined :: SBV a)
|
||||||
Just nm -> error $ "Cannot quick-check in the presence of existential variable " ++ nm ++ " :: " ++ showType (undefined :: SBV a)
|
Just nm -> error $ "Cannot quick-check in the presence of existential variable " ++ nm ++ " :: " ++ showType (undefined :: SBV a)
|
||||||
Concrete _ -> do v@(SBV _ (Left cw)) <- liftIO random
|
Concrete _ -> do v@(SBV _ (Left cw)) <- liftIO rand
|
||||||
liftIO $ modifyIORef (rCInfo st) ((maybe "_" id mbNm, cw):)
|
liftIO $ modifyIORef (rCInfo st) ((maybe "_" id mbNm, cw):)
|
||||||
return v
|
return v
|
||||||
_ -> do (sw, internalName) <- liftIO $ newSW st k
|
_ -> do (sw, internalName) <- liftIO $ newSW st k
|
||||||
@ -868,7 +897,7 @@ addAxiom nm ax = do
|
|||||||
|
|
||||||
-- | Run a symbolic computation in Proof mode and return a 'Result'. The boolean
|
-- | Run a symbolic computation in Proof mode and return a 'Result'. The boolean
|
||||||
-- argument indicates if this is a sat instance or not.
|
-- argument indicates if this is a sat instance or not.
|
||||||
runSymbolic :: Bool -> Symbolic a -> IO Result
|
runSymbolic :: (Bool, Maybe SMTConfig) -> Symbolic a -> IO Result
|
||||||
runSymbolic b c = snd `fmap` runSymbolic' (Proof b) c
|
runSymbolic b c = snd `fmap` runSymbolic' (Proof b) c
|
||||||
|
|
||||||
-- | Run a symbolic computation, and return a extra value paired up with the 'Result'
|
-- | Run a symbolic computation, and return a extra value paired up with the 'Result'
|
||||||
@ -894,6 +923,7 @@ runSymbolic' currentRunMode (Symbolic c) = do
|
|||||||
Concrete g -> newIORef g
|
Concrete g -> newIORef g
|
||||||
_ -> newStdGen >>= newIORef
|
_ -> newStdGen >>= newIORef
|
||||||
let st = State { runMode = currentRunMode
|
let st = State { runMode = currentRunMode
|
||||||
|
, pathCond = SBV KBool (Left trueCW)
|
||||||
, rStdGen = rGen
|
, rStdGen = rGen
|
||||||
, rCInfo = cInfo
|
, rCInfo = cInfo
|
||||||
, rctr = ctr
|
, rctr = ctr
|
||||||
@ -912,34 +942,37 @@ runSymbolic' currentRunMode (Symbolic c) = do
|
|||||||
, rAICache = aiCache
|
, rAICache = aiCache
|
||||||
, rConstraints = cstrs
|
, rConstraints = cstrs
|
||||||
}
|
}
|
||||||
_ <- newConst st (mkConstCW (KBounded False 1) (0::Integer)) -- s(-2) == falseSW
|
_ <- newConst st falseCW -- s(-2) == falseSW
|
||||||
_ <- newConst st (mkConstCW (KBounded False 1) (1::Integer)) -- s(-1) == trueSW
|
_ <- newConst st trueCW -- s(-1) == trueSW
|
||||||
r <- runReaderT c st
|
r <- runReaderT c st
|
||||||
res <- readResult st
|
res <- extractSymbolicSimulationState st
|
||||||
return $ (r, res)
|
return (r, res)
|
||||||
|
|
||||||
readResult :: State -> IO Result
|
-- | Grab the program from a running symbolic simulation state. This is useful for internal purposes, for
|
||||||
readResult st = do
|
-- instance when implementing 'sBranch'.
|
||||||
rpgm <- readIORef (spgm st)
|
extractSymbolicSimulationState :: State -> IO Result
|
||||||
inpsO <- reverse `fmap` readIORef (rinps st)
|
extractSymbolicSimulationState st@State{ spgm=pgm, rinps=inps, routs=outs, rtblMap=tables, rArrayMap=arrays, rUIMap=uis, raxioms=axioms
|
||||||
outsO <- reverse `fmap` readIORef (routs st)
|
, rUsedKinds=usedKinds, rCgMap=cgs, rCInfo=cInfo, rConstraints = cstrs} = do
|
||||||
let swap (a, b) = (b, a)
|
SBVPgm rpgm <- readIORef pgm
|
||||||
cmp (a, _) (b, _) = a `compare` b
|
inpsO <- reverse `fmap` readIORef inps
|
||||||
cnsts <- (sortBy cmp . map swap . Map.toList) `fmap` readIORef (rconstMap st)
|
outsO <- reverse `fmap` readIORef outs
|
||||||
tbls <- (sortBy (\((x, _, _), _) ((y, _, _), _) -> x `compare` y) . map swap . Map.toList) `fmap` readIORef (rtblMap st)
|
let swap (a, b) = (b, a)
|
||||||
arrs <- IMap.toAscList `fmap` readIORef (rArrayMap st)
|
cmp (a, _) (b, _) = a `compare` b
|
||||||
unint <- Map.toList `fmap` readIORef (rUIMap st)
|
cnsts <- (sortBy cmp . map swap . Map.toList) `fmap` readIORef (rconstMap st)
|
||||||
axs <- reverse `fmap` readIORef (raxioms st)
|
tbls <- (sortBy (\((x, _, _), _) ((y, _, _), _) -> x `compare` y) . map swap . Map.toList) `fmap` readIORef tables
|
||||||
knds <- readIORef (rUsedKinds st)
|
arrs <- IMap.toAscList `fmap` readIORef arrays
|
||||||
cgMap <- Map.toList `fmap` readIORef (rCgMap st)
|
unint <- Map.toList `fmap` readIORef uis
|
||||||
traceVals <- reverse `fmap` readIORef (rCInfo st)
|
axs <- reverse `fmap` readIORef axioms
|
||||||
extraCstrs <- reverse `fmap` readIORef (rConstraints st)
|
knds <- readIORef usedKinds
|
||||||
return $ Result knds traceVals cgMap inpsO cnsts tbls arrs unint axs rpgm extraCstrs outsO
|
cgMap <- Map.toList `fmap` readIORef cgs
|
||||||
|
traceVals <- reverse `fmap` readIORef cInfo
|
||||||
|
extraCstrs <- reverse `fmap` readIORef cstrs
|
||||||
|
return $ Result knds traceVals cgMap inpsO cnsts tbls arrs unint axs (SBVPgm rpgm) extraCstrs outsO
|
||||||
|
|
||||||
getResult :: Symbolic Result
|
getResult :: Symbolic Result
|
||||||
getResult = do
|
getResult = do
|
||||||
st <- ask
|
st <- ask
|
||||||
liftIO $ readResult st
|
liftIO $ extractSymbolicSimulationState st
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- * Symbolic Words
|
-- * Symbolic Words
|
||||||
@ -1024,11 +1057,11 @@ class (HasKind a, Ord a) => SymWord a where
|
|||||||
let k = KUninterpreted sortName
|
let k = KUninterpreted sortName
|
||||||
liftIO $ registerKind st k
|
liftIO $ registerKind st k
|
||||||
let q = case (mbQ, runMode st) of
|
let q = case (mbQ, runMode st) of
|
||||||
(Just x, _) -> x
|
(Just x, _) -> x
|
||||||
(Nothing, Proof True) -> EX
|
(Nothing, Proof (True, _)) -> EX
|
||||||
(Nothing, Proof False) -> ALL
|
(Nothing, Proof (False, _)) -> ALL
|
||||||
(Nothing, Concrete{}) -> error $ "SBV: Uninterpreted sort " ++ sortName ++ " can not be used in concrete simulation mode."
|
(Nothing, Concrete{}) -> error $ "SBV: Uninterpreted sort " ++ sortName ++ " can not be used in concrete simulation mode."
|
||||||
(Nothing, CodeGen) -> error $ "SBV: Uninterpreted sort " ++ sortName ++ " can not be used in code-generation mode."
|
(Nothing, CodeGen) -> error $ "SBV: Uninterpreted sort " ++ sortName ++ " can not be used in code-generation mode."
|
||||||
ctr <- liftIO $ incCtr st
|
ctr <- liftIO $ incCtr st
|
||||||
let sw = SW k (NodeId ctr)
|
let sw = SW k (NodeId ctr)
|
||||||
nm = maybe ('s':show ctr) id mbNm
|
nm = maybe ('s':show ctr) id mbNm
|
||||||
@ -1264,6 +1297,55 @@ instance NFData a => NFData (SBV a) where
|
|||||||
rnf (SBV x y) = rnf x `seq` rnf y `seq` ()
|
rnf (SBV x y) = rnf x `seq` rnf y `seq` ()
|
||||||
instance NFData SBVPgm
|
instance NFData SBVPgm
|
||||||
|
|
||||||
|
instance NFData SMTResult where
|
||||||
|
rnf (Unsatisfiable _) = ()
|
||||||
|
rnf (Satisfiable _ xs) = rnf xs `seq` ()
|
||||||
|
rnf (Unknown _ xs) = rnf xs `seq` ()
|
||||||
|
rnf (ProofError _ xs) = rnf xs `seq` ()
|
||||||
|
rnf (TimeOut _) = ()
|
||||||
|
|
||||||
|
instance NFData SMTModel where
|
||||||
|
rnf (SMTModel assocs unints uarrs) = rnf assocs `seq` rnf unints `seq` rnf uarrs `seq` ()
|
||||||
|
|
||||||
|
-- | SMT-Lib logics. If left unspecified SBV will pick the logic based on what it determines is needed. However, the
|
||||||
|
-- user can override this choice using the 'useLogic' parameter to the configuration. This is especially handy if
|
||||||
|
-- one is experimenting with custom logics that might be supported on new solvers.
|
||||||
|
data SMTLibLogic
|
||||||
|
= AUFLIA -- ^ Formulas over the theory of linear integer arithmetic and arrays extended with free sort and function symbols but restricted to arrays with integer indices and values
|
||||||
|
| AUFLIRA -- ^ Linear formulas with free sort and function symbols over one- and two-dimentional arrays of integer index and real value
|
||||||
|
| AUFNIRA -- ^ Formulas with free function and predicate symbols over a theory of arrays of arrays of integer index and real value
|
||||||
|
| LRA -- ^ Linear formulas in linear real arithmetic
|
||||||
|
| UFLRA -- ^ Linear real arithmetic with uninterpreted sort and function symbols.
|
||||||
|
| UFNIA -- ^ Non-linear integer arithmetic with uninterpreted sort and function symbols.
|
||||||
|
| QF_ABV -- ^ Quantifier-free formulas over the theory of bitvectors and bitvector arrays
|
||||||
|
| QF_AUFBV -- ^ Quantifier-free formulas over the theory of bitvectors and bitvector arrays extended with free sort and function symbols
|
||||||
|
| QF_AUFLIA -- ^ Quantifier-free linear formulas over the theory of integer arrays extended with free sort and function symbols
|
||||||
|
| QF_AX -- ^ Quantifier-free formulas over the theory of arrays with extensionality
|
||||||
|
| QF_BV -- ^ Quantifier-free formulas over the theory of fixed-size bitvectors
|
||||||
|
| QF_IDL -- ^ Difference Logic over the integers. Boolean combinations of inequations of the form x - y < b where x and y are integer variables and b is an integer constant
|
||||||
|
| QF_LIA -- ^ Unquantified linear integer arithmetic. In essence, Boolean combinations of inequations between linear polynomials over integer variables
|
||||||
|
| QF_LRA -- ^ Unquantified linear real arithmetic. In essence, Boolean combinations of inequations between linear polynomials over real variables.
|
||||||
|
| QF_NIA -- ^ Quantifier-free integer arithmetic.
|
||||||
|
| QF_NRA -- ^ Quantifier-free real arithmetic.
|
||||||
|
| QF_RDL -- ^ Difference Logic over the reals. In essence, Boolean combinations of inequations of the form x - y < b where x and y are real variables and b is a rational constant.
|
||||||
|
| QF_UF -- ^ Unquantified formulas built over a signature of uninterpreted (i.e., free) sort and function symbols.
|
||||||
|
| QF_UFBV -- ^ Unquantified formulas over bitvectors with uninterpreted sort function and symbols.
|
||||||
|
| QF_UFIDL -- ^ Difference Logic over the integers (in essence) but with uninterpreted sort and function symbols.
|
||||||
|
| QF_UFLIA -- ^ Unquantified linear integer arithmetic with uninterpreted sort and function symbols.
|
||||||
|
| QF_UFLRA -- ^ Unquantified linear real arithmetic with uninterpreted sort and function symbols.
|
||||||
|
| QF_UFNRA -- ^ Unquantified non-linear real arithmetic with uninterpreted sort and function symbols.
|
||||||
|
| QF_FPABV -- ^ Quantifier-free formulas over the theory of floating point numbers, arrays, and bit-vectors
|
||||||
|
| QF_FPA -- ^ Quantifier-free formulas over the theory of floating point numbers
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | Chosen logic for the solver
|
||||||
|
data Logic = PredefinedLogic SMTLibLogic -- ^ Use one of the logics as defined by the standard
|
||||||
|
| CustomLogic String -- ^ Use this name for the logic
|
||||||
|
|
||||||
|
instance Show Logic where
|
||||||
|
show (PredefinedLogic l) = show l
|
||||||
|
show (CustomLogic s) = s
|
||||||
|
|
||||||
-- | Translation tricks needed for specific capabilities afforded by each solver
|
-- | Translation tricks needed for specific capabilities afforded by each solver
|
||||||
data SolverCapabilities = SolverCapabilities {
|
data SolverCapabilities = SolverCapabilities {
|
||||||
capSolverName :: String -- ^ Name of the solver
|
capSolverName :: String -- ^ Name of the solver
|
||||||
@ -1277,3 +1359,84 @@ data SolverCapabilities = SolverCapabilities {
|
|||||||
, supportsFloats :: Bool -- ^ Does the solver support single-precision floating point numbers?
|
, supportsFloats :: Bool -- ^ Does the solver support single-precision floating point numbers?
|
||||||
, supportsDoubles :: Bool -- ^ Does the solver support double-precision floating point numbers?
|
, supportsDoubles :: Bool -- ^ Does the solver support double-precision floating point numbers?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Solver configuration. See also 'z3', 'yices', 'cvc4', 'boolector', 'mathSAT', etc. which are instantiations of this type for those solvers, with
|
||||||
|
-- reasonable defaults. In particular, custom configuration can be created by varying those values. (Such as @z3{verbose=True}@.)
|
||||||
|
--
|
||||||
|
-- Most fields are self explanatory. The notion of precision for printing algebraic reals stems from the fact that such values does
|
||||||
|
-- not necessarily have finite decimal representations, and hence we have to stop printing at some depth. It is important to
|
||||||
|
-- emphasize that such values always have infinite precision internally. The issue is merely with how we print such an infinite
|
||||||
|
-- precision value on the screen. The field 'printRealPrec' controls the printing precision, by specifying the number of digits after
|
||||||
|
-- the decimal point. The default value is 16, but it can be set to any positive integer.
|
||||||
|
--
|
||||||
|
-- When printing, SBV will add the suffix @...@ at the and of a real-value, if the given bound is not sufficient to represent the real-value
|
||||||
|
-- exactly. Otherwise, the number will be written out in standard decimal notation. Note that SBV will always print the whole value if it
|
||||||
|
-- is precise (i.e., if it fits in a finite number of digits), regardless of the precision limit. The limit only applies if the representation
|
||||||
|
-- of the real value is not finite, i.e., if it is not rational.
|
||||||
|
data SMTConfig = SMTConfig {
|
||||||
|
verbose :: Bool -- ^ Debug mode
|
||||||
|
, timing :: Bool -- ^ Print timing information on how long different phases took (construction, solving, etc.)
|
||||||
|
, sBranchTimeOut :: Maybe Int -- ^ How much time to give to the solver for each call of 'sBranch' check. (In seconds. Default: No limit.)
|
||||||
|
, timeOut :: Maybe Int -- ^ How much time to give to the solver. (In seconds. Default: No limit.)
|
||||||
|
, printBase :: Int -- ^ Print integral literals in this base (2, 8, and 10, and 16 are supported.)
|
||||||
|
, printRealPrec :: Int -- ^ Print algebraic real values with this precision. (SReal, default: 16)
|
||||||
|
, solverTweaks :: [String] -- ^ Additional lines of script to give to the solver (user specified)
|
||||||
|
, satCmd :: String -- ^ Usually "(check-sat)". However, users might tweak it based on solver characteristics.
|
||||||
|
, smtFile :: Maybe FilePath -- ^ If Just, the generated SMT script will be put in this file (for debugging purposes mostly)
|
||||||
|
, useSMTLib2 :: Bool -- ^ If True, we'll treat the solver as using SMTLib2 input format. Otherwise, SMTLib1
|
||||||
|
, solver :: SMTSolver -- ^ The actual SMT solver.
|
||||||
|
, roundingMode :: RoundingMode -- ^ Rounding mode to use for floating-point conversions
|
||||||
|
, useLogic :: Maybe Logic -- ^ If Nothing, pick automatically. Otherwise, either use the given one, or use the custom string.
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Show SMTConfig where
|
||||||
|
show = show . solver
|
||||||
|
|
||||||
|
-- | A model, as returned by a solver
|
||||||
|
data SMTModel = SMTModel {
|
||||||
|
modelAssocs :: [(String, CW)] -- ^ Mapping of symbolic values to constants.
|
||||||
|
, modelArrays :: [(String, [String])] -- ^ Arrays, very crude; only works with Yices.
|
||||||
|
, modelUninterps :: [(String, [String])] -- ^ Uninterpreted funcs; very crude; only works with Yices.
|
||||||
|
}
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | The result of an SMT solver call. Each constructor is tagged with
|
||||||
|
-- the 'SMTConfig' that created it so that further tools can inspect it
|
||||||
|
-- and build layers of results, if needed. For ordinary uses of the library,
|
||||||
|
-- this type should not be needed, instead use the accessor functions on
|
||||||
|
-- it. (Custom Show instances and model extractors.)
|
||||||
|
data SMTResult = Unsatisfiable SMTConfig -- ^ Unsatisfiable
|
||||||
|
| Satisfiable SMTConfig SMTModel -- ^ Satisfiable with model
|
||||||
|
| Unknown SMTConfig SMTModel -- ^ Prover returned unknown, with a potential (possibly bogus) model
|
||||||
|
| ProofError SMTConfig [String] -- ^ Prover errored out
|
||||||
|
| TimeOut SMTConfig -- ^ Computation timed out (see the 'timeout' combinator)
|
||||||
|
|
||||||
|
-- | A script, to be passed to the solver.
|
||||||
|
data SMTScript = SMTScript {
|
||||||
|
scriptBody :: String -- ^ Initial feed
|
||||||
|
, scriptModel :: Maybe String -- ^ Optional continuation script, if the result is sat
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | An SMT engine
|
||||||
|
type SMTEngine = SMTConfig -> Bool -> [(Quantifier, NamedSymVar)] -> [(String, UnintKind)] -> [Either SW (SW, [SW])] -> String -> IO SMTResult
|
||||||
|
|
||||||
|
-- | Solvers that SBV is aware of
|
||||||
|
data Solver = Z3
|
||||||
|
| Yices
|
||||||
|
| Boolector
|
||||||
|
| CVC4
|
||||||
|
| MathSAT
|
||||||
|
deriving (Show, Enum, Bounded)
|
||||||
|
|
||||||
|
-- | An SMT solver
|
||||||
|
data SMTSolver = SMTSolver {
|
||||||
|
name :: Solver -- ^ The solver in use
|
||||||
|
, executable :: String -- ^ The path to its executable
|
||||||
|
, options :: [String] -- ^ Options to provide to the solver
|
||||||
|
, engine :: SMTEngine -- ^ The solver engine, responsible for interpreting solver output
|
||||||
|
, xformExitCode :: ExitCode -> ExitCode -- ^ Should we re-interpret exit codes. Most solvers behave rationally, i.e., id will do. Some (like CVC4) don't.
|
||||||
|
, capabilities :: SolverCapabilities -- ^ Various capabilities of the solver
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Show SMTSolver where
|
||||||
|
show = show . name
|
||||||
|
@ -52,6 +52,23 @@ import Data.SBV.BitVectors.AlgReals
|
|||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.Utils.Boolean
|
import Data.SBV.Utils.Boolean
|
||||||
|
|
||||||
|
import Data.SBV.Provers.Prover (isSBranchFeasibleInState)
|
||||||
|
|
||||||
|
-- The following two imports are only needed because of the doctest expressions we have. Sigh..
|
||||||
|
-- It might be a good idea to reorg some of the content to avoid this.
|
||||||
|
import Data.SBV.Provers.Prover (isVacuous, prove)
|
||||||
|
import Data.SBV.SMT.SMT (ThmResult)
|
||||||
|
|
||||||
|
-- | Newer versions of GHC (Starting with 7.8 I think), distinguishes between FiniteBits and Bits classes.
|
||||||
|
-- We should really use FiniteBitSize for SBV which would make things better. In the interim, just work
|
||||||
|
-- around pesky warnings..
|
||||||
|
ghcBitSize :: Bits a => a -> Int
|
||||||
|
#if __GLASGOW_HASKELL__ >= 708
|
||||||
|
ghcBitSize x = maybe (error "SBV.ghcBitSize: Unexpected non-finite usage!") id (bitSizeMaybe x)
|
||||||
|
#else
|
||||||
|
ghcBitSize = bitSize
|
||||||
|
#endif
|
||||||
|
|
||||||
noUnint :: String -> a
|
noUnint :: String -> a
|
||||||
noUnint x = error $ "Unexpected operation called on uninterpreted value: " ++ show x
|
noUnint x = error $ "Unexpected operation called on uninterpreted value: " ++ show x
|
||||||
|
|
||||||
@ -76,20 +93,20 @@ liftSym2 opS _ _ _ _ _ a@(SBV k _) b
|
|||||||
|
|
||||||
liftSym2B :: (State -> Kind -> SW -> SW -> IO SW) -> (CW -> CW -> Bool) -> (AlgReal -> AlgReal -> Bool) -> (Integer -> Integer -> Bool) -> (Float -> Float -> Bool) -> (Double -> Double -> Bool) -> SBV b -> SBV b -> SBool
|
liftSym2B :: (State -> Kind -> SW -> SW -> IO SW) -> (CW -> CW -> Bool) -> (AlgReal -> AlgReal -> Bool) -> (Integer -> Integer -> Bool) -> (Float -> Float -> Bool) -> (Double -> Double -> Bool) -> SBV b -> SBV b -> SBool
|
||||||
liftSym2B _ okCW opCR opCI opCF opCD (SBV _ (Left a)) (SBV _ (Left b)) | okCW a b = literal (liftCW2 opCR opCI opCF opCD noUnint2 a b)
|
liftSym2B _ okCW opCR opCI opCF opCD (SBV _ (Left a)) (SBV _ (Left b)) | okCW a b = literal (liftCW2 opCR opCI opCF opCD noUnint2 a b)
|
||||||
liftSym2B opS _ _ _ _ _ a b = SBV (KBounded False 1) $ Right $ liftSW2 opS (KBounded False 1) a b
|
liftSym2B opS _ _ _ _ _ a b = SBV KBool $ Right $ liftSW2 opS KBool a b
|
||||||
|
|
||||||
liftSym1Bool :: (State -> Kind -> SW -> IO SW) -> (Bool -> Bool) -> SBool -> SBool
|
liftSym1Bool :: (State -> Kind -> SW -> IO SW) -> (Bool -> Bool) -> SBool -> SBool
|
||||||
liftSym1Bool _ opC (SBV _ (Left a)) = literal $ opC $ cwToBool a
|
liftSym1Bool _ opC (SBV _ (Left a)) = literal $ opC $ cwToBool a
|
||||||
liftSym1Bool opS _ a = SBV (KBounded False 1) $ Right $ cache c
|
liftSym1Bool opS _ a = SBV KBool $ Right $ cache c
|
||||||
where c st = do sw <- sbvToSW st a
|
where c st = do sw <- sbvToSW st a
|
||||||
opS st (KBounded False 1) sw
|
opS st KBool sw
|
||||||
|
|
||||||
liftSym2Bool :: (State -> Kind -> SW -> SW -> IO SW) -> (Bool -> Bool -> Bool) -> SBool -> SBool -> SBool
|
liftSym2Bool :: (State -> Kind -> SW -> SW -> IO SW) -> (Bool -> Bool -> Bool) -> SBool -> SBool -> SBool
|
||||||
liftSym2Bool _ opC (SBV _ (Left a)) (SBV _ (Left b)) = literal (cwToBool a `opC` cwToBool b)
|
liftSym2Bool _ opC (SBV _ (Left a)) (SBV _ (Left b)) = literal (cwToBool a `opC` cwToBool b)
|
||||||
liftSym2Bool opS _ a b = SBV (KBounded False 1) $ Right $ cache c
|
liftSym2Bool opS _ a b = SBV KBool $ Right $ cache c
|
||||||
where c st = do sw1 <- sbvToSW st a
|
where c st = do sw1 <- sbvToSW st a
|
||||||
sw2 <- sbvToSW st b
|
sw2 <- sbvToSW st b
|
||||||
opS st (KBounded False 1) sw1 sw2
|
opS st KBool sw1 sw2
|
||||||
|
|
||||||
mkSymOpSC :: (SW -> SW -> Maybe SW) -> Op -> State -> Kind -> SW -> SW -> IO SW
|
mkSymOpSC :: (SW -> SW -> Maybe SW) -> Op -> State -> Kind -> SW -> SW -> IO SW
|
||||||
mkSymOpSC shortCut op st k a b = maybe (newExpr st k (SBVApp op [a, b])) return (shortCut a b)
|
mkSymOpSC shortCut op st k a b = maybe (newExpr st k (SBVApp op [a, b])) return (shortCut a b)
|
||||||
@ -128,8 +145,8 @@ genMkSymVar k mbq Nothing = genVar_ mbq k
|
|||||||
genMkSymVar k mbq (Just s) = genVar mbq k s
|
genMkSymVar k mbq (Just s) = genVar mbq k s
|
||||||
|
|
||||||
instance SymWord Bool where
|
instance SymWord Bool where
|
||||||
mkSymWord = genMkSymVar (KBounded False 1)
|
mkSymWord = genMkSymVar KBool
|
||||||
literal x = genLiteral (KBounded False 1) (if x then (1::Integer) else 0)
|
literal x = genLiteral KBool (if x then (1::Integer) else 0)
|
||||||
fromCW = cwToBool
|
fromCW = cwToBool
|
||||||
mbMaxBound = Just maxBound
|
mbMaxBound = Just maxBound
|
||||||
mbMinBound = Just minBound
|
mbMinBound = Just minBound
|
||||||
@ -755,10 +772,12 @@ instance ({-Num a,-} Bits a, SymWord a) => Bits (SBV a) where
|
|||||||
| True = liftSym2 (mkSymOp XOr) (const (const True)) (noReal "xor") xor (noFloat "xor") (noDouble "xor") x y
|
| True = liftSym2 (mkSymOp XOr) (const (const True)) (noReal "xor") xor (noFloat "xor") (noDouble "xor") x y
|
||||||
complement = liftSym1 (mkSymOp1 Not) (noRealUnary "complement") complement (noFloatUnary "complement") (noDoubleUnary "complement")
|
complement = liftSym1 (mkSymOp1 Not) (noRealUnary "complement") complement (noFloatUnary "complement") (noDoubleUnary "complement")
|
||||||
bitSize x = case kindOf x of KBounded _ w -> w
|
bitSize x = case kindOf x of KBounded _ w -> w
|
||||||
|
#if __GLASGOW_HASKELL__ >= 708
|
||||||
|
bitSizeMaybe _ = Just $ intSizeOf (undefined :: a)
|
||||||
|
#endif
|
||||||
isSigned x = case kindOf x of KBounded s _ -> s
|
isSigned x = case kindOf x of KBounded s _ -> s
|
||||||
bit i = 1 `shiftL` i
|
bit i = 1 `shiftL` i
|
||||||
setBit x i = x .|. sbvFromInteger (kindOf x) (bit i)
|
setBit x i = x .|. sbvFromInteger (kindOf x) (bit i)
|
||||||
--BH TODO: implement setBit, clearBit, etc. separately.
|
|
||||||
shiftL x y
|
shiftL x y
|
||||||
| y < 0 = shiftR x (-y)
|
| y < 0 = shiftR x (-y)
|
||||||
| y == 0 = x
|
| y == 0 = x
|
||||||
@ -770,12 +789,12 @@ instance ({-Num a,-} Bits a, SymWord a) => Bits (SBV a) where
|
|||||||
rotateL x y
|
rotateL x y
|
||||||
| y < 0 = rotateR x (-y)
|
| y < 0 = rotateR x (-y)
|
||||||
| y == 0 = x
|
| y == 0 = x
|
||||||
| isBounded x = let sz = bitSize x in liftSym1 (mkSymOp1 (Rol (y `mod` sz))) (noRealUnary "rotateL") (rot True sz y) (noFloatUnary "rotateL") (noDoubleUnary "rotateL") x
|
| isBounded x = let sz = ghcBitSize x in liftSym1 (mkSymOp1 (Rol (y `mod` sz))) (noRealUnary "rotateL") (rot True sz y) (noFloatUnary "rotateL") (noDoubleUnary "rotateL") x
|
||||||
| True = shiftL x y -- for unbounded Integers, rotateL is the same as shiftL in Haskell
|
| True = shiftL x y -- for unbounded Integers, rotateL is the same as shiftL in Haskell
|
||||||
rotateR x y
|
rotateR x y
|
||||||
| y < 0 = rotateL x (-y)
|
| y < 0 = rotateL x (-y)
|
||||||
| y == 0 = x
|
| y == 0 = x
|
||||||
| isBounded x = let sz = bitSize x in liftSym1 (mkSymOp1 (Ror (y `mod` sz))) (noRealUnary "rotateR") (rot False sz y) (noFloatUnary "rotateR") (noDoubleUnary "rotateR") x
|
| isBounded x = let sz = ghcBitSize x in liftSym1 (mkSymOp1 (Ror (y `mod` sz))) (noRealUnary "rotateR") (rot False sz y) (noFloatUnary "rotateR") (noDoubleUnary "rotateR") x
|
||||||
| True = shiftR x y -- for unbounded integers, rotateR is the same as shiftR in Haskell
|
| True = shiftR x y -- for unbounded integers, rotateR is the same as shiftR in Haskell
|
||||||
-- NB. testBit is *not* implementable on non-concrete symbolic words
|
-- NB. testBit is *not* implementable on non-concrete symbolic words
|
||||||
x `testBit` i
|
x `testBit` i
|
||||||
@ -835,7 +854,7 @@ setBitTo x i b = ite b (setBit x i) (clearBit x i)
|
|||||||
sbvShiftLeft :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
sbvShiftLeft :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
||||||
sbvShiftLeft x i
|
sbvShiftLeft x i
|
||||||
| isSigned i = error "sbvShiftLeft: shift amount should be unsigned"
|
| isSigned i = error "sbvShiftLeft: shift amount should be unsigned"
|
||||||
| True = select [x `shiftL` k | k <- [0 .. bitSize x - 1]] z i
|
| True = select [x `shiftL` k | k <- [0 .. ghcBitSize x - 1]] z i
|
||||||
where z = sbvFromInteger (kindOf x) 0
|
where z = sbvFromInteger (kindOf x) 0
|
||||||
|
|
||||||
-- | Generalization of 'shiftR', when the shift-amount is symbolic. Since Haskell's
|
-- | Generalization of 'shiftR', when the shift-amount is symbolic. Since Haskell's
|
||||||
@ -848,7 +867,7 @@ sbvShiftLeft x i
|
|||||||
sbvShiftRight :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
sbvShiftRight :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
||||||
sbvShiftRight x i
|
sbvShiftRight x i
|
||||||
| isSigned i = error "sbvShiftRight: shift amount should be unsigned"
|
| isSigned i = error "sbvShiftRight: shift amount should be unsigned"
|
||||||
| True = select [x `shiftR` k | k <- [0 .. bitSize x - 1]] z i
|
| True = select [x `shiftR` k | k <- [0 .. ghcBitSize x - 1]] z i
|
||||||
where z = sbvFromInteger (kindOf x) 0
|
where z = sbvFromInteger (kindOf x) 0
|
||||||
|
|
||||||
-- | Arithmetic shift-right with a symbolic unsigned shift amount. This is equivalent
|
-- | Arithmetic shift-right with a symbolic unsigned shift amount. This is equivalent
|
||||||
@ -870,7 +889,7 @@ sbvSignedShiftArithRight x i
|
|||||||
sbvRotateLeft :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
sbvRotateLeft :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
||||||
sbvRotateLeft x i
|
sbvRotateLeft x i
|
||||||
| isSigned i = error "sbvRotateLeft: shift amount should be unsigned"
|
| isSigned i = error "sbvRotateLeft: shift amount should be unsigned"
|
||||||
| True = select [x `rotateL` k | k <- [0 .. bitSize x - 1]] z i
|
| True = select [x `rotateL` k | k <- [0 .. ghcBitSize x - 1]] z i
|
||||||
where z = sbvFromInteger (kindOf x) 0
|
where z = sbvFromInteger (kindOf x) 0
|
||||||
|
|
||||||
-- | Generalization of 'rotateR', when the shift-amount is symbolic. Since Haskell's
|
-- | Generalization of 'rotateR', when the shift-amount is symbolic. Since Haskell's
|
||||||
@ -879,7 +898,7 @@ sbvRotateLeft x i
|
|||||||
sbvRotateRight :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
sbvRotateRight :: (SIntegral a, SIntegral b) => SBV a -> SBV b -> SBV a
|
||||||
sbvRotateRight x i
|
sbvRotateRight x i
|
||||||
| isSigned i = error "sbvRotateRight: shift amount should be unsigned"
|
| isSigned i = error "sbvRotateRight: shift amount should be unsigned"
|
||||||
| True = select [x `rotateR` k | k <- [0 .. bitSize x - 1]] z i
|
| True = select [x `rotateR` k | k <- [0 .. ghcBitSize x - 1]] z i
|
||||||
where z = sbvFromInteger (kindOf x) 0
|
where z = sbvFromInteger (kindOf x) 0
|
||||||
|
|
||||||
-- | Full adder. Returns the carry-out from the addition.
|
-- | Full adder. Returns the carry-out from the addition.
|
||||||
@ -902,14 +921,14 @@ fullAdder a b
|
|||||||
fullMultiplier :: SIntegral a => SBV a -> SBV a -> (SBV a, SBV a)
|
fullMultiplier :: SIntegral a => SBV a -> SBV a -> (SBV a, SBV a)
|
||||||
fullMultiplier a b
|
fullMultiplier a b
|
||||||
| isSigned a = error "fullMultiplier: only works on unsigned numbers"
|
| isSigned a = error "fullMultiplier: only works on unsigned numbers"
|
||||||
| True = (go (bitSize a) 0 a, a*b)
|
| True = (go (ghcBitSize a) 0 a, a*b)
|
||||||
where go 0 p _ = p
|
where go 0 p _ = p
|
||||||
go n p x = let (c, p') = ite (lsb x) (fullAdder p b) (false, p)
|
go n p x = let (c, p') = ite (lsb x) (fullAdder p b) (false, p)
|
||||||
(o, p'') = shiftIn c p'
|
(o, p'') = shiftIn c p'
|
||||||
(_, x') = shiftIn o x
|
(_, x') = shiftIn o x
|
||||||
in go (n-1) p'' x'
|
in go (n-1) p'' x'
|
||||||
shiftIn k v = (lsb v, mask .|. (v `shiftR` 1))
|
shiftIn k v = (lsb v, mask .|. (v `shiftR` 1))
|
||||||
where mask = ite k (bit (bitSize v - 1)) 0
|
where mask = ite k (bit (ghcBitSize v - 1)) 0
|
||||||
|
|
||||||
-- | Little-endian blasting of a word into its bits. Also see the 'FromBits' class.
|
-- | Little-endian blasting of a word into its bits. Also see the 'FromBits' class.
|
||||||
blastLE :: (Num a, Bits a, SymWord a) => SBV a -> [SBool]
|
blastLE :: (Num a, Bits a, SymWord a) => SBV a -> [SBool]
|
||||||
@ -1071,12 +1090,11 @@ instance SDivisible Integer where
|
|||||||
instance SDivisible CW where
|
instance SDivisible CW where
|
||||||
sQuotRem a b
|
sQuotRem a b
|
||||||
| CWInteger x <- cwVal a, CWInteger y <- cwVal b
|
| CWInteger x <- cwVal a, CWInteger y <- cwVal b
|
||||||
= let (r1, r2) = sQuotRem x y in (a { cwVal = CWInteger r1 }, b { cwVal = CWInteger r2 })
|
= let (r1, r2) = sQuotRem x y in (normCW a{ cwVal = CWInteger r1 }, normCW b{ cwVal = CWInteger r2 })
|
||||||
--BH FIXME: -2^(n-1) `div` (-1) can overflow.
|
|
||||||
sQuotRem a b = error $ "SBV.sQuotRem: impossible, unexpected args received: " ++ show (a, b)
|
sQuotRem a b = error $ "SBV.sQuotRem: impossible, unexpected args received: " ++ show (a, b)
|
||||||
sDivMod a b
|
sDivMod a b
|
||||||
| CWInteger x <- cwVal a, CWInteger y <- cwVal b
|
| CWInteger x <- cwVal a, CWInteger y <- cwVal b
|
||||||
= let (r1, r2) = sDivMod x y in (a { cwVal = CWInteger r1 }, b { cwVal = CWInteger r2 })
|
= let (r1, r2) = sDivMod x y in (normCW a { cwVal = CWInteger r1 }, normCW b { cwVal = CWInteger r2 })
|
||||||
sDivMod a b = error $ "SBV.sDivMod: impossible, unexpected args received: " ++ show (a, b)
|
sDivMod a b = error $ "SBV.sDivMod: impossible, unexpected args received: " ++ show (a, b)
|
||||||
|
|
||||||
instance SDivisible SWord64 where
|
instance SDivisible SWord64 where
|
||||||
@ -1177,6 +1195,24 @@ class Mergeable a where
|
|||||||
-- The idea is that use symbolicMerge if you know the condition is symbolic,
|
-- The idea is that use symbolicMerge if you know the condition is symbolic,
|
||||||
-- otherwise use ite, if there's a chance it might be concrete.
|
-- otherwise use ite, if there's a chance it might be concrete.
|
||||||
ite :: SBool -> a -> a -> a
|
ite :: SBool -> a -> a -> a
|
||||||
|
-- | Branch on a condition, much like 'ite'. The exception is that SBV will
|
||||||
|
-- check to make sure if the test condition is feasible by making an external
|
||||||
|
-- call to the SMT solver. Note that this can be expensive, thus we shall use
|
||||||
|
-- a time-out value ('sBranchTimeOut'). There might be zero, one, or two such
|
||||||
|
-- external calls per 'sBranch' call:
|
||||||
|
--
|
||||||
|
-- - If condition is statically known to be True/False: 0 calls
|
||||||
|
-- - In this case, we simply constant fold..
|
||||||
|
--
|
||||||
|
-- - If condition is determined to be unsatisfiable : 1 call
|
||||||
|
-- - In this case, we know then-branch is infeasible, so just take the else-branch
|
||||||
|
--
|
||||||
|
-- - If condition is determined to be satisfable : 2 calls
|
||||||
|
-- - In this case, we know then-branch is feasible, but we still have to check if the else-branch is
|
||||||
|
--
|
||||||
|
-- In summary, 'sBranch' calls can be expensive, but they can help with the so-called symbolic-termination
|
||||||
|
-- problem. See "Data.SBV.Examples.Misc.SBranch" for an example.
|
||||||
|
sBranch :: SBool -> a -> a -> a
|
||||||
-- | Total indexing operation. @select xs default index@ is intuitively
|
-- | Total indexing operation. @select xs default index@ is intuitively
|
||||||
-- the same as @xs !! index@, except it evaluates to @default@ if @index@
|
-- the same as @xs !! index@, except it evaluates to @default@ if @index@
|
||||||
-- overflows
|
-- overflows
|
||||||
@ -1185,6 +1221,7 @@ class Mergeable a where
|
|||||||
ite s a b
|
ite s a b
|
||||||
| Just t <- unliteral s = if t then a else b
|
| Just t <- unliteral s = if t then a else b
|
||||||
| True = symbolicMerge s a b
|
| True = symbolicMerge s a b
|
||||||
|
sBranch s = ite (reduceInPathCondition s)
|
||||||
-- NB. Earlier implementation of select used the binary-search trick
|
-- NB. Earlier implementation of select used the binary-search trick
|
||||||
-- on the index to chop down the search space. While that is a good trick
|
-- on the index to chop down the search space. While that is a good trick
|
||||||
-- in general, it doesn't work for SBV since we do not have any notion of
|
-- in general, it doesn't work for SBV since we do not have any notion of
|
||||||
@ -1200,86 +1237,14 @@ class Mergeable a where
|
|||||||
|
|
||||||
-- SBV
|
-- SBV
|
||||||
instance SymWord a => Mergeable (SBV a) where
|
instance SymWord a => Mergeable (SBV a) where
|
||||||
-- the strict match and checking of literal equivalence is essential below,
|
-- sBranch is essentially the default method, but we are careful in not forcing the
|
||||||
-- as otherwise we risk hanging onto huge closures and blow stack! This is
|
-- arguments as ite does, since sBranch is expected to be used when one of the
|
||||||
-- against the feel that merging shouldn't look at branches if the test
|
-- branches is likely to be in a branch that's recursively evaluated.
|
||||||
-- expression is constant. However, it's OK to do it this way since we
|
sBranch s a b
|
||||||
-- expect "ite" to be used in such cases which already checks for that. That
|
| Just t <- unliteral sReduced = if t then a else b
|
||||||
-- is the use case of the symbolicMerge should be when the test is symbolic.
|
| True = symbolicWordMerge False sReduced a b
|
||||||
-- Of course, we do not have a way of enforcing that in the user code, but
|
where sReduced = reduceInPathCondition s
|
||||||
-- at least our library code respects that invariant.
|
symbolicMerge = symbolicWordMerge True
|
||||||
symbolicMerge t a@(SBV{}) b@(SBV{})
|
|
||||||
| Just av <- unliteral a, Just bv <- unliteral b, rationalSBVCheck a b, av == bv
|
|
||||||
= a
|
|
||||||
| True
|
|
||||||
= SBV k $ Right $ cache c
|
|
||||||
where k = kindOf a
|
|
||||||
c st = do swt <- sbvToSW st t
|
|
||||||
case () of
|
|
||||||
() | swt == trueSW -> sbvToSW st a -- these two cases should never be needed as we expect symbolicMerge to be
|
|
||||||
() | swt == falseSW -> sbvToSW st b -- called with symbolic tests, but just in case..
|
|
||||||
() -> do {- It is tempting to record the choice of the test expression here as we branch down to the 'then' and 'else' branches. That is,
|
|
||||||
when we evaluate 'a', we can make use of the fact that the test expression is True, and similarly we can use the fact that it
|
|
||||||
is False when b is evaluated. In certain cases this can cut down on symbolic simulation significantly, for instance if
|
|
||||||
repetitive decisions are made in a recursive loop. Unfortunately, the implementation of this idea is quite tricky, due to
|
|
||||||
our sharing based implementation. As the 'then' branch is evaluated, we will create many expressions that are likely going
|
|
||||||
to be "reused" when the 'else' branch is executed. But, it would be *dead wrong* to share those values, as they were "cached"
|
|
||||||
under the incorrect assumptions. To wit, consider the following:
|
|
||||||
|
|
||||||
foo x y = ite (y .== 0) k (k+1)
|
|
||||||
where k = ite (y .== 0) x (x+1)
|
|
||||||
|
|
||||||
When we reduce the 'then' branch of the first ite, we'd record the assumption that y is 0. But while reducing the 'then' branch, we'd
|
|
||||||
like to share 'k', which would evaluate (correctly) to 'x' under the given assumption. When we backtrack and evaluate the 'else'
|
|
||||||
branch of the first ite, we'd see 'k' is needed again, and we'd look it up from our sharing map to find (incorrectly) that its value
|
|
||||||
is 'x', which was stored there under the assumption that y was 0, which no longer holds. Clearly, this is unsound.
|
|
||||||
|
|
||||||
A sound implementation would have to precisely track which assumptions were active at the time expressions get shared. That is,
|
|
||||||
in the above example, we should record that the value of 'k' was cached under the assumption that 'y' is 0. While sound, this
|
|
||||||
approach unfortunately leads to significant loss of valid sharing when the value itself had nothing to do with the assumption itself.
|
|
||||||
To wit, consider:
|
|
||||||
|
|
||||||
foo x y = ite (y .== 0) k (k+1)
|
|
||||||
where k = x+5
|
|
||||||
|
|
||||||
If we tracked the assumptions, we would recompute 'k' twice, since the branch assumptions would differ. Clearly, there is no need to
|
|
||||||
re-compute 'k' in this case since its value is independent of y. Note that the whole SBV performance story is based on agressive sharing,
|
|
||||||
and losing that would have other significant ramifications.
|
|
||||||
|
|
||||||
The "proper" solution would be to track, with each shared computation, precisely which assumptions it actually *depends* on, rather
|
|
||||||
than blindly recording all the assumptions present at that time. SBV's symbolic simulation engine clearly has all the info needed to do this
|
|
||||||
properly, but the implementation is not straightforward at all. For each subexpression, we would need to chase down its dependencies
|
|
||||||
transitively, which can require a lot of scanning of the generated program causing major slow-down; thus potentially defeating the
|
|
||||||
whole purpose of sharing in the first place.
|
|
||||||
|
|
||||||
Design choice: Keep it simple, and simply do not track the assumption at all. This will maximize sharing, at the cost of evaluating
|
|
||||||
unreachable branches. I think the simplicity is more important at this point than efficiency.
|
|
||||||
|
|
||||||
Also note that the user can avoid most such issues by properly combining if-then-else's with common conditions together. That is, the
|
|
||||||
first program above should be written like this:
|
|
||||||
|
|
||||||
foo x y = ite (y .== 0) x (x+2)
|
|
||||||
|
|
||||||
In general, the following transformations should be done whenever possible:
|
|
||||||
|
|
||||||
ite e1 (ite e1 e2 e3) e4 --> ite e1 e2 e4
|
|
||||||
ite e1 e2 (ite e1 e3 e4) --> ite e1 e2 e4
|
|
||||||
|
|
||||||
This is in accordance with the general rule-of-thumb stating conditionals should be avoided as much as possible. However, we might prefer
|
|
||||||
the following:
|
|
||||||
|
|
||||||
ite e1 (f e2 e4) (f e3 e5) --> f (ite e1 e2 e3) (ite e1 e4 e5)
|
|
||||||
|
|
||||||
especially if this expression happens to be inside 'f's body itself (i.e., when f is recursive), since it reduces the number of
|
|
||||||
recursive calls. Clearly, programming with symbolic simulation in mind is another kind of beast alltogether.
|
|
||||||
-}
|
|
||||||
swa <- sbvToSW st a -- evaluate 'then' branch
|
|
||||||
swb <- sbvToSW st b -- evaluate 'else' branch
|
|
||||||
case () of -- merge:
|
|
||||||
() | swa == swb -> return swa
|
|
||||||
() | swa == trueSW && swb == falseSW -> return swt
|
|
||||||
() | swa == falseSW && swb == trueSW -> newExpr st k (SBVApp Not [swt])
|
|
||||||
() -> newExpr st k (SBVApp Ite [swt, swa, swb])
|
|
||||||
-- Custom version of select that translates to SMT-Lib tables at the base type of words
|
-- Custom version of select that translates to SMT-Lib tables at the base type of words
|
||||||
select xs err ind
|
select xs err ind
|
||||||
| SBV _ (Left c) <- ind = case cwVal c of
|
| SBV _ (Left c) <- ind = case cwVal c of
|
||||||
@ -1299,6 +1264,86 @@ instance SymWord a => Mergeable (SBV a) where
|
|||||||
let len = length xs
|
let len = length xs
|
||||||
newExpr st kElt (SBVApp (LkUp (idx, kInd, kElt, len) swi swe) [])
|
newExpr st kElt (SBVApp (LkUp (idx, kInd, kElt, len) swi swe) [])
|
||||||
|
|
||||||
|
-- symbolically merge two SBV words, based on the boolean condition given.
|
||||||
|
-- The first argument controls whether we want to reduce the branches
|
||||||
|
-- separately first, which avoids hanging onto huge thunks, and is usually
|
||||||
|
-- the right thing to do for ite. But we precisely do not want to do that
|
||||||
|
-- in case of sBranch, which is the case when one of the branches (typically
|
||||||
|
-- the "else" branch is hanging off of a recursive call.
|
||||||
|
symbolicWordMerge :: SymWord a => Bool -> SBool -> SBV a -> SBV a -> SBV a
|
||||||
|
symbolicWordMerge force t a b
|
||||||
|
| force, Just av <- unliteral a, Just bv <- unliteral b, rationalSBVCheck a b, av == bv
|
||||||
|
= a
|
||||||
|
| True
|
||||||
|
= SBV k $ Right $ cache c
|
||||||
|
where k = kindOf a
|
||||||
|
c st = do swt <- sbvToSW st t
|
||||||
|
case () of
|
||||||
|
() | swt == trueSW -> sbvToSW st a -- these two cases should never be needed as we expect symbolicMerge to be
|
||||||
|
() | swt == falseSW -> sbvToSW st b -- called with symbolic tests, but just in case..
|
||||||
|
() -> do {- It is tempting to record the choice of the test expression here as we branch down to the 'then' and 'else' branches. That is,
|
||||||
|
when we evaluate 'a', we can make use of the fact that the test expression is True, and similarly we can use the fact that it
|
||||||
|
is False when b is evaluated. In certain cases this can cut down on symbolic simulation significantly, for instance if
|
||||||
|
repetitive decisions are made in a recursive loop. Unfortunately, the implementation of this idea is quite tricky, due to
|
||||||
|
our sharing based implementation. As the 'then' branch is evaluated, we will create many expressions that are likely going
|
||||||
|
to be "reused" when the 'else' branch is executed. But, it would be *dead wrong* to share those values, as they were "cached"
|
||||||
|
under the incorrect assumptions. To wit, consider the following:
|
||||||
|
|
||||||
|
foo x y = ite (y .== 0) k (k+1)
|
||||||
|
where k = ite (y .== 0) x (x+1)
|
||||||
|
|
||||||
|
When we reduce the 'then' branch of the first ite, we'd record the assumption that y is 0. But while reducing the 'then' branch, we'd
|
||||||
|
like to share 'k', which would evaluate (correctly) to 'x' under the given assumption. When we backtrack and evaluate the 'else'
|
||||||
|
branch of the first ite, we'd see 'k' is needed again, and we'd look it up from our sharing map to find (incorrectly) that its value
|
||||||
|
is 'x', which was stored there under the assumption that y was 0, which no longer holds. Clearly, this is unsound.
|
||||||
|
|
||||||
|
A sound implementation would have to precisely track which assumptions were active at the time expressions get shared. That is,
|
||||||
|
in the above example, we should record that the value of 'k' was cached under the assumption that 'y' is 0. While sound, this
|
||||||
|
approach unfortunately leads to significant loss of valid sharing when the value itself had nothing to do with the assumption itself.
|
||||||
|
To wit, consider:
|
||||||
|
|
||||||
|
foo x y = ite (y .== 0) k (k+1)
|
||||||
|
where k = x+5
|
||||||
|
|
||||||
|
If we tracked the assumptions, we would recompute 'k' twice, since the branch assumptions would differ. Clearly, there is no need to
|
||||||
|
re-compute 'k' in this case since its value is independent of y. Note that the whole SBV performance story is based on agressive sharing,
|
||||||
|
and losing that would have other significant ramifications.
|
||||||
|
|
||||||
|
The "proper" solution would be to track, with each shared computation, precisely which assumptions it actually *depends* on, rather
|
||||||
|
than blindly recording all the assumptions present at that time. SBV's symbolic simulation engine clearly has all the info needed to do this
|
||||||
|
properly, but the implementation is not straightforward at all. For each subexpression, we would need to chase down its dependencies
|
||||||
|
transitively, which can require a lot of scanning of the generated program causing major slow-down; thus potentially defeating the
|
||||||
|
whole purpose of sharing in the first place.
|
||||||
|
|
||||||
|
Design choice: Keep it simple, and simply do not track the assumption at all. This will maximize sharing, at the cost of evaluating
|
||||||
|
unreachable branches. I think the simplicity is more important at this point than efficiency.
|
||||||
|
|
||||||
|
Also note that the user can avoid most such issues by properly combining if-then-else's with common conditions together. That is, the
|
||||||
|
first program above should be written like this:
|
||||||
|
|
||||||
|
foo x y = ite (y .== 0) x (x+2)
|
||||||
|
|
||||||
|
In general, the following transformations should be done whenever possible:
|
||||||
|
|
||||||
|
ite e1 (ite e1 e2 e3) e4 --> ite e1 e2 e4
|
||||||
|
ite e1 e2 (ite e1 e3 e4) --> ite e1 e2 e4
|
||||||
|
|
||||||
|
This is in accordance with the general rule-of-thumb stating conditionals should be avoided as much as possible. However, we might prefer
|
||||||
|
the following:
|
||||||
|
|
||||||
|
ite e1 (f e2 e4) (f e3 e5) --> f (ite e1 e2 e3) (ite e1 e4 e5)
|
||||||
|
|
||||||
|
especially if this expression happens to be inside 'f's body itself (i.e., when f is recursive), since it reduces the number of
|
||||||
|
recursive calls. Clearly, programming with symbolic simulation in mind is another kind of beast alltogether.
|
||||||
|
-}
|
||||||
|
swa <- sbvToSW (st `extendPathCondition` (&&& t)) a -- evaluate 'then' branch
|
||||||
|
swb <- sbvToSW (st `extendPathCondition` (&&& bnot t)) b -- evaluate 'else' branch
|
||||||
|
case () of -- merge:
|
||||||
|
() | swa == swb -> return swa
|
||||||
|
() | swa == trueSW && swb == falseSW -> return swt
|
||||||
|
() | swa == falseSW && swb == trueSW -> newExpr st k (SBVApp Not [swt])
|
||||||
|
() -> newExpr st k (SBVApp Ite [swt, swa, swb])
|
||||||
|
|
||||||
-- Unit
|
-- Unit
|
||||||
instance Mergeable () where
|
instance Mergeable () where
|
||||||
symbolicMerge _ _ _ = ()
|
symbolicMerge _ _ _ = ()
|
||||||
@ -1400,10 +1445,10 @@ instance (SymWord a, Bounded a) => Bounded (SBV a) where
|
|||||||
|
|
||||||
-- SArrays are both "EqSymbolic" and "Mergeable"
|
-- SArrays are both "EqSymbolic" and "Mergeable"
|
||||||
instance EqSymbolic (SArray a b) where
|
instance EqSymbolic (SArray a b) where
|
||||||
(SArray _ a) .== (SArray _ b) = SBV (KBounded False 1) $ Right $ cache c
|
(SArray _ a) .== (SArray _ b) = SBV KBool $ Right $ cache c
|
||||||
where c st = do ai <- uncacheAI a st
|
where c st = do ai <- uncacheAI a st
|
||||||
bi <- uncacheAI b st
|
bi <- uncacheAI b st
|
||||||
newExpr st (KBounded False 1) (SBVApp (ArrEq ai bi) [])
|
newExpr st KBool (SBVApp (ArrEq ai bi) [])
|
||||||
|
|
||||||
instance SymWord b => Mergeable (SArray a b) where
|
instance SymWord b => Mergeable (SArray a b) where
|
||||||
symbolicMerge = mergeArrays
|
symbolicMerge = mergeArrays
|
||||||
@ -1460,12 +1505,6 @@ instance HasKind a => Uninterpreted (SBV a) where
|
|||||||
| True = do newUninterpreted st nm (SBVType [ka]) (fst `fmap` mbCgData)
|
| True = do newUninterpreted st nm (SBVType [ka]) (fst `fmap` mbCgData)
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) []
|
newExpr st ka $ SBVApp (Uninterpreted nm) []
|
||||||
|
|
||||||
-- Forcing an argument; this is a necessary evil to make sure all the arguments
|
|
||||||
-- to an uninterpreted function are evaluated before called; the semantics of
|
|
||||||
-- such functions is necessarily strict; deviating from Haskell's
|
|
||||||
forceArg :: SW -> IO ()
|
|
||||||
forceArg (SW k n) = k `seq` n `seq` return ()
|
|
||||||
|
|
||||||
-- Functions of one argument
|
-- Functions of one argument
|
||||||
instance (SymWord b, HasKind a) => Uninterpreted (SBV b -> SBV a) where
|
instance (SymWord b, HasKind a) => Uninterpreted (SBV b -> SBV a) where
|
||||||
sbvUninterpret mbCgData nm = f
|
sbvUninterpret mbCgData nm = f
|
||||||
@ -1479,7 +1518,7 @@ instance (SymWord b, HasKind a) => Uninterpreted (SBV b -> SBV a) where
|
|||||||
result st | Just (_, v) <- mbCgData, inProofMode st = sbvToSW st (v arg0)
|
result st | Just (_, v) <- mbCgData, inProofMode st = sbvToSW st (v arg0)
|
||||||
| True = do newUninterpreted st nm (SBVType [kb, ka]) (fst `fmap` mbCgData)
|
| True = do newUninterpreted st nm (SBVType [kb, ka]) (fst `fmap` mbCgData)
|
||||||
sw0 <- sbvToSW st arg0
|
sw0 <- sbvToSW st arg0
|
||||||
mapM_ forceArg [sw0]
|
mapM_ forceSWArg [sw0]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0]
|
||||||
|
|
||||||
-- Functions of two arguments
|
-- Functions of two arguments
|
||||||
@ -1497,7 +1536,7 @@ instance (SymWord c, SymWord b, HasKind a) => Uninterpreted (SBV c -> SBV b -> S
|
|||||||
| True = do newUninterpreted st nm (SBVType [kc, kb, ka]) (fst `fmap` mbCgData)
|
| True = do newUninterpreted st nm (SBVType [kc, kb, ka]) (fst `fmap` mbCgData)
|
||||||
sw0 <- sbvToSW st arg0
|
sw0 <- sbvToSW st arg0
|
||||||
sw1 <- sbvToSW st arg1
|
sw1 <- sbvToSW st arg1
|
||||||
mapM_ forceArg [sw0, sw1]
|
mapM_ forceSWArg [sw0, sw1]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1]
|
||||||
|
|
||||||
-- Functions of three arguments
|
-- Functions of three arguments
|
||||||
@ -1517,7 +1556,7 @@ instance (SymWord d, SymWord c, SymWord b, HasKind a) => Uninterpreted (SBV d ->
|
|||||||
sw0 <- sbvToSW st arg0
|
sw0 <- sbvToSW st arg0
|
||||||
sw1 <- sbvToSW st arg1
|
sw1 <- sbvToSW st arg1
|
||||||
sw2 <- sbvToSW st arg2
|
sw2 <- sbvToSW st arg2
|
||||||
mapM_ forceArg [sw0, sw1, sw2]
|
mapM_ forceSWArg [sw0, sw1, sw2]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2]
|
||||||
|
|
||||||
-- Functions of four arguments
|
-- Functions of four arguments
|
||||||
@ -1539,7 +1578,7 @@ instance (SymWord e, SymWord d, SymWord c, SymWord b, HasKind a) => Uninterprete
|
|||||||
sw1 <- sbvToSW st arg1
|
sw1 <- sbvToSW st arg1
|
||||||
sw2 <- sbvToSW st arg2
|
sw2 <- sbvToSW st arg2
|
||||||
sw3 <- sbvToSW st arg3
|
sw3 <- sbvToSW st arg3
|
||||||
mapM_ forceArg [sw0, sw1, sw2, sw3]
|
mapM_ forceSWArg [sw0, sw1, sw2, sw3]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3]
|
||||||
|
|
||||||
-- Functions of five arguments
|
-- Functions of five arguments
|
||||||
@ -1563,7 +1602,7 @@ instance (SymWord f, SymWord e, SymWord d, SymWord c, SymWord b, HasKind a) => U
|
|||||||
sw2 <- sbvToSW st arg2
|
sw2 <- sbvToSW st arg2
|
||||||
sw3 <- sbvToSW st arg3
|
sw3 <- sbvToSW st arg3
|
||||||
sw4 <- sbvToSW st arg4
|
sw4 <- sbvToSW st arg4
|
||||||
mapM_ forceArg [sw0, sw1, sw2, sw3, sw4]
|
mapM_ forceSWArg [sw0, sw1, sw2, sw3, sw4]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3, sw4]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3, sw4]
|
||||||
|
|
||||||
-- Functions of six arguments
|
-- Functions of six arguments
|
||||||
@ -1589,7 +1628,7 @@ instance (SymWord g, SymWord f, SymWord e, SymWord d, SymWord c, SymWord b, HasK
|
|||||||
sw3 <- sbvToSW st arg3
|
sw3 <- sbvToSW st arg3
|
||||||
sw4 <- sbvToSW st arg4
|
sw4 <- sbvToSW st arg4
|
||||||
sw5 <- sbvToSW st arg5
|
sw5 <- sbvToSW st arg5
|
||||||
mapM_ forceArg [sw0, sw1, sw2, sw3, sw4, sw5]
|
mapM_ forceSWArg [sw0, sw1, sw2, sw3, sw4, sw5]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3, sw4, sw5]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3, sw4, sw5]
|
||||||
|
|
||||||
-- Functions of seven arguments
|
-- Functions of seven arguments
|
||||||
@ -1618,7 +1657,7 @@ instance (SymWord h, SymWord g, SymWord f, SymWord e, SymWord d, SymWord c, SymW
|
|||||||
sw4 <- sbvToSW st arg4
|
sw4 <- sbvToSW st arg4
|
||||||
sw5 <- sbvToSW st arg5
|
sw5 <- sbvToSW st arg5
|
||||||
sw6 <- sbvToSW st arg6
|
sw6 <- sbvToSW st arg6
|
||||||
mapM_ forceArg [sw0, sw1, sw2, sw3, sw4, sw5, sw6]
|
mapM_ forceSWArg [sw0, sw1, sw2, sw3, sw4, sw5, sw6]
|
||||||
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3, sw4, sw5, sw6]
|
newExpr st ka $ SBVApp (Uninterpreted nm) [sw0, sw1, sw2, sw3, sw4, sw5, sw6]
|
||||||
|
|
||||||
-- Uncurried functions of two arguments
|
-- Uncurried functions of two arguments
|
||||||
@ -1655,7 +1694,39 @@ instance (SymWord h, SymWord g, SymWord f, SymWord e, SymWord d, SymWord c, SymW
|
|||||||
sbvUninterpret mbCgData nm = let f = sbvUninterpret (uc7 `fmap` mbCgData) nm in \(arg0, arg1, arg2, arg3, arg4, arg5, arg6) -> f arg0 arg1 arg2 arg3 arg4 arg5 arg6
|
sbvUninterpret mbCgData nm = let f = sbvUninterpret (uc7 `fmap` mbCgData) nm in \(arg0, arg1, arg2, arg3, arg4, arg5, arg6) -> f arg0 arg1 arg2 arg3 arg4 arg5 arg6
|
||||||
where uc7 (cs, fn) = (cs, \a b c d e f g -> fn (a, b, c, d, e, f, g))
|
where uc7 (cs, fn) = (cs, \a b c d e f g -> fn (a, b, c, d, e, f, g))
|
||||||
|
|
||||||
-- | Adding arbitrary constraints.
|
-- | Adding arbitrary constraints. When adding constraints, one has to be careful about
|
||||||
|
-- making sure they are not inconsistent. The function 'isVacuous' can be use for this purpose.
|
||||||
|
-- Here is an example. Consider the following predicate:
|
||||||
|
--
|
||||||
|
-- >>> let pred = do { x <- forall "x"; constrain $ x .< x; return $ x .>= (5 :: SWord8) }
|
||||||
|
--
|
||||||
|
-- This predicate asserts that all 8-bit values are larger than 5, subject to the constraint that the
|
||||||
|
-- values considered satisfy @x .< x@, i.e., they are less than themselves. Since there are no values that
|
||||||
|
-- satisfy this constraint, the proof will pass vacuously:
|
||||||
|
--
|
||||||
|
-- >>> prove pred
|
||||||
|
-- Q.E.D.
|
||||||
|
--
|
||||||
|
-- We can use 'isVacuous' to make sure to see that the pass was vacuous:
|
||||||
|
--
|
||||||
|
-- >>> isVacuous pred
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- While the above example is trivial, things can get complicated if there are multiple constraints with
|
||||||
|
-- non-straightforward relations; so if constraints are used one should make sure to check the predicate
|
||||||
|
-- is not vacuously true. Here's an example that is not vacuous:
|
||||||
|
--
|
||||||
|
-- >>> let pred' = do { x <- forall "x"; constrain $ x .> 6; return $ x .>= (5 :: SWord8) }
|
||||||
|
--
|
||||||
|
-- This time the proof passes as expected:
|
||||||
|
--
|
||||||
|
-- >>> prove pred'
|
||||||
|
-- Q.E.D.
|
||||||
|
--
|
||||||
|
-- And the proof is not vacuous:
|
||||||
|
--
|
||||||
|
-- >>> isVacuous pred'
|
||||||
|
-- False
|
||||||
constrain :: SBool -> Symbolic ()
|
constrain :: SBool -> Symbolic ()
|
||||||
constrain c = addConstraint Nothing c (bnot c)
|
constrain c = addConstraint Nothing c (bnot c)
|
||||||
|
|
||||||
@ -1665,6 +1736,25 @@ constrain c = addConstraint Nothing c (bnot c)
|
|||||||
pConstrain :: Double -> SBool -> Symbolic ()
|
pConstrain :: Double -> SBool -> Symbolic ()
|
||||||
pConstrain t c = addConstraint (Just t) c (bnot c)
|
pConstrain t c = addConstraint (Just t) c (bnot c)
|
||||||
|
|
||||||
|
-- | Boolean symbolic reduction. See if we can reduce a boolean condition to true/false
|
||||||
|
-- using the path context information, by making external calls to the SMT solvers. Used in the
|
||||||
|
-- implementation of 'sBranch'.
|
||||||
|
reduceInPathCondition :: SBool -> SBool
|
||||||
|
reduceInPathCondition b
|
||||||
|
| isConcrete b = b -- No reduction is needed, already a concrete value
|
||||||
|
| True = SBV k $ Right $ cache c
|
||||||
|
where k = kindOf b
|
||||||
|
c st = do -- Now that we know our boolean is not obviously true/false. Need to make an external
|
||||||
|
-- call to the SMT solver to see if we can prove it is necessarily one of those
|
||||||
|
let pc = getPathCondition st
|
||||||
|
satTrue <- isSBranchFeasibleInState st "then" (pc &&& b)
|
||||||
|
if not satTrue
|
||||||
|
then return falseSW -- condition is not satisfiable; so it must be necessarily False.
|
||||||
|
else do satFalse <- isSBranchFeasibleInState st "else" (pc &&& bnot b)
|
||||||
|
if not satFalse -- negation of the condition is not satisfiable; so it must be necessarily True.
|
||||||
|
then return trueSW
|
||||||
|
else sbvToSW st b -- condition is not necessarily always True/False. So, keep symbolic.
|
||||||
|
|
||||||
-- Quickcheck interface on symbolic-booleans..
|
-- Quickcheck interface on symbolic-booleans..
|
||||||
instance Testable SBool where
|
instance Testable SBool where
|
||||||
property (SBV _ (Left b)) = property (cwToBool b)
|
property (SBV _ (Left b)) = property (cwToBool b)
|
||||||
@ -1703,5 +1793,9 @@ slet x f = SBV k $ Right $ cache r
|
|||||||
res = f xsbv
|
res = f xsbv
|
||||||
sbvToSW st res
|
sbvToSW st res
|
||||||
|
|
||||||
|
-- We use 'isVacuous' and 'prove' only for the "test" section in this file, and GHC complains about that. So, this shuts it up.
|
||||||
|
__unused :: a
|
||||||
|
__unused = error "__unused" (isVacuous :: SBool -> IO Bool) (prove :: SBool -> IO ThmResult)
|
||||||
|
|
||||||
{-# ANN module "HLint: ignore Eta reduce" #-}
|
{-# ANN module "HLint: ignore Eta reduce" #-}
|
||||||
{-# ANN module "HLint: ignore Reduce duplication" #-}
|
{-# ANN module "HLint: ignore Reduce duplication" #-}
|
||||||
|
@ -27,7 +27,6 @@ import Data.Word (Word8, Word16, Word32, Word64)
|
|||||||
import Numeric (showIntAtBase, showHex, readInt)
|
import Numeric (showIntAtBase, showHex, readInt)
|
||||||
|
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.BitVectors.Model () -- instances only
|
|
||||||
|
|
||||||
-- | PrettyNum class captures printing of numbers in hex and binary formats; also supporting negative numbers.
|
-- | PrettyNum class captures printing of numbers in hex and binary formats; also supporting negative numbers.
|
||||||
--
|
--
|
||||||
|
@ -81,6 +81,7 @@ genericSign x
|
|||||||
| Just c <- unliteral x = literal $ fromIntegral c
|
| Just c <- unliteral x = literal $ fromIntegral c
|
||||||
| True = SBV k (Right (cache y))
|
| True = SBV k (Right (cache y))
|
||||||
where k = case kindOf x of
|
where k = case kindOf x of
|
||||||
|
KBool -> error "Data.SBV.SignCast.genericSign: Called on boolean value"
|
||||||
KBounded False n -> KBounded True n
|
KBounded False n -> KBounded True n
|
||||||
KBounded True _ -> error "Data.SBV.SignCast.genericSign: Called on signed value"
|
KBounded True _ -> error "Data.SBV.SignCast.genericSign: Called on signed value"
|
||||||
KUnbounded -> error "Data.SBV.SignCast.genericSign: Called on unbounded value"
|
KUnbounded -> error "Data.SBV.SignCast.genericSign: Called on unbounded value"
|
||||||
@ -97,6 +98,7 @@ genericUnsign x
|
|||||||
| Just c <- unliteral x = literal $ fromIntegral c
|
| Just c <- unliteral x = literal $ fromIntegral c
|
||||||
| True = SBV k (Right (cache y))
|
| True = SBV k (Right (cache y))
|
||||||
where k = case kindOf x of
|
where k = case kindOf x of
|
||||||
|
KBool -> error "Data.SBV.SignCast.genericUnSign: Called on boolean value"
|
||||||
KBounded True n -> KBounded False n
|
KBounded True n -> KBounded False n
|
||||||
KBounded False _ -> error "Data.SBV.SignCast.genericUnSign: Called on unsigned value"
|
KBounded False _ -> error "Data.SBV.SignCast.genericUnSign: Called on unsigned value"
|
||||||
KUnbounded -> error "Data.SBV.SignCast.genericUnSign: Called on unbounded value"
|
KUnbounded -> error "Data.SBV.SignCast.genericUnSign: Called on unbounded value"
|
||||||
|
@ -14,10 +14,12 @@
|
|||||||
-- - "Data.SBV.Bridge.Z3"
|
-- - "Data.SBV.Bridge.Z3"
|
||||||
--
|
--
|
||||||
-- - "Data.SBV.Bridge.CVC4"
|
-- - "Data.SBV.Bridge.CVC4"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.MathSAT"
|
||||||
---------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
module Data.SBV.Bridge.Boolector (
|
module Data.SBV.Bridge.Boolector (
|
||||||
-- * CVC4 specific interface
|
-- * Boolector specific interface
|
||||||
sbvCurrentSolver
|
sbvCurrentSolver
|
||||||
-- ** Proving and checking satisfiability
|
-- ** Proving and checking satisfiability
|
||||||
, prove, sat, allSat, isVacuous, isTheorem, isSatisfiable
|
, prove, sat, allSat, isVacuous, isTheorem, isSatisfiable
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
-- - "Data.SBV.Bridge.Z3"
|
-- - "Data.SBV.Bridge.Z3"
|
||||||
--
|
--
|
||||||
-- - "Data.SBV.Bridge.Boolector"
|
-- - "Data.SBV.Bridge.Boolector"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.MathSAT"
|
||||||
---------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
module Data.SBV.Bridge.CVC4 (
|
module Data.SBV.Bridge.CVC4 (
|
||||||
|
107
sbv/Data/SBV/Bridge/MathSAT.hs
Normal file
107
sbv/Data/SBV/Bridge/MathSAT.hs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
---------------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : Data.SBV.Bridge.MathSAT
|
||||||
|
-- Copyright : (c) Levent Erkok
|
||||||
|
-- License : BSD3
|
||||||
|
-- Maintainer : erkokl@gmail.com
|
||||||
|
-- Stability : experimental
|
||||||
|
--
|
||||||
|
-- Interface to the MathSAT SMT solver. Import this module if you want to use the
|
||||||
|
-- MathSAT SMT prover as your backend solver. Also see:
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.Yices"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.Z3"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.CVC4"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.Boolector"
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module Data.SBV.Bridge.MathSAT (
|
||||||
|
-- * MathSAT specific interface
|
||||||
|
sbvCurrentSolver
|
||||||
|
-- ** Proving and checking satisfiability
|
||||||
|
, prove, sat, allSat, isVacuous, isTheorem, isSatisfiable
|
||||||
|
-- ** Optimization routines
|
||||||
|
, optimize, minimize, maximize
|
||||||
|
-- * Non-MathSAT specific SBV interface
|
||||||
|
-- $moduleExportIntro
|
||||||
|
, module Data.SBV
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.SBV hiding (prove, sat, allSat, isVacuous, isTheorem, isSatisfiable, optimize, minimize, maximize, sbvCurrentSolver)
|
||||||
|
|
||||||
|
-- | Current solver instance, pointing to cvc4.
|
||||||
|
sbvCurrentSolver :: SMTConfig
|
||||||
|
sbvCurrentSolver = mathSAT
|
||||||
|
|
||||||
|
-- | Prove theorems, using the CVC4 SMT solver
|
||||||
|
prove :: Provable a
|
||||||
|
=> a -- ^ Property to check
|
||||||
|
-> IO ThmResult -- ^ Response from the SMT solver, containing the counter-example if found
|
||||||
|
prove = proveWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Find satisfying solutions, using the CVC4 SMT solver
|
||||||
|
sat :: Provable a
|
||||||
|
=> a -- ^ Property to check
|
||||||
|
-> IO SatResult -- ^ Response of the SMT Solver, containing the model if found
|
||||||
|
sat = satWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Find all satisfying solutions, using the CVC4 SMT solver
|
||||||
|
allSat :: Provable a
|
||||||
|
=> a -- ^ Property to check
|
||||||
|
-> IO AllSatResult -- ^ List of all satisfying models
|
||||||
|
allSat = allSatWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Check vacuity of the explicit constraints introduced by calls to the 'constrain' function, using the CVC4 SMT solver
|
||||||
|
isVacuous :: Provable a
|
||||||
|
=> a -- ^ Property to check
|
||||||
|
-> IO Bool -- ^ True if the constraints are unsatisifiable
|
||||||
|
isVacuous = isVacuousWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Check if the statement is a theorem, with an optional time-out in seconds, using the CVC4 SMT solver
|
||||||
|
isTheorem :: Provable a
|
||||||
|
=> Maybe Int -- ^ Optional time-out, specify in seconds
|
||||||
|
-> a -- ^ Property to check
|
||||||
|
-> IO (Maybe Bool) -- ^ Returns Nothing if time-out expires
|
||||||
|
isTheorem = isTheoremWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Check if the statement is satisfiable, with an optional time-out in seconds, using the CVC4 SMT solver
|
||||||
|
isSatisfiable :: Provable a
|
||||||
|
=> Maybe Int -- ^ Optional time-out, specify in seconds
|
||||||
|
-> a -- ^ Property to check
|
||||||
|
-> IO (Maybe Bool) -- ^ Returns Nothing if time-out expiers
|
||||||
|
isSatisfiable = isSatisfiableWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Optimize cost functions, using the CVC4 SMT solver
|
||||||
|
optimize :: (SatModel a, SymWord a, Show a, SymWord c, Show c)
|
||||||
|
=> OptimizeOpts -- ^ Parameters to optimization (Iterative, Quantified, etc.)
|
||||||
|
-> (SBV c -> SBV c -> SBool) -- ^ Betterness check: This is the comparison predicate for optimization
|
||||||
|
-> ([SBV a] -> SBV c) -- ^ Cost function
|
||||||
|
-> Int -- ^ Number of inputs
|
||||||
|
-> ([SBV a] -> SBool) -- ^ Validity function
|
||||||
|
-> IO (Maybe [a]) -- ^ Returns Nothing if there is no valid solution, otherwise an optimal solution
|
||||||
|
optimize = optimizeWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Minimize cost functions, using the CVC4 SMT solver
|
||||||
|
minimize :: (SatModel a, SymWord a, Show a, SymWord c, Show c)
|
||||||
|
=> OptimizeOpts -- ^ Parameters to optimization (Iterative, Quantified, etc.)
|
||||||
|
-> ([SBV a] -> SBV c) -- ^ Cost function to minimize
|
||||||
|
-> Int -- ^ Number of inputs
|
||||||
|
-> ([SBV a] -> SBool) -- ^ Validity function
|
||||||
|
-> IO (Maybe [a]) -- ^ Returns Nothing if there is no valid solution, otherwise an optimal solution
|
||||||
|
minimize = minimizeWith sbvCurrentSolver
|
||||||
|
|
||||||
|
-- | Maximize cost functions, using the CVC4 SMT solver
|
||||||
|
maximize :: (SatModel a, SymWord a, Show a, SymWord c, Show c)
|
||||||
|
=> OptimizeOpts -- ^ Parameters to optimization (Iterative, Quantified, etc.)
|
||||||
|
-> ([SBV a] -> SBV c) -- ^ Cost function to maximize
|
||||||
|
-> Int -- ^ Number of inputs
|
||||||
|
-> ([SBV a] -> SBool) -- ^ Validity function
|
||||||
|
-> IO (Maybe [a]) -- ^ Returns Nothing if there is no valid solution, otherwise an optimal solution
|
||||||
|
maximize = maximizeWith sbvCurrentSolver
|
||||||
|
|
||||||
|
{- $moduleExportIntro
|
||||||
|
The remainder of the SBV library that is common to all back-end SMT solvers, directly coming from the "Data.SBV" module.
|
||||||
|
-}
|
@ -14,6 +14,8 @@
|
|||||||
-- - "Data.SBV.Bridge.CVC4"
|
-- - "Data.SBV.Bridge.CVC4"
|
||||||
--
|
--
|
||||||
-- - "Data.SBV.Bridge.Z3"
|
-- - "Data.SBV.Bridge.Z3"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.MathSAT"
|
||||||
---------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
module Data.SBV.Bridge.Yices (
|
module Data.SBV.Bridge.Yices (
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
-- - "Data.SBV.Bridge.CVC4"
|
-- - "Data.SBV.Bridge.CVC4"
|
||||||
--
|
--
|
||||||
-- - "Data.SBV.Bridge.Yices"
|
-- - "Data.SBV.Bridge.Yices"
|
||||||
|
--
|
||||||
|
-- - "Data.SBV.Bridge.MathSAT"
|
||||||
---------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
module Data.SBV.Bridge.Z3 (
|
module Data.SBV.Bridge.Z3 (
|
||||||
|
@ -24,7 +24,6 @@ import System.Random
|
|||||||
import Text.PrettyPrint.HughesPJ
|
import Text.PrettyPrint.HughesPJ
|
||||||
|
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.BitVectors.AlgReals
|
|
||||||
import Data.SBV.BitVectors.PrettyNum (shex, showCFloat, showCDouble)
|
import Data.SBV.BitVectors.PrettyNum (shex, showCFloat, showCDouble)
|
||||||
import Data.SBV.Compilers.CodeGen
|
import Data.SBV.Compilers.CodeGen
|
||||||
|
|
||||||
@ -162,6 +161,7 @@ showCType = show . kindOf
|
|||||||
-- | The printf specifier for the type
|
-- | The printf specifier for the type
|
||||||
specifier :: CgConfig -> SW -> Doc
|
specifier :: CgConfig -> SW -> Doc
|
||||||
specifier cfg sw = case kindOf sw of
|
specifier cfg sw = case kindOf sw of
|
||||||
|
KBool -> spec (False, 1)
|
||||||
KBounded b i -> spec (b, i)
|
KBounded b i -> spec (b, i)
|
||||||
KUnbounded -> spec (True, fromJust (cgInteger cfg))
|
KUnbounded -> spec (True, fromJust (cgInteger cfg))
|
||||||
KReal -> specF (fromJust (cgReal cfg))
|
KReal -> specF (fromJust (cgReal cfg))
|
||||||
@ -443,7 +443,7 @@ genCProg cfg fn proto (Result kindInfo _tvals cgs ins preConsts tbls arrs _ _ (S
|
|||||||
len (KFloat{}) = 6 -- SFloat
|
len (KFloat{}) = 6 -- SFloat
|
||||||
len (KDouble{}) = 7 -- SDouble
|
len (KDouble{}) = 7 -- SDouble
|
||||||
len (KUnbounded{}) = 8
|
len (KUnbounded{}) = 8
|
||||||
len (KBounded False 1) = 5 -- SBool
|
len KBool = 5 -- SBool
|
||||||
len (KBounded False n) = 5 + length (show n) -- SWordN
|
len (KBounded False n) = 5 + length (show n) -- SWordN
|
||||||
len (KBounded True n) = 4 + length (show n) -- SIntN
|
len (KBounded True n) = 4 + length (show n) -- SIntN
|
||||||
len (KUninterpreted s) = die $ "Uninterpreted sort: " ++ s
|
len (KUninterpreted s) = die $ "Uninterpreted sort: " ++ s
|
||||||
@ -499,8 +499,8 @@ ppExpr cfg consts (SBVApp op opArgs) = p op (map (showSW cfg consts) opArgs)
|
|||||||
p (Shr i) [a] = shift False i a (head opArgs)
|
p (Shr i) [a] = shift False i a (head opArgs)
|
||||||
p Not [a] = case kindOf (head opArgs) of
|
p Not [a] = case kindOf (head opArgs) of
|
||||||
-- be careful about booleans, bitwise complement is not correct for them!
|
-- be careful about booleans, bitwise complement is not correct for them!
|
||||||
KBounded False 1 -> text "!" <> a
|
KBool -> text "!" <> a
|
||||||
_ -> text "~" <> a
|
_ -> text "~" <> a
|
||||||
p Ite [a, b, c] = a <+> text "?" <+> b <+> text ":" <+> c
|
p Ite [a, b, c] = a <+> text "?" <+> b <+> text ":" <+> c
|
||||||
p (LkUp (t, k, _, len) ind def) []
|
p (LkUp (t, k, _, len) ind def) []
|
||||||
| not rtc = lkUp -- ignore run-time-checks per user request
|
| not rtc = lkUp -- ignore run-time-checks per user request
|
||||||
@ -517,6 +517,7 @@ ppExpr cfg consts (SBVApp op opArgs) = p op (map (showSW cfg consts) opArgs)
|
|||||||
canOverflow True sz = (2::Integer)^(sz-1)-1 >= fromIntegral len
|
canOverflow True sz = (2::Integer)^(sz-1)-1 >= fromIntegral len
|
||||||
canOverflow False sz = (2::Integer)^sz -1 >= fromIntegral len
|
canOverflow False sz = (2::Integer)^sz -1 >= fromIntegral len
|
||||||
(needsCheckL, needsCheckR) = case k of
|
(needsCheckL, needsCheckR) = case k of
|
||||||
|
KBool -> (False, canOverflow False (1::Int))
|
||||||
KBounded sg sz -> (sg, canOverflow sg sz)
|
KBounded sg sz -> (sg, canOverflow sg sz)
|
||||||
KReal -> die "array index with real value"
|
KReal -> die "array index with real value"
|
||||||
KFloat -> die "array index with float value"
|
KFloat -> die "array index with float value"
|
||||||
|
@ -16,6 +16,7 @@ module Data.SBV.Compilers.CodeGen where
|
|||||||
|
|
||||||
import Control.Monad.Trans
|
import Control.Monad.Trans
|
||||||
import Control.Monad.State.Lazy
|
import Control.Monad.State.Lazy
|
||||||
|
import Control.Applicative (Applicative)
|
||||||
import Data.Char (toLower, isSpace)
|
import Data.Char (toLower, isSpace)
|
||||||
import Data.List (nub, isPrefixOf, intercalate, (\\))
|
import Data.List (nub, isPrefixOf, intercalate, (\\))
|
||||||
import System.Directory (createDirectory, doesDirectoryExist, doesFileExist)
|
import System.Directory (createDirectory, doesDirectoryExist, doesFileExist)
|
||||||
@ -75,7 +76,7 @@ initCgState = CgState {
|
|||||||
-- reference parameters (for returning composite values in languages such as C),
|
-- reference parameters (for returning composite values in languages such as C),
|
||||||
-- and return values.
|
-- and return values.
|
||||||
newtype SBVCodeGen a = SBVCodeGen (StateT CgState Symbolic a)
|
newtype SBVCodeGen a = SBVCodeGen (StateT CgState Symbolic a)
|
||||||
deriving (Monad, MonadIO, MonadState CgState)
|
deriving (Applicative, Functor, Monad, MonadIO, MonadState CgState)
|
||||||
|
|
||||||
-- | Reach into symbolic monad from code-generation
|
-- | Reach into symbolic monad from code-generation
|
||||||
liftSymbolic :: Symbolic a -> SBVCodeGen a
|
liftSymbolic :: Symbolic a -> SBVCodeGen a
|
||||||
@ -109,7 +110,7 @@ data CgSRealType = CgFloat -- ^ @float@
|
|||||||
| CgLongDouble -- ^ @long double@
|
| CgLongDouble -- ^ @long double@
|
||||||
deriving Eq
|
deriving Eq
|
||||||
|
|
||||||
-- As they would be used in a C program
|
-- | 'Show' instance for 'cgSRealType' displays values as they would be used in a C program
|
||||||
instance Show CgSRealType where
|
instance Show CgSRealType where
|
||||||
show CgFloat = "float"
|
show CgFloat = "float"
|
||||||
show CgDouble = "double"
|
show CgDouble = "double"
|
||||||
@ -217,6 +218,7 @@ isCgMakefile :: CgPgmKind -> Bool
|
|||||||
isCgMakefile CgMakefile{} = True
|
isCgMakefile CgMakefile{} = True
|
||||||
isCgMakefile _ = False
|
isCgMakefile _ = False
|
||||||
|
|
||||||
|
-- | A simple way to print bundles, mostly for debugging purposes.
|
||||||
instance Show CgPgmBundle where
|
instance Show CgPgmBundle where
|
||||||
show (CgPgmBundle _ fs) = intercalate "\n" $ map showFile fs
|
show (CgPgmBundle _ fs) = intercalate "\n" $ map showFile fs
|
||||||
where showFile :: (FilePath, (CgPgmKind, [Doc])) -> String
|
where showFile :: (FilePath, (CgPgmKind, [Doc])) -> String
|
||||||
|
@ -15,16 +15,13 @@ module Data.SBV.Internals (
|
|||||||
-- * Running symbolic programs /manually/
|
-- * Running symbolic programs /manually/
|
||||||
Result, SBVRunMode(..), runSymbolic, runSymbolic'
|
Result, SBVRunMode(..), runSymbolic, runSymbolic'
|
||||||
-- * Other internal structures useful for low-level programming
|
-- * Other internal structures useful for low-level programming
|
||||||
, SBV(..), slet, CW, mkConstCW, genVar, genVar_
|
, SBV(..), slet, CW(..), Kind(..), CWVal(..), AlgReal(..), mkConstCW, genVar, genVar_
|
||||||
, mkSymSBVWithRandom
|
|
||||||
, Quantifier(..)
|
|
||||||
, liftQRem, liftDMod
|
, liftQRem, liftDMod
|
||||||
-- * Compilation to C
|
-- * Compilation to C
|
||||||
, compileToC', compileToCLib', CgPgmBundle(..), CgPgmKind(..)
|
, compileToC', compileToCLib', CgPgmBundle(..), CgPgmKind(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.SBV.BitVectors.Data (Result, SBVRunMode(..), runSymbolic, runSymbolic'
|
import Data.SBV.BitVectors.Data (Result, SBVRunMode(..), runSymbolic, runSymbolic', SBV(..), CW(..), Kind(..), CWVal(..), AlgReal(..), mkConstCW)
|
||||||
, SBV(..), CW, mkConstCW, Quantifier(..), mkSymSBVWithRandom)
|
|
||||||
import Data.SBV.BitVectors.Model (genVar, genVar_, slet, liftQRem, liftDMod)
|
import Data.SBV.BitVectors.Model (genVar, genVar_, slet, liftQRem, liftDMod)
|
||||||
import Data.SBV.Compilers.C (compileToC', compileToCLib')
|
import Data.SBV.Compilers.C (compileToC', compileToCLib')
|
||||||
import Data.SBV.Compilers.CodeGen (CgPgmBundle(..), CgPgmKind(..))
|
import Data.SBV.Compilers.CodeGen (CgPgmBundle(..), CgPgmKind(..))
|
||||||
|
@ -26,10 +26,10 @@ import Data.SBV.SMT.SMTLib
|
|||||||
|
|
||||||
-- | The description of the Boolector SMT solver
|
-- | The description of the Boolector SMT solver
|
||||||
-- The default executable is @\"boolector\"@, which must be in your path. You can use the @SBV_BOOLECTOR@ environment variable to point to the executable on your system.
|
-- The default executable is @\"boolector\"@, which must be in your path. You can use the @SBV_BOOLECTOR@ environment variable to point to the executable on your system.
|
||||||
-- The default options are @\"--lang smt\"@. You can use the @SBV_BOOLECTOR_OPTIONS@ environment variable to override the options.
|
-- The default options are @\"-m --smt2\"@. You can use the @SBV_BOOLECTOR_OPTIONS@ environment variable to override the options.
|
||||||
boolector :: SMTSolver
|
boolector :: SMTSolver
|
||||||
boolector = SMTSolver {
|
boolector = SMTSolver {
|
||||||
name = "boolector"
|
name = Boolector
|
||||||
, executable = "boolector"
|
, executable = "boolector"
|
||||||
, options = ["-m", "--smt2"]
|
, options = ["-m", "--smt2"]
|
||||||
, engine = \cfg _isSat qinps modelMap _skolemMap pgm -> do
|
, engine = \cfg _isSat qinps modelMap _skolemMap pgm -> do
|
||||||
|
@ -30,7 +30,7 @@ import Data.SBV.SMT.SMTLib
|
|||||||
-- The default options are @\"--lang smt\"@. You can use the @SBV_CVC4_OPTIONS@ environment variable to override the options.
|
-- The default options are @\"--lang smt\"@. You can use the @SBV_CVC4_OPTIONS@ environment variable to override the options.
|
||||||
cvc4 :: SMTSolver
|
cvc4 :: SMTSolver
|
||||||
cvc4 = SMTSolver {
|
cvc4 = SMTSolver {
|
||||||
name = "cvc4"
|
name = CVC4
|
||||||
, executable = "cvc4"
|
, executable = "cvc4"
|
||||||
, options = ["--lang", "smt"]
|
, options = ["--lang", "smt"]
|
||||||
, engine = \cfg isSat qinps modelMap skolemMap pgm -> do
|
, engine = \cfg isSat qinps modelMap skolemMap pgm -> do
|
||||||
@ -57,7 +57,7 @@ cvc4 = SMTSolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
where zero :: Kind -> String
|
where zero :: Kind -> String
|
||||||
zero (KBounded False 1) = "#b0"
|
zero KBool = "false"
|
||||||
zero (KBounded _ sz) = "#x" ++ replicate (sz `div` 4) '0'
|
zero (KBounded _ sz) = "#x" ++ replicate (sz `div` 4) '0'
|
||||||
zero KUnbounded = "0"
|
zero KUnbounded = "0"
|
||||||
zero KReal = "0.0"
|
zero KReal = "0.0"
|
||||||
|
92
sbv/Data/SBV/Provers/MathSAT.hs
Normal file
92
sbv/Data/SBV/Provers/MathSAT.hs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : Data.SBV.Provers.MathSAT
|
||||||
|
-- Copyright : (c) Levent Erkok
|
||||||
|
-- License : BSD3
|
||||||
|
-- Maintainer : erkokl@gmail.com
|
||||||
|
-- Stability : experimental
|
||||||
|
--
|
||||||
|
-- The connection to the MathSAT SMT solver
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
|
||||||
|
module Data.SBV.Provers.MathSAT(mathSAT) where
|
||||||
|
|
||||||
|
import qualified Control.Exception as C
|
||||||
|
|
||||||
|
import Data.Function (on)
|
||||||
|
import Data.List (sortBy, intercalate)
|
||||||
|
import System.Environment (getEnv)
|
||||||
|
|
||||||
|
import Data.SBV.BitVectors.Data
|
||||||
|
import Data.SBV.SMT.SMT
|
||||||
|
import Data.SBV.SMT.SMTLib
|
||||||
|
|
||||||
|
-- | The description of the MathSAT SMT solver
|
||||||
|
-- The default executable is @\"mathsat\"@, which must be in your path. You can use the @SBV_MATHSAT@ environment variable to point to the executable on your system.
|
||||||
|
-- The default options are @\"-input=smt2\"@. You can use the @SBV_MATHSAT_OPTIONS@ environment variable to override the options.
|
||||||
|
mathSAT :: SMTSolver
|
||||||
|
mathSAT = SMTSolver {
|
||||||
|
name = MathSAT
|
||||||
|
, executable = "mathsat"
|
||||||
|
, options = ["-input=smt2"]
|
||||||
|
, engine = \cfg _isSat qinps modelMap skolemMap pgm -> do
|
||||||
|
execName <- getEnv "SBV_MATHSAT" `C.catch` (\(_ :: C.SomeException) -> return (executable (solver cfg)))
|
||||||
|
execOpts <- (words `fmap` getEnv "SBV_MATHSAT_OPTIONS") `C.catch` (\(_ :: C.SomeException) -> return (options (solver cfg)))
|
||||||
|
let cfg' = cfg { solver = (solver cfg) {executable = execName, options = addTimeOut (timeOut cfg) execOpts}
|
||||||
|
}
|
||||||
|
tweaks = case solverTweaks cfg' of
|
||||||
|
[] -> ""
|
||||||
|
ts -> unlines $ "; --- user given solver tweaks ---" : ts ++ ["; --- end of user given tweaks ---"]
|
||||||
|
script = SMTScript {scriptBody = tweaks ++ pgm, scriptModel = Just (cont skolemMap)}
|
||||||
|
standardSolver cfg' script id (ProofError cfg') (interpretSolverOutput cfg' (extractMap (map snd qinps) modelMap))
|
||||||
|
, xformExitCode = id
|
||||||
|
, capabilities = SolverCapabilities {
|
||||||
|
capSolverName = "MathSAT"
|
||||||
|
, mbDefaultLogic = Nothing
|
||||||
|
, supportsMacros = False
|
||||||
|
, supportsProduceModels = True
|
||||||
|
, supportsQuantifiers = True
|
||||||
|
, supportsUninterpretedSorts = True
|
||||||
|
, supportsUnboundedInts = True
|
||||||
|
, supportsReals = True
|
||||||
|
, supportsFloats = False
|
||||||
|
, supportsDoubles = False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
where zero :: Kind -> String
|
||||||
|
zero KBool = "false"
|
||||||
|
zero (KBounded _ sz) = "#x" ++ replicate (sz `div` 4) '0'
|
||||||
|
zero KUnbounded = "0"
|
||||||
|
zero KReal = "0.0"
|
||||||
|
zero KFloat = error "SBV.MathSAT.zero: Unexpected sort SFloat"
|
||||||
|
zero KDouble = error "SBV.MathSAT.zero: Unexpected sort SDouble"
|
||||||
|
zero (KUninterpreted s) = error $ "SBV.MathSAT.zero: Unexpected uninterpreted sort: " ++ s
|
||||||
|
cont skolemMap = intercalate "\n" $ concatMap extract skolemMap
|
||||||
|
where -- In the skolemMap:
|
||||||
|
-- * Left's are universals: i.e., the model should be true for
|
||||||
|
-- any of these. So, we simply "echo 0" for these values.
|
||||||
|
-- * Right's are existentials. If there are no dependencies (empty list), then we can
|
||||||
|
-- simply use get-value to extract it's value. Otherwise, we have to apply it to
|
||||||
|
-- an appropriate number of 0's to get the final value.
|
||||||
|
extract (Left s) = ["(echo \"((" ++ show s ++ " " ++ zero (kindOf s) ++ "))\")"]
|
||||||
|
extract (Right (s, [])) = ["(get-value (" ++ show s ++ "))"]
|
||||||
|
extract (Right (s, ss)) = let g = "(get-value ((" ++ show s ++ concat [' ' : zero (kindOf a) | a <- ss] ++ ")))" in [g]
|
||||||
|
addTimeOut Nothing o = o
|
||||||
|
addTimeOut (Just _) _ = error "MathSAT: Timeout values are not supported by MathSat"
|
||||||
|
|
||||||
|
extractMap :: [NamedSymVar] -> [(String, UnintKind)] -> [String] -> SMTModel
|
||||||
|
extractMap inps _modelMap solverLines =
|
||||||
|
SMTModel { modelAssocs = map snd $ sortByNodeId $ concatMap (interpretSolverModelLine inps . cvt) solverLines
|
||||||
|
, modelUninterps = []
|
||||||
|
, modelArrays = []
|
||||||
|
}
|
||||||
|
where sortByNodeId :: [(Int, a)] -> [(Int, a)]
|
||||||
|
sortByNodeId = sortBy (compare `on` fst)
|
||||||
|
cvt :: String -> String
|
||||||
|
cvt s = case words s of
|
||||||
|
[var, val] -> "((" ++ var ++ " #b" ++ map tr val ++ "))"
|
||||||
|
_ -> s -- good-luck..
|
||||||
|
where tr 'x' = '0'
|
||||||
|
tr x = x
|
@ -18,55 +18,53 @@ module Data.SBV.Provers.Prover (
|
|||||||
SMTSolver(..), SMTConfig(..), Predicate, Provable(..)
|
SMTSolver(..), SMTConfig(..), Predicate, Provable(..)
|
||||||
, ThmResult(..), SatResult(..), AllSatResult(..), SMTResult(..)
|
, ThmResult(..), SatResult(..), AllSatResult(..), SMTResult(..)
|
||||||
, isSatisfiable, isSatisfiableWith, isTheorem, isTheoremWith
|
, isSatisfiable, isSatisfiableWith, isTheorem, isTheoremWith
|
||||||
, Equality(..)
|
|
||||||
, prove, proveWith
|
, prove, proveWith
|
||||||
, sat, satWith
|
, sat, satWith
|
||||||
, allSat, allSatWith
|
, allSat, allSatWith
|
||||||
, isVacuous, isVacuousWith
|
, isVacuous, isVacuousWith
|
||||||
, solve
|
|
||||||
, SatModel(..), Modelable(..), displayModels, extractModels
|
, SatModel(..), Modelable(..), displayModels, extractModels
|
||||||
, boolector, cvc4, yices, z3, defaultSMTCfg
|
, getModelDictionaries, getModelValues, getModelUninterpretedValues
|
||||||
|
, boolector, cvc4, yices, z3, mathSAT, defaultSMTCfg
|
||||||
, compileToSMTLib, generateSMTBenchmarks
|
, compileToSMTLib, generateSMTBenchmarks
|
||||||
, sbvCheckSolverInstallation
|
, isSBranchFeasibleInState
|
||||||
, internalSatWith, internalIsSatisfiableWith, internalIsSatisfiable
|
, internalSatWith, internalIsSatisfiableWith, internalIsSatisfiable
|
||||||
, internalProveWith, internalIsTheoremWith, internalIsTheorem
|
, internalProveWith, internalIsTheoremWith, internalIsTheorem
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Control.Exception as E
|
import Control.Monad (when, unless)
|
||||||
|
import Control.Monad.Trans (liftIO)
|
||||||
import Control.Concurrent (forkIO, newChan, writeChan, getChanContents)
|
import Data.List (intercalate)
|
||||||
import Control.Monad (when, unless, void)
|
import Data.Maybe (mapMaybe, fromMaybe)
|
||||||
import Control.Monad.Trans(liftIO)
|
import System.FilePath (addExtension, splitExtension)
|
||||||
import Data.List (intercalate)
|
import System.Time (getClockTime)
|
||||||
import Data.Maybe (fromJust, isJust, mapMaybe)
|
import System.IO.Unsafe (unsafeInterleaveIO)
|
||||||
import System.FilePath (addExtension)
|
|
||||||
import System.Time (getClockTime)
|
|
||||||
|
|
||||||
import qualified Data.Set as Set (Set, toList)
|
import qualified Data.Set as Set (Set, toList)
|
||||||
|
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.BitVectors.Model
|
|
||||||
import Data.SBV.SMT.SMT
|
import Data.SBV.SMT.SMT
|
||||||
import Data.SBV.SMT.SMTLib
|
import Data.SBV.SMT.SMTLib
|
||||||
import qualified Data.SBV.Provers.Boolector as Boolector
|
import qualified Data.SBV.Provers.Boolector as Boolector
|
||||||
import qualified Data.SBV.Provers.CVC4 as CVC4
|
import qualified Data.SBV.Provers.CVC4 as CVC4
|
||||||
import qualified Data.SBV.Provers.Yices as Yices
|
import qualified Data.SBV.Provers.Yices as Yices
|
||||||
import qualified Data.SBV.Provers.Z3 as Z3
|
import qualified Data.SBV.Provers.Z3 as Z3
|
||||||
|
import qualified Data.SBV.Provers.MathSAT as MathSAT
|
||||||
import Data.SBV.Utils.TDiff
|
import Data.SBV.Utils.TDiff
|
||||||
import Data.SBV.Utils.Boolean
|
|
||||||
|
|
||||||
mkConfig :: SMTSolver -> Bool -> [String] -> SMTConfig
|
mkConfig :: SMTSolver -> Bool -> [String] -> SMTConfig
|
||||||
mkConfig s isSMTLib2 tweaks = SMTConfig { verbose = False
|
mkConfig s isSMTLib2 tweaks = SMTConfig { verbose = False
|
||||||
, timing = False
|
, timing = False
|
||||||
, timeOut = Nothing
|
, sBranchTimeOut = Nothing
|
||||||
, printBase = 10
|
, timeOut = Nothing
|
||||||
, printRealPrec = 16
|
, printBase = 10
|
||||||
, smtFile = Nothing
|
, printRealPrec = 16
|
||||||
, solver = s
|
, smtFile = Nothing
|
||||||
, solverTweaks = tweaks
|
, solver = s
|
||||||
, useSMTLib2 = isSMTLib2
|
, solverTweaks = tweaks
|
||||||
, satCmd = "(check-sat)"
|
, useSMTLib2 = isSMTLib2
|
||||||
, roundingMode = RoundNearestTiesToEven
|
, satCmd = "(check-sat)"
|
||||||
|
, roundingMode = RoundNearestTiesToEven
|
||||||
|
, useLogic = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | Default configuration for the Boolector SMT solver
|
-- | Default configuration for the Boolector SMT solver
|
||||||
@ -83,8 +81,11 @@ yices = mkConfig Yices.yices False []
|
|||||||
|
|
||||||
-- | Default configuration for the Z3 SMT solver
|
-- | Default configuration for the Z3 SMT solver
|
||||||
z3 :: SMTConfig
|
z3 :: SMTConfig
|
||||||
--z3 = mkConfig Z3.z3 True ["(set-option :smt.mbqi true) ; use model based quantifier instantiation"]
|
z3 = mkConfig Z3.z3 True ["(set-option :smt.mbqi true) ; use model based quantifier instantiation"]
|
||||||
z3 = mkConfig Z3.z3 True []
|
|
||||||
|
-- | Default configuration for the MathSAT SMT solver
|
||||||
|
mathSAT :: SMTConfig
|
||||||
|
mathSAT = mkConfig MathSAT.mathSAT True []
|
||||||
|
|
||||||
-- | The default solver used by SBV. This is currently set to z3.
|
-- | The default solver used by SBV. This is currently set to z3.
|
||||||
defaultSMTCfg :: SMTConfig
|
defaultSMTCfg :: SMTConfig
|
||||||
@ -233,16 +234,6 @@ prove = proveWith defaultSMTCfg
|
|||||||
sat :: Provable a => a -> IO SatResult
|
sat :: Provable a => a -> IO SatResult
|
||||||
sat = satWith defaultSMTCfg
|
sat = satWith defaultSMTCfg
|
||||||
|
|
||||||
-- | Form the symbolic conjunction of a given list of boolean conditions. Useful in expressing
|
|
||||||
-- problems with constraints, like the following:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- do [x, y, z] <- sIntegers [\"x\", \"y\", \"z\"]
|
|
||||||
-- solve [x .> 5, y + z .< x]
|
|
||||||
-- @
|
|
||||||
solve :: [SBool] -> Symbolic SBool
|
|
||||||
solve = return . bAnd
|
|
||||||
|
|
||||||
-- | Return all satisfying assignments for a predicate, equivalent to @'allSatWith' 'defaultSMTCfg'@.
|
-- | Return all satisfying assignments for a predicate, equivalent to @'allSatWith' 'defaultSMTCfg'@.
|
||||||
-- Satisfying assignments are constructed lazily, so they will be available as returned by the solver
|
-- Satisfying assignments are constructed lazily, so they will be available as returned by the solver
|
||||||
-- and on demand.
|
-- and on demand.
|
||||||
@ -254,40 +245,8 @@ solve = return . bAnd
|
|||||||
allSat :: Provable a => a -> IO AllSatResult
|
allSat :: Provable a => a -> IO AllSatResult
|
||||||
allSat = allSatWith defaultSMTCfg
|
allSat = allSatWith defaultSMTCfg
|
||||||
|
|
||||||
-- | Check if the given constraints are satisfiable, equivalent to @'isVacuousWith' 'defaultSMTCfg'@. This
|
-- | Check if the given constraints are satisfiable, equivalent to @'isVacuousWith' 'defaultSMTCfg'@.
|
||||||
-- call can be used to ensure that the specified constraints (via 'constrain') are satisfiable, i.e., that
|
-- See the function 'constrain' for an example use of 'isVacuous'.
|
||||||
-- the proof involving these constraints is not passing vacuously. Here is an example. Consider the following
|
|
||||||
-- predicate:
|
|
||||||
--
|
|
||||||
-- >>> let pred = do { x <- forall "x"; constrain $ x .< x; return $ x .>= (5 :: SWord8) }
|
|
||||||
--
|
|
||||||
-- This predicate asserts that all 8-bit values are larger than 5, subject to the constraint that the
|
|
||||||
-- values considered satisfy @x .< x@, i.e., they are less than themselves. Since there are no values that
|
|
||||||
-- satisfy this constraint, the proof will pass vacuously:
|
|
||||||
--
|
|
||||||
-- >>> prove pred
|
|
||||||
-- Q.E.D.
|
|
||||||
--
|
|
||||||
-- We can use 'isVacuous' to make sure to see that the pass was vacuous:
|
|
||||||
--
|
|
||||||
-- >>> isVacuous pred
|
|
||||||
-- True
|
|
||||||
--
|
|
||||||
-- While the above example is trivial, things can get complicated if there are multiple constraints with
|
|
||||||
-- non-straightforward relations; so if constraints are used one should make sure to check the predicate
|
|
||||||
-- is not vacuously true. Here's an example that is not vacuous:
|
|
||||||
--
|
|
||||||
-- >>> let pred' = do { x <- forall "x"; constrain $ x .> 6; return $ x .>= (5 :: SWord8) }
|
|
||||||
--
|
|
||||||
-- This time the proof passes as expected:
|
|
||||||
--
|
|
||||||
-- >>> prove pred'
|
|
||||||
-- Q.E.D.
|
|
||||||
--
|
|
||||||
-- And the proof is not vacuous:
|
|
||||||
--
|
|
||||||
-- >>> isVacuous pred'
|
|
||||||
-- False
|
|
||||||
isVacuous :: Provable a => a -> IO Bool
|
isVacuous :: Provable a => a -> IO Bool
|
||||||
isVacuous = isVacuousWith defaultSMTCfg
|
isVacuous = isVacuousWith defaultSMTCfg
|
||||||
|
|
||||||
@ -414,7 +373,7 @@ internalSatWith config b = do
|
|||||||
-- | Determine if the constraints are vacuous using the given SMT-solver
|
-- | Determine if the constraints are vacuous using the given SMT-solver
|
||||||
isVacuousWith :: Provable a => SMTConfig -> a -> IO Bool
|
isVacuousWith :: Provable a => SMTConfig -> a -> IO Bool
|
||||||
isVacuousWith config a = do
|
isVacuousWith config a = do
|
||||||
Result ki tr uic is cs ts as uis ax asgn cstr _ <- runSymbolic True $ forAll_ a >>= output
|
Result ki tr uic is cs ts as uis ax asgn cstr _ <- runSymbolic (True, Just config) $ forAll_ a >>= output
|
||||||
case cstr of
|
case cstr of
|
||||||
[] -> return False -- no constraints, no need to check
|
[] -> return False -- no constraints, no need to check
|
||||||
_ -> do let is' = [(EX, i) | (_, i) <- is] -- map all quantifiers to "exists" for the constraint check
|
_ -> do let is' = [(EX, i) | (_, i) <- is] -- map all quantifiers to "exists" for the constraint check
|
||||||
@ -435,45 +394,40 @@ allSatWith config p = do
|
|||||||
msg "Checking Satisfiability, all solutions.."
|
msg "Checking Satisfiability, all solutions.."
|
||||||
sbvPgm@(qinps, _, _, ki, _) <- simulate converter config True [] p
|
sbvPgm@(qinps, _, _, ki, _) <- simulate converter config True [] p
|
||||||
let usorts = [s | KUninterpreted s <- Set.toList ki]
|
let usorts = [s | KUninterpreted s <- Set.toList ki]
|
||||||
unless (null usorts) $ error $ "SBV.allSat: All-sat calls are not supported in the presence of uninterpreted sorts: " ++ unwords usorts
|
unless (null usorts) $ msg $ "SBV.allSat: Uninterpreted sorts present: " ++ unwords usorts
|
||||||
++ "\n Only 'sat' and 'prove' calls are available when uninterpreted sorts are used."
|
++ "\n SBV will use equivalence classes to generate all-satisfying instances."
|
||||||
resChan <- newChan
|
results <- unsafeInterleaveIO $ go sbvPgm (1::Int) []
|
||||||
let add = writeChan resChan . Just
|
|
||||||
stop = writeChan resChan Nothing
|
|
||||||
final r = add r >> stop
|
|
||||||
die m = final (ProofError config [m])
|
|
||||||
-- only fork if non-verbose.. otherwise stdout gets garbled
|
|
||||||
fork io = if verbose config then io else void (forkIO io)
|
|
||||||
fork $ E.catch (go sbvPgm add stop final (1::Int) [])
|
|
||||||
(\e -> die (show (e::E.SomeException)))
|
|
||||||
results <- getChanContents resChan
|
|
||||||
-- See if there are any existentials below any universals
|
-- See if there are any existentials below any universals
|
||||||
-- If such is the case, then the solutions are unique upto prefix existentials
|
-- If such is the case, then the solutions are unique upto prefix existentials
|
||||||
let w = ALL `elem` map fst qinps
|
let w = ALL `elem` map fst qinps
|
||||||
return $ AllSatResult (w, map fromJust (takeWhile isJust results))
|
return $ AllSatResult (w, results)
|
||||||
where msg = when (verbose config) . putStrLn . ("** " ++)
|
where msg = when (verbose config) . putStrLn . ("** " ++)
|
||||||
go sbvPgm add stop final = loop
|
go sbvPgm = loop
|
||||||
where loop !n nonEqConsts = do
|
where loop !n nonEqConsts = do
|
||||||
curResult <- invoke nonEqConsts n sbvPgm
|
curResult <- invoke nonEqConsts n sbvPgm
|
||||||
case curResult of
|
case curResult of
|
||||||
Nothing -> stop
|
Nothing -> return []
|
||||||
Just (SatResult r) -> case r of
|
Just (SatResult r) -> let cont model = do rest <- unsafeInterleaveIO $ loop (n+1) (modelAssocs model : nonEqConsts)
|
||||||
Satisfiable _ (SMTModel [] _ _) -> final r
|
return (r : rest)
|
||||||
Unknown _ (SMTModel [] _ _) -> final r
|
in case r of
|
||||||
ProofError _ _ -> final r
|
Satisfiable _ (SMTModel [] _ _) -> return [r]
|
||||||
TimeOut _ -> stop
|
Unknown _ (SMTModel [] _ _) -> return [r]
|
||||||
Unsatisfiable _ -> stop
|
ProofError _ _ -> return [r]
|
||||||
Satisfiable _ model -> add r >> loop (n+1) (modelAssocs model : nonEqConsts)
|
TimeOut _ -> return []
|
||||||
Unknown _ model -> add r >> loop (n+1) (modelAssocs model : nonEqConsts)
|
Unsatisfiable _ -> return []
|
||||||
|
Satisfiable _ model -> cont model
|
||||||
|
Unknown _ model -> cont model
|
||||||
invoke nonEqConsts n (qinps, modelMap, skolemMap, _, smtLibPgm) = do
|
invoke nonEqConsts n (qinps, modelMap, skolemMap, _, smtLibPgm) = do
|
||||||
msg $ "Looking for solution " ++ show n
|
msg $ "Looking for solution " ++ show n
|
||||||
case addNonEqConstraints (roundingMode config) qinps nonEqConsts smtLibPgm of
|
case addNonEqConstraints (roundingMode config) qinps nonEqConsts smtLibPgm of
|
||||||
Nothing -> -- no new constraints added, stop
|
Nothing -> -- no new constraints added, stop
|
||||||
return Nothing
|
return Nothing
|
||||||
Just finalPgm -> do msg $ "Generated SMTLib program:\n" ++ finalPgm
|
Just finalPgm -> do msg $ "Generated SMTLib program:\n" ++ finalPgm
|
||||||
smtAnswer <- engine (solver config) config True qinps modelMap skolemMap finalPgm
|
smtAnswer <- engine (solver config) (updateName (n-1) config) True qinps modelMap skolemMap finalPgm
|
||||||
msg "Done.."
|
msg "Done.."
|
||||||
return $ Just $ SatResult smtAnswer
|
return $ Just $ SatResult smtAnswer
|
||||||
|
updateName i cfg = cfg{smtFile = upd `fmap` smtFile cfg}
|
||||||
|
where upd nm = let (b, e) = splitExtension nm in b ++ "_allSat_" ++ show i ++ e
|
||||||
|
|
||||||
type SMTProblem = ( [(Quantifier, NamedSymVar)] -- inputs
|
type SMTProblem = ( [(Quantifier, NamedSymVar)] -- inputs
|
||||||
, [(String, UnintKind)] -- model-map
|
, [(String, UnintKind)] -- model-map
|
||||||
@ -497,7 +451,7 @@ simulate converter config isSat comments predicate = do
|
|||||||
let msg = when (verbose config) . putStrLn . ("** " ++)
|
let msg = when (verbose config) . putStrLn . ("** " ++)
|
||||||
isTiming = timing config
|
isTiming = timing config
|
||||||
msg "Starting symbolic simulation.."
|
msg "Starting symbolic simulation.."
|
||||||
res <- timeIf isTiming "problem construction" $ runSymbolic isSat $ (if isSat then forSome_ else forAll_) predicate >>= output
|
res <- timeIf isTiming "problem construction" $ runSymbolic (isSat, Just config) $ (if isSat then forSome_ else forAll_) predicate >>= output
|
||||||
msg $ "Generated symbolic trace:\n" ++ show res
|
msg $ "Generated symbolic trace:\n" ++ show res
|
||||||
msg "Translating to SMT-Lib.."
|
msg "Translating to SMT-Lib.."
|
||||||
runProofOn converter config isSat comments res
|
runProofOn converter config isSat comments res
|
||||||
@ -507,7 +461,7 @@ runProofOn converter config isSat comments res =
|
|||||||
let isTiming = timing config
|
let isTiming = timing config
|
||||||
solverCaps = capabilities (solver config)
|
solverCaps = capabilities (solver config)
|
||||||
in case res of
|
in case res of
|
||||||
Result ki _qcInfo _codeSegs is consts tbls arrs uis axs pgm cstrs [o@(SW (KBounded False 1) _)] ->
|
Result ki _qcInfo _codeSegs is consts tbls arrs uis axs pgm cstrs [o@(SW KBool _)] ->
|
||||||
timeIf isTiming "translation"
|
timeIf isTiming "translation"
|
||||||
$ let uiMap = mapMaybe arrayUIKind arrs ++ map unintFnUIKind uis
|
$ let uiMap = mapMaybe arrayUIKind arrs ++ map unintFnUIKind uis
|
||||||
skolemMap = skolemize (if isSat then is else map flipQ is)
|
skolemMap = skolemize (if isSat then is else map flipQ is)
|
||||||
@ -518,7 +472,7 @@ runProofOn converter config isSat comments res =
|
|||||||
where go [] (_, sofar) = reverse sofar
|
where go [] (_, sofar) = reverse sofar
|
||||||
go ((ALL, (v, _)):rest) (us, sofar) = go rest (v:us, Left v : sofar)
|
go ((ALL, (v, _)):rest) (us, sofar) = go rest (v:us, Left v : sofar)
|
||||||
go ((EX, (v, _)):rest) (us, sofar) = go rest (us, Right (v, reverse us) : sofar)
|
go ((EX, (v, _)):rest) (us, sofar) = go rest (us, Right (v, reverse us) : sofar)
|
||||||
in return (is, uiMap, skolemMap, ki, converter (roundingMode config) solverCaps ki isSat comments is skolemMap consts tbls arrs uis axs pgm cstrs o)
|
in return (is, uiMap, skolemMap, ki, converter (roundingMode config) (useLogic config) solverCaps ki isSat comments is skolemMap consts tbls arrs uis axs pgm cstrs o)
|
||||||
Result _kindInfo _qcInfo _codeSegs _is _consts _tbls _arrs _uis _axs _pgm _cstrs os -> case length os of
|
Result _kindInfo _qcInfo _codeSegs _is _consts _tbls _arrs _uis _axs _pgm _cstrs os -> case length os of
|
||||||
0 -> error $ "Impossible happened, unexpected non-outputting result\n" ++ show res
|
0 -> error $ "Impossible happened, unexpected non-outputting result\n" ++ show res
|
||||||
1 -> error $ "Impossible happened, non-boolean output in " ++ show os
|
1 -> error $ "Impossible happened, non-boolean output in " ++ show os
|
||||||
@ -527,56 +481,23 @@ runProofOn converter config isSat comments res =
|
|||||||
++ "\nDetected while generating the trace:\n" ++ show res
|
++ "\nDetected while generating the trace:\n" ++ show res
|
||||||
++ "\n*** Check calls to \"output\", they are typically not needed!"
|
++ "\n*** Check calls to \"output\", they are typically not needed!"
|
||||||
|
|
||||||
-- | Check whether the given solver is installed and is ready to go. This call does a
|
-- | Check if a branch condition is feasible in the current state
|
||||||
-- simple call to the solver to ensure all is well.
|
isSBranchFeasibleInState :: State -> String -> SBool -> IO Bool
|
||||||
sbvCheckSolverInstallation :: SMTConfig -> IO Bool
|
isSBranchFeasibleInState st branch cond = do
|
||||||
sbvCheckSolverInstallation cfg = do ThmResult r <- proveWith cfg $ \x -> (x+x) .== ((x*2) :: SWord8)
|
let cfg = let pickedConfig = fromMaybe defaultSMTCfg (getSBranchRunConfig st)
|
||||||
case r of
|
in pickedConfig { timeOut = sBranchTimeOut pickedConfig }
|
||||||
Unsatisfiable _ -> return True
|
msg = when (verbose cfg) . putStrLn . ("** " ++)
|
||||||
_ -> return False
|
sw <- sbvToSW st cond
|
||||||
|
() <- forceSWArg sw
|
||||||
-- | Equality as a proof method. Allows for
|
Result ki tr uic is cs ts as uis ax asgn cstr _ <- liftIO $ extractSymbolicSimulationState st
|
||||||
-- very concise construction of equivalence proofs, which is very typical in
|
let -- Construct the corresponding sat-checker for the branch. Note that we need to
|
||||||
-- bit-precise proofs.
|
-- forget about the quantifiers and just use an "exist", as we're looking for a
|
||||||
infix 4 ===
|
-- point-satisfiability check here; whatever the original program was.
|
||||||
class Equality a where
|
pgm = Result ki tr uic [(EX, n) | (_, n) <- is] cs ts as uis ax asgn cstr [sw]
|
||||||
(===) :: a -> a -> IO ThmResult
|
cvt = if useSMTLib2 cfg then toSMTLib2 else toSMTLib1
|
||||||
|
check <- runProofOn cvt cfg True [] pgm >>= callSolver True ("sBranch: Checking " ++ show branch ++ " feasibility") SatResult cfg
|
||||||
instance (SymWord a, EqSymbolic z) => Equality (SBV a -> z) where
|
res <- case check of
|
||||||
k === l = prove $ \a -> k a .== l a
|
SatResult (Unsatisfiable _) -> return False
|
||||||
|
_ -> return True -- No risks, even if it timed-our or anything else, we say it's feasible
|
||||||
instance (SymWord a, SymWord b, EqSymbolic z) => Equality (SBV a -> SBV b -> z) where
|
msg $ "sBranch: Conclusion: " ++ if res then "Feasible" else "Unfeasible"
|
||||||
k === l = prove $ \a b -> k a b .== l a b
|
return res
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, EqSymbolic z) => Equality ((SBV a, SBV b) -> z) where
|
|
||||||
k === l = prove $ \a b -> k (a, b) .== l (a, b)
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> z) where
|
|
||||||
k === l = prove $ \a b c -> k a b c .== l a b c
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c) -> z) where
|
|
||||||
k === l = prove $ \a b c -> k (a, b, c) .== l (a, b, c)
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> z) where
|
|
||||||
k === l = prove $ \a b c d -> k a b c d .== l a b c d
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d) -> z) where
|
|
||||||
k === l = prove $ \a b c d -> k (a, b, c, d) .== l (a, b, c, d)
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> SBV e -> z) where
|
|
||||||
k === l = prove $ \a b c d e -> k a b c d e .== l a b c d e
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d, SBV e) -> z) where
|
|
||||||
k === l = prove $ \a b c d e -> k (a, b, c, d, e) .== l (a, b, c, d, e)
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> SBV e -> SBV f -> z) where
|
|
||||||
k === l = prove $ \a b c d e f -> k a b c d e f .== l a b c d e f
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f) -> z) where
|
|
||||||
k === l = prove $ \a b c d e f -> k (a, b, c, d, e, f) .== l (a, b, c, d, e, f)
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SymWord g, EqSymbolic z) => Equality (SBV a -> SBV b -> SBV c -> SBV d -> SBV e -> SBV f -> SBV g -> z) where
|
|
||||||
k === l = prove $ \a b c d e f g -> k a b c d e f g .== l a b c d e f g
|
|
||||||
|
|
||||||
instance (SymWord a, SymWord b, SymWord c, SymWord d, SymWord e, SymWord f, SymWord g, EqSymbolic z) => Equality ((SBV a, SBV b, SBV c, SBV d, SBV e, SBV f, SBV g) -> z) where
|
|
||||||
k === l = prove $ \a b c d e f g -> k (a, b, c, d, e, f, g) .== l (a, b, c, d, e, f, g)
|
|
||||||
|
@ -11,10 +11,9 @@
|
|||||||
|
|
||||||
module Data.SBV.Provers.SExpr where
|
module Data.SBV.Provers.SExpr where
|
||||||
|
|
||||||
import Control.Monad.Error () -- for Monad (Either String) instance
|
import Data.Char (isDigit, ord)
|
||||||
import Data.Char (isDigit, ord)
|
import Data.List (isPrefixOf)
|
||||||
import Data.List (isPrefixOf)
|
import Numeric (readInt, readDec, readHex, fromRat)
|
||||||
import Numeric (readInt, readDec, readHex, fromRat)
|
|
||||||
|
|
||||||
import Data.SBV.BitVectors.AlgReals
|
import Data.SBV.BitVectors.AlgReals
|
||||||
import Data.SBV.BitVectors.Data (nan, infinity)
|
import Data.SBV.BitVectors.Data (nan, infinity)
|
||||||
|
@ -31,7 +31,7 @@ import Data.SBV.SMT.SMTLib
|
|||||||
-- The default options are @\"-m -f\"@, which is valid for Yices 2.1 series. You can use the @SBV_YICES_OPTIONS@ environment variable to override the options.
|
-- The default options are @\"-m -f\"@, which is valid for Yices 2.1 series. You can use the @SBV_YICES_OPTIONS@ environment variable to override the options.
|
||||||
yices :: SMTSolver
|
yices :: SMTSolver
|
||||||
yices = SMTSolver {
|
yices = SMTSolver {
|
||||||
name = "Yices"
|
name = Yices
|
||||||
, executable = "yices-smt"
|
, executable = "yices-smt"
|
||||||
-- , options = ["-tc", "-smt", "-e"] -- For Yices1
|
-- , options = ["-tc", "-smt", "-e"] -- For Yices1
|
||||||
, options = ["-m", "-f"] -- For Yices2
|
, options = ["-m", "-f"] -- For Yices2
|
||||||
|
@ -23,6 +23,7 @@ import qualified System.Info as S(os)
|
|||||||
|
|
||||||
import Data.SBV.BitVectors.AlgReals
|
import Data.SBV.BitVectors.AlgReals
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
|
import Data.SBV.BitVectors.PrettyNum
|
||||||
import Data.SBV.SMT.SMT
|
import Data.SBV.SMT.SMT
|
||||||
import Data.SBV.SMT.SMTLib
|
import Data.SBV.SMT.SMTLib
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ optionPrefix
|
|||||||
-- The default options are @\"-in -smt2\"@, which is valid for Z3 4.1. You can use the @SBV_Z3_OPTIONS@ environment variable to override the options.
|
-- The default options are @\"-in -smt2\"@, which is valid for Z3 4.1. You can use the @SBV_Z3_OPTIONS@ environment variable to override the options.
|
||||||
z3 :: SMTSolver
|
z3 :: SMTSolver
|
||||||
z3 = SMTSolver {
|
z3 = SMTSolver {
|
||||||
name = "z3"
|
name = Z3
|
||||||
, executable = "z3"
|
, executable = "z3"
|
||||||
, options = map (optionPrefix:) ["in", "smt2"]
|
, options = map (optionPrefix:) ["in", "smt2"]
|
||||||
, engine = \cfg isSat qinps modelMap skolemMap pgm -> do
|
, engine = \cfg isSat qinps modelMap skolemMap pgm -> do
|
||||||
@ -49,8 +50,8 @@ z3 = SMTSolver {
|
|||||||
[] -> ""
|
[] -> ""
|
||||||
ts -> unlines $ "; --- user given solver tweaks ---" : ts ++ ["; --- end of user given tweaks ---"]
|
ts -> unlines $ "; --- user given solver tweaks ---" : ts ++ ["; --- end of user given tweaks ---"]
|
||||||
dlim = printRealPrec cfg'
|
dlim = printRealPrec cfg'
|
||||||
--ppDecLim = "(set-option :pp.decimal_precision " ++ show dlim ++ ")\n"
|
ppDecLim = "(set-option :pp.decimal_precision " ++ show dlim ++ ")\n"
|
||||||
script = SMTScript {scriptBody = tweaks ++ {- ppDecLim ++ -} pgm, scriptModel = Just (cont skolemMap)}
|
script = SMTScript {scriptBody = tweaks ++ ppDecLim ++ pgm, scriptModel = Just (cont (roundingMode cfg) skolemMap)}
|
||||||
if dlim < 1
|
if dlim < 1
|
||||||
then error $ "SBV.Z3: printRealPrec value should be at least 1, invalid value received: " ++ show dlim
|
then error $ "SBV.Z3: printRealPrec value should be at least 1, invalid value received: " ++ show dlim
|
||||||
else standardSolver cfg' script cleanErrs (ProofError cfg') (interpretSolverOutput cfg' (extractMap isSat qinps modelMap))
|
else standardSolver cfg' script cleanErrs (ProofError cfg') (interpretSolverOutput cfg' (extractMap isSat qinps modelMap))
|
||||||
@ -68,28 +69,27 @@ z3 = SMTSolver {
|
|||||||
, supportsDoubles = True
|
, supportsDoubles = True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
where -- Get rid of the following when z3_4.0 is out
|
where cleanErrs = intercalate "\n" . filter (not . junk) . lines
|
||||||
cleanErrs = intercalate "\n" . filter (not . junk) . lines
|
|
||||||
junk = ("WARNING:" `isPrefixOf`)
|
junk = ("WARNING:" `isPrefixOf`)
|
||||||
zero :: Kind -> String
|
zero :: RoundingMode -> Kind -> String
|
||||||
zero (KBounded False 1) = "#b0"
|
zero _ KBool = "false"
|
||||||
zero (KBounded _ sz) = "#x" ++ replicate (sz `div` 4) '0'
|
zero _ (KBounded _ sz) = "#x" ++ replicate (sz `div` 4) '0'
|
||||||
zero KUnbounded = "0"
|
zero _ KUnbounded = "0"
|
||||||
zero KReal = "0.0"
|
zero _ KReal = "0.0"
|
||||||
zero KFloat = error "Z3:TBD: Figure out how to write float constants!"
|
zero rm KFloat = showSMTFloat rm 0
|
||||||
zero KDouble = error "Z3:TBD: Figure out how to write double constants!"
|
zero rm KDouble = showSMTDouble rm 0
|
||||||
zero (KUninterpreted s) = error $ "SBV.Z3.zero: Unexpected uninterpreted sort: " ++ s
|
zero _ (KUninterpreted s) = error $ "SBV.Z3.zero: Unexpected uninterpreted sort: " ++ s
|
||||||
cont skolemMap = intercalate "\n" $ concatMap extract skolemMap
|
cont rm skolemMap = intercalate "\n" $ concatMap extract skolemMap
|
||||||
where -- In the skolemMap:
|
where -- In the skolemMap:
|
||||||
-- * Left's are universals: i.e., the model should be true for
|
-- * Left's are universals: i.e., the model should be true for
|
||||||
-- any of these. So, we simply "echo 0" for these values.
|
-- any of these. So, we simply "echo 0" for these values.
|
||||||
-- * Right's are existentials. If there are no dependencies (empty list), then we can
|
-- * Right's are existentials. If there are no dependencies (empty list), then we can
|
||||||
-- simply use get-value to extract it's value. Otherwise, we have to apply it to
|
-- simply use get-value to extract it's value. Otherwise, we have to apply it to
|
||||||
-- an appropriate number of 0's to get the final value.
|
-- an appropriate number of 0's to get the final value.
|
||||||
extract (Left s) = ["(echo \"((" ++ show s ++ " " ++ zero (kindOf s) ++ "))\")"]
|
extract (Left s) = ["(echo \"((" ++ show s ++ " " ++ zero rm (kindOf s) ++ "))\")"]
|
||||||
extract (Right (s, [])) = let g = "(get-value (" ++ show s ++ "))" in getVal (kindOf s) g
|
extract (Right (s, [])) = let g = "(get-value (" ++ show s ++ "))" in getVal (kindOf s) g
|
||||||
extract (Right (s, ss)) = let g = "(get-value ((" ++ show s ++ concat [' ' : zero (kindOf a) | a <- ss] ++ ")))" in getVal (kindOf s) g
|
extract (Right (s, ss)) = let g = "(get-value ((" ++ show s ++ concat [' ' : zero rm (kindOf a) | a <- ss] ++ ")))" in getVal (kindOf s) g
|
||||||
getVal KReal g = ["(set-option :pp.decimal false)", g, "(set-option :pp.decimal true)", g]
|
getVal KReal g = ["(set-option :pp.decimal false) " ++ g, "(set-option :pp.decimal true) " ++ g]
|
||||||
getVal _ g = [g]
|
getVal _ g = [g]
|
||||||
addTimeOut Nothing o = o
|
addTimeOut Nothing o = o
|
||||||
addTimeOut (Just i) o
|
addTimeOut (Just i) o
|
||||||
|
@ -16,88 +16,23 @@ module Data.SBV.SMT.SMT where
|
|||||||
import qualified Control.Exception as C
|
import qualified Control.Exception as C
|
||||||
|
|
||||||
import Control.Concurrent (newEmptyMVar, takeMVar, putMVar, forkIO)
|
import Control.Concurrent (newEmptyMVar, takeMVar, putMVar, forkIO)
|
||||||
import Control.DeepSeq (NFData(..))
|
|
||||||
import Control.Monad (when, zipWithM)
|
import Control.Monad (when, zipWithM)
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
import Data.Int (Int8, Int16, Int32, Int64)
|
import Data.Int (Int8, Int16, Int32, Int64)
|
||||||
import Data.List (intercalate, isPrefixOf, isInfixOf)
|
import Data.List (intercalate, isPrefixOf, isInfixOf)
|
||||||
import Data.Maybe (isNothing, fromJust)
|
|
||||||
import Data.Word (Word8, Word16, Word32, Word64)
|
import Data.Word (Word8, Word16, Word32, Word64)
|
||||||
import System.Directory (findExecutable)
|
import System.Directory (findExecutable)
|
||||||
import System.Process (readProcessWithExitCode, runInteractiveProcess, waitForProcess)
|
import System.Process (runInteractiveProcess, waitForProcess, terminateProcess)
|
||||||
import System.Exit (ExitCode(..))
|
import System.Exit (ExitCode(..))
|
||||||
import System.IO (hClose, hFlush, hPutStr, hGetContents, hGetLine)
|
import System.IO (hClose, hFlush, hPutStr, hGetContents, hGetLine)
|
||||||
|
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
import Data.SBV.BitVectors.AlgReals
|
import Data.SBV.BitVectors.AlgReals
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.BitVectors.PrettyNum
|
import Data.SBV.BitVectors.PrettyNum
|
||||||
import Data.SBV.Utils.TDiff
|
import Data.SBV.Utils.TDiff
|
||||||
|
|
||||||
-- | Solver configuration. See also 'z3', 'yices', 'cvc4', and 'boolector, which are instantiations of this type for those solvers, with
|
|
||||||
-- reasonable defaults. In particular, custom configuration can be created by varying those values. (Such as @z3{verbose=True}@.)
|
|
||||||
--
|
|
||||||
-- Most fields are self explanatory. The notion of precision for printing algebraic reals stems from the fact that such values does
|
|
||||||
-- not necessarily have finite decimal representations, and hence we have to stop printing at some depth. It is important to
|
|
||||||
-- emphasize that such values always have infinite precision internally. The issue is merely with how we print such an infinite
|
|
||||||
-- precision value on the screen. The field 'printRealPrec' controls the printing precision, by specifying the number of digits after
|
|
||||||
-- the decimal point. The default value is 16, but it can be set to any positive integer.
|
|
||||||
--
|
|
||||||
-- When printing, SBV will add the suffix @...@ at the and of a real-value, if the given bound is not sufficient to represent the real-value
|
|
||||||
-- exactly. Otherwise, the number will be written out in standard decimal notation. Note that SBV will always print the whole value if it
|
|
||||||
-- is precise (i.e., if it fits in a finite number of digits), regardless of the precision limit. The limit only applies if the representation
|
|
||||||
-- of the real value is not finite, i.e., if it is not rational.
|
|
||||||
data SMTConfig = SMTConfig {
|
|
||||||
verbose :: Bool -- ^ Debug mode
|
|
||||||
, timing :: Bool -- ^ Print timing information on how long different phases took (construction, solving, etc.)
|
|
||||||
, timeOut :: Maybe Int -- ^ How much time to give to the solver. (In seconds)
|
|
||||||
, printBase :: Int -- ^ Print integral literals in this base (2, 8, and 10, and 16 are supported.)
|
|
||||||
, printRealPrec :: Int -- ^ Print algebraic real values with this precision. (SReal, default: 16)
|
|
||||||
, solverTweaks :: [String] -- ^ Additional lines of script to give to the solver (user specified)
|
|
||||||
, satCmd :: String -- ^ Usually "(check-sat)". However, users might tweak it based on solver characteristics.
|
|
||||||
, smtFile :: Maybe FilePath -- ^ If Just, the generated SMT script will be put in this file (for debugging purposes mostly)
|
|
||||||
, useSMTLib2 :: Bool -- ^ If True, we'll treat the solver as using SMTLib2 input format. Otherwise, SMTLib1
|
|
||||||
, solver :: SMTSolver -- ^ The actual SMT solver.
|
|
||||||
, roundingMode :: RoundingMode -- ^ Rounding mode to use for floating-point conversions
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | An SMT engine
|
|
||||||
type SMTEngine = SMTConfig -> Bool -> [(Quantifier, NamedSymVar)] -> [(String, UnintKind)] -> [Either SW (SW, [SW])] -> String -> IO SMTResult
|
|
||||||
|
|
||||||
-- | An SMT solver
|
|
||||||
data SMTSolver = SMTSolver {
|
|
||||||
name :: String -- ^ Printable name of the solver
|
|
||||||
, executable :: String -- ^ The path to its executable
|
|
||||||
, options :: [String] -- ^ Options to provide to the solver
|
|
||||||
, engine :: SMTEngine -- ^ The solver engine, responsible for interpreting solver output
|
|
||||||
, xformExitCode :: ExitCode -> ExitCode -- ^ Should we re-interpret exit codes. Most solvers behave rationally, i.e., id will do. Some (like CVC4) don't.
|
|
||||||
, capabilities :: SolverCapabilities -- ^ Various capabilities of the solver
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | A model, as returned by a solver
|
|
||||||
data SMTModel = SMTModel {
|
|
||||||
modelAssocs :: [(String, CW)]
|
|
||||||
, modelArrays :: [(String, [String])] -- very crude!
|
|
||||||
, modelUninterps :: [(String, [String])] -- very crude!
|
|
||||||
}
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
-- | The result of an SMT solver call. Each constructor is tagged with
|
|
||||||
-- the 'SMTConfig' that created it so that further tools can inspect it
|
|
||||||
-- and build layers of results, if needed. For ordinary uses of the library,
|
|
||||||
-- this type should not be needed, instead use the accessor functions on
|
|
||||||
-- it. (Custom Show instances and model extractors.)
|
|
||||||
data SMTResult = Unsatisfiable SMTConfig -- ^ Unsatisfiable
|
|
||||||
| Satisfiable SMTConfig SMTModel -- ^ Satisfiable with model
|
|
||||||
| Unknown SMTConfig SMTModel -- ^ Prover returned unknown, with a potential (possibly bogus) model
|
|
||||||
| ProofError SMTConfig [String] -- ^ Prover errored out
|
|
||||||
| TimeOut SMTConfig -- ^ Computation timed out (see the 'timeout' combinator)
|
|
||||||
|
|
||||||
-- | A script, to be passed to the solver.
|
|
||||||
data SMTScript = SMTScript {
|
|
||||||
scriptBody :: String -- ^ Initial feed
|
|
||||||
, scriptModel :: Maybe String -- ^ Optional continuation script, if the result is sat
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Extract the final configuration from a result
|
-- | Extract the final configuration from a result
|
||||||
resultConfig :: SMTResult -> SMTConfig
|
resultConfig :: SMTResult -> SMTConfig
|
||||||
resultConfig (Unsatisfiable c) = c
|
resultConfig (Unsatisfiable c) = c
|
||||||
@ -106,16 +41,6 @@ resultConfig (Unknown c _) = c
|
|||||||
resultConfig (ProofError c _) = c
|
resultConfig (ProofError c _) = c
|
||||||
resultConfig (TimeOut c) = c
|
resultConfig (TimeOut c) = c
|
||||||
|
|
||||||
instance NFData SMTResult where
|
|
||||||
rnf (Unsatisfiable _) = ()
|
|
||||||
rnf (Satisfiable _ xs) = rnf xs `seq` ()
|
|
||||||
rnf (Unknown _ xs) = rnf xs `seq` ()
|
|
||||||
rnf (ProofError _ xs) = rnf xs `seq` ()
|
|
||||||
rnf (TimeOut _) = ()
|
|
||||||
|
|
||||||
instance NFData SMTModel where
|
|
||||||
rnf (SMTModel assocs unints uarrs) = rnf assocs `seq` rnf unints `seq` rnf uarrs `seq` ()
|
|
||||||
|
|
||||||
-- | A 'prove' call results in a 'ThmResult'
|
-- | A 'prove' call results in a 'ThmResult'
|
||||||
newtype ThmResult = ThmResult SMTResult
|
newtype ThmResult = ThmResult SMTResult
|
||||||
|
|
||||||
@ -127,17 +52,19 @@ newtype SatResult = SatResult SMTResult
|
|||||||
-- we should warn the user about prefix-existentials.
|
-- we should warn the user about prefix-existentials.
|
||||||
newtype AllSatResult = AllSatResult (Bool, [SMTResult])
|
newtype AllSatResult = AllSatResult (Bool, [SMTResult])
|
||||||
|
|
||||||
|
-- | User friendly way of printing theorem results
|
||||||
instance Show ThmResult where
|
instance Show ThmResult where
|
||||||
show (ThmResult r) = showSMTResult "Q.E.D."
|
show (ThmResult r) = showSMTResult "Q.E.D."
|
||||||
"Unknown" "Unknown. Potential counter-example:\n"
|
"Unknown" "Unknown. Potential counter-example:\n"
|
||||||
"Falsifiable" "Falsifiable. Counter-example:\n" r
|
"Falsifiable" "Falsifiable. Counter-example:\n" r
|
||||||
|
|
||||||
|
-- | User friendly way of printing satisfiablity results
|
||||||
instance Show SatResult where
|
instance Show SatResult where
|
||||||
show (SatResult r) = showSMTResult "Unsatisfiable"
|
show (SatResult r) = showSMTResult "Unsatisfiable"
|
||||||
"Unknown" "Unknown. Potential model:\n"
|
"Unknown" "Unknown. Potential model:\n"
|
||||||
"Satisfiable" "Satisfiable. Model:\n" r
|
"Satisfiable" "Satisfiable. Model:\n" r
|
||||||
|
|
||||||
-- NB. The Show instance of AllSatResults have to be careful in being lazy enough
|
-- | The Show instance of AllSatResults. Note that we have to be careful in being lazy enough
|
||||||
-- as the typical use case is to pull results out as they become available.
|
-- as the typical use case is to pull results out as they become available.
|
||||||
instance Show AllSatResult where
|
instance Show AllSatResult where
|
||||||
show (AllSatResult (e, xs)) = go (0::Int) xs
|
show (AllSatResult (e, xs)) = go (0::Int) xs
|
||||||
@ -178,51 +105,73 @@ genParse :: Integral a => Kind -> [CW] -> Maybe (a, [CW])
|
|||||||
genParse k (x@(CW _ (CWInteger i)):r) | kindOf x == k = Just (fromIntegral i, r)
|
genParse k (x@(CW _ (CWInteger i)):r) | kindOf x == k = Just (fromIntegral i, r)
|
||||||
genParse _ _ = Nothing
|
genParse _ _ = Nothing
|
||||||
|
|
||||||
-- Base case, that comes in handy if there are no real variables
|
-- | Base case for 'SatModel' at unit type. Comes in handy if there are no real variables.
|
||||||
instance SatModel () where
|
instance SatModel () where
|
||||||
parseCWs xs = return ((), xs)
|
parseCWs xs = return ((), xs)
|
||||||
|
|
||||||
|
-- | 'Bool' as extracted from a model
|
||||||
instance SatModel Bool where
|
instance SatModel Bool where
|
||||||
parseCWs xs = do (x, r) <- genParse (KBounded False 1) xs
|
parseCWs xs = do (x, r) <- genParse KBool xs
|
||||||
return ((x :: Integer) /= 0, r)
|
return ((x :: Integer) /= 0, r)
|
||||||
|
|
||||||
|
-- | 'Word8' as extracted from a model
|
||||||
instance SatModel Word8 where
|
instance SatModel Word8 where
|
||||||
parseCWs = genParse (KBounded False 8)
|
parseCWs = genParse (KBounded False 8)
|
||||||
|
|
||||||
|
-- | 'Int8' as extracted from a model
|
||||||
instance SatModel Int8 where
|
instance SatModel Int8 where
|
||||||
parseCWs = genParse (KBounded True 8)
|
parseCWs = genParse (KBounded True 8)
|
||||||
|
|
||||||
|
-- | 'Word16' as extracted from a model
|
||||||
instance SatModel Word16 where
|
instance SatModel Word16 where
|
||||||
parseCWs = genParse (KBounded False 16)
|
parseCWs = genParse (KBounded False 16)
|
||||||
|
|
||||||
|
-- | 'Int16' as extracted from a model
|
||||||
instance SatModel Int16 where
|
instance SatModel Int16 where
|
||||||
parseCWs = genParse (KBounded True 16)
|
parseCWs = genParse (KBounded True 16)
|
||||||
|
|
||||||
|
-- | 'Word32' as extracted from a model
|
||||||
instance SatModel Word32 where
|
instance SatModel Word32 where
|
||||||
parseCWs = genParse (KBounded False 32)
|
parseCWs = genParse (KBounded False 32)
|
||||||
|
|
||||||
|
-- | 'Int32' as extracted from a model
|
||||||
instance SatModel Int32 where
|
instance SatModel Int32 where
|
||||||
parseCWs = genParse (KBounded True 32)
|
parseCWs = genParse (KBounded True 32)
|
||||||
|
|
||||||
|
-- | 'Word64' as extracted from a model
|
||||||
instance SatModel Word64 where
|
instance SatModel Word64 where
|
||||||
parseCWs = genParse (KBounded False 64)
|
parseCWs = genParse (KBounded False 64)
|
||||||
|
|
||||||
|
-- | 'Int64' as extracted from a model
|
||||||
instance SatModel Int64 where
|
instance SatModel Int64 where
|
||||||
parseCWs = genParse (KBounded True 64)
|
parseCWs = genParse (KBounded True 64)
|
||||||
|
|
||||||
|
-- | 'Integer' as extracted from a model
|
||||||
instance SatModel Integer where
|
instance SatModel Integer where
|
||||||
parseCWs = genParse KUnbounded
|
parseCWs = genParse KUnbounded
|
||||||
|
|
||||||
|
-- | 'AlgReal' as extracted from a model
|
||||||
instance SatModel AlgReal where
|
instance SatModel AlgReal where
|
||||||
parseCWs (CW KReal (CWAlgReal i) : r) = Just (i, r)
|
parseCWs (CW KReal (CWAlgReal i) : r) = Just (i, r)
|
||||||
parseCWs _ = Nothing
|
parseCWs _ = Nothing
|
||||||
|
|
||||||
|
-- | 'Float' as extracted from a model
|
||||||
|
instance SatModel Float where
|
||||||
|
parseCWs (CW KFloat (CWFloat i) : r) = Just (i, r)
|
||||||
|
parseCWs _ = Nothing
|
||||||
|
|
||||||
|
-- | 'Double' as extracted from a model
|
||||||
|
instance SatModel Double where
|
||||||
|
parseCWs (CW KDouble (CWDouble i) : r) = Just (i, r)
|
||||||
|
parseCWs _ = Nothing
|
||||||
|
|
||||||
instance SatModel CW where
|
instance SatModel CW where
|
||||||
parseCWs (cw : r) = Just (cw, r)
|
parseCWs (cw : r) = Just (cw, r)
|
||||||
parseCWs [] = Nothing
|
parseCWs [] = Nothing
|
||||||
|
|
||||||
-- when reading a list; go as long as we can (maximal-munch)
|
-- | A list of values as extracted from a model. When reading a list, we
|
||||||
-- note that this never fails..
|
-- go as long as we can (maximal-munch). Note that this never fails, as
|
||||||
|
-- we can always return the empty list!
|
||||||
instance SatModel a => SatModel [a] where
|
instance SatModel a => SatModel [a] where
|
||||||
parseCWs [] = Just ([], [])
|
parseCWs [] = Just ([], [])
|
||||||
parseCWs xs = case parseCWs xs of
|
parseCWs xs = case parseCWs xs of
|
||||||
@ -231,31 +180,37 @@ instance SatModel a => SatModel [a] where
|
|||||||
Nothing -> Just ([], ys)
|
Nothing -> Just ([], ys)
|
||||||
Nothing -> Just ([], xs)
|
Nothing -> Just ([], xs)
|
||||||
|
|
||||||
|
-- | Tuples extracted from a model
|
||||||
instance (SatModel a, SatModel b) => SatModel (a, b) where
|
instance (SatModel a, SatModel b) => SatModel (a, b) where
|
||||||
parseCWs as = do (a, bs) <- parseCWs as
|
parseCWs as = do (a, bs) <- parseCWs as
|
||||||
(b, cs) <- parseCWs bs
|
(b, cs) <- parseCWs bs
|
||||||
return ((a, b), cs)
|
return ((a, b), cs)
|
||||||
|
|
||||||
|
-- | 3-Tuples extracted from a model
|
||||||
instance (SatModel a, SatModel b, SatModel c) => SatModel (a, b, c) where
|
instance (SatModel a, SatModel b, SatModel c) => SatModel (a, b, c) where
|
||||||
parseCWs as = do (a, bs) <- parseCWs as
|
parseCWs as = do (a, bs) <- parseCWs as
|
||||||
((b, c), ds) <- parseCWs bs
|
((b, c), ds) <- parseCWs bs
|
||||||
return ((a, b, c), ds)
|
return ((a, b, c), ds)
|
||||||
|
|
||||||
|
-- | 4-Tuples extracted from a model
|
||||||
instance (SatModel a, SatModel b, SatModel c, SatModel d) => SatModel (a, b, c, d) where
|
instance (SatModel a, SatModel b, SatModel c, SatModel d) => SatModel (a, b, c, d) where
|
||||||
parseCWs as = do (a, bs) <- parseCWs as
|
parseCWs as = do (a, bs) <- parseCWs as
|
||||||
((b, c, d), es) <- parseCWs bs
|
((b, c, d), es) <- parseCWs bs
|
||||||
return ((a, b, c, d), es)
|
return ((a, b, c, d), es)
|
||||||
|
|
||||||
|
-- | 5-Tuples extracted from a model
|
||||||
instance (SatModel a, SatModel b, SatModel c, SatModel d, SatModel e) => SatModel (a, b, c, d, e) where
|
instance (SatModel a, SatModel b, SatModel c, SatModel d, SatModel e) => SatModel (a, b, c, d, e) where
|
||||||
parseCWs as = do (a, bs) <- parseCWs as
|
parseCWs as = do (a, bs) <- parseCWs as
|
||||||
((b, c, d, e), fs) <- parseCWs bs
|
((b, c, d, e), fs) <- parseCWs bs
|
||||||
return ((a, b, c, d, e), fs)
|
return ((a, b, c, d, e), fs)
|
||||||
|
|
||||||
|
-- | 6-Tuples extracted from a model
|
||||||
instance (SatModel a, SatModel b, SatModel c, SatModel d, SatModel e, SatModel f) => SatModel (a, b, c, d, e, f) where
|
instance (SatModel a, SatModel b, SatModel c, SatModel d, SatModel e, SatModel f) => SatModel (a, b, c, d, e, f) where
|
||||||
parseCWs as = do (a, bs) <- parseCWs as
|
parseCWs as = do (a, bs) <- parseCWs as
|
||||||
((b, c, d, e, f), gs) <- parseCWs bs
|
((b, c, d, e, f), gs) <- parseCWs bs
|
||||||
return ((a, b, c, d, e, f), gs)
|
return ((a, b, c, d, e, f), gs)
|
||||||
|
|
||||||
|
-- | 7-Tuples extracted from a model
|
||||||
instance (SatModel a, SatModel b, SatModel c, SatModel d, SatModel e, SatModel f, SatModel g) => SatModel (a, b, c, d, e, f, g) where
|
instance (SatModel a, SatModel b, SatModel c, SatModel d, SatModel e, SatModel f, SatModel g) => SatModel (a, b, c, d, e, f, g) where
|
||||||
parseCWs as = do (a, bs) <- parseCWs as
|
parseCWs as = do (a, bs) <- parseCWs as
|
||||||
((b, c, d, e, f, g), hs) <- parseCWs bs
|
((b, c, d, e, f, g), hs) <- parseCWs bs
|
||||||
@ -268,6 +223,19 @@ class Modelable a where
|
|||||||
-- | Extract a model, the result is a tuple where the first argument (if True)
|
-- | Extract a model, the result is a tuple where the first argument (if True)
|
||||||
-- indicates whether the model was "probable". (i.e., if the solver returned unknown.)
|
-- indicates whether the model was "probable". (i.e., if the solver returned unknown.)
|
||||||
getModel :: SatModel b => a -> Either String (Bool, b)
|
getModel :: SatModel b => a -> Either String (Bool, b)
|
||||||
|
-- | Extract a model dictionary. Extract a dictionary mapping the variables to
|
||||||
|
-- their respective values as returned by the SMT solver. Also see `getModelDictionaries`.
|
||||||
|
getModelDictionary :: a -> M.Map String CW
|
||||||
|
-- | Extract a model value for a given element. Also see `getModelValues`.
|
||||||
|
getModelValue :: SymWord b => String -> a -> Maybe b
|
||||||
|
getModelValue v r = fromCW `fmap` (v `M.lookup` getModelDictionary r)
|
||||||
|
-- | Extract a representative name for the model value of an uninterpreted kind.
|
||||||
|
-- This is supposed to correspond to the value as computed internally by the
|
||||||
|
-- SMT solver; and is unportable from solver to solver. Also see `getModelUninterpretedValues`.
|
||||||
|
getModelUninterpretedValue :: String -> a -> Maybe String
|
||||||
|
getModelUninterpretedValue v r = case v `M.lookup` getModelDictionary r of
|
||||||
|
Just (CW _ (CWUninterpreted s)) -> Just s
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
-- | A simpler variant of 'getModel' to get a model out without the fuss.
|
-- | A simpler variant of 'getModel' to get a model out without the fuss.
|
||||||
extractModel :: SatModel b => a -> Maybe b
|
extractModel :: SatModel b => a -> Maybe b
|
||||||
@ -280,14 +248,31 @@ class Modelable a where
|
|||||||
extractModels :: SatModel a => AllSatResult -> [a]
|
extractModels :: SatModel a => AllSatResult -> [a]
|
||||||
extractModels (AllSatResult (_, xs)) = [ms | Right (_, ms) <- map getModel xs]
|
extractModels (AllSatResult (_, xs)) = [ms | Right (_, ms) <- map getModel xs]
|
||||||
|
|
||||||
|
-- | Get dictionaries from an all-sat call. Similar to `getModelDictionary`.
|
||||||
|
getModelDictionaries :: AllSatResult -> [M.Map String CW]
|
||||||
|
getModelDictionaries (AllSatResult (_, xs)) = map getModelDictionary xs
|
||||||
|
|
||||||
|
-- | Extract value of a variable from an all-sat call. Similar to `getModelValue`.
|
||||||
|
getModelValues :: SymWord b => String -> AllSatResult -> [Maybe b]
|
||||||
|
getModelValues s (AllSatResult (_, xs)) = map (s `getModelValue`) xs
|
||||||
|
|
||||||
|
-- | Extract value of an uninterpreted variable from an all-sat call. Similar to `getModelUninterpretedValue`.
|
||||||
|
getModelUninterpretedValues :: String -> AllSatResult -> [Maybe String]
|
||||||
|
getModelUninterpretedValues s (AllSatResult (_, xs)) = map (s `getModelUninterpretedValue`) xs
|
||||||
|
|
||||||
|
-- | 'ThmResult' as a generic model provider
|
||||||
instance Modelable ThmResult where
|
instance Modelable ThmResult where
|
||||||
getModel (ThmResult r) = getModel r
|
getModel (ThmResult r) = getModel r
|
||||||
modelExists (ThmResult r) = modelExists r
|
modelExists (ThmResult r) = modelExists r
|
||||||
|
getModelDictionary (ThmResult r) = getModelDictionary r
|
||||||
|
|
||||||
|
-- | 'SatResult' as a generic model provider
|
||||||
instance Modelable SatResult where
|
instance Modelable SatResult where
|
||||||
getModel (SatResult r) = getModel r
|
getModel (SatResult r) = getModel r
|
||||||
modelExists (SatResult r) = modelExists r
|
modelExists (SatResult r) = modelExists r
|
||||||
|
getModelDictionary (SatResult r) = getModelDictionary r
|
||||||
|
|
||||||
|
-- | 'SMTResult' as a generic model provider
|
||||||
instance Modelable SMTResult where
|
instance Modelable SMTResult where
|
||||||
getModel (Unsatisfiable _) = Left "SBV.getModel: Unsatisfiable result"
|
getModel (Unsatisfiable _) = Left "SBV.getModel: Unsatisfiable result"
|
||||||
getModel (Unknown _ m) = Right (True, parseModelOut m)
|
getModel (Unknown _ m) = Right (True, parseModelOut m)
|
||||||
@ -297,6 +282,11 @@ instance Modelable SMTResult where
|
|||||||
modelExists (Satisfiable{}) = True
|
modelExists (Satisfiable{}) = True
|
||||||
modelExists (Unknown{}) = False -- don't risk it
|
modelExists (Unknown{}) = False -- don't risk it
|
||||||
modelExists _ = False
|
modelExists _ = False
|
||||||
|
getModelDictionary (Unsatisfiable _) = M.empty
|
||||||
|
getModelDictionary (Unknown _ m) = M.fromList (modelAssocs m)
|
||||||
|
getModelDictionary (ProofError _ _) = M.empty
|
||||||
|
getModelDictionary (TimeOut _) = M.empty
|
||||||
|
getModelDictionary (Satisfiable _ m) = M.fromList (modelAssocs m)
|
||||||
|
|
||||||
-- | Extract a model out, will throw error if parsing is unsuccessful
|
-- | Extract a model out, will throw error if parsing is unsuccessful
|
||||||
parseModelOut :: SatModel a => SMTModel -> a
|
parseModelOut :: SatModel a => SMTModel -> a
|
||||||
@ -357,8 +347,9 @@ shUA (f, cases) = (" -- array: " ++ f) : map shC cases
|
|||||||
where shC s = " " ++ s
|
where shC s = " " ++ s
|
||||||
|
|
||||||
-- | Helper function to spin off to an SMT solver.
|
-- | Helper function to spin off to an SMT solver.
|
||||||
pipeProcess :: SMTConfig -> String -> String -> [String] -> SMTScript -> (String -> String) -> IO (Either String [String])
|
pipeProcess :: SMTConfig -> String -> [String] -> SMTScript -> (String -> String) -> IO (Either String [String])
|
||||||
pipeProcess cfg nm execName opts script cleanErrs = do
|
pipeProcess cfg execName opts script cleanErrs = do
|
||||||
|
let nm = show (name (solver cfg))
|
||||||
mbExecPath <- findExecutable execName
|
mbExecPath <- findExecutable execName
|
||||||
case mbExecPath of
|
case mbExecPath of
|
||||||
Nothing -> return $ Left $ "Unable to locate executable for " ++ nm
|
Nothing -> return $ Left $ "Unable to locate executable for " ++ nm
|
||||||
@ -397,13 +388,13 @@ standardSolver config script cleanErrs failure success = do
|
|||||||
exec = executable smtSolver
|
exec = executable smtSolver
|
||||||
opts = options smtSolver
|
opts = options smtSolver
|
||||||
isTiming = timing config
|
isTiming = timing config
|
||||||
nmSolver = name smtSolver
|
nmSolver = show (name smtSolver)
|
||||||
msg $ "Calling: " ++ show (unwords (exec:opts))
|
msg $ "Calling: " ++ show (unwords (exec:opts))
|
||||||
case smtFile config of
|
case smtFile config of
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
Just f -> do putStrLn $ "** Saving the generated script in file: " ++ show f
|
Just f -> do msg $ "Saving the generated script in file: " ++ show f
|
||||||
writeFile f (scriptBody script)
|
writeFile f (scriptBody script)
|
||||||
contents <- timeIf isTiming nmSolver $ pipeProcess config nmSolver exec opts script cleanErrs
|
contents <- timeIf isTiming nmSolver $ pipeProcess config exec opts script cleanErrs
|
||||||
msg $ nmSolver ++ " output:\n" ++ either id (intercalate "\n") contents
|
msg $ nmSolver ++ " output:\n" ++ either id (intercalate "\n") contents
|
||||||
case contents of
|
case contents of
|
||||||
Left e -> return $ failure (lines e)
|
Left e -> return $ failure (lines e)
|
||||||
@ -413,41 +404,46 @@ standardSolver config script cleanErrs failure success = do
|
|||||||
-- and can speak SMT-Lib2 (just a little).
|
-- and can speak SMT-Lib2 (just a little).
|
||||||
runSolver :: SMTConfig -> FilePath -> [String] -> SMTScript -> IO (ExitCode, String, String)
|
runSolver :: SMTConfig -> FilePath -> [String] -> SMTScript -> IO (ExitCode, String, String)
|
||||||
runSolver cfg execPath opts script
|
runSolver cfg execPath opts script
|
||||||
| isNothing $ scriptModel script
|
= do (send, ask, cleanUp, pid) <- do
|
||||||
= let checkCmd | useSMTLib2 cfg = '\n' : satCmd cfg
|
|
||||||
| True = ""
|
|
||||||
in readProcessWithExitCode execPath opts (scriptBody script ++ checkCmd)
|
|
||||||
| True
|
|
||||||
= do (send, ask, cleanUp) <- do
|
|
||||||
(inh, outh, errh, pid) <- runInteractiveProcess execPath opts Nothing Nothing
|
(inh, outh, errh, pid) <- runInteractiveProcess execPath opts Nothing Nothing
|
||||||
let send l = hPutStr inh (l ++ "\n") >> hFlush inh
|
let send l = hPutStr inh (l ++ "\n") >> hFlush inh
|
||||||
recv = hGetLine outh `C.catch` (\(_ :: C.SomeException) -> return "")
|
recv = hGetLine outh
|
||||||
ask l = send l >> recv
|
ask l = send l >> recv
|
||||||
cleanUp r = do outMVar <- newEmptyMVar
|
cleanUp response
|
||||||
out <- hGetContents outh
|
= do hClose inh
|
||||||
_ <- forkIO $ C.evaluate (length out) >> putMVar outMVar ()
|
outMVar <- newEmptyMVar
|
||||||
err <- hGetContents errh
|
out <- hGetContents outh
|
||||||
_ <- forkIO $ C.evaluate (length err) >> putMVar outMVar ()
|
_ <- forkIO $ C.evaluate (length out) >> putMVar outMVar ()
|
||||||
hClose inh
|
err <- hGetContents errh
|
||||||
takeMVar outMVar
|
_ <- forkIO $ C.evaluate (length err) >> putMVar outMVar ()
|
||||||
takeMVar outMVar
|
takeMVar outMVar
|
||||||
hClose outh
|
takeMVar outMVar
|
||||||
hClose errh
|
hClose outh
|
||||||
ex <- waitForProcess pid
|
hClose errh
|
||||||
-- if the status is unknown, prepare for the possibility of not having a model
|
ex <- waitForProcess pid
|
||||||
-- TBD: This is rather crude and potentially Z3 specific
|
return $ case response of
|
||||||
return $ if "unknown" `isPrefixOf` r && "error" `isInfixOf` (out ++ err)
|
Nothing -> (ex, out, err)
|
||||||
then (ExitSuccess, r , "")
|
Just (r, vals) -> -- if the status is unknown, prepare for the possibility of not having a model
|
||||||
else (ex, r ++ "\n" ++ out, err)
|
-- TBD: This is rather crude and potentially Z3 specific
|
||||||
return (send, ask, cleanUp)
|
let finalOut = intercalate "\n" (r : vals)
|
||||||
mapM_ send (lines (scriptBody script))
|
in if "unknown" `isPrefixOf` r && "error" `isInfixOf` (out ++ err)
|
||||||
r <- ask $ satCmd cfg
|
then (ExitSuccess, finalOut , "")
|
||||||
when (any (`isPrefixOf` r) ["sat", "unknown"]) $ do
|
else (ex, finalOut ++ "\n" ++ out, err)
|
||||||
let mls = lines (fromJust (scriptModel script))
|
return (send, ask, cleanUp, pid)
|
||||||
when (verbose cfg) $ do putStrLn "** Sending the following model extraction commands:"
|
let executeSolver = do mapM_ send (lines (scriptBody script))
|
||||||
mapM_ putStrLn mls
|
response <- case scriptModel script of
|
||||||
mapM_ send mls
|
Nothing -> do send $ satCmd cfg
|
||||||
cleanUp r
|
return Nothing
|
||||||
|
Just ls -> do r <- ask $ satCmd cfg
|
||||||
|
vals <- if any (`isPrefixOf` r) ["sat", "unknown"]
|
||||||
|
then do let mls = lines ls
|
||||||
|
when (verbose cfg) $ do putStrLn "** Sending the following model extraction commands:"
|
||||||
|
mapM_ putStrLn mls
|
||||||
|
mapM ask mls
|
||||||
|
else return []
|
||||||
|
return $ Just (r, vals)
|
||||||
|
cleanUp response
|
||||||
|
executeSolver `C.onException` terminateProcess pid
|
||||||
|
|
||||||
-- | In case the SMT-Lib solver returns a response over multiple lines, compress them so we have
|
-- | In case the SMT-Lib solver returns a response over multiple lines, compress them so we have
|
||||||
-- each S-Expression spanning only a single line. We'll ignore things line parentheses inside quotes
|
-- each S-Expression spanning only a single line. We'll ignore things line parentheses inside quotes
|
||||||
|
@ -14,7 +14,6 @@ module Data.SBV.SMT.SMTLib(SMTLibPgm, SMTLibConverter, toSMTLib1, toSMTLib2, add
|
|||||||
import Data.Char (isDigit)
|
import Data.Char (isDigit)
|
||||||
|
|
||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.SMT.SMT
|
|
||||||
import Data.SBV.Provers.SExpr
|
import Data.SBV.Provers.SExpr
|
||||||
import qualified Data.SBV.SMT.SMTLib1 as SMT1
|
import qualified Data.SBV.SMT.SMTLib1 as SMT1
|
||||||
import qualified Data.SBV.SMT.SMTLib2 as SMT2
|
import qualified Data.SBV.SMT.SMTLib2 as SMT2
|
||||||
@ -23,6 +22,7 @@ import qualified Data.Set as Set (Set, member, toList)
|
|||||||
|
|
||||||
-- | An instance of SMT-Lib converter; instantiated for SMT-Lib v1 and v2. (And potentially for newer versions in the future.)
|
-- | An instance of SMT-Lib converter; instantiated for SMT-Lib v1 and v2. (And potentially for newer versions in the future.)
|
||||||
type SMTLibConverter = RoundingMode -- ^ User selected rounding mode to be used for floating point arithmetic
|
type SMTLibConverter = RoundingMode -- ^ User selected rounding mode to be used for floating point arithmetic
|
||||||
|
-> Maybe Logic -- ^ User selected logic to use. If Nothing, pick automatically.
|
||||||
-> SolverCapabilities -- ^ Capabilities of the backend solver targeted
|
-> SolverCapabilities -- ^ Capabilities of the backend solver targeted
|
||||||
-> Set.Set Kind -- ^ Kinds used in the problem
|
-> Set.Set Kind -- ^ Kinds used in the problem
|
||||||
-> Bool -- ^ is this a sat problem?
|
-> Bool -- ^ is this a sat problem?
|
||||||
@ -45,7 +45,7 @@ toSMTLib1 :: SMTLibConverter
|
|||||||
-- | Convert to SMTLib-2 format
|
-- | Convert to SMTLib-2 format
|
||||||
toSMTLib2 :: SMTLibConverter
|
toSMTLib2 :: SMTLibConverter
|
||||||
(toSMTLib1, toSMTLib2) = (cvt SMTLib1, cvt SMTLib2)
|
(toSMTLib1, toSMTLib2) = (cvt SMTLib1, cvt SMTLib2)
|
||||||
where cvt v roundMode solverCaps kindInfo isSat comments qinps skolemMap consts tbls arrs uis axs asgnsSeq cstrs out
|
where cvt v roundMode smtLogic solverCaps kindInfo isSat comments qinps skolemMap consts tbls arrs uis axs asgnsSeq cstrs out
|
||||||
| KUnbounded `Set.member` kindInfo && not (supportsUnboundedInts solverCaps)
|
| KUnbounded `Set.member` kindInfo && not (supportsUnboundedInts solverCaps)
|
||||||
= unsupported "unbounded integers"
|
= unsupported "unbounded integers"
|
||||||
| KReal `Set.member` kindInfo && not (supportsReals solverCaps)
|
| KReal `Set.member` kindInfo && not (supportsReals solverCaps)
|
||||||
@ -64,7 +64,7 @@ toSMTLib2 :: SMTLibConverter
|
|||||||
unsupported w = error $ "SBV: Given problem needs " ++ w ++ ", which is not supported by SBV for the chosen solver: " ++ capSolverName solverCaps
|
unsupported w = error $ "SBV: Given problem needs " ++ w ++ ", which is not supported by SBV for the chosen solver: " ++ capSolverName solverCaps
|
||||||
aliasTable = map (\(_, (x, y)) -> (y, x)) qinps
|
aliasTable = map (\(_, (x, y)) -> (y, x)) qinps
|
||||||
converter = if v == SMTLib1 then SMT1.cvt else SMT2.cvt
|
converter = if v == SMTLib1 then SMT1.cvt else SMT2.cvt
|
||||||
(pre, post) = converter roundMode solverCaps kindInfo isSat comments qinps skolemMap consts tbls arrs uis axs asgnsSeq cstrs out
|
(pre, post) = converter roundMode smtLogic solverCaps kindInfo isSat comments qinps skolemMap consts tbls arrs uis axs asgnsSeq cstrs out
|
||||||
needsFloats = KFloat `Set.member` kindInfo
|
needsFloats = KFloat `Set.member` kindInfo
|
||||||
needsDoubles = KDouble `Set.member` kindInfo
|
needsDoubles = KDouble `Set.member` kindInfo
|
||||||
needsQuantifiers
|
needsQuantifiers
|
||||||
|
@ -42,6 +42,7 @@ nonEq (s, c) = "(not (= " ++ s ++ " " ++ cvtCW c ++ "))"
|
|||||||
|
|
||||||
-- | Translate a problem into an SMTLib1 script
|
-- | Translate a problem into an SMTLib1 script
|
||||||
cvt :: RoundingMode -- ^ User selected rounding mode to be used for floating point arithmetic
|
cvt :: RoundingMode -- ^ User selected rounding mode to be used for floating point arithmetic
|
||||||
|
-> Maybe Logic -- ^ SMT-Lib logic, if requested by the user
|
||||||
-> SolverCapabilities -- ^ capabilities of the current solver
|
-> SolverCapabilities -- ^ capabilities of the current solver
|
||||||
-> Set.Set Kind -- ^ kinds used
|
-> Set.Set Kind -- ^ kinds used
|
||||||
-> Bool -- ^ is this a sat problem?
|
-> Bool -- ^ is this a sat problem?
|
||||||
@ -57,8 +58,9 @@ cvt :: RoundingMode -- ^ User selected rounding mode to be used
|
|||||||
-> [SW] -- ^ extra constraints
|
-> [SW] -- ^ extra constraints
|
||||||
-> SW -- ^ output variable
|
-> SW -- ^ output variable
|
||||||
-> ([String], [String])
|
-> ([String], [String])
|
||||||
cvt _roundingMode _solverCaps _kindInfo isSat comments qinps _skolemInps consts tbls arrs uis axs asgnsSeq cstrs out = (pre, post)
|
cvt _roundingMode smtLogic _solverCaps _kindInfo isSat comments qinps _skolemInps consts tbls arrs uis axs asgnsSeq cstrs out = (pre, post)
|
||||||
where logic
|
where logic
|
||||||
|
| Just l <- smtLogic = show l
|
||||||
| null tbls && null arrs && null uis = "QF_BV"
|
| null tbls && null arrs && null uis = "QF_BV"
|
||||||
| True = "QF_AUFBV"
|
| True = "QF_AUFBV"
|
||||||
inps = map (fst . snd) qinps
|
inps = map (fst . snd) qinps
|
||||||
@ -152,7 +154,7 @@ cvtCnst (s, c) = " :assumption (= " ++ show s ++ " " ++ cvtCW c ++ ")"
|
|||||||
|
|
||||||
-- no need to worry about Int/Real here as we don't support them with the SMTLib1 interface..
|
-- no need to worry about Int/Real here as we don't support them with the SMTLib1 interface..
|
||||||
cvtCW :: CW -> String
|
cvtCW :: CW -> String
|
||||||
cvtCW (CW (KBounded False 1) (CWInteger v)) = if v == 0 then "false" else "true"
|
cvtCW (CW KBool (CWInteger v)) = if v == 0 then "false" else "true"
|
||||||
cvtCW x@(CW _ (CWInteger v)) | not (hasSign x) = "bv" ++ show v ++ "[" ++ show (intSizeOf x) ++ "]"
|
cvtCW x@(CW _ (CWInteger v)) | not (hasSign x) = "bv" ++ show v ++ "[" ++ show (intSizeOf x) ++ "]"
|
||||||
-- signed numbers (with 2's complement representation) is problematic
|
-- signed numbers (with 2's complement representation) is problematic
|
||||||
-- since there's no way to put a bvneg over a positive number to get minBound..
|
-- since there's no way to put a bvneg over a positive number to get minBound..
|
||||||
@ -261,7 +263,7 @@ cvtType (SBVType []) = error "SBV.SMT.SMTLib1.cvtType: internal: received an emp
|
|||||||
cvtType (SBVType xs) = unwords $ map kindType xs
|
cvtType (SBVType xs) = unwords $ map kindType xs
|
||||||
|
|
||||||
kindType :: Kind -> String
|
kindType :: Kind -> String
|
||||||
kindType (KBounded False 1) = "Bool"
|
kindType KBool = "Bool"
|
||||||
kindType (KBounded _ s) = "BitVec[" ++ show s ++ "]"
|
kindType (KBounded _ s) = "BitVec[" ++ show s ++ "]"
|
||||||
kindType KUnbounded = die "unbounded Integer"
|
kindType KUnbounded = die "unbounded Integer"
|
||||||
kindType KReal = die "real value"
|
kindType KReal = die "real value"
|
||||||
|
@ -12,13 +12,15 @@
|
|||||||
|
|
||||||
module Data.SBV.SMT.SMTLib2(cvt, addNonEqConstraints) where
|
module Data.SBV.SMT.SMTLib2(cvt, addNonEqConstraints) where
|
||||||
|
|
||||||
import Data.Bits (bit)
|
import Data.Bits (bit)
|
||||||
import Data.Char (intToDigit)
|
import Data.Char (intToDigit)
|
||||||
|
import Data.Function (on)
|
||||||
|
import Data.Ord (comparing)
|
||||||
import qualified Data.Foldable as F (toList)
|
import qualified Data.Foldable as F (toList)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.IntMap as IM
|
import qualified Data.IntMap as IM
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import Data.List (intercalate, partition)
|
import Data.List (intercalate, partition, groupBy, sortBy)
|
||||||
import Numeric (showIntAtBase, showHex)
|
import Numeric (showIntAtBase, showHex)
|
||||||
|
|
||||||
import Data.SBV.BitVectors.AlgReals
|
import Data.SBV.BitVectors.AlgReals
|
||||||
@ -36,7 +38,7 @@ addNonEqConstraints rm qinps allNonEqConstraints (SMTLibPgm _ (aliasTable, pre,
|
|||||||
| True
|
| True
|
||||||
= Just $ intercalate "\n" $ pre
|
= Just $ intercalate "\n" $ pre
|
||||||
++ [ "; --- refuted-models ---" ]
|
++ [ "; --- refuted-models ---" ]
|
||||||
++ concatMap (nonEqs rm) (map (map intName) nonEqConstraints)
|
++ refutedModel
|
||||||
++ post
|
++ post
|
||||||
where refutedModel = concatMap (nonEqs rm) (map (map intName) nonEqConstraints)
|
where refutedModel = concatMap (nonEqs rm) (map (map intName) nonEqConstraints)
|
||||||
intName (s, c)
|
intName (s, c)
|
||||||
@ -47,11 +49,27 @@ addNonEqConstraints rm qinps allNonEqConstraints (SMTLibPgm _ (aliasTable, pre,
|
|||||||
topUnivs = [s | (_, (_, s)) <- takeWhile (\p -> fst p == EX) qinps]
|
topUnivs = [s | (_, (_, s)) <- takeWhile (\p -> fst p == EX) qinps]
|
||||||
|
|
||||||
nonEqs :: RoundingMode -> [(String, CW)] -> [String]
|
nonEqs :: RoundingMode -> [(String, CW)] -> [String]
|
||||||
nonEqs _ [] = []
|
nonEqs rm scs = format $ interp ps ++ disallow (map eqClass uninterpClasses)
|
||||||
nonEqs rm [sc] = ["(assert " ++ nonEq rm sc ++ ")"]
|
where (ups, ps) = partition (isUninterpreted . snd) scs
|
||||||
nonEqs rm (sc:r) = ["(assert (or " ++ nonEq rm sc]
|
format [] = []
|
||||||
++ map ((" " ++) . nonEq rm) r
|
format [m] = ["(assert " ++ m ++ ")"]
|
||||||
++ [" ))"]
|
format (m:ms) = ["(assert (or " ++ m]
|
||||||
|
++ map (" " ++) ms
|
||||||
|
++ [" ))"]
|
||||||
|
-- Regular (or interpreted) sorts simply get a constraint that we disallow the current assignment
|
||||||
|
interp = map $ nonEq rm
|
||||||
|
-- Determine the equivalnce classes of uninterpreted sorts:
|
||||||
|
uninterpClasses = filter (\l -> length l > 1) -- Only need this class if it has at least two members
|
||||||
|
. map (map fst) -- throw away sorts, we only need the names
|
||||||
|
. groupBy ((==) `on` snd) -- make sure they belong to the same sort and have the same value
|
||||||
|
. sortBy (comparing snd) -- sort them according to their sorts first
|
||||||
|
$ ups -- take the uninterpreted sorts
|
||||||
|
-- Uninterpreted sorts get a constraint that says the equivalence classes as determined by the solver are disallowed:
|
||||||
|
eqClass :: [String] -> String
|
||||||
|
eqClass [] = error "SBV.allSat.nonEqs: Impossible happened, disallow received an empty list"
|
||||||
|
eqClass cs = "(= " ++ unwords cs ++ ")"
|
||||||
|
-- Now, take the conjunction of equivalence classes and assert it's negation:
|
||||||
|
disallow = map $ \ec -> "(not " ++ ec ++ ")"
|
||||||
|
|
||||||
nonEq :: RoundingMode -> (String, CW) -> String
|
nonEq :: RoundingMode -> (String, CW) -> String
|
||||||
nonEq rm (s, c) = "(not (= " ++ s ++ " " ++ cvtCW rm c ++ "))"
|
nonEq rm (s, c) = "(not (= " ++ s ++ " " ++ cvtCW rm c ++ "))"
|
||||||
@ -61,6 +79,7 @@ tbd e = error $ "SBV.SMTLib2: Not-yet-supported: " ++ e
|
|||||||
|
|
||||||
-- | Translate a problem into an SMTLib2 script
|
-- | Translate a problem into an SMTLib2 script
|
||||||
cvt :: RoundingMode -- ^ User selected rounding mode to be used for floating point arithmetic
|
cvt :: RoundingMode -- ^ User selected rounding mode to be used for floating point arithmetic
|
||||||
|
-> Maybe Logic -- ^ SMT-Lib logic, if requested by the user
|
||||||
-> SolverCapabilities -- ^ capabilities of the current solver
|
-> SolverCapabilities -- ^ capabilities of the current solver
|
||||||
-> Set.Set Kind -- ^ kinds used
|
-> Set.Set Kind -- ^ kinds used
|
||||||
-> Bool -- ^ is this a sat problem?
|
-> Bool -- ^ is this a sat problem?
|
||||||
@ -76,7 +95,7 @@ cvt :: RoundingMode -- ^ User selected rounding mode to be used
|
|||||||
-> [SW] -- ^ extra constraints
|
-> [SW] -- ^ extra constraints
|
||||||
-> SW -- ^ output variable
|
-> SW -- ^ output variable
|
||||||
-> ([String], [String])
|
-> ([String], [String])
|
||||||
cvt rm solverCaps kindInfo isSat comments inputs skolemInps consts tbls arrs uis axs (SBVPgm asgnsSeq) cstrs out = (pre, [])
|
cvt rm smtLogic solverCaps kindInfo isSat comments inputs skolemInps consts tbls arrs uis axs (SBVPgm asgnsSeq) cstrs out = (pre, [])
|
||||||
where -- the logic is an over-approaximation
|
where -- the logic is an over-approaximation
|
||||||
hasInteger = KUnbounded `Set.member` kindInfo
|
hasInteger = KUnbounded `Set.member` kindInfo
|
||||||
hasReal = KReal `Set.member` kindInfo
|
hasReal = KReal `Set.member` kindInfo
|
||||||
@ -85,6 +104,8 @@ cvt rm solverCaps kindInfo isSat comments inputs skolemInps consts tbls arrs uis
|
|||||||
hasBVs = not $ null [() | KBounded{} <- Set.toList kindInfo]
|
hasBVs = not $ null [() | KBounded{} <- Set.toList kindInfo]
|
||||||
sorts = [s | KUninterpreted s <- Set.toList kindInfo]
|
sorts = [s | KUninterpreted s <- Set.toList kindInfo]
|
||||||
logic
|
logic
|
||||||
|
| Just l <- smtLogic
|
||||||
|
= ["(set-logic " ++ show l ++ ") ; NB. User specified."]
|
||||||
| hasDouble || hasFloat -- NB. We don't check for quantifiers here, we probably should..
|
| hasDouble || hasFloat -- NB. We don't check for quantifiers here, we probably should..
|
||||||
= if hasBVs
|
= if hasBVs
|
||||||
then ["(set-logic QF_FPABV)"]
|
then ["(set-logic QF_FPABV)"]
|
||||||
@ -244,7 +265,7 @@ swFunType :: [SW] -> SW -> String
|
|||||||
swFunType ss s = "(" ++ unwords (map swType ss) ++ ") " ++ swType s
|
swFunType ss s = "(" ++ unwords (map swType ss) ++ ") " ++ swType s
|
||||||
|
|
||||||
smtType :: Kind -> String
|
smtType :: Kind -> String
|
||||||
smtType (KBounded False 1) = "Bool"
|
smtType KBool = "Bool"
|
||||||
smtType (KBounded _ sz) = "(_ BitVec " ++ show sz ++ ")"
|
smtType (KBounded _ sz) = "(_ BitVec " ++ show sz ++ ")"
|
||||||
smtType KUnbounded = "Int"
|
smtType KUnbounded = "Int"
|
||||||
smtType KReal = "Real"
|
smtType KReal = "Real"
|
||||||
@ -267,14 +288,16 @@ cvtSW skolemMap s
|
|||||||
| True
|
| True
|
||||||
= show s
|
= show s
|
||||||
|
|
||||||
-- SMTLib2 requires the width of literals to match the type exactly;
|
-- Carefully code hex numbers, SMTLib is picky about lengths of hex constants. For the time
|
||||||
-- hexadecimal works only for sizes that are multiples of 4.
|
-- being, SBV only supports sizes that are multiples of 4, but the below code is more robust
|
||||||
|
-- in case of future extensions to support arbitrary sizes.
|
||||||
hex :: Int -> Integer -> String
|
hex :: Int -> Integer -> String
|
||||||
|
hex 1 v = "#b" ++ show v
|
||||||
hex sz v
|
hex sz v
|
||||||
| sz `mod` 4 == 0 = "#x" ++ pad (sz `div` 4) (showHex v "")
|
| sz `mod` 4 == 0 = "#x" ++ pad (sz `div` 4) (showHex v "")
|
||||||
| otherwise = "#b" ++ pad sz (showBin v "")
|
| True = "#b" ++ pad sz (showBin v "")
|
||||||
where pad n s = replicate (n - length s) '0' ++ s
|
where pad n s = replicate (n - length s) '0' ++ s
|
||||||
showBin = showIntAtBase 2 intToDigit
|
showBin = showIntAtBase 2 intToDigit
|
||||||
|
|
||||||
cvtCW :: RoundingMode -> CW -> String
|
cvtCW :: RoundingMode -> CW -> String
|
||||||
cvtCW rm x
|
cvtCW rm x
|
||||||
@ -318,7 +341,8 @@ cvtExp rm skolemMap tableMap expr@(SBVApp _ arguments) = sh expr
|
|||||||
boolOp = all isBoolean arguments
|
boolOp = all isBoolean arguments
|
||||||
bad | intOp = error $ "SBV.SMTLib2: Unsupported operation on unbounded integers: " ++ show expr
|
bad | intOp = error $ "SBV.SMTLib2: Unsupported operation on unbounded integers: " ++ show expr
|
||||||
| True = error $ "SBV.SMTLib2: Unsupported operation on real values: " ++ show expr
|
| True = error $ "SBV.SMTLib2: Unsupported operation on real values: " ++ show expr
|
||||||
ensureBV = bvOp || bad
|
ensureBVOrBool = bvOp || boolOp || bad
|
||||||
|
ensureBV = bvOp || bad
|
||||||
addRM s = s ++ " " ++ smtRoundingMode rm
|
addRM s = s ++ " " ++ smtRoundingMode rm
|
||||||
lift2 o _ [x, y] = "(" ++ o ++ " " ++ x ++ " " ++ y ++ ")"
|
lift2 o _ [x, y] = "(" ++ o ++ " " ++ x ++ " " ++ y ++ ")"
|
||||||
lift2 o _ sbvs = error $ "SBV.SMTLib2.sh.lift2: Unexpected arguments: " ++ show (o, sbvs)
|
lift2 o _ sbvs = error $ "SBV.SMTLib2.sh.lift2: Unexpected arguments: " ++ show (o, sbvs)
|
||||||
@ -351,6 +375,7 @@ cvtExp rm skolemMap tableMap expr@(SBVApp _ arguments) = sh expr
|
|||||||
| needsCheck = "(ite " ++ cond ++ ssw e ++ " " ++ lkUp ++ ")"
|
| needsCheck = "(ite " ++ cond ++ ssw e ++ " " ++ lkUp ++ ")"
|
||||||
| True = lkUp
|
| True = lkUp
|
||||||
where needsCheck = case aKnd of
|
where needsCheck = case aKnd of
|
||||||
|
KBool -> (2::Integer) > fromIntegral l
|
||||||
KBounded _ n -> (2::Integer)^n > fromIntegral l
|
KBounded _ n -> (2::Integer)^n > fromIntegral l
|
||||||
KUnbounded -> True
|
KUnbounded -> True
|
||||||
KReal -> error "SBV.SMT.SMTLib2.cvtExp: unexpected real valued index"
|
KReal -> error "SBV.SMT.SMTLib2.cvtExp: unexpected real valued index"
|
||||||
@ -362,6 +387,7 @@ cvtExp rm skolemMap tableMap expr@(SBVApp _ arguments) = sh expr
|
|||||||
| hasSign i = "(or " ++ le0 ++ " " ++ gtl ++ ") "
|
| hasSign i = "(or " ++ le0 ++ " " ++ gtl ++ ") "
|
||||||
| True = gtl ++ " "
|
| True = gtl ++ " "
|
||||||
(less, leq) = case aKnd of
|
(less, leq) = case aKnd of
|
||||||
|
KBool -> error "SBV.SMT.SMTLib2.cvtExp: unexpected boolean valued index"
|
||||||
KBounded{} -> if hasSign i then ("bvslt", "bvsle") else ("bvult", "bvule")
|
KBounded{} -> if hasSign i then ("bvslt", "bvsle") else ("bvult", "bvule")
|
||||||
KUnbounded -> ("<", "<=")
|
KUnbounded -> ("<", "<=")
|
||||||
KReal -> ("<", "<=")
|
KReal -> ("<", "<=")
|
||||||
@ -402,7 +428,7 @@ cvtExp rm skolemMap tableMap expr@(SBVApp _ arguments) = sh expr
|
|||||||
| intOp = "(div " ++ ssw a ++ " " ++ show (bit i :: Integer) ++ ")" -- Implement shiftR by division by 2^i
|
| intOp = "(div " ++ ssw a ++ " " ++ show (bit i :: Integer) ++ ")" -- Implement shiftR by division by 2^i
|
||||||
| True = bad
|
| True = bad
|
||||||
sh (SBVApp op args)
|
sh (SBVApp op args)
|
||||||
| Just f <- lookup op smtBVOpTable, ensureBV
|
| Just f <- lookup op smtBVOpTable, ensureBVOrBool
|
||||||
= f (any hasSign args) (map ssw args)
|
= f (any hasSign args) (map ssw args)
|
||||||
where -- The first 4 operators below do make sense for Integer's in Haskell, but there's
|
where -- The first 4 operators below do make sense for Integer's in Haskell, but there's
|
||||||
-- no obvious counterpart for them in the SMTLib translation.
|
-- no obvious counterpart for them in the SMTLib translation.
|
||||||
@ -446,6 +472,7 @@ cvtExp rm skolemMap tableMap expr@(SBVApp _ arguments) = sh expr
|
|||||||
, (Rem, lift2 "mod")
|
, (Rem, lift2 "mod")
|
||||||
]
|
]
|
||||||
smtOpFloatDoubleTable = smtIntRealShared
|
smtOpFloatDoubleTable = smtIntRealShared
|
||||||
|
++ [(Quot, lift2WM "/")]
|
||||||
smtIntRealShared = [ (Plus, lift2WM "+")
|
smtIntRealShared = [ (Plus, lift2WM "+")
|
||||||
, (Minus, lift2WM "-")
|
, (Minus, lift2WM "-")
|
||||||
, (Times, lift2WM "*")
|
, (Times, lift2WM "*")
|
||||||
|
@ -22,13 +22,13 @@ import Data.SBV.BitVectors.Data
|
|||||||
-- warm-up count and the convergence factor. Maximum iteration count can also
|
-- warm-up count and the convergence factor. Maximum iteration count can also
|
||||||
-- be specified, at which point convergence won't be sought. The boolean controls verbosity.
|
-- be specified, at which point convergence won't be sought. The boolean controls verbosity.
|
||||||
expectedValueWith :: Outputtable a => Bool -> Int -> Maybe Int -> Double -> Symbolic a -> IO [Double]
|
expectedValueWith :: Outputtable a => Bool -> Int -> Maybe Int -> Double -> Symbolic a -> IO [Double]
|
||||||
expectedValueWith verbose warmupCount mbMaxIter epsilon m
|
expectedValueWith chatty warmupCount mbMaxIter epsilon m
|
||||||
| warmupCount < 0 || epsilon < 0
|
| warmupCount < 0 || epsilon < 0
|
||||||
= error $ "SBV.expectedValue: warmup count and epsilon both must be non-negative, received: " ++ show (warmupCount, epsilon)
|
= error $ "SBV.expectedValue: warmup count and epsilon both must be non-negative, received: " ++ show (warmupCount, epsilon)
|
||||||
| True
|
| True
|
||||||
= warmup warmupCount (repeat 0) >>= go warmupCount
|
= warmup warmupCount (repeat 0) >>= go warmupCount
|
||||||
where progress s | not verbose = return ()
|
where progress s | not chatty = return ()
|
||||||
| True = putStr $ "\r*** " ++ s
|
| True = putStr $ "\r*** " ++ s
|
||||||
warmup :: Int -> [Integer] -> IO [Integer]
|
warmup :: Int -> [Integer] -> IO [Integer]
|
||||||
warmup 0 v = do progress $ "Warmup complete, performed " ++ show warmupCount ++ " rounds.\n"
|
warmup 0 v = do progress $ "Warmup complete, performed " ++ show warmupCount ++ " rounds.\n"
|
||||||
return v
|
return v
|
||||||
@ -42,7 +42,7 @@ expectedValueWith verbose warmupCount mbMaxIter epsilon m
|
|||||||
let cval o = case o `lookup` cs of
|
let cval o = case o `lookup` cs of
|
||||||
Nothing -> error "SBV.expectedValue: Cannot compute expected-values in the presence of uninterpreted constants!"
|
Nothing -> error "SBV.expectedValue: Cannot compute expected-values in the presence of uninterpreted constants!"
|
||||||
Just cw -> case (cwKind cw, cwVal cw) of
|
Just cw -> case (cwKind cw, cwVal cw) of
|
||||||
(KBounded False 1, _) -> if cwToBool cw then 1 else 0
|
(KBool, _) -> if cwToBool cw then 1 else 0
|
||||||
(KBounded{}, CWInteger v) -> v
|
(KBounded{}, CWInteger v) -> v
|
||||||
(KUnbounded, CWInteger v) -> v
|
(KUnbounded, CWInteger v) -> v
|
||||||
(KReal, _) -> error "Cannot compute expected-values for real valued results."
|
(KReal, _) -> error "Cannot compute expected-values for real valued results."
|
||||||
|
@ -111,7 +111,7 @@ haskell vname vs = intercalate "\n" $ [ "-- Automatically generated by SBV. Do n
|
|||||||
valOf [x] = s x
|
valOf [x] = s x
|
||||||
valOf xs = "[" ++ intercalate ", " (map s xs) ++ "]"
|
valOf xs = "[" ++ intercalate ", " (map s xs) ++ "]"
|
||||||
t cw = case kindOf cw of
|
t cw = case kindOf cw of
|
||||||
KBounded False 1 -> "Bool"
|
KBool -> "Bool"
|
||||||
KBounded False 8 -> "Word8"
|
KBounded False 8 -> "Word8"
|
||||||
KBounded False 16 -> "Word16"
|
KBounded False 16 -> "Word16"
|
||||||
KBounded False 32 -> "Word32"
|
KBounded False 32 -> "Word32"
|
||||||
@ -127,7 +127,7 @@ haskell vname vs = intercalate "\n" $ [ "-- Automatically generated by SBV. Do n
|
|||||||
KUninterpreted us -> error $ "SBV.renderTest: Unsupported uninterpreted sort: " ++ us
|
KUninterpreted us -> error $ "SBV.renderTest: Unsupported uninterpreted sort: " ++ us
|
||||||
_ -> error $ "SBV.renderTest: Unexpected CW: " ++ show cw
|
_ -> error $ "SBV.renderTest: Unexpected CW: " ++ show cw
|
||||||
s cw = case cwKind cw of
|
s cw = case cwKind cw of
|
||||||
KBounded False 1 -> take 5 (show (cwToBool cw) ++ repeat ' ')
|
KBool -> take 5 (show (cwToBool cw) ++ repeat ' ')
|
||||||
KBounded sgn sz -> let CWInteger w = cwVal cw in shex False True (sgn, sz) w
|
KBounded sgn sz -> let CWInteger w = cwVal cw in shex False True (sgn, sz) w
|
||||||
KUnbounded -> let CWInteger w = cwVal cw in shexI False True w
|
KUnbounded -> let CWInteger w = cwVal cw in shexI False True w
|
||||||
KFloat -> let CWFloat w = cwVal cw in showHFloat w
|
KFloat -> let CWFloat w = cwVal cw in showHFloat w
|
||||||
@ -198,7 +198,7 @@ c n vs = intercalate "\n" $
|
|||||||
]
|
]
|
||||||
where mkField p cw i = " " ++ t ++ " " ++ p ++ show i ++ ";"
|
where mkField p cw i = " " ++ t ++ " " ++ p ++ show i ++ ";"
|
||||||
where t = case cwKind cw of
|
where t = case cwKind cw of
|
||||||
KBounded False 1 -> "SBool"
|
KBool -> "SBool"
|
||||||
KBounded False 8 -> "SWord8"
|
KBounded False 8 -> "SWord8"
|
||||||
KBounded False 16 -> "SWord16"
|
KBounded False 16 -> "SWord16"
|
||||||
KBounded False 32 -> "SWord32"
|
KBounded False 32 -> "SWord32"
|
||||||
@ -215,7 +215,7 @@ c n vs = intercalate "\n" $
|
|||||||
_ -> error $ "SBV.renderTest: Unexpected CW: " ++ show cw
|
_ -> error $ "SBV.renderTest: Unexpected CW: " ++ show cw
|
||||||
mkLine (is, os) = "{{" ++ intercalate ", " (map v is) ++ "}, {" ++ intercalate ", " (map v os) ++ "}}"
|
mkLine (is, os) = "{{" ++ intercalate ", " (map v is) ++ "}, {" ++ intercalate ", " (map v os) ++ "}}"
|
||||||
v cw = case cwKind cw of
|
v cw = case cwKind cw of
|
||||||
KBounded False 1 -> if cwToBool cw then "true " else "false"
|
KBool -> if cwToBool cw then "true " else "false"
|
||||||
KBounded sgn sz -> let CWInteger w = cwVal cw in shex False True (sgn, sz) w
|
KBounded sgn sz -> let CWInteger w = cwVal cw in shex False True (sgn, sz) w
|
||||||
KUnbounded -> let CWInteger w = cwVal cw in shexI False True w
|
KUnbounded -> let CWInteger w = cwVal cw in shexI False True w
|
||||||
KFloat -> let CWFloat w = cwVal cw in showCFloat w
|
KFloat -> let CWFloat w = cwVal cw in showCFloat w
|
||||||
@ -231,11 +231,11 @@ c n vs = intercalate "\n" $
|
|||||||
inp cw i = mkBool cw (n ++ "[i].input.i" ++ show i)
|
inp cw i = mkBool cw (n ++ "[i].input.i" ++ show i)
|
||||||
out cw i = mkBool cw (n ++ "[i].output.o" ++ show i)
|
out cw i = mkBool cw (n ++ "[i].output.o" ++ show i)
|
||||||
mkBool cw s = case cwKind cw of
|
mkBool cw s = case cwKind cw of
|
||||||
KBounded False 1 -> "(" ++ s ++ " == true) ? \"true \" : \"false\""
|
KBool -> "(" ++ s ++ " == true) ? \"true \" : \"false\""
|
||||||
_ -> s
|
_ -> s
|
||||||
fmtString = unwords (map fmt is) ++ " -> " ++ unwords (map fmt os)
|
fmtString = unwords (map fmt is) ++ " -> " ++ unwords (map fmt os)
|
||||||
fmt cw = case cwKind cw of
|
fmt cw = case cwKind cw of
|
||||||
KBounded False 1 -> "%s"
|
KBool -> "%s"
|
||||||
KBounded False 8 -> "0x%02\"PRIx8\""
|
KBounded False 8 -> "0x%02\"PRIx8\""
|
||||||
KBounded False 16 -> "0x%04\"PRIx16\"U"
|
KBounded False 16 -> "0x%04\"PRIx16\"U"
|
||||||
KBounded False 32 -> "0x%08\"PRIx32\"UL"
|
KBounded False 32 -> "0x%08\"PRIx32\"UL"
|
||||||
@ -267,7 +267,7 @@ forte vname bigEndian ss vs = intercalate "\n" $ [ "// Automatically generated b
|
|||||||
toF True = '1'
|
toF True = '1'
|
||||||
toF False = '0'
|
toF False = '0'
|
||||||
blast cw = case cwKind cw of
|
blast cw = case cwKind cw of
|
||||||
KBounded False 1 -> [toF (cwToBool cw)]
|
KBool -> [toF (cwToBool cw)]
|
||||||
KBounded False 8 -> xlt 8 (cwVal cw)
|
KBounded False 8 -> xlt 8 (cwVal cw)
|
||||||
KBounded False 16 -> xlt 16 (cwVal cw)
|
KBounded False 16 -> xlt 16 (cwVal cw)
|
||||||
KBounded False 32 -> xlt 32 (cwVal cw)
|
KBounded False 32 -> xlt 32 (cwVal cw)
|
||||||
|
@ -19,7 +19,7 @@ import Data.Maybe (fromJust)
|
|||||||
import Data.SBV.BitVectors.Data
|
import Data.SBV.BitVectors.Data
|
||||||
import Data.SBV.BitVectors.Model (OrdSymbolic(..), EqSymbolic(..))
|
import Data.SBV.BitVectors.Model (OrdSymbolic(..), EqSymbolic(..))
|
||||||
import Data.SBV.Provers.Prover (satWith, defaultSMTCfg)
|
import Data.SBV.Provers.Prover (satWith, defaultSMTCfg)
|
||||||
import Data.SBV.SMT.SMT (SatModel, getModel, SMTConfig(..))
|
import Data.SBV.SMT.SMT (SatModel, getModel)
|
||||||
import Data.SBV.Utils.Boolean
|
import Data.SBV.Utils.Boolean
|
||||||
|
|
||||||
-- | Optimizer configuration. Note that iterative and quantified approaches are in general not interchangeable.
|
-- | Optimizer configuration. Note that iterative and quantified approaches are in general not interchangeable.
|
||||||
|
@ -9,12 +9,13 @@
|
|||||||
-- Implementation of polynomial arithmetic
|
-- Implementation of polynomial arithmetic
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE PatternGuards #-}
|
{-# LANGUAGE PatternGuards #-}
|
||||||
{-# LANGUAGE TypeSynonymInstances #-}
|
{-# LANGUAGE TypeSynonymInstances #-}
|
||||||
|
|
||||||
module Data.SBV.Tools.Polynomial {-(Polynomial(..), crc, crcBV)-} where
|
module Data.SBV.Tools.Polynomial (Polynomial(..), crc, crcBV, ites, addPoly, mdp) where
|
||||||
|
|
||||||
import Data.Bits (Bits(..))
|
import Data.Bits (Bits(..))
|
||||||
import Data.List (genericTake)
|
import Data.List (genericTake)
|
||||||
@ -100,7 +101,11 @@ sp st a
|
|||||||
| True = foldr (\x y -> sh x ++ " + " ++ y) (sh (last cs)) (init cs) ++ t
|
| True = foldr (\x y -> sh x ++ " + " ++ y) (sh (last cs)) (init cs) ++ t
|
||||||
where t | st = " :: GF(2^" ++ show n ++ ")"
|
where t | st = " :: GF(2^" ++ show n ++ ")"
|
||||||
| True = ""
|
| True = ""
|
||||||
|
#if __GLASGOW_HASKELL__ >= 708
|
||||||
|
n = maybe (error "SBV.Polynomial.sp: Unexpected non-finite usage!") id (bitSizeMaybe a)
|
||||||
|
#else
|
||||||
n = bitSize a
|
n = bitSize a
|
||||||
|
#endif
|
||||||
is = [n-1, n-2 .. 0]
|
is = [n-1, n-2 .. 0]
|
||||||
cs = map fst $ filter snd $ zip is (map (testBit a) is)
|
cs = map fst $ filter snd $ zip is (map (testBit a) is)
|
||||||
sh 0 = "1"
|
sh 0 = "1"
|
||||||
|
Loading…
Reference in New Issue
Block a user