我正在尝试使用Z3来推理子串,并且遇到了一些非直观的行为。当被要求确定'xy'是否出现在'xy'内时,Z3返回'sat',但当被询问'x'是否在'x'中时,它返回'unknown',或'x'在'xy'中。
我评论了以下代码来说明这种行为:
(set-logic AUFLIA)
(declare-sort Char 0)
;characters to build strings are _x_ and _y_
(declare-fun _x_ () Char)
(declare-fun _y_ () Char)
(assert (distinct _x_ _y_))
;string literals
(declare-fun findMeX () (Array Int Char))
(declare-fun findMeXY () (Array Int Char))
(declare-fun x () (Array Int Char))
(declare-fun xy () (Array Int Char))
(declare-fun length ( (Array Int Char) ) Int )
;set findMeX = 'x'
(assert (= (select findMeX 0) _x_))
(assert (= (length findMeX) 1))
;set findMeXY = 'xy'
(assert (= (select findMeXY 0) _x_))
(assert (= (select findMeXY 1) _y_))
(assert (= (length findMeXY) 2))
;set x = 'x'
(assert (= (select x 0) _x_))
(assert (= (length x) 1))
;set xy = 'xy'
(assert (= (select xy 0) _x_))
(assert (= (select xy 1) _y_))
(assert (= (length xy) 2))
现在问题已经建立,我们试图找到子串:
;search for findMeX='x' in x='x'
(push 1)
(assert
(exists
((offset Int))
(and
(<= offset (- (length x) (length findMeX)))
(>= offset 0)
(forall
((index Int))
(=>
(and
(< index (length findMeX))
(>= index 0))
(=
(select x (+ index offset))
(select findMeX index)))))))
(check-sat) ;'sat' expected, 'unknown' returned
(pop 1)
如果我们在findMeXY
中搜索xy
,则解算器会按预期返回“sat”。似乎由于求解器可以处理'xy'的查询,它应该能够处理'x'。此外,如果在findMeX='x'
中搜索'xy='xy'
,则会返回“未知”。
有人可以提出解释,或者可能是在SMT求解器中表达此问题的替代模型吗?
答案 0 :(得分:5)
观察到的行为的简短答案是:Z3返回'unknown',因为你的断言包含量词。
Z3包含许多用于处理量词的程序和启发式方法。 Z3使用一种称为基于模型的量化实例化(MBQI)的技术来构建模型,以满足像您这样的查询。 第一步是该过程包括基于满足量词免费断言的解释创建候选解释。 不幸的是,在当前的Z3中,该步骤与阵列理论不能平滑地相互作用。 基本问题是阵列理论构建的解释不能被这个模块修改。
一个公平的问题是:为什么我们删除push / pop命令时它会起作用? 它的工作原理是,当不使用增量求解命令(如push和pop命令)时,Z3使用更积极的简化(预处理)步骤。
我看到了两个可能的问题解决方法。
您可以避免使用量词,并继续使用数组理论。这在您的示例中是可行的,因为您知道所有“字符串”的长度。因此,您可以扩展量词。 虽然这种方法看起来很尴尬,但它在实践中和许多验证和测试工具中都有用。
您可以避免数组理论。您将字符串声明为未解释的排序,就像您对Char所做的那样。然后,声明一个函数char-of,它应该返回一个字符串的第i个字符。 你可以对这个操作进行公理化。例如,如果两个字符串具有相同的长度并且包含相同的字符,则可以说两个字符串相等:
(assert (forall ((s1 String) (s2 String))
(=> (and
(= (length s1) (length s2))
(forall ((i Int))
(=> (and (<= 0 i) (< i (length s1)))
(= (char-of s1 i) (char-of s2 i)))))
(= s1 s2))))
以下链接包含使用第二种方法编码的脚本: http://rise4fun.com/Z3/yD3
第二种方法更具吸引力,并且可以让您证明字符串更复杂的属性。 但是,编写令人满意的量化公式非常容易,Z3将无法构建模型。 Z3 Guide描述了MBQI模块的主要功能和限制。 它包含可以由Z3处理的可判定片段。 顺便说一句,请注意,如果你有量词,那么丢弃数组理论不会是一个大问题。该指南展示了如何使用量词和函数对数组进行编码。
您可以在以下文章中找到有关MBQI如何工作的更多信息: