#[allow(unused_imports)]
use crate::prelude::*;
#[derive(Debug, Clone, Default)]
pub struct SizeManagerStats {
pub size_rejected: usize,
pub total_considered: usize,
pub avg_accepted_size: f64,
pub avg_rejected_size: f64,
pub current_limit: usize,
}
impl SizeManagerStats {
pub fn display(&self) {
println!("Clause Size Manager Statistics:");
println!(" Total considered: {}", self.total_considered);
println!(" Size rejected: {}", self.size_rejected);
if self.total_considered > 0 {
let accept_rate = 100.0 * (self.total_considered - self.size_rejected) as f64
/ self.total_considered as f64;
println!(" Acceptance rate: {:.1}%", accept_rate);
}
println!(" Avg accepted size: {:.1}", self.avg_accepted_size);
println!(" Avg rejected size: {:.1}", self.avg_rejected_size);
println!(" Current limit: {}", self.current_limit);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeAdjustmentStrategy {
Fixed,
Geometric,
Adaptive,
Luby,
}
#[derive(Debug)]
pub struct ClauseSizeManager {
current_limit: usize,
min_limit: usize,
max_limit: usize,
strategy: SizeAdjustmentStrategy,
growth_factor: f64,
stats: SizeManagerStats,
conflicts_since_adjustment: u64,
adjustment_interval: u64,
accepted_size_sum: u64,
accepted_count: usize,
rejected_size_sum: u64,
}
impl Default for ClauseSizeManager {
fn default() -> Self {
Self::new()
}
}
impl ClauseSizeManager {
#[must_use]
pub fn new() -> Self {
Self {
current_limit: 30,
min_limit: 10,
max_limit: 100,
strategy: SizeAdjustmentStrategy::Geometric,
growth_factor: 1.1,
stats: SizeManagerStats::default(),
conflicts_since_adjustment: 0,
adjustment_interval: 10000,
accepted_size_sum: 0,
accepted_count: 0,
rejected_size_sum: 0,
}
}
#[must_use]
pub fn with_limits(min: usize, initial: usize, max: usize) -> Self {
Self {
current_limit: initial,
min_limit: min,
max_limit: max,
strategy: SizeAdjustmentStrategy::Geometric,
growth_factor: 1.1,
stats: SizeManagerStats::default(),
conflicts_since_adjustment: 0,
adjustment_interval: 10000,
accepted_size_sum: 0,
accepted_count: 0,
rejected_size_sum: 0,
}
}
pub fn set_strategy(&mut self, strategy: SizeAdjustmentStrategy) {
self.strategy = strategy;
}
pub fn should_learn(&mut self, size: usize, lbd: u32) -> bool {
self.stats.total_considered += 1;
if lbd <= 2 {
self.accepted_size_sum += size as u64;
self.accepted_count += 1;
return true;
}
if size <= self.current_limit {
self.accepted_size_sum += size as u64;
self.accepted_count += 1;
true
} else {
self.stats.size_rejected += 1;
self.rejected_size_sum += size as u64;
false
}
}
pub fn adjust_limit(&mut self, conflicts: u64) {
self.conflicts_since_adjustment += 1;
if self.conflicts_since_adjustment < self.adjustment_interval {
return;
}
self.conflicts_since_adjustment = 0;
if self.accepted_count > 0 {
self.stats.avg_accepted_size =
self.accepted_size_sum as f64 / self.accepted_count as f64;
}
if self.stats.size_rejected > 0 {
self.stats.avg_rejected_size =
self.rejected_size_sum as f64 / self.stats.size_rejected as f64;
}
self.stats.current_limit = self.current_limit;
match self.strategy {
SizeAdjustmentStrategy::Fixed => {
}
SizeAdjustmentStrategy::Geometric => {
let new_limit = (self.current_limit as f64 * self.growth_factor) as usize;
self.current_limit = new_limit.min(self.max_limit);
}
SizeAdjustmentStrategy::Adaptive => {
let rejection_rate = if self.stats.total_considered > 0 {
self.stats.size_rejected as f64 / self.stats.total_considered as f64
} else {
0.0
};
if rejection_rate > 0.3 {
self.current_limit = (self.current_limit + 5).min(self.max_limit);
} else if rejection_rate < 0.1 {
self.current_limit = (self.current_limit.saturating_sub(2)).max(self.min_limit);
}
}
SizeAdjustmentStrategy::Luby => {
let luby_value = self.luby((conflicts / self.adjustment_interval) as u32);
self.current_limit = (self.min_limit + luby_value as usize).min(self.max_limit);
}
}
}
#[allow(clippy::only_used_in_recursion)]
fn luby(&self, i: u32) -> u32 {
let mut power = 1u32;
while power * 2 <= i + 1 {
power *= 2;
}
if power == i + 1 {
if power == 1 { 1 } else { power / 2 }
} else {
self.luby(i + 1 - power)
}
}
#[must_use]
pub fn current_limit(&self) -> usize {
self.current_limit
}
#[must_use]
pub fn stats(&self) -> &SizeManagerStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = SizeManagerStats::default();
self.stats.current_limit = self.current_limit;
self.accepted_size_sum = 0;
self.accepted_count = 0;
self.rejected_size_sum = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_size_manager_creation() {
let manager = ClauseSizeManager::new();
assert_eq!(manager.current_limit(), 30);
}
#[test]
fn test_should_learn_within_limit() {
let mut manager = ClauseSizeManager::new();
assert!(manager.should_learn(20, 5)); assert!(manager.should_learn(30, 5)); }
#[test]
fn test_should_learn_exceeds_limit() {
let mut manager = ClauseSizeManager::new();
assert!(!manager.should_learn(50, 5)); assert_eq!(manager.stats().size_rejected, 1);
}
#[test]
fn test_should_learn_high_quality() {
let mut manager = ClauseSizeManager::new();
assert!(manager.should_learn(100, 2));
assert_eq!(manager.stats().size_rejected, 0);
}
#[test]
fn test_geometric_growth() {
let mut manager = ClauseSizeManager::new();
manager.set_strategy(SizeAdjustmentStrategy::Geometric);
let initial = manager.current_limit();
manager.conflicts_since_adjustment = manager.adjustment_interval;
manager.adjust_limit(10000);
assert!(manager.current_limit() > initial);
}
#[test]
fn test_fixed_strategy() {
let mut manager = ClauseSizeManager::new();
manager.set_strategy(SizeAdjustmentStrategy::Fixed);
let initial = manager.current_limit();
manager.conflicts_since_adjustment = manager.adjustment_interval;
manager.adjust_limit(10000);
assert_eq!(manager.current_limit(), initial);
}
#[test]
fn test_adaptive_increase() {
let mut manager = ClauseSizeManager::new();
manager.set_strategy(SizeAdjustmentStrategy::Adaptive);
for _ in 0..100 {
manager.should_learn(50, 5); }
let initial = manager.current_limit();
manager.conflicts_since_adjustment = manager.adjustment_interval;
manager.adjust_limit(10000);
assert!(manager.current_limit() > initial);
}
#[test]
fn test_adaptive_decrease() {
let mut manager = ClauseSizeManager::new();
manager.set_strategy(SizeAdjustmentStrategy::Adaptive);
for _ in 0..100 {
manager.should_learn(10, 5); }
let initial = manager.current_limit();
manager.conflicts_since_adjustment = manager.adjustment_interval;
manager.adjust_limit(10000);
assert!(manager.current_limit() < initial);
}
#[test]
fn test_luby_strategy_available() {
let mut manager = ClauseSizeManager::new();
manager.set_strategy(SizeAdjustmentStrategy::Luby);
manager.conflicts_since_adjustment = manager.adjustment_interval;
manager.adjust_limit(10000);
assert!(manager.current_limit() > 0);
}
#[test]
fn test_stats_tracking() {
let mut manager = ClauseSizeManager::new();
manager.should_learn(20, 5);
manager.should_learn(50, 5);
manager.should_learn(15, 5);
let stats = manager.stats();
assert_eq!(stats.total_considered, 3);
assert_eq!(stats.size_rejected, 1); }
}