problemreductions/models/set/
maximum_set_packing.rs1use crate::registry::{ConstructionError, CreateSpec, ProblemSchemaEntry, VariantDimension};
7use crate::traits::Problem;
8use crate::types::{Max, One, WeightElement};
9use num_traits::Zero;
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MaximumSetPacking",
16 display_name: "Maximum Set Packing",
17 aliases: &[],
18 dimensions: &[VariantDimension::new("weight", "One", &["One", "i64", "f64"])],
19 category: crate::registry::ProblemCategory::Set,
20 module_path: module_path!(),
21 description: "Find maximum weight collection of disjoint sets",
22 fields: MaximumSetPackingCreateSpec::<One>::FIELDS,
23 }
24}
25
26#[derive(Debug, Clone, Serialize)]
55pub struct MaximumSetPacking<W = i64> {
56 sets: Vec<Vec<usize>>,
58 weights: Vec<W>,
60}
61
62#[derive(Deserialize)]
63struct MaximumSetPackingData<W> {
64 sets: Vec<Vec<usize>>,
65 weights: Vec<W>,
66}
67
68impl<'de, W> Deserialize<'de> for MaximumSetPacking<W>
69where
70 W: WeightElement + Deserialize<'de>,
71{
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: serde::Deserializer<'de>,
75 {
76 let data = MaximumSetPackingData::deserialize(deserializer)?;
77 Self::with_weights(data.sets, data.weights).map_err(serde::de::Error::custom)
78 }
79}
80
81#[derive(Debug, Deserialize, crate::CreateSpec)]
82struct MaximumSetPackingCreateSpec<W> {
83 subsets: Vec<Vec<usize>>,
85 weights: Vec<W>,
87}
88
89impl<W: WeightElement> TryFrom<MaximumSetPackingCreateSpec<W>> for MaximumSetPacking<W> {
90 type Error = ConstructionError;
91
92 fn try_from(spec: MaximumSetPackingCreateSpec<W>) -> Result<Self, Self::Error> {
93 Self::with_weights(spec.subsets, spec.weights)
94 }
95}
96
97impl<W: Clone + Default> MaximumSetPacking<W> {
98 pub fn new(sets: Vec<Vec<usize>>) -> Self
100 where
101 W: WeightElement,
102 {
103 let num_sets = sets.len();
104 let weights = vec![W::unit(); num_sets];
105 Self { sets, weights }
106 }
107
108 pub fn with_weights(sets: Vec<Vec<usize>>, weights: Vec<W>) -> Result<Self, ConstructionError>
110 where
111 W: WeightElement,
112 {
113 if sets.len() != weights.len() {
114 return Err(ConstructionError::Conversion(
115 "weights length must match number of sets".into(),
116 ));
117 }
118 for (index, weight) in weights.iter().enumerate() {
119 weight.validate_element(&format!("set weight at index {index}"))?;
120 }
121 Ok(Self { sets, weights })
122 }
123
124 pub fn num_sets(&self) -> usize {
126 self.sets.len()
127 }
128
129 pub fn sets(&self) -> &[Vec<usize>] {
131 &self.sets
132 }
133
134 pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
136 self.sets.get(index)
137 }
138
139 pub fn sets_overlap(&self, i: usize, j: usize) -> bool {
141 if let (Some(set_i), Some(set_j)) = (self.sets.get(i), self.sets.get(j)) {
142 let set_i: HashSet<_> = set_i.iter().collect();
143 set_j.iter().any(|e| set_i.contains(e))
144 } else {
145 false
146 }
147 }
148
149 pub fn overlapping_pairs(&self) -> Vec<(usize, usize)> {
151 let mut pairs = Vec::new();
152 for i in 0..self.sets.len() {
153 for j in (i + 1)..self.sets.len() {
154 if self.sets_overlap(i, j) {
155 pairs.push((i, j));
156 }
157 }
158 }
159 pairs
160 }
161
162 pub fn universe_size(&self) -> usize {
164 self.sets()
165 .iter()
166 .flat_map(|s| s.iter())
167 .max()
168 .map_or(0, |&m| m + 1)
169 }
170
171 pub fn weights_ref(&self) -> &Vec<W> {
173 &self.weights
174 }
175
176 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
178 is_valid_packing(&self.sets, config)
179 }
180}
181
182impl<W> Problem for MaximumSetPacking<W>
183where
184 W: WeightElement + crate::variant::VariantParam,
185{
186 const NAME: &'static str = "MaximumSetPacking";
187 type Solution = Vec<bool>;
188 type Value = Max<W::Sum>;
189
190 crate::problem_parameters![("num_sets", num_sets), ("universe_size", universe_size),];
191
192 fn evaluate(
193 &self,
194 config: &Self::Solution,
195 ) -> Result<Max<W::Sum>, crate::traits::EvaluationError> {
196 if config.len() != self.sets.len() {
197 return Err(crate::traits::EvaluationError::InvalidConfiguration(
198 "set-selection length does not match the family".into(),
199 ));
200 }
201 Ok({
202 if !is_valid_packing(&self.sets, config) {
203 return Ok(Max(None));
204 }
205 let mut total = W::Sum::zero();
206 for (i, &selected) in config.iter().enumerate() {
207 if selected {
208 total = W::checked_add_to_sum(
209 total,
210 self.weights[i].to_sum(),
211 "summing selected set-packing weights",
212 )?;
213 }
214 }
215 Max(Some(total))
216 })
217 }
218
219 fn variant() -> Vec<(&'static str, &'static str)> {
220 crate::variant_params![W]
221 }
222}
223
224impl<W> crate::solvers::BruteForceProblem for MaximumSetPacking<W>
225where
226 W: WeightElement + crate::variant::VariantParam,
227{
228 fn dimensions(&self) -> Vec<usize> {
229 vec![2; self.sets.len()]
230 }
231}
232
233#[derive(Debug, Deserialize, crate::CreateSpec)]
234struct MaximumSetPackingOneCreateSpec {
235 subsets: Vec<Vec<usize>>,
237}
238impl TryFrom<MaximumSetPackingOneCreateSpec> for MaximumSetPacking<One> {
239 type Error = ConstructionError;
240 fn try_from(spec: MaximumSetPackingOneCreateSpec) -> Result<Self, Self::Error> {
241 let weights = vec![One; spec.subsets.len()];
242 Self::with_weights(spec.subsets, weights)
243 }
244}
245
246crate::declare_variants! {
247 default MaximumSetPacking<One> => "2^num_sets" create MaximumSetPackingOneCreateSpec,
248 MaximumSetPacking<i64> => "2^num_sets" create MaximumSetPackingCreateSpec<i64>,
249 MaximumSetPacking<f64> => "2^num_sets" create MaximumSetPackingCreateSpec<f64>,
250}
251
252crate::register_brute_force! {
253 MaximumSetPacking<One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
254 MaximumSetPacking<i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
255 MaximumSetPacking<f64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
256}
257
258fn is_valid_packing(sets: &[Vec<usize>], config: &[bool]) -> bool {
260 let selected_sets: Vec<_> = config
261 .iter()
262 .enumerate()
263 .filter(|(_, &selected)| selected)
264 .map(|(i, _)| i)
265 .collect();
266
267 for i in 0..selected_sets.len() {
269 for j in (i + 1)..selected_sets.len() {
270 let set_i: HashSet<_> = sets[selected_sets[i]].iter().collect();
271 if sets[selected_sets[j]].iter().any(|e| set_i.contains(e)) {
272 return false;
273 }
274 }
275 }
276 true
277}
278
279#[cfg(test)]
281pub(crate) fn is_set_packing(sets: &[Vec<usize>], selected: &[bool]) -> bool {
282 if selected.len() != sets.len() {
283 return false;
284 }
285
286 is_valid_packing(sets, selected)
287}
288
289#[cfg(feature = "example-db")]
290pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
291 vec![crate::example_db::specs::ModelExampleSpec {
292 id: "maximum_set_packing",
293 instance: Box::new(MaximumSetPacking::<i64>::new(vec![
294 vec![0, 1],
295 vec![1, 2],
296 vec![2, 3],
297 vec![3, 4],
298 ])),
299 optimal_config: serde_json::json!(vec![false, true, false, true]),
300 optimal_value: serde_json::json!(2),
301 }]
302}
303
304#[cfg(test)]
305#[path = "../../unit_tests/models/set/maximum_set_packing.rs"]
306mod tests;