Parsing SMTLIB using the Z3 C API without losing `push`, `pop` and `check-sat` commands - z3

I'm using the Z3_parse_smtlib2_string function from the Z3 C API (via Haskell's Z3 lib) to parse an SMTLIB file and apply some tactics to simplify its content, however I notice that any push, pop and check-sat commands appear to be swallowed by this function and do not appear in the resulting AST.
Is there anyway that I can parse this without losing these commands (and then apply the required tactics, once again without losing them)?

I don't think it's possible to do this with Z3_parse_smtlib2_string. As you can see in the documentation "It returns a formula comprising of the conjunction of assertions in the scope (up to push/pop) at the end of the string." See: https://z3prover.github.io/api/html/group__capi.html#ga7905ebec9289b9fe5debcad965f6267e
Note that the reason for this is not just mere "not-implemented" or "buggy." Look at the return type of the function you're using. It returns a Z3_ast_vector, and Z3_ast only captures "expressions" in the SMTLib language. But push/pop etc. are not considered expressions by Z3, but rather commands; i.e., they are internally represented differently. (Whether this was a conscious choice or historical is something I'm not sure about.)
I don't think there's a function to do what you're asking; i.e., can return both expressions and commands. You can ask at https://github.com/Z3Prover/z3/discussions to see if the developers can provide an alternative API, or if they already have something exposed to the users that achieves this.

Related

dafny corresponding SMT queries

I'm trying to inspect a simple looping program that finds the maximal element in an integer array. Here is the permalink here. Everything works fine, but I'm really interested in
the resulting SMT file, so I extracted it using:
$ dafny /compile:3 /proverLog:./mySMT.smt myCode.dfy
Then ran with z3 as follows:
$ z3 ./mySMT.smt
I got 3 unsat responses and I was wondering what are the corresponding 3 queries?
I looked at the *.smt file and found 11K of machine-generated SMT.
Any tips on deciphering the smt file? thanks!
If you want the resulting SMT file, then that 11K file is your answer. I imagine that looking at it will lead you to the conclusion that you don't actually want to look at the resulting SMT file.
So, I don't know what it is that you want to accomplish. If you want to learn more about your program, then the best way is to work (only) from the Dafny program text. For example, you can add more assert statements to, essentially, ask the verifier if the given condition is provable at the location of the statement.
If you're interested in how Dafny encodes its verification conditions (that is, if you yourself are a tool developer and want to learn how to generate good verification conditions), then I suggest you use the /print switch to generate the Boogie program that Dafny generates. With some understanding of the Boogie intermediate verification language, the Boogie code is readable. For a more tutorial account of how to encode a Dafny-like language into Boogie, I recommend:
"Specification and verification of object-oriented software",
K. Rustan M. Leino.
Lecture note, Marktoberdorf 2008.
Rustan
PS. Unless you insist on particular formatting, you can print your array elements without using a loop if you first convert the array's element to a sequence:
print "a = ", a[..], "\n";

Models and recursive functions

As previously asked Z3 can not provide a model for recursive functions. However, is it possible to tell Z3 (preferably via the Java API) that a model without a definition for recursive functions is sufficient (or indeed choose the functions I am interested in, essentially I do not need a model for non-constant functions)? Obviously, that might lead to queries that return sat although some functions do not have a model. Basically, the user then has to ensure that these functions do have some model.
However, I would assume this is not really realizable the way Z3 or SMT solvers work, i.e., I would assume Z3 needs a (partial) model of recursive functions to evalutate expressions.
The MBQI implementation for quantifiers doesn't really work nicely with recursive functions.
There is one thing that might do the trick for you: replace recursive function definitions by bounded-recursive functions. You add an extra argument that counts the number of unfoldings you are willing to explore of the function. Whenever you apply the recursive function in the rest of the input, set the depth parameter. You can use either algebraic data-type Peano numbers or integers.
The symbolic automata toolkit uses this approach, for example.
I found one paper that explores the idea given in Nikolaj Bjorner's answer further: "Computing with an SMT solver" by Amin, Leino and Rompf (2014).
Furthermore, I found "Satisfiability Modulo Recursive Programs" by Suter, Köksal and Kuncak (2011) which unfolds calls to recursive functions successively until the satisfiability can be decided.

Which logics are supported by z3?

Is there a complete listing of all theories/logics that z3 supports? I have consulted this SMTLIB Tutorial which provides a number of logics, but I do not believe that the list is exhaustive. The z3 documentation itself doesn't seem to specify which logics are supported.
I ask because I have an smt file which cannot be solved under any of the logics in the SMTLIB Tutorial (when specified with 'set-logic'), but can be solved when no logic is specified.
For Z3, I have not seen such a list in the documentation, but you can find it in the source code if you really want to know. The list starts around line 65 of check_logic.cpp. I parsed out the list for you using a scary awk one-liner, and found this as of May 20, 2016 (between Z3 4.4.1 and 4.4.2):
"AUFLIA", "AUFLIRA", "AUFNIRA", "LRA", "QF_ABV", "QF_AUFBV", "QF_UFBV", "QF_AUFLIA", "QF_AX", "QF_BV", "QF_IDL", "QF_RDL", "QF_LIA", "QF_LRA", "QF_NIA", "QF_NRA", "QF_UF", "QF_UFIDL", "QF_UFLIA", "QF_UFLRA", "QF_UFNRA", "UFLRA", "UFNIA", "UFBV", "QF_S"
You can compare this to the official list of SMT-LIB 2 logics.
Probably more importantly for you is what the "best logic" is for your application. It sounds like you have a large and varying set of problems that you want Z3 to apply whatever tactics it can to. In that case, for now, it's best to leave the logic unspecified. The problem is that in SMT-LIB v2.0 there was no all-encompassing logic -- the largest logic by some standards was AUFNIRA, but this does not include, for example, bit vectors. As a result, CVC4 introduced a non-standard ALL_SUPPORTED logic, and Z3 performs best for some classes of problems when no logic is specified. This shortcoming of the SMT-LIB 2.0 standard is addressed in SMT-LIB 2.5, with a new logic called "ALL". However, this is not yet supported by either Z3 or CVC4.
You specify a logic in Z3 to ensure that Z3 uses a particular strategy and engine that is typically useful for the class of formulas expressed in this logic.
If no logic is specified, then Z3 falls back to a default mode. There is no logic corresponding to
this default mode: it integrates multiple engines.

Z3: Is a custom theory extension appropriate for my application?

I have precise and validated descriptions of the behaviors of many X86 instructions in terms amenable to encoding in QF_ABV and solving directly with the standard solver (using no special solving strategies). I wrote an SMT-LIB script whose interface matches my ultimate goal perfectly:
X86State, a record sort describing x86 machine state (registers and flags as bitvectors, and memory as an array).
X86Instr, a record sort describing x86 instructions (enumerated mnemonics, operands as an ML-like discriminated union describing registers, memory expressions, etc.)
A function x86-translate taking an X86State and an X86Instr, and returning a new X86State. It decodes the X86Instr and produces a new X86State in terms of the symbolic effects of the given X86Instr on the input X86State.
It's great for prototyping: the user can write x86 easily and directly. After simplifying a formula built using the library, all functions and extraneous data types are eliminated, leaving a QF_ABV expression. I hoped that users could simply (set-logic QF_ABV) and #include my script (alas, neither the SMT-LIB standard nor Z3 support #include).
Unfortunately, by defining functions and types, the script requires theories such as uninterpreted functions, thus requiring a logic other than QF_ABV (or even QF_AUFBV due to the types). My experience with SMT solvers dictates that the lowest acceptable logic should be specified for best solving time. Also, it is unclear whether I can reuse my SMT-LIB script in a programmatic context (e.g. OCaml, Python, C) as I desire. Finally, the script is a bit verbose given the lack of higher-order functions, and my lack of access to par leading to code duplication.
Thus, despite having accomplished my technical goals, I think that SMT-LIB might be the wrong approach. Is there a more natural avenue for interacting with Z3 to implement my x86 instruction description / QF_ABV translation scheme? Is the SMT-LIB script re-usable at all in these avenues? For example, you can build "custom OCaml top-levels", i.e. interpreters with scripts "burned into them". Something like that could be nice. Or do I have to re-implement the functionality in another language, in a program that interacts with Z3 via a theory extension (C DLL)? What's the best option here?
Well, I don't think that people write .smt2 files by hand. These are usually generated automatically by some program.
I find the Z3 Python interface quite nice, so I guess you could give it a try. But you can always write a simple .smt2 dumper from any language.
BTW, do you plan releasing the specification you wrote for X86? I would be really interested!

Can Z3 check the satisfiability of recursive functions on bounded data structures?

I know that Z3 cannot check the satisfiability of formulas that contain recursive functions. But, I wonder if Z3 can handle such formulas over bounded data structures. For example, I've defined a list of length at most two in my Z3 program and a function, called last, to return the last element of the list. However, Z3 does not terminate when asked to check the satisfiability of a formula that contains last.
Is there a way to use recursive functions over bounded lists in Z3?
(Note that this related to your other question as well.) We looked at such cases as part of the Leon verifier project. What we are doing there is avoiding the use of quantifiers and instead "unrolling" the recursive function definitions: if we see the term length(lst) in the formula, we expand it using the definition of length by introducing a new equality: length(lst) = if(isNil(lst)) 0 else 1 + length(tail(lst)). You can view this as a manual quantifier instantiation procedure.
If you're interested in lists of length at most two, doing the manual instantiation for all terms, then doing it once more for the new list terms should be enough, as long as you add the term:
isCons(lst) => ((isCons(tail(lst)) => isNil(tail(tail(lst))))
for each list. In practice you of course don't want to generate these equalities and implications manually; in our case, we wrote a program that is essentially a loop around Z3 adding more such axioms when needed.
A very interesting property (very related to your question) is that it turns out that for some functions (such as length), using successive unrollings will give you a complete decision procedure. Ie. even if you don't constrain the size of the datastructures, you will eventually be able to conclude SAT or UNSAT (for the quantifier-free case).
You can find more details in our paper Satisfiability Modulo Recursive Programs, or I'm happy to give more here.
You may be interested in the work of Erik Reeber on SULFA, the ``Subclass of Unrollable List Formulas in ACL2.'' He showed in his PhD thesis how a large class of list-oriented formulas can be proven by unrolling function definitions and applying SAT-based methods. He proved decidability for the SULFA class using these methods.
See, e.g., http://www.cs.utexas.edu/~reeber/IJCAR-2006.pdf .

Resources