Skip to main content

pumpkin_core/containers/
sparse_set.rs

1//! A set for keeping track of which values are still part of the original domain, allows O(1)
2//! removals and O(|D|) traversal of the domain (where D are the values which are currently in the
3//! domain).
4//!
5//! # Theoretical
6//! The idea of this structure is to allow efficient removal and traversal of the values which are
7//! still in the domain at the "cost" of expensive queries to check whether a value is currently in
8//! the domain.
9//!
10//! The idea is that the sparse-set keeps track of the number of elements which are in
11//! the domain in ([`SparseSet::size`]) and it guarantees that the first [`SparseSet::size`] values
12//! are in the domain. To remove a value, the element at index `i` is swapped with the element at
13//! index [`SparseSet::size`] and [`SparseSet::size`] is afterwards decremented by 1. This does not
14//! allow the reclamation of memory when an element is removed from the structure but it allows easy
15//! backtracking by simply moving the [`SparseSet::size`] pointer.
16//!
17//! # Practical
18//! Our implementation follows [\[1\]](https://hal.science/hal-01339250/document). The [`SparseSet`]
19//! structure keeps track of a number of variables; the main practical consideration is that a
20//! function `mapping` should be provided which maps every
21//! value in the domain to an index in \[0..|domain|\) in a bijective manner.
22//!
23//! # Bibliography
24//! \[1\] V. le C. de Saint-Marcq, P. Schaus, C. Solnon, and C. Lecoutre, ‘Sparse-sets for domain
25//! implementation’, in CP workshop on Techniques foR Implementing Constraint programming Systems
26//! (TRICS), 2013, pp. 1–10.
27
28use crate::pumpkin_assert_moderate;
29use crate::pumpkin_assert_simple;
30
31/// A set for keeping track of which values are still part of the original domain based on [\[1\]](https://hal.science/hal-01339250/document).
32/// See the module level documentation for more information.
33///
34/// It provides O(1) removals of values from the domain and O(|D|) traversal of the domain (where D
35/// are the values which are currently in the domain).
36///
37/// Note that it is required that each element contained in the domain can be
38/// uniquely mapped to an index in the range [0, |D|) (i.e. the mapping function is bijective)
39///
40/// # Bibliography
41/// \[1\] V. le C. de Saint-Marcq, P. Schaus, C. Solnon, and C. Lecoutre, ‘Sparse-sets for domain
42/// implementation’, in CP workshop on Techniques foR Implementing Constraint programming Systems
43/// (TRICS), 2013, pp. 1–10.
44#[derive(Debug, Clone)]
45pub struct SparseSet<T> {
46    /// The number of elements which are currently in the domain
47    size: usize,
48    /// The current state of the domain, this structure guarantees that the first
49    /// [`size`][SparseSet::size] elements are part of the domain
50    domain: Vec<T>,
51    /// Stores for each value of T what its corresponding index is in
52    /// [`domain`][`SparseSet::domain`]
53    indices: Vec<usize>,
54    /// A bijective function which takes as input an element `T` and returns an index in the range
55    /// [0, |D_{original}|) to be used for retrieving values from
56    /// [`indices`][`SparseSet::indices`]
57    mapping: fn(&T) -> usize,
58}
59
60impl<T> SparseSet<T> {
61    /// Assumption: It is assumed that `mapping` is a bijective function which
62    /// will return an index which is in the range [0, |D_{original}|) (where D_{original} is
63    /// the initial domain before any operations have been performed).
64    pub fn new(input: Vec<T>, mapping: fn(&T) -> usize) -> Self {
65        let input_len = input.len();
66        SparseSet {
67            size: input_len,
68            domain: input,
69            indices: (0..input_len).collect::<Vec<_>>(),
70            mapping,
71        }
72    }
73
74    pub fn set_to_empty(&mut self) {
75        self.indices = vec![usize::MAX; self.indices.len()];
76        self.domain.clear();
77        self.size = 0;
78    }
79
80    pub fn restore_temporarily_removed(&mut self) {
81        self.size = self.domain.len();
82    }
83
84    /// Determines whether the domain represented by the [`SparseSet`] is empty
85    pub fn is_empty(&self) -> bool {
86        self.size == 0
87    }
88
89    /// Returns how many elements are part of the domain
90    pub fn len(&self) -> usize {
91        self.size
92    }
93
94    /// Returns the `index`th element in the domain; if `index` is larger than or equal to
95    /// [`SparseSet::len`] then this method will panic.
96    pub fn get(&self, index: usize) -> &T {
97        pumpkin_assert_simple!(index < self.size);
98        &self.domain[index]
99    }
100
101    /// Swaps the elements at positions `i` and `j` in [`domain`][SparseSet::domain] and swaps the
102    /// corresponding indices in [`indices`][SparseSet::indices]
103    fn swap(&mut self, i: usize, j: usize) {
104        self.domain.swap(i, j);
105        self.indices[(self.mapping)(&self.domain[i])] = i;
106        self.indices[(self.mapping)(&self.domain[j])] = j;
107    }
108
109    /// Remove the value of `to_remove` from the domain; if the value is not in the domain then this
110    /// method does not perform any operations.
111    pub fn remove(&mut self, to_remove: &T) {
112        if self.indices[(self.mapping)(to_remove)] < self.size {
113            // The element is part of the domain and should be removed
114            self.size -= 1;
115            if self.size > 0 {
116                self.swap(self.indices[(self.mapping)(to_remove)], self.size);
117            }
118
119            self.swap(
120                self.indices[(self.mapping)(to_remove)],
121                self.domain.len() - 1,
122            );
123            let element = self.domain.pop().expect("Has to have something to pop.");
124            pumpkin_assert_moderate!((self.mapping)(&element) == (self.mapping)(to_remove));
125            self.indices[(self.mapping)(to_remove)] = usize::MAX;
126        } else if self.indices[(self.mapping)(to_remove)] < self.domain.len() {
127            self.swap(
128                self.indices[(self.mapping)(to_remove)],
129                self.domain.len() - 1,
130            );
131            let element = self.domain.pop().expect("Has to have something to pop.");
132            pumpkin_assert_moderate!((self.mapping)(&element) == (self.mapping)(to_remove));
133            self.indices[(self.mapping)(to_remove)] = usize::MAX;
134        }
135    }
136
137    pub fn remove_temporarily(&mut self, to_remove: &T) {
138        if self.indices[(self.mapping)(to_remove)] < self.size {
139            // The element is part of the domain and should be removed
140            self.size -= 1;
141            self.swap(self.indices[(self.mapping)(to_remove)], self.size);
142        }
143    }
144
145    /// Determines whehter the `element` is contained in the domain of the sparse-set.
146    pub fn contains(&self, element: &T) -> bool {
147        (self.mapping)(element) < self.indices.len()
148            && self.indices[(self.mapping)(element)] < self.size
149    }
150
151    /// Accomodates the `element`.
152    pub fn accommodate(&mut self, element: &T) {
153        let index = (self.mapping)(element);
154        if self.indices.len() <= index {
155            self.indices.resize(index + 1, usize::MAX);
156        }
157    }
158
159    /// Inserts the element if it is not already contained in the sparse set.
160    pub fn insert(&mut self, element: T) {
161        if !self.contains(&element) {
162            self.accommodate(&element);
163
164            self.indices[(self.mapping)(&element)] = self.domain.len();
165            self.domain.push(element);
166            self.swap(self.size, self.domain.len() - 1);
167            self.size += 1;
168        }
169    }
170
171    /// Returns an iterator which goes over the values in the domain of the sparse-set
172    pub fn iter(&self) -> impl Iterator<Item = &T> {
173        self.domain[..self.size].iter()
174    }
175
176    pub fn out_of_domain(&self) -> impl Iterator<Item = &T> {
177        self.domain[self.size..].iter()
178    }
179}
180
181impl<T> IntoIterator for SparseSet<T> {
182    type Item = T;
183
184    type IntoIter = std::iter::Take<std::vec::IntoIter<T>>;
185
186    fn into_iter(self) -> Self::IntoIter {
187        self.domain.into_iter().take(self.size)
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use super::SparseSet;
194
195    fn mapping_function(input: &u32) -> usize {
196        *input as usize
197    }
198
199    #[test]
200    fn test_len() {
201        let sparse_set = SparseSet::new(vec![0, 1, 2], mapping_function);
202        assert_eq!(sparse_set.len(), 3);
203    }
204
205    #[test]
206    fn removal() {
207        let mut sparse_set = SparseSet::new(vec![0, 1, 2], mapping_function);
208        sparse_set.remove(&1);
209        assert_eq!(sparse_set.domain, vec![0, 2]);
210        assert_eq!(sparse_set.size, 2);
211        assert_eq!(sparse_set.indices, vec![0, usize::MAX, 1]);
212    }
213
214    #[test]
215    fn removal_adjusts_size() {
216        let mut sparse_set = SparseSet::new(vec![0, 1, 2], mapping_function);
217        assert_eq!(sparse_set.size, 3);
218        sparse_set.remove(&0);
219        assert_eq!(sparse_set.size, 2);
220    }
221
222    #[test]
223    fn remove_all_elements_leads_to_empty_set() {
224        let mut sparse_set = SparseSet::new(vec![0, 1, 2], mapping_function);
225        sparse_set.remove(&0);
226        sparse_set.remove(&1);
227        sparse_set.remove(&2);
228        assert!(sparse_set.is_empty());
229    }
230
231    #[test]
232    fn iter1() {
233        let sparse_set = SparseSet::new(vec![5, 10, 2], mapping_function);
234        let v: Vec<u32> = sparse_set.iter().copied().collect();
235        assert_eq!(v.len(), 3);
236        assert!(v.contains(&10));
237        assert!(v.contains(&5));
238        assert!(v.contains(&2));
239    }
240
241    #[test]
242    fn iter2() {
243        let mut sparse_set = SparseSet::new(vec![5, 10, 2], mapping_function);
244        sparse_set.insert(100);
245        sparse_set.insert(2);
246        sparse_set.insert(20);
247        sparse_set.remove(&10);
248        sparse_set.insert(10);
249        sparse_set.remove(&10);
250
251        let v: Vec<u32> = sparse_set.iter().copied().collect();
252        assert_eq!(v.len(), 5);
253        assert!(v.contains(&5));
254        assert!(v.contains(&2));
255        assert!(v.contains(&100));
256        assert!(v.contains(&20));
257    }
258}