通过使用未解释的排序和函数,这种推理确实是可能的。但是请注意,对此类结构的推理通常需要量化的公理,而 SMT 求解器通常不太擅长使用量词进行推理。
话虽如此,这就是我将如何使用 SBV。
首先,一些样板代码来获得未解释的类型T
:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Generics
import Data.SBV
-- Uninterpreted type T
data T = TBase () deriving (Eq, Ord, Data, Typeable, Read, Show)
instance SymWord T
instance HasKind T
type ST = SBV T
完成此操作后,您将可以访问未解释的类型T
及其符号对应物ST
。让我们声明plus
and zero
,再次声明具有正确类型的未解释常量:
-- Uninterpreted addition
plus :: ST -> ST -> ST
plus = uninterpret "plus"
-- Uninterpreted zero
zero :: ST
zero = uninterpret "zero"
到目前为止,我们告诉 SBV 的只是存在一个类型T
、一个函数plus
和一个常量zero
;明确地未被解释。也就是说,SMT 求解器除了它们具有给定类型之外不做任何假设。
让我们首先尝试证明0+x = x
:
bad = prove $ \x -> zero `plus` x .== x
如果您尝试此操作,您将收到以下响应:
*Main> bad
Falsifiable. Counter-example:
s0 = T!val!0 :: T
SMT 求解器告诉您的是该属性不成立,这是一个不成立的值。该值T!val!0
是Z3
特定的响应;其他求解器可以返回其他东西。它本质上是该类型居民的内部标识符T
;除此之外,我们对此一无所知。当然,这并不是非常有用,因为您并不真正知道它plus
与 和建立了什么关联zero
,但这是可以预料的。
为了证明这个性质,让我们再告诉 SMT 求解器两件事。首先,这plus
是可交换的。其次,zero
右边添加的内容没有任何作用。这些是通过addAxiom
调用完成的。不幸的是,您必须使用 SMTLib 语法编写公理,因为 SBV(至少目前)不支持使用 Haskell 编写的公理。还要注意我们在这里切换到使用Symbolic
monad:
good = prove $ do
addAxiom "plus-zero-axioms"
[ "(assert (forall ((x T) (y T)) (= (plus x y) (plus y x))))"
, "(assert (forall ((x T)) (= (plus x zero) x)))"
]
x <- free "x"
return $ zero `plus` x .== x
注意我们如何告诉求解器x+y = y+x
和x+0 = x
,并要求它证明0+x = x
。以这种方式编写公理看起来非常丑陋,因为您必须使用 SMTLib 语法,但这是当前的情况。现在我们有:
*Main> good
Q.E.D.
量化公理和未解释类型/函数并不是通过 SBV 接口使用的最简单的东西,但您可以通过这种方式获得一些好处。如果您在公理中大量使用量词,则求解器不太可能回答您的查询;并且可能会做出回应unknown
。这完全取决于您使用的求解器,以及要证明的属性有多难。