@@ -70,7 +70,7 @@ class HeuristicPlacer : public PlacerBase {
7070 size_t windowMinHeight_;
7171
7272public:
73- // / The configuration of the A* placer
73+ // / The configuration of the heuristic placer
7474 struct Config {
7575 /* *
7676 * @brief This flag indicates whether the placement should use a window when
@@ -155,9 +155,7 @@ class HeuristicPlacer : public PlacerBase {
155155 * @brief The maximum number of nodes that are allowed to be visited in the
156156 * A* search tree.
157157 * @detsils If this number is exceeded, the search is aborted and an error
158- * is raised. In the current implementation, one node roughly consumes 140
159- * Byte. Hence, allowing 10,000,000 nodes results in memory consumption of
160- * about 2 GB plus the size of the rest of the data structures.
158+ * is raised.
161159 */
162160 size_t maxNodes = 10'000'000 ;
163161 /* *
@@ -378,6 +376,8 @@ class HeuristicPlacer : public PlacerBase {
378376 std::vector<Node*> maxHeap_;
379377 // / The maximum number of elements in the heap.
380378 SizeType heapCapacity_;
379+ // / The comparison functor used to determine priority.
380+ PriorityCompare compare_;
381381
382382 /* *
383383 * Starts to establish the min-heap property from the given index upwards.
@@ -387,7 +387,7 @@ class HeuristicPlacer : public PlacerBase {
387387 assert (i < minHeap_.size ());
388388 while (i > 0 ) {
389389 size_t parent = (i - 1 ) / 2 ;
390- if (PriorityCompare{} (minHeap_[i]->value , minHeap_[parent]->value )) {
390+ if (compare_ (minHeap_[i]->value , minHeap_[parent]->value )) {
391391 std::swap (minHeap_[i], minHeap_[parent]);
392392 minHeap_[i]->minHeapIndex = i;
393393 minHeap_[parent]->minHeapIndex = parent;
@@ -406,7 +406,7 @@ class HeuristicPlacer : public PlacerBase {
406406 assert (i < maxHeap_.size ());
407407 while (i > 0 ) {
408408 size_t parent = (i - 1 ) / 2 ;
409- if (PriorityCompare{} (maxHeap_[parent]->value , maxHeap_[i]->value )) {
409+ if (compare_ (maxHeap_[parent]->value , maxHeap_[i]->value )) {
410410 std::swap (maxHeap_[i], maxHeap_[parent]);
411411 maxHeap_[i]->maxHeapIndex = i;
412412 maxHeap_[parent]->maxHeapIndex = parent;
@@ -428,13 +428,11 @@ class HeuristicPlacer : public PlacerBase {
428428 size_t smallest = i;
429429
430430 if (leftChild < minHeap_.size () &&
431- PriorityCompare{}(minHeap_[leftChild]->value ,
432- minHeap_[smallest]->value )) {
431+ compare_ (minHeap_[leftChild]->value , minHeap_[smallest]->value )) {
433432 smallest = leftChild;
434433 }
435434 if (rightChild < minHeap_.size () &&
436- PriorityCompare{}(minHeap_[rightChild]->value ,
437- minHeap_[smallest]->value )) {
435+ compare_ (minHeap_[rightChild]->value , minHeap_[smallest]->value )) {
438436 smallest = rightChild;
439437 }
440438 if (smallest != i) {
@@ -459,13 +457,11 @@ class HeuristicPlacer : public PlacerBase {
459457 size_t largest = i;
460458
461459 if (leftChild < maxHeap_.size () &&
462- PriorityCompare{}(maxHeap_[largest]->value ,
463- maxHeap_[leftChild]->value )) {
460+ compare_ (maxHeap_[largest]->value , maxHeap_[leftChild]->value )) {
464461 largest = leftChild;
465462 }
466463 if (rightChild < maxHeap_.size () &&
467- PriorityCompare{}(maxHeap_[largest]->value ,
468- maxHeap_[rightChild]->value )) {
464+ compare_ (maxHeap_[largest]->value , maxHeap_[rightChild]->value )) {
469465 largest = rightChild;
470466 }
471467 if (largest != i) {
@@ -493,7 +489,7 @@ class HeuristicPlacer : public PlacerBase {
493489 maxHeap_.reserve (heapCapacity_);
494490 }
495491 /* *
496- * @returns the top element of the stack .
492+ * @returns the top element of the priority queue .
497493 * @note If @ref empty returns `true`, calling this function is
498494 * undefined behavior.
499495 */
@@ -502,7 +498,7 @@ class HeuristicPlacer : public PlacerBase {
502498 return minHeap_.front ()->value ;
503499 }
504500 /* *
505- * @returns the top element of the stack .
501+ * @returns the top element of the priority queue .
506502 * @note If @ref empty returns `true`, calling this function is
507503 * undefined behavior.
508504 */
@@ -535,15 +531,26 @@ class HeuristicPlacer : public PlacerBase {
535531 std::swap (maxHeap_[i], maxHeap_.back ());
536532 maxHeap_.pop_back ();
537533 maxHeap_[i]->maxHeapIndex = i;
538- heapifyMaxHeapDown (i);
534+ // Restoring heap property may require moving the swapped element up
535+ // or down.
536+ if (i > 0 ) {
537+ const size_t parent = (i - 1 ) / 2 ;
538+ if (compare_ (maxHeap_[parent]->value , maxHeap_[i]->value )) {
539+ heapifyMaxHeapUp (i);
540+ } else {
541+ heapifyMaxHeapDown (i);
542+ }
543+ } else {
544+ heapifyMaxHeapDown (i);
545+ }
539546 }
540547 }
541548 assert (minHeap_.size () == maxHeap_.size ());
542549 assert (minHeap_.size () <= heapCapacity_);
543550 }
544- // / @returns `true` if the stack is empty.
551+ // / @returns `true` if the queue is empty.
545552 [[nodiscard]] auto empty () const -> bool { return minHeap_.empty (); }
546- // / @brief Inserts an element at the top .
553+ // / @brief Inserts an element into the priority queue .
547554 auto push (ValueType&& value) -> void {
548555 assert (minHeap_.size () == maxHeap_.size ());
549556 if (heapCapacity_ > 0 ) {
@@ -556,7 +563,7 @@ class HeuristicPlacer : public PlacerBase {
556563 } else {
557564 assert (minHeap_.size () == heapCapacity_);
558565 // if capacity is reached, only insert the value if smaller than max
559- if (PriorityCompare{} (value, maxHeap_.front ()->value )) {
566+ if (compare_ (value, maxHeap_.front ()->value )) {
560567 const auto i = maxHeap_.front ()->minHeapIndex ;
561568 assert (i < minHeap_.size ());
562569 minHeap_[i] = std::make_unique<Node>(i, 0 , std::move (value));
@@ -593,10 +600,12 @@ class HeuristicPlacer : public PlacerBase {
593600 * given node.
594601 * @param isGoal is a function to determine whether a given node is a goal
595602 * node.
596- * @param getCost is a function returning the cost of a node in the graph.
603+ * @param getCost is a function returning the cost of a node in the graph. The
604+ * cost of the start node must be 0.
597605 * @param getHeuristic is a function returning the heuristic value of a given
598606 * node.
599- * @param trials is the number of restarts IDS performs.
607+ * @param trials is the number of attempts to find a goal node that are
608+ * performed at most. This parameter must be greater than 0.
600609 * @param queueCapacity is the capacity of the queue used for the iterative
601610 * diving search. For the actual capacity, the current value of trial is
602611 * added.
@@ -611,6 +620,9 @@ class HeuristicPlacer : public PlacerBase {
611620 const std::function<double(const Node&)>& getCost,
612621 const std::function<double(const Node&)>& getHeuristic, size_t trials,
613622 const size_t queueCapacity) -> std::shared_ptr<const Node> {
623+ if (trials == 0 ) {
624+ throw std::invalid_argument (" IDS requires trials >= 1" );
625+ }
614626 struct Item {
615627 double priority; // < sum of cost and heuristic
616628 std::shared_ptr<const Node> node; // < pointer to the node
@@ -625,6 +637,8 @@ class HeuristicPlacer : public PlacerBase {
625637 return a.priority < b.priority ;
626638 }
627639 };
640+ // Initial capacity accounts for shrinking: popAndShrink() decrements
641+ // capacity on each trial
628642 BoundedPriorityQueue<Item, ItemCompare> queue (queueCapacity + trials);
629643 std::optional<Item> goal;
630644 Item currentItem{getHeuristic (*start), start};
@@ -702,9 +716,6 @@ class HeuristicPlacer : public PlacerBase {
702716 * general graphs. This is because it does not keep track of visited nodes and
703717 * therefore cannot detect cycles. Also, for DAGs it may expand nodes multiple
704718 * times when they can be reached by different paths from the start node.
705- * @note @p getHeuristic must be admissible, meaning that it never
706- * overestimates the cost to reach the goal from the current node calculated
707- * by @p getCost for every edge on the path.
708719 * @note The calling program has to make sure that the pointers passed to this
709720 * function are valid and that the iterators are not invalidated during the
710721 * search, e.g., by calling one of the passed functions like @p getNeighbors.
@@ -714,11 +725,11 @@ class HeuristicPlacer : public PlacerBase {
714725 * @param isGoal is a function that returns true if a node is one of
715726 * potentially multiple goals
716727 * @param getCost is a function that returns the total cost to reach that
717- * particular node from the start node
728+ * particular node from the start node. The cost of the start node must be 0.
718729 * @param getHeuristic is a function that returns the heuristic cost from the
719730 * node to any goal.
720- * @param maxNodes is the maximum number of held in the priority queue before
721- * the search is aborted.
731+ * @param maxNodes is the maximum number of nodes held in the priority queue
732+ * before the search is aborted. This parameter must be greater than 0 .
722733 * @return a vector of node references representing the path from the start to
723734 * a goal
724735 */
@@ -731,6 +742,9 @@ class HeuristicPlacer : public PlacerBase {
731742 const std::function<double(const Node&)>& getCost,
732743 const std::function<double(const Node&)>& getHeuristic,
733744 size_t maxNodes) -> std::shared_ptr<const Node> {
745+ if (maxNodes == 0 ) {
746+ throw std::invalid_argument (" `maxNodes` must be greater than 0" );
747+ }
734748 // ===--------------------------------------------------------------------===//
735749 // Setup open set structure
736750 // ===--------------------------------------------------------------------===//
@@ -758,7 +772,7 @@ class HeuristicPlacer : public PlacerBase {
758772 // ===--------------------------------------------------------------------===//
759773 // Perform A* search
760774 // ===--------------------------------------------------------------------===//
761- while (openSet. size () < maxNodes && !openSet.empty ()) {
775+ while (!openSet.empty ()) {
762776 auto itm = openSet.top ();
763777 openSet.pop ();
764778 // if a goal is reached, that is the shortest path to a goal under the
@@ -776,13 +790,12 @@ class HeuristicPlacer : public PlacerBase {
776790 openSet.emplace (cost + heuristic, neighbor);
777791 }
778792 }
779- }
780- if (openSet.size () >= maxNodes) {
781- throw std::runtime_error (
782- " Maximum number of nodes reached. Increase max_nodes or increase "
783- " deepening_value and deepening_factor to reduce the number of "
784- " explored "
785- " nodes." );
793+ if (openSet.size () >= maxNodes) {
794+ throw std::runtime_error (
795+ " Maximum number of nodes reached. Increase max_nodes or increase "
796+ " deepening_value and deepening_factor to reduce the number of "
797+ " explored nodes." );
798+ }
786799 }
787800 throw std::runtime_error (
788801 " No path from start to any goal found. This may be caused by a too "
0 commit comments