blob: 439952449b0f7661b22fe563bbd13894c2f954cb [file] [log] [blame]
Briana6553ed2014-04-02 21:26:46 -07001#ifndef AOS_COMMON_CONTROLS_POLYTOPE_H_
2#define AOS_COMMON_CONTROLS_POLYTOPE_H_
Austin Schuha6c257a2013-10-27 15:36:40 -07003
4#include "Eigen/Dense"
Brian Silverman258b9172015-09-19 14:32:57 -04005#include "third_party/cddlib/lib-src/setoper.h"
6#include "third_party/cddlib/lib-src/cdd.h"
Austin Schuha6c257a2013-10-27 15:36:40 -07007
Brian Silvermanaba7bf62016-01-31 18:03:59 -05008#include "aos/common/logging/logging.h"
9#include "aos/common/logging/matrix_logging.h"
10
Austin Schuha6c257a2013-10-27 15:36:40 -070011namespace aos {
12namespace controls {
13
Brian Silvermanaba7bf62016-01-31 18:03:59 -050014// A number_of_dimensions dimensional polytope.
15// This represents the region consisting of all points X such that H * X <= k.
16// The vertices are calculated at construction time because we always use those
17// and libcdd is annoying about calculating vertices. In particular, for some
18// random-seeming polytopes it refuses to calculate the vertices completely. To
19// avoid issues with that, using the "shifting" constructor is recommended
20// whenever possible.
Austin Schuha6c257a2013-10-27 15:36:40 -070021template <int number_of_dimensions>
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070022class Polytope {
Austin Schuha6c257a2013-10-27 15:36:40 -070023 public:
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070024 virtual ~Polytope() {}
Austin Schuha6c257a2013-10-27 15:36:40 -070025
26 // Returns a reference to H.
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070027 virtual Eigen::Ref<
28 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions>>
29 H() const = 0;
Austin Schuha6c257a2013-10-27 15:36:40 -070030
31 // Returns a reference to k.
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070032 virtual Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k()
33 const = 0;
Austin Schuha6c257a2013-10-27 15:36:40 -070034
35 // Returns the number of dimensions in the polytope.
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070036 constexpr int ndim() const { return number_of_dimensions; }
Austin Schuha6c257a2013-10-27 15:36:40 -070037
38 // Returns the number of constraints currently in the polytope.
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070039 int num_constraints() const { return k().rows(); }
Austin Schuha6c257a2013-10-27 15:36:40 -070040
41 // Returns true if the point is inside the polytope.
Brian Silverman6dd2c532014-03-29 23:34:39 -070042 bool IsInside(Eigen::Matrix<double, number_of_dimensions, 1> point) const;
Austin Schuha6c257a2013-10-27 15:36:40 -070043
44 // Returns the list of vertices inside the polytope.
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070045 virtual Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> Vertices()
46 const = 0;
47};
48
49template <int number_of_dimensions, int num_vertices>
50Eigen::Matrix<double, number_of_dimensions, num_vertices> ShiftPoints(
51 const Eigen::Matrix<double, number_of_dimensions, num_vertices> &vertices,
52 const Eigen::Matrix<double, number_of_dimensions, 1> &offset) {
53 Eigen::Matrix<double, number_of_dimensions, num_vertices> ans = vertices;
54 for (int i = 0; i < num_vertices; ++i) {
55 ans.col(i) += offset;
56 }
57 return ans;
58}
59
60template <int number_of_dimensions, int num_constraints, int num_vertices>
61class HVPolytope : public Polytope<number_of_dimensions> {
62 public:
63 // Constructs a polytope given the H and k matrices.
64 HVPolytope(Eigen::Ref<const Eigen::Matrix<double, num_constraints,
65 number_of_dimensions>> H,
66 Eigen::Ref<const Eigen::Matrix<double, num_constraints, 1>> k,
67 Eigen::Ref<const Eigen::Matrix<double, number_of_dimensions,
68 num_vertices>> vertices)
69 : H_(H), k_(k), vertices_(vertices) {}
70
71 Eigen::Ref<const Eigen::Matrix<double, num_constraints, number_of_dimensions>>
72 static_H() const {
73 return H_;
74 }
75
76 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions>>
77 H() const override {
78 return H_;
79 }
80
81 Eigen::Ref<const Eigen::Matrix<double, num_constraints, 1>> static_k()
Brian Silvermanaba7bf62016-01-31 18:03:59 -050082 const {
Austin Schuhc7a0a3d2016-10-15 16:22:47 -070083 return k_;
84 }
85 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k()
86 const override {
87 return k_;
88 }
89
90 Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> Vertices()
91 const override {
92 return vertices_;
93 }
94
95 Eigen::Matrix<double, number_of_dimensions, num_vertices>
96 StaticVertices() const {
Brian Silvermanaba7bf62016-01-31 18:03:59 -050097 return vertices_;
98 }
Austin Schuha6c257a2013-10-27 15:36:40 -070099
100 private:
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700101 const Eigen::Matrix<double, num_constraints, number_of_dimensions> H_;
102 const Eigen::Matrix<double, num_constraints, 1> k_;
103 const Eigen::Matrix<double, number_of_dimensions, num_vertices> vertices_;
104};
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500105
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500106
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700107
108template <int number_of_dimensions>
109class HPolytope : public Polytope<number_of_dimensions> {
110 public:
111 // Constructs a polytope given the H and k matrices.
112 HPolytope(Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic,
113 number_of_dimensions>> H,
114 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k)
115 : H_(H), k_(k), vertices_(CalculateVertices(H, k)) {}
116
117 // This is an initialization function shared across all instantiations of this
118 // template.
119 // This must be called at least once before creating any instances. It is
120 // not thread-safe.
121 static void Init() { dd_set_global_constants(); }
122
123 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions>>
124 H() const override {
125 return H_;
126 }
127 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k()
128 const override {
129 return k_;
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500130 }
131
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700132 Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> Vertices()
133 const override {
134 return vertices_;
135 }
136
137 private:
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500138 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> H_;
139 const Eigen::Matrix<double, Eigen::Dynamic, 1> k_;
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500140 const Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> vertices_;
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700141
142 static Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic>
143 CalculateVertices(
144 Eigen::Ref<
145 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions>> &H,
146 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> &k);
Austin Schuha6c257a2013-10-27 15:36:40 -0700147};
148
149template <int number_of_dimensions>
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700150bool Polytope<number_of_dimensions>::IsInside(
Brian Silverman6dd2c532014-03-29 23:34:39 -0700151 Eigen::Matrix<double, number_of_dimensions, 1> point) const {
Austin Schuh550e7812017-11-19 12:11:02 -0800152 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> k_ptr = k();
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700153 for (int i = 0; i < k_ptr.rows(); ++i) {
Austin Schuh550e7812017-11-19 12:11:02 -0800154 double ev = (H().row(i) * point)(0, 0);
155 if (ev > k_ptr(i, 0)) {
Austin Schuha6c257a2013-10-27 15:36:40 -0700156 return false;
157 }
158 }
159 return true;
160}
161
162template <int number_of_dimensions>
163Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic>
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500164HPolytope<number_of_dimensions>::CalculateVertices(
Austin Schuhc7a0a3d2016-10-15 16:22:47 -0700165 Eigen::Ref<
166 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions>> &H,
167 Eigen::Ref<const Eigen::Matrix<double, Eigen::Dynamic, 1>> &k) {
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500168 dd_MatrixPtr matrix = dd_CreateMatrix(k.rows(), number_of_dimensions + 1);
Austin Schuha6c257a2013-10-27 15:36:40 -0700169
170 // Copy the data over. TODO(aschuh): Is there a better way? I hate copying...
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500171 for (int i = 0; i < k.rows(); ++i) {
172 dd_set_d(matrix->matrix[i][0], k(i, 0));
173 for (int j = 0; j < number_of_dimensions; ++j) {
174 dd_set_d(matrix->matrix[i][j + 1], -H(i, j));
Austin Schuha6c257a2013-10-27 15:36:40 -0700175 }
176 }
177
178 matrix->representation = dd_Inequality;
179 matrix->numbtype = dd_Real;
180
181 dd_ErrorType error;
182 dd_PolyhedraPtr polyhedra = dd_DDMatrix2Poly(matrix, &error);
183 if (error != dd_NoError || polyhedra == NULL) {
184 dd_WriteErrorMessages(stderr, error);
185 dd_FreeMatrix(matrix);
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500186 LOG_MATRIX(ERROR, "bad H", H);
187 LOG_MATRIX(ERROR, "bad k_", k);
188 LOG(FATAL, "dd_DDMatrix2Poly failed\n");
Austin Schuha6c257a2013-10-27 15:36:40 -0700189 }
190
191 dd_MatrixPtr vertex_matrix = dd_CopyGenerators(polyhedra);
192
193 int num_vertices = 0;
194 int num_rays = 0;
195 for (int i = 0; i < vertex_matrix->rowsize; ++i) {
196 if (dd_get_d(vertex_matrix->matrix[i][0]) == 0) {
197 num_rays += 1;
198 } else {
199 num_vertices += 1;
200 }
201 }
202
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500203 // Rays are unsupported right now. This may change in the future.
204 CHECK_EQ(0, num_rays);
205
Austin Schuha6c257a2013-10-27 15:36:40 -0700206 Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> vertices(
207 number_of_dimensions, num_vertices);
208
209 int vertex_index = 0;
210 for (int i = 0; i < vertex_matrix->rowsize; ++i) {
211 if (dd_get_d(vertex_matrix->matrix[i][0]) != 0) {
212 for (int j = 0; j < number_of_dimensions; ++j) {
213 vertices(j, vertex_index) = dd_get_d(vertex_matrix->matrix[i][j + 1]);
214 }
215 ++vertex_index;
216 }
217 }
218 dd_FreeMatrix(vertex_matrix);
219 dd_FreePolyhedra(polyhedra);
220 dd_FreeMatrix(matrix);
221
222 return vertices;
223}
224
225} // namespace controls
226} // namespace aos
227
Briana6553ed2014-04-02 21:26:46 -0700228#endif // AOS_COMMON_CONTROLS_POLYTOPE_H_