├── cnfs ├── empty-form.cnf ├── empty-clause.cnf ├── issue-182.cnf ├── uf8.cnf ├── README.md ├── uf20-01.cnf ├── uf100-010.cnf └── unsat.cnf ├── src ├── primitive │ ├── mod.rs │ ├── luby.rs │ └── ema.rs ├── assign │ ├── ema.rs │ ├── evsids.rs │ ├── learning_rate.rs │ ├── select.rs │ ├── trail_saving.rs │ ├── var.rs │ ├── heap.rs │ └── mod.rs ├── cdb │ ├── activity.rs │ ├── ema.rs │ ├── cid.rs │ ├── watch_cache.rs │ ├── unsat_certificate.rs │ ├── sls.rs │ ├── binary.rs │ └── clause.rs ├── solver │ ├── restart.rs │ ├── validate.rs │ └── stage.rs ├── lib.rs ├── processor │ ├── subsume.rs │ ├── mod.rs │ ├── heap.rs │ └── eliminate.rs ├── bin │ ├── dmcr.rs │ └── splr.rs └── cnf │ └── mod.rs ├── .gitlab-ci.yml ├── LICENSE ├── .gitignore ├── .github ├── workflows │ └── rust.yml └── dependabot.yml ├── misc ├── algorithm.tex └── splr-all.rs ├── flake.lock ├── flake.nix ├── tests ├── isat2.rs ├── isat.rs └── verify-certificates.rs ├── Cargo.toml └── Cargo.lock /cnfs/empty-form.cnf: -------------------------------------------------------------------------------- 1 | p cnf 0 0 2 | -------------------------------------------------------------------------------- /cnfs/empty-clause.cnf: -------------------------------------------------------------------------------- 1 | p cnf 0 1 2 | 0 3 | 4 | -------------------------------------------------------------------------------- /src/primitive/mod.rs: -------------------------------------------------------------------------------- 1 | /// methods on clause activity 2 | pub mod ema; 3 | /// methods on binary link, namely binary clause 4 | pub mod luby; 5 | 6 | pub use self::{ema::*, luby::*}; 7 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | 4 | rust-latest: 5 | stage: build 6 | image: rust:latest 7 | script: 8 | - cargo build --verbose 9 | - cargo test --verbose 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This Source Code Form is subject to the terms of the Mozilla Public 2 | License, v. 2.0. If a copy of the MPL was not distributed with this 3 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | 4 | # Generated by Cargo 5 | # will have compiled files and executables 6 | /target/ 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | 11 | *.cnf 12 | .ans_* 13 | .vscode 14 | *.ipynb 15 | /result 16 | -------------------------------------------------------------------------------- /cnfs/issue-182.cnf: -------------------------------------------------------------------------------- 1 | p cnf 6 19 2 | -2 0 3 | -1 -2 0 4 | -3 -4 0 5 | 1 3 4 -5 0 6 | 1 -3 -4 -5 0 7 | -1 3 -4 -5 0 8 | -1 -3 4 -5 0 9 | -1 -3 -4 5 0 10 | -1 3 4 5 0 11 | 1 -3 4 5 0 12 | 1 3 -4 5 0 13 | 1 3 -6 0 14 | 1 4 -6 0 15 | 3 4 -6 0 16 | -1 -3 6 0 17 | -1 -4 6 0 18 | -3 -4 6 0 19 | -1 -6 0 20 | -5 -6 0 21 | -------------------------------------------------------------------------------- /cnfs/uf8.cnf: -------------------------------------------------------------------------------- 1 | c -1: 2, 1: 2 2 | c -2: 2, 2: 3 3 | c -3: 2, 3: 4 4 | c -4: 2, 4: 1 5 | c -5: 3, 5: 2 6 | c -6: 3, 6: 2 7 | c -7: 2, 7: 3 8 | c -8: 3, 8: 3 9 | p cnf 8 13 10 | 4 -8 1 0 11 | 3 8 -5 0 12 | -5 -8 -6 0 13 | -2 7 -6 0 14 | 1 -3 -7 0 15 | -3 -8 7 0 16 | 2 3 5 0 17 | -6 3 8 0 18 | 2 -5 -7 0 19 | 8 -2 3 0 20 | -1 -4 7 0 21 | -1 5 6 0 22 | 2 6 -4 0 23 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Prepare 11 | run: rustup update 12 | - name: Build executables 13 | run: cargo build --verbose 14 | - name: Build library 15 | run: cargo build --lib --verbose 16 | - name: Run tests 17 | run: cargo test --verbose 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /src/assign/ema.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | const ASG_EWA_LEN: usize = 16; 4 | const ASG_EWA_SLOW: usize = 8192; 5 | 6 | /// An assignment history used for blocking restart. 7 | #[derive(Clone, Debug)] 8 | pub struct ProgressASG { 9 | ema: Ewa2, 10 | } 11 | 12 | impl Default for ProgressASG { 13 | fn default() -> ProgressASG { 14 | ProgressASG { 15 | ema: Ewa2::::new(0.0), 16 | } 17 | } 18 | } 19 | 20 | impl Instantiate for ProgressASG { 21 | fn instantiate(_config: &Config, _cnf: &CNFDescription) -> Self { 22 | ProgressASG { 23 | ema: Ewa2::new(0.0).with_slow(ASG_EWA_SLOW), 24 | } 25 | } 26 | } 27 | 28 | impl EmaIF for ProgressASG { 29 | fn get_fast(&self) -> f64 { 30 | self.ema.get() 31 | } 32 | fn trend(&self) -> f64 { 33 | self.ema.trend() 34 | } 35 | } 36 | 37 | impl EmaMutIF for ProgressASG { 38 | type Input = usize; 39 | fn update(&mut self, n: usize) { 40 | self.ema.update(n as f64); 41 | } 42 | fn as_view(&self) -> &EmaView { 43 | self.ema.as_view() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /misc/algorithm.tex: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | \documentclass{article} 3 | \usepackage[boxed,ruled,vlined]{algorithm2e} 4 | \usepackage{xcolor} 5 | \begin{document} 6 | \begin{algorithm} 7 | \KwOut{satisfiability of the given CNF} 8 | \SetKwComment{Comment}{}{} 9 | \BlankLine 10 | \Begin{ 11 | \While{there is an unassigned or unpropagated var}{ 12 | \If{there is no unpropagated var}{ 13 | select a decision var and its phase\; 14 | } 15 | \uIf{BCP() returns an conflict}{ 16 | \If{decision level is root level}{ 17 | \Return{UNSAT}\; 18 | } 19 | CONFLICT-ANALYSIS()\; 20 | BACKTRACK()\; 21 | } 22 | \If{restart condition holds or the current stage ends}{ 23 | RESTART()\; 24 | \If{a new stage begins}{ 25 | REDUCE()\; 26 | update restart conditions\; 27 | \If{a new cycle begins}{ 28 | VIVIFY()\; 29 | select var phases\; 30 | \If{a new segment begins}{ 31 | ELIMINATE()\; 32 | rescale var activities\; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | \Return{SAT}\; 39 | } 40 | \caption{Main search loop in Splr-0.17 (src/solver/search.rs)} 41 | \end{algorithm} 42 | \end{document} 43 | -------------------------------------------------------------------------------- /src/cdb/activity.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "clause_rewarding")] 2 | use {super::ClauseId, crate::types::*, std::num::NonZeroU32}; 3 | 4 | /// clause activity 5 | /// Note: vivifier has its own conflict analyzer, which never call reward functions. 6 | 7 | impl ActivityIF for ClauseDB { 8 | fn activity(&self, _cid: ClauseId) -> f64 { 9 | unreachable!() 10 | } 11 | fn set_activity(&mut self, cid: ClauseId, val: f64) { 12 | self[cid].reward = val; 13 | } 14 | #[inline] 15 | fn reward_at_analysis(&mut self, cid: ClauseId) { 16 | self.clause[NonZeroU32::get(cid.ordinal) as usize].update_activity( 17 | self.tick, 18 | self.activity_decay, 19 | self.activity_anti_decay, 20 | ); 21 | } 22 | fn update_activity_tick(&mut self) { 23 | self.tick += 1; 24 | } 25 | fn update_activity_decay(&mut self, decay: f64) { 26 | self.activity_decay = decay; 27 | self.activity_anti_decay = 1.0 - decay; 28 | } 29 | } 30 | 31 | impl Clause { 32 | #[inline] 33 | pub fn update_activity(&mut self, t: usize, decay: f64, reward: f64) -> f64 { 34 | if self.timestamp < t { 35 | self.reward *= decay.powi(t as i32 - self.timestamp as i32); 36 | self.reward += reward; 37 | self.timestamp = t; 38 | } 39 | self.reward 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/cdb/ema.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | const LBD_EWA_LEN: usize = 16; 4 | const LBD_EWA_SLOW: usize = 8192; 5 | 6 | /// An EMA of learnt clauses' LBD, used for forcing restart. 7 | #[derive(Clone, Debug)] 8 | pub struct ProgressLBD { 9 | ema: Ewa2, 10 | num: usize, 11 | sum: usize, 12 | } 13 | 14 | impl Default for ProgressLBD { 15 | fn default() -> ProgressLBD { 16 | ProgressLBD { 17 | ema: Ewa2::new(0.0), 18 | num: 0, 19 | sum: 0, 20 | } 21 | } 22 | } 23 | 24 | impl Instantiate for ProgressLBD { 25 | fn instantiate(_config: &Config, _: &CNFDescription) -> Self { 26 | ProgressLBD { 27 | ema: Ewa2::new(0.0).with_slow(LBD_EWA_SLOW), 28 | ..ProgressLBD::default() 29 | } 30 | } 31 | } 32 | 33 | impl EmaIF for ProgressLBD { 34 | fn get_fast(&self) -> f64 { 35 | self.ema.get_fast() 36 | } 37 | fn get_slow(&self) -> f64 { 38 | self.ema.get_fast() 39 | } 40 | fn trend(&self) -> f64 { 41 | self.ema.trend() 42 | } 43 | } 44 | 45 | impl EmaMutIF for ProgressLBD { 46 | type Input = u16; 47 | fn update(&mut self, d: Self::Input) { 48 | self.num += 1; 49 | self.sum += d as usize; 50 | self.ema.update(d as f64); 51 | } 52 | fn reset_to(&mut self, val: f64) { 53 | self.ema.reset_to(val); 54 | } 55 | fn as_view(&self) -> &EmaView { 56 | self.ema.as_view() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/cdb/cid.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::ClauseId, 3 | std::{fmt, num::NonZeroU32}, 4 | }; 5 | 6 | /// API for Clause Id. 7 | pub trait ClauseIdIF { 8 | /// return `true` if a given clause id is made from a `Lit`. 9 | fn is_lifted_lit(&self) -> bool; 10 | } 11 | 12 | impl Default for ClauseId { 13 | #[inline] 14 | /// return the default empty clause, used in a reason slot or no conflict path. 15 | fn default() -> Self { 16 | ClauseId { 17 | ordinal: unsafe { NonZeroU32::new_unchecked(0x7FFF_FFFF) }, 18 | } 19 | } 20 | } 21 | 22 | impl From for ClauseId { 23 | #[inline] 24 | fn from(u: usize) -> ClauseId { 25 | ClauseId { 26 | ordinal: unsafe { NonZeroU32::new_unchecked(u as u32) }, 27 | } 28 | } 29 | } 30 | 31 | impl From for usize { 32 | #[inline] 33 | fn from(cid: ClauseId) -> usize { 34 | NonZeroU32::get(cid.ordinal) as usize 35 | } 36 | } 37 | 38 | impl fmt::Debug for ClauseId { 39 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 40 | write!(f, "{}C", self.ordinal) 41 | } 42 | } 43 | 44 | impl fmt::Display for ClauseId { 45 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 46 | write!(f, "{}C", self.ordinal) 47 | } 48 | } 49 | 50 | impl ClauseIdIF for ClauseId { 51 | /// return `true` if the clause is generated from a literal by Eliminator. 52 | fn is_lifted_lit(&self) -> bool { 53 | 0 != 0x8000_0000 & NonZeroU32::get(self.ordinal) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/assign/evsids.rs: -------------------------------------------------------------------------------- 1 | /// Var Rewarding based on EVSIDS 2 | use { 3 | super::{AssignStack, Var}, 4 | crate::types::*, 5 | }; 6 | 7 | impl ActivityIF for AssignStack { 8 | fn activity(&self, vi: VarId) -> f64 { 9 | self.var[vi].reward 10 | } 11 | fn set_activity(&mut self, vi: VarId, val: f64) { 12 | self.var[vi].reward = val; 13 | } 14 | fn reward_at_analysis(&mut self, vi: VarId) { 15 | let s = self.activity_decay_step; 16 | let t = self.ordinal; 17 | let v = &mut self.var[vi]; 18 | if v.timestamp == t { 19 | return; 20 | } 21 | v.timestamp = t; 22 | v.reward += s; 23 | const SCALE: f64 = 1e-100; 24 | const SCALE_MAX: f64 = 1e100; 25 | if SCALE_MAX < v.reward { 26 | for v in &mut self.var[1..] { 27 | v.reward *= SCALE; 28 | } 29 | self.activity_decay_step *= SCALE; 30 | } 31 | } 32 | fn update_activity_decay(&mut self, _: f64) { 33 | self.activity_decay = self 34 | .activity_decay_default 35 | .min(self.activity_decay + self.activity_decay_step); 36 | self.activity_anti_decay = 1.0 - self.activity_decay; 37 | } 38 | fn update_activity_tick(&mut self) { 39 | const INC_SCALE: f64 = 1.01; 40 | if self.ordinal == 0 { 41 | self.activity_decay *= 0.5; 42 | self.activity_anti_decay = 1.0 - self.activity_decay; 43 | } 44 | self.ordinal += 1; 45 | self.activity_decay_step *= INC_SCALE; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cnfs/README.md: -------------------------------------------------------------------------------- 1 | # A sample CNF collection 2 | 3 | ### Corner cases 4 | 5 | If you try to build your SAT solver from scratch, they are corner cases you have to pay attention. 6 | 7 | - [empty-clause.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/empty-clause.cnf) 8 | - [empty-form.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/empty-form.cnf) 9 | 10 | ### Basic examples 11 | 12 | If you think to complete CDCL algorithm, check with them. 13 | 14 | - [sample.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/sample.cnf) 15 | - [unsat.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/unsat.cnf) 16 | - [uf8.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/uf8.cnf) 17 | - [uf20-01.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/uf20-01.cnf) 18 | 19 | ### Midde scale problems 20 | 21 | And you think your solver is great, try them. 3-SAT problems (N=360) are hard. But your solver must solve them. 22 | 23 | - [uf100-010.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/uf100-010.cnf) 24 | - [uf250-02.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/uf250-02.cnf) 25 | - [unif-k3-r4.25-v360-c1530-S1028159446-096.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/unif-k3-r4.25-v360-c1530-S1028159446-096.cnf) 26 | - [unif-k3-r4.25-v360-c1530-S1293537826-039.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/unif-k3-r4.25-v360-c1530-S1293537826-039.cnf) 27 | 28 | ### Problems from the real competition 29 | 30 | If you catched up the implementations of modern solvers and reasech trend, try it. 31 | 32 | - [a_rphp035_05.cnf](https://github.com/shnarazk/splr/blob/main/cnfs/a_rphp035_05.cnf) 33 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1738060267, 6 | "narHash": "sha256-G1/V/sHbx5F9Vyu4soB6+wXdK64+HQG4qVl97ASdFR8=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "347934f4c466af8249ad5b28c0b81a41c2e1eb8e", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "repo": "nixpkgs", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs_2": { 19 | "locked": { 20 | "lastModified": 1730988789, 21 | "narHash": "sha256-Yx1f4uPcvel/iv9sbx60lVS8oihlgN+qeUuMZ3T4Vvc=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "c1612975107c932f1e903d8fab1636334402c6d2", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "NixOS", 29 | "repo": "nixpkgs", 30 | "type": "github" 31 | } 32 | }, 33 | "root": { 34 | "inputs": { 35 | "nixpkgs": "nixpkgs", 36 | "sat-bench": "sat-bench" 37 | } 38 | }, 39 | "sat-bench": { 40 | "inputs": { 41 | "nixpkgs": "nixpkgs_2" 42 | }, 43 | "locked": { 44 | "lastModified": 1737501229, 45 | "narHash": "sha256-duysXOsivHd3Gp5eel+fCuamjCpfPec548bB+RRVzNE=", 46 | "owner": "shnarazk", 47 | "repo": "SAT-bench", 48 | "rev": "42cf084f0b3b6993d58bfd1997e870421e904d38", 49 | "type": "github" 50 | }, 51 | "original": { 52 | "owner": "shnarazk", 53 | "repo": "SAT-bench", 54 | "type": "github" 55 | } 56 | } 57 | }, 58 | "root": "root", 59 | "version": 7 60 | } 61 | -------------------------------------------------------------------------------- /cnfs/uf20-01.cnf: -------------------------------------------------------------------------------- 1 | c This Formular is generated by mcnf 2 | c 3 | c horn? no 4 | c forced? no 5 | c mixed sat? no 6 | c clause length = 3 7 | c 8 | p cnf 20 91 9 | 4 -18 19 0 10 | 3 18 -5 0 11 | -5 -8 -15 0 12 | -20 7 -16 0 13 | 10 -13 -7 0 14 | -12 -9 17 0 15 | 17 19 5 0 16 | -16 9 15 0 17 | 11 -5 -14 0 18 | 18 -10 13 0 19 | -3 11 12 0 20 | -6 -17 -8 0 21 | -18 14 1 0 22 | -19 -15 10 0 23 | 12 18 -19 0 24 | -8 4 7 0 25 | -8 -9 4 0 26 | 7 17 -15 0 27 | 12 -7 -14 0 28 | -10 -11 8 0 29 | 2 -15 -11 0 30 | 9 6 1 0 31 | -11 20 -17 0 32 | 9 -15 13 0 33 | 12 -7 -17 0 34 | -18 -2 20 0 35 | 20 12 4 0 36 | 19 11 14 0 37 | -16 18 -4 0 38 | -1 -17 -19 0 39 | -13 15 10 0 40 | -12 -14 -13 0 41 | 12 -14 -7 0 42 | -7 16 10 0 43 | 6 10 7 0 44 | 20 14 -16 0 45 | -19 17 11 0 46 | -7 1 -20 0 47 | -5 12 15 0 48 | -4 -9 -13 0 49 | 12 -11 -7 0 50 | -5 19 -8 0 51 | 1 16 17 0 52 | 20 -14 -15 0 53 | 13 -4 10 0 54 | 14 7 10 0 55 | -5 9 20 0 56 | 10 1 -19 0 57 | -16 -15 -1 0 58 | 16 3 -11 0 59 | -15 -10 4 0 60 | 4 -15 -3 0 61 | -10 -16 11 0 62 | -8 12 -5 0 63 | 14 -6 12 0 64 | 1 6 11 0 65 | -13 -5 -1 0 66 | -7 -2 12 0 67 | 1 -20 19 0 68 | -2 -13 -8 0 69 | 15 18 4 0 70 | -11 14 9 0 71 | -6 -15 -2 0 72 | 5 -12 -15 0 73 | -6 17 5 0 74 | -13 5 -19 0 75 | 20 -1 14 0 76 | 9 -17 15 0 77 | -5 19 -18 0 78 | -12 8 -10 0 79 | -18 14 -4 0 80 | 15 -9 13 0 81 | 9 -5 -1 0 82 | 10 -19 -14 0 83 | 20 9 4 0 84 | -9 -2 19 0 85 | -5 13 -17 0 86 | 2 -10 -18 0 87 | -18 3 11 0 88 | 7 -9 17 0 89 | -15 -6 -3 0 90 | -2 3 -13 0 91 | 12 3 -2 0 92 | -2 -3 17 0 93 | 20 -15 -16 0 94 | -5 -17 -19 0 95 | -20 -18 11 0 96 | -9 1 -5 0 97 | -19 9 17 0 98 | 12 -2 17 0 99 | 4 -16 -5 0 100 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A modern SAT solver in Rust"; 3 | inputs.nixpkgs.url = "github:NixOS/nixpkgs"; 4 | inputs.sat-bench.url = "github:shnarazk/SAT-bench"; 5 | outputs = { self, nixpkgs, sat-bench }: 6 | { 7 | packages = builtins.listToAttrs 8 | (map 9 | (system: 10 | with import nixpkgs { system = "${system}"; }; 11 | { 12 | name = system; 13 | value = { 14 | default = 15 | rustPlatform.buildRustPackage rec { 16 | version = "0.17.4-20250128"; 17 | name = "splr-${version}"; 18 | pname = "splr"; 19 | src = self; 20 | cargoHash = "sha256-Mbd15EIej0903yh6LWUmegpfujZScKMXedqgNBjjM30="; 21 | buildInputs = rustc.buildInputs ++ lib.optional stdenv.isDarwin [ libiconv ]; 22 | buildPhase = "cargo build --release"; 23 | installPhase = '' 24 | mkdir -p $out/bin; 25 | install -t $out/bin target/release/splr target/release/dmcr 26 | ''; 27 | }; 28 | }; 29 | } 30 | ) 31 | [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ] 32 | ); 33 | devShell = builtins.listToAttrs 34 | (map 35 | (system: 36 | with import nixpkgs { system = "${system}"; }; 37 | { 38 | name = system; 39 | value = mkShell { 40 | packages = [ 41 | bashInteractive 42 | samply 43 | tokei 44 | # cargo-watch 45 | # nixpkgs.lldb_19 46 | sat-bench.packages.${system}.default 47 | ]; 48 | }; 49 | } 50 | ) 51 | [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ] 52 | ); 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /tests/isat2.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "incremental_solver")] 2 | /// WARNING: this test requires feature "incremental_solver". 3 | ///```ignore 4 | /// cargo test --test isat2 --features incremental_solver --release -- --nocapture --test-threads=1 5 | ///``` 6 | use splr::*; 7 | use std::{env::args, path::Path}; 8 | 9 | fn main() { 10 | let cnf = args().nth(1).expect("takes an arg"); 11 | let assumptions = Vec::new(); 12 | let ns1 = run(Path::new(&cnf), &assumptions, false); 13 | let ns2 = run(Path::new(&cnf), &assumptions, true); 14 | println!("#solution: {} w/o elim; {} w/ elim", ns1, ns2); 15 | } 16 | 17 | #[test] 18 | fn all_solutions_of_uf8() { 19 | drive(Path::new("cnfs/uf8.cnf"), vec![4, 5, -6, 7, 8]); 20 | } 21 | 22 | #[test] 23 | fn all_solutions_of_uf20() { 24 | drive(Path::new("cnfs/uf20-01.cnf"), vec![-4, 5, 6, 7, 8]); 25 | } 26 | 27 | #[allow(dead_code)] 28 | /// cargo test --test isat2 --features incremental_solver --release 29 | /// #[cfg_attr(feature = "incremental_solver", test)] 30 | fn all_solutions_of_uf100() { 31 | drive(Path::new("cnfs/uf100-010.cnf"), vec![]); 32 | } 33 | 34 | fn drive(cnf: &Path, mother: Vec) { 35 | for i in 0..=mother.len() { 36 | let assumptions = &mother[0..i]; 37 | let ns1 = run(cnf, assumptions, false); 38 | let ns2 = run(cnf, assumptions, true); 39 | println!("#solution: {} w/o elim; {} w/ elim", ns1, ns2); 40 | assert_eq!(ns1, ns2); 41 | } 42 | } 43 | 44 | fn run(cnf: &Path, assigns: &[i32], switch: bool) -> usize { 45 | println!("-------------------- {:?}, {}", assigns, switch); 46 | let mut solver = Solver::try_from(cnf).expect("panic"); 47 | // solver.state.config.enable_eliminator = switch; 48 | for n in assigns.iter() { 49 | solver.add_assignment(*n).expect("no"); 50 | } 51 | let mut count = 0; 52 | for res in solver.iter() { 53 | count += 1; 54 | println!("s SATISFIABLE({}): {:?}", count, res); 55 | } 56 | count 57 | } 58 | -------------------------------------------------------------------------------- /src/solver/restart.rs: -------------------------------------------------------------------------------- 1 | //! Module `restart` provides restart heuristics. 2 | use crate::types::*; 3 | 4 | /// API for [`restart`](`crate::solver::RestartIF::restart`) 5 | pub trait RestartIF: Instantiate { 6 | /// check blocking and forcing restart condition. 7 | fn restart(&mut self, ldb: &EmaView, ent: &EmaView) -> bool; 8 | /// set stabilization parameters 9 | fn set_stage_parameters(&mut self, step: usize); 10 | /// adjust restart threshold 11 | fn set_segment_parameters(&mut self, segment_scale: usize); 12 | } 13 | 14 | const FUEL: f64 = 2.0; 15 | const SCALE: f64 = 64.0; 16 | 17 | /// `RestartManager` provides restart API and holds data about restart conditions. 18 | #[derive(Clone, Debug, Default)] 19 | pub struct RestartManager { 20 | penetration_energy: f64, 21 | pub penetration_energy_charged: f64, 22 | penetration_energy_unit: f64, 23 | field_scale: f64, 24 | } 25 | 26 | impl Instantiate for RestartManager { 27 | fn instantiate(_config: &Config, _cnf: &CNFDescription) -> Self { 28 | RestartManager { 29 | penetration_energy: FUEL, 30 | penetration_energy_charged: FUEL, 31 | penetration_energy_unit: FUEL, 32 | field_scale: 1.0 / SCALE, 33 | } 34 | } 35 | fn handle(&mut self, e: SolverEvent) { 36 | if e == SolverEvent::Restart { 37 | self.penetration_energy = self.penetration_energy_charged; 38 | } 39 | } 40 | } 41 | 42 | impl RestartIF for RestartManager { 43 | fn restart(&mut self, lbd: &EmaView, ent: &EmaView) -> bool { 44 | let gscale = |x: f64| self.field_scale * (x - 1.0) + 1.0; 45 | self.penetration_energy -= (lbd.trend() + gscale(ent.trend())) - 2.0; 46 | self.penetration_energy < 0.0 47 | } 48 | fn set_segment_parameters(&mut self, segment_scale: usize) { 49 | let factor = 0.5 * (segment_scale.trailing_zeros() + 1) as f64; 50 | self.field_scale = 1.0 / (SCALE - factor); 51 | self.penetration_energy_unit *= 10.0_f64.powf(-0.1); 52 | } 53 | fn set_stage_parameters(&mut self, stage_scale: usize) { 54 | let e = self.penetration_energy_unit * (stage_scale as f64); 55 | self.penetration_energy_charged = e; 56 | self.penetration_energy = e; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /misc/splr-all.rs: -------------------------------------------------------------------------------- 1 | use std::{env::args, path::Path}; 2 | /// All solutions solver implementation based on feature 'incremental solver' 3 | /// But the main purpose is to check the correctness of the implementaion of 4 | /// feature 'incremental solver'. 5 | /// 6 | /// To run me: 7 | ///```ignore 8 | /// cargo run --features incremental_solver --example all-solutions -- a.cnf 9 | ///``` 10 | 11 | fn main() { 12 | let cnf_file: String = args().nth(1).expect("takes an arg"); 13 | run(Path::new(&cnf_file)); 14 | } 15 | 16 | #[cfg(not(feature = "incremental_solver"))] 17 | fn run(_cnf_file: &Path) -> Vec> { 18 | panic!( 19 | "Pleane run as: cargo run --features incremental_solver --example all-solutions -- a.cnf" 20 | ); 21 | } 22 | 23 | #[cfg(feature = "incremental_solver")] 24 | fn run(cnf_file: &Path) -> Vec> { 25 | use splr::cnf::*; 26 | use splr::*; 27 | #[cfg(feature = "clause_elimination")] 28 | { 29 | panic?("You can't build incremental_solver with feature clause_elimination.") 30 | } 31 | let mut solutions = Vec::new(); 32 | let name = cnf_file.file_stem().expect("It seems a strange filename"); 33 | let mut cnf = match CNF::load(cnf_file) { 34 | Ok(c) => c, 35 | Err(CNFOperationError::AddingEmptyClause) => { 36 | return Vec::new(); 37 | } 38 | Err(e) => { 39 | panic!("{e:?}"); 40 | } 41 | }; 42 | println!("{cnf}"); 43 | let mut solver = Solver::try_from(cnf_file).expect("panic"); 44 | let mut count = 0; 45 | for res in solver.iter() { 46 | count += 1; 47 | if res.is_empty() { 48 | // Is this possible? Yes, an empty form has the solution which contains no literal. 49 | return vec![vec![]]; 50 | } 51 | let refuter: Vec = res.iter().map(|l| -l).collect::>(); 52 | solutions.push(res); 53 | cnf.add_clause(refuter).expect("An internal error"); 54 | let dump_name = format!("{}-refuting-{count}.cnf", name.to_string_lossy()); 55 | let dump = Path::new(&dump_name); 56 | cnf.save(dump).expect("???"); 57 | println!("s SATISFIABLE({count}) => {dump_name}"); 58 | } 59 | let dump_name = format!("{}-refuting-all{count}.cnf", name.to_string_lossy()); 60 | let dump = Path::new(&dump_name); 61 | cnf.save(dump).expect("???"); 62 | println!("#solution: {count} => {dump_name}"); 63 | solutions 64 | } 65 | -------------------------------------------------------------------------------- /src/solver/validate.rs: -------------------------------------------------------------------------------- 1 | //! Crate `validator` implements a model checker. 2 | use crate::{ 3 | assign::{AssignIF, PropagateIF}, 4 | cdb::ClauseDBIF, 5 | solver::Solver, 6 | types::{Lit, MaybeInconsistent, SolverError}, 7 | }; 8 | 9 | /// API for SAT validator like [`inject_assignment`](`crate::solver::ValidateIF::inject_assignment`), [`validate`](`crate::solver::ValidateIF::validate`) and so on. 10 | pub trait ValidateIF { 11 | /// load a assignment set into solver. 12 | /// 13 | /// # Errors 14 | /// 15 | /// if solver becomes inconsistent. 16 | fn inject_assignment(&mut self, vec: &[i32]) -> MaybeInconsistent; 17 | /// return `true` is the loaded assignment set is satisfiable (a model of a problem). 18 | fn validate(&self) -> Option>; 19 | } 20 | 21 | impl ValidateIF for Solver { 22 | /// inject an assignment set into solver. 23 | /// An assignment set is represented by a list of `i32`. 24 | /// 25 | /// #Example 26 | /// 27 | /// ``` 28 | /// use crate::{splr::config::Config, splr::types::*}; 29 | /// use splr::solver::{Solver, ValidateIF}; 30 | /// 31 | /// let cnf = CNFDescription { 32 | /// num_of_variables: 4, 33 | /// ..CNFDescription::default() 34 | /// }; 35 | /// let mut s = Solver::instantiate(&Config::default(), &cnf); 36 | /// assert_eq!(s.inject_assignment(&[1i32, -2, 3]), Ok(())); 37 | ///``` 38 | /// 39 | fn inject_assignment(&mut self, vec: &[i32]) -> MaybeInconsistent { 40 | if vec.is_empty() { 41 | return Err(SolverError::Inconsistent); 42 | } 43 | for i in vec { 44 | self.asg.assign_at_root_level(Lit::from(*i))?; 45 | } 46 | Ok(()) 47 | } 48 | /// returns None if the given assignment is a model of a problem. 49 | /// Otherwise returns a clause which is not satisfiable under a given assignment. 50 | /// 51 | /// #Example 52 | /// 53 | /// ``` 54 | /// use crate::{splr::config::Config, splr::types::*}; 55 | /// use splr::solver::{Solver, ValidateIF}; 56 | /// 57 | /// let cnf = CNFDescription { 58 | /// num_of_variables: 4, 59 | /// ..CNFDescription::default() 60 | /// }; 61 | /// let mut s = Solver::instantiate(&Config::default(), &cnf); 62 | /// s.inject_assignment(&[1i32, -2, 3]); 63 | /// assert_eq!(s.validate(), None); 64 | ///``` 65 | /// 66 | fn validate(&self) -> Option> { 67 | self.cdb 68 | .validate(&self.asg.assign_ref(), true) 69 | .map(|cid| Vec::::from(&self.cdb[cid])) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::upper_case_acronyms)] 2 | /*! 3 | # A modern CDCL SAT solver in Rust 4 | 5 | Splr is a pure Rustic SAT solver, based on [Glucose 4.1](https://www.labri.fr/perso/lsimon/glucose/). 6 | It adopts various research results on SAT solvers: 7 | 8 | - CDCL, watch literals, and so on from [Minisat](http://minisat.se) and the ancestors 9 | - Glucose-like dynamic blocking/forcing restarts based on [EMAs](https://arxiv.org/abs/1506.08905) 10 | - heuristics adaptation 11 | - pre/in-process simplification based on clause subsumption and variable elimination 12 | - Chronological backtrack and non-chronological backjump 13 | - Learning Rate Based Branching and Reason Side Rewarding 14 | - Rephase 15 | - Search stabilization 16 | - clause vivification 17 | 18 | *Many thanks to SAT researchers.* 19 | 20 | # Examples 21 | 22 | ## Build a solver from a configuration based on a CNF file, then solve it. 23 | 24 | ``` 25 | use splr::*; 26 | use std::path::Path; 27 | 28 | let config = Config::from(Path::new("cnfs/sample.cnf")); 29 | if let Ok(mut s) = Solver::build(&config) { 30 | if let Ok(ans) = s.solve() { 31 | println!("{:?}", ans); 32 | } 33 | } 34 | ``` 35 | 36 | ## On-memory direct conversion from a vec to a solution 37 | 38 | ``` 39 | use splr::*; 40 | 41 | let v: Vec> = vec![vec![1, 2], vec![-1, 3], vec![1, -3], vec![-1, 2]]; 42 | match Certificate::try_from(v).expect("panic!") { 43 | Certificate::UNSAT => 0, 44 | Certificate::SAT(vec) => vec.len(), 45 | }; 46 | ``` 47 | 48 | ## Incremental solver 49 | 50 | Splr provides 'incremental solver mode' if you built it with feature 'incremental_solver'. 51 | This document covers extra functions only if you built it with `cargo doc --features incremental_solver`. 52 | 53 | */ 54 | /// Module `assign` implements Boolean Constraint Propagation and decision var selection. 55 | pub mod assign; 56 | /// Module `cdb` provides [`Clause`](`crate::cdb::Clause`) object and its manager [`ClauseDB`](`crate::cdb::ClauseDB`). 57 | pub mod cdb; 58 | /// Module `cnf` provides basic operations on CNF files 59 | pub mod cnf; 60 | /// Module `config` provides solver's configuration and CLI. 61 | pub mod config; 62 | /// Module `primitive` provides some fundamental data structures. 63 | pub mod primitive; 64 | /// Module `processor` implements a simplifier: clause subsumption and var elimination. 65 | pub mod processor; 66 | /// Module `solver` provides the top-level API as a SAT solver. 67 | pub mod solver; 68 | /// Module `state` is a collection of internal data. 69 | pub mod state; 70 | /// Module `types` provides various building blocks, including some common traits. 71 | pub mod types; 72 | 73 | pub use { 74 | config::Config, 75 | primitive::{ema::*, luby::*}, 76 | solver::{Certificate, SatSolverIF, SolveIF, Solver, ValidateIF}, 77 | types::{PropertyDereference, PropertyReference, SolverError}, 78 | }; 79 | 80 | /// Splr version number. 81 | pub const VERSION: &str = env!("CARGO_PKG_VERSION"); 82 | 83 | #[macro_use] 84 | extern crate bitflags; 85 | -------------------------------------------------------------------------------- /src/primitive/luby.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, num::NonZeroU32}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct LubySeries { 5 | index: usize, 6 | seq: isize, 7 | size: usize, 8 | max_value: usize, 9 | } 10 | 11 | impl Default for LubySeries { 12 | fn default() -> Self { 13 | LubySeries { 14 | index: 0, 15 | seq: 0, 16 | size: 1, 17 | max_value: 1, 18 | } 19 | } 20 | } 21 | 22 | impl fmt::Display for LubySeries { 23 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 24 | write!(f, "Luby[index:{}]", self.index) 25 | } 26 | } 27 | 28 | impl Iterator for LubySeries { 29 | type Item = NonZeroU32; 30 | /// Find the finite subsequence that contains index 'x', and the 31 | /// size of that subsequence as: 1, 1, 2, 1, 1, 2, 4, 1, 1, 2, 1, 1, 2, 4, 8 32 | fn next(&mut self) -> Option { 33 | self.index += 1; 34 | let mut seq = self.seq; 35 | let mut size = self.size; 36 | while size < self.index + 1 { 37 | self.seq = seq; 38 | seq += 1; 39 | self.size = size; 40 | size = 2 * size + 1; 41 | } 42 | let mut index = self.index; 43 | while size - 1 != index { 44 | size = (size - 1) >> 1; 45 | seq -= 1; 46 | index %= size; 47 | } 48 | let val = 2usize.pow(seq as u32); 49 | if self.max_value < val { 50 | self.max_value = val; 51 | } 52 | NonZeroU32::new(val as u32) 53 | } 54 | } 55 | 56 | impl LubySeries { 57 | pub fn next_unchecked(&mut self) -> usize { 58 | self.index += 1; 59 | let mut seq = self.seq; 60 | let mut size = self.size; 61 | while size < self.index + 1 { 62 | self.seq = seq; 63 | seq += 1; 64 | self.size = size; 65 | size = 2 * size + 1; 66 | } 67 | let mut index = self.index; 68 | while size - 1 != index { 69 | size = (size - 1) >> 1; 70 | seq -= 1; 71 | index %= size; 72 | } 73 | let val = 2usize.pow(seq as u32); 74 | if self.max_value < val { 75 | self.max_value = val; 76 | } 77 | val 78 | } 79 | pub fn max_value(&self) -> usize { 80 | self.max_value 81 | } 82 | pub fn reset(&mut self) { 83 | self.index = 0; 84 | self.seq = 0; 85 | self.size = 1; 86 | } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | 93 | #[test] 94 | fn test_luby_series() { 95 | let mut luby = LubySeries::default(); 96 | let v = vec![1, 2, 1, 1, 2, 4, 1, 1, 2, 1, 1, 2, 4, 8]; 97 | let l = v 98 | .iter() 99 | .map(|_| luby.next_unchecked()) 100 | .collect::>(); 101 | assert_eq!(l, v); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/assign/learning_rate.rs: -------------------------------------------------------------------------------- 1 | /// Var Rewarding based on Learning Rate Rewarding and Reason Side Rewarding 2 | use { 3 | super::{stack::AssignStack, var::Var}, 4 | crate::types::*, 5 | }; 6 | 7 | impl ActivityIF for AssignStack { 8 | #[inline] 9 | fn activity(&self, vi: VarId) -> f64 { 10 | self.var[vi].reward 11 | } 12 | // fn activity_slow(&self, vi: VarId) -> f64 { 13 | // self.var[vi].reward_ema.get() 14 | // } 15 | fn set_activity(&mut self, vi: VarId, val: f64) { 16 | self.var[vi].reward = val; 17 | } 18 | fn reward_at_analysis(&mut self, vi: VarId) { 19 | self.var[vi].turn_on(FlagVar::USED); 20 | } 21 | #[inline] 22 | fn reward_at_assign(&mut self, _vi: VarId) {} 23 | #[inline] 24 | fn reward_at_propagation(&mut self, _vi: VarId) {} 25 | #[inline] 26 | fn reward_at_unassign(&mut self, vi: VarId) { 27 | self.var[vi].update_activity(self.activity_decay, self.activity_anti_decay); 28 | } 29 | fn update_activity_decay(&mut self, scaling: f64) { 30 | self.activity_decay = scaling; 31 | self.activity_anti_decay = 1.0 - scaling; 32 | } 33 | // Note: `update_rewards` should be called before `cancel_until` 34 | #[inline] 35 | fn update_activity_tick(&mut self) { 36 | self.tick += 1; 37 | } 38 | } 39 | 40 | impl AssignStack { 41 | pub fn rescale_activity(&mut self, scaling: f64) { 42 | for v in self.var.iter_mut().skip(1) { 43 | v.reward *= scaling; 44 | } 45 | } 46 | // pub fn set_activity_trend(&mut self) -> f64 { 47 | // let mut nv = 0; 48 | // let mut inc = 0; 49 | // let mut activity_sum: f64 = 0.0; 50 | // // let mut dec = 1; 51 | // for (vi, v) in self.var.iter_mut().enumerate().skip(1) { 52 | // if v.is(FlagVar::ELIMINATED) || self.level[vi] == self.root_level { 53 | // continue; 54 | // } 55 | // nv += 1; 56 | // activity_sum += v.reward; 57 | // let trend = v.reward_ema.trend(); 58 | // if 1.0 < trend { 59 | // inc += 1; 60 | // } 61 | // } 62 | // self.activity_averaged = activity_sum / nv as f64; 63 | // self.cwss = inc as f64 / nv as f64; 64 | // // println!("inc rate:{:>6.4}", self.cwss); 65 | // self.cwss 66 | // } 67 | } 68 | 69 | impl Var { 70 | fn update_activity(&mut self, decay: f64, reward: f64) -> f64 { 71 | // Note: why the condition can be broken. 72 | // 73 | // 1. asg.ordinal += 1; 74 | // 1. handle_conflict -> cancel_until -> reward_at_unassign 75 | // 1. assign_by_implication -> v.timestamp = asg.ordinal 76 | // 1. restart 77 | // 1. cancel_until -> reward_at_unassign -> assertion failed 78 | // 79 | self.reward *= decay; 80 | if self.is(FlagVar::USED) { 81 | self.reward += reward; 82 | self.turn_off(FlagVar::USED); 83 | } 84 | // self.reward_ema.update(self.reward); 85 | self.reward 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "splr" 3 | version = "0.17.4" 4 | authors = ["Narazaki Shuji "] 5 | description = "A modern CDCL SAT solver in Rust" 6 | edition = "2021" 7 | license = "MPL-2.0" 8 | readme = "README.md" 9 | repository = "https://github.com/shnarazk/splr" 10 | homepage = "https://github.com/shnarazk/splr" 11 | keywords = ["SAT", "SAT-solver", "logic", "satisfiability"] 12 | categories = ["science", "mathematics"] 13 | default-run = "splr" 14 | rust-version = "1.84" 15 | 16 | [dependencies] 17 | bitflags = "^2.8" 18 | instant = { version = "0.1", features = ["wasm-bindgen"], optional = true } 19 | 20 | [features] 21 | default = [ 22 | ### Essential 23 | # "incremental_solver", 24 | "unsafe_access", 25 | 26 | ### Heuristics 27 | # "bi_clause_completion", 28 | # "clause_rewarding", 29 | "dynamic_restart_threshold", 30 | "LRB_rewarding", 31 | "reason_side_rewarding", 32 | "rephase", 33 | "reward_annealing", 34 | # "stochastic_local_search", 35 | # "suppress_reason_chain", 36 | "two_mode_reduction", 37 | "trail_saving", 38 | 39 | ### Logic formula processor 40 | "clause_elimination", 41 | "clause_vivification", 42 | 43 | ### For DEBUG 44 | # "boundary_check", 45 | # "maintain_watch_cache", 46 | 47 | ### platform dependency 48 | # "platform_wasm" 49 | ] 50 | assign_rate = [] # for debug and study 51 | best_phases_tracking = [] # save the best-so-far assignment, used by 'rephase' 52 | bi_clause_completion = [] # this will increase memory pressure 53 | boundary_check = [] # for debug 54 | chrono_BT = [] # NOT WORK 55 | clause_elimination = [] # pre(in)-processor setting 56 | clause_rewarding = [] # clauses have activities w/ decay rate 57 | clause_vivification = [] # pre(in)-processor setting 58 | debug_propagation = [] # for debug 59 | dynamic_restart_threshold = [] # control restart spans like Glucose 60 | EMA_calibration = [] # each exponential moving average has a calbration value 61 | EVSIDS = [] # Eponential Variable State Independent Decaying Sum 62 | incremental_solver = [] # for all solution SAT sover, you can't use this with clause_elimination 63 | just_used = [] # Var and clause have 'just_used' flags 64 | LRB_rewarding = [] # Vearning Rate Based rewarding, a new var activity criteria 65 | maintain_watch_cache = [] # for DEBUG 66 | no_IO = [] # to embed Splr into non-std environments 67 | platform_wasm = [ 68 | "instant" # use instant::Duration instead of std::time::Duration 69 | ] 70 | reason_side_rewarding = [] # an idea used in Learning-rate based rewarding 71 | rephase = [ # search around the best-so-far candidate repeatedly 72 | "best_phases_tracking", 73 | ] 74 | reward_annealing = [] # use bigger and smaller decay rates cycliclly 75 | stochastic_local_search = [ # since 0.17 76 | # "reward_annealing", 77 | "rephase", 78 | ] 79 | support_user_assumption = [] # NOT WORK (defined in Glucose) 80 | suppress_reason_chain = [] # make direct links between a dicision var and its implications 81 | trace_analysis = [] # for debug 82 | trace_elimination = [] # for debug 83 | trace_equivalency = [] # for debug 84 | trail_saving = [] # reduce propagation cost by reusing propagation chain 85 | two_mode_reduction = [] # exploration mode and exploitation mode since 0.17 86 | unsafe_access = [] # access elements of vectors without boundary checking 87 | 88 | [profile.release] 89 | lto = "fat" 90 | codegen-units = 1 91 | opt-level = 3 92 | panic = "abort" 93 | strip = true 94 | 95 | [[example]] 96 | name = "all-solutions" 97 | path = "misc/splr-all.rs" 98 | -------------------------------------------------------------------------------- /tests/isat.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "incremental_solver")] 2 | /// WARNING: this test requires feature "incremental_solver". 3 | ///```ignore 4 | /// cargo test --test isat --features incremental_solver --release -- --nocapture --test-threads=1 5 | ///``` 6 | use splr::*; 7 | use std::{env::args, path::Path}; 8 | 9 | fn main() { 10 | let cnf = args().nth(1).expect("takes an arg"); 11 | let assumptions = Vec::new(); 12 | let ns1 = run(Path::new(&cnf), &assumptions, false); 13 | let ns2 = run(Path::new(&cnf), &assumptions, true); 14 | println!("#solution: {} w/o elim; {} w/ elim", ns1, ns2); 15 | } 16 | 17 | #[test] 18 | fn all_solutions_of_uf8() { 19 | drive(Path::new("cnfs/uf8.cnf"), vec![4, 5, -6, 7, 8]); 20 | } 21 | 22 | #[test] 23 | fn all_solutions_of_uf20() { 24 | drive(Path::new("cnfs/uf20-01.cnf"), vec![-4, 5, 6, 7, 8]); 25 | } 26 | 27 | #[allow(dead_code)] 28 | /// cargo test --test isat --features incremental_solver --release 29 | fn all_solutions_of_uf100() { 30 | drive(Path::new("cnfs/uf100-010.cnf"), vec![]); 31 | } 32 | 33 | fn drive(cnf: &Path, mother: Vec) { 34 | for i in 0..=mother.len() { 35 | let assumptions = &mother[0..i]; 36 | let ns1 = run(cnf, assumptions, false); 37 | let ns2 = run(cnf, assumptions, true); 38 | println!("#solution: {} w/o elim; {} w/ elim", ns1, ns2); 39 | debug_assert_eq!(ns1, ns2); 40 | } 41 | } 42 | 43 | #[cfg(not(feature = "no_IO"))] 44 | fn run(cnf: &Path, assigns: &[i32], switch: bool) -> usize { 45 | println!("-------------------- {:?}, {}", assigns, switch); 46 | let mut solver = Solver::try_from(cnf).expect("panic"); 47 | // solver.state.config.enable_eliminator = switch; 48 | for n in assigns.iter() { 49 | solver.add_assignment(*n).expect("no"); 50 | } 51 | let mut count = 0; 52 | loop { 53 | match solver.solve() { 54 | Ok(Certificate::SAT(mut ans)) => { 55 | count += 1; 56 | println!("s SATISFIABLE({}): {:?}", count, ans); 57 | 58 | // 59 | // Run an external validator 60 | // 61 | { 62 | let mut validator = Solver::try_from(cnf).expect("panic"); 63 | validator 64 | .inject_assignment(&ans) 65 | .expect("It's completely broken!"); 66 | if let Some(v) = validator.validate() { 67 | panic!("It's an invalid assignment against clause {:?}.", v); 68 | } 69 | } 70 | 71 | for i in ans.iter_mut() { 72 | *i *= -1; 73 | } 74 | // Or you can build a new clause which literals are flipped. 75 | // let ans: Vec = ans.iter().map(|i| -i).collect::>(); 76 | assert!(1 < ans.len()); 77 | match solver.add_clause(ans.clone()) { 78 | Err(SolverError::Inconsistent) => { 79 | println!("c no (more) answer due to level zero conflict"); 80 | break; 81 | } 82 | Err(SolverError::EmptyClause) => { 83 | println!("c no (more) answer due to an empty clause"); 84 | break; 85 | } 86 | Err(e) => { 87 | println!("s UNKNOWN; {:?} by {:?}", e, ans); 88 | break; 89 | } 90 | Ok(_) => solver.reset(), 91 | } 92 | } 93 | Ok(Certificate::UNSAT) => { 94 | println!("s UNSATISFIABLE"); 95 | break; 96 | } 97 | Err(e) => { 98 | println!("s UNKNOWN; {}", e); 99 | break; 100 | } 101 | } 102 | } 103 | count 104 | } 105 | -------------------------------------------------------------------------------- /src/cdb/watch_cache.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::ClauseId, 3 | crate::types::*, 4 | std::{ 5 | collections::HashMap, 6 | ops::{Index, IndexMut}, 7 | }, 8 | }; 9 | 10 | pub type WatchCacheList = Vec<(ClauseId, Lit)>; 11 | pub type WatchCache = WatchCacheList; 12 | 13 | pub trait WatchCacheIF { 14 | fn get_watch(&self, cid: &ClauseId) -> Option<&Lit>; 15 | fn remove_watch(&mut self, cid: &ClauseId); 16 | fn insert_watch(&mut self, cid: ClauseId, l: Lit); 17 | fn append_watch(&mut self, appendant: Self); 18 | fn update_watch(&mut self, cid: ClauseId, l: Lit); 19 | } 20 | 21 | impl WatchCacheIF for WatchCacheList { 22 | fn get_watch(&self, cid: &ClauseId) -> Option<&Lit> { 23 | self.iter().find_map(|e| (e.0 == *cid).then_some(&e.1)) 24 | } 25 | fn remove_watch(&mut self, cid: &ClauseId) { 26 | if let Some(i) = self.iter().position(|e| e.0 == *cid) { 27 | self.swap_remove(i); 28 | } 29 | } 30 | fn insert_watch(&mut self, cid: ClauseId, l: Lit) { 31 | debug_assert!(self.iter().all(|e| e.0 != cid)); 32 | self.push((cid, l)); 33 | } 34 | fn append_watch(&mut self, mut appendant: Self) { 35 | self.append(&mut appendant); 36 | } 37 | fn update_watch(&mut self, cid: ClauseId, l: Lit) { 38 | for e in self.iter_mut() { 39 | if e.0 == cid { 40 | e.1 = l; 41 | break; 42 | } 43 | } 44 | } 45 | } 46 | 47 | impl Index for Vec> { 48 | type Output = HashMap; 49 | #[inline] 50 | fn index(&self, l: Lit) -> &Self::Output { 51 | #[cfg(feature = "unsafe_access")] 52 | unsafe { 53 | self.get_unchecked(usize::from(l)) 54 | } 55 | #[cfg(not(feature = "unsafe_access"))] 56 | &self[usize::from(l)] 57 | } 58 | } 59 | 60 | impl IndexMut for Vec> { 61 | #[inline] 62 | fn index_mut(&mut self, l: Lit) -> &mut Self::Output { 63 | #[cfg(feature = "unsafe_access")] 64 | unsafe { 65 | self.get_unchecked_mut(usize::from(l)) 66 | } 67 | #[cfg(not(feature = "unsafe_access"))] 68 | &mut self[usize::from(l)] 69 | } 70 | } 71 | 72 | impl Index for Vec { 73 | type Output = WatchCache; 74 | #[inline] 75 | fn index(&self, l: Lit) -> &Self::Output { 76 | #[cfg(feature = "unsafe_access")] 77 | unsafe { 78 | self.get_unchecked(usize::from(l)) 79 | } 80 | #[cfg(not(feature = "unsafe_access"))] 81 | &self[usize::from(l)] 82 | } 83 | } 84 | 85 | impl IndexMut for Vec { 86 | #[inline] 87 | fn index_mut(&mut self, l: Lit) -> &mut Self::Output { 88 | #[cfg(feature = "unsafe_access")] 89 | unsafe { 90 | self.get_unchecked_mut(usize::from(l)) 91 | } 92 | #[cfg(not(feature = "unsafe_access"))] 93 | &mut self[usize::from(l)] 94 | } 95 | } 96 | 97 | pub type WatchCacheProxy = usize; 98 | 99 | pub struct WatchCacheIterator { 100 | pub index: usize, 101 | end_at: usize, 102 | // checksum: usize, 103 | } 104 | 105 | impl Iterator for WatchCacheIterator { 106 | type Item = WatchCacheProxy; 107 | fn next(&mut self) -> Option { 108 | // assert!(self.checksum == self.end_at - self.index); 109 | (self.index < self.end_at).then_some({ 110 | // assert!(0 < self.checksum); 111 | // self.checksum -= 1; 112 | self.index 113 | }) 114 | } 115 | } 116 | 117 | impl WatchCacheIterator { 118 | pub fn new(len: usize) -> Self { 119 | WatchCacheIterator { 120 | index: 0, 121 | end_at: len, 122 | // checksum: len, 123 | } 124 | } 125 | pub fn restore_entry(&mut self) { 126 | self.index += 1; 127 | } 128 | pub fn detach_entry(&mut self) { 129 | // assert!(self.end_at != 0); 130 | self.end_at -= 1; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/cdb/unsat_certificate.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | #[cfg(not(feature = "no_IO"))] 3 | use std::{ 4 | fs::File, 5 | io::{BufWriter, Write}, 6 | ops::Neg, 7 | path::PathBuf, 8 | }; 9 | 10 | #[cfg(feature = "no_IO")] 11 | #[derive(Debug, Default)] 12 | pub struct CertificationStore(); 13 | 14 | #[cfg(feature = "no_IO")] 15 | impl Instantiate for CertificationStore { 16 | fn instantiate(_config: &Config, _cnf: &CNFDescription) -> Self { 17 | CertificationStore() 18 | } 19 | } 20 | 21 | #[cfg(feature = "no_IO")] 22 | impl CertificationStore { 23 | pub fn is_active(&self) -> bool { 24 | false 25 | } 26 | pub fn add_clause(&mut self, _clause: &[Lit]) {} 27 | pub fn delete_clause(&mut self, _vec: &[Lit]) {} 28 | pub fn close(&mut self) {} 29 | } 30 | 31 | #[cfg(not(feature = "no_IO"))] 32 | const DUMP_INTERVAL: usize = 4096 * 16; 33 | 34 | #[cfg(not(feature = "no_IO"))] 35 | /// Struct for saving UNSAT certification 36 | #[derive(Debug, Default)] 37 | pub struct CertificationStore { 38 | /// clause history to make certification 39 | queue: Vec, 40 | target: Option, 41 | buffer: Option>, 42 | } 43 | 44 | impl Clone for CertificationStore { 45 | fn clone(&self) -> Self { 46 | Self::default() 47 | } 48 | } 49 | 50 | #[cfg(not(feature = "no_IO"))] 51 | impl Instantiate for CertificationStore { 52 | fn instantiate(config: &Config, _cnf: &CNFDescription) -> Self { 53 | #[cfg(not(feature = "no_IO"))] 54 | if config.use_certification { 55 | let cert: PathBuf = config.io_odir.join(&config.io_pfile); 56 | if let Ok(out) = File::create(&cert) { 57 | return CertificationStore { 58 | queue: Vec::with_capacity(DUMP_INTERVAL + 1024), 59 | buffer: Some(BufWriter::new(out)), 60 | target: Some(cert), 61 | }; 62 | } 63 | } 64 | CertificationStore::default() 65 | } 66 | } 67 | 68 | #[cfg(not(feature = "no_IO"))] 69 | impl CertificationStore { 70 | pub fn is_active(&self) -> bool { 71 | self.buffer.is_some() 72 | } 73 | pub fn add_clause(&mut self, clause: &[Lit]) { 74 | self.queue.push(clause.len() as i32); 75 | for l in clause.iter() { 76 | self.queue.push(i32::from(*l)); 77 | } 78 | if DUMP_INTERVAL < self.queue.len() { 79 | self.dump_to_file(); 80 | } 81 | } 82 | pub fn delete_clause(&mut self, clause: &[Lit]) { 83 | self.queue.push((clause.len() as i32).neg()); 84 | for l in clause.iter() { 85 | self.queue.push(i32::from(*l)); 86 | } 87 | if DUMP_INTERVAL < self.queue.len() { 88 | self.dump_to_file(); 89 | } 90 | } 91 | pub fn close(&mut self) { 92 | if self.buffer.is_none() { 93 | return; 94 | } 95 | self.dump_to_file(); 96 | if let Some(ref mut buf) = self.buffer { 97 | let _ = buf.write_all(b"0\n"); 98 | self.buffer = None; 99 | self.target = None; 100 | } 101 | } 102 | } 103 | 104 | #[cfg(not(feature = "no_IO"))] 105 | impl CertificationStore { 106 | fn dump_to_file(&mut self) { 107 | let mut index = 0; 108 | if let Some(ref mut buf) = self.buffer { 109 | while index < self.queue.len() { 110 | let l = self.queue[index]; 111 | if l < 0 && buf.write_all(b"d ").is_err() { 112 | self.buffer = None; 113 | break; 114 | } 115 | for _ in 0..(l.unsigned_abs() as usize) { 116 | index += 1; 117 | if buf 118 | .write_all(format!("{} ", self.queue[index]).as_bytes()) 119 | .is_err() 120 | { 121 | self.buffer = None; 122 | return; 123 | } 124 | } 125 | if buf.write_all(b"0\n").is_err() { 126 | self.buffer = None; 127 | break; 128 | } 129 | index += 1; 130 | } 131 | } 132 | self.queue.clear(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/processor/subsume.rs: -------------------------------------------------------------------------------- 1 | /// Module `eliminator` implements clause subsumption and var elimination. 2 | use { 3 | super::{EliminateIF, Eliminator}, 4 | crate::{assign::AssignIF, cdb::ClauseDBIF, types::*}, 5 | }; 6 | 7 | #[derive(Clone, Eq, Debug, Ord, PartialEq, PartialOrd)] 8 | enum Subsumable { 9 | None, 10 | By(Lit), 11 | Success, 12 | } 13 | 14 | impl Eliminator { 15 | pub fn try_subsume( 16 | &mut self, 17 | asg: &mut impl AssignIF, 18 | cdb: &mut impl ClauseDBIF, 19 | cid: ClauseId, 20 | did: ClauseId, 21 | ) -> MaybeInconsistent { 22 | match have_subsuming_lit(cdb, cid, did) { 23 | Subsumable::Success => { 24 | #[cfg(feature = "trace_elimination")] 25 | println!( 26 | "BackSubsC => {} {} subsumed completely by {} {:#}", 27 | did, cdb[did], cid, cdb[cid], 28 | ); 29 | debug_assert!(!cdb[did].is_dead()); 30 | if !cdb[did].is(FlagClause::LEARNT) { 31 | cdb[cid].turn_off(FlagClause::LEARNT); 32 | } 33 | self.remove_cid_occur(asg, did, &mut cdb[did]); 34 | cdb.remove_clause(did); 35 | self.num_subsumed += 1; 36 | } 37 | // To avoid making a big clause, we have to add a condition for combining them. 38 | Subsumable::By(l) => { 39 | debug_assert!(cid.is_lifted_lit()); 40 | #[cfg(feature = "trace_elimination")] 41 | println!("BackSubC subsumes {} from {} and {}", l, cid, did); 42 | strengthen_clause(asg, cdb, self, did, !l)?; 43 | self.enqueue_var(asg, l.vi(), true); 44 | } 45 | Subsumable::None => (), 46 | } 47 | Ok(()) 48 | } 49 | } 50 | 51 | /// returns a literal if these clauses can be merged by the literal. 52 | fn have_subsuming_lit(cdb: &mut impl ClauseDBIF, cid: ClauseId, other: ClauseId) -> Subsumable { 53 | debug_assert!(!other.is_lifted_lit()); 54 | if cid.is_lifted_lit() { 55 | let l = Lit::from(cid); 56 | let oh = &cdb[other]; 57 | for lo in oh.iter() { 58 | if l == !*lo { 59 | return Subsumable::By(l); 60 | } 61 | } 62 | return Subsumable::None; 63 | } 64 | // let mut ret: Subsumable = Subsumable::Success; 65 | let ch = &cdb[cid]; 66 | debug_assert!(1 < ch.len()); 67 | let ob = &cdb[other]; 68 | debug_assert!(1 < ob.len()); 69 | debug_assert!(ob.contains(ob[0])); 70 | debug_assert!(ob.contains(ob[1])); 71 | 'next: for l in ch.iter() { 72 | for lo in ob.iter() { 73 | if *l == *lo { 74 | continue 'next; 75 | // } else if ret == Subsumable::Success && *l == !*lo { 76 | // ret = Subsumable::By(*l); 77 | // continue 'next; 78 | } 79 | } 80 | return Subsumable::None; 81 | } 82 | Subsumable::Success 83 | } 84 | 85 | /// removes `l` from clause `cid` 86 | /// - calls `enqueue_clause` 87 | /// - calls `enqueue_var` 88 | fn strengthen_clause( 89 | asg: &mut impl AssignIF, 90 | cdb: &mut impl ClauseDBIF, 91 | elim: &mut Eliminator, 92 | cid: ClauseId, 93 | l: Lit, 94 | ) -> MaybeInconsistent { 95 | debug_assert!(!cdb[cid].is_dead()); 96 | debug_assert!(1 < cdb[cid].len()); 97 | match cdb.transform_by_elimination(cid, l) { 98 | RefClause::Clause(_ci) => { 99 | #[cfg(feature = "trace_elimination")] 100 | println!("cid {} drops literal {}", cid, l); 101 | 102 | elim.enqueue_clause(cid, &mut cdb[cid]); 103 | elim.remove_lit_occur(asg, l, cid); 104 | Ok(()) 105 | } 106 | RefClause::RegisteredClause(_) => { 107 | elim.remove_cid_occur(asg, cid, &mut cdb[cid]); 108 | cdb.remove_clause(cid); 109 | Ok(()) 110 | } 111 | RefClause::UnitClause(l0) => { 112 | cdb.certificate_add_assertion(l0); 113 | elim.remove_cid_occur(asg, cid, &mut cdb[cid]); 114 | cdb.remove_clause(cid); 115 | match asg.assigned(l0) { 116 | None => asg.assign_at_root_level(l0), 117 | Some(true) => Ok(()), 118 | Some(false) => Err(SolverError::RootLevelConflict((l0, asg.reason(l0.vi())))), 119 | } 120 | } 121 | RefClause::Dead | RefClause::EmptyClause => unreachable!("strengthen_clause"), 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/cdb/sls.rs: -------------------------------------------------------------------------------- 1 | /// Implementation of Stochastic Local Search 2 | use { 3 | crate::{assign::AssignIF, types::*}, 4 | std::collections::HashMap, 5 | }; 6 | 7 | pub trait StochasticLocalSearchIF { 8 | /// returns the decision level of the given assignment and the one of the final assignment. 9 | /// Note: the lower level a set of clauses make a conflict at, 10 | /// the higher learning rate a solver can keep and the better learnt clauses we will have. 11 | /// This would be a better criteria that can be used in CDCL solvers. 12 | fn stochastic_local_search( 13 | &mut self, 14 | asg: &impl AssignIF, 15 | start: &mut HashMap, 16 | limit: usize, 17 | ) -> (usize, usize); 18 | } 19 | 20 | impl StochasticLocalSearchIF for ClauseDB { 21 | fn stochastic_local_search( 22 | &mut self, 23 | _asg: &impl AssignIF, 24 | assignment: &mut HashMap, 25 | limit: usize, 26 | ) -> (usize, usize) { 27 | let mut returns: (usize, usize) = (0, 0); 28 | let mut last_flip = self.num_clause; 29 | let mut seed = 721_109; 30 | for step in 1..=limit { 31 | let mut unsat_clauses = 0; 32 | // let mut level: DecisionLevel = 0; 33 | // CONSIDER: counting only given (permanent) clauses. 34 | let mut flip_target: HashMap = HashMap::new(); 35 | let mut target_clause: Option<&Clause> = None; 36 | for c in self.clause.iter().skip(1).filter(|c| !c.is_dead()) { 37 | // let mut cls_lvl: DecisionLevel = 0; 38 | if c.is_falsified(assignment, &mut flip_target) { 39 | unsat_clauses += 1; 40 | // for l in c.lits.iter() { 41 | // cls_lvl = cls_lvl.max(asg.level(l.vi())); 42 | // } 43 | // level = level.max(cls_lvl); 44 | if target_clause.is_none() || unsat_clauses == step { 45 | target_clause = Some(c); 46 | for l in c.lits.iter() { 47 | flip_target.entry(l.vi()).or_insert(0); 48 | } 49 | } 50 | } 51 | } 52 | if step == 1 { 53 | returns.0 = unsat_clauses; 54 | // returns.0 = level as usize; 55 | } 56 | returns.1 = unsat_clauses; 57 | // returns.1 = level as usize; 58 | if unsat_clauses == 0 || step == limit { 59 | break; 60 | } 61 | seed = ((((!seed & 0x0000_0000_ffff_ffff) * 1_304_003) % 2_003_819) 62 | ^ ((!last_flip & 0x0000_0000_ffff_ffff) * seed)) 63 | % 3_754_873; 64 | if let Some(c) = target_clause { 65 | let beta: f64 = 3.2 - 2.1 / (1.0 + unsat_clauses as f64).log(2.0); 66 | // let beta: f64 = if unsat_clauses <= 3 { 1.0 } else { 3.0 }; 67 | let factor = |vi| beta.powf(-(*flip_target.get(vi).unwrap() as f64)); 68 | let vars = c.lits.iter().map(|l| l.vi()).collect::>(); 69 | let index = ((seed % 100) as f64 / 100.0) * vars.iter().map(factor).sum::(); 70 | let mut sum: f64 = 0.0; 71 | for vi in vars.iter() { 72 | sum += factor(vi); 73 | if index <= sum { 74 | assignment.entry(*vi).and_modify(|e| *e = !*e); 75 | last_flip = *vi; 76 | break; 77 | } 78 | } 79 | } else { 80 | break; 81 | } 82 | } 83 | returns 84 | } 85 | } 86 | 87 | impl Clause { 88 | fn is_falsified( 89 | &self, 90 | assignment: &HashMap, 91 | flip_target: &mut HashMap, 92 | ) -> bool { 93 | let mut num_sat = 0; 94 | let mut sat_vi = 0; 95 | for l in self.iter() { 96 | let vi = l.vi(); 97 | match assignment.get(&vi) { 98 | Some(b) if *b == l.as_bool() => { 99 | if num_sat == 1 { 100 | return false; 101 | } 102 | num_sat += 1; 103 | sat_vi = vi; 104 | } 105 | None => unreachable!(), 106 | _ => (), 107 | } 108 | } 109 | if num_sat == 0 { 110 | true 111 | } else { 112 | *flip_target.entry(sat_vi).or_insert(0) += 1; 113 | false 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "bitflags" 7 | version = "2.8.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 10 | 11 | [[package]] 12 | name = "bumpalo" 13 | version = "3.16.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 16 | 17 | [[package]] 18 | name = "cfg-if" 19 | version = "1.0.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 22 | 23 | [[package]] 24 | name = "instant" 25 | version = "0.1.13" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 28 | dependencies = [ 29 | "cfg-if", 30 | "js-sys", 31 | "wasm-bindgen", 32 | "web-sys", 33 | ] 34 | 35 | [[package]] 36 | name = "js-sys" 37 | version = "0.3.77" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 40 | dependencies = [ 41 | "once_cell", 42 | "wasm-bindgen", 43 | ] 44 | 45 | [[package]] 46 | name = "log" 47 | version = "0.4.25" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 50 | 51 | [[package]] 52 | name = "once_cell" 53 | version = "1.20.2" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 56 | 57 | [[package]] 58 | name = "proc-macro2" 59 | version = "1.0.93" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 62 | dependencies = [ 63 | "unicode-ident", 64 | ] 65 | 66 | [[package]] 67 | name = "quote" 68 | version = "1.0.38" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 71 | dependencies = [ 72 | "proc-macro2", 73 | ] 74 | 75 | [[package]] 76 | name = "rustversion" 77 | version = "1.0.19" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 80 | 81 | [[package]] 82 | name = "splr" 83 | version = "0.17.4" 84 | dependencies = [ 85 | "bitflags", 86 | "instant", 87 | ] 88 | 89 | [[package]] 90 | name = "syn" 91 | version = "2.0.96" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 94 | dependencies = [ 95 | "proc-macro2", 96 | "quote", 97 | "unicode-ident", 98 | ] 99 | 100 | [[package]] 101 | name = "unicode-ident" 102 | version = "1.0.16" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" 105 | 106 | [[package]] 107 | name = "wasm-bindgen" 108 | version = "0.2.100" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 111 | dependencies = [ 112 | "cfg-if", 113 | "once_cell", 114 | "rustversion", 115 | "wasm-bindgen-macro", 116 | ] 117 | 118 | [[package]] 119 | name = "wasm-bindgen-backend" 120 | version = "0.2.100" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 123 | dependencies = [ 124 | "bumpalo", 125 | "log", 126 | "proc-macro2", 127 | "quote", 128 | "syn", 129 | "wasm-bindgen-shared", 130 | ] 131 | 132 | [[package]] 133 | name = "wasm-bindgen-macro" 134 | version = "0.2.100" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 137 | dependencies = [ 138 | "quote", 139 | "wasm-bindgen-macro-support", 140 | ] 141 | 142 | [[package]] 143 | name = "wasm-bindgen-macro-support" 144 | version = "0.2.100" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 147 | dependencies = [ 148 | "proc-macro2", 149 | "quote", 150 | "syn", 151 | "wasm-bindgen-backend", 152 | "wasm-bindgen-shared", 153 | ] 154 | 155 | [[package]] 156 | name = "wasm-bindgen-shared" 157 | version = "0.2.100" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 160 | dependencies = [ 161 | "unicode-ident", 162 | ] 163 | 164 | [[package]] 165 | name = "web-sys" 166 | version = "0.3.77" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 169 | dependencies = [ 170 | "js-sys", 171 | "wasm-bindgen", 172 | ] 173 | -------------------------------------------------------------------------------- /src/cdb/binary.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::ClauseId, 3 | crate::types::*, 4 | std::{ 5 | collections::HashMap, 6 | ops::{Index, IndexMut}, 7 | }, 8 | }; 9 | 10 | /// storage of binary links 11 | pub type BinaryLinkList = Vec<(Lit, ClauseId)>; 12 | 13 | impl Index for Vec { 14 | type Output = BinaryLinkList; 15 | #[inline] 16 | fn index(&self, l: Lit) -> &Self::Output { 17 | #[cfg(feature = "unsafe_access")] 18 | unsafe { 19 | self.get_unchecked(usize::from(l)) 20 | } 21 | #[cfg(not(feature = "unsafe_access"))] 22 | &self[usize::from(l)] 23 | } 24 | } 25 | 26 | impl IndexMut for Vec { 27 | #[inline] 28 | fn index_mut(&mut self, l: Lit) -> &mut Self::Output { 29 | #[cfg(feature = "unsafe_access")] 30 | unsafe { 31 | self.get_unchecked_mut(usize::from(l)) 32 | } 33 | #[cfg(not(feature = "unsafe_access"))] 34 | &mut self[usize::from(l)] 35 | } 36 | } 37 | 38 | /// storage with mapper to `ClauseId` of binary links 39 | #[derive(Clone, Debug, Default)] 40 | pub struct BinaryLinkDB { 41 | hash: HashMap<(Lit, Lit), ClauseId>, 42 | list: Vec, 43 | } 44 | 45 | impl Instantiate for BinaryLinkDB { 46 | fn instantiate(_conf: &Config, cnf: &CNFDescription) -> Self { 47 | let num_lit = 2 * (cnf.num_of_variables + 1); 48 | BinaryLinkDB { 49 | hash: HashMap::new(), 50 | list: vec![Vec::new(); num_lit], 51 | } 52 | } 53 | fn handle(&mut self, _e: SolverEvent) {} 54 | } 55 | 56 | pub trait BinaryLinkIF { 57 | /// add a mapping from a pair of Lit to a `ClauseId` 58 | fn add(&mut self, lit0: Lit, lit1: Lit, cid: ClauseId); 59 | /// remove a pair of `Lit`s 60 | fn remove(&mut self, lit0: Lit, lit1: Lit) -> MaybeInconsistent; 61 | /// return 'ClauseId` linked from a pair of `Lit`s 62 | fn search(&self, lit0: Lit, lit1: Lit) -> Option<&ClauseId>; 63 | /// return the all links that include `Lit`. 64 | /// Note this is not a `watch_list`. The other literal has an opposite phase. 65 | fn connect_with(&self, lit: Lit) -> &BinaryLinkList; 66 | /// add new var 67 | fn add_new_var(&mut self); 68 | // /// sort links based on var activities 69 | // fn reorder(&mut self, asg: &impl AssignIF); 70 | } 71 | 72 | impl BinaryLinkIF for BinaryLinkDB { 73 | fn add(&mut self, lit0: Lit, lit1: Lit, cid: ClauseId) { 74 | let l0 = lit0.min(lit1); 75 | let l1 = lit0.max(lit1); 76 | self.hash.insert((l0, l1), cid); 77 | self.list[lit0].push((lit1, cid)); 78 | self.list[lit1].push((lit0, cid)); 79 | } 80 | fn remove(&mut self, lit0: Lit, lit1: Lit) -> MaybeInconsistent { 81 | let l0 = lit0.min(lit1); 82 | let l1 = lit0.max(lit1); 83 | self.hash.remove(&(l0, l1)); 84 | self.list[lit0].delete_unstable(|p| p.0 == lit1); 85 | self.list[lit1].delete_unstable(|p| p.0 == lit0); 86 | Ok(()) 87 | } 88 | fn search(&self, lit0: Lit, lit1: Lit) -> Option<&ClauseId> { 89 | let l0 = lit0.min(lit1); 90 | let l1 = lit0.max(lit1); 91 | self.hash.get(&(l0, l1)) 92 | } 93 | fn connect_with(&self, lit: Lit) -> &BinaryLinkList { 94 | &self.list[lit] 95 | } 96 | fn add_new_var(&mut self) { 97 | for _ in 0..2 { 98 | self.list.push(Vec::new()); 99 | } 100 | } 101 | /* 102 | fn reorder(&mut self, asg: &impl AssignIF) { 103 | let nv = self.list.len() / 2; 104 | let thr: f64 = (1usize..nv).map(|i| asg.activity(i)).sum::() 105 | / (1usize..nv) 106 | .filter(|i| { 107 | !asg.var(*i).is(FlagVar::ELIMINATED) 108 | && asg.reason(*i) != AssignReason::Decision(0) 109 | }) 110 | .count() as f64; 111 | 'next_lit: for (l, vec) in self.list.iter_mut().enumerate().skip(2) { 112 | if asg.var(Lit::from(l).vi()).is(FlagVar::ELIMINATED) { 113 | continue 'next_lit; 114 | } 115 | if 0.5 * thr <= asg.activity(Lit::from(l).vi()) { 116 | vec.sort_by_cached_key(|p| 117 | (asg.activity(p.0.vi()) * -100_000.0) as isize); 118 | } else { 119 | // Run just the first stage of quick sort. 120 | let len = vec.len(); 121 | let mut i = 0; 122 | let mut j = len; 123 | while i < j { 124 | loop { 125 | if len == i { 126 | continue 'next_lit; 127 | } 128 | if asg.activity(vec[i].0.vi()) < thr { 129 | break; 130 | } 131 | i += 1; 132 | } 133 | loop { 134 | if j == 0 { 135 | continue 'next_lit; 136 | } 137 | j -= 1; 138 | if thr < asg.activity(vec[j].0.vi()) { 139 | break; 140 | } 141 | } 142 | vec.swap(i, j); 143 | i += 1; 144 | } 145 | } 146 | } 147 | } 148 | */ 149 | } 150 | -------------------------------------------------------------------------------- /tests/verify-certificates.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | fs::{self, File}, 4 | path::Path, 5 | process::{Command, Stdio}, 6 | }; 7 | 8 | /* 9 | for f in ${UUF250}*.cnf; do 10 | target=`basename -s .cnf $f` 11 | rm -f ${target}.out 12 | splr -c -p ${target}.out ${f} > /dev/null 13 | if [ -z ${target}.out ]; then 14 | echo ' FAIL TO CERTIFICATE: ${f}' 15 | exit 1; 16 | egrep -v '^[cs]' < ${target}.out > ${target}.drat 17 | gratgen ${f} ${target}.drat -o ${target}.grat -j 4 > /dev/null 18 | gratchk unsat ${f} ${target}.grat 19 | */ 20 | 21 | fn main() { 22 | if let Some(uuf250) = env::args().nth(1) { 23 | let path = Path::new(&uuf250); 24 | for e in path 25 | .read_dir() 26 | .unwrap_or_else(|_| panic!("not exist {:?}", path)) 27 | { 28 | if let Ok(cnf) = e.map(|e| e.path()) { 29 | if let Some(target) = cnf.file_name().map(Path::new) { 30 | let out = target.with_extension("out"); 31 | let drat = target.with_extension("drat"); 32 | let grat = target.with_extension("grat"); 33 | // println!("########################################"); 34 | print!("# {}", cnf.file_name().unwrap().to_string_lossy()); 35 | // rm -f ${target}.out 36 | if out.exists() { 37 | fs::remove_file(&out).expect("fail to rm"); 38 | } 39 | // splr -c -p ${target}.out ${f} > /dev/null 40 | Command::new("splr") 41 | .args(&["-c", "-p", &*out.to_string_lossy(), &*cnf.to_string_lossy()]) 42 | .stdout(Stdio::null()) 43 | .output() 44 | .expect("failed to execute Splr"); 45 | // if [ -z ${target}.out ]; then 46 | // echo ' FAIL TO CERTIFICATE: ${f}' 47 | // exit 1; 48 | // fi 49 | if !out.exists() { 50 | println!( 51 | " FAIL TO CERTIFICATE: {} => {}", 52 | cnf.file_name().unwrap().to_string_lossy(), 53 | out.to_string_lossy(), 54 | ); 55 | panic!("abort"); 56 | } 57 | // egrep -v '^[cs]' < ${target}.out > ${target}.drat 58 | Command::new("egrep") 59 | .args(&["-v", "^[cs]"]) 60 | .stdin(File::open(out).expect("")) 61 | .stdout(File::create(&drat).expect("")) 62 | .output() 63 | .expect(""); 64 | // gratgen ${f} ${target}.drat -o ${target}.grat -j 4 > /dev/null 65 | Command::new("gratgen") 66 | .args(&[ 67 | &*cnf.to_string_lossy(), 68 | &*drat.to_string_lossy(), 69 | "-o", 70 | &*grat.to_string_lossy(), 71 | "-j", 72 | "4", 73 | ]) 74 | .stdin(Stdio::piped()) 75 | .stdout(Stdio::null()) 76 | .stderr(Stdio::null()) 77 | .output() 78 | .unwrap(); 79 | // .expect(&format!("FAIL TO GENERATE: {} => {}", 80 | // cnf.file_name().unwrap().to_string_lossy(), 81 | // grat.to_string_lossy(), 82 | // )); 83 | // gratchk unsat ${f} ${target}.grat 84 | if grat.exists() { 85 | print!(" => {}", grat.to_string_lossy()); 86 | } else { 87 | println!( 88 | " FAIL TO CONVERT: {} => {}", 89 | cnf.file_name().unwrap().to_string_lossy(), 90 | grat.to_string_lossy(), 91 | ); 92 | panic!("abort"); 93 | } 94 | let mut pass = false; 95 | if let Ok(out) = Command::new("gratchk") 96 | .args(&["unsat", &*cnf.to_string_lossy(), &*grat.to_string_lossy()]) 97 | .stdin(Stdio::piped()) 98 | .stderr(Stdio::null()) 99 | .output() 100 | { 101 | let str = String::from_utf8_lossy(&out.stdout); 102 | for l in (*str).split('\n') { 103 | if l.contains(&"s VERIFIED UNSAT") { 104 | pass = true; 105 | println!(" => VERIFIED UNSAT"); 106 | break; 107 | } 108 | } 109 | } 110 | if !pass { 111 | println!( 112 | " FAIL TO CERTIFICATE: {} => {}", 113 | cnf.file_name().unwrap().to_string_lossy(), 114 | grat.to_string_lossy(), 115 | ); 116 | panic!("abort"); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/solver/stage.rs: -------------------------------------------------------------------------------- 1 | /// An implementation of CaDiCaL-style blocker. 2 | /// I define it as a 'search mode', or stage, changer. 3 | /// A stage is a span sharing same restart parameters. 4 | /// And it also define the interval of clause reduction. 5 | use crate::types::*; 6 | 7 | #[derive(Clone, Debug, Default)] 8 | pub struct StageManager { 9 | cycle: usize, 10 | stage: usize, 11 | segment: usize, 12 | unit_size: usize, 13 | luby_iter: LubySeries, 14 | max_scale_of_segment: usize, 15 | scale: usize, 16 | end_of_stage: usize, 17 | next_is_new_segment: bool, 18 | cycle_starting_stage: usize, 19 | segment_starting_stage: usize, 20 | segment_starting_cycle: usize, 21 | } 22 | 23 | impl Instantiate for StageManager { 24 | fn instantiate(_: &Config, cnf: &CNFDescription) -> StageManager { 25 | let unit_size = (cnf.num_of_variables as f64).sqrt() as usize; 26 | StageManager { 27 | unit_size, 28 | max_scale_of_segment: 1, 29 | scale: 1, 30 | end_of_stage: unit_size, 31 | next_is_new_segment: false, 32 | ..StageManager::default() 33 | } 34 | } 35 | fn handle(&mut self, _: SolverEvent) {} 36 | } 37 | 38 | impl StageManager { 39 | pub fn new(unit_size: usize) -> Self { 40 | StageManager { 41 | cycle: 0, 42 | stage: 0, 43 | segment: 0, 44 | unit_size, 45 | luby_iter: LubySeries::default(), 46 | max_scale_of_segment: 1, 47 | scale: 1, 48 | end_of_stage: unit_size, 49 | next_is_new_segment: false, 50 | cycle_starting_stage: 0, 51 | segment_starting_stage: 0, 52 | segment_starting_cycle: 0, 53 | } 54 | } 55 | pub fn initialize(&mut self, unit_size: usize) { 56 | self.cycle = 0; 57 | self.unit_size = unit_size; 58 | self.scale = 1; 59 | self.max_scale_of_segment = 1; 60 | self.end_of_stage = unit_size; 61 | self.next_is_new_segment = true; 62 | } 63 | pub fn reset(&mut self) { 64 | self.cycle = 0; 65 | self.scale = 1; 66 | self.max_scale_of_segment = 1; 67 | self.end_of_stage = self.unit_size; 68 | self.next_is_new_segment = true; 69 | } 70 | /// returns: 71 | /// - Some(true): it's a beginning of a new cycle and a new segment, a 2nd-level group. 72 | /// - Some(false): a beginning of a new cycle. 73 | /// - None: the other case. 74 | pub fn prepare_new_stage(&mut self, now: usize) -> Option { 75 | let mut new_cycle = false; 76 | let mut new_segment = false; 77 | self.scale = self.luby_iter.next_unchecked(); 78 | self.stage += 1; 79 | if self.scale == 1 { 80 | self.cycle += 1; 81 | self.cycle_starting_stage = self.stage; 82 | new_cycle = true; 83 | if self.next_is_new_segment { 84 | self.segment += 1; 85 | self.max_scale_of_segment *= 2; 86 | self.next_is_new_segment = false; 87 | self.segment_starting_stage = self.stage; 88 | self.segment_starting_cycle = self.cycle; 89 | new_segment = true; 90 | } 91 | } 92 | if self.max_scale_of_segment == self.scale { 93 | self.next_is_new_segment = true; 94 | } 95 | let span = self.current_span(); 96 | self.end_of_stage = now + span; 97 | new_cycle.then_some(new_segment) 98 | } 99 | pub fn stage_ended(&self, now: usize) -> bool { 100 | self.end_of_stage == now 101 | } 102 | /// returns the number of conflicts in the current stage 103 | /// Note: we need not to make a strong correlation between this value and 104 | /// scale defined by Luby series. So this is fine. 105 | pub fn current_span(&self) -> usize { 106 | self.cycle * self.unit_size 107 | } 108 | pub fn current_stage(&self) -> usize { 109 | self.stage 110 | } 111 | pub fn current_cycle(&self) -> usize { 112 | self.cycle 113 | } 114 | /// returns the scaling factor used in the current span 115 | pub fn current_scale(&self) -> usize { 116 | self.scale 117 | } 118 | /// returns the current index for the level 2 segments 119 | pub fn current_segment(&self) -> usize { 120 | self.segment 121 | } 122 | /// returns a recommending number of redicible learnt clauses, based on 123 | /// the length of span. 124 | pub fn num_reducible(&self, reducing_factor: f64) -> usize { 125 | let span = self.current_span(); 126 | // let scale = (self.current_scale() as f64).powf(0.6); 127 | // let keep = scale * self.unit_size as f64; 128 | let keep = (span as f64).powf(1.0 - reducing_factor) as usize; 129 | span.saturating_sub(keep) 130 | } 131 | /// returns the maximum factor so far. 132 | /// None: `luby_iter.max_value` holds the maximum value so far. 133 | /// This means it is the value found at the last segment. 134 | /// So the current value should be the next value, which is the double. 135 | pub fn max_scale(&self) -> usize { 136 | self.max_scale_of_segment 137 | } 138 | pub fn cycle_starting_stage(&self) -> usize { 139 | self.cycle_starting_stage 140 | } 141 | pub fn segment_starting_cycle(&self) -> usize { 142 | self.segment_starting_cycle 143 | } 144 | pub fn segment_starting_stage(&self) -> usize { 145 | self.segment_starting_stage 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/processor/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! * private module `eliminate` provides var elimination 3 | //! * private module `subsume` provides clause subsumption 4 | //! 5 | //!# Example 6 | //! 7 | //!``` 8 | //! use splr::{processor::{self, Eliminator, EliminateIF}, solver::Solver, types::{Instantiate, PropertyDereference}}; 9 | //! use std::path::Path; 10 | //! 11 | //! let mut s = Solver::try_from(Path::new("cnfs/sample.cnf")).expect("failed to load"); 12 | //! let Solver { 13 | //! ref mut asg, 14 | //! ref mut cdb, 15 | //! ref mut state, 16 | //! .. 17 | //! } = s; 18 | //! let mut elim = Eliminator::instantiate(&state.config, &state.cnf); 19 | //! elim.simplify(asg, cdb, state, false).expect("panic"); 20 | //! assert!(!state.config.enable_eliminator || 0 < asg.num_eliminated_vars); 21 | //!``` 22 | 23 | mod eliminate; 24 | mod heap; 25 | mod simplify; 26 | mod subsume; 27 | 28 | use { 29 | crate::{ 30 | assign::AssignIF, 31 | cdb::ClauseDBIF, 32 | processor::heap::{LitOccurs, VarOccHeap}, 33 | state::State, 34 | types::*, 35 | }, 36 | std::slice::Iter, 37 | }; 38 | 39 | /// API for Eliminator like `activate`, `stop`, `eliminate` and so on. 40 | ///``` 41 | /// use crate::{splr::config::Config, splr::types::*}; 42 | /// use crate::splr::processor::{Eliminator, EliminateIF}; 43 | /// use crate::splr::solver::Solver; 44 | /// 45 | /// let mut s = Solver::instantiate(&Config::default(), &CNFDescription::default()); 46 | /// let mut elim = Eliminator::instantiate(&s.state.config, &s.state.cnf); 47 | /// assert_eq!(elim.is_running(), false); 48 | /// assert_eq!(elim.simplify(&mut s.asg, &mut s.cdb, &mut s.state, false), Ok(())); 49 | ///``` 50 | pub trait EliminateIF: Instantiate { 51 | /// check if the eliminator is running. 52 | fn is_running(&self) -> bool; 53 | /// rebuild occur lists. 54 | fn prepare(&mut self, asg: &mut impl AssignIF, cdb: &mut impl ClauseDBIF, force: bool); 55 | /// enqueue a var into eliminator's var queue. 56 | fn enqueue_var(&mut self, asg: &mut impl AssignIF, vi: VarId, upward: bool); 57 | /// simplify database by: 58 | /// * removing satisfiable clauses 59 | /// * calling exhaustive simplifier that tries **clause subsumption** and **variable elimination**. 60 | /// 61 | /// Note: `force_run` is used only at the beginning of `solve' for simple satisfiability check 62 | /// 63 | /// # Errors 64 | /// 65 | /// if solver becomes inconsistent. 66 | fn simplify( 67 | &mut self, 68 | asg: &mut impl AssignIF, 69 | cdb: &mut impl ClauseDBIF, 70 | state: &mut State, 71 | force_run: bool, 72 | ) -> MaybeInconsistent; 73 | /// return the order of vars based on their occurrences 74 | fn sorted_iterator(&self) -> Iter<'_, u32>; 75 | /// return vi's stats 76 | fn stats(&self, vi: VarId) -> Option<(usize, usize)>; 77 | /// return the constraints on eliminated literals. 78 | fn eliminated_lits(&mut self) -> &mut Vec; 79 | } 80 | 81 | #[derive(Copy, Clone, Eq, Debug, PartialEq)] 82 | enum EliminatorMode { 83 | Dormant, 84 | Running, 85 | } 86 | 87 | /// Literal eliminator 88 | #[derive(Clone, Debug)] 89 | pub struct Eliminator { 90 | enable: bool, 91 | mode: EliminatorMode, 92 | clause_queue: Vec, 93 | var_queue: VarOccHeap, 94 | bwdsub_assigns: usize, 95 | /// constraints on eliminated var. It is used by `extend_model`. 96 | elim_lits: Vec, 97 | /// Maximum number of clauses to try to eliminate a var 98 | eliminate_var_occurrence_limit: usize, 99 | /// Stop elimination if the increase of clauses is over this 100 | eliminate_grow_limit: usize, 101 | /// A criteria by the product's of its positive occurrences and negative ones 102 | eliminate_occurrence_limit: usize, 103 | /// Stop subsumption if the size of a clause is over this 104 | subsume_literal_limit: usize, 105 | /// var 106 | var: Vec, 107 | pub num_subsumed: usize, 108 | } 109 | 110 | #[cfg(not(feature = "no_IO"))] 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | use crate::{assign::VarManipulateIF, processor::EliminateIF, solver::Solver}; 115 | use std::path::Path; 116 | 117 | #[test] 118 | fn check_elimination() { 119 | let mut config = Config::default(); 120 | if !config.enable_eliminator { 121 | return; 122 | } 123 | config.quiet_mode = true; 124 | let mut s = Solver::try_from(Path::new("cnfs/sample.cnf")).expect("failed to load"); 125 | let Solver { 126 | ref mut asg, 127 | ref mut cdb, 128 | ref mut state, 129 | .. 130 | } = s; 131 | let mut elim = Eliminator::instantiate(&state.config, &state.cnf); 132 | assert!(elim.enable); 133 | elim.simplify(asg, cdb, state, false).expect(""); 134 | assert!(!asg.var_iter().skip(1).all(|v| v.is(FlagVar::ELIMINATED))); 135 | assert!(0 < asg.num_eliminated_vars); 136 | assert_eq!( 137 | asg.num_eliminated_vars, 138 | asg.var_iter().filter(|v| v.is(FlagVar::ELIMINATED)).count() 139 | ); 140 | let elim_vars = asg 141 | .var_iter() 142 | .enumerate() 143 | .skip(1) 144 | .filter(|(_, v)| v.is(FlagVar::ELIMINATED)) 145 | .map(|(vi, _)| vi) 146 | .collect::>(); 147 | assert_eq!( 148 | 0, 149 | cdb.iter() 150 | .skip(1) 151 | .filter(|c| !c.is_dead()) 152 | .filter(|c| c.iter().any(|l| elim_vars.contains(&l.vi()))) 153 | .count() 154 | ); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/assign/select.rs: -------------------------------------------------------------------------------- 1 | // Decision var selection 2 | 3 | #[cfg(feature = "rephase")] 4 | use super::property; 5 | 6 | use { 7 | super::{heap::VarHeapIF, stack::AssignStack}, 8 | crate::types::*, 9 | std::collections::HashMap, 10 | }; 11 | 12 | /// ```ignore 13 | /// let x: Option = var_assign!(self, lit.vi()); 14 | /// ``` 15 | #[cfg(feature = "unsafe_access")] 16 | macro_rules! var_assign { 17 | ($asg: expr, $var: expr) => { 18 | unsafe { $asg.var.get_unchecked($var).assign } 19 | }; 20 | } 21 | #[cfg(not(feature = "unsafe_access"))] 22 | macro_rules! var_assign { 23 | ($asg: expr, $var: expr) => { 24 | $asg.assign[$var] 25 | }; 26 | } 27 | 28 | /// API for var selection, depending on an internal heap. 29 | pub trait VarSelectIF { 30 | #[cfg(feature = "rephase")] 31 | /// return best phases 32 | fn best_phases_ref(&mut self, default_value: Option) -> HashMap; 33 | #[cfg(feature = "rephase")] 34 | /// force an assignment obtained by SLS 35 | fn override_rephasing_target(&mut self, assignment: &HashMap) -> usize; 36 | /// give rewards to vars selected by SLS 37 | fn reward_by_sls(&mut self, assignment: &HashMap) -> usize; 38 | #[cfg(feature = "rephase")] 39 | /// select rephasing target 40 | fn select_rephasing_target(&mut self); 41 | #[cfg(feature = "rephase")] 42 | /// check the consistency 43 | fn check_consistency_of_best_phases(&mut self); 44 | /// select a new decision variable. 45 | fn select_decision_literal(&mut self) -> Lit; 46 | /// update the internal heap on var order. 47 | fn update_order(&mut self, v: VarId); 48 | /// rebuild the internal var_order 49 | fn rebuild_order(&mut self); 50 | } 51 | 52 | impl VarSelectIF for AssignStack { 53 | #[cfg(feature = "rephase")] 54 | fn best_phases_ref(&mut self, default_value: Option) -> HashMap { 55 | self.var 56 | .iter() 57 | .enumerate() 58 | .filter_map(|(vi, v)| { 59 | if v.level == self.root_level || v.is(FlagVar::ELIMINATED) { 60 | default_value.map(|b| (vi, b)) 61 | } else { 62 | Some(( 63 | vi, 64 | self.best_phases.get(&vi).map_or( 65 | self.var[vi].assign.unwrap_or_else(|| v.is(FlagVar::PHASE)), 66 | |(b, _)| *b, 67 | ), 68 | )) 69 | } 70 | }) 71 | .collect::>() 72 | } 73 | #[cfg(feature = "rephase")] 74 | fn override_rephasing_target(&mut self, assignment: &HashMap) -> usize { 75 | let mut num_flipped = 0; 76 | for (vi, b) in assignment.iter() { 77 | if self.best_phases.get(vi).is_none_or(|(p, _)| *p != *b) { 78 | num_flipped += 1; 79 | self.best_phases.insert(*vi, (*b, AssignReason::None)); 80 | } 81 | } 82 | num_flipped 83 | } 84 | fn reward_by_sls(&mut self, assignment: &HashMap) -> usize { 85 | let mut num_flipped = 0; 86 | for (vi, b) in assignment.iter() { 87 | let v = &mut self.var[*vi]; 88 | if v.is(FlagVar::PHASE) != *b { 89 | num_flipped += 1; 90 | v.set(FlagVar::PHASE, *b); 91 | v.reward *= self.activity_decay; 92 | v.reward += self.activity_anti_decay; 93 | self.update_heap(*vi); 94 | } 95 | } 96 | num_flipped 97 | } 98 | #[cfg(feature = "rephase")] 99 | fn select_rephasing_target(&mut self) { 100 | if self.best_phases.is_empty() { 101 | return; 102 | } 103 | self.check_consistency_of_best_phases(); 104 | if self.derefer(property::Tusize::NumUnassertedVar) <= self.best_phases.len() { 105 | self.best_phases.clear(); 106 | return; 107 | } 108 | debug_assert!(self 109 | .best_phases 110 | .iter() 111 | .all(|(vi, b)| self.var[*vi].assign != Some(!b.0))); 112 | self.num_rephase += 1; 113 | for (vi, (b, _)) in self.best_phases.iter() { 114 | let v = &mut self.var[*vi]; 115 | v.set(FlagVar::PHASE, *b); 116 | } 117 | } 118 | #[cfg(feature = "rephase")] 119 | fn check_consistency_of_best_phases(&mut self) { 120 | if self 121 | .best_phases 122 | .iter() 123 | .any(|(vi, b)| self.var[*vi].assign == Some(!b.0)) 124 | { 125 | self.best_phases.clear(); 126 | self.num_best_assign = self.num_asserted_vars + self.num_eliminated_vars; 127 | } 128 | } 129 | fn select_decision_literal(&mut self) -> Lit { 130 | let vi = self.select_var(); 131 | Lit::from((vi, self.var[vi].is(FlagVar::PHASE))) 132 | } 133 | fn update_order(&mut self, v: VarId) { 134 | self.update_heap(v); 135 | } 136 | fn rebuild_order(&mut self) { 137 | self.clear_heap(); 138 | for vi in 1..self.var.len() { 139 | if var_assign!(self, vi).is_none() && !self.var[vi].is(FlagVar::ELIMINATED) { 140 | self.insert_heap(vi); 141 | } 142 | } 143 | } 144 | } 145 | 146 | impl AssignStack { 147 | /// select a decision var 148 | fn select_var(&mut self) -> VarId { 149 | loop { 150 | let vi = self.get_heap_root(); 151 | if var_assign!(self, vi).is_none() && !self.var[vi].is(FlagVar::ELIMINATED) { 152 | return vi; 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/assign/trail_saving.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "trail_saving")] 2 | /// implement boolean constraint propagation, backjump 3 | /// This version can handle Chronological and Non Chronological Backtrack. 4 | use { 5 | super::{heap::VarHeapIF, AssignStack, PropagateIF, VarManipulateIF}, 6 | crate::{cdb::ClauseDBIF, types::*}, 7 | }; 8 | 9 | #[cfg(feature = "chrono_BT")] 10 | use super::AssignIF; 11 | 12 | /// Methods on trail saving. 13 | pub trait TrailSavingIF { 14 | fn save_trail(&mut self, to_lvl: DecisionLevel); 15 | fn reuse_saved_trail(&mut self, cdb: &impl ClauseDBIF) -> PropagationResult; 16 | fn clear_saved_trail(&mut self); 17 | } 18 | 19 | impl TrailSavingIF for AssignStack { 20 | fn save_trail(&mut self, to_lvl: DecisionLevel) { 21 | let lim = self.trail_lim[to_lvl as usize]; 22 | let dl = self.trail_lim.len(); 23 | let mut free: usize = lim; 24 | self.clear_saved_trail(); 25 | if 2 <= dl { 26 | let lim2 = self.trail_lim[dl - 2]; 27 | let activity_threshold = self.var[self.trail[lim2].vi()].reward; 28 | for i in (lim..lim2).rev() { 29 | let l = self.trail[i]; 30 | let vi = l.vi(); 31 | 32 | // 33 | //## mutliple backtracks not work 34 | // 35 | // if let Some(i) = self.trail_saved.iter().position(|k| k.vi() == vi) { 36 | // if self.trail_saved[i] == !l { 37 | // self.trail_saved.drain(0..=i); 38 | // } 39 | // } 40 | 41 | self.trail_saved.push(l); 42 | self.var[vi].reason_saved = self.var[vi].reason; 43 | self.reward_at_unassign(vi); 44 | if activity_threshold <= self.var[vi].reward { 45 | self.insert_heap(vi); 46 | } 47 | } 48 | free = lim2; 49 | } 50 | for i in free..self.trail.len() { 51 | let vi = self.trail[i].vi(); 52 | self.reward_at_unassign(vi); 53 | self.insert_heap(vi); 54 | } 55 | } 56 | fn reuse_saved_trail(&mut self, cdb: &impl ClauseDBIF) -> PropagationResult { 57 | let q = self.stage_scale.trailing_zeros() as u16 58 | + (cdb.derefer(crate::cdb::property::Tf64::LiteralBlockEntanglement) as u16) / 2; 59 | 60 | #[cfg(feature = "chrono_BT")] 61 | let dl = self.decision_level(); 62 | 63 | for i in (0..self.trail_saved.len()).rev() { 64 | let lit = self.trail_saved[i]; 65 | let vi = lit.vi(); 66 | let old_reason = self.var[vi].reason_saved; 67 | match (self.assigned(lit), old_reason) { 68 | (Some(true), _) => (), 69 | (None, AssignReason::BinaryLink(link)) => { 70 | debug_assert_ne!(link.vi(), lit.vi()); 71 | debug_assert_eq!(self.assigned(link), Some(true)); 72 | self.num_repropagation += 1; 73 | 74 | self.assign_by_implication( 75 | lit, 76 | old_reason, 77 | #[cfg(feature = "chrono_BT")] 78 | dl, 79 | ); 80 | } 81 | // reason refinement by ignoring this dependecy 82 | (None, AssignReason::Implication(c)) if q < cdb[c].rank => { 83 | self.insert_heap(vi); 84 | return self.truncate_trail_saved(i + 1); 85 | } 86 | (None, AssignReason::Implication(cid)) => { 87 | debug_assert_eq!(cdb[cid].lit0(), lit); 88 | debug_assert!(cdb[cid] 89 | .iter() 90 | .skip(1) 91 | .all(|l| self.assigned(*l) == Some(false))); 92 | self.num_repropagation += 1; 93 | 94 | self.assign_by_implication( 95 | lit, 96 | old_reason, 97 | #[cfg(feature = "chrono_BT")] 98 | dl, 99 | ); 100 | } 101 | (Some(false), AssignReason::BinaryLink(link)) => { 102 | debug_assert_ne!(link.vi(), lit.vi()); 103 | debug_assert_eq!(self.assigned(link), Some(true)); 104 | let _ = self.truncate_trail_saved(i + 1); // reduce heap ops. 105 | self.clear_saved_trail(); 106 | return Err((lit, old_reason)); 107 | } 108 | (Some(false), AssignReason::Implication(cid)) => { 109 | debug_assert!(cdb[cid].iter().all(|l| self.assigned(*l) == Some(false))); 110 | let _ = self.truncate_trail_saved(i + 1); // reduce heap ops. 111 | self.clear_saved_trail(); 112 | return Err((cdb[cid].lit0(), AssignReason::Implication(cid))); 113 | } 114 | (_, AssignReason::Decision(lvl)) => { 115 | debug_assert_ne!(0, lvl); 116 | self.insert_heap(vi); 117 | return self.truncate_trail_saved(i + 1); 118 | } 119 | _ => unreachable!("from_saved_trail"), 120 | } 121 | } 122 | self.trail_saved.clear(); 123 | Ok(()) 124 | } 125 | fn clear_saved_trail(&mut self) { 126 | for j in 0..self.trail_saved.len() { 127 | let l = self.trail_saved[j]; 128 | self.insert_heap(l.vi()); 129 | } 130 | self.trail_saved.clear(); 131 | } 132 | } 133 | 134 | impl AssignStack { 135 | fn truncate_trail_saved(&mut self, len: usize) -> PropagationResult { 136 | self.trail_saved.truncate(len); 137 | Ok(()) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /cnfs/uf100-010.cnf: -------------------------------------------------------------------------------- 1 | c This Formular is generated by mcnf 2 | c 3 | c horn? no 4 | c forced? no 5 | c mixed sat? no 6 | c clause length = 3 7 | c 8 | p cnf 100 430 9 | 99 -57 -46 0 10 | -91 -62 19 0 11 | -59 22 63 0 12 | -20 -26 -98 0 13 | 6 57 88 0 14 | 89 30 82 0 15 | 20 -23 -94 0 16 | 59 42 96 0 17 | -70 -58 48 0 18 | 51 -59 74 0 19 | 64 70 -33 0 20 | -79 -23 -88 0 21 | 46 20 13 0 22 | 64 -9 -8 0 23 | 29 -26 -19 0 24 | 6 -29 -69 0 25 | -3 53 -94 0 26 | -61 71 74 0 27 | 23 -75 -22 0 28 | 91 14 -22 0 29 | -9 -21 -15 0 30 | 48 -61 -41 0 31 | -95 -12 37 0 32 | 81 8 23 0 33 | 99 28 22 0 34 | 7 44 58 0 35 | -10 68 76 0 36 | -17 -4 38 0 37 | 2 -53 -29 0 38 | -33 -53 93 0 39 | 92 80 -73 0 40 | 64 -11 -61 0 41 | 66 77 73 0 42 | -25 -31 -97 0 43 | 20 88 -4 0 44 | 42 -33 -57 0 45 | -7 -46 68 0 46 | 37 -51 5 0 47 | 53 79 66 0 48 | 100 -52 8 0 49 | 80 5 -83 0 50 | -94 -25 -9 0 51 | -90 29 -44 0 52 | -30 90 76 0 53 | 15 38 59 0 54 | -72 -21 27 0 55 | 18 51 -70 0 56 | -20 -2 90 0 57 | 60 26 -16 0 58 | 42 -36 92 0 59 | 22 -30 52 0 60 | -94 1 -60 0 61 | 25 46 -74 0 62 | -42 69 96 0 63 | 12 45 52 0 64 | -35 67 40 0 65 | -95 -9 24 0 66 | 26 96 -100 0 67 | 52 -72 -66 0 68 | -20 34 -23 0 69 | 32 83 44 0 70 | 55 -29 93 0 71 | 22 -73 -17 0 72 | -84 49 52 0 73 | 13 43 66 0 74 | 92 -39 -91 0 75 | -73 -40 24 0 76 | -82 87 -75 0 77 | 85 -3 100 0 78 | 69 7 -66 0 79 | 43 -32 -12 0 80 | -30 38 36 0 81 | 55 -84 87 0 82 | -80 68 8 0 83 | -1 72 8 0 84 | -40 -50 81 0 85 | -47 -13 -85 0 86 | -82 21 -71 0 87 | -74 28 20 0 88 | -83 -8 96 0 89 | 39 -13 51 0 90 | 65 -10 46 0 91 | 35 73 -32 0 92 | -58 -15 9 0 93 | -1 -77 -22 0 94 | 98 5 -8 0 95 | 83 89 -80 0 96 | -40 -73 -43 0 97 | -52 35 -98 0 98 | -75 13 69 0 99 | -92 -7 -41 0 100 | 69 -38 -52 0 101 | -19 -6 62 0 102 | 44 -29 -73 0 103 | 33 -63 98 0 104 | -37 50 -14 0 105 | -41 -17 -7 0 106 | 82 84 -78 0 107 | -81 87 -83 0 108 | 38 -60 -33 0 109 | -43 -97 -40 0 110 | 26 13 -5 0 111 | -4 86 -53 0 112 | 36 -9 83 0 113 | 88 100 69 0 114 | 11 78 -25 0 115 | 38 -59 10 0 116 | -12 -95 1 0 117 | 61 12 -37 0 118 | 54 17 97 0 119 | -52 -47 10 0 120 | -74 -35 -3 0 121 | -98 -89 -88 0 122 | -26 -14 76 0 123 | -60 11 -22 0 124 | -89 -84 6 0 125 | 41 34 37 0 126 | 37 15 -2 0 127 | -88 -38 69 0 128 | -68 59 -37 0 129 | -49 4 53 0 130 | -56 -42 -46 0 131 | 5 21 -77 0 132 | 61 -33 26 0 133 | -56 -63 -84 0 134 | 61 -1 2 0 135 | -26 10 45 0 136 | 34 7 -50 0 137 | 99 74 26 0 138 | 50 58 84 0 139 | 33 11 -37 0 140 | -37 50 100 0 141 | -10 -56 8 0 142 | -53 -65 -93 0 143 | -100 -38 54 0 144 | -20 4 14 0 145 | 47 -75 87 0 146 | -48 65 63 0 147 | -75 -76 -93 0 148 | 64 54 -19 0 149 | 20 57 94 0 150 | -70 59 -5 0 151 | 64 53 24 0 152 | 54 82 -22 0 153 | 27 13 -30 0 154 | 35 -49 23 0 155 | -59 51 -93 0 156 | 23 -70 -50 0 157 | 93 -49 -39 0 158 | 90 -22 100 0 159 | 85 1 -78 0 160 | 41 87 9 0 161 | -31 72 -84 0 162 | -36 10 28 0 163 | -61 13 44 0 164 | -90 15 11 0 165 | -25 -62 34 0 166 | 26 -46 32 0 167 | 40 -72 -38 0 168 | 75 7 5 0 169 | -25 -52 92 0 170 | 35 -88 -50 0 171 | -55 -7 29 0 172 | 100 41 77 0 173 | -88 -42 23 0 174 | 31 81 -39 0 175 | -39 -32 5 0 176 | -24 -100 -31 0 177 | -82 11 -46 0 178 | 82 -88 -25 0 179 | -52 -97 60 0 180 | 45 39 35 0 181 | -50 30 -27 0 182 | -50 -94 -7 0 183 | 79 -70 39 0 184 | 83 24 1 0 185 | -45 58 40 0 186 | 7 57 -76 0 187 | 30 -86 -82 0 188 | 87 -59 39 0 189 | 30 -44 36 0 190 | -32 31 7 0 191 | -17 16 70 0 192 | -80 78 -92 0 193 | -31 100 -32 0 194 | 81 -54 37 0 195 | -65 -16 -53 0 196 | -85 -2 78 0 197 | -61 74 4 0 198 | -26 65 16 0 199 | -50 15 -94 0 200 | -8 -95 -94 0 201 | -22 52 -81 0 202 | 25 67 -48 0 203 | -46 45 -47 0 204 | -2 51 95 0 205 | -74 44 -20 0 206 | 45 25 38 0 207 | 100 -49 11 0 208 | -28 -23 -74 0 209 | 55 -85 -26 0 210 | -75 54 6 0 211 | 81 -65 -35 0 212 | -62 89 -6 0 213 | -1 63 -5 0 214 | -50 92 -73 0 215 | 37 16 6 0 216 | 53 -90 87 0 217 | 70 68 50 0 218 | 42 66 38 0 219 | 65 94 -98 0 220 | 77 -50 -57 0 221 | -72 50 58 0 222 | -27 18 -52 0 223 | 9 -85 22 0 224 | 89 81 -45 0 225 | 5 65 52 0 226 | -96 74 -5 0 227 | 56 58 -72 0 228 | 91 -34 -77 0 229 | 59 -46 -45 0 230 | 98 80 25 0 231 | -82 -99 -23 0 232 | 41 7 12 0 233 | 5 70 81 0 234 | -75 73 89 0 235 | 74 26 72 0 236 | -5 -28 -43 0 237 | 32 -86 8 0 238 | 48 4 13 0 239 | -73 -95 -11 0 240 | -9 -45 83 0 241 | 1 -71 38 0 242 | 40 93 3 0 243 | 90 85 7 0 244 | -40 86 -21 0 245 | -8 55 36 0 246 | 1 -36 73 0 247 | -55 -18 -28 0 248 | -61 -54 -49 0 249 | 93 95 -47 0 250 | 5 52 -48 0 251 | -42 -19 15 0 252 | -34 -15 77 0 253 | 52 -32 -89 0 254 | 12 81 -98 0 255 | -30 78 39 0 256 | 11 60 40 0 257 | 74 -73 50 0 258 | -91 17 60 0 259 | 30 -47 -27 0 260 | -17 -1 30 0 261 | 77 80 8 0 262 | 51 78 -19 0 263 | -29 -18 100 0 264 | -35 -26 -81 0 265 | 14 70 51 0 266 | -91 1 99 0 267 | -45 -67 80 0 268 | 46 -96 25 0 269 | -79 -19 -47 0 270 | -10 93 -69 0 271 | -81 26 94 0 272 | -82 69 77 0 273 | -69 77 40 0 274 | -14 -1 42 0 275 | 60 69 14 0 276 | 23 -10 -74 0 277 | 26 71 -67 0 278 | -46 -1 -74 0 279 | -8 -96 -50 0 280 | -28 -24 83 0 281 | -79 -63 6 0 282 | -21 -69 -22 0 283 | 54 41 -52 0 284 | 54 -32 -9 0 285 | 49 -10 -61 0 286 | -84 -25 98 0 287 | 98 97 69 0 288 | 87 3 76 0 289 | 98 3 43 0 290 | 84 -73 40 0 291 | 37 61 27 0 292 | 34 -36 98 0 293 | 38 -43 -49 0 294 | -24 -78 100 0 295 | 96 66 -95 0 296 | -37 -46 -32 0 297 | -64 13 -6 0 298 | -8 -53 -4 0 299 | -53 -89 62 0 300 | -75 -51 10 0 301 | -40 -21 66 0 302 | 42 47 -58 0 303 | 9 -15 -25 0 304 | -96 -23 79 0 305 | 1 49 -84 0 306 | -30 57 -89 0 307 | -41 70 18 0 308 | -70 26 39 0 309 | -85 -33 32 0 310 | -97 -90 -55 0 311 | 48 30 -28 0 312 | 8 -65 26 0 313 | -50 32 69 0 314 | 97 -12 37 0 315 | -35 71 15 0 316 | -60 98 -44 0 317 | -43 -41 -33 0 318 | -12 15 -27 0 319 | 85 100 64 0 320 | -48 62 -8 0 321 | 77 -10 -56 0 322 | 89 40 -29 0 323 | 16 -20 8 0 324 | -94 84 9 0 325 | 96 53 98 0 326 | 61 91 -58 0 327 | 5 100 93 0 328 | 27 -35 20 0 329 | -44 71 3 0 330 | -89 23 -60 0 331 | -35 87 -65 0 332 | 45 7 -76 0 333 | -38 67 30 0 334 | -77 44 -80 0 335 | -86 -27 48 0 336 | -7 40 -87 0 337 | -43 -50 38 0 338 | -18 -56 -62 0 339 | 52 -100 45 0 340 | -19 -81 75 0 341 | -88 -60 67 0 342 | -36 -100 58 0 343 | -6 -98 -2 0 344 | -36 -24 -57 0 345 | 28 74 -34 0 346 | -29 -27 45 0 347 | -4 36 -2 0 348 | 9 -14 -79 0 349 | 68 -16 75 0 350 | 76 23 4 0 351 | 95 53 -69 0 352 | 42 52 4 0 353 | 25 22 26 0 354 | 54 99 22 0 355 | -59 98 -39 0 356 | -4 1 -15 0 357 | 40 82 14 0 358 | -56 -50 -47 0 359 | 87 7 -77 0 360 | -25 -93 95 0 361 | 25 58 2 0 362 | 1 69 23 0 363 | -40 67 97 0 364 | 65 34 -56 0 365 | 39 66 83 0 366 | 35 -53 93 0 367 | -77 93 91 0 368 | 47 70 -44 0 369 | 98 -50 49 0 370 | 78 -56 -20 0 371 | -46 37 -48 0 372 | -57 21 -60 0 373 | 4 74 31 0 374 | -40 5 -97 0 375 | 26 21 44 0 376 | 1 -77 -64 0 377 | -18 -14 13 0 378 | -39 74 13 0 379 | 17 61 -23 0 380 | 1 74 87 0 381 | 72 -20 -95 0 382 | 27 -86 66 0 383 | -78 -2 9 0 384 | 9 88 90 0 385 | -98 2 12 0 386 | -1 -17 50 0 387 | 47 79 -3 0 388 | -47 -96 67 0 389 | -85 95 26 0 390 | 12 23 -28 0 391 | 53 7 90 0 392 | 21 -49 20 0 393 | -89 -6 32 0 394 | 94 46 11 0 395 | -45 -31 -60 0 396 | 44 7 -73 0 397 | -74 -30 -23 0 398 | -30 -41 88 0 399 | -79 90 95 0 400 | -26 52 -45 0 401 | 50 -67 5 0 402 | 96 -74 -43 0 403 | -100 -97 36 0 404 | 36 37 21 0 405 | -44 52 80 0 406 | 86 -68 90 0 407 | 4 41 47 0 408 | 21 -3 78 0 409 | -43 97 -30 0 410 | 16 74 37 0 411 | -89 95 -7 0 412 | 82 7 14 0 413 | 70 -49 -23 0 414 | -56 -100 80 0 415 | -42 -87 -75 0 416 | -18 -79 -24 0 417 | -39 88 11 0 418 | -37 64 53 0 419 | 39 -19 16 0 420 | 66 -77 -93 0 421 | -61 45 55 0 422 | -81 41 23 0 423 | -21 45 1 0 424 | 9 -67 39 0 425 | 42 -55 -5 0 426 | -9 60 16 0 427 | 99 25 -58 0 428 | -59 37 10 0 429 | 79 19 -3 0 430 | 45 84 47 0 431 | 21 -26 5 0 432 | 54 88 -73 0 433 | -77 69 25 0 434 | 23 7 -52 0 435 | -18 69 -78 0 436 | 11 -38 19 0 437 | 63 28 96 0 438 | 24 15 73 0 439 | 440 | 441 | 442 | -------------------------------------------------------------------------------- /src/assign/var.rs: -------------------------------------------------------------------------------- 1 | /// Var struct and Database management API 2 | use { 3 | super::{heap::VarHeapIF, stack::AssignStack, AssignIF}, 4 | crate::types::*, 5 | std::{ 6 | fmt, 7 | slice::{Iter, IterMut}, 8 | }, 9 | }; 10 | 11 | /// Object representing a variable. 12 | #[derive(Clone, Debug)] 13 | pub struct Var { 14 | /// assignment 15 | pub(crate) assign: Option, 16 | /// decision level 17 | pub(super) level: DecisionLevel, 18 | /// assign Reason 19 | pub(super) reason: AssignReason, 20 | /// last reason for assignment. 21 | #[cfg(feature = "trail_saving")] 22 | pub(super) reason_saved: AssignReason, 23 | /// the `Flag`s (8 bits) 24 | pub(crate) flags: FlagVar, 25 | /// a dynamic evaluation criterion like EVSIDS or ACID. 26 | pub(super) reward: f64, 27 | // reward_ema: Ema2, 28 | #[cfg(feature = "boundary_check")] 29 | pub propagated_at: usize, 30 | #[cfg(feature = "boundary_check")] 31 | pub timestamp: usize, 32 | #[cfg(feature = "boundary_check")] 33 | pub state: VarState, 34 | } 35 | 36 | impl Default for Var { 37 | fn default() -> Var { 38 | Var { 39 | assign: None, 40 | level: 0, 41 | reason: AssignReason::None, 42 | #[cfg(feature = "trail_saving")] 43 | reason_saved: AssignReason::None, 44 | flags: FlagVar::empty(), 45 | reward: 0.0, 46 | // reward_ema: Ema2::new(200).with_slow(4_000), 47 | #[cfg(feature = "boundary_check")] 48 | propagated_at: 0, 49 | #[cfg(feature = "boundary_check")] 50 | timestamp: 0, 51 | #[cfg(feature = "boundary_check")] 52 | state: VarState::Unassigned(0), 53 | } 54 | } 55 | } 56 | 57 | impl fmt::Display for Var { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | let st = |flag, mes| if self.is(flag) { mes } else { "" }; 60 | write!(f, "V{{{}}}", st(FlagVar::ELIMINATED, ", eliminated"),) 61 | } 62 | } 63 | 64 | impl Var { 65 | /// return a new vector of $n$ `Var`s. 66 | pub fn new_vars(n: usize) -> Vec { 67 | (0..n as u32 + 1) 68 | .map(|n| { 69 | Var { 70 | level: n, // each literal occupies a single level. 71 | ..Default::default() 72 | } 73 | }) 74 | .collect::>() 75 | } 76 | pub fn activity(&self) -> f64 { 77 | self.reward 78 | } 79 | } 80 | 81 | impl FlagIF for Var { 82 | type FlagType = FlagVar; 83 | #[inline] 84 | fn is(&self, flag: Self::FlagType) -> bool { 85 | self.flags.contains(flag) 86 | } 87 | #[inline] 88 | fn set(&mut self, f: Self::FlagType, b: bool) { 89 | self.flags.set(f, b); 90 | } 91 | #[inline] 92 | fn turn_off(&mut self, flag: Self::FlagType) { 93 | self.flags.remove(flag); 94 | } 95 | #[inline] 96 | fn turn_on(&mut self, flag: Self::FlagType) { 97 | self.flags.insert(flag); 98 | } 99 | #[inline] 100 | fn toggle(&mut self, flag: Self::FlagType) { 101 | self.flags.toggle(flag); 102 | } 103 | } 104 | 105 | /// Var manipulation 106 | pub trait VarManipulateIF { 107 | /// return the assignment of var. 108 | fn assign(&self, vi: VarId) -> Option; 109 | /// return *the value* of a literal. 110 | fn assigned(&self, l: Lit) -> Option; 111 | /// return the assign level of var. 112 | fn level(&self, vi: VarId) -> DecisionLevel; 113 | /// return the reason of assignment. 114 | fn reason(&self, vi: VarId) -> AssignReason; 115 | /// return the var. 116 | fn var(&self, vi: VarId) -> &Var; 117 | /// return the var. 118 | fn var_mut(&mut self, vi: VarId) -> &mut Var; 119 | /// return an iterator over Vars. 120 | fn var_iter(&self) -> Iter<'_, Var>; 121 | /// return an mutable iterator over Vars. 122 | fn var_iter_mut(&mut self) -> IterMut<'_, Var>; 123 | /// set var status to asserted. 124 | fn make_var_asserted(&mut self, vi: VarId); 125 | /// set var status to eliminated. 126 | fn make_var_eliminated(&mut self, vi: VarId); 127 | } 128 | 129 | impl VarManipulateIF for AssignStack { 130 | fn assigned(&self, l: Lit) -> Option { 131 | match self.var[l.vi()].assign { 132 | Some(x) if !bool::from(l) => Some(!x), 133 | x => x, 134 | } 135 | } 136 | #[inline] 137 | fn assign(&self, vi: VarId) -> Option { 138 | #[cfg(feature = "unsafe_access")] 139 | unsafe { 140 | self.var.get_unchecked(vi).assign 141 | } 142 | #[cfg(not(feature = "unsafe_access"))] 143 | self.assign[vi] 144 | } 145 | #[inline] 146 | fn level(&self, vi: VarId) -> DecisionLevel { 147 | #[cfg(feature = "unsafe_access")] 148 | unsafe { 149 | self.var.get_unchecked(vi).level 150 | } 151 | #[cfg(not(feature = "unsafe_access"))] 152 | self.level[vi] 153 | } 154 | #[inline] 155 | fn reason(&self, vi: VarId) -> AssignReason { 156 | #[cfg(feature = "unsafe_access")] 157 | unsafe { 158 | self.var.get_unchecked(vi).reason 159 | } 160 | #[cfg(not(feature = "unsafe_access"))] 161 | self.reason[vi] 162 | } 163 | #[inline] 164 | fn var(&self, vi: VarId) -> &Var { 165 | #[cfg(feature = "unsafe_access")] 166 | unsafe { 167 | self.var.get_unchecked(vi) 168 | } 169 | #[cfg(not(feature = "unsafe_access"))] 170 | &self.var[vi] 171 | } 172 | #[inline] 173 | fn var_mut(&mut self, vi: VarId) -> &mut Var { 174 | #[cfg(feature = "unsafe_access")] 175 | unsafe { 176 | self.var.get_unchecked_mut(vi) 177 | } 178 | #[cfg(not(feature = "unsafe_access"))] 179 | &mut self.var[vi] 180 | } 181 | fn var_iter(&self) -> Iter<'_, Var> { 182 | self.var.iter() 183 | } 184 | fn var_iter_mut(&mut self) -> IterMut<'_, Var> { 185 | self.var.iter_mut() 186 | } 187 | fn make_var_asserted(&mut self, vi: VarId) { 188 | self.var[vi].reason = AssignReason::Decision(0); 189 | self.set_activity(vi, 0.0); 190 | self.remove_from_heap(vi); 191 | 192 | #[cfg(feature = "boundary_check")] 193 | { 194 | self.var[vi].timestamp = self.tick; 195 | } 196 | 197 | #[cfg(feature = "best_phases_tracking")] 198 | self.check_best_phase(vi); 199 | } 200 | fn make_var_eliminated(&mut self, vi: VarId) { 201 | if !self.var[vi].is(FlagVar::ELIMINATED) { 202 | self.var[vi].turn_on(FlagVar::ELIMINATED); 203 | self.set_activity(vi, 0.0); 204 | self.remove_from_heap(vi); 205 | debug_assert_eq!(self.decision_level(), self.root_level); 206 | self.trail.retain(|l| l.vi() != vi); 207 | self.num_eliminated_vars += 1; 208 | 209 | #[cfg(feature = "boundary_check")] 210 | { 211 | self.var[vi].timestamp = self.tick; 212 | } 213 | 214 | #[cfg(feature = "trace_elimination")] 215 | { 216 | let lv = self.level[vi]; 217 | if self.root_level == self.level[vi] && self.assign[vi].is_some() { 218 | panic!("v:{}, dl:{}", self.var[vi], self.decision_level()); 219 | } 220 | if !(self.root_level < self.level[vi] || self.assign[vi].is_none()) { 221 | panic!( 222 | "v:{}, lvl:{} => {}, dl:{}, assign:{:?} ", 223 | self.var[vi], 224 | lv, 225 | self.level[vi], 226 | self.decision_level(), 227 | self.assign[vi], 228 | ); 229 | } 230 | debug_assert!(self.root_level < self.level[vi] || self.assign[vi].is_none()); 231 | } 232 | } else { 233 | #[cfg(feature = "boundary_check")] 234 | panic!("double elimination"); 235 | } 236 | } 237 | } 238 | 239 | #[cfg(feature = "best_phases_tracking")] 240 | impl AssignStack { 241 | /// check usability of the saved best phase. 242 | /// return `true` if the current best phase got invalid. 243 | fn check_best_phase(&mut self, vi: VarId) -> bool { 244 | if let Some((b, _)) = self.best_phases.get(&vi) { 245 | debug_assert!(self.var[vi].assign.is_some()); 246 | if self.var[vi].assign != Some(*b) { 247 | if self.root_level == self.var[vi].level { 248 | self.best_phases.clear(); 249 | self.num_best_assign = self.num_asserted_vars + self.num_eliminated_vars; 250 | } 251 | return true; 252 | } 253 | } 254 | false 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/assign/heap.rs: -------------------------------------------------------------------------------- 1 | /// 2 | /// Heap struct for selecting decision vars 3 | /// 4 | use {super::stack::AssignStack, crate::types::*, std::fmt}; 5 | 6 | #[cfg(feature = "trail_saving")] 7 | use super::TrailSavingIF; 8 | 9 | /// Heap of VarId, based on var activity. 10 | // # Note 11 | // - both fields has a fixed length. Don't use push and pop. 12 | // - `idxs[0]` contains the number of alive elements 13 | // `indx` is positions. So the unused field 0 can hold the last position as a special case. 14 | #[derive(Clone, Debug, Default)] 15 | pub struct VarIdHeap { 16 | /// order : usize -> VarId::from, -- Which var is the n-th best? 17 | heap: Vec, 18 | /// VarId : -> order : usize::from -- How good is the var? 19 | /// `idxs[0]` holds the number of alive elements 20 | idxs: Vec, 21 | } 22 | 23 | impl fmt::Display for VarIdHeap { 24 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 25 | write!( 26 | f, 27 | " - seek pointer - nth -> var: {:?}\n - var -> nth: {:?}", 28 | self.heap, self.idxs, 29 | ) 30 | } 31 | } 32 | 33 | impl VarIdHeap { 34 | pub fn new(n: usize) -> Self { 35 | let mut heap = Vec::with_capacity(n + 1); 36 | let mut idxs = Vec::with_capacity(n + 1); 37 | heap.push(0); 38 | idxs.push(n as u32); 39 | for i in 1..=n { 40 | heap.push(i as u32); 41 | idxs.push(i as u32); 42 | } 43 | idxs[0] = n as u32; 44 | VarIdHeap { heap, idxs } 45 | } 46 | } 47 | 48 | /// Internal heap manipulation API 49 | pub trait VarHeapIF { 50 | fn clear_heap(&mut self); 51 | fn expand_heap(&mut self); 52 | fn insert_heap(&mut self, vi: VarId); 53 | fn update_heap(&mut self, v: VarId); 54 | fn get_heap_root(&mut self) -> VarId; 55 | fn percolate_up(&mut self, start: u32); 56 | fn percolate_down(&mut self, start: u32); 57 | fn remove_from_heap(&mut self, vs: VarId); 58 | } 59 | 60 | impl VarHeapIF for AssignStack { 61 | fn clear_heap(&mut self) { 62 | self.var_order.clear(); 63 | } 64 | fn expand_heap(&mut self) { 65 | // Fill a new slot with the value that would be used in VarIdHeap::new. 66 | let id = self.var_order.heap.len() as u32; 67 | self.var_order.heap.push(id); 68 | self.var_order.idxs.push(id); 69 | } 70 | fn update_heap(&mut self, v: VarId) { 71 | debug_assert!(v != 0, "Invalid VarId"); 72 | let start = self.var_order.idxs[v]; 73 | if self.var_order.contains(v) { 74 | self.percolate_up(start); 75 | } 76 | } 77 | fn insert_heap(&mut self, vi: VarId) { 78 | let i = self.var_order.insert(vi); 79 | self.percolate_up(i as u32); 80 | } 81 | fn get_heap_root(&mut self) -> VarId { 82 | #[cfg(feature = "trail_saving")] 83 | if self.var_order.is_empty() { 84 | self.clear_saved_trail(); 85 | } 86 | let vs = self.var_order.get_root(); 87 | if 1 < self.var_order.len() { 88 | self.percolate_down(1); 89 | } 90 | vs 91 | } 92 | fn percolate_up(&mut self, start: u32) { 93 | let mut q = start; 94 | let vq = self.var_order.heap[q as usize]; 95 | debug_assert!(0 < vq, "size of heap is too small"); 96 | let aq = self.activity(vq as usize); 97 | loop { 98 | let p = q / 2; 99 | if p == 0 { 100 | self.var_order.heap[q as usize] = vq; 101 | debug_assert!(vq != 0, "Invalid index in percolate_up"); 102 | self.var_order.idxs[vq as usize] = q; 103 | return; 104 | } else { 105 | let vp = self.var_order.heap[p as usize]; 106 | let ap = self.activity(vp as usize); 107 | if ap < aq { 108 | // move down the current parent, and make it empty 109 | self.var_order.heap[q as usize] = vp; 110 | debug_assert!(vq != 0, "Invalid index in percolate_up"); 111 | self.var_order.idxs[vp as usize] = q; 112 | q = p; 113 | } else { 114 | self.var_order.heap[q as usize] = vq; 115 | debug_assert!(vq != 0, "Invalid index in percolate_up"); 116 | self.var_order.idxs[vq as usize] = q; 117 | return; 118 | } 119 | } 120 | } 121 | } 122 | fn percolate_down(&mut self, start: u32) { 123 | let n = self.var_order.len(); 124 | let mut i = start; 125 | let vi = self.var_order.heap[i as usize]; 126 | let ai = self.activity(vi as usize); 127 | loop { 128 | let l = 2 * i; // left 129 | if l < n as u32 { 130 | let vl = self.var_order.heap[l as usize]; 131 | let al = self.activity(vl as usize); 132 | let r = l + 1; // right 133 | let (target, vc, ac) = if r < (n as u32) 134 | && al < self.activity(self.var_order.heap[r as usize] as usize) 135 | { 136 | let vr = self.var_order.heap[r as usize]; 137 | (r, vr, self.activity(vr as usize)) 138 | } else { 139 | (l, vl, al) 140 | }; 141 | if ai < ac { 142 | self.var_order.heap[i as usize] = vc; 143 | self.var_order.idxs[vc as usize] = i; 144 | i = target; 145 | } else { 146 | self.var_order.heap[i as usize] = vi; 147 | debug_assert!(vi != 0, "invalid index"); 148 | self.var_order.idxs[vi as usize] = i; 149 | return; 150 | } 151 | } else { 152 | self.var_order.heap[i as usize] = vi; 153 | debug_assert!(vi != 0, "invalid index"); 154 | self.var_order.idxs[vi as usize] = i; 155 | return; 156 | } 157 | } 158 | } 159 | fn remove_from_heap(&mut self, vi: VarId) { 160 | if let Some(i) = self.var_order.remove(vi) { 161 | self.percolate_down(i as u32); 162 | } 163 | } 164 | } 165 | 166 | trait VarOrderIF { 167 | fn clear(&mut self); 168 | fn contains(&self, v: VarId) -> bool; 169 | fn get_root(&mut self) -> VarId; 170 | fn len(&self) -> usize; 171 | fn insert(&mut self, vi: VarId) -> usize; 172 | fn is_empty(&self) -> bool; 173 | fn remove(&mut self, vi: VarId) -> Option; 174 | } 175 | 176 | impl VarOrderIF for VarIdHeap { 177 | fn clear(&mut self) { 178 | for i in 0..self.idxs.len() { 179 | self.idxs[i] = i as u32; 180 | self.heap[i] = i as u32; 181 | } 182 | } 183 | fn contains(&self, v: VarId) -> bool { 184 | self.idxs[v] <= self.idxs[0] 185 | } 186 | fn get_root(&mut self) -> VarId { 187 | let s: usize = 1; 188 | let vs = self.heap[s]; 189 | let n = self.idxs[0]; 190 | let vn = self.heap[n as usize]; 191 | debug_assert!(vn != 0, "Invalid VarId for heap: vn {vn}, n {n}"); 192 | debug_assert!(vs != 0, "Invalid VarId for heap: vs {vs}, n {n}"); 193 | self.heap.swap(n as usize, s); 194 | self.idxs.swap(vn as usize, vs as usize); 195 | self.idxs[0] -= 1; 196 | vs as VarId 197 | } 198 | fn len(&self) -> usize { 199 | self.idxs[0] as usize 200 | } 201 | #[allow(clippy::unnecessary_cast)] 202 | fn insert(&mut self, vi: VarId) -> usize { 203 | if self.contains(vi) { 204 | return self.idxs[vi as usize] as usize; 205 | } 206 | let i = self.idxs[vi]; 207 | let n = self.idxs[0] + 1; 208 | let vn = self.heap[n as usize]; 209 | self.heap.swap(i as usize, n as usize); 210 | self.idxs.swap(vi, vn as usize); 211 | self.idxs[0] = n; 212 | n as usize 213 | } 214 | fn is_empty(&self) -> bool { 215 | self.idxs[0] == 0 216 | } 217 | fn remove(&mut self, vi: VarId) -> Option { 218 | let i = self.idxs[vi]; 219 | let n = self.idxs[0]; 220 | debug_assert_ne!(i, 0); 221 | if n < i { 222 | return None; 223 | } 224 | let vn = self.heap[n as usize]; 225 | self.heap.swap(n as usize, i as usize); 226 | self.idxs.swap(vn as usize, vi); 227 | self.idxs[0] -= 1; 228 | if 1 < self.idxs[0] { 229 | return Some(i as usize); 230 | } 231 | None 232 | } 233 | } 234 | 235 | impl VarIdHeap { 236 | #[allow(dead_code)] 237 | fn peek(&self) -> VarId { 238 | self.heap[1] as VarId 239 | } 240 | #[allow(dead_code)] 241 | fn check(&self, s: &str) { 242 | let h = &mut self.heap.clone()[1..]; 243 | let d = &mut self.idxs.clone()[1..]; 244 | h.sort_unstable(); 245 | d.sort_unstable(); 246 | for i in 0..h.len() { 247 | if h[i] != i as u32 + 1 { 248 | panic!("heap {} {} {:?}", i, h[i], h); 249 | } 250 | if d[i] != i as u32 + 1 { 251 | panic!("idxs {} {} {:?}", i, d[i], d); 252 | } 253 | } 254 | println!(" - pass var_order test at {s}"); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/bin/dmcr.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(feature = "no_IO"))] 2 | // A simple DIMACS Model Checker in Rust, which can't be compiled with feature 'no_IO' 3 | #![allow(unused_imports)] 4 | use { 5 | splr::{Config, SatSolverIF, Solver, ValidateIF}, 6 | std::{ 7 | env, 8 | fs::File, 9 | io::{stdin, BufRead, BufReader, Result}, 10 | path::{Path, PathBuf}, 11 | }, 12 | }; 13 | 14 | const ABOUT: &str = "DIMACS-format Model Checker in Rust"; 15 | const RED: &str = "\x1B[001m\x1B[031m"; 16 | const GREEN: &str = "\x1B[001m\x1B[032m"; 17 | const BLUE: &str = "\x1B[001m\x1B[034m"; 18 | const RESET: &str = "\x1B[000m"; 19 | 20 | struct TargetOpts { 21 | /// an assign file generated by slpr 22 | assign: Option, 23 | /// a CNF file 24 | problem: std::path::PathBuf, 25 | /// disable colorized output 26 | no_color: bool, 27 | } 28 | 29 | impl Default for TargetOpts { 30 | fn default() -> Self { 31 | TargetOpts { 32 | assign: None, 33 | problem: PathBuf::new(), 34 | no_color: false, 35 | } 36 | } 37 | } 38 | 39 | impl TargetOpts { 40 | pub fn inject_from_args(&mut self) { 41 | let mut help = false; 42 | let mut version = false; 43 | if let Some(ref cnf) = std::env::args().last() { 44 | let path = PathBuf::from(cnf.clone()); 45 | if path.exists() { 46 | self.problem = path; 47 | } 48 | } 49 | let mut iter = std::env::args().skip(1); 50 | while let Some(arg) = iter.next() { 51 | if let Some(name) = arg.strip_prefix("--") { 52 | let flags = ["no-color", "help", "version"]; 53 | let options_path = ["assign"]; 54 | if flags.contains(&name) { 55 | match name { 56 | "no-color" => self.no_color = true, 57 | "help" => help = true, 58 | "version" => version = true, 59 | _ => panic!("invalid flag: {name}"), 60 | } 61 | } else if options_path.contains(&name) { 62 | if options_path.contains(&name) { 63 | if let Some(val) = iter.next() { 64 | match name { 65 | "assign" => self.assign = Some(PathBuf::from(val)), 66 | _ => panic!("not"), 67 | } 68 | } else { 69 | panic!("no argument for {name}"); 70 | } 71 | } else { 72 | panic!("invalid argument: {name}"); 73 | } 74 | } 75 | } else if let Some(name) = arg.strip_prefix('-') { 76 | let flags = ["C", "h", "V"]; 77 | let options = ["a"]; 78 | if flags.contains(&name) { 79 | match name { 80 | "C" => self.no_color = true, 81 | "h" => help = true, 82 | "V" => version = true, 83 | _ => panic!(), 84 | } 85 | } else if options.contains(&name) { 86 | if let Some(val) = iter.next() { 87 | match name { 88 | "a" => self.assign = Some(PathBuf::from(val)), 89 | _ => panic!("invalid option: {name}"), 90 | } 91 | } else { 92 | panic!("no argument for {name}"); 93 | } 94 | } 95 | } else if !self.problem.exists() || self.problem.to_string_lossy() != arg { 96 | panic!("invalid argument: {arg}"); 97 | } 98 | } 99 | if help { 100 | println!("{ABOUT}\n{HELP_MESSAGE}"); 101 | std::process::exit(0); 102 | } 103 | if version { 104 | println!("{}", env!("CARGO_PKG_VERSION")); 105 | std::process::exit(0); 106 | } 107 | } 108 | } 109 | 110 | const HELP_MESSAGE: &str = " 111 | USAGE: 112 | dmcr [FLAGS] [OPTIONS] 113 | FLAGS: 114 | -h, --help Prints help information 115 | -C, --no-color disable colorized output 116 | -V, --version Prints version information 117 | OPTIONS: 118 | -a, --assign an assign file generated by slpr 119 | ARGS: 120 | a CNF file 121 | "; 122 | 123 | #[allow(clippy::field_reassign_with_default)] 124 | fn main() { 125 | let mut from_file = true; 126 | let mut found = false; 127 | let mut args = TargetOpts::default(); 128 | args.inject_from_args(); 129 | let cnf = args 130 | .problem 131 | .to_str() 132 | .unwrap_or_else(|| panic!("{} does not exist.", args.problem.to_str().unwrap())); 133 | let mut config = Config::default(); 134 | config.cnf_file = args.problem.clone(); 135 | config.quiet_mode = true; 136 | let (red, green, blue) = if args.no_color { 137 | (RESET, RESET, RESET) 138 | } else { 139 | (RED, GREEN, BLUE) 140 | }; 141 | let mut s = Solver::build(&config).expect("failed to load"); 142 | if args.assign.is_none() { 143 | args.assign = Some(PathBuf::from(format!( 144 | "ans_{}", 145 | Path::new(&args.problem) 146 | .file_name() 147 | .unwrap() 148 | .to_string_lossy() 149 | ))); 150 | } 151 | if let Some(f) = &args.assign { 152 | if let Ok(d) = File::open(f.as_path()) { 153 | if let Some(vec) = read_assignment(&mut BufReader::new(d), cnf, &args.assign) { 154 | if s.inject_assignment(&vec).is_err() { 155 | println!( 156 | "{}{} seems an unsat problem but no proof.{}", 157 | blue, 158 | args.problem.to_str().unwrap(), 159 | RESET 160 | ); 161 | return; 162 | } 163 | } else { 164 | return; 165 | } 166 | found = true; 167 | } 168 | } 169 | if !found { 170 | if let Some(vec) = read_assignment(&mut BufReader::new(stdin()), cnf, &args.assign) { 171 | if s.inject_assignment(&vec).is_err() { 172 | println!( 173 | "{}{} seems an unsat problem but no proof.{}", 174 | blue, 175 | args.problem.to_str().unwrap(), 176 | RESET, 177 | ); 178 | return; 179 | } 180 | found = true; 181 | from_file = false; 182 | } else { 183 | return; 184 | } 185 | } 186 | if !found { 187 | println!("There's no assign file."); 188 | return; 189 | } 190 | match s.validate() { 191 | Some(v) => println!( 192 | "{}An invalid assignment set for {}{} due to {:?}.", 193 | red, 194 | args.problem.to_str().unwrap(), 195 | RESET, 196 | v, 197 | ), 198 | None if from_file => println!( 199 | "{}A valid assignment set for {}{} is found in {}", 200 | green, 201 | &args.problem.to_str().unwrap(), 202 | RESET, 203 | &args.assign.unwrap().to_str().unwrap(), 204 | ), 205 | None => println!( 206 | "{}A valid assignment set for {}.{}", 207 | green, 208 | &args.problem.to_str().unwrap(), 209 | RESET, 210 | ), 211 | } 212 | } 213 | 214 | fn read_assignment(rs: &mut dyn BufRead, cnf: &str, assign: &Option) -> Option> { 215 | let mut buf = String::new(); 216 | loop { 217 | match rs.read_line(&mut buf) { 218 | Ok(0) => return Some(Vec::new()), 219 | Ok(_) => { 220 | if buf.starts_with('c') { 221 | buf.clear(); 222 | continue; 223 | } 224 | if buf.starts_with("s ") { 225 | if buf.starts_with("s SATISFIABLE") { 226 | buf.clear(); 227 | continue; 228 | } else if buf.starts_with("s UNSATISFIABLE") { 229 | println!("{cnf} seems an unsatisfiable problem. I can't handle it."); 230 | return None; 231 | } else if let Some(asg) = assign { 232 | println!("{} seems an illegal format file.", asg.to_str().unwrap(),); 233 | return None; 234 | } else { 235 | buf.clear(); 236 | } 237 | } 238 | if let Some(stripped) = buf.strip_prefix("v ") { 239 | let mut v: Vec = Vec::new(); 240 | for s in stripped.split_whitespace() { 241 | match s.parse::() { 242 | Ok(0) => break, 243 | Ok(x) => v.push(x), 244 | Err(e) => panic!("{e} by {s}"), 245 | } 246 | } 247 | return Some(v); 248 | } 249 | panic!("Failed to parse here: {buf}"); 250 | } 251 | Err(e) => panic!("{}", e), 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/cnf/mod.rs: -------------------------------------------------------------------------------- 1 | // pub mod cnf; 2 | // pub use self::cnf::*; 3 | use std::{ 4 | collections::HashSet, 5 | fs::File, 6 | io::{BufRead, BufReader}, 7 | path::Path, 8 | }; 9 | 10 | const TOO_MANY_CLAUSES: usize = 100_000; 11 | 12 | pub type Clause = Vec; 13 | 14 | #[derive(Debug, Eq, PartialEq)] 15 | pub enum CNFOperationError { 16 | AddingClauseExists, 17 | AddingEmptyClause, 18 | AddignConflictingAssingment, 19 | ReadingCNFFile, 20 | ParsingCNF, 21 | CreatingCNFFile, 22 | WritingCNFFile, 23 | UnknownError(String), 24 | } 25 | 26 | #[derive(Debug, Default, Eq, PartialEq)] 27 | pub struct CNF { 28 | num_vars: u32, 29 | assign: HashSet, 30 | clauses: Vec, 31 | cls_map: HashSet>, 32 | no_check_uniqueness: bool, 33 | } 34 | 35 | impl std::fmt::Display for CNF { 36 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 37 | write!( 38 | f, 39 | "CNF({} vars, {} clauses)", 40 | self.num_vars(), 41 | self.num_clauses() 42 | ) 43 | } 44 | } 45 | 46 | pub trait CnfIf: Sized { 47 | type Error; 48 | // Add a `Clause` and returns: 49 | // - `Some(self)`: if add it successfully 50 | // - `None`: if the clause is in it already 51 | fn add_clause>(&mut self, clause: C) -> Result<&mut CNF, Self::Error>; 52 | fn from_vec_i32>(clauses: V) -> Result; 53 | fn load(file: &Path) -> Result; 54 | fn num_vars(&self) -> u32; 55 | fn num_clauses(&self) -> usize; 56 | fn save(&self, file: &Path) -> Result<(), Self::Error>; 57 | fn dump_to_string(&self) -> String; 58 | } 59 | 60 | impl CnfIf for CNF { 61 | type Error = CNFOperationError; 62 | fn add_clause>(&mut self, clause: C) -> Result<&mut CNF, Self::Error> { 63 | let c = clause.as_ref().clone(); 64 | let mut cc = c.clone(); 65 | cc.sort_unstable(); 66 | if !self.no_check_uniqueness && TOO_MANY_CLAUSES < self.clauses.len() { 67 | self.no_check_uniqueness = true; 68 | self.cls_map.clear(); 69 | } 70 | if !self.no_check_uniqueness && self.cls_map.iter().any(|ref_c| *ref_c == cc) { 71 | return Err(CNFOperationError::AddingClauseExists); 72 | } 73 | // assert!(!c.is_empty()); 74 | if c.is_empty() { 75 | return Err(CNFOperationError::AddingEmptyClause); 76 | } 77 | self.num_vars = self 78 | .num_vars 79 | .max(c.iter().map(|v| v.unsigned_abs()).max().unwrap()); 80 | if !self.no_check_uniqueness { 81 | self.cls_map.insert(cc); 82 | } 83 | self.clauses.push(c); 84 | Ok(self) 85 | } 86 | fn from_vec_i32>(clauses: V) -> Result { 87 | let mut cnf: CNF = CNF::default(); 88 | let mut max_var: u32 = 0; 89 | for ref_c in clauses.as_ref().iter() { 90 | match ref_c.len() { 91 | 0 => { 92 | return Err(CNFOperationError::AddingEmptyClause); 93 | } 94 | 1 => { 95 | let l: i32 = ref_c[0]; 96 | if cnf.assign.contains(&-l) { 97 | return Err(CNFOperationError::AddignConflictingAssingment); 98 | } 99 | cnf.assign.insert(l); 100 | } 101 | _ => { 102 | let mut cc = ref_c.clone(); 103 | cc.sort_unstable(); 104 | cnf.cls_map.insert(cc); 105 | cnf.clauses.push(ref_c.clone()); 106 | } 107 | } 108 | max_var = max_var.max(ref_c.iter().map(|v| v.unsigned_abs()).max().unwrap()); 109 | } 110 | cnf.num_vars = max_var; 111 | Ok(cnf) 112 | } 113 | fn load(path: &Path) -> Result { 114 | let fs = File::open(path).map_err(|_| CNFOperationError::ReadingCNFFile)?; 115 | let mut reader = BufReader::new(fs); 116 | let mut buf = String::new(); 117 | let mut nv: u32 = 0; 118 | let mut nc: usize = 0; 119 | let mut num_clause: usize = 0; 120 | let mut found_valid_header = false; 121 | let mut clause_extists_already = false; 122 | let mut cnf = CNF::default(); 123 | loop { 124 | buf.clear(); 125 | match reader.read_line(&mut buf) { 126 | Ok(0) => break, 127 | Ok(_) if buf == "\n" => (), 128 | Ok(_) if buf.starts_with('c') => (), 129 | Ok(_) if found_valid_header => { 130 | let mut vec: Vec = Vec::new(); 131 | for seg in buf.split(' ') { 132 | if let Ok(l) = seg.parse::() { 133 | if l == 0 { 134 | break; 135 | } 136 | vec.push(l); 137 | } 138 | } 139 | debug_assert!(!vec.is_empty()); 140 | if let Err(e) = cnf.add_clause(vec) { 141 | if e == CNFOperationError::AddingClauseExists { 142 | clause_extists_already = true; 143 | } else { 144 | return Err(e); 145 | } 146 | } else { 147 | num_clause += 1; 148 | } 149 | } 150 | Ok(_) => { 151 | let mut iter = buf.split_whitespace(); 152 | if iter.next() == Some("p") && iter.next() == Some("cnf") { 153 | if let Some(v) = iter.next().map(|s| s.parse::().ok().unwrap()) { 154 | if let Some(c) = iter.next().map(|s| s.parse::().ok().unwrap()) { 155 | nv = v as u32; 156 | nc = c; 157 | found_valid_header = true; 158 | cnf.no_check_uniqueness = TOO_MANY_CLAUSES < nc; 159 | } 160 | } 161 | } 162 | } 163 | Err(e) => { 164 | return Err(CNFOperationError::UnknownError(format!("IOError ({e:?})"))); 165 | } 166 | } 167 | } 168 | if !found_valid_header { 169 | return Err(CNFOperationError::ParsingCNF); 170 | } 171 | if cnf.num_vars() != nv { 172 | println!("Warning: there are less variables than its declaration."); 173 | } 174 | if clause_extists_already { 175 | assert_eq!(cnf.num_clauses(), num_clause); 176 | println!("Warning: there are less clauses than its declaration."); 177 | } else if cnf.num_clauses() < TOO_MANY_CLAUSES { 178 | assert_eq!(cnf.num_clauses(), nc); 179 | } 180 | Ok(cnf) 181 | } 182 | fn num_vars(&self) -> u32 { 183 | self.num_vars 184 | } 185 | fn num_clauses(&self) -> usize { 186 | self.clauses.len() 187 | } 188 | fn save(&self, file: &Path) -> Result<(), Self::Error> { 189 | use std::io::Write; 190 | if let Ok(f) = File::create(file) { 191 | let mut buf = std::io::BufWriter::new(f); 192 | buf.write_all(self.dump_to_string().as_bytes()) 193 | .map_err(|_| CNFOperationError::WritingCNFFile) 194 | } else { 195 | Err(CNFOperationError::CreatingCNFFile) 196 | } 197 | } 198 | fn dump_to_string(&self) -> String { 199 | format!( 200 | "p cnf {} {}\n{} 0\n", 201 | self.num_vars, 202 | self.clauses.len(), 203 | self.clauses 204 | .iter() 205 | .map(|cls| cls 206 | .iter() 207 | .map(|ch| format!("{ch}")) 208 | .collect::>() 209 | .join(" ")) 210 | .collect::>() 211 | .join(" 0\n"), 212 | ) 213 | } 214 | } 215 | 216 | #[cfg(test)] 217 | mod tests { 218 | use super::*; 219 | use std::path::Path; 220 | 221 | #[test] 222 | fn it_works() { 223 | let build = CNF::from_vec_i32(vec![]); 224 | assert!(build.is_ok()); 225 | let mut cnf = build.unwrap(); 226 | assert!(cnf.add_clause(vec![1, 3, 2]).is_ok()); 227 | assert_eq!(cnf.num_clauses(), 1); 228 | assert_eq!(cnf.num_vars(), 3); 229 | assert!(cnf.add_clause(vec![-1, -4, 3]).is_ok()); 230 | assert_eq!(cnf.num_clauses(), 2); 231 | assert_eq!(cnf.num_vars(), 4); 232 | let output = cnf.dump_to_string(); 233 | let mut line = output.lines(); 234 | assert_eq!(line.next(), Some("p cnf 4 2")); 235 | assert_eq!(line.next(), Some("1 3 2 0")); 236 | assert_eq!(line.next(), Some("-1 -4 3 0")); 237 | } 238 | #[test] 239 | fn test_load_uf8() { 240 | let build = CNF::load(Path::new("cnfs/uf8.cnf")); 241 | dbg!(build.is_ok()); 242 | let cnf = build.unwrap(); 243 | assert_eq!(cnf.num_clauses(), 13); 244 | assert_eq!(cnf.num_vars(), 8); 245 | } 246 | #[test] 247 | fn test_load_sample() { 248 | let build = CNF::load(Path::new("cnfs/sample.cnf")); 249 | dbg!(build.is_ok()); 250 | let cnf = build.unwrap(); 251 | assert_eq!(cnf.num_clauses(), 1065); 252 | assert_eq!(cnf.num_vars(), 250); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/cdb/clause.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::{assign::AssignIF, types::*}, 3 | std::{ 4 | fmt, 5 | ops::{Index, IndexMut, Range, RangeFrom}, 6 | slice::Iter, 7 | }, 8 | }; 9 | 10 | /// A representation of 'clause' 11 | #[derive(Clone, Debug, Eq, PartialEq, PartialOrd)] 12 | pub struct Clause { 13 | /// The literals in a clause. 14 | pub(super) lits: Vec, 15 | /// Flags (8 bits) 16 | pub(crate) flags: FlagClause, 17 | /// A static clause evaluation criterion like LBD, NDD, or something. 18 | pub rank: u16, 19 | /// A record of the rank at previos stage. 20 | pub rank_old: u16, 21 | /// the index from which `propagate` starts searching an un-falsified literal. 22 | /// Since it's just a hint, we don't need u32 or usize. 23 | pub search_from: u16, 24 | 25 | #[cfg(any(feature = "boundary_check", feature = "clause_rewarding"))] 26 | /// the number of conflicts at which this clause was used in `conflict_analyze` 27 | timestamp: usize, 28 | 29 | #[cfg(feature = "clause_rewarding")] 30 | /// A dynamic clause evaluation criterion based on the number of references. 31 | reward: f64, 32 | 33 | #[cfg(feature = "boundary_check")] 34 | pub birth: usize, 35 | #[cfg(feature = "boundary_check")] 36 | pub moved_at: Propagate, 37 | } 38 | 39 | /// API for Clause, providing literal accessors. 40 | pub trait ClauseIF { 41 | /// return true if it contains no literals; a clause after unit propagation. 42 | fn is_empty(&self) -> bool; 43 | /// return true if it contains no literals; a clause after unit propagation. 44 | fn is_dead(&self) -> bool; 45 | /// return 1st watch 46 | fn lit0(&self) -> Lit; 47 | /// return 2nd watch 48 | fn lit1(&self) -> Lit; 49 | /// return `true` if the clause contains the literal 50 | fn contains(&self, lit: Lit) -> bool; 51 | /// check clause satisfiability 52 | fn is_satisfied_under(&self, asg: &impl AssignIF) -> bool; 53 | /// return an iterator over its literals. 54 | fn iter(&self) -> Iter<'_, Lit>; 55 | /// return the number of literals. 56 | fn len(&self) -> usize; 57 | 58 | #[cfg(feature = "boundary_check")] 59 | /// return timestamp. 60 | fn timestamp(&self) -> usize; 61 | #[cfg(feature = "boundary_check")] 62 | fn set_birth(&mut self, time: usize); 63 | } 64 | 65 | impl Default for Clause { 66 | fn default() -> Clause { 67 | Clause { 68 | lits: vec![], 69 | flags: FlagClause::empty(), 70 | rank: 0, 71 | rank_old: 0, 72 | search_from: 2, 73 | 74 | #[cfg(any(feature = "boundary_check", feature = "clause_rewarding"))] 75 | timestamp: 0, 76 | 77 | #[cfg(feature = "clause_rewarding")] 78 | reward: 0.0, 79 | 80 | #[cfg(feature = "boundary_check")] 81 | birth: 0, 82 | #[cfg(feature = "boundary_check")] 83 | moved_at: Propagate::None, 84 | } 85 | } 86 | } 87 | 88 | impl Index for Clause { 89 | type Output = Lit; 90 | #[inline] 91 | fn index(&self, i: usize) -> &Lit { 92 | #[cfg(feature = "unsafe_access")] 93 | unsafe { 94 | self.lits.get_unchecked(i) 95 | } 96 | #[cfg(not(feature = "unsafe_access"))] 97 | &self.lits[i] 98 | } 99 | } 100 | 101 | impl IndexMut for Clause { 102 | #[inline] 103 | fn index_mut(&mut self, i: usize) -> &mut Lit { 104 | #[cfg(feature = "unsafe_access")] 105 | unsafe { 106 | self.lits.get_unchecked_mut(i) 107 | } 108 | #[cfg(not(feature = "unsafe_access"))] 109 | &mut self.lits[i] 110 | } 111 | } 112 | 113 | impl Index> for Clause { 114 | type Output = [Lit]; 115 | #[inline] 116 | fn index(&self, r: Range) -> &[Lit] { 117 | #[cfg(feature = "unsafe_access")] 118 | unsafe { 119 | self.lits.get_unchecked(r) 120 | } 121 | #[cfg(not(feature = "unsafe_access"))] 122 | &self.lits[r] 123 | } 124 | } 125 | 126 | impl Index> for Clause { 127 | type Output = [Lit]; 128 | #[inline] 129 | fn index(&self, r: RangeFrom) -> &[Lit] { 130 | #[cfg(feature = "unsafe_access")] 131 | unsafe { 132 | self.lits.get_unchecked(r) 133 | } 134 | #[cfg(not(feature = "unsafe_access"))] 135 | &self.lits[r] 136 | } 137 | } 138 | 139 | impl IndexMut> for Clause { 140 | #[inline] 141 | fn index_mut(&mut self, r: Range) -> &mut [Lit] { 142 | #[cfg(feature = "unsafe_access")] 143 | unsafe { 144 | self.lits.get_unchecked_mut(r) 145 | } 146 | #[cfg(not(feature = "unsafe_access"))] 147 | &mut self.lits[r] 148 | } 149 | } 150 | 151 | impl IndexMut> for Clause { 152 | #[inline] 153 | fn index_mut(&mut self, r: RangeFrom) -> &mut [Lit] { 154 | #[cfg(feature = "unsafe_access")] 155 | unsafe { 156 | self.lits.get_unchecked_mut(r) 157 | } 158 | #[cfg(not(feature = "unsafe_access"))] 159 | &mut self.lits[r] 160 | } 161 | } 162 | 163 | impl<'a> IntoIterator for &'a Clause { 164 | type Item = &'a Lit; 165 | type IntoIter = Iter<'a, Lit>; 166 | fn into_iter(self) -> Self::IntoIter { 167 | self.lits.iter() 168 | } 169 | } 170 | 171 | impl<'a> IntoIterator for &'a mut Clause { 172 | type Item = &'a Lit; 173 | type IntoIter = Iter<'a, Lit>; 174 | fn into_iter(self) -> Self::IntoIter { 175 | self.lits.iter() 176 | } 177 | } 178 | 179 | impl From<&Clause> for Vec { 180 | fn from(c: &Clause) -> Vec { 181 | c.lits.iter().map(|l| i32::from(*l)).collect::>() 182 | } 183 | } 184 | 185 | impl ClauseIF for Clause { 186 | fn is_empty(&self) -> bool { 187 | self.lits.is_empty() 188 | } 189 | fn is_dead(&self) -> bool { 190 | self.lits.is_empty() 191 | } 192 | fn iter(&self) -> Iter<'_, Lit> { 193 | self.lits.iter() 194 | } 195 | #[inline] 196 | fn lit0(&self) -> Lit { 197 | #[cfg(feature = "unsafe_access")] 198 | unsafe { 199 | *self.lits.get_unchecked(0) 200 | } 201 | #[cfg(not(feature = "unsafe_access"))] 202 | self.lits[0] 203 | } 204 | #[inline] 205 | fn lit1(&self) -> Lit { 206 | #[cfg(feature = "unsafe_access")] 207 | unsafe { 208 | *self.lits.get_unchecked(1) 209 | } 210 | #[cfg(not(feature = "unsafe_access"))] 211 | self.lits[1] 212 | } 213 | fn contains(&self, lit: Lit) -> bool { 214 | self.lits.contains(&lit) 215 | } 216 | fn is_satisfied_under(&self, asg: &impl AssignIF) -> bool { 217 | for l in self.lits.iter() { 218 | if asg.assigned(*l) == Some(true) { 219 | return true; 220 | } 221 | } 222 | false 223 | } 224 | fn len(&self) -> usize { 225 | self.lits.len() 226 | } 227 | 228 | #[cfg(feature = "boundary_check")] 229 | /// return timestamp. 230 | fn timestamp(&self) -> usize { 231 | self.timestamp 232 | } 233 | #[cfg(feature = "boundary_check")] 234 | fn set_birth(&mut self, time: usize) { 235 | self.birth = time; 236 | } 237 | } 238 | 239 | impl FlagIF for Clause { 240 | type FlagType = FlagClause; 241 | #[inline] 242 | fn is(&self, flag: Self::FlagType) -> bool { 243 | self.flags.contains(flag) 244 | } 245 | #[inline] 246 | fn set(&mut self, f: Self::FlagType, b: bool) { 247 | self.flags.set(f, b); 248 | } 249 | #[inline] 250 | fn turn_off(&mut self, flag: Self::FlagType) { 251 | self.flags.remove(flag); 252 | } 253 | #[inline] 254 | fn turn_on(&mut self, flag: Self::FlagType) { 255 | self.flags.insert(flag); 256 | } 257 | #[inline] 258 | fn toggle(&mut self, flag: Self::FlagType) { 259 | self.flags.toggle(flag); 260 | } 261 | } 262 | 263 | impl fmt::Display for Clause { 264 | #[cfg(feature = "boundary_check")] 265 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 266 | let st = |flag, mes| if self.is(flag) { mes } else { "" }; 267 | write!( 268 | f, 269 | "{{{:?}b{}{}{}}}", 270 | i32s(&self.lits), 271 | self.birth, 272 | st(FlagClause::LEARNT, ", learnt"), 273 | st(FlagClause::ENQUEUED, ", enqueued"), 274 | ) 275 | } 276 | #[cfg(not(feature = "boundary_check"))] 277 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 278 | let st = |flag, mes| if self.is(flag) { mes } else { "" }; 279 | write!( 280 | f, 281 | "{{{:?}{}{}}}", 282 | i32s(&self.lits), 283 | st(FlagClause::LEARNT, ", learnt"), 284 | st(FlagClause::ENQUEUED, ", enqueued"), 285 | ) 286 | } 287 | } 288 | 289 | impl Clause { 290 | /// update rank field with the present LBD. 291 | // If it's big enough, skip the loop. 292 | pub fn update_lbd(&mut self, asg: &impl AssignIF, lbd_temp: &mut [usize]) -> usize { 293 | if 8192 <= self.lits.len() { 294 | self.rank = u16::MAX; 295 | return u16::MAX as usize; 296 | } 297 | let key: usize = lbd_temp[0] + 1; 298 | lbd_temp[0] = key; 299 | let mut cnt = 0; 300 | for l in &self.lits { 301 | let lv = asg.level(l.vi()); 302 | if lv == 0 { 303 | continue; 304 | } 305 | let p = &mut lbd_temp[lv as usize]; 306 | if *p != key { 307 | *p = key; 308 | cnt += 1; 309 | } 310 | } 311 | self.rank = cnt; 312 | cnt as usize 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/processor/heap.rs: -------------------------------------------------------------------------------- 1 | /// Module `eliminator` implements clause subsumption and var elimination. 2 | use { 3 | crate::{assign::AssignIF, types::*}, 4 | std::fmt, 5 | }; 6 | 7 | pub trait VarOrderIF { 8 | fn clear(&mut self, asg: &mut impl AssignIF); 9 | fn len(&self) -> usize; 10 | fn insert(&mut self, occur: &[LitOccurs], vi: VarId, upward: bool); 11 | fn is_empty(&self) -> bool; 12 | fn select_var(&mut self, occur: &[LitOccurs], asg: &impl AssignIF) -> Option; 13 | #[allow(dead_code)] 14 | fn rebuild(&mut self, asg: &impl AssignIF, occur: &[LitOccurs]); 15 | } 16 | 17 | /// Mapping from Literal to Clauses. 18 | #[derive(Clone, Debug, Default)] 19 | pub struct LitOccurs { 20 | pub aborted: bool, 21 | pub pos_occurs: Vec, 22 | pub neg_occurs: Vec, 23 | } 24 | 25 | impl LitOccurs { 26 | /// return a new vector of $n$ `LitOccurs`s. 27 | pub fn new(n: usize) -> Vec { 28 | let mut vec = Vec::with_capacity(n + 1); 29 | for _ in 0..=n { 30 | vec.push(LitOccurs::default()); 31 | } 32 | vec 33 | } 34 | pub fn clear(&mut self) { 35 | self.aborted = false; 36 | self.pos_occurs.clear(); 37 | self.neg_occurs.clear(); 38 | } 39 | pub fn activity(&self) -> usize { 40 | if self.aborted { 41 | usize::MAX 42 | } else { 43 | self.pos_occurs.len().min(self.neg_occurs.len()) 44 | } 45 | } 46 | pub fn is_empty(&self) -> bool { 47 | self.pos_occurs.is_empty() && self.neg_occurs.is_empty() 48 | } 49 | pub fn len(&self) -> usize { 50 | self.pos_occurs.len() + self.neg_occurs.len() 51 | } 52 | } 53 | 54 | /// Var heap structure based on the number of occurrences 55 | /// # Note 56 | /// - both fields has a fixed length. Don't use push and pop. 57 | /// - `idxs[0]` contains the number of alive elements 58 | /// `indx` is positions. So the unused field 0 can hold the last position as a special case. 59 | #[derive(Clone, Debug)] 60 | pub struct VarOccHeap { 61 | pub heap: Vec, // order : usize -> VarId::from 62 | pub idxs: Vec, // VarId : -> order : usize::from 63 | } 64 | 65 | impl VarOccHeap { 66 | pub fn new(n: usize, init: usize) -> Self { 67 | let mut heap = Vec::with_capacity(n + 1); 68 | let mut idxs = Vec::with_capacity(n + 1); 69 | heap.push(0); 70 | idxs.push(n as u32); 71 | for i in 1..=n { 72 | heap.push(i as u32); 73 | idxs.push(i as u32); 74 | } 75 | idxs[0] = init as u32; 76 | VarOccHeap { heap, idxs } 77 | } 78 | } 79 | 80 | impl VarOrderIF for VarOccHeap { 81 | fn insert(&mut self, occur: &[LitOccurs], vi: VarId, upward: bool) { 82 | debug_assert!(vi < self.heap.len()); 83 | if self.contains(vi) { 84 | let i = self.idxs[vi]; 85 | if upward { 86 | self.percolate_up(occur, i); 87 | } else { 88 | self.percolate_down(occur, i); 89 | } 90 | return; 91 | } 92 | let i = self.idxs[vi]; 93 | let n = self.idxs[0] + 1; 94 | let vn = self.heap[n as usize]; 95 | self.heap.swap(i as usize, n as usize); 96 | self.idxs.swap(vi, vn as usize); 97 | debug_assert!((n as usize) < self.heap.len()); 98 | self.idxs[0] = n; 99 | self.percolate_up(occur, n); 100 | } 101 | fn clear(&mut self, asg: &mut impl AssignIF) { 102 | for v in &mut self.heap[0..self.idxs[0] as usize] { 103 | asg.var_mut(*v as usize).turn_off(FlagVar::ENQUEUED); 104 | } 105 | self.reset() 106 | } 107 | fn len(&self) -> usize { 108 | self.idxs[0] as usize 109 | } 110 | fn is_empty(&self) -> bool { 111 | self.idxs[0] == 0 112 | } 113 | fn select_var(&mut self, occur: &[LitOccurs], asg: &impl AssignIF) -> Option { 114 | loop { 115 | let vi = self.get_root(occur); 116 | if vi == 0 { 117 | return None; 118 | } 119 | if !asg.var(vi).is(FlagVar::ELIMINATED) { 120 | return Some(vi); 121 | } 122 | } 123 | } 124 | fn rebuild(&mut self, asg: &impl AssignIF, occur: &[LitOccurs]) { 125 | self.reset(); 126 | for (vi, v) in asg.var_iter().enumerate().skip(1) { 127 | if asg.assign(vi).is_none() && !v.is(FlagVar::ELIMINATED) { 128 | self.insert(occur, vi, true); 129 | } 130 | } 131 | } 132 | } 133 | 134 | impl VarOccHeap { 135 | fn contains(&self, v: VarId) -> bool { 136 | self.idxs[v] <= self.idxs[0] 137 | } 138 | fn reset(&mut self) { 139 | for i in 0..self.idxs.len() { 140 | self.idxs[i] = i as u32; 141 | self.heap[i] = i as u32; 142 | } 143 | } 144 | fn get_root(&mut self, occur: &[LitOccurs]) -> VarId { 145 | let s = 1; 146 | let vs = self.heap[s]; 147 | let n = self.idxs[0]; 148 | debug_assert!((n as usize) < self.heap.len()); 149 | if n == 0 { 150 | return 0; 151 | } 152 | let vn = self.heap[n as usize]; 153 | debug_assert!(vn != 0, "Invalid VarId for heap"); 154 | debug_assert!(vs != 0, "Invalid VarId for heap"); 155 | self.heap.swap(n as usize, s); 156 | self.idxs.swap(vn as usize, vs as usize); 157 | self.idxs[0] -= 1; 158 | if 1 < self.idxs[0] { 159 | self.percolate_down(occur, 1); 160 | } 161 | vs as usize 162 | } 163 | fn percolate_up(&mut self, occur: &[LitOccurs], start: u32) { 164 | let mut q = start; 165 | let vq = self.heap[q as usize]; 166 | debug_assert!(0 < vq, "size of heap is too small"); 167 | let aq = occur[vq as usize].activity(); 168 | loop { 169 | let p = q / 2; 170 | if p == 0 { 171 | self.heap[q as usize] = vq; 172 | debug_assert!(vq != 0, "Invalid index in percolate_up"); 173 | self.idxs[vq as usize] = q; 174 | return; 175 | } else { 176 | let vp = self.heap[p as usize]; 177 | let ap = occur[vp as usize].activity(); 178 | if ap > aq { 179 | // move down the current parent, and make it empty 180 | self.heap[q as usize] = vp; 181 | debug_assert!(vq != 0, "Invalid index in percolate_up"); 182 | self.idxs[vp as usize] = q; 183 | q = p; 184 | } else { 185 | self.heap[q as usize] = vq; 186 | debug_assert!(vq != 0, "Invalid index in percolate_up"); 187 | self.idxs[vq as usize] = q; 188 | return; 189 | } 190 | } 191 | } 192 | } 193 | fn percolate_down(&mut self, occur: &[LitOccurs], start: u32) { 194 | let n = self.len(); 195 | let mut i = start; 196 | let vi = self.heap[i as usize]; 197 | let ai = occur[vi as usize].activity(); 198 | loop { 199 | let l = 2 * i; // left 200 | if l < (n as u32) { 201 | let vl = self.heap[l as usize]; 202 | let al = occur[vl as usize].activity(); 203 | let r = l + 1; // right 204 | let (target, vc, ac) = 205 | if r < (n as u32) && al > occur[self.heap[r as usize] as usize].activity() { 206 | let vr = self.heap[r as usize]; 207 | (r, vr, occur[vr as usize].activity()) 208 | } else { 209 | (l, vl, al) 210 | }; 211 | if ai > ac { 212 | self.heap[i as usize] = vc; 213 | self.idxs[vc as usize] = i; 214 | i = target; 215 | } else { 216 | self.heap[i as usize] = vi; 217 | debug_assert!(vi != 0, "invalid index"); 218 | self.idxs[vi as usize] = i; 219 | return; 220 | } 221 | } else { 222 | self.heap[i as usize] = vi; 223 | debug_assert!(vi != 0, "invalid index"); 224 | self.idxs[vi as usize] = i; 225 | return; 226 | } 227 | } 228 | } 229 | /* #[allow(dead_code)] 230 | fn peek(&self) -> VarId { 231 | self.heap[1] 232 | } 233 | #[allow(dead_code)] 234 | fn remove(&mut self, occur: &[LitOccurs], vs: VarId) { 235 | let s = self.idxs[vs]; 236 | let n = self.idxs[0]; 237 | if n < s { 238 | return; 239 | } 240 | let vn = self.heap[n]; 241 | self.heap.swap(n, s); 242 | self.idxs.swap(vn, vs); 243 | self.idxs[0] -= 1; 244 | if 1 < self.idxs[0] { 245 | self.percolate_down(occur, 1); 246 | } 247 | } 248 | #[allow(dead_code)] 249 | fn check(&self, s: &str) { 250 | let h = &mut self.heap.clone()[1..]; 251 | let d = &mut self.idxs.clone()[1..]; 252 | h.sort(); 253 | d.sort(); 254 | for i in 0..h.len() { 255 | if h[i] != i + 1 { 256 | panic!("heap {} {} {:?}", i, h[i], h); 257 | } 258 | if d[i] != i + 1 { 259 | panic!("idxs {} {} {:?}", i, d[i], d); 260 | } 261 | } 262 | println!(" - pass var_order test at {}", s); 263 | } */ 264 | } 265 | 266 | impl fmt::Display for VarOccHeap { 267 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 268 | write!( 269 | f, 270 | " - seek pointer - nth -> var: {:?}\n - var -> nth: {:?}", 271 | self.heap, self.idxs, 272 | ) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/assign/mod.rs: -------------------------------------------------------------------------------- 1 | // Module `assign` implements Boolean Constraint Propagation and decision var selection. 2 | // This version can handle Chronological and Non Chronological Backtrack. 3 | 4 | /// Ema 5 | mod ema; 6 | /// Heap 7 | mod heap; 8 | /// Boolean constraint propagation 9 | mod propagate; 10 | /// Var rewarding 11 | #[cfg_attr(feature = "EVSIDS", path = "evsids.rs")] 12 | #[cfg_attr(feature = "LRB_rewarding", path = "learning_rate.rs")] 13 | mod reward; 14 | /// Decision var selection 15 | mod select; 16 | /// assignment management 17 | mod stack; 18 | /// trail saving 19 | mod trail_saving; 20 | /// var struct and its methods 21 | mod var; 22 | 23 | pub use self::{ 24 | propagate::PropagateIF, 25 | property::*, 26 | select::VarSelectIF, 27 | stack::AssignStack, 28 | var::{Var, VarManipulateIF}, 29 | }; 30 | use { 31 | crate::{cdb::ClauseDBIF, types::*}, 32 | std::{fmt, ops::Range, slice::Iter}, 33 | }; 34 | 35 | #[cfg(feature = "trail_saving")] 36 | pub use self::trail_saving::TrailSavingIF; 37 | 38 | /// API about assignment like 39 | /// [`decision_level`](`crate::assign::AssignIF::decision_level`), 40 | /// [`stack`](`crate::assign::AssignIF::stack`), 41 | /// [`best_assigned`](`crate::assign::AssignIF::best_assigned`), and so on. 42 | pub trait AssignIF: 43 | ActivityIF 44 | + Instantiate 45 | + PropagateIF 46 | + VarManipulateIF 47 | + PropertyDereference 48 | + PropertyReference 49 | { 50 | /// return root level. 51 | fn root_level(&self) -> DecisionLevel; 52 | /// return a literal in the stack. 53 | fn stack(&self, i: usize) -> Lit; 54 | /// return literals in the range of stack. 55 | fn stack_range(&self, r: Range) -> &[Lit]; 56 | /// return the number of assignments. 57 | fn stack_len(&self) -> usize; 58 | /// return the number of assignments at a given decision level `u`. 59 | /// 60 | /// ## Caveat 61 | /// - it emits a panic by out of index range. 62 | /// - it emits a panic if the level is 0. 63 | fn len_upto(&self, n: DecisionLevel) -> usize; 64 | /// return `true` if there's no assignment. 65 | fn stack_is_empty(&self) -> bool; 66 | /// return an iterator over assignment stack. 67 | fn stack_iter(&self) -> Iter<'_, Lit>; 68 | /// return the current decision level. 69 | fn decision_level(&self) -> DecisionLevel; 70 | ///return the decision var's id at that level. 71 | fn decision_vi(&self, lv: DecisionLevel) -> VarId; 72 | /// return `true` if there are un-propagated assignments. 73 | fn remains(&self) -> bool; 74 | /// return a reference to `assign`. 75 | fn assign_ref(&self) -> Vec>; 76 | /// return the largest number of assigned vars. 77 | fn best_assigned(&mut self) -> Option; 78 | /// return `true` if no best_phases 79 | #[cfg(feature = "rephase")] 80 | fn best_phases_invalid(&self) -> bool; 81 | /// inject assignments for eliminated vars. 82 | fn extend_model(&mut self, c: &mut impl ClauseDBIF) -> Vec>; 83 | /// return `true` if the set of literals is satisfiable under the current assignment. 84 | fn satisfies(&self, c: &[Lit]) -> bool; 85 | } 86 | 87 | /// Reasons of assignments 88 | #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] 89 | pub enum AssignReason { 90 | /// Implication by binary clause 91 | BinaryLink(Lit), 92 | /// Assigned by decision 93 | Decision(DecisionLevel), 94 | /// Assigned by a non-binary clause. 95 | Implication(ClauseId), 96 | /// None of the above. 97 | None, 98 | } 99 | 100 | impl fmt::Display for AssignReason { 101 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 102 | match self { 103 | &AssignReason::BinaryLink(_) => write!(f, "Implied by a binary clause"), 104 | AssignReason::Decision(0) => write!(f, "Asserted"), 105 | AssignReason::Decision(lvl) => write!(f, "Decided at level {lvl}"), 106 | AssignReason::Implication(cid) => write!(f, "Implied by {cid}"), 107 | AssignReason::None => write!(f, "Not assigned"), 108 | } 109 | } 110 | } 111 | 112 | #[cfg(feature = "boundary_check")] 113 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 114 | pub struct Assign { 115 | pub at: usize, 116 | pub pos: Option, 117 | pub lvl: DecisionLevel, 118 | pub lit: i32, 119 | pub val: Option, 120 | pub by: AssignReason, 121 | pub state: VarState, 122 | } 123 | 124 | #[cfg(feature = "boundary_check")] 125 | // return the list of composing literals: 126 | // 1. literal itself 127 | // 1. the value 128 | // 1. the position in trail 129 | // 1. last time propagated 130 | // 1. its level 131 | // 1. its assign reason 132 | pub trait DebugReportIF { 133 | fn report(&self, asg: &AssignStack) -> Vec; 134 | } 135 | 136 | #[cfg(feature = "boundary_check")] 137 | fn make_lit_report(asg: &AssignStack, lit: &Lit) -> Assign { 138 | let vi = lit.vi(); 139 | Assign { 140 | lit: i32::from(lit), 141 | val: asg.assigned(*lit), 142 | pos: asg.trail.iter().position(|l| vi == l.vi()), 143 | lvl: asg.level(vi), 144 | by: asg.reason(vi), 145 | at: asg.var(vi).propagated_at, 146 | state: asg.var[vi].state, 147 | } 148 | } 149 | 150 | #[cfg(feature = "boundary_check")] 151 | impl DebugReportIF for Lit { 152 | fn report(&self, asg: &AssignStack) -> Vec { 153 | vec![make_lit_report(asg, self)] 154 | } 155 | } 156 | 157 | #[cfg(feature = "boundary_check")] 158 | impl DebugReportIF for [Lit] { 159 | fn report(&self, asg: &AssignStack) -> Vec { 160 | self.iter() 161 | .map(|l| make_lit_report(asg, l)) 162 | .collect::>() 163 | } 164 | } 165 | 166 | #[cfg(feature = "boundary_check")] 167 | impl DebugReportIF for Clause { 168 | fn report(&self, asg: &AssignStack) -> Vec { 169 | let mut l = self 170 | .iter() 171 | .map(|l| make_lit_report(asg, l)) 172 | .collect::>(); 173 | l.sort(); 174 | l 175 | } 176 | } 177 | 178 | pub mod property { 179 | use super::stack::AssignStack; 180 | use crate::types::*; 181 | 182 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 183 | pub enum Tusize { 184 | NumConflict, 185 | NumDecision, 186 | NumPropagation, 187 | NumRephase, 188 | NumRestart, 189 | // 190 | //## var stat 191 | // 192 | NumVar, 193 | NumAssertedVar, 194 | NumEliminatedVar, 195 | NumReconflict, 196 | NumRepropagation, 197 | /// the number of vars in `the unreachable core' 198 | NumUnassertedVar, 199 | NumUnassignedVar, 200 | NumUnreachableVar, 201 | RootLevel, 202 | } 203 | 204 | pub const USIZES: [Tusize; 14] = [ 205 | Tusize::NumConflict, 206 | Tusize::NumDecision, 207 | Tusize::NumPropagation, 208 | Tusize::NumRephase, 209 | Tusize::NumRestart, 210 | Tusize::NumVar, 211 | Tusize::NumAssertedVar, 212 | Tusize::NumEliminatedVar, 213 | Tusize::NumReconflict, 214 | Tusize::NumRepropagation, 215 | Tusize::NumUnassertedVar, 216 | Tusize::NumUnassignedVar, 217 | Tusize::NumUnreachableVar, 218 | Tusize::RootLevel, 219 | ]; 220 | 221 | impl PropertyDereference for AssignStack { 222 | #[inline] 223 | fn derefer(&self, k: Tusize) -> usize { 224 | match k { 225 | Tusize::NumConflict => self.num_conflict, 226 | Tusize::NumDecision => self.num_decision, 227 | Tusize::NumPropagation => self.num_propagation, 228 | Tusize::NumRephase => self.num_rephase, 229 | Tusize::NumRestart => self.num_restart, 230 | Tusize::NumVar => self.num_vars, 231 | Tusize::NumAssertedVar => self.num_asserted_vars, 232 | Tusize::NumEliminatedVar => self.num_eliminated_vars, 233 | Tusize::NumReconflict => self.num_reconflict, 234 | Tusize::NumRepropagation => self.num_repropagation, 235 | Tusize::NumUnassertedVar => { 236 | self.num_vars - self.num_asserted_vars - self.num_eliminated_vars 237 | } 238 | Tusize::NumUnassignedVar => { 239 | self.num_vars 240 | - self.num_asserted_vars 241 | - self.num_eliminated_vars 242 | - self.trail.len() 243 | } 244 | Tusize::NumUnreachableVar => self.num_vars - self.num_best_assign, 245 | Tusize::RootLevel => self.root_level as usize, 246 | } 247 | } 248 | } 249 | 250 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 251 | pub enum Tf64 { 252 | AverageVarActivity, 253 | CurrentWorkingSetSize, 254 | VarDecayRate, 255 | } 256 | 257 | pub const F64S: [Tf64; 3] = [ 258 | Tf64::AverageVarActivity, 259 | Tf64::CurrentWorkingSetSize, 260 | Tf64::VarDecayRate, 261 | ]; 262 | 263 | impl PropertyDereference for AssignStack { 264 | #[inline] 265 | fn derefer(&self, k: Tf64) -> f64 { 266 | match k { 267 | Tf64::AverageVarActivity => 0.0, // self.activity_averaged, 268 | Tf64::CurrentWorkingSetSize => 0.0, // self.cwss, 269 | Tf64::VarDecayRate => self.activity_decay, 270 | } 271 | } 272 | } 273 | 274 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 275 | pub enum TEma { 276 | AssignRate, 277 | DecisionPerConflict, 278 | PropagationPerConflict, 279 | ConflictPerRestart, 280 | ConflictPerBaseRestart, 281 | BestPhaseDivergenceRate, 282 | } 283 | 284 | pub const EMAS: [TEma; 6] = [ 285 | TEma::AssignRate, 286 | TEma::DecisionPerConflict, 287 | TEma::PropagationPerConflict, 288 | TEma::ConflictPerRestart, 289 | TEma::ConflictPerBaseRestart, 290 | TEma::BestPhaseDivergenceRate, 291 | ]; 292 | 293 | impl PropertyReference for AssignStack { 294 | #[inline] 295 | fn refer(&self, k: TEma) -> &EmaView { 296 | match k { 297 | TEma::AssignRate => self.assign_rate.as_view(), 298 | TEma::DecisionPerConflict => self.dpc_ema.as_view(), 299 | TEma::PropagationPerConflict => self.ppc_ema.as_view(), 300 | TEma::ConflictPerRestart => self.cpr_ema.as_view(), 301 | TEma::ConflictPerBaseRestart => self.cpr_ema.as_view(), 302 | TEma::BestPhaseDivergenceRate => self.bp_divergence_ema.as_view(), 303 | } 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /cnfs/unsat.cnf: -------------------------------------------------------------------------------- 1 | p cnf 83 570 2 | -1 -4 61 63 0 3 | -1 -4 61 66 0 4 | -1 -7 50 51 52 63 0 5 | -1 -7 50 51 52 73 0 6 | -1 -7 61 63 0 7 | -1 -7 61 66 0 8 | -1 -8 50 51 52 63 0 9 | -1 -8 50 51 52 73 0 10 | -1 -9 50 51 52 63 0 11 | -1 -9 50 51 52 73 0 12 | -1 -10 50 51 52 63 0 13 | -1 -10 50 51 52 73 0 14 | -1 -11 50 51 52 63 0 15 | -1 -11 50 51 52 73 0 16 | -1 -12 50 51 52 63 0 17 | -1 -12 50 51 52 73 0 18 | -1 -13 49 61 63 0 19 | -1 -13 49 61 66 0 20 | -1 -16 26 63 0 21 | -1 -16 26 73 0 22 | -1 -17 26 63 0 23 | -1 -17 26 73 0 24 | -1 -17 61 63 0 25 | -1 -17 61 66 0 26 | -1 -18 26 63 0 27 | -1 -18 26 73 0 28 | -1 -18 61 63 0 29 | -1 -18 61 66 0 30 | -1 -19 61 63 0 31 | -1 -19 61 66 0 32 | -1 -20 61 63 0 33 | -1 -20 61 66 0 34 | -1 -21 61 63 0 35 | -1 -21 61 66 0 36 | -1 -22 61 63 0 37 | -1 -22 61 66 0 38 | -1 -23 61 63 0 39 | -1 -23 61 66 0 40 | -1 -24 61 63 0 41 | -1 -24 61 66 0 42 | -1 37 -49 64 0 43 | -1 37 -49 70 0 44 | -1 -69 0 45 | -1 -70 0 46 | -1 -71 0 47 | -2 -4 61 63 0 48 | -2 -4 61 66 0 49 | -2 -7 61 63 0 50 | -2 -7 61 66 0 51 | -2 -14 49 61 63 0 52 | -2 -14 49 61 66 0 53 | -2 -17 61 63 0 54 | -2 -17 61 66 0 55 | -2 -18 61 63 0 56 | -2 -18 61 66 0 57 | -2 -19 61 63 0 58 | -2 -19 61 66 0 59 | -2 -20 61 63 0 60 | -2 -20 61 66 0 61 | -2 -21 61 63 0 62 | -2 -21 61 66 0 63 | -2 -22 61 63 0 64 | -2 -22 61 66 0 65 | -2 -23 61 63 0 66 | -2 -23 61 66 0 67 | -2 -24 61 63 0 68 | -2 -24 61 66 0 69 | -2 38 -49 64 0 70 | -2 38 -49 70 0 71 | -2 -49 64 0 72 | -2 -49 70 0 73 | -3 -4 61 63 0 74 | -3 -4 61 66 0 75 | -3 -7 61 63 0 76 | -3 -7 61 66 0 77 | -3 -15 49 61 63 0 78 | -3 -15 49 61 66 0 79 | -3 -17 61 63 0 80 | -3 -17 61 66 0 81 | -3 -18 61 63 0 82 | -3 -18 61 66 0 83 | -3 -19 61 63 0 84 | -3 -19 61 66 0 85 | -3 -20 61 63 0 86 | -3 -20 61 66 0 87 | -3 -21 61 63 0 88 | -3 -21 61 66 0 89 | -3 -22 61 63 0 90 | -3 -22 61 66 0 91 | -3 -23 61 63 0 92 | -3 -23 61 66 0 93 | -3 -24 61 63 0 94 | -3 -24 61 66 0 95 | -3 39 -49 64 0 96 | -3 39 -49 70 0 97 | -4 -7 61 63 0 98 | -4 -7 61 66 0 99 | -4 -13 50 51 63 0 100 | -4 -13 50 51 73 0 101 | -4 -14 50 51 63 0 102 | -4 -14 50 51 73 0 103 | -4 -15 50 51 63 0 104 | -4 -15 50 51 73 0 105 | -4 -16 26 63 0 106 | -4 -16 26 73 0 107 | -4 -16 27 -28 50 51 -61 63 0 108 | -4 -16 27 -28 50 51 -61 74 0 109 | -4 -16 49 61 63 0 110 | -4 -16 49 61 66 0 111 | -4 -16 50 51 63 0 112 | -4 -16 50 51 73 0 113 | -4 -17 25 -28 -29 50 51 -61 63 0 114 | -4 -17 25 -28 -29 50 51 -61 72 0 115 | -4 -17 26 63 0 116 | -4 -17 26 73 0 117 | -4 -17 27 -28 -29 50 51 -61 63 0 118 | -4 -17 27 -28 -29 50 51 -61 74 0 119 | -4 -17 50 51 63 0 120 | -4 -17 50 51 73 0 121 | -4 -18 25 -28 -30 50 51 -61 63 0 122 | -4 -18 25 -28 -30 50 51 -61 72 0 123 | -4 -18 26 63 0 124 | -4 -18 26 73 0 125 | -4 -18 50 51 63 0 126 | -4 -18 50 51 73 0 127 | -4 -20 61 63 0 128 | -4 -20 61 66 0 129 | -4 -21 61 63 0 130 | -4 -21 61 66 0 131 | -4 -22 61 63 0 132 | -4 -22 61 66 0 133 | -4 -23 61 63 0 134 | -4 -23 61 66 0 135 | -4 -24 61 63 0 136 | -4 -24 61 66 0 137 | -4 -25 -49 64 0 138 | -4 25 -49 65 0 139 | -4 25 -49 70 0 140 | -4 -25 -49 72 0 141 | -4 -26 -49 64 0 142 | -4 26 -49 65 0 143 | -4 26 -49 70 0 144 | -4 -26 -49 73 0 145 | -4 -27 -49 64 0 146 | -4 27 -49 65 0 147 | -4 27 -49 70 0 148 | -4 -27 -49 74 0 149 | -5 -7 61 63 0 150 | -5 -7 61 66 0 151 | -5 -13 50 51 63 0 152 | -5 -13 50 51 73 0 153 | -5 -14 50 51 63 0 154 | -5 -14 50 51 73 0 155 | -5 -15 50 51 63 0 156 | -5 -15 50 51 73 0 157 | -5 -16 27 -28 -29 50 51 -61 63 0 158 | -5 -16 27 -28 -29 50 51 -61 74 0 159 | -5 -16 50 51 63 0 160 | -5 -16 50 51 73 0 161 | -5 -17 25 -29 50 51 -61 63 0 162 | -5 -17 25 -29 50 51 -61 72 0 163 | -5 -17 27 -29 50 51 -61 63 0 164 | -5 -17 27 -29 50 51 -61 74 0 165 | -5 -17 49 61 63 0 166 | -5 -17 49 61 66 0 167 | -5 -17 50 51 63 0 168 | -5 -17 50 51 73 0 169 | -5 -18 25 -29 -30 50 51 -61 63 0 170 | -5 -18 25 -29 -30 50 51 -61 72 0 171 | -5 -18 50 51 63 0 172 | -5 -18 50 51 73 0 173 | -5 -20 61 63 0 174 | -5 -20 61 66 0 175 | -5 -21 61 63 0 176 | -5 -21 61 66 0 177 | -5 -22 61 63 0 178 | -5 -22 61 66 0 179 | -5 -23 61 63 0 180 | -5 -23 61 66 0 181 | -5 -24 61 63 0 182 | -5 -24 61 66 0 183 | -5 -25 -49 64 0 184 | -5 25 -49 65 0 185 | -5 25 -49 70 0 186 | -5 -25 -49 72 0 187 | -5 -26 -49 64 0 188 | -5 26 -49 65 0 189 | -5 26 -49 70 0 190 | -5 -26 -49 73 0 191 | -5 -27 -49 64 0 192 | -5 27 -49 65 0 193 | -5 27 -49 70 0 194 | -5 -27 -49 74 0 195 | -6 -7 61 63 0 196 | -6 -7 61 66 0 197 | -6 -13 50 51 63 0 198 | -6 -13 50 51 73 0 199 | -6 -14 50 51 63 0 200 | -6 -14 50 51 73 0 201 | -6 -15 50 51 63 0 202 | -6 -15 50 51 73 0 203 | -6 -16 27 -28 -30 50 51 -61 63 0 204 | -6 -16 27 -28 -30 50 51 -61 74 0 205 | -6 -16 50 51 63 0 206 | -6 -16 50 51 73 0 207 | -6 -17 25 -29 -30 50 51 -61 63 0 208 | -6 -17 25 -29 -30 50 51 -61 72 0 209 | -6 -17 27 -29 -30 50 51 -61 63 0 210 | -6 -17 27 -29 -30 50 51 -61 74 0 211 | -6 -17 50 51 63 0 212 | -6 -17 50 51 73 0 213 | -6 -18 25 -30 50 51 -61 63 0 214 | -6 -18 25 -30 50 51 -61 72 0 215 | -6 -18 49 61 63 0 216 | -6 -18 49 61 66 0 217 | -6 -18 50 51 63 0 218 | -6 -18 50 51 73 0 219 | -6 -20 61 63 0 220 | -6 -20 61 66 0 221 | -6 -21 61 63 0 222 | -6 -21 61 66 0 223 | -6 -22 61 63 0 224 | -6 -22 61 66 0 225 | -6 -23 61 63 0 226 | -6 -23 61 66 0 227 | -6 -24 61 63 0 228 | -6 -24 61 66 0 229 | -6 -25 -49 64 0 230 | -6 25 -49 65 0 231 | -6 25 -49 70 0 232 | -6 -25 -49 72 0 233 | -6 -26 -49 64 0 234 | -6 26 -49 65 0 235 | -6 26 -49 70 0 236 | -6 -26 -49 73 0 237 | -6 -27 -49 64 0 238 | -6 27 -49 65 0 239 | -6 27 -49 70 0 240 | -6 -27 -49 74 0 241 | -7 -13 50 51 63 0 242 | -7 -13 50 51 73 0 243 | -7 -14 50 51 52 63 0 244 | -7 -14 50 51 52 73 0 245 | -7 -14 50 51 63 0 246 | -7 -14 50 51 73 0 247 | -7 -15 50 51 52 63 0 248 | -7 -15 50 51 52 73 0 249 | -7 -15 50 51 63 0 250 | -7 -15 50 51 73 0 251 | -7 -16 27 -28 -31 50 51 -61 63 0 252 | -7 -16 27 -28 -31 50 51 -61 74 0 253 | -7 -16 50 51 52 63 0 254 | -7 -16 50 51 52 73 0 255 | -7 -16 50 51 63 0 256 | -7 -16 50 51 73 0 257 | -7 -17 25 -29 -31 50 51 -61 63 0 258 | -7 -17 25 -29 -31 50 51 -61 72 0 259 | -7 -17 27 -29 -31 50 51 -61 63 0 260 | -7 -17 27 -29 -31 50 51 -61 74 0 261 | -7 -17 50 51 52 63 0 262 | -7 -17 50 51 52 73 0 263 | -7 -17 50 51 63 0 264 | -7 -17 50 51 73 0 265 | -7 -18 25 -30 -31 50 51 -61 63 0 266 | -7 -18 25 -30 -31 50 51 -61 72 0 267 | -7 -18 50 51 52 63 0 268 | -7 -18 50 51 52 73 0 269 | -7 -18 50 51 63 0 270 | -7 -18 50 51 73 0 271 | -7 -19 49 61 63 0 272 | -7 -19 49 61 66 0 273 | -7 -49 64 0 274 | -7 -49 67 0 275 | -8 -13 50 51 63 0 276 | -8 -13 50 51 73 0 277 | -8 -14 50 51 52 63 0 278 | -8 -14 50 51 52 73 0 279 | -8 -14 50 51 63 0 280 | -8 -14 50 51 73 0 281 | -8 -15 50 51 52 63 0 282 | -8 -15 50 51 52 73 0 283 | -8 -15 50 51 63 0 284 | -8 -15 50 51 73 0 285 | -8 -16 27 -28 -32 50 51 -61 63 0 286 | -8 -16 27 -28 -32 50 51 -61 74 0 287 | -8 -16 50 51 52 63 0 288 | -8 -16 50 51 52 73 0 289 | -8 -16 50 51 63 0 290 | -8 -16 50 51 73 0 291 | -8 -17 25 -29 -32 50 51 -61 63 0 292 | -8 -17 25 -29 -32 50 51 -61 72 0 293 | -8 -17 27 -29 -32 50 51 -61 63 0 294 | -8 -17 27 -29 -32 50 51 -61 74 0 295 | -8 -17 50 51 52 63 0 296 | -8 -17 50 51 52 73 0 297 | -8 -17 50 51 63 0 298 | -8 -17 50 51 73 0 299 | -8 -18 25 -30 -32 50 51 -61 63 0 300 | -8 -18 25 -30 -32 50 51 -61 72 0 301 | -8 -18 50 51 52 63 0 302 | -8 -18 50 51 52 73 0 303 | -8 -18 50 51 63 0 304 | -8 -18 50 51 73 0 305 | -8 -20 49 61 63 0 306 | -8 -20 49 61 66 0 307 | -8 -49 64 0 308 | -8 -49 67 0 309 | -9 -13 50 51 63 0 310 | -9 -13 50 51 73 0 311 | -9 -14 50 51 52 63 0 312 | -9 -14 50 51 52 73 0 313 | -9 -14 50 51 63 0 314 | -9 -14 50 51 73 0 315 | -9 -15 50 51 52 63 0 316 | -9 -15 50 51 52 73 0 317 | -9 -15 50 51 63 0 318 | -9 -15 50 51 73 0 319 | -9 -16 27 -28 -33 50 51 -61 63 0 320 | -9 -16 27 -28 -33 50 51 -61 74 0 321 | -9 -16 50 51 52 63 0 322 | -9 -16 50 51 52 73 0 323 | -9 -16 50 51 63 0 324 | -9 -16 50 51 73 0 325 | -9 -17 25 -29 -33 50 51 -61 63 0 326 | -9 -17 25 -29 -33 50 51 -61 72 0 327 | -9 -17 27 -29 -33 50 51 -61 63 0 328 | -9 -17 27 -29 -33 50 51 -61 74 0 329 | -9 -17 50 51 52 63 0 330 | -9 -17 50 51 52 73 0 331 | -9 -17 50 51 63 0 332 | -9 -17 50 51 73 0 333 | -9 -18 25 -30 -33 50 51 -61 63 0 334 | -9 -18 25 -30 -33 50 51 -61 72 0 335 | -9 -18 50 51 52 63 0 336 | -9 -18 50 51 52 73 0 337 | -9 -18 50 51 63 0 338 | -9 -18 50 51 73 0 339 | -9 -21 49 61 63 0 340 | -9 -21 49 61 66 0 341 | -9 -49 64 0 342 | -9 -49 67 0 343 | -10 -14 50 51 52 63 0 344 | -10 -14 50 51 52 73 0 345 | -10 -15 50 51 52 63 0 346 | -10 -15 50 51 52 73 0 347 | -10 -16 50 51 52 63 0 348 | -10 -16 50 51 52 73 0 349 | -10 -17 50 51 52 63 0 350 | -10 -17 50 51 52 73 0 351 | -10 -18 50 51 52 63 0 352 | -10 -18 50 51 52 73 0 353 | -10 -22 49 61 63 0 354 | -10 -22 49 61 66 0 355 | -11 -14 50 51 52 63 0 356 | -11 -14 50 51 52 73 0 357 | -11 -15 50 51 52 63 0 358 | -11 -15 50 51 52 73 0 359 | -11 -16 50 51 52 63 0 360 | -11 -16 50 51 52 73 0 361 | -11 -17 50 51 52 63 0 362 | -11 -17 50 51 52 73 0 363 | -11 -18 50 51 52 63 0 364 | -11 -18 50 51 52 73 0 365 | -11 -23 49 61 63 0 366 | -11 -23 49 61 66 0 367 | -12 -14 50 51 52 63 0 368 | -12 -14 50 51 52 73 0 369 | -12 -15 50 51 52 63 0 370 | -12 -15 50 51 52 73 0 371 | -12 -16 50 51 52 63 0 372 | -12 -16 50 51 52 73 0 373 | -12 -17 50 51 52 63 0 374 | -12 -17 50 51 52 73 0 375 | -12 -18 50 51 52 63 0 376 | -12 -18 50 51 52 73 0 377 | -12 -24 49 61 63 0 378 | -12 -24 49 61 66 0 379 | -49 -63 0 380 | 49 -64 0 381 | 49 -65 0 382 | -50 61 63 0 383 | -50 61 66 0 384 | -51 61 63 0 385 | -51 61 66 0 386 | 63 64 65 0 387 | -63 -67 0 388 | -64 -66 0 389 | -65 -66 0 390 | -65 -67 0 391 | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 0 392 | -66 -68 0 393 | -67 -68 0 394 | -68 -69 0 395 | -68 -70 0 396 | -68 -71 0 397 | -68 -72 0 398 | -68 -73 0 399 | -68 -74 0 400 | -68 -75 0 401 | -68 -76 0 402 | -68 -77 0 403 | -68 -78 0 404 | -68 -79 0 405 | -68 -80 0 406 | -68 -81 0 407 | -68 -82 0 408 | -68 -83 0 409 | -69 -73 0 410 | -69 -74 0 411 | -69 -75 0 412 | -69 -76 0 413 | -69 -77 0 414 | -69 -78 0 415 | -69 -79 0 416 | -69 -80 0 417 | -69 -81 0 418 | -69 -82 0 419 | -69 -83 0 420 | -70 -73 0 421 | -70 -74 0 422 | -70 -75 0 423 | -70 -76 0 424 | -70 -77 0 425 | -70 -78 0 426 | -70 -79 0 427 | -70 -80 0 428 | -70 -81 0 429 | -70 -82 0 430 | -70 -83 0 431 | -71 -73 0 432 | -71 -74 0 433 | -71 -75 0 434 | -71 -76 0 435 | -71 -77 0 436 | -71 -78 0 437 | -71 -79 0 438 | -71 -80 0 439 | -71 -81 0 440 | -71 -82 0 441 | -71 -83 0 442 | -72 -73 0 443 | -72 -74 0 444 | -72 -75 0 445 | -72 -76 0 446 | -72 -77 0 447 | -72 -78 0 448 | -72 -79 0 449 | -72 -80 0 450 | -72 -81 0 451 | -72 -82 0 452 | -72 -83 0 453 | -73 -74 0 454 | -73 -75 0 455 | -73 -76 0 456 | -73 -77 0 457 | -73 -78 0 458 | -73 -79 0 459 | -73 -80 0 460 | -73 -81 0 461 | -73 -82 0 462 | -73 -83 0 463 | -74 -75 0 464 | -74 -76 0 465 | -74 -77 0 466 | -74 -78 0 467 | -74 -79 0 468 | -74 -80 0 469 | -74 -81 0 470 | -74 -82 0 471 | -74 -83 0 472 | -75 -76 0 473 | -75 -77 0 474 | -75 -78 0 475 | -75 -79 0 476 | -75 -80 0 477 | -75 -81 0 478 | -75 -82 0 479 | -75 -83 0 480 | -76 -77 0 481 | -76 -78 0 482 | -76 -79 0 483 | -76 -80 0 484 | -76 -81 0 485 | -76 -82 0 486 | -76 -83 0 487 | -77 -78 0 488 | -77 -79 0 489 | -77 -80 0 490 | -77 -81 0 491 | -77 -82 0 492 | -77 -83 0 493 | -78 -79 0 494 | -78 -80 0 495 | -78 -81 0 496 | -78 -82 0 497 | -78 -83 0 498 | -79 -80 0 499 | -79 -81 0 500 | -79 -82 0 501 | -79 -83 0 502 | -80 -81 0 503 | -80 -82 0 504 | -80 -83 0 505 | -81 -82 0 506 | -81 -83 0 507 | -82 -83 0 508 | -1 0 509 | -2 0 510 | -3 0 511 | -4 0 512 | 5 0 513 | -6 0 514 | -7 0 515 | -8 0 516 | -9 0 517 | -10 0 518 | -11 0 519 | -12 0 520 | -13 0 521 | -14 0 522 | -15 0 523 | -16 0 524 | 17 0 525 | -18 0 526 | -19 0 527 | -20 0 528 | -21 0 529 | -22 0 530 | -23 0 531 | -24 0 532 | -25 0 533 | -26 0 534 | -27 0 535 | 28 0 536 | 29 0 537 | -30 0 538 | 31 0 539 | 32 0 540 | 33 0 541 | -34 0 542 | 35 0 543 | -36 0 544 | 37 0 545 | 38 0 546 | 39 0 547 | 40 0 548 | 41 0 549 | 42 0 550 | -43 0 551 | 44 0 552 | -45 0 553 | -46 0 554 | -47 0 555 | -48 0 556 | -49 0 557 | -50 0 558 | -51 0 559 | 52 0 560 | -53 0 561 | -54 0 562 | -55 0 563 | -56 0 564 | -57 0 565 | -58 0 566 | -59 0 567 | -60 0 568 | 61 0 569 | -62 0 570 | -63 0 571 | -66 0 572 | -------------------------------------------------------------------------------- /src/primitive/ema.rs: -------------------------------------------------------------------------------- 1 | /// API for observing EMA. 2 | pub trait EmaIF { 3 | /// return the current value. 4 | fn get_fast(&self) -> f64 { 5 | unimplemented!() 6 | } 7 | /// return the current value. 8 | fn get(&self) -> f64 { 9 | self.get_fast() 10 | } 11 | fn get_slow(&self) -> f64 { 12 | unimplemented!() 13 | } 14 | /// return a ratio of short / long statistics. 15 | fn trend(&self) -> f64 { 16 | unimplemented!() 17 | } 18 | } 19 | 20 | pub trait EmaSingleIF: EmaIF { 21 | /// return the current value. 22 | fn get(&self) -> f64 { 23 | self.get_fast() 24 | } 25 | } 26 | 27 | /// API for Exponential Moving Average, EMA, like `get`, `reset`, `update` and so on. 28 | pub trait EmaMutIF: EmaIF { 29 | /// the type of the argument of `update`. 30 | type Input; 31 | /// reset internal data. 32 | fn reset_to(&mut self, _: f64) {} 33 | fn reset_fast(&mut self) {} 34 | fn reset_slow(&mut self) {} 35 | /// catch up with the current state. 36 | fn update(&mut self, x: Self::Input); 37 | /// return a view. 38 | fn as_view(&self) -> &EmaView; 39 | /// set value. 40 | fn set_value(&mut self, _x: f64) {} 41 | } 42 | 43 | #[derive(Clone, Debug)] 44 | pub struct EmaView { 45 | fast: f64, 46 | slow: f64, 47 | } 48 | 49 | impl EmaIF for EmaView { 50 | fn get_fast(&self) -> f64 { 51 | self.fast 52 | } 53 | fn get_slow(&self) -> f64 { 54 | self.slow 55 | } 56 | fn trend(&self) -> f64 { 57 | self.fast / self.slow 58 | } 59 | } 60 | 61 | /// Exponential Moving Average, with a calibrator if feature `EMA_calibration` is on. 62 | #[derive(Clone, Debug)] 63 | pub struct Ema { 64 | val: EmaView, 65 | #[cfg(feature = "EMA_calibration")] 66 | cal: f64, 67 | sca: f64, 68 | } 69 | 70 | impl EmaIF for Ema { 71 | #[cfg(feature = "EMA_calibration")] 72 | fn get_fast(&self) -> f64 { 73 | self.val.fast / self.cal 74 | } 75 | #[cfg(not(feature = "EMA_calibration"))] 76 | fn get_fast(&self) -> f64 { 77 | self.val.fast 78 | } 79 | fn get_slow(&self) -> f64 { 80 | unimplemented!() 81 | } 82 | fn trend(&self) -> f64 { 83 | unimplemented!() 84 | } 85 | } 86 | 87 | impl EmaMutIF for Ema { 88 | type Input = f64; 89 | #[cfg(not(feature = "EMA_calibration"))] 90 | fn update(&mut self, x: Self::Input) { 91 | self.val.fast = self.sca * x + (1.0 - self.sca) * self.val.fast; 92 | } 93 | #[cfg(feature = "EMA_calibration")] 94 | fn update(&mut self, x: Self::Input) { 95 | self.val.fast = self.sca * x + (1.0 - self.sca) * self.val.fast; 96 | self.cal = self.sca + (1.0 - self.sca) * self.cal; 97 | } 98 | fn as_view(&self) -> &EmaView { 99 | &self.val 100 | } 101 | fn set_value(&mut self, x: f64) { 102 | self.val.fast = x; 103 | self.val.slow = x; 104 | } 105 | } 106 | 107 | impl Ema { 108 | pub fn new(s: usize) -> Ema { 109 | Ema { 110 | val: EmaView { 111 | fast: 0.0, 112 | slow: 0.0, 113 | }, 114 | #[cfg(feature = "EMA_calibration")] 115 | cal: 0.0, 116 | sca: 1.0 / (s as f64), 117 | } 118 | } 119 | /// set value. 120 | pub fn with_value(mut self, x: f64) -> Ema { 121 | self.val.fast = x; 122 | self.val.slow = x; 123 | self 124 | } 125 | } 126 | 127 | /// Exponential Moving Average pair, with a calibrator if feature `EMA_calibration` is on. 128 | #[derive(Clone, Debug)] 129 | pub struct Ema2 { 130 | ema: EmaView, 131 | #[cfg(feature = "EMA_calibration")] 132 | calf: f64, 133 | #[cfg(feature = "EMA_calibration")] 134 | cals: f64, 135 | fe: f64, 136 | se: f64, 137 | } 138 | 139 | impl EmaIF for Ema2 { 140 | #[cfg(feature = "EMA_calibration")] 141 | fn get_fast(&self) -> f64 { 142 | self.ema.fast / self.calf 143 | } 144 | #[cfg(not(feature = "EMA_calibration"))] 145 | fn get_fast(&self) -> f64 { 146 | self.ema.fast 147 | } 148 | #[cfg(feature = "EMA_calibration")] 149 | fn get_slow(&self) -> f64 { 150 | self.ema.slow / self.calf 151 | } 152 | #[cfg(not(feature = "EMA_calibration"))] 153 | fn get_slow(&self) -> f64 { 154 | self.ema.slow 155 | } 156 | #[cfg(feature = "EMA_calibration")] 157 | fn trend(&self) -> f64 { 158 | self.ema.fast / self.ema.slow * (self.cals / self.calf) 159 | } 160 | #[cfg(not(feature = "EMA_calibration"))] 161 | fn trend(&self) -> f64 { 162 | self.ema.fast / self.ema.slow 163 | } 164 | } 165 | 166 | impl EmaMutIF for Ema2 { 167 | type Input = f64; 168 | #[cfg(not(feature = "EMA_calibration"))] 169 | fn update(&mut self, x: Self::Input) { 170 | self.ema.fast = self.fe * x + (1.0 - self.fe) * self.ema.fast; 171 | self.ema.slow = self.se * x + (1.0 - self.se) * self.ema.slow; 172 | } 173 | #[cfg(feature = "EMA_calibration")] 174 | fn update(&mut self, x: Self::Input) { 175 | self.ema.fast = self.fe * x + (1.0 - self.fe) * self.ema.fast; 176 | self.ema.slow = self.se * x + (1.0 - self.se) * self.ema.slow; 177 | self.calf = self.fe + (1.0 - self.fe) * self.calf; 178 | self.cals = self.se + (1.0 - self.se) * self.cals; 179 | } 180 | fn reset_to(&mut self, val: f64) { 181 | self.ema.fast = val; 182 | } 183 | #[cfg(not(feature = "EMA_calibration"))] 184 | fn reset_fast(&mut self) { 185 | self.ema.fast = self.ema.slow; 186 | } 187 | #[cfg(feature = "EMA_calibration")] 188 | fn reset_fast(&mut self) { 189 | self.ema.fast = self.ema.slow; 190 | self.calf = self.cals; 191 | } 192 | #[cfg(not(feature = "EMA_calibration"))] 193 | fn reset_slow(&mut self) { 194 | self.ema.slow = self.ema.fast; 195 | } 196 | #[cfg(feature = "EMA_calibration")] 197 | fn reset_slow(&mut self) { 198 | self.ema.slow = self.ema.fast; 199 | self.cals = self.calf; 200 | } 201 | fn as_view(&self) -> &EmaView { 202 | &self.ema 203 | } 204 | } 205 | 206 | impl Ema2 { 207 | pub fn new(len: usize) -> Ema2 { 208 | Ema2 { 209 | ema: EmaView { 210 | fast: 0.0, 211 | slow: 0.0, 212 | }, 213 | #[cfg(feature = "EMA_calibration")] 214 | calf: 0.0, 215 | #[cfg(feature = "EMA_calibration")] 216 | cals: 0.0, 217 | fe: 1.0 / (len as f64), 218 | se: 1.0 / (len as f64), 219 | } 220 | } 221 | // set secondary EMA parameter 222 | pub fn with_slow(mut self, s: usize) -> Ema2 { 223 | self.se = 1.0 / (s as f64); 224 | self 225 | } 226 | pub fn get_slow(&self) -> f64 { 227 | self.ema.slow // / self.calf 228 | } 229 | /// set value. 230 | pub fn with_value(mut self, x: f64) -> Self { 231 | self.ema.fast = x; 232 | self.ema.slow = x; 233 | #[cfg(feature = "EMA_calibration")] 234 | { 235 | self.calf = 1.0; 236 | self.cals = 1.0; 237 | } 238 | self 239 | } 240 | } 241 | 242 | /// Ema of Sequence of usize 243 | #[derive(Clone, Debug)] 244 | pub struct EmaSU { 245 | last: f64, 246 | ema: Ema, 247 | } 248 | 249 | impl EmaIF for EmaSU { 250 | fn get_fast(&self) -> f64 { 251 | self.ema.get_fast() 252 | } 253 | fn get_slow(&self) -> f64 { 254 | unimplemented!() 255 | } 256 | fn trend(&self) -> f64 { 257 | unimplemented!() 258 | } 259 | } 260 | 261 | impl EmaMutIF for EmaSU { 262 | type Input = usize; 263 | fn update(&mut self, x: Self::Input) { 264 | let diff: f64 = x as f64 - self.last; 265 | self.ema.update(diff); 266 | self.last = x as f64; 267 | } 268 | fn as_view(&self) -> &EmaView { 269 | self.ema.as_view() 270 | } 271 | } 272 | 273 | impl EmaSU { 274 | pub fn new(s: usize) -> Self { 275 | EmaSU { 276 | last: 0.0, 277 | ema: Ema::new(s), 278 | } 279 | } 280 | pub fn update_base(&mut self, x: usize) { 281 | self.last = x as f64; 282 | } 283 | pub fn get_ema(&self) -> &Ema { 284 | &self.ema 285 | } 286 | } 287 | 288 | /// Equally-Weighted-Average, namely, Average 289 | #[derive(Clone, Debug)] 290 | pub struct Ewa { 291 | ema: EmaView, 292 | pool: [f64; N], 293 | last: usize, 294 | } 295 | 296 | impl EmaIF for Ewa { 297 | fn get_fast(&self) -> f64 { 298 | self.ema.fast 299 | } 300 | } 301 | 302 | impl EmaMutIF for Ewa { 303 | type Input = f64; 304 | fn update(&mut self, x: Self::Input) { 305 | self.ema.fast -= self.pool[self.last]; 306 | let val = x / N as f64; 307 | self.ema.fast += val; 308 | self.pool[self.last] = val; 309 | self.last = (self.last + 1) % N; 310 | } 311 | fn as_view(&self) -> &EmaView { 312 | &self.ema 313 | } 314 | } 315 | 316 | impl Ewa { 317 | pub fn new(initial: f64) -> Self { 318 | Ewa:: { 319 | ema: EmaView { 320 | fast: initial, 321 | slow: initial, 322 | }, 323 | pool: [initial; N], 324 | last: 0, 325 | } 326 | } 327 | } 328 | 329 | /// Exponential Moving Average pair, with a calibrator if feature `EMA_calibration` is on. 330 | #[derive(Clone, Debug)] 331 | pub struct Ewa2 { 332 | ema: EmaView, 333 | pool: [f64; N], 334 | last: usize, 335 | #[cfg(feature = "EMA_calibration")] 336 | cals: f64, 337 | se: f64, 338 | sx: f64, 339 | } 340 | 341 | impl EmaIF for Ewa2 { 342 | fn get_fast(&self) -> f64 { 343 | self.ema.fast 344 | } 345 | #[cfg(not(feature = "EMA_calibration"))] 346 | fn trend(&self) -> f64 { 347 | self.ema.fast / self.ema.slow 348 | } 349 | #[cfg(feature = "EMA_calibration")] 350 | fn trend(&self) -> f64 { 351 | self.ema.fast * self.cals / self.slow 352 | } 353 | } 354 | 355 | impl EmaMutIF for Ewa2 { 356 | type Input = f64; 357 | fn update(&mut self, x: Self::Input) { 358 | self.ema.fast -= self.pool[self.last]; 359 | let val = x / N as f64; 360 | self.ema.fast += val; 361 | self.pool[self.last] = val; 362 | self.last = (self.last + 1) % N; 363 | self.ema.slow = self.se * x + self.sx * self.ema.slow; 364 | #[cfg(feature = "EMA_calibration")] 365 | { 366 | self.cals = self.se + self.sx * self.cals; 367 | } 368 | } 369 | fn reset_to(&mut self, val: f64) { 370 | self.ema.fast = val; 371 | } 372 | #[cfg(not(feature = "EMA_calibration"))] 373 | fn reset_fast(&mut self) { 374 | unimplemented!(); 375 | } 376 | #[cfg(feature = "EMA_calibration")] 377 | fn reset_fast(&mut self) { 378 | unimplemented!(); 379 | } 380 | #[cfg(not(feature = "EMA_calibration"))] 381 | fn reset_slow(&mut self) { 382 | self.ema.slow = self.ema.fast; 383 | } 384 | #[cfg(feature = "EMA_calibration")] 385 | fn reset_slow(&mut self) { 386 | self.ema.slow = self.fast.get(); 387 | self.cals = self.calf; 388 | } 389 | fn as_view(&self) -> &EmaView { 390 | &self.ema 391 | } 392 | } 393 | 394 | impl Ewa2 { 395 | pub fn new(initial: f64) -> Ewa2 { 396 | Ewa2:: { 397 | ema: EmaView { 398 | fast: initial, 399 | slow: initial, 400 | }, 401 | pool: [initial; N], 402 | last: 0, 403 | #[cfg(feature = "EMA_calibration")] 404 | cals: initial, 405 | se: 1.0 / (N as f64), 406 | sx: 1.0 - 1.0 / (N as f64), 407 | } 408 | } 409 | // set secondary EMA parameter 410 | pub fn with_slow(mut self, s: usize) -> Self { 411 | self.se = 1.0 / (s as f64); 412 | self.sx = 1.0 - self.se; 413 | self 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/bin/splr.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(feature = "no_IO"))] 2 | /// SAT solver for Propositional Logic in Rust, which can't be compiled with feature 'no_IO' 3 | use { 4 | splr::{ 5 | assign, cdb, 6 | config::{self, CERTIFICATION_DEFAULT_FILENAME}, 7 | solver::*, 8 | state::{self, LogF64Id, LogUsizeId}, 9 | Config, EmaIF, PropertyDereference, PropertyReference, SolverError, VERSION, 10 | }, 11 | std::{ 12 | borrow::Cow, 13 | env, 14 | fs::File, 15 | io::{BufWriter, Write}, 16 | path::PathBuf, 17 | thread, 18 | time::Duration, 19 | }, 20 | }; 21 | 22 | const RED: &str = "\x1B[001m\x1B[031m"; 23 | const GREEN: &str = "\x1B[001m\x1B[032m"; 24 | const BLUE: &str = "\x1B[001m\x1B[034m"; 25 | const RESET: &str = "\x1B[000m"; 26 | 27 | fn colored(v: Result, no_color: bool) -> Cow<'static, str> { 28 | if no_color { 29 | match v { 30 | Ok(false) => Cow::Borrowed("s UNSATISFIABLE"), 31 | Ok(true) => Cow::Borrowed("s SATISFIABLE"), 32 | Err(_) => Cow::Borrowed("s UNKNOWN"), 33 | } 34 | } else { 35 | match v { 36 | Ok(false) => Cow::from(format!("{BLUE}s UNSATISFIABLE{RESET}")), 37 | Ok(true) => Cow::from(format!("{GREEN}s SATISFIABLE{RESET}")), 38 | Err(_) => Cow::from(format!("{RED}s UNKNOWN{RESET}")), 39 | } 40 | } 41 | } 42 | 43 | fn main() { 44 | let mut config = Config::default(); 45 | config.inject_from_args(); 46 | config.splr_interface = true; 47 | if !config.cnf_file.exists() { 48 | println!( 49 | "{} does not exist.", 50 | config.cnf_file.file_name().unwrap().to_str().unwrap() 51 | ); 52 | return; 53 | } 54 | let cnf_file = config.cnf_file.to_string_lossy(); 55 | let ans_file: Option = match config.io_rfile.to_string_lossy().as_ref() { 56 | "-" => None, 57 | "" => Some(config.io_odir.join(PathBuf::from(format!( 58 | "ans_{}", 59 | config.cnf_file.file_name().unwrap().to_string_lossy(), 60 | )))), 61 | _ => Some(config.io_odir.join(&config.io_rfile)), 62 | }; 63 | if config.io_pfile.to_string_lossy() != CERTIFICATION_DEFAULT_FILENAME 64 | && !config.use_certification 65 | { 66 | println!("Abort: You set a proof filename with '--proof' explicitly, but didn't set '--certify'. It doesn't look good."); 67 | return; 68 | } 69 | if let Ok(val) = env::var("SPLR_TIMEOUT") { 70 | if let Ok(timeout) = val.parse::() { 71 | let input = cnf_file.as_ref().to_string(); 72 | let no_color = config.no_color; 73 | thread::spawn(move || { 74 | thread::sleep(Duration::from_millis(timeout * 1000)); 75 | println!( 76 | "{} (TimeOut): {}", 77 | colored(Err(&SolverError::TimeOut), no_color), 78 | input 79 | ); 80 | std::process::exit(0); 81 | }); 82 | } 83 | } 84 | let mut s = match Solver::build(&config) { 85 | Err(SolverError::EmptyClause | SolverError::RootLevelConflict(_)) => { 86 | println!( 87 | "\x1B[1G\x1B[K{}: {}", 88 | colored(Ok(false), config.no_color), 89 | config.cnf_file.file_name().unwrap().to_string_lossy(), 90 | ); 91 | std::process::exit(20); 92 | } 93 | Err(e) => { 94 | panic!("{e:?}"); 95 | } 96 | Ok(solver) => solver, 97 | }; 98 | let res = s.solve(); 99 | save_result(&mut s, &res, &cnf_file, ans_file); 100 | std::process::exit(match res { 101 | Ok(Certificate::SAT(_)) => 10, 102 | Ok(Certificate::UNSAT) => 20, 103 | Err(_) => 0, 104 | }); 105 | } 106 | 107 | fn save_result + std::fmt::Display>( 108 | s: &mut Solver, 109 | res: &SolverResult, 110 | input: S, 111 | output: Option, 112 | ) { 113 | let mut ofile; 114 | let mut otty; 115 | let mut redirect = false; 116 | let mut buf: &mut dyn Write = match output { 117 | Some(ref file) => { 118 | if let Ok(f) = File::create(file) { 119 | ofile = BufWriter::new(f); 120 | &mut ofile 121 | } else { 122 | redirect = true; 123 | otty = BufWriter::new(std::io::stdout()); 124 | &mut otty 125 | } 126 | } 127 | None => { 128 | otty = BufWriter::new(std::io::stdout()); 129 | &mut otty 130 | } 131 | }; 132 | match res { 133 | Ok(Certificate::SAT(v)) => { 134 | match output { 135 | Some(ref f) if redirect && !s.state.config.quiet_mode => println!( 136 | " Result|dump: to STDOUT instead of {} due to an IO error.", 137 | f.to_string_lossy(), 138 | ), 139 | Some(ref f) if !s.state.config.quiet_mode => { 140 | println!(" Result|file: {}", f.to_str().unwrap(),) 141 | } 142 | _ => (), 143 | } 144 | println!("{}: {}", colored(Ok(true), s.state.config.no_color), input); 145 | if let Err(why) = (|| { 146 | buf.write_all( 147 | format!("c This file was generated by splr-{VERSION} for {input}\nc \n") 148 | .as_bytes(), 149 | )?; 150 | report(s, buf)?; 151 | buf.write_all(b"s SATISFIABLE\nv ")?; 152 | for x in v { 153 | buf.write_all(format!("{x} ").as_bytes())?; 154 | } 155 | buf.write(b"0\n") 156 | })() { 157 | println!("Abort: failed to save by {why}!"); 158 | } 159 | } 160 | Ok(Certificate::UNSAT) => { 161 | match output { 162 | Some(ref f) if redirect && !s.state.config.quiet_mode => println!( 163 | " Result|dump: to STDOUT instead of {} due to an IO error.", 164 | f.to_string_lossy(), 165 | ), 166 | Some(ref f) if !s.state.config.quiet_mode => { 167 | println!(" Result|file: {}", f.to_str().unwrap(),) 168 | } 169 | _ => (), 170 | } 171 | if s.state.config.use_certification { 172 | s.save_certification(); 173 | println!( 174 | " Certificate|file: {}", 175 | s.state.config.io_pfile.to_string_lossy() 176 | ); 177 | } 178 | println!("{}: {}", colored(Ok(false), s.state.config.no_color), input); 179 | if let Err(why) = (|| { 180 | buf.write_all( 181 | format!( 182 | "c The empty assignment set generated by splr-{VERSION} for {input}\nc \n", 183 | ) 184 | .as_bytes(), 185 | )?; 186 | report(s, &mut buf)?; 187 | buf.write_all(b"s UNSATISFIABLE\n")?; 188 | buf.write_all(b"0\n") 189 | })() { 190 | println!("Abort: failed to save by {why}!"); 191 | } 192 | } 193 | Err(e) => { 194 | match output { 195 | Some(ref f) if redirect && !s.state.config.quiet_mode => println!( 196 | " Result|dump: to STDOUT instead of {} due to an IO error.", 197 | f.to_string_lossy(), 198 | ), 199 | Some(ref f) if !s.state.config.quiet_mode => { 200 | println!(" Result|file: {}", f.to_str().unwrap(),) 201 | } 202 | _ => (), 203 | } 204 | println!( 205 | "{} ({}): {}", 206 | colored(Err(e), s.state.config.no_color), 207 | e, 208 | input 209 | ); 210 | if let Err(why) = (|| { 211 | buf.write_all( 212 | format!("c An assignment set generated by splr-{VERSION} for {input}\nc \n",) 213 | .as_bytes(), 214 | )?; 215 | report(s, buf)?; 216 | buf.write_all(format!("c {}\n{}\n", e, colored(Err(e), true)).as_bytes())?; 217 | buf.write(b"0\n") 218 | })() { 219 | println!("Abort: failed to save by {why}!"); 220 | } 221 | } 222 | } 223 | } 224 | 225 | fn report(s: &Solver, out: &mut dyn Write) -> std::io::Result<()> { 226 | let state = &s.state; 227 | let elapsed: Duration = s.state.start.elapsed(); 228 | let tm: f64 = elapsed.as_millis() as f64 / 1_000.0; 229 | out.write_all( 230 | format!( 231 | "c {:<43}, #var:{:9}, #cls:{:9}\n", 232 | state 233 | .config 234 | .cnf_file 235 | .file_name() 236 | .map_or(Cow::from("file with strange chars"), |f| f 237 | .to_string_lossy()), 238 | state.target.num_of_variables, 239 | state.target.num_of_clauses, 240 | ) 241 | .as_bytes(), 242 | )?; 243 | out.write_all( 244 | format!( 245 | "c #conflict:{:>11}, #decision:{:>13}, #propagate:{:>15},\n", 246 | state[LogUsizeId::NumConflict], 247 | state[LogUsizeId::NumDecision], 248 | state[LogUsizeId::NumPropagate], 249 | ) 250 | .as_bytes(), 251 | )?; 252 | out.write_all( 253 | format!( 254 | "c Assignment|#rem:{:>9}, #fix:{:>9}, #elm:{:>9}, prg%:{:>9.4},\n", 255 | state[LogUsizeId::RemainingVar], 256 | state[LogUsizeId::AssertedVar], 257 | state[LogUsizeId::EliminatedVar], 258 | state[LogF64Id::Progress], 259 | ) 260 | .as_bytes(), 261 | )?; 262 | out.write_all( 263 | format!( 264 | "c Clause|Remv:{:>9}, LBD2:{:>9}, BinC:{:>9}, Perm:{:>9},\n", 265 | state[LogUsizeId::RemovableClause], 266 | state[LogUsizeId::LBD2Clause], 267 | state[LogUsizeId::BiClause], 268 | state[LogUsizeId::PermanentClause], 269 | ) 270 | .as_bytes(), 271 | )?; 272 | out.write_all( 273 | format!( 274 | "c Conflict|entg:{:>9.4}, cLvl:{:>9.4}, bLvl:{:>9.4}, /cpr:{:>9.2},\n", 275 | state[LogF64Id::LiteralBlockEntanglement], 276 | state[LogF64Id::CLevel], 277 | state[LogF64Id::BLevel], 278 | state[LogF64Id::ConflictPerRestart], 279 | ) 280 | .as_bytes(), 281 | )?; 282 | out.write_all( 283 | format!( 284 | "c Learing|avrg:{:>9.4}, trnd:{:>9.4}, #RST:{:>9}, /dpc:{:>9.2},\n", 285 | state[LogF64Id::EmaLBD], 286 | state[LogF64Id::TrendLBD], 287 | state[LogUsizeId::Restart], 288 | state[LogF64Id::DecisionPerConflict], 289 | ) 290 | .as_bytes(), 291 | )?; 292 | out.write_all( 293 | format!( 294 | "c misc|vivC:{:>9}, subC:{:>9}, core:{:>9}, /ppc:{:>9.2},\n", 295 | state[LogUsizeId::VivifiedClause], 296 | state[LogUsizeId::SubsumedClause], 297 | state[LogUsizeId::UnreachableCore], 298 | state[LogF64Id::PropagationPerConflict], 299 | ) 300 | .as_bytes(), 301 | )?; 302 | out.write_all(format!("c Strategy|mode: generic, time:{tm:9.2},\n").as_bytes())?; 303 | out.write_all("c \n".as_bytes())?; 304 | for key in &config::property::F64S { 305 | out.write_all( 306 | format!( 307 | "c config::{:<27}{:>19.3}\n", 308 | format!("{key:?}"), 309 | s.state.config.derefer(*key), 310 | ) 311 | .as_bytes(), 312 | )?; 313 | } 314 | for key in &assign::property::USIZES { 315 | out.write_all( 316 | format!( 317 | "c assign::{:<27}{:>15}\n", 318 | format!("{key:?}"), 319 | s.asg.derefer(*key), 320 | ) 321 | .as_bytes(), 322 | )?; 323 | } 324 | for key in &assign::property::EMAS { 325 | out.write_all( 326 | format!( 327 | "c assign::{:<27}{:>19.3}\n", 328 | format!("{key:?}"), 329 | s.asg.refer(*key).get(), 330 | ) 331 | .as_bytes(), 332 | )?; 333 | } 334 | for key in &cdb::property::USIZES { 335 | out.write_all( 336 | format!( 337 | "c clause::{:<27}{:>15}\n", 338 | format!("{key:?}"), 339 | s.cdb.derefer(*key), 340 | ) 341 | .as_bytes(), 342 | )?; 343 | } 344 | for key in &cdb::property::F64 { 345 | out.write_all( 346 | format!( 347 | "c clause::{:<27}{:>19.3}\n", 348 | format!("{key:?}"), 349 | s.cdb.derefer(*key), 350 | ) 351 | .as_bytes(), 352 | )?; 353 | } 354 | for key in &state::property::USIZES { 355 | out.write_all( 356 | format!( 357 | "c state::{:<28}{:>15}\n", 358 | format!("{key:?}"), 359 | s.state.derefer(*key), 360 | ) 361 | .as_bytes(), 362 | )?; 363 | } 364 | for key in &state::property::EMAS { 365 | out.write_all( 366 | format!( 367 | "c state::{:<28}{:>19.3}\n", 368 | format!("{key:?}"), 369 | s.state.refer(*key).get(), 370 | ) 371 | .as_bytes(), 372 | )?; 373 | } 374 | 375 | out.write_all(b"c \n")?; 376 | Ok(()) 377 | } 378 | -------------------------------------------------------------------------------- /src/processor/eliminate.rs: -------------------------------------------------------------------------------- 1 | /// Module `eliminator` implements clause subsumption and var elimination. 2 | use { 3 | super::Eliminator, 4 | crate::{assign::AssignIF, cdb::ClauseDBIF, solver::SolverEvent, state::State, types::*}, 5 | }; 6 | 7 | // Stop elimination if a generated resolvent is larger than this 8 | const COMBINATION_LIMIT: f64 = 32.0; 9 | 10 | pub fn eliminate_var( 11 | asg: &mut impl AssignIF, 12 | cdb: &mut impl ClauseDBIF, 13 | elim: &mut Eliminator, 14 | state: &mut State, 15 | vi: VarId, 16 | timedout: &mut usize, 17 | ) -> MaybeInconsistent { 18 | let v = &mut asg.var(vi); 19 | let w = &mut elim.var[vi]; 20 | if asg.assign(vi).is_some() || w.aborted { 21 | return Ok(()); 22 | } 23 | debug_assert!(!v.is(FlagVar::ELIMINATED)); 24 | // count only alive clauses 25 | // Note: it may contain the target literal somehow. So the following may be failed. 26 | // debug_assert!(w.pos_occurs.iter().all(|c| cdb[*c].is_dead() || cdb[*c].contains(Lit::from((vi, true))))); 27 | w.pos_occurs 28 | .retain(|&c| cdb[c].contains(Lit::from((vi, true)))); 29 | // debug_assert!(w.pos_occurs.iter().all(|c| cdb[*c].is_dead() || cdb[*c].contains(Lit::from((vi, false))))); 30 | w.neg_occurs 31 | .retain(|&c| cdb[c].contains(Lit::from((vi, false)))); 32 | 33 | let num_combination = w.pos_occurs.len() * w.neg_occurs.len(); 34 | 35 | if *timedout < num_combination 36 | || skip_var_elimination( 37 | asg, 38 | cdb, 39 | &w.pos_occurs, 40 | &w.neg_occurs, 41 | vi, 42 | elim.eliminate_grow_limit, 43 | ) 44 | { 45 | return Ok(()); 46 | } else { 47 | *timedout -= num_combination; 48 | } 49 | let pos = w.pos_occurs.clone(); 50 | let neg = w.neg_occurs.clone(); 51 | #[cfg(feature = "trace_elimination")] 52 | println!("# eliminate_var {}", vi); 53 | // OK, eliminate the literal and build constraints on it. 54 | make_eliminated_clauses(cdb, &mut elim.elim_lits, vi, &pos, &neg); 55 | let vec = &mut state.new_learnt; 56 | // println!("eliminate_var {}: |p|: {} and |n|: {}", vi, (*pos).len(), (*neg).len()); 57 | // Produce clauses in cross product: 58 | for p in pos.iter() { 59 | let learnt_p = cdb[*p].is(FlagClause::LEARNT); 60 | for n in neg.iter() { 61 | match merge(asg, cdb, *p, *n, vi, vec) { 62 | 0 => { 63 | #[cfg(feature = "trace_elimination")] 64 | println!( 65 | " - eliminate_var {}: fusion {}{} and {}{}", 66 | vi, p, cdb[*p], n, cdb[*n], 67 | ); 68 | } 69 | 1 => { 70 | let lit = vec[0]; 71 | #[cfg(feature = "trace_elimination")] 72 | println!( 73 | " - eliminate_var {}: found assign {} from {}{} and {}{}", 74 | vi, lit, p, cdb[*p], n, cdb[*n], 75 | ); 76 | match asg.assigned(lit) { 77 | Some(true) => (), 78 | Some(false) => { 79 | return Err(SolverError::RootLevelConflict(( 80 | lit, 81 | asg.reason(lit.vi()), 82 | ))); 83 | } 84 | None => { 85 | debug_assert!(asg.assigned(lit).is_none()); 86 | cdb.certificate_add_assertion(lit); 87 | asg.assign_at_root_level(lit)?; 88 | } 89 | } 90 | } 91 | _ => { 92 | debug_assert!(vec.iter().all(|l| !vec.contains(&!*l))); 93 | match cdb.new_clause(asg, vec, learnt_p && cdb[*n].is(FlagClause::LEARNT)) { 94 | RefClause::Clause(ci) => { 95 | // the merged clause might be a duplicated clause. 96 | elim.add_cid_occur(asg, ci, &mut cdb[ci], true); 97 | 98 | #[cfg(feature = "trace_elimination")] 99 | println!( 100 | " - eliminate_var {}: X {} from {} and {}", 101 | vi, cdb[ci], cdb[*p], cdb[*n], 102 | ); 103 | } 104 | RefClause::Dead => (), 105 | RefClause::EmptyClause => (), 106 | RefClause::RegisteredClause(_) => (), 107 | RefClause::UnitClause(_) => (), 108 | } 109 | } 110 | } 111 | } 112 | } 113 | // 114 | //## VAR ELIMINATION 115 | // 116 | debug_assert!(pos.iter().all(|cid| !cdb[*cid].is_dead())); 117 | debug_assert!(neg.iter().all(|cid| !cdb[*cid].is_dead())); 118 | for cid in pos.iter() { 119 | if cdb[*cid].is_dead() { 120 | continue; 121 | } 122 | #[cfg(feature = "incremental_solver")] 123 | { 124 | if !cdb[*cid].is(FlagClause::LEARNT) { 125 | cdb.make_permanent_immortal(*cid); 126 | } 127 | } 128 | elim.remove_cid_occur(asg, *cid, &mut cdb[*cid]); 129 | cdb.remove_clause(*cid); 130 | } 131 | for cid in neg.iter() { 132 | if cdb[*cid].is_dead() { 133 | continue; 134 | } 135 | #[cfg(feature = "incremental_solver")] 136 | { 137 | if !cdb[*cid].is(FlagClause::LEARNT) { 138 | cdb.make_permanent_immortal(*cid); 139 | } 140 | } 141 | elim.remove_cid_occur(asg, *cid, &mut cdb[*cid]); 142 | cdb.remove_clause(*cid); 143 | } 144 | elim[vi].clear(); 145 | asg.handle(SolverEvent::Eliminate(vi)); 146 | state.restart.handle(SolverEvent::Eliminate(vi)); 147 | elim.backward_subsumption_check(asg, cdb, timedout) 148 | } 149 | 150 | /// returns `true` if elimination is impossible. 151 | fn skip_var_elimination( 152 | asg: &impl AssignIF, 153 | cdb: &impl ClauseDBIF, 154 | pos: &[ClauseId], 155 | neg: &[ClauseId], 156 | v: VarId, 157 | grow_limit: usize, 158 | ) -> bool { 159 | // avoid thrashing 160 | let limit = match cdb.check_size() { 161 | Ok(true) => grow_limit, 162 | Ok(false) => grow_limit / 4, 163 | Err(_) => return true, 164 | }; 165 | let clslen = pos.len() + neg.len(); 166 | let mut cnt = 0; 167 | let scale: f64 = 0.5; 168 | let mut average_len: f64 = 0.0; 169 | for c_pos in pos { 170 | for c_neg in neg { 171 | if let Some(clause_size) = merge_cost(asg, cdb, *c_pos, *c_neg, v) { 172 | if clause_size == 0 { 173 | continue; 174 | } 175 | cnt += 1; 176 | average_len *= 1.0 - scale; 177 | average_len += scale * clause_size as f64; 178 | if clslen + limit < cnt || COMBINATION_LIMIT < average_len { 179 | return true; 180 | } 181 | } else { 182 | debug_assert!(false, "impossible"); 183 | } 184 | } 185 | } 186 | false 187 | } 188 | 189 | /// Returns the the-size-of-clause-being-generated. 190 | /// - `(false, -)` if one of the clauses is always satisfied. 191 | /// - `(true, n)` if they are merge-able to a n-literal clause. 192 | fn merge_cost( 193 | asg: &impl AssignIF, 194 | cdb: &impl ClauseDBIF, 195 | cp: ClauseId, 196 | cq: ClauseId, 197 | vi: VarId, 198 | ) -> Option { 199 | let c_p = &cdb[cp]; 200 | let c_q = &cdb[cq]; 201 | let mut cond: Option = None; 202 | let mut cond2: Option = None; 203 | let mut count = 0; 204 | 205 | 'next_lit: for lit in c_p.iter() { 206 | if lit.vi() == vi { 207 | cond = Some(*lit); 208 | continue; 209 | } 210 | debug_assert!(!asg.var(lit.vi()).is(FlagVar::ELIMINATED)); 211 | // if this is the last occurrence of this literal, count it. 212 | for l in c_q.iter() { 213 | if !*lit == *l { 214 | return Some(0); 215 | } else if *lit == *l || asg.var(l.vi()).is(FlagVar::ELIMINATED) { 216 | continue 'next_lit; 217 | } 218 | } 219 | count += 1; 220 | } 221 | cond?; 222 | for lit in c_q.iter() { 223 | if lit.vi() == vi { 224 | if cond == Some(!*lit) { 225 | cond2 = Some(*lit); 226 | continue; 227 | } else { 228 | return None; 229 | } 230 | } 231 | debug_assert!(!asg.var(lit.vi()).is(FlagVar::ELIMINATED)); 232 | count += 1; 233 | } 234 | cond2.map(|_| count) 235 | } 236 | 237 | /// Return the real length of the generated clause by merging two clauses. 238 | /// Return **zero** if one of the clauses is always satisfied. (merge_vec should not be used.) 239 | fn merge( 240 | asg: &mut impl AssignIF, 241 | cdb: &mut impl ClauseDBIF, 242 | cip: ClauseId, 243 | ciq: ClauseId, 244 | vi: VarId, 245 | vec: &mut Vec, 246 | ) -> usize { 247 | vec.clear(); 248 | let pqb = &cdb[cip]; 249 | let qpb = &cdb[ciq]; 250 | let ps_smallest = pqb.len() < qpb.len(); 251 | let (pb, qb) = if ps_smallest { (pqb, qpb) } else { (qpb, pqb) }; 252 | #[cfg(feature = "trace_elimination")] 253 | println!("# merge {} & {}", pb, qb); 254 | if pb.iter().filter(|l| l.vi() != vi).any(|l| qb.contains(!*l)) { 255 | return 0; 256 | } 257 | 258 | let mut lits = pb 259 | .iter() 260 | .filter(|l| l.vi() != vi && !qb.contains(**l)) 261 | .copied() 262 | .collect::>(); 263 | lits.append( 264 | &mut qb 265 | .iter() 266 | .filter(|l| l.vi() != vi) 267 | .copied() 268 | .collect::>(), 269 | ); 270 | std::mem::swap(&mut lits, vec); 271 | debug_assert!(vec.iter().all(|l| !asg.var(l.vi()).is(FlagVar::ELIMINATED))); 272 | debug_assert!(vec.iter().all(|l| l.vi() != vi)); 273 | vec.len() 274 | } 275 | 276 | fn make_eliminated_clauses( 277 | cdb: &mut impl ClauseDBIF, 278 | store: &mut Vec, 279 | v: VarId, 280 | pos: &[ClauseId], 281 | neg: &[ClauseId], 282 | ) { 283 | if neg.len() < pos.len() { 284 | for cid in neg { 285 | debug_assert!(!cdb[*cid].is_dead()); 286 | make_eliminated_clause(cdb, store, v, *cid); 287 | } 288 | make_eliminating_unit_clause(store, Lit::from((v, true))); 289 | } else { 290 | for cid in pos { 291 | debug_assert!(!cdb[*cid].is_dead()); 292 | make_eliminated_clause(cdb, store, v, *cid); 293 | } 294 | make_eliminating_unit_clause(store, Lit::from((v, false))); 295 | } 296 | } 297 | 298 | fn make_eliminating_unit_clause(store: &mut Vec, x: Lit) { 299 | #[cfg(feature = "trace_elimination")] 300 | println!(" - eliminator save {}", x); 301 | store.push(x); 302 | store.push(Lit::from(1usize)); 303 | } 304 | 305 | fn make_eliminated_clause( 306 | cdb: &mut impl ClauseDBIF, 307 | store: &mut Vec, 308 | vi: VarId, 309 | cid: ClauseId, 310 | ) { 311 | let first = store.len(); 312 | // Copy clause to the vector. Remember the position where the variable 'v' occurs: 313 | let c = &cdb[cid]; 314 | debug_assert!(!c.is_empty()); 315 | for l in c.iter() { 316 | store.push(*l); 317 | if l.vi() == vi { 318 | let index = store.len() - 1; 319 | debug_assert_eq!(store[index], *l); 320 | debug_assert_eq!(store[index].vi(), vi); 321 | // swap the first literal with the 'v'. So that the literal containing 'v' will occur first in the clause. 322 | store.swap(index, first); 323 | } 324 | } 325 | // Store the length of the clause last: 326 | debug_assert_eq!(store[first].vi(), vi); 327 | store.push(Lit::from(c.len())); 328 | #[cfg(feature = "trace_elimination")] 329 | println!("# make_eliminated_clause: eliminate({}) clause {}", vi, c); 330 | } 331 | 332 | #[cfg(test)] 333 | mod tests { 334 | use super::*; 335 | use crate::{assign::VarManipulateIF, cdb::ClauseDB, processor::EliminateIF, solver::Solver}; 336 | use ::std::path::Path; 337 | 338 | impl Clause { 339 | #[allow(dead_code)] 340 | fn as_vec(&self) -> Vec { 341 | self.iter().map(|l| i32::from(*l)).collect::>() 342 | } 343 | } 344 | impl ClauseDB { 345 | #[allow(dead_code)] 346 | fn as_vec(&self) -> Vec> { 347 | self.iter() 348 | .skip(1) 349 | .filter(|c| !c.is_dead()) 350 | .map(|c| c.as_vec()) 351 | .collect::>() 352 | } 353 | } 354 | #[cfg(not(feature = "no_IO"))] 355 | #[test] 356 | fn test_eliminate_var() { 357 | let Solver { 358 | ref mut asg, 359 | ref mut cdb, 360 | ref mut state, 361 | .. 362 | } = Solver::try_from(Path::new("cnfs/uf8.cnf")).expect("failed to load"); 363 | if !state.config.enable_eliminator { 364 | return; 365 | } 366 | let mut timedout = 10_000; 367 | let vi = 4; 368 | 369 | let mut elim = Eliminator::instantiate(&state.config, &state.cnf); 370 | elim.prepare(asg, cdb, true); 371 | eliminate_var(asg, cdb, &mut elim, state, vi, &mut timedout).expect("panic"); 372 | assert!(asg.var(vi).is(FlagVar::ELIMINATED)); 373 | assert!(cdb 374 | .iter() 375 | .skip(1) 376 | .filter(|c| c.is_dead()) 377 | .all(|c| c.is_empty())); 378 | assert!(cdb 379 | .iter() 380 | .skip(1) 381 | .all(|c| c.iter().all(|l| *l != Lit::from((vi, false))) 382 | && c.iter().all(|l| *l != Lit::from((vi, false))))); 383 | } 384 | } 385 | --------------------------------------------------------------------------------