b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 1 | /// A 2-SAT instance over boolean variables, solved through strongly |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 2 | /// connected components of the implication graph. |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 3 | pub(crate) struct TwoSat { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 4 | vars: usize, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 5 | clauses: Vec<(Lit, Lit)>, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 6 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 7 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 9 | pub(crate) struct Lit { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 10 | pub var: usize, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 11 | pub value: bool, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 12 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 13 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 14 | impl Lit { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 15 | pub fn new(var: usize, value: bool) -> Self { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 16 | Self { var, value } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 17 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 18 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 19 | fn node(self) -> usize { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 20 | 2 * self.var + usize::from(self.value) |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 21 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 22 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 23 | fn negated(self) -> Self { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 24 | Self { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 25 | var: self.var, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 26 | value: !self.value, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 27 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 28 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 29 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 30 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 31 | impl TwoSat { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 32 | pub fn new(vars: usize) -> Self { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 33 | Self { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 34 | vars, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 35 | clauses: Vec::new(), |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 36 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 37 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 38 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 39 | pub fn either(&mut self, a: Lit, b: Lit) { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 40 | self.clauses.push((a, b)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 41 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 42 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 43 | pub fn require(&mut self, a: Lit) { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 44 | self.clauses.push((a, a)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 45 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 46 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 47 | pub fn solve(&self) -> Option<Vec<bool>> { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 48 | let n = 2 * self.vars; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 49 | let mut graph = vec![Vec::new(); n]; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 50 | for &(a, b) in &self.clauses { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 51 | graph[a.negated().node()].push(b.node()); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 52 | graph[b.negated().node()].push(a.node()); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 53 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 54 | let comp = tarjan(&graph); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 55 | let mut out = Vec::with_capacity(self.vars); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 56 | for v in 0..self.vars { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 57 | let (f, t) = (comp[2 * v], comp[2 * v + 1]); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 58 | if f == t { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 59 | return None; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 60 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 61 | out.push(t < f); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 62 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 63 | Some(out) |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 64 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 65 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 66 | /// A solution that takes each variable's preferred value, in order, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 67 | /// whenever that stays satisfiable. |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 68 | pub fn solve_preferring(mut self, preferred: &[bool]) -> Option<Vec<bool>> { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 69 | self.solve()?; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 70 | for (var, &value) in preferred.iter().enumerate() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 71 | self.require(Lit::new(var, value)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 72 | if self.solve().is_none() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 73 | self.clauses.pop(); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 74 | self.require(Lit::new(var, !value)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 75 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 76 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 77 | self.solve() |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 78 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 79 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 80 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 81 | /// Tarjan's algorithm, iterative. Components come out in reverse |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 82 | /// topological order, so a lower index means later in the implication order. |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 83 | fn tarjan(graph: &[Vec<usize>]) -> Vec<usize> { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 84 | struct State { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 85 | index: Vec<usize>, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 86 | low: Vec<usize>, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 87 | on_stack: Vec<bool>, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 88 | stack: Vec<usize>, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 89 | next: usize, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 90 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 91 | impl State { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 92 | fn enter(&mut self, v: usize, work: &mut Vec<(usize, usize)>) { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 93 | self.index[v] = self.next; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 94 | self.low[v] = self.next; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 95 | self.next += 1; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 96 | self.stack.push(v); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 97 | self.on_stack[v] = true; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 98 | work.push((v, 0)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 99 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 100 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 101 | let n = graph.len(); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 102 | let mut st = State { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 103 | index: vec![usize::MAX; n], |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 104 | low: vec![0; n], |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 105 | on_stack: vec![false; n], |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 106 | stack: Vec::new(), |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 107 | next: 0, |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 108 | }; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 109 | let mut comp = vec![usize::MAX; n]; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 110 | let mut next_comp = 0; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 111 | let mut work = Vec::new(); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 112 | for root in 0..n { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 113 | if st.index[root] != usize::MAX { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 114 | continue; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 115 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 116 | st.enter(root, &mut work); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 117 | while let Some(&(v, edge)) = work.last() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 118 | if let Some(&w) = graph[v].get(edge) { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 119 | if let Some(top) = work.last_mut() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 120 | top.1 += 1; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 121 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 122 | if st.index[w] == usize::MAX { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 123 | st.enter(w, &mut work); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 124 | } else if st.on_stack[w] { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 125 | st.low[v] = st.low[v].min(st.index[w]); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 126 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 127 | continue; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 128 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 129 | work.pop(); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 130 | if let Some(&(parent, _)) = work.last() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 131 | st.low[parent] = st.low[parent].min(st.low[v]); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 132 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 133 | if st.low[v] == st.index[v] { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 134 | while let Some(w) = st.stack.pop() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 135 | st.on_stack[w] = false; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 136 | comp[w] = next_comp; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 137 | if w == v { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 138 | break; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 139 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 140 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 141 | next_comp += 1; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 142 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 143 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 144 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 145 | comp |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 146 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 147 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 148 | #[cfg(test)] |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 149 | mod tests { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 150 | use super::*; |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 151 | |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 152 | #[test] |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 153 | fn unsatisfiable() { |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 154 | let mut s = TwoSat::new(1); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 155 | s.require(Lit::new(0, true)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 156 | s.require(Lit::new(0, false)); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 157 | assert!(s.solve().is_none()); |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 158 | } |
b563d2da1c80 | atagen | 2026-10-16 08:25 UTC | 159 | } |