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}