Encoding extended naturals in SBV












2















I'm experimenting with the following way of encoding extended naturals in SMT-LIB (I define a datatype analogous to Maybe Integer):



; extended integers -- if first field is true, then the value is infinity
(declare-datatypes () ((IntX (mk-int-x (is-infty Bool) (not-infty Int)))))

; addition
(define-fun plus ((x IntX) (y IntX)) IntX
(ite (or (is-infty x) (is-infty y))
(mk-int-x true 0)
(mk-int-x false (+ (not-infty x) (not-infty y)))))

(declare-fun x () IntX)
(assert (= x (plus x (mk-int-x false 1))))
; x = x+1 when x |-> infty
(get-model)
(exit)


How would I go about to encode this in SBV? I tried the following, but that just crashed SBV. Also I somehow doubt that this would do what I want, but I'm not familiar enough with how SBV works.



!/usr/bin/env stack
{- stack script
--resolver nightly-2018-11-23
--package sbv
--package syb
-}

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Generics
import Data.SBV

data IntX = IntX (Maybe Integer) deriving (Eq, Ord, Data, Read, Show, SymWord, HasKind)

pretty :: IntX -> String
pretty = case
IntX Nothing -> "∞"
IntX n -> show n

instance Num IntX where
(+) (IntX x) (IntX y) = IntX $ (+) <$> x <*> y
(*) (IntX x) (IntX y) = IntX $ (*) <$> x <*> y
fromInteger = IntX . Just

ex1 = sat $ do
x :: SBV IntX <- free "x"
return $ x .== x + 1

main :: IO ()
main = print =<< ex1




~/temp ✘ ./sbv.hs
sbv.hs: SBV.SMT.SMTLib2.cvtExp.sh: impossible happened; can't translate: s0 + s1
CallStack (from HasCallStack):

error, called at ./Data/SBV/SMT/SMTLib2.hs:681:13 in sbv-7.12-9AiNAYtrUhB8YA6mr6BTn4:Data.SBV.SMT.SMTLib2









share|improve this question

























  • The functions abs, negate and signum has not implemented for class Num. Otherwise, the code seems no problem.

    – assembly.jc
    Nov 23 '18 at 12:38











  • Hm, I've implemented the missing methods, but it crashes all the same.

    – buggymcbugfix
    Nov 23 '18 at 14:14
















2















I'm experimenting with the following way of encoding extended naturals in SMT-LIB (I define a datatype analogous to Maybe Integer):



; extended integers -- if first field is true, then the value is infinity
(declare-datatypes () ((IntX (mk-int-x (is-infty Bool) (not-infty Int)))))

; addition
(define-fun plus ((x IntX) (y IntX)) IntX
(ite (or (is-infty x) (is-infty y))
(mk-int-x true 0)
(mk-int-x false (+ (not-infty x) (not-infty y)))))

(declare-fun x () IntX)
(assert (= x (plus x (mk-int-x false 1))))
; x = x+1 when x |-> infty
(get-model)
(exit)


How would I go about to encode this in SBV? I tried the following, but that just crashed SBV. Also I somehow doubt that this would do what I want, but I'm not familiar enough with how SBV works.



!/usr/bin/env stack
{- stack script
--resolver nightly-2018-11-23
--package sbv
--package syb
-}

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Generics
import Data.SBV

data IntX = IntX (Maybe Integer) deriving (Eq, Ord, Data, Read, Show, SymWord, HasKind)

pretty :: IntX -> String
pretty = case
IntX Nothing -> "∞"
IntX n -> show n

instance Num IntX where
(+) (IntX x) (IntX y) = IntX $ (+) <$> x <*> y
(*) (IntX x) (IntX y) = IntX $ (*) <$> x <*> y
fromInteger = IntX . Just

ex1 = sat $ do
x :: SBV IntX <- free "x"
return $ x .== x + 1

main :: IO ()
main = print =<< ex1




~/temp ✘ ./sbv.hs
sbv.hs: SBV.SMT.SMTLib2.cvtExp.sh: impossible happened; can't translate: s0 + s1
CallStack (from HasCallStack):

error, called at ./Data/SBV/SMT/SMTLib2.hs:681:13 in sbv-7.12-9AiNAYtrUhB8YA6mr6BTn4:Data.SBV.SMT.SMTLib2









share|improve this question

























  • The functions abs, negate and signum has not implemented for class Num. Otherwise, the code seems no problem.

    – assembly.jc
    Nov 23 '18 at 12:38











  • Hm, I've implemented the missing methods, but it crashes all the same.

    – buggymcbugfix
    Nov 23 '18 at 14:14














2












2








2


1






I'm experimenting with the following way of encoding extended naturals in SMT-LIB (I define a datatype analogous to Maybe Integer):



; extended integers -- if first field is true, then the value is infinity
(declare-datatypes () ((IntX (mk-int-x (is-infty Bool) (not-infty Int)))))

; addition
(define-fun plus ((x IntX) (y IntX)) IntX
(ite (or (is-infty x) (is-infty y))
(mk-int-x true 0)
(mk-int-x false (+ (not-infty x) (not-infty y)))))

(declare-fun x () IntX)
(assert (= x (plus x (mk-int-x false 1))))
; x = x+1 when x |-> infty
(get-model)
(exit)


How would I go about to encode this in SBV? I tried the following, but that just crashed SBV. Also I somehow doubt that this would do what I want, but I'm not familiar enough with how SBV works.



!/usr/bin/env stack
{- stack script
--resolver nightly-2018-11-23
--package sbv
--package syb
-}

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Generics
import Data.SBV

data IntX = IntX (Maybe Integer) deriving (Eq, Ord, Data, Read, Show, SymWord, HasKind)

pretty :: IntX -> String
pretty = case
IntX Nothing -> "∞"
IntX n -> show n

instance Num IntX where
(+) (IntX x) (IntX y) = IntX $ (+) <$> x <*> y
(*) (IntX x) (IntX y) = IntX $ (*) <$> x <*> y
fromInteger = IntX . Just

ex1 = sat $ do
x :: SBV IntX <- free "x"
return $ x .== x + 1

main :: IO ()
main = print =<< ex1




~/temp ✘ ./sbv.hs
sbv.hs: SBV.SMT.SMTLib2.cvtExp.sh: impossible happened; can't translate: s0 + s1
CallStack (from HasCallStack):

error, called at ./Data/SBV/SMT/SMTLib2.hs:681:13 in sbv-7.12-9AiNAYtrUhB8YA6mr6BTn4:Data.SBV.SMT.SMTLib2









share|improve this question
















I'm experimenting with the following way of encoding extended naturals in SMT-LIB (I define a datatype analogous to Maybe Integer):



; extended integers -- if first field is true, then the value is infinity
(declare-datatypes () ((IntX (mk-int-x (is-infty Bool) (not-infty Int)))))

; addition
(define-fun plus ((x IntX) (y IntX)) IntX
(ite (or (is-infty x) (is-infty y))
(mk-int-x true 0)
(mk-int-x false (+ (not-infty x) (not-infty y)))))

(declare-fun x () IntX)
(assert (= x (plus x (mk-int-x false 1))))
; x = x+1 when x |-> infty
(get-model)
(exit)


How would I go about to encode this in SBV? I tried the following, but that just crashed SBV. Also I somehow doubt that this would do what I want, but I'm not familiar enough with how SBV works.



!/usr/bin/env stack
{- stack script
--resolver nightly-2018-11-23
--package sbv
--package syb
-}

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Generics
import Data.SBV

data IntX = IntX (Maybe Integer) deriving (Eq, Ord, Data, Read, Show, SymWord, HasKind)

pretty :: IntX -> String
pretty = case
IntX Nothing -> "∞"
IntX n -> show n

instance Num IntX where
(+) (IntX x) (IntX y) = IntX $ (+) <$> x <*> y
(*) (IntX x) (IntX y) = IntX $ (*) <$> x <*> y
fromInteger = IntX . Just

ex1 = sat $ do
x :: SBV IntX <- free "x"
return $ x .== x + 1

main :: IO ()
main = print =<< ex1




~/temp ✘ ./sbv.hs
sbv.hs: SBV.SMT.SMTLib2.cvtExp.sh: impossible happened; can't translate: s0 + s1
CallStack (from HasCallStack):

error, called at ./Data/SBV/SMT/SMTLib2.hs:681:13 in sbv-7.12-9AiNAYtrUhB8YA6mr6BTn4:Data.SBV.SMT.SMTLib2






haskell z3 smt sbv






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 23 '18 at 14:16







buggymcbugfix

















asked Nov 23 '18 at 10:00









buggymcbugfixbuggymcbugfix

1208




1208













  • The functions abs, negate and signum has not implemented for class Num. Otherwise, the code seems no problem.

    – assembly.jc
    Nov 23 '18 at 12:38











  • Hm, I've implemented the missing methods, but it crashes all the same.

    – buggymcbugfix
    Nov 23 '18 at 14:14



















  • The functions abs, negate and signum has not implemented for class Num. Otherwise, the code seems no problem.

    – assembly.jc
    Nov 23 '18 at 12:38











  • Hm, I've implemented the missing methods, but it crashes all the same.

    – buggymcbugfix
    Nov 23 '18 at 14:14

















The functions abs, negate and signum has not implemented for class Num. Otherwise, the code seems no problem.

– assembly.jc
Nov 23 '18 at 12:38





The functions abs, negate and signum has not implemented for class Num. Otherwise, the code seems no problem.

– assembly.jc
Nov 23 '18 at 12:38













Hm, I've implemented the missing methods, but it crashes all the same.

– buggymcbugfix
Nov 23 '18 at 14:14





Hm, I've implemented the missing methods, but it crashes all the same.

– buggymcbugfix
Nov 23 '18 at 14:14












1 Answer
1






active

oldest

votes


















4














The fundamental issue here is that your code is mixing Haskell's concrete Maybe type and trying to treat it as a symbolic object. But you're on the right track with how you implemented that in SMT-Lib2: You essentially need to write the corresponding code in SBV.



I'd start with:



{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}

import Data.SBV
import Data.SBV.Control
import GHC.Generics (Generic)


This is just boilerplate; and you don't need the Data.SBV.Control import unless you want to use the query mode, but it does come in handy as we shall see.



The first thing to do is to encode your IntX type symbolically; just like you did in 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>"


Nothing above should be surprising, except perhaps the deriving of Generic and Mergeable. It simply enables SBV to be able to use ite on your extended naturals. Also note how the Show instance is careful in distinguishing concrete and symbolic values by using unliteral.



Next, we add a few convenience functions, again nothing surprising:



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))


Now we can make IntX a number:



instance Num SIntX where
(+) = liftB (+)
(*) = liftB (*)
negate = liftU negate
abs = liftU abs
signum = liftU signum
fromInteger = nat . literal


(Note that the semantics of this means oo - oo = oo, which is at best questionable. But that's besides the point. You might have to explicitly define - and deal with that as you wish. Similar comments apply for signum.)



Since you want to test for equality, we also have to define the symbolic version of that:



instance EqSymbolic SIntX where
a .== b = ite (isInf a &&& isInf b) true
$ ite (isInf a ||| isInf b) false
$ xVal a .== xVal b


Similarly, if you want to compare, you'll have to define an OrdSymbolic instance; but the idea remains the same.



We need a way to create symbolic extended naturals. The following function does it nicely:



freeSIntX :: String -> Symbolic SIntX
freeSIntX nm = do i <- sBool $ nm ++ "_isInf"
v <- sInteger $ nm ++ "_xVal"
return $ SIntX { isInf = i, xVal = v }


Strictly speaking, you don't need to name the variables. (i.e., the nm parameter isn't needed.) But I find it helpful to always name my variables for obvious reasons.



Now, we can code your example:



ex1 :: IO SatResult
ex1 = sat $ do x <- freeSIntX "x"
return $ x .== x+1


When I run this, I get:



*Main> ex1
Satisfiable. Model:
x_isInf = True :: Bool
x_xVal = 0 :: Integer


Which is what you were looking for, I believe.



When you're dealing with larger programs, it's beneficial to be able to extract IntX values more directly and program further with them. This is when the query mode comes in handy. First, a helper:



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


Now we can code:



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


And we get:



*Main> ex2
IntX Nothing


I hope this helps. I've put all this code in a gist: https://gist.github.com/LeventErkok/facfd067b813028390c89803b3a0e887






share|improve this answer





















  • 1





    Extremely helpful answer. Thank you for going into so much detail.

    – buggymcbugfix
    Nov 23 '18 at 16:36











  • PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

    – buggymcbugfix
    Nov 25 '18 at 20:39








  • 1





    You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

    – Levent Erkok
    Nov 25 '18 at 21:18











  • If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

    – buggymcbugfix
    Nov 25 '18 at 23:28






  • 1





    I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

    – Levent Erkok
    Nov 26 '18 at 2:08












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53444430%2fencoding-extended-naturals-in-sbv%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









4














The fundamental issue here is that your code is mixing Haskell's concrete Maybe type and trying to treat it as a symbolic object. But you're on the right track with how you implemented that in SMT-Lib2: You essentially need to write the corresponding code in SBV.



I'd start with:



{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}

import Data.SBV
import Data.SBV.Control
import GHC.Generics (Generic)


This is just boilerplate; and you don't need the Data.SBV.Control import unless you want to use the query mode, but it does come in handy as we shall see.



The first thing to do is to encode your IntX type symbolically; just like you did in 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>"


Nothing above should be surprising, except perhaps the deriving of Generic and Mergeable. It simply enables SBV to be able to use ite on your extended naturals. Also note how the Show instance is careful in distinguishing concrete and symbolic values by using unliteral.



Next, we add a few convenience functions, again nothing surprising:



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))


Now we can make IntX a number:



instance Num SIntX where
(+) = liftB (+)
(*) = liftB (*)
negate = liftU negate
abs = liftU abs
signum = liftU signum
fromInteger = nat . literal


(Note that the semantics of this means oo - oo = oo, which is at best questionable. But that's besides the point. You might have to explicitly define - and deal with that as you wish. Similar comments apply for signum.)



Since you want to test for equality, we also have to define the symbolic version of that:



instance EqSymbolic SIntX where
a .== b = ite (isInf a &&& isInf b) true
$ ite (isInf a ||| isInf b) false
$ xVal a .== xVal b


Similarly, if you want to compare, you'll have to define an OrdSymbolic instance; but the idea remains the same.



We need a way to create symbolic extended naturals. The following function does it nicely:



freeSIntX :: String -> Symbolic SIntX
freeSIntX nm = do i <- sBool $ nm ++ "_isInf"
v <- sInteger $ nm ++ "_xVal"
return $ SIntX { isInf = i, xVal = v }


Strictly speaking, you don't need to name the variables. (i.e., the nm parameter isn't needed.) But I find it helpful to always name my variables for obvious reasons.



Now, we can code your example:



ex1 :: IO SatResult
ex1 = sat $ do x <- freeSIntX "x"
return $ x .== x+1


When I run this, I get:



*Main> ex1
Satisfiable. Model:
x_isInf = True :: Bool
x_xVal = 0 :: Integer


Which is what you were looking for, I believe.



When you're dealing with larger programs, it's beneficial to be able to extract IntX values more directly and program further with them. This is when the query mode comes in handy. First, a helper:



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


Now we can code:



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


And we get:



*Main> ex2
IntX Nothing


I hope this helps. I've put all this code in a gist: https://gist.github.com/LeventErkok/facfd067b813028390c89803b3a0e887






share|improve this answer





















  • 1





    Extremely helpful answer. Thank you for going into so much detail.

    – buggymcbugfix
    Nov 23 '18 at 16:36











  • PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

    – buggymcbugfix
    Nov 25 '18 at 20:39








  • 1





    You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

    – Levent Erkok
    Nov 25 '18 at 21:18











  • If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

    – buggymcbugfix
    Nov 25 '18 at 23:28






  • 1





    I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

    – Levent Erkok
    Nov 26 '18 at 2:08
















4














The fundamental issue here is that your code is mixing Haskell's concrete Maybe type and trying to treat it as a symbolic object. But you're on the right track with how you implemented that in SMT-Lib2: You essentially need to write the corresponding code in SBV.



I'd start with:



{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}

import Data.SBV
import Data.SBV.Control
import GHC.Generics (Generic)


This is just boilerplate; and you don't need the Data.SBV.Control import unless you want to use the query mode, but it does come in handy as we shall see.



The first thing to do is to encode your IntX type symbolically; just like you did in 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>"


Nothing above should be surprising, except perhaps the deriving of Generic and Mergeable. It simply enables SBV to be able to use ite on your extended naturals. Also note how the Show instance is careful in distinguishing concrete and symbolic values by using unliteral.



Next, we add a few convenience functions, again nothing surprising:



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))


Now we can make IntX a number:



instance Num SIntX where
(+) = liftB (+)
(*) = liftB (*)
negate = liftU negate
abs = liftU abs
signum = liftU signum
fromInteger = nat . literal


(Note that the semantics of this means oo - oo = oo, which is at best questionable. But that's besides the point. You might have to explicitly define - and deal with that as you wish. Similar comments apply for signum.)



Since you want to test for equality, we also have to define the symbolic version of that:



instance EqSymbolic SIntX where
a .== b = ite (isInf a &&& isInf b) true
$ ite (isInf a ||| isInf b) false
$ xVal a .== xVal b


Similarly, if you want to compare, you'll have to define an OrdSymbolic instance; but the idea remains the same.



We need a way to create symbolic extended naturals. The following function does it nicely:



freeSIntX :: String -> Symbolic SIntX
freeSIntX nm = do i <- sBool $ nm ++ "_isInf"
v <- sInteger $ nm ++ "_xVal"
return $ SIntX { isInf = i, xVal = v }


Strictly speaking, you don't need to name the variables. (i.e., the nm parameter isn't needed.) But I find it helpful to always name my variables for obvious reasons.



Now, we can code your example:



ex1 :: IO SatResult
ex1 = sat $ do x <- freeSIntX "x"
return $ x .== x+1


When I run this, I get:



*Main> ex1
Satisfiable. Model:
x_isInf = True :: Bool
x_xVal = 0 :: Integer


Which is what you were looking for, I believe.



When you're dealing with larger programs, it's beneficial to be able to extract IntX values more directly and program further with them. This is when the query mode comes in handy. First, a helper:



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


Now we can code:



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


And we get:



*Main> ex2
IntX Nothing


I hope this helps. I've put all this code in a gist: https://gist.github.com/LeventErkok/facfd067b813028390c89803b3a0e887






share|improve this answer





















  • 1





    Extremely helpful answer. Thank you for going into so much detail.

    – buggymcbugfix
    Nov 23 '18 at 16:36











  • PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

    – buggymcbugfix
    Nov 25 '18 at 20:39








  • 1





    You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

    – Levent Erkok
    Nov 25 '18 at 21:18











  • If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

    – buggymcbugfix
    Nov 25 '18 at 23:28






  • 1





    I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

    – Levent Erkok
    Nov 26 '18 at 2:08














4












4








4







The fundamental issue here is that your code is mixing Haskell's concrete Maybe type and trying to treat it as a symbolic object. But you're on the right track with how you implemented that in SMT-Lib2: You essentially need to write the corresponding code in SBV.



I'd start with:



{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}

import Data.SBV
import Data.SBV.Control
import GHC.Generics (Generic)


This is just boilerplate; and you don't need the Data.SBV.Control import unless you want to use the query mode, but it does come in handy as we shall see.



The first thing to do is to encode your IntX type symbolically; just like you did in 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>"


Nothing above should be surprising, except perhaps the deriving of Generic and Mergeable. It simply enables SBV to be able to use ite on your extended naturals. Also note how the Show instance is careful in distinguishing concrete and symbolic values by using unliteral.



Next, we add a few convenience functions, again nothing surprising:



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))


Now we can make IntX a number:



instance Num SIntX where
(+) = liftB (+)
(*) = liftB (*)
negate = liftU negate
abs = liftU abs
signum = liftU signum
fromInteger = nat . literal


(Note that the semantics of this means oo - oo = oo, which is at best questionable. But that's besides the point. You might have to explicitly define - and deal with that as you wish. Similar comments apply for signum.)



Since you want to test for equality, we also have to define the symbolic version of that:



instance EqSymbolic SIntX where
a .== b = ite (isInf a &&& isInf b) true
$ ite (isInf a ||| isInf b) false
$ xVal a .== xVal b


Similarly, if you want to compare, you'll have to define an OrdSymbolic instance; but the idea remains the same.



We need a way to create symbolic extended naturals. The following function does it nicely:



freeSIntX :: String -> Symbolic SIntX
freeSIntX nm = do i <- sBool $ nm ++ "_isInf"
v <- sInteger $ nm ++ "_xVal"
return $ SIntX { isInf = i, xVal = v }


Strictly speaking, you don't need to name the variables. (i.e., the nm parameter isn't needed.) But I find it helpful to always name my variables for obvious reasons.



Now, we can code your example:



ex1 :: IO SatResult
ex1 = sat $ do x <- freeSIntX "x"
return $ x .== x+1


When I run this, I get:



*Main> ex1
Satisfiable. Model:
x_isInf = True :: Bool
x_xVal = 0 :: Integer


Which is what you were looking for, I believe.



When you're dealing with larger programs, it's beneficial to be able to extract IntX values more directly and program further with them. This is when the query mode comes in handy. First, a helper:



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


Now we can code:



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


And we get:



*Main> ex2
IntX Nothing


I hope this helps. I've put all this code in a gist: https://gist.github.com/LeventErkok/facfd067b813028390c89803b3a0e887






share|improve this answer















The fundamental issue here is that your code is mixing Haskell's concrete Maybe type and trying to treat it as a symbolic object. But you're on the right track with how you implemented that in SMT-Lib2: You essentially need to write the corresponding code in SBV.



I'd start with:



{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}

import Data.SBV
import Data.SBV.Control
import GHC.Generics (Generic)


This is just boilerplate; and you don't need the Data.SBV.Control import unless you want to use the query mode, but it does come in handy as we shall see.



The first thing to do is to encode your IntX type symbolically; just like you did in 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>"


Nothing above should be surprising, except perhaps the deriving of Generic and Mergeable. It simply enables SBV to be able to use ite on your extended naturals. Also note how the Show instance is careful in distinguishing concrete and symbolic values by using unliteral.



Next, we add a few convenience functions, again nothing surprising:



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))


Now we can make IntX a number:



instance Num SIntX where
(+) = liftB (+)
(*) = liftB (*)
negate = liftU negate
abs = liftU abs
signum = liftU signum
fromInteger = nat . literal


(Note that the semantics of this means oo - oo = oo, which is at best questionable. But that's besides the point. You might have to explicitly define - and deal with that as you wish. Similar comments apply for signum.)



Since you want to test for equality, we also have to define the symbolic version of that:



instance EqSymbolic SIntX where
a .== b = ite (isInf a &&& isInf b) true
$ ite (isInf a ||| isInf b) false
$ xVal a .== xVal b


Similarly, if you want to compare, you'll have to define an OrdSymbolic instance; but the idea remains the same.



We need a way to create symbolic extended naturals. The following function does it nicely:



freeSIntX :: String -> Symbolic SIntX
freeSIntX nm = do i <- sBool $ nm ++ "_isInf"
v <- sInteger $ nm ++ "_xVal"
return $ SIntX { isInf = i, xVal = v }


Strictly speaking, you don't need to name the variables. (i.e., the nm parameter isn't needed.) But I find it helpful to always name my variables for obvious reasons.



Now, we can code your example:



ex1 :: IO SatResult
ex1 = sat $ do x <- freeSIntX "x"
return $ x .== x+1


When I run this, I get:



*Main> ex1
Satisfiable. Model:
x_isInf = True :: Bool
x_xVal = 0 :: Integer


Which is what you were looking for, I believe.



When you're dealing with larger programs, it's beneficial to be able to extract IntX values more directly and program further with them. This is when the query mode comes in handy. First, a helper:



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


Now we can code:



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


And we get:



*Main> ex2
IntX Nothing


I hope this helps. I've put all this code in a gist: https://gist.github.com/LeventErkok/facfd067b813028390c89803b3a0e887







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 23 '18 at 16:19

























answered Nov 23 '18 at 16:01









Levent ErkokLevent Erkok

8,21011128




8,21011128








  • 1





    Extremely helpful answer. Thank you for going into so much detail.

    – buggymcbugfix
    Nov 23 '18 at 16:36











  • PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

    – buggymcbugfix
    Nov 25 '18 at 20:39








  • 1





    You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

    – Levent Erkok
    Nov 25 '18 at 21:18











  • If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

    – buggymcbugfix
    Nov 25 '18 at 23:28






  • 1





    I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

    – Levent Erkok
    Nov 26 '18 at 2:08














  • 1





    Extremely helpful answer. Thank you for going into so much detail.

    – buggymcbugfix
    Nov 23 '18 at 16:36











  • PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

    – buggymcbugfix
    Nov 25 '18 at 20:39








  • 1





    You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

    – Levent Erkok
    Nov 25 '18 at 21:18











  • If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

    – buggymcbugfix
    Nov 25 '18 at 23:28






  • 1





    I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

    – Levent Erkok
    Nov 26 '18 at 2:08








1




1





Extremely helpful answer. Thank you for going into so much detail.

– buggymcbugfix
Nov 23 '18 at 16:36





Extremely helpful answer. Thank you for going into so much detail.

– buggymcbugfix
Nov 23 '18 at 16:36













PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

– buggymcbugfix
Nov 25 '18 at 20:39







PS: in my case I am dealing with constraints over the semiring (Nat U {oo}, +, *, 0, 1), so I don't want to have to think about subtraction/inverses. Therefore, I think I will just leave out the irrelevant definitions and ignore that GHC is yelling at me... I also add the constraint v .>= 0 to the definition of freeSIntX (or more appropriately freeSNatX).

– buggymcbugfix
Nov 25 '18 at 20:39






1




1





You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

– Levent Erkok
Nov 25 '18 at 21:18





You can replace the "irrelevant" definitions with error "shouldn't be called!" just to be safe. And yes; adding constrain $ v .>= 0 to freeSNatX is a nice trick. One thing to worry about is that your modeling domain has "junk" in it, in the domain theoretic sense: A single infinity is modeled by (infinitely) many distinct elements. This can cause trouble in certain cases, but there are ways to deal with that should it be a problem.

– Levent Erkok
Nov 25 '18 at 21:18













If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

– buggymcbugfix
Nov 25 '18 at 23:28





If I understand your point right, I thought of this too, so I've decided to use xVal = -1 in the infinity case: constrain $ ite i (v .== -1) (v .>= 0). Of course this means that the boolean (isInf) constraints are sort of superfluous, because I could just denote infinity using the integer's sign bit. Not sure if that is a good idea though.

– buggymcbugfix
Nov 25 '18 at 23:28




1




1





I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

– Levent Erkok
Nov 26 '18 at 2:08





I was thinking more along allSat like situations, where you want to find all satisfying assignments. Since the model contains junk, the SMT solver would return "different" results which are essentially the same for you. (The way to deal with that is to assert the negation of the equivalence class in a query.) But looks like that's not an issue for you, and perhaps the >= -1 constraint sufficient as it eliminates the junk and lack of subtraction avoids that space. So long as it works, all is good!

– Levent Erkok
Nov 26 '18 at 2:08




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53444430%2fencoding-extended-naturals-in-sbv%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

"Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

Alcedinidae

Origin of the phrase “under your belt”?