Z3-Python 中的 SAT 查询正在变慢:增量 SAT 怎么样?

SAT queries are slowing down in Z3-Python: what about incremental SAT?

在 Z3 (Python) 中,循环内的 SAT 查询速度变慢,我可以使用增量 SAT 来解决这个问题吗?

问题如下:我正在一个循环中执行具体的 SAT 搜索。在每次迭代中,我都会得到一个模型(当然,我存储模型的否定,以免再次探索同一个模型)。而且,如果该模型满足某个属性,那么我还添加它的一个子查询,并在公式中添加其他限制 .并再次迭代,直到获得 UNSAT(即“不再有模型”)。

我提供代码的定向快照:

  ...
  s = Solver()
  s.add(True)
  while  s.check() == sat: 
    s.check()
    m = s.model()
    phi = add_modelNegation(m)
    s.add(phi) #in order not to explore the same model again
    if holds_property(m): #if the model holds a property
       s = add_moreConstraints(s,m) #add other constrains to the formula
  ...

问题是,随着 s 必须求解的公式越来越大,Z3 开始更难找到这些模型。没关系:这应该会发生,因为现在由于增加了限制,查找模型变得更加困难。然而,就我而言,它发生得太多了:计算速度甚至减半;即求解器需要找到新模型的时间是经过一些迭代后的两倍。

因此,我想实现某种增量求解,想知道Z3中是否有原生方法可以这样做。

我已经在很多页面上读到过这个(例如,参见 How incremental solving works in Z3?), but only found this response in How to use incremental solving with z3py 有趣的:

PythonAPI自动“递增”。这只是意味着能够多次调用命令 check(),而求解器不会忘记之前看到的内容(即调用 check(),断言更多事实,再次调用 check();第二个check()将从一开始就考虑所有断言。

我不确定我是否理解,因此我提出了一个简单的问题:响应意味着增量SAT确实用于Z3的SAT?我认为我正在寻找另一个增量;例如:如果在SAT迭代次数230中不可避免地有一个变量(比如b1)是true,那么这是一个以后不会改变的事实,你可以将它设置为1,简化公式而不是重新推理与 b1 有任何关系,因为所有模型(如果有的话)都会有 b1Z3的这个增量SAT是否考虑了这些情况?

如果没有,我该如何实现?

我知道 PySat 或 MiniSat 中有一些实现,但我想在 Z3.

中实现

对于与 z3 求解性能相关的任何事情,没有一种适合所有情况。每个具体问题都可以从不同的想法中受益。

增量求解术语“增量求解”在SAT/SMT上下文中具有非常具体的含义。这意味着您可以在调用 check 后继续向系统添加断言,而不会忘记您之前添加的断言。这就是使它渐进的原因。此外,您可以设置跳跃点;也就是说,您可以告诉求解器“忘记”您在程序的某个点之后输入的断言,实质上是遍历一堆断言。有关详细信息,请参阅 https://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2021-05-12.pdf 的第 3.9 节,特别是其中讨论“断言堆栈”的部分。

而且,如前所述,您无需为 z3 做任何特定的增量操作。默认情况下它是增量的,即,您可以在调用 check 后简单地添加新的断言,或者使用 push/pop 调用等。(例如,将其与 CVC4 进行比较;这是默认情况下不是增量的。如果你想在增量模式下使用 CVC4,你必须传递一个特定的命令行参数。)这样做的主要原因是增量模式需要额外的簿记,CVC4 不愿意为此付费,除非你明确要求它这样做。对于 z3,开发人员决定始终在不使用任何命令行开关的情况下使其增量。

关于您关于 b1 为真时会发生什么的特定问题:好吧,如果您以某种方式确定 b1 始终为真,只需断言即可。求解器会自动利用这一点;没有什么特别需要做的。请注意,z3 在处理您的程序时会学习大量引理,并将它们添加到其内部数据库中。但是如果你有一些外部机制可以让你推断出一个特定的约束,那么就继续添加它吧。 (当然,这是否合理取决于您,而不是 z3;但那是另一个问题。)

加速枚举“找到我的所有解决方案”循环的一个特定“技巧”是采用分而治之的方法,而不是“添加先前模型的否定并迭代”在实践中,这会对性能产生重大影响。我认为你应该试试这个主意。在这里解释:https://theory.stanford.edu/~nikolaj/programmingz3.html#sec-blocking-evaluations As you can see, the all_smt function defined at the end of that section takes specific advantage of incrementality (note the calls to push/pop) to speed up the model-search process, by carefully dividing the search space into disjoint segments, instead of doing a random-walk. Using this might give you the speed-up you need. But, again, as with anything performance specific, you'll need to tell us more about exactly what problem you are solving: None of these methods can avoid performance problems caused by modeling issues. (For instance, using integers to model booleans is one common pitfall.) See this answer for some generic advice: