对于对z3
或对它的怪癖感兴趣的人有更深的了解,这将是一个正确的问题。
您好,我正在运行以下测试以了解GADT如何在z3
python中工作。值unfoo(bar(foo(b)))
似乎等于任何整数?是这样吗?
以下是有效的测试-您能帮助解释一下为什么起作用吗?
import pytest
from z3 import Datatype, Solver, IntSort, Int
def test_stackoverflow():
FooBar = Datatype('FooBar')
FooBar.declare('foo', ('unfoo', IntSort()))
FooBar.declare('bar', ('unbar', FooBar))
FooBar = FooBar.create()
foo = FooBar.foo
unfoo = FooBar.unfoo
bar = FooBar.bar
unbar = FooBar.unbar
solver = Solver()
solver.push()
a = Int('a')
b = Int('b')
solver.add(a == unfoo(bar(foo(b))))
assert str(solver.check()) == "sat"
model = solver.model()
assert model.evaluate(a).as_long() == 1
assert model.evaluate(b).as_long() == 0
solver.pop()
答案 0 :(得分:1)
这确实令人困惑,但是我认为z3做正确的事。
如果我们转储生成的SMT-Lib,则更容易看到发生了什么。 (在致电print solver.sepxr()
之前先添加check
。)我得到:
(declare-datatypes ((FooBar 0)) (((foo (unfoo Int)) (bar (unbar FooBar)))))
(declare-fun b () Int)
(declare-fun a () Int)
(assert (= a (unfoo (bar (foo b)))))
需要一些凝视,但这是其中涉及的类型:
b
是Int
(foo b)
是FooBar
,但特别是它具有构造函数foo
。(bar (foo b))
是FooBar
,但特别是它具有构造函数bar
。(unfoo (bar (foo b))
是Int
,但是它将unfoo
选择器应用于由bar
构造的对象。其中存在一个问题:您已经用其他构建的术语“破坏了”术语。
针对此类情况的典型“ SMTLib”答案是“未指定”。就是说,逻辑不保证什么成立,因此允许求解器以其想要的任何方式实例化。因此,您得到的模型是正确的;虽然有点令人困惑。
要更好地了解这一点,请想象您将如何使用Haskell这样的语言进行编码:
data FooBar = Foo {unfoo :: Int} | Bar {unbar :: FooBar}
check a b = a == unfoo (Bar (Foo b))
让我们尝试:(ghci
是Haskell解释器):
ghci> check 1 0
*** Exception: No match in record selector unfoo
啊!它告诉我们,我们搞砸了。我们可以解决吗?我们开始:
data FooBar = Foo Int | Bar {unbar :: FooBar}
unfoo :: FooBar -> Int
unfoo (Foo i) = i
unfoo (Bar _) = 1 -- Conveniently pick the result here!
check a b = a == unfoo (Bar (Foo b))
我们得到:
ghci> check 1 0
True
Voila!请注意,我是如何自己定义unfoo
才能使其“令人满意”的。
本质上,z3做同样的事情。由于unfoo
析构函数应用于用bar
构造的事物的指定不足,因此它只是选择一种使问题可满足的解释。综上所述,当您定义诸如unfoo
之类的析构函数时,您要说的是:
foo
值,请告诉我里面的内容foo
的值,请随便给我;只要它的类型正确并且满足我的其他限制条件即可。这正是Z3为您所做的。我希望这很清楚。很棒的例子!