Skip to main content

problemreductions/models/misc/
minimum_code_generation_unlimited_registers.rs

1//! Minimum Code Generation with Unlimited Registers.
2//!
3//! Given a directed acyclic graph G = (V, A) with maximum out-degree 2
4//! (an expression DAG) and a partition of arcs into left (L) and right (R)
5//! operand sets, find a program of minimum number of instructions for an
6//! unlimited-register machine using 2-address instructions. The left operand's
7//! register is destroyed (overwritten by the result); a LOAD (copy) instruction
8//! is needed to preserve values before destruction. NP-complete
9//! [Aho, Johnson, and Ullman, 1977].
10
11use crate::registry::{FieldInfo, ProblemSchemaEntry};
12use crate::traits::Problem;
13use crate::types::Min;
14use serde::{Deserialize, Serialize};
15
16inventory::submit! {
17    ProblemSchemaEntry {
18        name: "MinimumCodeGenerationUnlimitedRegisters",
19        display_name: "Minimum Code Generation (Unlimited Registers)",
20        aliases: &[],
21        dimensions: &[],
22        module_path: module_path!(),
23        description: "Find minimum-length instruction sequence for an unlimited-register machine with 2-address instructions to evaluate an expression DAG",
24        fields: &[
25            FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices n = |V|" },
26            FieldInfo { name: "left_arcs", type_name: "Vec<(usize, usize)>", description: "Left operand arcs L: (parent, child) — child's register is destroyed" },
27            FieldInfo { name: "right_arcs", type_name: "Vec<(usize, usize)>", description: "Right operand arcs R: (parent, child) — child's register is preserved" },
28        ],
29    }
30}
31
32/// Minimum Code Generation with Unlimited Registers.
33///
34/// Given a directed acyclic graph G = (V, A) with maximum out-degree 2,
35/// where arcs are partitioned into left (L) and right (R) operand sets,
36/// leaves (out-degree 0) are input values each in its own register,
37/// internal vertices are 2-address operations (the left operand's register
38/// is overwritten by the result), and roots (in-degree 0) are values to
39/// compute, find a program of minimum instructions using OP and LOAD (copy).
40///
41/// # Representation
42///
43/// The configuration is a permutation of internal (non-leaf) vertices
44/// giving their evaluation order. `config[i]` is the evaluation position
45/// for internal vertex `i` (0-indexed among internal vertices).
46///
47/// # Example
48///
49/// ```
50/// use problemreductions::models::misc::MinimumCodeGenerationUnlimitedRegisters;
51/// use problemreductions::{Problem, Solver, BruteForce, Min};
52///
53/// // 5 vertices: leaves {3,4}, internal {0,1,2}
54/// // v1 = op(v3, v4), v2 = op(v3, v4), v0 = op(v1, v2)
55/// let problem = MinimumCodeGenerationUnlimitedRegisters::new(
56///     5,
57///     vec![(1,3),(2,3),(0,1)],  // left arcs (child destroyed)
58///     vec![(1,4),(2,4),(0,2)],  // right arcs (child preserved)
59/// );
60/// let result = BruteForce::new().solve(&problem);
61/// assert_eq!(result, Min(Some(4)));
62/// ```
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct MinimumCodeGenerationUnlimitedRegisters {
65    /// Number of vertices |V|.
66    num_vertices: usize,
67    /// Left operand arcs (parent, child) — child's register is destroyed.
68    left_arcs: Vec<(usize, usize)>,
69    /// Right operand arcs (parent, child) — child's register is preserved.
70    right_arcs: Vec<(usize, usize)>,
71}
72
73impl MinimumCodeGenerationUnlimitedRegisters {
74    /// Create a new instance.
75    ///
76    /// # Arguments
77    ///
78    /// * `num_vertices` - Total number of vertices
79    /// * `left_arcs` - Left operand arcs (parent, child); child register is destroyed by OP
80    /// * `right_arcs` - Right operand arcs (parent, child); child register is preserved
81    ///
82    /// # Panics
83    ///
84    /// Panics if any arc index is out of bounds, if any vertex has out-degree > 2,
85    /// if left and right arcs for binary vertices are inconsistent, or if a vertex
86    /// has a self-loop.
87    pub fn new(
88        num_vertices: usize,
89        left_arcs: Vec<(usize, usize)>,
90        right_arcs: Vec<(usize, usize)>,
91    ) -> Self {
92        let mut left_count = vec![0usize; num_vertices];
93        let mut right_count = vec![0usize; num_vertices];
94
95        for &(parent, child) in &left_arcs {
96            assert!(
97                parent < num_vertices && child < num_vertices,
98                "Left arc ({parent}, {child}) out of bounds for {num_vertices} vertices"
99            );
100            assert!(
101                parent != child,
102                "Self-loop ({parent}, {parent}) not allowed"
103            );
104            left_count[parent] += 1;
105        }
106        for &(parent, child) in &right_arcs {
107            assert!(
108                parent < num_vertices && child < num_vertices,
109                "Right arc ({parent}, {child}) out of bounds for {num_vertices} vertices"
110            );
111            assert!(
112                parent != child,
113                "Self-loop ({parent}, {parent}) not allowed"
114            );
115            right_count[parent] += 1;
116        }
117
118        for v in 0..num_vertices {
119            let out = left_count[v] + right_count[v];
120            assert!(out <= 2, "Vertex {v} has out-degree {out} > 2");
121            // Binary vertex: exactly one left and one right
122            if out == 2 {
123                assert!(
124                    left_count[v] == 1 && right_count[v] == 1,
125                    "Binary vertex {v} must have exactly 1 left and 1 right arc"
126                );
127            }
128            // Unary vertex: one left arc (result overwrites operand register)
129            if out == 1 {
130                assert!(
131                    left_count[v] == 1 && right_count[v] == 0,
132                    "Unary vertex {v} must have exactly 1 left arc and 0 right arcs"
133                );
134            }
135        }
136
137        Self {
138            num_vertices,
139            left_arcs,
140            right_arcs,
141        }
142    }
143
144    /// Get the number of vertices.
145    pub fn num_vertices(&self) -> usize {
146        self.num_vertices
147    }
148
149    /// Get the left operand arcs.
150    pub fn left_arcs(&self) -> &[(usize, usize)] {
151        &self.left_arcs
152    }
153
154    /// Get the right operand arcs.
155    pub fn right_arcs(&self) -> &[(usize, usize)] {
156        &self.right_arcs
157    }
158
159    /// Get the number of leaf vertices (out-degree 0).
160    pub fn num_leaves(&self) -> usize {
161        self.num_vertices - self.num_internal()
162    }
163
164    /// Get the number of internal (non-leaf) vertices.
165    pub fn num_internal(&self) -> usize {
166        let mut has_children = vec![false; self.num_vertices];
167        for &(parent, _) in &self.left_arcs {
168            has_children[parent] = true;
169        }
170        for &(parent, _) in &self.right_arcs {
171            has_children[parent] = true;
172        }
173        has_children.iter().filter(|&&b| b).count()
174    }
175
176    /// Determine which vertices are internal (non-leaf, i.e. out-degree > 0).
177    fn internal_vertices(&self) -> Vec<usize> {
178        let mut has_children = vec![false; self.num_vertices];
179        for &(parent, _) in &self.left_arcs {
180            has_children[parent] = true;
181        }
182        for &(parent, _) in &self.right_arcs {
183            has_children[parent] = true;
184        }
185        (0..self.num_vertices)
186            .filter(|&v| has_children[v])
187            .collect()
188    }
189
190    /// Get the left child of a vertex, if any.
191    fn left_child(&self, v: usize) -> Option<usize> {
192        self.left_arcs
193            .iter()
194            .find(|&&(parent, _)| parent == v)
195            .map(|&(_, child)| child)
196    }
197
198    /// Get the right child of a vertex, if any.
199    fn right_child(&self, v: usize) -> Option<usize> {
200        self.right_arcs
201            .iter()
202            .find(|&&(parent, _)| parent == v)
203            .map(|&(_, child)| child)
204    }
205
206    /// Simulate the unlimited-register machine for a given evaluation order
207    /// of internal vertices and return the instruction count, or `None` if
208    /// the ordering is invalid (not a permutation or violates dependencies).
209    ///
210    /// With unlimited registers:
211    /// - Each leaf starts in its own register
212    /// - OP v: computes v, result overwrites the left operand's register
213    /// - LOAD: copies a register value (needed when a left operand is still
214    ///   needed later and would be destroyed)
215    /// - Cost = num_OPs + num_LOADs
216    pub fn simulate(&self, config: &[usize]) -> Option<usize> {
217        let internal = self.internal_vertices();
218        let n_internal = internal.len();
219        if config.len() != n_internal {
220            return None;
221        }
222
223        // config[i] = evaluation position for internal vertex index i
224        // Build order: order[pos] = index into `internal`
225        let mut order = vec![0usize; n_internal];
226        let mut used = vec![false; n_internal];
227        for (i, &pos) in config.iter().enumerate() {
228            if pos >= n_internal {
229                return None;
230            }
231            if used[pos] {
232                return None;
233            }
234            used[pos] = true;
235            order[pos] = i;
236        }
237
238        // Track which vertices have been computed
239        let mut computed = vec![false; self.num_vertices];
240        // All leaves are "computed" (available in registers from the start)
241        let has_children: Vec<bool> = {
242            let mut hc = vec![false; self.num_vertices];
243            for &(parent, _) in &self.left_arcs {
244                hc[parent] = true;
245            }
246            for &(parent, _) in &self.right_arcs {
247                hc[parent] = true;
248            }
249            hc
250        };
251        for v in 0..self.num_vertices {
252            if !has_children[v] {
253                computed[v] = true;
254            }
255        }
256
257        // For each value, count how many future operations still need it
258        // as a LEFT operand. Only left operands get destroyed.
259        // But we also need to know total future uses (left + right) to know
260        // if a value is still needed at all.
261        let mut future_left_uses = vec![0usize; self.num_vertices];
262        let mut future_right_uses = vec![0usize; self.num_vertices];
263        for &idx in &order {
264            let v = internal[idx];
265            if let Some(lc) = self.left_child(v) {
266                future_left_uses[lc] += 1;
267            }
268            if let Some(rc) = self.right_child(v) {
269                future_right_uses[rc] += 1;
270            }
271        }
272
273        let mut instructions = 0usize;
274
275        // With unlimited registers, each value has its own register.
276        // When OP v executes: result goes into left_child's register.
277        // If left_child's value is still needed by a future operation,
278        // we must LOAD (copy) it first.
279
280        for step in 0..n_internal {
281            let v = internal[order[step]];
282            let lc = self.left_child(v);
283            let rc = self.right_child(v);
284
285            // Check dependencies
286            if let Some(l) = lc {
287                if !computed[l] {
288                    return None;
289                }
290            }
291            if let Some(r) = rc {
292                if !computed[r] {
293                    return None;
294                }
295            }
296
297            // Decrement future use counts
298            if let Some(l) = lc {
299                future_left_uses[l] -= 1;
300            }
301            if let Some(r) = rc {
302                future_right_uses[r] -= 1;
303            }
304
305            // Check if left operand needs to be copied before destruction
306            if let Some(l) = lc {
307                let still_needed = future_left_uses[l] + future_right_uses[l] > 0;
308                if still_needed {
309                    instructions += 1; // LOAD (copy)
310                }
311            }
312
313            // OP v
314            instructions += 1;
315
316            // Mark v as computed
317            computed[v] = true;
318        }
319
320        Some(instructions)
321    }
322}
323
324impl Problem for MinimumCodeGenerationUnlimitedRegisters {
325    const NAME: &'static str = "MinimumCodeGenerationUnlimitedRegisters";
326    type Value = Min<usize>;
327
328    fn variant() -> Vec<(&'static str, &'static str)> {
329        crate::variant_params![]
330    }
331
332    fn dims(&self) -> Vec<usize> {
333        let n_internal = self.num_internal();
334        vec![n_internal; n_internal]
335    }
336
337    fn evaluate(&self, config: &[usize]) -> Min<usize> {
338        Min(self.simulate(config))
339    }
340}
341
342crate::declare_variants! {
343    default MinimumCodeGenerationUnlimitedRegisters => "2 ^ num_vertices",
344}
345
346#[cfg(feature = "example-db")]
347pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
348    vec![crate::example_db::specs::ModelExampleSpec {
349        id: "minimum_code_generation_unlimited_registers",
350        // Issue #902 example: 5 vertices, leaves {3,4}, internal {0,1,2}
351        // left_arcs: (1,3),(2,3),(0,1)
352        // right_arcs: (1,4),(2,4),(0,2)
353        // Optimal order: v1,v2,v0 with 1 copy of v3 = 4 instructions
354        // Internal vertices sorted: [0, 1, 2]
355        // Order v1(pos 0), v2(pos 1), v0(pos 2)
356        // config[0]=2 (v0 at pos 2), config[1]=0 (v1 at pos 0), config[2]=1 (v2 at pos 1)
357        instance: Box::new(MinimumCodeGenerationUnlimitedRegisters::new(
358            5,
359            vec![(1, 3), (2, 3), (0, 1)],
360            vec![(1, 4), (2, 4), (0, 2)],
361        )),
362        optimal_config: vec![2, 0, 1],
363        optimal_value: serde_json::json!(4),
364    }]
365}
366
367#[cfg(test)]
368#[path = "../../unit_tests/models/misc/minimum_code_generation_unlimited_registers.rs"]
369mod tests;