problemreductions/models/misc/minimum_register_sufficiency_for_loops.rs
1//! Minimum Register Sufficiency for Loops problem implementation.
2//!
3//! Given a loop of length N and a set of variables, each active during a
4//! contiguous circular arc of timesteps, assign registers to variables
5//! minimizing the number of distinct registers used, such that no two
6//! conflicting (overlapping) variables share the same register.
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: "MinimumRegisterSufficiencyForLoops",
16 display_name: "Minimum Register Sufficiency for Loops",
17 aliases: &[],
18 dimensions: &[],
19 module_path: module_path!(),
20 description: "Assign registers to loop variables minimizing register count, no two conflicting variables share a register",
21 fields: &[
22 FieldInfo { name: "loop_length", type_name: "usize", description: "Loop length N (number of timesteps)" },
23 FieldInfo { name: "variables", type_name: "Vec<(usize, usize)>", description: "Variables as (start_time, duration) circular arcs" },
24 ],
25 }
26}
27
28/// The Minimum Register Sufficiency for Loops problem.
29///
30/// Given a loop of length N (representing N timesteps arranged in a circle)
31/// and a set of variables, each active during a contiguous circular arc of
32/// timesteps specified by (start_time, duration), assign a register index
33/// to each variable such that:
34/// - No two variables with overlapping circular arcs share the same register
35/// - The number of distinct registers used is minimized
36///
37/// This is equivalent to the circular arc graph coloring problem, where each
38/// variable corresponds to a circular arc and registers correspond to colors.
39///
40/// # Representation
41///
42/// Each variable is assigned a register index from `{0, ..., n-1}` where n is
43/// the number of variables. The configuration `config[i]` gives the register
44/// assigned to variable i.
45///
46/// # Example
47///
48/// ```
49/// use problemreductions::models::misc::MinimumRegisterSufficiencyForLoops;
50/// use problemreductions::{Problem, Solver, BruteForce, Min};
51///
52/// // 3 variables on a loop of length 6, all pairs conflict
53/// let problem = MinimumRegisterSufficiencyForLoops::new(
54/// 6,
55/// vec![(0, 3), (2, 3), (4, 3)],
56/// );
57/// let solver = BruteForce::new();
58/// let solution = solver.find_witness(&problem);
59/// assert!(solution.is_some());
60/// let val = problem.evaluate(&solution.unwrap());
61/// assert_eq!(val, Min(Some(3)));
62/// ```
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct MinimumRegisterSufficiencyForLoops {
65 /// Loop length N (number of timesteps in the circular loop).
66 loop_length: usize,
67 /// Variables as (start_time, duration) pairs representing circular arcs.
68 variables: Vec<(usize, usize)>,
69}
70
71impl MinimumRegisterSufficiencyForLoops {
72 /// Create a new Minimum Register Sufficiency for Loops instance.
73 ///
74 /// # Panics
75 ///
76 /// Panics if `loop_length` is zero, if any duration is zero or exceeds
77 /// `loop_length`, or if any `start_time >= loop_length`.
78 pub fn new(loop_length: usize, variables: Vec<(usize, usize)>) -> Self {
79 assert!(loop_length > 0, "loop_length must be positive");
80 for (i, &(start, dur)) in variables.iter().enumerate() {
81 assert!(
82 start < loop_length,
83 "Variable {} start_time {} >= loop_length {}",
84 i,
85 start,
86 loop_length
87 );
88 assert!(
89 dur > 0 && dur <= loop_length,
90 "Variable {} duration {} must be in [1, {}]",
91 i,
92 dur,
93 loop_length
94 );
95 }
96 Self {
97 loop_length,
98 variables,
99 }
100 }
101
102 /// Get the loop length N.
103 pub fn loop_length(&self) -> usize {
104 self.loop_length
105 }
106
107 /// Get the number of variables.
108 pub fn num_variables(&self) -> usize {
109 self.variables.len()
110 }
111
112 /// Get the variables as (start_time, duration) pairs.
113 pub fn variables(&self) -> &[(usize, usize)] {
114 &self.variables
115 }
116
117 /// Check if two circular arcs overlap.
118 ///
119 /// Arc [s, s+l) mod N covers timesteps {s, s+1, ..., s+l-1} mod N.
120 /// Two arcs overlap iff their covered timestep sets intersect.
121 fn arcs_overlap(s1: usize, l1: usize, s2: usize, l2: usize, n: usize) -> bool {
122 // Use the modular distance check:
123 // Timestep t is in arc [s, s+l) mod N iff (t - s) mod N < l
124 // Two arcs are disjoint iff arc2 fits entirely in the gap of arc1
125 // or arc1 fits entirely in the gap of arc2.
126 // Gap of arc [s, s+l) is [(s+l) mod N, s) with length N-l.
127
128 // If either arc covers the entire loop, they always overlap (if both non-empty)
129 if l1 == n || l2 == n {
130 return true;
131 }
132
133 // Check if arc2 is entirely in the gap of arc1.
134 // Gap of arc1 starts at (s1+l1) % n and has length n-l1.
135 // Arc2 fits in this gap if the "gap distance" of s2 from gap_start
136 // plus l2 <= n - l1.
137 let gap1_start = (s1 + l1) % n;
138 let dist_s2_in_gap1 = (s2 + n - gap1_start) % n;
139 if dist_s2_in_gap1 + l2 <= n - l1 {
140 return false;
141 }
142
143 // Check if arc1 is entirely in the gap of arc2.
144 let gap2_start = (s2 + l2) % n;
145 let dist_s1_in_gap2 = (s1 + n - gap2_start) % n;
146 if dist_s1_in_gap2 + l1 <= n - l2 {
147 return false;
148 }
149
150 true
151 }
152}
153
154impl Problem for MinimumRegisterSufficiencyForLoops {
155 const NAME: &'static str = "MinimumRegisterSufficiencyForLoops";
156 type Value = Min<usize>;
157
158 fn variant() -> Vec<(&'static str, &'static str)> {
159 crate::variant_params![]
160 }
161
162 fn dims(&self) -> Vec<usize> {
163 let n = self.variables.len();
164 vec![n; n]
165 }
166
167 fn evaluate(&self, config: &[usize]) -> Min<usize> {
168 let n = self.variables.len();
169 if config.len() != n {
170 return Min(None);
171 }
172 // Check all register indices are in valid range
173 if config.iter().any(|&r| r >= n) {
174 return Min(None);
175 }
176
177 // Check for conflicts: no two overlapping variables share a register
178 for i in 0..n {
179 for j in (i + 1)..n {
180 if config[i] == config[j] {
181 let (s1, l1) = self.variables[i];
182 let (s2, l2) = self.variables[j];
183 if Self::arcs_overlap(s1, l1, s2, l2, self.loop_length) {
184 return Min(None);
185 }
186 }
187 }
188 }
189
190 // Count distinct registers used
191 let mut used = vec![false; n];
192 for &r in config {
193 used[r] = true;
194 }
195 let count = used.iter().filter(|&&u| u).count();
196 Min(Some(count))
197 }
198}
199
200crate::declare_variants! {
201 default MinimumRegisterSufficiencyForLoops => "num_variables ^ num_variables",
202}
203
204#[cfg(feature = "example-db")]
205pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
206 vec![crate::example_db::specs::ModelExampleSpec {
207 id: "minimum_register_sufficiency_for_loops",
208 // 3 variables on a loop of length 6, all pairs conflict (K3)
209 // Optimal: 3 registers (chromatic number of K3)
210 instance: Box::new(MinimumRegisterSufficiencyForLoops::new(
211 6,
212 vec![(0, 3), (2, 3), (4, 3)],
213 )),
214 optimal_config: vec![0, 1, 2],
215 optimal_value: serde_json::json!(3),
216 }]
217}
218
219#[cfg(test)]
220#[path = "../../unit_tests/models/misc/minimum_register_sufficiency_for_loops.rs"]
221mod tests;