options.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 "options.hpp"
27 #include <algorithm>
28 #include <locale>
29 
30 namespace casadi {
31 
32  const Options::Entry* Options::find(const std::string& name) const {
33  // Check if in one of the bases
34  for (auto&& b : bases) {
35  const Options::Entry* entry = b->find(name);
36  if (entry) return entry;
37  }
38 
39  // Lookup in this class
40  auto it = entries.find(name);
41  if (it!=entries.end()) {
42  return &it->second;
43  } else {
44  return nullptr;
45  }
46  }
47 
48  void Options::Entry::disp(const std::string& name, std::ostream &stream) const {
49  stream << "> \"" << name << "\" ["
51  << "] ";
52 
53  // Print out the description on a new line.
54  stream << " \"" << this->description << "\""<< std::endl;
55  }
56 
57  void Options::disp(std::ostream& stream) const {
58  // Print bases
59  for (auto&& b : bases) {
60  b->disp(stream);
61  }
62 
63  // Print all entries
64  for (auto&& e : entries) {
65  e.second.disp(e.first, stream);
66  }
67  }
68 
69  double Options::word_distance(const std::string &a, const std::string &b) {
71  if (a == b) return 0;
72  casadi_int na = a.size();
73  casadi_int nb = b.size();
74  if (na == 0) return static_cast<double>(nb);
75  if (nb == 0) return static_cast<double>(na);
76 
77  std::vector<casadi_int> v0(nb+1, 0);
78  std::vector<casadi_int> v1(nb+1, 0);
79 
80  for (casadi_int i=0;i<nb+1;++i)
81  v0[i] = i;
82 
83  char s;
84  char t;
85  std::locale loc;
86  for (casadi_int i=0;i<na;i++) {
87  v1[0] = i + 1;
88  for (casadi_int j=0; j<nb; j++) {
89  s = std::tolower(a[i], loc);
90  t = std::tolower(b[j], loc);
91  casadi_int cost = 0;
92  if (s != t)
93  cost = 1;
94 
95  v1[j+1] = std::min(std::min(v1[j] + 1, v0[j+1] + 1), v0[j] + cost);
96  }
97 
98  for (casadi_int j=0; j<nb+1; j++)
99  v0[j] = v1[j];
100  }
101 
102  return static_cast<double>(v1[nb]);
103  }
104 
105  std::vector<std::string> Options::suggestions(const std::string& word, casadi_int amount) const {
106  // Best distances so far
107  const double inf = std::numeric_limits<double>::infinity();
108  std::vector<std::pair<double, std::string>> best(amount, {inf, ""});
109 
110  // Iterate over elements
111  best_matches(word, best);
112 
113  // Sort the elements in ascending order
114  stable_sort(best.begin(), best.end());
115 
116  // Collect the values that are non-infinite
117  std::vector<std::string> ret;
118  ret.reserve(amount);
119  for (auto&& e : best) {
120  if (e.first!=inf) {
121  ret.push_back(e.second);
122  }
123  }
124  return ret;
125  }
126 
127  void Options::best_matches(const std::string& word,
128  std::vector<std::pair<double, std::string> >& best) const {
129  // Iterate over bases
130  for (auto&& b : bases) {
131  b->best_matches(word, best);
132  }
133 
134  // Worst match so far
135  auto worst = max_element(best.begin(), best.end());
136 
137  // Loop over entries
138  for (auto&& e : entries) {
139  // Get word distance
140  double d = word_distance(e.first, word);
141 
142  // Keep if better than the worst amongst the suggestions
143  if (d < worst->first) {
144  worst->first = d;
145  worst->second = e.first;
146  worst = max_element(best.begin(), best.end());
147  }
148  }
149  }
150 
151  bool Options::has_dot(const Dict& opts) {
152  for (auto&& op : opts) {
153  if (op.first.find('.') != std::string::npos || op.first.find("__") != std::string::npos) {
154  return true;
155  }
156  // Call recursively
157  if (op.second.is_dict() && has_dot(op.second)) return true;
158  }
159  return false;
160  }
161 
162  bool Options::has_null(const Dict& opts) {
163  for (auto&& op : opts) {
164  if (op.second.is_null()) return true;
165  }
166  return false;
167  }
168 
169  bool Options::is_sane(const Dict& opts) {
170  return !has_dot(opts) && !has_null(opts);
171  }
172 
173  Dict Options::sanitize(const Dict& opts, bool top_level) {
174  // Drop nulls
175  if (top_level && has_null(opts)) {
176  // Create a new dictionary without the null entries
177  Dict ret;
178  for (auto&& op : opts) {
179  if (!op.second.is_null()) ret[op.first] = op.second;
180  }
181  return sanitize(ret, false);
182  }
183 
184  // Treat the case where any of the options have a dot (dictionary shorthand)
185  if (has_dot(opts)) {
186  // New options dictionary being constructed
187  Dict ret;
188 
189  // Sub-dictionary and corresponding name being constructed
190  Dict sopts;
191  std::string sname;
192 
193  // Process options
194  for (auto&& op : opts) {
195  // Find the dot if any
196  std::string::size_type dotpos = op.first.find('.'), dotpos_end;
197  if (dotpos==std::string::npos) {
198  dotpos = op.first.find("__");
199  if (dotpos!=std::string::npos) dotpos_end = dotpos+2;
200  } else {
201  dotpos_end = dotpos+1;
202  }
203 
204  // Flush last sub-dictionary
205  if (!sname.empty() && (dotpos==std::string::npos
206  || op.first.compare(0, dotpos, sname)!=0)) {
207  update_dict(ret, sname, sanitize(sopts, false), true);
208 
209  sname.clear();
210  sopts.clear();
211  }
212 
213  GenericType value = op.second;
214  if (value.is_dict()) {
215  value = sanitize(value, false);
216  }
217 
218  // Add to dictionary
219  if (dotpos != std::string::npos) {
220  sname = op.first.substr(0, dotpos);
221  std::string target_name = op.first.substr(dotpos_end);
222  sopts[target_name] = value;
223  } else {
224  update_dict(ret, op.first, value, true);
225  }
226  }
227 
228  // Flush trailing sub-dictionary
229  if (!sname.empty()) {
230  update_dict(ret, sname, sanitize(sopts, false), true);
231  }
232 
233  return ret;
234  }
235 
236  // Nothing to do
237  return opts;
238  }
239 
240  void Options::check(const Dict& opts) const {
241  // Make sure all options exist and have the correct type
242  for (auto&& op : opts) {
243  const Options::Entry* entry = find(op.first);
244 
245  // Informative error message if option does not exist
246  if (entry==nullptr) {
247  std::stringstream ss;
248  ss << "Unknown option: " << op.first << std::endl;
249  ss << std::endl;
250  ss << "Did you mean one of the following?" << std::endl;
251  for (auto&& s : suggestions(op.first)) {
252  print_one(s, ss);
253  }
254  ss << "Use print_options() to get a full list of options." << std::endl;
255  casadi_error(ss.str());
256  }
257 
258  // Check type
259  casadi_assert(op.second.can_cast_to(entry->type),
260  "Illegal type for " + op.first + ": " +
261  op.second.get_description() +
262  " cannot be cast to " +
264 
265  }
266  }
267 
268  void Options::print_all(std::ostream &stream) const {
269  stream << "\"Option name\" [type] = value" << std::endl;
270  disp(stream);
271  stream << std::endl;
272  }
273 
274  void Options::print_one(const std::string &name, std::ostream &stream) const {
275  const Options::Entry* entry = find(name);
276  if (entry!=nullptr) {
277  entry->disp(name, stream);
278  } else {
279  stream << " \"" << name << "\" does not exist.";
280  }
281  }
282 
283  std::vector<std::string> Options::all() const {
284  std::vector<std::string> ret;
285  for (auto&& e : entries) ret.push_back(e.first);
286  return ret;
287  }
288 
289  std::string Options::type(const std::string& name) const {
290  const Options::Entry* entry = find(name);
291  casadi_assert(entry!=nullptr, "Option \"" + name + "\" does not exist");
293  }
294 
295  std::string Options::info(const std::string& name) const {
296  const Options::Entry* entry = find(name);
297  casadi_assert(entry!=nullptr, "Option \"" + name + "\" does not exist");
298  return entry->description;
299  }
300 
301 } // namespace casadi
Generic data type, can hold different types such as bool, casadi_int, std::string etc.
static std::string get_type_description(TypeID type)
Get a description of a type.
bool is_dict() const
Check if a particular type.
The casadi namespace.
Definition: archiver.cpp:28
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const double inf
infinity
Definition: calculus.hpp:50
void update_dict(Dict &target, const Dict &source, bool recurse)
Update the target dictionary in place with source elements.
std::string description
Definition: options.hpp:48
void disp(const std::string &name, std::ostream &stream) const
Definition: options.cpp:48
std::string type(const std::string &name) const
Definition: options.cpp:289
std::vector< const Options * > bases
Definition: options.hpp:43
static bool is_sane(const Dict &opts)
Is the dictionary sane.
Definition: options.cpp:169
std::vector< std::string > all() const
Definition: options.cpp:283
void print_all(std::ostream &stream) const
Print list of options.
Definition: options.cpp:268
std::string info(const std::string &name) const
Definition: options.cpp:295
void disp(std::ostream &stream) const
Definition: options.cpp:57
static bool has_null(const Dict &opts)
Does the dictionary has null objects.
Definition: options.cpp:162
static double word_distance(const std::string &a, const std::string &b)
A distance metric between two words.
Definition: options.cpp:69
static Dict sanitize(const Dict &opts, bool top_level=true)
Sanitize a options dictionary.
Definition: options.cpp:173
static bool has_dot(const Dict &opts)
Does the dictionary contain a dot.
Definition: options.cpp:151
std::map< std::string, Entry > entries
Definition: options.hpp:55
const Options::Entry * find(const std::string &name) const
Definition: options.cpp:32
std::vector< std::string > suggestions(const std::string &word, casadi_int amount=5) const
Get the best suggestions for a misspelled word.
Definition: options.cpp:105
void check(const Dict &opts) const
Check if options exist.
Definition: options.cpp:240
void print_one(const std::string &name, std::ostream &stream) const
Print all information there is to know about a certain option.
Definition: options.cpp:274
void best_matches(const std::string &word, std::vector< std::pair< double, std::string > > &best) const
Find best matches.
Definition: options.cpp:127