mirror of
https://github.com/ellmau/adf-obdd.git
synced 2025-12-19 09:29:36 +01:00
Add first 3 value nogood handling
Tests for conclusions drawn upon an no-good has been done
This commit is contained in:
parent
5a7d6ef7be
commit
6ee643ae20
@ -2,4 +2,61 @@
|
||||
|
||||
pub mod threevalued;
|
||||
pub mod twovalued;
|
||||
use std::fmt::Display;
|
||||
|
||||
pub use twovalued::*;
|
||||
|
||||
// pub trait NoGood: std::fmt::Debug + Eq {
|
||||
// /// Returns the number of set (i.e. active) variables.
|
||||
// fn len(&self) -> usize;
|
||||
// }
|
||||
|
||||
// #[derive(Debug)]
|
||||
// pub struct NoGoodStore<N>
|
||||
// where
|
||||
// N: NoGood + Clone,
|
||||
// {
|
||||
// store: Vec<Vec<N>>,
|
||||
// }
|
||||
|
||||
// impl<T> NoGoodStore<T>
|
||||
// where
|
||||
// T: NoGood + Clone,
|
||||
// {
|
||||
// /// Creates a new [NoGoodStore] and assumes a size compatible with the underlying [NoGood] implementations.
|
||||
// pub fn new(size: u32) -> Self {
|
||||
// Self {
|
||||
// store: vec![Vec::new(); size as usize],
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Tries to create a new [NoGoodStore].
|
||||
// /// Does not succeed if the size is too big for the underlying [NoGood] implementations.
|
||||
// pub fn try_new(size: usize) -> Option<Self> {
|
||||
// Some(Self::new(size.try_into().ok()?))
|
||||
// }
|
||||
|
||||
// pub fn add_ng(&mut self, nogood: T) {
|
||||
// let mut idx = nogood.len();
|
||||
// if idx > 0 {
|
||||
// idx -= 1;
|
||||
// if !self.store[idx].contains(&nogood) {
|
||||
// self.store[idx].push(nogood);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<T> Display for NoGoodStore<T>
|
||||
// where
|
||||
// T: NoGood + Clone,
|
||||
// {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// writeln!(f, "NoGoodStats: [")?;
|
||||
// for (arity, vec) in self.store.iter().enumerate() {
|
||||
// writeln!(f, "{arity}: {}", vec.len())?;
|
||||
// log::debug!("Nogoods:\n {:?}", vec);
|
||||
// }
|
||||
// write!(f, "]")
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
//! Three-valued NoGoods
|
||||
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use roaring::RoaringBitmap;
|
||||
|
||||
use crate::datatypes::Term;
|
||||
@ -25,22 +23,154 @@ impl Interpretation {
|
||||
self.0 = self.0.invert();
|
||||
self
|
||||
}
|
||||
|
||||
fn active(&self) -> RoaringBitmap {
|
||||
self.0.active()
|
||||
}
|
||||
|
||||
fn hint(&self) -> RoaringBitmap {
|
||||
((&self.0.can_be_true & &self.0.can_be_false)
|
||||
| (&self.0.can_be_true & &self.0.can_be_und)
|
||||
| (&self.0.can_be_false & &self.0.can_be_und))
|
||||
& &self.active()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
fn idx_as_triple(&self, idx: u32) -> (bool, bool, bool) {
|
||||
self.0.idx_as_triple(idx)
|
||||
}
|
||||
|
||||
pub fn conclude(mut self, ng: &NoGood) -> Conclusion {
|
||||
log::debug!("conclude: {:?} with NoGood {:?}", self, ng);
|
||||
if self.len() + 1 < ng.len() { // not matching
|
||||
}
|
||||
let interpretation_cred_active = self.active();
|
||||
let interpretation_active = &interpretation_cred_active & (self.hint() ^ self.0.full());
|
||||
let nogood_active = ng.active();
|
||||
|
||||
let implication = (&interpretation_active ^ &nogood_active) & &nogood_active;
|
||||
let scep_match = self.sceptical_matches(ng);
|
||||
|
||||
match implication.len() {
|
||||
// learning might be possible
|
||||
1 => {
|
||||
if (scep_match & interpretation_active).len() == nogood_active.len() - 1 {
|
||||
let idx: u32 = implication
|
||||
.min()
|
||||
.expect("Checked that implication has a minimal value to be found.");
|
||||
let (ng_t, ng_f, ng_u) = ng.idx_as_triple(idx);
|
||||
let mut changes: u8 = 0;
|
||||
if ng_t && self.0.can_be_true.remove(idx) {
|
||||
changes += 1;
|
||||
}
|
||||
if ng_f && self.0.can_be_false.remove(idx) {
|
||||
changes += 1;
|
||||
}
|
||||
if ng_u && self.0.can_be_und.remove(idx) {
|
||||
changes += 1;
|
||||
}
|
||||
if self.0.can_be_true.contains(idx)
|
||||
|| self.0.can_be_false.contains(idx)
|
||||
|| self.0.can_be_und.contains(idx)
|
||||
{
|
||||
if changes > 0 {
|
||||
Conclusion::Update(self)
|
||||
} else {
|
||||
Conclusion::NoChange(self)
|
||||
}
|
||||
} else {
|
||||
// inconsistency
|
||||
Conclusion::Inconsistent(self)
|
||||
}
|
||||
} else {
|
||||
Conclusion::NoChange(self)
|
||||
}
|
||||
}
|
||||
// ng-consistency check might be possible
|
||||
_ => {
|
||||
if (scep_match & interpretation_active).len() == nogood_active.len() {
|
||||
Conclusion::Inconsistent(self)
|
||||
} else {
|
||||
Conclusion::NoChange(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn credulous_matches(&self, other: &NoGood) -> RoaringBitmap {
|
||||
let true_match = (&self.0.can_be_true ^ &other.can_be_true) ^ self.0.full();
|
||||
let false_match = (&self.0.can_be_false ^ &other.can_be_false) ^ self.0.full();
|
||||
let und_match = (&self.0.can_be_und ^ &other.can_be_und) ^ self.0.full();
|
||||
|
||||
true_match | false_match | und_match
|
||||
}
|
||||
|
||||
fn sceptical_matches(&self, other: &NoGood) -> RoaringBitmap {
|
||||
let true_match = (&self.0.can_be_true ^ &other.can_be_true) ^ self.0.full();
|
||||
let false_match = (&self.0.can_be_false ^ &other.can_be_false) ^ self.0.full();
|
||||
let und_match = (&self.0.can_be_und ^ &other.can_be_und) ^ self.0.full();
|
||||
|
||||
true_match & false_match & und_match
|
||||
}
|
||||
|
||||
/// Returns the credulous and sceptical matches of the [Interpretation] and a given [NoGood]
|
||||
fn matches(&self, other: &NoGood) -> (RoaringBitmap, RoaringBitmap) {
|
||||
let true_match = (&self.0.can_be_true ^ &other.can_be_true) ^ self.0.full();
|
||||
let false_match = (&self.0.can_be_false ^ &other.can_be_false) ^ self.0.full();
|
||||
let und_match = (&self.0.can_be_und ^ &other.can_be_und) ^ self.0.full();
|
||||
|
||||
(
|
||||
&true_match | &false_match | &und_match,
|
||||
true_match & false_match & und_match,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Conclusion {
|
||||
Update(Interpretation),
|
||||
NoChange(Interpretation),
|
||||
Inconsistent(Interpretation),
|
||||
}
|
||||
|
||||
impl Conclusion {
|
||||
fn consistent(self) -> Option<Interpretation> {
|
||||
match self {
|
||||
Conclusion::Update(val) => Some(val),
|
||||
Conclusion::NoChange(val) => Some(val),
|
||||
Conclusion::Inconsistent(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of an Interpretation with Hints about truth-values.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NoGood {
|
||||
can_be_true: RoaringBitmap,
|
||||
can_be_false: RoaringBitmap,
|
||||
can_be_und: RoaringBitmap,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl Default for NoGood {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
can_be_true: Default::default(),
|
||||
can_be_false: Default::default(),
|
||||
can_be_und: Default::default(),
|
||||
size: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for NoGood {}
|
||||
impl PartialEq for NoGood {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.can_be_true.borrow() ^ other.can_be_true.borrow()).is_empty()
|
||||
&& (self.can_be_false.borrow() ^ other.can_be_false.borrow()).is_empty()
|
||||
&& (self.can_be_und.borrow() ^ other.can_be_und.borrow()).is_empty()
|
||||
(&self.can_be_true ^ &other.can_be_true).is_empty()
|
||||
&& (&self.can_be_false ^ &other.can_be_false).is_empty()
|
||||
&& (&self.can_be_und ^ &other.can_be_und).is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,22 +182,42 @@ impl From<Interpretation> for NoGood {
|
||||
|
||||
impl NoGood {
|
||||
fn invert(mut self) -> Self {
|
||||
self.can_be_true ^= RoaringBitmap::full();
|
||||
self.can_be_false ^= RoaringBitmap::full();
|
||||
self.can_be_und ^= RoaringBitmap::full();
|
||||
self.can_be_true ^= self.full();
|
||||
self.can_be_false ^= self.full();
|
||||
self.can_be_und ^= self.full();
|
||||
self
|
||||
}
|
||||
|
||||
fn idx_as_triple(&self, idx: u32) -> (bool, bool, bool) {
|
||||
(
|
||||
self.can_be_true.contains(idx),
|
||||
self.can_be_false.contains(idx),
|
||||
self.can_be_und.contains(idx),
|
||||
)
|
||||
}
|
||||
|
||||
fn full(&self) -> RoaringBitmap {
|
||||
let mut result = RoaringBitmap::default();
|
||||
result.insert_range(0..self.size);
|
||||
result
|
||||
}
|
||||
|
||||
/// Creates a [NoGood], based on the given Vector of [Terms][Term]
|
||||
pub fn from_term_vec(term_vec: &[Term]) -> Self {
|
||||
let mut result = Self {
|
||||
can_be_true: RoaringBitmap::full(),
|
||||
can_be_false: RoaringBitmap::full(),
|
||||
can_be_und: RoaringBitmap::full(),
|
||||
can_be_true: RoaringBitmap::default(),
|
||||
can_be_false: RoaringBitmap::default(),
|
||||
can_be_und: RoaringBitmap::default(),
|
||||
size: TryInto::<u32>::try_into(term_vec.len()).expect("no-good learner implementation is based on the assumption that only u32::MAX-many variables are in place"),
|
||||
};
|
||||
result.can_be_true.insert_range(0..result.size);
|
||||
result.can_be_false.insert_range(0..result.size);
|
||||
result.can_be_und.insert_range(0..result.size);
|
||||
log::debug!("{:?}", result);
|
||||
term_vec.iter().enumerate().for_each(|(idx, val)| {
|
||||
let idx:u32 = idx.try_into().expect("no-good learner implementation is based on the assumption that only u32::MAX-many variables are in place");
|
||||
if val.is_truth_value() {
|
||||
log::trace!("idx {idx} val: {val:?}");
|
||||
if val.is_true() {
|
||||
result.can_be_false.remove(idx);
|
||||
result.can_be_und.remove(idx);
|
||||
@ -77,6 +227,139 @@ impl NoGood {
|
||||
}
|
||||
}
|
||||
});
|
||||
log::debug!("{:?}", result);
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns the number of set variables.
|
||||
pub fn len(&self) -> usize {
|
||||
self.active()
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("Expecting to be on a 64 bit system")
|
||||
}
|
||||
|
||||
fn active(&self) -> RoaringBitmap {
|
||||
(&self.can_be_true & &self.can_be_false & &self.can_be_und) ^ &self.full()
|
||||
}
|
||||
|
||||
fn no_matches(&self, other: &NoGood) -> RoaringBitmap {
|
||||
let no_true_match = &self.can_be_true ^ &other.can_be_true;
|
||||
let no_false_match = &self.can_be_false ^ &other.can_be_false;
|
||||
let no_und_match = &self.can_be_und ^ &other.can_be_und;
|
||||
|
||||
no_true_match | no_false_match | no_und_match
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
#[test]
|
||||
fn create_ng() {
|
||||
let terms = vec![Term::TOP, Term(22), Term(13232), Term::BOT, Term::TOP];
|
||||
let ng = NoGood::from_term_vec(&terms);
|
||||
|
||||
assert_eq!(terms.len(), 5);
|
||||
assert_eq!(ng.size, 5);
|
||||
log::debug!("{:?}", ng.active());
|
||||
assert_eq!(ng.active().len(), 3);
|
||||
assert_eq!(ng.idx_as_triple(0), (true, false, false));
|
||||
assert_eq!(ng.idx_as_triple(1), (true, true, true));
|
||||
assert_eq!(ng.idx_as_triple(2), (true, true, true));
|
||||
assert_eq!(ng.idx_as_triple(3), (false, true, false));
|
||||
assert_eq!(ng.idx_as_triple(4), (true, false, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_interpretation() {
|
||||
let terms = vec![Term::TOP, Term(22), Term(13232), Term::BOT, Term::TOP];
|
||||
let interpretation = Interpretation::from_term_vec(&terms);
|
||||
|
||||
assert_eq!(interpretation.active().len(), 3);
|
||||
assert_eq!(interpretation.idx_as_triple(0), (true, false, false));
|
||||
assert_eq!(interpretation.idx_as_triple(1), (true, true, true));
|
||||
assert_eq!(interpretation.idx_as_triple(2), (true, true, true));
|
||||
assert_eq!(interpretation.idx_as_triple(3), (false, true, false));
|
||||
assert_eq!(interpretation.idx_as_triple(4), (true, false, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conclude() {
|
||||
let ng1 = Interpretation::from_term_vec(&[
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
Term::TOP,
|
||||
Term::BOT,
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
]);
|
||||
let ng2 = Interpretation::from_term_vec(&[
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
Term(13232),
|
||||
Term::BOT,
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
]);
|
||||
let ng3 = Interpretation::from_term_vec(&[
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
Term::BOT,
|
||||
Term::BOT,
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
]);
|
||||
|
||||
let mut ng4 = ng3.clone();
|
||||
ng4.0.can_be_und.insert(2);
|
||||
ng4.0.can_be_false.remove(2);
|
||||
|
||||
let ng_too_big = Interpretation::from_term_vec(&[
|
||||
Term::TOP,
|
||||
Term(22),
|
||||
Term::TOP,
|
||||
Term::BOT,
|
||||
Term::TOP,
|
||||
Term::BOT,
|
||||
]);
|
||||
|
||||
assert!(ng1.clone().conclude(&ng2.0).consistent().is_none());
|
||||
let result = ng2.clone().conclude(&ng1.0).consistent().unwrap();
|
||||
assert_eq!(result.idx_as_triple(0), (true, false, false));
|
||||
assert_eq!(result.idx_as_triple(1), (true, true, true));
|
||||
assert_eq!(result.idx_as_triple(2), (false, true, true));
|
||||
assert_eq!(result.idx_as_triple(3), (false, true, false));
|
||||
assert_eq!(result.idx_as_triple(4), (true, false, false));
|
||||
|
||||
let result = result.conclude(&ng3.0).consistent().unwrap();
|
||||
assert_eq!(result.idx_as_triple(0), (true, false, false));
|
||||
assert_eq!(result.idx_as_triple(1), (true, true, true));
|
||||
assert_eq!(result.idx_as_triple(2), (false, false, true));
|
||||
assert_eq!(result.idx_as_triple(3), (false, true, false));
|
||||
assert_eq!(result.idx_as_triple(4), (true, false, false));
|
||||
|
||||
assert!(result.conclude(&ng4.0).consistent().is_none());
|
||||
|
||||
let result = ng2
|
||||
.clone()
|
||||
.conclude(&ng1.0)
|
||||
.consistent()
|
||||
.and_then(|i| i.conclude(&ng4.0).consistent())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result.idx_as_triple(0), (true, false, false));
|
||||
assert_eq!(result.idx_as_triple(1), (true, true, true));
|
||||
assert_eq!(result.idx_as_triple(2), (false, true, false));
|
||||
assert_eq!(result.idx_as_triple(3), (false, true, false));
|
||||
assert_eq!(result.idx_as_triple(4), (true, false, false));
|
||||
|
||||
log::debug!("{:?}", ng2.clone().conclude(&ng_too_big.0));
|
||||
if let Conclusion::NoChange(interpretation) = ng2.clone().conclude(&ng_too_big.0) {
|
||||
assert_eq!(interpretation, ng2);
|
||||
} else {
|
||||
panic!("test failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user