problemreductions/models/set/
two_dimensional_consecutive_sets.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use serde::de::Error as _;
11use serde::{Deserialize, Serialize};
12use std::collections::HashSet;
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "TwoDimensionalConsecutiveSets",
17 display_name: "2-Dimensional Consecutive Sets",
18 aliases: &[],
19 dimensions: &[],
20 category: crate::registry::ProblemCategory::Set,
21 module_path: module_path!(),
22 description: "Determine if alphabet can be partitioned into ordered groups with intersection and consecutiveness constraints",
23 fields: &[
24 FieldInfo { name: "alphabet_size", type_name: "usize", description: "Size of the alphabet (elements are 0..alphabet_size-1)" },
25 FieldInfo { name: "subsets", type_name: "Vec<Vec<usize>>", description: "Collection of subsets of the alphabet" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize)]
58pub struct TwoDimensionalConsecutiveSets {
59 alphabet_size: usize,
61 subsets: Vec<Vec<usize>>,
63}
64
65#[derive(Debug, Deserialize)]
66struct TwoDimensionalConsecutiveSetsUnchecked {
67 alphabet_size: usize,
68 subsets: Vec<Vec<usize>>,
69}
70
71fn validate(
72 alphabet_size: usize,
73 subsets: &[Vec<usize>],
74) -> Result<(), crate::registry::ConstructionError> {
75 if alphabet_size == 0 {
76 return Err("Alphabet size must be positive".to_string().into());
77 }
78
79 for (i, subset) in subsets.iter().enumerate() {
80 let mut seen = HashSet::new();
81 for &elem in subset {
82 if elem >= alphabet_size {
83 return Err(format!(
84 "Subset {} contains element {} which is outside alphabet of size {}",
85 i, elem, alphabet_size
86 )
87 .into());
88 }
89 if !seen.insert(elem) {
90 return Err(format!("Subset {} contains duplicate element {}", i, elem).into());
91 }
92 }
93 }
94
95 Ok(())
96}
97
98impl<'de> Deserialize<'de> for TwoDimensionalConsecutiveSets {
99 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
100 where
101 D: serde::Deserializer<'de>,
102 {
103 let unchecked = TwoDimensionalConsecutiveSetsUnchecked::deserialize(deserializer)?;
104 Self::try_new(unchecked.alphabet_size, unchecked.subsets).map_err(D::Error::custom)
105 }
106}
107
108impl TwoDimensionalConsecutiveSets {
109 pub fn try_new(
111 alphabet_size: usize,
112 subsets: Vec<Vec<usize>>,
113 ) -> Result<Self, crate::registry::ConstructionError> {
114 validate(alphabet_size, &subsets)?;
115 let subsets = subsets
116 .into_iter()
117 .map(|mut s| {
118 s.sort();
119 s
120 })
121 .collect();
122 Ok(Self {
123 alphabet_size,
124 subsets,
125 })
126 }
127
128 pub fn new(alphabet_size: usize, subsets: Vec<Vec<usize>>) -> Self {
135 Self::try_new(alphabet_size, subsets).unwrap_or_else(|message| panic!("{message}"))
136 }
137
138 pub fn alphabet_size(&self) -> usize {
140 self.alphabet_size
141 }
142
143 pub fn num_subsets(&self) -> usize {
145 self.subsets.len()
146 }
147
148 pub fn subsets(&self) -> &[Vec<usize>] {
150 &self.subsets
151 }
152}
153
154impl Problem for TwoDimensionalConsecutiveSets {
155 const NAME: &'static str = "TwoDimensionalConsecutiveSets";
156 type Solution = Vec<usize>;
157 type Value = crate::types::Or;
158
159 crate::problem_parameters![
160 ("alphabet_size", alphabet_size),
161 ("num_subsets", num_subsets),
162 ];
163
164 fn evaluate(
165 &self,
166 config: &Self::Solution,
167 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
168 Ok({
169 crate::types::Or({
170 if config.len() != self.alphabet_size {
171 return Err(crate::traits::EvaluationError::InvalidConfiguration(
172 "group assignment length does not match the alphabet".into(),
173 ));
174 }
175 if config.iter().any(|&v| v >= self.alphabet_size) {
176 return Err(crate::traits::EvaluationError::InvalidConfiguration(
177 "group assignment contains an out-of-range group".into(),
178 ));
179 }
180
181 let mut used = vec![false; self.alphabet_size];
183 for &group in config {
184 used[group] = true;
185 }
186 let mut dense_labels = vec![0; self.alphabet_size];
187 let mut next_label = 0;
188 for (label, is_used) in used.into_iter().enumerate() {
189 if is_used {
190 dense_labels[label] = next_label;
191 next_label += 1;
192 }
193 }
194
195 for subset in &self.subsets {
196 if subset.is_empty() {
197 continue;
198 }
199 let groups: Vec<usize> =
200 subset.iter().map(|&s| dense_labels[config[s]]).collect();
201
202 let unique: HashSet<usize> = groups.iter().copied().collect();
204 if unique.len() != subset.len() {
205 return Ok(crate::types::Or(false));
206 }
207
208 let min_g = *unique.iter().min().unwrap();
210 let max_g = *unique.iter().max().unwrap();
211 if max_g - min_g + 1 != subset.len() {
212 return Ok(crate::types::Or(false));
213 }
214 }
215
216 true
217 })
218 })
219 }
220
221 fn variant() -> Vec<(&'static str, &'static str)> {
222 crate::variant_params![]
223 }
224}
225
226impl crate::solvers::BruteForceProblem for TwoDimensionalConsecutiveSets {
227 fn dimensions(&self) -> Vec<usize> {
228 vec![self.alphabet_size; self.alphabet_size]
229 }
230}
231
232crate::declare_variants! {
233 default TwoDimensionalConsecutiveSets => "alphabet_size^alphabet_size",
234}
235
236crate::register_brute_force! {
237 TwoDimensionalConsecutiveSets,
238}
239
240#[cfg(feature = "example-db")]
241pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
242 vec![crate::example_db::specs::ModelExampleSpec {
243 id: "two_dimensional_consecutive_sets",
244 instance: Box::new(TwoDimensionalConsecutiveSets::new(
245 6,
246 vec![
247 vec![0, 1, 2],
248 vec![3, 4, 5],
249 vec![1, 3],
250 vec![2, 4],
251 vec![0, 5],
252 ],
253 )),
254 optimal_config: serde_json::json!(vec![0, 1, 2, 2, 3, 1]),
255 optimal_value: serde_json::json!(true),
256 }]
257}
258
259#[cfg(test)]
260#[path = "../../unit_tests/models/set/two_dimensional_consecutive_sets.rs"]
261mod tests;