PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/Data/SBV/SMT/SMTLib1.hs

http://github.com/LeventErkok/sbv
Haskell | 258 lines | 208 code | 23 blank | 27 comment | 10 complexity | 42b672f1cb84a0af488478eba5b886e4 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. -----------------------------------------------------------------------------
  2. -- |
  3. -- Module : Data.SBV.SMT.SMTLib1
  4. -- Copyright : (c) Levent Erkok
  5. -- License : BSD3
  6. -- Maintainer : erkokl@gmail.com
  7. -- Stability : experimental
  8. --
  9. -- Conversion of symbolic programs to SMTLib format, Using v1 of the standard
  10. -----------------------------------------------------------------------------
  11. {-# LANGUAGE PatternGuards #-}
  12. module Data.SBV.SMT.SMTLib1(cvt, addNonEqConstraints) where
  13. import qualified Data.Foldable as F (toList)
  14. import Data.List (intercalate)
  15. import Data.SBV.BitVectors.Data
  16. -- | Add constraints to generate /new/ models. This function is used to query the SMT-solver, while
  17. -- disallowing a previous model.
  18. addNonEqConstraints :: [[(String, CW)]] -> SMTLibPgm -> Maybe String
  19. addNonEqConstraints nonEqConstraints (SMTLibPgm _ (aliasTable, pre, post)) = Just $ intercalate "\n" $
  20. pre
  21. ++ [ " ; --- refuted-models ---" ]
  22. ++ concatMap nonEqs (map (map intName) nonEqConstraints)
  23. ++ post
  24. where intName (s, c)
  25. | Just sw <- s `lookup` aliasTable = (show sw, c)
  26. | True = (s, c)
  27. nonEqs :: [(String, CW)] -> [String]
  28. nonEqs [] = []
  29. nonEqs [sc] = [" :assumption " ++ nonEq sc]
  30. nonEqs (sc:r) = [" :assumption (or " ++ nonEq sc]
  31. ++ map ((" " ++) . nonEq) r
  32. ++ [" )"]
  33. nonEq :: (String, CW) -> String
  34. nonEq (s, c) = "(not (= " ++ s ++ " " ++ cvtCW c ++ "))"
  35. -- | Translate a problem into an SMTLib1 script
  36. cvt :: (Bool, Bool) -- ^ has infinite precision integers/reals
  37. -> Bool -- ^ is this a sat problem?
  38. -> [String] -- ^ extra comments to place on top
  39. -> [String] -- ^ uninterpreted sorts
  40. -> [(Quantifier, NamedSymVar)] -- ^ inputs
  41. -> [Either SW (SW, [SW])] -- ^ skolemized version of the inputs
  42. -> [(SW, CW)] -- ^ constants
  43. -> [((Int, Kind, Kind), [SW])] -- ^ auto-generated tables
  44. -> [(Int, ArrayInfo)] -- ^ user specified arrays
  45. -> [(String, SBVType)] -- ^ uninterpreted functions/constants
  46. -> [(String, [String])] -- ^ user given axioms
  47. -> SBVPgm -- ^ assignments
  48. -> [SW] -- ^ extra constraints
  49. -> SW -- ^ output variable
  50. -> ([String], [String])
  51. cvt (hasIntegers, hasReals) isSat comments sorts qinps _skolemInps consts tbls arrs uis axs asgnsSeq cstrs out
  52. | hasIntegers
  53. = error "SBV: Unbounded integers are not supported in the SMTLib1/yices interface. (Use z3 instead.)"
  54. | hasReals
  55. = error "SBV: The real value domain is not supported in the SMTLib1/yices interface. (Use z3 instead.)"
  56. | not ((isSat && allExistential) || (not isSat && allUniversal))
  57. = error "SBV: The chosen solver does not support quantified variables. (Use z3 instead.)"
  58. | not (null sorts)
  59. = error "SBV: The chosen solver does not support unintepreted sorts. (Use z3 instead.)"
  60. | True
  61. = (pre, post)
  62. where quantifiers = map fst qinps
  63. allExistential = all (== EX) quantifiers
  64. allUniversal = all (== ALL) quantifiers
  65. logic
  66. | null tbls && null arrs && null uis = "QF_BV"
  67. | True = "QF_AUFBV"
  68. inps = map (fst . snd) qinps
  69. pre = [ "; Automatically generated by SBV. Do not edit." ]
  70. ++ map ("; " ++) comments
  71. ++ ["(benchmark sbv"
  72. , " :logic " ++ logic
  73. , " :status unknown"
  74. , " ; --- inputs ---"
  75. ]
  76. ++ map decl inps
  77. ++ [ " ; --- declarations ---" ]
  78. ++ map (decl . fst) consts
  79. ++ map (decl . fst) asgns
  80. ++ [ " ; --- constants ---" ]
  81. ++ map cvtCnst consts
  82. ++ [ " ; --- tables ---" ]
  83. ++ concatMap mkTable tbls
  84. ++ [ " ; --- arrays ---" ]
  85. ++ concatMap declArray arrs
  86. ++ [ " ; --- uninterpreted constants ---" ]
  87. ++ concatMap declUI uis
  88. ++ [ " ; --- user given axioms ---" ]
  89. ++ map declAx axs
  90. ++ [ " ; --- assignments ---" ]
  91. ++ map cvtAsgn asgns
  92. post = [ " ; --- constraints ---" ]
  93. ++ map mkCstr cstrs
  94. ++ [ " ; --- formula ---" ]
  95. ++ [mkFormula isSat out]
  96. ++ [")"]
  97. asgns = F.toList (pgmAssignments asgnsSeq)
  98. mkCstr s = " :assumption (= " ++ show s ++ " bv1[1])"
  99. -- TODO: Does this work for SMT-Lib when the index/element types are signed?
  100. -- Currently we ignore the signedness of the arguments, as there appears to be no way
  101. -- to capture that in SMT-Lib; and likely it does not matter. Would be good to check
  102. -- explicitly though.
  103. mkTable :: ((Int, Kind, Kind), [SW]) -> [String]
  104. mkTable ((i, ak, rk), elts) = (" :extrafuns ((" ++ t ++ " Array[" ++ show at ++ ":" ++ show rt ++ "]))") : zipWith mkElt elts [(0::Int)..]
  105. where t = "table" ++ show i
  106. mkElt x k = " :assumption (= (select " ++ t ++ " bv" ++ show k ++ "[" ++ show at ++ "]) " ++ show x ++ ")"
  107. (at, rt) = case (ak, rk) of
  108. (KBounded _ a, KBounded _ b) -> (a, b)
  109. _ -> die $ "mkTable: Unbounded table component: " ++ show (ak, rk)
  110. -- Unexpected input, or things we will probably never support
  111. die :: String -> a
  112. die msg = error $ "SBV->SMTLib1: Unexpected: " ++ msg
  113. declArray :: (Int, ArrayInfo) -> [String]
  114. declArray (i, (_, (ak, rk), ctx)) = adecl : ctxInfo
  115. where nm = "array_" ++ show i
  116. adecl = " :extrafuns ((" ++ nm ++ " Array[" ++ show at ++ ":" ++ show rt ++ "]))"
  117. (at, rt) = case (ak, rk) of
  118. (KBounded _ a, KBounded _ b) -> (a, b)
  119. _ -> die $ "declArray: Unbounded array component: " ++ show (ak, rk)
  120. ctxInfo = case ctx of
  121. ArrayFree Nothing -> []
  122. ArrayFree (Just sw) -> declA sw
  123. ArrayReset _ sw -> declA sw
  124. ArrayMutate j a b -> [" :assumption (= " ++ nm ++ " (store array_" ++ show j ++ " " ++ show a ++ " " ++ show b ++ "))"]
  125. ArrayMerge t j k -> [" :assumption (= " ++ nm ++ " (ite (= bv1[1] " ++ show t ++ ") array_" ++ show j ++ " array_" ++ show k ++ "))"]
  126. declA sw = let iv = nm ++ "_freeInitializer"
  127. in [ " :extrafuns ((" ++ iv ++ " BitVec[" ++ show at ++ "]))"
  128. , " :assumption (= (select " ++ nm ++ " " ++ iv ++ ") " ++ show sw ++ ")"
  129. ]
  130. declAx :: (String, [String]) -> String
  131. declAx (nm, ls) = (" ;; -- user given axiom: " ++ nm ++ "\n ") ++ intercalate "\n " ls
  132. declUI :: (String, SBVType) -> [String]
  133. declUI (i, t) = [" :extrafuns ((uninterpreted_" ++ i ++ " " ++ cvtType t ++ "))"]
  134. mkFormula :: Bool -> SW -> String
  135. mkFormula isSat s
  136. | isSat = " :formula (= " ++ show s ++ " bv1[1])"
  137. | True = " :formula (= " ++ show s ++ " bv0[1])"
  138. -- SMTLib represents signed/unsigned quantities with the same type
  139. decl :: SW -> String
  140. decl s = " :extrafuns ((" ++ show s ++ " BitVec[" ++ show (intSizeOf s) ++ "]))"
  141. cvtAsgn :: (SW, SBVExpr) -> String
  142. cvtAsgn (s, e) = " :assumption (= " ++ show s ++ " " ++ cvtExp e ++ ")"
  143. cvtCnst :: (SW, CW) -> String
  144. cvtCnst (s, c) = " :assumption (= " ++ show s ++ " " ++ cvtCW c ++ ")"
  145. -- no need to worry about Int/Real here as we don't support them with the SMTLib1 interface..
  146. cvtCW :: CW -> String
  147. cvtCW x@(CW _ (CWInteger v)) | not (hasSign x) = "bv" ++ show v ++ "[" ++ show (intSizeOf x) ++ "]"
  148. -- signed numbers (with 2's complement representation) is problematic
  149. -- since there's no way to put a bvneg over a positive number to get minBound..
  150. -- Hence, we punt and use binary notation in that particular case
  151. cvtCW x@(CW _ (CWInteger v)) | v == least = mkMinBound (intSizeOf x)
  152. where least = negate (2 ^ intSizeOf x)
  153. cvtCW x@(CW _ (CWInteger v)) = negIf (v < 0) $ "bv" ++ show (abs v) ++ "[" ++ show (intSizeOf x) ++ "]"
  154. cvtCW x = error $ "SBV.SMTLib1.cvtCW: Unexpected CW: " ++ show x -- unbounded/real, shouldn't reach here
  155. negIf :: Bool -> String -> String
  156. negIf True a = "(bvneg " ++ a ++ ")"
  157. negIf False a = a
  158. -- anamoly at the 2's complement min value! Have to use binary notation here
  159. -- as there is no positive value we can provide to make the bvneg work.. (see above)
  160. mkMinBound :: Int -> String
  161. mkMinBound i = "bv1" ++ replicate (i-1) '0' ++ "[" ++ show i ++ "]"
  162. rot :: String -> Int -> SW -> String
  163. rot o c x = "(" ++ o ++ "[" ++ show c ++ "] " ++ show x ++ ")"
  164. -- only used for bounded SWs
  165. shft :: String -> String -> Int -> SW -> String
  166. shft oW oS c x = "(" ++ o ++ " " ++ show x ++ " " ++ cvtCW c' ++ ")"
  167. where s = hasSign x
  168. c' = mkConstCW (kindOf x) c
  169. o = if s then oS else oW
  170. cvtExp :: SBVExpr -> String
  171. cvtExp (SBVApp Ite [a, b, c]) = "(ite (= bv1[1] " ++ show a ++ ") " ++ show b ++ " " ++ show c ++ ")"
  172. cvtExp (SBVApp (Rol i) [a]) = rot "rotate_left" i a
  173. cvtExp (SBVApp (Ror i) [a]) = rot "rotate_right" i a
  174. cvtExp (SBVApp (Shl i) [a]) = shft "bvshl" "bvshl" i a
  175. cvtExp (SBVApp (Shr i) [a]) = shft "bvlshr" "bvashr" i a
  176. cvtExp (SBVApp (LkUp (t, ak, _, l) i e) [])
  177. | needsCheck = "(ite " ++ cond ++ show e ++ " " ++ lkUp ++ ")"
  178. | True = lkUp
  179. where at = case ak of
  180. KBounded _ n -> n
  181. _ -> die $ "cvtExp: Unbounded lookup component" ++ show ak
  182. needsCheck = (2::Integer)^at > fromIntegral l
  183. lkUp = "(select table" ++ show t ++ " " ++ show i ++ ")"
  184. cond
  185. | hasSign i = "(or " ++ le0 ++ " " ++ gtl ++ ") "
  186. | True = gtl ++ " "
  187. (less, leq) = if hasSign i then ("bvslt", "bvsle") else ("bvult", "bvule")
  188. mkCnst = cvtCW . mkConstCW (kindOf i)
  189. le0 = "(" ++ less ++ " " ++ show i ++ " " ++ mkCnst 0 ++ ")"
  190. gtl = "(" ++ leq ++ " " ++ mkCnst l ++ " " ++ show i ++ ")"
  191. cvtExp (SBVApp (Extract i j) [a]) = "(extract[" ++ show i ++ ":" ++ show j ++ "] " ++ show a ++ ")"
  192. cvtExp (SBVApp (ArrEq i j) []) = "(ite (= array_" ++ show i ++ " array_" ++ show j ++") bv1[1] bv0[1])"
  193. cvtExp (SBVApp (ArrRead i) [a]) = "(select array_" ++ show i ++ " " ++ show a ++ ")"
  194. cvtExp (SBVApp (Uninterpreted nm) []) = "uninterpreted_" ++ nm
  195. cvtExp (SBVApp (Uninterpreted nm) args) = "(uninterpreted_" ++ nm ++ " " ++ unwords (map show args) ++ ")"
  196. cvtExp inp@(SBVApp op args)
  197. | Just f <- lookup op smtOpTable
  198. = f (any hasSign args) (map show args)
  199. | True
  200. = error $ "SBV.SMT.SMTLib1.cvtExp: impossible happened; can't translate: " ++ show inp
  201. where lift2 o _ [x, y] = "(" ++ o ++ " " ++ x ++ " " ++ y ++ ")"
  202. lift2 o _ sbvs = error $ "SBV.SMTLib1.cvtExp.lift2: Unexpected arguments: " ++ show (o, sbvs)
  203. lift2B oU oS sgn sbvs = "(ite " ++ lift2S oU oS sgn sbvs ++ " bv1[1] bv0[1])"
  204. lift2S oU oS sgn sbvs
  205. | sgn
  206. = lift2 oS sgn sbvs
  207. | True
  208. = lift2 oU sgn sbvs
  209. lift2N o sgn sbvs = "(bvnot " ++ lift2 o sgn sbvs ++ ")"
  210. lift1 o _ [x] = "(" ++ o ++ " " ++ x ++ ")"
  211. lift1 o _ sbvs = error $ "SBV.SMT.SMTLib1.cvtExp.lift1: Unexpected arguments: " ++ show (o, sbvs)
  212. smtOpTable = [ (Plus, lift2 "bvadd")
  213. , (Minus, lift2 "bvsub")
  214. , (Times, lift2 "bvmul")
  215. , (Quot, lift2S "bvudiv" "bvsdiv")
  216. , (Rem, lift2S "bvurem" "bvsrem")
  217. , (Equal, lift2 "bvcomp")
  218. , (NotEqual, lift2N "bvcomp")
  219. , (LessThan, lift2B "bvult" "bvslt")
  220. , (GreaterThan, lift2B "bvugt" "bvsgt")
  221. , (LessEq, lift2B "bvule" "bvsle")
  222. , (GreaterEq, lift2B "bvuge" "bvsge")
  223. , (And, lift2 "bvand")
  224. , (Or, lift2 "bvor")
  225. , (XOr, lift2 "bvxor")
  226. , (Not, lift1 "bvnot")
  227. , (Join, lift2 "concat")
  228. ]
  229. cvtType :: SBVType -> String
  230. cvtType (SBVType []) = error "SBV.SMT.SMTLib1.cvtType: internal: received an empty type!"
  231. cvtType (SBVType xs) = unwords $ map sh xs
  232. where sh (KBounded _ s) = "BitVec[" ++ show s ++ "]"
  233. sh KUnbounded = die "unbounded Integer"
  234. sh KReal = die "real value"
  235. sh (KUninterpreted s) = die $ "uninterpreted sort: " ++ s