Skip to content

Repository files navigation

A modern SAT Solver for Propositional Logic in Rust

Splr is a modern and deterministic SAT solver in Rust, inspired by Glucose 4.1 and Kissat. It adopts, or adopted, various research results on modern SAT solvers:

  • CDCL, watch literals, LBD and so on from Kissat, Glucose, Minisat and the ancestors
  • Luby series based restart control. The current implementation of the Luby series has $O(1)$ time complexity and $O(1)$ space complexity. Its correctness is proved in Lean4.
  • pre/in-processor to simplify the given CNF
  • two branching variable selection schemes: Learning-Rate Based Branching (LRB) with Reason Side Rewarding and Variable Move To the Front (VMTF)
  • clause vivification
  • trail saving
  • various rephasing schemes

Many thanks to SAT researchers.

Please check ChangeLog about recent updates.

Correctness

Though Splr comes with ABSOLUTELY NO WARRANTY, I'd like to show some results.

Version 0.19.0

  • All satisfiable problems were checked by dmcr (src/bin/dmcr)
  • All unsatisfiable problems were checked by drat-trim
# CNF picked from SAT Competition 2025 time (s) ret result
1 04648cef5bed430ab6429991fa9e107d-ramsey_3_6_19.norm TO 124 --
2 0c0430a68f147be18ab3fded07f30fdb-oddball_53_5_tto_z 1159.27 10
3 0ccb0f855352783a972be45188bf3164-SCPC-500-12.cnf 76.68 20
4 0e1d562093d5f4fc9013cf4a14a03f70-Break_12_50.xml.cn 12.19 10
5 110f8eb8b9b80204fe955ea0973bbb00-clqcl_30_7_6.norma TO 124 --
6 24bde22f729a988fb2394b644cb60d39-SC25_Timetable_C_4 12.00 10
7 2d0c041c0fe72dc32527bfbf34f63e61-170223547.cnf TO 124 --
8 35b9091b90bd28a492c9556d6fc4348d-bp4_TCO_CSO_ZR.nor 606.28 10
9 35ec95b9b2398fb522db178855016ae0-MVRoundRobin_n14_d TO 124 --
10 46a8727e27d848faafd83a990c2e01a7-case8.normalised.c TO 124 --
11 482295be38dc1d63a16f3cf649ef7ef6-myciel6-cn.used-as 4296.87 20
12 53c21f3e78f060883026b5a12ba691d8-maximum_constraine 14.02 10
13 57b478982ee9aba245ba792452b18fe3-VanDerWaerden_pd_2 334.90 10
14 6147e666b75f603a4c4490d21ab654cd-hid-uns-enc-6-1-0- 790.16 20
15 65f7145996bbec02b90bd0fa64a20502-test_v7_r12_vr10_c TO 124 --
16 68e33d998466bbdd4bfb7249a5790e4f-arles_thres10_p10_ 0.02 20
17 83aa254f7d17e1df7bee19322ac4752b-1.normalised.cnf TO 124 --
18 8e62c5d47920ffe36052f86177403e70-SC25_Timetable_C_3 26.73 10
19 908433870bee8ba2c86f266d0b002fdb-MVRoundRobin_n20_d TO 124 --
20 918d9e7c2e197312517736421d728958-SCPC-500-1.cnf 118.29 20
21 91c429adc2dc8430461b6d87a9aef335-16_16_booth_wallac TO 124 --
22 967b58fea99a99b8da592d3e2fe7139b-dubois50.cnf.mis-9 TO 124 --
23 98a9352230efc411c092f1dcdcdedcfc-bp4_BC012_IXA_LPI_ TO 124 --
24 9b5f767eb5c14eb888d51acf70e045c8-uniqinv40prop.cnf 2606.99 20
25 a0bcdaffb0ea36b678899fd86bdc7f18-arles_thres10_p10_ 0.03 20
26 a1fdd60d2570f47fb14956ac9e96951f-oddball_22_5_ttf.n 29.45 20
27 a70883771fd1c210d94a916d52510a3a-gm28sparrc.cnf 15.67 20
28 b3d3680b3287a989ce61a6db1054efd2-case20.normalised. 34.24 10
29 b9ed6fd14f4fc969ec966a4b54c36872-n320p5q2_n.apx_16. 67.38 10
30 c21096fa2f550785c33dc862d83bc941-case17.normalised. 441.79 10
31 cb950b9accfb53eb98f77b0f995ac0ae-rphp5_050_shuffled TO 124 --
32 d5928883c1e1f70764a31a83aa419eaf-oski15a01b42s_opt. TO 124 --
33 d8666a18cf3a32af0a606099f0070b4b-7.normalised.cnf TO 124 --
34 ddf9620410e6a4351f64c745670ef5d4-oddball_57_5_tto_z 1432.37 10
35 e23edb67db2d1dfdbfe2f4c02d09c6c7-14.normalised.cnf 1010.25 10
36 e430acf720b63044e5c825a00a76b0eb-rphp_p25_r25.cnf TO 124 --
37 e442248e155eb81a811edd1deca8a2cd-sudoku-N30-23.cnf TO 124 --
38 f17dfbed8c18716a41b231702e127524-SC25_Timetable_C_4 667.20 10
39 f25a1df88f89c6bcbe2602fa7f6e816b-1-TC-256-K-63.sani 399.84 10
40 f33a6163305d6559043b7438a692dea9-simon-r17-1.saniti 2991.86 10

med: 226.59, max: 4296.87,total (except 16 timeouts): 17144.50

Version 0.17.0

Install

Just run cargo install splr after installing the latest cargo. Two executables will be installed:

  • splr -- the solver
  • dmcr -- a very simple model checker to verify a satisfiable assignment set generated by splr.

Alternatively, Nix users can use nix build.

About no_std environment and feature no_IO

If you want to build a library for no_std environment, or if you want to compile with feature no_IO, you have to run cargo build --lib --features no_IO. They are incompatible with cargo install.

  • [2024-02-03] Feature platform_wasm was added.

Usage

Splr is a standalone program, taking a CNF file. The result will be saved to a file, whose format is defined by SAT competition 2011 rules.

$ splr cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf
unif-k3-r4.25-v360-c1530-S1293537826-039.cnf       360,1530 |time:  1653.99
 #conflict:    8496129, #decision:     13925320, #propagate:      398292916
  Assignment|#rem:      350, #fix:        0, #elm:       10, prg%:   2.7778
      Clause|Remv:    78724, LBD2:    53329, BinC:        0, Perm:     1522
    Conflict|cLvl:    17.39, bLvl:    16.25,  LBD:    16.52, /cpr: 39415.27
  Luby stage| idx:      263, ti1%:    19.00, ti2%:     0.41, /dpc:     1.27
      LRB(★)| LRB:    17.86, VMTF:    17.86, core:      128, /ppc:    56.77
      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.19.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:    8496129, #decision:     13925320, #propagate:      398292916,
c   Assignment|#rem:      350, #fix:        0, #elm:       10, prg%:   2.7778,
c       Clause|Remv:    78724, LBD2:    53329, BinC:        0, Perm:     1522,
c     Conflict|cLvl:  17.3942, bLvl:  16.2540, #RST:      350, /cpr: 39415.27,
c      Learing| LBD:  16.5243, ti1%:  18.9986, ti2%:     0.41, /dpc:     1.27,
c         misc| LRB:    17.86, VMTF:    17.86, core:      128, /ppc:    56.77,
c     Strategy|mode:  generic, time:  1653.99,
c
c   assign::NumConflict                        8496129
c   assign::NumDecision                       13925320
c   assign::NumPropagation                   398292916
c   assign::NumRestart                             264
c   assign::NumVar                                 360
c   assign::NumAssertedVar                           0
c   assign::NumEliminatedVar                        10
c   assign::NumReconflict                          704
c   assign::NumRepropagation                    417429
c   assign::NumUnassertedVar                       350
c   assign::NumUnassignedVar                       350
c   assign::RootLevel                                0
c   assign::DecisionPerConflict                      1.272
c   assign::PropagationPerConflict                  56.765
c   assign::ConflictPerRestart                   39031.375
c   assign::ConflictPerBaseRestart               39031.375
c   assign::ConlictDistanceAverage                   0.000
c   clause::NumBiClause                              0
c   clause::NumBiLearnt                              0
c   clause::NumClause                            80246
c   clause::NumLBD2                              53329
c   clause::NumLearnt                            78724
c   clause::NumReduction                           263
c   clause::NumReRegistration                        0
c   clause::LBD                                     16.524
c   clause::Tier1ClauseRatio                         0.190
c   clause::Tier2ClauseRatio                         0.004
c   state::Vivification                            264
c   state::VivifiedClause                         3151
c   state::VivifiedVar                               0
c   state::NumCycle                                  7
c   state::NumStage                                134
c   state::IntervalScale                             2
c   state::IntervalScaleMax                          7
c   state::ChronologicalBacktrackRate                0.000
c   state::BackjumpLevel                            20.567
c   state::ConflictLevel                            21.800
c   LBD 1 (0.004): 0.000, 0.004, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
c   LBD 2 (0.031): 0.000, 0.031, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
c   LBD 3 (0.107): 0.000, 0.000, 0.107, 0.000, 0.000, 0.000, 0.000, 0.000
c   LBD 4 (0.201): 0.000, 0.000, 0.201, 0.000, 0.000, 0.000, 0.000, 0.000
c   LBD 5 (0.250): 0.000, 0.000, 0.250, 0.000, 0.000, 0.000, 0.000, 0.000
c   LBD 6 (0.197): 0.000, 0.000, 0.197, 0.000, 0.000, 0.000, 0.000, 0.000
c   LBD>6 (0.210): 0.000, 0.000, 0.000, 0.199, 0.010, 0.000, 0.000, 0.000
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

Verify UNSAT answer with drat-trim

If you want to certificate unsatisfiability, use --certify or -c and use a proof checker like drat-trim, Grat, or gratchk, and so on.

Firstly run splr with the certificate option -c.

$ splr -c cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf
unif-k3-r4.25-v360-c1530-S1028159446-096.cnf       360,1530 |time:   433.91
 #conflict:    4567454, #decision:      7606131, #propagate:      206567216
  Assignment|#rem:      321, #fix:       28, #elm:       11, prg%:  10.8333
      Clause|Remv:    19906, LBD2:    37637, BinC:      286, Perm:     1403
    Conflict|cLvl:     9.00, bLvl:     7.89,  LBD:     3.72, /cpr: 20057.27
  Luby stage| idx:      172, ti1%:    18.51, ti2%:     0.13, /dpc:     1.07
      LRB(→)| LRB:    29.47, VMTF:    24.14, core:      295, /ppc:    44.30
      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

Then use 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 2505800 of 4619164 lemmas in core using 84730284 resolution steps
c 0 RAT lemmas in core; 1532064 redundant literals in core lemmas
s VERIFIED
c verification time: 161.150 seconds

Calling Splr from Rust programs

Since 0.4.0, you can use Splr in your programs. (Here I suppose that you use 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),
    }
}

Note: As of version 0.18.0, Splr no longer supports incremental mode, as it is not part of the development direction.

sample code from my sudoku solver

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!();
    }
}

Mnemonics used in the progress message

mnemonic meaning
#var the number of variables used in the given CNF file
#cls the number of clauses used in the given CNF file
time the elapsed CPU time in seconds
#conflict the number of conflicts
#decision the number of decisions
#propagate the number of propagates (its unit is literal)
#rem the number of remaining variables
#fix the number of asserted variables (which have been assigned a value at decision level zero)
#elm the number of eliminated variables
prg% the percentage of remaining variables / total variables
Remv the current number of learnt clauses that are not bi-clauses
LBD2 the accumulated number of learnt clauses which LBDs are 2
BinC the current number of binary learnt clauses
Perm the current number of given clauses and binary learnt clauses
cLvl the EMA, Exponential Moving Average, of decision levels at which conflicts occur
bLvl the EMA of decision levels to which backjumps go
LBD the EMA of LBD of learnt clauses
/cpr the EMA of conflicts per restart
idx the current index of Luby sequence
ti1% the ratio of the learnt clauses which LBDs are in [3,6]
ti2% the ratio of the learnt clauses which LBDs are greater than 6
/dpc the EMA of decisions per conflict
LRB(-) the current Var reward scheme is Learning-Rate Based
LongLRB(-) the current Var reward scheme is Learning-Rate Based with a very long span
VMTF(-) the current Var reward scheme is VMTF
LongVMTF(-) the current Var reward scheme is VMTF with a very long span
(→) the active rephasing scheme is walk
(★) the active rephasing scheme is best
(⊥) the active rephasing scheme is false
(⊤) the active rephasing scheme is true
(∼) the active rephasing scheme is random
(¬) the active rephasing scheme is Inverted
(φ) the active rephasing scheme is polarity
LRB the ratio of asserted variables found by LRB in percentages
VMTF the ratio of asserted variables found by VMTF in percentages
core the number of unreachable variables
/ppc the EMA of propagations per conflict

Command line options


A modern CDCL SAT solver in Rust
Activated features: Luby-based clause elimination, Luby-based clause vivification,
 reason-side rewarding, Luby-based re-phasing, 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
  -H, --heatmap             Shows clause heatmap
  -j, --journal             Shows log about restart stages
  -V, --version             Prints version information
OPTIONS:
      --cl <c-cls-lim>      Soft limit of #clauses (6MC/GB)         0
      --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
ARGS:
  <cnf-file>    DIMACS CNF file

Solver description

Splr-0.19.0 adopts the following features by default:

  • Learning-Rate Based (LRB) var rewarding[4]
  • Reason-side var rewarding[4]
  • chronological backtrack[5] disabled since 0.12 due to incorrect UNSAT certificates.
  • Trail saving[3]
  • clause vivification[6]
  • Luby series based on the number of conflicts is used as the trigger of
    • restart
    • clause reduction
    • in-processor (clause elimination, subsumption and vivification)
    • re-configuration of var phases and var activities

The following figure explains the flow used in the latest Splr.

search algorithm in Splr 0.19

Bibliography

  • [1] G. Audemard and L. Simon, "Predicting learnt clauses quality in modern SAT solvers," in International Joint Conference on Artificial Intelligence 2009, pp. 399–404, 2009.

  • [2] G. Audemard and L. Simon, "Refining restarts strategies for SAT and UNSAT," in LNCS, vol.7513, pp.118–126, 2012.

  • [3] R. Hickey and F. Bacchus, "Trail Saving on Backtrack", SAT 2020, LNCS, vol. 12178, pp.46-61, 2020.

  • [4] J. H. Liang, V. Ganesh, P. Poupart, and K. Czarnecki, "Learning Rate Based Branching Heuristic for SAT Solvers," in LNCS, vol.9710, pp.123–140, 2016.

  • [5] A. Nadel and V. Ryvchin, "Chronological Backtracking," in Theory and Applications of Satisfiability Testing - SAT 2018, June 2018, pp.111–121, 2018.

  • [6] C. Piette, Y. Hamadi, and L. Saïs, "Vivifying propositional clausal formulae," Front. Artif. Intell. Appl., vol.178, pp.525–529, 2008.

License

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.


2020-2026, Narazaki Shuji

About

A modern (trail saving, pre/in processor, LRB, rephrase rotation) CDCL SAT solver in Rust, strongly inspired by Kissat

Topics

Resources

Stars

114 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages