aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
multiPriorityQueue_tpl.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 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(Cmp compare, Size capacity) :
37  _indices_(capacity >> 1, true, false), _cmp_(compare) {
38  _heap_.reserve(capacity);
39 
40  // for debugging purposes
41  GUM_CONSTRUCTOR(MultiPriorityQueue);
42  }
43 
44  // initializer list constructor
45  template < typename Val, typename Priority, typename Cmp, typename Alloc >
48  _indices_(Size(list.size()) / 2, true, false) {
49  // fill the queue
51  for (const auto& elt: list) {
53  }
54 
55  // for debugging purposes
57  }
58 
59  // copy constructor
60  template < typename Val, typename Priority, typename Cmp, typename Alloc >
65  // for debugging purposes
67 
68  // fill the heap structure
69  for (const auto& val_and_index: _indices_) {
70  const Val* val = &(val_and_index.first);
71  const std::vector< Size >& vect = val_and_index.second;
72  for (auto index: vect) {
74  }
75  }
76  }
77 
78  // generalized copy constructor
79  template < typename Val, typename Priority, typename Cmp, typename Alloc >
80  template < typename OtherAlloc >
85  // for debugging purposes
87 
88  // fill the heap structure
89  if (_nb_elements_) {
91  for (const auto& elt: from._heap_) {
93  }
94  for (const auto& val_and_index: _indices_) {
95  const Val* val = &(val_and_index.first);
96  const std::vector< Size >& vect = val_and_index.second;
97  for (auto index: vect) {
99  }
100  }
101  }
102  }
103 
104  // move constructor
105  template < typename Val, typename Priority, typename Cmp, typename Alloc >
110  _cmp_(std::move(from._cmp_)) {
111  // for debugging purposes
113  }
114 
115  // destructor
116  template < typename Val, typename Priority, typename Cmp, typename Alloc >
118  // for debugging purposes
120  }
121 
122  // copy operator
123  template < typename Val, typename Priority, typename Cmp, typename Alloc >
126  const MultiPriorityQueue< Val, Priority, Cmp, Alloc >& from) {
127  // for debugging purposes
129 
130  try {
131  // set the comprison function
132  _cmp_ = from._cmp_;
133 
134  // copy the indices and the heap
136  _heap_ = from._heap_;
138 
139  // restore the link between _indices_ and _heap_
140  for (const auto& val_and_index: _indices_) {
141  const Val* val = &(val_and_index.first);
142  const std::vector< Size >& vect = val_and_index.second;
143  for (auto index: vect) {
144  _heap_[index].second = val;
145  }
146  }
147  } catch (...) {
148  _heap_.clear();
149  _indices_.clear();
150  _nb_elements_ = 0;
151 
152  throw;
153  }
154 
155  return *this;
156  }
157 
158  // generalized copy operator
159  template < typename Val, typename Priority, typename Cmp, typename Alloc >
160  template < typename OtherAlloc >
164  // for debugging purposes
166 
167  try {
168  // set the comprison function
169  _cmp_ = from._cmp_;
170 
171  // copy the indices and the heap
174 
175  // restore the link between _indices_ and _heap_
176  _heap_.clear();
178  for (const auto& elt: from._heap_) {
180  }
181  for (const auto& val_and_index: _indices_) {
182  const Val* val = &(val_and_index.first);
183  const std::vector< Size >& vect = val_and_index.second;
184  for (auto index: vect) {
185  _heap_[index].second = val;
186  }
187  }
188  } catch (...) {
189  _heap_.clear();
190  _indices_.clear();
191  _nb_elements_ = 0;
192 
193  throw;
194  }
195 
196  return *this;
197  }
198 
199  // move operator
200  template < typename Val, typename Priority, typename Cmp, typename Alloc >
204  // avoid self assignment
205  if (this != &from) {
206  // for debugging purposes
208 
209  _cmp_ = std::move(from._cmp_);
211  _heap_ = std::move(from._heap_);
213  }
214 
215  return *this;
216  }
217 
218  // returns the element at the top of the priority queue
219  template < typename Val, typename Priority, typename Cmp, typename Alloc >
221  if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
222 
223  return *(_heap_[0].second);
224  }
225 
226  // returns the priority of the top element
227  template < typename Val, typename Priority, typename Cmp, typename Alloc >
229  if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
230 
231  return _heap_[0].first;
232  }
233 
234  // returns the number of elements in the priority queue
235  template < typename Val, typename Priority, typename Cmp, typename Alloc >
237  return _nb_elements_;
238  }
239 
240  // return the size of the array storing the priority queue
241  template < typename Val, typename Priority, typename Cmp, typename Alloc >
243  return Size(_heap_.capacity());
244  }
245 
246  // changes the size of the array storing the priority queue
247  template < typename Val, typename Priority, typename Cmp, typename Alloc >
249  if (new_size < _nb_elements_) return;
250 
253  }
254 
255  // removes all the elements from the queue
256  template < typename Val, typename Priority, typename Cmp, typename Alloc >
258  _nb_elements_ = 0;
259  _heap_.clear();
260  _indices_.clear();
261  }
262 
263  // removes the element at index elt from the priority queue
264  template < typename Val, typename Priority, typename Cmp, typename Alloc >
266  if (index >= _nb_elements_) return;
267 
268  // remove the element from the hashtable
269  const Val& del_val = *(_heap_[index].second);
271  if (vect_index.size() == 1)
273  else {
274  for (auto& v_index: vect_index) {
275  if (v_index == index) {
276  v_index = vect_index.back();
278  break;
279  }
280  }
281  }
282 
283  // put the last element at the "index" location
284  std::pair< Priority, const Val* > last = std::move(_heap_.back());
285  _heap_.pop_back();
286  --_nb_elements_;
287 
288  if (!_nb_elements_ || (index == _nb_elements_)) return;
289 
290  // restore the heap property
291  Size i = index;
292 
293  for (Size j = (index << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
294  // let j be the max child
295  if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
296 
297  // if "last" is lower than heap[j], "last" must be stored at index i
298  if (_cmp_(last.first, _heap_[j].first)) break;
299 
300  // else pull up the jth node
301  _heap_[i] = std::move(_heap_[j]);
303  for (auto& v_index: vect_index) {
304  if (v_index == j) {
305  v_index = i;
306  break;
307  }
308  }
309  }
310 
311  // put "last" back into the heap
312  _heap_[i] = std::move(last);
314  for (auto& v_index: last_indices) {
315  if (v_index == _nb_elements_) {
316  v_index = i;
317  break;
318  }
319  }
320  }
321 
322  // removes a given element from the priority queue (but does not return it)
323  template < typename Val, typename Priority, typename Cmp, typename Alloc >
325  try {
326  eraseByPos(_indices_[val][0]);
327  } catch (NotFound&) {}
328  }
329 
330  // removes the top of the priority queue (but does not return it)
331  template < typename Val, typename Priority, typename Cmp, typename Alloc >
333  eraseByPos(0);
334  }
335 
336  // removes the top element from the priority queue and return it
337  template < typename Val, typename Priority, typename Cmp, typename Alloc >
339  if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
340 
341  Val v = *(_heap_[0].second);
342  eraseByPos(0);
343 
344  return v;
345  }
346 
347  // returns a hashtable the keys of which are the values stored in the queue
348  template < typename Val, typename Priority, typename Cmp, typename Alloc >
349  INLINE const HashTable< Val, std::vector< Size > >&
351  return reinterpret_cast< const HashTable< Val, std::vector< Size > >& >(_indices_);
352  }
353 
354  // inserts a new (a copy) element in the priority queue
355  template < typename Val, typename Priority, typename Cmp, typename Alloc >
357  const Priority& priority) {
358  // create the entry in the indices hashtable
359  const Val* new_val;
360  std::vector< Size >* new_vect;
361  if (!_indices_.exists(val)) {
362  auto& new_elt = _indices_.insert(val, std::vector< Size >());
363  new_val = &(new_elt.first);
364  new_vect = &(new_elt.second);
365  } else {
366  new_val = &(_indices_.key(val));
367  new_vect = &(_indices_[val]);
368  }
369 
370  try {
371  new_vect->push_back(0);
372  } catch (...) {
373  if (new_vect->empty()) { _indices_.erase(val); }
374  throw;
375  }
376 
377  try {
379  } catch (...) {
380  if (new_vect->size() == 1) { _indices_.erase(val); }
381  throw;
382  }
383 
385  ++_nb_elements_;
386 
387  // restore the heap property
388  Size i = _nb_elements_ - 1;
389 
390  for (Size j = (i - 1) >> 1; i && _cmp_(new_heap_val.first, _heap_[j].first);
391  i = j, j = (j - 1) >> 1) {
392  _heap_[i] = std::move(_heap_[j]);
394  for (auto& index: vect_index) {
395  if (index == j) {
396  index = i;
397  break;
398  }
399  }
400  }
401 
402  // put the new bucket into the heap
404  _heap_[i].second = new_val;
405  new_vect->back() = i;
406 
407  return i;
408  }
409 
410  // inserts a new (a copy) element in the priority queue
411  template < typename Val, typename Priority, typename Cmp, typename Alloc >
413  // create the entry in the indices hashtable
414  const Val* new_val;
415  std::vector< Size >* new_vect;
416  if (!_indices_.exists(val)) {
417  auto& new_elt = _indices_.insert(std::move(val), std::vector< Size >());
418  new_val = &(new_elt.first);
419  new_vect = &(new_elt.second);
420  } else {
421  new_val = &(_indices_.key(val));
422  new_vect = &(_indices_[val]);
423  }
424 
425  try {
426  new_vect->push_back(0);
427  } catch (...) {
428  if (new_vect->empty()) { _indices_.erase(*new_val); }
429  throw;
430  }
431 
432  try {
434  } catch (...) {
435  if (new_vect->size() == 1) { _indices_.erase(*new_val); }
436  throw;
437  }
438 
440  ++_nb_elements_;
441 
442  // restore the heap property
443  Size i = _nb_elements_ - 1;
444 
445  for (Size j = (i - 1) >> 1; i && _cmp_(new_heap_val.first, _heap_[j].first);
446  i = j, j = (j - 1) >> 1) {
447  _heap_[i] = std::move(_heap_[j]);
449  for (auto& index: vect_index) {
450  if (index == j) {
451  index = i;
452  break;
453  }
454  }
455  }
456 
457  // put the new bucket into the heap
459  _heap_[i].second = new_val;
460  new_vect->back() = i;
461 
462  return i;
463  }
464 
465  // emplace a new element into the priority queue
466  template < typename Val, typename Priority, typename Cmp, typename Alloc >
467  template < typename... Args >
470  = std::make_pair< Val, Priority >(std::forward< Args >(args)...);
472  }
473 
474  // indicates whether the priority queue is empty
475  template < typename Val, typename Priority, typename Cmp, typename Alloc >
476  INLINE bool MultiPriorityQueue< Val, Priority, Cmp, Alloc >::empty() const noexcept {
477  return (_nb_elements_ == 0);
478  }
479 
480  // indicates whether the priority queue contains a given value
481  template < typename Val, typename Priority, typename Cmp, typename Alloc >
483  return _indices_.exists(val);
484  }
485 
486  // returns the element at position "index" in the priority queue
487  template < typename Val, typename Priority, typename Cmp, typename Alloc >
489  if (index >= _nb_elements_) {
490  GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
491  }
492 
493  return *(_heap_[index].second);
494  }
495 
496  // displays the content of the queue
497  template < typename Val, typename Priority, typename Cmp, typename Alloc >
499  bool deja = false;
501  stream << "[";
502 
503  for (Size i = 0; i != _nb_elements_; ++i, deja = true) {
504  if (deja) stream << " , ";
505 
506  stream << "(" << _heap_[i].first << " , " << *(_heap_[i].second) << ")";
507  }
508 
509  stream << "]";
510 
511  return stream.str();
512  }
513 
514  // changes the size of the internal structure storing the priority queue
515  template < typename Val, typename Priority, typename Cmp, typename Alloc >
517  Size index,
518  const Priority& new_priority) {
519  // check whether the element the priority of which should be changed exists
520  if (index >= _nb_elements_) {
521  GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
522  }
523 
524  // get the element itself
525  const Val* val = _heap_[index].second;
526 
527  // restore the heap property
528  Size i = index;
529 
530  // move val upward if needed
531  for (Size j = (i - 1) >> 1; i && _cmp_(new_priority, _heap_[j].first);
532  i = j, j = (j - 1) >> 1) {
533  _heap_[i] = std::move(_heap_[j]);
535  for (auto& idx: vect_index) {
536  if (idx == j) {
537  idx = i;
538  break;
539  }
540  }
541  }
542 
543  // move val downward if needed
544  for (Size j = (i << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
545  // let j be the max child
546  if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
547 
548  // if "val" is lower than heap[j], "val" must be stored at index i
549  if (_cmp_(new_priority, _heap_[j].first)) break;
550 
551  // else pull up the jth node
552  _heap_[i] = std::move(_heap_[j]);
554  for (auto& idx: vect_index) {
555  if (idx == j) {
556  idx = i;
557  break;
558  }
559  }
560  }
561 
562  // update the index of val
564  _heap_[i].second = val;
566  for (auto& idx: vect_index) {
567  if (idx == index) {
568  idx = i;
569  break;
570  }
571  }
572 
573  return i;
574  }
575 
576  // changes the size of the internal structure storing the priority queue
577  template < typename Val, typename Priority, typename Cmp, typename Alloc >
580  // check whether the element the priority of which should be changed exists
581  if (index >= _nb_elements_) {
582  GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
583  }
584 
585  // get the element itself
586  const Val* val = _heap_[index].second;
587 
588  // restore the heap property
589  Size i = index;
590 
591  // move val upward if needed
592  for (Size j = (i - 1) >> 1; i && _cmp_(new_priority, _heap_[j].first);
593  i = j, j = (j - 1) >> 1) {
594  _heap_[i] = std::move(_heap_[j]);
596  for (auto& idx: vect_index) {
597  if (idx == j) {
598  idx = i;
599  break;
600  }
601  }
602  }
603 
604  // move val downward if needed
605  for (Size j = (i << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
606  // let j be the max child
607  if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
608 
609  // if "val" is lower than heap[j], "val" must be stored at index i
610  if (_cmp_(new_priority, _heap_[j].first)) break;
611 
612  // else pull up the jth node
613  _heap_[i] = std::move(_heap_[j]);
615  for (auto& idx: vect_index) {
616  if (idx == j) {
617  idx = i;
618  break;
619  }
620  }
621  }
622 
623  // update the index of val
625  _heap_[i].second = val;
627  for (auto& idx: vect_index) {
628  if (idx == index) {
629  idx = i;
630  break;
631  }
632  }
633 
634  return i;
635  }
636 
637  // modifies the priority of each instance of a given element
638  template < typename Val, typename Priority, typename Cmp, typename Alloc >
640  const Priority& new_priority) {
642 
643  for (auto index: vect_index) {
645  }
646  }
647 
648  // modifies the priority of each instance of a given element
649  template < typename Val, typename Priority, typename Cmp, typename Alloc >
650  INLINE const Priority&
652  return _heap_[_indices_[elt][0]].first;
653  }
654 
655  // A \c << operator for priority queues
656  template < typename Val, typename Priority, typename Cmp, typename Alloc >
658  const MultiPriorityQueue< Val, Priority, Cmp, Alloc >& queue) {
659  stream << queue.toString();
660  return stream;
661  }
662 
663 } /* namespace gum */
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643