use std::collections::{HashMap, HashSet};
use crate::formula::PropositionalFormula;
use log::debug;
#[derive(Debug, PartialEq, Clone)]
pub struct Theory {
formulas: HashSet<PropositionalFormula>,
}
impl Theory {
pub fn new() -> Self {
Self {
formulas: HashSet::new(),
}
}
pub fn from_propositional_formula(formula: PropositionalFormula) -> Self {
let mut formulas: HashSet<PropositionalFormula> = HashSet::new();
formulas.insert(formula);
Self { formulas }
}
pub fn formulas(&self) -> impl Iterator<Item = &PropositionalFormula> {
self.formulas.iter()
}
pub fn add(&mut self, formula: PropositionalFormula) {
self.formulas.insert(formula);
}
pub fn is_fully_expanded(&self) -> bool {
self.formulas.iter().all(PropositionalFormula::is_literal)
}
pub fn has_contradictions(&self) -> bool {
let mut literal_occurrence_map: HashMap<&str, (bool, bool)> = HashMap::new();
for formula in &self.formulas {
if self.check_formula(formula, &mut literal_occurrence_map) {
return true;
}
}
debug!("for the formulas:\n{:#?}", &self.formulas);
debug!("construct the HashSet:\n{:#?}", &literal_occurrence_map);
debug!("the theory contains no contradictions:\n{:#?}", &self);
false
}
fn check_formula<'a>(
&self,
formula: &'a PropositionalFormula,
literal_occurrence_map: &mut HashMap<&'a str, (bool, bool)>,
) -> bool {
match formula {
PropositionalFormula::Variable(v) => {
if let Some((has_literal, has_negation)) = literal_occurrence_map.get_mut(v.name())
{
if *has_negation {
true
} else {
*has_literal = true;
false
}
} else {
literal_occurrence_map.insert(v.name(), (true, false));
false
}
}
PropositionalFormula::Negation(Some(f)) => match &**f {
PropositionalFormula::Variable(v) => {
if let Some((has_literal, has_negation)) =
literal_occurrence_map.get_mut(v.name())
{
if *has_literal {
true
} else {
*has_negation = true;
false
}
} else {
literal_occurrence_map.insert(v.name(), (false, true));
false
}
}
PropositionalFormula::Negation(Some(ref g)) => {
self.check_formula(g, literal_occurrence_map)
}
_ => false,
},
_ => false,
}
}
pub fn get_non_literal_formula(&mut self) -> Option<PropositionalFormula> {
self.formulas.iter().cloned().find(|f| !f.is_literal())
}
pub fn swap_formula(
&mut self,
existing: &PropositionalFormula,
replacement: PropositionalFormula,
) {
if self.formulas.remove(existing) {
self.formulas.insert(replacement);
}
}
pub fn swap_formula2(
&mut self,
existing: &PropositionalFormula,
replacements: (PropositionalFormula, PropositionalFormula),
) {
if self.formulas.remove(existing) {
self.formulas.insert(replacements.0);
self.formulas.insert(replacements.1);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::formula::Variable;
use assert2::check;
#[test]
fn test_construction() {
let theory =
Theory::from_propositional_formula(PropositionalFormula::variable(Variable::new("a")));
check!(theory.formulas().count() == 1);
}
#[test]
fn test_get_formulas() {
let formula_1 = PropositionalFormula::variable(Variable::new("a"));
let formula_2 = PropositionalFormula::variable(Variable::new("b"));
let mut theory = Theory::new();
theory.add(formula_1);
theory.add(formula_2);
check!(theory.formulas().count() == 2);
}
#[test]
fn test_add_fresh_formula() {
let formula_1 = PropositionalFormula::variable(Variable::new("a"));
let mut theory = Theory::new();
check!(theory.formulas().count() == 0);
theory.add(formula_1);
check!(theory.formulas().count() == 1);
}
#[test]
fn test_add_duplicate_formula() {
let formula_1 = PropositionalFormula::variable(Variable::new("a"));
let mut theory = Theory::new();
check!(theory.formulas().count() == 0);
theory.add(formula_1.clone());
check!(theory.formulas().count() == 1);
theory.add(formula_1.clone());
check!(theory.formulas().count() == 1);
}
#[test]
fn test_all_fully_expanded() {
let formula_1 = PropositionalFormula::variable(Variable::new("a"));
let formula_2 = PropositionalFormula::negated(Box::new(PropositionalFormula::variable(
Variable::new("b"),
)));
let formula_3 = PropositionalFormula::variable(Variable::new("a"));
let mut theory = Theory::new();
theory.add(formula_1);
theory.add(formula_2);
theory.add(formula_3);
check!(theory.is_fully_expanded());
}
#[test]
fn test_partially_expanded() {
let formula_1 = PropositionalFormula::variable(Variable::new("a"));
let formula_2 = PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("b"))),
Box::new(PropositionalFormula::variable(Variable::new("c"))),
)));
let formula_3 = PropositionalFormula::variable(Variable::new("d"));
let mut theory = Theory::new();
theory.add(formula_1);
theory.add(formula_2);
theory.add(formula_3);
check!(!theory.is_fully_expanded());
}
#[test]
fn test_none_fully_expanded() {
let formula_1 =
PropositionalFormula::negated(Box::new(PropositionalFormula::biimplication(
Box::new(PropositionalFormula::variable(Variable::new("e"))),
Box::new(PropositionalFormula::variable(Variable::new("a"))),
)));
let formula_2 = PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("b"))),
Box::new(PropositionalFormula::variable(Variable::new("c"))),
)));
let formula_3 = PropositionalFormula::negated(Box::new(PropositionalFormula::negated(
Box::new(PropositionalFormula::variable(Variable::new("f"))),
)));
let mut theory = Theory::new();
theory.add(formula_1);
theory.add(formula_2);
theory.add(formula_3);
check!(!theory.is_fully_expanded());
}
#[test]
fn test_simple_has_contradictions() {
let literal_a = PropositionalFormula::variable(Variable::new("a"));
let negated_literal_a = PropositionalFormula::negated(Box::new(literal_a.clone()));
let mut theory = Theory::new();
theory.add(literal_a);
theory.add(negated_literal_a);
check!(theory.has_contradictions());
}
#[test]
fn test_simple_has_no_contradictions() {
let literal_a = PropositionalFormula::variable(Variable::new("a"));
let literal_b = PropositionalFormula::variable(Variable::new("b"));
let mut theory = Theory::new();
theory.add(literal_a);
theory.add(literal_b);
check!(!theory.has_contradictions());
}
#[test]
fn test_complex_has_contradictions() {
let literal_a = PropositionalFormula::variable(Variable::new("a"));
let non_literal_1 =
PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("b"))),
Box::new(PropositionalFormula::variable(Variable::new("c"))),
)));
let literal_d = PropositionalFormula::variable(Variable::new("d"));
let negated_literal_a = PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
));
let mut theory = Theory::new();
theory.add(literal_a);
theory.add(non_literal_1);
theory.add(literal_d);
theory.add(negated_literal_a);
check!(theory.has_contradictions());
}
#[test]
fn test_complex_has_no_contradictions() {
let literal_a = PropositionalFormula::variable(Variable::new("a"));
let non_literal_1 =
PropositionalFormula::negated(Box::new(PropositionalFormula::conjunction(
Box::new(PropositionalFormula::variable(Variable::new("b"))),
Box::new(PropositionalFormula::variable(Variable::new("c"))),
)));
let literal_d = PropositionalFormula::variable(Variable::new("d"));
let negated_literal_f = PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("f")),
));
let mut theory = Theory::new();
theory.add(literal_a);
theory.add(non_literal_1);
theory.add(literal_d);
theory.add(negated_literal_f);
check!(!theory.has_contradictions());
}
#[test]
fn test_double_negation_no_contradiction() {
let literal_a = PropositionalFormula::variable(Variable::new("a"));
let double_negated_literal_a =
PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
))));
let mut theory = Theory::new();
theory.add(literal_a);
theory.add(double_negated_literal_a);
check!(!theory.has_contradictions());
}
#[test]
fn test_recursive_negation_no_contradictions() {
let negated_literal_a = PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
));
let triple_negated_literal_a = PropositionalFormula::negated(Box::new(
PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
)))),
));
let mut theory = Theory::new();
theory.add(negated_literal_a);
theory.add(triple_negated_literal_a);
check!(!theory.has_contradictions());
}
#[test]
fn test_recursive_negation_has_contradictions() {
let negated_literal_a = PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
));
let quad_negated_literal_a =
PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::negated(Box::new(PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
)))),
))));
let mut theory = Theory::new();
theory.add(negated_literal_a);
theory.add(quad_negated_literal_a);
check!(theory.has_contradictions());
}
}