problemreductions/models/set/
consecutive_sets.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "ConsecutiveSets",
16 display_name: "Consecutive Sets",
17 aliases: &[],
18 dimensions: &[],
19 category: crate::registry::ProblemCategory::Set,
20 module_path: module_path!(),
21 description: "Determine if a string exists where each subset's elements appear consecutively",
22 fields: &[
23 FieldInfo { name: "alphabet_size", type_name: "usize", description: "Size of the alphabet (elements are 0..alphabet_size-1)" },
24 FieldInfo { name: "subsets", type_name: "Vec<Vec<usize>>", description: "Collection of subsets of the alphabet" },
25 FieldInfo { name: "bound_k", type_name: "usize", description: "Maximum string length K" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(try_from = "ConsecutiveSetsData")]
75pub struct ConsecutiveSets {
76 alphabet_size: usize,
78 subsets: Vec<Vec<usize>>,
80 bound_k: usize,
82}
83
84#[derive(Deserialize)]
85struct ConsecutiveSetsData {
86 alphabet_size: usize,
87 subsets: Vec<Vec<usize>>,
88 bound_k: usize,
89}
90
91impl TryFrom<ConsecutiveSetsData> for ConsecutiveSets {
92 type Error = crate::registry::ConstructionError;
93
94 fn try_from(data: ConsecutiveSetsData) -> Result<Self, Self::Error> {
95 Self::try_new(data.alphabet_size, data.subsets, data.bound_k)
96 }
97}
98
99impl ConsecutiveSets {
100 pub fn new(alphabet_size: usize, subsets: Vec<Vec<usize>>, bound_k: usize) -> Self {
107 Self::try_new(alphabet_size, subsets, bound_k).unwrap_or_else(|error| panic!("{error}"))
108 }
109
110 fn try_new(
111 alphabet_size: usize,
112 mut subsets: Vec<Vec<usize>>,
113 bound_k: usize,
114 ) -> Result<Self, crate::registry::ConstructionError> {
115 if bound_k == 0 {
116 return Err("bound_k must be positive, got 0".into());
117 }
118 for (index, subset) in subsets.iter_mut().enumerate() {
119 let mut seen = HashSet::with_capacity(subset.len());
120 for &element in subset.iter() {
121 if element >= alphabet_size {
122 return Err(format!("subset {index} contains element {element} outside alphabet of size {alphabet_size}").into());
123 }
124 if !seen.insert(element) {
125 return Err(format!("subset {index} contains duplicate elements").into());
126 }
127 }
128 subset.sort();
129 }
130 Ok(Self {
131 alphabet_size,
132 subsets,
133 bound_k,
134 })
135 }
136
137 pub fn alphabet_size(&self) -> usize {
139 self.alphabet_size
140 }
141
142 pub fn num_subsets(&self) -> usize {
144 self.subsets.len()
145 }
146
147 pub fn bound_k(&self) -> usize {
149 self.bound_k
150 }
151
152 pub fn subsets(&self) -> &[Vec<usize>] {
154 &self.subsets
155 }
156}
157
158impl Problem for ConsecutiveSets {
159 const NAME: &'static str = "ConsecutiveSets";
160 type Solution = Vec<Option<usize>>;
161 type Value = crate::types::Or;
162
163 crate::problem_parameters![
164 ("alphabet_size", alphabet_size),
165 ("num_subsets", num_subsets),
166 ("bound_k", bound_k),
167 ];
168
169 fn evaluate(
170 &self,
171 config: &Self::Solution,
172 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
173 if config.len() != self.bound_k {
174 return Err(crate::traits::EvaluationError::InvalidConfiguration(
175 "ordering representation length does not match the bound".into(),
176 ));
177 }
178 if config
179 .iter()
180 .any(|symbol| symbol.is_some_and(|value| value >= self.alphabet_size))
181 {
182 return Err(crate::traits::EvaluationError::InvalidConfiguration(
183 "ordering representation contains an out-of-range symbol".into(),
184 ));
185 }
186 let config = config
187 .iter()
188 .map(|symbol| symbol.unwrap_or(self.alphabet_size))
189 .collect::<Vec<_>>();
190 Ok({
191 crate::types::Or({
192 let unused = self.alphabet_size;
194 let str_len = config
195 .iter()
196 .rposition(|&v| v != unused)
197 .map_or(0, |p| p + 1);
198
199 let w = &config[..str_len];
201 if w.contains(&unused) {
202 return Ok(crate::types::Or(false));
203 }
204
205 let mut subset_membership = vec![0usize; self.alphabet_size];
206 let mut seen_in_window = vec![0usize; self.alphabet_size];
207 let mut subset_stamp = 1usize;
208 let mut window_stamp = 1usize;
209
210 for subset in &self.subsets {
212 let subset_len = subset.len();
213 if subset_len == 0 {
214 continue; }
216 if subset_len > str_len {
217 return Ok(crate::types::Or(false)); }
219
220 for &elem in subset {
221 subset_membership[elem] = subset_stamp;
222 }
223
224 let mut found = false;
225 for start in 0..=(str_len - subset_len) {
226 let window = &w[start..start + subset_len];
227 let current_window_stamp = window_stamp;
228 window_stamp += 1;
229
230 if window.iter().all(|&elem| {
234 let is_member = subset_membership[elem] == subset_stamp;
235 let is_new = seen_in_window[elem] != current_window_stamp;
236 if is_member && is_new {
237 seen_in_window[elem] = current_window_stamp;
238 true
239 } else {
240 false
241 }
242 }) {
243 found = true;
245 break;
246 }
247 }
248 if !found {
249 return Ok(crate::types::Or(false));
250 }
251
252 subset_stamp += 1;
253 }
254
255 true
256 })
257 })
258 }
259
260 fn variant() -> Vec<(&'static str, &'static str)> {
261 crate::variant_params![]
262 }
263}
264
265impl crate::solvers::BruteForceProblem for ConsecutiveSets {
266 fn dimensions(&self) -> Vec<usize> {
267 vec![self.alphabet_size + 1; self.bound_k]
269 }
270}
271
272crate::declare_variants! {
273 default ConsecutiveSets => "alphabet_size^bound_k * num_subsets",
274}
275
276crate::register_brute_force! {
277 ConsecutiveSets decode |problem: &ConsecutiveSets, indices: Vec<usize>| indices.into_iter().map(|value| (value != problem.alphabet_size()).then_some(value)).collect(),
278}
279
280#[cfg(feature = "example-db")]
281pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
282 vec![crate::example_db::specs::ModelExampleSpec {
283 id: "consecutive_sets",
284 instance: Box::new(ConsecutiveSets::new(
286 6,
287 vec![vec![0, 4], vec![2, 4], vec![2, 5], vec![1, 5], vec![1, 3]],
288 6,
289 )),
290 optimal_config: serde_json::json!(vec![0, 4, 2, 5, 1, 3]),
291 optimal_value: serde_json::json!(true),
292 }]
293}
294
295#[cfg(test)]
296#[path = "../../unit_tests/models/set/consecutive_sets.rs"]
297mod tests;