use crate::formula::PropositionalFormula;
pub mod tableau;
pub mod theory;
pub use tableau::Tableau;
pub use theory::Theory;
use log::debug;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExpansionKind {
Alpha(Box<PropositionalFormula>, Option<Box<PropositionalFormula>>),
Beta(Box<PropositionalFormula>, Box<PropositionalFormula>),
}
pub fn is_satisfiable(propositional_formula: &PropositionalFormula) -> bool {
let mut tableau = Tableau::from_starting_propositional_formula(propositional_formula.clone());
debug!("starting with tableau:\n{:#?}", &tableau);
while !tableau.is_empty() {
let mut theory = tableau.pop_theory().unwrap();
debug!("current_theory:\n{:#?}", &theory);
if theory.is_fully_expanded() && !theory.has_contradictions() {
return true;
} else {
let non_literal_formula = theory.get_non_literal_formula().unwrap();
debug!("current non_literal: {:#?}", &non_literal_formula);
match expand_non_literal_formula(&non_literal_formula).unwrap() {
ExpansionKind::Alpha(literal_1, optional_literal_2) => {
debug!(
"apply alpha expansion: [LEFT = {:#?}], [RIGHT = {:#?}]",
&literal_1, &optional_literal_2
);
debug!("theory before expansion: {:#?}", &theory);
let mut new_theory = theory.clone();
debug!(
"new_theory before expansion:\n{:#?}",
&new_theory.formulas().collect::<Vec<_>>()
);
if let Some(literal_2) = optional_literal_2 {
new_theory.swap_formula2(&non_literal_formula, (*literal_1, *literal_2));
} else {
new_theory.swap_formula(&non_literal_formula, *literal_1);
}
debug!(
"new_theory after expansion:\n{:#?}",
&new_theory.formulas().collect::<Vec<_>>()
);
if !tableau.contains(&new_theory) && !new_theory.has_contradictions() {
tableau.push_theory(new_theory);
}
}
ExpansionKind::Beta(literal_1, literal_2) => {
let mut new_theory_1 = theory.clone();
let mut new_theory_2 = theory.clone();
new_theory_1.swap_formula(&non_literal_formula, *literal_1);
new_theory_2.swap_formula(&non_literal_formula, *literal_2);
if !tableau.contains(&new_theory_1) && !new_theory_1.has_contradictions() {
tableau.push_theory(new_theory_1);
}
if !tableau.contains(&new_theory_2) && !new_theory_2.has_contradictions() {
tableau.push_theory(new_theory_2);
}
}
}
}
}
false
}
fn expand_non_literal_formula(non_literal: &PropositionalFormula) -> Option<ExpansionKind> {
match non_literal {
PropositionalFormula::Conjunction(Some(a), Some(b)) => {
return Some(ExpansionKind::Alpha(a.clone(), Some(b.clone())));
}
PropositionalFormula::Biimplication(Some(a), Some(b)) => {
let alpha_1 = PropositionalFormula::implication(a.clone(), b.clone());
let alpha_2 = PropositionalFormula::implication(a.clone(), b.clone());
return Some(ExpansionKind::Alpha(
Box::new(alpha_1),
Some(Box::new(alpha_2)),
));
}
PropositionalFormula::Disjunction(Some(a), Some(b)) => {
return Some(ExpansionKind::Beta(a.clone(), b.clone()));
}
PropositionalFormula::Implication(Some(a), Some(b)) => {
let beta_1 = PropositionalFormula::negated(a.clone());
return Some(ExpansionKind::Beta(Box::new(beta_1), b.clone()));
}
PropositionalFormula::Negation(Some(f)) => match &**f {
PropositionalFormula::Negation(Some(a)) => {
return Some(ExpansionKind::Alpha(a.clone(), None));
}
PropositionalFormula::Disjunction(Some(a), Some(b)) => {
let alpha_1 = PropositionalFormula::negated(a.clone());
let alpha_2 = PropositionalFormula::negated(b.clone());
return Some(ExpansionKind::Alpha(
Box::new(alpha_1),
Some(Box::new(alpha_2)),
));
}
PropositionalFormula::Conjunction(Some(a), Some(b)) => {
let beta_1 = PropositionalFormula::negated(a.clone());
let beta_2 = PropositionalFormula::negated(b.clone());
return Some(ExpansionKind::Beta(Box::new(beta_1), Box::new(beta_2)));
}
PropositionalFormula::Implication(Some(a), Some(b)) => {
let alpha_2 = PropositionalFormula::negated(b.clone());
return Some(ExpansionKind::Alpha(a.clone(), Some(Box::new(alpha_2))));
}
PropositionalFormula::Biimplication(Some(a), Some(b)) => {
let beta_1 = PropositionalFormula::conjunction(
a.clone(),
Box::new(PropositionalFormula::negated(b.clone())),
);
let beta_2 = PropositionalFormula::conjunction(
b.clone(),
Box::new(PropositionalFormula::negated(a.clone())),
);
return Some(ExpansionKind::Beta(Box::new(beta_1), Box::new(beta_2)));
}
_ => {
return None;
}
},
_ => {
return None;
}
}
}
pub fn is_valid(formula: &PropositionalFormula) -> bool {
let negated_formula = PropositionalFormula::negated(Box::new(formula.clone()));
!is_satisfiable(&negated_formula)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::formula::Variable;
use assert2::check;
#[test]
fn test_propositional_variable() {
let formula = PropositionalFormula::variable(Variable::new("a"));
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_conjunction_same_variable() {
let formula = PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("a"))),
);
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_conjunction_different_variables() {
let formula = PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("b"))),
);
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_disjunction_same_variable() {
let formula = PropositionalFormula::disjunction(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("a"))),
);
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_disjunction_different_variables() {
let formula = PropositionalFormula::disjunction(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("b"))),
);
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_implication_different_variables() {
let formula = PropositionalFormula::implication(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("b"))),
);
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_biimplication_different_variables() {
let formula = PropositionalFormula::biimplication(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("b"))),
);
check!(is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_contradiction() {
let formula = PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
);
check!(!is_satisfiable(&formula));
check!(!is_valid(&formula));
}
#[test]
fn test_tautology_disjunction() {
let formula = PropositionalFormula::disjunction(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
);
check!(is_satisfiable(&formula));
check!(is_valid(&formula));
}
#[test]
fn test_tautology_disjunction_nested_negation() {
let formula = PropositionalFormula::disjunction(
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::negated(Box::new(PropositionalFormula::variable(
Variable::new("a"),
))),
))),
);
check!(is_satisfiable(&formula));
check!(is_valid(&formula));
}
#[test]
fn test_tautology_implication_literal() {
let formula = PropositionalFormula::implication(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("a"))),
);
check!(is_satisfiable(&formula));
check!(is_valid(&formula));
}
#[test]
fn test_tautology_implication_negated_literal() {
let formula = PropositionalFormula::implication(
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
);
check!(is_satisfiable(&formula));
check!(is_valid(&formula));
}
#[test]
fn test_tautology_biimplication_literal() {
let formula = PropositionalFormula::biimplication(
Box::new(PropositionalFormula::variable(Variable::new("a"))),
Box::new(PropositionalFormula::variable(Variable::new("a"))),
);
check!(is_satisfiable(&formula));
check!(is_valid(&formula));
}
#[test]
fn test_tautology_biimplication_negated_literal() {
let formula = PropositionalFormula::biimplication(
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))),
);
check!(is_satisfiable(&formula));
check!(is_valid(&formula));
}
}