#sat-solver #sat #satisfiability #logic

bin+lib splr

基于 Rust 的现代 CDCL SAT 求解器

31 个版本

0.17.2 2024年2月4日
0.17.1 2023年7月9日
0.17.0 2023年1月30日
0.16.3 2022年9月16日
0.1.2 2019年3月7日

#27 in 数学

Download history 8/week @ 2024-03-14 87/week @ 2024-03-21 28/week @ 2024-03-28 10/week @ 2024-04-04 1/week @ 2024-04-18 6/week @ 2024-04-25 6/week @ 2024-05-16 16/week @ 2024-05-23 21/week @ 2024-05-30 8/week @ 2024-06-06 4/week @ 2024-06-13 49/week @ 2024-06-20 12/week @ 2024-06-27

每月 75 次下载
3 crates 中使用

MPL-2.0 许可证

480KB
11K SLoC

Rust 中命题逻辑的现代 SAT 求解器

Splr 是基于 Rust 的现代 SAT 求解器,基于 Glucose 4.1。它采用了或采用了现代 SAT 求解器的各种研究成果

  • 从 Glucose、Minisat 及其祖先中获得的 CDCLwatch literalsLBD
  • 基于 Luby 系列的重启控制
  • 类似 Glucose 的 动态阻塞/强制重启
  • 简化给定的 CNF 的前/处理
  • 基于 基于学习率分支 的分支变量选择,带有 Reason Side Rewarding 或 EVSIDS
  • CaDiCaL 类的扩展阶段保存
  • 受到 CadiCaL 启发的 重启稳定化
  • 子句活化
  • 跟踪保存

感谢 SAT 研究人员。

请查看关于最近更新的 变更日志

正确性

虽然 Splr 附带 绝对没有任何保证,但我愿意展示一些结果。

版本 0.17.0

安装

安装最新版本的 cargo 后,只需运行 cargo install splr。将安装两个可执行文件

  • splr -- 求解器
  • dmcr -- 一个非常简单的模型检查器,用于验证由 splr 生成的 可满足 赋值集。

或者,Nix 用户可以使用 nix build

关于 no_std 环境和特性 no_IO

如果您想为 no_std 环境构建库,或者如果您想使用 no_IO 功能进行编译,您必须运行以下命令:cargo build --lib --features no_IO。它们与 cargo install 不兼容。

  • [2024-02-03] 新增了功能 platform_wasm

用法

Splr 是一个独立程序,接受一个 CNF 文件。结果将保存到文件中,其格式由 SAT 竞赛 2011 规则 定义。

$ splr cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
unif-k3-r4.25-v360-c1530-S1293537826-039.cnf       360,1530 |time:   732.01
 #conflict:    9663289, #decision:     23326959, #propagate:      546184944
  Assignment|#rem:      351, #fix:        0, #elm:        9, prg%:   2.5000
      Clause|Remv:    69224, LBD2:     2857, BinC:        1, Perm:     1522
    Conflict|entg:   7.0499, cLvl:  12.2451, bLvl:  11.1030, /cpr:    30.74
    Learning|avrg:  10.4375, trnd:   1.0069, #RST:   566140, /dpc:     1.18
        misc|vivC:     7370, subC:        0, core:      122, /ppc:    61.53
      Result|file: ./ans_unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
s SATISFIABLE: cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
$ cat ans_unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
c This file was generated by splr-0.15.0 for cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
c
c unif-k3-r4.25-v360-c1530-S1293537826-039.cnf, #var:      360, #cls:     1530
c  #conflict:    9663289, #decision:     23326959, #propagate:      546184944,
c   Assignment|#rem:      351, #fix:        0, #elm:        9, prg%:   2.5000,
c       Clause|Remv:    69224, LBD2:     2857, BinC:        1, Perm:     1522,
c     Conflict|entg:   7.0499, cLvl:  12.2451, bLvl:  11.1030, /cpr:    30.74,
c      Learing|avrg:  10.4375, trnd:   1.0069, #RST:   566140, /dpc:     1.18,
c         misc|vivC:     7370, subC:        0, core:      122, /ppc:    61.53,
c     Strategy|mode:  generic, time:   732.03,
c
c   config::VarRewardDecayRate                       0.960
c   assign::NumConflict                        9663289
c   assign::NumDecision                       23326959
c   assign::NumPropagation                   546184944
c   assign::NumRephase                             734
c   assign::NumRestart                          566141
c   assign::NumVar                                 360
c   assign::NumAssertedVar                           0
c   assign::NumEliminatedVar                         9
c   assign::NumReconflict                          653
c   assign::NumRepropagation                  12460396
c   assign::NumUnassertedVar                       351
c   assign::NumUnassignedVar                       351
c   assign::NumUnreachableVar                        0
c   assign::RootLevel                                0
c   assign::AssignRate                               0.000
c   assign::DecisionPerConflict                      1.179
c   assign::PropagationPerConflict                  61.527
c   assign::ConflictPerRestart                     122.388
c   assign::ConflictPerBaseRestart                 122.388
c   assign::BestPhaseDivergenceRate                  0.000
c   clause::NumBiClause                              1
c   clause::NumBiClauseCompletion                    0
c   clause::NumBiLearnt                              1
c   clause::NumClause                            70746
c   clause::NumLBD2                               2857
c   clause::NumLearnt                            69224
c   clause::NumReduction                          1461
c   clause::NumReRegistration                        0
c   clause::Timestamp                                0
c   clause::LiteralBlockDistance                    10.438
c   clause::LiteralBlockEntanglement                 7.050
c   state::Vivification                            735
c   state::VivifiedClause                         7370
c   state::VivifiedVar                               0
c   state::NumCycle                                734
c   state::NumStage                               1461
c   state::IntervalScale                             1
c   state::IntervalScaleMax                       1024
c   state::BackjumpLevel                            11.103
c   state::ConflictLevel                            12.245
c
s SATISFIABLE
v 1 -2 3 4 5 6 -7 -8 9 -10 -11 -12 13 -14 ...  -360 0
$ dmcr cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
A valid assignment set for cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf is found in ans_unif-k3-r4.25-v360-c1530-S1293537826-039.cnf

如果您想证明不可满足性,请使用 --certify-c,并使用如 Grid 这样的证明检查器。

首先使用证书选项 -c 运行 splr。

$ splr -c cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf
unif-k3-r4.25-v360-c1530-S1028159446-096.cnf       360,1530 |time:   204.09
 #conflict:    4018458, #decision:      9511129, #propagate:      221662222
  Assignment|#rem:      345, #fix:        7, #elm:        8, prg%:   4.1667
      Clause|Remv:    11290, LBD2:     2018, BinC:      137, Perm:     1517
    Conflict|entg:   4.5352, cLvl:   8.0716, bLvl:   6.9145, /cpr:   112.08
    Learning|avrg:   1.5625, trnd:   0.2219, #RST:   237295, /dpc:     1.07
        misc|vivC:     4085, subC:        0, core:      345, /ppc:    52.55
      Result|file: ./ans_unif-k3-r4.25-v360-c1530-S1028159446-096.cnf
 Certificate|file: proof.drat
s UNSATISFIABLE: cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf

A: 使用 drat-trim 验证

$ drat-trim cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf proof.drat
c parsing input formula with 360 variables and 1530 clauses
c finished parsing
c detected empty clause; start verification via backward checking
c 1530 of 1530 clauses in core
c 2036187 of 4029964 lemmas in core using 68451907 resolution steps
c 0 RAT lemmas in core; 908116 redundant literals in core lemmas
s VERIFIED
c verification time: 105.841 seconds

B: 使用 gratchk 验证

首先,您必须将生成的 DRAT 文件转换为 GRAT 文件。

$ gratgen cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf proof.drat -o proof.grat
c sizeof(cdb_t) = 4
c sizeof(cdb_t*) = 8
c Using RAT run heuristics
c Parsing formula ... 1ms
c Parsing proof (ASCII format) ... 32251ms
c Forward pass ... 2073ms
c Starting Backward pass
c Single threaded mode

0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
***************************************************
c Waiting for aux-threads ...done
c Lemmas processed by threads: 2032698 mdev: 0
c Finished Backward pass: 65356ms
c Writing combined proof ... 19088ms
s VERIFIED
c Timing statistics (ms)
c Parsing:  32253
c Checking: 67465
c   * bwd:  65356
c Writing:  19088
c Overall:  118808
c   * vrf:  99720

c Lemma statistics
c RUP lemmas:  2032698
c RAT lemmas:  0
c   RAT run heuristics:   0
c Total lemmas:  2032698

c Size statistics (bytes)
c Number of clauses: 4031493
c Clause DB size:  309680252
c Item list:       128778112
c Pivots store:    16777216

然后使用 gratchk 验证它。

$ gratchk unsat cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf proof.grat
c Reading cnf
c Reading proof
c Done
c Verifying unsat
s VERIFIED UNSAT

从 Rust 程序中调用 Splr

从 0.4.0 版本开始,您可以在您的程序中使用 Splr。(这里假设您使用 Rust 2021。)

use splr::*;

fn main() {
    let v: Vec<Vec<i32>> = vec![vec![1, 2], vec![-1, 3], vec![1, -3], vec![-1, 2]];
    match Certificate::try_from(v) {
        Ok(Certificate::SAT(ans)) => println!("s SATISFIABLE: {:?}", ans),
        Ok(Certificate::UNSAT) => println!("s UNSATISFIABLE"),
        Err(e) => panic!("s UNKNOWN; {}", e),
    }
}

所有解决方案 SAT 求解器都是作为 'incremental_solver' 功能的应用程序

以下示例需要 'incremental_solver' 功能。您需要以下依赖项

splr = { version = "^0.17", features = ["incremental_solver"] }

在此配置下,模块 splr 提供了一些额外的功能

  • splr::求解器::重置(&mut self)
  • splr::Solver::add_var(&mut self) // 增加最后一个变量的编号
  • splr::求解器::添加子句(&mut self,vec: AsRef<[i32]>) -> Result<&mutSolver, SolverError>

在调用 add_varadd_clausesolve 之前,您必须先调用 reset。通过这种方式,splr 忘记了关于之前公式的所有信息,特别是由预处理器/中间处理器(如子句归约)引起的非等价变换。所以从技术上讲,splr 不是一个增量求解器。

如果 vec 无效,则 'add_clause' 将会发出错误。

use splr::*;
use std::env::args;

fn main() {
    let cnf = args().nth(1).expect("takes an arg");
    let assigns: Vec<i32> = Vec::new();
    println!("#solutions: {}", run(&cnf, &assigns));
}

#[cfg(feature = "incremental_solver")]
fn run(cnf: &str, assigns: &[i32]) -> usize {
    let mut solver = Solver::try_from(cnf).expect("panic at loading a CNF");
    for n in assigns.iter() {
        solver.add_assignment(*n).expect("panic at assertion");
    }
    let mut count = 0;
    loop {
        match solver.solve() {
            Ok(Certificate::SAT(ans)) => {
                count += 1;
                println!("s SATISFIABLE({}): {:?}", count, ans);
                let ans = ans.iter().map(|i| -i).collect::<Vec<i32>>();
                match solver.add_clause(ans) {
                    Err(SolverError::Inconsistent) => {
                        println!("c no answer due to level zero conflict");
                        break;
                    }
                    Err(e) => {
                        println!("s UNKNOWN; {:?}", e);
                        break;
                    }
                    Ok(_) => solver.reset(),
                }
            }
            Ok(Certificate::UNSAT) => {
                println!("s UNSATISFIABLE");
                break;
            }
            Err(e) => {
                println!("s UNKNOWN; {}", e);
                break;
            }
        }
    }
    count
}

从 0.4.1 版本开始,Solveriter()。因此,您可以迭代满足的 'solution: Vec<i32>',如下所示

#[cfg(feature = "incremental_solver")]
for (i, v) in Solver::try_from(cnf).expect("panic").iter().enumerate() {
    println!("{}-th answer: {:?}", i, v);
}

来自我的 数独求解器 的示例代码

https://github.com/shnarazk/sudoku_sat/blob/4490b4358e5f3b72803a566323a6c8c196627f92/src/bin/sudoku400.rs#L36-L60

    let mut solver = Solver::try_from((config, rules.as_ref())).expect("panic");
    for a in setting.iter() {
        solver.add_assignment(*a).expect("panic");
    }
    for ans in solver.iter().take(1) {
        let mut picked = ans.iter().filter(|l| 0 < **l).collect::<Vec<&i32>>();
        for _i in 1..=range {
            for _j in 1..=range {
                let (_i, _j, d, _b) = Cell::decode(*picked.remove(0));
                print!("{:>2} ", d);
            }
            println!();
        }
        println!();
    }
}

进度消息中使用的助记符

mnemonic meaning
#var 在给定的 CNF 文件中使用的变量数
#cls 在给定的 CNF 文件中使用的子句数
time 经过的 CPU 时间(以秒为单位,如果不可用则为时钟时间)
#conflict 冲突次数
#decision 决策次数
#propagate 传播次数(单位是文字)
#rem 剩余变量的数量
#fix 断言变量的数量(在决策层零时已赋值)
#elm 消除变量的数量
prg% 剩余变量 / 总变量的百分比
Remv 当前非双谓词学习子句的数量
LBD2 累积的LBD为2的学习子句数量
BinC 当前二进制学习子句的数量
Perm 当前给定子句和二进制学习子句的数量
entg '文字块纠缠'的平均值
cLvl 发生冲突的决策级别的EMA
bLvl 回溯跳跃到的决策级别的EMA
/cpr 每次重启的冲突EMA
avrg 学习子句的LBD的EMA,指数移动平均
trnd LBD的EMA当前趋势
#RST 重启次数
/dpc 每次冲突的决策EMA
vivC 活化子句的数量
subC 被消除子句处理器归约的子句数量
core 不可达变量的数量
/ppc 每次冲突的传播EMA
time 已过CPU时间(秒)

命令行选项

A modern CDCL SAT solver in Rust
Activated features: best phase tracking, stage-based clause elimination, stage-based clause vivification, stage-based dynamic restart threshold, Learning-Rate Based rewarding, reason-side rewarding, stage-based re-phasing, two-mode reduction, trail saving, unsafe access

USAGE:
  splr [FLAGS] [OPTIONS] <cnf-file>
FLAGS:
  -h, --help                Prints help information
  -C, --no-color            Disable coloring
  -q, --quiet               Disable any progress message
  -c, --certify             Writes a DRAT UNSAT certification file
  -j, --journal             Shows log about restart stages
  -l, --log                 Uses Glucose-like progress report
  -V, --version             Prints version information
OPTIONS:
      --cl <c-cls-lim>      Soft limit of #clauses (6MC/GB)         0
      --crl <cls-rdc-lbd>   Clause reduction LBD threshold          5
      --cr1 <cls-rdc-rm1>   Clause reduction ratio for mode1        0.20
      --cr2 <cls-rdc-rm2>   Clause reduction ratio for mode2        0.05
      --ecl <elm-cls-lim>   Max #lit for clause subsume            64
      --evl <elm-grw-lim>   Grow limit of #cls in var elim.         0
      --evo <elm-var-occ>   Max #cls for var elimination        20000
  -o, --dir <io-outdir>     Output directory                         .
  -p, --proof <io-pfile>    DRAT Cert. filename                 proof.drat
  -r, --result <io-rfile>   Result filename/stdout
  -t, --timeout <timeout>   CPU time limit in sec.               5000
      --vdr <vrw-dcy-rat>   Var reward decay rate                   0.96
ARGS:
  <cnf-file>    DIMACS CNF file

求解器描述

Splr-0.17.0默认采用以下功能

  • 基于学习率的(LRB)变量奖励和子句奖励[4]
  • 理由侧变量奖励[4]
  • 按时间顺序回溯[5] 由于不正确的UNSAT证书,自0.12版以来已禁用。
  • 子句活化[6]
  • 基于冲突数量的Luby序列定义了'阶段/周期/段',这些用作重启的触发器
    • 重启
    • 子句减少
    • 处理器内(子句消除、归约和活化)
    • 变量阶段和变量活动的重新配置
    • 基于子句质量的理由细化扩展的跟踪保存的重新配置

(Splr-0.15.0及以上版本尝试丢弃之前版本中使用的各种基于动态和启发式控制的方案。)

以下图表解释了最新Splr中使用的流程

search algorithm in Splr 0.17

我在这里使用以下术语

  • 一个阶段 -- 求解器使用相同重启参数的跨度
  • 一个周期 -- 对应的Luby值形成一个非递减序列的连续跨度组
  • 一个段 -- 被新的最大Luby值出现分隔的连续周期组

参考文献

  • G. Audemard和L. Simon,"在现代SAT求解器中预测学习子句的质量",在2009年国际人工智能联合会议,第399–404页,2009。

  • G. Audemard和L. Simon,"针对SAT和UNSAT的重启策略细化",在LNCS,第7513卷,第118–126页,2012。

  • R. Hickey和F. Bacchus,"回溯的跟踪保存",SAT 2020LNCS,第12178卷,第46-61页,2020。

  • J. H. Liang,V. Ganesh,P. Poupart和K. Czarnecki,"基于学习率的SAT求解器分支启发式算法",在LNCS,第9710卷,第123–140页,2016。

  • A. Nadel和V. Ryvchin,"按时间顺序回溯",在2018年SAT测试理论和应用 - SAT 2018,2018年6月,第111–121页,2018。

  • C. Piette,Y. Hamadi和L. Saïs,"活化命题子句公式",Front. Artif. Intell. Appl.,第178卷,第525–529页,2008。

许可证

本源代码形式受Mozilla公共许可证(MPL)第2.0版条款约束。如果本文件未附带MPL副本,您可以在http://mozilla.org/MPL/2.0/获得一份。


2020-2024,Narazaki Shuji

依赖关系

~0–1.4MB
~28K SLoC