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