problemreductions/models/set/
set_basis.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::{Problem, SatisfactionProblem};
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "SetBasis",
14 display_name: "Set Basis",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Determine whether a collection of sets admits a basis of size k under union",
19 fields: &[
20 FieldInfo { name: "universe_size", type_name: "usize", description: "Size of the ground set S" },
21 FieldInfo { name: "collection", type_name: "Vec<Vec<usize>>", description: "Collection C of target subsets of S" },
22 FieldInfo { name: "k", type_name: "usize", description: "Required number of basis sets" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SetBasis {
35 universe_size: usize,
37 collection: Vec<Vec<usize>>,
39 k: usize,
41}
42
43impl SetBasis {
44 pub fn new(universe_size: usize, collection: Vec<Vec<usize>>, k: usize) -> Self {
50 let mut collection = collection;
51 for (set_index, set) in collection.iter_mut().enumerate() {
52 set.sort_unstable();
53 set.dedup();
54 for &element in set.iter() {
55 assert!(
56 element < universe_size,
57 "Set {} contains element {} which is outside universe of size {}",
58 set_index,
59 element,
60 universe_size
61 );
62 }
63 }
64
65 Self {
66 universe_size,
67 collection,
68 k,
69 }
70 }
71
72 pub fn universe_size(&self) -> usize {
74 self.universe_size
75 }
76
77 pub fn num_sets(&self) -> usize {
79 self.collection.len()
80 }
81
82 pub fn basis_size(&self) -> usize {
84 self.k
85 }
86
87 pub fn collection(&self) -> &[Vec<usize>] {
89 &self.collection
90 }
91
92 pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
94 self.collection.get(index)
95 }
96
97 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
99 self.evaluate(config)
100 }
101
102 fn decode_basis(&self, config: &[usize]) -> Option<Vec<Vec<usize>>> {
103 let expected_len = self.k * self.universe_size;
104 if config.len() != expected_len || config.iter().any(|&value| value > 1) {
105 return None;
106 }
107
108 let mut basis = Vec::with_capacity(self.k);
109 for row in 0..self.k {
110 let mut subset = Vec::new();
111 let start = row * self.universe_size;
112 for element in 0..self.universe_size {
113 if config[start + element] == 1 {
114 subset.push(element);
115 }
116 }
117 basis.push(subset);
118 }
119 Some(basis)
120 }
121
122 fn is_subset(candidate: &[usize], target_membership: &[bool]) -> bool {
123 candidate.iter().all(|&element| target_membership[element])
124 }
125
126 fn can_represent_target(basis: &[Vec<usize>], target: &[usize], universe_size: usize) -> bool {
127 let mut target_membership = vec![false; universe_size];
128 for &element in target {
129 if element >= universe_size {
130 return false;
131 }
132 target_membership[element] = true;
133 }
134
135 let mut covered = vec![false; universe_size];
136 for subset in basis {
137 if Self::is_subset(subset, &target_membership) {
138 for &element in subset {
139 covered[element] = true;
140 }
141 }
142 }
143
144 target.iter().all(|&element| covered[element])
145 }
146}
147
148impl Problem for SetBasis {
149 const NAME: &'static str = "SetBasis";
150 type Metric = bool;
151
152 fn dims(&self) -> Vec<usize> {
153 vec![2; self.k * self.universe_size]
154 }
155
156 fn evaluate(&self, config: &[usize]) -> bool {
157 let Some(basis) = self.decode_basis(config) else {
158 return false;
159 };
160
161 self.collection
162 .iter()
163 .all(|target| Self::can_represent_target(&basis, target, self.universe_size))
164 }
165
166 fn variant() -> Vec<(&'static str, &'static str)> {
167 crate::variant_params![]
168 }
169}
170
171impl SatisfactionProblem for SetBasis {}
172
173crate::declare_variants! {
174 default sat SetBasis => "2^(basis_size * universe_size)",
175}
176
177#[cfg(feature = "example-db")]
178pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
179 vec![crate::example_db::specs::ModelExampleSpec {
180 id: "set_basis",
181 instance: Box::new(SetBasis::new(
182 4,
183 vec![vec![0, 1], vec![1, 2], vec![0, 2], vec![0, 1, 2]],
184 3,
185 )),
186 optimal_config: vec![0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
187 optimal_value: serde_json::json!(true),
188 }]
189}
190
191#[cfg(test)]
192#[path = "../../unit_tests/models/set/set_basis.rs"]
193mod tests;