我在Haskell中使用SBV(使用Z3后端)来创建一些理论证明。我想检查具有给定约束的forall x
和y
(例如x + y = y + x
,其中+
是“加号运算符”,而不是添加)其他一些术语是有效的。我想定义关于+
表达式的公理(如关联性,身份等),然后检查是否有进一步的等式,例如检查a + (b + c) == (a + c) + b
是否有效正式a
,b
和c
。
我试图使用类似的东西来实现它:
main = do
let x = forall "x"
let y = forall "y"
out <- prove $ (x .== x)
print "end"
但似乎我们不能在符号值上使用.==
运算符。这是缺少功能还是错误使用?我们能够以某种方式使用SBV吗?
答案 0 :(得分:11)
通过使用未解释的排序和功能,这种推理确实是可能的。然而,需要注意的是,对这种结构的推理通常需要量化的公理,而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
和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
。这一切都取决于你使用的求解器,以及要证明的属性有多难。
答案 1 :(得分:4)
您对API的使用并不是很正确。证明数学等式的最简单方法是使用简单的函数。例如,关于无界整数的关联性可以用这种方式表示:
prove $ \x y z -> x + (y + z) .== (x + y) + (z :: SInteger)
如果你需要一个更具编程性的界面(有时你会),那么你可以使用Symbolic
monad,因此:
plusAssoc = prove $ do x <- sInteger "x"
y <- sInteger "y"
z <- sInteger "z"
return $ x + (y + z) .== (x + y) + z
我建议浏览hackage网站中提供的许多示例,以熟悉API:https://hackage.haskell.org/package/sbv