Z3 在使用数组定义变量时,是否有方法指定变量的域?

Z3 在使用数组定义变量时,是否有方法指定变量的域?,z3,smt,Z3,Smt,我的程序从smt2文件中读取约束,所有变量都定义为数组。比如说 (declare-fun x () (Array (_ BitVec 32) (_ BitVec 8) ) ) (declare-fun y () (Array (_ BitVec 32) (_ BitVec 8) ) ) (assert (bvslt (concat (select x (_ bv3 32) ) (concat (select x (_ bv2 32) ) (concat (select x (_ b

我的程序从smt2文件中读取约束,所有变量都定义为数组。比如说

(declare-fun x () (Array (_ BitVec 32) (_ BitVec 8) ) )
(declare-fun y () (Array (_ BitVec 32) (_ BitVec 8) ) )
(assert (bvslt  (concat  (select  x (_ bv3 32) ) (concat  (select  x (_ bv2 32) ) (concat  (select  x (_ bv1 32) ) (select  x (_ bv0 32) ) ) ) ) (concat  (select  y (_ bv3 32) ) (concat  (select  y (_ bv2 32) ) (concat  (select  y (_ bv1 32) ) (select  y (_ bv0 32) ) ) ) ) ) )
(check-sat)
(exit)
省略了其他一些约束。有时,解算器会将x的值设置为:

(store (store (store ((as const (Array (_ BitVec 32) (_ BitVec 8))) #xfe)
                     #x00000002
                     #x00)
              #x00000001
              #xff)
       #x00000003
       #x80)
根据定义,数组的每个元素都是十六进制值,因此该值应为0x8000fffe。这个值超出了C++中整数的上限。当我把它转换回int时,它是一个负值。所以我猜Z3将数组定义的所有变量都视为无符号int。 例如,如果约束为x>y,则解算器可能会给出 x=0x8000fffe 及 y=0x00000001。这些值在无符号比较中满足约束,但在进行有符号比较时,x为负,y为正,因此是错误的。我想知道在将数字定义为数组时,是否有方法告诉解算器这些数字是有符号的

添加于2019年9月14日22:26:43 我有两个smt2文件,一个是

(set-logic QF_AUFBV )
(declare-fun x () (Array (_ BitVec 32) (_ BitVec 8) ) )
(declare-fun y () (Array (_ BitVec 32) (_ BitVec 8) ) )
(assert (bvslt  (concat  (select  x (_ bv3 32) ) (concat  (select  x (_ bv2 32) ) (concat  (select  x (_ bv1 32) ) (select  x (_ bv0 32) ) ) ) ) (concat  (select  y (_ bv3 32) ) (concat  (select  y (_ bv2 32) ) (concat  (select  y (_ bv1 32) ) (select  y (_ bv0 32) ) ) ) ) ) )
(check-sat)
(exit)
该约束仅为x
(set-logic QF_AUFBV )
(declare-fun x () (Array (_ BitVec 32) (_ BitVec 8) ) )
(declare-fun y () (Array (_ BitVec 32) (_ BitVec 8) ) )
(assert (let ( (?B1 (concat  (select  y (_ bv3 32) ) (concat  (select  y (_ bv2 32) ) (concat  (select  y (_ bv1 32) ) (select  y (_ bv0 32) ) ) ) ) ) (?B2 (concat  (select  x (_ bv3 32) ) (concat  (select  x (_ bv2 32) ) (concat  (select  x (_ bv1 32) ) (select  x (_ bv0 32) ) ) ) ) ) ) (let ( (?B3 (bvsub  ?B1 ?B2 ) ) ) (and  (and  (and  (and  (and  (=  false (=  (_ bv0 32) ?B2 ) ) (=  false (=  (_ bv0 32) ?B1 ) ) ) (=  false (bvslt  ?B1 ?B2 ) ) ) (=  false (=  (_ bv0 32) ?B3 ) ) ) (=  false (bvslt  ?B3 ?B2 ) ) ) (=  (_ bv0 32) (bvsub  ?B3 ?B2 ) ) ) ) ) )
(check-sat)
(exit)
那是

 [(! (0 == x)), 
   (! (0 == y)), 
   (! ( y < x)),
   (! (0 ==( y - x))), 
   (! (( y - x) < x)), 
   (0 ==(( y - x) - x)) ]
所以x=0x8000fffe,y=0x0001fffc。转换为十进制,x=2147549180,y=131068。所以y-x-x是-4294967296,不是小数点0。解算器认为它是满意的,因为4294967296是

1 00000000 00000000 00000000 00000000
在二进制中,“1”是第33位,将被删除。因此-4294967296在内存中被视为0x00。这就是我问这个问题的原因。X和y应该是整数,所以0x8000fffe是-0x0000fffe,也就是-65534。y是131068。y-x-x显然不是0。所以就整数而言,这些值不满足约束。表达式y-x-x似乎是按无符号规则计算的。

位向量没有符号 SMTLib中没有有符号或无符号位向量的概念。一个位向量只是一个位序列,没有任何附加的关于如何将其视为数字的语义

然而,区分符号性的是操作。这就是为什么您有
bvslt
bult
;例如,对于有符号和无符号小于比较。您可能希望在此处阅读逻辑描述:

长话短说,所有的解算器告诉你的是,结果包含这些位;如何将其解释为无符号字或有符号2的补码完全取决于您。请注意,这完全符合机器算法在硬件中的实现方式,在硬件中,您只需拥有包含位序列的寄存器。是指令根据他们可能选择的惯例来处理这些值

我希望这是清楚的;请随意询问具体案例;发布完整的程序也总是很有帮助的,只要它们从细节中抽象出来,并描述你想要做的事情

另请参见前面的问题,该问题将更为详细:

避免溢流/底流 您可以要求z3在位向量运算期间避免溢出/下溢。然而,这将需要为您想要执行的每个操作添加额外的断言,因此可能会变得相当混乱。(另外,看起来您希望使用Klee;我不确定Klee是否允许您从一开始就使用它。)本文详细介绍了该技术:

特别是,您需要通读第5.1节:作者描述了如何“注释”每个算术运算,并断言它不会显式溢出。例如,如果您想确保添加不会溢出;首先将位向量从32位扩展到33位;进行加法运算,并检查结果的33位是否为
1
。为了避免溢出,只需编写一个断言,说明位不能是
1
。下面是一个例子:

; Two 32-bit variables
(declare-fun x () (_ BitVec 32))
(declare-fun y () (_ BitVec 32))

; Zero-Extend them to 33-bits
(define-fun x33 () (_ BitVec 33) (concat #b0 x))
(define-fun y33 () (_ BitVec 33) (concat #b0 y))

; Add them
(define-fun  extendedAdd () (_ BitVec 33) (bvadd x33 y33))

; Get the sign bit
(define-fun signBit () (_ BitVec 1) ((_ extract 32 32) extendedAdd))

; Assert that the addition won't overflow:
(assert (= signBit #b0))

; Regular addition result:
(define-fun addResult () (_ BitVec 32) ((_ extract 31 0) extendedAdd))

; Now you can use addResult as the result of x+y; and you'll
; be assured that this addition will never overflow

(check-sat)
(get-model)
您还必须在每次操作中检查下溢。进一步增加了复杂性

正如你所看到的,这可能会变得非常棘手,乘法的规则实际上相当棘手。为了简化此过程,z3实际上提供了用于乘法溢出检查的内置原语,称为:

  • bvsul\u noovfl
    :仅当有符号乘法未溢出时为True
  • bvsul\u noudfl
    :仅当有符号乘法未下溢时为True
  • bvumul\u noovfl
    :仅当无符号乘法未溢出时为True
没有用于检查无符号乘法是否会下溢的谓词,因为这不会发生。但重点仍然是:您必须对每个操作进行注释,并显式地断言相关条件。这最好在代码生成期间由更高级别的API来完成,一些z3绑定确实支持此类操作。(例如,请参见SMT解算器顶部的Haskell层如何处理此问题。)如果要按比例执行此操作,可能需要构建一些自动生成的机制,因为手动执行此操作极易出错


或者您可以切换并使用
Int
type,它永远不会溢出!但是,当然,你不再对实际运行的程序建模,而是对实际整数值进行推理;这可能是可以接受的,取决于您的问题域。

谢谢您的回复!我编辑了我的问题并添加了一个示例。你能看一下吗?谢谢这里的解算器非常正确。与
bvsub
/
bvadd
等操作一起使用时的32位向量。;遵循常规的模运算规则。如果希望解算器将值视为实际整数,则应将其指定为整数,而不是位向量。请参见此处:对于这种情况,x==5和y==10个工作满足约束。有没有办法让解算器在执行算术运算时生成不会溢出的值?这当然是可能的;不幸的是,虽然没那么容易。请参阅我的扩展答案。
; Two 32-bit variables
(declare-fun x () (_ BitVec 32))
(declare-fun y () (_ BitVec 32))

; Zero-Extend them to 33-bits
(define-fun x33 () (_ BitVec 33) (concat #b0 x))
(define-fun y33 () (_ BitVec 33) (concat #b0 y))

; Add them
(define-fun  extendedAdd () (_ BitVec 33) (bvadd x33 y33))

; Get the sign bit
(define-fun signBit () (_ BitVec 1) ((_ extract 32 32) extendedAdd))

; Assert that the addition won't overflow:
(assert (= signBit #b0))

; Regular addition result:
(define-fun addResult () (_ BitVec 32) ((_ extract 31 0) extendedAdd))

; Now you can use addResult as the result of x+y; and you'll
; be assured that this addition will never overflow

(check-sat)
(get-model)