在 Z3 中创建一个传递函数而不是自反函数

Creating a transitive and not reflexive function in Z3

我正在尝试在 Z3 中创建一个可传递但不自反的函数。 IE。如果 (transitive a b)(transitive b c) 成立,那么 (transitive a c) 应该成立,但 (transitive a a) 不应该成立。

我试过按以下方式进行,有 5 "tests"。第一个符合我的预期,但第二个失败并导致 unknown

(declare-datatypes () ((T T1 T2 T3)))

(declare-fun f (T T) Bool)
(assert(f T1 T2))
(assert(f T2 T3))

; Make sure that f is not reflexive
(assert
  (forall ((x T))
    (not (f x x))))

; Now we create the transitivity function ourselves
(define-fun-rec transitive ((x T) (y T)) Bool
    (or 
        (f x y)
        (exists ((z T))
            (and 
                (f x z)
                (transitive z y)))))

; This works and gives sat
(push)
(assert (not (transitive T1 T1)))
(assert (not (transitive T2 T2)))
(assert (not (transitive T3 T3)))
(check-sat)
(pop)

; This fails with "unknown" and the verbose flag gives: (smt.mbqi "max instantiations 1000 reached")
(push)
(assert 
    (forall ((x T))
        (not (transitive x x))))
(check-sat)
(pop)

我的问题是:第二次测试与第一次测试有何不同?为什么最后一个给出 unknown,而前一个工作得很好?

"verbose" 消息是这里的提示。 mbqi 代表基于模型的量词实例化。这是一种在SMT求解中处理量词的方法。在第一种情况下,MBQI 设法找到了一个模型。但是您的 transitive 函数对于 MBQI 来说太复杂了,无法处理,因此它放弃了。增加限制不太可能解决问题,也不是长期解决方案。

长话短说,递归定义很难处理,带量词的递归定义更难。逻辑变得半可判定,你受制于启发式方法。即使您找到了让 z3 为此计算模型的方法,它也很脆弱。这类问题不适合SMT解决;最好使用合适的定理证明器,例如 Isabelle、Hol、Coq、Lean。 Agda 等。几乎所有这些工具都提供 "tactics" 来将子目标分派给 SMT 求解器,因此您可以两全其美。 (当然你失去了完全的自动化,但是有了量词,你不能指望更好。)