libprop_sat_solver/tableaux_solver/
mod.rs1use crate::formula::PropositionalFormula;
4
5pub mod tableau;
6pub mod theory;
7pub use tableau::Tableau;
8pub use theory::Theory;
9
10use log::debug;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ExpansionKind {
15 Alpha(Box<PropositionalFormula>, Option<Box<PropositionalFormula>>),
25 Beta(Box<PropositionalFormula>, Box<PropositionalFormula>),
34}
35
36pub fn is_satisfiable(propositional_formula: &PropositionalFormula) -> bool {
98 let mut tableau = Tableau::from_starting_propositional_formula(propositional_formula.clone());
99 debug!("starting with tableau:\n{:#?}", &tableau);
100
101 while !tableau.is_empty() {
102 let mut theory = tableau.pop_theory().unwrap();
104 debug!("current_theory:\n{:#?}", &theory);
105
106 if theory.is_fully_expanded() && !theory.has_contradictions() {
107 return true;
116 } else {
117 let non_literal_formula = theory.get_non_literal_formula().unwrap();
120 debug!("current non_literal: {:#?}", &non_literal_formula);
121
122 match expand_non_literal_formula(&non_literal_formula).unwrap() {
125 ExpansionKind::Alpha(literal_1, optional_literal_2) => {
126 debug!(
127 "apply alpha expansion: [LEFT = {:#?}], [RIGHT = {:#?}]",
128 &literal_1, &optional_literal_2
129 );
130
131 debug!("theory before expansion: {:#?}", &theory);
132 let mut new_theory = theory.clone();
134
135 debug!(
136 "new_theory before expansion:\n{:#?}",
137 &new_theory.formulas().collect::<Vec<_>>()
138 );
139
140 if let Some(literal_2) = optional_literal_2 {
141 new_theory.swap_formula2(&non_literal_formula, (*literal_1, *literal_2));
142 } else {
143 new_theory.swap_formula(&non_literal_formula, *literal_1);
144 }
145
146 debug!(
147 "new_theory after expansion:\n{:#?}",
148 &new_theory.formulas().collect::<Vec<_>>()
149 );
150
151 if !tableau.contains(&new_theory) && !new_theory.has_contradictions() {
152 tableau.push_theory(new_theory);
153 }
154 }
155 ExpansionKind::Beta(literal_1, literal_2) => {
156 let mut new_theory_1 = theory.clone();
157 let mut new_theory_2 = theory.clone();
158
159 new_theory_1.swap_formula(&non_literal_formula, *literal_1);
160 new_theory_2.swap_formula(&non_literal_formula, *literal_2);
161
162 if !tableau.contains(&new_theory_1) && !new_theory_1.has_contradictions() {
163 tableau.push_theory(new_theory_1);
164 }
165
166 if !tableau.contains(&new_theory_2) && !new_theory_2.has_contradictions() {
167 tableau.push_theory(new_theory_2);
168 }
169 }
170 }
171 }
172 }
173
174 false
178}
179
180fn expand_non_literal_formula(non_literal: &PropositionalFormula) -> Option<ExpansionKind> {
181 match non_literal {
182 PropositionalFormula::Conjunction(Some(a), Some(b)) => {
189 return Some(ExpansionKind::Alpha(a.clone(), Some(b.clone())));
190 }
191 PropositionalFormula::Biimplication(Some(a), Some(b)) => {
192 let alpha_1 = PropositionalFormula::implication(a.clone(), b.clone());
193 let alpha_2 = PropositionalFormula::implication(a.clone(), b.clone());
194 return Some(ExpansionKind::Alpha(
195 Box::new(alpha_1),
196 Some(Box::new(alpha_2)),
197 ));
198 }
199 PropositionalFormula::Disjunction(Some(a), Some(b)) => {
200 return Some(ExpansionKind::Beta(a.clone(), b.clone()));
201 }
202 PropositionalFormula::Implication(Some(a), Some(b)) => {
203 let beta_1 = PropositionalFormula::negated(a.clone());
204 return Some(ExpansionKind::Beta(Box::new(beta_1), b.clone()));
205 }
206
207 PropositionalFormula::Negation(Some(f)) => match &**f {
218 PropositionalFormula::Negation(Some(a)) => {
219 return Some(ExpansionKind::Alpha(a.clone(), None));
220 }
221 PropositionalFormula::Disjunction(Some(a), Some(b)) => {
222 let alpha_1 = PropositionalFormula::negated(a.clone());
223 let alpha_2 = PropositionalFormula::negated(b.clone());
224 return Some(ExpansionKind::Alpha(
225 Box::new(alpha_1),
226 Some(Box::new(alpha_2)),
227 ));
228 }
229 PropositionalFormula::Conjunction(Some(a), Some(b)) => {
230 let beta_1 = PropositionalFormula::negated(a.clone());
231 let beta_2 = PropositionalFormula::negated(b.clone());
232 return Some(ExpansionKind::Beta(Box::new(beta_1), Box::new(beta_2)));
233 }
234 PropositionalFormula::Implication(Some(a), Some(b)) => {
235 let alpha_2 = PropositionalFormula::negated(b.clone());
236 return Some(ExpansionKind::Alpha(a.clone(), Some(Box::new(alpha_2))));
237 }
238 PropositionalFormula::Biimplication(Some(a), Some(b)) => {
239 let beta_1 = PropositionalFormula::conjunction(
240 a.clone(),
241 Box::new(PropositionalFormula::negated(b.clone())),
242 );
243 let beta_2 = PropositionalFormula::conjunction(
244 b.clone(),
245 Box::new(PropositionalFormula::negated(a.clone())),
246 );
247
248 return Some(ExpansionKind::Beta(Box::new(beta_1), Box::new(beta_2)));
249 }
250 _ => {
251 return None;
252 }
253 },
254 _ => {
255 return None;
256 }
257 }
258}
259
260pub fn is_valid(formula: &PropositionalFormula) -> bool {
264 let negated_formula = PropositionalFormula::negated(Box::new(formula.clone()));
265 !is_satisfiable(&negated_formula)
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use crate::formula::Variable;
272 use assert2::check;
273
274 #[test]
275 fn test_propositional_variable() {
276 let formula = PropositionalFormula::variable(Variable::new("a"));
278
279 check!(is_satisfiable(&formula));
280 check!(!is_valid(&formula));
281 }
282
283 #[test]
284 fn test_conjunction_same_variable() {
285 let formula = PropositionalFormula::conjunction(
287 Box::new(PropositionalFormula::variable(Variable::new("a"))),
288 Box::new(PropositionalFormula::variable(Variable::new("a"))),
289 );
290
291 check!(is_satisfiable(&formula));
292 check!(!is_valid(&formula));
293 }
294
295 #[test]
296 fn test_conjunction_different_variables() {
297 let formula = PropositionalFormula::conjunction(
299 Box::new(PropositionalFormula::variable(Variable::new("a"))),
300 Box::new(PropositionalFormula::variable(Variable::new("b"))),
301 );
302
303 check!(is_satisfiable(&formula));
304 check!(!is_valid(&formula));
305 }
306
307 #[test]
308 fn test_disjunction_same_variable() {
309 let formula = PropositionalFormula::disjunction(
311 Box::new(PropositionalFormula::variable(Variable::new("a"))),
312 Box::new(PropositionalFormula::variable(Variable::new("a"))),
313 );
314
315 check!(is_satisfiable(&formula));
316 check!(!is_valid(&formula));
317 }
318
319 #[test]
320 fn test_disjunction_different_variables() {
321 let formula = PropositionalFormula::disjunction(
323 Box::new(PropositionalFormula::variable(Variable::new("a"))),
324 Box::new(PropositionalFormula::variable(Variable::new("b"))),
325 );
326
327 check!(is_satisfiable(&formula));
328 check!(!is_valid(&formula));
329 }
330
331 #[test]
332 fn test_implication_different_variables() {
333 let formula = PropositionalFormula::implication(
335 Box::new(PropositionalFormula::variable(Variable::new("a"))),
336 Box::new(PropositionalFormula::variable(Variable::new("b"))),
337 );
338
339 check!(is_satisfiable(&formula));
340 check!(!is_valid(&formula));
341 }
342
343 #[test]
344 fn test_biimplication_different_variables() {
345 let formula = PropositionalFormula::biimplication(
347 Box::new(PropositionalFormula::variable(Variable::new("a"))),
348 Box::new(PropositionalFormula::variable(Variable::new("b"))),
349 );
350
351 check!(is_satisfiable(&formula));
352 check!(!is_valid(&formula));
353 }
354
355 #[test]
356 fn test_contradiction() {
357 let formula = PropositionalFormula::conjunction(
359 Box::new(PropositionalFormula::variable(Variable::new("a"))),
360 Box::new(PropositionalFormula::negated(Box::new(
361 PropositionalFormula::variable(Variable::new("a")),
362 ))),
363 );
364
365 check!(!is_satisfiable(&formula));
366 check!(!is_valid(&formula));
367 }
368
369 #[test]
370 fn test_tautology_disjunction() {
371 let formula = PropositionalFormula::disjunction(
373 Box::new(PropositionalFormula::variable(Variable::new("a"))),
374 Box::new(PropositionalFormula::negated(Box::new(
375 PropositionalFormula::variable(Variable::new("a")),
376 ))),
377 );
378
379 check!(is_satisfiable(&formula));
380 check!(is_valid(&formula));
381 }
382
383 #[test]
384 fn test_tautology_disjunction_nested_negation() {
385 let formula = PropositionalFormula::disjunction(
387 Box::new(PropositionalFormula::negated(Box::new(
388 PropositionalFormula::variable(Variable::new("a")),
389 ))),
390 Box::new(PropositionalFormula::negated(Box::new(
391 PropositionalFormula::negated(Box::new(PropositionalFormula::variable(
392 Variable::new("a"),
393 ))),
394 ))),
395 );
396
397 check!(is_satisfiable(&formula));
398 check!(is_valid(&formula));
399 }
400
401 #[test]
402 fn test_tautology_implication_literal() {
403 let formula = PropositionalFormula::implication(
405 Box::new(PropositionalFormula::variable(Variable::new("a"))),
406 Box::new(PropositionalFormula::variable(Variable::new("a"))),
407 );
408
409 check!(is_satisfiable(&formula));
410 check!(is_valid(&formula));
411 }
412
413 #[test]
414 fn test_tautology_implication_negated_literal() {
415 let formula = PropositionalFormula::implication(
417 Box::new(PropositionalFormula::negated(Box::new(
418 PropositionalFormula::variable(Variable::new("a")),
419 ))),
420 Box::new(PropositionalFormula::negated(Box::new(
421 PropositionalFormula::variable(Variable::new("a")),
422 ))),
423 );
424
425 check!(is_satisfiable(&formula));
426 check!(is_valid(&formula));
427 }
428
429 #[test]
430 fn test_tautology_biimplication_literal() {
431 let formula = PropositionalFormula::biimplication(
433 Box::new(PropositionalFormula::variable(Variable::new("a"))),
434 Box::new(PropositionalFormula::variable(Variable::new("a"))),
435 );
436
437 check!(is_satisfiable(&formula));
438 check!(is_valid(&formula));
439 }
440
441 #[test]
442 fn test_tautology_biimplication_negated_literal() {
443 let formula = PropositionalFormula::biimplication(
445 Box::new(PropositionalFormula::negated(Box::new(
446 PropositionalFormula::variable(Variable::new("a")),
447 ))),
448 Box::new(PropositionalFormula::negated(Box::new(
449 PropositionalFormula::variable(Variable::new("a")),
450 ))),
451 );
452
453 check!(is_satisfiable(&formula));
454 check!(is_valid(&formula));
455 }
456}