z3和浮点系数的解释

时间:2017-10-13 02:35:26

标签: z3 z3py

我正在玩一个小的多目标整数规划问题:

enter image description here

在Z3中(使用Python绑定),我们可以非常优雅地说明这一点:

from z3 import *

x1,x2 = Ints('x1 x2')
z1,z2 = Reals('z1 z2')
opt = Optimize()
opt.set(priority='pareto')
opt.add(x1 >= 0, x2 >=0, x1 <= 2, x2 <= 2)
opt.add(x1 <= 2*x2)
# this version is ok: 
#    opt.add(z1 == x1 - 2*x2, z2 == -x1 + 3*x2)
# this truncates coefficients (round down to integer): 
#    opt.add(z1 == 0.5*x1 - 1.0*x2, z2 == -0.5*x1 + 1.5*x2)
# this one seems to work: 
#    opt.add(z1 == 0.5*ToReal(x1) - 1.0*ToReal(x2), z2 == -0.5*ToReal(x1) + 1.5*ToReal(x2))
opt.add(z1 == x1 - 2*x2, z2 == -x1 + 3*x2)
f1 = opt.maximize(z1)
f2 = opt.maximize(z2)
while opt.check() == sat:
    print(opt.model())

这正确解决并给出:

[x1 = 2, x2 = 1, z2 = 1, z1 = 0]
[x1 = 0, x2 = 2, z2 = 6, z1 = -4]
[x1 = 2, x2 = 2, z2 = 4, z1 = -2]
[x1 = 1, x2 = 1, z2 = 2, z1 = -1]
[x1 = 1, x2 = 2, z2 = 5, z1 = -3]

由于我的真实问题有目标的浮点系数,我将目标除以2:

opt.add(z1 == 0.5*x1 - 1.0*x2, z2 == -0.5*x1 + 1.5*x2)

该模型应为x变量提供相同的五种解决方案。但是,当我们运行它时,我们会看到一些错误的结果:

[x1 = 0, x2 = 0, z2 = 0, z1 = 0]
[x1 = 0, x2 = 2, z2 = 2, z1 = -2]
[x1 = 0, x2 = 1, z2 = 1, z1 = -1]

当我打印opt时,我可以看到出错的地方:

(assert (= z1 (to_real (- (* 0 x1) (* 1 x2)))))
(assert (= z2 (to_real (+ (* 0 x1) (* 1 x2)))))

系数被静默截断并转换为整数:0.5到达0,1.5变为1.

解决方法似乎是:

opt.add(z1 == 0.5*ToReal(x1) - 1.0*ToReal(x2), z2 == -0.5*ToReal(x1) + 1.5*ToReal(x2))

这将浮点系数转换为它们的合理等价物:

(assert (= z1 (- (* (/ 1.0 2.0) (to_real x1)) (* 1.0 (to_real x2)))))
(assert (= z2 (+ (* (- (/ 1.0 2.0)) (to_real x1)) (* (/ 3.0 2.0) (to_real x2)))))

现在0.5变为(/ 1.0 2.0),1.5由(/ 3.0 2.0)表示。

我的问题是:

  1. 此截断是“按设计”吗?
  2. 我的解决方法是正确的方法吗?或者我应该完全避免浮点系数?
  3. 打印的有理数(/ 1.0 2.0)似乎暗示仍有浮点数。这真的是(/ 1 2)吗? (我认为这些实际上是重要的)。

1 个答案:

答案 0 :(得分:1)

我认为你基本上回答了自己的问题。底线是Python是一种无类型语言,因此当您将不同类型的操作数混合并匹配到算术运算符时,您将受到库的支配,因为它将为您“匹配”这些类型,并且这并不奇怪它在这里做错了。在SMT-Lib2或任何其他更强类型的绑定中,您将获得类型错误。

永远不要在算术中混合类型,并且始终是显式的。或者,更好的是,使用一个在其类型系统中强制执行此操作的接口,而不是隐式强制执行常量。所以,简短的回答是,是的;这是设计上的,但不是因为任何深层原因,而是Python绑定的行为方式。

这是一个更简单的演示:

>>> from z3 import *
>>> x = Int('x')
>>> y = Real('y')
>>> x*2.5
x*2
>>> y*2.5
y*5/2

因此,看起来一旦你有一个声明的变量,那么与它们交互的常量会自动强制转换为该变量的类型。但我根本不会指望这一点:当你在一个无类型的环境中工作时,最好总是明确的。