Parasol Planning Library (PPL)
SSSP.h
Go to the documentation of this file.
1 #ifndef PMPL_SSSP_H_
2 #define PMPL_SSSP_H_
3 
4 #include <functional>
5 #include <iomanip>
6 #include <iostream>
7 #include <limits>
8 #include <list>
9 #include <memory>
10 #include <queue>
11 #include <set>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <vector>
15 
16 
21 template <typename GraphType>
23  std::unordered_map<typename GraphType::vertex_descriptor,
24  std::vector<typename GraphType::vertex_descriptor>>;
25 
26 
30 template <typename GraphType>
31 struct SSSPOutput {
32 
33  typedef typename GraphType::vertex_descriptor VD;
34  typedef std::unordered_map<VD, double> DistanceMap;
35  typedef std::vector<VD> Ordering;
37  typedef std::unordered_map<VD, VD> ParentMap;
38 
43 
44 };
45 
46 
50 enum class SSSPTermination {
51  Continue,
52  EndBranch,
53  EndSearch
54 };
55 
56 
58 std::ostream& operator<<(std::ostream& _os, const SSSPTermination& _t);
59 
60 
65 template <typename GraphType>
67  std::function<SSSPTermination(typename GraphType::vertex_iterator&,
68  const SSSPOutput<GraphType>& _sssp)>;
69 
70 
73 template <typename GraphType>
76  return [](typename GraphType::vertex_iterator&, const SSSPOutput<GraphType>&)
77  {
79  };
80 }
81 
82 
91 template <typename GraphType>
93  std::function<double(typename GraphType::adj_edge_iterator&, const double,
94  const double)>;
95 
96 
100 template <typename GraphType>
103  return [](typename GraphType::adj_edge_iterator& _ei,
104  const double _sourceDistance,
105  const double _targetDistance)
106  {
107  const double edgeWeight = _ei->property().GetWeight(),
108  newDistance = _sourceDistance + edgeWeight;
109  return newDistance;
110  };
111 }
112 
118 template <typename GraphType>
120  std::function<double(
121  const GraphType* g,
122  typename GraphType::vertex_descriptor source,
123  typename GraphType::vertex_descriptor target)>;
124 
127 template <typename GraphType>
130  return [](const GraphType* _g,
131  typename GraphType::vertex_descriptor _source,
132  typename GraphType::vertex_descriptor _target)
133  {
134  return 0.0;
135  };
136 }
137 
138 
144 template <typename GraphType>
146  std::function<void(
147  GraphType* g,
148  typename GraphType::vertex_descriptor vd)>;
149 
152 template <typename GraphType>
155  return [](GraphType* _g,
156  typename GraphType::vertex_descriptor _vd){};
157 }
158 
159 
160 
170 template<typename GraphType>
173  GraphType* const _g,
174  const std::vector<typename GraphType::vertex_descriptor>& _starts,
175  std::vector<typename GraphType::vertex_descriptor>& _goals,
180  const double _startDistance = 0)
181 {
182  //static constexpr bool debug = true;
183  //if(debug)
184  // std::cout << "AStarSSSP" << std::endl;
185 
186  using VD = typename GraphType::vertex_descriptor;
187  using EI = typename GraphType::adj_edge_iterator;
188 
192  struct element {
193  VD parent;
194  VD vd;
195  double g;
196  double h;
197 
203  element(const VD _parent, const VD _target, const double _g,
204  const double _h) : parent(_parent), vd(_target), g(_g), h(_h) {}
205 
207  bool operator>(const element& _e) const noexcept {
208  return g + h > _e.g + _e.h;
209  }
210  };
211 
212  std::priority_queue<element,
213  std::vector<element>,
214  std::greater<element>> frontier;
215 
216  std::unordered_set<VD> seen;
217  std::unordered_map<VD, double> cost;
218 
219  SSSPOutput <GraphType> output;
220 
221  auto relax = [&_g, &cost, &frontier, &_weight, &_heuristic](EI& _ei) {
222  const VD source = _ei->source(),
223  target = _ei->target();
224 
225  const double sourceCost = cost[source],
226  targetCost = cost.count(target)
227  ? cost[target]
228  : std::numeric_limits<double>::infinity(),
229  newG = _weight(_ei, sourceCost, targetCost),
230  newH = _heuristic(_g, source, target);
231 
232  if(newG + newH >= targetCost)
233  return;
234 
235  cost[target] = newG + newH;
236  frontier.emplace(source, target, newG, newH);
237  };
238 
239  // Initialize each starting node
240  for(const auto start : _starts) {
241  cost[start] = _startDistance;
242  // TODO should heuristic be 0 at start? may need to change later
243  frontier.emplace(start, start, 0, 0);
244  }
245 
246  // A*
247  while(!frontier.empty()) {
248  // Get the next element
249  element current = frontier.top();
250  frontier.pop();
251 
252  // If seen this node, it is stale. Discard.
253  if(seen.count(current.vd))
254  continue;
255  seen.insert(current.vd);
256 
257  output.ordering.push_back(current.vd);
258  output.distance[current.vd] = cost[current.vd];
259  output.successors[current.parent].push_back(current.vd);
260  output.parent[current.vd] = current.parent;
261 
262  // Get vertex iterator for current vertex
263  _neighbors(_g, current.vd);
264  auto vi = _g->find_vertex(current.vd);
265  // Check for early termination
266  auto stop = _earlyStop(vi, output);
267 
268  /*if(debug)
269  std::cout << "\tVertex: " << current.vd
270  << ", parent: " << current.parent
271  << ", score: " << std::setprecision(4) << cost[current.vd]
272  << ", stop: " << stop
273  << std::endl;*/
274  if(stop == SSSPTermination::Continue)
275  // Continue search as usual
276  ;
277  else if(stop == SSSPTermination::EndBranch)
278  // End this branch (do not relax neighbors)
279  continue;
280  else if(stop == SSSPTermination::EndSearch)
281  // End the entire search
282  break;
283 
284  // Relax each outgoing edge
285  for(auto ei = vi->begin(); ei != vi->end(); ++ei) {
286  relax(ei);
287  }
288  }
289 
290  // The _starts were added to their own successor map - fix that now.
291  for(const auto start: _starts) {
292  auto& startMap = output.successors[start];
293  startMap.erase(std::find(startMap.begin(), startMap.end(), start));
294  }
295 
296  return output;
297 }
298 
308 template<typename GraphType>
311  GraphType* const _g,
312  const std::vector<typename GraphType::vertex_descriptor>& _starts,
313  std::vector<typename GraphType::vertex_descriptor>& _goals,
316  auto _neighbors = SSSPDefaultNeighbors<GraphType>();
317  auto _heuristic = SSSPDefaultHeuristic<GraphType>();
318 
319  return AStarSSSP(_g, _starts, _goals, _weight, _heuristic, _neighbors, _earlyStop);
320 }
321 
328 template <typename GraphType>
331  GraphType* const _g,
332  const std::vector<typename GraphType::vertex_descriptor>& _starts,
335  const SSSPAdjacencyMap<GraphType>& _adjacencyMap = {},
336  const double _startDistance = 0)
337 {
338  static constexpr bool debug = false;
339  const bool customAdjacency = !_adjacencyMap.empty();
340  if(debug)
341  std::cout << "DijkstraSSSP" << std::endl;
342 
343  using VD = typename GraphType::vertex_descriptor;
344  using EI = typename GraphType::adj_edge_iterator;
345 
349  struct element {
350 
351  VD parent;
352  VD vd;
353  double distance;
354 
359  element(const VD _parent, const VD _target, const double _distance)
360  : parent(_parent), vd(_target), distance(_distance) {}
361 
363  bool operator>(const element& _e) const noexcept {
364  return distance > _e.distance;
365  }
366 
367  };
368 
369  // Define a min priority queue for dijkstras. We will not update elements when
370  // better distances are found - instead we will track the most up-to-date
371  // distance and ignore elements with different values. This is effectively a
372  // lazy delete of stale elements.
373  std::priority_queue<element,
374  std::vector<element>,
375  std::greater<element>> pq;
376 
377  // Initialize visited and temporary distance maps. The later holds an *exact*
378  // copy of the most up-to-date distance for each node. The absence of an entry
379  // will be taken as the initial state.
380  std::unordered_set<VD> visited;
381  std::unordered_map<VD, double> distance;
382 
383  // Initialize the output object.
384  SSSPOutput<GraphType> output;
385 
386  // Define a relax edge function.
387  auto relax = [&distance, &pq, &_weight](EI& _ei) {
388  const VD source = _ei->source(),
389  target = _ei->target();
390 
391  const double sourceDistance = distance[source],
392  targetDistance = distance.count(target)
393  ? distance[target]
394  : std::numeric_limits<double>::infinity(),
395  newDistance = _weight(_ei, sourceDistance, targetDistance);
396 
397  // If the new distance isn't better, quit.
398  if(newDistance >= targetDistance)
399  return;
400 
401  // Otherwise, update target distance and add the target to the queue.
402  distance[target] = newDistance;
403  pq.emplace(source, target, newDistance);
404  };
405 
406  // Initialize each start node.
407  for(const auto start : _starts) {
408  distance[start] = _startDistance;
409  pq.emplace(start, start, 0);
410  }
411 
412  // Dijkstras.
413  while(!pq.empty()) {
414  // Get the next element.
415  element current = pq.top();
416  pq.pop();
417 
418  // If we are done with this node, the element is stale. Discard.
419  if(visited.count(current.vd))
420  continue;
421  visited.insert(current.vd);
422 
423  // Save this score and successor relationship.
424  output.ordering.push_back(current.vd);
425  output.distance[current.vd] = distance[current.vd];
426  output.successors[current.parent].push_back(current.vd);
427  output.parent[current.vd] = current.parent;
428 
429  // Check for early termination.
430  auto vi = _g->find_vertex(current.vd);
431  auto stop = _earlyStop(vi, output);
432 
433  if(debug)
434  std::cout << "\tVertex: " << current.vd
435  << ", parent: " << current.parent
436  << ", score: " << std::setprecision(4) << distance[current.vd]
437  << ", stop: " << stop
438  << std::endl;
439 
440  if(stop == SSSPTermination::Continue)
441  // Continue the search as usual.
442  ;
443  else if(stop == SSSPTermination::EndBranch)
444  // End this branch (do not relax neighbors).
445  continue;
446  else if(stop == SSSPTermination::EndSearch)
447  // End the entire search.
448  break;
449 
450  // Relax each outgoing edge.
451  for(auto ei = vi->begin(); ei != vi->end(); ++ei) {
452  // If we are not using custom adjacency, simply relax the edge.
453  if(!customAdjacency)
454  relax(ei);
455  // Otherwise, only relax if this edge appears in _adjacencyMap.
456  else if(_adjacencyMap.count(current.vd)) {
457  const auto& neighbors = _adjacencyMap.at(current.vd);
458  auto iter = std::find(neighbors.begin(), neighbors.end(), ei->target());
459  if(iter != neighbors.end())
460  relax(ei);
461  }
462  }
463  }
464 
465  // The _starts were added to their own successor map - fix that now.
466  for(const auto start : _starts) {
467  auto& startMap = output.successors[start];
468  startMap.erase(std::find(startMap.begin(), startMap.end(), start));
469  }
470 
471  return output;
472 }
473 
474 
481 template <typename GraphType>
484  GraphType* const _g,
485  const std::vector<typename GraphType::vertex_descriptor>& _starts,
487  const SSSPAdjacencyMap<GraphType>& _adjacencyMap = {})
488 {
489  auto stop = SSSPDefaultTermination<GraphType>();
490 
491  return DijkstraSSSP(_g, _starts, _weight, stop, _adjacencyMap);
492 }
493 
494 
501 template <typename GraphType>
504  GraphType* const _g,
505  const std::vector<typename GraphType::vertex_descriptor>& _starts,
507  const SSSPAdjacencyMap<GraphType>& _adjacencyMap = {})
508 {
509  auto weight = SSSPDefaultPathWeight<GraphType>();
510 
511  return DijkstraSSSP(_g, _starts, weight, _stop, _adjacencyMap);
512 }
513 
514 
520 template <typename GraphType>
523  GraphType* const _g,
524  const std::vector<typename GraphType::vertex_descriptor>& _starts,
525  const SSSPAdjacencyMap<GraphType>& _adjacencyMap = {})
526 {
527  auto weight = SSSPDefaultPathWeight<GraphType>();
528  auto stop = SSSPDefaultTermination<GraphType>();
529 
530  return DijkstraSSSP(_g, _starts, weight, stop, _adjacencyMap);
531 }
532 
533 
534 
539 
541  size_t m_vid;
542  double m_distance;
543  std::shared_ptr<TwoVariableSSSPNode> m_parent;
544  double m_waitTimeSteps{0};
545 
546  TwoVariableSSSPNode(size_t _vid, double _distance, std::shared_ptr<TwoVariableSSSPNode> _parent)
547  : m_vid(_vid), m_distance(_distance), m_parent(_parent) {}
548 
549 };
550 
551 template <typename GraphType>
552 std::shared_ptr<TwoVariableSSSPNode>
554  GraphType* const _g,
555  const std::vector<typename GraphType::vertex_descriptor>& _starts,
556  std::unordered_set<size_t> _goals,
557  const double _startTime,
558  const double _minEndTime,
559  const double _lastConstraint,
560  const double _lastGoalConstraint,
562 {
563  using NodePtr = std::shared_ptr<TwoVariableSSSPNode>;
564 
565  // Set up a priority queue for the elements.
566  auto compare = [](const NodePtr& _a, const NodePtr& _b) {
567  return _a->m_distance > _b->m_distance;
568  };
569  std::priority_queue<NodePtr,
570  std::deque<NodePtr>,
571  decltype(compare)> pq(compare);
572 
573  // Set up structures to track visitation and distance.
574  std::unordered_set<size_t> visitedPostConstraints;
575  std::unordered_map<double, std::unordered_set<size_t>> discoveredVertices;
576 
577  for(const size_t vid : _starts)
578  {
579  pq.emplace(new TwoVariableSSSPNode(vid, _startTime, nullptr));
580  discoveredVertices[_startTime].insert(vid);
581  }
582 
583  NodePtr current;
584 
585  //while(!_goals.count(current->m_vid) or current->m_distance < _minEndTime)
586  while(!pq.empty())
587  {
588  // Get the next node.
589  current = pq.top();
590  pq.pop();
591 
592  // Extract the current VID and distance.
593  const size_t vid = current->m_vid;
594  const double distance = current->m_distance;
595 
596  // Check if we're past the last constraint on the goal location.
597  const bool pastLastGoalConstraint = distance > _lastGoalConstraint;
598 
599  // If this is a goal and we're past the last constraint on the goal location,
600  // we're done (we need to continue searching otherwise to ensure we don't violate
601  // a constraint while sitting on the goal).
602  //if(pastLastConstraint and _goals.count(vid))
603  if(pastLastGoalConstraint and _goals.count(vid))
604  {
605  // Wait until the minimum end time if needed.
606  current->m_waitTimeSteps = std::max(0., _minEndTime - distance);
607  return current;
608  }
609 
610  // Check if we're past the last constraint.
611  const bool pastLastConstraint = distance > _lastConstraint;
612 
613  // If we're past the last constraint, we start tracking visited status since
614  // we can no longer benefit from revisitation.
615  if(pastLastConstraint)
616  {
617  // Skip this node if we've already visited after the last constraint.
618  if(visitedPostConstraints.count(vid))
619  continue;
620  // Mark this node as visited.
621  visitedPostConstraints.insert(vid);
622  }
623 
625  // Add children of the current node to the queue.
626  auto vit = _g->find_vertex(vid);
627  for(auto eit = vit->begin(); eit != vit->end(); eit++)
628  {
629  const size_t target = eit->target();
630  // TODO When we're past the last constraint, we might know the best
631  // distance to this target which could avoid extraneous conflict
632  // checking.
633  // Use a dummy infinite distance to force revisiting this node regardless
634  // of the prior best path.
635  const double bestTargetDistance = std::numeric_limits<double>::infinity();
636 
637  // Compute the distance to the target through this path and edge.
638  const double newDistance = _weight(eit, distance, bestTargetDistance);
639 
640  // If the new distance is better and we haven't tried it already.
641  if(newDistance < bestTargetDistance and
642  !discoveredVertices[newDistance].count(target))
643  {
644  discoveredVertices[newDistance].insert(target);
645  pq.emplace(new TwoVariableSSSPNode(target, newDistance, current));
646  }
647  }
648  }
649 
650  return nullptr;
651 }
652 
653 
654 #endif
std::function< SSSPTermination(typename GraphType::vertex_iterator &, const SSSPOutput< GraphType > &_sssp)> SSSPTerminationCriterion
Definition: SSSP.h:68
std::unordered_map< typename GraphType::vertex_descriptor, std::vector< typename GraphType::vertex_descriptor > > SSSPAdjacencyMap
Definition: SSSP.h:24
std::ostream & operator<<(std::ostream &_os, const SSSPTermination &_t)
Debug output for termination criteria.
Definition: SSSP.cpp:9
SSSPTermination
The possible early-termination conditions for an SSSP run.
Definition: SSSP.h:50
@ EndSearch
End the entire search process.
@ EndBranch
End the branch at this vertex and do not relax its neighbors.
@ Continue
Proceed as usual.
SSSPOutput< GraphType > DijkstraSSSP(GraphType *const _g, const std::vector< typename GraphType::vertex_descriptor > &_starts, SSSPPathWeightFunction< GraphType > &_weight, SSSPTerminationCriterion< GraphType > &_earlyStop, const SSSPAdjacencyMap< GraphType > &_adjacencyMap={}, const double _startDistance=0)
Definition: SSSP.h:330
SSSPHeuristicFunction< GraphType > SSSPDefaultHeuristic()
Definition: SSSP.h:129
SSSPTerminationCriterion< GraphType > SSSPDefaultTermination()
Definition: SSSP.h:75
std::shared_ptr< TwoVariableSSSPNode > TwoVariableDijkstraSSSP(GraphType *const _g, const std::vector< typename GraphType::vertex_descriptor > &_starts, std::unordered_set< size_t > _goals, const double _startTime, const double _minEndTime, const double _lastConstraint, const double _lastGoalConstraint, SSSPPathWeightFunction< GraphType > &_weight)
Definition: SSSP.h:553
SSSPOutput< GraphType > AStarSSSP(GraphType *const _g, const std::vector< typename GraphType::vertex_descriptor > &_starts, std::vector< typename GraphType::vertex_descriptor > &_goals, SSSPPathWeightFunction< GraphType > &_weight, SSSPHeuristicFunction< GraphType > &_heuristic, SSSPNeighborsFunction< GraphType > &_neighbors, SSSPTerminationCriterion< GraphType > &_earlyStop, const double _startDistance=0)
Definition: SSSP.h:172
SSSPNeighborsFunction< GraphType > SSSPDefaultNeighbors()
Definition: SSSP.h:154
SSSPPathWeightFunction< GraphType > SSSPDefaultPathWeight()
Definition: SSSP.h:102
std::function< void(GraphType *g, typename GraphType::vertex_descriptor vd)> SSSPNeighborsFunction
Definition: SSSP.h:148
std::function< double(const GraphType *g, typename GraphType::vertex_descriptor source, typename GraphType::vertex_descriptor target)> SSSPHeuristicFunction
Definition: SSSP.h:123
std::function< double(typename GraphType::adj_edge_iterator &, const double, const double)> SSSPPathWeightFunction
Definition: SSSP.h:94
The output of an SSSP run.
Definition: SSSP.h:31
DistanceMap distance
Distance to each cell from start.
Definition: SSSP.h:39
std::unordered_map< VD, VD > ParentMap
Definition: SSSP.h:37
GraphType::vertex_descriptor VD
Definition: SSSP.h:33
std::unordered_map< VD, double > DistanceMap
Definition: SSSP.h:34
Adjacency successors
Maps predecessor -> successors.
Definition: SSSP.h:41
SSSPAdjacencyMap< GraphType > Adjacency
Definition: SSSP.h:36
Ordering ordering
Cell discovery ordering.
Definition: SSSP.h:40
ParentMap parent
Maps successor -> parent.
Definition: SSSP.h:42
std::vector< VD > Ordering
Definition: SSSP.h:35
Definition: SSSP.h:540
std::shared_ptr< TwoVariableSSSPNode > m_parent
Definition: SSSP.h:543
double m_distance
Definition: SSSP.h:542
double m_waitTimeSteps
Definition: SSSP.h:544
TwoVariableSSSPNode(size_t _vid, double _distance, std::shared_ptr< TwoVariableSSSPNode > _parent)
Definition: SSSP.h:546
size_t m_vid
Definition: SSSP.h:541