4

The below Python snippet illustrates a strage performance behavior of Z3. Without push() call, the z3 checks the formula in 0.1s. With push() (and no extra assertions), z3 takes 0.8s. A similar outcome occurs even after swapping s.append(f) and s.push().

import time
import z3
f = z3.parse_smt2_file("smt-lib/QF_BV/RWS/Example_1.txt.smt2")
s = z3.Solver()
s.append(f)
t1 = time.time()
s.check()  # 0.10693597793579102 seconds
print(time.time() - t1)

s = z3.Solver()
t1 = time.time()
s.append(f)
s.push()
s.check()  # 0.830916166305542 seconds
print(time.time() - t1)

Any idea why does this slowdown occur? And, how can it be addressed?

I'm using z3-4.3.2.bb56885147e4-x64-osx-10.9.2.

  • Thanks, this is a good repro. Z3 uses the incremental solver for the second instance. It is not clear from the use case why this should be the case, so it points to a usability bug. Internally, Z3 uses a factory pattern to create solvers. I am looking into whether this is where the cause is or whether it is a side-effect of the previous solver that overrides the state. – Nikolaj Bjorner Oct 17 '14 at 05:35
  • @NikolajBjorner The behavior is the same even when the snippets are executed in different Python processes. – Venkatesh-Prasad Ranganath Oct 17 '14 at 22:55

1 Answers1

5

Your example uses "push" in the second instantiation. This makes a huge difference. Z3's bit-vector solver is (not yet) amenable to incremental use over the API. It works only for stand-along assertions and does not interoperate with scopes (push/pop). The first invocation uses the bit-vector SAT solver. There is no "push/pop" going on. The second invocation follows a call to "push". Z3 then determines that in order to track scope dependencies it has to use the incremental SMT core, which does not benefit from the more efficient bit-vector solver.

Nikolaj Bjorner
  • 8,229
  • 14
  • 15