造型";交换数组中的两个元素会产生置换;在Z3中

造型";交换数组中的两个元素会产生置换;在Z3中,z3,Z3,我想在Z3中建模,在一个数组中交换两个元素会创建一个置换 交换两个元素可以非常自然地建模: (declare-sort Obj) ; a0 is original array, a2 is array after swap (declare-const a0 (Array Int Obj)) (declare-const a1 (Array Int Obj)) (declare-const a2 (Array Int Obj)) (declare-const i Int) (declare-co

我想在Z3中建模,在一个数组中交换两个元素会创建一个置换

交换两个元素可以非常自然地建模:

(declare-sort Obj)
; a0 is original array, a2 is array after swap
(declare-const a0 (Array Int Obj))
(declare-const a1 (Array Int Obj))
(declare-const a2 (Array Int Obj))
(declare-const i Int)
(declare-const j Int)

(assert (= a1 (store a0 i (select a0 j))))
(assert (= a2 (store a1 j (select a0 i))))
但是我如何建模“a2是a0的排列”并检查这是一个有效的语句

在一个类似的问题()中,作者提供了一个
permutation
函数,用于检查两个数组是否是彼此的置换。然而,这有两个问题。首先,函数可以考虑两个数组作为排列,例如一个数组包含一个对象<代码> x<代码>两次,另一个数组只包含一个代码>代码> x>代码>。第二,Z3甚至不能解决涉及这个函数的非常简单的断言(因此这个问题)

在答案中,有人建议使用序列来模拟问题。这个答案中的置换函数还有一个问题,即如果数组可以多次包含一个对象,那么它是错误的。另外,对我来说,用序列来表示两个元素的交换的建模似乎非常不自然。

两个用于证明两个数组或序列的相等模置换的常见解决方案(在软件验证中)是1。将序列抽象为多个集合,并证明这些集合的相等性,以及2。维护置换见证,即将每个元素从其新索引映射到其原始索引的函数(另请参见或,对于不合适的,请参见)

下面是您的原始编码,以及维护置换见证pwi的代码。每次修改(交换)原始数组后,都会更新见证,这样,对于每个索引k,它始终会为您提供现在可以在索引k处找到的元素的原始数组索引。 初始见证
pw0
是标识函数

(set-option :auto_config false)
(set-option :smt.mbqi false)

(declare-sort Obj)

; a0 is original array, a2 is array after swap
(declare-const a0 (Array Int Obj))
(declare-const a1 (Array Int Obj))
(declare-const a2 (Array Int Obj))
(declare-const i Int)
(declare-const j Int)

; Permutation witness
(declare-const pw0 (Array Int Int))
(declare-const pw1 (Array Int Int))
(declare-const pw2 (Array Int Int))
; The initial permutation witness is the identity function
(assert (forall ((k Int)) (= (select pw0 k) k)))

; (check-sat) ; Sanity check (must not return UNSAT)

(push)
  ; Check that the initial permutation witness is the identity function
  (assert (not (forall ((k Int)) (= (select a0 k) (select a0 (select pw0 k))))))
  (check-sat) ; UNSAT unexpected
(pop)

; Swap two elements of the array
(assert (= a1 (store a0 i (select a0 j))))
(assert (= a2 (store a1 j (select a0 i))))

; Update the permutation witness correspondingly
(assert (= pw1 (store pw0 i (select pw0 j))))
(assert (= pw2 (store pw1 j (select pw0 i))))

(push)
  ; Check that pw2 indeed witnesses the permutation of a2 w.r.t. a0
  (assert (not (forall ((k Int)) (= (select a2 k) (select a0 (select pw2 k))))))
  (check-sat) ; UNSAT unexpected
(pop)

; (check-sat) ; Sanity check (must not return UNSAT)


(declare-const a3 (Array Int Obj))
(declare-const a4 (Array Int Obj))
(declare-const pw3 (Array Int Int))
(declare-const pw4 (Array Int Int))

(push)
  ; Another swap ...
  (assert (= a3 (store a2 j (select a2 (+ i 1)))))
  (assert (= a4 (store a3 (+ i 1) (select a2 j))))
  ; ... but we forgot to update the permutation witness
  (assert (= pw4 pw2))

  (assert (not (forall ((k Int)) (= (select a4 k) (select a0 (select pw4 k))))))
  (check-sat) ; Must not return UNSAT
(pop)

(push)
  ; A swap gone wrong ...
  (assert (= a3 (store a2 j (select a2 (+ i 1)))))
  (assert (= a4 (store a3 (+ i 1) (select a3 j)))) ; Last occurrence of a3 should be a2 (fix --> UNSAT)
  ; ... but the permutation witness is updated correctly
  (assert (= pw3 (store pw2 j (select pw2 (+ i 1)))))
  (assert (= pw4 (store pw3 (+ i 1) (select pw2 j))))

  (assert (not (forall ((k Int)) (= (select a4 k) (select a0 (select pw4 k))))))
  (check-sat) ; Must not return UNSAT
(pop)