kodama/
queue.rs

1use crate::float::Float;
2
3// BREADCRUMBS: Look into moving `nearest` into this heap structure. It seems
4// like it might be doing too much at once, but things might actually become
5// clearer by coupling them together since they do actually seem coupled in
6// the generic algorithm.
7
8/// A priority queue of clusters implement by a min-binary-heap.
9///
10/// Elements in the queue are cluster labels, and are weighted by a candidate
11/// minimum dissimilarity between it and its nearest cluster.
12#[derive(Debug, Default)]
13pub struct LinkageHeap<T> {
14    /// A heap of observations. A node N has children at 2N+1 and 2N+2.
15    heap: Vec<usize>,
16    /// A map from observation to its position in `heap`.
17    observations: Vec<usize>,
18    /// The priority associated with each observation.
19    priorities: Vec<T>,
20    /// Observations that have been removed.
21    removed: Vec<bool>,
22}
23
24impl<T: Float> LinkageHeap<T> {
25    pub fn new() -> LinkageHeap<T> {
26        LinkageHeap::with_len(0)
27    }
28
29    pub fn with_len(len: usize) -> LinkageHeap<T> {
30        LinkageHeap {
31            heap: (0..len).collect(),
32            observations: (0..len).collect(),
33            priorities: vec![T::max_value(); len],
34            removed: vec![false; len],
35        }
36    }
37
38    pub fn reset(&mut self, len: usize) {
39        self.heap.resize(len, 0);
40        self.observations.resize(len, 0);
41        self.priorities.resize(len, T::max_value());
42        self.removed.resize(len, false);
43
44        for i in 0..len {
45            self.heap[i] = i;
46            self.observations[i] = i;
47            self.priorities[i] = T::max_value();
48            self.removed[i] = false;
49        }
50    }
51
52    pub fn is_empty(&self) -> bool {
53        self.len() == 0
54    }
55
56    pub fn len(&self) -> usize {
57        self.heap.len()
58    }
59
60    pub fn pop(&mut self) -> Option<usize> {
61        if self.is_empty() {
62            return None;
63        }
64        if self.heap.len() >= 2 {
65            let (first, last) = (self.heap[0], *self.heap.last().unwrap());
66            self.swap(first, last);
67        }
68        let last = self.heap.pop().unwrap();
69        self.removed[last] = true;
70        if self.heap.len() >= 2 {
71            let first = self.heap[0];
72            self.sift_down(first);
73        }
74        Some(last)
75    }
76
77    pub fn peek(&self) -> Option<usize> {
78        if self.is_empty() {
79            None
80        } else {
81            Some(self.heap[0])
82        }
83    }
84
85    pub fn heapify<F: FnMut(&mut [T])>(&mut self, mut f: F) {
86        let len = self.priorities.len();
87        self.reset(len);
88        f(&mut self.priorities);
89
90        for i in (0..len / 2).rev() {
91            let o = self.heap[i];
92            self.sift_down(o);
93        }
94    }
95
96    pub fn priority(&self, observation: usize) -> &T {
97        assert!(!self.removed[observation]);
98        &self.priorities[observation]
99    }
100
101    pub fn set_priority(&mut self, observation: usize, priority: T) {
102        assert!(!self.removed[observation]);
103
104        let old = self.priorities[observation];
105        self.priorities[observation] = priority;
106        if priority < old {
107            self.sift_up(observation);
108        } else if priority > old {
109            self.sift_down(observation);
110        }
111    }
112
113    fn sift_up(&mut self, o: usize) {
114        loop {
115            let po = match self.parent(o) {
116                None => break,
117                Some(po) => po,
118            };
119            if self.priorities[po] < self.priorities[o] {
120                break;
121            }
122            self.swap(o, po);
123        }
124    }
125
126    fn sift_down(&mut self, o: usize) {
127        loop {
128            let mut child = o;
129            let (left, right) = self.children(o);
130            if let Some(left) = left {
131                if self.priorities[left] < self.priorities[child] {
132                    child = left;
133                }
134            }
135            if let Some(right) = right {
136                if self.priorities[right] < self.priorities[child] {
137                    child = right;
138                }
139            }
140            if o == child {
141                break;
142            }
143            self.swap(o, child);
144        }
145    }
146
147    fn parent(&self, o: usize) -> Option<usize> {
148        if self.observations[o] == 0 {
149            None
150        } else {
151            Some(self.heap[(self.observations[o] - 1) / 2])
152        }
153    }
154
155    fn children(&self, o: usize) -> (Option<usize>, Option<usize>) {
156        let i = self.observations[o];
157        let (left, right) = (2 * i + 1, 2 * i + 2);
158        (self.heap.get(left).cloned(), self.heap.get(right).cloned())
159    }
160
161    fn swap(&mut self, o1: usize, o2: usize) {
162        self.heap.swap(self.observations[o1], self.observations[o2]);
163        self.observations.swap(o1, o2);
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use crate::float::Float;
170
171    use super::LinkageHeap;
172
173    fn is_sorted_asc<T: Float>(xs: &[T]) -> bool {
174        for win in xs.windows(2) {
175            if win[0] > win[1] {
176                return false;
177            }
178        }
179        true
180    }
181
182    fn pop_all<T: Float>(heap: &mut LinkageHeap<T>) -> Vec<T> {
183        let mut xs = vec![];
184        while let Some(o) = heap.peek() {
185            xs.push(*heap.priority(o));
186            heap.pop().unwrap();
187        }
188        xs
189    }
190
191    fn new_heap<T: Float>(priorities: &[T]) -> LinkageHeap<T> {
192        let mut heap = LinkageHeap::with_len(priorities.len());
193        for (i, p) in priorities.iter().enumerate() {
194            heap.set_priority(i, *p);
195        }
196        heap
197    }
198
199    fn heapify<T: Float>(priorities: &[T]) -> LinkageHeap<T> {
200        let mut heap = LinkageHeap::with_len(priorities.len());
201        heap.heapify(|ps| ps.copy_from_slice(priorities));
202        heap
203    }
204
205    #[test]
206    fn simple() {
207        let mut heap = new_heap(&[2.0, 1.0, 10.0, 5.0, 4.0, 4.5]);
208        let ps = pop_all(&mut heap);
209        assert_eq!(ps, &[1.0, 2.0, 4.0, 4.5, 5.0, 10.0]);
210
211        let mut heap = heapify(&[2.0, 1.0, 10.0, 5.0, 4.0, 4.5]);
212        let ps = pop_all(&mut heap);
213        assert_eq!(ps, &[1.0, 2.0, 4.0, 4.5, 5.0, 10.0]);
214    }
215
216    #[test]
217    fn empty() {
218        let mut heap = new_heap::<f64>(&[]);
219        let ps = pop_all(&mut heap);
220        assert_eq!(ps, &[]);
221
222        let mut heap = heapify::<f64>(&[]);
223        let ps = pop_all(&mut heap);
224        assert_eq!(ps, &[]);
225    }
226
227    #[test]
228    fn one() {
229        let mut heap = new_heap(&[1.0]);
230        let ps = pop_all(&mut heap);
231        assert_eq!(ps, &[1.0]);
232
233        let mut heap = heapify(&[1.0]);
234        let ps = pop_all(&mut heap);
235        assert_eq!(ps, &[1.0]);
236    }
237
238    #[test]
239    fn two() {
240        let mut heap = new_heap(&[2.0, 1.0]);
241        let ps = pop_all(&mut heap);
242        assert_eq!(ps, &[1.0, 2.0]);
243
244        let mut heap = heapify(&[2.0, 1.0]);
245        let ps = pop_all(&mut heap);
246        assert_eq!(ps, &[1.0, 2.0]);
247    }
248
249    quickcheck::quickcheck! {
250        fn prop_heap_invariant(xs: Vec<f64>) -> bool {
251            let mut xs = xs;
252            for x in &mut xs {
253                if x.is_nan() {
254                    *x = 0.0;
255                }
256            }
257            let mut heap = new_heap(&xs);
258            is_sorted_asc(&pop_all(&mut heap))
259        }
260    }
261
262    quickcheck::quickcheck! {
263        fn prop_heapify_heap_invariant(xs: Vec<f64>) -> bool {
264            let mut xs = xs;
265            for x in &mut xs {
266                if x.is_nan() {
267                    *x = 0.0;
268                }
269            }
270            let mut heap = heapify(&xs);
271            is_sorted_asc(&pop_all(&mut heap))
272        }
273    }
274}