blob: 5d49dd09ccc3d8c1c46ed9e762cb1d207c70e463 [file] [log] [blame]
Parker Schuhbe36c5b2018-02-19 01:06:50 -08001#ifndef Y2018_CONTROL_LOOPS_SUPERSTRUCTURE_ARM_GRAPH_H_
2#define Y2018_CONTROL_LOOPS_SUPERSTRUCTURE_ARM_GRAPH_H_
3
Parker Schuhbe36c5b2018-02-19 01:06:50 -08004#include <algorithm>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07005#include <cassert>
6#include <cstdlib>
Parker Schuhbe36c5b2018-02-19 01:06:50 -08007#include <limits>
8#include <memory>
9#include <vector>
10
11namespace y2018 {
12namespace control_loops {
13namespace superstructure {
14namespace arm {
15
16// Grr... normal priority queues don't allow modifying the node cost.
17// This relies on pointer stability for the passed in T.
18template <typename T>
19class IntrusivePriorityQueue {
20 public:
21 // T is expected to be like so:
22 //
23 // struct T {
24 // IntrusivePriorityQueue<T>::QueueEntry* entry_ = nullptr;
25 // };
26 //
27 // This QueueEntry is like a unique_ptr that always maintains a link
28 // back to the unique_ptr pointing to that entry. This maintains the
29 // mapping between the elements and their indexes in the list.
30 // This mapping is crucial for supporting O(log(n)) decrease-key
31 // operations.
32 class QueueEntry {
33 public:
34 QueueEntry(T *value) : value_(value) { value_->entry_ = this; }
35
Austin Schuhcb091712018-02-21 20:01:55 -080036 QueueEntry(QueueEntry &&o) : value_(::std::move(o.value_)) {
Parker Schuhbe36c5b2018-02-19 01:06:50 -080037 if (value_) {
38 value_->entry_ = this;
39 }
40 o.value_ = nullptr;
41 }
42
43 QueueEntry &operator=(QueueEntry &&o) {
44 if (this == &o) return *this;
45 std::swap(value_, o.value_);
46 if (value_) value_->entry_ = this;
47 if (o.value_) o.value_->entry_ = this;
48 return *this;
49 }
50
51 ~QueueEntry() {
52 if (value_) value_->entry_ = nullptr;
53 }
54
55 T *get() { return value_; }
56
57 bool operator<(const QueueEntry &o) { return *value_ < *o.value_; }
58
59 private:
60 friend class IntrusivePriorityQueue;
61 T *value_ = nullptr;
62 };
63
64 // Conceptually, nodes start at inifinity and can only decrease in cost.
65 // The infinity nodes are not in the queue.
66 void InsertOrDecrease(T *t) {
67 if (t->entry_ != nullptr) {
68 std::push_heap(queue_.begin(), queue_.begin() + (GetIndex(t) + 1));
69 } else {
70 queue_.emplace_back(t);
71 std::push_heap(queue_.begin(), queue_.end());
72 }
73 }
74
75 // For testing.
76 void VerifyCorrectness() {
77 for (size_t i = 0; i < queue_.size(); ++i) {
78 assert(queue_[i].value_->entry_ == &queue_[i]);
79 }
80
81 assert(std::is_heap(queue_.begin(), queue_.end()));
82 }
83
84 T *PopMin() {
85 T *result = queue_.front().get();
86 std::pop_heap(queue_.begin(), queue_.end());
87 queue_.pop_back();
88 return result;
89 }
90
91 bool Empty() const { return queue_.empty(); }
92
93 void Clear() { queue_.clear(); }
94
95 void Reserve(size_t n) { queue_.reserve(n); }
96
97 private:
98 size_t GetIndex(T *t) { return t->entry_ - &queue_[0]; }
99
100 std::vector<QueueEntry> queue_;
101};
102
103// You pass in a graph (with num_vertexes) and a set of edges described by
104// edges.
105// Then, this will search that graph to find the optimal path from all points
106// to a goal point.
107//
108// A traversal algorithm would be as such:
109// // Initialize graph...
110// graph.SetGoal(my_goal);
111// size_t current_node = node;
112//
113// while (current_node != my_goal) {
114// edge = argmin(GetCostToGoal(edge.dest) for edge in
115// Neighbors(current_node));
116// Travel along edge # edge.edge_id.
117// current_node = edge.dest;
118// }
119class SearchGraph {
120 public:
121 struct Edge {
122 size_t start;
123 size_t end;
124 double cost;
125 };
126
127 // The part of an edge stored at every vertex.
128 struct HalfEdge {
129 size_t dest;
130 // This id matches the corresponding id in the edges array from the
131 // constructor.
132 size_t edge_id;
133 bool operator<(const HalfEdge &o) const { return dest < o.dest; }
134 };
135
Austin Schuhcb091712018-02-21 20:01:55 -0800136 SearchGraph(size_t num_vertexes, std::vector<Edge> &&edges);
137 SearchGraph(size_t num_vertexes, ::std::initializer_list<Edge> edges);
138 SearchGraph(SearchGraph &&o)
Austin Schuh47d74942018-03-04 01:15:59 -0800139 : vertexes_(::std::move(o.vertexes_)),
Austin Schuhcb091712018-02-21 20:01:55 -0800140 edges_(::std::move(o.edges_)),
Austin Schuh47d74942018-03-04 01:15:59 -0800141 vertex_heap_(::std::move(o.vertex_heap_)) {
142 last_searched_vertex_ = std::numeric_limits<size_t>::max();
143 }
Austin Schuhcb091712018-02-21 20:01:55 -0800144
Parker Schuhbe36c5b2018-02-19 01:06:50 -0800145 ~SearchGraph();
146
147 // Set the goal vertex.
148 void SetGoal(size_t vertex);
149
150 // Compute the cost if you're at vertex.
151 double GetCostToGoal(size_t vertex);
152
153 // For walking the graph yourself.
154 const std::vector<HalfEdge> &Neighbors(size_t vertex) {
155 assert(vertex < vertexes_.size());
156 return vertexes_[vertex].forward;
157 }
158
159 size_t num_vertexes() const { return vertexes_.size(); }
160
Austin Schuhcb091712018-02-21 20:01:55 -0800161 const std::vector<Edge> &edges() const { return edges_; }
162
Parker Schuhbe36c5b2018-02-19 01:06:50 -0800163 private:
Parker Schuhbe36c5b2018-02-19 01:06:50 -0800164 struct Vertex {
165 std::vector<HalfEdge> forward;
166 std::vector<HalfEdge> reverse;
167 double cached_distance = std::numeric_limits<double>::infinity();
168
169 bool operator<(const Vertex &o) {
170 return cached_distance > o.cached_distance;
171 }
172
173 IntrusivePriorityQueue<Vertex>::QueueEntry *entry_ = nullptr;
174 };
175
176 double GetCost(const HalfEdge &edge) { return edges_[edge.edge_id].cost; }
177 std::vector<Vertex> vertexes_;
178 std::vector<Edge> edges_;
179
180 IntrusivePriorityQueue<Vertex> vertex_heap_;
Austin Schuhcb091712018-02-21 20:01:55 -0800181
Austin Schuh47d74942018-03-04 01:15:59 -0800182 size_t last_searched_vertex_ = std::numeric_limits<size_t>::max();
Parker Schuhbe36c5b2018-02-19 01:06:50 -0800183};
184
185} // namespace arm
186} // namespace superstructure
187} // namespace control_loops
188} // namespace y2018
189
190#endif // Y2018_CONTROL_LOOPS_SUPERSTRUCTURE_ARM_GRAPH_H_