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;