在语义上,一个量词 forall x: T 。 e(x) 等价于 e(x_1) && e(x_2) && ...,其中 x_i 是 T。如果 T 有无限多(或静态未知的许多)值,那么直观地清楚 SMT 求解器不能简单地将量词转换为等价合取。
在这种情况下,经典的方法是 patterns(也称为 triggers),由 Simplify 首创并在 Z3 和其他版本中可用。这个想法相当简单:用户使用一种语法模式来注释量词,该语法模式为何时(以及如何)实例化量词提供启发。
这是一个例子(伪代码):
assume forall x :: {foo(x)} foo(x) ==> false
这里,{foo(x)} 是模式,它向 SMT 求解器表明,只要求解器获得基本项 foo(something),就应该实例化量词。例如:
assume forall x :: {foo(x)} foo(x) ==> 0 < x
assume foo(y)
assert 0 < y
由于当量化变量x 被y 实例化时,基础术语foo(y) 与触发器foo(x) 匹配,因此求解器将相应地实例化量词并学习0 < y。
不过,模式和量化触发很困难。考虑这个例子:
assume forall x :: {foo(x)} (foo(x) || bar(x)) ==> 0 < y
assume bar(y)
assert 0 < y
在这里,量词不会被实例化,因为基本术语 bar(y) 与所选模式不匹配。
前面的例子表明模式会导致不完整。但是,它们也可能导致终止问题。考虑这个例子:
assume forall x :: {f(x)} !f(x) || f(f(x))
assert f(y)
该模式现在承认一个匹配循环,这可能导致不终止。基础术语f(y) 允许实例化量词,从而产生基础术语f(f(y))。不幸的是,f(f(y)) 匹配触发器(将x 与f(y) 实例化),从而产生f(f(f(y))) ...
许多人都害怕模式,而且确实很难做到正确。另一方面,制定触发策略(给定一组量词,找到允许正确实例化的模式,但理想情况下不超过这些)最终“只”需要逻辑推理和纪律。
好的起点是:
* https://rise4fun.com/Z3/tutorial/,“量词”部分
* http://moskal.me/smt/e-matching.pdf
* https://dl.acm.org/citation.cfm?id=1670416
* http://viper.ethz.ch/tutorial/,“量词”部分
Z3 还提供基于模型的量词实例化 (MBQI),这是一种不使用模式的量词方法。据我所知,不幸的是,它的文档记录也少得多,但 Z3 教程也有一个关于 MBQI 的简短部分。