problemreductions/models/misc/
minimum_register_sufficiency_for_loops.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
65#[serde(try_from = "MinimumRegisterSufficiencyForLoopsData")]
66pub struct MinimumRegisterSufficiencyForLoops {
67 loop_length: usize,
69 variables: Vec<(usize, usize)>,
71}
72
73#[derive(Deserialize)]
74struct MinimumRegisterSufficiencyForLoopsData {
75 loop_length: usize,
76 variables: Vec<(usize, usize)>,
77}
78
79impl TryFrom<MinimumRegisterSufficiencyForLoopsData> for MinimumRegisterSufficiencyForLoops {
80 type Error = crate::registry::ConstructionError;
81 fn try_from(data: MinimumRegisterSufficiencyForLoopsData) -> Result<Self, Self::Error> {
82 Self::try_new(data.loop_length, data.variables)
83 }
84}
85
86impl MinimumRegisterSufficiencyForLoops {
87 pub fn new(loop_length: usize, variables: Vec<(usize, usize)>) -> Self {
94 Self::try_new(loop_length, variables).unwrap_or_else(|error| panic!("{error}"))
95 }
96
97 fn try_new(
98 loop_length: usize,
99 variables: Vec<(usize, usize)>,
100 ) -> Result<Self, crate::registry::ConstructionError> {
101 if loop_length == 0 {
102 return Err("loop_length must be positive".into());
103 }
104 for (i, &(start, dur)) in variables.iter().enumerate() {
105 if start >= loop_length {
106 return Err(format!(
107 "Variable {} start_time {} >= loop_length {}",
108 i, start, loop_length
109 )
110 .into());
111 }
112 if dur == 0 || dur > loop_length {
113 return Err(format!(
114 "Variable {} duration {} must be in [1, {}]",
115 i, dur, loop_length
116 )
117 .into());
118 }
119 }
120 Ok(Self {
121 loop_length,
122 variables,
123 })
124 }
125
126 pub fn loop_length(&self) -> usize {
128 self.loop_length
129 }
130
131 pub fn num_variables(&self) -> usize {
133 self.variables.len()
134 }
135
136 pub fn variables(&self) -> &[(usize, usize)] {
138 &self.variables
139 }
140
141 fn arcs_overlap(s1: usize, l1: usize, s2: usize, l2: usize, n: usize) -> bool {
146 if l1 == n || l2 == n {
154 return true;
155 }
156
157 let gap1_start = (s1 + l1) % n;
162 let dist_s2_in_gap1 = (s2 + n - gap1_start) % n;
163 if dist_s2_in_gap1 + l2 <= n - l1 {
164 return false;
165 }
166
167 let gap2_start = (s2 + l2) % n;
169 let dist_s1_in_gap2 = (s1 + n - gap2_start) % n;
170 if dist_s1_in_gap2 + l1 <= n - l2 {
171 return false;
172 }
173
174 true
175 }
176}
177
178impl Problem for MinimumRegisterSufficiencyForLoops {
179 const NAME: &'static str = "MinimumRegisterSufficiencyForLoops";
180 type Solution = Vec<usize>;
181 type Value = Min<i64>;
182
183 crate::problem_parameters![
184 ("loop_length", loop_length),
185 ("num_variables", num_variables),
186 ];
187
188 fn variant() -> Vec<(&'static str, &'static str)> {
189 crate::variant_params![]
190 }
191
192 fn evaluate(
193 &self,
194 config: &Self::Solution,
195 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
196 Ok({
197 let n = self.variables.len();
198 if config.len() != n {
199 return Err(crate::traits::EvaluationError::InvalidConfiguration(
200 "register assignment length does not match the variables".into(),
201 ));
202 }
203 if config.iter().any(|®ister| register >= n) {
205 return Err(crate::traits::EvaluationError::InvalidConfiguration(
206 "register assignment contains an out-of-range register".into(),
207 ));
208 }
209 for i in 0..n {
211 for j in (i + 1)..n {
212 if config[i] == config[j] {
213 let (s1, l1) = self.variables[i];
214 let (s2, l2) = self.variables[j];
215 if Self::arcs_overlap(s1, l1, s2, l2, self.loop_length) {
216 return Ok(Min(None));
217 }
218 }
219 }
220 }
221
222 let mut used = vec![false; n];
224 for &r in config {
225 used[r] = true;
226 }
227 let count = used.iter().filter(|&&u| u).count();
228 Min(Some(i64::try_from(count).map_err(|_| {
229 crate::traits::EvaluationError::IntegerOverflow(
230 "converting register count to i64".into(),
231 )
232 })?))
233 })
234 }
235}
236
237impl crate::solvers::BruteForceProblem for MinimumRegisterSufficiencyForLoops {
238 fn dimensions(&self) -> Vec<usize> {
239 let n = self.variables.len();
240 vec![n; n]
241 }
242}
243
244crate::declare_variants! {
245 default MinimumRegisterSufficiencyForLoops => "num_variables ^ num_variables",
246}
247
248crate::register_brute_force! {
249 MinimumRegisterSufficiencyForLoops,
250}
251
252#[cfg(feature = "example-db")]
253pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
254 vec![crate::example_db::specs::ModelExampleSpec {
255 id: "minimum_register_sufficiency_for_loops",
256 instance: Box::new(MinimumRegisterSufficiencyForLoops::new(
259 6,
260 vec![(0, 3), (2, 3), (4, 3)],
261 )),
262 optimal_config: serde_json::json!(vec![0, 1, 2]),
263 optimal_value: serde_json::json!(3),
264 }]
265}
266
267#[cfg(test)]
268#[path = "../../unit_tests/models/misc/minimum_register_sufficiency_for_loops.rs"]
269mod tests;