problemreductions/models/misc/
register_sufficiency.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "RegisterSufficiency",
14 display_name: "Register Sufficiency",
15 aliases: &[],
16 dimensions: &[],
17 category: crate::registry::ProblemCategory::Misc,
18 module_path: module_path!(),
19 description: "Determine whether a DAG computation can be performed using K or fewer registers",
20 fields: &[
21 FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices n = |V|" },
22 FieldInfo { name: "arcs", type_name: "Vec<(usize, usize)>", description: "Directed arcs (v, u) meaning v depends on u" },
23 FieldInfo { name: "bound", type_name: "usize", description: "Register bound K" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(try_from = "RegisterSufficiencyData")]
60pub struct RegisterSufficiency {
61 num_vertices: usize,
63 arcs: Vec<(usize, usize)>,
65 bound: usize,
67}
68
69#[derive(Deserialize)]
70struct RegisterSufficiencyData {
71 num_vertices: usize,
72 arcs: Vec<(usize, usize)>,
73 bound: usize,
74}
75
76impl TryFrom<RegisterSufficiencyData> for RegisterSufficiency {
77 type Error = crate::registry::ConstructionError;
78
79 fn try_from(data: RegisterSufficiencyData) -> Result<Self, Self::Error> {
80 Self::try_new(data.num_vertices, data.arcs, data.bound)
81 }
82}
83
84impl RegisterSufficiency {
85 pub fn new(num_vertices: usize, arcs: Vec<(usize, usize)>, bound: usize) -> Self {
92 Self::try_new(num_vertices, arcs, bound).unwrap_or_else(|error| panic!("{error}"))
93 }
94
95 fn try_new(
96 num_vertices: usize,
97 arcs: Vec<(usize, usize)>,
98 bound: usize,
99 ) -> Result<Self, crate::registry::ConstructionError> {
100 for &(v, u) in &arcs {
101 if v >= num_vertices || u >= num_vertices {
102 return Err(format!(
103 "Arc ({}, {}) out of bounds for {} vertices",
104 v, u, num_vertices
105 )
106 .into());
107 }
108 if v == u {
109 return Err(format!("Self-loop ({}, {}) not allowed in a DAG", v, u).into());
110 }
111 }
112 Ok(Self {
113 num_vertices,
114 arcs,
115 bound,
116 })
117 }
118
119 pub fn num_vertices(&self) -> usize {
121 self.num_vertices
122 }
123
124 pub fn num_arcs(&self) -> usize {
126 self.arcs.len()
127 }
128
129 pub fn num_sinks(&self) -> usize {
131 let mut has_dependent = vec![false; self.num_vertices];
132 for &(_, dependency) in &self.arcs {
133 has_dependent[dependency] = true;
134 }
135 has_dependent.into_iter().filter(|&flag| !flag).count()
136 }
137
138 pub fn bound(&self) -> usize {
140 self.bound
141 }
142
143 pub fn arcs(&self) -> &[(usize, usize)] {
145 &self.arcs
146 }
147
148 pub fn simulate_registers(
152 &self,
153 config: &[usize],
154 ) -> Result<Option<i64>, crate::traits::EvaluationError> {
155 let n = self.num_vertices;
156 if config.len() != n {
157 return Ok(None);
158 }
159
160 let mut order = vec![0usize; n]; let mut used = vec![false; n];
163 for (vertex, &position) in config.iter().enumerate() {
164 if position >= n {
165 return Ok(None);
166 }
167 if used[position] {
168 return Ok(None);
169 }
170 used[position] = true;
171 order[position] = vertex;
172 }
173
174 let mut dependencies: Vec<Vec<usize>> = vec![vec![]; n];
178 let mut dependents: Vec<Vec<usize>> = vec![vec![]; n];
179 for &(v, u) in &self.arcs {
180 dependencies[v].push(u);
181 dependents[u].push(v);
182 }
183
184 let mut last_use = vec![0usize; n];
187 for u in 0..n {
188 if dependents[u].is_empty() {
189 last_use[u] = n; } else {
193 let mut latest = 0;
194 for &v in &dependents[u] {
195 latest = latest.max(config[v]);
196 }
197 last_use[u] = latest;
198 }
199 }
200
201 let mut max_registers = 0;
202
203 for step in 0..n {
205 let vertex = order[step];
206
207 for &dep in &dependencies[vertex] {
210 if config[dep] >= step {
211 return Ok(None);
213 }
214 }
215
216 let reg_count = order[..=step]
224 .iter()
225 .filter(|&&v| last_use[v] > step)
226 .count();
227
228 max_registers = max_registers.max(reg_count);
229 }
230
231 Ok(Some(i64::try_from(max_registers).map_err(|_| {
232 crate::traits::EvaluationError::IntegerOverflow(
233 "converting register-usage count to i64".into(),
234 )
235 })?))
236 }
237
238 pub fn solve_exact(&self) -> Option<Vec<usize>> {
250 let n = self.num_vertices;
251 if n == 0 {
252 return Some(vec![]);
253 }
254
255 let mut dependents: Vec<Vec<usize>> = vec![vec![]; n];
256 let mut dependencies: Vec<Vec<usize>> = vec![vec![]; n];
257 let mut in_degree = vec![0usize; n];
258 for &(v, u) in &self.arcs {
259 in_degree[v] += 1;
260 dependents[u].push(v);
261 dependencies[v].push(u);
262 }
263 for neighbors in dependents.iter_mut().chain(dependencies.iter_mut()) {
264 neighbors.sort_unstable();
265 }
266
267 let mut state = BnBState {
268 n,
269 bound: self.bound,
270 config: vec![0usize; n],
271 live: vec![false; n],
272 live_count: 0,
273 computed: vec![false; n],
274 failed: std::collections::HashSet::new(),
275 remaining_in_degree: in_degree.clone(),
276 remaining_deps: dependents.iter().map(|d| d.len()).collect(),
277 ready: (0..n).filter(|&v| in_degree[v] == 0).collect(),
278 dependents,
279 dependencies,
280 };
281 state.ready.sort_unstable();
282
283 let input_block = if state
287 .dependencies
288 .iter()
289 .filter(|deps| !deps.is_empty() && deps.iter().all(|&v| in_degree[v] == 0))
290 .all(|deps| state.ready.iter().all(|v| deps.contains(v)))
291 {
292 state.ready.clone()
293 } else {
294 Vec::new()
295 };
296 if state.backtrack(0, &input_block) {
297 Some(state.config)
298 } else {
299 None
300 }
301 }
302}
303
304struct BnBState {
305 n: usize,
306 bound: usize,
307 config: Vec<usize>,
308 live: Vec<bool>,
309 live_count: usize,
310 computed: Vec<bool>,
311 failed: std::collections::HashSet<Vec<bool>>,
312 remaining_in_degree: Vec<usize>,
313 remaining_deps: Vec<usize>,
314 ready: Vec<usize>,
315 dependents: Vec<Vec<usize>>,
316 dependencies: Vec<Vec<usize>>,
317}
318
319impl BnBState {
320 fn backtrack(&mut self, step: usize, block: &[usize]) -> bool {
321 if step == self.n {
322 return true;
323 }
324 if block.is_empty() && self.failed.contains(&self.computed) {
327 return false;
328 }
329
330 let mut candidates = if block.is_empty() {
332 self.ready.clone()
333 } else {
334 vec![block[0]]
335 };
336 candidates.sort_by_cached_key(|&v| {
337 let frees = self.dependencies[v]
338 .iter()
339 .filter(|&&dep| self.remaining_deps[dep] == 1 && self.live[dep])
340 .count();
341 let group_size = self
342 .ready
343 .iter()
344 .filter(|&&w| {
345 self.dependencies[v] == self.dependencies[w]
346 && self.dependents[v] == self.dependents[w]
347 })
348 .count();
349 let unblocks = self.dependents[v].iter().any(|&consumer| {
350 self.remaining_in_degree[consumer]
351 == group_size
352 * self.dependents[v]
353 .iter()
354 .filter(|&&w| w == consumer)
355 .count()
356 });
357 (
359 std::cmp::Reverse(frees),
360 std::cmp::Reverse(unblocks),
361 group_size,
362 v,
363 )
364 });
365 if block.is_empty() {
366 candidates.retain(|&v| {
367 !self.ready.iter().any(|&w| {
368 w < v
369 && self.dependencies[v] == self.dependencies[w]
370 && self.dependents[v] == self.dependents[w]
371 })
372 });
373 if candidates.first().is_some_and(|&v| {
376 self.dependencies[v]
377 .iter()
378 .any(|&dep| self.remaining_deps[dep] == 1 && self.live[dep])
379 }) {
380 candidates.truncate(1);
381 }
382 }
383
384 for &vertex in &candidates {
385 let following = if block.is_empty() {
389 self.ready
390 .iter()
391 .copied()
392 .filter(|&v| {
393 v != vertex
394 && self.dependencies[v] == self.dependencies[vertex]
395 && self.dependents[v] == self.dependents[vertex]
396 })
397 .collect::<Vec<_>>()
398 } else {
399 block[1..].to_vec()
400 };
401 self.config[vertex] = step;
402 self.computed[vertex] = true;
403
404 let was_live = self.live[vertex];
405 if !was_live {
406 self.live[vertex] = true;
407 self.live_count += 1;
408 }
409
410 let mut freed = Vec::new();
411 for &dep in &self.dependencies[vertex] {
412 self.remaining_deps[dep] -= 1;
413 if self.remaining_deps[dep] == 0 && self.live[dep] {
414 self.live[dep] = false;
415 self.live_count -= 1;
416 freed.push(dep);
417 }
418 }
419
420 if self.live_count <= self.bound {
421 self.ready.retain(|&v| v != vertex);
422 let mut newly_ready = Vec::new();
423 for &dep in &self.dependents[vertex] {
424 self.remaining_in_degree[dep] -= 1;
425 if self.remaining_in_degree[dep] == 0 {
426 self.ready.push(dep);
427 newly_ready.push(dep);
428 }
429 }
430
431 if self.backtrack(step + 1, &following) {
432 return true;
433 }
434
435 for &dep in &newly_ready {
436 self.ready.retain(|&v| v != dep);
437 }
438 for &dep in &self.dependents[vertex] {
439 self.remaining_in_degree[dep] += 1;
440 }
441 self.ready.push(vertex);
442 self.ready.sort_unstable();
443 }
444
445 for &dep in &freed {
446 self.live[dep] = true;
447 self.live_count += 1;
448 }
449 for &dep in &self.dependencies[vertex] {
450 self.remaining_deps[dep] += 1;
451 }
452
453 if !was_live {
454 self.live[vertex] = false;
455 self.live_count -= 1;
456 }
457 self.computed[vertex] = false;
458 }
459
460 if block.is_empty() {
461 self.failed.insert(self.computed.clone());
462 }
463 false
464 }
465}
466
467impl Problem for RegisterSufficiency {
468 const NAME: &'static str = "RegisterSufficiency";
469 type Solution = Vec<usize>;
470 type Value = crate::types::Or;
471
472 crate::problem_parameters![
473 ("bound", bound),
474 ("num_arcs", num_arcs),
475 ("num_sinks", num_sinks),
476 ("num_vertices", num_vertices),
477 ];
478
479 fn variant() -> Vec<(&'static str, &'static str)> {
480 crate::variant_params![]
481 }
482
483 fn evaluate(
484 &self,
485 config: &Self::Solution,
486 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
487 if config.len() != self.num_vertices {
488 return Err(crate::traits::EvaluationError::InvalidConfiguration(
489 "evaluation ordering length does not match the graph vertices".into(),
490 ));
491 }
492 if config.iter().any(|&position| position >= self.num_vertices) {
493 return Err(crate::traits::EvaluationError::InvalidConfiguration(
494 "evaluation ordering contains an out-of-range position".into(),
495 ));
496 }
497 let bound = i64::try_from(self.bound).map_err(|_| {
498 crate::traits::EvaluationError::IntegerOverflow(
499 "converting register bound to i64".into(),
500 )
501 })?;
502 Ok(crate::types::Or(
503 self.simulate_registers(config)?
504 .is_some_and(|max_reg| max_reg <= bound),
505 ))
506 }
507}
508
509impl crate::solvers::BruteForceProblem for RegisterSufficiency {
510 fn dimensions(&self) -> Vec<usize> {
511 vec![self.num_vertices; self.num_vertices]
512 }
513}
514
515crate::declare_variants! {
516 default RegisterSufficiency => "num_vertices ^ 2 * 2 ^ num_vertices",
517}
518
519crate::register_brute_force! {
520 RegisterSufficiency,
521}
522
523#[cfg(feature = "example-db")]
524pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
525 vec![crate::example_db::specs::ModelExampleSpec {
526 id: "register_sufficiency",
527 instance: Box::new(RegisterSufficiency::new(
531 7,
532 vec![
533 (2, 0),
534 (2, 1),
535 (3, 1),
536 (4, 2),
537 (4, 3),
538 (5, 0),
539 (6, 4),
540 (6, 5),
541 ],
542 3,
543 )),
544 optimal_config: serde_json::json!(vec![0, 1, 2, 3, 5, 4, 6]),
547 optimal_value: serde_json::json!(true),
548 }]
549}
550
551#[cfg(test)]
552#[path = "../../unit_tests/models/misc/register_sufficiency.rs"]
553mod tests;