use super::covering_strategies::cover_with_caching;
use super::sample_merger::{AndMerger, OrMerger, SampleMerger};
use super::t_iterator::TInteractionIter;
use super::{Sample, SamplingResult, SatWrapper};
use crate::Ddnnf;
use crate::NodeType;
use crate::ddnnf::extended_ddnnf::ExtendedDdnnf;
use crate::util::rng;
use itertools::Itertools;
use rand::prelude::SliceRandom;
use std::cmp::min;
use std::collections::{HashMap, HashSet};
use streaming_iterator::StreamingIterator;
pub struct TWiseSampler<'a, A: AndMerger, O: OrMerger> {
pub(crate) ddnnf: &'a Ddnnf,
pub(crate) partial_samples: HashMap<usize, SamplingResult>,
and_merger: A,
or_merger: O,
}
impl<'a, A: AndMerger, O: OrMerger> TWiseSampler<'a, A, O> {
pub fn new(ddnnf: &'a Ddnnf, and_merger: A, or_merger: O) -> Self {
Self {
ddnnf,
partial_samples: HashMap::with_capacity(ddnnf.nodes.len()),
and_merger,
or_merger,
}
}
pub fn sample(&mut self, t: usize) -> SamplingResult {
let sat_solver = SatWrapper::new(self.ddnnf);
for node_id in 0..self.ddnnf.nodes.len() {
let partial_sample = self.partial_sample(node_id);
self.partial_samples.insert(node_id, partial_sample);
}
let root_id = self.ddnnf.nodes.len() - 1;
let result = self
.partial_samples
.remove(&root_id)
.expect("Root sample does not exist!");
if let SamplingResult::ResultWithSample(mut sample) = result {
sample = trim_and_resample(
root_id,
sample,
t,
self.ddnnf.number_of_variables as usize,
&sat_solver,
);
complete_partial_configs(
&mut sample,
root_id,
&sat_solver,
self.ddnnf.number_of_variables as i32,
);
return sample.into();
}
result
}
pub(crate) fn partial_sample(&mut self, node_id: usize) -> SamplingResult {
let node = self.ddnnf.nodes.get(node_id).expect("Node does not exist!");
match &node.ntype {
NodeType::Literal { literal } => SamplingResult::ResultWithSample(
Sample::from_literal(*literal, self.ddnnf.number_of_variables as usize),
),
NodeType::And { children } => {
let sample = self.sample_node(&self.and_merger, node_id, children);
self.remove_unneeded(node_id, children);
sample
}
NodeType::Or { children } => {
let sample = self.sample_node(&self.or_merger, node_id, children);
self.remove_unneeded(node_id, children);
sample
}
NodeType::True => SamplingResult::Empty,
NodeType::False => SamplingResult::Void,
}
}
fn sample_node<M: SampleMerger>(
&self,
sampler: &M,
id: usize,
children: &[usize],
) -> SamplingResult {
let children: Vec<&SamplingResult> = children
.iter()
.map(|child| {
self.partial_samples
.get(child)
.expect("Samples of child node not present!")
})
.collect();
if sampler.is_void(&children) {
return SamplingResult::Void;
}
let samples: Vec<&Sample> = children
.iter()
.filter_map(|sample: &&SamplingResult| sample.optional())
.collect();
sampler.merge_all(id, &samples).into()
}
fn remove_unneeded(&mut self, node_id: usize, children: &[usize]) {
children
.iter()
.filter(|&&id| {
let node = self.ddnnf.nodes.get(id).expect("Node does not exist!");
node.parents.iter().all(|&parent| parent <= node_id)
})
.for_each(|id| {
self.partial_samples
.remove(id)
.expect("Sample does not exist!");
});
}
}
fn complete_partial_configs(
sample: &mut Sample,
root: usize,
sat_solver: &SatWrapper,
number_of_variables: i32,
) {
let vars: Vec<i32> = (1..=number_of_variables).collect();
for config in sample.partial_configs.iter_mut() {
for &var in vars.iter() {
if config.contains(var) || config.contains(-var) {
continue;
}
config.update_sat_state(sat_solver, root);
let mut sat_state = config
.get_sat_state()
.cloned()
.expect("sat state should exist after calling update_sat_state()");
if sat_solver.is_sat_cached(&[var], &mut sat_state) {
config.add(var);
} else {
config.add(-var);
}
}
}
debug_assert!(
sample
.iter()
.all(|config| !config.get_literals().contains(&0))
);
}
pub fn complete_partial_configs_optimal(sample: &mut Sample, ext_ddnnf: &ExtendedDdnnf) {
while let Some(config) = sample.partial_configs.pop() {
let literals = config.get_decided_literals().collect_vec();
let completed_config = ext_ddnnf
.calc_best_config(&literals[..])
.expect("Config should be exist");
debug_assert!(
completed_config.config.get_n_decided_literals() == sample.vars.len(),
"{:?} != {:?}",
completed_config.config.get_n_decided_literals(),
sample.vars.len()
);
sample.add(completed_config.config);
}
}
#[inline]
pub fn trim_and_resample(
node_id: usize,
sample: Sample,
t: usize,
number_of_variables: usize,
sat_solver: &SatWrapper,
) -> Sample {
if sample.is_empty() {
return sample;
}
let t = min(sample.get_vars().len(), t);
let (ranks, avg_rank) = calc_stats(&sample, t);
let (mut new_sample, literals_to_resample) = trim_sample(&sample, &ranks, avg_rank);
let mut literals_to_resample: Vec<i32> = literals_to_resample.into_iter().collect();
literals_to_resample.sort_unstable();
literals_to_resample.shuffle(&mut rng());
let mut iter = TInteractionIter::new(&literals_to_resample, min(t, literals_to_resample.len()));
while let Some(interaction) = iter.next() {
cover_with_caching(
&mut new_sample,
interaction,
sat_solver,
node_id,
number_of_variables,
);
}
if new_sample.len() < sample.len() {
new_sample
} else {
sample
}
}
#[inline]
fn trim_sample(sample: &Sample, ranks: &[f64], avg_rank: f64) -> (Sample, HashSet<i32>) {
let mut literals_to_resample: HashSet<i32> = HashSet::new();
let mut new_sample = Sample::new_from_samples(&[sample]);
let complete_len = sample.complete_configs.len();
for (index, config) in sample.iter().enumerate() {
if ranks[index] < avg_rank {
literals_to_resample.extend(config.get_decided_literals());
} else if index < complete_len {
new_sample.add_complete(config.clone());
} else {
new_sample.add_partial(config.clone());
}
}
(new_sample, literals_to_resample)
}
#[inline]
fn calc_stats(sample: &Sample, t: usize) -> (Vec<f64>, f64) {
let mut unique_coverage = vec![0; sample.len()];
let mut iter =
TInteractionIter::new(sample.get_literals(), min(sample.get_literals().len(), t));
while let Some(interaction) = iter.next() {
if let Some(conf_index) = find_unique_covering_conf(sample, interaction) {
unique_coverage[conf_index] += 1;
}
}
let mut ranks = vec![0.0; sample.len()];
let mut sum: f64 = 0.0;
for (index, config) in sample.iter().enumerate() {
let config_size = config.n_decided_literals;
ranks[index] = unique_coverage[index] as f64 / config_size.pow(t as u32) as f64;
sum += ranks[index];
}
let avg_rank = sum / sample.len() as f64;
(ranks, avg_rank)
}
#[inline]
fn find_unique_covering_conf(sample: &Sample, interaction: &[i32]) -> Option<usize> {
let mut result = None;
for (index, config) in sample.iter().enumerate() {
if config.covers(interaction) {
if result.is_none() {
result = Some(index);
} else {
return None;
}
}
}
result
}