【问题标题】:Z3PY extremely slow with many variables?Z3PY 非常慢,有很多变量?
【发布时间】:2021-03-25 10:33:56
【问题描述】:

我一直在使用 Z3PY 中的优化器,并且在我的项目中只使用 Z3 ints 和 (x

我创建了 26 个 Z3 整数。然后我添加了硬约束,它不应小于 1 或大于 26。此外,所有数字都必须是唯一的。根本没有添加其他约束。 换句话说,求解器将找到的解决方案是一个简单的顺序 [1,2,3,4,5....最多 26]。以求解器发现的方式排序。

我的意思是这是一件简单的事情,除了我提到的那些之外,真的没有任何限制。求解器在 0.4 秒或类似的时间内解决了这个问题,快速且足够。这是预期的。但是如果我将变量的数量增加到 49(当然现在的约束是它不应低于 1 或超过 49),求解器大约需要 1 分钟才能求解。对于这么简单的任务,这似乎真的很慢?应该是这样吗,有人知道吗?时间复杂度真的大大增加了吗?

(我知道我可以在这个特定的实验中使用 Solver() 而不是 Optimizer(),它会在一秒钟内解决,但实际上我需要使用 Optimizer 来完成,因为我有很多软限制工作。)

编辑:为我的示例添加一些代码。

I declare an array with Z3 ints that I call "reqs".
The array is consisting of 26 variables in one example and 49 in the other example I am talking about.

solver = Optimize()

 for i in (reqs):
     solver.add(i >= 1)

for i in (reqs):
    solver.add(i <= len(reqs))

    d = Distinct(reqs)
    solver.add(d)

res = solver.check()
print(res)

【问题讨论】:

  • 优化引擎预计不会高效;他们也没有像现有的求解器经常做的那样得到关注和加速技巧。但是每个基准测试都是不同的,所以你应该发布具体的例子来看看可以做什么,如果有的话。
  • 我现在已将代码添加到原始帖子中。但是,是的,我明白了。但是感觉有点奇怪,从 26 个变量变为 49 个变量时速度要慢得多。使用 Yices(在 SMTlib2 中编写和执行)需要一毫秒或其他时间。在这里使用 Z3PY 需要一分钟,有 49 个变量。

标签: z3 solver smt z3py


【解决方案1】:

每个基准测试都是独一无二的,不可能想出一个在所有情况下都同样适用的好策略。但是您描述的场景很简单,可以处理。性能问题来自以下事实:Distinct 为求解器创建了太多不等式(数量为二次方),并且随着变量数量的增加,优化器很难处理它们。

根据经验,您应该尽可能避免使用Distinct。对于这种特殊情况,对变量施加严格的排序就足够了。 (当然,根据您的其他限制,这可能并不总是可行,但您所描述的似乎可以从这个技巧中受益。)所以,我会这样编码:

from z3 import *

reqs = [Int('i_%d' % i) for i in range(50)]

solver = Optimize()

for i in reqs:
  solver.add(i >= 1, i <= len(reqs))

for i, j in zip(reqs, reqs[1:]):
  solver.add(i < j)

res = solver.check()
print(res)
print(solver.model())

当我运行它时,我得到:

$ time python a.py
sat
[i_39 = 40,
 i_3 = 4,
 ...
 i_0 = 1,
 i_2 = 3]
python a.py  0.27s user 0.09s system 98% cpu 0.365 total

这很简短。希望您可以将其推广到您的原始问题。

【讨论】:

  • 非常感谢!现在不使用 Distinct 时它真的很快。我使用了数百个软约束。一个问题。我运行了很多次,以获得很多值,然后我计算中位数。看看它的表现如何。在我更改代码之前,我使用了: set_option('smt.random_seed', random.randint(0, 2 ** 8)) 这样它就不会在每次执行时产生完全相同的结果。但是现在当不使用 distinct 时,似乎这不起作用。我现在在每次执行中都得到完全相同的结果。你知道怎么做吗?
  • 你不应该真的依赖随机种子来获得“不同”的模型。这种方法非常容易出错,因为它对求解器的内部结构很敏感,即使看起来相对无害的更改也会停止工作。我建议使用您的随机数来打乱变量的顺序。在我的代码中,有一个严格的排序:i0 &lt; i1 &lt; i2 ... 等。使用 Python 端的随机种子来获得不同的排序:i43 &lt; i22 &lt; i35 ... 等;而不是依赖求解器。 (请注意,这种改组将纯粹作为 Python 代码完成,与求解无关。)
猜你喜欢
  • 2015-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多