在Z3中定义带约束的代数数据类型

在Z3中定义带约束的代数数据类型,z3,Z3,我在网上看过一些定义代数数据类型的资料,比如Z3中的IntList。我想知道如何定义具有逻辑约束的代数数据类型。例如,如何定义表示正整数的PosSort。SMT中的Total函数 函数在SMT中总是合计的,这就提出了一个问题:如何对部分函数进行编码,例如PosSort的数据类型构造函数。因此,如果Z3/SMT对代数数据类型的内置支持支持部分数据类型构造函数,我会感到惊讶(这一点似乎是一致的) 编码部分函数:理论 然而,并不是所有的希望都落空了,但您可能必须自己对ADT进行编码。假设一个总函数f:

我在网上看过一些定义代数数据类型的资料,比如Z3中的IntList。我想知道如何定义具有逻辑约束的代数数据类型。例如,如何定义表示正整数的PosSort。

SMT中的Total函数

函数在SMT中总是合计的,这就提出了一个问题:如何对部分函数进行编码,例如
PosSort
的数据类型构造函数。因此,如果Z3/SMT对代数数据类型的内置支持支持部分数据类型构造函数,我会感到惊讶(这一点似乎是一致的)

编码部分函数:理论

然而,并不是所有的希望都落空了,但您可能必须自己对ADT进行编码。假设一个总函数
f:a->B
,它应该对部分数据类型构造函数
f:a~>B
进行建模,其域都是满足
p(a)
a
。在这里,
A
可以是
Int
B
可以是
List[A]
p(A)
可以是
0
f(A)
可以定义为
f(A):=A::Nil
(我在这里使用的是伪代码,但你应该明白了)

一种方法是确保绝不将
f
应用于非正的
a
。根据SMT代码的来源,可以在每次应用
f
之前检查该约束(并提出
f
的错误不适用)


另一种方法是对
f
进行细化,并有条件地对其进行定义,例如沿着
0f(a):=a::Nil
。这样,
f
仍然是total(如前所述,您很可能不得不接受),但是它的值对于
a是未定义的,您正在寻找的是谓词子类型;据我所知,Yices是唯一一个支持它的SMT解决方案:

具体请参见此处的示例:

不幸的是,这是“旧”的Yices,我认为不再支持这种特定的输入语言。正如Malte提到的,SMTLib也不支持谓词子类型

假设输出SMTLib是“生成的”,则可以插入“检查”以确保所有元素都保留在域中。但这相当麻烦,也不清楚如何处理偏袒。欠规范是一个很好的技巧,但它可能会变得非常棘手,并导致很难调试的规范

如果您确实需要谓词子类型,那么SMT解算器可能不是问题域的最佳选择。定理证明程序、依赖类型语言等可能更合适。例如,一个实际的例子是用于Haskell程序的LiquidHaskell系统,它允许谓词附加到类型上,以精确地执行您正在尝试的操作;并使用SMT解算器释放相关条件:

如果您想坚持使用SMT解决方案,并且不介意使用较旧的系统,我建议您使用Yices,它支持谓词子类型来建模此类问题。在SMT解决的背景下,它是(现在仍然是)这一理念的最佳实现之一

axiom destruct_over_construct_Cons {
  forall head: Int, tail: list :: {Cons(head, tail)}
    0 < head ==>
         head_Cons(Cons(head, tail)) == head
      && tail_Cons(Cons(head, tail)) == tail
}

...

axiom type_of_Cons {
  forall head: Int, tail: list :: 
    0 < head ==> type(Cons(head, tail)) == type_Cons()
}
method test_quantifiers() {
    /* The elements of a deconstructed Cons are equivalent to the corresponding arguments of Cons */
    assert forall head: Int, tail: list, xs: list ::
      0 < head ==>
        is_Cons(xs) ==> (head == head_Cons(xs) && tail == tail_Cons(xs) <==> Cons(head, tail) == xs)

    /* Two Cons are equal iff their constructors' arguments are equal */
    assert forall head1: Int, head2: Int, tail1: list, tail2: list ::
      (0 < head1 && 0 < head2) ==>
        (Cons(head1, tail1) == Cons(head2, tail2)
          <==> 
        head1 == head2 && tail1 == tail2)
}