非线性约束的z3超时

时间:2013-05-10 17:13:42

标签: z3 nonlinear-functions nonlinear-optimization

将z3用于非线性真实算术时,我遇到了超时问题。以下代码用于检查4维超矩形是否具有大于50000的体积并且还满足一些约束。但是z3在1分钟内无法给出答案。如何让它更快?

a0min = Real('a0min')
a0max = Real('a0max')
a1min = Real('a1min')
a1max = Real('a1max')
a2min = Real('a2min')
a2max = Real('a2max')
a3min = Real('a3min')
a3max = Real('a3max')
volume = Real('volume')

s = Tactic('qfnra-nlsat').solver()

s.add(-a0min * (1.0 - a1max) + a2max * a3max <= -95000.0,
a0min >  0.0,
a0min < a0max,
a0max < 1000000.0,
a1min > 0.0,
a1min < a1max,
a1max < 1.0,
a2min > 1.0,
a2min < a2max,
a2max < 1000.0,
a3min > 1.0,
a3min < a3max,
a3max < 50.0,
(a0max - a0min) * (a1max -  a1min) * (a2max -  a2min) * (a3max - a3min) > 50000.0,
volume == (a0max - a0min) * (a1max -  a1min) * (a2max -  a2min) * (a3max - a3min))
print s.check()

有一件有趣的事情:如果更换一些“&gt;”和“&lt;”使用“&lt; =”和“&gt; =”,z3求解器可以在两秒内返回答案。相应的代码如下。谁知道为什么会这样?

a0min = Real('a0min')
a0max = Real('a0max')
a1min = Real('a1min')
a1max = Real('a1max')
a2min = Real('a2min')
a2max = Real('a2max')
a3min = Real('a3min')
a3max = Real('a3max')
volume = Real('volume')

s = Tactic('qfnra-nlsat').solver()

s.add(-a0min * (1.0 - a1max) + a2max * a3max <= -95000.0,
a0min >=  0.0,
a0min <= a0max,
a0max <= 1000000.0,
a1min >= 0.0,
a1min <= a1max,
a1max <= 1.0,
a2min >= 1.0,
a2min <= a2max,
a2max <= 1000.0,
a3min >= 1.0,
a3min <= a3max,
a3max <= 50.0,

(a0max - a0min) * (a1max -  a1min) * (a2max -  a2min) * (a3max - a3min) > 50000.0,
volume == (a0max - a0min) * (a1max -  a1min) * (a2max -  a2min) * (a3max - a3min))
print s.check()

1 个答案:

答案 0 :(得分:2)

在第一种情况下,Z3陷入困境&#34;用真正的代数数字计算。如果我们用

执行你的脚本
 valgrind --tool=callgrind python nl.py

几分钟后中断,然后运行

 kcachegrind 

我们将看到Z3卡在algebraic_numbers::isolate_roots内。 nlsat求解器使用的当前实数代数数字包非常低效。 我们有new package用于使用代数数字进行计算,但它尚未与nlsat集成。

避免代数计算的一个技巧是仅使用严格的不等式。平等volume == ...是无害的,因为它在预处理时间内被消除了。 如果我们用-a0min * (1.0 - a1max) + a2max * a3max < -95000.0替换第一个约束,那么Z3也会很快终止(也可以在线here)。

a0min = Real('a0min')
a0max = Real('a0max')
a1min = Real('a1min')
a1max = Real('a1max')
a2min = Real('a2min')
a2max = Real('a2max')
a3min = Real('a3min')
a3max = Real('a3max')
volume = Real('volume')

s = Tactic('qfnra-nlsat').solver()

s.add(-a0min * (1.0 - a1max) + a2max * a3max < -95000.0,
a0min >  0.0,
a0min < a0max,
a0max < 1000000.0,
a1min > 0.0,
a1min < a1max,
a1max < 1.0,
a2min > 1.0,
a2min < a2max,
a2max < 1000.0,
a3min > 1.0,
a3min < a3max,
a3max < 50.0,
(a0max - a0min) * (a1max -  a1min) * (a2max -  a2min) * (a3max - a3min) > 50000.0,
volume == (a0max - a0min) * (a1max -  a1min) * (a2max -  a2min) * (a3max - a3min))
print s.check()
print s.model()

BTW,当您用<=替换所有内容时,nlsat仍然必须使用真正的代数计算,但更改会影响nlsat搜索树,并设法找到解决方案在它陷入真正的代数数字计算之前。

以下是nlsat获得的三个地方&#34;卡住了#34;在我们尝试/检查的例子中

  • 真正的代数数字计算(就像你的例子)。当我们用新的包替换当前包时,这将被修复。

  • 多项式因子分解。在Z3中实现的当前算法效率非常低。这也可以修复。

  • Subresultant computations 的。这个是棘手的,因为据我所知Z3使用非常有效的实现。性能类似于Mathematica等最先进系统的实现。当Z3回溯搜索时使用此操作。不错的属性是我们可以证明Z3将始终使用这种方法终止。一种替代方案是考虑不保证终止但更便宜的近似方法。不幸的是,这不像前两个那样是一个小改动。