1
0
mirror of https://github.com/ellmau/adf-obdd.git synced 2025-12-19 09:29:36 +01:00

Add ng option with heu to binary

Fix various bugs with the nogood learner
This commit is contained in:
Stefan Ellmauthaler 2022-06-22 17:15:51 +02:00
parent 5da2c2cf62
commit 86084d7221
Failed to extract signature
8 changed files with 169 additions and 50 deletions

79
Cargo.lock generated
View File

@ -2,26 +2,11 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "adf_bdd"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e781519ea5434514f014476c02ccee777b28e600ad58fadca195715acb194c69"
dependencies = [
"biodivine-lib-bdd 0.3.0",
"derivative",
"lexical-sort",
"log",
"nom",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "adf_bdd" name = "adf_bdd"
version = "0.2.5" version = "0.2.5"
dependencies = [ dependencies = [
"biodivine-lib-bdd 0.4.0", "biodivine-lib-bdd",
"derivative", "derivative",
"env_logger 0.9.0", "env_logger 0.9.0",
"lexical-sort", "lexical-sort",
@ -32,14 +17,15 @@ dependencies = [
"roaring", "roaring",
"serde", "serde",
"serde_json", "serde_json",
"strum",
"test-log", "test-log",
] ]
[[package]] [[package]]
name = "adf_bdd-solver" name = "adf_bdd-solver"
version = "0.2.4" version = "0.2.5"
dependencies = [ dependencies = [
"adf_bdd 0.2.4", "adf_bdd",
"assert_cmd", "assert_cmd",
"assert_fs", "assert_fs",
"clap", "clap",
@ -48,6 +34,7 @@ dependencies = [
"predicates", "predicates",
"serde", "serde",
"serde_json", "serde_json",
"strum",
] ]
[[package]] [[package]]
@ -110,16 +97,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "biodivine-lib-bdd"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcfb460c1131729ec8d797ce0be49ec102506e764ce62f20a40056516851c5"
dependencies = [
"fxhash",
"rand 0.7.3",
]
[[package]] [[package]]
name = "biodivine-lib-bdd" name = "biodivine-lib-bdd"
version = "0.4.0" version = "0.4.0"
@ -168,16 +145,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.1.18" version = "3.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" checksum = "9f1fe12880bae935d142c8702d500c63a4e8634b6c3c57ad72bf978fc7b6249a"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive", "clap_derive",
"clap_lex", "clap_lex",
"indexmap", "indexmap",
"lazy_static", "once_cell",
"strsim", "strsim",
"termcolor", "termcolor",
"textwrap", "textwrap",
@ -185,9 +162,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.1.18" version = "3.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" checksum = "ed6db9e867166a43a53f7199b5e4d1f522a1e5bd626654be263c999ce59df39a"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -198,9 +175,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.0" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" checksum = "87eba3c8c7f42ef17f6c659fc7416d0f4758cd3e58861ee63c5fa4a4dde649e4"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@ -515,9 +492,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.9.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
@ -742,6 +719,12 @@ dependencies = [
"retain_mut", "retain_mut",
] ]
[[package]]
name = "rustversion"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.9" version = "1.0.9"
@ -794,6 +777,28 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.92" version = "1.0.92"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "adf_bdd-solver" name = "adf_bdd-solver"
version = "0.2.4" version = "0.2.5"
authors = ["Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>"] authors = ["Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>"]
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
@ -14,12 +14,13 @@ name = "adf-bdd"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
adf_bdd = { version="0.2.4", default-features = false } adf_bdd = { version="0.2.5", path="../lib", default-features = false }
clap = {version = "3.1.18", features = [ "derive", "cargo", "env" ]} clap = {version = "3.2.6", features = [ "derive", "cargo", "env" ]}
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_info" ] } log = { version = "0.4", features = [ "max_level_trace", "release_max_level_info" ] }
serde = { version = "1.0", features = ["derive","rc"] } serde = { version = "1.0", features = ["derive","rc"] }
serde_json = "1.0" serde_json = "1.0"
env_logger = "0.9" env_logger = "0.9"
strum = { version = "0.24" }
[dev-dependencies] [dev-dependencies]
assert_cmd = "2.0" assert_cmd = "2.0"

View File

@ -74,6 +74,7 @@ use adf_bdd::adfbiodivine::Adf as BdAdf;
use adf_bdd::parser::AdfParser; use adf_bdd::parser::AdfParser;
use clap::Parser; use clap::Parser;
use strum::VariantNames;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about)] #[clap(author, version, about)]
@ -84,7 +85,7 @@ struct App {
/// Sets the verbosity to 'warn', 'info', 'debug' or 'trace' if -v and -q are not use /// Sets the verbosity to 'warn', 'info', 'debug' or 'trace' if -v and -q are not use
#[clap(long = "rust_log", env)] #[clap(long = "rust_log", env)]
rust_log: Option<String>, rust_log: Option<String>,
/// choose the bdd implementation of either 'biodivine', 'naive', or hybrid /// Choose the bdd implementation of either 'biodivine', 'naive', or hybrid
#[clap(long = "lib", default_value = "hybrid")] #[clap(long = "lib", default_value = "hybrid")]
implementation: String, implementation: String,
/// Sets log verbosity (multiple times means more verbose) /// Sets log verbosity (multiple times means more verbose)
@ -120,6 +121,12 @@ struct App {
/// Compute the stable models with a single-formula rewriting on internal representation(only hybrid lib-mode) /// Compute the stable models with a single-formula rewriting on internal representation(only hybrid lib-mode)
#[clap(long = "stmrew2")] #[clap(long = "stmrew2")]
stable_rew2: bool, stable_rew2: bool,
/// Compute the stable models with the nogood-learning based approach
#[clap(long = "stmng")]
stable_ng: bool,
/// Choose which heuristics shall be used by the nogood-learning approach
#[clap(long, possible_values = adf_bdd::adf::heuristics::Heuristic::VARIANTS.iter().filter(|&v| v != &"Custom"))]
heu: Option<adf_bdd::adf::heuristics::Heuristic<'static>>,
/// Compute the complete models /// Compute the complete models
#[clap(long = "com")] #[clap(long = "com")]
complete: bool, complete: bool,
@ -244,6 +251,12 @@ impl App {
print!("{}", printer.print_interpretation(&model)); print!("{}", printer.print_interpretation(&model));
} }
} }
if self.stable_ng {
for model in naive_adf.stable_nogood(self.heu.unwrap_or_default()) {
print!("{}", printer.print_interpretation(&model));
}
}
} }
"biodivine" => { "biodivine" => {
if self.counter.is_some() { if self.counter.is_some() {

View File

@ -31,6 +31,7 @@ serde_json = "1.0"
biodivine-lib-bdd = "0.4.0" biodivine-lib-bdd = "0.4.0"
derivative = "2.2.0" derivative = "2.2.0"
roaring = "0.9.0" roaring = "0.9.0"
strum = { version = "0.24", features = ["derive"] }
[dev-dependencies] [dev-dependencies]
test-log = "0.2" test-log = "0.2"

View File

@ -815,7 +815,7 @@ impl Adf {
cur_interpr = self.update_interpretation_fixpoint_upd(&cur_interpr, &mut update_fp); cur_interpr = self.update_interpretation_fixpoint_upd(&cur_interpr, &mut update_fp);
if update_fp { if update_fp {
log::trace!("fixpount updated"); log::trace!("fixpount updated");
stack.push((false, cur_interpr.as_slice().into())); //stack.push((false, cur_interpr.as_slice().into()));
} else if !update_ng { } else if !update_ng {
// No updates done this loop // No updates done this loop
if !self.is_two_valued(&cur_interpr) { if !self.is_two_valued(&cur_interpr) {
@ -832,6 +832,7 @@ impl Adf {
} }
} }
} }
log::info!("{ng_store}");
result result
} }
} }

View File

@ -5,6 +5,13 @@ In addition there is the public enum [Heuristic], which allows to set a heuristi
use super::Adf; use super::Adf;
use crate::datatypes::{Term, Var}; use crate::datatypes::{Term, Var};
use strum::{EnumString, EnumVariantNames};
/// Return value for heuristics.
pub type RetVal = Option<(Var, Term)>;
/// Signature for heuristics functions.
pub type HeuristicFn = dyn Fn(&Adf, &[Term]) -> RetVal;
pub(crate) fn heu_simple(_adf: &Adf, interpr: &[Term]) -> Option<(Var, Term)> { pub(crate) fn heu_simple(_adf: &Adf, interpr: &[Term]) -> Option<(Var, Term)> {
for (idx, term) in interpr.iter().enumerate() { for (idx, term) in interpr.iter().enumerate() {
if !term.is_truth_value() { if !term.is_truth_value() {
@ -14,25 +21,59 @@ pub(crate) fn heu_simple(_adf: &Adf, interpr: &[Term]) -> Option<(Var, Term)> {
None None
} }
/// Return value for heuristics. pub(crate) fn heu_mc_minpaths_maxvarimp(adf: &Adf, interpr: &[Term]) -> Option<(Var, Term)> {
pub type RetVal = Option<(Var, Term)>; interpr
/// Signature for heuristics functions. .iter()
pub type HeuristicFn = dyn Fn(&Adf, &[Term]) -> RetVal; .enumerate()
.filter(|(_var, term)| !term.is_truth_value())
.min_by(|(vara, &terma), (varb, &termb)| {
match adf
.bdd
.paths(terma, true)
.minimum()
.cmp(&adf.bdd.paths(termb, true).minimum())
{
std::cmp::Ordering::Equal => adf
.bdd
.passive_var_impact(Var::from(*vara), interpr)
.cmp(&adf.bdd.passive_var_impact(Var::from(*varb), interpr)),
value => value,
}
})
.map(|(var, term)| {
(
Var::from(var),
(!adf.bdd.paths(*term, true).more_models()).into(),
)
})
}
/// Enumeration of all currently implemented heuristics. /// Enumeration of all currently implemented heuristics.
/// It represents a public view on the crate-view implementations of heuristics. /// It represents a public view on the crate-view implementations of heuristics.
#[derive(EnumString, EnumVariantNames, Copy, Clone)]
pub enum Heuristic<'a> { pub enum Heuristic<'a> {
/// Implementation of a simple heuristic. /// Implementation of a simple heuristic.
/// This will just take the first not decided variable and maps it value to (`true`)[Term::TOP]. /// This will just take the first not decided variable and maps it value to (`true`)[Term::TOP].
Simple, Simple,
/// Implementation of a heuristic, which which uses minimal number of [paths][crate::obdd::Bdd::paths] and maximal [variable-impact][crate::obdd::Bdd::passive_var_impact to identify the variable to be set.
/// As the value of the variable value with the minimal model-path is chosen.
MinModMinPathsMaxVarImp,
/// Allow passing in an externally-defined custom heuristic. /// Allow passing in an externally-defined custom heuristic.
#[strum(disabled)]
Custom(&'a HeuristicFn), Custom(&'a HeuristicFn),
} }
impl Default for Heuristic<'_> {
fn default() -> Self {
Self::Simple
}
}
impl std::fmt::Debug for Heuristic<'_> { impl std::fmt::Debug for Heuristic<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Simple => write!(f, "Simple"), Self::Simple => write!(f, "Simple"),
Self::MinModMinPathsMaxVarImp => write!(f, "Minimal model count as value and minimum paths with maximal variable impact as variable choice"),
Self::Custom(_) => f.debug_tuple("Custom function").finish(), Self::Custom(_) => f.debug_tuple("Custom function").finish(),
} }
} }
@ -42,7 +83,23 @@ impl Heuristic<'_> {
pub(crate) fn get_heuristic(&self) -> &(dyn Fn(&Adf, &[Term]) -> RetVal + '_) { pub(crate) fn get_heuristic(&self) -> &(dyn Fn(&Adf, &[Term]) -> RetVal + '_) {
match self { match self {
Heuristic::Simple => &heu_simple, Heuristic::Simple => &heu_simple,
Heuristic::MinModMinPathsMaxVarImp => &heu_mc_minpaths_maxvarimp,
Self::Custom(f) => f, Self::Custom(f) => f,
} }
} }
} }
// impl FromStr for Heuristic<'_> {
// type Err = ();
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// match s {
// "simple" => Ok(Self::Simple),
// "MinmcMinpathsMaxvarimp" => Ok(Self::MinModMinPathsMaxVarImp),
// _ => {
// log::info!("Unknown heuristic {s}");
// Err(())
// }
// }
// }
// }

View File

@ -25,6 +25,16 @@ impl From<usize> for Term {
} }
} }
impl From<bool> for Term {
fn from(val: bool) -> Self {
if val {
Self::TOP
} else {
Self::BOT
}
}
}
impl From<&biodivine_lib_bdd::Bdd> for Term { impl From<&biodivine_lib_bdd::Bdd> for Term {
fn from(val: &biodivine_lib_bdd::Bdd) -> Self { fn from(val: &biodivine_lib_bdd::Bdd) -> Self {
if val.is_true() { if val.is_true() {

View File

@ -2,7 +2,8 @@
use std::{ use std::{
borrow::Borrow, borrow::Borrow,
ops::{BitAnd, BitOr, BitXor}, fmt::Display,
ops::{BitAnd, BitOr, BitXor, BitXorAssign},
}; };
use crate::datatypes::Term; use crate::datatypes::Term;
@ -117,12 +118,22 @@ impl NoGood {
.borrow() .borrow()
.bitxor(other.active.borrow()) .bitxor(other.active.borrow())
.bitand(self.active.borrow()); .bitand(self.active.borrow());
if implication.len() == 1 {
let bothactive = self.active.borrow().bitand(other.active.borrow());
let mut no_matches = bothactive.borrow().bitand(other.value.borrow());
no_matches.bitxor_assign(bothactive.bitand(self.value.borrow()));
if implication.len() == 1 && no_matches.is_empty() {
let pos = implication let pos = implication
.min() .min()
.expect("just checked that there is one element to be found"); .expect("just checked that there is one element to be found");
log::trace!(
"Conclude {:?}",
Some((pos as usize, !self.value.contains(pos)))
);
Some((pos as usize, !self.value.contains(pos))) Some((pos as usize, !self.value.contains(pos)))
} else { } else {
log::trace!("Nothing to Conclude");
None None
} }
} }
@ -175,6 +186,16 @@ pub struct NoGoodStore {
duplicates: DuplicateElemination, duplicates: DuplicateElemination,
} }
impl Display for NoGoodStore {
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())?;
}
write!(f, "]")
}
}
impl NoGoodStore { impl NoGoodStore {
/// Creates a new [NoGoodStore] and assumes a size compatible with the underlying [NoGood] implementation. /// Creates a new [NoGoodStore] and assumes a size compatible with the underlying [NoGood] implementation.
pub fn new(size: u32) -> NoGoodStore { pub fn new(size: u32) -> NoGoodStore {
@ -227,6 +248,7 @@ impl NoGoodStore {
/// *Returns* [None] if there is a conflict /// *Returns* [None] if there is a conflict
pub fn conclusions(&self, nogood: &NoGood) -> Option<NoGood> { pub fn conclusions(&self, nogood: &NoGood) -> Option<NoGood> {
let mut result = nogood.clone(); let mut result = nogood.clone();
log::trace!("ng-store: {:?}", self.store);
self.store self.store
.iter() .iter()
.enumerate() .enumerate()
@ -236,6 +258,7 @@ impl NoGoodStore {
}) })
.try_fold(&mut result, |acc, ng| { .try_fold(&mut result, |acc, ng| {
if ng.is_violating(acc) { if ng.is_violating(acc) {
log::trace!("ng conclusion violating");
None None
} else { } else {
acc.disjunction(&ng); acc.disjunction(&ng);
@ -261,7 +284,15 @@ impl NoGoodStore {
pub(crate) fn conclusion_closure(&self, interpretation: &[Term]) -> ClosureResult { pub(crate) fn conclusion_closure(&self, interpretation: &[Term]) -> ClosureResult {
let mut update = true; let mut update = true;
let mut result = match self.conclusions(&interpretation.into()) { let mut result = match self.conclusions(&interpretation.into()) {
Some(val) => val.update_term_vec(interpretation, &mut update), Some(val) => {
log::trace!(
"conclusion-closure step 1: val:{:?} -> {:?}",
val,
val.update_term_vec(interpretation, &mut update)
);
val.update_term_vec(interpretation, &mut update)
}
None => return ClosureResult::Inconsistent, None => return ClosureResult::Inconsistent,
}; };
if !update { if !update {
@ -786,7 +817,7 @@ mod test {
assert_eq!( assert_eq!(
ngs.conclusion_closure(&[Term(1), Term(3), Term(3), Term(9), Term(0), Term(1)]), ngs.conclusion_closure(&[Term(1), Term(3), Term(3), Term(9), Term(0), Term(1)]),
ClosureResult::Update(vec![Term(1), Term(0), Term(0), Term(1), Term(0), Term(1)]) ClosureResult::Update(vec![Term(1), Term(0), Term(3), Term(9), Term(0), Term(1)])
); );
} }
} }