ForwardQueue.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 name of the University of Toronto 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/eitstar/ForwardQueue.h"
38 
39 #include <algorithm>
40 #include <cmath>
41 
42 #include "ompl/geometric/planners/informedtrees/eitstar/Direction.h"
43 #include "ompl/geometric/planners/informedtrees/eitstar/State.h"
44 
45 namespace ompl
46 {
47  namespace geometric
48  {
49  namespace eitstar
50  {
51  ForwardQueue::ForwardQueue(const std::shared_ptr<const ompl::base::OptimizationObjective> &objective,
52  const std::shared_ptr<const ompl::base::StateSpace> &space)
53  : objective_(objective), space_(space)
54  {
55  queue_.reserve(100u);
56  }
57 
58  bool ForwardQueue::empty() const
59  {
60  return size() == 0u;
61  }
62 
63  std::size_t ForwardQueue::size() const
64  {
65  return queue_.size();
66  }
67 
68  void ForwardQueue::insertOrUpdate(const Edge &edge)
69  {
70  modifiedQueue_ = true;
71 
72  // We update by removing the old edge and inserting the new.
73  std::pair<std::size_t, std::size_t> key = std::make_pair(edge.source->getId(), edge.target->getId());
74  const auto element = makeElement(edge);
75  auto it = queue_.find(key);
76  if (it != queue_.end())
77  {
78  it->second = element;
79  }
80  else
81  {
82  queue_.insert({key, element});
83  }
84 
85  // Add it to the cache in the target state.
86  edge.target->addToSourcesOfIncomingEdgesInForwardQueue(edge.source);
87  }
88 
89  void ForwardQueue::insertOrUpdate(const std::vector<Edge> &edges)
90  {
91  modifiedQueue_ = true;
92 
93  // For now, let's just do this naively.
94  for (const auto &edge : edges)
95  {
96  insertOrUpdate(edge);
97  }
98  }
99 
100  void ForwardQueue::remove(const Edge &edge)
101  {
102  const std::pair<std::size_t, std::size_t> key =
103  std::make_pair(edge.source->getId(), edge.target->getId());
104  const auto it = queue_.find(key);
105  if (it != queue_.cend())
106  {
107  // The forward queue does not have a lookup, just erase the edge.
108  queue_.erase(it);
109  edge.target->removeFromSourcesOfIncomingEdgesInForwardQueue(edge.source);
110 
111  modifiedQueue_ = true;
112  }
113  else
114  {
115  throw std::out_of_range("Can not remove edge from the forward queue, because it is not in the "
116  "queue.");
117  }
118  }
119 
120  ForwardQueue::Container::const_iterator ForwardQueue::getFrontIter(double suboptimalityFactor)
121  {
122  if (cacheQueueLookup_ && !modifiedQueue_)
123  {
124  return front_;
125  }
126 
127  auto lowerBoundEdgeCost = objective_->infiniteCost();
128  auto bestCostEdgeCost = objective_->infiniteCost();
129 
130  auto lowerBoundEdge = queue_.cbegin();
131  auto bestCostEdge = queue_.cbegin();
132 
133  if (std::isfinite(suboptimalityFactor))
134  {
135  // Get the lower bounding edge and corresponding cost.
136  lowerBoundEdge = getLowerBoundCostEdge();
137  lowerBoundEdgeCost = inflateCost(get(lowerBoundEdge).first.lowerBoundCost, suboptimalityFactor);
138 
139  // Find the best estimate edge and corresponding cost.
140  bestCostEdge = getBestCostEstimateEdge();
141  bestCostEdgeCost = inflateCost(get(bestCostEdge).first.estimatedCost, suboptimalityFactor);
142  }
143 
144  // Find the least effort edge and corresponding cost.
145  auto bestEffortEdge = queue_.cbegin();
146  auto bestEffortEdgeCost = objective_->infiniteCost();
147  auto bestEffortLowerBoundCost = objective_->infiniteCost();
148  for (auto it = queue_.cbegin(); it != queue_.cend(); ++it)
149  {
150  if (get(it).first.estimatedEffort < get(bestEffortEdge).first.estimatedEffort &&
151  !objective_->isCostBetterThan(bestCostEdgeCost, get(it).first.estimatedCost))
152  {
153  bestEffortEdgeCost = get(it).first.estimatedCost;
154  bestEffortLowerBoundCost = get(it).first.lowerBoundCost;
155  bestEffortEdge = it;
156  }
157  else if (get(it).first.estimatedEffort == get(bestEffortEdge).first.estimatedEffort &&
158  !objective_->isCostBetterThan(bestCostEdgeCost, get(it).first.estimatedCost) &&
159  objective_->isCostBetterThan(get(it).first.lowerBoundCost, bestEffortLowerBoundCost))
160  {
161  bestEffortEdgeCost = get(it).first.estimatedCost;
162  bestEffortLowerBoundCost = get(it).first.lowerBoundCost;
163  bestEffortEdge = it;
164  }
165  }
166 
167  // Return the correct edge.
168  if (objective_->isCostBetterThan(bestEffortEdgeCost, lowerBoundEdgeCost))
169  {
170  return bestEffortEdge;
171  }
172  else if (objective_->isCostBetterThan(get(bestCostEdge).first.estimatedCost, lowerBoundEdgeCost))
173  {
174  return bestCostEdge;
175  }
176  else
177  {
178  return lowerBoundEdge;
179  }
180  }
181 
182  Edge ForwardQueue::peek(double suboptimalityFactor)
183  {
184  // Make sure the queue contains edges.
185  if (queue_.empty())
186  {
187  throw std::out_of_range("Forward queue is empty, cannot peek.");
188  }
189 
190  front_ = getFrontIter(suboptimalityFactor);
191  auto edge = get(front_).second;
192 
193  // this call updates the cached minimum effort
194  cachedMinEdgeEffort_ = getMinEffortToCome();
195  modifiedQueue_ = false;
196 
197  return edge;
198  }
199 
200  void ForwardQueue::updateIfExists(const Edge &edge)
201  {
202  // Get an iterator to the element in the queue.
203  const std::pair<std::size_t, std::size_t> key =
204  std::make_pair(edge.source->getId(), edge.target->getId());
205  const auto it = queue_.find(key);
206  if (it == queue_.end())
207  {
208  return;
209  }
210 
211  it->second = makeElement(edge);
212  modifiedQueue_ = true;
213  }
214 
215  Edge ForwardQueue::pop(double suboptimalityFactor)
216  {
217  auto it = getFrontIter(suboptimalityFactor);
218 
219  const auto edge = get(it).second;
220  queue_.erase(it);
221  edge.target->removeFromSourcesOfIncomingEdgesInForwardQueue(edge.source);
222 
223  modifiedQueue_ = true;
224 
225  return edge;
226  }
227 
228  unsigned int ForwardQueue::getMinEffortToCome() const
229  {
230  if (cacheQueueLookup_ && !modifiedQueue_)
231  {
232  return cachedMinEdgeEffort_;
233  }
234 
235  unsigned int minEdgeEffort = std::numeric_limits<unsigned int>::max();
236  for (auto it = queue_.cbegin(); it != queue_.cend(); ++it)
237  {
238  const auto edge = get(it).second;
239 
240  if (edge.target->hasReverseVertex() || edge.target->hasForwardVertex())
241  {
242  continue;
243  }
244 
245  if (edge.source->isWhitelisted(edge.target))
246  {
247  continue;
248  }
249 
250  // Get the number of checks already performed on this edge.
251  const unsigned int performedChecks = edge.target->getIncomingCollisionCheckResolution(edge.source);
252  const unsigned int fullSegmentCount =
253  space_->validSegmentCount(edge.source->raw(), edge.target->raw());
254 
255  const unsigned int edgeEffort = fullSegmentCount - performedChecks;
256 
257  if (edgeEffort < minEdgeEffort)
258  {
259  minEdgeEffort = edgeEffort;
260  }
261 
262  if (minEdgeEffort == 0u)
263  {
264  cachedMinEdgeEffort_ = 0;
265  return 0u;
266  }
267  }
268 
269  cachedMinEdgeEffort_ = minEdgeEffort;
270  return minEdgeEffort;
271  }
272 
274  {
275  auto it = getLowerBoundCostEdge();
276  return get(it).first.lowerBoundCost;
277  }
278 
279  bool ForwardQueue::containsOpenTargets(std::size_t reverseSearchTag) const
280  {
281  return std::find_if(queue_.begin(), queue_.end(),
282  [reverseSearchTag](const auto &edge)
283  {
284  auto &a = edge.second;
285  return a.second.target->asReverseVertex()->getExpandTag() != reverseSearchTag;
286  }) != queue_.end();
287  }
288 
289  void ForwardQueue::clear()
290  {
291  for (const auto &element : queue_)
292  {
293  const auto &edge = element.second.second;
294  edge.target->resetSourcesOfIncomingEdgesInForwardQueue();
295  }
296  queue_.clear();
297  modifiedQueue_ = true;
298  }
299 
300  std::vector<Edge> ForwardQueue::getEdges() const
301  {
302  std::vector<Edge> edges;
303  edges.reserve(queue_.size());
304  for (const auto &element : queue_)
305  {
306  const auto &edge = element.second.second;
307  edges.emplace_back(edge);
308  }
309  return edges;
310  }
311 
312  void ForwardQueue::rebuild()
313  {
314  const auto edges = getEdges();
315  clear();
316  insertOrUpdate(edges);
317 
318  modifiedQueue_ = true;
319  }
320 
321  std::pair<ForwardQueue::EdgeKeys, Edge> ForwardQueue::makeElement(const Edge &edge) const
322  {
323  // Compute the costs and effort.
324  const auto lowerBoundCostOfEdge = lowerBoundCost(edge);
325  const auto estimatedCostOfEdge = estimateCost(edge);
326  const auto estimatedEffortOfEdge = estimateEffort(edge);
327 
328  return {{lowerBoundCostOfEdge, estimatedCostOfEdge, estimatedEffortOfEdge}, {edge.source, edge.target}};
329  }
330 
331  ForwardQueue::Container::iterator ForwardQueue::getBestCostEstimateEdge()
332  {
333  // Find the best estimate edge and corresponding cost.
334  return std::min_element(queue_.begin(), queue_.end(),
335  [this](const auto &a, const auto &b)
336  {
337  const auto &aEdgeKey = a.second.first;
338  const auto &bEdgeKey = b.second.first;
339  return objective_->isCostBetterThan(aEdgeKey.estimatedCost,
340  bEdgeKey.estimatedCost);
341  });
342  }
343 
344  ForwardQueue::Container::const_iterator ForwardQueue::getBestCostEstimateEdge() const
345  {
346  // Find the best estimate edge and corresponding cost.
347  return std::min_element(queue_.cbegin(), queue_.cend(),
348  [this](const auto &a, const auto &b)
349  {
350  const auto &aEdgeKey = a.second.first;
351  const auto &bEdgeKey = b.second.first;
352  return objective_->isCostBetterThan(aEdgeKey.estimatedCost,
353  bEdgeKey.estimatedCost);
354  });
355  }
356 
357  ForwardQueue::Container::iterator ForwardQueue::getLowerBoundCostEdge()
358  {
359  auto it = std::max_element(queue_.begin(), queue_.end(),
360  [this](const auto &a, const auto &b)
361  {
362  const auto &aEdgeKey = a.second.first;
363  const auto &bEdgeKey = b.second.first;
364  return !objective_->isCostBetterThan(aEdgeKey.lowerBoundCost,
365  bEdgeKey.lowerBoundCost);
366  });
367 
368  return it;
369  }
370 
371  ForwardQueue::Container::const_iterator ForwardQueue::getLowerBoundCostEdge() const
372  {
373  return std::max_element(queue_.cbegin(), queue_.cend(),
374  [this](const auto &a, const auto &b)
375  {
376  const auto &aEdgeKey = a.second.first;
377  const auto &bEdgeKey = b.second.first;
378  return !objective_->isCostBetterThan(aEdgeKey.lowerBoundCost,
379  bEdgeKey.lowerBoundCost);
380  });
381  }
382 
383  ompl::base::Cost ForwardQueue::inflateCost(const ompl::base::Cost &cost, double factor) const
384  {
385  // Will this work with objectives that maximize cost (utility)?
386  if (!std::isfinite(factor) || !objective_->isFinite(cost))
387  {
388  return objective_->infiniteCost();
389  }
390  else
391  {
392  return ompl::base::Cost(cost.value() * factor);
393  }
394  }
395 
396  std::size_t ForwardQueue::estimateEffort(const Edge &edge) const
397  {
398  unsigned int edgeEffort = 0u;
399  if (edge.source->isWhitelisted(edge.target))
400  {
401  edgeEffort = 0u;
402  }
403  else
404  {
405  const unsigned int fullSegmentCount =
406  space_->validSegmentCount(edge.source->raw(), edge.target->raw());
407 
408  // Get the number of checks already performed on this edge.
409  const unsigned int performedChecks = edge.target->getIncomingCollisionCheckResolution(edge.source);
410 
411  edgeEffort = fullSegmentCount - performedChecks;
412  }
413 
414  // Make sure this doesn't overflow.
415  if (std::numeric_limits<unsigned int>::max() - edgeEffort < edge.target->getEstimatedEffortToGo())
416  {
417  return std::numeric_limits<unsigned int>::max();
418  }
419 
420  // Return the total effort
421  return edge.target->getEstimatedEffortToGo() + edgeEffort;
422  }
423 
424  ompl::base::Cost ForwardQueue::estimateCost(const Edge &edge) const
425  {
426  return objective_->combineCosts(objective_->combineCosts(edge.source->getCurrentCostToCome(),
427  objective_->motionCostBestEstimate(
428  edge.source->raw(), edge.target->raw())),
429  edge.target->getEstimatedCostToGo());
430  }
431 
432  ompl::base::Cost ForwardQueue::lowerBoundCost(const Edge &edge) const
433  {
434  return objective_->combineCosts(
435  objective_->combineCosts(edge.source->getCurrentCostToCome(),
436  objective_->motionCostHeuristic(edge.source->raw(), edge.target->raw())),
437  edge.target->getAdmissibleCostToGo());
438  }
439 
440  } // namespace eitstar
441 
442  } // namespace geometric
443 
444 } // namespace ompl
std::shared_ptr< State > source
The parent state of this edge.
Definition: Edge.h:165
std::vector< Edge > getEdges() const
Copies all edges into a vector and returns the vector.
A struct for basic edge data.
Definition: Edge.h:152
double value() const
The value of the cost.
Definition: Cost.h:152
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:111
void insertOrUpdate(const Edge &edge)
Inserts or updates an edge in the queue.
Edge pop(double suboptimalityFactor)
Returns and deletes the top element of the queue.
bool containsOpenTargets(std::size_t reverseSearchTag) const
Returns whether the queue contains edges with targets that are open or unconnected in the reverse sea...
std::size_t size() const
Returns how many elements are in the queue.
void updateIfExists(const Edge &edge)
Update an edge in the queue. Does nothing if the edge is not in the queue.
Edge peek(double suboptimalityFactor)
Returns a copy to the next edge.
ForwardQueue(const std::shared_ptr< const ompl::base::OptimizationObjective > &objective, const std::shared_ptr< const ompl::base::StateSpace > &space)
Constructs the queue given the objective and state space.
unsigned int getMinEffortToCome() const
Returns the minimum effort that remains.
std::size_t estimateEffort(const Edge &edge) const
Estimates the effort that remains to validate a solution through an edge.
void clear()
Clears the queue, i.e., deletes all elements from it.
std::shared_ptr< State > target
The child state of this edge.
Definition: Edge.h:168
ompl::base::Cost getLowerBoundOnOptimalSolutionCost() const
Returns a lower bound on the resolution-optimal solution cost.
bool empty() const
Returns whether the queue is empty.
void remove(const Edge &edge)
Removes an edge from the queue. Throws if the edge is not in the queue.
void rebuild()
Rebuilds the queue.
Main namespace. Contains everything in this library.
Definition: AppBase.h:21