#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Literal {
pub variable: usize,
pub positive: bool,
}
impl Literal {
#[must_use]
pub const fn positive(variable: usize) -> Self {
Self {
variable,
positive: true,
}
}
#[must_use]
pub const fn negative(variable: usize) -> Self {
Self {
variable,
positive: false,
}
}
#[must_use]
pub const fn negated(self) -> Self {
Self {
variable: self.variable,
positive: !self.positive,
}
}
const fn value(self, assignment: &[Option<bool>]) -> Option<bool> {
match assignment[self.variable] {
Some(value) => Some(value == self.positive),
None => None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CnfFormula {
variable_count: usize,
clauses: Vec<Vec<Literal>>,
}
impl CnfFormula {
#[must_use]
pub const fn new(variable_count: usize) -> Self {
Self {
variable_count,
clauses: Vec::new(),
}
}
pub fn add_clause(&mut self, literals: Vec<Literal>) {
self.clauses.push(literals);
}
#[allow(dead_code)] #[must_use]
pub const fn variable_count(&self) -> usize {
self.variable_count
}
#[must_use]
pub fn clauses(&self) -> &[Vec<Literal>] {
&self.clauses
}
#[must_use]
pub fn solve(&self) -> SatOutcome {
let mut assignment = vec![None; self.variable_count];
if search(&self.clauses, &mut assignment) {
let model = assignment
.into_iter()
.map(|value| value.unwrap_or(false))
.collect();
SatOutcome::Satisfiable(model)
} else {
SatOutcome::Unsatisfiable
}
}
#[allow(dead_code)] #[must_use]
pub fn is_satisfiable(&self) -> bool {
matches!(self.solve(), SatOutcome::Satisfiable(_))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SatOutcome {
Satisfiable(Vec<bool>),
Unsatisfiable,
}
enum ClauseStatus {
Satisfied,
Conflict,
Unit(Literal),
Unresolved,
}
fn clause_status(clause: &[Literal], assignment: &[Option<bool>]) -> ClauseStatus {
let mut unassigned = None;
let mut unassigned_count = 0usize;
for literal in clause {
match literal.value(assignment) {
Some(true) => return ClauseStatus::Satisfied,
Some(false) => {}
None => {
unassigned = Some(*literal);
unassigned_count += 1;
}
}
}
match unassigned_count {
0 => ClauseStatus::Conflict,
1 => ClauseStatus::Unit(unassigned.expect("one unassigned literal recorded")),
_ => ClauseStatus::Unresolved,
}
}
fn search(clauses: &[Vec<Literal>], assignment: &mut [Option<bool>]) -> bool {
let mut trail = Vec::new();
if !propagate(clauses, assignment, &mut trail) {
undo(assignment, &trail);
return false;
}
if all_satisfied(clauses, assignment) {
return true;
}
let Some(variable) = first_unassigned(assignment) else {
undo(assignment, &trail);
return false;
};
for value in [false, true] {
assignment[variable] = Some(value);
if search(clauses, assignment) {
return true;
}
assignment[variable] = None;
}
undo(assignment, &trail);
false
}
fn propagate(
clauses: &[Vec<Literal>],
assignment: &mut [Option<bool>],
trail: &mut Vec<usize>,
) -> bool {
loop {
let mut progressed = false;
for clause in clauses {
match clause_status(clause, assignment) {
ClauseStatus::Conflict => return false,
ClauseStatus::Unit(literal) => {
assignment[literal.variable] = Some(literal.positive);
trail.push(literal.variable);
progressed = true;
}
ClauseStatus::Satisfied | ClauseStatus::Unresolved => {}
}
}
if !progressed {
if let Some(literal) = find_pure_literal(clauses, assignment) {
assignment[literal.variable] = Some(literal.positive);
trail.push(literal.variable);
progressed = true;
}
}
if !progressed {
return true;
}
}
}
fn find_pure_literal(clauses: &[Vec<Literal>], assignment: &[Option<bool>]) -> Option<Literal> {
let mut seen_positive = vec![false; assignment.len()];
let mut seen_negative = vec![false; assignment.len()];
for clause in clauses {
if clause
.iter()
.any(|literal| literal.value(assignment) == Some(true))
{
continue;
}
for literal in clause {
if literal.value(assignment).is_some() {
continue;
}
if literal.positive {
seen_positive[literal.variable] = true;
} else {
seen_negative[literal.variable] = true;
}
}
}
for variable in 0..assignment.len() {
if assignment[variable].is_some() {
continue;
}
match (seen_positive[variable], seen_negative[variable]) {
(true, false) => return Some(Literal::positive(variable)),
(false, true) => return Some(Literal::negative(variable)),
_ => {}
}
}
None
}
fn all_satisfied(clauses: &[Vec<Literal>], assignment: &[Option<bool>]) -> bool {
clauses.iter().all(|clause| {
clause
.iter()
.any(|literal| literal.value(assignment) == Some(true))
})
}
fn first_unassigned(assignment: &[Option<bool>]) -> Option<usize> {
assignment.iter().position(Option::is_none)
}
fn undo(assignment: &mut [Option<bool>], trail: &[usize]) {
for &variable in trail {
assignment[variable] = None;
}
}