#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(
unsafe_code,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
use crate::sat::literal::{Literal, Variable};
use crate::sat::solver::Solutions;
use clap::ValueEnum;
use core::ops::{Index, IndexMut};
use itertools::Itertools;
use rustc_hash::FxHashMap;
use std::fmt::{Debug, Display};
use std::hash::Hash;
#[derive(Debug, Clone, PartialEq, Eq, Copy, Default, Hash, PartialOrd, Ord)]
pub enum VarState {
#[default]
Unassigned,
Assigned(bool),
}
impl VarState {
#[must_use]
pub const fn is_assigned(self) -> bool {
matches!(self, Self::Assigned(_))
}
#[must_use]
pub const fn is_unassigned(self) -> bool {
!self.is_assigned()
}
#[must_use]
pub const fn is_true(self) -> bool {
matches!(self, Self::Assigned(true))
}
#[must_use]
pub const fn is_false(self) -> bool {
matches!(self, Self::Assigned(false))
}
}
impl From<VarState> for Option<bool> {
fn from(s: VarState) -> Self {
match s {
VarState::Assigned(b) => Some(b),
VarState::Unassigned => None,
}
}
}
impl From<Option<bool>> for VarState {
fn from(b: Option<bool>) -> Self {
b.map_or(Self::Unassigned, VarState::Assigned)
}
}
pub trait Assignment:
Index<usize, Output = VarState> + IndexMut<usize, Output = VarState> + Debug + Clone
{
fn new(n_vars: usize) -> Self;
fn num_vars(&self) -> usize;
fn set(&mut self, var: Variable, b: bool);
fn reset(&mut self);
fn assign(&mut self, l: impl Literal) {
self.set(l.variable(), l.polarity());
}
fn unassign(&mut self, var: Variable);
fn get_solutions(&self) -> Solutions;
fn is_assigned(&self, var: Variable) -> bool {
self[var as usize].is_assigned()
}
fn var_value(&self, var: Variable) -> Option<bool> {
self[var as usize].into()
}
fn all_assigned(&self) -> bool;
fn literal_value(&self, l: impl Literal) -> Option<bool> {
self.var_value(l.variable()).map(|b| b == l.polarity())
}
fn unassigned(&self) -> impl Iterator<Item = Variable> + '_ {
(0..self.num_vars()).filter_map(move |i| {
#[allow(clippy::cast_possible_truncation)]
let var = i as Variable;
if self[var as usize].is_unassigned() {
Some(var)
} else {
None
}
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct VecAssignment {
states: Vec<VarState>,
}
impl Index<usize> for VecAssignment {
type Output = VarState;
fn index(&self, index: usize) -> &Self::Output {
unsafe { self.states.get_unchecked(index) }
}
}
impl IndexMut<usize> for VecAssignment {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
unsafe { self.states.get_unchecked_mut(index) }
}
}
impl Assignment for VecAssignment {
fn new(n_vars: usize) -> Self {
Self {
states: vec![VarState::Unassigned; n_vars],
}
}
fn num_vars(&self) -> usize {
self.states.len()
}
fn set(&mut self, var: Variable, b: bool) {
self[var as usize] = VarState::Assigned(b);
}
fn reset(&mut self) {
self.states.fill(VarState::Unassigned);
}
fn unassign(&mut self, var: Variable) {
self[var as usize] = VarState::Unassigned;
}
fn get_solutions(&self) -> Solutions {
Solutions::new(
&self
.states
.iter()
.enumerate()
.filter_map(|(i, s)| {
#[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
let var_id = i as i32;
match s {
VarState::Assigned(true) => Some(var_id),
VarState::Assigned(false) => Some(-var_id),
_ => None,
}
})
.collect_vec(),
)
}
fn all_assigned(&self) -> bool {
self.states.iter().all(|v| v.is_assigned())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HashMapAssignment {
map: FxHashMap<Variable, VarState>,
num_vars: usize,
}
impl Index<usize> for HashMapAssignment {
type Output = VarState;
fn index(&self, index: usize) -> &Self::Output {
#[allow(clippy::cast_possible_truncation)]
self.map
.get(&(index as Variable))
.unwrap_or(&VarState::Unassigned)
}
}
impl IndexMut<usize> for HashMapAssignment {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
#[allow(clippy::cast_possible_truncation)]
self.map
.entry(index as Variable)
.or_insert(VarState::Unassigned)
}
}
impl Assignment for HashMapAssignment {
fn new(n_vars: usize) -> Self {
Self {
map: FxHashMap::default(),
num_vars: n_vars,
}
}
fn num_vars(&self) -> usize {
self.num_vars
}
fn set(&mut self, var: Variable, b: bool) {
self.map.insert(var, VarState::Assigned(b));
}
fn reset(&mut self) {
self.map.clear();
}
fn unassign(&mut self, var: Variable) {
self.map.insert(var, VarState::Unassigned);
}
fn get_solutions(&self) -> Solutions {
Solutions::new(
&self
.map
.iter()
.filter_map(|(&var, s)| {
#[allow(clippy::cast_possible_wrap)]
let var_id = var as i32;
match s {
VarState::Assigned(true) => Some(var_id),
VarState::Assigned(false) => Some(-var_id),
_ => None,
}
})
.collect_vec(),
)
}
fn all_assigned(&self) -> bool {
self.map.len() == self.num_vars && self.map.values().all(|v| v.is_assigned())
}
}
#[derive(Clone, Debug)]
pub enum AssignmentImpls {
Vec(VecAssignment),
HashMap(HashMapAssignment),
}
impl Index<usize> for AssignmentImpls {
type Output = VarState;
fn index(&self, index: usize) -> &Self::Output {
match self {
Self::Vec(v) => v.index(index),
Self::HashMap(v) => v.index(index),
}
}
}
impl IndexMut<usize> for AssignmentImpls {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match self {
Self::Vec(v) => v.index_mut(index),
Self::HashMap(v) => v.index_mut(index),
}
}
}
impl Assignment for AssignmentImpls {
fn new(n_vars: usize) -> Self {
Self::Vec(VecAssignment::new(n_vars))
}
fn num_vars(&self) -> usize {
match self {
Self::Vec(v) => v.num_vars(),
Self::HashMap(v) => v.num_vars(),
}
}
fn set(&mut self, var: Variable, b: bool) {
match self {
Self::Vec(v) => v.set(var, b),
Self::HashMap(v) => v.set(var, b),
}
}
fn reset(&mut self) {
match self {
Self::Vec(v) => v.reset(),
Self::HashMap(v) => v.reset(),
}
}
fn unassign(&mut self, var: Variable) {
match self {
Self::Vec(v) => v.unassign(var),
Self::HashMap(v) => v.unassign(var),
}
}
fn get_solutions(&self) -> Solutions {
match self {
Self::Vec(v) => v.get_solutions(),
Self::HashMap(v) => v.get_solutions(),
}
}
fn all_assigned(&self) -> bool {
match self {
Self::Vec(v) => v.all_assigned(),
Self::HashMap(v) => v.all_assigned(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy, Default, Hash, ValueEnum)]
pub enum AssignmentType {
#[default]
Vec,
HashMap,
}
impl Display for AssignmentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Vec => write!(f, "vec"),
Self::HashMap => write!(f, "hash-map"),
}
}
}
impl AssignmentType {
#[must_use]
pub fn to_impl(self, n_vars: usize) -> AssignmentImpls {
match self {
Self::Vec => AssignmentImpls::Vec(VecAssignment::new(n_vars)),
Self::HashMap => AssignmentImpls::HashMap(HashMapAssignment::new(n_vars)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sat::literal::PackedLiteral;
#[test]
fn test_var_state() {
assert!(VarState::Unassigned.is_unassigned());
assert!(!VarState::Unassigned.is_assigned());
assert!(!VarState::Unassigned.is_true());
assert!(!VarState::Unassigned.is_false());
assert!(!VarState::Assigned(true).is_unassigned());
assert!(VarState::Assigned(true).is_assigned());
assert!(VarState::Assigned(true).is_true());
assert!(!VarState::Assigned(true).is_false());
assert!(!VarState::Assigned(false).is_unassigned());
assert!(VarState::Assigned(false).is_assigned());
assert!(!VarState::Assigned(false).is_true());
assert!(VarState::Assigned(false).is_false());
}
#[allow(clippy::cognitive_complexity)]
fn test_assignment<A: Assignment>(a: &mut A) {
a.set(1, true);
a.set(2, false);
a.set(3, true);
assert!(a.is_assigned(1));
assert!(a.is_assigned(2));
assert!(a.is_assigned(3));
assert!(!a.is_assigned(0));
assert_eq!(a.var_value(1), Some(true));
assert_eq!(a.var_value(2), Some(false));
assert_eq!(a.var_value(3), Some(true));
assert_eq!(a.var_value(0), None);
assert_eq!(a.literal_value(PackedLiteral::new(1, false)), Some(false));
assert_eq!(a.literal_value(PackedLiteral::new(1, true)), Some(true));
assert_eq!(a.literal_value(PackedLiteral::new(2, false)), Some(true));
assert_eq!(a.literal_value(PackedLiteral::new(2, true)), Some(false));
assert_eq!(a.literal_value(PackedLiteral::new(3, false)), Some(false));
assert_eq!(a.literal_value(PackedLiteral::new(3, true)), Some(true));
a.unassign(1);
assert!(!a.is_assigned(1));
assert_eq!(a.var_value(1), None);
assert_eq!(a.literal_value(PackedLiteral::new(1, false)), None);
let s = a.get_solutions();
assert_eq!(s, Solutions::new(&[-2, 3]));
let unassigned_vars = a.unassigned().collect_vec();
assert_eq!(unassigned_vars, vec![0, 1]);
assert!(!a.all_assigned());
a.set(0, true);
a.set(1, true);
assert!(a.all_assigned());
a.reset();
assert!(!a.is_assigned(0));
assert!(!a.is_assigned(1));
assert!(!a.is_assigned(2));
assert!(!a.is_assigned(3));
assert!(!a.all_assigned());
}
#[test]
fn test_assignment_vec() {
let mut a: VecAssignment = Assignment::new(4);
test_assignment(&mut a);
}
#[test]
fn test_hashmap_assignment() {
let mut a = HashMapAssignment::new(4);
test_assignment(&mut a);
}
#[test]
fn test_assignment_unassigned_default_impl() {
let a = VecAssignment::new(4);
assert!(!a.is_assigned(0));
assert!(!a.is_assigned(1));
assert!(!a.is_assigned(2));
assert!(!a.is_assigned(3));
assert_eq!(a.unassigned().count(), 4);
assert!(!a.all_assigned());
let b = HashMapAssignment::new(4);
assert!(!b.is_assigned(0));
assert!(!b.is_assigned(1));
assert_eq!(b.unassigned().count(), 4);
assert!(!b.all_assigned());
let c = VecAssignment::new(0);
assert!(c.all_assigned());
let d = HashMapAssignment::new(0);
assert!(d.all_assigned());
}
}