ctrl-utils
eigen_string.h
1 
10 #ifndef CTRL_UTILS_EIGEN_STRING_H_
11 #define CTRL_UTILS_EIGEN_STRING_H_
12 
13 #include <exception> // std::invalid_argument
14 #include <string> // std::string, std::to_string
15 #include <sstream> // std::stringstream
16 
17 #include "eigen.h"
18 
19 namespace ctrl_utils {
20 
28 template<typename Derived>
29 Derived DecodeMatlab(const std::string& str);
30 
38 template<typename Derived>
39 std::string EncodeMatlab(const Eigen::DenseBase<Derived>& matrix);
40 
48 template<typename Derived>
49 Derived DecodeJson(const std::string& str);
50 
58 template<typename Derived>
59 std::string EncodeJson(const Eigen::DenseBase<Derived>& matrix);
60 
61 } // namespace ctrl_utils
62 
63 namespace Eigen {
64 
65 template<typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
66 std::stringstream& operator>>(std::stringstream& ss,
67  Eigen::Matrix<Scalar, Rows, Cols, Options,
68  MaxRows, MaxCols>& matrix) {
69  using PlainMatrix = Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>;
70  matrix = ctrl_utils::DecodeMatlab<PlainMatrix>(ss.str());
71  return ss;
72 }
73 
74 template<typename Derived>
75 std::stringstream& operator<<(std::stringstream& ss, const Eigen::DenseBase<Derived>& matrix) {
76  ss << ctrl_utils::EncodeMatlab(matrix);
77  return ss;
78 }
79 
80 template<typename Derived>
81 std::stringstream& operator>>(std::stringstream& ss, Eigen::QuaternionBase<Derived>& quat) {
82  ss >> quat.coeffs();
83  return ss;
84 }
85 
86 template<typename Derived>
87 std::stringstream& operator<<(std::stringstream& ss, const Eigen::QuaternionBase<Derived>& quat) {
88  ss << ctrl_utils::EncodeMatlab(quat.coeffs());
89  return ss;
90 }
91 
92 } // namespace Eigen
93 
94 namespace ctrl_utils {
95 
96 template<typename Derived>
97 Derived DecodeMatlab(const std::string& str) {
98  std::string str_local = str;
99 
100  // Create matrix to return
101  Derived matrix;
102  size_t num_cols = matrix.cols();
103  size_t num_rows = matrix.rows();
104  typename Derived::Scalar eof; // Used to check for eof and print Matrix scalar type
105 
106  // Count number of columns
107  size_t idx_col_end = 0;
108  if (num_cols == 0 || (num_cols == 1 && num_rows == 0)) {
109  num_cols = 0;
110  size_t idx = 0;
111  idx_col_end = str.find_first_of(';');
112  while (idx < idx_col_end) {
113  // Skip over extra whitespace
114  idx = str.find_first_not_of(' ', idx);
115  if (idx >= idx_col_end) break;
116 
117  // Find next delimiter
118  if (str[idx] == ';') break;
119  idx = str.find_first_of(' ', idx + 1);
120  ++num_cols;
121  }
122  }
123 
124  // Count number of rows
125  if (num_rows == 0) {
126  size_t idx = idx_col_end;
127  if (idx_col_end != 0) { // First row already traversed
128  idx = idx_col_end;
129  num_rows = 1;
130  }
131  while (idx != std::string::npos) {
132  // Clear semicolons as we go
133  if (idx != 0) str_local[idx] = ' ';
134 
135  // Find next delimiter
136  idx = str.find_first_of(';', idx + 1);
137  ++num_rows;
138  }
139  // Ignore trailing semicolon
140  idx = str.find_last_not_of(' ');
141  if (str[idx] == ';') --num_rows;
142  } else {
143  // Clear remaining semicolons
144  for (size_t idx = idx_col_end; idx < str.size(); idx++) {
145  if (str[idx] == ';') str_local[idx] = ' ';
146  }
147  }
148 
149  // Check number of rows and columns
150  if (num_cols == 0)
151  throw std::invalid_argument(
152  "DecodeMatlab(): Failed to decode Eigen::MatrixX" +
153  std::string(typeid(eof).name()) + "(" + std::to_string(num_rows) +
154  ", " + std::to_string(num_cols) + ") from: (" + str + ").");
155  if (num_rows == 1) {
156  // Convert to vector
157  num_rows = num_cols;
158  num_cols = 1;
159  }
160  if (matrix.rows() == 0 || matrix.cols() == 0) {
161  matrix.resize(num_rows, num_cols);
162  }
163 
164  // Parse matrix
165  std::stringstream ss(str_local);
166  for (size_t i = 0; i < num_rows; ++i) {
167  for (size_t j = 0; j < num_cols; ++j) {
168  ss >> matrix(i,j);
169  if (ss.fail()) {
170  throw std::invalid_argument(
171  "DecodeMatlab(): Failed to decode Eigen::MatrixX" +
172  std::string(typeid(eof).name()) + "(" + std::to_string(num_rows) +
173  ", " + std::to_string(num_cols) + ") from: (" + str + ").");
174  }
175  }
176  }
177 
178  // Make sure there are no numbers left
179  ss >> eof;
180  if (!ss.fail()) {
181  throw std::invalid_argument(
182  "DecodeMatlab(): Failed to decode Eigen::MatrixX" +
183  std::string(typeid(eof).name()) + "(" + std::to_string(num_rows) +
184  ", " + std::to_string(num_cols) + ") from: (" + str + ").");
185  }
186 
187  return matrix;
188 }
189 
190 template<typename Derived>
191 std::string EncodeMatlab(const Eigen::DenseBase<Derived>& matrix) {
192  std::stringstream ss;
193  ss.precision(std::numeric_limits<typename Derived::Scalar>::digits10);
194  if (matrix.cols() == 1) { // Column vector
195  // [[1],[2],[3],[4]] => "1 2 3 4"
196  for (int i = 0; i < matrix.rows(); ++i) {
197  if (i > 0) ss << " ";
198  ss << matrix(i);
199  }
200  } else { // matrix
201  // [1,2,3,4] => "1 2 3 4"
202  // [[1,2],[3,4]] => "1 2; 3 4"
203  for (int i = 0; i < matrix.rows(); ++i) {
204  if (i > 0) ss << "; ";
205  for (int j = 0; j < matrix.cols(); ++j) {
206  if (j > 0) ss << " ";
207  ss << matrix(i,j);
208  }
209  }
210  }
211  return ss.str();
212 }
213 
214 template<typename Derived>
215 std::string EncodeJson(const Eigen::DenseBase<Derived>& matrix) {
216  std::string s = "[";
217  if (matrix.cols() == 1) { // Column vector
218  // [[1],[2],[3],[4]] => "[1,2,3,4]"
219  for (int i = 0; i < matrix.rows(); ++i) {
220  if (i > 0) s.append(",");
221  s.append(std::to_string(matrix(i,0)));
222  }
223  } else { // Matrix
224  // [[1,2,3,4]] => "[1,2,3,4]"
225  // [[1,2],[3,4]] => "[[1,2],[3,4]]"
226  for (int i = 0; i < matrix.rows(); ++i) {
227  if (i > 0) s.append(",");
228  // Nest arrays only if there are multiple rows
229  if (matrix.rows() > 1) s.append("[");
230  for (int j = 0; j < matrix.cols(); ++j) {
231  if (j > 0) s.append(",");
232  s.append(std::to_string(matrix(i,j)));
233  }
234  // Nest arrays only if there are multiple rows
235  if (matrix.rows() > 1) s.append("]");
236  }
237  }
238  s.append("]");
239  return s;
240 }
241 
242 template<typename Derived>
243 Derived DecodeJson(const std::string& str) {
244  // Find last nested row delimiter
245  size_t idx_row_end = str.find_last_of(']');
246  if (idx_row_end != std::string::npos) {
247  size_t idx_temp = str.substr(0, idx_row_end).find_last_of(']');
248  if (idx_temp != std::string::npos) idx_row_end = idx_temp;
249  }
250 
251  // Count number of columns
252  size_t num_cols = 0;
253  size_t idx = 0;
254  size_t idx_col_end = str.find_first_of(']');
255  while (idx < idx_col_end) {
256  // Skip over extra whitespace
257  idx = str.find_first_not_of(' ', idx);
258  if (idx >= idx_col_end) break;
259 
260  // Find next delimiter
261  idx = str.find_first_of(',', idx + 1);
262  ++num_cols;
263  }
264  if (idx > idx_col_end) idx = idx_col_end;
265 
266  // Count number of rows
267  size_t num_rows = 1; // First row already traversed
268  while (idx < idx_row_end) {
269  // Skip over irrelevant characters
270  idx = str.find_first_not_of(']', idx);
271  if (idx >= idx_row_end) break;
272 
273  // Find next delimiter
274  idx = str.find_first_of(']', idx + 1);
275  ++num_rows;
276  }
277 
278  // Check number of rows and columns
279  if (num_cols == 0)
280  throw std::runtime_error("RedisClient: Failed to decode Eigen Matrix from: " + str + ".");
281  if (num_rows == 1) {
282  // Convert to vector
283  num_rows = num_cols;
284  num_cols = 1;
285  }
286 
287  // Parse matrix
288  Eigen::MatrixXd matrix(num_rows, num_cols);
289  std::string str_local(str);
290  for (char delimiter : ",[]") {
291  std::replace(str_local.begin(), str_local.end(), delimiter, ' ');
292  }
293  std::stringstream ss(str_local);
294  for (size_t i = 0; i < num_rows; ++i) {
295  for (size_t j = 0; j < num_cols; ++j) {
296  std::string val;
297  ss >> val;
298  try {
299  matrix(i,j) = std::stod(val);
300  } catch (const std::exception& e) {
301  throw std::runtime_error("RedisClient: Failed to decode Eigen Matrix from: " + str + ".");
302  }
303  }
304  }
305 
306  return matrix;
307 }
308 
309 } // namespace ctrl_utils
310 
311 #endif // CTRL_UTILS_EIGEN_STRING_H_
Definition: ctrl_utils.cc:18
std::string EncodeJson(const Eigen::DenseBase< Derived > &matrix)
Definition: eigen_string.h:215
Derived DecodeJson(const std::string &str)
Definition: eigen_string.h:243
std::ostream & operator<<(std::ostream &os, const Args &args)
Definition: argparse.h:544
std::string EncodeMatlab(const Eigen::DenseBase< Derived > &matrix)
Definition: eigen_string.h:191
Derived DecodeMatlab(const std::string &str)
Definition: eigen_string.h:97