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)]
59pub struct RegisterSufficiency {
60 num_vertices: usize,
62 arcs: Vec<(usize, usize)>,
64 bound: usize,
66}
67
68impl RegisterSufficiency {
69 pub fn new(num_vertices: usize, arcs: Vec<(usize, usize)>, bound: usize) -> Self {
76 for &(v, u) in &arcs {
77 assert!(
78 v < num_vertices && u < num_vertices,
79 "Arc ({}, {}) out of bounds for {} vertices",
80 v,
81 u,
82 num_vertices
83 );
84 assert!(v != u, "Self-loop ({}, {}) not allowed in a DAG", v, u);
85 }
86 Self {
87 num_vertices,
88 arcs,
89 bound,
90 }
91 }
92
93 pub fn num_vertices(&self) -> usize {
95 self.num_vertices
96 }
97
98 pub fn num_arcs(&self) -> usize {
100 self.arcs.len()
101 }
102
103 pub fn num_sinks(&self) -> usize {
105 let mut has_dependent = vec![false; self.num_vertices];
106 for &(_, dependency) in &self.arcs {
107 has_dependent[dependency] = true;
108 }
109 has_dependent.into_iter().filter(|&flag| !flag).count()
110 }
111
112 pub fn bound(&self) -> usize {
114 self.bound
115 }
116
117 pub fn arcs(&self) -> &[(usize, usize)] {
119 &self.arcs
120 }
121
122 pub fn simulate_registers(
126 &self,
127 config: &[usize],
128 ) -> Result<Option<i64>, crate::traits::EvaluationError> {
129 let n = self.num_vertices;
130 if config.len() != n {
131 return Ok(None);
132 }
133
134 let mut order = vec![0usize; n]; let mut used = vec![false; n];
137 for (vertex, &position) in config.iter().enumerate() {
138 if position >= n {
139 return Ok(None);
140 }
141 if used[position] {
142 return Ok(None);
143 }
144 used[position] = true;
145 order[position] = vertex;
146 }
147
148 let mut dependencies: Vec<Vec<usize>> = vec![vec![]; n];
152 let mut dependents: Vec<Vec<usize>> = vec![vec![]; n];
153 for &(v, u) in &self.arcs {
154 dependencies[v].push(u);
155 dependents[u].push(v);
156 }
157
158 let mut last_use = vec![0usize; n];
161 for u in 0..n {
162 if dependents[u].is_empty() {
163 last_use[u] = n; } else {
167 let mut latest = 0;
168 for &v in &dependents[u] {
169 latest = latest.max(config[v]);
170 }
171 last_use[u] = latest;
172 }
173 }
174
175 let mut max_registers = 0;
176
177 for step in 0..n {
179 let vertex = order[step];
180
181 for &dep in &dependencies[vertex] {
184 if config[dep] >= step {
185 return Ok(None);
187 }
188 }
189
190 let reg_count = order[..=step]
198 .iter()
199 .filter(|&&v| last_use[v] > step)
200 .count();
201
202 max_registers = max_registers.max(reg_count);
203 }
204
205 Ok(Some(i64::try_from(max_registers).map_err(|_| {
206 crate::traits::EvaluationError::IntegerOverflow(
207 "converting register-usage count to i64".into(),
208 )
209 })?))
210 }
211
212 pub fn solve_exact(&self) -> Option<Vec<usize>> {
224 let n = self.num_vertices;
225 if n == 0 {
226 return Some(vec![]);
227 }
228
229 let mut dependents: Vec<Vec<usize>> = vec![vec![]; n];
230 let mut dependencies: Vec<Vec<usize>> = vec![vec![]; n];
231 let mut in_degree = vec![0u32; n];
232 for &(v, u) in &self.arcs {
233 in_degree[v] += 1;
234 dependents[u].push(v);
235 dependencies[v].push(u);
236 }
237
238 let mut state = BnBState {
239 n,
240 bound: self.bound,
241 config: vec![0usize; n],
242 live: vec![false; n],
243 live_count: 0,
244 remaining_in_degree: in_degree.clone(),
245 remaining_deps: dependents.iter().map(|d| d.len()).collect(),
246 ready: (0..n).filter(|&v| in_degree[v] == 0).collect(),
247 dependents,
248 dependencies,
249 };
250 state.ready.sort_unstable();
251
252 if state.backtrack(0) {
253 Some(state.config)
254 } else {
255 None
256 }
257 }
258}
259
260struct BnBState {
261 n: usize,
262 bound: usize,
263 config: Vec<usize>,
264 live: Vec<bool>,
265 live_count: usize,
266 remaining_in_degree: Vec<u32>,
267 remaining_deps: Vec<usize>,
268 ready: Vec<usize>,
269 dependents: Vec<Vec<usize>>,
270 dependencies: Vec<Vec<usize>>,
271}
272
273impl BnBState {
274 fn backtrack(&mut self, step: usize) -> bool {
275 if step == self.n {
276 return true;
277 }
278
279 let mut candidates = self.ready.clone();
281 candidates.sort_by_key(|&v| {
282 let frees = self.dependencies[v]
283 .iter()
284 .filter(|&&dep| self.remaining_deps[dep] == 1 && self.live[dep])
285 .count();
286 std::cmp::Reverse(frees)
287 });
288
289 for &vertex in &candidates {
290 self.config[vertex] = step;
291
292 let was_live = self.live[vertex];
293 if !was_live {
294 self.live[vertex] = true;
295 self.live_count += 1;
296 }
297
298 let mut freed = Vec::new();
299 for &dep in &self.dependencies[vertex] {
300 self.remaining_deps[dep] -= 1;
301 if self.remaining_deps[dep] == 0 && self.live[dep] {
302 self.live[dep] = false;
303 self.live_count -= 1;
304 freed.push(dep);
305 }
306 }
307
308 if self.live_count <= self.bound {
309 self.ready.retain(|&v| v != vertex);
310 let mut newly_ready = Vec::new();
311 for &dep in &self.dependents[vertex] {
312 self.remaining_in_degree[dep] -= 1;
313 if self.remaining_in_degree[dep] == 0 {
314 self.ready.push(dep);
315 newly_ready.push(dep);
316 }
317 }
318
319 if self.backtrack(step + 1) {
320 return true;
321 }
322
323 for &dep in &newly_ready {
324 self.ready.retain(|&v| v != dep);
325 }
326 for &dep in &self.dependents[vertex] {
327 self.remaining_in_degree[dep] += 1;
328 }
329 self.ready.push(vertex);
330 self.ready.sort_unstable();
331 }
332
333 for &dep in &freed {
334 self.live[dep] = true;
335 self.live_count += 1;
336 }
337 for &dep in &self.dependencies[vertex] {
338 self.remaining_deps[dep] += 1;
339 }
340
341 if !was_live {
342 self.live[vertex] = false;
343 self.live_count -= 1;
344 }
345 }
346
347 false
348 }
349}
350
351impl Problem for RegisterSufficiency {
352 const NAME: &'static str = "RegisterSufficiency";
353 type Solution = Vec<usize>;
354 type Value = crate::types::Or;
355
356 crate::problem_parameters![
357 ("bound", bound),
358 ("num_arcs", num_arcs),
359 ("num_sinks", num_sinks),
360 ("num_vertices", num_vertices),
361 ];
362
363 fn variant() -> Vec<(&'static str, &'static str)> {
364 crate::variant_params![]
365 }
366
367 fn evaluate(
368 &self,
369 config: &Self::Solution,
370 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
371 if config.len() != self.num_vertices {
372 return Err(crate::traits::EvaluationError::InvalidConfiguration(
373 "evaluation ordering length does not match the graph vertices".into(),
374 ));
375 }
376 if config.iter().any(|&position| position >= self.num_vertices) {
377 return Err(crate::traits::EvaluationError::InvalidConfiguration(
378 "evaluation ordering contains an out-of-range position".into(),
379 ));
380 }
381 let bound = i64::try_from(self.bound).map_err(|_| {
382 crate::traits::EvaluationError::IntegerOverflow(
383 "converting register bound to i64".into(),
384 )
385 })?;
386 Ok(crate::types::Or(
387 self.simulate_registers(config)?
388 .is_some_and(|max_reg| max_reg <= bound),
389 ))
390 }
391}
392
393impl crate::solvers::BruteForceProblem for RegisterSufficiency {
394 fn dimensions(&self) -> Vec<usize> {
395 vec![self.num_vertices; self.num_vertices]
396 }
397}
398
399crate::declare_variants! {
400 default RegisterSufficiency => "num_vertices ^ 2 * 2 ^ num_vertices",
401}
402
403crate::register_brute_force! {
404 RegisterSufficiency,
405}
406
407#[cfg(feature = "example-db")]
408pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
409 vec![crate::example_db::specs::ModelExampleSpec {
410 id: "register_sufficiency",
411 instance: Box::new(RegisterSufficiency::new(
415 7,
416 vec![
417 (2, 0),
418 (2, 1),
419 (3, 1),
420 (4, 2),
421 (4, 3),
422 (5, 0),
423 (6, 4),
424 (6, 5),
425 ],
426 3,
427 )),
428 optimal_config: serde_json::json!(vec![0, 1, 2, 3, 5, 4, 6]),
431 optimal_value: serde_json::json!(true),
432 }]
433}
434
435#[cfg(test)]
436#[path = "../../unit_tests/models/misc/register_sufficiency.rs"]
437mod tests;