blob: ccd257b8bff818e85dc5646a425f74cb922ae4b7 [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>
22class HPolytope {
23 public:
Brian Silvermanaba7bf62016-01-31 18:03:59 -050024 // Constructs a polytope given the H and k matrices.
25 // Do NOT use this to calculate a polytope derived from another one in a way
26 // another constructor can be used instead.
27 HPolytope(
28 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> &H,
29 const Eigen::Matrix<double, Eigen::Dynamic, 1> &k)
30 : H_(H), k_(k), vertices_(CalculateVertices(H, k)) {}
31
32 // Constructs a polytope with H = other.H() * H_multiplier and
33 // k = other.k() + other.H() * k_shifter.
34 // This avoids calculating the vertices for the new H and k directly because
35 // that sometimes confuses libcdd depending on what exactly the new H and k
36 // are.
37 //
38 // This currently has the following restrictions (CHECKed internally) because
39 // we don't have uses for anything else:
40 // If number_of_dimensions is not 1, it must be 2, other.H() *
41 // H_multiplier.inverse() * k_shifter must have its first 2 columns and last
42 // 2 columns as opposites, and the 1st+2nd and 3rd+4th vertices must be
43 // normal to each other.
44 HPolytope(const HPolytope<number_of_dimensions> &other,
45 const Eigen::Matrix<double, number_of_dimensions,
46 number_of_dimensions> &H_multiplier,
47 const Eigen::Matrix<double, number_of_dimensions, 1> &k_shifter)
48 : H_(other.H() * H_multiplier),
49 k_(other.k() + other.H() * k_shifter),
50 vertices_(
51 ShiftVertices(CalculateVertices(H_, other.k()),
52 other.H() * H_multiplier.inverse() * k_shifter)) {}
Austin Schuha6c257a2013-10-27 15:36:40 -070053
Brian Silverman6da04272014-05-18 18:47:48 -070054 // This is an initialization function shared across all instantiations of this
55 // template.
Brian Silvermanaba7bf62016-01-31 18:03:59 -050056 // This must be called at least once before creating any instances. It is
Brian Silverman6da04272014-05-18 18:47:48 -070057 // not thread-safe.
Austin Schuha6c257a2013-10-27 15:36:40 -070058 static void Init() {
59 dd_set_global_constants();
60 }
61
62 // Returns a reference to H.
Brian Silvermanaba7bf62016-01-31 18:03:59 -050063 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> &H() const {
Austin Schuha6c257a2013-10-27 15:36:40 -070064 return H_;
65 }
66
67 // Returns a reference to k.
Brian Silvermanaba7bf62016-01-31 18:03:59 -050068 const Eigen::Matrix<double, Eigen::Dynamic, 1> &k() const { return k_; }
Austin Schuha6c257a2013-10-27 15:36:40 -070069
70 // Returns the number of dimensions in the polytope.
71 int ndim() const { return number_of_dimensions; }
72
73 // Returns the number of constraints currently in the polytope.
74 int num_constraints() const { return k_.rows(); }
75
76 // Returns true if the point is inside the polytope.
Brian Silverman6dd2c532014-03-29 23:34:39 -070077 bool IsInside(Eigen::Matrix<double, number_of_dimensions, 1> point) const;
Austin Schuha6c257a2013-10-27 15:36:40 -070078
79 // Returns the list of vertices inside the polytope.
Brian Silvermanaba7bf62016-01-31 18:03:59 -050080 const Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> &Vertices()
81 const {
82 return vertices_;
83 }
Austin Schuha6c257a2013-10-27 15:36:40 -070084
85 private:
Brian Silvermanaba7bf62016-01-31 18:03:59 -050086 static Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic>
87 CalculateVertices(
88 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> &H,
89 const Eigen::Matrix<double, Eigen::Dynamic, 1> &k);
90
91 static Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic>
92 ShiftVertices(const Eigen::Matrix<double, number_of_dimensions,
93 Eigen::Dynamic> &vertices,
94 const Eigen::Matrix<double, Eigen::Dynamic, 1> &shift) {
95 static_assert(number_of_dimensions <= 2, "not implemented yet");
96 if (vertices.cols() != number_of_dimensions * 2) {
97 LOG(FATAL,
98 "less vertices not supported yet: %zd vertices vs %d dimensions\n",
99 vertices.cols(), number_of_dimensions);
100 }
101 if (number_of_dimensions == 2) {
102 if ((shift.row(0) + shift.row(1)).norm() > 0.0001) {
103 LOG_MATRIX(FATAL, "bad shift amount", shift.row(0) + shift.row(1));
104 }
105 if ((shift.row(2) + shift.row(3)).norm() > 0.0001) {
106 LOG_MATRIX(FATAL, "bad shift amount", shift.row(2) + shift.row(3));
107 }
108 if (vertices.col(0).dot(vertices.col(1)) > 0.0001) {
109 ::Eigen::Matrix<double, number_of_dimensions, 2> bad;
110 bad.col(0) = vertices.col(0);
111 bad.col(1) = vertices.col(1);
112 LOG_MATRIX(FATAL, "bad vertices", bad);
113 }
114 if (vertices.col(2).dot(vertices.col(3)) > 0.0001) {
115 ::Eigen::Matrix<double, number_of_dimensions, 2> bad;
116 bad.col(0) = vertices.col(2);
117 bad.col(1) = vertices.col(3);
118 LOG_MATRIX(FATAL, "bad vertices", bad);
119 }
120 }
121
122 Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> r(
123 number_of_dimensions, vertices.cols());
124 Eigen::Matrix<double, number_of_dimensions, 1> real_shift;
125 real_shift(0, 0) = shift(0, 0);
126 if (number_of_dimensions == 2) real_shift(1, 0) = shift(2, 0);
127 for (int i = 0; i < vertices.cols(); ++i) {
128 r.col(i) = vertices.col(i) + real_shift;
129 }
130 return r;
131 }
132
133 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> H_;
134 const Eigen::Matrix<double, Eigen::Dynamic, 1> k_;
135
136 const Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> vertices_;
Austin Schuha6c257a2013-10-27 15:36:40 -0700137};
138
139template <int number_of_dimensions>
140bool HPolytope<number_of_dimensions>::IsInside(
Brian Silverman6dd2c532014-03-29 23:34:39 -0700141 Eigen::Matrix<double, number_of_dimensions, 1> point) const {
Austin Schuha6c257a2013-10-27 15:36:40 -0700142 auto ev = H_ * point;
143 for (int i = 0; i < num_constraints(); ++i) {
144 if (ev(i, 0) > k_(i, 0)) {
145 return false;
146 }
147 }
148 return true;
149}
150
151template <int number_of_dimensions>
152Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic>
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500153HPolytope<number_of_dimensions>::CalculateVertices(
154 const Eigen::Matrix<double, Eigen::Dynamic, number_of_dimensions> &H,
155 const Eigen::Matrix<double, Eigen::Dynamic, 1> &k) {
156 dd_MatrixPtr matrix = dd_CreateMatrix(k.rows(), number_of_dimensions + 1);
Austin Schuha6c257a2013-10-27 15:36:40 -0700157
158 // Copy the data over. TODO(aschuh): Is there a better way? I hate copying...
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500159 for (int i = 0; i < k.rows(); ++i) {
160 dd_set_d(matrix->matrix[i][0], k(i, 0));
161 for (int j = 0; j < number_of_dimensions; ++j) {
162 dd_set_d(matrix->matrix[i][j + 1], -H(i, j));
Austin Schuha6c257a2013-10-27 15:36:40 -0700163 }
164 }
165
166 matrix->representation = dd_Inequality;
167 matrix->numbtype = dd_Real;
168
169 dd_ErrorType error;
170 dd_PolyhedraPtr polyhedra = dd_DDMatrix2Poly(matrix, &error);
171 if (error != dd_NoError || polyhedra == NULL) {
172 dd_WriteErrorMessages(stderr, error);
173 dd_FreeMatrix(matrix);
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500174 LOG_MATRIX(ERROR, "bad H", H);
175 LOG_MATRIX(ERROR, "bad k_", k);
176 LOG(FATAL, "dd_DDMatrix2Poly failed\n");
Austin Schuha6c257a2013-10-27 15:36:40 -0700177 }
178
179 dd_MatrixPtr vertex_matrix = dd_CopyGenerators(polyhedra);
180
181 int num_vertices = 0;
182 int num_rays = 0;
183 for (int i = 0; i < vertex_matrix->rowsize; ++i) {
184 if (dd_get_d(vertex_matrix->matrix[i][0]) == 0) {
185 num_rays += 1;
186 } else {
187 num_vertices += 1;
188 }
189 }
190
Brian Silvermanaba7bf62016-01-31 18:03:59 -0500191 // Rays are unsupported right now. This may change in the future.
192 CHECK_EQ(0, num_rays);
193
Austin Schuha6c257a2013-10-27 15:36:40 -0700194 Eigen::Matrix<double, number_of_dimensions, Eigen::Dynamic> vertices(
195 number_of_dimensions, num_vertices);
196
197 int vertex_index = 0;
198 for (int i = 0; i < vertex_matrix->rowsize; ++i) {
199 if (dd_get_d(vertex_matrix->matrix[i][0]) != 0) {
200 for (int j = 0; j < number_of_dimensions; ++j) {
201 vertices(j, vertex_index) = dd_get_d(vertex_matrix->matrix[i][j + 1]);
202 }
203 ++vertex_index;
204 }
205 }
206 dd_FreeMatrix(vertex_matrix);
207 dd_FreePolyhedra(polyhedra);
208 dd_FreeMatrix(matrix);
209
210 return vertices;
211}
212
213} // namespace controls
214} // namespace aos
215
Briana6553ed2014-04-02 21:26:46 -0700216#endif // AOS_COMMON_CONTROLS_POLYTOPE_H_