Loading...
Searching...
No Matches
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
45namespace 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
59 {
60 return size() == 0u;
61 }
62
63 std::size_t ForwardQueue::size() const
64 {
65 return queue_.size();
66 }
67
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
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
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
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
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
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition Cost.h:48
double value() const
The value of the cost.
Definition Cost.h:56
virtual Cost motionCostHeuristic(const State *s1, const State *s2) const
Defines an admissible estimate on the optimal cost on the motion between states s1 and s2....
virtual Cost combineCosts(Cost c1, Cost c2) const
Get the cost that corresponds to combining the costs c1 and c2. Default implementation defines this c...
virtual bool isCostBetterThan(Cost c1, Cost c2) const
Check whether the the cost c1 is considered better than the cost c2. By default, this returns true if...
virtual Cost motionCostBestEstimate(const State *s1, const State *s2) const
Defines a possibly inadmissible estimate on the optimal cost on the motion between states s1 and s2....
virtual Cost infiniteCost() const
Get a cost which is greater than all other costs in this OptimizationObjective; required for use in D...
void clear()
Clears the queue, i.e., deletes all elements from it.
void updateIfExists(const Edge &edge)
Update an edge in the queue. Does nothing if the edge is not in the queue.
bool empty() const
Returns whether the queue is empty.
std::vector< Edge > getEdges() const
Copies all edges into a vector and returns the vector.
bool containsOpenTargets(std::size_t reverseSearchTag) const
Returns whether the queue contains edges with targets that are open or unconnected in the reverse sea...
ompl::base::Cost getLowerBoundOnOptimalSolutionCost() const
Returns a lower bound on the resolution-optimal solution cost.
void remove(const Edge &edge)
Removes an edge from the queue. Throws if the edge is not in the queue.
std::size_t estimateEffort(const Edge &edge) const
Estimates the effort that remains to validate a solution through an edge.
std::size_t size() const
Returns how many elements are in the queue.
void insertOrUpdate(const Edge &edge)
Inserts or updates an edge in the queue.
Edge peek(double suboptimalityFactor)
Returns a copy to the next edge.
unsigned int getMinEffortToCome() const
Returns the minimum effort that remains.
Edge pop(double suboptimalityFactor)
Returns and deletes the top element of the queue.
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.
This namespace contains code that is specific to planning under geometric constraints.
Main namespace. Contains everything in this library.
A struct for basic edge data.
Definition Edge.h:57
std::shared_ptr< State > source
The parent state of this edge.
Definition Edge.h:69
std::shared_ptr< State > target
The child state of this edge.
Definition Edge.h:72