Skip to main content

easy_sat_rs/
kernel.rs

1use crate::{
2    common::{ActivityTable, Clause, Literal, Phases, Variable, Watches},
3    constants::SATResult,
4};
5
6/// 求解过程统计信息。
7#[derive(Debug, Clone, Default)]
8pub struct Statistics {
9    /// 冲突次数。
10    pub conflicts: usize,
11    /// 决策次数。
12    pub decisions: usize,
13    /// 传播次数(预留计数位,便于后续扩展)。
14    pub propagations: usize,
15    /// 赋值次数。
16    pub assignments: usize,
17}
18
19/// CDCL 求解核心内核状态。
20///
21/// `Kernel` 聚合了:
22/// - CNF 子句库与 watcher 索引(2WL);
23/// - trail/决策层等搜索状态;
24/// - VSIDS 与相位策略;
25/// - 冲突分析与学习子句所需的临时缓存。
26pub struct Kernel {
27    /// 变量赋值表:`-1=false`,`0=unassigned`,`1=true`。
28    pub assignment: Vec<i8>,
29    /// 变量元信息表(层级、原因子句等)。
30    pub vars: Vec<Variable>,
31    /// 子句数据库(原始子句 + 学习子句)。
32    pub clauses: Vec<Clause>,
33    /// 2WL watcher 桶。
34    pub watches: Vec<Vec<Watches>>,
35    /// trail(仅存当前被赋为真的文字)。
36    pub trail: Vec<Literal>,
37    /// VSIDS 活跃度表。
38    pub vsids: ActivityTable,
39    /// 相位策略状态表。
40    pub phases: Phases,
41    /// 当前求解结果。
42    pub result: SATResult,
43
44    /// 当前决策层。
45    pub level: usize,
46    /// 冲突分析后要回跳到的层级。
47    pub backtrack_level: usize,
48    /// trail 中“已完成传播”的前缀长度。
49    pub propagated: usize,
50    /// 当前已赋值变量计数。
51    pub assigned: usize,
52
53    /// 当前冲突信息 `(clause_id, conflict_lit)`。
54    pub conflict: Option<(usize, Literal)>,
55    /// 冲突分析中的临时学习子句缓冲(lemma)。
56    pub lemma: Vec<Literal>,
57    /// 回跳后需要立即断言的 `(literal, reason_clause)`。
58    pub learnt: (Literal, Option<usize>),
59
60    /// 统计信息。
61    pub statistics: Statistics,
62    /// 冲突分析与 LBD 计算用的时间戳标记表。
63    mark_table: Vec<usize>,
64    /// 标记表当前 epoch。
65    mark_epoch: usize,
66    /// DIMACS 流式解析时暂存“尚未遇到 0 终止符”的子句。
67    pending_clause: Vec<Literal>,
68}
69
70impl Kernel {
71    /// 构建指定变量上限的求解内核。
72    pub fn new(max_vars: usize) -> Self {
73        Self {
74            // 变量编号从 1 开始,索引 0 保留不用。
75            assignment: vec![0; max_vars + 1],
76            vars: vec![Variable::default(); max_vars + 1],
77            clauses: vec![],
78            watches: vec![Vec::new(); max_vars * 2],
79            trail: vec![],
80            vsids: ActivityTable::new(max_vars),
81            phases: Phases::new(max_vars),
82            result: SATResult::UNKNOWN,
83            level: 0,
84            backtrack_level: 0,
85            propagated: 0,
86            assigned: 0,
87            conflict: None,
88            lemma: Vec::new(),
89            learnt: (0, None),
90            pending_clause: Vec::new(),
91            statistics: Statistics::default(),
92            mark_table: vec![0; max_vars + 1],
93            mark_epoch: 1,
94        }
95    }
96
97    /// 以流式方式添加子句文字。
98    ///
99    /// - `Some(lit)`:把文字加入当前待完成子句;
100    /// - `None`:结束当前子句,落库并挂接 watcher。
101    pub fn add(&mut self, lit: Option<Literal>) {
102        if let Some(lit) = lit {
103            self.pending_clause.push(lit);
104        } else {
105            let literals = std::mem::take(&mut self.pending_clause);
106            let clause = Clause::new().with_literals(literals).with_lbd(0);
107            let clause_id = self.clauses.len();
108            self.clauses.push(clause);
109            self.attach_clause_watchers(clause_id);
110        }
111    }
112
113    #[inline]
114    /// 在当前赋值下评估文字取值。
115    ///
116    /// 返回值约定:
117    /// - `1`:文字为真;
118    /// - `-1`:文字为假;
119    /// - `0`:变量尚未赋值。
120    pub fn value(&self, lit: Literal) -> i8 {
121        assert_ne!(lit, 0);
122        assert!(lit.unsigned_abs() < self.assignment.len());
123        let value = self.assignment[lit.unsigned_abs()];
124        if lit > 0 { value } else { -value }
125    }
126
127    /// 把非零文字映射到 watcher 桶索引。
128    ///
129    /// 每个变量对应两个桶:
130    /// - 正文字 `v` -> `2*(v-1)`
131    /// - 反文字 `-v` -> `2*(v-1)+1`
132    ///
133    /// 例:`v = 3` 时,
134    /// - `watch(3)` 的索引是 `4`
135    /// - `watch(-3)` 的索引是 `5`
136    #[inline]
137    fn watcher_index(&self, lit: Literal) -> usize {
138        assert_ne!(lit, 0);
139        assert!(lit.unsigned_abs() < self.assignment.len());
140        let var = lit.unsigned_abs();
141        ((var - 1) << 1) | usize::from(lit < 0)
142    }
143
144    /// 向 `watched_lit` 对应桶追加一个 watcher。
145    ///
146    /// 本实现遵循 2WL 常见约定:通常存入的是 `-w`(而非 `w`),
147    /// 即“让被监视文字 `w` 失效时会被触发”的桶,便于传播时 O(相关子句数) 访问。
148    #[inline]
149    pub fn add_watch(&mut self, watched_lit: Literal, watch: Watches) {
150        let idx = self.watcher_index(watched_lit);
151        self.watches[idx].push(watch);
152    }
153
154    /// 为新子句初始化 watcher。
155    ///
156    /// - 单子句 `(l)`:在 `watch(-l)` 挂一个 watcher;
157    /// - 非单子句 `(l0 ∨ l1 ∨ ...)`:
158    ///   - `l0` 的 watcher 放在 `watch(-l0)`,`blocker=l1`
159    ///   - `l1` 的 watcher 放在 `watch(-l1)`,`blocker=l0`
160    ///
161    /// 这样传播时只需读取被新真值文字触发的桶即可。
162    ///
163    /// 例:子句 `(x1 ∨ -x4 ∨ x7)` 初始挂接后:
164    /// - `watch(-x1)` 存一条 watcher,`blocker = -x4`
165    /// - `watch(x4)` 存一条 watcher,`blocker = x1`
166    fn attach_clause_watchers(&mut self, clause_id: usize) {
167        let Some(clause) = self.clauses.get(clause_id) else {
168            return;
169        };
170
171        let literals = clause.literals();
172        assert_ne!(literals.len(), 0);
173        if literals.len() == 1 {
174            let lit = literals[0];
175            self.add_watch(-lit, Watches { clause_id, blocker: lit });
176        } else {
177            let lit0 = literals[0];
178            let lit1 = literals[1];
179            self.add_watch(-lit0, Watches { clause_id, blocker: lit1 });
180            self.add_watch(-lit1, Watches { clause_id, blocker: lit0 });
181        }
182    }
183
184    /// 取走并返回 `lit` 的整桶 watcher。
185    ///
186    /// 传播阶段采用 “take -> 处理 -> set” 方式,避免借用冲突并允许 watcher 迁移。
187    pub fn watches(&mut self, lit: Literal) -> Vec<Watches> {
188        let idx = self.watcher_index(lit);
189        std::mem::take(&mut self.watches[idx])
190    }
191
192    /// 将传播后压实得到的 watcher 桶写回。
193    pub fn set_watches(&mut self, lit: Literal, ws: Vec<Watches>) {
194        let idx = self.watcher_index(lit);
195        self.watches[idx] = ws;
196    }
197
198    /// 写入一次变量赋值并记录到 trail。
199    ///
200    /// - `reason=None`:决策赋值;
201    /// - `reason=Some(clause_id)`:传播蕴含赋值。
202    ///
203    /// 例:若在第 5 层写入 `lit = -9` 且 `reason = Some(12)`,
204    /// 表示变量 `x9` 在第 5 层被子句 `12` 蕴含为假。
205    pub fn assign(&mut self, var_id: usize, lit: Literal, reason: Option<usize>) {
206        assert!(var_id > 0 && var_id < self.assignment.len());
207        let mut var = self.vars[var_id];
208        self.trail.push(lit);
209        var.level = self.level;
210        var.trail_index = self.trail.len();
211        var.reason = reason;
212        self.vars[var_id] = var;
213        self.assignment[var_id] = if lit > 0 { 1 } else { -1 };
214        self.phases.save_phase_for_variable(var_id, lit > 0);
215        self.assigned += 1;
216    }
217
218    #[inline]
219    /// 撤销变量赋值(用于回跳时弹栈)。
220    pub fn reset_value(&mut self, var_id: usize) {
221        assert!(var_id > 0 && var_id < self.assignment.len());
222        self.phases.save_phase_for_variable(var_id, self.assignment[var_id] > 0);
223        self.assignment[var_id] = 0;
224        self.assigned -= 1;
225    }
226
227    #[inline]
228    /// 申请一个新的标记 epoch。
229    ///
230    /// 若发生 `usize` 回绕,则清空标记表并从 1 重新开始。
231    pub fn next_mark_epoch(&mut self) -> usize {
232        self.mark_epoch = self.mark_epoch.wrapping_add(1);
233        if self.mark_epoch == 0 {
234            self.mark_epoch = 1;
235            self.mark_table.fill(0);
236        }
237        self.mark_epoch
238    }
239
240    #[inline]
241    /// 读取标记表在 `idx` 处的时间戳。
242    pub fn mark_at(&self, idx: usize) -> usize {
243        self.mark_table.get(idx).copied().unwrap_or(0)
244    }
245
246    #[inline]
247    /// 设置标记表 `idx` 的时间戳,不足则自动扩容。
248    pub fn set_mark_at(&mut self, idx: usize, stamp: usize) {
249        if idx >= self.mark_table.len() {
250            self.mark_table.resize(idx + 1, 0);
251        }
252        self.mark_table[idx] = stamp;
253    }
254
255    /// 新增学习子句并初始化 watcher,返回子句编号。
256    pub fn add_learned_clause_with_lbd(&mut self, literals: Vec<Literal>, lbd: u32) -> usize {
257        assert!(!literals.is_empty(), "learned clause should not be empty");
258        let clause = Clause::new().with_ordered_literals(literals).with_lbd(lbd);
259        let clause_id = self.clauses.len();
260        self.clauses.push(clause);
261        self.attach_clause_watchers(clause_id);
262        clause_id
263    }
264
265    /// 判断当前是否已给所有变量赋值。
266    pub fn satisfied(&self) -> bool {
267        self.assigned + 1 == self.assignment.len()
268    }
269}