use crate::resolution::{Clause, Literal};
use std::collections::HashMap;
use std::fmt;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Var(pub u32);
impl fmt::Display for Var {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "v{}", self.0)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Formula {
Var(Var),
Not(Box<Formula>),
And(Vec<Formula>),
Or(Vec<Formula>),
Implies(Box<Formula>, Box<Formula>),
Iff(Box<Formula>, Box<Formula>),
Xor(Box<Formula>, Box<Formula>),
}
#[allow(dead_code)]
impl Formula {
#[must_use]
pub fn var(v: u32) -> Self {
Self::Var(Var(v))
}
#[must_use]
#[allow(clippy::should_implement_trait)]
pub fn not(f: Formula) -> Self {
Self::Not(Box::new(f))
}
#[must_use]
pub fn and(formulas: Vec<Formula>) -> Self {
Self::And(formulas)
}
#[must_use]
pub fn or(formulas: Vec<Formula>) -> Self {
Self::Or(formulas)
}
#[must_use]
pub fn implies(a: Formula, b: Formula) -> Self {
Self::Implies(Box::new(a), Box::new(b))
}
#[must_use]
pub fn iff(a: Formula, b: Formula) -> Self {
Self::Iff(Box::new(a), Box::new(b))
}
#[must_use]
pub fn xor(a: Formula, b: Formula) -> Self {
Self::Xor(Box::new(a), Box::new(b))
}
}
#[allow(dead_code)]
pub struct CnfTransformer {
next_var: u32,
#[allow(dead_code)]
tseitin_vars: HashMap<String, Var>,
clauses: Vec<Clause>,
}
#[allow(dead_code)]
impl CnfTransformer {
#[must_use]
pub fn new(first_var: u32) -> Self {
Self {
next_var: first_var,
tseitin_vars: HashMap::new(),
clauses: Vec::new(),
}
}
fn fresh_var(&mut self) -> Var {
let v = Var(self.next_var);
self.next_var += 1;
v
}
pub fn transform(&mut self, formula: &Formula) -> Var {
match formula {
Formula::Var(v) => *v,
Formula::Not(f) => {
let sub_var = self.transform(f);
let result_var = self.fresh_var();
self.clauses.push(Clause::new(vec![
Literal::neg(result_var.0),
Literal::neg(sub_var.0),
]));
self.clauses.push(Clause::new(vec![
Literal::pos(result_var.0),
Literal::pos(sub_var.0),
]));
result_var
}
Formula::And(formulas) => {
let sub_vars: Vec<Var> = formulas.iter().map(|f| self.transform(f)).collect();
let result_var = self.fresh_var();
let mut clause_lits = vec![Literal::pos(result_var.0)];
for &v in &sub_vars {
clause_lits.push(Literal::neg(v.0));
}
self.clauses.push(Clause::new(clause_lits));
for &v in &sub_vars {
self.clauses.push(Clause::new(vec![
Literal::neg(result_var.0),
Literal::pos(v.0),
]));
}
result_var
}
Formula::Or(formulas) => {
let sub_vars: Vec<Var> = formulas.iter().map(|f| self.transform(f)).collect();
let result_var = self.fresh_var();
let mut clause_lits = vec![Literal::neg(result_var.0)];
for &v in &sub_vars {
clause_lits.push(Literal::pos(v.0));
}
self.clauses.push(Clause::new(clause_lits));
for &v in &sub_vars {
self.clauses.push(Clause::new(vec![
Literal::pos(result_var.0),
Literal::neg(v.0),
]));
}
result_var
}
Formula::Implies(a, b) => {
let not_a = Formula::not((**a).clone());
let equiv = Formula::or(vec![not_a, (**b).clone()]);
self.transform(&equiv)
}
Formula::Iff(a, b) => {
let a_implies_b = Formula::implies((**a).clone(), (**b).clone());
let b_implies_a = Formula::implies((**b).clone(), (**a).clone());
let equiv = Formula::and(vec![a_implies_b, b_implies_a]);
self.transform(&equiv)
}
Formula::Xor(a, b) => {
let a_or_b = Formula::or(vec![(**a).clone(), (**b).clone()]);
let not_a_or_not_b = Formula::or(vec![
Formula::not((**a).clone()),
Formula::not((**b).clone()),
]);
let equiv = Formula::and(vec![a_or_b, not_a_or_not_b]);
self.transform(&equiv)
}
}
}
#[must_use]
pub fn clauses(&self) -> &[Clause] {
&self.clauses
}
pub fn take_clauses(self) -> Vec<Clause> {
self.clauses
}
#[must_use]
pub fn next_var(&self) -> u32 {
self.next_var
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CnfStats {
pub clause_count: usize,
pub literal_count: usize,
pub tseitin_vars: usize,
pub max_clause_size: usize,
pub avg_clause_size: f64,
}
#[allow(dead_code)]
impl CnfStats {
#[must_use]
pub fn compute(clauses: &[Clause], original_vars: u32, final_vars: u32) -> Self {
let clause_count = clauses.len();
let literal_count: usize = clauses.iter().map(|c| c.literals.len()).sum();
let max_clause_size = clauses.iter().map(|c| c.literals.len()).max().unwrap_or(0);
let avg_clause_size = if clause_count > 0 {
literal_count as f64 / clause_count as f64
} else {
0.0
};
let tseitin_vars = (final_vars - original_vars) as usize;
Self {
clause_count,
literal_count,
tseitin_vars,
max_clause_size,
avg_clause_size,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cnf_var() {
let mut transformer = CnfTransformer::new(1);
let formula = Formula::var(1);
let result = transformer.transform(&formula);
assert_eq!(result, Var(1));
assert_eq!(transformer.clauses().len(), 0);
}
#[test]
fn test_cnf_not() {
let mut transformer = CnfTransformer::new(2);
let formula = Formula::not(Formula::var(1));
let result = transformer.transform(&formula);
assert_eq!(result, Var(2));
assert_eq!(transformer.clauses().len(), 2);
}
#[test]
fn test_cnf_and() {
let mut transformer = CnfTransformer::new(3);
let formula = Formula::and(vec![Formula::var(1), Formula::var(2)]);
let result = transformer.transform(&formula);
assert_eq!(result, Var(3));
assert_eq!(transformer.clauses().len(), 3);
}
#[test]
fn test_cnf_or() {
let mut transformer = CnfTransformer::new(3);
let formula = Formula::or(vec![Formula::var(1), Formula::var(2)]);
let result = transformer.transform(&formula);
assert_eq!(result, Var(3));
assert_eq!(transformer.clauses().len(), 3);
}
#[test]
fn test_cnf_implies() {
let mut transformer = CnfTransformer::new(3);
let formula = Formula::implies(Formula::var(1), Formula::var(2));
let _result = transformer.transform(&formula);
assert!(!transformer.clauses().is_empty());
}
#[test]
fn test_cnf_iff() {
let mut transformer = CnfTransformer::new(3);
let formula = Formula::iff(Formula::var(1), Formula::var(2));
let _result = transformer.transform(&formula);
assert!(!transformer.clauses().is_empty());
}
#[test]
fn test_cnf_xor() {
let mut transformer = CnfTransformer::new(3);
let formula = Formula::xor(Formula::var(1), Formula::var(2));
let _result = transformer.transform(&formula);
assert!(!transformer.clauses().is_empty());
}
#[test]
fn test_cnf_complex() {
let mut transformer = CnfTransformer::new(4);
let left = Formula::and(vec![Formula::var(1), Formula::var(2)]);
let right = Formula::and(vec![Formula::var(2), Formula::var(3)]);
let formula = Formula::or(vec![left, right]);
let _result = transformer.transform(&formula);
assert!(!transformer.clauses().is_empty());
}
#[test]
fn test_cnf_stats() {
let clauses = vec![
Clause::new(vec![Literal::pos(1), Literal::neg(2)]),
Clause::new(vec![Literal::pos(2), Literal::pos(3), Literal::neg(4)]),
Clause::new(vec![Literal::neg(1)]),
];
let stats = CnfStats::compute(&clauses, 4, 10);
assert_eq!(stats.clause_count, 3);
assert_eq!(stats.literal_count, 6);
assert_eq!(stats.max_clause_size, 3);
assert_eq!(stats.tseitin_vars, 6);
assert!((stats.avg_clause_size - 2.0).abs() < 0.01);
}
}