为什么不能将空字段中的值计数与整数进行比较?

时间:2018-07-18 21:25:54

标签: alloy

我创建了一个测试,以比较空字段中的值数与数字。我对结果感到惊讶。

首先,我创建了一个带有字段f的签名,可以选择将一个A原子映射到另一个A原子:

sig A {f: lone A}

然后我创建了这个表达式:如果f为空,则f映射的A原子数的计数小于8:

check {
    no A.f =>
        # A.f < 8 
}

我运行了检查命令,合金分析仪找到了一个反例。这让我感到非常惊讶。

我打开了评估工具,并输入了以下内容:

我输入了:A

评估者回答:{}

我输入了:no A.f

评估者回答:true

我输入了:# A.f

评估者回答:0

我输入了:# A.f < 8

评估者回答:false

嗯?

为什么0 < 8为假?

1 个答案:

答案 0 :(得分:2)

合金只有有限的一组整数,因为每个整数在寻找解决方案方面都相对昂贵。默认情况下,此设置为:

Int
  ┌──┬──┬──┬──┬──┬──┬──┬──┬─┬─┬─┬─┬─┬─┬─┬─┐⁻¹
  │-8│-7│-6│-5│-4│-3│-2│-1│0│1│2│3│4│5│6│7│  
  └──┴──┴──┴──┴──┴──┴──┴──┴─┴─┴─┴─┴─┴─┴─┴─┘  

当您执行7 + 1时,您实际上得到-8!

尝试一下...只需在评估器中输入8:

 8
   -8

这实际上并不限于Alloy,C,C ++,Java和大多数其他语言都执行相同的操作,您通常不会注意到它的原因是因为环绕点要高得多。对于Java int,它超过20亿。原因是将整数存储为一组位。一旦达到最大值,加法将需要一个额外的位。由于该位不存在,因此将其静默忽略。 (认识到到目前为止,我从未见过任何能处理此溢出的代码。)

因此,当您的最大数量为7时,合金中的默认数量为8,而实际上您使用的是8 -8

  # A.f < -8 

我们有这种恐惧的原因是当将Int合金提供给SAT求解器时,它也打包在一个位集中。

合金为此一直在苦苦挣扎,并且可以选择防止溢出。最初,我认为这是从天上来的礼物,但由于我意识到它的工作原理,因此禁用了它。问题在于它删除了可能有效的解决方案。找到解决方案并不算太坏,因为您会注意到那里什么都没有,但是对于断言来说则是相当糟糕的,因为断言可能会说模型实际上有没有解决方案。这让我不寒而栗,因为我想依靠一个断言。通过查看实际用例,我决定宁愿显式处理模型中的溢出,因为它们实际上也是最终产品中的问题。许多已知的错误是由意外溢出引起的。因此,将它们隐藏起来的模型不是很有用。

那么您如何处理呢?这样做的语法有点奇怪。您必须指定整数编码的 bitwidth 。因此,您可以将模型更改为:

sig A {f:lone A}

check {
    no A.f =>
        # A.f < 8 
} for 5 int

for 5 int将SAT编码的位宽设置​​为5位。 5位= 5 ^ 2 = 32。这样便得到了整数-16..15。

这显然是一条巨大的绊索。幸运的是,正在进行许多出色的工作,以使Alloy在SMT求解器上运行。 SMT求解器将具有比Alloy更自然的数字处理能力,这不会使您失望。

也就是说,如果您使用的常量整数不适合可用的Int集合,则可以尝试至少产生一个错误。也许您可以提交错误?