3

I am using the Z3 Python interface as part of a research tool that I am writing, and I noticed some pretty odd behavior when I run the Z3 solver repeatedly on the same query: In particular, I seem to not get the same results each time, even though I explicitly reset the solver before running. For reference, here's my code:

import z3

with open("query.smt", "r") as smt_reader:
    query_lines = "".join(smt_reader.readlines())
    for i in xrange(3):
        solver = z3.Solver()
        solver.reset()

        queryExpr = z3.parse_smt2_string(query_lines)
        equivalences = queryExpr.children()[:-1]
        for equivalence in equivalences:
            solver.add(equivalence)

        # Obtain the Boolean variables associated with the constraints.
        constraintVars = [equivalence.children()[0] for equivalence
                          in equivalences]
        # Check the satisfiability of the query.
        querySatResult = solver.check(*constraintVars)

        print solver.model().sexpr()
        print solver.statistics()
        print ""

The code above re-creates the Z3 solver thrice and checks the satisfiability of the same query. The query is located here.

While the above section of code is not exactly how I will be using the Z3 Python interface, the problem stemmed from a realization that the Z3 solver, when called twice at different points of the code on the same query, returned different results. I was wondering if this was intentional, and whether there was any way to disable this or to ensure determinism.

Jon Kotker
  • 199
  • 2
  • 10

1 Answers1

4

I'm assuming by different you meant a different model. If the result changes from sat to unsat, then it is a bug.

That being said, if we solve the same problem twice in the same execution path, then Z3 can produce different models. Z3 assigns internal unique IDs to expressions. The internal IDs are used to break ties in some heuristics used by Z3. Note that the loop in your program is creating/deleting expressions. So, in each iteration, the expressions representing your constraints may have different internal IDs, and consequently the solver may produce different solutions.

See the following related question:

Community
  • 1
  • 1
Leonardo de Moura
  • 21,065
  • 2
  • 47
  • 53
  • Oh hm, okay, that makes sense. Is there any way around this though? Should I be using the Z3 Python interface in a different way? – Jon Kotker Mar 31 '13 at 20:06
  • 1
    And yes, I did in fact mean a different model by "different". Sorry for the ambiguity. – Jon Kotker Mar 31 '13 at 20:07
  • Jon, I don't see any workaround. I realize this is an issue. I have been trying to modify the code to break ties using the total order on the expressions defined in the file `src/ast/ast_lt.cpp`. However, this not a perfect solution either, because we would start getting a different solution when we simply rename a variable. If you have ideas/suggestions, please feel free to start a discussion thread at http://z3.codeplex.com/discussions – Leonardo de Moura Apr 01 '13 at 04:00
  • Ah okay. :( Well, I think a workaround for my tool for now would be to memoize the queries that have already been run. This should hopefully work until the Z3 team is able to address the issue. Thanks for the reply! :) – Jon Kotker Apr 01 '13 at 06:19