onnx_function.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 "onnx_function_impl.hpp"
26 #include "graph_builder_internal.hpp"
27 #include "casadi_misc.hpp"
28 #include "filesystem_impl.hpp"
29 
30 namespace casadi {
31 
34  ret["model_path"] = model_path_;
35  return ret;
36  }
37 
38  bool has_onnxbackend(const std::string& solver) {
39  return OnnxFunction::has_plugin(solver);
40  }
41 
42  void load_onnxbackend(const std::string& solver) {
44  }
45 
46  std::vector<std::string> onnxbackend_solvers() {
47  std::vector<std::string> ret;
48  for (auto&& s : OnnxFunction::solvers_) ret.push_back(s.first);
49  return ret;
50  }
51 
52  std::string onnxbackend_doc(const std::string& solver) {
53  return OnnxFunction::getPlugin(solver).doc;
54  }
55 
56  std::map<std::string, OnnxFunction::Plugin> OnnxFunction::solvers_;
57 
58 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
59  std::mutex OnnxFunction::mutex_solvers_;
60 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
61 
62  const std::string OnnxFunction::infix_ = "onnx";
63 
64  std::string OnnxFunction::meta_doc = "";
65 
66  const Options OnnxFunction::options_
68  {{"provider",
69  {OT_STRING, "Execution provider for the ONNX runtime backend"}},
70  {"dim_bindings",
71  {OT_DICT, "Sizes for symbolic/dynamic tensor dimensions (name -> size)"}},
72  {"input_shapes",
73  {OT_DICT, "Explicit shapes for inputs (name -> shape), overriding the model's"}},
74  {"input_values",
75  {OT_DICT, "Baked-in input values (name -> value); these inputs are not exposed"}},
76  {"fwd_dim",
77  {OT_STRING, "Symbolic dimension naming the forward seed count [nfwd]"}},
78  {"adj_dim",
79  {OT_STRING, "Symbolic dimension naming the adjoint seed count [nadj]"}}
80  }
81  };
82 
83  std::string onnx_dtype_name(casadi_int t) {
84  switch (t) {
85  case 1: return "FLOAT"; case 2: return "UINT8"; case 3: return "INT8";
86  case 4: return "UINT16"; case 5: return "INT16"; case 6: return "INT32";
87  case 7: return "INT64"; case 8: return "STRING"; case 9: return "BOOL";
88  case 10: return "FLOAT16"; case 11: return "DOUBLE"; case 12: return "UINT32";
89  case 13: return "UINT64"; case 16: return "BFLOAT16";
90  default: return "TYPE" + str(t);
91  }
92  }
93 
94  casadi_int onnx_dtype_enum(const std::string& name) {
95  static const std::map<std::string, casadi_int> m = {
96  {"FLOAT", 1}, {"UINT8", 2}, {"INT8", 3}, {"UINT16", 4}, {"INT16", 5},
97  {"INT32", 6}, {"INT64", 7}, {"STRING", 8}, {"BOOL", 9}, {"FLOAT16", 10},
98  {"DOUBLE", 11}, {"UINT32", 12}, {"UINT64", 13}, {"BFLOAT16", 16}};
99  auto it = m.find(name);
100  return it != m.end() ? it->second : 0;
101  }
102 
103  // Select a subset of tensors by name, preserving the requested order (empty request => all)
104  static std::vector<OnnxTensorInfo> select_tensors(const std::vector<OnnxTensorInfo>& all,
105  const std::vector<std::string>& req) {
106  if (req.empty()) return all;
107  std::vector<OnnxTensorInfo> sel;
108  for (const std::string& name : req) {
109  bool found = false;
110  for (const OnnxTensorInfo& t : all)
111  if (t.name == name) { sel.push_back(t); found = true; break; }
112  casadi_assert(found, "ONNX tensor '" + name + "' not found in model");
113  }
114  return sel;
115  }
116 
117  OnnxFunction::OnnxFunction(const std::string& name, const GraphBuilderInternal* gb,
118  const std::vector<std::string>& inputs,
119  const std::vector<std::string>& outputs)
120  : FunctionInternal(name) {
121  // Freeze a snapshot: the builder may mutate afterwards without affecting this function.
122  // Shapes are resolved by the builder (dim bindings + input-shape overrides applied).
123  model_data_ = gb->model_data_;
127  model_path_ = gb->model_path_;
128  builder_opts_ = gb->opts_;
129  std::vector<OnnxTensorInfo> all_out;
130  for (const Node& n : gb->node_list()) {
131  OnnxTensorInfo t;
132  t.name = n.name;
133  t.elem_type = onnx_dtype_enum(n.dtype);
134  t.shape = gb->resolved_shape(n);
135  t.numel = 1;
136  for (casadi_int d : t.shape) t.numel *= d;
137  if (n.io == "input") {
138  all_in_.push_back(t);
139  model_inputs_.insert(t.name);
140  } else {
141  all_out.push_back(t);
142  model_outputs_.insert(t.name);
143  }
144  }
145  in_ = select_tensors(all_in_, inputs); // exposed CasADi inputs (selection)
146  out_ = select_tensors(all_out, outputs); // exposed CasADi outputs (selection)
147  }
148 
150  clear_mem();
151  }
152 
153  // Pack/unpack a vector<OnnxTensorInfo> as parallel arrays
154  static void pack_tensors(SerializingStream& s, const std::string& d,
155  const std::vector<OnnxTensorInfo>& v) {
156  std::vector<std::string> names;
157  std::vector<std::vector<casadi_int>> shapes;
158  std::vector<casadi_int> elem_types, numels;
159  for (const OnnxTensorInfo& t : v) {
160  names.push_back(t.name);
161  shapes.push_back(t.shape);
162  elem_types.push_back(t.elem_type);
163  numels.push_back(t.numel);
164  }
165  s.pack(d + "::names", names);
166  s.pack(d + "::shapes", shapes);
167  s.pack(d + "::elem_types", elem_types);
168  s.pack(d + "::numels", numels);
169  }
170  static void unpack_tensors(DeserializingStream& s, const std::string& d,
171  std::vector<OnnxTensorInfo>& v) {
172  std::vector<std::string> names;
173  std::vector<std::vector<casadi_int>> shapes;
174  std::vector<casadi_int> elem_types, numels;
175  s.unpack(d + "::names", names);
176  s.unpack(d + "::shapes", shapes);
177  s.unpack(d + "::elem_types", elem_types);
178  s.unpack(d + "::numels", numels);
179  v.clear();
180  for (size_t k = 0; k < names.size(); ++k)
181  v.push_back(OnnxTensorInfo{names[k], shapes[k], elem_types[k], numels[k]});
182  }
183 
187  }
188 
191  s.version("OnnxFunction", 2);
192  s.pack("OnnxFunction::model_data", std::string(model_data_.begin(), model_data_.end()));
193  pack_tensors(s, "OnnxFunction::in", in_);
194  pack_tensors(s, "OnnxFunction::out", out_);
195  pack_tensors(s, "OnnxFunction::all_in", all_in_);
196  s.pack("OnnxFunction::in_src", in_src_);
197  s.pack("OnnxFunction::in_val", in_val_);
198  s.pack("OnnxFunction::model_inputs",
199  std::vector<std::string>(model_inputs_.begin(), model_inputs_.end()));
200  s.pack("OnnxFunction::model_outputs",
201  std::vector<std::string>(model_outputs_.begin(), model_outputs_.end()));
202  s.pack("OnnxFunction::fwd_dim", fwd_dim_);
203  s.pack("OnnxFunction::adj_dim", adj_dim_);
204  s.pack("OnnxFunction::input_values", input_values_);
205  s.pack("OnnxFunction::model_path", model_path_);
206  s.pack("OnnxFunction::dim_bindings", dim_bindings_);
207  s.pack("OnnxFunction::input_shapes", input_shapes_);
208  s.pack("OnnxFunction::derivative_opts", derivative_opts_);
209  s.pack("OnnxFunction::builder_opts", builder_opts_);
210  }
211 
213  int version = s.version("OnnxFunction", 1, 2);
214  std::string bytes;
215  s.unpack("OnnxFunction::model_data", bytes);
216  model_data_.assign(bytes.begin(), bytes.end());
217  unpack_tensors(s, "OnnxFunction::in", in_);
218  unpack_tensors(s, "OnnxFunction::out", out_);
219  unpack_tensors(s, "OnnxFunction::all_in", all_in_);
220  s.unpack("OnnxFunction::in_src", in_src_);
221  s.unpack("OnnxFunction::in_val", in_val_);
222  std::vector<std::string> mi, mo;
223  s.unpack("OnnxFunction::model_inputs", mi);
224  model_inputs_ = std::set<std::string>(mi.begin(), mi.end());
225  s.unpack("OnnxFunction::model_outputs", mo);
226  model_outputs_ = std::set<std::string>(mo.begin(), mo.end());
227  s.unpack("OnnxFunction::fwd_dim", fwd_dim_);
228  s.unpack("OnnxFunction::adj_dim", adj_dim_);
229  s.unpack("OnnxFunction::input_values", input_values_);
230  if (version >= 2) {
231  s.unpack("OnnxFunction::model_path", model_path_);
232  s.unpack("OnnxFunction::dim_bindings", dim_bindings_);
233  s.unpack("OnnxFunction::input_shapes", input_shapes_);
234  s.unpack("OnnxFunction::derivative_opts", derivative_opts_);
235  s.unpack("OnnxFunction::builder_opts", builder_opts_);
236  }
237  }
238 
241  }
242 
243  void OnnxFunction::init(const Dict& opts) {
244  for (auto&& op : opts) {
245  if (op.first == "fwd_dim") fwd_dim_ = op.second.to_string();
246  else if (op.first == "adj_dim") adj_dim_ = op.second.to_string();
247  if (op.first == "provider" || op.first == "fwd_dim" || op.first == "adj_dim")
248  derivative_opts_[op.first] = op.second;
249  }
250  // Baked inputs are fed a fixed value, not exposed as Function inputs
251  std::vector<OnnxTensorInfo> exposed;
252  for (const OnnxTensorInfo& t : in_) if (!input_values_.count(t.name)) exposed.push_back(t);
253  in_ = exposed;
254  build_io_map(); // baked-feed map over all model inputs
255  Dict inferred_opts = opts;
256  if (!opts.count("is_diff_in")) {
257  std::vector<bool> inferred;
258  bool found = false;
259  auto infer = [&](const std::string& kind, const std::set<std::string>& inputs,
260  const std::set<std::string>& outputs) -> bool {
261  bool signature = false;
262  for (const auto& y : out_) {
263  signature |= kind == "fwd" ? outputs.count("fwd_" + y.name)
264  : inputs.count("adj_" + y.name);
265  }
266  if (!signature) return false;
267  std::vector<bool> mask;
268  for (const auto& x : in_) {
269  mask.push_back(kind == "fwd" ? inputs.count("fwd_" + x.name)
270  : outputs.count("adj_" + x.name));
271  }
272  casadi_assert(!found || mask == inferred,
273  "ONNX derivative signatures disagree on differentiable inputs; specify is_diff_in");
274  inferred = mask;
275  found = true;
276  return true;
277  };
278  for (const std::string kind : {"fwd", "adj"}) {
279  if (infer(kind, model_inputs_, model_outputs_)) continue;
280  std::string path = derivative_path(kind);
281  if (path.empty() || !Filesystem::exists(path)) continue;
282  GraphBuilder sibling(path, builder_opts_);
283  std::set<std::string> inputs, outputs;
284  for (const Node& node : sibling.get()->node_list()) {
285  (node.io == "input" ? inputs : outputs).insert(node.name);
286  }
287  infer(kind, inputs, outputs);
288  }
289  if (found) inferred_opts["is_diff_in"] = inferred;
290  }
291  FunctionInternal::init(inferred_opts);
292  }
293 
295  // in_src: exposed-arg index (>=0), -2 baked value, or -1 unwired (fed a default later)
296  in_src_.clear();
297  in_val_.clear();
298  for (const OnnxTensorInfo& t : all_in_) {
299  casadi_int src = -1;
300  for (casadi_int j = 0; j < static_cast<casadi_int>(in_.size()); ++j)
301  if (in_[j].name == t.name) { src = j; break; }
302  auto bv = input_values_.find(t.name);
303  if (src < 0 && bv != input_values_.end()) {
304  casadi_assert(static_cast<casadi_int>(bv->second.size()) == t.numel,
305  "Baked value for '" + t.name + "' has " + str(bv->second.size())
306  + " elements, expected " + str(t.numel));
307  src = -2;
308  for (double v : bv->second) in_val_.push_back(v);
309  } else {
310  in_val_.insert(in_val_.end(), t.numel, 0.0); // placeholder block keeps voff aligned
311  }
312  in_src_.push_back(src);
313  }
314  }
315 
316  Sparsity OnnxFunction::tensor_sparsity(const std::vector<casadi_int>& shape) {
317  // CasADi is 2-D: rank-0/1/2 map directly, higher ranks flatten to a column vector
318  if (shape.empty()) return Sparsity::dense(1, 1);
319  if (shape.size() == 1) return Sparsity::dense(shape[0], 1);
320  if (shape.size() == 2) return Sparsity::dense(shape[0], shape[1]);
321  casadi_int numel = 1;
322  for (casadi_int d : shape) numel *= d;
323  return Sparsity::dense(numel, 1);
324  }
325 
326  Function OnnxFunction::wrap_derivative(const std::string& kind, const std::string& name,
327  const std::vector<std::string>& inames, const std::vector<std::string>& onames,
328  const std::vector<Sparsity>& in_sp, const std::vector<Sparsity>& out_sp,
329  const Dict& dim_bind, const Dict& opts) const {
330  bool embedded = has_derivative(kind, model_inputs_, model_outputs_);
331  GraphBuilder b = embedded ? GraphBuilder(name, model_data_, "onnx", builder_opts_)
333  GraphBuilderInternal* bi = b.get();
334  bi->model_path_ = derivative_path(kind);
336  for (auto&& d : dim_bind) bi->dim_bindings_[d.first] = d.second;
339  std::set<std::string> inputs, outputs;
340  for (const Node& n : bi->node_list()) {
341  (n.io == "input" ? inputs : outputs).insert(n.name);
342  }
343  casadi_assert(has_derivative(kind, inputs, outputs),
344  "ONNX derivative '" + bi->name_ + "' has an incomplete " + kind + " signature");
345  std::vector<std::string> cin, con;
346  for (const std::string& nm : inames) if (inputs.count(nm)) cin.push_back(nm);
347  for (const std::string& nm : onames) if (outputs.count(nm)) con.push_back(nm);
348  // Infer unbound matrix axes, including packed columns such as nadj * nfwd.
349  for (const Node& n : bi->node_list()) {
350  if (n.dimension.size() != 2) continue;
351  const auto& names = n.io == "input" ? inames : onames;
352  const auto& sparsities = n.io == "input" ? in_sp : out_sp;
353  for (size_t i = 0; i < names.size(); ++i) {
354  if (names[i] != n.name) continue;
355  for (size_t k = 0; k < 2; ++k) {
356  const std::string& param = n.dim_params[k];
357  if (n.dimension[k] < 0 && !param.empty() && !bi->dim_bindings_.count(param))
358  bi->dim_bindings_[param] = k == 0 ? sparsities[i].size1() : sparsities[i].size2();
359  }
360  }
361  }
362  // Preserve differentiability through both the ONNX core and its signature wrapper.
363  std::vector<bool> din(inames.size(), true), dout(onames.size(), true);
364  auto di = opts.find("is_diff_in"), do_ = opts.find("is_diff_out");
365  if (di != opts.end()) {
366  din = di->second.to_bool_vector();
367  } else {
368  for (size_t i = 0; i < in_.size(); ++i) din[i] = diff_in(i);
369  for (size_t j = 0; j < out_.size(); ++j) din[in_.size() + j] = diff_out(j);
370  }
371  if (do_ != opts.end()) {
372  dout = do_->second.to_bool_vector();
373  } else if (kind == "jac") {
374  for (size_t j = 0; j < out_.size(); ++j)
375  for (size_t i = 0; i < in_.size(); ++i)
376  dout[j * in_.size() + i] = diff_out(j) && diff_in(i);
377  }
378  std::vector<bool> cdi, cdo;
379  for (size_t i = 0; i < inames.size(); ++i) if (inputs.count(inames[i])) cdi.push_back(din[i]);
380  for (size_t i = 0; i < onames.size(); ++i) if (outputs.count(onames[i])) cdo.push_back(dout[i]);
381  Dict copts = derivative_opts_;
382  copts["is_diff_in"] = cdi;
383  copts["is_diff_out"] = cdo;
384  Function g = OnnxFunction::create(plugin_name(), name + "_core", bi, cin, con, copts);
385  for (size_t i = 0; i < inames.size(); ++i) {
386  if (!inputs.count(inames[i])) continue;
387  casadi_assert(g.sparsity_in(inames[i]).size() == in_sp[i].size(),
388  "ONNX derivative input '" + inames[i] + "' has an incompatible shape");
389  }
390  for (size_t i = 0; i < onames.size(); ++i) {
391  if (!outputs.count(onames[i])) continue;
392  casadi_assert(g.sparsity_out(onames[i]).size() == out_sp[i].size(),
393  "ONNX derivative output '" + onames[i] + "' has an incompatible shape");
394  }
395  // Present CasADi's full derivative signature: feed present inputs, zero-fill absent outputs
396  std::map<std::string, MX> m;
397  std::vector<MX> args(inames.size());
398  for (size_t i = 0; i < inames.size(); ++i) {
399  // Omitted inputs must not keep unused primal evaluations alive.
400  Sparsity sp = inputs.count(inames[i]) ? in_sp[i] : Sparsity(in_sp[i].size());
401  args[i] = MX::sym(inames[i], sp);
402  m[inames[i]] = args[i];
403  }
404  std::vector<MX> gin;
405  for (const std::string& nm : cin) gin.push_back(m[nm]);
406  std::vector<MX> gout = g(gin);
407  std::map<std::string, MX> mo;
408  for (size_t k = 0; k < con.size(); ++k) mo[con[k]] = gout[k];
409  std::vector<MX> outs(onames.size());
410  for (size_t j = 0; j < onames.size(); ++j) {
411  auto it = mo.find(onames[j]);
412  outs[j] = it != mo.end() ? it->second : MX::zeros(out_sp[j]);
413  }
414  Dict wopts;
415  wopts["is_diff_in"] = din;
416  wopts["is_diff_out"] = dout;
417  auto it = opts.find("derivative_of");
418  if (it != opts.end()) wopts["derivative_of"] = it->second;
419  return Function(name, args, outs, inames, onames, wopts);
420  }
421 
422  std::string OnnxFunction::derivative_path(const std::string& kind) const {
423  if (model_path_.empty() || !Filesystem::is_enabled()) return std::string();
425  + kind + "_" + Filesystem::filename(model_path_);
426  }
427 
428  bool OnnxFunction::has_derivative(const std::string& kind,
429  const std::set<std::string>& inputs, const std::set<std::string>& outputs) const {
430  std::string pref = diff_prefix(kind);
431  bool any_in = false, any_out = false;
432  for (size_t i = 0; i < in_.size(); ++i) {
433  if (!diff_in(i)) continue;
434  any_in = true;
435  if (kind == "fwd" && !inputs.count(pref + in_[i].name)) return false;
436  if (kind == "adj" && !outputs.count(pref + in_[i].name)) return false;
437  if (kind == "jac") {
438  for (size_t j = 0; j < out_.size(); ++j) {
439  if (diff_out(j) && !outputs.count("jac_" + out_[j].name + "_" + in_[i].name))
440  return false;
441  }
442  }
443  }
444  for (size_t j = 0; j < out_.size(); ++j) {
445  if (!diff_out(j)) continue;
446  any_out = true;
447  if (kind == "fwd" && !outputs.count(pref + out_[j].name)) return false;
448  if (kind == "adj" && !inputs.count(pref + out_[j].name)) return false;
449  }
450  return any_in && any_out;
451  }
452 
453  bool OnnxFunction::has_forward(casadi_int nfwd) const {
455  || (!model_path_.empty() && Filesystem::is_enabled()
457  }
458 
459  Function OnnxFunction::get_forward(casadi_int nfwd, const std::string& name,
460  const std::vector<std::string>& inames,
461  const std::vector<std::string>& onames,
462  const Dict& opts) const {
463  std::vector<Sparsity> isp, osp;
464  for (const OnnxTensorInfo& x : in_) isp.push_back(tensor_sparsity(x.shape));
465  for (const OnnxTensorInfo& y : out_) isp.push_back(tensor_sparsity(y.shape));
466  for (const OnnxTensorInfo& x : in_) {
467  Sparsity s = tensor_sparsity(x.shape);
468  isp.push_back(Sparsity::dense(s.size1(), nfwd * s.size2()));
469  }
470  for (const OnnxTensorInfo& y : out_) {
471  Sparsity s = tensor_sparsity(y.shape);
472  osp.push_back(Sparsity::dense(s.size1(), nfwd * s.size2()));
473  }
474  std::string dim = diff_prefix("fwd");
475  dim.replace(0, 3, fwd_dim_);
476  dim.pop_back(); // Drop the trailing underscore: nfwd, nfwd2, nfwd3, ...
477  Dict db; db[dim] = nfwd;
478  return wrap_derivative("fwd", name, inames, onames, isp, osp, db, opts);
479  }
480 
481  bool OnnxFunction::has_reverse(casadi_int nadj) const {
483  || (!model_path_.empty() && Filesystem::is_enabled()
485  }
486 
487  Function OnnxFunction::get_reverse(casadi_int nadj, const std::string& name,
488  const std::vector<std::string>& inames,
489  const std::vector<std::string>& onames,
490  const Dict& opts) const {
491  std::vector<Sparsity> isp, osp;
492  for (const OnnxTensorInfo& x : in_) isp.push_back(tensor_sparsity(x.shape));
493  for (const OnnxTensorInfo& y : out_) isp.push_back(tensor_sparsity(y.shape));
494  for (const OnnxTensorInfo& y : out_) {
495  Sparsity s = tensor_sparsity(y.shape);
496  isp.push_back(Sparsity::dense(s.size1(), nadj * s.size2()));
497  }
498  for (const OnnxTensorInfo& x : in_) {
499  Sparsity s = tensor_sparsity(x.shape);
500  osp.push_back(Sparsity::dense(s.size1(), nadj * s.size2()));
501  }
502  std::string dim = diff_prefix("adj");
503  dim.replace(0, 3, adj_dim_);
504  dim.pop_back(); // Drop the trailing underscore: nadj, nadj2, nadj3, ...
505  Dict db; db[dim] = nadj;
506  return wrap_derivative("adj", name, inames, onames, isp, osp, db, opts);
507  }
508 
511  || (!model_path_.empty() && Filesystem::is_enabled()
513  }
514 
515  Function OnnxFunction::get_jacobian(const std::string& name,
516  const std::vector<std::string>& inames,
517  const std::vector<std::string>& onames,
518  const Dict& opts) const {
519  std::vector<Sparsity> isp, osp;
520  for (const OnnxTensorInfo& x : in_) isp.push_back(tensor_sparsity(x.shape));
521  for (const OnnxTensorInfo& y : out_) isp.push_back(tensor_sparsity(y.shape));
522  for (const OnnxTensorInfo& y : out_) {
523  Sparsity so = tensor_sparsity(y.shape);
524  for (const OnnxTensorInfo& x : in_) {
525  Sparsity si = tensor_sparsity(x.shape);
526  osp.push_back(Sparsity::dense(so.size1() * so.size2(), si.size1() * si.size2()));
527  }
528  }
529  return wrap_derivative("jac", name, inames, onames, isp, osp, Dict(), opts);
530  }
531 
532  Function OnnxFunction::create(const std::string& solver, const std::string& name,
533  const GraphBuilderInternal* gb,
534  const std::vector<std::string>& inputs,
535  const std::vector<std::string>& outputs,
536  const Dict& opts) {
537  return Function::create(getPlugin(solver).creator(name, gb, inputs, outputs, opts), opts);
538  }
539 
540  Function OnnxFunction::from_model_data(const std::string& solver, const std::string& name,
541  const std::vector<uint8_t>& model_data,
542  const std::vector<std::string>& inputs,
543  const std::vector<std::string>& outputs,
544  const Dict& opts) {
545  // Stage the model + configuration in a GraphBuilder, then freeze an OnnxFunction snapshot
546  GraphBuilder b(name, model_data, "onnx");
547  GraphBuilderInternal* bi = b.get();
548  Dict fopts;
549  for (auto&& op : opts) {
550  if (op.first == "dim_bindings") {
551  for (auto&& d : static_cast<Dict>(op.second)) bi->dim_bindings_[d.first] = d.second;
552  } else if (op.first == "input_shapes") {
553  for (auto&& d : static_cast<Dict>(op.second))
554  bi->input_shapes_[d.first] = d.second.to_int_vector();
555  } else if (op.first == "input_values") {
556  for (auto&& d : static_cast<Dict>(op.second))
557  bi->input_values_[d.first] = d.second.to_double_vector();
558  } else {
559  fopts[op.first] = op.second;
560  }
561  }
562  return OnnxFunction::create(solver, name, bi, inputs, outputs, fopts);
563  }
564 
565 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
static std::string parent_path(const std::string &path)
Definition: filesystem.cpp:58
static std::string filename(const std::string &path)
Definition: filesystem.cpp:73
static std::string ensure_trailing_slash(const std::string &path)
Definition: filesystem.cpp:155
static bool is_enabled()
Definition: filesystem.cpp:83
static bool exists(const std::string &path)
Definition: filesystem.cpp:150
Internal class for Function.
std::string diff_prefix(const std::string &prefix) const
Determine prefix for differentiated functions.
void init(const Dict &opts) override
Initialize.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
bool has_derivative() const
Can derivatives be calculated in any way?
static const Options options_
Options.
void serialize_type(SerializingStream &s) const override
Serialize type information.
std::string signature(const std::string &fname) const
Code generate the function.
Function object.
Definition: function.hpp:60
const Sparsity & sparsity_out(casadi_int ind) const
Get sparsity of a given output.
Definition: function.cpp:1183
static Function create(FunctionInternal *node)
Create from node.
Definition: function.cpp:488
const Sparsity & sparsity_in(casadi_int ind) const
Get sparsity of a given input.
Definition: function.cpp:1167
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
static MX zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
Internal class for GraphBuilder.
std::map< std::string, std::vector< double > > input_values_
Dict opts_
Original constructor options, retained for lazy derivative model loading.
const std::vector< Node > & node_list() const
std::string model_path_
Absolute source filename, empty for in-memory models.
std::vector< casadi_int > resolved_shape(const Node &n) const
std::map< std::string, casadi_int > dim_bindings_
Pending configuration carried into create()
std::map< std::string, std::vector< casadi_int > > input_shapes_
A mutable, format-neutral interface to a computational-graph model.
GraphBuilderInternal * get() const
Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return function that calculates forward derivatives.
static Sparsity tensor_sparsity(const std::vector< casadi_int > &shape)
Map an ONNX N-D shape to a 2-D CasADi sparsity (rank<=2 direct, rank>2 flattened column)
std::string derivative_path(const std::string &kind) const
Construct the sibling filename for a derivative of this entry point.
std::string fwd_dim_
Symbolic dimensions naming the forward/adjoint seed counts (bound in get_forward/reverse)
static const std::string infix_
static std::map< std::string, Plugin > solvers_
Plugin registry.
Function get_jacobian(const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return Jacobian of all input elements with respect to all output elements.
std::map< std::string, casadi_int > dim_bindings_
std::set< std::string > model_outputs_
Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return function that calculates adjoint derivatives.
std::string model_path_
Filename of this entry point, used for lazy, recursive sibling discovery.
std::vector< casadi_int > in_src_
Per all_in_ entry: exposed-arg index (>=0), -2 baked value, or -1 unwired (default)
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
std::map< std::string, std::vector< casadi_int > > input_shapes_
std::vector< OnnxTensorInfo > in_
Metadata for the exposed inputs/outputs (the selection)
Function wrap_derivative(const std::string &kind, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const std::vector< Sparsity > &in_sp, const std::vector< Sparsity > &out_sp, const Dict &dim_bind, const Dict &opts) const
Wrap an embedded or sibling derivative with CasADi's full signature.
bool has_jacobian() const override
Return Jacobian of all input elements with respect to all output elements.
void serialize_type(SerializingStream &s) const override
Serialize type information.
std::map< std::string, std::vector< double > > input_values_
Baked input values: input name -> value; such inputs are not exposed as Function inputs.
bool diff_out(casadi_int i) const
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into a plugin instance (dispatches on the plugin name)
Dict info() const override
void build_io_map()
Compute the per-model-input feed map: in_src_ (arg index / -2 baked / -1 default) + in_val_.
bool has_forward(casadi_int nfwd) const override
Return function that calculates forward derivatives.
static Function create(const std::string &solver, const std::string &name, const GraphBuilderInternal *gb, const std::vector< std::string > &inputs, const std::vector< std::string > &outputs, const Dict &opts)
Plugin factory.
static const Options options_
Options.
static std::string meta_doc
Documentation string.
Dict builder_opts_
Original GraphBuilder constructor options for derivative model loading.
std::vector< OnnxTensorInfo > out_
std::vector< OnnxTensorInfo > all_in_
Metadata for every model input (a runtime backend must feed all of them)
std::vector< uint8_t > model_data_
Serialized ONNX model.
static Function from_model_data(const std::string &solver, const std::string &name, const std::vector< uint8_t > &model_data, const std::vector< std::string > &inputs, const std::vector< std::string > &outputs, const Dict &opts)
Freeze a function from raw model bytes: stage a transient GraphBuilder, then create()
std::set< std::string > model_inputs_
Names of every input/output tensor in the model (for derivative detection)
bool diff_in(casadi_int i) const
True if input/output index is differentiable (is_diff_in/out, default true)
OnnxFunction(const std::string &name, const GraphBuilderInternal *gb, const std::vector< std::string > &inputs, const std::vector< std::string > &outputs)
Construct by freezing a snapshot of a builder's metadata + config (exposed selection)
std::vector< double > in_val_
Baked input values, flat over all_in_ (numel each; placeholder block when not baked)
bool has_reverse(casadi_int nadj) const override
Return function that calculates adjoint derivatives.
void init(const Dict &opts) override
Initialize.
static bool has_plugin(const std::string &pname, bool verbose=false)
Check if a plugin is available or can be loaded.
void serialize_type(SerializingStream &s) const
Serialize type information.
static Plugin & getPlugin(const std::string &pname)
Load and get the creator function.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
virtual const char * plugin_name() const=0
static Plugin load_plugin(const std::string &pname, bool register_plugin=true, bool needs_lock=true)
Load a plugin dynamically.
Base class for FunctionInternal and LinsolInternal.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
General sparsity class.
Definition: sparsity.hpp:106
casadi_int size1() const
Get the 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:1028
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
std::pair< casadi_int, casadi_int > size() const
Get the shape.
Definition: sparsity.cpp:152
std::string onnxbackend_doc(const std::string &solver)
Get documentation for an ONNX runtime backend.
void load_onnxbackend(const std::string &solver)
Load an ONNX runtime backend.
bool has_onnxbackend(const std::string &solver)
Check if a given ONNX runtime backend is available.
std::vector< std::string > onnxbackend_solvers()
List available ONNX runtime backends.
The casadi namespace.
Definition: archiver.cpp:28
std::string onnx_dtype_name(casadi_int t)
Human-readable name of an ONNX element-type enum (1=FLOAT, 11=DOUBLE, 7=INT64, ......
casadi_int onnx_dtype_enum(const std::string &name)
ONNX element-type enum for a human-readable name (inverse of onnx_dtype_name; 0 if unknown)
static std::vector< OnnxTensorInfo > select_tensors(const std::vector< OnnxTensorInfo > &all, const std::vector< std::string > &req)
static void unpack_tensors(DeserializingStream &s, const std::string &d, std::vector< OnnxTensorInfo > &v)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
bool all(const std::vector< bool > &v)
Check if all arguments are true.
Definition: casadi_misc.cpp:81
static void pack_tensors(SerializingStream &s, const std::string &d, const std::vector< OnnxTensorInfo > &v)
std::vector< casadi_int > path(const std::vector< casadi_int > &map, casadi_int i_start)
Metadata for one graph tensor (graph input or output)
Metadata for a single ONNX tensor (input or output); shape is resolved by the builder.
casadi_int elem_type
ONNX element type enum (1=float, 11=double, 7=int64)
std::vector< casadi_int > shape
Resolved shape (dynamic dims bound or set to 1)
std::string name
ONNX tensor name.
casadi_int numel
Number of elements in the resolved shape.