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