sbv-14.1: Data/SBV/Tools/BVOptimize.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.SBV.Tools.BVOptimize
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Bit-vector optimization based on linear scan of the bits. The optimization
-- engines are usually not incremental, and they perform poorly for optimizing
-- bit-vector values in the presence of complicated constraints. We implement
-- a simple optimizer by scanning the bits from top-to-bottom to minimize/maximize
-- unsigned bit vector quantities, using the regular (i.e., incremental) solver.
-- This can lead to better performance for this class of problems.
--
-- This implementation is based on an idea by Nikolaj Bjorner, see <https://github.com/Z3Prover/z3/issues/7156>.
-----------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Data.SBV.Tools.BVOptimize (
-- ** Maximizing bit-vectors
-- $maxBVEx
maxBV, maxBVWith
-- ** Minimizing bit-vectors
-- $minBVEx
, minBV, minBVWith
) where
import Control.Monad
import Data.SBV
import Data.SBV.Control
#ifdef DOCTEST
-- $setup
-- >>> :set -XDataKinds
-- >>> import Data.SBV
#endif
{- $maxBVEx
Here is a simple example of maximizing a bit-vector value:
>>> :{
runSMT $ do x :: SWord 32 <- free "x"
constrain $ x .> 5
constrain $ x .< 27
maxBV False x
:}
Satisfiable. Model:
x = 26 :: Word32
-}
-- | Maximize the value of an unsigned bit-vector value, using the default solver.
maxBV :: SFiniteBits a
=> Bool -- ^ Do we want unsat-cores if unsatisfiable?
-> SBV a -- ^ Value to maximize
-> Symbolic SatResult
maxBV = maxBVWith defaultSMTCfg
-- | Maximize the value of an unsigned bit-vector value, using the given solver.
maxBVWith :: SFiniteBits a => SMTConfig -> Bool-> SBV a -> Symbolic SatResult
maxBVWith = minMaxBV True
{- $minBVEx
Here is a simple example of minimizing a bit-vector value:
>>> :{
runSMT $ do x :: SWord 32 <- free "x"
constrain $ x .> 5
constrain $ x .< 27
minBV False x
:}
Satisfiable. Model:
x = 6 :: Word32
-}
-- | Minimize the value of an unsigned bit-vector value, using the default solver.
minBV :: SFiniteBits a
=> Bool -- ^ Do we want unsat-cores if unsatisfiable?
-> SBV a -- ^ Value to minimize
-> Symbolic SatResult
minBV = minBVWith defaultSMTCfg
-- | Minimize the value of an unsigned bit-vector value, using the given solver.
minBVWith :: SFiniteBits a => SMTConfig -> Bool-> SBV a -> Symbolic SatResult
minBVWith = minMaxBV False
-- | min/max a given unsigned bit-vector. We walk down the bits in an incremental
-- fashion. If we are maximizing, we try to make the bits set as we go down, otherwise
-- we try to unset them. We keep adding the constraints so long as they are satisfiable,
-- and at the end, get the optimal value produced.
minMaxBV :: SFiniteBits a => Bool -> SMTConfig -> Bool -> SBV a -> Symbolic SatResult
minMaxBV isMax cfg getUC v
| hasSign v
= error $ "minMaxBV works on unsigned bit-vectors, received: " ++ show (kindOf v)
| True
= do when getUC $ setOption $ ProduceUnsatCores True
query $ go (blastBE v)
where uc | getUC = Just <$> getUnsatCore
| True = pure Nothing
rSat = SatResult . Satisfiable cfg <$> getModel
rUnk = SatResult . Unknown cfg <$> getUnknownReason
rUnsat = SatResult . Unsatisfiable cfg <$> uc
go :: [SBool] -> Query SatResult
go [] = do r <- checkSat
case r of
Sat -> rSat
Unsat -> rUnsat
Unk -> rUnk
DSat {} -> error "minMaxBV: Unexpected DSat result"
go (b:bs) = do push 1
if isMax then constrain b
else constrain $ sNot b
r <- checkSat
case r of
Sat -> go bs >>= \res -> pop 1 >> pure res
Unsat -> pop 1 >> go bs
Unk -> pop 1 >> rUnk
DSat{} -> error "minMaxBV: Unexpected DSat result"