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