use super::*;
use crate::tests::common::{clause_dimacs, lit};
use crate::tests::pmc_oracle::brute_force_mc;
#[test]
fn anytime_reduces_toy_cnf() {
let mut f = CnfFormula {
num_vars: 10,
clauses: Vec::new(),
};
for c in [
&[1, 2][..],
&[1, 2, 9],
&[3, -4],
&[3, -4, 5],
&[-1, 6],
&[6, -2],
] {
f.clauses.push(clause_dimacs(c));
}
let deadline = Instant::now() + Duration::from_secs(30);
let r = reduce_anytime(
&f,
deadline,
ArjunOptions::default(),
false,
)
.expect("no VITRI_* knob is set in this test")
.expect("reduce");
assert!(r.formula.num_vars <= 10);
assert!(r.multiplier_exp > 0, "exp={}", r.multiplier_exp);
}
fn deadline_probe_formula() -> CnfFormula {
let mut clauses = Vec::new();
for v in 0..11u32 {
clauses.push(Clause::new(vec![lit(v, false), lit(v + 1, true)]));
}
for v in 0..10u32 {
clauses.push(Clause::new(vec![
lit(v, true),
lit(v + 1, false),
lit(v + 2, true),
]));
clauses.push(Clause::new(vec![
lit(v, false),
lit(v + 1, true),
lit(v + 2, false),
]));
}
CnfFormula {
num_vars: 16,
clauses,
}
}
#[test]
fn anytime_deadline_honored_and_sound() {
let formula = deadline_probe_formula();
let expected = brute_force_mc(&formula);
let budget = Duration::from_millis(300);
let started = Instant::now();
let r = reduce_anytime_inner(
&formula,
started + budget,
ArjunOptions::default(),
false,
);
let elapsed = started.elapsed();
assert!(
elapsed < budget + Duration::from_secs(10),
"deadline not honored in-process: returned after {:?} against a {:?} budget",
elapsed,
budget
);
if let Some(r) = r {
let reduced = brute_force_mc(&r.formula);
let got = reduced.clone() << r.multiplier_exp;
assert_eq!(
got, expected,
"deadline-cut reduction is not count-preserving: {} << {} = {} != {}",
reduced, r.multiplier_exp, got, expected
);
}
}
#[test]
fn far_deadline_reduction_is_deterministic() {
let formula = deadline_probe_formula();
let a = reduce_anytime_inner(
&formula,
Instant::now() + Duration::from_secs(600),
ArjunOptions::default(),
false,
)
.expect("reduce with 600s budget");
let b = reduce_anytime_inner(
&formula,
Instant::now() + Duration::from_secs(4200),
ArjunOptions::default(),
false,
)
.expect("reduce with 4200s budget");
assert_eq!(
a.formula, b.formula,
"far-future deadline changed the reduction"
);
assert_eq!(a.multiplier_exp, b.multiplier_exp);
assert_eq!(a.backbone, b.backbone);
assert_eq!(a.equiv, b.equiv);
assert_eq!(a.independent_support, b.independent_support);
}
#[test]
fn reduce_anytime_fork_matches_direct() {
let formula = CnfFormula {
num_vars: 6,
clauses: vec![
Clause::new(vec![lit(0, true)]),
Clause::new(vec![lit(0, false), lit(1, true)]),
Clause::new(vec![lit(1, false), lit(2, true)]),
Clause::new(vec![lit(3, true), lit(4, false)]),
Clause::new(vec![lit(3, false), lit(4, true)]),
],
};
let budget = Duration::from_secs(30);
let forked = reduce_anytime(
&formula,
Instant::now() + budget,
ArjunOptions::default(),
false,
)
.expect("no VITRI_* knob is set in this test")
.expect("forked reduce");
let direct = reduce_anytime_inner(
&formula,
Instant::now() + budget,
ArjunOptions::default(),
false,
)
.expect("direct reduce");
assert_eq!(
forked.formula, direct.formula,
"reduced formula differs across the fork"
);
assert_eq!(forked.multiplier_exp, direct.multiplier_exp);
assert_eq!(
forked.backbone, direct.backbone,
"backbone harvest lost/altered by the fork"
);
assert_eq!(
forked.equiv, direct.equiv,
"equiv harvest lost/altered by the fork"
);
assert_eq!(forked.learnt_clauses, direct.learnt_clauses);
assert_eq!(
forked.independent_support, direct.independent_support,
"independent support lost/altered by the fork",
);
for var in forked.independent_support.iter_vars() {
assert!(
var.0 < forked.formula.num_vars,
"support variable {} is outside the final checkpoint's {} variables",
var.0,
forked.formula.num_vars,
);
}
assert_eq!(
forked.input_to_reduced_lit, direct.input_to_reduced_lit,
"input->reduced variable map lost/altered by the fork",
);
assert!(
!forked.backbone.is_empty(),
"expected a non-empty backbone harvest"
);
assert_eq!(
forked.input_to_reduced_lit.len(),
formula.num_vars as usize,
"the map must be indexed by input variable",
);
let mut claimed = vec![false; forked.formula.num_vars as usize];
for e in forked.input_to_reduced_lit.iter().flatten() {
let r = e.unsigned_abs() as usize;
assert!(
r >= 1 && r <= forked.formula.num_vars as usize,
"map names reduced var {r}, outside 1..={}",
forked.formula.num_vars,
);
assert!(!claimed[r - 1], "two input vars map onto reduced var {r}");
claimed[r - 1] = true;
}
}