#[allow(unused_imports)]
use crate::prelude::*;
use oxiz_sat::Lit;
#[derive(Debug, Clone)]
pub struct MinimizerConfig {
pub recursive: bool,
pub binary_resolution: bool,
pub max_depth: u32,
pub use_stamping: bool,
}
impl Default for MinimizerConfig {
fn default() -> Self {
Self {
recursive: true,
binary_resolution: true,
max_depth: 100,
use_stamping: true,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct MinimizerStats {
pub conflicts_minimized: u64,
pub literals_removed: u64,
pub avg_reduction: f64,
pub time_us: u64,
}
pub struct ConflictMinimizer {
config: MinimizerConfig,
stats: MinimizerStats,
stamp: Vec<u32>,
current_stamp: u32,
}
impl ConflictMinimizer {
pub fn new() -> Self {
Self::with_config(MinimizerConfig::default())
}
pub fn with_config(config: MinimizerConfig) -> Self {
Self {
config,
stats: MinimizerStats::default(),
stamp: Vec::new(),
current_stamp: 0,
}
}
pub fn stats(&self) -> &MinimizerStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = MinimizerStats::default();
}
pub fn minimize(&mut self, conflict: &[Lit]) -> Vec<Lit> {
#[cfg(feature = "std")]
let start = std::time::Instant::now();
let original_size = conflict.len();
let max_var = conflict
.iter()
.map(|lit| lit.var().index())
.max()
.unwrap_or(0);
if max_var >= self.stamp.len() {
self.stamp.resize(max_var + 1, 0);
}
self.current_stamp += 1;
let mut minimized = Vec::with_capacity(conflict.len());
let conflict_set: FxHashSet<Lit> = conflict.iter().copied().collect();
for &lit in conflict {
if self.can_be_removed(lit, &conflict_set) {
continue;
}
minimized.push(lit);
}
let removed = original_size - minimized.len();
self.stats.conflicts_minimized += 1;
self.stats.literals_removed += removed as u64;
if self.stats.conflicts_minimized > 0 {
let total_removed = self.stats.literals_removed as f64;
let total_processed = self.stats.conflicts_minimized as f64 * original_size as f64;
self.stats.avg_reduction = total_removed / total_processed;
}
#[cfg(feature = "std")]
{
self.stats.time_us += start.elapsed().as_micros() as u64;
}
minimized
}
fn can_be_removed(&mut self, lit: Lit, _conflict: &FxHashSet<Lit>) -> bool {
if !self.config.recursive {
return false;
}
if self.config.use_stamping {
let var_idx = lit.var().index();
if var_idx < self.stamp.len() && self.stamp[var_idx] == self.current_stamp {
return false; }
if var_idx < self.stamp.len() {
self.stamp[var_idx] = self.current_stamp;
}
}
false }
pub fn minimize_binary(&mut self, conflict: &[Lit]) -> Vec<Lit> {
if !self.config.binary_resolution {
return conflict.to_vec();
}
conflict.to_vec()
}
pub fn self_subsume(&mut self, conflict: &[Lit]) -> Vec<Lit> {
conflict.to_vec()
}
}
impl Default for ConflictMinimizer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use oxiz_sat::Var;
fn lit(var: u32, positive: bool) -> Lit {
let v = Var::new(var);
if positive { Lit::pos(v) } else { Lit::neg(v) }
}
#[test]
fn test_minimizer_creation() {
let minimizer = ConflictMinimizer::new();
assert_eq!(minimizer.stats().conflicts_minimized, 0);
}
#[test]
fn test_minimizer_config() {
let config = MinimizerConfig {
recursive: false,
binary_resolution: false,
max_depth: 50,
use_stamping: false,
};
let minimizer = ConflictMinimizer::with_config(config);
assert!(!minimizer.config.recursive);
}
#[test]
fn test_minimize_trivial() {
let mut minimizer = ConflictMinimizer::new();
let conflict = vec![lit(0, true), lit(1, false), lit(2, true)];
let minimized = minimizer.minimize(&conflict);
assert_eq!(minimized.len(), conflict.len());
assert_eq!(minimizer.stats().conflicts_minimized, 1);
}
#[test]
fn test_stats_update() {
let mut minimizer = ConflictMinimizer::new();
minimizer.minimize(&[lit(0, true), lit(1, false)]);
minimizer.minimize(&[lit(2, true), lit(3, false), lit(4, true)]);
assert_eq!(minimizer.stats().conflicts_minimized, 2);
}
}