aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
multiPriorityQueue_tpl.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 Template implementations of multi priority queues.
25  *
26  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27  */
28 
29 // to help IDE parser
30 #include <agrum/tools/core/multiPriorityQueue.h>
31 
32 namespace gum {
33 
34  // basic constructor
35  template < typename Val, typename Priority, typename Cmp, typename Alloc >
36  MultiPriorityQueue< Val, Priority, Cmp, Alloc >::MultiPriorityQueue(
37  Cmp compare,
38  Size capacity) :
39  indices__(capacity >> 1, true, false),
40  cmp__(compare) {
41  heap__.reserve(capacity);
42 
43  // for debugging purposes
44  GUM_CONSTRUCTOR(MultiPriorityQueue);
45  }
46 
47  // initializer list constructor
48  template < typename Val, typename Priority, typename Cmp, typename Alloc >
51  indices__(Size(list.size()) / 2, true, false) {
52  // fill the queue
54  for (const auto& elt: list) {
56  }
57 
58  // for debugging purposes
60  }
61 
62  // copy constructor
63  template < typename Val, typename Priority, typename Cmp, typename Alloc >
68  cmp__(from.cmp__) {
69  // for debugging purposes
71 
72  // fill the heap structure
73  for (const auto& val_and_index: indices__) {
74  const Val* val = &(val_and_index.first);
75  const std::vector< Size >& vect = val_and_index.second;
76  for (auto index: vect) {
78  }
79  }
80  }
81 
82  // generalized copy constructor
83  template < typename Val, typename Priority, typename Cmp, typename Alloc >
84  template < typename OtherAlloc >
89  // for debugging purposes
91 
92  // fill the heap structure
93  if (nb_elements__) {
95  for (const auto& elt: from.heap__) {
97  }
98  for (const auto& val_and_index: indices__) {
99  const Val* val = &(val_and_index.first);
100  const std::vector< Size >& vect = val_and_index.second;
101  for (auto index: vect) {
102  heap__[index].second = val;
103  }
104  }
105  }
106  }
107 
108  // move constructor
109  template < typename Val, typename Priority, typename Cmp, typename Alloc >
115  // for debugging purposes
117  }
118 
119  // destructor
120  template < typename Val, typename Priority, typename Cmp, typename Alloc >
122  // for debugging purposes
124  }
125 
126  // copy operator
127  template < typename Val, typename Priority, typename Cmp, typename Alloc >
130  const MultiPriorityQueue< Val, Priority, Cmp, Alloc >& from) {
131  // for debugging purposes
133 
134  try {
135  // set the comprison function
136  cmp__ = from.cmp__;
137 
138  // copy the indices and the heap
140  heap__ = from.heap__;
142 
143  // restore the link between indices__ and heap__
144  for (const auto& val_and_index: indices__) {
145  const Val* val = &(val_and_index.first);
146  const std::vector< Size >& vect = val_and_index.second;
147  for (auto index: vect) {
148  heap__[index].second = val;
149  }
150  }
151  } catch (...) {
152  heap__.clear();
153  indices__.clear();
154  nb_elements__ = 0;
155 
156  throw;
157  }
158 
159  return *this;
160  }
161 
162  // generalized copy operator
163  template < typename Val, typename Priority, typename Cmp, typename Alloc >
164  template < typename OtherAlloc >
168  // for debugging purposes
170 
171  try {
172  // set the comprison function
173  cmp__ = from.cmp__;
174 
175  // copy the indices and the heap
178 
179  // restore the link between indices__ and heap__
180  heap__.clear();
182  for (const auto& elt: from.heap__) {
184  }
185  for (const auto& val_and_index: indices__) {
186  const Val* val = &(val_and_index.first);
187  const std::vector< Size >& vect = val_and_index.second;
188  for (auto index: vect) {
189  heap__[index].second = val;
190  }
191  }
192  } catch (...) {
193  heap__.clear();
194  indices__.clear();
195  nb_elements__ = 0;
196 
197  throw;
198  }
199 
200  return *this;
201  }
202 
203  // move operator
204  template < typename Val, typename Priority, typename Cmp, typename Alloc >
208  // avoid self assignment
209  if (this != &from) {
210  // for debugging purposes
212 
213  cmp__ = std::move(from.cmp__);
215  heap__ = std::move(from.heap__);
217  }
218 
219  return *this;
220  }
221 
222  // returns the element at the top of the priority queue
223  template < typename Val, typename Priority, typename Cmp, typename Alloc >
225  if (!nb_elements__) { GUM_ERROR(NotFound, "empty priority queue"); }
226 
227  return *(heap__[0].second);
228  }
229 
230  // returns the priority of the top element
231  template < typename Val, typename Priority, typename Cmp, typename Alloc >
232  INLINE const Priority&
234  if (!nb_elements__) { GUM_ERROR(NotFound, "empty priority queue"); }
235 
236  return heap__[0].first;
237  }
238 
239  // returns the number of elements in the priority queue
240  template < typename Val, typename Priority, typename Cmp, typename Alloc >
241  INLINE Size
242  MultiPriorityQueue< Val, Priority, Cmp, Alloc >::size() const noexcept {
243  return nb_elements__;
244  }
245 
246  // return the size of the array storing the priority queue
247  template < typename Val, typename Priority, typename Cmp, typename Alloc >
248  INLINE Size
249  MultiPriorityQueue< Val, Priority, Cmp, Alloc >::capacity() const noexcept {
250  return Size(heap__.capacity());
251  }
252 
253  // changes the size of the array storing the priority queue
254  template < typename Val, typename Priority, typename Cmp, typename Alloc >
255  INLINE void
257  if (new_size < nb_elements__) return;
258 
261  }
262 
263  // removes all the elements from the queue
264  template < typename Val, typename Priority, typename Cmp, typename Alloc >
266  nb_elements__ = 0;
267  heap__.clear();
268  indices__.clear();
269  }
270 
271  // removes the element at index elt from the priority queue
272  template < typename Val, typename Priority, typename Cmp, typename Alloc >
274  if (index >= nb_elements__) return;
275 
276  // remove the element from the hashtable
277  const Val& del_val = *(heap__[index].second);
279  if (vect_index.size() == 1)
281  else {
282  for (auto& v_index: vect_index) {
283  if (v_index == index) {
284  v_index = vect_index.back();
286  break;
287  }
288  }
289  }
290 
291  // put the last element at the "index" location
292  std::pair< Priority, const Val* > last = std::move(heap__.back());
293  heap__.pop_back();
294  --nb_elements__;
295 
296  if (!nb_elements__ || (index == nb_elements__)) return;
297 
298  // restore the heap property
299  Size i = index;
300 
301  for (Size j = (index << 1) + 1; j < nb_elements__; i = j, j = (j << 1) + 1) {
302  // let j be the max child
303  if ((j + 1 < nb_elements__) && cmp__(heap__[j + 1].first, heap__[j].first))
304  ++j;
305 
306  // if "last" is lower than heap[j], "last" must be stored at index i
307  if (cmp__(last.first, heap__[j].first)) break;
308 
309  // else pull up the jth node
310  heap__[i] = std::move(heap__[j]);
312  for (auto& v_index: vect_index) {
313  if (v_index == j) {
314  v_index = i;
315  break;
316  }
317  }
318  }
319 
320  // put "last" back into the heap
321  heap__[i] = std::move(last);
323  for (auto& v_index: last_indices) {
324  if (v_index == nb_elements__) {
325  v_index = i;
326  break;
327  }
328  }
329  }
330 
331  // removes a given element from the priority queue (but does not return it)
332  template < typename Val, typename Priority, typename Cmp, typename Alloc >
333  INLINE void
335  try {
336  eraseByPos(indices__[val][0]);
337  } catch (NotFound&) {}
338  }
339 
340  // removes the top of the priority queue (but does not return it)
341  template < typename Val, typename Priority, typename Cmp, typename Alloc >
343  eraseByPos(0);
344  }
345 
346  // removes the top element from the priority queue and return it
347  template < typename Val, typename Priority, typename Cmp, typename Alloc >
349  if (!nb_elements__) { GUM_ERROR(NotFound, "empty priority queue"); }
350 
351  Val v = *(heap__[0].second);
352  eraseByPos(0);
353 
354  return v;
355  }
356 
357  // returns a hashtable the keys of which are the values stored in the queue
358  template < typename Val, typename Priority, typename Cmp, typename Alloc >
359  INLINE const HashTable< Val, std::vector< Size > >&
361  return reinterpret_cast< const HashTable< Val, std::vector< Size > >& >(
362  indices__);
363  }
364 
365  // inserts a new (a copy) element in the priority queue
366  template < typename Val, typename Priority, typename Cmp, typename Alloc >
368  const Val& val,
369  const Priority& priority) {
370  // create the entry in the indices hashtable
371  const Val* new_val;
372  std::vector< Size >* new_vect;
373  if (!indices__.exists(val)) {
374  auto& new_elt = indices__.insert(val, std::vector< Size >());
375  new_val = &(new_elt.first);
376  new_vect = &(new_elt.second);
377  } else {
378  new_val = &(indices__.key(val));
379  new_vect = &(indices__[val]);
380  }
381 
382  try {
383  new_vect->push_back(0);
384  } catch (...) {
385  if (new_vect->empty()) { indices__.erase(val); }
386  throw;
387  }
388 
389  try {
391  } catch (...) {
392  if (new_vect->size() == 1) { indices__.erase(val); }
393  throw;
394  }
395 
396  std::pair< Priority, const Val* > new_heap_val
398  ++nb_elements__;
399 
400  // restore the heap property
401  Size i = nb_elements__ - 1;
402 
403  for (Size j = (i - 1) >> 1; i && cmp__(new_heap_val.first, heap__[j].first);
404  i = j, j = (j - 1) >> 1) {
405  heap__[i] = std::move(heap__[j]);
407  for (auto& index: vect_index) {
408  if (index == j) {
409  index = i;
410  break;
411  }
412  }
413  }
414 
415  // put the new bucket into the heap
417  heap__[i].second = new_val;
418  new_vect->back() = i;
419 
420  return i;
421  }
422 
423  // inserts a new (a copy) element in the priority queue
424  template < typename Val, typename Priority, typename Cmp, typename Alloc >
425  Size
427  Priority&& priority) {
428  // create the entry in the indices hashtable
429  const Val* new_val;
430  std::vector< Size >* new_vect;
431  if (!indices__.exists(val)) {
432  auto& new_elt = indices__.insert(std::move(val), std::vector< Size >());
433  new_val = &(new_elt.first);
434  new_vect = &(new_elt.second);
435  } else {
436  new_val = &(indices__.key(val));
437  new_vect = &(indices__[val]);
438  }
439 
440  try {
441  new_vect->push_back(0);
442  } catch (...) {
443  if (new_vect->empty()) { indices__.erase(*new_val); }
444  throw;
445  }
446 
447  try {
449  std::pair< Priority, const Val* >(std::move(priority), new_val));
450  } catch (...) {
451  if (new_vect->size() == 1) { indices__.erase(*new_val); }
452  throw;
453  }
454 
455  std::pair< Priority, const Val* > new_heap_val
457  ++nb_elements__;
458 
459  // restore the heap property
460  Size i = nb_elements__ - 1;
461 
462  for (Size j = (i - 1) >> 1; i && cmp__(new_heap_val.first, heap__[j].first);
463  i = j, j = (j - 1) >> 1) {
464  heap__[i] = std::move(heap__[j]);
466  for (auto& index: vect_index) {
467  if (index == j) {
468  index = i;
469  break;
470  }
471  }
472  }
473 
474  // put the new bucket into the heap
476  heap__[i].second = new_val;
477  new_vect->back() = i;
478 
479  return i;
480  }
481 
482  // emplace a new element into the priority queue
483  template < typename Val, typename Priority, typename Cmp, typename Alloc >
484  template < typename... Args >
485  INLINE Size
488  = std::make_pair< Val, Priority >(std::forward< Args >(args)...);
490  }
491 
492  // indicates whether the priority queue is empty
493  template < typename Val, typename Priority, typename Cmp, typename Alloc >
494  INLINE bool
495  MultiPriorityQueue< Val, Priority, Cmp, Alloc >::empty() const noexcept {
496  return (nb_elements__ == 0);
497  }
498 
499  // indicates whether the priority queue contains a given value
500  template < typename Val, typename Priority, typename Cmp, typename Alloc >
502  const Val& val) const {
503  return indices__.exists(val);
504  }
505 
506  // returns the element at position "index" in the priority queue
507  template < typename Val, typename Priority, typename Cmp, typename Alloc >
509  Size index) const {
510  if (index >= nb_elements__) {
511  GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue");
512  }
513 
514  return *(heap__[index].second);
515  }
516 
517  // displays the content of the queue
518  template < typename Val, typename Priority, typename Cmp, typename Alloc >
520  bool deja = false;
522  stream << "[";
523 
524  for (Size i = 0; i != nb_elements__; ++i, deja = true) {
525  if (deja) stream << " , ";
526 
527  stream << "(" << heap__[i].first << " , " << *(heap__[i].second) << ")";
528  }
529 
530  stream << "]";
531 
532  return stream.str();
533  }
534 
535  // changes the size of the internal structure storing the priority queue
536  template < typename Val, typename Priority, typename Cmp, typename Alloc >
538  Size index,
539  const Priority& new_priority) {
540  // check whether the element the priority of which should be changed exists
541  if (index >= nb_elements__) {
542  GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue");
543  }
544 
545  // get the element itself
546  const Val* val = heap__[index].second;
547 
548  // restore the heap property
549  Size i = index;
550 
551  // move val upward if needed
552  for (Size j = (i - 1) >> 1; i && cmp__(new_priority, heap__[j].first);
553  i = j, j = (j - 1) >> 1) {
554  heap__[i] = std::move(heap__[j]);
556  for (auto& idx: vect_index) {
557  if (idx == j) {
558  idx = i;
559  break;
560  }
561  }
562  }
563 
564  // move val downward if needed
565  for (Size j = (i << 1) + 1; j < nb_elements__; i = j, j = (j << 1) + 1) {
566  // let j be the max child
567  if ((j + 1 < nb_elements__) && cmp__(heap__[j + 1].first, heap__[j].first))
568  ++j;
569 
570  // if "val" is lower than heap[j], "val" must be stored at index i
571  if (cmp__(new_priority, heap__[j].first)) break;
572 
573  // else pull up the jth node
574  heap__[i] = std::move(heap__[j]);
576  for (auto& idx: vect_index) {
577  if (idx == j) {
578  idx = i;
579  break;
580  }
581  }
582  }
583 
584  // update the index of val
586  heap__[i].second = val;
588  for (auto& idx: vect_index) {
589  if (idx == index) {
590  idx = i;
591  break;
592  }
593  }
594 
595  return i;
596  }
597 
598  // changes the size of the internal structure storing the priority queue
599  template < typename Val, typename Priority, typename Cmp, typename Alloc >
601  Size index,
603  // check whether the element the priority of which should be changed exists
604  if (index >= nb_elements__) {
605  GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue");
606  }
607 
608  // get the element itself
609  const Val* val = heap__[index].second;
610 
611  // restore the heap property
612  Size i = index;
613 
614  // move val upward if needed
615  for (Size j = (i - 1) >> 1; i && cmp__(new_priority, heap__[j].first);
616  i = j, j = (j - 1) >> 1) {
617  heap__[i] = std::move(heap__[j]);
619  for (auto& idx: vect_index) {
620  if (idx == j) {
621  idx = i;
622  break;
623  }
624  }
625  }
626 
627  // move val downward if needed
628  for (Size j = (i << 1) + 1; j < nb_elements__; i = j, j = (j << 1) + 1) {
629  // let j be the max child
630  if ((j + 1 < nb_elements__) && cmp__(heap__[j + 1].first, heap__[j].first))
631  ++j;
632 
633  // if "val" is lower than heap[j], "val" must be stored at index i
634  if (cmp__(new_priority, heap__[j].first)) break;
635 
636  // else pull up the jth node
637  heap__[i] = std::move(heap__[j]);
639  for (auto& idx: vect_index) {
640  if (idx == j) {
641  idx = i;
642  break;
643  }
644  }
645  }
646 
647  // update the index of val
649  heap__[i].second = val;
651  for (auto& idx: vect_index) {
652  if (idx == index) {
653  idx = i;
654  break;
655  }
656  }
657 
658  return i;
659  }
660 
661  // modifies the priority of each instance of a given element
662  template < typename Val, typename Priority, typename Cmp, typename Alloc >
664  const Val& elt,
665  const Priority& new_priority) {
667 
668  for (auto index: vect_index) {
670  }
671  }
672 
673  // modifies the priority of each instance of a given element
674  template < typename Val, typename Priority, typename Cmp, typename Alloc >
676  const Val& elt) const {
677  return heap__[indices__[elt][0]].first;
678  }
679 
680  // A \c << operator for priority queues
681  template < typename Val, typename Priority, typename Cmp, typename Alloc >
682  INLINE std::ostream&
684  const MultiPriorityQueue< Val, Priority, Cmp, Alloc >& queue) {
685  stream << queue.toString();
686  return stream;
687  }
688 
689 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669