Count ones in BitVec in Z3 with SMT 2 input format

后端 未结 1 2039
故里飘歌
故里飘歌 2021-01-28 03:59

Is there a compact way of counting the number of bits that are set to 1 in a BitVec in Z3 using SMT 2 input format?

The accepted answer for this question: Sum of all the

相关标签:
1条回答
  • 2021-01-28 04:18

    There's really no easy way to do this directly in SMTLib for the time being.The best you can really do is to roll-your-own for each bit-vector size; rather ugly, but easy to generate code:

    (set-logic QF_BV)
    (set-option :produce-models true)
    
    (define-fun popCount8 ((x (_ BitVec 8))) (_ BitVec 8)
                          (bvadd (ite (= #b1 ((_ extract 0 0) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 1 1) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 2 2) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 3 3) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 4 4) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 5 5) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 6 6) x)) #x01 #x00)
                                 (ite (= #b1 ((_ extract 7 7) x)) #x01 #x00)))
    
    
    ; test
    (define-fun x () (_ BitVec 8) #xAB)
    (declare-fun result () (_ BitVec 8))
    (assert (= result (popCount8 x)))
    (check-sat)
    ; Should be 5!
    (get-value (result))
    

    This prints:

    sat
    ((result #x05))
    

    Using recursion

    Recent versions of the SMTLib standard allow for recursive functions which might be employed to do this "programmatically," but solver support remains rather sketchy when recursive functions are involved. The following works with z3, but other solvers may not fare so well. With that warning, here's a fancy way of doing it using recursion:

    (set-logic BV)
    (set-option :produce-models true)
    
    (define-fun-rec popCount8_rec ((x (_ BitVec 8)) (i (_ BitVec 8)) (accum (_ BitVec 8))) (_ BitVec 8)
        (ite (= i #x08)
             accum
             (popCount8_rec (bvshl x #x01)
                            (bvadd i #x01)
                            (bvadd accum (ite (= #b1 ((_ extract 7 7) x)) #x01 #x00)))))
    
    (define-fun popCount8 ((x (_ BitVec 8))) (_ BitVec 8) (popCount8_rec x #x00 #x00))
    
    ; test
    (define-fun x () (_ BitVec 8) #xAB)
    (declare-fun result () (_ BitVec 8))
    (assert (= result (popCount8 x)))
    (check-sat)
    ; Should be 5!
    (get-value (result))
    

    This also prints:

    sat
    ((result #x05))
    

    Parametric Polymorphism

    Note that whichever method you choose, you have to write a separate function for each size N, in popCountN. SMTLib doesn't allow users to define functions that work on "parametric" types. This is a fundamental limitation of the logic, and also one of the main reasons (though definitely not the only one!) why many people prefer scripting SMT solvers from higher level languages to avoid such boilerplate code.

    A Python trick

    Your best bet is to unroll your own version as outlined above. A common trick to use in Z3 is that you can use the Python program you linked and print s.sexpr() at some point and look at what the generator produced. You can then cut-paste that into SMTLib if you like; though of course beware the usual cut-and-paste errors.

    0 讨论(0)
提交回复
热议问题