#![allow(dead_code, missing_docs)]
#[allow(unused_imports)]
use crate::prelude::*;
use oxiz_sat::Lit;
pub type ConstraintId = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WatchType {
OnFalse,
OnTrue,
OnChange,
}
#[derive(Debug, Clone)]
pub struct Watch {
pub constraint: ConstraintId,
pub watch_type: WatchType,
pub watch_index: usize,
}
impl Watch {
pub fn new(constraint: ConstraintId, watch_type: WatchType, watch_index: usize) -> Self {
Self {
constraint,
watch_type,
watch_index,
}
}
}
#[derive(Debug, Clone)]
pub struct WatchedConstraint {
pub id: ConstraintId,
pub literals: Vec<Lit>,
pub watched: Vec<usize>,
pub data: ConstraintData,
}
#[derive(Debug, Clone)]
pub enum ConstraintData {
Generic,
Linear { coeffs: Vec<i64>, bound: i64 },
Cardinality { k: usize },
PseudoBoolean { coeffs: Vec<i64>, bound: i64 },
}
#[derive(Debug, Clone)]
pub struct WatchedConfig {
pub num_watches: usize,
pub lazy_updates: bool,
pub batch_threshold: usize,
}
impl Default for WatchedConfig {
fn default() -> Self {
Self {
num_watches: 2,
lazy_updates: false,
batch_threshold: 10,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct WatchedStats {
pub propagations: u64,
pub watch_updates: u64,
pub conflicts: u64,
pub batch_propagations: u64,
}
pub struct WatchedPropagator {
config: WatchedConfig,
stats: WatchedStats,
watches: FxHashMap<Lit, Vec<Watch>>,
constraints: FxHashMap<ConstraintId, WatchedConstraint>,
next_id: ConstraintId,
prop_queue: VecDeque<(Lit, ConstraintId)>,
}
impl WatchedPropagator {
pub fn new(config: WatchedConfig) -> Self {
Self {
config,
stats: WatchedStats::default(),
watches: FxHashMap::default(),
constraints: FxHashMap::default(),
next_id: 0,
prop_queue: VecDeque::new(),
}
}
pub fn default_config() -> Self {
Self::new(WatchedConfig::default())
}
pub fn add_constraint(&mut self, literals: Vec<Lit>, data: ConstraintData) -> ConstraintId {
let id = self.next_id;
self.next_id += 1;
let num_watches = self.config.num_watches.min(literals.len());
let watched: Vec<usize> = (0..num_watches).collect();
let constraint = WatchedConstraint {
id,
literals: literals.clone(),
watched: watched.clone(),
data,
};
self.constraints.insert(id, constraint);
for &watch_idx in &watched {
let lit = literals[watch_idx];
let watch = Watch::new(id, WatchType::OnFalse, watch_idx);
self.watches.entry(lit).or_default().push(watch);
}
id
}
pub fn remove_constraint(&mut self, id: ConstraintId) {
if let Some(constraint) = self.constraints.remove(&id) {
for &watch_idx in &constraint.watched {
let lit = constraint.literals[watch_idx];
if let Some(watch_list) = self.watches.get_mut(&lit) {
watch_list.retain(|w| w.constraint != id);
}
}
}
}
pub fn notify_assigned(&mut self, lit: Lit, value: bool) -> Result<Vec<Lit>, ConstraintId> {
let mut propagated = Vec::new();
let watch_lit = if value { !lit } else { lit };
let watches = match self.watches.get(&watch_lit).cloned() {
Some(w) => w,
None => return Ok(propagated),
};
for watch in watches {
let result = self.process_watch(watch, lit, value)?;
propagated.extend(result);
}
self.stats.propagations += propagated.len() as u64;
Ok(propagated)
}
fn process_watch(
&mut self,
watch: Watch,
assigned_lit: Lit,
assigned_value: bool,
) -> Result<Vec<Lit>, ConstraintId> {
let constraint = match self.constraints.get(&watch.constraint) {
Some(c) => c.clone(),
None => return Ok(Vec::new()),
};
if self.should_update_watch(&constraint, watch.watch_index, assigned_lit, assigned_value) {
self.stats.watch_updates += 1;
if let Some(new_watch_idx) = self.find_new_watch(&constraint, watch.watch_index) {
self.update_watch(watch.constraint, watch.watch_index, new_watch_idx);
return Ok(Vec::new());
}
return self.check_propagation(&constraint);
}
Ok(Vec::new())
}
fn should_update_watch(
&self,
constraint: &WatchedConstraint,
watch_idx: usize,
assigned_lit: Lit,
assigned_value: bool,
) -> bool {
let watched_lit = constraint.literals[watch_idx];
watched_lit == assigned_lit && !assigned_value
}
fn find_new_watch(
&self,
constraint: &WatchedConstraint,
_old_watch_idx: usize,
) -> Option<usize> {
for (idx, _lit) in constraint.literals.iter().enumerate() {
if !constraint.watched.contains(&idx) {
return Some(idx);
}
}
None
}
fn update_watch(&mut self, constraint_id: ConstraintId, old_idx: usize, new_idx: usize) {
if let Some(constraint) = self.constraints.get_mut(&constraint_id) {
let old_lit = constraint.literals[old_idx];
let new_lit = constraint.literals[new_idx];
if let Some(watch_list) = self.watches.get_mut(&old_lit) {
watch_list.retain(|w| !(w.constraint == constraint_id && w.watch_index == old_idx));
}
let watch = Watch::new(constraint_id, WatchType::OnFalse, new_idx);
self.watches.entry(new_lit).or_default().push(watch);
if let Some(pos) = constraint.watched.iter().position(|&idx| idx == old_idx) {
constraint.watched[pos] = new_idx;
}
}
}
fn check_propagation(
&mut self,
constraint: &WatchedConstraint,
) -> Result<Vec<Lit>, ConstraintId> {
match &constraint.data {
ConstraintData::Cardinality { k: _ } => {
Ok(Vec::new())
}
_ => {
Ok(Vec::new())
}
}
}
pub fn num_constraints(&self) -> usize {
self.constraints.len()
}
pub fn stats(&self) -> &WatchedStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = WatchedStats::default();
}
}
#[cfg(test)]
mod tests {
use super::*;
use oxiz_sat::Var;
#[test]
fn test_propagator_creation() {
let propagator = WatchedPropagator::default_config();
assert_eq!(propagator.num_constraints(), 0);
}
#[test]
fn test_add_constraint() {
let mut propagator = WatchedPropagator::default_config();
let lit1 = Lit::pos(Var::new(0));
let lit2 = Lit::pos(Var::new(1));
let lit3 = Lit::pos(Var::new(2));
let id =
propagator.add_constraint(vec![lit1, lit2, lit3], ConstraintData::Cardinality { k: 2 });
assert_eq!(propagator.num_constraints(), 1);
assert!(propagator.constraints.contains_key(&id));
}
#[test]
fn test_remove_constraint() {
let mut propagator = WatchedPropagator::default_config();
let lit1 = Lit::pos(Var::new(0));
let lit2 = Lit::pos(Var::new(1));
let id = propagator.add_constraint(vec![lit1, lit2], ConstraintData::Generic);
assert_eq!(propagator.num_constraints(), 1);
propagator.remove_constraint(id);
assert_eq!(propagator.num_constraints(), 0);
}
#[test]
fn test_stats() {
let propagator = WatchedPropagator::default_config();
assert_eq!(propagator.stats().propagations, 0);
}
}