Skip to main content

problemreductions/models/set/
set_basis.rs

1//! Set Basis problem implementation.
2//!
3//! Given a collection of sets over a finite universe and an integer `k`,
4//! determine whether there exist `k` basis sets such that every target set
5//! can be reconstructed as a union of some subcollection of the basis.
6
7use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::{Problem, SatisfactionProblem};
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12    ProblemSchemaEntry {
13        name: "SetBasis",
14        display_name: "Set Basis",
15        aliases: &[],
16        dimensions: &[],
17        module_path: module_path!(),
18        description: "Determine whether a collection of sets admits a basis of size k under union",
19        fields: &[
20            FieldInfo { name: "universe_size", type_name: "usize", description: "Size of the ground set S" },
21            FieldInfo { name: "collection", type_name: "Vec<Vec<usize>>", description: "Collection C of target subsets of S" },
22            FieldInfo { name: "k", type_name: "usize", description: "Required number of basis sets" },
23        ],
24    }
25}
26
27/// The Set Basis decision problem.
28///
29/// Given a collection `C` of subsets of a finite set `S` and an integer `k`,
30/// determine whether there exists a collection `B` of exactly `k` subsets of
31/// `S` such that every set in `C` can be expressed as the union of some
32/// subcollection of `B`.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SetBasis {
35    /// Size of the universe (elements are `0..universe_size`).
36    universe_size: usize,
37    /// Collection of target sets.
38    collection: Vec<Vec<usize>>,
39    /// Number of basis sets to encode in a configuration.
40    k: usize,
41}
42
43impl SetBasis {
44    /// Create a new Set Basis instance.
45    ///
46    /// # Panics
47    ///
48    /// Panics if any element in `collection` lies outside the universe.
49    pub fn new(universe_size: usize, collection: Vec<Vec<usize>>, k: usize) -> Self {
50        let mut collection = collection;
51        for (set_index, set) in collection.iter_mut().enumerate() {
52            set.sort_unstable();
53            set.dedup();
54            for &element in set.iter() {
55                assert!(
56                    element < universe_size,
57                    "Set {} contains element {} which is outside universe of size {}",
58                    set_index,
59                    element,
60                    universe_size
61                );
62            }
63        }
64
65        Self {
66            universe_size,
67            collection,
68            k,
69        }
70    }
71
72    /// Return the universe size.
73    pub fn universe_size(&self) -> usize {
74        self.universe_size
75    }
76
77    /// Return the number of target sets.
78    pub fn num_sets(&self) -> usize {
79        self.collection.len()
80    }
81
82    /// Return the required basis size.
83    pub fn basis_size(&self) -> usize {
84        self.k
85    }
86
87    /// Return the target collection.
88    pub fn collection(&self) -> &[Vec<usize>] {
89        &self.collection
90    }
91
92    /// Return a single target set.
93    pub fn get_set(&self, index: usize) -> Option<&Vec<usize>> {
94        self.collection.get(index)
95    }
96
97    /// Check whether the configuration is a satisfying Set Basis solution.
98    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
99        self.evaluate(config)
100    }
101
102    fn decode_basis(&self, config: &[usize]) -> Option<Vec<Vec<usize>>> {
103        let expected_len = self.k * self.universe_size;
104        if config.len() != expected_len || config.iter().any(|&value| value > 1) {
105            return None;
106        }
107
108        let mut basis = Vec::with_capacity(self.k);
109        for row in 0..self.k {
110            let mut subset = Vec::new();
111            let start = row * self.universe_size;
112            for element in 0..self.universe_size {
113                if config[start + element] == 1 {
114                    subset.push(element);
115                }
116            }
117            basis.push(subset);
118        }
119        Some(basis)
120    }
121
122    fn is_subset(candidate: &[usize], target_membership: &[bool]) -> bool {
123        candidate.iter().all(|&element| target_membership[element])
124    }
125
126    fn can_represent_target(basis: &[Vec<usize>], target: &[usize], universe_size: usize) -> bool {
127        let mut target_membership = vec![false; universe_size];
128        for &element in target {
129            if element >= universe_size {
130                return false;
131            }
132            target_membership[element] = true;
133        }
134
135        let mut covered = vec![false; universe_size];
136        for subset in basis {
137            if Self::is_subset(subset, &target_membership) {
138                for &element in subset {
139                    covered[element] = true;
140                }
141            }
142        }
143
144        target.iter().all(|&element| covered[element])
145    }
146}
147
148impl Problem for SetBasis {
149    const NAME: &'static str = "SetBasis";
150    type Metric = bool;
151
152    fn dims(&self) -> Vec<usize> {
153        vec![2; self.k * self.universe_size]
154    }
155
156    fn evaluate(&self, config: &[usize]) -> bool {
157        let Some(basis) = self.decode_basis(config) else {
158            return false;
159        };
160
161        self.collection
162            .iter()
163            .all(|target| Self::can_represent_target(&basis, target, self.universe_size))
164    }
165
166    fn variant() -> Vec<(&'static str, &'static str)> {
167        crate::variant_params![]
168    }
169}
170
171impl SatisfactionProblem for SetBasis {}
172
173crate::declare_variants! {
174    default sat SetBasis => "2^(basis_size * universe_size)",
175}
176
177#[cfg(feature = "example-db")]
178pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
179    vec![crate::example_db::specs::ModelExampleSpec {
180        id: "set_basis",
181        instance: Box::new(SetBasis::new(
182            4,
183            vec![vec![0, 1], vec![1, 2], vec![0, 2], vec![0, 1, 2]],
184            3,
185        )),
186        optimal_config: vec![0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
187        optimal_value: serde_json::json!(true),
188    }]
189}
190
191#[cfg(test)]
192#[path = "../../unit_tests/models/set/set_basis.rs"]
193mod tests;