pub struct Pldag {
pub nodes: IndexMap<ID, Node>,
}Expand description
A Primitive Logic Directed Acyclic Graph (PL-DAG).
The PL-DAG represents a logical system where:
- Primitive nodes are leaf variables with bounds
- Composite nodes represent logical constraints over other nodes
- Each node has an associated coefficient for accumulation operations
The DAG structure ensures no cycles and enables efficient bottom-up propagation.
Fields§
§nodes: IndexMap<ID, Node>Map from node IDs to their corresponding nodes
Implementations§
Source§impl Pldag
impl Pldag
Sourcepub fn new() -> Pldag
pub fn new() -> Pldag
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}pub fn get_hash(&self) -> u64
Sourcepub fn transitive_dependencies(&self) -> HashMap<ID, HashSet<ID>>
pub fn transitive_dependencies(&self) -> HashMap<ID, HashSet<ID>>
Computes the transitive dependency closure for all nodes.
For each node in the DAG, this method calculates all nodes that can be reached by following dependency edges (the set of all descendants).
§Returns
A map from each node ID to the set of all node IDs it transitively depends on
Sourcepub fn presolve(
&self,
seed: &IndexMap<&str, Bound>,
) -> Result<Presolved, String>
pub fn presolve( &self, seed: &IndexMap<&str, Bound>, ) -> Result<Presolved, String>
Runs the presolve / bound-propagation phase on this Pldag
without mutating it and returns a new graph in which:
- every primitive variable carries the tightest interval that can be
proven from the current model and the optional
seedassignments; - every composite node (linear row) whose truth-value collapses to
always true
(1, 1)or always false(0, 0)is rewritten as a primitive with that fixed bound; - all other composites are copied unchanged, but may reference the tightened bounds of their primitives.
The original DAG is left untouched so you can keep it for explanations or alternative presolve strategies.
§Parameters
seed– an optional mapid → (min, max)that fixes or tightens the bounds of some primitives before propagation starts.
For Boolean variables use(0,0)to force false and(1,1)to force true.
§Returns
-
Ok(Presolved)
Presolved.tightenedis the new, tightenedPldag.
Presolved.fixedcollects every node whose bound collapsed to a single value(v,v)during presolve.
Usefixed⊕solve(tightened)to reconstruct a full solution. -
Err(String)
The string is the ID of the first composite row found infeasible (its upper bound fell below 0, i.e. the row is violated under all possible assignments).
§Algorithm (short version)
- Build a reverse adjacency (child → parents) once.
- Seed a FIFO queue with every node whose bounds are already fixed.
- While the queue is not empty
* re-evaluate each parent’s bound;
* detect(0,0)⇒ unsat;
* if a parent’s bound tightens to(v,v)push it.
This is classic arc consistency for ≥-constraints and runs in O(#edges) time. - Re-build a fresh
Pldagfrom the final bounds table.
§Panics
This function never panics on well-formed DAGs (acyclic and with
every coefficient variable present in self.nodes). Malformed inputs
may trigger unwrap() panics inside the implementation.
Sourcepub fn primitive_combinations(&self) -> impl Iterator<Item = HashMap<ID, i64>>
pub fn primitive_combinations(&self) -> impl Iterator<Item = HashMap<ID, i64>>
Generates all possible combinations of primitive variable assignments.
Enumerates the Cartesian product of all primitive variable bounds, yielding every possible complete assignment of values to primitive variables.
§Returns
An iterator over all possible variable assignments as HashMaps
Sourcepub fn propagate(&self, assignment: &IndexMap<&str, Bound>) -> Assignment
pub fn propagate(&self, assignment: &IndexMap<&str, Bound>) -> Assignment
Propagates bounds through the DAG bottom-up.
Starting from the given variable assignments, this method computes bounds for all composite nodes by propagating constraints upward through the DAG.
§Arguments
assignment- Initial assignment of bounds to variables
§Returns
Complete assignment including bounds for all reachable nodes
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn propagate_default(&self) -> Assignment
pub fn propagate_default(&self) -> Assignment
Propagates bounds using default primitive variable bounds.
Convenience method that calls propagate with the default bounds
of all primitive variables as defined in the DAG.
§Returns
Complete assignment with bounds for all nodes
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn propagate_many_coefs(
&self,
assignments: Vec<&IndexMap<&str, Bound>>,
) -> Vec<ValuedAssignment> ⓘ
pub fn propagate_many_coefs( &self, assignments: Vec<&IndexMap<&str, Bound>>, ) -> Vec<ValuedAssignment> ⓘ
Propagates bounds and accumulates coefficients for multiple assignments.
For each input assignment, this method:
- Propagates bounds through the DAG
- Accumulates coefficients where each parent’s coefficient is the sum of its children’s coefficients plus its own coefficient
§Arguments
assignments- Vector of assignments to process
§Returns
Vector of valued assignments, each containing bounds and accumulated coefficients
Sourcepub fn propagate_coefs(
&self,
assignment: &IndexMap<&str, Bound>,
) -> ValuedAssignment
pub fn propagate_coefs( &self, assignment: &IndexMap<&str, Bound>, ) -> ValuedAssignment
Propagates bounds and accumulates coefficients for a single assignment.
Convenience method that calls propagate_many_coefs with a single assignment.
§Arguments
assignment- The assignment to propagate
§Returns
A valued assignment containing bounds and accumulated coefficients for all nodes
§Panics
Panics if no assignments are returned (should not happen under normal circumstances)
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn propagate_coefs_default(&self) -> ValuedAssignment
pub fn propagate_coefs_default(&self) -> ValuedAssignment
Propagates bounds and coefficients using default primitive bounds.
Convenience method that calls propagate_coefs with the default bounds
of all primitive variables.
§Returns
A valued assignment with bounds and coefficients for all nodes
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn get_objective(&self) -> IndexMap<String, f64>
pub fn get_objective(&self) -> IndexMap<String, f64>
Retrieves the objective function coefficients from all primitive nodes.
Collects the coefficients of all primitive (leaf) nodes in the DAG, which represent the objective function values for ILP optimization. Composite nodes are excluded from the result.
§Returns
A map from primitive variable names to their objective coefficients
Sourcepub fn to_sparse_polyhedron(
&self,
double_binding: bool,
integer_constraints: bool,
fixed_constraints: bool,
) -> SparsePolyhedron
pub fn to_sparse_polyhedron( &self, double_binding: bool, integer_constraints: bool, fixed_constraints: bool, ) -> SparsePolyhedron
Converts the PL-DAG to a sparse polyhedron for ILP solving.
Transforms the logical constraints in the DAG into a system of linear inequalities suitable for integer linear programming solvers.
§Arguments
double_binding- If true, creates bidirectional implications for composite nodesinteger_constraints- If true, adds bounds constraints for integer variablesfixed_constraints- If true, adds equality constraints for fixed primitive variables
§Returns
A SparsePolyhedron representing the DAG constraints
Sourcepub fn to_sparse_polyhedron_default(&self) -> SparsePolyhedron
pub fn to_sparse_polyhedron_default(&self) -> SparsePolyhedron
Converts the PL-DAG to a sparse polyhedron with default settings.
Convenience method that calls to_sparse_polyhedron with all options enabled:
double_binding=true, integer_constraints=true, fixed_constraints=true.
§Returns
A SparsePolyhedron with full constraint encoding
Sourcepub fn to_dense_polyhedron(
&self,
double_binding: bool,
integer_constraints: bool,
fixed_constraints: bool,
) -> DensePolyhedron
pub fn to_dense_polyhedron( &self, double_binding: bool, integer_constraints: bool, fixed_constraints: bool, ) -> DensePolyhedron
Sourcepub fn to_dense_polyhedron_default(&self) -> DensePolyhedron
pub fn to_dense_polyhedron_default(&self) -> DensePolyhedron
Converts the PL-DAG to a dense polyhedron with default settings.
§Returns
A DensePolyhedron with all constraint options enabled
Sourcepub fn set_coef(&mut self, id: &str, coefficient: f64) -> bool
pub fn set_coef(&mut self, id: &str, coefficient: f64) -> bool
Sets the coefficient for a specific node.
Updates the coefficient value associated with the given node ID. If the node doesn’t exist, this method has no effect.
§Arguments
id- The node ID to updatecoefficient- The new coefficient value
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn set_primitive(&mut self, id: &str, bound: Bound)
pub fn set_primitive(&mut self, id: &str, bound: Bound)
Creates a primitive (leaf) variable with the specified bounds.
Primitive variables represent the base variables in the DAG and have no dependencies on other nodes.
§Arguments
id- Unique identifier for the variablebound- The allowed range (min, max) for this variable
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn set_primitives(&mut self, ids: Vec<&str>, bound: Bound)
pub fn set_primitives(&mut self, ids: Vec<&str>, bound: Bound)
Creates multiple primitive variables with the same bounds.
Convenience method to create several primitive variables at once. Duplicate IDs are automatically filtered out.
§Arguments
ids- Vector of unique identifiers for the variablesbound- The common bound to apply to all variables
Sourcepub fn set_gelineq(
&mut self,
coefficient_variables: Vec<(&str, i64)>,
bias: i64,
) -> Option<ID>
pub fn set_gelineq( &mut self, coefficient_variables: Vec<(&str, i64)>, bias: i64, ) -> Option<ID>
Creates a general linear inequality constraint.
Creates a constraint of the form: sum(coeff_i * var_i) + bias >= 0. The constraint is automatically assigned a unique ID based on its content.
§Arguments
coefficient_variables- Vector of (variable_id, coefficient) pairsbias- Constant bias term
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_equal(&mut self, references: Vec<&str>, value: i64) -> Option<ID>
pub fn set_equal(&mut self, references: Vec<&str>, value: i64) -> Option<ID>
Creates an equality constraint: sum(variables) == value.
Implemented as the conjunction of “at least” and “at most” constraints.
§Arguments
references- Vector of variable IDs to sumvalue- Required exact sum
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_and<T>(&mut self, references: Vec<T>) -> Option<ID>
pub fn set_and<T>(&mut self, references: Vec<T>) -> Option<ID>
Creates a logical AND constraint.
Returns true if and only if ALL referenced variables are true. Implemented as: sum(variables) >= count(variables).
§Arguments
references- Vector of variable IDs to AND together
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_or<T>(&mut self, references: Vec<T>) -> Option<ID>
pub fn set_or<T>(&mut self, references: Vec<T>) -> Option<ID>
Creates a logical OR constraint.
Returns true if AT LEAST ONE of the referenced variables is true. Implemented as: sum(variables) >= 1.
§Arguments
references- Vector of variable IDs to OR together
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Examples found in repository?
4fn main() {
5 // Build your PL-DAG
6 // For example, we create a model of three boolean variables x, y and z.
7 // We bind them to an OR constraint.
8 let mut pldag: Pldag = Pldag::new();
9
10 // First setup the primitive variables
11 pldag.set_primitive("x", (0, 1));
12 pldag.set_primitive("y", (0, 1));
13 pldag.set_primitive("z", (0, 1));
14
15 // A reference ID is returned
16 let root = pldag.set_or(vec![
17 "x",
18 "y",
19 "z",
20 ]).unwrap();
21
22 // 1. Validate a combination:
23 let mut inputs: IndexMap<&str, Bound> = IndexMap::new();
24 let validated = pldag.propagate(&inputs);
25 // Since nothing is given, and all other variables implicitly have bounds (0, 1) from the pldag model,
26 // the root will be (0,1) since there's not enough information to evaluate the root `or` node.
27 println!("Root valid? {}", *validated.get(&root).unwrap() == (1, 1)); // This will be false
28
29 // If we however fix x to be zero, then we can check the result
30 inputs.insert("x", (0,0));
31 let revalidated = pldag.propagate(&inputs);
32 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be false
33
34 // 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).
35 inputs.insert("y", (1,1));
36 inputs.insert("z", (1,1));
37 let revalidated = pldag.propagate(&inputs);
38 println!("Root valid? {}", *revalidated.get(&root).unwrap() == (1, 1)); // This will be true
39
40 // 2. Score a configuration:
41 // We can score a configuration by setting coefficients on nodes.
42 pldag.set_coef("x", 1.0);
43 pldag.set_coef("y", 2.0);
44 pldag.set_coef("z", 3.0);
45 // Add a discount value if the root is true
46 pldag.set_coef(&root, -1.0);
47
48 // Use propagate_coefs to get both bounds and accumulated coefficients
49 let scores = pldag.propagate_coefs(&inputs);
50 // The result contains (bounds, coefficients) for each node
51 let root_result = scores.get(&root).unwrap();
52 println!("Root bounds: {:?}, Total score: {:?}", root_result.0, root_result.1);
53
54 // And notice what will happen if we remove the x value (i.e. x being (0,1))
55 inputs.insert("x", (0,1));
56 let scores = pldag.propagate_coefs(&inputs);
57 // The coefficients will reflect the range of possible values
58 let root_result = scores.get(&root).unwrap();
59 println!("Root bounds: {:?}, Score range: {:?}", root_result.0, root_result.1);
60
61 // .. and if we set x to be 0, then the score will be more constrained.
62 inputs.insert("x", (0,0));
63 let scores = pldag.propagate_coefs(&inputs);
64 let root_result = scores.get(&root).unwrap();
65 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
66
67 // .. and if we set y and z to be 0, then the root will be 0.
68 inputs.insert("y", (0,0));
69 inputs.insert("z", (0,0));
70 let scores = pldag.propagate_coefs(&inputs);
71 let root_result = scores.get(&root).unwrap();
72 println!("Root bounds: {:?}, Score: {:?}", root_result.0, root_result.1);
73
74 // Build a simple OR‑of‑three model
75 let mut pldag = Pldag::new();
76 pldag.set_primitive("x", (0, 1));
77 pldag.set_primitive("y", (0, 1));
78 pldag.set_primitive("z", (0, 1));
79 let root = pldag.set_or(vec!["x", "y", "z"]).unwrap();
80
81 // 1. Validate a combination
82 let validated = pldag.propagate_default();
83 println!("root bound = {:?}", validated[&root]);
84
85 // 2. Optimise with coefficients
86 pldag.set_coef("x", 1.0);
87 pldag.set_coef("y", 2.0);
88 pldag.set_coef("z", 3.0);
89 pldag.set_coef(&root, -1.0);
90 let scored = pldag.propagate_coefs_default();
91 println!("root value = {:?}", scored[&root].1);
92}Sourcepub fn set_nand<T>(&mut self, references: Vec<T>) -> Option<ID>
pub fn set_nand<T>(&mut self, references: Vec<T>) -> Option<ID>
Creates a logical NAND constraint.
Returns true if NOT ALL of the referenced variables are true. Implemented as: sum(variables) <= count(variables) - 1.
§Arguments
references- Vector of variable IDs to NAND together
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_not<T>(&mut self, references: Vec<T>) -> Option<ID>
pub fn set_not<T>(&mut self, references: Vec<T>) -> Option<ID>
Creates a logical NOT constraint.
Returns true if NONE of the referenced variables are true. Functionally equivalent to NOR. Implemented as: sum(variables) <= 0.
§Arguments
references- Vector of variable IDs to negate
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_xor<T>(&mut self, references: Vec<T>) -> Option<ID>
pub fn set_xor<T>(&mut self, references: Vec<T>) -> Option<ID>
Creates a logical XOR constraint.
Returns true if EXACTLY ONE of the referenced variables is true. Implemented as the conjunction of OR and “at most 1” constraints.
§Arguments
references- Vector of variable IDs to XOR together
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_xnor<T>(&mut self, references: Vec<T>) -> Option<ID>
pub fn set_xnor<T>(&mut self, references: Vec<T>) -> Option<ID>
Creates a logical XNOR constraint.
Returns true if an EVEN NUMBER of the referenced variables are true (including zero). Implemented as: (sum >= 2) OR (sum <= 0).
§Arguments
references- Vector of variable IDs to XNOR together
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_imply<T, U>(&mut self, condition: T, consequence: U) -> Option<ID>
pub fn set_imply<T, U>(&mut self, condition: T, consequence: U) -> Option<ID>
Creates a logical IMPLICATION constraint: condition -> consequence.
Returns true if the condition is false OR the consequence is true. Implemented as: NOT(condition) OR consequence.
§Arguments
condition- The condition variable IDconsequence- The consequence variable ID
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Sourcepub fn set_equiv<T, U>(&mut self, lhs: T, rhs: U) -> Option<ID>
pub fn set_equiv<T, U>(&mut self, lhs: T, rhs: U) -> Option<ID>
Creates a logical EQUIVALENCE constraint: lhs <-> rhs.
Returns true if both variables have the same truth value. Implemented as: (lhs -> rhs) AND (rhs -> lhs).
§Arguments
lhs- The left-hand side variable IDrhs- The right-hand side variable ID
§Returns
The unique ID assigned to this constraint OR None if it failed to create the constraint
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Pldag
impl RefUnwindSafe for Pldag
impl Send for Pldag
impl Sync for Pldag
impl Unpin for Pldag
impl UnwindSafe for Pldag
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more