Python z3中的GADT出现意外行为,获取的值等于每个整数

Python z3中的GADT出现意外行为,获取的值等于每个整数,python,z3,z3py,Python,Z3,Z3py,这将是一个正确的问题,有人更深入地了解z3或在其怪癖的兴趣 大家好,我正在运行以下测试,以了解GADT如何在z3 python中工作。看起来unfoobarfoob的值等于任何整数?是这样吗 下面是一个有效的测试-你能解释一下为什么它有效吗 import pytest from z3 import Datatype, Solver, IntSort, Int def test_stackoverflow(): FooBar = Datatype('FooBar') FooBar

这将是一个正确的问题,有人更深入地了解z3或在其怪癖的兴趣

大家好,我正在运行以下测试,以了解GADT如何在z3 python中工作。看起来unfoobarfoob的值等于任何整数?是这样吗

下面是一个有效的测试-你能解释一下为什么它有效吗

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()

这确实令人困惑,但我认为z3做的是正确的

如果我们转储生成的SMT库,就更容易看到发生了什么。在调用check之前添加print solver.sepxr。我得到:

这需要一点凝视,但以下是涉及的类型:

b是整数 foob是一个FooBar,但它特别具有构造函数foo。 bar foob是一个FooBar,但它特别具有构造函数bar。 unfoo-bar-foob是一个Int,但它将unfoo选择器应用于使用bar构造的对象。 这就是问题所在:你用其他东西构建的东西破坏了一个术语

对于这种情况,典型的“SMTLib”答案没有明确说明。也就是说,逻辑不保证什么是有效的,因此允许解算器以它想要的任何方式实例化。所以,你得到的模型是正确的;虽然有点困惑

为了更好地了解这一点,想象一下您将如何用Haskell这样的语言编写代码:

数据FooBar=Foo{unfoo::Int}| Bar{unbar::FooBar} 检查a b=a==unfoo Bar Foo b 让我们试试:ghci是Haskell解释器:

ghci> check 1 0
*** Exception: No match in record selector unfoo
啊!!它告诉我们我们搞砸了。我们能修好它吗?我们开始:

数据FooBar=Foo Int | Bar{unbar::FooBar} unfoo::FooBar->Int unfoo Foo i=i unfoo Bar=1-在此处方便地选择结果! 检查a b=a==unfoo Bar Foo b 我们得到:

ghci> check 1 0
True
瞧!请注意,我是如何定义unfoo的,以使其令人满意

本质上,z3做了同样的事情。由于应用于使用bar构造的对象的unfoo析构函数未指定,因此它只选择一个使问题可满足的解释。总而言之,当您定义unfoo之类的析构函数时,您要说的是:

如果你收到一个foo值,那么告诉我里面是什么 如果你收到一个非foo值,那么给我你想要的;只要它的类型正确并且满足我的其他约束条件。 而这正是Z3为你所做的。我希望这是清楚的。不过这是个很酷的例子

ghci> check 1 0
True