Z3:如何最好地编码"开关语句"?

时间:2014-06-12 15:26:22

标签: z3 smt

我想创建一个表达式,选择一组给定的表达式。给出一系列表达式

Expr[] availableExprs = ...;

具有静态已知长度,我希望Z3选择其中任何一个(如switch语句)。如果问题是SAT,我需要一种方法来找出在模型中选择了哪些(它在数组中的索引)。

对此进行编码的最快方法是什么?

到目前为止,我考虑过这些方法:

  1. 整数限制为[0, arrayLength)并使用ITE选择其中一个表达式。该模型允许我提取此整数。不幸的是,这将整数理论引入模型(之前根本没有使用整数)。
  2. 每个可能的选择一个布尔值。使用ITE选择表达式。断言这些布尔中只有一个是真的。这个策略不需要任何特殊的理论(我认为),但编码可能过于冗长。
  3. 将表达式存储到数组表达式中,并使用整数从该数组中读取。这节省了ITE链,但引入了阵列理论。
  4. 显然,所有这些都有效,但它们似乎都有缺点。什么是最好的策略?

2 个答案:

答案 0 :(得分:3)

如果您想要编码的是元素v是有限集合{e1,...,en}(使用排序U),您可以使用smtlib2执行此操作,如下所示:

(declare-fun v () U)
(declare-fun p1 () Bool)
...
(assert (= p1 (= v e1)))
(assert (= p2 (= v e2)))
...
(assert (= pn (= v en)))

(assert (or p1 ... pn))

变量v将等于{e1 ... en}的“数组”中的一个元素。如果选择变量v等于ei,则pi必须为真。这基本上是对尼古拉的建议的重述,但重铸任意种类。

请注意,多个pi可能设置为true,因为无法保证ei!= ej。如果您需要确保没有选择两个元素,您需要确定您想要的语义。如果{e1 ... en}已经必须是不同的,则不需要添加任何内容。如果“数组”元素必须是不同的但不是必须区分,则可以断言

(assert (distinct e1 ... en))

(这可能会在内部扩展到n中的二次方。) 你可以说没有2个p变量可以一次为真。请注意,这是一个较弱的陈述。为了看到这一点,假设v = e1 = 1且e2 = e3 = 0.那么p1 =真,p2 = p3 =假。这些约束的明显编码是二次的:

(assert (or (not pi) (not pj))) ; for all i < j

如果您需要更好的编码,请尝试查看如何在Translating Pseudo-Boolean Constraints into SAT部分5.3中编码“p1 + ... + pn&lt; = 1”。如果明显的编码失败,我只会尝试这个。

答案 1 :(得分:1)

我认为您希望确保每个表达式都是无量词的,并且仅使用公式中已存在的函数和谓词。如果不是这种情况,则为每个索引引入一个新的命题变量p_i,并向解算器断言ctx.MkIff(p_i,availableExprs [i])。 当Z3生成模型时,使用model.Eval(p_i)并检查结果是否为“True”表达式。