Kissat SAT Solver
以下文字来源于:
(1)https://fmv.jku.at/kissat/
(2)Armin Biere, Katalin Fazekas, Mathias Fleury, Maximillian Heisinger.
CaDiCaL, Kissat, Paracooba, Plingeling and Treengeling Entering the SAT Competition 2020.
In Proc. of SAT Competition 2020 - Solver and Benchmark Descriptions, Tomas Balyo, Nils Froleyks, Marijn Heule, Markus Iser, Matti Järvisalo, Martin Suda (editors), vol. B-2020-1 of Department of Computer Science Report Series B, 2020. pages 50-53, University of Helsinki, 2020.
The Kissat SAT solver is a condensed and improved reimplementation of CaDiCaL in C.
It has improved data structures, better scheduling of inprocessing, optimized algorithms and implementation.
译文:它改进了数据结构,优化了处理调度,优化了算法和实现。
cadical改进到kissat的源起: | |
One reason is using the C++ container “std::vector” for most data structures (e.g., to hold flags, values, decision levels, reasons, scores). They are also mostly zero initialized. Instead, we now use the C memory allocator “calloc”. It provides zero initialization on-demand by the virtual memory system and reduces resident set size accordingly. This design decision also raised the question, whether we can reuse some other features of LINGELING [4] to further reduce memory.
|
|
kissat新的技术环节 | |
1. 二值观察体系的构建方式 2.采用宏的方式处理复杂性及遍历型的封装操作 3.“目标阶段”技术被采用c语言代码编写使用 |
|
具体说明: 1. 二值观察体系的构建方式 In KISSAT we therefore completely inline binary clauses in watcher stacks to reduce the size of watches from 16 bytes in CADICAL to 4 bytes for binary and 8 bytes for large clauses (due to the blocking literal). (1)This in turn requires to use 4-byte offsets instead of pointers to reference large (non-binary) clauses. (2)binary clauses now really only exist in watcher lists. 二元子句没有分配内存。large子句仍然分配了内存。 (3)监察列表的操作所做的改变: In KISSAT we use a dedicated implementation of stacks of watchers, requiring only two offsets (of together 8 bytes in the compact competition configuration) instead of 3 pointers (requiring 24 bytes on a 64-bit architecture). 译文:在KISSAT中,我们使用专用的监视器堆栈实现,只需要两个偏移量 8字节(紧凑竞争配置),而不是3个指针(在64位架构上需要24字节)。
This became possible by assuming that the all-bits-one word is not a legal watch and free memory in the watcher stack arena is 译文:这可以通过假设全位一字不是合法的观察元,并且监视器堆栈中的空闲内存被标记为全位一字来实现。
Pushing a watch on a watcher stack requires checking whether the word after the top element is illegal (all-bits-one). If so, it is overwritten. Otherwise the whole stack is moved to the end of the allocated part in the watcher arena. 译文:将一个表推入监视器堆栈需要检查顶部元素后面的单词是否非法(全位1)。如果是,它将被覆盖。否则,整个堆栈将被移动到观察者竞技场中分配部分的末端。 This produces the overhead that once in a while the watcher arena requires defragmentation and is usually performed after collecting redundant clauses in “reduce”. 译文:这就产生了开销,有时监视程序需要碎片整理,通常在收集“reduce”中的冗余子句之后执行。在reduce之后执行碎片整理。
In order to distinguish binary and large watches in watcher stacks, we use bit-stuffing as in LINGELING. 译文:为了区分表栈中的二进制表和大表,我们采用了LINGELING中的位填充方法。 Since these large clauses are allocated 8-byte aligned in the clause arena, the maximum size of this arena is 8·231 bytes (16 GB). Note that in practice many large CNFs consist mostly of binary clauses, which due to inlining do not require any space in this arena. 译文:由于这些大的子句在子句竞技场中被分配8字节,因此该竞技场的最大大小为8.231字节(16 GB)。在实践中,许多大型cnf主要由二元子句组成,由于内联,这些子句在这个领域不需要任何空间。
Further, beside data structures for variables, watch lists occupy a large fraction of the overall memory. Actually the largest CNFs we ever encountered in applications easily stay below this limit while in total KISSAT reaches 100 GB memory usage. 译文:此外,除了变量的数据结构外,监视列表还占据了总体内存的很大一部分。实际上,我们在应用程序中遇到的最大的cnf很容易保持在这个限制以下,而总的KISSAT内存使用量达到100 GB。 On top of that, other solvers including CADICAL often need more than 4 times more main memory than KISSAT. 译文:除此之外,包括CADICAL在内的其他求解器通常需要比KISSAT多4倍以上的主内存。
Due to inlining binary clauses redundant and irredundant binary clauses have to be distinguished [5], which requires another watcher bit (“redundant”). 由于内联二进制子句必须区分冗余和非冗余二进制子句[5],这需要另一个监视位(“冗余”)。 Finally, as in CADICAL, hyper binary resolvents are generated in vasts amounts [6] during failed literal probing and vivification [7] and have to be recycled quite aggressively. To mark these hyper binary resolvents we need a third watcher bit (“hyper”) and the effective number of bits for literals is reduced to 29. Thus, the solver can only handle 268 435 455 = 228 − 1 variables. 译文:在失败的文字探测和生动化过程中,会产生大量的超二进制解析[6],必须进行大力回收。 译文:为了标记这些超二进制解析,我们需要第三个监视位(“hyper”)。
2. 采用宏的方式处理复杂性及遍历型的封装操作 (用宏替代for循环等重复操作) For KISSAT we were able to almost completely encapsulate this complexity using macros. (1)In “dense mode” (during for instance variable elimination) the solver maintains full occurrence lists for all irredundant clauses. (2)In the default “sparse mode” (during search) only two literals per large clause are watched and large clause watches have an additional blocking literal. 译文:在默认的“稀疏模式”中(在搜索期间),每个大子句只监视两个字面值,而大子句监视有一个额外的阻塞字面值。 Thus, as in LINGELING, watch sizes vary between one and two words, whichs lead to very cumbersome and verbose watch list traversal code in LINGELING repeated all over the source code. 译文:因此,与linglingling一样,观察元的大小在一到两个文字之间变化,这导致非常繁琐和冗长的观察列表遍历代码在整个源代码中重复出现。
3.“目标阶段”技术被采用c语言代码编写使用 Otherwise we ported all the important algorithms from CADICAL, and were also able to reconfirm their effectiveness in a fresh implementation. In this regard using “target phases” as introduced last year in CADICAL [1] should be emphasized, which after careful porting, gave a large improvement on satisfiable instances. 能求出更多的SAT样例。
4. Thus subsumption on learned clauses becomes less important and we only apply it on irredundant clauses before and during bounded variable elimination. We have both a fast forward subsumption pass for all clauses as well incremental backward but now also forward subsumption during variable elimination, carefully monitoring variables occurring in added or removed (irredundant) clauses, which allows us to focus the inprocessing effort. 译文:因此,对已学习子句的包容变得不那么重要,我们只在有界变量消去之前和过程中对不冗余的子句进行包容。
5. The clause arena keeps irredundant clauses before redundant clauses, which allows during reduction of learned clauses in “reduce” to traverse only the redundant part of the arena. 译文:子句竞技场将不冗余的子句放在冗余的子句之前,这使得在“reduce”中学习到的子句的还原过程中只遍历竞技场的冗余部分。
译文:由于手表在竞技场中包含对大型条款的偏移,我们可以在此过程中完全避免访问不必要的(原始)条款。 译文:这大大减少了在子句缩减过程中刷新和重新连接监视列表中的监视器的热点。注意,除了“restart”之外,“reduce”是CDCL求解器中最常调用的过程(在核心过程“propagate”、“decide”和“analyze”之后)。 6. In comparison to CADICAL inprocessing procedures are scheduled slightly differently. First there is no forward subsumption of clauses outside of the “eliminate” procedure. In KISSAT compacting the variable range is part of “reduce” and actually always performed if new variables became inacive (eliminated, substituted or unit). Otherwise “probe” and “eliminate” call the same algorithms as in CADICAL, except for vivification which became part of “probe” and duplicated binary clause removal (aka hyper unary resolution), which has moved from “subsume” (thus in CADICAL triggered during search and during variable elimination) to “eliminate”. 译文:与CADICAL相比,加工程序的安排略有不同。首先,在“排除”程序之外没有预先包含条款。 译文:在KISSAT中,压缩变量范围是“reduce”的一部分,实际上总是在新变量变得无效(消除,替换或单位)时执行。否则,“probe”和“eliminate”调用与CADICAL中相同的算法,除了vivification成为“probe”和重复二进制子句移除的一部分(又名超一元解析),它已经从“subsume”(因此在CADICAL中在搜索和变量消除期间触发)移动到“eliminate”。
7. More importantly we have a more sophisticated scaling procedure for the number of conflicts between calls to “probe” and “eliminate”, which as in CADICAL takes the size of the formula into account, but now applies an additional scaling function instead of just linearly increasing the base interval in terms of n denoting how often the procedure was executed. 译文:更重要的是,对于“探测”和“消除”调用之间的冲突数量,我们有了一个更复杂的缩放过程,在CADICAL中,它考虑了公式的大小,但现在应用了一个额外的缩放函数,而不是仅仅用n线性增加基间隔,n表示过程执行的频率。
8. Since boolean constraint propagation is considered the hot-spot for SAT solvers, CADICAL uses separate specialized propagation procedures during search, failed literal probing and vivification. In KISSAT we have factored out propagation code in a header file which can be instantianted slightly differently by these procedures, so taking advantage of dedicated propagation code while keeping the code in one place. 译文:由于布尔约束传播被认为是SAT求解器的热点,CADICAL在搜索、失败的文字探测和验证过程中使用单独的专门传播过程。在KISSAT中,我们在头文件中分解了传播代码,这些代码可以通过这些过程进行稍微不同的实例化,因此可以利用专用的传播代码,同时将代码保留在一个地方。
9. The concept of quiet “stable phases” without many restarts and “non-stable phases” with aggressive restarting was renamed. We call it now “stable mode” and “focused mode” to avoid the name clash with “phases” in “phase saving” (and “target phases”). We further realized that mode switching should not entirely be based on conflicts, since the conflict rate per second varies substantially with and without frequent restarts (as well as using target phases during stable mode). 译文:不需要多次重启的安静“稳定阶段”和主动重启的“非稳定阶段”的概念被重新命名。我们现在称其为“稳定模式”和“聚焦模式”,以避免与“阶段保存”(和“目标阶段”)中的“阶段”名称冲突。我们进一步意识到模式切换不应该完全基于冲突,因为每秒的冲突率在频繁重启和不频繁重启的情况下变化很大(以及在稳定模式期间使用目标相位)。
10. Since the solver starts in focused mode, these focused mode intervals can still be based on a (quadratically) increasing conflict interval. For the next stable interval we then attempt to use the same time. Of course, in order to keep the solver deterministic, this requires to use another metric than run time. In CADICAL we simply doubled the conflict interval after each mode switch which did not perform as well in our experiments as this new scheme. 译文:由于求解器以聚焦模式启动,这些聚焦模式间隔仍然可以基于(二次)增加的冲突间隔。对于下一个稳定区间,我们尝试使用相同的时间。当然,为了保持求解器的确定性,这需要使用运行时间以外的另一个度量。在CADICAL中,我们只是将每次模式切换后的冲突间隔增加一倍,在我们的实验中表现不如这个新方案。
11. Our first attempt to limit the time spend in stable mode was to use the number of propagations as metric. But this was not precise enough, since propagations per second still vary substantially with and without many restarts. Instead we now count “ticks”, which approximate the number of cache lines accessed during propagations. This refines what Donald Knuth calls “mems” but lifted to cache lines and restricted to only count watcher stack access and large clause dereferences, ignoring for instance accessing the value of a literal. 译文:为了限制在稳定模式下花费的时间,我们的第一个尝试是使用传播次数作为度量。但这还不够精确,因为每秒的传播量在多次重启和不多次重启的情况下仍然有很大的变化。相反,我们现在计算“tick”,它近似于在传播期间访问的缓存线的数量。这改进了Donald Knuth所说的“mems”,但将其提升到缓存行,并限制为仅计数监视程序堆栈访问和大子句解引用,而忽略访问字面量的值。
12. Cache line counting is necessary because in certain large instances with almost exclusively binary clauses most time is spend in accessing the watches with inlined binary clauses in watcher stacks and not in dereferencing large clauses, while in general, and for other instances with a more balanced fraction of large and binary clauses, a single clause dereference is still considerably more costly than accessing an inlined binary clause. Computing these “ticks” was useful limit the time spent in other procedures, e.g., vivification, in terms of time spent during search (more precisely the time spend in propagation). 译文: 缓存行计数是必要的,因为在某些几乎只有二进制子句的大型实例中,大部分时间都花在访问监视器堆栈中带有内联二进制子句的表上,而不是解引用大子句。而通常,对于其他具有更平衡的大子句和二进制子句的实例,单个子句解引用仍然比访问内联二进制子句要昂贵得多。计算这些“刻度”很有用,可以限制在其他程序中花费的时间,例如,在搜索过程中花费的时间(更准确地说,是在搜索过程中花费的时间)
13. While porting the idea of target phases [1], we realized that erasing the current saved phases by for instance setting them to random phases, might destroy the benefit of saved phases to remember satisfying assignments of disconnected components of the CNF [10]. Instead of decomposing the CNF explicitly into disconnected components, as suggested in [10], we simply compute the largest autarky of the full assignment represented by saved phases, following an algorithm originally proposed by Oliver Kullman (also described in [2]). 译文:在移植目标相位的思想[1]时,我们意识到,通过例如将当前保存的相位设置为随机相位来删除它们,可能会破坏保存的相位记住CNF的断开组件的满意分配的好处[10]。我们没有像[10]中建议的那样将CNF显式地分解为不相连的组件,而是简单地计算由保存的阶段表示的完整分配的最大自依赖,遵循Oliver Kullman最初提出的算法(也在[2]中进行了描述)。
14. This unique autarky contains all the satisfying assignments for disconnected components (as well as for instance pure literals). If the autarky is non-empty, its variables are considered to be eliminated and all clauses touched by it are pushed on the reconstruction stack. We determine this autarky each time before we erase saved phases in “rephase” and once again if new saved phases have been determined through local search. 译文:这种独特的独立性包含了断开连接组件的所有令人满意的赋值(以及例如纯字面量)。如果autarky不为空,则认为它的变量被消除,并且与它接触的所有子句都被压入重构堆栈。每次在“重新相位”中擦除保存的相位之前,以及在通过本地搜索确定新保存的相位之前,我们都会确定此相关性。
15. Finally, combining chronological backtracking [11] with CDCL turns out to break almost the same invariants [12] as on-the-fly self-subsuming resolution [13], [14] and thus we added both, while CADICAL is missing the latter. Both techniques produce additional conflicts without learning a clause and thus initially we based all scheduling on the number of learned clauses instead on the number of conflicts, but our experiments revealed that using the number of conflicts provides similar performance and we now rely on that for scheduling. 译文:最后,将时间回溯[11]与CDCL相结合,结果发现,它打破了与即时自包含分辨率[13]、[14]几乎相同的不变量[12],因此我们添加了两者,而CADICAL缺少后者。这两种技术都产生了额外的冲突,而没有学习一个子句,因此最初我们基于学习子句的数量而不是冲突的数量来调度,但我们的实验表明,使用冲突的数量提供了类似的性能,我们现在依靠它来调度。
16. As last year for CADICAL we submit three configurations of KISSAT, one targeting satisfiable instances (“sat”) always using target phases (also in focused mode), one for unsatisfiable instances (“unsat”), which stays in focused mode, and the default configuration (“default”), which alternates between stable and focused mode as described above, but only uses target phases in stable mode. 译文:与去年的CADICAL一样,我们提交了三种KISSAT配置,一种是针对可满足实例(“sat”),总是使用目标阶段(也处于聚焦模式),一种是针对不可满足实例(“unsat”),它保持在聚焦模式,以及默认配置(“default”),它在如上所述的稳定和聚焦模式之间交替,但只在稳定模式下使用目标阶段。 |
|
These improved data structures described above obviously require too many changes and we decided to start over with |
|