use partial_ref::{partial, PartialRef};
use crate::{
context::{parts::*, Context},
prop::Reason,
};
use super::{ClauseAlloc, Tier};
pub fn collect_garbage(
mut ctx: partial!(
Context,
mut ClauseAllocP,
mut ClauseDbP,
mut ImplGraphP,
mut WatchlistsP,
TrailP,
),
) {
let alloc = ctx.part(ClauseAllocP);
let db = ctx.part(ClauseDbP);
if db.garbage_size * 2 > alloc.buffer_size() {
collect_garbage_now(ctx.borrow());
}
}
fn collect_garbage_now(
mut ctx: partial!(
Context,
mut ClauseAllocP,
mut ClauseDbP,
mut ImplGraphP,
mut WatchlistsP,
TrailP,
),
) {
ctx.part_mut(WatchlistsP).disable();
mark_asserting_clauses(ctx.borrow());
let (db, mut ctx) = ctx.split_part_mut(ClauseDbP);
let (impl_graph, mut ctx) = ctx.split_part_mut(ImplGraphP);
let alloc = ctx.part_mut(ClauseAllocP);
assert!(
db.garbage_size <= alloc.buffer_size(),
"Inconsistent garbage tracking in ClauseDb"
);
let current_size = alloc.buffer_size() - db.garbage_size;
let mut new_alloc = ClauseAlloc::with_capacity(current_size * 2);
let mut new_clauses = vec![];
let mut new_by_tier: [Vec<_>; Tier::count()] = Default::default();
db.clauses.retain(|&cref| {
let clause = alloc.clause(cref);
let mut header = *clause.header();
if header.deleted() {
false
} else {
let clause_is_asserting = header.mark();
header.set_mark(false);
let new_cref = new_alloc.add_clause(header, clause.lits());
new_clauses.push(new_cref);
new_by_tier[header.tier() as usize].push(new_cref);
if clause_is_asserting {
let asserted_lit = clause.lits()[0];
debug_assert_eq!(impl_graph.reason(asserted_lit.var()), &Reason::Long(cref));
impl_graph.update_reason(asserted_lit.var(), Reason::Long(new_cref));
}
true
}
});
*ctx.part_mut(ClauseAllocP) = new_alloc;
db.clauses = new_clauses;
db.by_tier = new_by_tier;
db.garbage_size = 0;
}
fn mark_asserting_clauses(mut ctx: partial!(Context, mut ClauseAllocP, ImplGraphP, TrailP,)) {
let (trail, mut ctx) = ctx.split_part(TrailP);
let (alloc, ctx) = ctx.split_part_mut(ClauseAllocP);
let impl_graph = ctx.part(ImplGraphP);
for &lit in trail.trail().iter() {
if let Reason::Long(cref) = impl_graph.reason(lit.var()) {
alloc.header_mut(*cref).set_mark(true);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cmp::max;
use partial_ref::IntoPartialRefMut;
use proptest::*;
use varisat_formula::{cnf::strategy::*, Lit};
use crate::{
clause::{db, ClauseHeader},
context::set_var_count,
prop::enqueue_assignment,
};
proptest! {
#[test]
fn garbage_collection(
input_a in cnf_formula(2..100usize, 500..1000, 3..30),
input_b in cnf_formula(2..100usize, 10..500, 4..20),
) {
let mut ctx = Context::default();
let mut ctx = ctx.into_partial_ref_mut();
set_var_count(ctx.borrow(), max(input_a.var_count(), input_b.var_count()));
let mut crefs_a = vec![];
let mut crefs_b = vec![];
for lits in input_a.iter() {
let header = ClauseHeader::new();
let cref = db::add_clause(ctx.borrow(), header, lits);
crefs_a.push(cref);
}
for lits in input_b.iter() {
let header = ClauseHeader::new();
let cref = db::add_clause(ctx.borrow(), header, lits);
crefs_b.push(cref);
if ctx.part(AssignmentP).lit_value(lits[0]) == None {
enqueue_assignment(ctx.borrow(), lits[0], Reason::Long(cref));
}
}
for cref in crefs_a {
db::delete_clause(ctx.borrow(), cref);
prop_assert!(ctx.part(ClauseDbP).garbage_size > 0);
}
let old_buffer_size = ctx.part(ClauseAllocP).buffer_size();
collect_garbage(ctx.borrow());
prop_assert!(
ctx.part(ClauseDbP).garbage_size * 2 < ctx.part(ClauseAllocP).buffer_size()
);
prop_assert!(
old_buffer_size > ctx.part(ClauseAllocP).buffer_size()
);
prop_assert!(!ctx.part(WatchlistsP).enabled());
let mut output_clauses: Vec<Vec<Lit>> = vec![];
for &cref in ctx.part(ClauseDbP).clauses.iter() {
let clause = ctx.part(ClauseAllocP).clause(cref);
if clause.header().deleted() {
continue;
}
prop_assert!(!clause.header().mark());
output_clauses.push(clause.lits().to_vec());
}
let mut input_clauses: Vec<Vec<Lit>> = input_b
.iter()
.map(|c| c.to_vec())
.collect();
output_clauses.sort();
input_clauses.sort();
prop_assert_eq!(input_clauses, output_clauses);
for &lit in ctx.part(TrailP).trail() {
if let Reason::Long(cref) = ctx.part(ImplGraphP).reason(lit.var()) {
prop_assert_eq!(ctx.part(ClauseAllocP).clause(*cref).lits()[0], lit)
}
}
}
}
}