Skip to main content

problemreductions/models/set/
minimum_set_covering.rs

1//! Set Covering problem implementation.
2//!
3//! The Set Covering problem asks for a minimum weight collection of sets
4//! that covers all elements in the universe.
5
6use 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/// The Set Covering problem.
30///
31/// Given a universe U of elements and a collection S of subsets of U,
32/// each with a weight, find a minimum weight subcollection of S
33/// that covers all elements in U.
34///
35/// # Example
36///
37/// ```
38/// use problemreductions::models::set::MinimumSetCovering;
39/// use problemreductions::{Problem, Solver, BruteForce};
40///
41/// // Universe: {0, 1, 2, 3}
42/// // Sets: S0={0,1}, S1={1,2}, S2={2,3}, S3={0,3}
43/// let problem = MinimumSetCovering::<i32>::new(
44///     4, // universe size
45///     vec![
46///         vec![0, 1],
47///         vec![1, 2],
48///         vec![2, 3],
49///         vec![0, 3],
50///     ],
51/// );
52///
53/// let solver = BruteForce::new();
54/// let solutions = solver.find_all_witnesses(&problem);
55///
56/// // Verify solutions cover all elements
57/// for sol in solutions {
58///     assert!(problem.evaluate(&sol).is_valid());
59/// }
60/// ```
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct MinimumSetCovering<W = i32> {
63    /// Size of the universe (elements are 0..universe_size).
64    universe_size: usize,
65    /// Collection of sets, each represented as a vector of elements.
66    sets: Vec<Vec<usize>>,
67    /// Weights for each set.
68    weights: Vec<W>,
69}
70
71impl<W: Clone + Default> MinimumSetCovering<W> {
72    /// Create a new Set Covering problem with unit weights.
73    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    /// Create a new Set Covering problem with custom weights.
87    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    /// Get the universe size.
97    pub fn universe_size(&self) -> usize {
98        self.universe_size
99    }
100
101    /// Get the number of sets.
102    pub fn num_sets(&self) -> usize {
103        self.sets.len()
104    }
105
106    /// Get the sets.
107    pub fn sets(&self) -> &[Vec<usize>] {
108        &self.sets
109    }
110
111    /// Get a specific set.
112    pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
113        self.sets.get(index)
114    }
115
116    /// Get a reference to the weights.
117    pub fn weights_ref(&self) -> &[W] {
118        &self.weights
119    }
120
121    /// Check if a configuration is a valid set cover.
122    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    /// Check which elements are covered by selected sets.
128    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/// Check if a selection of sets forms a valid set cover.
178#[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;