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 module_path: module_path!(),
21 description: "Determine if alphabet can be partitioned into ordered groups with intersection and consecutiveness constraints",
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 ],
26 }
27}
28
29#[derive(Debug, Clone, Serialize)]
57pub struct TwoDimensionalConsecutiveSets {
58 alphabet_size: usize,
60 subsets: Vec<Vec<usize>>,
62}
63
64#[derive(Debug, Deserialize)]
65struct TwoDimensionalConsecutiveSetsUnchecked {
66 alphabet_size: usize,
67 subsets: Vec<Vec<usize>>,
68}
69
70fn validate(alphabet_size: usize, subsets: &[Vec<usize>]) -> Result<(), String> {
71 if alphabet_size == 0 {
72 return Err("Alphabet size must be positive".to_string());
73 }
74
75 for (i, subset) in subsets.iter().enumerate() {
76 let mut seen = HashSet::new();
77 for &elem in subset {
78 if elem >= alphabet_size {
79 return Err(format!(
80 "Subset {} contains element {} which is outside alphabet of size {}",
81 i, elem, alphabet_size
82 ));
83 }
84 if !seen.insert(elem) {
85 return Err(format!("Subset {} contains duplicate element {}", i, elem));
86 }
87 }
88 }
89
90 Ok(())
91}
92
93impl<'de> Deserialize<'de> for TwoDimensionalConsecutiveSets {
94 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95 where
96 D: serde::Deserializer<'de>,
97 {
98 let unchecked = TwoDimensionalConsecutiveSetsUnchecked::deserialize(deserializer)?;
99 Self::try_new(unchecked.alphabet_size, unchecked.subsets).map_err(D::Error::custom)
100 }
101}
102
103impl TwoDimensionalConsecutiveSets {
104 pub fn try_new(alphabet_size: usize, subsets: Vec<Vec<usize>>) -> Result<Self, String> {
106 validate(alphabet_size, &subsets)?;
107 let subsets = subsets
108 .into_iter()
109 .map(|mut s| {
110 s.sort();
111 s
112 })
113 .collect();
114 Ok(Self {
115 alphabet_size,
116 subsets,
117 })
118 }
119
120 pub fn new(alphabet_size: usize, subsets: Vec<Vec<usize>>) -> Self {
127 Self::try_new(alphabet_size, subsets).unwrap_or_else(|message| panic!("{message}"))
128 }
129
130 pub fn alphabet_size(&self) -> usize {
132 self.alphabet_size
133 }
134
135 pub fn num_subsets(&self) -> usize {
137 self.subsets.len()
138 }
139
140 pub fn subsets(&self) -> &[Vec<usize>] {
142 &self.subsets
143 }
144}
145
146impl Problem for TwoDimensionalConsecutiveSets {
147 const NAME: &'static str = "TwoDimensionalConsecutiveSets";
148 type Value = crate::types::Or;
149
150 fn dims(&self) -> Vec<usize> {
151 vec![self.alphabet_size; self.alphabet_size]
152 }
153
154 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
155 crate::types::Or({
156 if config.len() != self.alphabet_size {
157 return crate::types::Or(false);
158 }
159 if config.iter().any(|&v| v >= self.alphabet_size) {
160 return crate::types::Or(false);
161 }
162
163 let mut used = vec![false; self.alphabet_size];
165 for &group in config {
166 used[group] = true;
167 }
168 let mut dense_labels = vec![0; self.alphabet_size];
169 let mut next_label = 0;
170 for (label, is_used) in used.into_iter().enumerate() {
171 if is_used {
172 dense_labels[label] = next_label;
173 next_label += 1;
174 }
175 }
176
177 for subset in &self.subsets {
178 if subset.is_empty() {
179 continue;
180 }
181 let groups: Vec<usize> = subset.iter().map(|&s| dense_labels[config[s]]).collect();
182
183 let unique: HashSet<usize> = groups.iter().copied().collect();
185 if unique.len() != subset.len() {
186 return crate::types::Or(false);
187 }
188
189 let min_g = *unique.iter().min().unwrap();
191 let max_g = *unique.iter().max().unwrap();
192 if max_g - min_g + 1 != subset.len() {
193 return crate::types::Or(false);
194 }
195 }
196
197 true
198 })
199 }
200
201 fn variant() -> Vec<(&'static str, &'static str)> {
202 crate::variant_params![]
203 }
204}
205
206crate::declare_variants! {
207 default TwoDimensionalConsecutiveSets => "alphabet_size^alphabet_size",
208}
209
210#[cfg(feature = "example-db")]
211pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
212 vec![crate::example_db::specs::ModelExampleSpec {
213 id: "two_dimensional_consecutive_sets",
214 instance: Box::new(TwoDimensionalConsecutiveSets::new(
215 6,
216 vec![
217 vec![0, 1, 2],
218 vec![3, 4, 5],
219 vec![1, 3],
220 vec![2, 4],
221 vec![0, 5],
222 ],
223 )),
224 optimal_config: vec![0, 1, 2, 2, 3, 1],
225 optimal_value: serde_json::json!(true),
226 }]
227}
228
229#[cfg(test)]
230#[path = "../../unit_tests/models/set/two_dimensional_consecutive_sets.rs"]
231mod tests;