factory.hpp
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 #ifndef CASADI_FACTORY_HPP
27 #define CASADI_FACTORY_HPP
28 
29 #include <algorithm>
30 #include "function.hpp"
31 
33 
34 namespace casadi {
35 
36  // A Jacobian or gradient block
37  struct Block {
38  size_t f, x;
39  std::string s;
40  bool calculated;
41  };
42 
43  // A Hessian block
44  struct HBlock {
45  size_t f, x1, x2;
46  std::string s;
47  bool calculated;
48  };
49 
50  // Helper class for generating new functions
51  template<typename MatType>
52  class Factory {
53  public:
54 
55  // All input and output expressions
56  std::vector<MatType> in_, out_;
57 
58  // Names of inputs and outputs
59  std::vector<std::string> iname_, oname_;
60 
61  // All input and output expressions created so far
62  std::map<std::string, size_t> imap_, omap_;
63  std::vector<bool> is_diff_in_, is_diff_out_;
64 
65  // Forward mode directional derivatives
66  std::vector<size_t> fwd_in_, fwd_out_;
67 
68  // Reverse mode directional derivatives
69  std::vector<size_t> adj_in_, adj_out_;
70 
71  // Jacobian/gradient blocks
72  std::vector<Block> jac_, grad_;
73 
74  // Hessian blocks
75  std::vector<HBlock> hess_;
76 
77  // Read a Jacobian or gradient block
78  Block block(const std::string& s1, const std::string& s) const;
79 
80  // Read a Hessian block
81  HBlock hblock(const std::string& s1, const std::string& s) const;
82 
83  // Add an input expression
84  void add_input(const std::string& s, const MatType& e, bool is_diff);
85 
86  // Add an output expression
87  void add_output(const std::string& s, const MatType& e, bool is_diff);
88 
89  // Add the dual variables
90  void add_dual(const Function::AuxOut& aux);
91 
92  // Request a factory input
93  std::string request_input(const std::string& s);
94 
95  // Request a factory output
96  std::string request_output(const std::string& s);
97 
98  // Calculate forward mode directional derivatives
99  void calculate_fwd(const Dict& opts);
100 
101  // Calculate reverse mode directional derivatives
102  void calculate_adj(const Dict& opts);
103 
104  // Find a Jacobian block
105  std::vector<Block>::iterator find_jac(size_t f, size_t x);
106 
107  // Find a Hessian block
108  std::vector<HBlock>::iterator find_hess(size_t f, size_t x1, size_t x2);
109 
110  // Calculate Jacobian blocks
111  void calculate_jac(const Dict& opts);
112 
113  // Calculate gradient blocks
114  void calculate_grad(const Dict& opts);
115 
116  // Calculate Hessian blocks, one expression
117  void calculate_hess(const Dict& opts, size_t f);
118 
119  // Calculate Hessian blocks
120  void calculate_hess(const Dict& opts);
121 
122  // Calculate requested outputs
123  void calculate(const Dict& opts = Dict());
124 
125  // Get input index from string
126  size_t imap(const std::string& s) const;
127 
128  // Get output index from string
129  size_t omap(const std::string& s) const;
130 
131  // Retrieve an input
132  MatType get_input(const std::string& s);
133 
134  // Retrieve an output
135  MatType get_output(const std::string& s);
136 
137  // Helper function
138  static bool has_prefix(const std::string& s);
139 
140  // Split prefix
141  static std::pair<std::string, std::string> split_prefix(const std::string& s);
142 
143  // Check if input exists
144  bool has_in(const std::string& s) const { return imap_.find(s)!=imap_.end();}
145 
146  // Check if out exists
147  bool has_out(const std::string& s) const { return omap_.find(s) != omap_.end();}
148 
149  // Get input scheme
150  const std::vector<std::string>& iname() const {return iname_;}
151  std::vector<std::string> iname(const std::vector<size_t>& ind) const;
152 
153  // Get output scheme
154  const std::vector<std::string>& oname() const {return oname_;}
155  std::vector<std::string> oname(const std::vector<size_t>& ind) const;
156  };
157 
158  template<typename MatType>
159  void Factory<MatType>::
160  add_input(const std::string& s, const MatType& e, bool is_diff) {
161  size_t ind = in_.size();
162  auto it = imap_.insert(std::make_pair(s, ind));
163  casadi_assert(it.second, "Duplicate input expression \"" + s + "\"");
164  is_diff_in_.push_back(is_diff);
165  in_.push_back(e);
166  iname_.push_back(s);
167  }
168 
169  template<typename MatType>
170  void Factory<MatType>::
171  add_output(const std::string& s, const MatType& e, bool is_diff) {
172  size_t ind = out_.size();
173  auto it = omap_.insert(std::make_pair(s, ind));
174  casadi_assert(it.second, "Duplicate output expression \"" + s + "\"");
175  is_diff_out_.push_back(is_diff);
176  out_.push_back(e);
177  oname_.push_back(s);
178  }
179 
180  template<typename MatType>
181  std::string Factory<MatType>::
182  request_input(const std::string& s) {
183  // Add input if not already available
184  if (!has_in(s)) {
185  // Get prefix
186  casadi_assert(has_prefix(s), "Cannot process \"" + s + "\" as input."
187  " Available: " + join(iname()) + ".");
188  std::pair<std::string, std::string> ss = split_prefix(s);
189  // Process specific prefixes
190  if (ss.first=="fwd") {
191  // Forward mode directional derivative
192  fwd_in_.push_back(imap(ss.second));
193  } else if (ss.first=="adj") {
194  // Reverse mode directional derivative
195  adj_in_.push_back(omap(ss.second));
196  }
197  }
198  // Replace colons with underscore
199  std::string ret = s;
200  std::replace(ret.begin(), ret.end(), ':', '_');
201  return ret;
202  }
203 
204  template<typename MatType>
205  std::string Factory<MatType>::
206  request_output(const std::string& s) {
207  // Quick return if already available
208  if (has_out(s)) return s;
209 
210  // Get prefix
211  casadi_assert(has_prefix(s), "Cannot process \"" + s + "\" as output."
212  " Available: " + join(oname()) + ".");
213  std::pair<std::string, std::string> ss = split_prefix(s);
214 
215  if (ss.first=="fwd") {
216  fwd_out_.push_back(omap(ss.second));
217  } else if (ss.first=="adj") {
218  adj_out_.push_back(imap(ss.second));
219  } else if (ss.first=="jac") {
220  jac_.push_back(block(ss.second, s));
221  } else if (ss.first=="grad") {
222  grad_.push_back(block(ss.second, s));
223  } else if (ss.first=="hess") {
224  hess_.push_back(hblock(ss.second, s));
225  } else {
226  // Assume attribute
227  request_output(ss.second);
228  }
229 
230  // Replace colons with underscore
231  std::string ret = s;
232  replace(ret.begin(), ret.end(), ':', '_');
233  return ret;
234  }
235 
236  template<typename MatType>
237  void Factory<MatType>::calculate_fwd(const Dict& opts) {
238  if (fwd_out_.empty()) return;
239  casadi_assert_dev(!fwd_in_.empty());
240 
241  std::vector<MatType> arg, res;
242  std::vector<std::vector<MatType>> seed(1), sens(1);
243  // Inputs and forward mode seeds
244  for (size_t iind : fwd_in_) {
245  arg.push_back(in_[iind]);
246  Sparsity sp = is_diff_in_.at(iind) ? arg.back().sparsity() : Sparsity(arg.back().size());
247  seed[0].push_back(MatType::sym("fwd_" + iname_[iind], sp));
248  add_input("fwd:" + iname_[iind], seed[0].back(), true);
249  }
250  // Outputs
251  for (size_t oind : fwd_out_) res.push_back(out_.at(oind));
252  // Calculate directional derivatives
253  Dict local_opts = opts;
254  local_opts["always_inline"] = true;
255  sens = forward(res, arg, seed, local_opts);
256 
257  // Get directional derivatives
258  for (size_t i = 0; i < fwd_out_.size(); ++i) {
259  std::string s = oname_.at(fwd_out_[i]);
260  Sparsity sp = is_diff_out_.at(fwd_out_[i]) ? res.at(i).sparsity()
261  : Sparsity(res.at(i).size());
262  add_output("fwd:" + s, project(sens[0].at(i), sp), is_diff_out_.at(fwd_out_[i]));
263  }
264  }
265 
266  template<typename MatType>
267  void Factory<MatType>::calculate_adj(const Dict& opts) {
268  if (adj_out_.empty()) return;
269  casadi_assert_dev(!adj_in_.empty());
270  std::vector<MatType> arg, res;
271  std::vector<std::vector<MatType>> seed(1), sens(1);
272  // Inputs
273  for (size_t ind : adj_out_) arg.push_back(in_[ind]);
274  // Outputs and reverse mode seeds
275  for (size_t ind : adj_in_) {
276  res.push_back(out_.at(ind));
277  Sparsity sp = is_diff_out_.at(ind) ? res.back().sparsity() : Sparsity(res.back().size());
278  seed[0].push_back(MatType::sym("adj_" + oname_[ind], sp));
279  add_input("adj:" + oname_[ind], seed[0].back(), true);
280  }
281  // Calculate directional derivatives
282  Dict local_opts;
283  local_opts["always_inline"] = true;
284  sens = reverse(res, arg, seed, local_opts);
285 
286  // Get directional derivatives
287  for (size_t i=0; i < adj_out_.size(); ++i) {
288  std::string s = iname_[adj_out_[i]];
289  Sparsity sp = is_diff_in_.at(adj_out_[i]) ? arg.at(i).sparsity() : Sparsity(arg.at(i).size());
290  add_output("adj:" + s, project(sens[0].at(i), sp), is_diff_in_.at(adj_out_[i]));
291  }
292  }
293 
294  template<typename MatType>
295  std::vector<Block>::iterator Factory<MatType>::find_jac(size_t f, size_t x) {
296  for (std::vector<Block>::iterator it = jac_.begin(); it != jac_.end(); ++it) {
297  if (it->f == f && it->x == x) return it;
298  }
299  // Not in list
300  return jac_.end();
301  }
302 
303  template<typename MatType>
304  std::vector<HBlock>::iterator Factory<MatType>::find_hess(size_t f, size_t x1, size_t x2) {
305  for (std::vector<HBlock>::iterator it = hess_.begin(); it != hess_.end(); ++it) {
306  if (it->f == f && it->x1 == x1 && it->x2 == x2) return it;
307  }
308  // Not in list
309  return hess_.end();
310  }
311 
312  template<typename MatType>
313  void Factory<MatType>::calculate_jac(const Dict& opts) {
314  // Calculate blocks for all non-differentiable inputs and outputs
315  for (auto &&b : jac_) {
316  if (is_diff_out_.at(b.f) && is_diff_in_.at(b.x)) {
317  b.calculated = false;
318  } else {
319  add_output(b.s, MatType(out_[b.f].numel(), in_[b.x].numel()), false);
320  b.calculated = true;
321  }
322  }
323  // Calculate regular blocks
324  for (auto &&b : jac_) {
325  // Skip if already calculated
326  if (b.calculated) continue;
327  // Find other blocks with the same input, but different (not yet calculated) outputs
328  std::vector<size_t> all_f;
329  for (auto &&b1 : jac_) {
330  if (b1.x == b.x && !b1.calculated) all_f.push_back(b1.f);
331  }
332  // Now find other blocks with *all* the same outputs, but different inputs
333  std::vector<size_t> all_x{b.x};
334  for (auto &&b1 : jac_) {
335  // Candidate b1.arg: Check if already added
336  if (std::count(all_x.begin(), all_x.end(), b1.x)) continue;
337  // Skip if all block are not requested or any block has already been calculated
338  bool skip = false;
339  for (size_t f1 : all_f) {
340  auto it = find_jac(f1, b1.x);
341  if (it == jac_.end() || it->calculated) {
342  skip = true;
343  break;
344  }
345  }
346  if (skip) continue;
347  // Keep candidate
348  all_x.push_back(b1.x);
349  }
350  try {
351  // Calculate Jacobian block(s)
352  if (all_f.size() == 1 && all_x.size() == 1) {
353  // Single block
354  add_output(b.s, MatType::jacobian(out_[b.f], in_[b.x], opts), true);
355  b.calculated = true;
356  } else {
357  // Sort blocks
358  std::sort(all_x.begin(), all_x.end());
359  std::sort(all_f.begin(), all_f.end());
360  // Collect components
361  std::vector<MatType> x(all_x.size()), f(all_f.size());
362  for (size_t i = 0; i < x.size(); ++i) x[i] = in_.at(all_x[i]);
363  for (size_t i = 0; i < f.size(); ++i) f[i] = out_.at(all_f[i]);
364  // Calculate Jacobian of all outputs with respect to all inputs
365  MatType J = MatType::jacobian(veccat(f), veccat(x), opts);
366  // Split Jacobian into blocks
367  std::vector<std::vector<MatType>> J_all = blocksplit(J, offset(f), offset(x));
368  // Save blocks
369  for (size_t i = 0; i < all_f.size(); ++i) {
370  for (size_t j = 0; j < all_x.size(); ++j) {
371  auto J_it = find_jac(all_f[i], all_x[j]);
372  if (J_it != jac_.end()) {
373  add_output(J_it->s, J_all.at(i).at(j), true);
374  J_it->calculated = true;
375  }
376  }
377  }
378  }
379  } catch (std::exception& e) {
380  std::stringstream ss;
381  ss << "Calculating Jacobian of " << oname(all_f) << " w.r.t. " << iname(all_x)
382  << ": " << e.what();
383  casadi_error(ss.str());
384  }
385  }
386  }
387 
388  template<typename MatType>
389  void Factory<MatType>::calculate_grad(const Dict& opts) {
390  for (auto &&b : grad_) {
391  const MatType& ex = out_.at(b.f);
392  const MatType& arg = in_[b.x];
393  if (is_diff_out_.at(b.f) && is_diff_in_.at(b.x)) {
394  add_output("grad:" + oname_[b.f] + ":" + iname_[b.x],
395  project(gradient(ex, arg, opts), arg.sparsity()), true);
396  } else {
397  casadi_assert(ex.is_scalar(), "Can only take gradient of scalar expression.");
398  add_output("grad:" + oname_[b.f] + ":" + iname_[b.x], MatType(1, arg.numel()), false);
399  }
400  }
401  }
402 
403  template<typename MatType>
404  void Factory<MatType>::calculate_hess(const Dict& opts, size_t f) {
405  // Handle all blocks for this expression
406  for (auto &&b : hess_) {
407  if (b.f != f) continue;
408  // Skip if already calculated
409  if (b.calculated) continue;
410  // Find other blocks with one of the arguments matching
411  std::vector<size_t> all_x1;
412  for (auto &&b1 : hess_) {
413  if (b1.f == b.f && !b1.calculated) {
414  if (b1.x1 == b.x1) {
415  // Block found
416  all_x1.push_back(b1.x2);
417  } else if (b1.x2 == b.x1) {
418  // Opposite block found
419  all_x1.push_back(b1.x1);
420  }
421  }
422  }
423  // Find other blocks with both of the arguments matching
424  std::vector<size_t> all_x2;
425  for (auto &&b1 : hess_) {
426  if (b1.f != f || b1.calculated) continue;
427  // Can either b1.x1 or b1.x2 be added to all_x2?
428  for (bool test_x1 : {false, true}) {
429  size_t cand = test_x1 ? b1.x1 : b1.x2;
430  size_t other = test_x1 ? b1.x2 : b1.x1;
431  bool cand_ok = true;
432  bool other_ok = false;
433  // Skip if already in all_x2
434  if (std::count(all_x2.begin(), all_x2.end(), cand)) continue;
435  // Loop over existing entries in x1
436  for (size_t a : all_x1) {
437  // The other argument must already be in all_x1
438  if (other == a) other_ok = true;
439  // Is block not requested?
440  auto it = find_hess(f, a, cand);
441  if (it == hess_.end() || it->calculated) {
442  // Also check mirror block, if there is one
443  if (a != cand) {
444  it = find_hess(f, cand, a);
445  if (it != hess_.end() && !it->calculated) continue;
446  }
447  // Not a candidate
448  cand_ok = false;
449  break;
450  }
451  }
452  // Keep candidate
453  if (cand_ok && other_ok) all_x2.push_back(cand);
454  }
455  }
456  // Calculate Hessian blocks
457  try {
458  if (all_x1.size() == 1 && all_x2.size() == 1) {
459  // Single block
460  MatType H = b.x1 == b.x2 ? hessian(out_.at(f), in_[b.x1], opts)
461  : jacobian(gradient(out_.at(f), in_[b.x1]), in_[b.x2]);
462  add_output(b.s, H, true);
463  b.calculated = true;
464  } else {
465  // Sort blocks
466  std::sort(all_x1.begin(), all_x1.end());
467  std::sort(all_x2.begin(), all_x2.end());
468  // Symmetric extended Hessian?
469  bool symmetric = all_x1 == all_x2;
470  // Collect components
471  std::vector<MatType> x1(all_x1.size()), x2(all_x2.size());
472  for (size_t i = 0; i < x1.size(); ++i) x1[i] = in_.at(all_x1[i]);
473  for (size_t i = 0; i < x2.size(); ++i) x2[i] = in_.at(all_x2[i]);
474  // Calculate extended Hessian
475  MatType H;
476  if (symmetric) {
477  H = hessian(out_.at(f), vertcat(x1));
478  } else {
479  H = jacobian(gradient(out_.at(f), vertcat(x1)), vertcat(x2));
480  }
481  // Split into blocks
482  std::vector<std::vector<MatType>> H_all = blocksplit(H, offset(x1), offset(x2));
483  // Collect Hessian blocks
484  for (auto &&b1 : hess_) {
485  if (b1.f == f && !b1.calculated) {
486  // Find arguments in all_x1 and all_x2
487  auto it_x1 = std::find(all_x1.begin(), all_x1.end(), b1.x1);
488  auto it_x2 = std::find(all_x2.begin(), all_x2.end(), b1.x2);
489  if (it_x1 != all_x1.end() && it_x2 != all_x2.end()) {
490  // Block located
491  const MatType& Hb = H_all.at(it_x1 - all_x1.begin()).at(it_x2 - all_x2.begin());
492  add_output(b1.s, Hb, true);
493  b1.calculated = true;
494  } else if (!symmetric) {
495  // Check mirror block
496  it_x1 = std::find(all_x1.begin(), all_x1.end(), b1.x2);
497  it_x2 = std::find(all_x2.begin(), all_x2.end(), b1.x1);
498  if (it_x1 != all_x1.end() && it_x2 != all_x2.end()) {
499  // Transpose of block located
500  const MatType& Hb = H_all.at(it_x1 - all_x1.begin()).at(it_x2 - all_x2.begin());
501  add_output(b1.s, Hb.T(), true);
502  b1.calculated = true;
503  }
504  }
505  }
506  }
507  }
508  } catch (std::exception& e) {
509  std::stringstream ss;
510  ss << "Calculating Hessian of " << oname_.at(f) << " w.r.t. " << iname(all_x1) << " and "
511  << iname(all_x2) << ": " << e.what();
512  casadi_error(ss.str());
513  }
514  }
515  }
516 
517  template<typename MatType>
518  void Factory<MatType>::calculate_hess(const Dict& opts) {
519  // Calculate blocks for all non-differentiable inputs and outputs
520  for (auto &&b : hess_) {
521  if (is_diff_out_.at(b.f) && is_diff_in_.at(b.x1) && is_diff_in_.at(b.x2)) {
522  b.calculated = false;
523  } else {
524  add_output(b.s, MatType(in_[b.x1].numel(), in_[b.x2].numel()), false);
525  b.calculated = true;
526  }
527  // Consistency check
528  casadi_assert(out_.at(b.f).is_scalar(),
529  "Can only take Hessian of scalar expression.");
530  }
531  // Calculate regular blocks
532  for (auto &&b : hess_) {
533  // Skip if already calculated
534  if (b.calculated) continue;
535  // Calculate all Hessian blocks for b.f
536  calculate_hess(opts, b.f);
537  }
538  }
539 
540  template<typename MatType>
541  void Factory<MatType>::add_dual(const Function::AuxOut& aux) {
542  // Dual variables
543  for (size_t k = 0; k < out_.size(); ++k) {
544  Sparsity sp = is_diff_out_[k] ? out_.at(k).sparsity() : Sparsity(out_.at(k).size());
545  add_input("lam:" + oname_[k], MatType::sym("lam_" + oname_[k], sp), true);
546  }
547  // Add linear combinations
548  for (auto i : aux) {
549  MatType lc = 0;
550  for (auto j : i.second) {
551  lc += dot(in_.at(imap_.at("lam:" + j)), out_.at(omap_.at(j)));
552  }
553  add_output(i.first, lc, true);
554  }
555  }
556 
557  template<typename MatType>
558  void Factory<MatType>::calculate(const Dict& opts) {
559  // Forward mode directional derivatives
560  try {
561  calculate_fwd(opts);
562  } catch (std::exception& e) {
563  casadi_error("Forward mode AD failed:\n" + str(e.what()));
564  }
565 
566  // Reverse mode directional derivatives
567  try {
568  calculate_adj(opts);
569  } catch (std::exception& e) {
570  casadi_error("Reverse mode AD failed:\n" + str(e.what()));
571  }
572 
573  // Jacobian blocks
574  try {
575  calculate_jac(opts);
576  } catch (std::exception& e) {
577  casadi_error("Jacobian generation failed:\n" + str(e.what()));
578  }
579 
580  // Gradient blocks
581  try {
582  calculate_grad(opts);
583  } catch (std::exception& e) {
584  casadi_error("Gradient generation failed:\n" + str(e.what()));
585  }
586 
587  // Hessian blocks
588  try {
589  calculate_hess(opts);
590  } catch (std::exception& e) {
591  casadi_error("Hessian generation failed:\n" + str(e.what()));
592  }
593  }
594 
595  template<typename MatType>
596  MatType Factory<MatType>::get_input(const std::string& s) {
597  auto it = imap_.find(s);
598  casadi_assert(it!=imap_.end(), "Cannot retrieve \"" + s + "\"");
599  return in_.at(it->second);
600  }
601 
602  template<typename MatType>
603  MatType Factory<MatType>::get_output(const std::string& s) {
604  // Quick return if output
605  auto it = omap_.find(s);
606  if (it!=omap_.end()) return out_.at(it->second);
607 
608  // Assume attribute
609  casadi_assert(has_prefix(s), "Cannot process \"" + s + "\"");
610  std::pair<std::string, std::string> ss = split_prefix(s);
611  std::string a = ss.first;
612  MatType r = get_output(ss.second);
613 
614  // Process attributes
615  if (a=="transpose") {
616  return r.T();
617  } else if (a=="triu") {
618  return triu(r);
619  } else if (a=="tril") {
620  return tril(r);
621  } else if (a=="densify") {
622  return densify(r);
623  } else if (a=="sym") {
624  casadi_warning("Attribute 'sym' has been deprecated. Hessians are symmetric by default.");
625  return r;
626  } else if (a=="withdiag") {
627  return project(r, r.sparsity() + Sparsity::diag(r.size1()));
628  } else {
629  casadi_error("Cannot process attribute \"" + a + "\"");
630  return MatType();
631  }
632  }
633 
634  template<typename MatType>
635  bool Factory<MatType>::
636  has_prefix(const std::string& s) {
637  return s.find(':') < s.size();
638  }
639 
640  template<typename MatType>
641  std::pair<std::string, std::string> Factory<MatType>::
642  split_prefix(const std::string& s) {
643  // Get prefix
644  casadi_assert_dev(!s.empty());
645  size_t pos = s.find(':');
646  casadi_assert(pos<s.size(), "Cannot process \"" + s + "\"");
647  return std::make_pair(s.substr(0, pos), s.substr(pos+1, std::string::npos));
648  }
649 
650  template<typename MatType>
651  std::vector<std::string> Factory<MatType>::iname(const std::vector<size_t>& ind) const {
652  std::vector<std::string> ret;
653  for (size_t i : ind) ret.push_back(iname_.at(i));
654  return ret;
655  }
656 
657  template<typename MatType>
658  std::vector<std::string> Factory<MatType>::oname(const std::vector<size_t>& ind) const {
659  std::vector<std::string> ret;
660  for (size_t i : ind) ret.push_back(oname_.at(i));
661  return ret;
662  }
663 
664  template<typename MatType>
665  size_t Factory<MatType>::imap(const std::string& s) const {
666  auto iind = imap_.find(s);
667  casadi_assert(iind != imap_.end(),
668  "Cannot process \"" + s + "\" as input. Available: " + join(oname()) + ".");
669  return iind->second;
670  }
671 
672  template<typename MatType>
673  size_t Factory<MatType>::omap(const std::string& s) const {
674  auto oind = omap_.find(s);
675  casadi_assert(oind != omap_.end(),
676  "Cannot process \"" + s + "\" as output. Available: " + join(oname()) + ".");
677  return oind->second;
678  }
679 
680  template<typename MatType>
681  Block Factory<MatType>::block(const std::string& s2, const std::string& s) const {
682  Block b;
683  b.s = s;
684  size_t pos = s2.find(':');
685  if (pos < s2.size()) {
686  b.f = omap(s2.substr(0, pos));
687  b.x = imap(s2.substr(pos+1, std::string::npos));
688  }
689  return b;
690  }
691 
692  template<typename MatType>
693  HBlock Factory<MatType>::hblock(const std::string& s2, const std::string& s) const {
694  HBlock b;
695  b.s = s;
696  size_t pos1 = s2.find(':');
697  if (pos1 < s2.size()) {
698  size_t pos2 = s2.find(':', pos1 + 1);
699  if (pos2 < s2.size()) {
700  b.f = omap(s2.substr(0, pos1));
701  b.x1 = imap(s2.substr(pos1 + 1, pos2 - pos1 - 1));
702  b.x2 = imap(s2.substr(pos2 + 1, std::string::npos));
703  }
704  }
705  return b;
706  }
707 
708 } // namespace casadi
710 
711 #endif // CASADI_FACTORY_HPP
std::map< std::string, std::vector< std::string > > AuxOut
Definition: function.hpp:395
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:183
The casadi namespace.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.