use crate::literal::{Lit, Var};
#[allow(unused_imports)]
use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct SavedTrail {
pub decisions: Vec<Lit>,
pub quality: f64,
pub use_count: usize,
}
impl SavedTrail {
#[must_use]
pub fn new(decisions: Vec<Lit>, quality: f64) -> Self {
Self {
decisions,
quality,
use_count: 0,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.decisions.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.decisions.is_empty()
}
}
#[derive(Debug, Clone, Default)]
pub struct TrailSavingStats {
pub trails_saved: usize,
pub trails_reused: usize,
pub successful_applications: usize,
pub failed_applications: usize,
pub total_trail_length: usize,
}
impl TrailSavingStats {
pub fn display(&self) {
println!("Trail Saving Statistics:");
println!(" Trails saved: {}", self.trails_saved);
println!(" Trails reused: {}", self.trails_reused);
println!(
" Successful applications: {}",
self.successful_applications
);
println!(" Failed applications: {}", self.failed_applications);
if self.trails_saved > 0 {
let avg_length = self.total_trail_length as f64 / self.trails_saved as f64;
println!(" Average trail length: {:.1}", avg_length);
}
if self.trails_reused > 0 {
let success_rate =
100.0 * self.successful_applications as f64 / self.trails_reused as f64;
println!(" Success rate: {:.1}%", success_rate);
}
}
}
#[derive(Debug)]
pub struct TrailSavingManager {
saved_trails: Vec<SavedTrail>,
max_trails: usize,
min_quality: f64,
current_trail: Vec<Lit>,
stats: TrailSavingStats,
}
impl Default for TrailSavingManager {
fn default() -> Self {
Self::new()
}
}
impl TrailSavingManager {
#[must_use]
pub fn new() -> Self {
Self {
saved_trails: Vec::new(),
max_trails: 10,
min_quality: 1.0,
current_trail: Vec::new(),
stats: TrailSavingStats::default(),
}
}
#[must_use]
pub fn with_config(max_trails: usize, min_quality: f64) -> Self {
Self {
saved_trails: Vec::new(),
max_trails,
min_quality,
current_trail: Vec::new(),
stats: TrailSavingStats::default(),
}
}
pub fn record_decision(&mut self, lit: Lit) {
self.current_trail.push(lit);
}
pub fn save_current_trail(&mut self, quality: f64) {
if quality < self.min_quality {
return;
}
if self.current_trail.is_empty() {
return;
}
let trail = SavedTrail::new(self.current_trail.clone(), quality);
self.stats.total_trail_length += trail.len();
self.stats.trails_saved += 1;
self.saved_trails.push(trail);
self.saved_trails.sort_by(|a, b| {
b.quality
.partial_cmp(&a.quality)
.unwrap_or(core::cmp::Ordering::Equal)
});
if self.saved_trails.len() > self.max_trails {
self.saved_trails.truncate(self.max_trails);
}
}
pub fn clear_current_trail(&mut self) {
self.current_trail.clear();
}
#[must_use]
pub fn get_best_trail(&mut self) -> Option<&mut SavedTrail> {
if self.saved_trails.is_empty() {
None
} else {
self.stats.trails_reused += 1;
Some(&mut self.saved_trails[0])
}
}
#[must_use]
pub fn get_trail(&mut self, index: usize) -> Option<&mut SavedTrail> {
if index < self.saved_trails.len() {
self.stats.trails_reused += 1;
Some(&mut self.saved_trails[index])
} else {
None
}
}
pub fn record_success(&mut self) {
self.stats.successful_applications += 1;
}
pub fn record_failure(&mut self) {
self.stats.failed_applications += 1;
}
#[must_use]
pub fn trails(&self) -> &[SavedTrail] {
&self.saved_trails
}
#[must_use]
pub fn num_trails(&self) -> usize {
self.saved_trails.len()
}
#[must_use]
pub fn stats(&self) -> &TrailSavingStats {
&self.stats
}
pub fn prune_trails(&mut self, quality_threshold: f64) {
self.saved_trails
.retain(|trail| trail.use_count > 0 || trail.quality >= quality_threshold);
}
pub fn clear(&mut self) {
self.saved_trails.clear();
self.current_trail.clear();
}
#[must_use]
pub fn is_var_in_trails(&self, var: Var) -> bool {
self.saved_trails
.iter()
.any(|trail| trail.decisions.iter().any(|&lit| lit.var() == var))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_trail_saving_manager_creation() {
let manager = TrailSavingManager::new();
assert_eq!(manager.num_trails(), 0);
assert_eq!(manager.max_trails, 10);
}
#[test]
fn test_record_and_save_trail() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.record_decision(Lit::neg(Var::new(1)));
manager.save_current_trail(10.0);
assert_eq!(manager.num_trails(), 1);
assert_eq!(manager.stats().trails_saved, 1);
}
#[test]
fn test_quality_threshold() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.save_current_trail(0.5);
assert_eq!(manager.num_trails(), 0);
manager.clear_current_trail();
manager.record_decision(Lit::pos(Var::new(1)));
manager.save_current_trail(10.0);
assert_eq!(manager.num_trails(), 1);
}
#[test]
fn test_max_trails_limit() {
let mut manager = TrailSavingManager::with_config(3, 1.0);
for i in 0..5 {
manager.record_decision(Lit::pos(Var::new(i)));
manager.save_current_trail((i + 1) as f64);
manager.clear_current_trail();
}
assert_eq!(manager.num_trails(), 3);
if let Some(best) = manager.get_best_trail() {
assert_eq!(best.quality, 5.0);
}
}
#[test]
fn test_get_best_trail() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.save_current_trail(5.0);
manager.clear_current_trail();
manager.record_decision(Lit::pos(Var::new(1)));
manager.save_current_trail(10.0);
if let Some(best) = manager.get_best_trail() {
assert_eq!(best.quality, 10.0);
assert_eq!(manager.stats().trails_reused, 1);
}
}
#[test]
fn test_trail_use_tracking() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.save_current_trail(5.0);
if let Some(trail) = manager.get_best_trail() {
trail.use_count += 1;
assert_eq!(trail.use_count, 1);
}
manager.record_success();
assert_eq!(manager.stats().successful_applications, 1);
}
#[test]
fn test_prune_trails() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.save_current_trail(2.0);
manager.clear_current_trail();
manager.record_decision(Lit::pos(Var::new(1)));
manager.save_current_trail(10.0);
manager.prune_trails(5.0);
assert_eq!(manager.num_trails(), 1);
if let Some(trail) = manager.get_best_trail() {
assert_eq!(trail.quality, 10.0);
}
}
#[test]
fn test_is_var_in_trails() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.record_decision(Lit::neg(Var::new(1)));
manager.save_current_trail(5.0);
assert!(manager.is_var_in_trails(Var::new(0)));
assert!(manager.is_var_in_trails(Var::new(1)));
assert!(!manager.is_var_in_trails(Var::new(2)));
}
#[test]
fn test_clear() {
let mut manager = TrailSavingManager::new();
manager.record_decision(Lit::pos(Var::new(0)));
manager.save_current_trail(5.0);
manager.clear();
assert_eq!(manager.num_trails(), 0);
assert!(manager.current_trail.is_empty());
}
#[test]
fn test_empty_trail_not_saved() {
let mut manager = TrailSavingManager::new();
manager.save_current_trail(10.0);
assert_eq!(manager.num_trails(), 0);
}
}