为什么这个简单的Z3证明如此之慢?

时间:2017-04-06 05:03:24

标签: z3

以下Z3代码在在线代码上超时:

; I want a function
(declare-fun f (Int) Int)

; I want it to be linear
(assert (forall ((a Int) (b Int)) (
  = (+ (f a) (f b)) (f (+ a b))
)))

; I want f(2) == 4
(assert (= (f 2) 4))

; TIMEOUT :(
(check-sat)

这个版本也是如此,它在reals上寻找一个函数:

(declare-fun f (Real) Real)
(assert (forall ((a Real) (b Real)) (
  = (+ (f a) (f b)) (f (+ a b))
)))
(assert (= (f 2) 4))
(check-sat)

当我给它一个矛盾时,它会更快:

(declare-fun f (Real) Real)
(assert (forall ((a Real) (b Real)) (
  = (+ (f a) (f b)) (f (+ a b))
)))
(assert (= (f 2) 4))
(assert (= (f 4) 7))
(check-sat)

我对定理证明是不可知的。这里有什么慢的?证明者是否有很多麻烦证明存在f(2)= 4的线性函数?

1 个答案:

答案 0 :(得分:3)

缓慢很可能是由于有问题的模式 / 触发器引起的量化实例化太多。如果您还不了解这些内容,请查看Z3 guide的相应部分。

底线:模式是一种语法启发式方法,向SMT求解器指示何时实例化量词。模式必须涵盖所有量化变量,并且模式中不允许使用解释函数,例如加法(+)。 匹配循环是一种情况,其中每个量词实例化都会产生进一步的量化实例化。

在您的情况下,Z3可能会选择模式集:pattern ((f a) (f b))(因为您没有明确提供模式)。这表明Z3实例化了当前证明搜索中已经发生基础项a, b(f a)的每个(f b)的量词。最初,证明搜索包含(f 2);因此,可以使用绑定到a, b的{​​{1}}来实例化量词。这会产生2, 2,可用于再次实例化量词(并且还与(f (+ 2 2))组合)。因此,Z3陷入匹配循环。

这是一个争论我的观点:

(f 2)

使用明确提供的模式,您将获得原始行为(以指定的超时为模)。此外,统计数据报告了量词的大量实例化(如果增加超时,则会更多)。

如果您评论第一个模式并取消注释第二个模式,即如果您"警卫"具有虚拟触发器的量词在证明搜索中不会出现,然后Z3立即终止。不过,Z3仍然会报告(set-option :smt.qi.profile true) (declare-fun f (Int) Int) (declare-fun T (Int Int) Bool) ; A dummy trigger function (assert (forall ((a Int) (b Int)) (! (= (+ (f a) (f b)) (f (+ a b))) :pattern ((f a) (f b)) ; :pattern ((T a b)) ))) (assert (= (f 2) 4)) (set-option :timeout 5000) ; 5s is enough (check-sat) (get-info :reason-unknown) (get-info :all-statistics) ,因为它已经知道"它没有考虑量化约束(这是unknown的要求;它也不能显示sat)。

有时可以重写量词以获得更好的触发行为。例如,Z3指南说明了在内射函数/反函数的上下文中。也许你能够在这里进行类似的转变。