Skip to main content

problemreductions/models/misc/
minimum_code_generation_one_register.rs

1//! Minimum Code Generation on a One-Register Machine.
2//!
3//! Given a directed acyclic graph G = (V, A) with maximum out-degree 2
4//! (an expression DAG), find a program of minimum number of instructions
5//! for a one-register machine (LOAD, STORE, OP) that computes all root
6//! vertices. NP-complete [Bruno and Sethi, 1976].
7
8use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use crate::types::Min;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14    ProblemSchemaEntry {
15        name: "MinimumCodeGenerationOneRegister",
16        display_name: "Minimum Code Generation (One Register)",
17        aliases: &[],
18        dimensions: &[],
19        module_path: module_path!(),
20        description: "Find minimum-length instruction sequence for a one-register machine to evaluate an expression DAG",
21        fields: &[
22            FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices n = |V|" },
23            FieldInfo { name: "edges", type_name: "Vec<(usize, usize)>", description: "Directed arcs (parent, child) in the expression DAG" },
24            FieldInfo { name: "num_leaves", type_name: "usize", description: "Number of leaf vertices (out-degree 0)" },
25        ],
26    }
27}
28
29/// Minimum Code Generation on a One-Register Machine.
30///
31/// Given a directed acyclic graph G = (V, A) with maximum out-degree 2,
32/// where leaves (out-degree 0) are input values in memory, internal vertices
33/// are operations, and roots (in-degree 0) are values to compute, find a
34/// program of minimum instructions using LOAD, STORE, and OP.
35///
36/// # Representation
37///
38/// The configuration is a permutation of internal (non-leaf) vertices
39/// giving their evaluation order. `config[i]` is the evaluation position
40/// for internal vertex `i` (0-indexed among internal vertices).
41///
42/// # Example
43///
44/// ```
45/// use problemreductions::models::misc::MinimumCodeGenerationOneRegister;
46/// use problemreductions::{Problem, Solver, BruteForce, Min};
47///
48/// // 7 vertices: leaves {4,5,6}, internal {0,1,2,3}
49/// // v3 = op(v5, v6), v1 = op(v3, v4), v2 = op(v3, v5), v0 = op(v1, v2)
50/// let problem = MinimumCodeGenerationOneRegister::new(
51///     7,
52///     vec![(0,1),(0,2),(1,3),(1,4),(2,3),(2,5),(3,5),(3,6)],
53///     3,
54/// );
55/// let result = BruteForce::new().solve(&problem);
56/// assert_eq!(result, Min(Some(8)));
57/// ```
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct MinimumCodeGenerationOneRegister {
60    /// Number of vertices |V|.
61    num_vertices: usize,
62    /// Directed arcs (parent, child) in the expression DAG.
63    edges: Vec<(usize, usize)>,
64    /// Number of leaf vertices (out-degree 0).
65    num_leaves: usize,
66}
67
68impl MinimumCodeGenerationOneRegister {
69    /// Create a new instance.
70    ///
71    /// # Arguments
72    ///
73    /// * `num_vertices` - Total number of vertices
74    /// * `edges` - Directed arcs (parent, child); parent depends on child
75    /// * `num_leaves` - Number of leaf vertices (out-degree 0)
76    ///
77    /// # Panics
78    ///
79    /// Panics if any edge index is out of bounds, if any vertex has
80    /// out-degree > 2, or if `num_leaves > num_vertices`.
81    pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>, num_leaves: usize) -> Self {
82        assert!(
83            num_leaves <= num_vertices,
84            "num_leaves ({num_leaves}) exceeds num_vertices ({num_vertices})"
85        );
86        let mut out_degree = vec![0usize; num_vertices];
87        for &(parent, child) in &edges {
88            assert!(
89                parent < num_vertices && child < num_vertices,
90                "Edge ({parent}, {child}) out of bounds for {num_vertices} vertices"
91            );
92            assert!(
93                parent != child,
94                "Self-loop ({parent}, {parent}) not allowed"
95            );
96            out_degree[parent] += 1;
97        }
98        for (v, &deg) in out_degree.iter().enumerate() {
99            assert!(deg <= 2, "Vertex {v} has out-degree {deg} > 2");
100        }
101        // Verify leaf count: leaves are vertices with out-degree 0
102        let actual_leaves = out_degree.iter().filter(|&&d| d == 0).count();
103        assert_eq!(
104            actual_leaves, num_leaves,
105            "Declared num_leaves ({num_leaves}) != actual leaf count ({actual_leaves})"
106        );
107        Self {
108            num_vertices,
109            edges,
110            num_leaves,
111        }
112    }
113
114    /// Get the number of vertices.
115    pub fn num_vertices(&self) -> usize {
116        self.num_vertices
117    }
118
119    /// Get the number of edges.
120    pub fn num_edges(&self) -> usize {
121        self.edges.len()
122    }
123
124    /// Get the number of leaf vertices.
125    pub fn num_leaves(&self) -> usize {
126        self.num_leaves
127    }
128
129    /// Get the number of internal (non-leaf) vertices.
130    pub fn num_internal(&self) -> usize {
131        self.num_vertices - self.num_leaves
132    }
133
134    /// Get the edges.
135    pub fn edges(&self) -> &[(usize, usize)] {
136        &self.edges
137    }
138
139    /// Compute the children (operands) of each vertex from the edge list.
140    fn children(&self) -> Vec<Vec<usize>> {
141        let mut ch = vec![vec![]; self.num_vertices];
142        for &(parent, child) in &self.edges {
143            ch[parent].push(child);
144        }
145        ch
146    }
147
148    /// Determine which vertices are internal (non-leaf, i.e. out-degree > 0).
149    fn internal_vertices(&self) -> Vec<usize> {
150        let children = self.children();
151        (0..self.num_vertices)
152            .filter(|&v| !children[v].is_empty())
153            .collect()
154    }
155
156    /// Determine which vertices are leaves (out-degree 0).
157    fn leaf_set(&self) -> Vec<bool> {
158        let children = self.children();
159        (0..self.num_vertices)
160            .map(|v| children[v].is_empty())
161            .collect()
162    }
163
164    /// Simulate the one-register machine for a given evaluation order of
165    /// internal vertices and return the instruction count, or `None` if the
166    /// ordering is invalid (not a permutation or violates dependencies).
167    pub fn simulate(&self, config: &[usize]) -> Option<usize> {
168        let internal = self.internal_vertices();
169        let n_internal = internal.len();
170        if config.len() != n_internal {
171            return None;
172        }
173
174        // config[i] = evaluation position for internal vertex index i
175        // (i indexes into the `internal` array)
176        // Build order: order[pos] = index into `internal`
177        let mut order = vec![0usize; n_internal];
178        let mut used = vec![false; n_internal];
179        for (i, &pos) in config.iter().enumerate() {
180            if pos >= n_internal {
181                return None;
182            }
183            if used[pos] {
184                return None;
185            }
186            used[pos] = true;
187            order[pos] = i;
188        }
189
190        let children = self.children();
191        let is_leaf = self.leaf_set();
192
193        // Track which internal vertices have been computed
194        let mut computed = vec![false; self.num_vertices];
195        // All leaves are "computed" (available in memory)
196        for v in 0..self.num_vertices {
197            if is_leaf[v] {
198                computed[v] = true;
199            }
200        }
201
202        // Build: for each vertex, which future internal vertices need it?
203        // We'll track this dynamically.
204        let mut future_uses = vec![0usize; self.num_vertices];
205        for &idx in &order {
206            let v = internal[idx];
207            for &c in &children[v] {
208                future_uses[c] += 1;
209            }
210        }
211
212        let mut register: Option<usize> = None; // which vertex value is in register
213        let mut in_memory = vec![false; self.num_vertices];
214        // Leaves start in memory
215        for v in 0..self.num_vertices {
216            if is_leaf[v] {
217                in_memory[v] = true;
218            }
219        }
220
221        let mut instructions = 0usize;
222
223        for step in 0..n_internal {
224            let v = internal[order[step]];
225
226            // Check dependencies: all children must be available
227            for &c in &children[v] {
228                let available = in_memory[c] || register == Some(c);
229                if !available {
230                    return None; // child was computed but lost (not stored, overwritten)
231                }
232            }
233
234            // Decrement future uses for children of v
235            for &c in &children[v] {
236                future_uses[c] -= 1;
237            }
238
239            let operands = &children[v];
240
241            // Before computing v, check if we need to STORE the current register value
242            // We need to store if:
243            // 1. Register holds a value
244            // 2. That value is still needed in the future
245            // 3. That value is not already in memory
246            if let Some(r) = register {
247                if !in_memory[r] && future_uses[r] > 0 {
248                    instructions += 1; // STORE
249                    in_memory[r] = true;
250                }
251            }
252
253            // Now compute v
254            if operands.len() == 2 {
255                let c0 = operands[0];
256                let c1 = operands[1];
257                let one_in_register = (register == Some(c0) && in_memory[c1])
258                    || (register == Some(c1) && in_memory[c0]);
259                if one_in_register {
260                    instructions += 1; // OP v (one operand in register, other in memory)
261                } else {
262                    // Need to LOAD one operand, OP with the other from memory
263                    instructions += 1; // LOAD
264                    instructions += 1; // OP
265                }
266            } else if operands.len() == 1 {
267                let c0 = operands[0];
268                if register == Some(c0) {
269                    instructions += 1; // OP v (unary)
270                } else {
271                    instructions += 1; // LOAD
272                    instructions += 1; // OP
273                }
274            }
275
276            register = Some(v);
277        }
278
279        Some(instructions)
280    }
281}
282
283impl Problem for MinimumCodeGenerationOneRegister {
284    const NAME: &'static str = "MinimumCodeGenerationOneRegister";
285    type Value = Min<usize>;
286
287    fn variant() -> Vec<(&'static str, &'static str)> {
288        crate::variant_params![]
289    }
290
291    fn dims(&self) -> Vec<usize> {
292        let n_internal = self.num_internal();
293        vec![n_internal; n_internal]
294    }
295
296    fn evaluate(&self, config: &[usize]) -> Min<usize> {
297        Min(self.simulate(config))
298    }
299}
300
301crate::declare_variants! {
302    default MinimumCodeGenerationOneRegister => "2 ^ num_vertices",
303}
304
305#[cfg(feature = "example-db")]
306pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
307    vec![crate::example_db::specs::ModelExampleSpec {
308        id: "minimum_code_generation_one_register",
309        // Issue #900 example: 7 vertices, leaves {4,5,6}, internal {0,1,2,3}
310        // Edges: (0,1),(0,2),(1,3),(1,4),(2,3),(2,5),(3,5),(3,6)
311        // Optimal order: v3, v2, v1, v0 with positions [3, 2, 1, 0]
312        // Wait — config[i] = position for internal vertex i.
313        // Internal vertices sorted: [0, 1, 2, 3]
314        // Optimal evaluation order: v3, v2, v1, v0
315        // v3 at position 0, v2 at position 1, v1 at position 2, v0 at position 3
316        // So config = [3, 2, 1, 0] (internal idx 0=v0 -> pos 3, idx 1=v1 -> pos 2, ...)
317        instance: Box::new(MinimumCodeGenerationOneRegister::new(
318            7,
319            vec![
320                (0, 1),
321                (0, 2),
322                (1, 3),
323                (1, 4),
324                (2, 3),
325                (2, 5),
326                (3, 5),
327                (3, 6),
328            ],
329            3,
330        )),
331        optimal_config: vec![3, 2, 1, 0],
332        optimal_value: serde_json::json!(8),
333    }]
334}
335
336#[cfg(test)]
337#[path = "../../unit_tests/models/misc/minimum_code_generation_one_register.rs"]
338mod tests;