optistack_internal.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 #include "optistack_internal.hpp"
26 #include "nlpsol.hpp"
27 #include "conic.hpp"
28 #include "function_internal.hpp"
29 #include "global_options.hpp"
30 #include "filesystem_impl.hpp"
31 
32 namespace casadi {
33 
34 class InternalOptiCallback : public FunctionInternal {
35  public:
36 
37  InternalOptiCallback(OptiNode& sol) : FunctionInternal(class_name()), sol_(sol) {}
38 
39  ~InternalOptiCallback() override {
40  clear_mem();
41  }
42 
44  std::string class_name() const override {return "InternalOptiCallback";}
45 
46  // Number of inputs and outputs
47  size_t get_n_in() override { return nlpsol_n_out();}
48 
49  Sparsity get_sparsity_in(casadi_int i) override {
50  std::string n = nlpsol_out(i);
51  casadi_int size = 0;
52  if (n=="f") {
53  size = 1;
54  } else if (n=="lam_x" || n=="x") {
55  size = sol_.nx();
56  } else if (n=="lam_g" || n=="g") {
57  size = sol_.ng();
58  } else if (n=="p" || n=="lam_p") {
59  size = sol_.np();
60  if (size==0) return Sparsity::dense(0, 0);
61  } else {
62  return Sparsity::dense(0, 0);
63  }
64  return Sparsity::dense(size, 1);
65  }
66 
67  void reset() { i=0; }
68 
69  // eval_dm has been defined instead of eval
70  bool has_eval_dm() const override { return true;}
71 
73  std::vector<DM> eval_dm(const std::vector<DM>& arg) const override {
74  DMDict r;
75 
76  for (casadi_int i=0;i<nlpsol_n_out();++i) {
77  r[nlpsol_out(i)] = arg[i];
78  }
79 
80  sol_.res(r);
81 
82  if (sol_.user_callback_) sol_.user_callback_->call(i);
83 
84  i+=1;
85  return {0};
86  }
87 
88  bool associated_with(const OptiNode* o) { return &sol_==o; }
89 
90  private:
91  OptiNode& sol_;
92  mutable casadi_int i;
93 };
94 
95 OptiNode* OptiNode::create(const std::string& problem_type) {
96 return new OptiNode(problem_type);
97 }
98 
99 
101  user_callback_ = callback;
102 }
103 
105  user_callback_ = nullptr;
106 }
107 
109  return user_callback_ != 0;
110 }
111 
112 std::string OptiNode::format_stacktrace(const Dict& stacktrace, casadi_int indent) {
113  std::string s_indent;
114  for (casadi_int i=0;i<indent;++i) {
115  s_indent+= " ";
116  }
117  std::string description;
118  std::string filename = stacktrace.at("file").as_string();
119  casadi_int line = stacktrace.at("line").as_int();
120  description += "called from " + filename +":"+str(line);
121  std::string name = stacktrace.at("name").as_string();
122  if (name!="Unknown" && name!= "<module>")
123  description += " in " + stacktrace.at("name").as_string();
124  try {
125  auto file_ptr = Filesystem::ifstream_ptr(filename);
126  std::istream& file = *file_ptr;
127  for (casadi_int i=0; i<line-1; ++i) {
128  file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
129  }
130  std::string contents; std::getline(file, contents);
131  auto it = contents.find_first_not_of(" \t\r\n");
132  if (it!=std::string::npos) {
133  description += "\n" + s_indent + contents.substr(it);
134  }
135  } catch(...) {
136  return description;
137  }
138  return description;
139 }
140 
141 std::string OptiNode::describe(const MX& expr, casadi_int indent, const Dict& opts) const {
142  if (problem_dirty()) return baked_copy().describe(expr, indent, opts);
143  casadi_int max_stacktrace_depth = 1;
144  for (const auto& op : opts) {
145  if (op.first=="max_stacktrace_depth") {
146  max_stacktrace_depth = op.second.as_int();
147  } else {
148  casadi_warning("Unknown option '" + op.first + "'");
149  }
150  }
151  std::string s_indent;
152  for (casadi_int i=0;i<indent;++i) {
153  s_indent+= " ";
154  }
155  std::string description = s_indent;
156  if (expr.is_symbolic()) {
157  if (has(expr)) {
158  description += "Opti " + variable_type_to_string(meta(expr).type) + " '" + expr.name() +
159  "' of shape " + expr.dim();
160  const Dict& extra = meta(expr).extra;
161  auto it = extra.find("stacktrace");
162  if (it!=extra.end()) {
163  for (const Dict& stacktrace : it->second.as_dict_vector()) {
164  description += ", " + format_stacktrace(stacktrace, indent+1);
165  if (--max_stacktrace_depth==0) break;
166  }
167  }
168  } else {
169  VariableType vt;
170  if (parse_opti_name(expr.name(), vt)) {
171  description += "Opti " + variable_type_to_string(vt) + " '" + expr.name() +
172  "' of shape " + expr.dim()+
173  ", belonging to a different instance of Opti.";
174  } else {
175  description += "MX symbol '" + expr.name() + "' of shape " + expr.dim();
176  description += ", declared outside of Opti.";
177  }
178  }
179  } else {
180  if (has_con(expr)) {
181  description = "Opti constraint of shape " + expr.dim();
182  const Dict& extra = meta_con(expr).extra;
183  auto it = extra.find("stacktrace");
184  if (it!=extra.end()) {
185  for (const Dict& stacktrace : it->second.as_dict_vector()) {
186  description += ", " + format_stacktrace(stacktrace, indent+1);
187  if (--max_stacktrace_depth==0) break;
188  }
189  }
190  } else {
191  std::vector<MX> s = symvar(expr);
192  if (s.empty()) {
193  description+= "Constant epxression.";
194  } else {
195  description+= "General expression, dependent on " + str(s.size()) + " symbols:";
196  for (casadi_int i=0;i<s.size();++i) {
197  description+= "\n"+describe(s[i], indent+1);
198  if (i>5) {
199  description+= "\n...";
200  break;
201  }
202  }
203  }
204  }
205  }
206 
207  return description;
208 }
209 
210 std::string OptiNode::g_describe(casadi_int i, const Dict& opts) const {
211  if (problem_dirty()) return baked_copy().g_describe(i, opts);
212  MX expr = g_lookup(i);
213  casadi_int local_i = i-meta_con(expr).start + GlobalOptions::start_index;
214  std::string description = describe(expr, 0, opts);
215  if (expr.numel()>1)
216  description += "\nAt nonzero " + str(local_i % expr.numel()) +
217  ", part " + str((casadi_int) local_i / expr.numel()) + ".";
218  return description;
219 }
220 
221 std::string OptiNode::x_describe(casadi_int i, const Dict& opts) const {
222  if (problem_dirty()) return baked_copy().x_describe(i, opts);
223  MX symbol = x_lookup(i);
224  casadi_int local_i = i-meta(symbol).start + GlobalOptions::start_index;
225  std::string description = describe(symbol, 0, opts);
226  if (symbol.numel()>1)
227  description += "\nAt nonzero " + str(local_i) + ".";
228  return description;
229 }
230 
231 MX OptiNode::x_lookup(casadi_int i) const {
232  if (problem_dirty()) return baked_copy().x_lookup(i);
233  casadi_assert(i>=0 && i<nx(),
234  "Index " + str(i) + " out of range for " + str(nx()) + " decision variables");
235  std::vector<MX> x = active_symvar(OPTI_VAR);
236  for (const auto& e : x) {
237  const MetaVar& m = meta(e);
238  if (i>=m.start && i<m.stop) return e;
239  }
240  casadi_error("Internal error");
241  return MX();
242 }
243 
244 MX OptiNode::g_lookup(casadi_int i) const {
245  if (problem_dirty()) return baked_copy().g_lookup(i);
246  casadi_assert(i>=0 && i<ng(),
247  "Index " + str(i) + " out of range for " + str(ng()) + " constraints");
248  for (const auto& e : g_) {
249  const MetaCon& m = meta_con(e);
250  if (i>=m.start && i<m.stop) return e;
251  }
252  casadi_error("Internal error");
253  return MX();
254 }
255 
256 OptiNode::OptiNode(const std::string& problem_type) :
257  count_(0), count_var_(0), count_par_(0), count_dual_(0) {
258  f_ = 0;
259  f_linear_scale_ = 1;
260  instance_number_ = instance_count_++;
261  user_callback_ = nullptr;
262  store_initial_[OPTI_VAR] = {};
263  store_initial_[OPTI_PAR] = {};
264  store_initial_[OPTI_DUAL_G] = {};
265  store_latest_[OPTI_VAR] = {};
266  store_latest_[OPTI_DUAL_G] = {};
267  casadi_assert(problem_type=="nlp" || problem_type=="conic",
268  "Specified problem type '" + problem_type + "'unknown. "
269  "Choose 'nlp' (default) or 'conic'.");
270  problem_type_ = problem_type;
272  helpers_ = std::make_shared<ValueCache>();
273 }
274 
276 }
277 
278 MX OptiNode::variable(casadi_int n, casadi_int m, const std::string& attribute) {
279 
280  // Prepare metadata
281  MetaVar meta_data;
282  meta_data.attribute = attribute;
283  meta_data.n = n;
284  meta_data.m = m;
285  meta_data.type = OPTI_VAR;
286  meta_data.count = count_++;
287  meta_data.i = count_var_++;
288  meta_data.domain = OPTI_DOMAIN_REAL;
289 
290  MX symbol, ret;
291 
292  if (attribute=="symmetric") {
293  casadi_assert(n==m, "You specified attribute 'symmetric', "
294  "while matrix is not even square, but " + str(n) + "-by-" + str(m) + ".");
295  symbol = MX::sym(name_prefix() + "x_" + str(count_var_), n*(n+1)/2);
296  ret = tril2symm(MX(Sparsity::lower(n), symbol));
297  } else if (attribute=="full") {
298  symbol = MX::sym(name_prefix() + "x_" + str(count_var_), n, m);
299  ret = symbol;
300  } else {
301  casadi_error("Unknown attribute '" + attribute + "'. Choose from 'full' or 'symmetric'.");
302  }
303 
304  // Store the symbol; preventing it from going ut of scope
305  symbols_.push_back(symbol);
306  store_initial_[OPTI_VAR].push_back(DM::zeros(symbol.sparsity()));
307  store_latest_[OPTI_VAR].push_back(DM::nan(symbol.sparsity()));
308  store_linear_scale_[OPTI_VAR].push_back(DM::ones(symbol.sparsity()));
309  store_linear_scale_offset_[OPTI_VAR].push_back(DM::zeros(symbol.sparsity()));
310 
311  set_meta(symbol, meta_data);
312  return ret;
313 }
314 
315 MX OptiNode::variable(const MX& symbol, const std::string& attribute) {
316 
317  // Prepare metadata
318  MetaVar meta_data;
319  meta_data.attribute = attribute;
320  meta_data.n = symbol.size1();
321  meta_data.m = symbol.size2();
322  meta_data.type = OPTI_VAR;
323  meta_data.count = count_++;
324  meta_data.i = count_var_++;
325 
326  // Store the symbol; preventing it from going ut of scope
327  symbols_.push_back(symbol);
328  store_initial_[OPTI_VAR].push_back(DM::zeros(symbol.sparsity()));
329  store_latest_[OPTI_VAR].push_back(DM::nan(symbol.sparsity()));
330  store_linear_scale_[OPTI_VAR].push_back(DM::ones(symbol.sparsity()));
331  store_linear_scale_offset_[OPTI_VAR].push_back(DM::zeros(symbol.sparsity()));
332 
333  set_meta(symbol, meta_data);
334  return symbol;
335 }
336 
337 MX OptiNode::variable(const Sparsity& sp, const std::string& attribute) {
338 
339  // Prepare metadata
340  MetaVar meta_data;
341  meta_data.attribute = attribute;
342  meta_data.n = sp.size1();
343  meta_data.m = sp.size2();
344  meta_data.type = OPTI_VAR;
345  meta_data.count = count_++;
346  meta_data.i = count_var_++;
347 
348  MX symbol = MX::sym(name_prefix() + "x_" + str(count_var_), sp);
349 
350  // Store the symbol; preventing it from going ut of scope
351  symbols_.push_back(symbol);
352  store_initial_[OPTI_VAR].push_back(DM::zeros(symbol.sparsity()));
353  store_latest_[OPTI_VAR].push_back(DM::nan(symbol.sparsity()));
354  store_linear_scale_[OPTI_VAR].push_back(DM::ones(symbol.sparsity()));
355  store_linear_scale_offset_[OPTI_VAR].push_back(DM::zeros(symbol.sparsity()));
356 
357  set_meta(symbol, meta_data);
358  return symbol;
359 }
360 
361 std::string OptiNode::name_prefix() const {
362  return "opti" + str(instance_number_) + "_";
363 }
364 
366  return Opti::create(new OptiNode(*this));
367 }
368 
369 void OptiNode::register_dual(MetaCon& c) {
370 
371  // Prepare metadata
372  MetaVar meta_data;
373  meta_data.attribute = "full";
374  meta_data.n = c.canon.size1();
375  meta_data.m = c.canon.size2();;
376  meta_data.type = OPTI_DUAL_G;
377  meta_data.count = count_++;
378  meta_data.i = count_dual_++;
379 
380  MX symbol, ret;
381  if (c.type==OPTI_PSD) {
382  symbol = MX();
383  ret = MX();
384  } else {
385  symbol = MX::sym(name_prefix()+"lam_g_"+str(count_dual_), c.canon.sparsity());
386 
387  casadi_assert_dev(c.canon.is_dense());
388 
389  Sparsity ret_sp = repmat(c.original.sparsity(), 1, c.n);
390 
391  casadi_int N = c.canon.sparsity().nnz();
392 
393  MX flat = vec(symbol);
394  if (c.type==OPTI_DOUBLE_INEQUALITY) {
395  MX v = MX::sym("v");
396  MX decide_left_right = vertcat(if_else_zero(v<0, -v), if_else_zero(v>=0, v));
397  Function sign = Function("sign", {v}, {decide_left_right});
398  Function sign_map = sign.map(c.canon.sparsity().nnz());
399  ret = MX(ret_sp, sign_map((c.flipped ? -1 : 1)*flat)[0].T());
400  } else {
401  casadi_int block_size = N / c.n;
402  std::vector<MX> original_blocks = vertsplit(
403  c.type==OPTI_EQUALITY ||
404  c.type==OPTI_GENERIC_EQUALITY ? flat : fabs(flat), block_size);
405  std::vector<MX> blocks(N);
406  for (casadi_int i=0;i<c.n;++i) {
407  casadi_int p = c.flipped? c.n-i-1: i;
408  blocks[p] = original_blocks[i];
409  }
410  ret = MX(ret_sp, vertcat(blocks));
411  }
412  }
413 
414  symbols_.push_back(symbol);
415  store_initial_[OPTI_DUAL_G].push_back(DM::zeros(symbol.sparsity()));
416  store_latest_[OPTI_DUAL_G].push_back(DM::nan(symbol.sparsity()));
417 
418  c.dual = ret;
419  c.dual_canon = symbol;
420 
421  set_meta(symbol, meta_data);
422 }
423 
424 MX OptiNode::parameter(const MX& symbol, const std::string& attribute) {
425  casadi_assert_dev(attribute=="full");
426 
427  // Prepare metadata
428  MetaVar meta_data;
429  meta_data.attribute = attribute;
430  meta_data.n = symbol.size1();
431  meta_data.m = symbol.size2();
432  meta_data.type = OPTI_PAR;
433  meta_data.count = count_++;
434  meta_data.i = count_par_++;
435 
436  symbols_.push_back(symbol);
437  store_initial_[OPTI_PAR].push_back(DM::nan(symbol.sparsity()));
438 
439  set_meta(symbol, meta_data);
440  return symbol;
441 }
442 
443 MX OptiNode::parameter(const Sparsity& sp, const std::string& attribute) {
444  casadi_assert_dev(attribute=="full");
445 
446  // Prepare metadata
447  MetaVar meta_data;
448  meta_data.attribute = attribute;
449  meta_data.n = sp.size1();
450  meta_data.m = sp.size2();
451  meta_data.type = OPTI_PAR;
452  meta_data.count = count_++;
453  meta_data.i = count_par_++;
454 
455  MX symbol = MX::sym(name_prefix() + "p_" + str(count_par_), sp);
456  symbols_.push_back(symbol);
457  store_initial_[OPTI_PAR].push_back(DM::nan(symbol.sparsity()));
458 
459  set_meta(symbol, meta_data);
460  return symbol;
461 }
462 
463 MX OptiNode::parameter(casadi_int n, casadi_int m, const std::string& attribute) {
464  // Prepare metadata
465  MetaVar meta_data;
466  meta_data.attribute = attribute;
467  meta_data.n = n;
468  meta_data.m = m;
469  meta_data.type = OPTI_PAR;
470  meta_data.count = count_++;
471  meta_data.i = count_par_++;
472 
473  MX symbol, ret;
474  if (attribute=="symmetric") {
475  casadi_assert(n==m, "You specified attribute 'symmetric', "
476  "while matrix is not even square, but " + str(n) + "-by-" + str(m) + ".");
477  symbol = MX::sym(name_prefix() + "p_" + str(count_par_), n*(n+1)/2);
478  ret = tril2symm(MX(Sparsity::lower(n), symbol));
479  } else if (attribute=="full") {
480  symbol = MX::sym(name_prefix() + "p_" + str(count_par_), n, m);
481  ret = symbol;
482  } else {
483  casadi_error("Unknown attribute '" + attribute + "'. Choose from 'full' or 'symmetric'.");
484  }
485 
486  symbols_.push_back(symbol);
487  store_initial_[OPTI_PAR].push_back(DM::nan(symbol.sparsity()));
488 
489  set_meta(symbol, meta_data);
490  return ret;
491 }
492 
494  assert_solved();
495  if (stats_.empty()) {
496  stats_ = solver_.stats();
497  is_simple_ = get_from_dict(stats_,
498  "detect_simple_bounds_is_simple",
499  std::vector<bool>(ng(), false));
500  target_x_ = get_from_dict(stats_,
501  "detect_simple_bounds_target_x",
502  std::vector<casadi_int>{});
503  casadi_assert_dev(is_simple_.size()==ng());
504 
505  g_index_reduce_g_.resize(ng());
506  g_index_reduce_x_.resize(ng(), -1);
507  g_index_unreduce_g_.resize(ng(), -1);
508 
509  casadi_int* target_x_ptr = target_x_.data();
510 
511  casadi_int k=0;
512  for (casadi_int i=0;i<is_simple_.size();++i) {
513  if (!is_simple_[i]) {
514  g_index_reduce_g_[i] = k;
515  g_index_unreduce_g_[k] = i;
516  k++;
517  } else {
518  g_index_reduce_g_[i] = -1;
519  g_index_reduce_x_[i] = *target_x_ptr;
520  target_x_ptr++;
521  }
522  }
523 
524  reduced_ = any(is_simple_);
525  }
526  return stats_;
527 }
528 
529 casadi_int OptiNode::g_index_reduce_g(casadi_int i) const {
530  stats();
531  return g_index_reduce_g_[i];
532 }
533 casadi_int OptiNode::g_index_unreduce_g(casadi_int i) const {
534  stats();
535  return g_index_unreduce_g_[i];
536 }
537 casadi_int OptiNode::g_index_reduce_x(casadi_int i) const {
538  stats();
539  return g_index_reduce_x_[i];
540 }
541 
542 std::string OptiNode::return_status() const {
543  Dict mystats;
544  try {
545  mystats = stats();
546  } catch (...) {
547  return "unknown";
548  }
549  if (mystats.find("return_status")!=mystats.end()) {
550  std::stringstream ss;
551  ss << mystats.at("return_status");
552  return ss.str();
553  }
554  return "unknown";
555 }
556 
557 bool OptiNode::return_success(bool accept_limit) const {
558  Dict mystats;
559  try {
560  mystats = stats();
561  } catch (...) {
562  return false;
563  }
564  bool success = false;
565  if (mystats.find("success")!=mystats.end()) success = mystats.at("success");
566  if (!accept_limit) return success;
567 
568  bool limited = false;
569  if (mystats.find("unified_return_status")!=mystats.end())
570  limited = mystats.at("unified_return_status")=="SOLVER_RET_LIMITED";
571  return success || limited;
572 }
573 
575  return solver_;
576 }
577 
578 void OptiNode::set_meta(const MX& m, const MetaVar& meta) {
579  meta_[m.get()] = meta;
580 }
581 
582 void OptiNode::set_meta_con(const MX& m, const MetaCon& meta) {
583  meta_con_[m.get()] = meta;
584 }
585 
586 void OptiNode::update_user_dict(const MX& m, const Dict& meta) {
587  if (has_con(m)) {
588  MetaCon m_update = get_meta_con(m);
589  MetaVar m_update2 = get_meta(m_update.dual_canon);
590  for (const auto & it : meta) {
591  m_update.extra[it.first] = it.second;
592  m_update2.extra[it.first] = it.second;
593  }
594  set_meta_con(m, m_update);
595  set_meta(m_update.dual_canon, m_update2);
596  } else {
597  for (const auto & s : MX::symvar(m)) {
598  MetaVar m_update = get_meta(s);
599  for (const auto & it : meta)
600  m_update.extra[it.first] = it.second;
601  set_meta(s, m_update);
602  }
603  }
604 }
605 
606 Dict OptiNode::user_dict(const MX& m) const {
607  if (has_con(m)) {
608  MetaCon meta = get_meta_con(m);
609  return meta.extra;
610  } else {
611  MetaVar meta = get_meta(m);
612  return meta.extra;
613  }
614 }
615 
616 MX OptiNode::dual(const MX& m) const {
617  return meta_con(m).dual;
618 }
619 
620 const MetaVar& OptiNode::meta(const MX& m) const {
621  assert_has(m);
622  auto find = meta_.find(m.get());
623  return find->second;
624 }
625 
626 const MetaCon& OptiNode::meta_con(const MX& m) const {
627  assert_has_con(m);
628  auto find = meta_con_.find(m.get());
629  return find->second;
630 }
631 
632 MetaVar& OptiNode::meta(const MX& m) {
633  assert_has(m);
634  auto find = meta_.find(m.get());
635  return find->second;
636 }
637 
638 MetaCon& OptiNode::meta_con(const MX& m) {
639  assert_has_con(m);
640  auto find = meta_con_.find(m.get());
641  return find->second;
642 }
643 
644 MetaVar OptiNode::get_meta(const MX& m) const {
645  return meta(m);
646 }
647 
649  return meta_con(m);
650 }
651 
652 bool OptiNode::has(const MX& m) const {
653  return meta_.find(m.get())!=meta_.end();
654 }
655 
656 bool OptiNode::has_con(const MX& m) const {
657  return meta_con_.find(m.get())!=meta_con_.end();
658 }
659 
660 void OptiNode::assert_has(const MX& m) const {
661  if (!has(m)) {
662  VariableType vt;
663  casadi_assert(m.is_symbolic(), "Symbol expected, got expression.");
664  if (parse_opti_name(m.name(), vt)) {
665  casadi_error("Unknown: " + describe(m));
666  } else {
667  casadi_error("Unknown: " + describe(m) + "\n"
668  "Note: you cannot use a raw MX.sym in your Opti problem,"
669  " only if you package it in a CasADi Function.");
670  }
671  }
672 }
673 
674 void OptiNode::assert_has_con(const MX& m) const {
675  casadi_assert(has_con(m), "Constraint not present in Opti stack.");
676 }
677 
678 casadi_int OptiNode::instance_count_ = 0;
679 
680 bool OptiNode::parse_opti_name(const std::string& name, VariableType& vt) const {
681  casadi_int i = name.find("opti");
682  if (i!=0) return false;
683 
684  i = name.find("_");
685  i++;
686  if (i==std::string::npos) return false;
687  if (name.substr(i, 1)=="x") {
688  vt = OPTI_VAR;
689  return true;
690  } else if (name.substr(i, 1)=="p") {
691  vt = OPTI_PAR;
692  return true;
693  } else if (name.substr(i, 5)=="lam_g") {
694  vt = OPTI_DUAL_G;
695  return true;
696  }
697 
698  return false;
699 }
700 
701 std::string OptiNode::variable_type_to_string(VariableType vt) const {
702  auto it = VariableType2String_.find(vt);
703  if (it==VariableType2String_.end()) return "unknown variable type";
704  return it->second;
705 
706 }
707 std::map<VariableType, std::string> OptiNode::VariableType2String_ =
708  {{OPTI_VAR, "decision variable"},
709  {OPTI_PAR, "parameter"},
710  {OPTI_DUAL_G, "dual variable"}};
711 
712 std::vector<MX> OptiNode::initial() const {
713  std::vector<MX> ret;
714  for (const auto& e : symvar()) {
715  if (meta(e).type==OPTI_VAR || meta(e).type==OPTI_DUAL_G)
716  ret.push_back(e==store_initial_.at(meta(e).type)[meta(e).i]);
717  }
718  return ret;
719 }
720 
721 std::vector<MX> OptiNode::value_variables() const {
722  std::vector<MX> ret;
723  for (const auto& e : symvar()) {
724  if (meta(e).type==OPTI_VAR)
725  ret.push_back(e==store_latest_.at(meta(e).type)[meta(e).i]);
726  }
727  return ret;
728 }
729 
730 std::vector<MX> OptiNode::value_parameters() const {
731  std::vector<MX> ret;
732  for (const auto& e : symvar()) {
733  if (meta(e).type==OPTI_PAR)
734  ret.push_back(e==store_initial_.at(meta(e).type)[meta(e).i]);
735  }
736  return ret;
737 }
738 
740  casadi_assert(!f_.is_empty() || !g_.empty(),
741  "You need to specify at least an objective (y calling 'minimize'), "
742  "or a constraint (by calling 'subject_to').");
743 
744  symbol_active_.clear();
745  symbol_active_.resize(symbols_.size());
746  helpers_ = std::make_shared<ValueCache>();
747 
748  // Gather all expressions
749  MX total_expr = vertcat(f_, veccat(g_));
750 
751  // Categorize the symbols appearing in those expressions
752  for (const auto& d : symvar(total_expr))
753  symbol_active_[meta(d).count] = true;
754 
755  std::vector<MX> x = active_symvar(OPTI_VAR);
756  for (casadi_int i=0;i<x.size();++i) meta(x[i]).active_i = i;
757 
758  casadi_int offset = 0;
759  for (const auto& v : x) {
760  meta(v).start = offset;
761  offset+= v.nnz();
762  meta(v).stop = offset;
763  }
764  std::vector<MX> p = active_symvar(OPTI_PAR);
765  for (casadi_int i=0;i<p.size();++i) meta(p[i]).active_i = i;
766 
767  // Fill the nlp definition
768  nlp_["x"] = veccat(x);
769  nlp_["p"] = veccat(p);
770 
771  nlp_unscaled_["x"] = veccat(x);
772  nlp_unscaled_["p"] = veccat(p);
773 
774  discrete_.clear();
775  for (const MX& e : x) {
776  discrete_.insert(discrete_.end(), e.nnz(), meta(e).domain == OPTI_DOMAIN_INTEGER);
777  }
778 
779  nlp_["f"] = f_;
780  nlp_unscaled_["f"] = f_;
781 
782  offset = 0;
783  for (casadi_int i=0;i<g_.size();++i) {
784  MetaCon& r = meta_con(g_[i]);
785  MetaVar& r2 = meta(r.dual_canon);
786  symbol_active_[r2.count] = true;
787 
788  // Compute offsets for this constraint:
789  // location into the global constraint variable
790  r.start = offset;
791  offset+= r.canon.nnz();
792  r.stop = offset;
793 
794  r2.start = r.start;
795  r2.stop = r.stop;
796 
797  }
798  index_all_to_g_.resize(offset);
799  offset = 0;
800  casadi_int offset_g = 0;
801  for (const auto& g : g_) {
802  const MetaCon& r = meta_con(g);
803  if (r.type==OPTI_PSD) {
804  for (casadi_int i=0;i<r.stop-r.start;++i) {
805  index_all_to_g_[r.start+i] = -1;
806  }
807  } else {
808  for (casadi_int i=0;i<r.stop-r.start;++i) {
809  index_all_to_g_[r.start+i] = offset_g+i;
810  }
811  offset_g += r.canon.nnz();
812  }
813  }
814 
815  std::vector<MX> lam = active_symvar(OPTI_DUAL_G);
816  for (casadi_int i=0;i<lam.size();++i) meta(lam[i]).active_i = i;
817 
818  lam_ = veccat(lam);
819 
820  // Collect bounds and canonical form of constraints
821  std::vector<MX> g_all, g_unscaled_all;
822  std::vector<MX> h_all, h_unscaled_all;
823  std::vector<MX> lbg_all, lbg_unscaled_all;
824  std::vector<MX> ubg_all, ubg_unscaled_all;
825 
826  g_linear_scale_.clear();
827  h_linear_scale_.clear();
828  std::vector<DM> g_linear_scale, h_linear_scale;
829 
830  equality_.clear();
831  for (const auto& g : g_) {
832  if (meta_con(g).type==OPTI_PSD) {
833  h_all.push_back(meta_con(g).canon/meta_con(g).linear_scale);
834  if (meta_con(g).canon.numel()==meta_con(g).linear_scale.numel()) {
835  h_linear_scale.push_back(meta_con(g).linear_scale);
836  } else {
837  casadi_assert_dev(meta_con(g).linear_scale.numel()==1);
838  h_linear_scale.push_back(DM::ones(meta_con(g).canon.sparsity())*meta_con(g).linear_scale);
839  }
840  h_unscaled_all.push_back(meta_con(g).canon);
841  } else {
842  g_all.push_back(meta_con(g).canon/meta_con(g).linear_scale);
843  if (meta_con(g).canon.numel()==meta_con(g).linear_scale.numel()) {
844  g_linear_scale.push_back(meta_con(g).linear_scale);
845  } else {
846  casadi_assert_dev(meta_con(g).linear_scale.numel()==1);
847  g_linear_scale.push_back(DM::ones(meta_con(g).canon.sparsity())*meta_con(g).linear_scale);
848  }
849  g_unscaled_all.push_back(meta_con(g).canon);
850  lbg_all.push_back(meta_con(g).lb/meta_con(g).linear_scale);
851  lbg_unscaled_all.push_back(meta_con(g).lb);
852  ubg_all.push_back(meta_con(g).ub/meta_con(g).linear_scale);
853  ubg_unscaled_all.push_back(meta_con(g).ub);
854 
855  equality_.insert(equality_.end(),
856  meta_con(g).canon.numel(),
857  meta_con(g).type==OPTI_EQUALITY || meta_con(g).type==OPTI_GENERIC_EQUALITY);
858  }
859  }
860 
861  nlp_["g"] = veccat(g_all);
862  g_linear_scale_ = veccat(g_linear_scale).nonzeros();
863  nlp_unscaled_["g"] = veccat(g_unscaled_all);
864  if (problem_type_=="conic") {
865  nlp_["h"] = diagcat(h_all);
866  nlp_unscaled_["h"] = diagcat(h_unscaled_all);
867  h_linear_scale_ = veccat(h_linear_scale).nonzeros();
868  }
869 
870  // Get scaling data
871  std::vector<DM> linear_scale = active_values(OPTI_VAR, store_linear_scale_);
872  std::vector<DM> linear_scale_offset = active_values(OPTI_VAR, store_linear_scale_offset_);
873 
874  linear_scale_ = veccat(linear_scale).nonzeros();
875  linear_scale_offset_ = veccat(linear_scale_offset).nonzeros();
876 
877  // Unscaled version of x
878  std::vector<MX> x_unscaled(x.size());
879  for (casadi_int i=0;i<x.size();++i) {
880  x_unscaled[i] = x[i]*linear_scale[i] + linear_scale_offset[i];
881  }
882 
883  // Perform substitution
884  std::vector<MX> expr = {nlp_["f"], nlp_["g"]};
885  if (problem_type_=="conic") expr.push_back(nlp_["h"]);
886  std::vector<MX> fgh = substitute(expr, x, x_unscaled);
887  nlp_["f"] = fgh[0];
888  nlp_["g"] = fgh[1];
889  if (problem_type_=="conic") {
890  nlp_["h"] = fgh[2];
891  }
892 
893  // Scale of objective
894  nlp_["f"] = nlp_["f"]/f_linear_scale_;
895 
896  // Create bounds helper function
897  MXDict bounds;
898  bounds["p"] = nlp_["p"];
899  bounds_lbg_ = veccat(lbg_all);
900  bounds_ubg_ = veccat(ubg_all);
901  bounds_unscaled_lbg_ = veccat(lbg_unscaled_all);
902  bounds_unscaled_ubg_ = veccat(ubg_unscaled_all);
903 
904  bounds["lbg"] = bounds_lbg_;
905  bounds["ubg"] = bounds_ubg_;
906 
907  bounds_ = Function("bounds", bounds, {"p"}, {"lbg", "ubg"});
908  mark_problem_dirty(false);
909 }
910 
912  if (problem_dirty()) return baked_copy().scale_helper(h);
913  std::string name = h.name();
914  MX g_linear_scale_mx = g_linear_scale();
915  MX g_linear_scale_inv = 1/g_linear_scale();
916  MX f_linear_scale_mx = f_linear_scale();
917  MX x_linear_scale_mx = x_linear_scale();
918  MX x_linear_scale_offset_mx = x_linear_scale_offset();
919  if (name=="nlp_jac_g") {
920  MXDict arg;
921  arg["x"] = MX::sym("x", nx());
922  arg["p"] = MX::sym("p", np());
923  MXDict args;
924  args["x"] = arg["x"]*x_linear_scale_mx+x_linear_scale_offset_mx;
925  args["p"] = arg["p"];
926  MXDict res = h(args);
927  for (const auto & it : res) {
928  if (it.first=="g") {
929  arg[it.first] = it.second*g_linear_scale_inv;
930  } else if (it.first=="jac_g_x") {
931  arg[it.first] = mtimes(
932  mtimes(diag(g_linear_scale_inv), it.second),
933  diag(x_linear_scale_mx));
934  } else {
935  casadi_error("Unknown output '" + it.first + "'. Expecting g, jac_g_x.");
936  }
937  }
938  Function ret(name, arg, h.name_in(), h.name_out());
939  return ret;
940  } else if (name=="nlp_hess_l") {
941  MXDict arg;
942  arg["x"] = MX::sym("x", nx());
943  arg["p"] = MX::sym("p", np());
944  arg["lam_f"] = MX::sym("lam_f");
945  arg["lam_g"] = MX::sym("lam_g", ng());
946  MXDict args;
947  args["x"] = arg["x"]*x_linear_scale_mx+x_linear_scale_offset_mx;
948  args["p"] = arg["p"];
949  args["lam_f"] = arg["lam_f"]/f_linear_scale_mx;
950  args["lam_g"] = arg["lam_g"]/g_linear_scale_mx;
951  MXDict res = h(args);
952  for (const auto & it : res) {
953  if (it.first=="triu_hess_gamma_x_x" ||
954  it.first=="hess_gamma_x_x" ||
955  (it.second.size1()==nx() && it.second.is_square())) {
956  MX D = diag(x_linear_scale_mx);
957  arg[it.first] = mtimes(mtimes(D, it.second), D);
958  } else {
959  casadi_error("Unknown output '" + it.first + "'. Expecting triu_hess_gamma_x_x");
960  }
961  }
962  Function ret(name, arg, h.name_in(), h.name_out());
963  return ret;
964  } else {
965  casadi_error("Unknown helper function '" + name + "'");
966  }
967 }
968 
969 void OptiNode::solver(const std::string& solver_name, const Dict& plugin_options,
970  const Dict& solver_options) {
971  solver_name_ = solver_name;
972  solver_options_ = plugin_options;
973  if (!solver_options.empty())
974  solver_options_[solver_name] = solver_options;
976 }
977 
978 std::vector<MX> OptiNode::sort(const std::vector<MX>& v) const {
979  // We exploit the fact that std::map is ordered
980 
981  // Populate map
982  std::map<casadi_int, MX> unordered;
983  for (const auto& d : v)
984  unordered[meta(d).count] = d;
985 
986  // Loop over map (ordered)
987  std::vector<MX> ret;
988  for (auto const &e : unordered)
989  ret.push_back(e.second);
990  return ret;
991 }
992 
993 std::vector<MX> OptiNode::symvar() const {
994  return symbols_;
995 }
996 
997 std::vector<MX> OptiNode::symvar(const MX& expr) const {
998  return sort(MX::symvar(expr));
999 }
1000 
1001 std::vector<MX> OptiNode::ineq_unchain(const MX& a, bool& flipped) {
1002  flipped = false;
1003  casadi_assert_dev(a.is_op(OP_LE) || a.is_op(OP_LT));
1004 
1005  // Is there inequalities in the left or right leaf?
1006  bool left = a.dep(0).is_op(OP_LE) || a.dep(0).is_op(OP_LT);
1007  bool right = a.dep(1).is_op(OP_LE) || a.dep(1).is_op(OP_LT);
1008  casadi_assert_dev(!left || !right);
1009 
1010  if (!left && !right)
1011  return {a.dep(0), a.dep(1)}; // Simple inequality
1012 
1013  // We have a chain of inequalities
1014  bool ineq = !left;
1015  std::vector<MX> ret = {a.dep(!ineq)};
1016  MX e = a.dep(ineq);
1017  while (e.is_op(OP_LE) || e.is_op(OP_LT)) {
1018  casadi_assert_dev(!e.is_op(OP_EQ));
1019  casadi_assert_dev(!e.dep(!ineq).is_op(OP_LE) && !e.dep(!ineq).is_op(OP_LT));
1020  ret.push_back(e.dep(!ineq));
1021  e = e.dep(ineq);
1022  }
1023  ret.push_back(e);
1024  if (left) std::reverse(ret.begin(), ret.end());
1025  flipped = !left;
1026 
1027  return ret;
1028 }
1029 
1030 void OptiNode::assert_only_opti_symbols(const MX& e) const {
1031  std::vector<MX> symbols = MX::symvar(e);
1032  for (const auto& s : symbols) assert_has(s);
1033 }
1034 
1035 void OptiNode::assert_only_opti_nondual(const MX& e) const {
1036  std::vector<MX> symbols = MX::symvar(e);
1037  for (const auto& s : symbols) {
1038  assert_has(s);
1039  casadi_assert(meta(s).type!=OPTI_DUAL_G, "Dual variables forbidden in this context.");
1040  }
1041 }
1042 
1043 bool OptiNode::is_parametric(const MX& expr) const {
1044  return symvar(expr, OPTI_VAR).empty();
1045 }
1046 
1047 MetaCon OptiNode::canon_expr(const MX& expr, const DM& linear_scale) const {
1048  MX c = expr;
1049 
1050  MetaCon con;
1051  con.original = expr;
1052  casadi_assert(linear_scale.is_scalar() || linear_scale.size()==expr.size(),
1053  "Linear scale must have the same size as the expression. "
1054  "You got linear_scale " + con.linear_scale.dim() + " while " + expr.dim() + " is expected.");
1055  con.linear_scale = linear_scale;
1056 
1057  if (c.is_op(OP_LE) || c.is_op(OP_LT)) { // Inequalities
1058  std::vector<MX> ret;
1059  bool flipped;
1060  std::vector<MX> args = ineq_unchain(c, flipped);
1061  std::vector<bool> parametric;
1062  for (auto &a : args) parametric.push_back(is_parametric(a));
1063 
1064  if (args.size()==2 && (parametric[0] || parametric[1])) {
1065  // case: g(x,p) <= bound(p)
1066  MX e = args[0]-args[1];
1067  if (e.is_vector()) {
1068  casadi_assert(!parametric[0] || !parametric[1],
1069  "Constraint must contain decision variables.");
1070  if (problem_type_=="conic") {
1071  if (args[0].op()==OP_NORMF || args[0].op()==OP_NORM2) {
1072  args[0] = -soc(args[0].dep(), args[1]);
1073  args[1] = 0;
1074  }
1075  } else {
1076  con.type = OPTI_INEQUALITY;
1077  if (parametric[0]) {
1078  con.lb = args[0]*DM::ones(e.sparsity());
1079  con.ub = inf*DM::ones(e.sparsity());
1080  con.canon = args[1]*DM::ones(e.sparsity());
1081  } else {
1082  con.lb = -inf*DM::ones(e.sparsity());
1083  con.ub = args[1]*DM::ones(e.sparsity());
1084  con.canon = args[0]*DM::ones(e.sparsity());
1085  }
1086  return con;
1087  }
1088  }
1089  // Fall through to generic inequalities
1090  } else if (args.size()==3 && parametric[0] && parametric[2]) {
1091  // lb(p) <= g(x,p) <= ub(p)
1092  casadi_assert(!parametric[1], "Constraint must contain decision variables.");
1094  con.lb = args[0]*DM::ones(args[1].sparsity());
1095  con.ub = args[2]*DM::ones(args[1].sparsity());
1096  con.canon = args[1]*DM::ones(args[1].sparsity());
1097  con.flipped = flipped;
1098  con.n = 2;
1099  return con;
1100  }
1101 
1102  bool type_known = false;
1103  for (casadi_int j=0;j<args.size()-1;++j) {
1104  MX e = args[j]-args[j+1];
1105  if (problem_type_=="conic") {
1106  if (args[j].op()==OP_NORMF || args[j].op()==OP_NORM2) {
1107  args[j] = -soc(args[j].dep(), args[j+1]);
1108  args[j+1] = 0;
1109  e = args[j]-args[j+1];
1110  }
1111  }
1112  if (e.is_vector()) {
1113  // g1(x,p) <= g2(x,p)
1114  ret.push_back(e);
1115  casadi_assert_dev(!type_known || con.type==OPTI_GENERIC_INEQUALITY);
1116  type_known = true;
1118  con.flipped = flipped;
1119  } else {
1120  // A(x,p) >= b(p)
1121  MX a = args[j+1];
1122  MX b = args[j];
1123  e = a-b;
1124 
1125  casadi_assert(e.size1()==e.size2(),
1126  "Matrix inequalities must be square. Did you mean element-wise inequality instead?");
1127  if (a.is_scalar()) a*= MX::eye(e.size1());
1128  if (b.is_scalar()) b*= MX::eye(e.size1());
1129  e = a-b;
1130 
1131  ret.push_back(e);
1132  casadi_assert_dev(!type_known || con.type==OPTI_PSD);
1133  type_known = true;
1134  con.type = OPTI_PSD;
1135  }
1136  }
1137 
1138  if (con.type==OPTI_GENERIC_INEQUALITY) {
1139  con.canon = veccat(ret);
1140  con.lb = -inf*DM::ones(con.canon.sparsity());
1141  con.ub = DM::zeros(con.canon.sparsity());
1142  con.n = ret.size();
1143  if (!con.linear_scale.is_scalar()) {
1144  con.linear_scale = repmat(con.linear_scale, ret.size(), 1);
1145  }
1146  } else {
1147  con.canon = diagcat(ret);
1148  con.n = ret.size();
1149  }
1150  return con;
1151  } else if (c.is_op(OP_EQ)) { // Inequalities
1152  casadi_assert(!is_parametric(c.dep(0)) || !is_parametric(c.dep(1)),
1153  "Constraint must contain decision variables.");
1154  MX e = c.dep(0)-c.dep(1);
1155  if (is_parametric(c.dep(0))) {
1156  con.canon = c.dep(1)*DM::ones(e.sparsity());
1157  con.lb = c.dep(0)*DM::ones(e.sparsity());
1158  con.type = OPTI_EQUALITY;
1159  casadi_assert(c.dep(0).size1()<=c.dep(1).size1() && c.dep(0).size2()<=c.dep(1).size2(),
1160  "Constraint shape mismatch.");
1161  } else if (is_parametric(c.dep(1))) {
1162  con.canon = c.dep(0)*DM::ones(e.sparsity());
1163  con.lb = c.dep(1)*DM::ones(e.sparsity());
1164  con.type = OPTI_EQUALITY;
1165  casadi_assert(c.dep(1).size1()<=c.dep(0).size1() && c.dep(1).size2()<=c.dep(0).size2(),
1166  "Constraint shape mismatch.");
1167  } else {
1168  con.lb = DM::zeros(e.sparsity());
1169  con.canon = e;
1171  }
1172  con.ub = con.lb;
1173  return con;
1174  } else { // Something else
1175  con.type = OPTI_UNKNOWN;
1176  con.canon = c;
1177  return con;
1178  }
1179 
1180 }
1181 
1183  casadi_assert(solved(),
1184  "This action is forbidden since you have not solved the Opti stack yet "
1185  "(with calling 'solve').");
1186 }
1187 
1189  casadi_assert(!problem_dirty(),
1190  "This action is forbidden since you have not baked the Opti stack yet "
1191  "(with calling 'solve').");
1192 }
1193 
1195  casadi_assert_dev(g_.empty());
1196  casadi_assert_dev(f_.is_empty());
1197 }
1198 
1199 void OptiNode::minimize(const MX& f, double linear_scale) {
1200  assert_only_opti_nondual(f);
1202  casadi_assert(f.is_scalar(), "Objective must be scalar, got " + f.dim() + ".");
1203  f_ = f;
1204  f_linear_scale_ = linear_scale;
1205 }
1206 
1207 void OptiNode::subject_to(const MX& g, const DM& linear_scale, const Dict& options) {
1208  assert_only_opti_nondual(g);
1210 
1211  casadi_assert(!g.is_empty(), "You passed an empty expression to `subject_to`. "
1212  "Make sure the number of rows and columns is non-zero. "
1213  "Got " + g.dim(true) + ".");
1214  casadi_assert(g.nnz()>0, "You passed a fully sparse expression to `subject_to`. "
1215  "Make sure the expression has at least one nonzero. "
1216  "Got " + g.dim(true) + ".");
1217  casadi_assert(!g.is_constant(), "You passed a constant to `subject_to`. "
1218  "You need a symbol to form a constraint.");
1219 
1220  // Store the meta-data
1221  MetaCon con = canon_expr(g, linear_scale);
1222  casadi_assert(con.type!=OPTI_UNKNOWN, "Constraint type unknown. Use ==, >= or <= .");
1223  set_meta_con(g, con);
1224  register_dual(meta_con(g));
1225  g_.push_back(g);
1226 
1227  for (auto && it : options) {
1228  if (it.first=="stacktrace") {
1229  meta_con(g).extra["stacktrace"] = it.second.to_dict_vector();
1230  meta(meta_con(g).dual_canon).extra["stacktrace"] = it.second.to_dict_vector();
1231  } else if (it.first=="meta") {
1232  update_user_dict(g, it.second.to_dict());
1233  } else {
1234  casadi_error("Unknown option: " + it.first);
1235  }
1236  }
1237 }
1238 
1241  g_.clear();
1242  store_initial_[OPTI_DUAL_G].clear();
1243  store_latest_[OPTI_DUAL_G].clear();
1244  count_dual_ = 0;
1245 }
1246 
1247 std::vector<MX> OptiNode::symvar(const MX& expr, VariableType type) const {
1248  std::vector<MX> ret;
1249  for (const auto& d : symvar(expr)) {
1250  if (meta(d).type==type) ret.push_back(d);
1251  }
1252 
1253  return ret;
1254 }
1255 
1256 void OptiNode::res(const DMDict& res) {
1257  const std::vector<double> & x_v = res.at("x").nonzeros();
1258  for (const auto &v : active_symvar(OPTI_VAR)) {
1259  casadi_int i = meta(v).i;
1260  std::vector<double> & data_v = store_latest_[OPTI_VAR][i].nonzeros();
1261  for (casadi_int i=0;i<data_v.size();++i) {
1262  casadi_int j = meta(v).start+i;
1263  data_v[i] = x_v[j]*linear_scale_[j] + linear_scale_offset_[j];
1264  }
1265  }
1266  if (res.find("lam_g")!=res.end()) {
1267  const std::vector<double> & lam_v = res.at("lam_g").nonzeros();
1268  for (const auto &v : active_symvar(OPTI_DUAL_G)) {
1269  casadi_int i = meta(v).i;
1270  std::vector<double> & data_v = store_latest_[OPTI_DUAL_G][i].nonzeros();
1271  for (casadi_int i=0;i<data_v.size();++i) {
1272  casadi_int j = meta(v).start+i;
1273  j = index_all_to_g_.at(j);
1274  if (j<0) continue;
1275  data_v[i] = lam_v.at(j)/g_linear_scale_.at(j)*f_linear_scale_;
1276  }
1277  }
1278  }
1279  res_ = res;
1280  mark_solved();
1281 }
1282 
1283 bool OptiNode::old_callback() const {
1284  if (callback_.is_null()) return false;
1285  InternalOptiCallback* cb = static_cast<InternalOptiCallback*>(callback_.get());
1286  return !cb->associated_with(this);
1287 }
1288 
1290  if (problem_dirty()) {
1291  bake();
1292  }
1293 
1294  // Verify the constraint types
1295  for (const auto& g : g_) {
1296  if (problem_type_!="conic") {
1297  if (meta_con(g).type==OPTI_PSD)
1298  casadi_error("Psd constraints not implemented yet. "
1299  "Perhaps you intended an element-wise inequality? "
1300  "In that case, make sure that the matrix is flattened (e.g. mat(:)).");
1301  }
1302  }
1303 
1304  Dict solver_options_all = solver_options_;
1305 
1306  if (solver_options_all.find("equality")==solver_options_all.end()) {
1307  solver_options_all["equality"] = equality_;
1308  }
1309 
1310  if (solver_options_all.find("discrete")==solver_options_all.end()) {
1311  solver_options_all["discrete"] = discrete_;
1312  }
1313 
1314  Dict opts = solver_options_all;
1315 
1316  // Handle callbacks
1317  if (callback && user_callback_) {
1318  callback_ = Function::create(new InternalOptiCallback(*this), Dict());
1319  opts["iteration_callback"] = callback_;
1320  }
1321 
1322  casadi_assert(!solver_name_.empty(),
1323  "You must call 'solver' on the Opti stack to select a solver. "
1324  "Suggestion: opti.solver('ipopt')");
1325 
1326  if (problem_type_=="conic") {
1327  return qpsol("solver", solver_name_, nlp_, opts);
1328  } else {
1329  return nlpsol("solver", solver_name_, nlp_, opts);
1330  }
1331 
1332 }
1333 
1334 // Solve the problem
1335 OptiSol OptiNode::solve(bool accept_limit) {
1336 
1337  if (problem_dirty()) {
1338  bake();
1339  }
1340 
1341  bool solver_update = solver_dirty() || old_callback() || (user_callback_ && callback_.is_null());
1342 
1343  if (solver_update) {
1344  solver_ = solver_construct(true);
1345  mark_solver_dirty(false);
1346  }
1347 
1348  solve_prepare();
1349  res(solve_actual(arg_));
1350 
1351  std::string ret = return_status();
1352 
1353  casadi_assert(return_success(accept_limit),
1354  "Solver failed. You may use opti.debug.value to investigate the latest values of variables."
1355  " return_status is '" + ret + "'");
1356 
1357  return copy();
1358 }
1359 
1360 // Solve the problem
1362 
1363 
1364  // Verify the constraint types
1365  for (const auto& g : g_) {
1366  if (meta_con(g).type==OPTI_UNKNOWN)
1367  casadi_error("Constraint type unknown. Use ==, >= or <= .");
1368  }
1369 
1370  if (user_callback_) {
1371  InternalOptiCallback* cb = static_cast<InternalOptiCallback*>(callback_.get());
1372  cb->reset();
1373  }
1374 
1375  // Get initial guess and parameter values
1376  arg_["x0"] = (veccat(active_values(OPTI_VAR))-linear_scale_offset_)/linear_scale_;
1377  arg_["p"] = veccat(active_values(OPTI_PAR));
1378  arg_["lam_g0"] = veccat(active_values(OPTI_DUAL_G));
1379  if (!arg_["p"].is_regular()) {
1380  std::vector<MX> s = active_symvar(OPTI_PAR);
1381  std::vector<DM> v = active_values(OPTI_PAR);
1382  for (casadi_int i=0;i<s.size();++i) {
1383  casadi_assert(v[i].is_regular(),
1384  "You have forgotten to assign a value to a parameter ('set_value'), "
1385  "or have set it to NaN/Inf:\n" + describe(s[i], 1));
1386  }
1387  }
1388 
1389 
1390  // Evaluate bounds for given parameter values
1391  DMDict arg;
1392  arg["p"] = arg_["p"];
1393  DMDict res = bounds_(arg);
1394  arg_["lbg"] = res["lbg"];
1395  arg_["ubg"] = res["ubg"];
1396 
1397  stats_.clear();
1398 }
1399 
1401  return solver_(arg);
1402 }
1403 
1404 bool override_num(const std::map<casadi_int, MX> & temp, std::vector<DM>& num, casadi_int i) {
1405  // Override when values are supplied
1406  auto it = temp.find(i);
1407  if (it==temp.end()) {
1408  return true;
1409  } else {
1410  Slice all;
1411  DM t = static_cast<DM>(it->second);
1412  num.back().set(t, false, all, all);
1413  }
1414  return false;
1415 }
1416 
1417 
1418 DM OptiNode::value(const MX& expr, const std::vector<MX>& values, bool scaled) const {
1419 
1420  std::shared_ptr<ValueHelper> vh;
1421  if (!helpers_->incache(expr, vh)) {
1422  vh = std::make_shared<ValueHelper>();
1423  std::vector<MX> x = symvar(expr, OPTI_VAR);
1424  std::vector<MX> p = symvar(expr, OPTI_PAR);
1425  std::vector<MX> lam = symvar(expr, OPTI_DUAL_G);
1426  vh->x = x;
1427  vh->p = p;
1428  vh->lam = lam;
1429  vh->helper = Function("helper", std::vector<MX>{veccat(x), veccat(p), veccat(lam)}, {expr});
1430  if (vh->helper.has_free())
1431  casadi_error("This expression has symbols that are not defined "
1432  "within Opti using variable/parameter.");
1433  helpers_->tocache_if_missing(expr, vh);
1434  }
1435 
1436  const std::vector<MX>& x = vh->x;
1437  const std::vector<MX>& p = vh->p;
1438  const std::vector<MX>& lam = vh->lam;
1439  const Function& helper = vh->helper;
1440 
1441  std::map<VariableType, std::map<casadi_int, MX> > temp;
1442  temp[OPTI_DUAL_G] = std::map<casadi_int, MX>();
1443  for (const auto& v : values) {
1444  casadi_assert_dev(v.is_op(OP_EQ));
1445  casadi_int i = meta(v.dep(1)).i;
1446  casadi_assert_dev(v.dep(0).is_constant());
1447  temp[meta(v.dep(1)).type][i] = v.dep(0);
1448  }
1449 
1450  bool undecided_vars = false;
1451  std::vector<DM> x_num;
1452  for (const auto& e : x) {
1453  casadi_int i = meta(e).i;
1454  x_num.push_back(store_latest_.at(OPTI_VAR).at(i));
1455  undecided_vars |= override_num(temp[OPTI_VAR], x_num, i);
1456  if (scaled) {
1457  x_num.back() = x_num.back()/store_linear_scale_.at(OPTI_VAR)[meta(e).i] -
1458  store_linear_scale_offset_.at(OPTI_VAR)[meta(e).i];
1459  }
1460  }
1461 
1462  std::vector<DM> lam_num;
1463  for (const auto& e : lam) {
1464  casadi_int i = meta(e).i;
1465  casadi_assert(i<store_latest_.at(OPTI_DUAL_G).size(),
1466  "This expression has a dual for a constraint that is not given to Opti:\n" +
1467  describe(e, 1));
1468  lam_num.push_back(store_latest_.at(OPTI_DUAL_G).at(i));
1469  undecided_vars |= override_num(temp[OPTI_DUAL_G], lam_num, i);
1470  }
1471 
1472  std::vector<DM> p_num;
1473  for (const auto& e : p) {
1474  casadi_int i = meta(e).i;
1475  p_num.push_back(store_initial_.at(OPTI_PAR).at(i));
1476  override_num(temp[OPTI_PAR], p_num, i);
1477  casadi_assert(p_num.back().is_regular(),
1478  "This expression depends on a parameter with unset value:\n"+
1479  describe(e, 1));
1480  }
1481 
1482  if (undecided_vars) {
1483  assert_solved();
1484  for (const auto& e : x)
1485  casadi_assert(symbol_active_[meta(e).count],
1486  "This expression has symbols that do not appear in the constraints and objective:\n" +
1487  describe(e, 1));
1488  for (const auto& e : lam)
1489  casadi_assert(symbol_active_[meta(e).count],
1490  "This expression has a dual for a constraint that is not given to Opti:\n" +
1491  describe(e, 1));
1492  }
1493 
1494  std::vector<DM> arg = helper(std::vector<DM>{veccat(x_num), veccat(p_num), veccat(lam_num)});
1495  return arg[0];
1496 }
1497 
1498 void OptiNode::assert_active_symbol(const MX& m) const {
1499  assert_has(m);
1500  assert_baked();
1501  casadi_assert(symbol_active_[meta(m).count], "Opti symbol is not used in Solver."
1502  " It does not make sense to assign a value to it:\n" + describe(m, 1));
1503 }
1504 
1505 void OptiNode::set_initial(const std::vector<MX>& assignments) {
1506  for (const auto& v : assignments) {
1507  casadi_assert_dev(v.is_op(OP_EQ));
1508  casadi_assert_dev(v.dep(0).is_constant());
1509  if (has(v.dep(1)))
1510  set_initial(v.dep(1), static_cast<DM>(v.dep(0)));
1511  }
1512 }
1513 
1514 void OptiNode::set_domain(const MX& x, const std::string& domain) {
1515  mark_solved(false);
1517  casadi_assert(x.is_valid_input(), "First argument to set_domain should be a variable.");
1518  DomainType type;
1519  if (domain=="real") {
1520  type = OPTI_DOMAIN_REAL;
1521  } else if (domain=="integer") {
1522  type = OPTI_DOMAIN_INTEGER;
1523  } else {
1524  casadi_error("Unknown domain '" + domain + "'. Known values are 'real', 'integer'.");
1525  }
1526  for (const auto& prim : x.primitives()) {
1527  MetaVar& m = meta(prim);
1528  m.domain = type;
1529  }
1530 }
1531 
1532 void OptiNode::set_value(const std::vector<MX>& assignments) {
1533  for (const auto& v : assignments) {
1534  casadi_assert_dev(v.is_op(OP_EQ));
1535  casadi_assert_dev(v.dep(0).is_constant());
1536  if (has(v.dep(1)))
1537  set_value(v.dep(1), static_cast<DM>(v.dep(0)));
1538  }
1539 }
1540 
1541 void OptiNode::set_value_internal(const MX& x, const DM& v,
1542  std::map< VariableType, std::vector<DM> >& store) {
1543  mark_solved(false);
1544  casadi_assert_dev(v.is_regular());
1545  if (x.is_symbolic()) {
1546  DM& target = store[meta(x).type][meta(x).i];
1547  Slice all;
1548  target.set(v, false, all, all);
1549  return;
1550  }
1551 
1552  // Obtain symbolic primitives
1553  std::vector<MX> symbols = MX::symvar(x);
1554  MX symbols_cat = veccat(symbols);
1555 
1556  std::string failmessage = "You cannot set initial/value of an arbitrary expression. "
1557  "Use symbols or simple mappings of symbols.";
1558 
1559  // Assert x is linear in its symbolic primitives
1560  for (bool b : which_depends(x, symbols_cat, 2, false)) casadi_assert(!b, failmessage);
1561 
1562  // Evaluate jacobian of expr wrt symbols
1563  Dict opts = {{"compact", true}};
1564  Function Jf("Jf", std::vector<MX>{}, std::vector<MX>{jacobian(x, veccat(symbols), opts)});
1565  DM J = Jf(std::vector<DM>{})[0];
1566  Sparsity sp_JT = J.T().sparsity();
1567 
1568  Function Ff("Ff", symbols, {x});
1569  DM E = Ff(std::vector<DM>(symbols.size(), 0))[0];
1570  std::vector<double>& e = E.nonzeros();
1571 
1572  // Cast the v input into the expected sparsity
1573  Slice all;
1574  DM value(x.sparsity());
1575  value.set(v, false, all, all);
1576 
1577  // Purge empty rows
1578  std::vector<casadi_int> filled_rows = sum2(J).get_row();
1579  J = J(filled_rows, all); // NOLINT(cppcoreguidelines-slicing)
1580 
1581  // Get rows and columns of the mapping
1582  std::vector<casadi_int> row, col;
1583  J.sparsity().get_triplet(row, col);
1584  const std::vector<double>& scaling = J.nonzeros();
1585  const std::vector<double>& data_original = value.nonzeros();
1586 
1587  std::vector<double> data; data.reserve(value.nnz());
1588  for (casadi_int i=0;i<value.nnz();++i) {
1589  double v = data_original[i];
1590  casadi_int nz = sp_JT.colind()[i+1]-sp_JT.colind()[i];
1591  casadi_assert(nz<=1, failmessage);
1592  if (nz) {
1593  data.push_back(v);
1594  } else {
1595  casadi_assert(v==e[i], "In initial/value assignment: "
1596  "inconsistent numerical values. At nonzero " + str(i) + ", lhs has "
1597  + str(e[i]) + ", while rhs has " + str(v) + ".");
1598  }
1599  }
1600 
1601  // Contiguous workspace for nonzeros of all involved symbols
1602  std::vector<double> temp(symbols_cat.nnz(), casadi::nan);
1603  for (casadi_int k=0;k<data.size();++k) {
1604  double& lhs = temp[col[k]];
1605  double rhs = data[row[k]]/scaling[row[k]];
1606  if (std::isnan(lhs)) {
1607  // Assign in the workspace
1608  lhs = rhs;
1609  } else {
1610  casadi_assert(lhs==rhs, "Initial/value assignment with mapping is ambiguous.");
1611  }
1612  }
1613 
1614  casadi_int offset = 0;
1615  for (const auto & s : symbols) {
1616  DM& target = store[meta(s).type][meta(s).i];
1617  std::vector<double>& data = target.nonzeros();
1618  // Loop over nonzeros in each symbol
1619  for (casadi_int i=0;i<s.nnz();++i) {
1620  // Copy from the workspace (barring fields that were not set)
1621  double v = temp[offset+i];
1622  if (!std::isnan(v)) data[i] = v;
1623  }
1624  offset+=s.nnz();
1625  }
1626 
1627 }
1628 
1629 void OptiNode::set_initial(const MX& x, const DM& v) {
1630  for (const auto & s : MX::symvar(x))
1631  casadi_assert(meta(s).type!=OPTI_PAR,
1632  "You cannot set an initial value for a parameter. Did you mean 'set_value'?");
1633  set_value_internal(x, v, store_initial_);
1634 }
1635 
1636 void OptiNode::set_value(const MX& x, const DM& v) {
1637  for (const auto & s : MX::symvar(x))
1638  casadi_assert(meta(s).type!=OPTI_VAR,
1639  "You cannot set a value for a variable. Did you mean 'set_initial'?");
1640  set_value_internal(x, v, store_initial_);
1641 }
1642 
1643 void OptiNode::set_linear_scale(const MX& x, const DM& scale, const DM& offset) {
1644  for (const auto & s : MX::symvar(x))
1645  casadi_assert(meta(s).type!=OPTI_PAR,
1646  "You cannot set a scale value for a parameter.");
1647  casadi_assert(scale.is_scalar() || scale.size()==x.size(),
1648  "Dimension mismatch in linear_scale. Expected " + x.dim() + ", got " + scale.dim()+ ".");
1649  set_value_internal(x, scale, store_linear_scale_);
1650  casadi_assert(offset.is_scalar() || offset.size()==x.size(),
1651  "Dimension mismatch in linear_scale offset. Expected " + x.dim() +
1652  ", got " + scale.dim()+ ".");
1653  set_value_internal(x, offset, store_linear_scale_offset_);
1654 }
1655 
1656 std::vector<MX> OptiNode::active_symvar(VariableType type) const {
1657  if (symbol_active_.empty()) return std::vector<MX>{};
1658  std::vector<MX> ret;
1659  for (const auto& s : symbols_) {
1660  if (symbol_active_[meta(s).count] && meta(s).type==type)
1661  ret.push_back(s);
1662  }
1663  return ret;
1664 }
1665 
1666 std::vector<MX> OptiNode::symvar(VariableType type) const {
1667  if (symbols_.empty()) return std::vector<MX>{};
1668  std::vector<MX> ret;
1669  for (const auto& s : symbols_) {
1670  if (meta(s).type==type)
1671  ret.push_back(s);
1672  }
1673  return ret;
1674 }
1675 
1676 std::vector<DM> OptiNode::active_values(VariableType type) const {
1677  return active_values(type, store_initial_);
1678 }
1679 
1681  const std::map< VariableType, std::vector<DM> >& store) const {
1682  if (symbol_active_.empty()) return std::vector<DM>{};
1683  std::vector<DM> ret;
1684  for (const auto& s : symbols_) {
1685  if (symbol_active_[meta(s).count] && meta(s).type==type) {
1686  ret.push_back(store.at(meta(s).type)[meta(s).i]);
1687  }
1688  }
1689  return ret;
1690 }
1691 
1692 Function OptiNode::to_function(const std::string& name,
1693  const std::vector<MX>& args, const std::vector<MX>& res,
1694  const std::vector<std::string>& name_in,
1695  const std::vector<std::string>& name_out,
1696  const Dict& opts) {
1697  if (problem_dirty()) return baked_copy().to_function(name, args, res, name_in, name_out, opts);
1698 
1699  Function solver = solver_construct(false);
1700 
1701  // Get initial guess and parameter values
1702  std::vector<MX> x0, p, lam_g;
1706 
1707  casadi_int k = 0;
1708  for (const auto& a : args) {
1709  casadi_assert(a.is_valid_input(), "Argument " + str(k) + " is not purely symbolic.");
1710  k++;
1711  for (const auto& prim : a.primitives()) {
1712  if (!symbol_active_[meta(prim).count]) continue;
1713  casadi_int i = meta(prim).active_i;
1714  if (meta(prim).type==OPTI_VAR) {
1715  x0.at(i) = prim;
1716  } else if (meta(prim).type==OPTI_PAR) {
1717  p.at(i) = prim;
1718  } else if (meta(prim).type==OPTI_DUAL_G) {
1719  lam_g.at(i) = prim;
1720  } else {
1721  casadi_error("Unknown");
1722  }
1723  }
1724  }
1725  MXDict arg;
1726  arg["p"] = veccat(p);
1727 
1728  // Evaluate bounds for given parameter values
1729  MXDict r = bounds_(arg);
1730  arg["x0"] = veccat(x0);
1731  arg["lam_g0"] = veccat(lam_g);
1732  arg["lbg"] = r["lbg"];
1733  arg["ubg"] = r["ubg"];
1734 
1735  r = solver(arg);
1736 
1737  std::vector<MX> helper_in = {veccat(active_symvar(OPTI_VAR)),
1738  veccat(active_symvar(OPTI_PAR)),
1739  veccat(active_symvar(OPTI_DUAL_G))};
1740  Dict h_opts;
1741  Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1742  Function helper("helper", helper_in, {res}, h_opts);
1743 
1744  std::vector<MX> arg_in = helper(std::vector<MX>{r.at("x"), arg["p"], r.at("lam_g")});
1745 
1746  return Function(name, args, arg_in, name_in, name_out, opts_remainder);
1747 
1748 }
1749 
1750 void OptiNode::disp(std::ostream &stream, bool more) const {
1751 
1752 }
1753 
1754 casadi_int OptiNode::instance_number() const {
1755  return instance_number_;
1756 }
1757 
1758 void OptiNode::show_infeasibilities(double tol, const Dict& opts) const {
1759  stats();
1760  std::vector<double> lbg_ = value(lbg()).get_elements();
1761  std::vector<double> ubg_ = value(ubg()).get_elements();
1762  std::vector<double> g_scaled_ = value(nlp_.at("g"), std::vector<MX>(), true).get_elements();
1763  std::vector<double> lbg_scaled_ = value(bounds_lbg_, std::vector<MX>(), true).get_elements();
1764  std::vector<double> ubg_scaled_ = value(bounds_ubg_, std::vector<MX>(), true).get_elements();
1765 
1766 
1767  std::vector<double> g_ = value(g()).get_elements();
1768  uout() << "Violated constraints (tol " << tol << "), in order of declaration:" << std::endl;
1769 
1770  for (casadi_int i=0;i<g_.size();++i) {
1771  double err = std::max(g_[i]-ubg_[i], lbg_[i]-g_[i]);
1772  double err_scaled = std::max(g_scaled_[i]-ubg_scaled_[i], lbg_scaled_[i]-g_scaled_[i]);
1773  if (err>=tol) {
1774  uout() << "------- i = " << i+GlobalOptions::start_index;
1775  uout() << "/" << g_.size();
1776 
1777  if (reduced_) {
1778  if (is_simple_[i]) {
1779  if (GlobalOptions::start_index==0) {
1780  uout() << " reduced to bound on x[" << g_index_reduce_x_.at(i) << "]";
1781  } else {
1782  uout() << " reduced to bound on x(" << g_index_reduce_x_.at(i)+1 << ")";
1783  }
1784  } else {
1785  uout() << " reduced to g[" << g_index_reduce_g_.at(i) << "]";
1786  }
1787  }
1788 
1789  uout() << " ------ " << std::endl;
1790  uout() << lbg_[i] << " <= " << g_[i] << " <= " << ubg_[i];
1791  uout() << " (viol " << err << ")" << std::endl;
1792  if (g_[i]!=g_scaled_[i]) {
1793  uout() << lbg_scaled_[i] << " <= " << g_scaled_[i] << " <= " << ubg_scaled_[i];
1794  uout() << " (scaled) (viol " << err_scaled << ")" << std::endl;
1795  }
1796  uout() << g_describe(i, opts) << std::endl;
1797  }
1798  }
1799 }
1800 
1801 } // namespace casadi
static std::unique_ptr< std::istream > ifstream_ptr(const std::string &path, std::ios_base::openmode mode=std::ios_base::in, bool fail=true)
Definition: filesystem.cpp:135
FunctionInternal(const std::string &name)
Constructor.
Function object.
Definition: function.hpp:126
FunctionInternal * get() const
Definition: function.cpp:505
const std::vector< std::string > & name_in() const
Get the names of all inputs, in order.
Definition: function.cpp:1113
const std::string & name() const
Get the name given at construction ("null" for a null object)
Definition: function.cpp:1504
static Function create(FunctionInternal *node)
Create from node.
Definition: function.cpp:488
Dict stats(int mem=0) const
Get the statistics recorded by the last numerical evaluation.
Definition: function.cpp:1080
const std::vector< std::string > & name_out() const
Get the names of all outputs, in order.
Definition: function.cpp:1117
casadi_int numel() const
Get the number of elements, size1()*size2()
bool is_dense() const
Check if the matrix is dense, i.e. nnz() equals numel()
bool is_empty(bool both=false) const
Check if the matrix is empty, i.e. if one of the dimensions is zero.
std::pair< casadi_int, casadi_int > size() const
Get the shape as a pair (number of rows, number of columns)
bool is_vector() const
Check if the matrix is a row or column vector.
casadi_int nnz() const
Get the number of structural nonzeros.
casadi_int size2() const
Get the second dimension (i.e. number of columns)
casadi_int size1() const
Get the first dimension (i.e. number of rows)
std::string dim(bool with_nz=false) const
Get string representation of dimensions.
static MatType ones(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries one.
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
const casadi_int * colind() const
Get the sparsity pattern. See the Sparsity class for details.
static MatType zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
bool is_scalar(bool scalar_and_dense=false) const
Check if the matrix is scalar, i.e. 1-by-1.
bool is_null() const
Is a null pointer?
static casadi_int start_index
MX - Matrix expression.
Definition: mx.hpp:145
bool is_valid_input() const
Check if the expression can be used as a Function input.
Definition: mx.cpp:957
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:615
std::string name() const
Get the name of a symbolic primitive.
Definition: mx.cpp:794
static std::vector< MX > symvar(const MX &x)
Definition: mx.cpp:2034
bool is_constant() const
Check if the node is a constant.
Definition: mx.cpp:802
static MX eye(casadi_int n)
Identity matrix constant of size n-by-n, with a diagonal sparsity pattern.
Definition: mx.cpp:603
MXNode * get() const
Get a const pointer to the node.
Definition: mx.cpp:567
bool is_op(casadi_int op) const
Check if the node is a certain operation.
Definition: mx.cpp:826
std::vector< MX > primitives() const
Get the symbolic primitives of a valid Function input.
Definition: mx.cpp:965
MX dep(casadi_int ch=0) const
Get the ch-th dependency (child node) of this node.
Definition: mx.cpp:786
bool is_symbolic() const
Check if the node is a symbolic primitive (created by MX.sym)
Definition: mx.cpp:798
std::vector< Scalar > & nonzeros()
Matrix< Scalar > T() const
Transpose the matrix.
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
void set(const Matrix< Scalar > &m, bool ind1, const Slice &rr)
Assign elements by a single (linear) index.
static Matrix< double > nan(const Sparsity &sp)
Create a matrix with every nonzero equal to nan.
bool is_regular() const
Check that no entry is nan, inf or -inf.
std::vector< Scalar > get_elements() const
Get all elements as a flat list in column-major order (Python: elements())
MX x_lookup(casadi_index i) const
Get the variable that owns entry i of the flattened x vector.
Definition: optistack.cpp:609
std::string describe(const MX &x, casadi_index indent=0, const Dict &opts=Dict()) const
Describe an expression in terms of this instance.
Definition: optistack.cpp:663
MX g_lookup(casadi_index i) const
Get the constraint expression that owns row i of g.
Definition: optistack.cpp:641
std::string x_describe(casadi_index i, const Dict &opts=Dict()) const
Describe entry i of the flattened x vector.
Definition: optistack.cpp:649
std::string g_describe(casadi_index i, const Dict &opts=Dict()) const
Describe row i of the constraint vector g.
Definition: optistack.cpp:656
Base class of iteration callbacks for Opti.
Definition: optistack.hpp:2764
A simplified interface for NLP modeling/solving.
Function solver_construct(bool callback=true)
Dict user_dict(const MX &m) const
casadi_int g_index_reduce_x(casadi_int i) const
std::vector< MX > value_variables() const
get assignment expressions for latest values
std::vector< MX > initial() const
get assignment expressions for initial values
std::vector< MX > active_symvar(VariableType type) const
std::string describe(const MX &x, casadi_int indent=0, const Dict &opts=Dict()) const
MX g() const
Get all (scalarised) constraint expressions as a column vector.
MetaCon get_meta_con(const MX &m) const
Get meta-data of symbol (for internal use only)
OptiAdvanced baked_copy() const
MX dual(const MX &m) const
get the dual variable
std::vector< DM > active_values(VariableType type) const
MX x() const
Get all (scalarised) decision variables as a symbolic column vector.
void minimize(const MX &f, double linear_scale=1)
Set objective.
casadi_int nx() const
Number of (scalarised) decision variables.
MX variable(casadi_int n=1, casadi_int m=1, const std::string &attribute="full")
Create a decision variable (symbol)
std::string x_describe(casadi_int i, const Dict &opts=Dict()) const
void bake()
Fix the structure of the optimization problem.
casadi_int instance_number() const
DM value(const MX &x, const std::vector< MX > &values=std::vector< MX >(), bool scaled=false) const
void disp(std::ostream &stream, bool more=false) const override
Print representation.
void set_domain(const MX &x, const std::string &domain)
Set domain of variable.
OptiSol solve(bool accept_limit)
Crunch the numbers; solve the problem.
casadi_int np() const
Number of (scalarised) parameters.
bool has_callback_class() const
MX g_lookup(casadi_int i) const
casadi_int ng() const
Number of (scalarised) constraints.
MetaVar get_meta(const MX &m) const
Get meta-data of symbol (for internal use only)
void subject_to()
Clear constraints.
std::vector< MX > symvar() const
void update_user_dict(const MX &m, const Dict &meta)
add meta-data of an expression
void set_meta(const MX &m, const MetaVar &meta)
Set meta-data of an expression.
MX parameter(casadi_int n=1, casadi_int m=1, const std::string &attribute="full")
Create a parameter (symbol); fixed during optimization.
bool is_parametric(const MX &expr) const
return true if expression is only dependant on Opti parameters, not variables
void assert_active_symbol(const MX &m) const
bool return_success(bool accept_limit) const
Did the solver return successfully?
std::vector< MX > value_parameters() const
bool problem_dirty() const
Function to_function(const std::string &name, const std::vector< MX > &args, const std::vector< MX > &res, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out, const Dict &opts)
Create a CasADi Function from the Opti solver.
Opti copy() const
Copy.
DM x_linear_scale_offset() const
MX x_lookup(casadi_int i) const
casadi_int g_index_reduce_g(casadi_int i) const
void mark_problem_dirty(bool flag=true)
OptiNode(const std::string &problem_type)
Create Opti Context.
std::string return_status() const
Get return status of solver.
void set_initial(const MX &x, const DM &v)
void mark_solver_dirty(bool flag=true)
MX p() const
Get all (scalarised) parameters as a symbolic column vector.
casadi_int g_index_unreduce_g(casadi_int i) const
void set_value(const MX &x, const DM &v)
Set value of parameter.
double f_linear_scale() const
MX lam_g() const
Get dual variables as a symbolic column vector.
void set_linear_scale(const MX &x, const DM &scale, const DM &offset)
Set scale of a decision variable.
std::string g_describe(casadi_int i, const Dict &opts=Dict()) const
friend class InternalOptiCallback
void solver(const std::string &solver, const Dict &plugin_options=Dict(), const Dict &solver_options=Dict())
Solver.
MX f() const
Get objective expression.
void mark_solved(bool flag=true)
Function scale_helper(const Function &h) const
Scale a helper function constructed via opti.x, opti.g, ...
Function casadi_solver() const
Get the underlying CasADi solver of the Opti stack.
static OptiNode * create(const std::string &problem_type)
MetaCon canon_expr(const MX &expr, const DM &linear_scale=1) const
Interpret an expression (for internal use only)
void show_infeasibilities(double tol=0, const Dict &opts=Dict()) const
DMDict solve_actual(const DMDict &args)
Dict stats() const
Get statistics.
void set_meta_con(const MX &m, const MetaCon &meta)
Set meta-data of an expression.
Snapshot of an Opti problem and its solution.
Definition: optistack.hpp:3563
A simplified interface for NLP modeling/solving.
Definition: optistack.hpp:236
Function scale_helper(const Function &h) const
Scale a helper function constructed via opti.x, opti.g, ...
Definition: optistack.cpp:254
Function to_function(const std::string &name, const std::vector< MX > &args, const std::vector< MX > &res, const Dict &opts=Dict())
Create a CasADi Function from the Opti solver.
Definition: optistack.cpp:435
static Opti create(OptiNode *node)
Definition: optistack.cpp:86
void clear_mem()
Clear all memory (called from destructor)
Class representing a Slice.
Definition: slice.hpp:48
General sparsity class.
Definition: sparsity.hpp:200
casadi_int size1() const
Number of rows.
Definition: sparsity.cpp:124
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern.
Definition: sparsity.cpp:1041
casadi_int nnz() const
Number of structural nonzeros.
Definition: sparsity.cpp:150
casadi_int size2() const
Number of columns.
Definition: sparsity.cpp:128
static Sparsity lower(casadi_int n)
Create a lower triangular square sparsity pattern.
Definition: sparsity.cpp:1078
Function qpsol(const std::string &name, const std::string &solver, const SXDict &qp, const Dict &opts)
Definition: conic.cpp:261
Function nlpsol(const std::string &name, const std::string &solver, const SXDict &nlp, const Dict &opts)
Definition: nlpsol.cpp:196
casadi_int nlpsol_n_out()
Number of NLP solver outputs.
Definition: nlpsol.cpp:347
std::vector< std::string > nlpsol_out()
Get the output scheme of NLP solvers.
Definition: nlpsol.cpp:290
The casadi namespace.
Definition: archiver.cpp:28
std::map< std::string, MX > MXDict
Definition: mx.hpp:5151
bool override_num(const std::map< casadi_int, MX > &temp, std::vector< DM > &num, casadi_int i)
T get_from_dict(const std::map< std::string, T > &d, const std::string &key, const T &default_value)
std::string description(Category v)
double if_else_zero(double x, double y)
Conditional assignment.
Definition: calculus.hpp:295
void assign_vector(const std::vector< S > &s, std::vector< D > &d)
VariableType
Kind of an Opti symbol (MetaVar::type)
Definition: optistack.hpp:2711
@ OPTI_DUAL_G
Definition: optistack.hpp:2714
double sign(double x)
Sign function, note that sign(nan) == nan.
Definition: calculus.hpp:270
std::vector< casadi_int > find(const std::vector< T > &v)
find nonzeros
DomainType
Domain of a decision variable (see Opti::set_domain)
Definition: optistack.hpp:2717
@ OPTI_DOMAIN_INTEGER
Definition: optistack.hpp:2719
@ OPTI_DOMAIN_REAL
Definition: optistack.hpp:2718
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const double inf
infinity
Definition: calculus.hpp:50
bool any(const std::vector< bool > &v)
Check if any arguments are true.
Definition: casadi_misc.cpp:88
@ OPTI_DOUBLE_INEQUALITY
Definition: optistack.hpp:2707
@ OPTI_INEQUALITY
Definition: optistack.hpp:2706
@ OPTI_GENERIC_INEQUALITY
Definition: optistack.hpp:2704
@ OPTI_EQUALITY
Definition: optistack.hpp:2705
@ OPTI_GENERIC_EQUALITY
Definition: optistack.hpp:2703
@ OPTI_UNKNOWN
Definition: optistack.hpp:2709
const double nan
Not a number.
Definition: calculus.hpp:53
bool all(const std::vector< bool > &v)
Check if all arguments are true.
Definition: casadi_misc.cpp:81
Matrix< double > DM
Definition: dm_fwd.hpp:33
bool is_regular(const std::vector< T > &v)
Checks if array does not contain NaN or Inf.
Dict extract_from_dict(const Dict &d, const std::string &key, T &value)
std::ostream & uout()
std::map< std::string, DM > DMDict
Definition: dm_fwd.hpp:36
@ OP_LT
Definition: calculus.hpp:70
@ OP_EQ
Definition: calculus.hpp:70
@ OP_NORM2
Definition: calculus.hpp:178
@ OP_LE
Definition: calculus.hpp:70
@ OP_NORMF
Definition: calculus.hpp:178
std::string filename(const std::string &path)
Definition: ghc.cpp:55
Metadata of a constraint, as returned by OptiAdvanced::canon_expr/get_meta_con.
Definition: optistack.hpp:2730
ConstraintType type
Definition: optistack.hpp:2734
casadi_int n
Definition: optistack.hpp:2737
Metadata of a symbol, as returned by OptiAdvanced::get_meta.
Definition: optistack.hpp:2745
casadi_int active_i
Definition: optistack.hpp:2753
std::string attribute
Definition: optistack.hpp:2746
DomainType domain
Definition: optistack.hpp:2750
VariableType type
Definition: optistack.hpp:2749
casadi_int count
Definition: optistack.hpp:2751
casadi_int i
Definition: optistack.hpp:2752
casadi_int m
Definition: optistack.hpp:2748
casadi_int n
Definition: optistack.hpp:2747