Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bdds #26

Merged
merged 51 commits into from
May 17, 2024
Merged

Bdds #26

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
33136f7
feat(bdd): initial ideas for Bdd implementation
daemontus May 9, 2024
921ee65
feat(bdd): expanded doc comments
AurumTheEnd May 10, 2024
bec44fa
feat(bdd): made iterators work
AurumTheEnd May 10, 2024
b528c97
refactor(bdd): moved BitAnd impl into traits module
AurumTheEnd May 10, 2024
7e7c1ff
refactor(bdd): refactored extend/prune bdd_variables functions into s…
AurumTheEnd May 10, 2024
56d1557
refactor(bdd): refactored bool fn trait into separate traits mod
AurumTheEnd May 10, 2024
7460963
fix(bdd): fixed clippy errors
AurumTheEnd May 10, 2024
67c926d
fix(bdd): removed redundant 'static trait bound for literals
AurumTheEnd May 10, 2024
1de7cd2
fix(table): removed redundant 'static trait bound for literals
AurumTheEnd May 10, 2024
20fd7ec
refactor(bdd): made util method make_inner_variable_set return Result…
AurumTheEnd May 10, 2024
127c0a3
feat(bdd): implemented TryFrom<Expression<T>> for Bdd<T>
AurumTheEnd May 10, 2024
dc3de43
feat(bdd): implemented TryFrom<TruthTable<T>> for Bdd<T>
AurumTheEnd May 10, 2024
535ec6b
refactor(bdd): made conversion methods pub(crate)
AurumTheEnd May 10, 2024
c51b606
feat(bdd): implemented TryFrom<Bdd<T>> for Expression<T>
AurumTheEnd May 10, 2024
ad70db6
feat(table): added simple util method for boolean point to table row …
AurumTheEnd May 10, 2024
cbd0171
feat(bdd): implemented TryFrom<Bdd<T>> for Table<T>
AurumTheEnd May 10, 2024
f537fa1
refactor(bdd): moved duplicate code to restrict_and_prune & union_and…
AurumTheEnd May 10, 2024
33df18b
feat(bdd): added n_ary version of union_and_extend util method
AurumTheEnd May 10, 2024
b1e9f5a
feat(bdd): implemented substitute
AurumTheEnd May 10, 2024
be1b4f9
refactor(bdd): introduced pub(crate) constructor
AurumTheEnd May 11, 2024
1d22946
feat(tests): expression_from_bdd
AurumTheEnd May 11, 2024
42aabcb
build: bumped lib_bdd version
AurumTheEnd May 13, 2024
be4c63a
feat(tests): bdd_from_expression
AurumTheEnd May 13, 2024
71aaaf5
fix(tests): included constants in bdd_from_expression tests
AurumTheEnd May 13, 2024
fa826a1
feat(tests): bdd_from_table
AurumTheEnd May 13, 2024
c104e67
fix(bdd): fixed index out of bounds in prune_variables
AurumTheEnd May 13, 2024
431cd7c
feat(tests): bdd.restrict
AurumTheEnd May 13, 2024
be30b0b
fix(bdd): fixed index out of bounds in extend_variables
AurumTheEnd May 13, 2024
afb5a98
feat(tests): some bdd.substitute
AurumTheEnd May 13, 2024
b8125b3
feat(tests): bdd.inputs
AurumTheEnd May 13, 2024
7daedfa
feat(bdd): implemented evaluate trait
AurumTheEnd May 13, 2024
9564e04
feat(test): bdd.*quantification
AurumTheEnd May 13, 2024
b390c55
feat(test): bdd.domain
AurumTheEnd May 13, 2024
913f385
feat(test): bdd.essential_degree
AurumTheEnd May 13, 2024
8d9af57
feat(test): bdd.image
AurumTheEnd May 13, 2024
ff45d68
feat(test): bdd.relation
AurumTheEnd May 13, 2024
392c762
feat(test): bdd.support
AurumTheEnd May 13, 2024
fd24848
fix(bdd): better (more correct) implementation of `extend` and `prune…
daemontus May 14, 2024
8e6d50b
fix(bdd): removed degree implementation which was the same as the def…
AurumTheEnd May 14, 2024
acda0fb
feat(tests): table_from_bdd
AurumTheEnd May 14, 2024
4958293
fix(bdd): uncommented test that was off by mistake
AurumTheEnd May 14, 2024
5275e14
refactor(bdd): extracted common functionality out of impl BitAnd for Bdd
AurumTheEnd May 14, 2024
aa9c92e
feat(bdd): implemented gather literals
AurumTheEnd May 14, 2024
3c99213
fix(tests): fixed names of table bitwise tests
AurumTheEnd May 14, 2024
e8d41d8
feat(tests): added bdd.and tests
AurumTheEnd May 14, 2024
6154887
feat(bdd): implemented BitOr & BitXor for bdd
AurumTheEnd May 14, 2024
c3e7aa6
feat(bdd): handled constant edgecases in expression_from_bdd
AurumTheEnd May 15, 2024
d5513ff
feat(tests): added constant test for bdd_from_expression
AurumTheEnd May 15, 2024
95352db
refactor(bdd): refactored bdd.evaluate to reduce coverage blindspots
AurumTheEnd May 15, 2024
dd29be4
feat(test): bdd.weight & bdd.sat_point
AurumTheEnd May 15, 2024
5e57d83
feat(test): bdd.is_implied_by
AurumTheEnd May 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tabled = "0.15.0"
csv = { version = "1.3.0", optional = true }
tempfile = { version = "3.10.1", optional = true }
num-bigint = "0.4.4"
biodivine-lib-bdd = "0.5.16"

pyo3 = { version = "0.21.2", features = ["abi3-py37", "extension-module", "num-bigint"], optional = true }

Expand Down
96 changes: 96 additions & 0 deletions src/bdd/iterators/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::iterators::DomainIterator;
use biodivine_lib_bdd::Bdd;
use biodivine_lib_bdd::BddValuation;

pub struct ImageIterator {
domain_iterator: Box<dyn Iterator<Item = Vec<bool>>>,
bdd: Bdd,
}

impl ImageIterator {
pub(crate) fn new(input_count: usize, bdd: &Bdd) -> Self {
Self {
domain_iterator: Box::new(DomainIterator::from_count(input_count)),
bdd: bdd.clone(),
}
}
}

impl Iterator for ImageIterator {
type Item = bool;

fn next(&mut self) -> Option<Self::Item> {
self.domain_iterator
.next()
.map(|it| self.bdd.eval_in(&BddValuation::new(it)))
}
}

#[cfg(test)]
mod tests {
use crate::bdd::Bdd;
use crate::expressions::var;
use crate::traits::{BooleanFunction, Evaluate};
use std::collections::BTreeMap;

#[test]
fn test_image_ok() {
let input = Bdd::try_from(var("d") & var("b") | var("a")).expect("Should not panic");

let mut actual = input.image();
let expected = [
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), false),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), false),
("d".to_string(), true),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), true),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), true),
("d".to_string(), true),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), false),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), false),
("d".to_string(), true),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), true),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), true),
("d".to_string(), true),
]))),
];

assert_eq!(actual.next(), expected[0]);
assert_eq!(actual.next(), expected[1]);
assert_eq!(actual.next(), expected[2]);
assert_eq!(actual.next(), expected[3]);
assert_eq!(actual.next(), expected[4]);
assert_eq!(actual.next(), expected[5]);
assert_eq!(actual.next(), expected[6]);
assert_eq!(actual.next(), expected[7]);

assert_eq!(actual.next(), None);
assert_eq!(actual.next(), None);
}
}
5 changes: 5 additions & 0 deletions src/bdd/iterators/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub use image::ImageIterator;
pub use support::SupportIterator;

mod image;
mod support;
90 changes: 90 additions & 0 deletions src/bdd/iterators/support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use biodivine_lib_bdd::{Bdd, OwnedBddSatisfyingValuations};

pub struct SupportIterator {
iterator: OwnedBddSatisfyingValuations,
}

impl SupportIterator {
pub(crate) fn new(bdd: &Bdd) -> Self {
Self {
iterator: bdd.clone().into_sat_valuations(),
}
}
}

impl Iterator for SupportIterator {
type Item = Vec<bool>;

fn next(&mut self) -> Option<Self::Item> {
self.iterator.next().map(|it| it.vector())
}
}

#[cfg(test)]
mod tests {
use crate::bdd::Bdd;
use crate::expressions::var;
use crate::traits::{BooleanFunction, Evaluate};
use std::collections::BTreeMap;

#[test]
fn test_image_ok() {
let input = Bdd::try_from(var("d") & var("b") | var("a")).expect("Should not panic here");

let mut actual = input.image();
let expected = [
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), false),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), false),
("d".to_string(), true),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), true),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), false),
("b".to_string(), true),
("d".to_string(), true),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), false),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), false),
("d".to_string(), true),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), true),
("d".to_string(), false),
]))),
Some(input.evaluate(&BTreeMap::from([
("a".to_string(), true),
("b".to_string(), true),
("d".to_string(), true),
]))),
];

assert_eq!(actual.next(), expected[0]);
assert_eq!(actual.next(), expected[1]);
assert_eq!(actual.next(), expected[2]);
assert_eq!(actual.next(), expected[3]);
assert_eq!(actual.next(), expected[4]);
assert_eq!(actual.next(), expected[5]);
assert_eq!(actual.next(), expected[6]);
assert_eq!(actual.next(), expected[7]);

assert_eq!(actual.next(), None);
assert_eq!(actual.next(), None);
}
}
150 changes: 150 additions & 0 deletions src/bdd/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
mod iterators;
mod traits;
mod utils;

use crate::bdd::utils::{extend_bdd_variables, prune_bdd_variables};
use biodivine_lib_bdd::{Bdd as InnerBdd, BddVariable, BddVariableSet};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Debug;
use std::num::TryFromIntError;

/*
Conversions:

Bdd -> Table: iterate support and use it to fill the table with ones.

Table -> Bdd: Build a `BddPartialValuation` for each one-row in the table and then
use `BddVariableSet::mk_dnf` to build `Bdd`.

Bdd -> Expression: `Bdd.to_optimized_dnf()` to convert to `Vec<BddPartialValuation>`, then
convert each partial valuation to one AND-clause and the whole vector to a disjunction.

Expression -> Bdd: Run gather literals, create a BddVariableSet, then recursively follow
the expression structure. Literal/Constant correspond to BddVariableSet.mk_literal/mk_constant,
not/and/or operators correspond to Bdd.not/and/or.

*/

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Bdd<TLiteral>
where
TLiteral: Debug + Clone + Eq + Ord,
{
/// Always-sorted vector of no more than 65k variables (see `lib-bdd`).
inputs: Vec<TLiteral>,
/// Holds the `lib_bdd` representation.
bdd: InnerBdd,
}

impl<TLiteral: Debug + Clone + Eq + Ord> Bdd<TLiteral> {
pub(crate) fn new(inner: InnerBdd, inputs: Vec<TLiteral>) -> Self {
Self { bdd: inner, inputs }
}

/// Converts a literal representation as a generic user struct
/// into `BddVariable` used by `lib_bdd::Bdd`.
///
/// If such a variable isn't used in this `Bdd`, the method returns `None`.
fn map_var_outer_to_inner(&self, variable: &TLiteral) -> Option<BddVariable> {
self.inputs
.binary_search(variable)
.ok()
.map(BddVariable::from_index)
}

/// Converts a `BddVariable` used by `lib_bdd::Bdd` into a literal representation
/// used by `self`.
///
/// If such a variable isn't used in this `Bdd`, the method returns `None`.
pub(crate) fn map_var_inner_to_outer(&self, variable: BddVariable) -> Option<TLiteral> {
self.inputs.get(variable.to_index()).cloned()
}

/// Creates a `BddVariableSet` used by `lib_bdd::Bdd`.
///
/// Since `lib_bdd` only supports up to 2<sup>16</sup> variables, this method returns an `Err` if
/// the number of `variables` is above that.
fn make_inner_variable_set(
variables: BTreeSet<TLiteral>,
) -> Result<BddVariableSet, TryFromIntError> {
let num_vars = u16::try_from(variables.len())?;
Ok(BddVariableSet::new_anonymous(num_vars))

Check warning on line 71 in src/bdd/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/bdd/mod.rs#L71

Added line #L71 was not covered by tests
}

pub(crate) fn inner(&self) -> &InnerBdd {
&self.bdd
}

fn restrict_and_prune_map<TValue>(
&self,
valuation: &BTreeMap<TLiteral, TValue>,
new_bdd: &Bdd<TLiteral>,
) -> Bdd<TLiteral> {
self.restrict_and_prune_common(valuation, new_bdd, |set, var| set.contains_key(var))
}

fn restrict_and_prune_set(
&self,
valuation: &BTreeSet<TLiteral>,
new_bdd: &Bdd<TLiteral>,
) -> Bdd<TLiteral> {
self.restrict_and_prune_common(valuation, new_bdd, |set, var| set.contains(var))
}

fn restrict_and_prune_common<TCollection, P: Fn(&TCollection, &&TLiteral) -> bool>(
&self,
valuation: &TCollection,
new_bdd: &Bdd<TLiteral>,
contains: P,
) -> Bdd<TLiteral> {
let restricted_inputs = self
.inputs
.iter()
.filter(|var| !contains(valuation, var))
.cloned()
.collect::<Vec<_>>();
prune_bdd_variables(new_bdd, &restricted_inputs)
}

fn union_and_extend(
&self,
other: &Bdd<TLiteral>,
) -> (Bdd<TLiteral>, Bdd<TLiteral>, Vec<TLiteral>) {
let mut common_inputs = self.inputs.clone();
for other in &other.inputs {
if !common_inputs.contains(other) {
common_inputs.push(other.clone());
}
}
common_inputs.sort();

let self_lifted = extend_bdd_variables(self, &common_inputs);
let other_lifted = extend_bdd_variables(other, &common_inputs);

(self_lifted, other_lifted, common_inputs)
}

fn union_and_extend_n_ary(
&self,
others: &mut BTreeMap<TLiteral, Bdd<TLiteral>>,
) -> (Bdd<TLiteral>, Vec<TLiteral>) {
let mut common_inputs = self.inputs.clone();

for other in others.values() {
for input in &other.inputs {
if !common_inputs.contains(input) {
common_inputs.push(input.clone());
}
}
}

common_inputs.sort();

let self_lifted = extend_bdd_variables(self, &common_inputs);
for other in others.values_mut() {
*other = extend_bdd_variables(other, &common_inputs);
}

(self_lifted, common_inputs)
}
}
Loading