libprop_sat_solver/tableaux_solver/
theory.rs1use std::collections::{HashMap, HashSet};
5
6use crate::formula::PropositionalFormula;
7
8use log::debug;
9
10#[derive(Debug, PartialEq, Clone)]
14pub struct Theory {
15 formulas: HashSet<PropositionalFormula>,
16}
17
18impl Theory {
19 pub fn new() -> Self {
21 Self {
22 formulas: HashSet::new(),
23 }
24 }
25
26 pub fn from_propositional_formula(formula: PropositionalFormula) -> Self {
28 let mut formulas: HashSet<PropositionalFormula> = HashSet::new();
29 formulas.insert(formula);
30
31 Self { formulas }
32 }
33
34 pub fn formulas(&self) -> impl Iterator<Item = &PropositionalFormula> {
36 self.formulas.iter()
37 }
38
39 pub fn add(&mut self, formula: PropositionalFormula) {
42 self.formulas.insert(formula);
43 }
44
45 pub fn is_fully_expanded(&self) -> bool {
48 self.formulas.iter().all(PropositionalFormula::is_literal)
49 }
50
51 pub fn has_contradictions(&self) -> bool {
66 let mut literal_occurrence_map: HashMap<&str, (bool, bool)> = HashMap::new();
68
69 for formula in &self.formulas {
70 if self.check_formula(formula, &mut literal_occurrence_map) {
71 return true;
72 }
73 }
74
75 debug!("for the formulas:\n{:#?}", &self.formulas);
76 debug!("construct the HashSet:\n{:#?}", &literal_occurrence_map);
77 debug!("the theory contains no contradictions:\n{:#?}", &self);
78
79 false
82 }
83
84 fn check_formula<'a>(
85 &self,
86 formula: &'a PropositionalFormula,
87 literal_occurrence_map: &mut HashMap<&'a str, (bool, bool)>,
88 ) -> bool {
89 match formula {
90 PropositionalFormula::Variable(v) => {
91 if let Some((has_literal, has_negation)) = literal_occurrence_map.get_mut(v.name())
92 {
93 if *has_negation {
94 true
97 } else {
98 *has_literal = true;
99 false
100 }
101 } else {
102 literal_occurrence_map.insert(v.name(), (true, false));
103 false
104 }
105 }
106 PropositionalFormula::Negation(Some(f)) => match &**f {
107 PropositionalFormula::Variable(v) => {
108 if let Some((has_literal, has_negation)) =
109 literal_occurrence_map.get_mut(v.name())
110 {
111 if *has_literal {
112 true
115 } else {
116 *has_negation = true;
117 false
118 }
119 } else {
120 literal_occurrence_map.insert(v.name(), (false, true));
121 false
122 }
123 }
124 PropositionalFormula::Negation(Some(ref g)) => {
125 self.check_formula(g, literal_occurrence_map)
129 }
130 _ => false,
131 },
132 _ => false,
133 }
134 }
135
136 pub fn get_non_literal_formula(&mut self) -> Option<PropositionalFormula> {
139 self.formulas.iter().cloned().find(|f| !f.is_literal())
140 }
141
142 pub fn swap_formula(
144 &mut self,
145 existing: &PropositionalFormula,
146 replacement: PropositionalFormula,
147 ) {
148 if self.formulas.remove(existing) {
149 self.formulas.insert(replacement);
150 }
151 }
152
153 pub fn swap_formula2(
155 &mut self,
156 existing: &PropositionalFormula,
157 replacements: (PropositionalFormula, PropositionalFormula),
158 ) {
159 if self.formulas.remove(existing) {
160 self.formulas.insert(replacements.0);
161 self.formulas.insert(replacements.1);
162 }
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::formula::Variable;
170 use assert2::check;
171
172 #[test]
173 fn test_construction() {
174 let theory =
175 Theory::from_propositional_formula(PropositionalFormula::variable(Variable::new("a")));
176
177 check!(theory.formulas().count() == 1);
178 }
179
180 #[test]
181 fn test_get_formulas() {
182 let formula_1 = PropositionalFormula::variable(Variable::new("a"));
183 let formula_2 = PropositionalFormula::variable(Variable::new("b"));
184
185 let mut theory = Theory::new();
186 theory.add(formula_1);
187 theory.add(formula_2);
188
189 check!(theory.formulas().count() == 2);
190 }
191
192 #[test]
193 fn test_add_fresh_formula() {
194 let formula_1 = PropositionalFormula::variable(Variable::new("a"));
195
196 let mut theory = Theory::new();
197 check!(theory.formulas().count() == 0);
198
199 theory.add(formula_1);
200 check!(theory.formulas().count() == 1);
201 }
202
203 #[test]
204 fn test_add_duplicate_formula() {
205 let formula_1 = PropositionalFormula::variable(Variable::new("a"));
206
207 let mut theory = Theory::new();
208 check!(theory.formulas().count() == 0);
209
210 theory.add(formula_1.clone());
211 check!(theory.formulas().count() == 1);
212
213 theory.add(formula_1.clone());
214 check!(theory.formulas().count() == 1);
215 }
216
217 #[test]
218 fn test_all_fully_expanded() {
219 let formula_1 = PropositionalFormula::variable(Variable::new("a"));
220 let formula_2 = PropositionalFormula::negated(Box::new(PropositionalFormula::variable(
221 Variable::new("b"),
222 )));
223 let formula_3 = PropositionalFormula::variable(Variable::new("a"));
224
225 let mut theory = Theory::new();
226 theory.add(formula_1);
227 theory.add(formula_2);
228 theory.add(formula_3);
229
230 check!(theory.is_fully_expanded());
231 }
232
233 #[test]
234 fn test_partially_expanded() {
235 let formula_1 = PropositionalFormula::variable(Variable::new("a"));
236 let formula_2 = PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
237 Box::new(PropositionalFormula::variable(Variable::new("b"))),
238 Box::new(PropositionalFormula::variable(Variable::new("c"))),
239 )));
240 let formula_3 = PropositionalFormula::variable(Variable::new("d"));
241
242 let mut theory = Theory::new();
243 theory.add(formula_1);
244 theory.add(formula_2);
245 theory.add(formula_3);
246
247 check!(!theory.is_fully_expanded());
248 }
249
250 #[test]
251 fn test_none_fully_expanded() {
252 let formula_1 =
253 PropositionalFormula::negated(Box::new(PropositionalFormula::biimplication(
254 Box::new(PropositionalFormula::variable(Variable::new("e"))),
255 Box::new(PropositionalFormula::variable(Variable::new("a"))),
256 )));
257 let formula_2 = PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
258 Box::new(PropositionalFormula::variable(Variable::new("b"))),
259 Box::new(PropositionalFormula::variable(Variable::new("c"))),
260 )));
261 let formula_3 = PropositionalFormula::negated(Box::new(PropositionalFormula::negated(
262 Box::new(PropositionalFormula::variable(Variable::new("f"))),
263 )));
264
265 let mut theory = Theory::new();
266 theory.add(formula_1);
267 theory.add(formula_2);
268 theory.add(formula_3);
269
270 check!(!theory.is_fully_expanded());
271 }
272
273 #[test]
274 fn test_simple_has_contradictions() {
275 let literal_a = PropositionalFormula::variable(Variable::new("a"));
276 let negated_literal_a = PropositionalFormula::negated(Box::new(literal_a.clone()));
277
278 let mut theory = Theory::new();
279 theory.add(literal_a);
280 theory.add(negated_literal_a);
281
282 check!(theory.has_contradictions());
283 }
284
285 #[test]
286 fn test_simple_has_no_contradictions() {
287 let literal_a = PropositionalFormula::variable(Variable::new("a"));
288 let literal_b = PropositionalFormula::variable(Variable::new("b"));
289
290 let mut theory = Theory::new();
291 theory.add(literal_a);
292 theory.add(literal_b);
293
294 check!(!theory.has_contradictions());
295 }
296
297 #[test]
298 fn test_complex_has_contradictions() {
299 let literal_a = PropositionalFormula::variable(Variable::new("a"));
300 let non_literal_1 =
301 PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
302 Box::new(PropositionalFormula::variable(Variable::new("b"))),
303 Box::new(PropositionalFormula::variable(Variable::new("c"))),
304 )));
305 let literal_d = PropositionalFormula::variable(Variable::new("d"));
306 let negated_literal_a = PropositionalFormula::negated(Box::new(
307 PropositionalFormula::variable(Variable::new("a")),
308 ));
309
310 let mut theory = Theory::new();
311 theory.add(literal_a);
312 theory.add(non_literal_1);
313 theory.add(literal_d);
314 theory.add(negated_literal_a);
315
316 check!(theory.has_contradictions());
317 }
318
319 #[test]
320 fn test_complex_has_no_contradictions() {
321 let literal_a = PropositionalFormula::variable(Variable::new("a"));
322 let non_literal_1 =
323 PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
324 Box::new(PropositionalFormula::variable(Variable::new("b"))),
325 Box::new(PropositionalFormula::variable(Variable::new("c"))),
326 )));
327 let literal_d = PropositionalFormula::variable(Variable::new("d"));
328 let negated_literal_f = PropositionalFormula::negated(Box::new(
329 PropositionalFormula::variable(Variable::new("f")),
330 ));
331
332 let mut theory = Theory::new();
333 theory.add(literal_a);
334 theory.add(non_literal_1);
335 theory.add(literal_d);
336 theory.add(negated_literal_f);
337
338 check!(!theory.has_contradictions());
339 }
340
341 #[test]
342 fn test_double_negation_no_contradiction() {
343 let literal_a = PropositionalFormula::variable(Variable::new("a"));
345 let double_negated_literal_a =
346 PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
347 PropositionalFormula::variable(Variable::new("a")),
348 ))));
349
350 let mut theory = Theory::new();
351 theory.add(literal_a);
352 theory.add(double_negated_literal_a);
353
354 check!(!theory.has_contradictions());
355 }
356
357 #[test]
358 fn test_recursive_negation_no_contradictions() {
359 let negated_literal_a = PropositionalFormula::negated(Box::new(
361 PropositionalFormula::variable(Variable::new("a")),
362 ));
363 let triple_negated_literal_a = PropositionalFormula::negated(Box::new(
364 PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
365 PropositionalFormula::variable(Variable::new("a")),
366 )))),
367 ));
368
369 let mut theory = Theory::new();
370 theory.add(negated_literal_a);
371 theory.add(triple_negated_literal_a);
372
373 check!(!theory.has_contradictions());
374 }
375
376 #[test]
377 fn test_recursive_negation_has_contradictions() {
378 let negated_literal_a = PropositionalFormula::negated(Box::new(
380 PropositionalFormula::variable(Variable::new("a")),
381 ));
382 let quad_negated_literal_a =
383 PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
384 PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
385 PropositionalFormula::variable(Variable::new("a")),
386 )))),
387 ))));
388
389 let mut theory = Theory::new();
390 theory.add(negated_literal_a);
391 theory.add(quad_negated_literal_a);
392
393 check!(theory.has_contradictions());
394 }
395}