casadi_misc.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "mx.hpp"
27 
28 #include "casadi_misc.hpp"
29 #include "global_options.hpp"
30 #include "filesystem_impl.hpp"
31 
32 #include "casadi_os.hpp"
33 #include <sstream>
34 #ifdef HAVE_MKSTEMPS
35 #define CASADI_NEED_UNISTD
36 #else // HAVE_MKSTEMPS
37 #ifdef HAVE_SIMPLE_MKSTEMPS
38 #ifdef _WIN32
39 #include <io.h>
40 #include <share.h>
41 #else
42 #define CASADI_NEED_UNISTD
43 #endif
44 #include <random>
45 #include <chrono>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <errno.h>
49 #endif // HAVE_SIMPLE_MKSTEMPS
50 #endif // HAVE_MKSTEMPS
51 
52 #ifdef CASADI_NEED_UNISTD
53 #include <unistd.h>
54 #endif
55 
56 #undef CASADI_NEED_UNISTD
57 
58 namespace casadi {
59 
60  int to_int(casadi_int rhs) {
61  casadi_assert(rhs<=std::numeric_limits<int>::max(), "Integer overflow detected.");
62  casadi_assert(rhs>=std::numeric_limits<int>::min(), "Integer overflow detected.");
63  return rhs;
64  }
65 
66  std::vector<int> to_int(const std::vector<casadi_int>& rhs) {
67  std::vector<int> ret;
68  ret.reserve(rhs.size());
69  for (casadi_int e : rhs) ret.push_back(to_int(e));
70  return ret;
71  }
72 
73  std::vector< std::vector<int> > to_int(
74  const std::vector< std::vector<casadi_int> >& rhs) {
75  std::vector< std::vector<int> > ret;
76  ret.reserve(rhs.size());
77  for (const std::vector<casadi_int>& e : rhs) ret.push_back(to_int(e));
78  return ret;
79  }
80 
81  bool all(const std::vector<bool>& v) {
82  for (auto && e : v) {
83  if (!e) return false;
84  }
85  return true;
86  }
87 
88  bool any(const std::vector<bool>& v) {
89  for (auto && e : v) {
90  if (e) return true;
91  }
92  return false;
93  }
94 
95  bool is_range(const std::vector<casadi_int>& v,
96  casadi_int start, casadi_int stop, casadi_int step) {
97  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
98  if (v.size()!=nret) return false;
99  casadi_int ind = start;
100  for (casadi_int e : v) {
101  if (e!=ind) return false;
102  ind += step;
103  }
104  return true;
105  }
106 
107  void check_offset(const std::vector<casadi_int>& offset, casadi_int n,
108  const std::string& fname) {
109  casadi_assert(!offset.empty() && offset.front()==0 && offset.back()==n
110  && is_monotone(offset), fname + ": offset " + str(offset) + " must start at 0, "
111  "be nondecreasing and end at " + str(n));
112  }
113 
114 
115  std::vector<casadi_int> range(casadi_int start, casadi_int stop,
116  casadi_int step, casadi_int len) {
117  start = std::min(start, len);
118  stop = std::min(stop, len);
119  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
120  std::vector<casadi_int> ret(nret);
121  casadi_int ind = start;
122  for (std::vector<casadi_int>::iterator it=ret.begin(); it!=ret.end(); ++it) {
123  *it = ind;
124  ind += step;
125  }
126  return ret;
127  }
128 
129  bool is_equally_spaced(const std::vector<double>& v) {
130  // Quick return if 2 or less entries
131  if (v.size()<=2) return true;
132  // Permitted error margin
133  // NOTE(@jaeandersson) 1e-14 good idea?
134  double margin = (v.back()-v.front())*1e-14;
135  // Make sure spacing is consistent throughout
136  double spacing = v[1]-v[0];
137  for (size_t i=2; i<v.size(); ++i) {
138  if (fabs(v[i]-v[i-1]-spacing)>margin) return false;
139  }
140  // Equal if reached this point
141  return true;
142  }
143 
144  std::vector<casadi_int> range(casadi_int stop) {
145  return range(0, stop);
146  }
147 
148  std::vector<casadi_int> complement(const std::vector<casadi_int> &v, casadi_int size) {
149  casadi_assert(in_range(v, size),
150  "complement: out of bounds. Some elements in v fall out of [0, size[");
151  std::vector<casadi_int> lookup(size, 0);
152  std::vector<casadi_int> ret;
153 
154  for (casadi_int i=0;i<v.size();i++) {
155  lookup[v[i]] = 1;
156  }
157 
158  for (casadi_int i=0;i<size;i++) {
159  if (lookup[i]==0) ret.push_back(i);
160  }
161 
162  return ret;
163 
164  }
165 
166  std::vector<casadi_int> lookupvector(const std::vector<casadi_int> &v, casadi_int size) {
167  casadi_assert(in_range(v, size),
168  "lookupvector: out of bounds. Some elements in v fall out of [0, size[");
169  std::vector<casadi_int> lookup(size, -1);
170 
171  for (casadi_int i=0;i<v.size();i++) {
172  lookup[v[i]] = i;
173  }
174  return lookup;
175  }
176 
177  std::vector<casadi_int> lookupvector(const std::vector<casadi_int> &v) {
178  casadi_assert_dev(!has_negative(v));
179  return lookupvector(v, (*std::max_element(v.begin(), v.end()))+1);
180  }
181 
182  bool is_permutation(const std::vector<casadi_int> &order) {
183  std::set<casadi_int> order_set(order.begin(), order.end());
184  return (order_set.size()==order.size()) &&
185  (*order_set.begin()==0) &&
186  (*order_set.rbegin()==order.size()-1);
187  }
188 
189  std::vector<casadi_int> invert_permutation(const std::vector<casadi_int> &a) {
190  casadi_assert(is_permutation(a), "Not a permutation");
191  std::vector<casadi_int> ret(a.size());
192  for (casadi_int i=0;i<a.size();++i) {
193  ret[a[i]] = i;
194  }
195  return ret;
196  }
197 
198  bvec_t* get_bvec_t(std::vector<double>& v) {
199  if (v.empty()) {
200  return nullptr;
201  } else {
202  return reinterpret_cast<bvec_t*>(&v.front());
203  }
204  }
205 
207  const bvec_t* get_bvec_t(const std::vector<double>& v) {
208  if (v.empty()) {
209  return nullptr;
210  } else {
211  return reinterpret_cast<const bvec_t*>(&v.front());
212  }
213  }
214 
215  std::string join(const std::vector<std::string>& l, const std::string& delim) {
216  std::stringstream ss;
217  for (casadi_int i=0;i<l.size();++i) {
218  if (i>0) ss << delim;
219  ss << l[i];
220  }
221  return ss.str();
222  }
223 
224  bool startswith(const std::string& s, const std::string& p) {
225  if (p.size()>s.size()) return false;
226  for (casadi_int i=0;i<p.size();++i) {
227  if (s[i]!=p[i]) return false;
228  }
229  return true;
230  }
231 
232  CASADI_EXPORT std::string replace(const std::string& s,
233  const std::string& p, const std::string& r) {
234  std::string ret = s;
235  std::string::size_type n = 0;
236  while ((n = ret.find(p, n)) != std::string::npos) {
237  ret.replace(n, p.size(), r);
238  n += r.size();
239  }
240  return ret;
241  }
242 
243  bool version_gt(const std::string& version_left, const std::string& version_right) {
244  std::vector<casadi_int> left_parts, right_parts;
245 
246  // Parse left version
247  std::stringstream left_stream(version_left);
248  std::string part;
249  while (std::getline(left_stream, part, '.')) {
250  left_parts.push_back(std::stoi(part));
251  }
252 
253  // Parse right version
254  std::stringstream right_stream(version_right);
255  while (std::getline(right_stream, part, '.')) {
256  right_parts.push_back(std::stoi(part));
257  }
258 
259  // Compare component by component
260  casadi_int min_size = std::min(left_parts.size(), right_parts.size());
261  for (casadi_int i = 0; i < min_size; ++i) {
262  if (left_parts[i] > right_parts[i]) return true;
263  if (left_parts[i] < right_parts[i]) return false;
264  }
265 
266  // If all components are equal, the longer version is greater
267  return left_parts.size() > right_parts.size();
268  }
269 
270  bool version_ge(const std::string& version_left, const std::string& version_right) {
271  std::vector<casadi_int> left_parts, right_parts;
272 
273  // Parse left version
274  std::stringstream left_stream(version_left);
275  std::string part;
276  while (std::getline(left_stream, part, '.')) {
277  left_parts.push_back(std::stoi(part));
278  }
279 
280  // Parse right version
281  std::stringstream right_stream(version_right);
282  while (std::getline(right_stream, part, '.')) {
283  right_parts.push_back(std::stoi(part));
284  }
285 
286  // Compare component by component
287  casadi_int min_size = std::min(left_parts.size(), right_parts.size());
288  for (casadi_int i = 0; i < min_size; ++i) {
289  if (left_parts[i] > right_parts[i]) return true;
290  if (left_parts[i] < right_parts[i]) return false;
291  }
292 
293  // If all components are equal, the longer version is greater or equal
294  return left_parts.size() >= right_parts.size();
295  }
296 
297  bool version_lt(const std::string& version_left, const std::string& version_right) {
298  return version_gt(version_right, version_left);
299  }
300 
301  bool version_le(const std::string& version_left, const std::string& version_right) {
302  return version_ge(version_right, version_left);
303  }
304 
305 #ifdef HAVE_SIMPLE_MKSTEMPS
306 int simple_mkstemps_fd(const std::string& prefix, const std::string& suffix, std::string &result) {
307  // Characters available for inventing filenames
308  std::string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
309  int char_size = static_cast<int>(chars.size());
310 
311  // How many tries do we allow?
312  casadi_int max_tries = std::numeric_limits<int>::max();
313 
314  // How long should the ID be to cover all tries?
315  double max_tries_d = static_cast<double>(max_tries);
316  double char_size_d = static_cast<double>(char_size);
317  int id_size = lround(ceil(log(max_tries_d)/log(char_size_d)));
318 
319  // Random number generator
320  std::default_random_engine rng(std::chrono::system_clock::now().time_since_epoch().count());
321  std::uniform_int_distribution<> r(0, char_size-1);
322 
323  for (casadi_int i=0;i<max_tries;++i) {
324  result = prefix;
325  for (casadi_int j=0;j<id_size;++j) {
326  result += chars.at(r(rng));
327  }
328  result += suffix;
329 
330 #ifdef _WIN32
331  int fd = _sopen(result.c_str(),
332  _O_BINARY | _O_CREAT | _O_EXCL | _O_RDWR, _SH_DENYNO, _S_IREAD | _S_IWRITE);
333  // Could add _O_TEMPORARY, but then no possiblity of !cleanup_
334 #else
335  int fd = open(result.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
336 #endif
337  if (fd != -1) return fd;
338  if (fd == -1 && errno != EEXIST) return -1;
339  }
340  return 0;
341  }
342 std::string simple_mkstemps(const std::string& prefix, const std::string& suffix) {
343  std::string ret;
344  int fd = simple_mkstemps_fd(prefix, suffix, ret);
345  if (fd==-1) {
346  casadi_error("Failed to create temporary file: '" + ret + ". '"
347  + (Filesystem::is_enabled() ? "" : "Does the directory exits? "
348  "Note that CasADi needs to be compiled with WITH_GHC_FILESYSTEM=ON "
349  "for directories to be automatically created."));
350  } else {
351 #ifdef _WIN32
352  _close(fd);
353 #else
354  close(fd);
355 #endif
356  }
357  return ret;
358 }
359 #endif // HAVE_SIMPLE_MKSTEMPS
360 
361  std::string temporary_file(const std::string& prefix,
362  const std::string& suffix,
363  const std::string& directory) {
364 
365  std::string temp_dir = Filesystem::ensure_trailing_slash(directory);
366  if (temp_dir.empty()) temp_dir = GlobalOptions::getTempWorkDir();
367 
368  if (Filesystem::is_enabled()) {
369  casadi_assert(Filesystem::ensure_directory_exists(temp_dir),
370  "Unable to create the required directory for '" + temp_dir + "'.");
371  }
372 
373  #ifdef HAVE_MKSTEMPS
374  // Preferred solution
375  std::string ret = temp_dir + prefix + "XXXXXX" + suffix;
376  if (mkstemps(&ret[0], static_cast<int>(suffix.size())) == -1) {
377  casadi_error("Failed to create temporary file: '" + ret + "'. "
378  + (Filesystem::is_enabled() ? "" : "Does the directory exits? "
379  "Note that CasADi needs to be compiled with WITH_GHC_FILESYSTEM=ON "
380  "for directories to be automatically created."));
381  }
382  return ret;
383  #else // HAVE_MKSTEMPS
384  #ifdef HAVE_SIMPLE_MKSTEMPS
385  return simple_mkstemps(temp_dir + prefix, suffix);
386  #else // HAVE_SIMPLE_MKSTEMPS
387  casadi_assert(temp_dir=="./", "tmpnam fallback not compatible with custom temporary directory");
388  // Fallback, may result in deprecation warnings
389  return std::string(tmpnam(nullptr)) + suffix;
390  #endif // HAVE_SIMPLE_MKSTEMPS
391  #endif // HAVE_MKSTEMPS
392  }
393 
394 
395  std::vector<bool> boolvec_not(const std::vector<bool> &v) {
396  std::vector<bool> ret(v.size());
397  std::transform(v.begin(), v.end(), ret.begin(),
398  [](bool v) -> bool { return !v; });
399  return ret;
400  }
401 
402  std::vector<bool> boolvec_and(const std::vector<bool> &lhs, const std::vector<bool> &rhs) {
403  casadi_assert(lhs.size()==rhs.size(), "Size mismatch.");
404  std::vector<bool> ret(lhs.size());
405  std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(),
406  [](bool a, bool b) -> bool { return a && b; });
407  return ret;
408  }
409 
410  std::vector<bool> boolvec_or(const std::vector<bool> &lhs, const std::vector<bool> &rhs) {
411  casadi_assert(lhs.size()==rhs.size(), "Size mismatch.");
412  std::vector<bool> ret(lhs.size());
413  std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(),
414  [](bool a, bool b) -> bool { return a || b; });
415  return ret;
416  }
417 
418 
419  std::vector<casadi_int> boolvec_to_index(const std::vector<bool> &v) {
420  std::vector<casadi_int> ret;
421  for (casadi_int i=0;i<v.size();++i) {
422  if (v[i]) ret.push_back(i);
423  }
424  return ret;
425  }
426 
427  void normalized_setup(std::istream& stream) {
428  stream.imbue(std::locale("C"));
429  }
430 
431  void normalized_setup(std::ostream& stream) {
432  stream.imbue(std::locale("C"));
433  stream << std::scientific;
434  stream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
435  }
436 
438  : stream_(os),
439  flags_(os.flags()),
440  precision_(os.precision()),
441  width_(os.width()),
442  locale_(os.getloc()) {}
443 
445  stream_.flags(flags_);
446  stream_.precision(precision_);
447  stream_.width(width_);
448  stream_.imbue(locale_);
449  }
450 
451  std::string str_bvec(bvec_t v) {
452  std::stringstream ss;
453  for (casadi_int i=0;i<sizeof(bvec_t)*8;++i) {
454  bool bit = v & (bvec_t(1) << i);
455  ss << (bit ? "1" : "0");
456  }
457  return ss.str();
458  }
459 
460  bvec_t bvec_or(const bvec_t* arg, casadi_int n) {
461  bvec_t acc = 0;
462  // vacuous truth
463  if (n==0) {
464  return~acc;
465  }
466  for (casadi_int i=0;i<n;++i) {
467  acc |= arg[i];
468  }
469  return acc;
470  }
471 
472 } // namespace casadi
static std::string ensure_trailing_slash(const std::string &path)
Definition: filesystem.cpp:155
static bool ensure_directory_exists(const std::string &path)
Definition: filesystem.cpp:105
static bool is_enabled()
Definition: filesystem.cpp:83
static std::string getTempWorkDir()
StreamStateGuard(std::ostream &os)
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
bvec_t * get_bvec_t(std::vector< double > &v)
bool is_equally_spaced(const std::vector< double > &v)
std::string join(const std::vector< std::string > &l, const std::string &delim)
std::vector< casadi_int > invert_permutation(const std::vector< casadi_int > &a)
inverse a permutation vector
unsigned long long bvec_t
bool version_le(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left <= version_right.
bool is_range(const std::vector< casadi_int > &v, casadi_int start, casadi_int stop, casadi_int step)
Check if a vector matches a range.
Definition: casadi_misc.cpp:95
bool startswith(const std::string &s, const std::string &p)
Checks if s starts with p.
int to_int(casadi_int rhs)
Definition: casadi_misc.cpp:60
bool has_negative(const std::vector< T > &v)
Check if the vector has negative entries.
bool version_gt(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left > version_right.
CASADI_EXPORT std::string replace(const std::string &s, const std::string &p, const std::string &r)
Replace all occurences of p with r in s.
void check_offset(const std::vector< casadi_int > &offset, casadi_int n, const std::string &fname)
Assert that offset starts at 0, is nondecreasing and ends at n.
bool is_monotone(const std::vector< T > &v)
Check if the vector is monotone.
std::string str_bvec(bvec_t v)
std::vector< bool > boolvec_or(const std::vector< bool > &lhs, const std::vector< bool > &rhs)
Or operation on boolean vector.
bool version_lt(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left < version_right.
std::string str(const T &v)
String representation, any type.
std::vector< casadi_int > lookupvector(const std::vector< casadi_int > &v, casadi_int size)
Returns a vector for quickly looking up entries of supplied list.
std::vector< bool > boolvec_not(const std::vector< bool > &v)
Invert all entries.
std::vector< bool > boolvec_and(const std::vector< bool > &lhs, const std::vector< bool > &rhs)
And operation on boolean vector.
bool any(const std::vector< bool > &v)
Check if any arguments are true.
Definition: casadi_misc.cpp:88
void normalized_setup(std::istream &stream)
bool all(const std::vector< bool > &v)
Check if all arguments are true.
Definition: casadi_misc.cpp:81
bool in_range(const std::vector< T > &v, casadi_int upper)
Check if for each element of v holds: v_i < upper.
bool is_permutation(const std::vector< casadi_int > &order)
Does the list represent a permutation?
std::vector< casadi_int > complement(const std::vector< casadi_int > &v, casadi_int size)
Returns the list of all i in [0, size[ not found in supplied list.
bool version_ge(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left >= version_right.
bvec_t bvec_or(const bvec_t *arg, casadi_int n)
Bit-wise or operation on bvec_t array.
std::string temporary_file(const std::string &prefix, const std::string &suffix, const std::string &directory)
std::vector< casadi_int > boolvec_to_index(const std::vector< bool > &v)