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