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