use itertools::Itertools;
use crate::datastructures::Assignment;
use crate::formulas::{FormulaFactory, Literal, StringLiteral, Variable};
#[derive(Debug, Clone)]
pub struct Model {
pos: Vec<Variable>,
neg: Vec<Variable>,
}
impl Model {
pub fn new<P, N>(pos: P, neg: N) -> Self
where
P: Into<Vec<Variable>>,
N: Into<Vec<Variable>>, {
Self { pos: pos.into(), neg: neg.into() }
}
pub fn from_variables(pos: &[Variable], neg: &[Variable]) -> Self {
Self { pos: pos.into(), neg: neg.into() }
}
pub fn from_lit(lit: Literal) -> Self {
if lit.phase() {
Self { pos: vec![lit.variable()], neg: vec![] }
} else {
Self { pos: vec![], neg: vec![lit.variable()] }
}
}
pub fn from_literals(literals: &[Literal]) -> Self {
let mut pos = Vec::with_capacity(literals.len());
let mut neg = Vec::with_capacity(literals.len());
for lit in literals {
if lit.phase() {
pos.push(lit.variable());
} else {
neg.push(lit.variable());
}
}
Self { pos, neg }
}
pub fn from_names(pos: &[&str], neg: &[&str], f: &FormulaFactory) -> Result<Self, String> {
let pos = names_to_indices(pos, f)?;
let neg = names_to_indices(neg, f)?;
Ok(Self { pos, neg })
}
pub fn pos(&self) -> &[Variable] {
&self.pos
}
pub fn neg(&self) -> &[Variable] {
&self.neg
}
pub fn len(&self) -> usize {
self.pos.len() + self.neg.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn literals(&self) -> Vec<Literal> {
let mut result = Vec::with_capacity(self.len());
self.pos.iter().for_each(|var| result.push(Literal::Pos(*var)));
self.neg.iter().for_each(|var| result.push(Literal::Neg(*var)));
result
}
pub fn string_literals<'c>(&self, f: &'c FormulaFactory) -> Vec<StringLiteral<'c>> {
let mut result = Vec::with_capacity(self.len());
self.pos.iter().for_each(|var| result.push(Literal::Pos(*var).to_string_lit(f)));
self.neg.iter().for_each(|var| result.push(Literal::Neg(*var).to_string_lit(f)));
result
}
pub fn compare(&self, other: &Self) -> bool {
Assignment::from(self) == Assignment::from(other)
}
pub fn to_string(&self, f: &FormulaFactory) -> String {
format!(
"Model{{pos=[{}], neg=[{}]}}",
self.pos.iter().map(|v| v.to_string_lit(f)).join(", "),
self.neg.iter().map(|v| v.to_string_lit(f)).join(", ")
)
}
}
impl AsRef<Self> for Model {
fn as_ref(&self) -> &Self {
self
}
}
impl<M: AsRef<Model>> From<M> for Assignment {
fn from(model: M) -> Self {
let m = model.as_ref();
Self::from_variables(&m.pos, &m.neg)
}
}
fn names_to_indices(names: &[&str], f: &FormulaFactory) -> Result<Vec<Variable>, String> {
let mut result = Vec::with_capacity(names.len());
for name in names {
let index = match f.variables.lookup(&(*name).to_string()) {
Some(i) => Variable::FF(i),
None => {
return Err(format!("Variable {} is not known in the given FormulaFactory", *name));
}
};
result.push(index);
}
Ok(result)
}
#[cfg(test)]
mod tests {
use crate::datastructures::{Assignment, Model};
use crate::formulas::{FormulaFactory, StringLiteral};
use crate::util::test_util::{lits, lits_list, vars_list};
use std::collections::BTreeSet;
#[test]
fn test_constructor1() {
let f = &FormulaFactory::new();
let vars1 = vars_list("a b c", f);
let vars2 = vars_list("x y", f);
let model = Model::from_variables(&vars1, &vars2);
assert_eq!(vars1, model.pos());
assert_eq!(vars2, model.neg());
assert_eq!(5, model.len());
}
#[test]
fn test_constructor2() {
let f = &FormulaFactory::new();
let vars1 = vars_list("a b c", f);
let vars2 = vars_list("x y", f);
let lits = lits_list("a b c ~x ~y", f);
let model = Model::from_literals(&lits);
assert_eq!(vars1, model.pos());
assert_eq!(vars2, model.neg());
assert_eq!(5, model.len());
}
#[test]
fn test_constructor3() {
let f = &FormulaFactory::new();
let vars1 = vars_list("a b c", f);
let vars2 = vars_list("x y", f);
let model = Model::from_names(&["a", "b", "c"], &["x", "y"], f).unwrap();
assert_eq!(vars1, model.pos());
assert_eq!(vars2, model.neg());
assert_eq!(5, model.len());
}
#[test]
fn test_is_empty() {
let f = &FormulaFactory::new();
let vars1 = vars_list("b a", f);
let vars2 = vars_list("x y", f);
let a1 = Model::new(vec![], vec![]);
let a2 = Model::new(vars1.clone(), vec![]);
let a3 = Model::new(vec![], vars2.clone());
let a4 = Model::new(vars1, vars2);
assert!(a1.is_empty());
assert!(!a2.is_empty());
assert!(!a3.is_empty());
assert!(!a4.is_empty());
}
#[test]
fn test_literals() {
let f = &FormulaFactory::new();
let vars1 = vars_list("b a", f);
let vars2 = vars_list("x y", f);
let a1 = Model::new(vec![], vec![]);
let a2 = Model::new(vars1.clone(), vec![]);
let a3 = Model::new(vec![], vars2.clone());
let a4 = Model::new(vars1, vars2);
assert_eq!(a1.literals(), vec![]);
assert_eq!(lits_list("b a", f), a2.literals());
assert_eq!(lits_list("~x ~y", f), a3.literals());
assert_eq!(lits_list("b a ~x ~y", f), a4.literals());
}
#[test]
fn test_string_literals() {
let f = &FormulaFactory::new();
let vars1 = vars_list("b a", f);
let vars2 = vars_list("x y", f);
let a1 = Model::new(vec![], vec![]);
let a2 = Model::new(vars1.clone(), vec![]);
let a3 = Model::new(vec![], vars2.clone());
let a4 = Model::new(vars1, vars2);
assert_eq!(Vec::<StringLiteral>::new(), a1.string_literals(f));
assert_eq!(vec![StringLiteral::new("b", true), StringLiteral::new("a", true)], a2.string_literals(f));
assert_eq!(vec![StringLiteral::new("x", false), StringLiteral::new("y", false),], a3.string_literals(f));
assert_eq!(
vec![
StringLiteral::new("b", true),
StringLiteral::new("a", true),
StringLiteral::new("x", false),
StringLiteral::new("y", false),
],
a4.string_literals(f)
);
}
#[test]
fn test_to_assignment() {
let f = &FormulaFactory::new();
let vars1 = vars_list("b a", f);
let vars2 = vars_list("x y", f);
let a1 = Model::new(vec![], vec![]);
let a2 = Model::new(vars1.clone(), vec![]);
let a3 = Model::new(vec![], vars2.clone());
let a4 = Model::new(vars1, vars2);
assert_eq!(Assignment::from_set(BTreeSet::new()), a1.into());
assert_eq!(Assignment::from_set(lits("b a", f)), a2.into());
assert_eq!(Assignment::from_set(lits("~x ~y", f)), a3.into());
assert_eq!(Assignment::from_set(lits("b a ~x ~y", f)), a4.into());
}
#[test]
fn test_to_string() {
let f = &FormulaFactory::new();
let vars1 = vars_list("b a", f);
let vars2 = vars_list("x y", f);
let a1 = Model::new(vec![], vec![]);
let a2 = Model::new(vars1.clone(), vec![]);
let a3 = Model::new(vec![], vars2.clone());
let a4 = Model::new(vars1, vars2);
assert_eq!("Model{pos=[], neg=[]}", a1.to_string(f));
assert_eq!("Model{pos=[b, a], neg=[]}", a2.to_string(f));
assert_eq!("Model{pos=[], neg=[x, y]}", a3.to_string(f));
assert_eq!("Model{pos=[b, a], neg=[x, y]}", a4.to_string(f));
}
}