Turi Create  4.0
mutable_queue.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 // Probabilistic Reasoning Library (PRL)
7 // Copyright 2005, 2008 (see AUTHORS.txt for a list of contributors)
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 
23 #ifndef TURI_MUTABLE_PRIORITY_QUEUE_HPP
24 #define TURI_MUTABLE_PRIORITY_QUEUE_HPP
25 
26 #include <vector>
27 #include <map>
28 #include <algorithm>
29 #include <boost/unordered_map.hpp>
30 
31 namespace turi {
32 
33  // Deprecated judy has trick
34  // template <typename T, typename Compare, typename Hash>
35  // class index_type_selector;
36  // template <typename T, typename Compare>
37  // class index_type_selector<T, Compare, void> {
38  // public:
39  // typedef std::map<T, size_t, Compare> index_map_type;
40  // };
41  // template <typename T, typename Compare, typename Hash>
42  // class index_type_selector {
43  // public:
44  // typedef judy_map_m<T, size_t, Hash, Compare> index_map_type;
45  // };
46 
47 
48 
49  /**
50  * A heap implementation of a priority queue that supports external
51  * priorities and priority updates. Both template arguments must be
52  * Assignable, EqualityComparable, and LessThanComparable.
53  *
54  * @param T
55  * the type of items stored in the priority queue.
56  * @param Priority
57  * the type used to prioritize items.
58  *
59  * @see Boost's mutable_queue in boost/pending/mutable_queue.hpp
60  * @todo Add a comparator
61  *
62  * \ingroup util
63  */
64  template <typename T, typename Priority>
65  class mutable_queue {
66  public:
67 
68  //! An element of the heap.
69  typedef typename std::pair<T, Priority> heap_element;
70 
71  protected:
72 
73  //! The storage type of the index map
74  typedef boost::unordered_map<T, size_t> index_map_type;
75 
76  //typedef judy_map_m<T, size_t, Compare> index_map_type;
77  // Deprecated judy hash trick
78  // typedef typename index_type_selector<T, Compare, Hash>::
79  // index_map_type index_map_type;
80 
81  //! The heap used to store the elements. The first element is unused.
82  std::vector<heap_element> heap;
83 
84  //! The map used to map from items to indexes in the heap.
85  index_map_type index_map;
86 
87  //! Returns the index of the left child of the supplied index.
88  size_t left(size_t i) const {
89  return 2 * i;
90  }
91 
92  //! Returns the index of the right child of the supplied index.
93  size_t right(size_t i) const {
94  return 2 * i + 1;
95  }
96 
97  //! Returns the index of the parent of the supplied index.
98  size_t parent(size_t i) const {
99  return i / 2;
100  }
101 
102  //! Extracts the priority at a heap location.
103  Priority priority_at(size_t i) {
104  return heap[i].second;
105  }
106 
107  //! Compares the priorities at two heap locations.
108  bool less(size_t i, size_t j) {
109  return heap[i].second < heap[j].second;
110  }
111 
112  //! Swaps the heap locations of two elements.
113  void swap(size_t i, size_t j) {
114  std::swap(heap[i], heap[j]);
115  index_map[heap[i].first] = i;
116  index_map[heap[j].first] = j;
117  }
118 
119  //! The traditional heapify function.
120  void heapify(size_t i) {
121  size_t l = left(i);
122  size_t r = right(i);
123  size_t s = size();
124  size_t largest = i;
125  if ((l <= s) && less(i, l))
126  largest = l;
127  if ((r <= s) && less(largest, r))
128  largest = r;
129  if (largest != i) {
130  swap(i, largest);
131  heapify(largest);
132  }
133  }
134 
135  public:
136  //! Default constructor.
138  : heap(1, std::make_pair(T(), Priority())) { }
139 
140  mutable_queue(const mutable_queue& other) :
141  heap(other.heap), index_map(other.index_map) { }
142 
143  mutable_queue& operator=(const mutable_queue& other) {
144  index_map = other.index_map;
145  heap = other.heap;
146  return *this;
147  }
148 
149  //! Returns the number of elements in the heap.
150  size_t size() const {
151  return heap.size() - 1;
152  }
153 
154  //! Returns true iff the queue is empty.
155  bool empty() const {
156  return size() == 0;
157  }
158 
159  //! Returns true if the queue contains the given value
160  bool contains(const T& item) const {
161  return index_map.count(item) > 0;
162  }
163 
164  //! Enqueues a new item in the queue.
165  void push(T item, Priority priority) {
166  heap.push_back(std::make_pair(item, priority));
167  size_t i = size();
168  index_map[item] = i;
169  while ((i > 1) && (priority_at(parent(i)) <= priority)) {
170  swap(i, parent(i));
171  i = parent(i);
172  }
173  }
174 
175  //! Accesses the item with maximum priority in the queue.
176  const std::pair<T, Priority>& top() const {
177  assert(!empty());
178  return heap[1];
179  }
180 
181  /**
182  * Removes the item with maximum priority from the queue, and
183  * returns it with its priority.
184  */
185  std::pair<T, Priority> pop() {
186  assert(!empty());
187  heap_element top = heap[1];
188  swap(1, size());
189  heap.pop_back();
190  heapify(1);
191  index_map.erase(top.first);
192  return top;
193  }
194 
195  //! Returns the weight associated with a key
196  Priority get(T item) const {
197  typename index_map_type::const_iterator iter = index_map.find(item);
198  assert(iter != index_map.end());
199  size_t i = iter->second;
200  return heap[i].second;
201  }
202 
203  //! Returns the priority associated with a key
204  Priority operator[](T item) const {
205  return get(item);
206  }
207 
208  /**
209  * Updates the priority associated with a item in the queue. This
210  * function fails if the item is not already present.
211  */
212  void update(T item, Priority priority) {
213  // Verify that the item is currently in the queue
214  typename index_map_type::const_iterator iter = index_map.find(item);
215  assert(iter != index_map.end());
216  // If it is already present update the priority
217  size_t i = iter->second;
218  heap[i].second = priority;
219  while ((i > 1) && (priority_at(parent(i)) < priority)) {
220  swap(i, parent(i));
221  i = parent(i);
222  }
223  heapify(i);
224  }
225 
226  /**
227  * Updates the priority associated with a item in the queue.
228  * If the item is not already present, insert it.
229  */
230  void push_or_update(T item, Priority priority) {
231  // Verify that the item is currently in the queue
232  typename index_map_type::const_iterator iter = index_map.find(item);
233  if(iter != index_map.end()) {
234  // If it is already present update the priority
235  size_t i = iter->second;
236  heap[i].second = priority;
237  while ((i > 1) && (priority_at(parent(i)) < priority)) {
238  swap(i, parent(i));
239  i = parent(i);
240  }
241  heapify(i);
242  }
243  else {
244  push(item, priority);
245  }
246  }
247 
248  /**
249  * If item is already in the queue, sets its priority to the maximum
250  * of the old priority and the new one. If the item is not in the queue,
251  * adds it to the queue.
252  *
253  * returns true if the items was not already
254  */
255  bool insert_max(T item, Priority priority) {
256  // determine if the item is already in the queue
257  typename index_map_type::const_iterator iter = index_map.find(item);
258  if(iter != index_map.end()) { // already present
259  // If it is already present update the priority
260  size_t i = iter->second;
261  heap[i].second = std::max(priority, heap[i].second);
262  // If the priority went up move the priority until its greater
263  // than its parent
264  while ((i > 1) && (priority_at(parent(i)) <= priority)) {
265  swap(i, parent(i));
266  i = parent(i);
267  }
268  // Trickle down if necessary
269  heapify(i); // This should not be necessary
270  return false;
271  } else { // not already present so simply add
272  push(item, priority);
273  return true;
274  }
275  }
276 
277  /**
278  * If item is already in the queue, sets its priority to the sum
279  * of the old priority and the new one. If the item is not in the queue,
280  * adds it to the queue.
281  *
282  * returns true if the item was already present
283  */
284  bool insert_cumulative(T item, Priority priority) {
285  // determine if the item is already in the queue
286  typename index_map_type::const_iterator iter = index_map.find(item);
287  if(iter != index_map.end()) { // already present
288  // If it is already present update the priority
289  size_t i = iter->second;
290  heap[i].second = priority + heap[i].second;
291  // If the priority went up move the priority until its greater
292  // than its parent
293  while ((i > 1) && (priority_at(parent(i)) <= priority)) {
294  swap(i, parent(i));
295  i = parent(i);
296  }
297  // Trickle down if necessary
298  heapify(i); // This should not be necessary
299  return false;
300  } else { // not already present so simply add
301  push(item, priority);
302  return true;
303  }
304  } // end of insert cumulative
305 
306 
307  //! Returns the values (key-priority pairs) in the priority queue
308  const std::vector<heap_element>& values() const {
309  return heap;
310  }
311 
312  //! Clears all the values (equivalent to stl clear)
313  void clear() {
314  heap.clear();
315  heap.push_back(std::make_pair(T(), Priority()));
316  index_map.clear();
317  }
318 
319  //! Remove an item from the queue
320  bool remove(T item) {
321  // Ensure that the element is in the queue
322  typename index_map_type::iterator iter = index_map.find(item);
323  // only if the element is present in the first place do we need
324  // remove it
325  if(iter != index_map.end()) {
326  size_t i = iter->second;
327  swap(i, size());
328  heap.pop_back();
329  heapify(i);
330  // erase the element from the index map
331  index_map.erase(iter);
332  return true;
333  }
334  return false;
335  }
336 
337  }; // class mutable_queue
338 
339 
340 
341 
342 
343 // // define a blank cosntant for the mutable queue
344 // #define BLANK (size_t(-1))
345 
346 // template <typename Priority>
347 // class mutable_queue<size_t, Priority> {
348 // public:
349 
350 
351 // //! An element of the heap.
352 // typedef typename std::pair<size_t, Priority> heap_element;
353 
354 // typedef size_t index_type;
355 
356 // protected:
357 
358 // //! The storage type of the index map
359 // typedef std::vector<index_type> index_map_type;
360 
361 // //! The heap used to store the elements. The first element is unused.
362 // std::vector<heap_element> heap;
363 
364 // //! The map used to map from items to indexes in the heap.
365 // index_map_type index_map;
366 
367 // //! Returns the index of the left child of the supplied index.
368 // size_t left(size_t i) const {
369 // return 2 * i;
370 // }
371 
372 // //! Returns the index of the right child of the supplied index.
373 // size_t right(size_t i) const {
374 // return 2 * i + 1;
375 // }
376 
377 // //! Returns the index of the parent of the supplied index.
378 // size_t parent(size_t i) const {
379 // return i / 2;
380 // }
381 
382 // //! Extracts the priority at a heap location.
383 // Priority priority_at(size_t i) {
384 // return heap[i].second;
385 // }
386 
387 // //! Compares the priorities at two heap locations.
388 // bool less(size_t i, size_t j) {
389 // assert( i < heap.size() );
390 // assert( j < heap.size() );
391 // return heap[i].second < heap[j].second;
392 // }
393 
394 // //! Swaps the heap locations of two elements.
395 // void swap(size_t i, size_t j) {
396 // if(i == j) return;
397 // std::swap(heap[i], heap[j]);
398 // assert(heap[i].first < index_map.size());
399 // assert(heap[j].first < index_map.size());
400 // index_map[heap[i].first] = i;
401 // index_map[heap[j].first] = j;
402 // }
403 
404 // //! The traditional heapify function.
405 // void heapify(size_t i) {
406 // size_t l = left(i);
407 // size_t r = right(i);
408 // size_t s = size();
409 // size_t largest = i;
410 // if ((l <= s) && less(i, l))
411 // largest = l;
412 // if ((r <= s) && less(largest, r))
413 // largest = r;
414 // if (largest != i) {
415 // swap(i, largest);
416 // heapify(largest);
417 // }
418 // }
419 
420 // public:
421 // //! Default constructor.
422 // mutable_queue()
423 // : heap(1, std::make_pair(-1, Priority())) { }
424 
425 // //! Returns the number of elements in the heap.
426 // size_t size() const {
427 // assert(heap.size() > 0);
428 // return heap.size() - 1;
429 // }
430 
431 // //! Returns true iff the queue is empty.
432 // bool empty() const {
433 // return size() == 0;
434 // }
435 
436 // //! Returns true if the queue contains the given value
437 // bool contains(const size_t& item) const {
438 // return item < index_map.size() &&
439 // index_map[item] != BLANK;
440 // }
441 
442 // //! Enqueues a new item in the queue.
443 // void push(size_t item, Priority priority) {
444 // assert(item != BLANK);
445 // heap.push_back(std::make_pair(item, priority));
446 // size_t i = size();
447 // if ( !(item < index_map.size()) ) {
448 // index_map.resize(item + 1, BLANK);
449 // }
450 // // Bubble up
451 // index_map[item] = i;
452 // while ((i > 1) && (priority_at(parent(i)) < priority)) {
453 // swap(i, parent(i));
454 // i = parent(i);
455 // }
456 // }
457 
458 // //! Accesses the item with maximum priority in the queue.
459 // const std::pair<size_t, Priority>& top() const {
460 // assert(heap.size() > 1);
461 // return heap[1];
462 // }
463 
464 // /**
465 // * Removes the item with maximum priority from the queue, and
466 // * returns it with its priority.
467 // */
468 // std::pair<size_t, Priority> pop() {
469 // assert(heap.size() > 1);
470 // heap_element top = heap[1];
471 // assert(top.first < index_map.size());
472 // swap(1, size());
473 // heap.pop_back();
474 // heapify(1);
475 // index_map[top.first] = BLANK;
476 // return top;
477 // }
478 
479 // //! Returns the weight associated with a key
480 // Priority get(size_t item) const {
481 // assert(item < index_map.size());
482 // assert(index_map[item] != BLANK);
483 // return heap[index_map[item]].second;
484 // }
485 
486 // //! Returns the priority associated with a key
487 // Priority operator[](size_t item) const {
488 // return get(item);
489 // }
490 
491 // /**
492 // * Updates the priority associated with a item in the queue. This
493 // * function fails if the item is not already present.
494 // */
495 // void update(size_t item, Priority priority) {
496 // assert(item < index_map.size());
497 // size_t i = index_map[item];
498 // heap[i].second = priority;
499 // while ((i > 1) && (priority_at(parent(i)) < priority)) {
500 // swap(i, parent(i));
501 // i = parent(i);
502 // }
503 // heapify(i);
504 // }
505 
506 // /**
507 // * If item is already in the queue, sets its priority to the maximum
508 // * of the old priority and the new one. If the item is not in the queue,
509 // * adds it to the queue.
510 // */
511 // void insert_max(size_t item, Priority priority) {
512 // assert(item != BLANK);
513 // if(!contains(item))
514 // push(item, priority);
515 // else {
516 // Priority effective_priority = std::max(get(item), priority);
517 // update(item, effective_priority);
518 // }
519 // }
520 
521 // //! Returns the values (key-priority pairs) in the priority queue
522 // const std::vector<heap_element>& values() const {
523 // return heap;
524 // }
525 
526 // //! Clears all the values (equivalent to stl clear)
527 // void clear() {
528 // heap.clear();
529 // heap.push_back(std::make_pair(-1, Priority()));
530 // index_map.clear();
531 // }
532 
533 // /**
534 // * Remove an item from the queue returning true if the item was
535 // * originally present
536 // */
537 // bool remove(size_t item) {
538 // if(contains(item)) {
539 // assert(size() > 0);
540 // assert(item < index_map.size());
541 // size_t i = index_map[item];
542 // assert(i != BLANK);
543 // swap(i, size());
544 // heap.pop_back();
545 // heapify(i);
546 // // erase the element from the index map
547 // index_map[item] = BLANK;
548 // return true;
549 // } else {
550 // // Item was not present
551 // return false;
552 // }
553 // }
554 // }; // class mutable_queue
555 
556 // #undef BLANK
557 
558 
559 
560 
561 
562 } // namespace turi
563 
564 #endif // #ifndef TURI_MUTABLE_PRIORITY_QUEUE_HPP
boost::unordered_map< T, size_t > index_map_type
The storage type of the index map.
bool less(size_t i, size_t j)
Compares the priorities at two heap locations.
void clear()
Clears all the values (equivalent to stl clear)
const std::pair< T, Priority > & top() const
Accesses the item with maximum priority in the queue.
void swap(size_t i, size_t j)
Swaps the heap locations of two elements.
const std::vector< heap_element > & values() const
Returns the values (key-priority pairs) in the priority queue.
bool insert_max(T item, Priority priority)
STL namespace.
index_map_type index_map
The map used to map from items to indexes in the heap.
size_t parent(size_t i) const
Returns the index of the parent of the supplied index.
void update(T item, Priority priority)
void push(T item, Priority priority)
Enqueues a new item in the queue.
bool insert_cumulative(T item, Priority priority)
Priority priority_at(size_t i)
Extracts the priority at a heap location.
void push_or_update(T item, Priority priority)
size_t left(size_t i) const
Returns the index of the left child of the supplied index.
std::pair< T, Priority > pop()
std::pair< T, Priority > heap_element
An element of the heap.
bool empty() const
Returns true iff the queue is empty.
std::vector< heap_element > heap
The heap used to store the elements. The first element is unused.
bool contains(const T &item) const
Returns true if the queue contains the given value.
size_t right(size_t i) const
Returns the index of the right child of the supplied index.
mutable_queue()
Default constructor.
Priority operator[](T item) const
Returns the priority associated with a key.
size_t size() const
Returns the number of elements in the heap.
void heapify(size_t i)
The traditional heapify function.