problemreductions/models/set/
exact_cover_by_3_sets.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10use std::collections::HashSet;
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "ExactCoverBy3Sets",
15 display_name: "Exact Cover by 3-Sets",
16 aliases: &["X3C"],
17 dimensions: &[],
18 module_path: module_path!(),
19 description: "Determine if a collection of 3-element subsets contains an exact cover",
20 fields: &[
21 FieldInfo { name: "universe_size", type_name: "usize", description: "Size of universe X (must be divisible by 3)" },
22 FieldInfo { name: "subsets", type_name: "Vec<[usize; 3]>", description: "Collection C of 3-element subsets of X" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ExactCoverBy3Sets {
58 universe_size: usize,
60 subsets: Vec<[usize; 3]>,
62}
63
64impl ExactCoverBy3Sets {
65 pub fn new(universe_size: usize, subsets: Vec<[usize; 3]>) -> Self {
72 assert!(
73 universe_size.is_multiple_of(3),
74 "Universe size must be divisible by 3, got {}",
75 universe_size
76 );
77 let mut subsets = subsets;
78 for (i, subset) in subsets.iter_mut().enumerate() {
79 assert!(
80 subset[0] != subset[1] && subset[0] != subset[2] && subset[1] != subset[2],
81 "Subset {} contains duplicate elements: {:?}",
82 i,
83 subset
84 );
85 for &elem in subset.iter() {
86 assert!(
87 elem < universe_size,
88 "Subset {} contains element {} which is outside universe of size {}",
89 i,
90 elem,
91 universe_size
92 );
93 }
94 subset.sort();
95 }
96 Self {
97 universe_size,
98 subsets,
99 }
100 }
101
102 pub fn universe_size(&self) -> usize {
104 self.universe_size
105 }
106
107 pub fn num_subsets(&self) -> usize {
109 self.subsets.len()
110 }
111
112 pub fn q(&self) -> usize {
117 self.universe_size / 3
118 }
119
120 pub fn num_sets(&self) -> usize {
122 self.num_subsets()
123 }
124
125 pub fn subsets(&self) -> &[[usize; 3]] {
127 &self.subsets
128 }
129
130 pub fn sets(&self) -> &[[usize; 3]] {
132 self.subsets()
133 }
134
135 pub fn get_subset(&self, index: usize) -> Option<&[usize; 3]> {
137 self.subsets.get(index)
138 }
139
140 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
145 self.evaluate(config).0
146 }
147
148 pub fn covered_elements(&self, config: &[usize]) -> HashSet<usize> {
150 let mut covered = HashSet::new();
151 for (i, &selected) in config.iter().enumerate() {
152 if selected == 1 {
153 if let Some(subset) = self.subsets.get(i) {
154 covered.extend(subset.iter().copied());
155 }
156 }
157 }
158 covered
159 }
160}
161
162impl Problem for ExactCoverBy3Sets {
163 const NAME: &'static str = "ExactCoverBy3Sets";
164 type Value = crate::types::Or;
165
166 fn dims(&self) -> Vec<usize> {
167 vec![2; self.subsets.len()]
168 }
169
170 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
171 crate::types::Or({
172 if config.len() != self.subsets.len() || config.iter().any(|&value| value > 1) {
173 return crate::types::Or(false);
174 }
175
176 let q = self.universe_size / 3;
177
178 let selected_count: usize = config.iter().filter(|&&v| v == 1).sum();
180 if selected_count != q {
181 return crate::types::Or(false);
182 }
183
184 let mut covered = HashSet::with_capacity(self.universe_size);
186 for (i, &selected) in config.iter().enumerate() {
187 if selected == 1 {
188 if let Some(subset) = self.subsets.get(i) {
189 for &elem in subset {
190 if !covered.insert(elem) {
191 return crate::types::Or(false);
193 }
194 }
195 }
196 }
197 }
198
199 covered.len() == self.universe_size
201 })
202 }
203
204 fn variant() -> Vec<(&'static str, &'static str)> {
205 crate::variant_params![]
206 }
207}
208
209crate::declare_variants! {
210 default ExactCoverBy3Sets => "2^universe_size",
211}
212
213#[cfg(feature = "example-db")]
214pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
215 vec![crate::example_db::specs::ModelExampleSpec {
216 id: "exact_cover_by_3_sets",
217 instance: Box::new(ExactCoverBy3Sets::new(
218 9,
219 vec![
220 [0, 1, 2],
221 [0, 2, 4],
222 [3, 4, 5],
223 [3, 5, 7],
224 [6, 7, 8],
225 [1, 4, 6],
226 [2, 5, 8],
227 ],
228 )),
229 optimal_config: vec![1, 0, 1, 0, 1, 0, 0],
230 optimal_value: serde_json::json!(true),
231 }]
232}
233
234#[cfg(test)]
235#[path = "../../unit_tests/models/set/exact_cover_by_3_sets.rs"]
236mod tests;