problemreductions/models/misc/
minimum_code_generation_one_register.rs1use 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 category: crate::registry::ProblemCategory::Misc,
20 module_path: module_path!(),
21 description: "Find minimum-length instruction sequence for a one-register machine to evaluate an expression DAG",
22 fields: &[
23 FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices n = |V|" },
24 FieldInfo { name: "edges", type_name: "Vec<(usize, usize)>", description: "Directed arcs (parent, child) in the expression DAG" },
25 FieldInfo { name: "num_leaves", type_name: "usize", description: "Number of leaf vertices (out-degree 0)" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct MinimumCodeGenerationOneRegister {
61 num_vertices: usize,
63 edges: Vec<(usize, usize)>,
65 num_leaves: usize,
67}
68
69impl MinimumCodeGenerationOneRegister {
70 pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>, num_leaves: usize) -> Self {
83 assert!(
84 num_leaves <= num_vertices,
85 "num_leaves ({num_leaves}) exceeds num_vertices ({num_vertices})"
86 );
87 let mut out_degree = vec![0usize; num_vertices];
88 for &(parent, child) in &edges {
89 assert!(
90 parent < num_vertices && child < num_vertices,
91 "Edge ({parent}, {child}) out of bounds for {num_vertices} vertices"
92 );
93 assert!(
94 parent != child,
95 "Self-loop ({parent}, {parent}) not allowed"
96 );
97 out_degree[parent] += 1;
98 }
99 for (v, °) in out_degree.iter().enumerate() {
100 assert!(deg <= 2, "Vertex {v} has out-degree {deg} > 2");
101 }
102 let actual_leaves = out_degree.iter().filter(|&&d| d == 0).count();
104 assert_eq!(
105 actual_leaves, num_leaves,
106 "Declared num_leaves ({num_leaves}) != actual leaf count ({actual_leaves})"
107 );
108 Self {
109 num_vertices,
110 edges,
111 num_leaves,
112 }
113 }
114
115 pub fn num_vertices(&self) -> usize {
117 self.num_vertices
118 }
119
120 pub fn num_edges(&self) -> usize {
122 self.edges.len()
123 }
124
125 pub fn num_leaves(&self) -> usize {
127 self.num_leaves
128 }
129
130 pub fn num_internal(&self) -> usize {
132 self.num_vertices - self.num_leaves
133 }
134
135 pub fn edges(&self) -> &[(usize, usize)] {
137 &self.edges
138 }
139
140 fn children(&self) -> Vec<Vec<usize>> {
142 let mut ch = vec![vec![]; self.num_vertices];
143 for &(parent, child) in &self.edges {
144 ch[parent].push(child);
145 }
146 ch
147 }
148
149 fn internal_vertices(&self) -> Vec<usize> {
151 let children = self.children();
152 (0..self.num_vertices)
153 .filter(|&v| !children[v].is_empty())
154 .collect()
155 }
156
157 fn leaf_set(&self) -> Vec<bool> {
159 let children = self.children();
160 (0..self.num_vertices)
161 .map(|v| children[v].is_empty())
162 .collect()
163 }
164
165 pub fn simulate(
169 &self,
170 config: &[usize],
171 ) -> Result<Option<i64>, crate::traits::EvaluationError> {
172 let internal = self.internal_vertices();
173 let n_internal = internal.len();
174 if config.len() != n_internal {
175 return Ok(None);
176 }
177
178 let mut order = vec![0usize; n_internal];
182 let mut used = vec![false; n_internal];
183 for (i, &pos) in config.iter().enumerate() {
184 if pos >= n_internal {
185 return Ok(None);
186 }
187 if used[pos] {
188 return Ok(None);
189 }
190 used[pos] = true;
191 order[pos] = i;
192 }
193
194 let children = self.children();
195 let is_leaf = self.leaf_set();
196
197 let mut computed = vec![false; self.num_vertices];
199 for v in 0..self.num_vertices {
201 if is_leaf[v] {
202 computed[v] = true;
203 }
204 }
205
206 let mut future_uses = vec![0usize; self.num_vertices];
209 for &idx in &order {
210 let v = internal[idx];
211 for &c in &children[v] {
212 future_uses[c] += 1;
213 }
214 }
215
216 let mut register: Option<usize> = None; let mut in_memory = vec![false; self.num_vertices];
218 for v in 0..self.num_vertices {
220 if is_leaf[v] {
221 in_memory[v] = true;
222 }
223 }
224
225 let mut instructions = 0_i64;
226
227 for step in 0..n_internal {
228 let v = internal[order[step]];
229
230 for &c in &children[v] {
232 let available = in_memory[c] || register == Some(c);
233 if !available {
234 return Ok(None); }
236 }
237
238 for &c in &children[v] {
240 future_uses[c] -= 1;
241 }
242
243 let operands = &children[v];
244
245 if let Some(r) = register {
251 if !in_memory[r] && future_uses[r] > 0 {
252 instructions = instructions.checked_add(1).ok_or_else(|| {
253 crate::traits::EvaluationError::IntegerOverflow(
254 "counting one-register instructions".to_string(),
255 )
256 })?; in_memory[r] = true;
258 }
259 }
260
261 if operands.len() == 2 {
263 let c0 = operands[0];
264 let c1 = operands[1];
265 let one_in_register = (register == Some(c0) && in_memory[c1])
266 || (register == Some(c1) && in_memory[c0]);
267 if one_in_register {
268 instructions = instructions.checked_add(1).ok_or_else(|| {
269 crate::traits::EvaluationError::IntegerOverflow(
270 "counting one-register instructions".to_string(),
271 )
272 })?; } else {
274 instructions = instructions.checked_add(2).ok_or_else(|| {
276 crate::traits::EvaluationError::IntegerOverflow(
277 "counting one-register instructions".to_string(),
278 )
279 })?; }
281 } else if operands.len() == 1 {
282 let c0 = operands[0];
283 if register == Some(c0) {
284 instructions = instructions.checked_add(1).ok_or_else(|| {
285 crate::traits::EvaluationError::IntegerOverflow(
286 "counting one-register instructions".to_string(),
287 )
288 })?; } else {
290 instructions = instructions.checked_add(2).ok_or_else(|| {
291 crate::traits::EvaluationError::IntegerOverflow(
292 "counting one-register instructions".to_string(),
293 )
294 })?; }
296 }
297
298 register = Some(v);
299 }
300
301 Ok(Some(instructions))
302 }
303}
304
305impl Problem for MinimumCodeGenerationOneRegister {
306 const NAME: &'static str = "MinimumCodeGenerationOneRegister";
307 type Solution = Vec<usize>;
308 type Value = Min<i64>;
309
310 crate::problem_parameters![
311 ("num_vertices", num_vertices),
312 ("num_edges", num_edges),
313 ("num_leaves", num_leaves),
314 ("num_internal", num_internal),
315 ];
316
317 fn variant() -> Vec<(&'static str, &'static str)> {
318 crate::variant_params![]
319 }
320
321 fn evaluate(
322 &self,
323 config: &Self::Solution,
324 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
325 let n = self.internal_vertices().len();
326 if config.len() != n {
327 return Err(crate::traits::EvaluationError::InvalidConfiguration(
328 "evaluation ordering length does not match the internal vertices".into(),
329 ));
330 }
331 if config.iter().any(|&position| position >= n) {
332 return Err(crate::traits::EvaluationError::InvalidConfiguration(
333 "evaluation ordering contains an out-of-range position".into(),
334 ));
335 }
336 Ok(Min(self.simulate(config)?))
337 }
338}
339
340impl crate::solvers::BruteForceProblem for MinimumCodeGenerationOneRegister {
341 fn dimensions(&self) -> Vec<usize> {
342 let n_internal = self.num_internal();
343 vec![n_internal; n_internal]
344 }
345}
346
347crate::declare_variants! {
348 default MinimumCodeGenerationOneRegister => "2 ^ num_vertices",
349}
350
351crate::register_brute_force! {
352 MinimumCodeGenerationOneRegister,
353}
354
355#[cfg(feature = "example-db")]
356pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
357 vec![crate::example_db::specs::ModelExampleSpec {
358 id: "minimum_code_generation_one_register",
359 instance: Box::new(MinimumCodeGenerationOneRegister::new(
368 7,
369 vec![
370 (0, 1),
371 (0, 2),
372 (1, 3),
373 (1, 4),
374 (2, 3),
375 (2, 5),
376 (3, 5),
377 (3, 6),
378 ],
379 3,
380 )),
381 optimal_config: serde_json::json!(vec![3, 2, 1, 0]),
382 optimal_value: serde_json::json!(8),
383 }]
384}
385
386#[cfg(test)]
387#[path = "../../unit_tests/models/misc/minimum_code_generation_one_register.rs"]
388mod tests;