aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
multiPriorityQueue_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
43
50
51// to help IDE parser
53
54namespace gum {
55
56 // basic constructor
57 template < typename Val, typename Priority, typename Cmp >
59 _indices_(capacity >> 1, true, false), _cmp_(compare) {
60 _heap_.reserve(capacity);
61
62 // for debugging purposes
63 GUM_CONSTRUCTOR(MultiPriorityQueue);
64 }
65
66 // initializer list constructor
67 template < typename Val, typename Priority, typename Cmp >
69 std::initializer_list< std::pair< Val, Priority > > list) :
70 _indices_(Size(list.size()) / 2, true, false) {
71 // fill the queue
72 _heap_.reserve(list.size());
73 for (const auto& elt: list) {
74 insert(elt.first, elt.second);
75 }
76
77 // for debugging purposes
78 GUM_CONSTRUCTOR(MultiPriorityQueue);
79 }
80
81 // copy constructor
82 template < typename Val, typename Priority, typename Cmp >
86 _cmp_(from._cmp_) {
87 // for debugging purposes
88 GUM_CONS_CPY(MultiPriorityQueue);
89
90 // fill the heap structure
91 for (const auto& val_and_index: _indices_) {
92 const Val* val = &(val_and_index.first);
93 const std::vector< Size >& vect = val_and_index.second;
94 for (auto index: vect) {
95 _heap_[index].second = val;
96 }
97 }
98 }
99
100 // move constructor
101 template < typename Val, typename Priority, typename Cmp >
104 _heap_(std::move(from._heap_)), _indices_(std::move(from._indices_)),
105 _nb_elements_(std::move(from._nb_elements_)), _cmp_(std::move(from._cmp_)) {
106 // for debugging purposes
107 GUM_CONS_MOV(MultiPriorityQueue);
108 }
109
110 // destructor
111 template < typename Val, typename Priority, typename Cmp >
113 // for debugging purposes
114 GUM_DESTRUCTOR(MultiPriorityQueue);
115 }
116
117 // copy operator
118 template < typename Val, typename Priority, typename Cmp >
121 // for debugging purposes
122 GUM_OP_CPY(MultiPriorityQueue);
123
124 try {
125 // set the comprison function
126 _cmp_ = from._cmp_;
127
128 // copy the indices and the heap
129 _indices_ = from._indices_;
130 _heap_ = from._heap_;
132
133 // restore the link between _indices_ and _heap_
134 for (const auto& val_and_index: _indices_) {
135 const Val* val = &(val_and_index.first);
136 const std::vector< Size >& vect = val_and_index.second;
137 for (auto index: vect) {
138 _heap_[index].second = val;
139 }
140 }
141 } catch (...) {
142 _heap_.clear();
143 _indices_.clear();
144 _nb_elements_ = 0;
145
146 throw;
147 }
148
149 return *this;
150 }
151
152 // move operator
153 template < typename Val, typename Priority, typename Cmp >
156 // avoid self assignment
157 if (this != &from) {
158 // for debugging purposes
159 GUM_OP_MOV(MultiPriorityQueue);
160
161 _cmp_ = std::move(from._cmp_);
162 _indices_ = std::move(from._indices_);
163 _heap_ = std::move(from._heap_);
164 _nb_elements_ = std::move(from._nb_elements_);
165 }
166
167 return *this;
168 }
169
170 // returns the element at the top of the priority queue
171 template < typename Val, typename Priority, typename Cmp >
173 if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
174
175 return *(_heap_[0].second);
176 }
177
178 // returns the priority of the top element
179 template < typename Val, typename Priority, typename Cmp >
181 if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
182
183 return _heap_[0].first;
184 }
185
186 // returns the number of elements in the priority queue
187 template < typename Val, typename Priority, typename Cmp >
191
192 // return the size of the array storing the priority queue
193 template < typename Val, typename Priority, typename Cmp >
195 return Size(_heap_.capacity());
196 }
197
198 // changes the size of the array storing the priority queue
199 template < typename Val, typename Priority, typename Cmp >
201 if (new_size < _nb_elements_) return;
202
203 _heap_.reserve(new_size);
204 _indices_.resize(new_size / 2);
205 }
206
207 // removes all the elements from the queue
208 template < typename Val, typename Priority, typename Cmp >
210 _nb_elements_ = 0;
211 _heap_.clear();
212 _indices_.clear();
213 }
214
215 // removes the element at index elt from the priority queue
216 template < typename Val, typename Priority, typename Cmp >
218 if (index >= _nb_elements_) return;
219
220 // remove the element from the hashtable
221 const Val& del_val = *(_heap_[index].second);
222 std::vector< Size >& vect_index = _indices_[del_val];
223 if (vect_index.size() == 1) _indices_.erase(del_val);
224 else {
225 for (auto& v_index: vect_index) {
226 if (v_index == index) {
227 v_index = vect_index.back();
228 vect_index.pop_back();
229 break;
230 }
231 }
232 }
234 // put the last element at the "index" location
235 std::pair< Priority, const Val* > last = std::move(_heap_.back());
236 _heap_.pop_back();
238
239 if (!_nb_elements_ || (index == _nb_elements_)) return;
240
241 // restore the heap property
242 Size i = index;
243
244 for (Size j = (index << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
245 // let j be the max child
246 if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
247
248 // if "last" is lower than heap[j], "last" must be stored at index i
249 if (_cmp_(last.first, _heap_[j].first)) break;
250
251 // else pull up the jth node
252 _heap_[i] = std::move(_heap_[j]);
253 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
254 for (auto& v_index: vect_index) {
255 if (v_index == j) {
256 v_index = i;
257 break;
259 }
260 }
261
262 // put "last" back into the heap
263 _heap_[i] = std::move(last);
264 std::vector< Size >& last_indices = _indices_[*(_heap_[i].second)];
265 for (auto& v_index: last_indices) {
266 if (v_index == _nb_elements_) {
267 v_index = i;
268 break;
269 }
270 }
272
273 // removes a given element from the priority queue (but does not return it)
274 template < typename Val, typename Priority, typename Cmp >
276 if (auto p = _indices_.tryGet(val)) eraseByPos((*p)[0]);
277 }
279 // removes the top of the priority queue (but does not return it)
280 template < typename Val, typename Priority, typename Cmp >
284
285 // removes the top element from the priority queue and return it
286 template < typename Val, typename Priority, typename Cmp >
288 if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
289
290 Val v = *(_heap_[0].second);
292
293 return v;
294 }
295
296 // returns a hashtable the keys of which are the values stored in the queue
297 template < typename Val, typename Priority, typename Cmp >
302
303 // inserts a new (a copy) element in the priority queue
304 template < typename Val, typename Priority, typename Cmp >
306 // create the entry in the indices hashtable
307 const Val* new_val;
308 std::vector< Size >* new_vect;
309 if (auto existing = _indices_.tryGet(val); !existing) {
310 auto& new_elt = _indices_.insert(val, std::vector< Size >());
311 new_val = &(new_elt.first);
312 new_vect = &(new_elt.second);
313 } else {
314 new_val = &(_indices_.key(val));
315 new_vect = &(*existing);
316 }
318 try {
319 new_vect->push_back(0);
320 } catch (...) {
321 if (new_vect->empty()) { _indices_.erase(val); }
322 throw;
323 }
324
325 try {
326 _heap_.push_back(std::pair< Priority, const Val* >(priority, new_val));
327 } catch (...) {
328 if (new_vect->size() == 1) { _indices_.erase(val); }
329 throw;
330 }
331
332 std::pair< Priority, const Val* > new_heap_val = std::move(_heap_[_nb_elements_]);
333 ++_nb_elements_;
334
335 // restore the heap property
336 Size i = _nb_elements_ - 1;
337
338 for (Size j = (i - 1) >> 1; i && _cmp_(new_heap_val.first, _heap_[j].first);
339 i = j, j = (j - 1) >> 1) {
340 _heap_[i] = std::move(_heap_[j]);
341 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
342 for (auto& index: vect_index) {
343 if (index == j) {
344 index = i;
345 break;
346 }
347 }
348 }
349
350 // put the new bucket into the heap
351 _heap_[i].first = std::move(new_heap_val.first);
352 _heap_[i].second = new_val;
353 new_vect->back() = i;
354
355 return i;
356 }
357
358 // inserts a new (a copy) element in the priority queue
359 template < typename Val, typename Priority, typename Cmp >
361 // create the entry in the indices hashtable
362 const Val* new_val;
363 std::vector< Size >* new_vect;
364 if (auto existing = _indices_.tryGet(val); !existing) {
365 auto& new_elt = _indices_.insert(std::move(val), std::vector< Size >());
366 new_val = &(new_elt.first);
367 new_vect = &(new_elt.second);
368 } else {
369 new_val = &(_indices_.key(val));
370 new_vect = &(*existing);
371 }
372
373 try {
374 new_vect->push_back(0);
375 } catch (...) {
376 if (new_vect->empty()) { _indices_.erase(*new_val); }
377 throw;
378 }
379
380 try {
381 _heap_.push_back(std::pair< Priority, const Val* >(std::move(priority), new_val));
382 } catch (...) {
383 if (new_vect->size() == 1) { _indices_.erase(*new_val); }
384 throw;
385 }
386
387 std::pair< Priority, const Val* > new_heap_val = std::move(_heap_[_nb_elements_]);
388 ++_nb_elements_;
390 // restore the heap property
391 Size i = _nb_elements_ - 1;
392
393 for (Size j = (i - 1) >> 1; i && _cmp_(new_heap_val.first, _heap_[j].first);
394 i = j, j = (j - 1) >> 1) {
395 _heap_[i] = std::move(_heap_[j]);
396 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
397 for (auto& index: vect_index) {
398 if (index == j) {
399 index = i;
400 break;
401 }
402 }
404
405 // put the new bucket into the heap
406 _heap_[i].first = std::move(new_heap_val.first);
407 _heap_[i].second = new_val;
408 new_vect->back() = i;
409
410 return i;
411 }
412
413 // emplace a new element into the priority queue
414 template < typename Val, typename Priority, typename Cmp >
415 template < typename... Args >
417 std::pair< Val, Priority > new_elt
418 = std::make_pair< Val, Priority >(std::forward< Args >(args)...);
419 return insert(std::move(new_elt.first), std::move(new_elt.second));
420 }
422 // indicates whether the priority queue is empty
423 template < typename Val, typename Priority, typename Cmp >
425 return (_nb_elements_ == 0);
426 }
428 // indicates whether the priority queue contains a given value
429 template < typename Val, typename Priority, typename Cmp >
431 return _indices_.exists(val);
432 }
433
434 // returns the element at position "index" in the priority queue
435 template < typename Val, typename Priority, typename Cmp >
437 if (index >= _nb_elements_) {
438 GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
439 }
440
441 return *(_heap_[index].second);
442 }
443
444 // displays the content of the queue
445 template < typename Val, typename Priority, typename Cmp >
447 bool deja = false;
448 std::stringstream stream;
449 stream << "[";
451 for (Size i = 0; i != _nb_elements_; ++i, deja = true) {
452 if (deja) stream << " , ";
453
454 stream << "(" << _heap_[i].first << " , " << *(_heap_[i].second) << ")";
455 }
456
457 stream << "]";
458
459 return stream.str();
460 }
461
462 // changes the size of the internal structure storing the priority queue
463 template < typename Val, typename Priority, typename Cmp >
465 const Priority& new_priority) {
466 // check whether the element the priority of which should be changed exists
467 if (index >= _nb_elements_) {
468 GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
469 }
470
471 // get the element itself
472 const Val* val = _heap_[index].second;
473
474 // restore the heap property
475 Size i = index;
476
477 // move val upward if needed
478 for (Size j = (i - 1) >> 1; i && _cmp_(new_priority, _heap_[j].first);
479 i = j, j = (j - 1) >> 1) {
480 _heap_[i] = std::move(_heap_[j]);
481 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
482 for (auto& idx: vect_index) {
483 if (idx == j) {
484 idx = i;
485 break;
486 }
487 }
488 }
489
490 // move val downward if needed
491 for (Size j = (i << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
492 // let j be the max child
493 if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
494
495 // if "val" is lower than heap[j], "val" must be stored at index i
496 if (_cmp_(new_priority, _heap_[j].first)) break;
497
498 // else pull up the jth node
499 _heap_[i] = std::move(_heap_[j]);
500 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
501 for (auto& idx: vect_index) {
502 if (idx == j) {
503 idx = i;
504 break;
505 }
506 }
507 }
508
509 // update the index of val
510 _heap_[i].first = new_priority;
511 _heap_[i].second = val;
512 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
513 for (auto& idx: vect_index) {
514 if (idx == index) {
515 idx = i;
516 break;
517 }
518 }
519
520 return i;
521 }
522
523 // changes the size of the internal structure storing the priority queue
524 template < typename Val, typename Priority, typename Cmp >
526 Priority&& new_priority) {
527 // check whether the element the priority of which should be changed exists
528 if (index >= _nb_elements_) {
529 GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
530 }
531
532 // get the element itself
533 const Val* val = _heap_[index].second;
534
535 // restore the heap property
536 Size i = index;
537
538 // move val upward if needed
539 for (Size j = (i - 1) >> 1; i && _cmp_(new_priority, _heap_[j].first);
540 i = j, j = (j - 1) >> 1) {
541 _heap_[i] = std::move(_heap_[j]);
542 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
543 for (auto& idx: vect_index) {
544 if (idx == j) {
545 idx = i;
546 break;
547 }
548 }
549 }
550
551 // move val downward if needed
552 for (Size j = (i << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
553 // let j be the max child
554 if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
555
556 // if "val" is lower than heap[j], "val" must be stored at index i
557 if (_cmp_(new_priority, _heap_[j].first)) break;
558
559 // else pull up the jth node
560 _heap_[i] = std::move(_heap_[j]);
561 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
562 for (auto& idx: vect_index) {
563 if (idx == j) {
564 idx = i;
565 break;
566 }
567 }
568 }
569
570 // update the index of val
571 _heap_[i].first = std::move(new_priority);
572 _heap_[i].second = val;
573 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
574 for (auto& idx: vect_index) {
575 if (idx == index) {
576 idx = i;
577 break;
578 }
579 }
580
581 return i;
582 }
583
584 // modifies the priority of each instance of a given element
585 template < typename Val, typename Priority, typename Cmp >
587 const Priority& new_priority) {
588 std::vector< Size >& vect_index = _indices_[elt];
589
590 for (auto index: vect_index) {
591 setPriorityByPos(index, new_priority);
592 }
593 }
594
595 // modifies the priority of each instance of a given element
596 template < typename Val, typename Priority, typename Cmp >
597 const Priority& MultiPriorityQueue< Val, Priority, Cmp >::priority(const Val& elt) const {
598 return _heap_[_indices_[elt][0]].first;
599 }
600
601 // A \c << operator for priority queues
602 template < typename Val, typename Priority, typename Cmp >
603 std::ostream& operator<<(std::ostream& stream,
605 stream << queue.toString();
606 return stream;
607 }
608
609} /* namespace gum */
The class for generic Hash Tables.
Definition hashTable.h:640
A MultiPriorityQueue is a heap in which each element has a mutable priority and duplicates are allowe...
Cmp _cmp_
Comparison function.
Size insert(const Val &val, const Priority &priority)
Inserts a new (a copy) element in the priority queue.
Size _nb_elements_
The number of elements in the heap.
const Val & top() const
Returns the element at the top of the priority queue.
void eraseByPos(Size index)
Removes the element at position "index" from the priority queue.
std::vector< std::pair< Priority, const Val * > > _heap_
An array storing all the elements of the heap as well as their score.
Val pop()
Removes the top element from the priority queue and return it.
void setPriority(const Val &elt, const Priority &new_priority)
Modifies the priority of each instance of a given element.
void erase(const Val &val)
Removes a given element from the priority queue (but does not return it).
bool empty() const noexcept
Indicates whether the priority queue is empty.
void eraseTop()
Removes the top of the priority queue (but does not return it).
MultiPriorityQueue< Val, Priority, Cmp > & operator=(const MultiPriorityQueue< Val, Priority, Cmp > &from)
Copy operator.
bool contains(const Val &val) const
Indicates whether the priority queue contains a given value.
const Val & operator[](Size index_elt) const
Returns the element at index "index_elt" from the priority queue.
Size setPriorityByPos(Size index, const Priority &new_priority)
Modifies the priority of the element at position "index" of the queue.
HashTable< Val, std::vector< Size > > _indices_
A hashtable for quickly finding the elements by their value.
const HashTable< Val, std::vector< Size > > & allValues() const
Returns a gum::HashTable the keys of which are the values stored in the queue.
Size capacity() const noexcept
Return the size of the internal structure storing the priority queue.
~MultiPriorityQueue()
Class destructor.
std::string toString() const
Displays the content of the queue.
void clear()
Removes all the elements from the queue.
const Priority & topPriority() const
Returns the priority of the top element.
const Priority & priority(const Val &elt) const
Returns the priority of an instance of the value passed in argument.
void resize(Size new_size)
Changes the size of the internal structure storing the priority queue.
Size emplace(Args &&... args)
Emplace a new element into the priority queue.
Size size() const noexcept
Returns the number of elements in the priority queue.
MultiPriorityQueue(Cmp compare=Cmp(), Size capacity=GUM_MULTIPLE_PRIORITY_QUEUE_DEFAULT_CAPACITY)
Basic constructor.
Exception : the element we looked for cannot be found.
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition types.h:74
Priority queues in which the same element can appear several times.
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
std::ostream & operator<<(std::ostream &stream, const AVLTree< Val, Cmp > &tree)
display the content of a tree
STL namespace.