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 num_sets(&self) -> usize {
114 self.num_subsets()
115 }
116
117 pub fn subsets(&self) -> &[[usize; 3]] {
119 &self.subsets
120 }
121
122 pub fn sets(&self) -> &[[usize; 3]] {
124 self.subsets()
125 }
126
127 pub fn get_subset(&self, index: usize) -> Option<&[usize; 3]> {
129 self.subsets.get(index)
130 }
131
132 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
137 self.evaluate(config).0
138 }
139
140 pub fn covered_elements(&self, config: &[usize]) -> HashSet<usize> {
142 let mut covered = HashSet::new();
143 for (i, &selected) in config.iter().enumerate() {
144 if selected == 1 {
145 if let Some(subset) = self.subsets.get(i) {
146 covered.extend(subset.iter().copied());
147 }
148 }
149 }
150 covered
151 }
152}
153
154impl Problem for ExactCoverBy3Sets {
155 const NAME: &'static str = "ExactCoverBy3Sets";
156 type Value = crate::types::Or;
157
158 fn dims(&self) -> Vec<usize> {
159 vec![2; self.subsets.len()]
160 }
161
162 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
163 crate::types::Or({
164 if config.len() != self.subsets.len() || config.iter().any(|&value| value > 1) {
165 return crate::types::Or(false);
166 }
167
168 let q = self.universe_size / 3;
169
170 let selected_count: usize = config.iter().filter(|&&v| v == 1).sum();
172 if selected_count != q {
173 return crate::types::Or(false);
174 }
175
176 let mut covered = HashSet::with_capacity(self.universe_size);
178 for (i, &selected) in config.iter().enumerate() {
179 if selected == 1 {
180 if let Some(subset) = self.subsets.get(i) {
181 for &elem in subset {
182 if !covered.insert(elem) {
183 return crate::types::Or(false);
185 }
186 }
187 }
188 }
189 }
190
191 covered.len() == self.universe_size
193 })
194 }
195
196 fn variant() -> Vec<(&'static str, &'static str)> {
197 crate::variant_params![]
198 }
199}
200
201crate::declare_variants! {
202 default ExactCoverBy3Sets => "2^universe_size",
203}
204
205#[cfg(feature = "example-db")]
206pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
207 vec![crate::example_db::specs::ModelExampleSpec {
208 id: "exact_cover_by_3_sets",
209 instance: Box::new(ExactCoverBy3Sets::new(
210 9,
211 vec![
212 [0, 1, 2],
213 [0, 2, 4],
214 [3, 4, 5],
215 [3, 5, 7],
216 [6, 7, 8],
217 [1, 4, 6],
218 [2, 5, 8],
219 ],
220 )),
221 optimal_config: vec![1, 0, 1, 0, 1, 0, 0],
222 optimal_value: serde_json::json!(true),
223 }]
224}
225
226#[cfg(test)]
227#[path = "../../unit_tests/models/set/exact_cover_by_3_sets.rs"]
228mod tests;