Loading...
Searching...
No Matches
ReverseQueue.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#include "ompl/geometric/planners/informedtrees/eitstar/ReverseQueue.h"
37
38#include <utility>
39
40#include "ompl/geometric/planners/informedtrees/eitstar/Direction.h"
41#include "ompl/geometric/planners/informedtrees/eitstar/State.h"
42
43namespace ompl
44{
45 namespace geometric
46 {
47 namespace eitstar
48 {
49 ReverseQueue::ReverseQueue(const std::shared_ptr<const ompl::base::OptimizationObjective> &objective,
50 const std::shared_ptr<const ompl::base::StateSpace> &space,
51 const bool isQueueCostOrdered)
52 : isQueueCostOrdered_(isQueueCostOrdered)
53 , objective_(objective)
54 , space_(space)
55 , queue_(isQueueCostOrdered_ ? getCostComparisonOperator() : getEffortComparisonOperator())
56 {
57 }
58
60 {
61 return size() == 0u;
62 }
63
64 std::size_t ReverseQueue::size() const
65 {
66 return queue_.size();
67 }
68
70 {
71 if (!updateIfExists(edge))
72 {
73 // Compute the keys.
74 const auto key1 = computeAdmissibleSolutionCost(edge);
75 const auto key2 = computeAdmissibleCostToComeToTarget(edge);
76 const auto key3 = computeAdmissibleSolutionEffort(edge);
77 const auto key4 = computeInadmissibleSolutionEffort(edge);
78
79 // Create the heap element.
80 const auto element = std::make_tuple(key1, key2, key3, key4, edge);
81
82 // Insert the edge with the key in the queue.
83 const auto elementPointer = queue_.insert(element);
84
85 // Remember the element.
86 edge.source->asReverseVertex()->outgoingReverseQueueLookup_.emplace_back(elementPointer);
87 }
88 }
89
90 void ReverseQueue::insertOrUpdate(const std::vector<Edge> &edges)
91 {
92 // Let's do this naively.
93 for (const auto &edge : edges)
94 {
95 insertOrUpdate(edge);
96 }
97 }
98
99 void ReverseQueue::setCostQueueOrder(const bool isQueueCostOrdered)
100 {
101 isQueueCostOrdered_ = isQueueCostOrdered;
102
103 if (!empty())
104 {
105 throw std::runtime_error("Can't update ordering of queue if there are elements in it.");
106 }
107 if (isQueueCostOrdered_)
108 {
109 queue_.getComparisonOperator() = getCostComparisonOperator();
110 }
111 else
112 {
113 queue_.getComparisonOperator() = getEffortComparisonOperator();
114 }
115 }
116
118 {
119 if (auto element = queue_.top())
120 {
121 return std::get<4>(element->data);
122 }
123 else
124 {
125 throw std::out_of_range("There are no elements to peek in the reverse queue.");
126 }
127 }
128
129 unsigned int ReverseQueue::peekEffort() const
130 {
131 if (auto element = queue_.top())
132 {
133 return std::get<2>(element->data);
134 }
135 else
136 {
137 throw std::out_of_range("There are no elements to peek in the reverse queue.");
138 }
139 }
140
141 bool ReverseQueue::updateIfExists(const Edge &edge)
142 {
143 // Check if the edge is in the queue via the reverse queue pointers.
144 const auto &lookup = edge.source->asReverseVertex()->outgoingReverseQueueLookup_;
145 const auto it = std::find_if(lookup.cbegin(), lookup.cend(), [&edge](const auto &p)
146 { return std::get<4>(p->data).target->getId() == edge.target->getId(); });
147
148 // Indicate that the edge is not in the queue by returning false.
149 if (it == lookup.cend())
150 {
151 return false;
152 }
153
154 // Update the cost and effort and the position of the edge in the queue.
155 std::get<0>((*it)->data) = computeAdmissibleSolutionCost(edge);
156 std::get<1>((*it)->data) = computeAdmissibleCostToComeToTarget(edge);
157 std::get<2>((*it)->data) = computeAdmissibleSolutionEffort(edge);
158 std::get<3>((*it)->data) = computeInadmissibleSolutionEffort(edge);
159 queue_.update(*it);
160
161 // Indicate that the edge was updated by returning true.
162 return true;
163 }
164
166 {
167 return objective_->combineCosts(
168 objective_->combineCosts(edge.source->getAdmissibleCostToGo(),
169 objective_->motionCostHeuristic(edge.target->raw(), edge.source->raw())),
170 edge.target->getLowerBoundCostToCome());
171 }
172
173 ompl::base::Cost ReverseQueue::computeAdmissibleCostToComeToTarget(const Edge &edge) const
174 {
175 return objective_->combineCosts(
176 edge.source->getAdmissibleCostToGo(),
177 objective_->motionCostHeuristic(edge.target->raw(), edge.source->raw()));
178 }
179
181 {
182 std::size_t edgeEffort = 0u;
183 if (edge.source->isWhitelisted(edge.target))
184 {
185 edgeEffort = 0u;
186 }
187 else
188 {
189 const std::size_t fullSegmentCount =
190 space_->validSegmentCount(edge.source->raw(), edge.target->raw());
191
192 // Get the number of checks already performed on this edge.
193 const std::size_t performedChecks = edge.target->getIncomingCollisionCheckResolution(edge.source);
194
195 // Sanity check for the number of performed collision checks.
196 assert(fullSegmentCount >= performedChecks);
197
198 // The estimated effort is the remaining number of collision checks.
199 edgeEffort = fullSegmentCount - performedChecks;
200 }
201
202 const unsigned int totalEffort =
203 edge.source->getEstimatedEffortToGo() + edgeEffort + edge.target->getLowerBoundEffortToCome();
204
205 if (std::numeric_limits<unsigned int>::max() - edgeEffort - edge.source->getEstimatedEffortToGo() <
206 edge.target->getLowerBoundEffortToCome())
207 {
208 return std::numeric_limits<unsigned int>::max();
209 }
210
211 return totalEffort;
212 }
213
214 std::function<bool(const ReverseQueue::HeapElement &, const ReverseQueue::HeapElement &)>
215 ReverseQueue::getCostComparisonOperator() const
216 {
217 return [&objective = objective_](const HeapElement &lhs, const HeapElement &rhs)
218 {
219 if (objective->isCostEquivalentTo(std::get<0>(lhs), std::get<0>(rhs)))
220 {
221 if (objective->isCostEquivalentTo(std::get<1>(lhs), std::get<1>(rhs)))
222 {
223 return std::get<2>(lhs) < std::get<2>(rhs);
224 }
225 else
226 {
227 return objective->isCostBetterThan(std::get<1>(lhs), std::get<1>(rhs));
228 }
229 }
230 else
231 {
232 return objective->isCostBetterThan(std::get<0>(lhs), std::get<0>(rhs));
233 }
234 };
235 }
236
237 std::function<bool(const ReverseQueue::HeapElement &, const ReverseQueue::HeapElement &)>
238 ReverseQueue::getEffortComparisonOperator() const
239 {
240 return [&objective = objective_](const HeapElement &lhs, const HeapElement &rhs)
241 {
242 if (std::get<2>(lhs) == std::get<2>(rhs))
243 {
244 if (std::get<3>(lhs) == std::get<3>(rhs))
245 {
246 if (objective->isCostEquivalentTo(std::get<0>(lhs), std::get<0>(rhs)))
247 {
248 return objective->isCostBetterThan(std::get<1>(lhs), std::get<1>(rhs));
249 }
250 else
251 {
252 return objective->isCostBetterThan(std::get<0>(lhs), std::get<0>(rhs));
253 }
254 }
255 else
256 {
257 return std::get<3>(lhs) < std::get<3>(rhs);
258 }
259 }
260 else
261 {
262 return std::get<2>(lhs) < std::get<2>(rhs);
263 }
264 };
265 }
266
267 unsigned int ReverseQueue::computeInadmissibleSolutionEffort(const Edge &edge) const
268 {
269 std::size_t edgeEffort = 0u;
270 if (edge.source->isWhitelisted(edge.target))
271 {
272 edgeEffort = 0u;
273 }
274 else
275 {
276 const std::size_t fullSegmentCount =
277 space_->validSegmentCount(edge.source->raw(), edge.target->raw());
278
279 // Get the number of checks already performed on this edge.
280 const std::size_t performedChecks = edge.target->getIncomingCollisionCheckResolution(edge.source);
281
282 // Sanity check for the number of performed collision checks.
283 assert(fullSegmentCount >= performedChecks);
284
285 // The estimated effort is the remaining number of collision checks.
286 edgeEffort = fullSegmentCount - performedChecks;
287 }
288
289 if (std::numeric_limits<unsigned int>::max() - edgeEffort - edge.source->getEstimatedEffortToGo() <
290 edge.target->getInadmissibleEffortToCome())
291 {
292 return std::numeric_limits<unsigned int>::max();
293 }
294
295 // return total effort
296 return edge.source->getEstimatedEffortToGo() + edgeEffort + edge.target->getInadmissibleEffortToCome();
297 }
298
300 {
301 assert(!queue_.empty());
302
303 // Get the top element, i.e., a pair that holds the key and the edge.
304 const auto element = queue_.top();
305
306 // Copy the data of the top edge.
307 auto edge = std::get<4>(element->data);
308
309 // If the source state of the edge does not have an associated vertex, it's a bug.
310 assert(edge.source->hasReverseVertex());
311
312 // Pop the element from the queue.
313 queue_.pop();
314
315 // Get a reference to the outgoing edge queue lookup of the parent vertex.
316 auto &lookup = edge.source->asReverseVertex()->outgoingReverseQueueLookup_;
317
318 // Remove the edge from the lookup.
319 auto it = std::find(lookup.begin(), lookup.end(), element);
320
321 // If the edge is not in the lookup, it's a bug.
322 assert(it != lookup.end());
323
324 // Swappedy pop.
325 std::iter_swap(it, lookup.rbegin());
326 lookup.pop_back();
327
328 // Return the element.
329 return edge;
330 }
331
333 {
334 return std::get<0>(queue_.top()->data);
335 }
336
338 {
339 // We need to ensure the reverse queue lookup is cleared for all edges in the queue.
340 std::vector<HeapElement> contents;
341 queue_.getContent(contents);
342 for (auto element : contents)
343 {
344 std::get<4>(element).source->asReverseVertex()->outgoingReverseQueueLookup_.clear();
345 }
346 queue_.clear();
347 }
348
349 std::vector<Edge> ReverseQueue::getEdges() const
350 {
351 std::vector<HeapElement> contents;
352 queue_.getContent(contents);
353 std::vector<Edge> edges;
354 edges.reserve(contents.size());
355 for (const auto &element : contents)
356 {
357 edges.push_back(std::get<4>(element));
358 }
359 return edges;
360 }
361
363 {
364 const auto edges = getEdges();
365 clear();
366 insertOrUpdate(edges);
367 }
368
369 void ReverseQueue::removeOutgoingEdges(const std::shared_ptr<Vertex> &vertex)
370 {
371 // Remove all elements from the queue.
372 for (const auto element : vertex->outgoingReverseQueueLookup_)
373 {
374 queue_.remove(element);
375 }
376
377 // Remove all elements from the lookup.
378 vertex->outgoingReverseQueueLookup_.clear();
379 }
380
381 } // namespace eitstar
382
383 } // namespace geometric
384
385} // namespace ompl
void update(Element *element)
Update an element in the heap.
Definition BinaryHeap.h:186
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition Cost.h:48
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...
ReverseQueue(const std::shared_ptr< const ompl::base::OptimizationObjective > &objective, const std::shared_ptr< const ompl::base::StateSpace > &space, const bool isQueueCostOrdered)
Constructs the queue with the given optimization objective and state space.
ompl::base::Cost getLowerBoundOnOptimalSolutionCost() const
Returns a lower bound on the resolution-optimal solution cost.
void insertOrUpdate(const Edge &edge)
Inserts or updates an element in the queue.
void clear()
Clears the queue, i.e., deletes all elements from it.
unsigned int computeAdmissibleSolutionEffort(const Edge &edge) const
Returns the admissible total potential effort of an edge.
bool empty() const
Returns whether the queue is empty.
unsigned int peekEffort() const
Get the effort corresponding to the top edge of the queue.
void removeOutgoingEdges(const std::shared_ptr< Vertex > &vertex)
Removes the outgoing edges of a vertex from the queue.
void setCostQueueOrder(const bool isQueueCostOrdered)
Updates the queue ordering depending on the given suboptimality factor.
ompl::base::Cost computeAdmissibleSolutionCost(const Edge &edge) const
Returns the admissible total potential solution cost of an edge.
const Edge & peek() const
Get a reference to the top edge in the queue.
std::vector< Edge > getEdges() const
Copies all edges into a vector and returns the vector.
std::size_t size() const
Returns the number of elements in the queue.
Edge pop()
Returns and deletes the top element of the queue.
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