1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
//! # `Bdd` clauses and valuations
//!
//! In many cases, we need to inspect the "contents" of a `Bdd`. That is, the valuations or clauses
//! that are stored in the graph.
//!
//! ## Iterators
//!
//! Most straightforward way is to list all valuations directly. Of course, this operation
//! can be very time consuming for a large `Bdd`. But if `Bdd.cardinality()` is sufficiently small,
//! it is possible to enumerate all valuations of a `Bdd`:
//!
//! ```rust
//! use biodivine_lib_bdd::{Bdd, ValuationsOfClauseIterator, BddVariableSet};
//! use std::collections::HashSet;
//! let variables = BddVariableSet::new(&["v1", "v2", "v3", "v4"]);
//! let bdd = variables.eval_expression_string("(v4 => (v1 & v2)) & (!v4 => (!v1 & v3))");
//!
//! bdd.sat_valuations().for_each(|valuation| {
//! // Assuming the valuation has the right number of variables, every `Bdd` evaluates
//! // to true/false in a valuation.
//! assert!(bdd.eval_in(&valuation));
//! });
//!
//! let sat = bdd.sat_valuations().collect::<HashSet<_>>();
//!
//! // You can also create an iterator over *every* valuation. This is effectively the same as
//! // calling variables.mk_true().sat_valuations().
//! let all_valuations = ValuationsOfClauseIterator::new_unconstrained(bdd.num_vars());
//! all_valuations.for_each(|valuation| {
//! assert_eq!(bdd.eval_in(&valuation), sat.contains(&valuation));
//! });
//!
//! // Of course, you can convert a valuation back to a `Bdd`:
//! let first = Bdd::from(bdd.sat_valuations().next().unwrap());
//! assert!(!first.and(&bdd).is_false());
//! assert!(first.is_valuation()); // Tests that a `Bdd` represents exactly one valuation.
//! ```
//!
//! If the number of valuations is too big, you can often still examine all *clauses* of a `Bdd`
//! (i.e. paths leading to `1`):
//!
//! ```rust
//! use biodivine_lib_bdd::{Bdd, ValuationsOfClauseIterator, BddVariableSet};
//! use std::collections::HashSet;
//! let variables = BddVariableSet::new(&["v1", "v2", "v3", "v4"]);
//! let bdd = variables.eval_expression_string("(v4 => (v1 & v2)) & (!v4 => (!v1 & v3))");
//!
//! let mut total = 0;
//! bdd.sat_clauses().for_each(|clause| {
//! // To convert a path back into a `Bdd`, we need to interpret it as a conjunctive clause.
//! let clause_bdd = variables.mk_conjunctive_clause(&clause);
//! assert!(!clause_bdd.and(&bdd).is_false());
//! // And we can also test whether the `Bdd` is a single path.
//! assert!(clause_bdd.is_clause());
//!
//! // Keep in mind that you can still iterate over valuations that match a specific path:
//! for valuation in ValuationsOfClauseIterator::new(clause, bdd.num_vars()) {
//! total += 1;
//! }
//! });
//!
//! assert_eq!(total, bdd.sat_valuations().count());
//! ```
//!
//! ## Path/valuation selectors
//!
//! If the `Bdd` is very big, even iterating paths can be a problem. However, you can still use
//! some of the following methods to obtain some insight into the structure of the `Bdd`:
//!
//! - `Bdd.first_valuation` and `Bdd.last_valuation` will give you the (lexicographically) first
//! and last valuation.
//! - Similarly, `Bdd.first_clause` and `Bdd.last_clause` will give you the first and last path
//! satisfying path.
//! - `Bdd.most_positive_valuation` and `Bdd.most_negative_valuation` return the valuations
//! with highest amount of positive/negative literals.
//! - `Bdd.most_fixed_clause` and `Bdd.most_free_clause` wil give you satisfying paths with
//! greatest and least amount of fixed variables respectively.
//! - `Bdd.random_valuation` and `Bdd.random_clause` allow selecting valaution/clause using
//! a random number generator.
//!
//!