problemreductions/models/set/
maximum_set_packing.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::{OptimizationProblem, Problem};
8use crate::types::{Direction, One, SolutionSize, WeightElement};
9use num_traits::Zero;
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MaximumSetPacking",
16 module_path: module_path!(),
17 description: "Find maximum weight collection of disjoint sets",
18 fields: &[
19 FieldInfo { name: "sets", type_name: "Vec<Vec<usize>>", description: "Collection of sets over a universe" },
20 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Weight for each set" },
21 ],
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct MaximumSetPacking<W = i32> {
55 sets: Vec<Vec<usize>>,
57 weights: Vec<W>,
59}
60
61impl<W: Clone + Default> MaximumSetPacking<W> {
62 pub fn new(sets: Vec<Vec<usize>>) -> Self
64 where
65 W: From<i32>,
66 {
67 let num_sets = sets.len();
68 let weights = vec![W::from(1); num_sets];
69 Self { sets, weights }
70 }
71
72 pub fn with_weights(sets: Vec<Vec<usize>>, weights: Vec<W>) -> Self {
74 assert_eq!(sets.len(), weights.len());
75 Self { sets, weights }
76 }
77
78 pub fn num_sets(&self) -> usize {
80 self.sets.len()
81 }
82
83 pub fn sets(&self) -> &[Vec<usize>] {
85 &self.sets
86 }
87
88 pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
90 self.sets.get(index)
91 }
92
93 pub fn sets_overlap(&self, i: usize, j: usize) -> bool {
95 if let (Some(set_i), Some(set_j)) = (self.sets.get(i), self.sets.get(j)) {
96 let set_i: HashSet<_> = set_i.iter().collect();
97 set_j.iter().any(|e| set_i.contains(e))
98 } else {
99 false
100 }
101 }
102
103 pub fn overlapping_pairs(&self) -> Vec<(usize, usize)> {
105 let mut pairs = Vec::new();
106 for i in 0..self.sets.len() {
107 for j in (i + 1)..self.sets.len() {
108 if self.sets_overlap(i, j) {
109 pairs.push((i, j));
110 }
111 }
112 }
113 pairs
114 }
115
116 pub fn universe_size(&self) -> usize {
118 self.sets()
119 .iter()
120 .flat_map(|s| s.iter())
121 .max()
122 .map_or(0, |&m| m + 1)
123 }
124
125 pub fn weights_ref(&self) -> &Vec<W> {
127 &self.weights
128 }
129
130 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
132 is_valid_packing(&self.sets, config)
133 }
134}
135
136impl<W> Problem for MaximumSetPacking<W>
137where
138 W: WeightElement + crate::variant::VariantParam,
139{
140 const NAME: &'static str = "MaximumSetPacking";
141 type Metric = SolutionSize<W::Sum>;
142
143 fn dims(&self) -> Vec<usize> {
144 vec![2; self.sets.len()]
145 }
146
147 fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
148 if !is_valid_packing(&self.sets, config) {
149 return SolutionSize::Invalid;
150 }
151 let mut total = W::Sum::zero();
152 for (i, &selected) in config.iter().enumerate() {
153 if selected == 1 {
154 total += self.weights[i].to_sum();
155 }
156 }
157 SolutionSize::Valid(total)
158 }
159
160 fn variant() -> Vec<(&'static str, &'static str)> {
161 crate::variant_params![W]
162 }
163}
164
165impl<W> OptimizationProblem for MaximumSetPacking<W>
166where
167 W: WeightElement + crate::variant::VariantParam,
168{
169 type Value = W::Sum;
170
171 fn direction(&self) -> Direction {
172 Direction::Maximize
173 }
174}
175
176crate::declare_variants! {
177 MaximumSetPacking<One> => "2^num_sets",
178 MaximumSetPacking<i32> => "2^num_sets",
179 MaximumSetPacking<f64> => "2^num_sets",
180}
181
182fn is_valid_packing(sets: &[Vec<usize>], config: &[usize]) -> bool {
184 let selected_sets: Vec<_> = config
185 .iter()
186 .enumerate()
187 .filter(|(_, &s)| s == 1)
188 .map(|(i, _)| i)
189 .collect();
190
191 for i in 0..selected_sets.len() {
193 for j in (i + 1)..selected_sets.len() {
194 let set_i: HashSet<_> = sets[selected_sets[i]].iter().collect();
195 if sets[selected_sets[j]].iter().any(|e| set_i.contains(e)) {
196 return false;
197 }
198 }
199 }
200 true
201}
202
203#[cfg(test)]
205pub(crate) fn is_set_packing(sets: &[Vec<usize>], selected: &[bool]) -> bool {
206 if selected.len() != sets.len() {
207 return false;
208 }
209
210 let config: Vec<usize> = selected.iter().map(|&b| if b { 1 } else { 0 }).collect();
211 is_valid_packing(sets, &config)
212}
213
214#[cfg(test)]
215#[path = "../../unit_tests/models/set/maximum_set_packing.rs"]
216mod tests;