aGrUM 2.3.2
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-2025 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-2025 *
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#pragma once
41
42
49
50// to help IDE parser
52
53namespace gum {
54
55 // basic constructor
56 template < typename Val, typename Priority, typename Cmp >
58 _indices_(capacity >> 1, true, false), _cmp_(compare) {
59 _heap_.reserve(capacity);
60
61 // for debugging purposes
62 GUM_CONSTRUCTOR(MultiPriorityQueue);
63 }
64
65 // initializer list constructor
66 template < typename Val, typename Priority, typename Cmp >
68 std::initializer_list< std::pair< Val, Priority > > list) :
69 _indices_(Size(list.size()) / 2, true, false) {
70 // fill the queue
71 _heap_.reserve(list.size());
72 for (const auto& elt: list) {
73 insert(elt.first, elt.second);
74 }
75
76 // for debugging purposes
77 GUM_CONSTRUCTOR(MultiPriorityQueue);
78 }
79
80 // copy constructor
81 template < typename Val, typename Priority, typename Cmp >
85 _cmp_(from._cmp_) {
86 // for debugging purposes
87 GUM_CONS_CPY(MultiPriorityQueue);
88
89 // fill the heap structure
90 for (const auto& val_and_index: _indices_) {
91 const Val* val = &(val_and_index.first);
92 const std::vector< Size >& vect = val_and_index.second;
93 for (auto index: vect) {
94 _heap_[index].second = val;
95 }
96 }
97 }
98
99 // move constructor
100 template < typename Val, typename Priority, typename Cmp >
103 _heap_(std::move(from._heap_)), _indices_(std::move(from._indices_)),
104 _nb_elements_(std::move(from._nb_elements_)), _cmp_(std::move(from._cmp_)) {
105 // for debugging purposes
106 GUM_CONS_MOV(MultiPriorityQueue);
107 }
108
109 // destructor
110 template < typename Val, typename Priority, typename Cmp >
112 // for debugging purposes
113 GUM_DESTRUCTOR(MultiPriorityQueue);
114 }
115
116 // copy operator
117 template < typename Val, typename Priority, typename Cmp >
120 // for debugging purposes
121 GUM_OP_CPY(MultiPriorityQueue);
122
123 try {
124 // set the comprison function
125 _cmp_ = from._cmp_;
126
127 // copy the indices and the heap
128 _indices_ = from._indices_;
129 _heap_ = from._heap_;
131
132 // restore the link between _indices_ and _heap_
133 for (const auto& val_and_index: _indices_) {
134 const Val* val = &(val_and_index.first);
135 const std::vector< Size >& vect = val_and_index.second;
136 for (auto index: vect) {
137 _heap_[index].second = val;
138 }
139 }
140 } catch (...) {
141 _heap_.clear();
142 _indices_.clear();
143 _nb_elements_ = 0;
144
145 throw;
146 }
147
148 return *this;
149 }
150
151 // move operator
152 template < typename Val, typename Priority, typename Cmp >
155 // avoid self assignment
156 if (this != &from) {
157 // for debugging purposes
158 GUM_OP_MOV(MultiPriorityQueue);
159
160 _cmp_ = std::move(from._cmp_);
161 _indices_ = std::move(from._indices_);
162 _heap_ = std::move(from._heap_);
163 _nb_elements_ = std::move(from._nb_elements_);
164 }
165
166 return *this;
167 }
168
169 // returns the element at the top of the priority queue
170 template < typename Val, typename Priority, typename Cmp >
172 if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
173
174 return *(_heap_[0].second);
175 }
176
177 // returns the priority of the top element
178 template < typename Val, typename Priority, typename Cmp >
180 if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
181
182 return _heap_[0].first;
183 }
184
185 // returns the number of elements in the priority queue
186 template < typename Val, typename Priority, typename Cmp >
188 return _nb_elements_;
189 }
190
191 // return the size of the array storing the priority queue
192 template < typename Val, typename Priority, typename Cmp >
194 return Size(_heap_.capacity());
195 }
196
197 // changes the size of the array storing the priority queue
198 template < typename Val, typename Priority, typename Cmp >
200 if (new_size < _nb_elements_) return;
201
202 _heap_.reserve(new_size);
203 _indices_.resize(new_size / 2);
204 }
205
206 // removes all the elements from the queue
207 template < typename Val, typename Priority, typename Cmp >
209 _nb_elements_ = 0;
210 _heap_.clear();
211 _indices_.clear();
212 }
213
214 // removes the element at index elt from the priority queue
215 template < typename Val, typename Priority, typename Cmp >
217 if (index >= _nb_elements_) return;
218
219 // remove the element from the hashtable
220 const Val& del_val = *(_heap_[index].second);
221 std::vector< Size >& vect_index = _indices_[del_val];
222 if (vect_index.size() == 1) _indices_.erase(del_val);
223 else {
224 for (auto& v_index: vect_index) {
225 if (v_index == index) {
226 v_index = vect_index.back();
227 vect_index.pop_back();
228 break;
229 }
230 }
231 }
232
233 // put the last element at the "index" location
234 std::pair< Priority, const Val* > last = std::move(_heap_.back());
235 _heap_.pop_back();
237
238 if (!_nb_elements_ || (index == _nb_elements_)) return;
239
240 // restore the heap property
241 Size i = index;
242
243 for (Size j = (index << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
244 // let j be the max child
245 if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
246
247 // if "last" is lower than heap[j], "last" must be stored at index i
248 if (_cmp_(last.first, _heap_[j].first)) break;
249
250 // else pull up the jth node
251 _heap_[i] = std::move(_heap_[j]);
252 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
253 for (auto& v_index: vect_index) {
254 if (v_index == j) {
255 v_index = i;
256 break;
257 }
259 }
260
261 // put "last" back into the heap
262 _heap_[i] = std::move(last);
263 std::vector< Size >& last_indices = _indices_[*(_heap_[i].second)];
264 for (auto& v_index: last_indices) {
265 if (v_index == _nb_elements_) {
266 v_index = i;
267 break;
268 }
269 }
270 }
272 // removes a given element from the priority queue (but does not return it)
273 template < typename Val, typename Priority, typename Cmp >
275 try {
276 eraseByPos(_indices_[val][0]);
277 } catch (NotFound const&) {}
279
280 // removes the top of the priority queue (but does not return it)
281 template < typename Val, typename Priority, typename Cmp >
285
286 // removes the top element from the priority queue and return it
287 template < typename Val, typename Priority, typename Cmp >
289 if (!_nb_elements_) { GUM_ERROR(NotFound, "empty priority queue") }
290
291 Val v = *(_heap_[0].second);
292 eraseByPos(0);
293
294 return v;
295 }
296
297 // returns a hashtable the keys of which are the values stored in the queue
298 template < typename Val, typename Priority, typename Cmp >
303
304 // inserts a new (a copy) element in the priority queue
305 template < typename Val, typename Priority, typename Cmp >
307 // create the entry in the indices hashtable
308 const Val* new_val;
309 std::vector< Size >* new_vect;
310 if (!_indices_.exists(val)) {
311 auto& new_elt = _indices_.insert(val, std::vector< Size >());
312 new_val = &(new_elt.first);
313 new_vect = &(new_elt.second);
314 } else {
315 new_val = &(_indices_.key(val));
316 new_vect = &(_indices_[val]);
317 }
318
319 try {
320 new_vect->push_back(0);
321 } catch (...) {
322 if (new_vect->empty()) { _indices_.erase(val); }
323 throw;
324 }
326 try {
327 _heap_.push_back(std::pair< Priority, const Val* >(priority, new_val));
328 } catch (...) {
329 if (new_vect->size() == 1) { _indices_.erase(val); }
330 throw;
331 }
332
333 std::pair< Priority, const Val* > new_heap_val = std::move(_heap_[_nb_elements_]);
334 ++_nb_elements_;
335
336 // restore the heap property
337 Size i = _nb_elements_ - 1;
338
339 for (Size j = (i - 1) >> 1; i && _cmp_(new_heap_val.first, _heap_[j].first);
340 i = j, j = (j - 1) >> 1) {
341 _heap_[i] = std::move(_heap_[j]);
342 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
343 for (auto& index: vect_index) {
344 if (index == j) {
345 index = i;
346 break;
347 }
348 }
349 }
350
351 // put the new bucket into the heap
352 _heap_[i].first = std::move(new_heap_val.first);
353 _heap_[i].second = new_val;
354 new_vect->back() = i;
355
356 return i;
357 }
358
359 // inserts a new (a copy) element in the priority queue
360 template < typename Val, typename Priority, typename Cmp >
362 // create the entry in the indices hashtable
363 const Val* new_val;
364 std::vector< Size >* new_vect;
365 if (!_indices_.exists(val)) {
366 auto& new_elt = _indices_.insert(std::move(val), std::vector< Size >());
367 new_val = &(new_elt.first);
368 new_vect = &(new_elt.second);
369 } else {
370 new_val = &(_indices_.key(val));
371 new_vect = &(_indices_[val]);
372 }
373
374 try {
375 new_vect->push_back(0);
376 } catch (...) {
377 if (new_vect->empty()) { _indices_.erase(*new_val); }
378 throw;
379 }
380
381 try {
382 _heap_.push_back(std::pair< Priority, const Val* >(std::move(priority), new_val));
383 } catch (...) {
384 if (new_vect->size() == 1) { _indices_.erase(*new_val); }
385 throw;
386 }
387
388 std::pair< Priority, const Val* > new_heap_val = std::move(_heap_[_nb_elements_]);
390
391 // restore the heap property
392 Size i = _nb_elements_ - 1;
393
394 for (Size j = (i - 1) >> 1; i && _cmp_(new_heap_val.first, _heap_[j].first);
395 i = j, j = (j - 1) >> 1) {
396 _heap_[i] = std::move(_heap_[j]);
397 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
398 for (auto& index: vect_index) {
399 if (index == j) {
400 index = i;
401 break;
402 }
404 }
405
406 // put the new bucket into the heap
407 _heap_[i].first = std::move(new_heap_val.first);
408 _heap_[i].second = new_val;
409 new_vect->back() = i;
410
411 return i;
412 }
413
414 // emplace a new element into the priority queue
415 template < typename Val, typename Priority, typename Cmp >
416 template < typename... Args >
418 std::pair< Val, Priority > new_elt
419 = std::make_pair< Val, Priority >(std::forward< Args >(args)...);
420 return insert(std::move(new_elt.first), std::move(new_elt.second));
422
423 // indicates whether the priority queue is empty
424 template < typename Val, typename Priority, typename Cmp >
426 return (_nb_elements_ == 0);
428
429 // indicates whether the priority queue contains a given value
430 template < typename Val, typename Priority, typename Cmp >
431 INLINE bool MultiPriorityQueue< Val, Priority, Cmp >::contains(const Val& val) const {
432 return _indices_.exists(val);
433 }
434
435 // returns the element at position "index" in the priority queue
436 template < typename Val, typename Priority, typename Cmp >
438 if (index >= _nb_elements_) {
439 GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
440 }
442 return *(_heap_[index].second);
443 }
444
445 // displays the content of the queue
446 template < typename Val, typename Priority, typename Cmp >
448 bool deja = false;
449 std::stringstream stream;
450 stream << "[";
451
452 for (Size i = 0; i != _nb_elements_; ++i, deja = true) {
453 if (deja) stream << " , ";
454
455 stream << "(" << _heap_[i].first << " , " << *(_heap_[i].second) << ")";
456 }
457
458 stream << "]";
459
460 return stream.str();
461 }
462
463 // changes the size of the internal structure storing the priority queue
464 template < typename Val, typename Priority, typename Cmp >
466 const Priority& new_priority) {
467 // check whether the element the priority of which should be changed exists
468 if (index >= _nb_elements_) {
469 GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
470 }
471
472 // get the element itself
473 const Val* val = _heap_[index].second;
474
475 // restore the heap property
476 Size i = index;
477
478 // move val upward if needed
479 for (Size j = (i - 1) >> 1; i && _cmp_(new_priority, _heap_[j].first);
480 i = j, j = (j - 1) >> 1) {
481 _heap_[i] = std::move(_heap_[j]);
482 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
483 for (auto& idx: vect_index) {
484 if (idx == j) {
485 idx = i;
486 break;
487 }
488 }
489 }
490
491 // move val downward if needed
492 for (Size j = (i << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
493 // let j be the max child
494 if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
495
496 // if "val" is lower than heap[j], "val" must be stored at index i
497 if (_cmp_(new_priority, _heap_[j].first)) break;
498
499 // else pull up the jth node
500 _heap_[i] = std::move(_heap_[j]);
501 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
502 for (auto& idx: vect_index) {
503 if (idx == j) {
504 idx = i;
505 break;
506 }
507 }
508 }
509
510 // update the index of val
511 _heap_[i].first = new_priority;
512 _heap_[i].second = val;
513 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
514 for (auto& idx: vect_index) {
515 if (idx == index) {
516 idx = i;
517 break;
518 }
519 }
520
521 return i;
522 }
523
524 // changes the size of the internal structure storing the priority queue
525 template < typename Val, typename Priority, typename Cmp >
527 Priority&& new_priority) {
528 // check whether the element the priority of which should be changed exists
529 if (index >= _nb_elements_) {
530 GUM_ERROR(NotFound, "not enough elements in the MultiPriorityQueue")
531 }
532
533 // get the element itself
534 const Val* val = _heap_[index].second;
535
536 // restore the heap property
537 Size i = index;
538
539 // move val upward if needed
540 for (Size j = (i - 1) >> 1; i && _cmp_(new_priority, _heap_[j].first);
541 i = j, j = (j - 1) >> 1) {
542 _heap_[i] = std::move(_heap_[j]);
543 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
544 for (auto& idx: vect_index) {
545 if (idx == j) {
546 idx = i;
547 break;
548 }
549 }
550 }
551
552 // move val downward if needed
553 for (Size j = (i << 1) + 1; j < _nb_elements_; i = j, j = (j << 1) + 1) {
554 // let j be the max child
555 if ((j + 1 < _nb_elements_) && _cmp_(_heap_[j + 1].first, _heap_[j].first)) ++j;
556
557 // if "val" is lower than heap[j], "val" must be stored at index i
558 if (_cmp_(new_priority, _heap_[j].first)) break;
559
560 // else pull up the jth node
561 _heap_[i] = std::move(_heap_[j]);
562 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
563 for (auto& idx: vect_index) {
564 if (idx == j) {
565 idx = i;
566 break;
567 }
568 }
569 }
570
571 // update the index of val
572 _heap_[i].first = std::move(new_priority);
573 _heap_[i].second = val;
574 std::vector< Size >& vect_index = _indices_[*(_heap_[i].second)];
575 for (auto& idx: vect_index) {
576 if (idx == index) {
577 idx = i;
578 break;
579 }
580 }
581
582 return i;
583 }
584
585 // modifies the priority of each instance of a given element
586 template < typename Val, typename Priority, typename Cmp >
588 const Priority& new_priority) {
589 std::vector< Size >& vect_index = _indices_[elt];
590
591 for (auto index: vect_index) {
592 setPriorityByPos(index, new_priority);
593 }
594 }
595
596 // modifies the priority of each instance of a given element
597 template < typename Val, typename Priority, typename Cmp >
598 INLINE const Priority& MultiPriorityQueue< Val, Priority, Cmp >::priority(const Val& elt) const {
599 return _heap_[_indices_[elt][0]].first;
600 }
601
602 // A \c << operator for priority queues
603 template < typename Val, typename Priority, typename Cmp >
604 INLINE std::ostream& operator<<(std::ostream& stream,
606 stream << queue.toString();
607 return stream;
608 }
609
610} /* namespace gum */
The class for generic Hash Tables.
Definition hashTable.h:637
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.
void resize(Size new_size)
Changes the size of the internal structure storing the priority queue.
const HashTable< Val, std::vector< Size > > & allValues() const
Returns a gum::HashTable the keys of which are the values stored in the queue.
const Priority & topPriority() const
Returns the priority of the top element.
Size size() const noexcept
Returns the number of elements in the priority queue.
Size capacity() const noexcept
Return the size of the internal structure storing the priority queue.
bool empty() const noexcept
Indicates whether the priority queue is empty.
void eraseByPos(Size index)
Removes the element at position "index" from the priority queue.
const Val & operator[](Size index_elt) const
Returns the element at index "index_elt" 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.
void setPriority(const Val &elt, const Priority &new_priority)
Modifies the priority of each instance of a given element.
const Val & top() const
Returns the element at the top of the priority queue.
Val pop()
Removes the top element from the priority queue and return it.
MultiPriorityQueue< Val, Priority, Cmp > & operator=(const MultiPriorityQueue< Val, Priority, Cmp > &from)
Copy operator.
const Priority & priority(const Val &elt) const
Returns the priority of an instance of the value passed in argument.
Size setPriorityByPos(Size index, const Priority &new_priority)
Modifies the priority of the element at position "index" of the queue.
bool contains(const Val &val) const
Indicates whether the priority queue contains a given value.
HashTable< Val, std::vector< Size > > _indices_
A hashtable for quickly finding the elements by their value.
~MultiPriorityQueue()
Class destructor.
void eraseTop()
Removes the top of the priority queue (but does not return it).
void erase(const Val &val)
Removes a given element from the priority queue (but does not return it).
std::string toString() const
Displays the content of the queue.
void clear()
Removes all the elements from the queue.
Size emplace(Args &&... args)
Emplace a new element into 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:72
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
Definition AVLTree.h:913
STL namespace.