Search code examples
pythonz3z3py

Using z3.BitVecRef vs Integer


A constant in a z3 solver can be a usual python type (1) or a z3 Ref (2) as in the following artificial example, where x and y undergo same constraint set through these two types:

from z3 import *

s = Solver()
x, y = BitVecs("x y",7)
d = BitVecVal(6, 7)

                   #constraints:
#                   ------------
s.add(x & 6 != 6)  #1'th or 2'th bit unset using Integer    #....(1)
#                                                   vs
s.add(y & d != d)  #1'th or 2'th bit unset using BitVecVal  #....(2)


check = s.check()
if check == sat:
    print(s.model())

So, is there one of (1), (2) to prefer? Seems, I don't understand why there are constant sorts if the same can be done with usual python types.


Solution

  • The resulting SMT code for Z3 can be shown as follows:

    #  https://stackoverflow.com/a/14629021/1911064
    def toSMT2Benchmark(f, status="unknown", name="benchmark", logic=""):
      v = (Ast * 0)()
      return Z3_benchmark_to_smtlib_string(f.ctx_ref(), name, logic, status, "", 0, v, f.as_ast())
    
    print(toSMT2Benchmark(x & 6 != 6))
    print(toSMT2Benchmark(y & d != d))
    

    Output:

    ; benchmark
    (set-info :status unknown)
    (declare-fun x () (_ BitVec 7))
    (assert
     (and (distinct (bvand x (_ bv6 7)) (_ bv6 7)) true))
    (check-sat)
    
    ; benchmark
    (set-info :status unknown)
    (declare-fun y () (_ BitVec 7))
    (assert
     (and (distinct (_ bv6 7) (bvand y (_ bv6 7))) true))
    (check-sat)
    

    So, both forms do create pretty much the same input for Z3.