Основная проблема здесь заключается в том, что ваш код смешивает конкретный тип Maybe
на Haskell и пытается рассматривать его как символический объект.Но вы на правильном пути к тому, как вы реализовали это в SMT-Lib2: вам по сути нужно написать соответствующий код в SBV.
Я бы начал с:
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
import Data.SBV
import Data.SBV.Control
import GHC.Generics (Generic)
Это просто шаблон;и вам не нужен импорт Data.SBV.Control
, если вы не хотите использовать режим запроса, но, как мы увидим, он пригодится.
Первое, что нужно сделать, это закодировать ваш тип IntX
символически;так же, как вы сделали в SMTLib:
data SIntX = SIntX { isInf :: SBool
, xVal :: SInteger
}
deriving (Generic, Mergeable)
instance Show SIntX where
show (SIntX inf val) = case (unliteral inf, unliteral val) of
(Just True, _) -> "oo"
(Just False, Just n) -> show n
_ -> "<symbolic>"
Ничто из вышеперечисленного не должно удивлять, кроме, возможно, получения Generic
и Mergeable
.Это просто позволяет SBV использовать ite
для ваших расширенных натуральных объектов.Также обратите внимание, как экземпляр Show
осторожен в различении конкретных и символических значений с помощью unliteral
.
Далее мы добавим несколько удобных функций, опять же ничего удивительного:
inf :: SIntX
inf = SIntX { isInf = true, xVal = 0 }
nat :: SInteger -> SIntX
nat v = SIntX { isInf = false, xVal = v }
liftU :: (SInteger -> SInteger) -> SIntX -> SIntX
liftU op a = ite (isInf a) inf (nat (op (xVal a)))
liftB :: (SInteger -> SInteger -> SInteger) -> SIntX -> SIntX -> SIntX
liftB op a b = ite (isInf a ||| isInf b) inf (nat (xVal a `op` xVal b))
Теперь мы можем сделать IntX
число:
instance Num SIntX where
(+) = liftB (+)
(*) = liftB (*)
negate = liftU negate
abs = liftU abs
signum = liftU signum
fromInteger = nat . literal
(Обратите внимание, что семантика этого означает oo - oo = oo
, что в лучшем случае сомнительно. Но это помимо сути. Возможно, вам придется явно определить -
и разберитесь с этим, как хотите. Аналогичные комментарии применимы к signum
.)
Поскольку вы хотите проверить на равенство, мы также должны определить символическую версию этого:
instance EqSymbolic SIntX where
a .== b = ite (isInf a &&& isInf b) true
$ ite (isInf a ||| isInf b) false
$ xVal a .== xVal b
Аналогично, если вы хотите сравнить, вам нужно определить экземпляр OrdSymbolic
;но идея остается прежней.
Нам нужен способ создания символических расширенных натуральных чисел.Следующая функция делает это красиво:
freeSIntX :: String -> Symbolic SIntX
freeSIntX nm = do i <- sBool $ nm ++ "_isInf"
v <- sInteger $ nm ++ "_xVal"
return $ SIntX { isInf = i, xVal = v }
Строго говоря, вам не нужно называть переменные.(т. е. параметр nm
не требуется.) Но я считаю полезным всегда называть мои переменные по очевидным причинам.
Теперь мы можем написать ваш пример:
ex1 :: IO SatResult
ex1 = sat $ do x <- freeSIntX "x"
return $ x .== x+1
Когда я запускаю это, я получаю:
*Main> ex1
Satisfiable. Model:
x_isInf = True :: Bool
x_xVal = 0 :: Integer
Я думаю, именно это вы и искали.
Когда вы работаете с более крупными программами, полезно иметь возможностьизвлекать IntX
значения более напрямую и программировать их дальше.Это когда режим запроса пригодится.Сначала помощник:
data IntX = IntX (Maybe Integer) deriving Show
queryX :: SIntX -> Query IntX
queryX (SIntX {isInf, xVal}) = do
b <- getValue isInf
v <- getValue xVal
return $ IntX $ if b then Nothing
else Just v
Теперь мы можем кодировать:
ex2 :: IO ()
ex2 = runSMT $ do x <- freeSIntX "x"
constrain $ x .== x+1
query $ do cs <- checkSat
case cs of
Unk -> error "Solver said Unknown!"
Unsat -> error "Solver said Unsatisfiable!"
Sat -> do v <- queryX x
io $ print v
И мы получаем:
*Main> ex2
IntX Nothing
Надеюсь, это поможет.Я положил весь этот код в суть: https://gist.github.com/LeventErkok/facfd067b813028390c89803b3a0e887