AITstar.cpp
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2019, University of Oxford
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the names of the copyright holders nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 // Authors: Marlin Strub
36 
37 #include "ompl/geometric/planners/informedtrees/AITstar.h"
38 
39 #include <algorithm>
40 #include <cmath>
41 #include <string>
42 
43 #include <boost/range/adaptor/reversed.hpp>
44 
45 #include "ompl/base/objectives/PathLengthOptimizationObjective.h"
46 #include "ompl/util/Console.h"
47 
48 using namespace std::string_literals;
49 using namespace ompl::geometric::aitstar;
50 
51 namespace ompl
52 {
53  namespace geometric
54  {
55  AITstar::AITstar(const ompl::base::SpaceInformationPtr &spaceInformation)
56  : ompl::base::Planner(spaceInformation, "AITstar")
57  , solutionCost_()
58  , graph_(solutionCost_)
59  , forwardQueue_([this](const auto &lhs, const auto &rhs) { return isEdgeBetter(lhs, rhs); })
60  , reverseQueue_([this](const auto &lhs, const auto &rhs) { return isVertexBetter(lhs, rhs); })
61  {
62  // Specify AIT*'s planner specs.
63  specs_.recognizedGoal = base::GOAL_SAMPLEABLE_REGION;
64  specs_.multithreaded = false;
65  specs_.approximateSolutions = true;
66  specs_.optimizingPaths = true;
67  specs_.directed = true;
68  specs_.provingSolutionNonExistence = false;
69  specs_.canReportIntermediateSolutions = true;
70 
71  // Register the setting callbacks.
72  declareParam<bool>("use_k_nearest", this, &AITstar::setUseKNearest, &AITstar::getUseKNearest, "0,1");
73  declareParam<double>("rewire_factor", this, &AITstar::setRewireFactor, &AITstar::getRewireFactor,
74  "1.0:0.01:3.0");
75  declareParam<std::size_t>("samples_per_batch", this, &AITstar::setBatchSize, &AITstar::getBatchSize,
76  "1:1:1000");
77  declareParam<bool>("use_graph_pruning", this, &AITstar::enablePruning, &AITstar::isPruningEnabled, "0,1");
78  declareParam<bool>("find_approximate_solutions", this, &AITstar::trackApproximateSolutions,
80  declareParam<std::size_t>("set_max_num_goals", this, &AITstar::setMaxNumberOfGoals,
81  &AITstar::getMaxNumberOfGoals, "1:1:1000");
82 
83  // Register the progress properties.
84  addPlannerProgressProperty("iterations INTEGER", [this]() { return std::to_string(numIterations_); });
85  addPlannerProgressProperty("best cost DOUBLE", [this]() { return std::to_string(solutionCost_.value()); });
86  addPlannerProgressProperty("state collision checks INTEGER",
87  [this]() { return std::to_string(graph_.getNumberOfStateCollisionChecks()); });
88  addPlannerProgressProperty("edge collision checks INTEGER",
89  [this]() { return std::to_string(numEdgeCollisionChecks_); });
90  addPlannerProgressProperty("nearest neighbour calls INTEGER",
91  [this]() { return std::to_string(graph_.getNumberOfNearestNeighborCalls()); });
92  }
93 
95  {
96  // Call the base-class setup.
97  Planner::setup();
98 
99  // Check that a problem definition has been set.
100  if (static_cast<bool>(Planner::pdef_))
101  {
102  // Default to path length optimization objective if none has been specified.
103  if (!pdef_->hasOptimizationObjective())
104  {
105  OMPL_WARN("%s: No optimization objective has been specified. Defaulting to path length.",
106  Planner::getName().c_str());
107  Planner::pdef_->setOptimizationObjective(
108  std::make_shared<ompl::base::PathLengthOptimizationObjective>(Planner::si_));
109  }
110 
111  if (static_cast<bool>(pdef_->getGoal()))
112  {
113  // If we were given a goal, make sure its of appropriate type.
114  if (!(pdef_->getGoal()->hasType(ompl::base::GOAL_SAMPLEABLE_REGION)))
115  {
116  OMPL_ERROR("AIT* is currently only implemented for goals that can be cast to "
117  "ompl::base::GOAL_SAMPLEABLE_GOAL_REGION.");
118  setup_ = false;
119  return;
120  }
121  }
122 
123  // Pull the optimization objective through the problem definition.
124  objective_ = pdef_->getOptimizationObjective();
125 
126  // Initialize the solution cost to be infinite.
127  solutionCost_ = objective_->infiniteCost();
128  approximateSolutionCost_ = objective_->infiniteCost();
129  approximateSolutionCostToGoal_ = objective_->infiniteCost();
130 
131  // Pull the motion validator through the space information.
132  motionValidator_ = si_->getMotionValidator();
133 
134  // Setup a graph.
135  graph_.setup(si_, pdef_, &pis_);
136  }
137  else
138  {
139  // AIT* can't be setup without a problem definition.
140  setup_ = false;
141  OMPL_WARN("AIT*: Unable to setup without a problem definition.");
142  }
143  }
144 
146  {
147  // Call the base planners validity check. This checks if the
148  // planner is setup if not then it calls setup().
149  checkValidity();
150 
151  // Ensure the planner is setup.
152  if (!setup_)
153  {
154  OMPL_ERROR("%s: The planner is not setup.", name_.c_str());
155  return ompl::base::PlannerStatus::StatusType::ABORT;
156  }
157 
158  // Ensure the space is setup.
159  if (!si_->isSetup())
160  {
161  OMPL_ERROR("%s: The space information is not setup.", name_.c_str());
162  return ompl::base::PlannerStatus::StatusType::ABORT;
163  }
164 
165  return ompl::base::PlannerStatus::StatusType::UNKNOWN;
166  }
167 
170  {
171  // If the graph currently does not have a start state, try to get one.
172  if (!graph_.hasAStartState())
173  {
174  graph_.updateStartAndGoalStates(terminationCondition, &pis_);
175 
176  // If we could not get a start state, then there's nothing to solve.
177  if (!graph_.hasAStartState())
178  {
179  OMPL_WARN("%s: No solution can be found as no start states are available", name_.c_str());
180  return ompl::base::PlannerStatus::StatusType::INVALID_START;
181  }
182  }
183 
184  // If the graph currently does not have a goal state, we wait until we get one.
185  if (!graph_.hasAGoalState())
186  {
187  graph_.updateStartAndGoalStates(terminationCondition, &pis_);
188 
189  // If the graph still doesn't have a goal after waiting, then there's nothing to solve.
190  if (!graph_.hasAGoalState())
191  {
192  OMPL_WARN("%s: No solution can be found as no goal states are available", name_.c_str());
193  return ompl::base::PlannerStatus::StatusType::INVALID_GOAL;
194  }
195  }
196 
197  // Would it be worth implementing a 'setup' or 'checked' status type?
198  return ompl::base::PlannerStatus::StatusType::UNKNOWN;
199  }
200 
202  {
203  graph_.clear();
204  forwardQueue_.clear();
205  reverseQueue_.clear();
206  if (objective_)
207  {
208  solutionCost_ = objective_->infiniteCost();
209  approximateSolutionCost_ = objective_->infiniteCost();
210  approximateSolutionCostToGoal_ = objective_->infiniteCost();
211  }
212  numIterations_ = 0u;
213  numInconsistentOrUnconnectedTargets_ = 0u;
214  Planner::clear();
215  setup_ = false;
216  }
217 
219  {
220  // Ensure that the planner and state space are setup before solving.
221  auto status = ensureSetup();
222 
223  // Return early if the planner or state space are not setup.
224  if (status == ompl::base::PlannerStatus::StatusType::ABORT)
225  {
226  return status;
227  }
228 
229  // Ensure that the problem has start and goal states before solving.
230  status = ensureStartAndGoalStates(terminationCondition);
231 
232  // Return early if the problem cannot be solved.
233  if (status == ompl::base::PlannerStatus::StatusType::INVALID_START ||
234  status == ompl::base::PlannerStatus::StatusType::INVALID_GOAL)
235  {
236  return status;
237  }
238 
239  OMPL_INFORM("%s: Solving the given planning problem. The current best solution cost is %.4f", name_.c_str(),
240  solutionCost_.value());
241 
242  // Iterate to solve the problem.
243  while (!terminationCondition && !objective_->isSatisfied(solutionCost_))
244  {
245  iterate(terminationCondition);
246  }
247 
248  // Someone might call ProblemDefinition::clearSolutionPaths() between invocations of Planner::sovle(), in
249  // which case previously found solutions are not registered with the problem definition anymore.
250  status = updateSolution();
251 
252  // Let the caller know the status.
253  informAboutPlannerStatus(status);
254  return status;
255  }
256 
258  {
259  return solutionCost_;
260  }
261 
263  {
264  // base::PlannerDataVertex takes a raw pointer to a state. I want to guarantee, that the state lives as
265  // long as the program lives.
266  static std::set<std::shared_ptr<Vertex>,
267  std::function<bool(const std::shared_ptr<Vertex> &, const std::shared_ptr<Vertex> &)>>
268  liveStates([](const auto &lhs, const auto &rhs) { return lhs->getId() < rhs->getId(); });
269 
270  // Fill the planner progress properties.
271  Planner::getPlannerData(data);
272 
273  // Get the vertices.
274  auto vertices = graph_.getVertices();
275 
276  // Add the vertices and edges.
277  for (const auto &vertex : vertices)
278  {
279  // Add the vertex to the live states.
280  liveStates.insert(vertex);
281 
282  // Add the vertex as the right kind of vertex.
283  if (graph_.isStart(vertex))
284  {
285  data.addStartVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
286  }
287  else if (graph_.isGoal(vertex))
288  {
289  data.addGoalVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
290  }
291  else
292  {
293  data.addVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
294  }
295 
296  // If it has a parent, add the corresponding edge.
297  if (vertex->hasForwardParent())
298  {
299  data.addEdge(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()),
300  ompl::base::PlannerDataVertex(vertex->getForwardParent()->getState(),
301  vertex->getForwardParent()->getId()));
302  }
303  }
304  }
305 
306  void AITstar::setBatchSize(std::size_t batchSize)
307  {
308  batchSize_ = batchSize;
309  }
310 
311  std::size_t AITstar::getBatchSize() const
312  {
313  return batchSize_;
314  }
315 
316  void AITstar::setRewireFactor(double rewireFactor)
317  {
318  graph_.setRewireFactor(rewireFactor);
319  }
320 
322  {
323  return graph_.getRewireFactor();
324  }
325 
327  {
328  trackApproximateSolutions_ = track;
329  if (!trackApproximateSolutions_)
330  {
331  if (static_cast<bool>(objective_))
332  {
333  approximateSolutionCost_ = objective_->infiniteCost();
334  approximateSolutionCostToGoal_ = objective_->infiniteCost();
335  }
336  }
337  }
338 
340  {
341  return trackApproximateSolutions_;
342  }
343 
344  void AITstar::enablePruning(bool prune)
345  {
346  isPruningEnabled_ = prune;
347  }
348 
350  {
351  return isPruningEnabled_;
352  }
353 
354  void AITstar::setUseKNearest(bool useKNearest)
355  {
356  graph_.setUseKNearest(useKNearest);
357  }
358 
360  {
361  return graph_.getUseKNearest();
362  }
363 
364  void AITstar::setMaxNumberOfGoals(unsigned int numberOfGoals)
365  {
366  graph_.setMaxNumberOfGoals(numberOfGoals);
367  }
368 
369  unsigned int AITstar::getMaxNumberOfGoals() const
370  {
371  return graph_.getMaxNumberOfGoals();
372  }
373 
374  void AITstar::rebuildForwardQueue()
375  {
376  // Get all edges from the queue.
377  std::vector<Edge> edges;
378  forwardQueue_.getContent(edges);
379 
380  // Rebuilding the queue invalidates the incoming and outgoing lookup.
381  for (const auto &edge : edges)
382  {
383  edge.getChild()->resetForwardQueueIncomingLookup();
384  edge.getParent()->resetForwardQueueOutgoingLookup();
385  }
386 
387  // Clear the queue.
388  forwardQueue_.clear();
389  numInconsistentOrUnconnectedTargets_ = 0u;
390 
391  // Insert all edges into the queue if they connect vertices that have been processed, otherwise store
392  // them in the cache of edges that are to be inserted.
393  for (auto &edge : edges)
394  {
395  insertOrUpdateInForwardQueue(
396  Edge(edge.getParent(), edge.getChild(), computeSortKey(edge.getParent(), edge.getChild())));
397  }
398  }
399 
400  void AITstar::clearForwardQueue()
401  {
402  std::vector<Edge> forwardQueue;
403  forwardQueue_.getContent(forwardQueue);
404  for (const auto &element : forwardQueue)
405  {
406  element.getChild()->resetForwardQueueIncomingLookup();
407  element.getParent()->resetForwardQueueOutgoingLookup();
408  }
409  forwardQueue_.clear();
410  numInconsistentOrUnconnectedTargets_ = 0u;
411  }
412 
413  void AITstar::rebuildReverseQueue()
414  {
415  // Rebuilding the reverse queue invalidates the reverse queue pointers.
416  std::vector<aitstar::KeyVertexPair> content;
417  reverseQueue_.getContent(content);
418  for (auto &element : content)
419  {
420  element.second->resetReverseQueuePointer();
421  }
422  reverseQueue_.clear();
423 
424  for (auto &vertex : content)
425  {
426  // Compute the sort key for the vertex queue.
427  std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>> element(
428  computeSortKey(vertex.second), vertex.second);
429  auto reverseQueuePointer = reverseQueue_.insert(element);
430  element.second->setReverseQueuePointer(reverseQueuePointer);
431  }
432  }
433 
434  void AITstar::clearReverseQueue()
435  {
436  std::vector<aitstar::KeyVertexPair> reverseQueue;
437  reverseQueue_.getContent(reverseQueue);
438  for (const auto &element : reverseQueue)
439  {
440  element.second->resetReverseQueuePointer();
441  }
442  reverseQueue_.clear();
443  }
444 
445  void AITstar::informAboutNewSolution() const
446  {
447  OMPL_INFORM("%s (%u iterations): Found a new exact solution of cost %.4f. Sampled a total of %u states, %u "
448  "of which were valid samples (%.1f \%). Processed %u edges, %u of which were collision checked "
449  "(%.1f \%). The forward search tree has %u vertices, %u of which are start states. The reverse "
450  "search tree has %u vertices, %u of which are goal states.",
451  name_.c_str(), numIterations_, solutionCost_.value(), graph_.getNumberOfSampledStates(),
452  graph_.getNumberOfValidSamples(),
453  graph_.getNumberOfSampledStates() == 0u ?
454  0.0 :
455  100.0 * (static_cast<double>(graph_.getNumberOfValidSamples()) /
456  static_cast<double>(graph_.getNumberOfSampledStates())),
457  numProcessedEdges_, numEdgeCollisionChecks_,
458  numProcessedEdges_ == 0u ? 0.0 :
459  100.0 * (static_cast<float>(numEdgeCollisionChecks_) /
460  static_cast<float>(numProcessedEdges_)),
461  countNumVerticesInForwardTree(), graph_.getStartVertices().size(),
462  countNumVerticesInReverseTree(), graph_.getGoalVertices().size());
463  }
464 
465  void AITstar::informAboutPlannerStatus(ompl::base::PlannerStatus::StatusType status) const
466  {
467  switch (status)
468  {
469  case ompl::base::PlannerStatus::StatusType::EXACT_SOLUTION:
470  {
471  OMPL_INFORM("%s (%u iterations): Found an exact solution of cost %.4f.", name_.c_str(),
472  numIterations_, solutionCost_.value());
473  break;
474  }
475  case ompl::base::PlannerStatus::StatusType::APPROXIMATE_SOLUTION:
476  {
477  OMPL_INFORM("%s (%u iterations): Did not find an exact solution, but found an approximate "
478  "solution "
479  "of cost %.4f which is %.4f away from a goal (in cost space).",
480  name_.c_str(), numIterations_, approximateSolutionCost_.value(),
481  approximateSolutionCostToGoal_.value());
482  break;
483  }
484  case ompl::base::PlannerStatus::StatusType::TIMEOUT:
485  {
486  if (trackApproximateSolutions_)
487  {
488  OMPL_INFORM("%s (%u iterations): Did not find any solution.", name_.c_str(), numIterations_);
489  }
490  else
491  {
492  OMPL_INFORM("%s (%u iterations): Did not find an exact solution, and tracking approximate "
493  "solutions is disabled.",
494  name_.c_str(), numIterations_);
495  }
496  break;
497  }
498  case ompl::base::PlannerStatus::StatusType::INFEASIBLE:
499  case ompl::base::PlannerStatus::StatusType::UNKNOWN:
500  case ompl::base::PlannerStatus::StatusType::INVALID_START:
501  case ompl::base::PlannerStatus::StatusType::INVALID_GOAL:
502  case ompl::base::PlannerStatus::StatusType::UNRECOGNIZED_GOAL_TYPE:
503  case ompl::base::PlannerStatus::StatusType::CRASH:
504  case ompl::base::PlannerStatus::StatusType::ABORT:
505  case ompl::base::PlannerStatus::StatusType::TYPE_COUNT:
506  {
507  OMPL_INFORM("%s (%u iterations): Unable to solve the given planning problem.", name_.c_str(),
508  numIterations_);
509  }
510  }
511 
512  OMPL_INFORM(
513  "%s (%u iterations): Sampled a total of %u states, %u of which were valid samples (%.1f \%). "
514  "Processed %u edges, %u of which were collision checked (%.1f \%). The forward search tree "
515  "has %u vertices. The reverse search tree has %u vertices.",
516  name_.c_str(), numIterations_, graph_.getNumberOfSampledStates(), graph_.getNumberOfValidSamples(),
517  graph_.getNumberOfSampledStates() == 0u ?
518  0.0 :
519  100.0 * (static_cast<double>(graph_.getNumberOfValidSamples()) /
520  static_cast<double>(graph_.getNumberOfSampledStates())),
521  numProcessedEdges_, numEdgeCollisionChecks_,
522  numProcessedEdges_ == 0u ?
523  0.0 :
524  100.0 * (static_cast<float>(numEdgeCollisionChecks_) / static_cast<float>(numProcessedEdges_)),
525  countNumVerticesInForwardTree(), countNumVerticesInReverseTree());
526  }
527 
528  void AITstar::insertGoalVerticesInReverseQueue()
529  {
530  for (const auto &goal : graph_.getGoalVertices())
531  {
532  // Set the cost to come from the goal to identity and the expanded cost to infinity.
533  goal->setExpandedCostToComeFromGoal(objective_->infiniteCost());
534  goal->setCostToComeFromGoal(objective_->identityCost());
535 
536  // Create an element for the queue.
537  aitstar::KeyVertexPair element({computeCostToGoToStartHeuristic(goal), objective_->identityCost()},
538  goal);
539 
540  // Insert the element into the queue and set the corresponding pointer.
541  auto reverseQueuePointer = reverseQueue_.insert(element);
542  goal->setReverseQueuePointer(reverseQueuePointer);
543  }
544  }
545 
546  void AITstar::expandStartVerticesIntoForwardQueue()
547  {
548  for (const auto &start : graph_.getStartVertices())
549  {
550  start->setCostToComeFromStart(objective_->identityCost());
551  insertOrUpdateInForwardQueue(getOutgoingEdges(start));
552  }
553  }
554 
555  bool AITstar::continueReverseSearch() const
556  {
557  // Never continue the reverse search if the reverse of forward queue is empty.
558  if (reverseQueue_.empty() || forwardQueue_.empty())
559  {
560  return false;
561  }
562 
563  // Get references to the best edge and vertex in the queues.
564  const auto &bestEdge = forwardQueue_.top()->data;
565  const auto &bestVertex = reverseQueue_.top()->data;
566 
567  // The reverse search must be continued if the best edge has an inconsistent child state or if the best
568  // vertex can potentially lead to a better solution than the best edge.
569  return !((bestEdge.getChild()->isConsistent() &&
570  objective_->isCostBetterThan(bestEdge.getSortKey()[0u], bestVertex.first[0u])) ||
571  numInconsistentOrUnconnectedTargets_ == 0u);
572  }
573 
574  bool AITstar::continueForwardSearch()
575  {
576  // Never continue the forward search if its queue is empty.
577  if (forwardQueue_.empty())
578  {
579  return false;
580  }
581 
582  // If the best edge in the forward queue has a potential total solution cost of infinity, the forward
583  // search does not need to be continued. This can happen if the reverse search did not reach any target
584  // state of the edges in the forward queue.
585  const auto &bestEdgeCost = forwardQueue_.top()->data.getSortKey()[0u];
586  if (!objective_->isFinite(bestEdgeCost))
587  {
588  return false;
589  }
590 
591  // The forward search can be stopped once the resolution optimal solution has been found.
592  return objective_->isCostBetterThan(bestEdgeCost, solutionCost_);
593  }
594 
595  std::vector<Edge> AITstar::getEdgesInQueue() const
596  {
597  std::vector<Edge> edges;
598  forwardQueue_.getContent(edges);
599  return edges;
600  }
601 
602  std::vector<std::shared_ptr<Vertex>> AITstar::getVerticesInQueue() const
603  {
604  // Get the content from the queue.
605  std::vector<std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>>> content;
606  reverseQueue_.getContent(content);
607 
608  // Return the vertices.
609  std::vector<std::shared_ptr<Vertex>> vertices;
610  for (const auto &pair : content)
611  {
612  vertices.emplace_back(pair.second);
613  }
614  return vertices;
615  }
616 
618  {
619  if (!forwardQueue_.empty())
620  {
621  return forwardQueue_.top()->data;
622  }
623 
624  return {};
625  }
626 
627  std::shared_ptr<Vertex> AITstar::getNextVertexInQueue() const
628  {
629  if (!reverseQueue_.empty())
630  {
631  return reverseQueue_.top()->data.second;
632  }
633 
634  return {};
635  }
636 
637  std::vector<std::shared_ptr<Vertex>> AITstar::getVerticesInReverseSearchTree() const
638  {
639  // Get all vertices from the graph.
640  auto vertices = graph_.getVertices();
641 
642  // Erase the vertices that are not in the reverse search tree.
643  vertices.erase(std::remove_if(vertices.begin(), vertices.end(),
644  [this](const std::shared_ptr<Vertex> &vertex) {
645  return !graph_.isGoal(vertex) && !vertex->hasReverseParent();
646  }),
647  vertices.end());
648  return vertices;
649  }
650 
651  void AITstar::iterate(const ompl::base::PlannerTerminationCondition &terminationCondition)
652  {
653  // If this is the first time solve is called, populate the queues.
654  if (numIterations_ == 0u)
655  {
656  insertGoalVerticesInReverseQueue();
657  expandStartVerticesIntoForwardQueue();
658  }
659 
660  // Keep track of the number of iterations.
661  ++numIterations_;
662 
663  // If the reverse search needs to be continued, do that now.
664  if (continueReverseSearch())
665  {
666  iterateReverseSearch();
667  } // If the reverse search is suspended, check whether the forward search needs to be continued.
668  else if (continueForwardSearch())
669  {
670  iterateForwardSearch();
671  } // If neither the forward search nor the reverse search needs to be continued, add more samples.
672  else
673  {
674  // Add new samples to the graph, respecting the termination condition.
675  if (graph_.addSamples(batchSize_, terminationCondition))
676  {
677  // Remove useless samples from the graph.
678  if (isPruningEnabled_)
679  {
680  graph_.prune();
681  }
682 
683  // Clear the reverse search tree.
684  for (auto &goal : graph_.getGoalVertices())
685  {
686  invalidateCostToComeFromGoalOfReverseBranch(goal);
687  }
688 
689  // Add new start and goal states if necessary.
691  {
693  }
694 
695  // Reinitialize the queues.
696  clearReverseQueue();
697  clearForwardQueue();
698  insertGoalVerticesInReverseQueue();
699  expandStartVerticesIntoForwardQueue();
700  }
701  }
702  }
703 
704  void AITstar::iterateForwardSearch()
705  {
706  assert(!forwardQueue_.empty());
707 
708  // Get the most promising edge.
709  auto parent = forwardQueue_.top()->data.getParent();
710  auto child = forwardQueue_.top()->data.getChild();
711  child->removeFromForwardQueueIncomingLookup(forwardQueue_.top());
712  parent->removeFromForwardQueueOutgoingLookup(forwardQueue_.top());
713  forwardQueue_.pop();
714 
715  // Ensure that the child is consistent and the parent isn't the goal.
716  assert(child->isConsistent());
717  assert(!graph_.isGoal(parent));
718 
719  // This counts as processing an edge.
720  ++numProcessedEdges_;
721 
722  // If this edge is already in the forward tree, it's a freeby.
723  if (child->hasForwardParent() && child->getForwardParent()->getId() == parent->getId())
724  {
725  insertOrUpdateInForwardQueue(getOutgoingEdges(child));
726  return;
727  } // Check if this edge can possibly improve the current search tree.
728  else if (objective_->isCostBetterThan(objective_->combineCosts(parent->getCostToComeFromStart(),
729  objective_->motionCostHeuristic(
730  parent->getState(), child->getState())),
731  child->getCostToComeFromStart()))
732  {
733  // The edge can possibly improve the solution and the path to the child. Let's check it for
734  // collision.
735  if (parent->isWhitelistedAsChild(child) ||
736  motionValidator_->checkMotion(parent->getState(), child->getState()))
737  {
738  // Remember that this is a good edge.
739  if (!parent->isWhitelistedAsChild(child))
740  {
741  parent->whitelistAsChild(child);
742  numEdgeCollisionChecks_++;
743  }
744 
745  // Compute the edge cost.
746  const auto edgeCost = objective_->motionCost(parent->getState(), child->getState());
747 
748  // Check if the edge can improve the cost to come to the child.
749  if (objective_->isCostBetterThan(
750  objective_->combineCosts(parent->getCostToComeFromStart(), edgeCost),
751  child->getCostToComeFromStart()))
752  {
753  // Rewire the child.
754  child->setForwardParent(parent, edgeCost);
755 
756  // Add it to the children of the parent.
757  parent->addToForwardChildren(child);
758 
759  // Share the good news with the whole branch.
760  child->updateCostOfForwardBranch();
761 
762  // Check if the solution can benefit from this.
763  updateSolution(child);
764 
765  // Insert the child's outgoing edges into the queue.
766  insertOrUpdateInForwardQueue(getOutgoingEdges(child));
767  }
768  }
769  else // This edge is in collision
770  {
771  // The edge should be blacklisted in both directions.
772  parent->blacklistAsChild(child);
773  child->blacklistAsChild(parent);
774 
775  // Repair the reverse search if this edge was in the reverse search tree.
776  if (parent->hasReverseParent() && parent->getReverseParent()->getId() == child->getId())
777  {
778  // The parent was connected to the child through an invalid edge, so we need to invalidate
779  // the branch of the reverse search tree starting from the parent.
780  invalidateCostToComeFromGoalOfReverseBranch(parent);
781  }
782  }
783  }
784  }
785 
786  void AITstar::iterateReverseSearch()
787  {
788  assert(!reverseQueue_.empty());
789 
790  // Get the most promising vertex and remove it from the queue.
791  auto vertex = reverseQueue_.top()->data.second;
792  reverseQueue_.pop();
793  vertex->resetReverseQueuePointer();
794 
795  // The open queue should not contain consistent vertices.
796  assert(!vertex->isConsistent());
797 
798  // Check if the vertex is underconsistent. g[s] < v[s].
799  if (objective_->isCostBetterThan(vertex->getCostToComeFromGoal(), vertex->getExpandedCostToComeFromGoal()))
800  {
801  // Make the vertex consistent and update the vertex.
802  vertex->setExpandedCostToComeFromGoal(vertex->getCostToComeFromGoal());
803  updateReverseSearchNeighbors(vertex);
804 
805  // Update the number of inconsistent targets in the forward queue.
806  numInconsistentOrUnconnectedTargets_ -= vertex->getForwardQueueIncomingLookup().size();
807  }
808  else
809  {
810  // Make the vertex overconsistent.
811  vertex->setExpandedCostToComeFromGoal(objective_->infiniteCost());
812 
813  // Update the vertex and its neighbors.
814  updateReverseSearchVertex(vertex);
815  updateReverseSearchNeighbors(vertex);
816  }
817  }
818 
819  bool AITstar::isEdgeBetter(const Edge &lhs, const Edge &rhs) const
820  {
821  return std::lexicographical_compare(
822  lhs.getSortKey().cbegin(), lhs.getSortKey().cend(), rhs.getSortKey().cbegin(), rhs.getSortKey().cend(),
823  [this](const auto &a, const auto &b) { return objective_->isCostBetterThan(a, b); });
824  }
825 
826  bool AITstar::isVertexBetter(const aitstar::KeyVertexPair &lhs, const aitstar::KeyVertexPair &rhs) const
827  {
828  // If the costs of two vertices are equal then we prioritize inconsistent vertices that are targets of
829  // edges in the forward queue.
830  if (objective_->isCostEquivalentTo(lhs.first[0u], rhs.first[0u]) &&
831  objective_->isCostEquivalentTo(lhs.first[1u], rhs.first[1u]))
832  {
833  return !lhs.second->getForwardQueueIncomingLookup().empty() && !lhs.second->isConsistent();
834  }
835  else
836  {
837  // Otherwise it's a regular lexicographical comparison of the keys.
838  return std::lexicographical_compare(
839  lhs.first.cbegin(), lhs.first.cend(), rhs.first.cbegin(), rhs.first.cend(),
840  [this](const auto &a, const auto &b) { return objective_->isCostBetterThan(a, b); });
841  }
842  }
843 
844  void AITstar::updateReverseSearchVertex(const std::shared_ptr<Vertex> &vertex)
845  {
846  // If the vertex is a goal, there's no updating to do.
847  if (graph_.isGoal(vertex))
848  {
849  assert(objective_->isCostEquivalentTo(vertex->getCostToComeFromGoal(), objective_->identityCost()));
850  return;
851  }
852 
853  // Get the best parent for this vertex.
854  auto bestParent = vertex->getReverseParent();
855  auto bestCost = vertex->hasReverseParent() ? vertex->getCostToComeFromGoal() : objective_->infiniteCost();
856 
857  // Check all neighbors as defined by the RGG.
858  for (const auto &neighbor : graph_.getNeighbors(vertex))
859  {
860  if (neighbor->getId() != vertex->getId() && !neighbor->isBlacklistedAsChild(vertex) &&
861  !vertex->isBlacklistedAsChild(neighbor))
862  {
863  auto edgeCost = objective_->motionCostHeuristic(neighbor->getState(), vertex->getState());
864  auto parentCost = objective_->combineCosts(neighbor->getExpandedCostToComeFromGoal(), edgeCost);
865  if (objective_->isCostBetterThan(parentCost, bestCost))
866  {
867  bestParent = neighbor;
868  bestCost = parentCost;
869  }
870  }
871  }
872 
873  // Check all children this vertex holds in the forward search.
874  for (const auto &forwardChild : vertex->getForwardChildren())
875  {
876  auto edgeCost = objective_->motionCostHeuristic(forwardChild->getState(), vertex->getState());
877  auto parentCost = objective_->combineCosts(forwardChild->getExpandedCostToComeFromGoal(), edgeCost);
878 
879  if (objective_->isCostBetterThan(parentCost, bestCost))
880  {
881  bestParent = forwardChild;
882  bestCost = parentCost;
883  }
884  }
885 
886  // Check the parent of this vertex in the forward search.
887  if (vertex->hasForwardParent())
888  {
889  auto forwardParent = vertex->getForwardParent();
890  auto edgeCost = objective_->motionCostHeuristic(forwardParent->getState(), vertex->getState());
891  auto parentCost = objective_->combineCosts(forwardParent->getExpandedCostToComeFromGoal(), edgeCost);
892 
893  if (objective_->isCostBetterThan(parentCost, bestCost))
894  {
895  bestParent = forwardParent;
896  bestCost = parentCost;
897  }
898  }
899 
900  // Set the best cost as the cost-to-come from the goal.
902 
903  // What happens next depends on whether the vertex is disconnected or not.
904  if (objective_->isFinite(bestCost))
905  {
906  // The vertex is connected. Update the reverse parent.
907  vertex->setReverseParent(bestParent);
908  bestParent->addToReverseChildren(vertex);
909  }
910  else
911  {
912  // This vertex is now orphaned. Reset the reverse parent if the vertex had one.
913  if (vertex->hasReverseParent())
914  {
915  vertex->getReverseParent()->removeFromReverseChildren(vertex->getId());
916  vertex->resetReverseParent();
917  }
918  }
919 
920  // If this has made the vertex inconsistent, insert or update it in the open queue.
921  if (!vertex->isConsistent())
922  {
923  insertOrUpdateInReverseQueue(vertex);
924  }
925  else // Remove this vertex from the queue if it is in the queue if it is consistent.
926  {
927  auto reverseQueuePointer = vertex->getReverseQueuePointer();
928  if (reverseQueuePointer)
929  {
930  reverseQueue_.remove(reverseQueuePointer);
931  vertex->resetReverseQueuePointer();
932  }
933  }
934 
935  // This vertex now has a changed cost-to-come in the reverse search. All edges in the forward queue that
936  // have this vertex as a target must be updated. This cannot be delayed, as whether the reverse search
937  // can be suspended depends on the best edge in the forward queue.
938  for (const auto &element : vertex->getForwardQueueIncomingLookup())
939  {
940  auto &edge = element->data;
941  edge.setSortKey(computeSortKey(edge.getParent(), edge.getChild()));
942  forwardQueue_.update(element);
943  }
944  }
945 
946  void AITstar::updateReverseSearchNeighbors(const std::shared_ptr<Vertex> &vertex)
947  {
948  // Start with the reverse search children, because if this vertex becomes the parent of a neighbor, that
949  // neighbor would be updated again as part of the reverse children.
950  for (const auto &child : vertex->getReverseChildren())
951  {
952  updateReverseSearchVertex(child);
953  }
954 
955  // We can now process the neighbors.
956  for (const auto &neighbor : graph_.getNeighbors(vertex))
957  {
958  if (neighbor->getId() != vertex->getId() && !neighbor->isBlacklistedAsChild(vertex) &&
959  !vertex->isBlacklistedAsChild(neighbor))
960  {
961  updateReverseSearchVertex(neighbor);
962  }
963  }
964 
965  // We also need to update the forward search children.
966  for (const auto &child : vertex->getForwardChildren())
967  {
968  updateReverseSearchVertex(child);
969  }
970 
971  // We also need to update the forward search parent if it exists.
972  if (vertex->hasForwardParent())
973  {
974  updateReverseSearchVertex(vertex->getForwardParent());
975  }
976  }
977 
978  void AITstar::insertOrUpdateInReverseQueue(const std::shared_ptr<Vertex> &vertex)
979  {
980  // Get the pointer to the element in the queue.
981  auto element = vertex->getReverseQueuePointer();
982 
983  // Update it if it is in the queue.
984  if (element)
985  {
986  element->data.first = computeSortKey(vertex);
987  reverseQueue_.update(element);
988  }
989  else // Insert it into the queue otherwise.
990  {
991  // Compute the sort key for the vertex queue.
992  std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>> element(computeSortKey(vertex),
993  vertex);
994 
995  // Insert the vertex into the queue, storing the corresponding pointer.
996  auto reverseQueuePointer = reverseQueue_.insert(element);
997  vertex->setReverseQueuePointer(reverseQueuePointer);
998  }
999  }
1000 
1001  void AITstar::insertOrUpdateInForwardQueue(const Edge &edge)
1002  {
1003  // Check if the edge is already in the queue and can be updated.
1004  const auto lookup = edge.getChild()->getForwardQueueIncomingLookup();
1005  const auto it = std::find_if(lookup.begin(), lookup.end(), [&edge](const auto element) {
1006  return element->data.getParent()->getId() == edge.getParent()->getId();
1007  });
1008 
1009  if (it != lookup.end())
1010  {
1011  // We used the incoming lookup of the child. Assert that it is already in the outgoing lookup of the
1012  // parent.
1013  assert(std::find_if(edge.getParent()->getForwardQueueOutgoingLookup().begin(),
1014  edge.getParent()->getForwardQueueOutgoingLookup().end(),
1015  [&edge](const auto element) {
1016  return element->data.getChild()->getId() == edge.getChild()->getId();
1017  }) != edge.getParent()->getForwardQueueOutgoingLookup().end());
1018 
1019  // This edge exists in the queue. If the new sort key is better than the old, we update it.
1020  if (isEdgeBetter(edge, (*it)->data))
1021  {
1022  (*it)->data.setSortKey(edge.getSortKey());
1023  forwardQueue_.update(*it);
1024  }
1025  }
1026  else
1027  {
1028  auto element = forwardQueue_.insert(edge);
1029  edge.getParent()->addToForwardQueueOutgoingLookup(element);
1030  edge.getChild()->addToForwardQueueIncomingLookup(element);
1031 
1032  // Incement the counter if the target is inconsistent.
1033  if (!edge.getChild()->isConsistent() || !objective_->isFinite(edge.getChild()->getCostToComeFromGoal()))
1034  {
1035  ++numInconsistentOrUnconnectedTargets_;
1036  }
1037  }
1038  }
1039 
1040  void AITstar::insertOrUpdateInForwardQueue(const std::vector<Edge> &edges)
1041  {
1042  for (const auto &edge : edges)
1043  {
1044  insertOrUpdateInForwardQueue(edge);
1045  }
1046  }
1047 
1048  std::shared_ptr<ompl::geometric::PathGeometric>
1049  AITstar::getPathToVertex(const std::shared_ptr<Vertex> &vertex) const
1050  {
1051  // Create the reverse path by following the parents to the start.
1052  std::vector<std::shared_ptr<Vertex>> reversePath;
1053  auto current = vertex;
1054  while (!graph_.isStart(current))
1055  {
1056  reversePath.emplace_back(current);
1057  current = current->getForwardParent();
1058  }
1059  reversePath.emplace_back(current);
1060 
1061  // Reverse the reverse path to get the forward path.
1062  auto path = std::make_shared<ompl::geometric::PathGeometric>(Planner::si_);
1063  for (const auto &vertex : boost::adaptors::reverse(reversePath))
1064  {
1065  path->append(vertex->getState());
1066  }
1067 
1068  return path;
1069  }
1070 
1071  std::array<ompl::base::Cost, 3u> AITstar::computeSortKey(const std::shared_ptr<Vertex> &parent,
1072  const std::shared_ptr<Vertex> &child) const
1073  {
1074  // Compute the sort key [g_T(start) + c_hat(start, neighbor) + h_hat(neighbor), g_T(start) +
1075  // c_hat(start, neighbor), g_T(start)].
1076  ompl::base::Cost edgeCostHeuristic = objective_->motionCostHeuristic(parent->getState(), child->getState());
1077  return {
1078  objective_->combineCosts(objective_->combineCosts(parent->getCostToComeFromStart(), edgeCostHeuristic),
1079  child->getCostToGoToGoal()),
1080  objective_->combineCosts(edgeCostHeuristic, child->getCostToGoToGoal()),
1081  parent->getCostToComeFromStart()};
1082  }
1083 
1084  std::array<ompl::base::Cost, 2u> AITstar::computeSortKey(const std::shared_ptr<Vertex> &vertex) const
1085  {
1086  // LPA* sort key is [min(g(x), v(x)) + h(x); min(g(x), v(x))].
1087  return {objective_->combineCosts(objective_->betterCost(vertex->getCostToComeFromGoal(),
1088  vertex->getExpandedCostToComeFromGoal()),
1089  computeCostToGoToStartHeuristic(vertex)),
1090  objective_->betterCost(vertex->getCostToComeFromGoal(), vertex->getExpandedCostToComeFromGoal())};
1091  }
1092 
1093  std::vector<Edge> AITstar::getOutgoingEdges(const std::shared_ptr<Vertex> &vertex) const
1094  {
1095  // Prepare the return variable.
1096  std::vector<Edge> outgoingEdges;
1097 
1098  // Insert the edges to the current children.
1099  for (const auto &child : vertex->getForwardChildren())
1100  {
1101  outgoingEdges.emplace_back(vertex, child, computeSortKey(vertex, child));
1102  }
1103 
1104  // Insert the edges to the current neighbors.
1105  for (const auto &neighbor : graph_.getNeighbors(vertex))
1106  {
1107  // We do not want self loops.
1108  if (vertex->getId() == neighbor->getId())
1109  {
1110  continue;
1111  }
1112 
1113  // If the neighbor is the reverse parent, it will explicitly be added later.
1114  if (vertex->hasReverseParent() && neighbor->getId() == vertex->getReverseParent()->getId())
1115  {
1116  continue;
1117  }
1118 
1119  // We do not want blacklisted edges.
1120  if (neighbor->isBlacklistedAsChild(vertex) || vertex->isBlacklistedAsChild(neighbor))
1121  {
1122  continue;
1123  }
1124 
1125  // If none of the above tests caught on, we can insert the edge.
1126  outgoingEdges.emplace_back(vertex, neighbor, computeSortKey(vertex, neighbor));
1127  }
1128 
1129  // Insert the edge to the reverse search parent.
1130  if (vertex->hasReverseParent())
1131  {
1132  const auto &reverseParent = vertex->getReverseParent();
1133  outgoingEdges.emplace_back(vertex, reverseParent, computeSortKey(vertex, reverseParent));
1134  }
1135 
1136  return outgoingEdges;
1137  }
1138 
1139  void AITstar::updateExactSolution()
1140  {
1141  // Check if any of the goals have a cost to come less than the current solution cost.
1142  for (const auto &goal : graph_.getGoalVertices())
1143  {
1144  // We need to check whether the cost is better, or whether someone has removed the exact solution
1145  // from the problem definition.
1146  if (objective_->isCostBetterThan(goal->getCostToComeFromStart(), solutionCost_) ||
1147  (!pdef_->hasExactSolution() && objective_->isFinite(goal->getCostToComeFromStart())))
1148  {
1149  // Remember the incumbent cost.
1150  solutionCost_ = goal->getCostToComeFromStart();
1151 
1152  // Create a solution.
1153  ompl::base::PlannerSolution solution(getPathToVertex(goal));
1154  solution.setPlannerName(name_);
1155 
1156  // Set the optimized flag.
1157  solution.setOptimized(objective_, solutionCost_, objective_->isSatisfied(solutionCost_));
1158 
1159  // Let the problem definition know that a new solution exists.
1160  pdef_->addSolutionPath(solution);
1161 
1162  // If enabled, pass the intermediate solution back through the callback:
1163  if (static_cast<bool>(pdef_->getIntermediateSolutionCallback()))
1164  {
1165  const auto& path = solution.path_->as<ompl::geometric::PathGeometric>()->getStates();
1166  // the callback requires a vector with const elements
1167  std::vector<const base::State *> const_path(path.begin(), path.end());
1168  pdef_->getIntermediateSolutionCallback()(this, const_path, solutionCost_);
1169  }
1170 
1171  // Let the user know about the new solution.
1172  informAboutNewSolution();
1173  }
1174  }
1175  }
1176 
1177  void AITstar::updateApproximateSolution()
1178  {
1179  for (auto &start : graph_.getStartVertices())
1180  {
1181  start->callOnForwardBranch([this](const auto &vertex) -> void { updateApproximateSolution(vertex); });
1182  }
1183  }
1184 
1185  void AITstar::updateApproximateSolution(const std::shared_ptr<Vertex> &vertex)
1186  {
1187  assert(trackApproximateSolutions_);
1188  if (vertex->hasForwardParent() || graph_.isStart(vertex))
1189  {
1190  auto costToGoal = computeCostToGoToGoal(vertex);
1191 
1192  // We need to check whether this is better than the current approximate solution or whether someone
1193  // has removed all approximate solutions from the problem definition.
1194  if (objective_->isCostBetterThan(costToGoal, approximateSolutionCostToGoal_) ||
1195  !pdef_->hasApproximateSolution())
1196  {
1197  // Remember the incumbent approximate cost.
1198  approximateSolutionCost_ = vertex->getCostToComeFromStart();
1199  approximateSolutionCostToGoal_ = costToGoal;
1200  ompl::base::PlannerSolution solution(getPathToVertex(vertex));
1201  solution.setPlannerName(name_);
1202 
1203  // Set the approximate flag.
1204  solution.setApproximate(costToGoal.value());
1205 
1206  // This solution is approximate and can not satisfy the objective.
1207  solution.setOptimized(objective_, approximateSolutionCost_, false);
1208 
1209  // Let the problem definition know that a new solution exists.
1210  pdef_->addSolutionPath(solution);
1211  }
1212  }
1213  };
1214 
1215  ompl::base::PlannerStatus::StatusType AITstar::updateSolution()
1216  {
1217  updateExactSolution();
1218  if (objective_->isFinite(solutionCost_))
1219  {
1220  return ompl::base::PlannerStatus::StatusType::EXACT_SOLUTION;
1221  }
1222  else if (trackApproximateSolutions_)
1223  {
1224  updateApproximateSolution();
1225  return ompl::base::PlannerStatus::StatusType::APPROXIMATE_SOLUTION;
1226  }
1227  else
1228  {
1229  return ompl::base::PlannerStatus::StatusType::TIMEOUT;
1230  }
1231  }
1232 
1233  ompl::base::PlannerStatus::StatusType AITstar::updateSolution(const std::shared_ptr<Vertex> &vertex)
1234  {
1235  updateExactSolution();
1236  if (objective_->isFinite(solutionCost_))
1237  {
1238  return ompl::base::PlannerStatus::StatusType::EXACT_SOLUTION;
1239  }
1240  else if (trackApproximateSolutions_)
1241  {
1242  updateApproximateSolution(vertex);
1243  return ompl::base::PlannerStatus::StatusType::APPROXIMATE_SOLUTION;
1244  }
1245  else
1246  {
1247  return ompl::base::PlannerStatus::StatusType::TIMEOUT;
1248  }
1249  }
1250 
1251  ompl::base::Cost AITstar::computeCostToGoToStartHeuristic(const std::shared_ptr<Vertex> &vertex) const
1252  {
1253  // We need to loop over all start vertices and see which is the closest one.
1254  ompl::base::Cost bestCost = objective_->infiniteCost();
1255  for (const auto &start : graph_.getStartVertices())
1256  {
1257  bestCost = objective_->betterCost(
1258  bestCost, objective_->motionCostHeuristic(vertex->getState(), start->getState()));
1259  }
1260  return bestCost;
1261  }
1262 
1263  ompl::base::Cost AITstar::computeCostToGoToGoalHeuristic(const std::shared_ptr<Vertex> &vertex) const
1264  {
1265  // We need to loop over all goal vertices and see which is the closest one.
1266  ompl::base::Cost bestCost = objective_->infiniteCost();
1267  for (const auto &goal : graph_.getGoalVertices())
1268  {
1269  bestCost = objective_->betterCost(
1270  bestCost, objective_->motionCostHeuristic(vertex->getState(), goal->getState()));
1271  }
1272  return bestCost;
1273  }
1274 
1275  ompl::base::Cost AITstar::computeCostToGoToGoal(const std::shared_ptr<Vertex> &vertex) const
1276  {
1277  // We need to loop over all goal vertices and see which is the closest one.
1278  ompl::base::Cost bestCost = objective_->infiniteCost();
1279  for (const auto &goal : graph_.getGoalVertices())
1280  {
1281  bestCost =
1282  objective_->betterCost(bestCost, objective_->motionCost(vertex->getState(), goal->getState()));
1283  }
1284  return bestCost;
1285  }
1286 
1287  ompl::base::Cost AITstar::computeBestCostToComeFromGoalOfAnyStart() const
1288  {
1289  // We need to loop over all start vertices and see which is the closest one.
1290  ompl::base::Cost bestCost = objective_->infiniteCost();
1291  for (const auto &start : graph_.getStartVertices())
1292  {
1293  bestCost = objective_->betterCost(bestCost, start->getCostToComeFromGoal());
1294  }
1295  return bestCost;
1296  }
1297 
1298  std::size_t AITstar::countNumVerticesInForwardTree() const
1299  {
1300  std::size_t numVerticesInForwardTree = 0u;
1301  auto vertices = graph_.getVertices();
1302  for (const auto &vertex : vertices)
1303  {
1304  if (graph_.isStart(vertex) || vertex->hasForwardParent())
1305  {
1306  ++numVerticesInForwardTree;
1307  }
1308  }
1309  return numVerticesInForwardTree;
1310  }
1311 
1312  std::size_t AITstar::countNumVerticesInReverseTree() const
1313  {
1314  std::size_t numVerticesInReverseTree = 0u;
1315  auto vertices = graph_.getVertices();
1316  for (const auto &vertex : vertices)
1317  {
1318  if (graph_.isGoal(vertex) || vertex->hasReverseParent())
1319  {
1320  ++numVerticesInReverseTree;
1321  }
1322  }
1323  return numVerticesInReverseTree;
1324  }
1325 
1326  void AITstar::invalidateCostToComeFromGoalOfReverseBranch(const std::shared_ptr<Vertex> &vertex)
1327  {
1328  // If this vertex is consistent before invalidation, then all incoming edges now have targets that are
1329  // inconsistent.
1330  if (vertex->isConsistent())
1331  {
1332  numInconsistentOrUnconnectedTargets_ += vertex->getForwardQueueIncomingLookup().size();
1333  }
1334 
1335  // Reset the cost to come from the goal and the reverse parent unless the vertex is itself a goal.
1336  if (!graph_.isGoal(vertex))
1337  {
1338  // Reset the cost to come from the goal.
1339  vertex->resetCostToComeFromGoal();
1340 
1341  // Reset the reverse parent.
1342  vertex->getReverseParent()->removeFromReverseChildren(vertex->getId());
1343  vertex->resetReverseParent();
1344  }
1345 
1346  // Reset the expanded cost to come from goal.
1348 
1349  // Update all affected edges in the forward queue.
1350  for (const auto &edge : vertex->getForwardQueueIncomingLookup())
1351  {
1352  edge->data.setSortKey(computeSortKey(edge->data.getParent(), edge->data.getChild()));
1353  forwardQueue_.update(edge);
1354  }
1355 
1356  // Remove this vertex from the reverse search queue if it is in it.
1357  auto reverseQueuePointer = vertex->getReverseQueuePointer();
1358  if (reverseQueuePointer)
1359  {
1360  reverseQueue_.remove(reverseQueuePointer);
1361  vertex->resetReverseQueuePointer();
1362  }
1363 
1364  // Update the cost of all reverse children.
1365  for (const auto &child : vertex->getReverseChildren())
1366  {
1367  invalidateCostToComeFromGoalOfReverseBranch(child);
1368  }
1369 
1370  // Update the reverse search vertex to ensure that this vertex is placed in open if necessary.
1371  updateReverseSearchVertex(vertex);
1372  }
1373 
1374  } // namespace geometric
1375 } // namespace ompl
PlannerTerminationCondition plannerAlwaysTerminatingCondition()
Simple termination condition that always returns true. The termination condition will always be met.
void clear() override
Clears the algorithm's internal state.
Definition: AITstar.cpp:201
bool hasAStartState() const
Returns whether the graph has a goal state.
bool haveMoreStartStates() const
Check if there are more potential start states.
Definition: Planner.cpp:339
std::vector< std::shared_ptr< Vertex > > getVertices() const
Get all vertices.
ompl::base::PlannerStatus::StatusType ensureSetup()
Checks whether the planner is successfully setup.
Definition: AITstar.cpp:145
A shared pointer wrapper for ompl::base::SpaceInformation.
ompl::base::Cost getCostToGoToGoal() const
Returns the cost to go heuristic from this vertex.
Definition: Vertex.cpp:144
std::vector< aitstar::Edge > getEdgesInQueue() const
Get the edge queue.
Definition: AITstar.cpp:595
void setup(const ompl::base::SpaceInformationPtr &spaceInformation, const ompl::base::ProblemDefinitionPtr &problemDefinition, ompl::base::PlannerInputStates *inputStates)
The setup method for the graph. Needed to have it on the stack.
Element * top() const
Return the top element. nullptr for an empty heap.
Definition: BinaryHeap.h:184
aitstar::Edge getNextEdgeInQueue() const
Get the next edge in the queue.
Definition: AITstar.cpp:617
void pop()
Remove the top element.
Definition: BinaryHeap.h:190
void setReverseQueuePointer(typename VertexQueue::Element *pointer)
Sets the reverse queue pointer of this vertex.
Definition: Vertex.cpp:465
std::vector< EdgeQueue::Element * > getForwardQueueIncomingLookup() const
Returns the forward queue incoming lookup of this vertex.
Definition: Vertex.cpp:511
void updateStartAndGoalStates(const ompl::base::PlannerTerminationCondition &terminationCondition, ompl::base::PlannerInputStates *inputStates)
Adds new start and goals to the graph if avavilable and creates a new informed sampler if necessary.
bool getUseKNearest() const
Whether the graph uses a k-nearest connection model. If false, it uses an r-disc model.
ProblemDefinitionPtr pdef_
The user set problem definition.
Definition: Planner.h:477
void setBatchSize(std::size_t batchSize)
Set the batch size.
Definition: AITstar.cpp:306
void setReverseParent(const std::shared_ptr< Vertex > &vertex)
Sets the parent vertex (in the reverse-search tree).
Definition: Vertex.cpp:283
bool isBlacklistedAsChild(const std::shared_ptr< Vertex > &vertex) const
Returns whether a child is blacklisted.
Definition: Vertex.cpp:384
std::shared_ptr< Vertex > getParent() const
Returns the parent in this edge.
Definition: Edge.cpp:156
ompl::base::State * getState()
Provides write access to the underlying state.
Definition: Vertex.cpp:114
double value() const
The value of the cost.
Definition: Cost.h:152
Representation of a solution to a planning problem.
bool hasAGoalState() const
Returns whether the graph has a goal state.
void resetCostToComeFromGoal()
Resets the cost to come to this vertex from the goal to infinity.
Definition: Vertex.cpp:192
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:111
void setMaxNumberOfGoals(unsigned int numberOfGoals)
Set the maximum number of goals AIT* will sample from sampleable goal regions.
Definition: AITstar.cpp:364
void clear()
Clear the heap.
Definition: BinaryHeap.h:176
bool empty() const
Check if the heap is empty.
Definition: BinaryHeap.h:259
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68
const std::vector< std::shared_ptr< Vertex > > & getStartVertices() const
Get the start vertices.
void setMaxNumberOfGoals(unsigned int maxNumberOfGoals)
Set the maximum number of goals AIT* will sample from sampleable goal regions.
std::size_t getId() const
Get the unique id of this vertex.
Definition: Vertex.cpp:109
Definition of a geometric path.
Definition: PathGeometric.h:97
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
Definition: PlannerData.h:238
virtual void checkValidity()
Check to see if the planner is in a working state (setup has been called, a goal was set,...
Definition: Planner.cpp:106
std::vector< std::shared_ptr< aitstar::Vertex > > getVerticesInQueue() const
Get the vertex queue.
Definition: AITstar.cpp:602
std::size_t getNumberOfValidSamples() const
Returns the total number of valid samples found.
std::shared_ptr< Vertex > getChild() const
Returns the child in this edge.
Definition: Edge.cpp:161
std::string name_
The name of this planner.
Definition: Planner.h:483
ompl::base::Cost getExpandedCostToComeFromGoal() const
Returns the cost to come to this vertex from the goal when it was expanded.
Definition: Vertex.cpp:139
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
void getContent(std::vector< _T > &content) const
Get the data stored in this heap.
Definition: BinaryHeap.h:271
void clear()
Resets the graph to its construction state, without resetting options.
void resetReverseParent()
Resets the reverse parent of the vertex.
Definition: Vertex.cpp:295
ompl::base::PlannerStatus::StatusType ensureStartAndGoalStates(const ompl::base::PlannerTerminationCondition &terminationCondition)
Checks whether the problem is successfully setup.
Definition: AITstar.cpp:169
double getRewireFactor() const
Get the reqire factor of the RGG.
std::shared_ptr< aitstar::Vertex > getNextVertexInQueue() const
Get the next vertex in the queue.
Definition: AITstar.cpp:627
A class to store the exit status of Planner::solve()
bool areApproximateSolutionsTracked() const
Get whether approximate solutions are tracked.
Definition: AITstar.cpp:339
void setCostToComeFromGoal(const ompl::base::Cost &cost)
Sets the cost to come to this vertex from the goal.
Definition: Vertex.cpp:187
void enablePruning(bool prune)
Set whether pruning is enabled or not.
Definition: AITstar.cpp:344
const std::array< ompl::base::Cost, 3u > & getSortKey() const
Returns the sort key associated with this edge.
Definition: Edge.cpp:166
std::shared_ptr< Vertex > getForwardParent() const
Returns the parent of the vertex (in the forward-search tree).
Definition: Vertex.cpp:162
std::size_t getNumberOfSampledStates() const
Returns the total number of sampled states.
std::vector< std::shared_ptr< aitstar::Vertex > > getVerticesInReverseSearchTree() const
Get the vertices in the reverse search tree.
Definition: AITstar.cpp:637
ompl::base::Cost getCostToComeFromGoal() const
Returns the cost to come to this vertex from the goal.
Definition: Vertex.cpp:134
void resetReverseQueuePointer()
Resets the reverse queue pointer.
Definition: Vertex.cpp:490
void getPlannerData(base::PlannerData &data) const override
Get the planner data.
Definition: AITstar.cpp:262
bool setup_
Flag indicating whether setup() has been called.
Definition: Planner.h:497
void setSortKey(const std::array< ompl::base::Cost, 3u > &key)
Sets the sort key associated with this edge.
Definition: Edge.cpp:171
bool isStart(const std::shared_ptr< Vertex > &vertex) const
Checks whether the vertex is a start vertex.
void setUseKNearest(bool useKNearest)
Whether to use a k-nearest connection model. If false, it uses an r-disc model.
double getRewireFactor() const
Get the rewire factor of the RGG graph.
Definition: AITstar.cpp:321
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
std::vector< std::shared_ptr< Vertex > > getForwardChildren() const
Returns this vertex's children in the forward search tree.
Definition: Vertex.cpp:436
@ GOAL_SAMPLEABLE_REGION
This bit is set if casting to sampleable goal regions (ompl::base::GoalSampleableRegion) is possible.
Definition: GoalTypes.h:152
void prune()
Prune all samples that can not contribute to a solution better than the current one.
unsigned int addVertex(const PlannerDataVertex &st)
Adds the given vertex to the graph data. The vertex index is returned. Duplicates are not added....
const std::vector< std::shared_ptr< Vertex > > & getGoalVertices() const
Get the goal vertices.
Element * insert(const _T &data)
Add a new element.
Definition: BinaryHeap.h:204
VertexQueue::Element * getReverseQueuePointer() const
Returns the reverse queue pointer of this vertex.
void remove(Element *element)
Remove a specific element.
Definition: BinaryHeap.h:196
bool hasReverseParent() const
Returns whether this vertex has a parent in the reverse search.
Definition: Vertex.cpp:167
PlannerInputStates pis_
Utility class to extract valid input states
Definition: Planner.h:480
std::vector< std::shared_ptr< Vertex > > getReverseChildren() const
Returns this vertex's children in the reverse search tree.
Definition: Vertex.cpp:447
bool isPruningEnabled() const
Get whether pruning is enabled or not.
Definition: AITstar.cpp:349
StatusType
The possible values of the status returned by a planner.
SpaceInformationPtr si_
The space information for which planning is done.
Definition: Planner.h:474
std::vector< std::shared_ptr< Vertex > > getNeighbors(const std::shared_ptr< Vertex > &vertex) const
Get neighbors of a vertex.
unsigned int addStartVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
std::size_t getBatchSize() const
Get the batch size.
Definition: AITstar.cpp:311
bool haveMoreGoalStates() const
Check if there are more potential goal states.
Definition: Planner.cpp:346
bool addSamples(std::size_t numNewSamples, const ompl::base::PlannerTerminationCondition &terminationCondition)
Adds a batch of samples and returns the samples it has added.
bool getUseKNearest() const
Get whether to use a k-nearest RGG connection model. If false, AIT* uses an r-disc model.
Definition: AITstar.cpp:359
ompl::base::Cost bestCost() const
Get the cost of the incumbent solution.
Definition: AITstar.cpp:257
std::shared_ptr< Vertex > getReverseParent() const
Returns the parent of the vertex (in the reverse-search tree).
Definition: Vertex.cpp:172
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
void update(Element *element)
Update an element in the heap.
Definition: BinaryHeap.h:250
void setRewireFactor(double rewireFactor)
Set the rewire factor of the RGG.
bool isGoal(const std::shared_ptr< Vertex > &vertex) const
Checks whether the vertex is a goal vertex.
void setup() override
Additional setup that can only be done once a problem definition is set.
Definition: AITstar.cpp:94
virtual bool addEdge(unsigned int v1, unsigned int v2, const PlannerDataEdge &edge=PlannerDataEdge(), Cost weight=Cost(1.0))
Adds a directed edge between the given vertex indexes. An optional edge structure and weight can be s...
unsigned int addGoalVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
unsigned int getMaxNumberOfGoals() const
Get the maximum number of goals AIT* will sample from sampleable goal regions.
bool hasForwardParent() const
Returns whether this vertex has a parent in the forward search.
Definition: Vertex.cpp:154
ompl::base::PlannerStatus solve(const ompl::base::PlannerTerminationCondition &terminationCondition) override
Solves a motion planning problem.
Definition: AITstar.cpp:218
void setUseKNearest(bool useKNearest)
Set whether to use a k-nearest RGG connection model. If false, AIT* uses an r-disc model.
Definition: AITstar.cpp:354
void setRewireFactor(double rewireFactor)
Set the rewire factor of the RGG graph.
Definition: AITstar.cpp:316
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition: PlannerData.h:122
unsigned int getMaxNumberOfGoals() const
Get the maximum number of goals AIT* will sample from sampleable goal regions.
Definition: AITstar.cpp:369
ompl::base::Cost getCostToComeFromStart() const
Returns the cost to come to this vertex from the start.
Definition: Vertex.cpp:129
Main namespace. Contains everything in this library.
Definition: AppBase.h:21
void trackApproximateSolutions(bool track)
Set whether to track approximate solutions.
Definition: AITstar.cpp:326
void resetExpandedCostToComeFromGoal()
Resets the expanded cost to come to this vertex from the goal to infinity.
Definition: Vertex.cpp:197
bool isConsistent() const
Returns whether the vertex is consistent, i.e., whether its cost-to-come is equal to the cost-to-come...
Definition: Vertex.cpp:459