| Crates.io | pldag-rust |
| lib.rs | pldag-rust |
| version | 2.0.0 |
| created_at | 2025-05-02 10:11:41.56423+00 |
| updated_at | 2025-06-26 09:15:26.439436+00 |
| description | A DAG-based framework where nodes encode arbitrary Boolean operations over literal leaves, with built-in support for assignment propagation and ILP export. |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1657534 |
| size | 89,538 |
A Primitive Logic Directed Acyclic Graph (PL-DAG) is a DAG in which every node encodes a logical operation and every leaf represents a literal. Interior nodes freely express arbitrary Boolean combinations of their predecessors—for example, an AND-node evaluates to true only if all of its incoming nodes (or leaves) evaluate to true. This "freedom" to assemble logical relationships with minimal ceremony makes the PL-DAG both powerful and easy to work with.
cargo add pldag-rust
The PL-DAG provides these core APIs for working with combinations and evaluations:
propagatefn propagate(
&self,
assignment: &Assignment, // IndexMap<String, Bound>
) -> Assignment;
Bound values, propagates bounds up the graph bottom-up, and returns the resulting bounds for each node. A Bound is a tuple (i64, i64) representing min and max values.propagate_coefsfn propagate_coefs(
&self,
assignment: &Assignment,
) -> ValuedAssignment; // IndexMap<String, MultiBound>
ValuedAssignment where each node maps to a MultiBound = (Bound, VBound). The coefficient for a parent node is the sum of its children's coefficients plus its own coefficient.propagate_default and propagate_coefs_defaultfn propagate_default(&self) -> Assignment;
fn propagate_coefs_default(&self) -> ValuedAssignment;
to_sparse_polyhedronfn to_sparse_polyhedron(&self, double_binding: bool, integer_constraints: bool, fixed_constraints: bool) -> SparsePolyhedron;
fn set_coef(&mut self, id: ID, coefficient: f64);
fn get_coef(&self, id: &ID) -> f64;
fn get_objective(&self) -> IndexMap<String, f64>;
fn set_primitive(&mut self, id: ID, bound: Bound);
get_objective() method retrieves objective function coefficients for ILP optimization.use indexmap::IndexMap;
use pldag::{Pldag, Bound};
// Build your PL-DAG
// For example, we create a model of three boolean variables x, y and z.
// We bind them to an OR constraint.
let mut pldag: Pldag = Pldag::new();
// First setup the primitive variables
pldag.set_primitive("x".to_string(), (0, 1));
pldag.set_primitive("y".to_string(), (0, 1));
pldag.set_primitive("z".to_string(), (0, 1));
// A reference ID is returned
let root = pldag.set_or(vec![
"x".to_string(),
"y".to_string(),
"z".to_string(),
]);
// 1. Validate a combination:
let mut inputs: IndexMap<String, Bound> = IndexMap::new();
let validated = pldag.propagate(&inputs);
// Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
// the root will be (0,1) since there's not enough information to evaluate the root `or` node.
println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
// If we however fix x to be zero, then we can check the result
inputs.insert("x".to_string(), (0,0));
let revalidated = pldag.propagate(&inputs);
println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
// However, fixing y and z to 1 will yield the root node to be true (since the root will be true if any of x, y or z is true).
inputs.insert("y".to_string(), (1,1));
inputs.insert("z".to_string(), (1,1));
let revalidated = pldag.propagate(&inputs);
println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
// 2. Score a configuration:
// We can score a configuration by setting coefficients on nodes.
pldag.set_coef("x".to_string(), 1.0);
pldag.set_coef("y".to_string(), 2.0);
pldag.set_coef("z".to_string(), 3.0);
// Add a discount value if the root is true
pldag.set_coef(root.clone(), -1.0);
// Use propagate_coefs to get both bounds and accumulated coefficients
let scores = pldag.propagate_coefs(&inputs);
// The result contains (bounds, coefficients) for each node
let root_result = scores.get(&root).unwrap();
println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
// And notice what will happen if we remove the x value (i.e. x being (0,1))
inputs.insert("x".to_string(), (0,1));
let scores = pldag.propagate_coefs(&inputs);
// The coefficients will reflect the range of possible values
let root_result = scores.get(&root).unwrap();
println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
// .. and if we set x to be 0, then the score will be more constrained.
inputs.insert("x".to_string(), (0,0));
let scores = pldag.propagate_coefs(&inputs);
let root_result = scores.get(&root).unwrap();
println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
// .. and if we set y and z to be 0, then the root will be 0.
inputs.insert("y".to_string(), (0,0));
inputs.insert("z".to_string(), (0,0));
let scores = pldag.propagate_coefs(&inputs);
let root_result = scores.get(&root).unwrap();
println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
Enjoy building and evaluating logical models with the PL-DAG!