use super::*;
use crate::tests::common::lit;
use crate::tests::pmc_oracle::{brute_force_mc, brute_force_wmc};
#[test]
fn anytime_weighted_count_preserving() {
let formula = CnfFormula {
num_vars: 5,
clauses: vec![
Clause::new(vec![lit(0, false), lit(1, true)]), Clause::new(vec![lit(1, false), lit(2, true)]), ],
};
let wpos: Vec<BigRational> = ["2/1", "3/1", "5/1", "7/1", "1/3"]
.iter()
.map(|s| crate::cnf::parse_weight(s).unwrap())
.collect();
let wneg: Vec<BigRational> = ["1/1", "1/2", "2/3", "1/1", "4/1"]
.iter()
.map(|s| crate::cnf::parse_weight(s).unwrap())
.collect();
let expected = brute_force_wmc(&formula, |v, val| {
let i = v as usize;
if val {
wpos[i].clone()
} else {
wneg[i].clone()
}
});
let mut weights_in: Vec<(i32, BigRational)> = Vec::new();
for v in 0..formula.num_vars {
let d = VarId(v).to_dimacs();
weights_in.push((d, wpos[v as usize].clone()));
weights_in.push((-d, wneg[v as usize].clone()));
}
let deadline = Instant::now() + Duration::from_secs(30);
let r = reduce_anytime_weighted(
&formula,
&weights_in,
deadline,
ArjunOptions::default(),
false,
)
.expect("the environment is usable")
.expect("reduce");
let (rneg, rpos): (Vec<BigRational>, Vec<BigRational>) =
r.weights.as_pairs().iter().cloned().unzip();
let reduced = brute_force_wmc(&r.formula, |v, val| {
let i = v as usize;
if val {
rpos[i].clone()
} else {
rneg[i].clone()
}
});
let got = reduced * &r.multiplier;
assert_eq!(
got, expected,
"weighted soundness violated: reduced × K = {got} != {expected} (orig weighted count)"
);
}
#[test]
fn power_of_two_exp() {
assert_eq!(multiplier_decimal_to_exp("1"), Some(0));
assert_eq!(multiplier_decimal_to_exp("32"), Some(5));
assert_eq!(multiplier_decimal_to_exp("1024"), Some(10));
let p100 = "1267650600228229401496703205376";
assert_eq!(multiplier_decimal_to_exp(p100), Some(100));
assert_eq!(multiplier_decimal_to_exp("3"), None);
assert_eq!(multiplier_decimal_to_exp("0"), None);
assert_eq!(multiplier_decimal_to_exp("48"), None); }
#[test]
fn anytime_count_preserving() {
let formula = CnfFormula {
num_vars: 5,
clauses: vec![
Clause::new(vec![
Literal::new(VarId(0), true),
Literal::new(VarId(1), true),
]),
Clause::new(vec![
Literal::new(VarId(1), false),
Literal::new(VarId(2), true),
]),
Clause::new(vec![
Literal::new(VarId(2), false),
Literal::new(VarId(3), true),
]),
],
};
let expected = brute_force_mc(&formula);
let deadline = Instant::now() + Duration::from_secs(30);
let r = reduce_anytime(
&formula,
deadline,
ArjunOptions::default(),
false,
)
.expect("no VITRI_* knob is set in this test")
.expect("reduce");
let reduced = brute_force_mc(&r.formula);
let got = reduced.clone() << r.multiplier_exp;
assert_eq!(
got, expected,
"soundness violated: reduced {} << {} = {} != {} (orig full count)",
reduced, r.multiplier_exp, got, expected
);
}
#[test]
fn anytime_count_preserving_no_sbva() {
let formula = CnfFormula {
num_vars: 5,
clauses: vec![
Clause::new(vec![
Literal::new(VarId(0), true),
Literal::new(VarId(1), true),
]),
Clause::new(vec![
Literal::new(VarId(1), false),
Literal::new(VarId(2), true),
]),
Clause::new(vec![
Literal::new(VarId(2), false),
Literal::new(VarId(3), true),
]),
],
};
let expected = brute_force_mc(&formula);
let deadline = Instant::now() + Duration::from_secs(30);
let r = reduce_anytime(
&formula,
deadline,
ArjunOptions::default(),
true,
)
.expect("no VITRI_* knob is set in this test")
.expect("reduce");
let reduced = brute_force_mc(&r.formula);
let got = reduced.clone() << r.multiplier_exp;
assert_eq!(
got, expected,
"no-SBVA soundness violated: reduced {} << {} = {} != {} (orig full count)",
reduced, r.multiplier_exp, got, expected
);
}
#[test]
fn seed_backbone_equiv_count_preserving() {
let formula = CnfFormula {
num_vars: 5,
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 expected = brute_force_mc(&formula);
let deadline = Instant::now() + Duration::from_secs(30);
let r = reduce_anytime(
&formula,
deadline,
ArjunOptions::default(),
false,
)
.expect("no VITRI_* knob is set in this test")
.expect("reduce");
assert!(
!r.backbone.is_empty(),
"expected a non-empty backbone harvest"
);
let mut seeded = formula.clone();
for &l in &r.backbone {
assert!(
l.var.0 < formula.num_vars,
"backbone var out of input space"
);
seeded.clauses.push(Clause::new(vec![l]));
}
for &(a, b) in &r.equiv {
assert!(
a.var.0 < formula.num_vars && b.var.0 < formula.num_vars,
"equiv var out of input space"
);
seeded.clauses.push(Clause::new(vec![a, b.negated()]));
seeded.clauses.push(Clause::new(vec![a.negated(), b]));
}
assert_eq!(
brute_force_mc(&seeded),
expected,
"seeding backbone+equiv changed the model count (unsound translation/encoding)"
);
}
#[test]
fn a_reseeded_reduction_is_count_preserving() {
let formula = CnfFormula {
num_vars: 5,
clauses: vec![
Clause::new(vec![
Literal::new(VarId(0), true),
Literal::new(VarId(1), true),
]),
Clause::new(vec![
Literal::new(VarId(1), false),
Literal::new(VarId(2), true),
]),
Clause::new(vec![
Literal::new(VarId(2), false),
Literal::new(VarId(3), true),
]),
],
};
let expected = brute_force_mc(&formula);
let r = reduce_anytime(
&formula,
Instant::now() + Duration::from_secs(30),
ArjunOptions {
seed: 7,
..ArjunOptions::default()
},
false,
)
.expect("no VITRI_* knob is set in this test")
.expect("reduce");
let reduced = brute_force_mc(&r.formula);
let got = reduced.clone() << r.multiplier_exp;
assert_eq!(
got, expected,
"a reseeded reduction is not count-preserving: {} << {} = {} != {}",
reduced, r.multiplier_exp, got, expected
);
}