Z3:如何最好地编码“a”;开关语句";?

Z3:如何最好地编码“a”;开关语句";?,z3,smt,Z3,Smt,我想创建一个表达式,它选择给定表达式集中的一个。给定一个表达式数组 Expr[] availableExprs = ...; 对于静态已知长度,我希望Z3选择其中任何一个(如switch语句)。如果问题是SAT,我需要一种方法来找出在模型中选择了哪一个(它在数组中的索引) 最快的编码方式是什么 到目前为止,我考虑了以下方法: 将整数限制为[0,arrayLength),并使用ITE选择其中一个表达式。模型允许我提取此整数。不幸的是,这将整数理论引入了模型(以前根本不使用整数) 为每个可能的选择

我想创建一个表达式,它选择给定表达式集中的一个。给定一个表达式数组

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

最快的编码方式是什么

到目前为止,我考虑了以下方法:

  • 整数限制为
    [0,arrayLength)
    ,并使用ITE选择其中一个表达式。模型允许我提取此整数。不幸的是,这将整数理论引入了模型(以前根本不使用整数)
  • 为每个可能的选择设置一个布尔值。使用ITE选择一个表达式。断言这些布尔值中有一个是正确的。这个策略不需要任何特殊的理论(我认为),但编码可能太冗长
  • 将表达式存储到一个数组中表达式并使用整数从该数组中读取。这保存了ITE链,但引入了数组理论

  • 显然,所有这些都是有效的,但它们似乎都有缺点。最佳策略是什么?

    我想你应该确保每个表达式都是无量词的,并且只使用公式中已经存在的函数和谓词。如果不是这样,那么为每个索引引入一个新的命题变量p_I,并断言ctx.MkIff(p_i,availableExprs[i])到解算器。
    当Z3生成一个模型时,使用model.Eval(p_i)并检查结果是否为表达式“True”。

    如果您只想编码一个元素v在有限集合{e1,…,en}(使用sort 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必须为真。这基本上是对尼古拉建议的重述,但为任意排序而重铸

    请注意,由于不保证ei!=ej,多个pi可能被设置为true。如果需要确保没有两个元素同时被选中,则需要确定所需的语义。如果{e1…en}已被限定为不同的,则无需添加任何内容。如果“数组”您可以断言,元素必须是不同的,但尚未被限定为不同的

    (assert (distinct e1 ... en))
    
    (这可能会在内部扩展为n的二次型。) 相反,您可以说没有两个p变量可以一次为真。请注意,这是一个较弱的语句。要看到这一点,假设v=e1=1,e2=e3=0。然后p1=true,p2=p3=false。这些约束的明显编码是二次编码:

    (assert (or (not pi) (not pj))) ; for all i < j
    
    (断言(或(非pi)(非pj));对于所有i

    如果您需要更好的编码,请尝试了解如何编码“p1+…+pn MkIff与MkEq相同,对吗?
    availableExprs[i]
    可以有任何类型(不一定是布尔型)。我不明白为什么我要断言“索引布尔型”“等于开关值。你能澄清一下吗?我将研究伪布尔约束和你提供的技巧。它们看起来是很好的研究方法。我将断言只有一个pn是真的,而不是区分输入(这在语义上是错误的).我正在合成程序,从另一个位向量计算一个位向量(如位计数程序).程序是一系列二进制运算符,每个运算符应用于以前计算的任何结果。我需要为运算符的每个输入选择任何可用的表达式,我需要知道它是哪一个,以便找出Z3程序合成的内容。顺便说一句,我同时尝试了我在问题中提出的所有策略。数组w最快的是布尔值,最慢的是布尔值。在我看来,嵌套得很深的ite链是有害的。您的断言技巧允许我绕过这一点。谢谢。
    (declare datatypes()((U e1 e2…en))
    不应该做同样的事情吗?