aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
multiPriorityQueue.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief Priority queues in which the same element can appear several times.
25  *
26  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27  */
28 
29 #ifndef GUM_MULTI_PRIORITY_QUEUE_H
30 #define GUM_MULTI_PRIORITY_QUEUE_H
31 
32 #include <functional>
33 #include <initializer_list>
34 #include <sstream>
35 #include <string>
36 #include <utility>
37 #include <vector>
38 
39 #include <agrum/agrum.h>
40 #include <agrum/tools/core/hashTable.h>
41 
42 namespace gum {
43 
44 #define GUM_MULTIPLE_PRIORITY_QUEUE_DEFAULT_CAPACITY 10
45 
46 #ifndef DOXYGEN_SHOULD_SKIP_THIS
47  // templates provided by this file
48  template < typename Val, typename Priority, typename Cmp, typename Alloc >
49  class MultiPriorityQueue;
50 
51  template < typename Val, typename Priority, typename Cmp, typename Alloc >
52  std::ostream& operator<<(std::ostream&,
53  const MultiPriorityQueue< Val, Priority, Cmp, Alloc >&);
54 
55 #endif // DOXYGEN_SHOULD_SKIP_THIS
56 
57  // ===========================================================================
58  // === PRIORITY QUEUES ===
59  // ===========================================================================
60  /**
61  * @class MultiPriorityQueue
62  * @headerfile multiPriorityQueue.h <agrum/tools/core/multiPriorityQueue.h>
63  * @brief A MultiPriorityQueue is a heap in which each element has a mutable
64  * priority and duplicates are allowed
65  * @ingroup basicstruct_group
66  * @ingroup priorityqueue_group
67  *
68  * A priority queue is quite similar to a heap except that a priority (a
69  * score) is assigned to each element in the structure. The elements are
70  * sorted according to a weak order on the scores. The priority of any
71  * element can be changed at any moment by the user. The priority queue then
72  * restores a heap property accordingly.
73  *
74  * @par Usage example:
75  * @code
76  * // create a priority queue of strings, the priorities of which are
77  * // integers the element at the top of the queue has the smallest priority
78  * MultiPriorityQueue<std::string> queue1;
79  *
80  * // insert elements into the queue
81  * queue1.insert (8, "AAA");
82  * queue1.insert (10, "BBB");
83  * queue1.insert (2, "CCC");
84  * queue1.insert (23, "DDD");
85  * queue1.insert (24, "EEE");
86  * queue1.insert (10, "AAA");
87  *
88  * // copy the queue
89  * MultiPriorityQueue<std::string> queue2 = queue1;
90  *
91  * // initializer list constructor
92  * MultiPriorityQueue<std::string,int>
93  * queue3 { std::pair<std::string,int> ( "aa", 3 ),
94  * std::pair<std::string,int> ( "bb", 2 ) };
95  *
96  * // create a priority queue of strings, the priorities of which are
97  * // pairs of ints
98  * MultiPriorityQueue< std::string, std::pair<int,int> > queue3;
99  *
100  * // get the top element, then remove it
101  * std::cerr << queue2.top() << std::endl;
102  * queue2.eraseTop();
103  *
104  * // get the top element, then remove it
105  * std::cerr << queue2.pop() << std::endl;
106  *
107  * // output the content of the queue
108  * std::cerr << queue1 << std::endl;
109  *
110  * // change the priority of the element at position 3
111  * Size new_pos=queue1.setPriorityByPos (3,100);
112  *
113  * // change the priority of all instances of element "AAA"
114  * queue1.setPriority ("AAA",100);
115  * @endcode
116  *
117  * @tparam Val The values type stored in the gum::MultiPriorityQueue.
118  * @tparam Priority The priorities type.
119  * @tparam Cmp The priorities comparator.
120  * @tparam Alloc The values allocator.
121  */
122  template < typename Val,
123  typename Priority = int,
124  typename Cmp = std::less< Priority >,
125  typename Alloc = std::allocator< Val > >
126  class MultiPriorityQueue {
127  /// Making all MultiPriorityQueue friend with themselves.
128  template < typename V, typename P, typename C, typename A >
129  friend class MultiPriorityQueue;
130 
131  public:
132  /// types for STL compliance
133  /// @{
134  using value_type = Val;
135  using reference = Val&;
136  using const_reference = const Val&;
137  using pointer = Val*;
138  using const_pointer = const Val*;
139  using difference_type = std::ptrdiff_t;
140  using allocator_type = Alloc;
141  /// @}
142 
143  /// The allocator for the indices.
144  using IndexAlloc = typename Alloc::template rebind<
145  std::pair< Val, std::vector< Size > > >::other;
146 
147  /// The allocator for the heap.
148  using HeapAlloc =
149  typename Alloc::template rebind< std::pair< Priority, const Val* > >::other;
150 
151  // ============================================================================
152  /// @name Constructors / Destructors
153  // ============================================================================
154  /// @{
155 
156  /**
157  * @brief Basic constructor. Creates an empty priority queue.
158  *
159  * @param compare a function taking two elements in argument, say e1 and
160  * e2, and returning a Boolean indicating wether e1 < e2, i.e., whether e1
161  * should be nearer than e2 to the top of the heap.
162  * @param capacity the size of the internal data structures containing the
163  * elements (could be for instance vectors or hashtables).
164  */
165  explicit MultiPriorityQueue(Cmp compare = Cmp(),
166  Size capacity
168 
169  /**
170  * @brief Initializer list constructor.
171  *
172  * The elements of the initializer list are pairs <Val,Priority>.
173  * The comparison function is the default one, i.e., std::less<Priority>.
174  *
175  * @param list The initializer list.
176  */
177  explicit MultiPriorityQueue(
178  std::initializer_list< std::pair< Val, Priority > > list);
179 
180  /**
181  * @brief Copy constructor.
182  * @param from The gum::MultiPriorityQueue to copy.
183  */
184  MultiPriorityQueue(
185  const MultiPriorityQueue< Val, Priority, Cmp, Alloc >& from);
186 
187  /// generalized copy constructor
188  /**
189  * @brief Generalized Copy constructor.
190  * @param from The gum::MultiPriorityQueue to copy.
191  * @tparam OtherAlloc The other gum::MultiPriorityQueue allocator.
192  */
193  template < typename OtherAlloc >
194  MultiPriorityQueue(
195  const MultiPriorityQueue< Val, Priority, Cmp, OtherAlloc >& from);
196 
197  /**
198  * @brief Move constructor.
199  * @param from The gum::MultiPriorityQueue to move.
200  */
201  MultiPriorityQueue(MultiPriorityQueue< Val, Priority, Cmp, Alloc >&& from);
202 
203  /**
204  * @brief Class destructor.
205  */
206  ~MultiPriorityQueue();
207 
208  /// @}
209  // ============================================================================
210  /// @name Operators
211  // ============================================================================
212  /// @{
213 
214  /**
215  * @brief Copy operator.
216  *
217  * When a problem occurs during the copy (for instance when not enough
218  * memory is available), the operator guarantees that the heap stays in a
219  * coherent state. Actually, the priority queue becomes empty. An exception
220  * is then thrown.
221  *
222  * @param from The gum::MultiPriorityQueue to copy.
223  */
224  MultiPriorityQueue< Val, Priority, Cmp, Alloc >&
225  operator=(const MultiPriorityQueue< Val, Priority, Cmp, Alloc >& from);
226 
227  /**
228  * @brief Generalized copy operator.
229  *
230  * When a problem occurs during the copy (for instance when not enough
231  * memory is available), the operator guarantees that the heap stays in a
232  * coherent state. Actually, the priority queue becomes empty. An exception
233  * is then thrown.
234  *
235  * @param from The gum::MultiPriorityQueue to copy.
236  * @tparam OtherAlloc The other gum::MultiPriorityQueue allocator.
237  */
238  template < typename OtherAlloc >
239  MultiPriorityQueue< Val, Priority, Cmp, Alloc >&
240  operator=(const MultiPriorityQueue< Val, Priority, Cmp, OtherAlloc >& from);
241 
242  /**
243  * @brief Move operator.
244  * @param from The gum::MultiPriorityQueue to copy.
245  */
246  MultiPriorityQueue< Val, Priority, Cmp, Alloc >&
247  operator=(MultiPriorityQueue< Val, Priority, Cmp, Alloc >&& from);
248 
249  /**
250  * @brief Returns the element at index "index_elt" from the priority queue.
251  * @param index_elt The index of the element to return.
252  * @return Returns the element at index "index_elt" from the priority
253  * queue.
254  * @throw NotFound Raised if the element does not exist.
255  */
256  const Val& operator[](Size index_elt) const;
257 
258  /// @}
259  // ============================================================================
260  /// @name Accessors / Modifiers
261  // ============================================================================
262  /// @{
263 
264  /**
265  * @brief Returns the number of elements in the priority queue.
266  * @return Returns the number of elements in the priority queue.
267  */
268  Size size() const noexcept;
269 
270  /**
271  * @brief Indicates whether the priority queue is empty.
272  * @return Indicates whether the priority queue is empty.
273  */
274  bool empty() const noexcept;
275 
276  /**
277  * @brief Indicates whether the priority queue contains a given value.
278  * @param val The value to check if it is in the priority queue.
279  * @return Returns true if the priority queue cotains the given value.
280  */
281  bool contains(const Val& val) const;
282 
283  /**
284  * @brief Returns the element at the top of the priority queue.
285  * @return Returns the element at the top of the priority queue.
286  * @throw NotFound Raised if the queue is empty.
287  */
288  const Val& top() const;
289 
290  /**
291  * @brief Returns the priority of the top element.
292  * @throw NotFound Raised if the queue is empty.
293  */
294  const Priority& topPriority() const;
295 
296  /**
297  * @brief Removes the top element from the priority queue and return it.
298  * @return Returns the top element from the priority queue.
299  * @throw NotFound Raised if the queue is empty.
300  */
301  Val pop();
302 
303  /**
304  * @brief Inserts a new (a copy) element in the priority queue.
305  *
306  * See method gum::MultiPriorityQueue::eraseByPos(Size) for more details
307  * about the index.
308  *
309  * @param val The value to insert.
310  * @param priority The value priority.
311  * @return Returns the index of the element inserted into the priority
312  * queue.
313  */
314  Size insert(const Val& val, const Priority& priority);
315 
316  /**
317  * @brief Inserts (by move) a new element in the priority queue.
318  *
319  * See method gum::MultiPriorityQueue::eraseByPos(Size) for more details
320  * about the index.
321  *
322  * @param val The value to insert.
323  * @param priority The value priority.
324  * @return Returns the index of the element inserted into the priority
325  * queue.
326  */
327  Size insert(Val&& val, Priority&& priority);
328 
329  /**
330  * @brief Emplace a new element into the priority queue.
331  *
332  * See method gum::MultiPriorityQueue::eraseByPos(Size) for more details
333  * about the index.
334  *
335  * @tparam Args The emplace arguments types.
336  * @param args The emplace arguments.
337  * @return the index of the element inserted into the priority queue.
338  */
339  template < typename... Args >
340  Size emplace(Args&&... args);
341 
342  /**
343  * @brief Removes the top of the priority queue (but does not return it).
344  *
345  * If the heap is empty, it does nothing (in particular, it does not throw
346  * any exception).
347  */
348  void eraseTop();
349 
350  /**
351  * @brief Removes the element at position "index" from the priority queue.
352  *
353  * If the element cannot be found, the function returns without throwing
354  * any exception.
355  *
356  * The priority is computed as follows: suppose that the queue is a
357  * complete binary tree where all levels are completely filled except,
358  * eventually, the last one. In this case, the elements of the last
359  * level are all on the left of the tree.
360  *
361  * We assign 0 to the root, then parsing the tree from top to bottom then
362  * from left to right we increment the index and assigned it to the current
363  * node. Doing so, we get a unique index for each element. This is
364  * precisely what the index passed in argument of the function represents.
365  *
366  * @param index represents the position of the element to be removed.
367  */
368  void eraseByPos(Size index);
369 
370  /**
371  * @brief Removes a given element from the priority queue (but does not
372  * return it).
373  *
374  * If the element cannot be found, the function returns without throwing
375  * any exception.
376  *
377  * If the queue contains several times this element, then the one with the
378  * smallest index is removed.
379  *
380  * @param val the element we wish to remove.
381  */
382  void erase(const Val& val);
383 
384  /**
385  * @brief Modifies the priority of the element at position "index" of the
386  * queue.
387  *
388  * @param index The index of the element to update.
389  * @param new_priority The element's new priority.
390  * @return Returns the elements new priority.
391  * @throw NotFound Raised if the element cannot be found.
392  */
393  Size setPriorityByPos(Size index, const Priority& new_priority);
394 
395  /**
396  * @brief Modifies the priority of the element at position "index" of the
397  * queue.
398  *
399  * @param index The index of the element to update.
400  * @param new_priority The element's new priority.
401  * @return Returns the elements new priority.
402  * @throw NotFound Raised if the element cannot be found.
403  */
404  Size setPriorityByPos(Size index, Priority&& new_priority);
405 
406  /**
407  * @brief Modifies the priority of each instance of a given element.
408  * @param elt The value to update.
409  * @param new_priority The values new priority.
410  * @throw NotFound Raised if the element cannot be found.
411  */
412  void setPriority(const Val& elt, const Priority& new_priority);
413 
414  /**
415  * @brief Returns the priority of an instance of the value passed in
416  * argument.
417  *
418  * Of course, this method is really meaningful only when there is only one
419  * instance of the given element within the PriorityQueue.
420  *
421  * @param elt The element for which the priority is returned.
422  * @return Returns the priority of an instance of the value passed in
423  * argument.
424  * @throw NotFound Raised if the element cannot be found.
425  */
426  const Priority& priority(const Val& elt) const;
427 
428  /**
429  * @brief Removes all the elements from the queue.
430  */
431  void clear();
432 
433  /**
434  * @brief Returns a gum::HashTable the keys of which are the values stored
435  * in the queue.
436  *
437  * The keys of the gum::HashTable correspond to the values stored in the
438  * priority queue and, for each key, the corresponding value is the list of
439  * indices in the queue where we can find the key.
440  *
441  * @return Returns a gum::HashTable the keys of which are the values stored
442  * in the queue.
443  */
444  const HashTable< Val, std::vector< Size > >& allValues() const;
445 
446  /**
447  * @brief Displays the content of the queue.
448  * @return Returns the content of the queue.
449  */
450  std::string toString() const;
451 
452  /// @}
453  // ============================================================================
454  /// @name Fine tuning
455  // ============================================================================
456  /// @{
457 
458  /**
459  * @brief Return the size of the internal structure storing the priority
460  * queue.
461  * @return Return the size of the internal structure storing the priority
462  * queue.
463  */
464  Size capacity() const noexcept;
465 
466  /**
467  * @brief Changes the size of the internal structure storing the priority
468  * queue.
469  * @param new_size The internal structure new size.
470  * @return Changes the size of the internal structure storing the priority
471  * queue.
472  */
473  void resize(Size new_size);
474 
475  /// @}
476 
477  private:
478  /// An array storing all the elements of the heap as well as their score.
479  std::vector< std::pair< Priority, const Val* >, HeapAlloc > heap__;
480 
481  /// A hashtable for quickly finding the elements by their value.
482  HashTable< Val, std::vector< Size >, IndexAlloc > indices__;
483 
484  /// The number of elements in the heap.
485  Size nb_elements__{0};
486 
487  /// Comparison function.
488  Cmp cmp__;
489  };
490 
491 } /* namespace gum */
492 
493 
494 #ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
495 # ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
496 extern template class gum::MultiPriorityQueue< std::string >;
497 # endif
498 #endif
499 #ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
500 # ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
501 extern template class gum::MultiPriorityQueue< int, int >;
502 # endif
503 #endif
504 
505 
506 // always include the implementation of the templates
507 #include <agrum/tools/core/multiPriorityQueue_tpl.h>
508 
509 #endif /* GUM_MULTI_PRIORITY_QUEUE_H */
#define GUM_MULTIPLE_PRIORITY_QUEUE_DEFAULT_CAPACITY