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 "casadi_os.hpp"
30 #ifdef HAVE_MKSTEMPS
31 #define CASADI_NEED_UNISTD
32 #else // HAVE_MKSTEMPS
33 #ifdef HAVE_SIMPLE_MKSTEMPS
34 #ifdef _WIN32
35 #include <io.h>
36 #include <share.h>
37 #else
38 #define CASADI_NEED_UNISTD
39 #endif
40 #include <random>
41 #include <chrono>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #endif // HAVE_SIMPLE_MKSTEMPS
46 #endif // HAVE_MKSTEMPS
47 
48 #ifdef CASADI_NEED_UNISTD
49 #include <unistd.h>
50 #endif
51 
52 #undef CASADI_NEED_UNISTD
53 
54 namespace casadi {
55 
56  int to_int(casadi_int rhs) {
57  casadi_assert(rhs<=std::numeric_limits<int>::max(), "Integer overflow detected.");
58  casadi_assert(rhs>=std::numeric_limits<int>::min(), "Integer overflow detected.");
59  return rhs;
60  }
61 
62  std::vector<int> to_int(const std::vector<casadi_int>& rhs) {
63  std::vector<int> ret;
64  ret.reserve(rhs.size());
65  for (casadi_int e : rhs) ret.push_back(to_int(e));
66  return ret;
67  }
68 
69  std::vector< std::vector<int> > to_int(
70  const std::vector< std::vector<casadi_int> >& rhs) {
71  std::vector< std::vector<int> > ret;
72  ret.reserve(rhs.size());
73  for (const std::vector<casadi_int>& e : rhs) ret.push_back(to_int(e));
74  return ret;
75  }
76 
77  bool all(const std::vector<bool>& v) {
78  for (auto && e : v) {
79  if (!e) return false;
80  }
81  return true;
82  }
83 
84  bool any(const std::vector<bool>& v) {
85  for (auto && e : v) {
86  if (e) return true;
87  }
88  return false;
89  }
90 
91  bool is_range(const std::vector<casadi_int>& v,
92  casadi_int start, casadi_int stop, casadi_int step) {
93  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
94  if (v.size()!=nret) return false;
95  casadi_int ind = start;
96  for (casadi_int e : v) {
97  if (e!=ind) return false;
98  ind += step;
99  }
100  return true;
101  }
102 
103 
104  std::vector<casadi_int> range(casadi_int start, casadi_int stop,
105  casadi_int step, casadi_int len) {
106  start = std::min(start, len);
107  stop = std::min(stop, len);
108  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
109  std::vector<casadi_int> ret(nret);
110  casadi_int ind = start;
111  for (std::vector<casadi_int>::iterator it=ret.begin(); it!=ret.end(); ++it) {
112  *it = ind;
113  ind += step;
114  }
115  return ret;
116  }
117 
118  bool is_equally_spaced(const std::vector<double>& v) {
119  // Quick return if 2 or less entries
120  if (v.size()<=2) return true;
121  // Permitted error margin
122  // NOTE(@jaeandersson) 1e-14 good idea?
123  double margin = (v.back()-v.front())*1e-14;
124  // Make sure spacing is consistent throughout
125  double spacing = v[1]-v[0];
126  for (size_t i=2; i<v.size(); ++i) {
127  if (fabs(v[i]-v[i-1]-spacing)>margin) return false;
128  }
129  // Equal if reached this point
130  return true;
131  }
132 
133  std::vector<casadi_int> range(casadi_int stop) {
134  return range(0, stop);
135  }
136 
137  std::vector<casadi_int> complement(const std::vector<casadi_int> &v, casadi_int size) {
138  casadi_assert(in_range(v, size),
139  "complement: out of bounds. Some elements in v fall out of [0, size[");
140  std::vector<casadi_int> lookup(size, 0);
141  std::vector<casadi_int> ret;
142 
143  for (casadi_int i=0;i<v.size();i++) {
144  lookup[v[i]] = 1;
145  }
146 
147  for (casadi_int i=0;i<size;i++) {
148  if (lookup[i]==0) ret.push_back(i);
149  }
150 
151  return ret;
152 
153  }
154 
155  std::vector<casadi_int> lookupvector(const std::vector<casadi_int> &v, casadi_int size) {
156  casadi_assert(in_range(v, size),
157  "lookupvector: out of bounds. Some elements in v fall out of [0, size[");
158  std::vector<casadi_int> lookup(size, -1);
159 
160  for (casadi_int i=0;i<v.size();i++) {
161  lookup[v[i]] = i;
162  }
163  return lookup;
164  }
165 
166  std::vector<casadi_int> lookupvector(const std::vector<casadi_int> &v) {
167  casadi_assert_dev(!has_negative(v));
168  return lookupvector(v, (*std::max_element(v.begin(), v.end()))+1);
169  }
170 
171  bool is_permutation(const std::vector<casadi_int> &order) {
172  std::set<casadi_int> order_set(order.begin(), order.end());
173  return (order_set.size()==order.size()) &&
174  (*order_set.begin()==0) &&
175  (*order_set.rbegin()==order.size()-1);
176  }
177 
178  std::vector<casadi_int> invert_permutation(const std::vector<casadi_int> &a) {
179  casadi_assert(is_permutation(a), "Not a permutation");
180  std::vector<casadi_int> ret(a.size());
181  for (casadi_int i=0;i<a.size();++i) {
182  ret[a[i]] = i;
183  }
184  return ret;
185  }
186 
187  // Better have a bool return flag saying if we need reorer at all
188  std::vector<casadi_int> tensor_permute_mapping(const std::vector<casadi_int>& dims,
189  const std::vector<casadi_int>& order) {
190 
191  // Get problem dimensions
192  casadi_int N = casadi::product(dims);
193  casadi_int n = dims.size();
194  // Quick return if no elements
195  if (N==0) return std::vector<casadi_int>();
196 
197  // One dimension => null-permutation
198  if (n==1) return range(N);
199 
200  // Allocate space for resulting mapping
201  std::vector<casadi_int> mapping(N);
202  // Quick return if scalar
203  if (n==0) return mapping;
204 
205 
206  // Compute cumulative product
207  std::vector<casadi_int> cumprod(n+1, 1);
208  for (casadi_int k=1;k<dims.size();++k) cumprod[k]=cumprod[k-1]*dims[k-1];
209 
210  // Elementary stride
211  casadi_int stride = cumprod[order[0]];
212 
213  // Split problem in inner and outer part
214  casadi_int N_inner = dims[order[0]];
215  casadi_int N_outer = N/N_inner;
216 
217  // Reorder dims, cumprod
218  std::vector<casadi_int> new_dims(n-1), new_cumprod(n-1, 1);
219  for (casadi_int k=0;k<n-1;++k) {
220  new_dims[k] = dims[order[k+1]];
221  new_cumprod[k] = cumprod[order[k+1]];
222  }
223 
224  // Bank of counters
225  std::vector<casadi_int> index_counters(n-1);
226 
227  // Inex into mapping
228  casadi_int m_ind = 0;
229 
230  for (casadi_int i=0;i<N_outer;++i) {
231  // Compute index
232  casadi_int ind = 0;
233  for (casadi_int k=0;k<n-1;++k) ind+=index_counters[k]*new_cumprod[k];
234 
235  // Fill in mapping
236  for (casadi_int j=0;j<N_inner;++j) {
237  mapping.at(m_ind++) = ind;
238  ind+=stride;
239  }
240 
241  // Bump first counter
242  index_counters[0]++;
243 
244  // Overflow counters when needed
245  for (casadi_int k=0;k<n-2;++k) {
246  if (index_counters[k]==new_dims[k]) {
247  index_counters[k] = 0;
248  index_counters[k+1]++;
249  }
250  }
251  }
252  return mapping;
253  }
254 
255  bvec_t* get_bvec_t(std::vector<double>& v) {
256  if (v.empty()) {
257  return nullptr;
258  } else {
259  return reinterpret_cast<bvec_t*>(&v.front());
260  }
261  }
262 
264  const bvec_t* get_bvec_t(const std::vector<double>& v) {
265  if (v.empty()) {
266  return nullptr;
267  } else {
268  return reinterpret_cast<const bvec_t*>(&v.front());
269  }
270  }
271 
272  std::string join(const std::vector<std::string>& l, const std::string& delim) {
273  std::stringstream ss;
274  for (casadi_int i=0;i<l.size();++i) {
275  if (i>0) ss << delim;
276  ss << l[i];
277  }
278  return ss.str();
279  }
280 
281  bool startswith(const std::string& s, const std::string& p) {
282  if (p.size()>s.size()) return false;
283  for (casadi_int i=0;i<p.size();++i) {
284  if (s[i]!=p[i]) return false;
285  }
286  return true;
287  }
288 
289  CASADI_EXPORT std::string replace(const std::string& s,
290  const std::string& p, const std::string& r) {
291  std::string ret = s;
292  std::string::size_type n = 0;
293  while ((n = ret.find(p, n)) != std::string::npos) {
294  ret.replace(n, p.size(), r);
295  n += r.size();
296  }
297  return ret;
298  }
299 
300 #ifdef HAVE_SIMPLE_MKSTEMPS
301 int simple_mkstemps_fd(const std::string& prefix, const std::string& suffix, std::string &result) {
302  // Characters available for inventing filenames
303  std::string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
304  int char_size = static_cast<int>(chars.size());
305 
306  // How many tries do we allow?
307  casadi_int max_tries = std::numeric_limits<int>::max();
308 
309  // How long should the ID be to cover all tries?
310  double max_tries_d = static_cast<double>(max_tries);
311  double char_size_d = static_cast<double>(char_size);
312  int id_size = lround(ceil(log(max_tries_d)/log(char_size_d)));
313 
314  // Random number generator
315  std::default_random_engine rng(std::chrono::system_clock::now().time_since_epoch().count());
316  std::uniform_int_distribution<> r(0, char_size-1);
317 
318  for (casadi_int i=0;i<max_tries;++i) {
319  result = prefix;
320  for (casadi_int j=0;j<id_size;++j) {
321  result += chars.at(r(rng));
322  }
323  result += suffix;
324 
325 #ifdef _WIN32
326  int fd = _sopen(result.c_str(),
327  _O_BINARY | _O_CREAT | _O_EXCL | _O_RDWR, _SH_DENYNO, _S_IREAD | _S_IWRITE);
328  // Could add _O_TEMPORARY, but then no possiblity of !cleanup_
329 #else
330  int fd = open(result.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
331 #endif
332  if (fd != -1) return fd;
333  if (fd == -1 && errno != EEXIST) return -1;
334  }
335  return 0;
336  }
337 std::string simple_mkstemps(const std::string& prefix, const std::string& suffix) {
338  std::string ret;
339  int fd = simple_mkstemps_fd(prefix, suffix, ret);
340  if (fd==-1) {
341  casadi_error("Failed to create temporary file: '" + ret + "'");
342  } else {
343 #ifdef _WIN32
344  _close(fd);
345 #else
346  close(fd);
347 #endif
348  }
349  return ret;
350 }
351 #endif // HAVE_SIMPLE_MKSTEMPS
352 
353  std::string temporary_file(const std::string& prefix, const std::string& suffix) {
354  #ifdef HAVE_MKSTEMPS
355  // Preferred solution
356  std::string ret = prefix + "XXXXXX" + suffix;
357  if (mkstemps(&ret[0], static_cast<int>(suffix.size())) == -1) {
358  casadi_error("Failed to create temporary file: '" + ret + "'");
359  }
360  return ret;
361  #else // HAVE_MKSTEMPS
362  #ifdef HAVE_SIMPLE_MKSTEMPS
363  return simple_mkstemps(prefix, suffix);
364  #else // HAVE_SIMPLE_MKSTEMPS
365  // Fallback, may result in deprecation warnings
366  return prefix + std::string(tmpnam(nullptr)) + suffix;
367  #endif // HAVE_SIMPLE_MKSTEMPS
368  #endif // HAVE_MKSTEMPS
369  }
370 
371 
372  std::vector<bool> boolvec_not(const std::vector<bool> &v) {
373  std::vector<bool> ret(v.size());
374  std::transform(v.begin(), v.end(), ret.begin(),
375  [](bool v) -> bool { return !v; });
376  return ret;
377  }
378 
379  std::vector<bool> boolvec_and(const std::vector<bool> &lhs, const std::vector<bool> &rhs) {
380  casadi_assert(lhs.size()==rhs.size(), "Size mismatch.");
381  std::vector<bool> ret(lhs.size());
382  std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(),
383  [](bool a, bool b) -> bool { return a && b; });
384  return ret;
385  }
386 
387  std::vector<bool> boolvec_or(const std::vector<bool> &lhs, const std::vector<bool> &rhs) {
388  casadi_assert(lhs.size()==rhs.size(), "Size mismatch.");
389  std::vector<bool> ret(lhs.size());
390  std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(),
391  [](bool a, bool b) -> bool { return a || b; });
392  return ret;
393  }
394 
395 
396  std::vector<casadi_int> boolvec_to_index(const std::vector<bool> &v) {
397  std::vector<casadi_int> ret;
398  for (casadi_int i=0;i<v.size();++i) {
399  if (v[i]) ret.push_back(i);
400  }
401  return ret;
402  }
403 
404  void normalized_setup(std::istream& stream) {
405  stream.imbue(std::locale("C"));
406  }
407 
408  void normalized_setup(std::ostream& stream) {
409  stream.imbue(std::locale("C"));
410  stream << std::scientific;
411  stream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
412  }
413 
414 
415  std::string str_bvec(bvec_t v) {
416  std::stringstream ss;
417  for (casadi_int i=0;i<sizeof(bvec_t)*8;++i) {
418  bool bit = v & (bvec_t(1) << i);
419  ss << (bit ? "1" : "0");
420  }
421  return ss.str();
422  }
423 
424  bvec_t bvec_or(const bvec_t* arg, casadi_int n) {
425  bvec_t acc = 0;
426  // vacuous truth
427  if (n==0) {
428  return~acc;
429  }
430  for (casadi_int i=0;i<n;++i) {
431  acc |= arg[i];
432  }
433  return acc;
434  }
435 
436 } // namespace casadi
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)
T product(const std::vector< T > &values)
product
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 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:91
std::string temporary_file(const std::string &prefix, const std::string &suffix)
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:56
bool has_negative(const std::vector< T > &v)
Check if the vector has negative entries.
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.
std::vector< casadi_int > tensor_permute_mapping(const std::vector< casadi_int > &dims, const std::vector< casadi_int > &order)
Computes a mapping for a (dense) tensor permutation.
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.
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:84
void normalized_setup(std::istream &stream)
bool all(const std::vector< bool > &v)
Check if all arguments are true.
Definition: casadi_misc.cpp:77
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.
bvec_t bvec_or(const bvec_t *arg, casadi_int n)
Bit-wise or operation on bvec_t array.
std::vector< casadi_int > boolvec_to_index(const std::vector< bool > &v)