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