mirror of
https://github.com/ellmau/adf-obdd.git
synced 2025-12-19 09:29:36 +01:00
Add a channel to inform other threads about bdd updates (#95)
* Add a channel to inform other threads about bdd updates * Redisign sender and receiver model for bdds * Add options for both directions and allow to utilise none, one, or both at the same time.
This commit is contained in:
parent
495f9c893f
commit
bc21ecabc7
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -21,7 +21,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adf_bdd"
|
name = "adf_bdd"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"biodivine-lib-bdd",
|
"biodivine-lib-bdd",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
|||||||
@ -16,7 +16,7 @@ name = "adf-bdd"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
adf_bdd = { version="0.3.0", path="../lib", default-features = false }
|
adf_bdd = { version="0.3.1", path="../lib", default-features = false }
|
||||||
clap = {version = "3.2.16", features = [ "derive", "cargo", "env" ]}
|
clap = {version = "3.2.16", 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"] }
|
||||||
@ -31,10 +31,11 @@ predicates = "2.1"
|
|||||||
assert_fs = "1.0"
|
assert_fs = "1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["adhoccounting", "variablelist", "adf_bdd/default" ]
|
default = ["adhoccounting", "variablelist", "adf_bdd/default", "frontend"]
|
||||||
adhoccounting = ["adf_bdd/adhoccounting"] # count models ad-hoc - disable if counting is not needed
|
adhoccounting = ["adf_bdd/adhoccounting"] # count models ad-hoc - disable if counting is not needed
|
||||||
importexport = ["adf_bdd/importexport"]
|
importexport = ["adf_bdd/importexport"]
|
||||||
variablelist = [ "HashSet", "adf_bdd/variablelist" ]
|
variablelist = [ "HashSet", "adf_bdd/variablelist" ]
|
||||||
HashSet = ["adf_bdd/HashSet"]
|
HashSet = ["adf_bdd/HashSet"]
|
||||||
adhoccountmodels = ["adf_bdd/adhoccountmodels"]
|
adhoccountmodels = ["adf_bdd/adhoccountmodels"]
|
||||||
benchmark = ["adf_bdd/benchmark"]
|
benchmark = ["adf_bdd/benchmark"]
|
||||||
|
frontend = ["adf_bdd/frontend"]
|
||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "adf_bdd"
|
name = "adf_bdd"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
authors = ["Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>"]
|
authors = ["Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
homepage = "https://ellmau.github.io/adf-obdd/"
|
homepage = "https://ellmau.github.io/adf-obdd/"
|
||||||
@ -42,10 +42,11 @@ quickcheck = "1"
|
|||||||
quickcheck_macros = "1"
|
quickcheck_macros = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["adhoccounting", "variablelist" ]
|
default = ["adhoccounting", "variablelist", "frontend" ]
|
||||||
adhoccounting = [] # count paths ad-hoc - disable if counting is not needed
|
adhoccounting = [] # count paths ad-hoc - disable if counting is not needed
|
||||||
importexport = []
|
importexport = []
|
||||||
variablelist = [ "HashSet" ]
|
variablelist = [ "HashSet" ]
|
||||||
HashSet = []
|
HashSet = []
|
||||||
adhoccountmodels = [ "adhoccounting" ] # count models as well as paths ad-hoc note that facet methods will need this feature too
|
adhoccountmodels = [ "adhoccounting" ] # count models as well as paths ad-hoc note that facet methods will need this feature too
|
||||||
benchmark = ["adhoccounting", "variablelist"] # set of features for speed benchmarks
|
benchmark = ["adhoccounting", "variablelist"] # set of features for speed benchmarks
|
||||||
|
frontend = []
|
||||||
@ -145,7 +145,7 @@ impl Var {
|
|||||||
/// Intuitively this is a binary tree structure, where the diagram is allowed to
|
/// Intuitively this is a binary tree structure, where the diagram is allowed to
|
||||||
/// pool same values to the same Node.
|
/// pool same values to the same Node.
|
||||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub(crate) struct BddNode {
|
pub struct BddNode {
|
||||||
var: Var,
|
var: Var,
|
||||||
lo: Term,
|
lo: Term,
|
||||||
hi: Term,
|
hi: Term,
|
||||||
@ -157,6 +157,12 @@ impl Display for BddNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for BddNode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::top_node()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BddNode {
|
impl BddNode {
|
||||||
/// Creates a new Node.
|
/// Creates a new Node.
|
||||||
pub fn new(var: Var, lo: Term, hi: Term) -> Self {
|
pub fn new(var: Var, lo: Term, hi: Term) -> Self {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
//! Module which represents obdds.
|
//! Module which represents obdds.
|
||||||
//!
|
//!
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
pub mod frontend;
|
||||||
pub mod vectorize;
|
pub mod vectorize;
|
||||||
use crate::datatypes::*;
|
use crate::datatypes::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -19,6 +21,12 @@ pub struct Bdd {
|
|||||||
cache: HashMap<BddNode, Term>,
|
cache: HashMap<BddNode, Term>,
|
||||||
#[serde(skip, default = "Bdd::default_count_cache")]
|
#[serde(skip, default = "Bdd::default_count_cache")]
|
||||||
count_cache: RefCell<HashMap<Term, CountNode>>,
|
count_cache: RefCell<HashMap<Term, CountNode>>,
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
#[serde(skip)]
|
||||||
|
sender: Option<crossbeam_channel::Sender<BddNode>>,
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
#[serde(skip)]
|
||||||
|
receiver: Option<crossbeam_channel::Receiver<BddNode>>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
ite_cache: HashMap<(Term, Term, Term), Term>,
|
ite_cache: HashMap<(Term, Term, Term), Term>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@ -42,7 +50,7 @@ impl Default for Bdd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Bdd {
|
impl Bdd {
|
||||||
/// Instantiate a new roBDD structures.
|
/// Instantiate a new roBDD structure.
|
||||||
/// Constants for the [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are prepared in that step too.
|
/// Constants for the [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are prepared in that step too.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
#[cfg(not(feature = "adhoccounting"))]
|
#[cfg(not(feature = "adhoccounting"))]
|
||||||
@ -53,6 +61,10 @@ impl Bdd {
|
|||||||
var_deps: vec![HashSet::new(), HashSet::new()],
|
var_deps: vec![HashSet::new(), HashSet::new()],
|
||||||
cache: HashMap::new(),
|
cache: HashMap::new(),
|
||||||
count_cache: RefCell::new(HashMap::new()),
|
count_cache: RefCell::new(HashMap::new()),
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
sender: None,
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
receiver: None,
|
||||||
ite_cache: HashMap::new(),
|
ite_cache: HashMap::new(),
|
||||||
restrict_cache: HashMap::new(),
|
restrict_cache: HashMap::new(),
|
||||||
}
|
}
|
||||||
@ -65,6 +77,10 @@ impl Bdd {
|
|||||||
var_deps: vec![HashSet::new(), HashSet::new()],
|
var_deps: vec![HashSet::new(), HashSet::new()],
|
||||||
cache: HashMap::new(),
|
cache: HashMap::new(),
|
||||||
count_cache: RefCell::new(HashMap::new()),
|
count_cache: RefCell::new(HashMap::new()),
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
sender: None,
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
receiver: None,
|
||||||
ite_cache: HashMap::new(),
|
ite_cache: HashMap::new(),
|
||||||
restrict_cache: HashMap::new(),
|
restrict_cache: HashMap::new(),
|
||||||
};
|
};
|
||||||
@ -264,6 +280,15 @@ impl Bdd {
|
|||||||
let new_term = Term(self.nodes.len());
|
let new_term = Term(self.nodes.len());
|
||||||
self.nodes.push(node);
|
self.nodes.push(node);
|
||||||
self.cache.insert(node, new_term);
|
self.cache.insert(node, new_term);
|
||||||
|
#[cfg(feature = "frontend")]
|
||||||
|
if let Some(send) = &self.sender {
|
||||||
|
match send.send(node) {
|
||||||
|
Ok(_) => log::trace!("Sent {node} to the channel."),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error {e} occurred when sending {node} to {:?}", send)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(feature = "variablelist")]
|
#[cfg(feature = "variablelist")]
|
||||||
{
|
{
|
||||||
let mut var_set: HashSet<Var> = self.var_deps[lo.value()]
|
let mut var_set: HashSet<Var> = self.var_deps[lo.value()]
|
||||||
|
|||||||
266
lib/src/obdd/frontend.rs
Normal file
266
lib/src/obdd/frontend.rs
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
//! Implementation of frontend-feature related methods and functions
|
||||||
|
//! See the Structs in the [obdd-module][super] for most of the implementations
|
||||||
|
|
||||||
|
use crate::datatypes::Term;
|
||||||
|
|
||||||
|
use super::BddNode;
|
||||||
|
impl super::Bdd {
|
||||||
|
/// Instantiate a new [roBDD][super::Bdd] structure.
|
||||||
|
/// Constants for the [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are prepared in that step too.
|
||||||
|
/// # Attention
|
||||||
|
/// Constants for [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are not sent, as they are considered to be existing in every [Bdd][super::Bdd] structure.
|
||||||
|
pub fn with_sender(sender: crossbeam_channel::Sender<BddNode>) -> Self {
|
||||||
|
// TODO nicer handling of the initialisation though overhead is not an issue here
|
||||||
|
let mut result = Self::new();
|
||||||
|
result.set_sender(sender);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instantiate a new [roBDD][super::Bdd] structure.
|
||||||
|
/// Constants for the [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are prepared in that step too.
|
||||||
|
/// # Attention
|
||||||
|
/// Note that mixing manipulating operations and utilising the communication channel for a receiving [roBDD][super::Bdd] may end up in inconsistent data.
|
||||||
|
/// So far, only manipulate the [roBDD][super::Bdd] if no further [recv][Self::recv] will be called.
|
||||||
|
pub fn with_receiver(receiver: crossbeam_channel::Receiver<BddNode>) -> Self {
|
||||||
|
// TODO nicer handling of the initialisation though overhead is not an issue here
|
||||||
|
let mut result = Self::new();
|
||||||
|
result.set_receiver(receiver);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instantiate a new [roBDD][super::Bdd] structure.
|
||||||
|
/// Constants for the [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are prepared in that step too.
|
||||||
|
/// # Attention
|
||||||
|
/// - Constants for [`⊤`][crate::datatypes::Term::TOP] and [`⊥`][crate::datatypes::Term::BOT] concepts are not sent, as they are considered to be existing in every [Bdd][super::Bdd] structure.
|
||||||
|
/// - Mixing manipulating operations and utilising the communication channel for a receiving [roBDD][super::Bdd] may end up in inconsistent data.
|
||||||
|
/// So far, only manipulate the [roBDD][super::Bdd] if no further [recv][Self::recv] will be called.
|
||||||
|
pub fn with_sender_receiver(
|
||||||
|
sender: crossbeam_channel::Sender<BddNode>,
|
||||||
|
receiver: crossbeam_channel::Receiver<BddNode>,
|
||||||
|
) -> Self {
|
||||||
|
let mut result = Self::new();
|
||||||
|
result.set_receiver(receiver);
|
||||||
|
result.set_sender(sender);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the currently used [sender][crossbeam_channel::Sender]
|
||||||
|
pub fn set_sender(&mut self, sender: crossbeam_channel::Sender<BddNode>) {
|
||||||
|
self.sender = Some(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the currently used [receiver][crossbeam_channel::Receiver]
|
||||||
|
pub fn set_receiver(&mut self, receiver: crossbeam_channel::Receiver<BddNode>) {
|
||||||
|
self.receiver = Some(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receives all information till the looked for [`Term`][crate::datatypes::Term] is either found or all data is read.
|
||||||
|
/// Note that the values are read, consumed, and added to the [Bdd][super::Bdd].
|
||||||
|
/// # Returns
|
||||||
|
/// - [`true`] if the [term][crate::datatypes::Term] is found (either in the [Bdd][super::Bdd] or in the channel.
|
||||||
|
/// - [`false`] if neither the [Bdd][super::Bdd] nor the channel contains the [term][crate::datatypes::Term].
|
||||||
|
pub fn recv(&mut self, term: Term) -> bool {
|
||||||
|
if term.value() < self.nodes.len() {
|
||||||
|
true
|
||||||
|
} else if let Some(recv) = &self.receiver {
|
||||||
|
loop {
|
||||||
|
match recv.try_recv() {
|
||||||
|
Ok(node) => {
|
||||||
|
let new_term = Term(self.nodes.len());
|
||||||
|
self.nodes.push(node);
|
||||||
|
self.cache.insert(node, new_term);
|
||||||
|
if let Some(send) = &self.sender {
|
||||||
|
match send.send(node) {
|
||||||
|
Ok(_) => log::trace!("Sent {node} to the channel."),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Error {e} occurred when sending {node} to {:?}",
|
||||||
|
send
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if new_term == term {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_bdd_updates() {
|
||||||
|
let (send, recv) = crossbeam_channel::unbounded();
|
||||||
|
let mut bdd = Bdd::with_sender(send);
|
||||||
|
|
||||||
|
let solving = std::thread::spawn(move || {
|
||||||
|
let v1 = bdd.variable(Var(0));
|
||||||
|
let v2 = bdd.variable(Var(1));
|
||||||
|
|
||||||
|
assert_eq!(v1, Term(2));
|
||||||
|
assert_eq!(v2, Term(3));
|
||||||
|
|
||||||
|
let t1 = bdd.and(v1, v2);
|
||||||
|
let nt1 = bdd.not(t1);
|
||||||
|
let ft = bdd.or(v1, nt1);
|
||||||
|
|
||||||
|
assert_eq!(ft, Term::TOP);
|
||||||
|
|
||||||
|
let v3 = bdd.variable(Var(2));
|
||||||
|
let nv3 = bdd.not(v3);
|
||||||
|
assert_eq!(bdd.and(v3, nv3), Term::BOT);
|
||||||
|
|
||||||
|
let conj = bdd.and(v1, v2);
|
||||||
|
assert_eq!(bdd.restrict(conj, Var(0), false), Term::BOT);
|
||||||
|
assert_eq!(bdd.restrict(conj, Var(0), true), v2);
|
||||||
|
|
||||||
|
let a = bdd.and(v3, v2);
|
||||||
|
let b = bdd.or(v2, v1);
|
||||||
|
|
||||||
|
let con1 = bdd.and(a, conj);
|
||||||
|
|
||||||
|
let end = bdd.or(con1, b);
|
||||||
|
log::debug!("Restrict test: restrict({},{},false)", end, Var(1));
|
||||||
|
let x = bdd.restrict(end, Var(1), false);
|
||||||
|
assert_eq!(x, Term(2));
|
||||||
|
});
|
||||||
|
|
||||||
|
let updates: Vec<BddNode> = recv.iter().collect();
|
||||||
|
assert_eq!(
|
||||||
|
updates,
|
||||||
|
vec![
|
||||||
|
BddNode::new(Var(0), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(1), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(3)),
|
||||||
|
BddNode::new(Var(1), Term(1), Term(0)),
|
||||||
|
BddNode::new(Var(0), Term(1), Term(5)),
|
||||||
|
BddNode::new(Var(2), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(2), Term(1), Term(0)),
|
||||||
|
BddNode::new(Var(1), Term(0), Term(7)),
|
||||||
|
BddNode::new(Var(0), Term(3), Term(1)),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(9)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
solving.join().expect("Both threads should terminate");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recv_send() {
|
||||||
|
let (send1, recv1) = crossbeam_channel::unbounded();
|
||||||
|
let (send2, recv2) = crossbeam_channel::unbounded();
|
||||||
|
let mut bdd1 = Bdd::with_sender(send1);
|
||||||
|
let mut bddm = Bdd::with_sender_receiver(send2, recv1);
|
||||||
|
let mut bddl = Bdd::with_receiver(recv2);
|
||||||
|
|
||||||
|
let solving = std::thread::spawn(move || {
|
||||||
|
let v1 = bdd1.variable(Var(0));
|
||||||
|
let v2 = bdd1.variable(Var(1));
|
||||||
|
|
||||||
|
assert_eq!(v1, Term(2));
|
||||||
|
assert_eq!(v2, Term(3));
|
||||||
|
|
||||||
|
let t1 = bdd1.and(v1, v2);
|
||||||
|
let nt1 = bdd1.not(t1);
|
||||||
|
let ft = bdd1.or(v1, nt1);
|
||||||
|
|
||||||
|
assert_eq!(ft, Term::TOP);
|
||||||
|
|
||||||
|
let v3 = bdd1.variable(Var(2));
|
||||||
|
let nv3 = bdd1.not(v3);
|
||||||
|
assert_eq!(bdd1.and(v3, nv3), Term::BOT);
|
||||||
|
|
||||||
|
let conj = bdd1.and(v1, v2);
|
||||||
|
assert_eq!(bdd1.restrict(conj, Var(0), false), Term::BOT);
|
||||||
|
assert_eq!(bdd1.restrict(conj, Var(0), true), v2);
|
||||||
|
|
||||||
|
let a = bdd1.and(v3, v2);
|
||||||
|
let b = bdd1.or(v2, v1);
|
||||||
|
|
||||||
|
let con1 = bdd1.and(a, conj);
|
||||||
|
|
||||||
|
let end = bdd1.or(con1, b);
|
||||||
|
log::debug!("Restrict test: restrict({},{},false)", end, Var(1));
|
||||||
|
let x = bdd1.restrict(end, Var(1), false);
|
||||||
|
assert_eq!(x, Term(2));
|
||||||
|
});
|
||||||
|
// allow the worker to fill the channels
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
// both are initialised, no updates so far
|
||||||
|
assert_eq!(bddm.nodes, bddl.nodes);
|
||||||
|
// receiving a truth constant should work without changing the bdd
|
||||||
|
assert!(bddm.recv(Term::TOP));
|
||||||
|
assert_eq!(bddm.nodes, bddl.nodes);
|
||||||
|
// receiving some element works for middle -> last, but not last -> middle
|
||||||
|
assert!(bddm.recv(Term(2)));
|
||||||
|
assert!(bddl.recv(Term(2)));
|
||||||
|
assert_eq!(bddl.nodes.len(), 3);
|
||||||
|
assert!(!bddl.recv(Term(5)));
|
||||||
|
// get all elements into middle bdd1
|
||||||
|
assert!(!bddm.recv(Term(usize::MAX)));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bddm.nodes,
|
||||||
|
vec![
|
||||||
|
BddNode::bot_node(),
|
||||||
|
BddNode::top_node(),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(1), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(3)),
|
||||||
|
BddNode::new(Var(1), Term(1), Term(0)),
|
||||||
|
BddNode::new(Var(0), Term(1), Term(5)),
|
||||||
|
BddNode::new(Var(2), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(2), Term(1), Term(0)),
|
||||||
|
BddNode::new(Var(1), Term(0), Term(7)),
|
||||||
|
BddNode::new(Var(0), Term(3), Term(1)),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(9)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// last bdd is still in the previous state
|
||||||
|
assert_eq!(
|
||||||
|
bddl.nodes,
|
||||||
|
vec![
|
||||||
|
BddNode::bot_node(),
|
||||||
|
BddNode::top_node(),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(1)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// and now catch up till 10
|
||||||
|
assert!(bddl.recv(Term(10)));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bddl.nodes,
|
||||||
|
vec![
|
||||||
|
BddNode::bot_node(),
|
||||||
|
BddNode::top_node(),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(1), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(0), Term(0), Term(3)),
|
||||||
|
BddNode::new(Var(1), Term(1), Term(0)),
|
||||||
|
BddNode::new(Var(0), Term(1), Term(5)),
|
||||||
|
BddNode::new(Var(2), Term(0), Term(1)),
|
||||||
|
BddNode::new(Var(2), Term(1), Term(0)),
|
||||||
|
BddNode::new(Var(1), Term(0), Term(7)),
|
||||||
|
BddNode::new(Var(0), Term(3), Term(1)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
solving.join().expect("Both threads should terminate");
|
||||||
|
|
||||||
|
// asking for 10 again works too
|
||||||
|
assert!(bddl.recv(Term(10)));
|
||||||
|
// fully catch up with the last bdd
|
||||||
|
assert!(bddl.recv(Term(11)));
|
||||||
|
assert_eq!(bddl.nodes, bddm.nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user