problemreductions/models/set/
set_basis.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "SetBasis",
14 display_name: "Set Basis",
15 aliases: &[],
16 dimensions: &[],
17 category: crate::registry::ProblemCategory::Set,
18 module_path: module_path!(),
19 description: "Determine whether a collection of sets admits a basis of size k under union",
20 fields: SetBasisCreateSpec::FIELDS,
21 }
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(try_from = "SetBasisData")]
32pub struct SetBasis {
33 universe_size: usize,
35 collection: Vec<Vec<usize>>,
37 k: usize,
39}
40
41#[derive(Deserialize)]
42struct SetBasisData {
43 universe_size: usize,
44 collection: Vec<Vec<usize>>,
45 k: usize,
46}
47
48impl TryFrom<SetBasisData> for SetBasis {
49 type Error = crate::registry::ConstructionError;
50
51 fn try_from(data: SetBasisData) -> Result<Self, Self::Error> {
52 Self::try_new(data.universe_size, data.collection, data.k)
53 }
54}
55
56#[derive(Debug, Deserialize, crate::CreateSpec)]
57struct SetBasisCreateSpec {
58 universe_size: usize,
60 subsets: Vec<Vec<usize>>,
62 k: usize,
64}
65
66impl TryFrom<SetBasisCreateSpec> for SetBasis {
67 type Error = crate::registry::ConstructionError;
68
69 fn try_from(spec: SetBasisCreateSpec) -> Result<Self, Self::Error> {
70 Self::try_new(spec.universe_size, spec.subsets, spec.k)
71 }
72}
73
74impl SetBasis {
75 pub fn new(universe_size: usize, collection: Vec<Vec<usize>>, k: usize) -> Self {
81 Self::try_new(universe_size, collection, k).unwrap_or_else(|error| panic!("{error}"))
82 }
83
84 fn try_new(
85 universe_size: usize,
86 mut collection: Vec<Vec<usize>>,
87 k: usize,
88 ) -> Result<Self, crate::registry::ConstructionError> {
89 for (index, set) in collection.iter_mut().enumerate() {
90 set.sort_unstable();
91 set.dedup();
92 if let Some(element) = set.iter().find(|&&element| element >= universe_size) {
93 return Err(format!("set {index} contains element {element} outside universe of size {universe_size}").into());
94 }
95 }
96 Ok(Self {
97 universe_size,
98 collection,
99 k,
100 })
101 }
102
103 pub fn universe_size(&self) -> usize {
105 self.universe_size
106 }
107
108 pub fn num_sets(&self) -> usize {
110 self.collection.len()
111 }
112
113 pub fn basis_size(&self) -> usize {
115 self.k
116 }
117
118 pub fn collection(&self) -> &[Vec<usize>] {
120 &self.collection
121 }
122
123 pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
125 self.collection.get(index)
126 }
127
128 pub fn is_valid_solution(
130 &self,
131 solution: &[Vec<bool>],
132 ) -> Result<bool, crate::traits::EvaluationError> {
133 if solution.len() != self.k
134 || solution
135 .iter()
136 .any(|subset| subset.len() != self.universe_size)
137 {
138 return Err(crate::traits::EvaluationError::InvalidConfiguration(
139 "set-basis dimensions do not match the instance".into(),
140 ));
141 }
142 let basis = Self::decode_basis(solution);
143 Ok(self
144 .collection
145 .iter()
146 .all(|target| Self::can_represent_target(&basis, target, self.universe_size)))
147 }
148
149 fn decode_basis(solution: &[Vec<bool>]) -> Vec<Vec<usize>> {
150 solution
151 .iter()
152 .map(|row| {
153 row.iter()
154 .enumerate()
155 .filter_map(|(element, &selected)| selected.then_some(element))
156 .collect()
157 })
158 .collect()
159 }
160
161 fn is_subset(candidate: &[usize], target_membership: &[bool]) -> bool {
162 candidate.iter().all(|&element| target_membership[element])
163 }
164
165 fn can_represent_target(basis: &[Vec<usize>], target: &[usize], universe_size: usize) -> bool {
166 let mut target_membership = vec![false; universe_size];
167 for &element in target {
168 target_membership[element] = true;
169 }
170
171 let mut covered = vec![false; universe_size];
172 for subset in basis {
173 if Self::is_subset(subset, &target_membership) {
174 for &element in subset {
175 covered[element] = true;
176 }
177 }
178 }
179
180 target.iter().all(|&element| covered[element])
181 }
182}
183
184impl Problem for SetBasis {
185 const NAME: &'static str = "SetBasis";
186 type Solution = Vec<Vec<bool>>;
187 type Value = crate::types::Or;
188
189 crate::problem_parameters![
190 ("universe_size", universe_size),
191 ("num_sets", num_sets),
192 ("basis_size", basis_size),
193 ];
194
195 fn evaluate(
196 &self,
197 solution: &Self::Solution,
198 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
199 Ok(crate::types::Or(self.is_valid_solution(solution)?))
200 }
201
202 fn variant() -> Vec<(&'static str, &'static str)> {
203 crate::variant_params![]
204 }
205}
206
207impl crate::solvers::BruteForceProblem for SetBasis {
208 fn dimensions(&self) -> Vec<usize> {
209 vec![2; self.k * self.universe_size]
210 }
211}
212
213crate::declare_variants! {
214 default SetBasis => "2^(basis_size * universe_size)" create SetBasisCreateSpec,
215}
216
217crate::register_brute_force! {
218 SetBasis decode |problem: &SetBasis, indices: Vec<usize>| if problem.universe_size() == 0 { vec![Vec::new(); problem.basis_size()] } else { indices.chunks(problem.universe_size()).map(crate::config::config_to_bits).collect() },
219}
220
221#[cfg(feature = "example-db")]
222pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
223 vec![crate::example_db::specs::ModelExampleSpec {
224 id: "set_basis",
225 instance: Box::new(SetBasis::new(
226 4,
227 vec![vec![0, 1], vec![1, 2], vec![0, 2], vec![0, 1, 2]],
228 3,
229 )),
230 optimal_config: serde_json::json!(vec![
231 vec![false, false, true, false],
232 vec![false, true, false, false],
233 vec![true, false, false, false]
234 ]),
235 optimal_value: serde_json::json!(true),
236 }]
237}
238
239#[cfg(test)]
240#[path = "../../unit_tests/models/set/set_basis.rs"]
241mod tests;