Closed lucaspena closed 6 months ago
Which version of SBV are you using? Can you confirm it's the latest? (i.e., 10.3 from https://hackage.haskell.org/package/sbv)
The fundamental problem here is that you're mixing and matching contexts, which is not allowed. Prior versions of SBV did not enforce context-consistency, leading to confusing errors like you get above. But if you're using 10.3, you should get a proper error message.
Can you try with 10.3 and let me know if you still see an issue? (Note that you'll get an error message from SBV when you run it, but hopefully it should be self-explanatory. If not, let me know.)
Ah ok thanks, yea switching to 10.3 gets the mixing contexts error for each case. However, this arose from attempting to just read a (concrete) value from a symbolic array. First, I tried something like this:
main :: IO ()
main = S.runSMT readTest >>= print
readTest :: S.Symbolic (Maybe Integer)
readTest = do
st <- freshSt
let newSt = st { f1 = S.writeArray (f1 st) 16 10 }
return $ readSym (f1 newSt) 16
readSym :: S.SArray Integer Integer -> S.SInteger -> Maybe Integer
readSym arr k = S.unliteral $ S.readArray arr k
but this prints Nothing
where I would like it to print Just 10
. Then I resorted to trying to "prove" the value was as expected as in my original post, but that led to the mixing contexts issue. Is there an easy way to just print out the value in a symbolic array? Provided a concrete key/value was written. Thanks, let me know.
Once you're in a symbolic context, you cannot call a "nested" prove/sat etc. All the interactions have to be in the same context. (Note that this isn't an SBV limitation. This is just how SMT-solvers work, every model is constructed with respect to one unique context. If you mix/match them, then all bets are off.)
Also, the idea that "reading a concrete value" is a bit misleading. In a symbolic context, there are no concrete values: Until you call check-sat
in the underlying prover, all you have are assertions. (Which may or may not be consistent at the point where you eventually call check-sat
.) So, to do things like you want, you should start interacting with the solver instead, using the query mode. Here's an example:
import Data.SBV
import Data.SBV.Control
data St = St
{ f1 :: !Arr
, f2 :: !Arr
} deriving Show
type Arr = SArray Integer Integer
freshSt :: Symbolic St
freshSt = do
field1 <- newArray "f1" Nothing
field2 <- newArray "f2" Nothing
return $ St { f1 = field1, f2 = field2 }
main :: IO ()
main = print =<< runSMT go
where go = do st <- freshSt
let newSt = st { f1 = writeArray (f1 st) 16 10 }
readVal <- sInteger "readVal"
constrain $ readVal .== readArray (f1 newSt) 16
query $ do ensureSat
getValue readVal
This prints:
*Main> main
10
You can now use the read-value of that location and do additional assertions etc., and call check-sat again.
I should note that query mode is a bit of an advanced feature, that you shouldn't really need for basic SBV usage. In general, you don't access any value until after a model is computed, which happens at the end of a prove
/sat
call; at which point the context gets destroyed. The query
mode example is how we stay in the same context to do additional programming with values that are computed by the solver, but for most uses of SBV it shouldn't be needed.
To sum up: No value is concrete till you call check-sat
, implicitly by prove/sat
, or explicitly in the query mode yourself. And you shouldn't mix/match contexts.
I hope that gets you going. Symbolic programming is a bit of a different beast, you need to carefully orchestrate what are constraints and what are regular values; and it isn't always easy to tell which. As a rule of thumb, if you ever feel like you need to call unliteral
yourself, you're probably modeling the problem incorrectly. Calls to unliteral
shouldn't be needed by end-user programs. (It's useful for internal SBV programming, but that's a whole another topic.)
Ok, thank you, this is certainly very helpful. This issue arose from a larger program where I was trying to inspect the state as an intermediate step. I see now that's not really possible the ways I was trying before, since the context gets destroyed at each prove
/runSMT
/checkSat
/etc call. I think I have a better understanding as to how to go about this now, though I may post a new issue if I have further questions. I really appreciate your prompt and in-depth responses, thanks again!
If you need to inspect intermediate values to constrain further values then query
is the way to go. It can be finicky to use the query mode, as it's definitely a lower-model of programming, but perhaps your use case calls for it. There are some query examples on hackage: See the examples under Documentation.SBV.Examples.Queries
.
I'm closing this ticket, but feel free to open others if you run into issues. Note that the stack-overflow forum has an sbv
tag which is more appropriate for questions about SBV. (And issue tracker is better suited for bug reports.) See here: https://stackoverflow.com/questions/tagged/sbv
Hello,
I am having an issue with a simple example using multiple symbolic arrays:
When I call
test
, the smt output looks as expected, and returnsQ.E.D
. However, if I swap the two initialization lines infreshSt
and call
test
, I get the following output:Notably, the
[FAIL]
line seems to attempt to initializearray_0
fromarray_1
which doesn't exist yet. In the successful version, the corresponding line looks likeIt seems that reading/writing from the array that was not initialized first is causing problems. I don't understand why the order of initialization of the two arrays would be an issue. Please let me know if I am misunderstanding something or using symbolic arrays incorrectly or what. Thank you.