problemreductions/models/set/
minimum_set_covering.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::traits::Problem;
8use crate::types::{Min, WeightElement};
9use num_traits::Zero;
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MinimumSetCovering",
16 display_name: "Minimum Set Covering",
17 aliases: &[],
18 dimensions: &[VariantDimension::new("weight", "i32", &["i32"])],
19 module_path: module_path!(),
20 description: "Find minimum weight collection covering the universe",
21 fields: &[
22 FieldInfo { name: "universe_size", type_name: "usize", description: "Size of the universe U" },
23 FieldInfo { name: "sets", type_name: "Vec<Vec<usize>>", description: "Collection of subsets of U" },
24 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Weight for each set" },
25 ],
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct MinimumSetCovering<W = i32> {
63 universe_size: usize,
65 sets: Vec<Vec<usize>>,
67 weights: Vec<W>,
69}
70
71impl<W: Clone + Default> MinimumSetCovering<W> {
72 pub fn new(universe_size: usize, sets: Vec<Vec<usize>>) -> Self
74 where
75 W: From<i32>,
76 {
77 let num_sets = sets.len();
78 let weights = vec![W::from(1); num_sets];
79 Self {
80 universe_size,
81 sets,
82 weights,
83 }
84 }
85
86 pub fn with_weights(universe_size: usize, sets: Vec<Vec<usize>>, weights: Vec<W>) -> Self {
88 assert_eq!(sets.len(), weights.len());
89 Self {
90 universe_size,
91 sets,
92 weights,
93 }
94 }
95
96 pub fn universe_size(&self) -> usize {
98 self.universe_size
99 }
100
101 pub fn num_sets(&self) -> usize {
103 self.sets.len()
104 }
105
106 pub fn sets(&self) -> &[Vec<usize>] {
108 &self.sets
109 }
110
111 pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
113 self.sets.get(index)
114 }
115
116 pub fn weights_ref(&self) -> &[W] {
118 &self.weights
119 }
120
121 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
123 let covered = self.covered_elements(config);
124 covered.len() == self.universe_size && (0..self.universe_size).all(|e| covered.contains(&e))
125 }
126
127 pub fn covered_elements(&self, config: &[usize]) -> HashSet<usize> {
129 let mut covered = HashSet::new();
130 for (i, &selected) in config.iter().enumerate() {
131 if selected == 1 {
132 if let Some(set) = self.sets.get(i) {
133 covered.extend(set.iter().copied());
134 }
135 }
136 }
137 covered
138 }
139}
140
141impl<W> Problem for MinimumSetCovering<W>
142where
143 W: WeightElement + crate::variant::VariantParam,
144{
145 const NAME: &'static str = "MinimumSetCovering";
146 type Value = Min<W::Sum>;
147
148 fn dims(&self) -> Vec<usize> {
149 vec![2; self.sets.len()]
150 }
151
152 fn evaluate(&self, config: &[usize]) -> Min<W::Sum> {
153 let covered = self.covered_elements(config);
154 let is_valid = covered.len() == self.universe_size
155 && (0..self.universe_size).all(|e| covered.contains(&e));
156 if !is_valid {
157 return Min(None);
158 }
159 let mut total = W::Sum::zero();
160 for (i, &selected) in config.iter().enumerate() {
161 if selected == 1 {
162 total += self.weights[i].to_sum();
163 }
164 }
165 Min(Some(total))
166 }
167
168 fn variant() -> Vec<(&'static str, &'static str)> {
169 crate::variant_params![W]
170 }
171}
172
173crate::declare_variants! {
174 default MinimumSetCovering<i32> => "2^num_sets",
175}
176
177#[cfg(test)]
179pub(crate) fn is_set_cover(universe_size: usize, sets: &[Vec<usize>], selected: &[bool]) -> bool {
180 if selected.len() != sets.len() {
181 return false;
182 }
183
184 let mut covered = HashSet::new();
185 for (i, &sel) in selected.iter().enumerate() {
186 if sel {
187 covered.extend(sets[i].iter().copied());
188 }
189 }
190
191 (0..universe_size).all(|e| covered.contains(&e))
192}
193
194#[cfg(feature = "example-db")]
195pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
196 vec![crate::example_db::specs::ModelExampleSpec {
197 id: "minimum_set_covering_i32",
198 instance: Box::new(MinimumSetCovering::<i32>::new(
199 5,
200 vec![vec![0, 1, 2], vec![1, 3], vec![2, 3, 4]],
201 )),
202 optimal_config: vec![1, 0, 1],
203 optimal_value: serde_json::json!(2),
204 }]
205}
206
207#[cfg(test)]
208#[path = "../../unit_tests/models/set/minimum_set_covering.rs"]
209mod tests;