pub(crate) mod objective_function;
pub(crate) mod optimal_configs;
use crate::Ddnnf;
use crate::ddnnf::anomalies::t_wise_sampling::Config;
use crate::ddnnf::extended_ddnnf::Attribute::{BoolAttr, FloatAttr, IntegerAttr, StringAttr};
use crate::ddnnf::extended_ddnnf::AttributeValue::{BoolVal, FloatVal, IntegerVal, StringVal};
use crate::ddnnf::extended_ddnnf::objective_function::ObjectiveFn;
use itertools::Itertools;
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct ExtendedDdnnf {
pub(crate) ddnnf: Ddnnf,
pub(crate) attrs: HashMap<String, Attribute>,
pub(crate) objective_fn_vals: Option<Vec<f64>>,
}
#[derive(Clone, Debug)]
pub struct AttributeT<T> {
vals: Vec<Option<T>>,
default_val: Option<T>,
}
impl<T> AttributeT<T> {
pub fn get_val(&self, var: u32) -> Option<&T> {
self.vals[(var - 1) as usize]
.as_ref()
.or(self.default_val.as_ref())
}
}
#[derive(Clone, Debug)]
pub enum Attribute {
IntegerAttr(AttributeT<i32>),
FloatAttr(AttributeT<f64>),
BoolAttr(AttributeT<bool>),
StringAttr(AttributeT<String>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum AttributeValue {
IntegerVal(i32),
FloatVal(f64),
BoolVal(bool),
StringVal(String),
}
impl Attribute {
pub fn get_val(&self, var: u32) -> Option<AttributeValue> {
match self {
IntegerAttr(attr) => attr.get_val(var).map(|val| IntegerVal(*val)),
FloatAttr(attr) => attr.get_val(var).map(|val| FloatVal(*val)),
BoolAttr(attr) => attr.get_val(var).map(|val| BoolVal(*val)),
StringAttr(attr) => attr.get_val(var).map(|val| StringVal(val.clone())),
}
}
pub fn new_integer_attr(values: Vec<Option<i32>>, default_val: Option<i32>) -> Self {
IntegerAttr(AttributeT {
vals: values,
default_val,
})
}
pub fn new_float_attr(values: Vec<Option<f64>>, default_val: Option<f64>) -> Self {
FloatAttr(AttributeT {
vals: values,
default_val,
})
}
pub fn new_bool_attr(values: Vec<Option<bool>>, default_val: Option<bool>) -> Self {
BoolAttr(AttributeT {
vals: values,
default_val,
})
}
pub fn new_string_attr(values: Vec<Option<String>>, default_val: Option<String>) -> Self {
StringAttr(AttributeT {
vals: values,
default_val,
})
}
}
impl Default for ExtendedDdnnf {
fn default() -> Self {
let ddnnf = Ddnnf::default();
let attrs = HashMap::new();
ExtendedDdnnf {
ddnnf,
attrs,
objective_fn_vals: None,
}
}
}
impl ExtendedDdnnf {
pub fn new(ddnnf: Ddnnf, attributes: HashMap<String, Attribute>) -> Self {
ExtendedDdnnf {
ddnnf,
attrs: attributes,
objective_fn_vals: None,
}
}
pub fn with_fitness_values(ddnnf: Ddnnf, fitness: Vec<f64>) -> Self {
Self {
ddnnf,
attrs: Default::default(),
objective_fn_vals: Some(fitness),
}
}
pub fn get_attr_val(&self, attr_name: &str, var: u32) -> Option<AttributeValue> {
if let Some(attr) = self.attrs.get(attr_name) {
return attr.get_val(var);
}
None
}
pub fn get_objective_fn_val(&self, var: u32) -> f64 {
match &self.objective_fn_vals {
Some(vals) => vals[(var - 1) as usize],
None => panic!("Objective function values have not been calculated."),
}
}
pub fn get_objective_fn_val_of_literals(&self, literals: &[i32]) -> f64 {
literals
.iter()
.map(|&literal| {
if literal < 0 {
0.0
}
else {
self.get_objective_fn_val(literal.unsigned_abs())
}
})
.sum()
}
pub fn get_objective_fn_val_of_config(&self, config: &Config) -> f64 {
let literals = config.get_decided_literals().collect_vec();
self.get_objective_fn_val_of_literals(&literals[..])
}
pub fn get_average_objective_fn_val_of_literals(&self, literals: &[i32]) -> f64 {
self.get_objective_fn_val_of_literals(literals) / literals.len() as f64
}
pub fn get_average_objective_fn_val_of_config(&self, config: &Config) -> f64 {
let literals = config.get_decided_literals().collect_vec();
self.get_average_objective_fn_val_of_literals(&literals[..])
}
pub fn calc_objective_fn_vals(&mut self, objective_fn: &ObjectiveFn) {
let vals = (1..=self.ddnnf.number_of_variables)
.map(|var| objective_fn.eval(var, &self.attrs))
.collect_vec();
self.objective_fn_vals = Some(vals);
}
pub fn merge_sorted_configs<'a>(
&self,
left: Vec<&'a Config>,
right: Vec<&'a Config>,
) -> Vec<&'a Config> {
let mut sorted_configs = Vec::with_capacity(left.len() + right.len());
let mut left_idx = 0;
let mut right_idx = 0;
while left_idx < left.len() && right_idx < right.len() {
if self.get_average_objective_fn_val_of_config(left[left_idx])
>= self.get_average_objective_fn_val_of_config(right[right_idx])
{
sorted_configs.push(left[left_idx]);
left_idx += 1;
} else {
sorted_configs.push(right[right_idx]);
right_idx += 1;
}
}
while left_idx < left.len() {
sorted_configs.push(left[left_idx]);
left_idx += 1;
}
while right_idx < right.len() {
sorted_configs.push(right[right_idx]);
right_idx += 1;
}
sorted_configs
}
pub fn insert_config_sorted(&self, config: Config, sorted_configs: &mut Vec<Config>) {
let config_val = self.get_average_objective_fn_val_of_config(&config);
let mut curr_idx = sorted_configs.len();
sorted_configs.push(config);
while curr_idx > 0
&& config_val > self.get_average_objective_fn_val_of_config(&sorted_configs[curr_idx])
{
sorted_configs.swap(curr_idx, curr_idx - 1);
curr_idx -= 1;
}
}
pub fn are_configs_sorted(&self, configs: Vec<&Config>) -> bool {
let mut curr_val = f64::MAX;
for config in configs {
let next_val = self.get_average_objective_fn_val_of_config(config);
if next_val > curr_val {
return false;
}
curr_val = next_val;
}
true
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::parser::{build_attributes, build_ddnnf};
use std::path::Path;
pub fn build_sandwich_ext_ddnnf() -> ExtendedDdnnf {
let ddnnf = build_ddnnf(Path::new("tests/data/sandwich.nnf"), Some(19));
let attributes = build_attributes(Path::new("tests/data/sandwich_attribute_vals.csv"));
ExtendedDdnnf::new(ddnnf, attributes)
}
#[test]
fn building_and_reading_attributes() {
let ext_ddnnf = build_sandwich_ext_ddnnf();
assert_eq!(
ext_ddnnf.get_attr_val("Calories", 3).unwrap(),
IntegerVal(203)
); assert_eq!(ext_ddnnf.get_attr_val("Price", 14).unwrap(), FloatVal(0.99)); assert_eq!(
ext_ddnnf.get_attr_val("Organic Food", 11).unwrap(),
BoolVal(false)
); }
#[test]
fn calculation_of_objective_function_values() {
use self::objective_function::{Condition::*, ObjectiveFn::*};
let mut ext_ddnnf = build_sandwich_ext_ddnnf();
let objective_fn = IfElse(
And(
BoolVar("Organic Food".to_string()).boxed(),
LessThan(NumVar("Calories".to_string()).boxed(), 10.0).boxed(),
)
.boxed(),
Mul(
NumVar("Calories".to_string()).boxed(),
NumConst(100.0).boxed(),
)
.boxed(),
Neg(NumVar("Price".to_string()).boxed()).boxed(),
);
let expected = vec![
0.0, 0.0, -1.99, -1.79, -1.79, 0.0, 0.0, -0.49, -0.69, -0.69, -0.59, 0.0, -1.29, -0.99, -1.39, 0.0, 200.0, 300.0, 200.0, ];
ext_ddnnf.calc_objective_fn_vals(&objective_fn);
assert_eq!(ext_ddnnf.objective_fn_vals.unwrap(), expected);
}
}