use crate::{Ddnnf, NodeType};
use ddnnife_cnf::{Clause, Cnf, Literal};
use std::collections::HashMap;
type Clauses = Vec<Clause>;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
enum OperationType {
And,
Or,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
struct Operation {
op_type: OperationType,
literals: Vec<Literal>,
}
impl Operation {
pub fn len(&self) -> usize {
self.literals.len()
}
pub fn is_empty(&self) -> bool {
self.literals.is_empty()
}
}
#[derive(Debug, Clone)]
struct Biconditional {
index: usize,
op_type: OperationType,
literals: Vec<Literal>,
}
impl From<Biconditional> for Clauses {
fn from(biconditional: Biconditional) -> Self {
let tseitin_literal = biconditional.index as Literal;
match biconditional.op_type {
OperationType::And => {
let mut first = Vec::with_capacity(biconditional.literals.len() + 1);
first.push(tseitin_literal);
first.extend(biconditional.literals.iter().map(|literal| -literal));
let mut clauses = Vec::with_capacity(biconditional.literals.len() + 1);
clauses.push(first);
clauses.extend(
biconditional
.literals
.iter()
.map(|literal| vec![-tseitin_literal, *literal]),
);
clauses
}
OperationType::Or => {
let mut first = Vec::with_capacity(biconditional.literals.len() + 1);
first.push(-tseitin_literal);
first.append(&mut biconditional.literals.clone());
let mut clauses = Vec::with_capacity(biconditional.literals.len() + 1);
clauses.push(first);
clauses.extend(
biconditional
.literals
.iter()
.map(|literal| vec![tseitin_literal, -literal]),
);
clauses
}
}
}
}
impl From<&Ddnnf> for Cnf {
fn from(ddnnf: &Ddnnf) -> Self {
let mut tseitin_index = ddnnf.number_of_variables as usize + 1;
let mut biconditionals = Vec::new();
let mut node_literals = vec![0; ddnnf.nodes.len()];
let mut cache = HashMap::new();
ddnnf.nodes.iter().enumerate().for_each(|(index, node)| {
let literal = match &node.ntype {
NodeType::And { children } => transform_operation(
Operation {
op_type: OperationType::And,
literals: nodes_to_literals(children, &node_literals),
},
&mut biconditionals,
&mut cache,
&mut tseitin_index,
),
NodeType::Or { children } => transform_operation(
Operation {
op_type: OperationType::Or,
literals: nodes_to_literals(children, &node_literals),
},
&mut biconditionals,
&mut cache,
&mut tseitin_index,
),
NodeType::Literal { literal } => *literal as Literal,
NodeType::True => unreachable!(),
NodeType::False => unreachable!(),
};
node_literals[index] = literal;
});
if tseitin_index == ddnnf.number_of_variables as usize + 1 {
return Cnf::default();
}
let mut cnf: Clauses = biconditionals.into_iter().flat_map(Clauses::from).collect();
cnf.push(vec![tseitin_index as Literal - 1]);
cnf.into()
}
}
fn nodes_to_literals(nodes: &[usize], node_literals: &[Literal]) -> Vec<Literal> {
nodes.iter().map(|node| node_literals[*node]).collect()
}
fn transform_operation(
operation: Operation,
biconditionals: &mut Vec<Biconditional>,
cache: &mut HashMap<Operation, usize>,
tseitin_index: &mut usize,
) -> Literal {
if operation.is_empty() {
panic!("Attempt to transform empty operation.");
}
if operation.len() == 1 {
return operation.literals[0] as Literal;
}
if let Some(&literal) = cache.get(&operation) {
return literal as Literal;
}
let current_index = *tseitin_index;
*tseitin_index += 1;
biconditionals.push(Biconditional {
index: current_index,
op_type: operation.op_type,
literals: operation.literals.clone(),
});
cache.insert(operation, current_index);
current_index as Literal
}