List of all members | Public Types | Public Member Functions | Static Public Member Functions | Static Public Attributes | Protected Member Functions | Protected Attributes
casadi::Onnx Class Reference

'onnx' plugin for GraphModel More...

#include <onnx_model.hpp>

Detailed Description

The ONNX backend for GraphModel and the CasADi <-> ONNX graph engine in one class: parses the protobuf model, fills a GraphBuilder's tensor metadata, and performs symbolic import (graph -> MXFunction) and export (Function -> ONNX bytes). The only class that links onnx/protobuf.

Author
Joris Gillis
Date
2026


List of available options
IdTypeDescriptionUsed in
casadi_realOT_STRINGReal type for exported tensors: 'double' (default) or 'float'casadi::Onnx

Definition at line 61 of file onnx_model.hpp.

Inheritance diagram for casadi::Onnx:
Inheritance graph
[legend]
Collaboration diagram for casadi::Onnx:
Collaboration graph
[legend]

Public Types

typedef GraphModelInternal *(* Creator) (const std::vector< uint8_t > &model_data)
 Plugin creator function type. More...
 
using weak_ref_type = WeakRefInternal
 
typedef int(* RegFcn) (Plugin *plugin)
 

Public Member Functions

 Onnx (const std::vector< uint8_t > &model_data)
 
 ~Onnx () override
 
const char * plugin_name () const override
 Query plugin name. More...
 
std::string class_name () const override
 Readable name of the internal class. More...
 
void init (const Dict &opts) override
 Initialize. More...
 
void fill_metadata (GraphBuilderInternal &gb) const override
 Populate a builder's Node metadata from the parsed model. More...
 
Function import_symbolic (const GraphBuilderInternal &gb, const std::string &name) override
 Rebuild the graph as a CasADi Function (symbolic import; mutates the backend's engine) More...
 
std::vector< uint8_t > export_symbolic (const Function &f, const Dict &opts) override
 Serialize a CasADi Function as model bytes (symbolic export; mutates the backend's engine) More...
 
void load (const Function &f)
 Load a CasADi Function and convert to the ONNX representation. More...
 
void set_dimension (const std::string &name, casadi_int dim)
 Set dimension for a symbolic variable. More...
 
Function create (const std::string &name)
 Create a CasADi Function from the loaded ONNX graph. More...
 
void load_bytes (const std::vector< uint8_t > &data)
 Load a graph from serialized ONNX bytes. More...
 
std::vector< uint8_t > save_bytes () const
 Serialize the loaded graph/model to ONNX bytes. More...
 
void disp (std::ostream &stream, bool more) const override
 Print a description of the object. More...
 
void construct (const Dict &opts)
 Prepare the backend for use. More...
 
const std::vector< uint8_t > & model_data () const
 Raw model bytes. More...
 
casadi_int getCount () const
 Get the reference count. More...
 
std::string debug_repr (const SharedObjectInternal *) const
 
GenericWeakRef< SharedObject, SharedObjectInternal > * weak ()
 Get a weak reference to the object. More...
 
virtual void deps_version_check (const std::string &stage) const
 
void serialize_type (SerializingStream &s) const
 Serialize type information. More...
 

Static Public Member Functions

static GraphModelInternalcreator (const std::vector< uint8_t > &model_data)
 Plugin factory. More...
 
static std::string shortname ()
 Short name. More...
 
static bool has_plugin (const std::string &pname, bool verbose=false)
 Check if a plugin is available or can be loaded. More...
 
static const Optionsplugin_options (const std::string &pname)
 Get the plugin options. More...
 
static Deserialize plugin_deserialize (const std::string &pname)
 Get the plugin deserialize_map. More...
 
static Plugin pluginFromRegFcn (RegFcn regfcn)
 Instantiate a Plugin struct from a factory function. More...
 
static Plugin load_plugin (const std::string &pname, bool register_plugin=true, bool needs_lock=true)
 Load a plugin dynamically. More...
 
static handle_t load_library (const std::string &libname, std::string &resultpath, bool global)
 Load a library dynamically. More...
 
static void registerPlugin (const Plugin &plugin, bool needs_lock=true)
 Register an integrator in the factory. More...
 
static void registerPlugin (RegFcn regfcn, bool needs_lock=true)
 Register an integrator in the factory. More...
 
static Plugin & getPlugin (const std::string &pname)
 Load and get the creator function. More...
 
static GraphModelInternalinstantiate (const std::string &fname, const std::string &pname, Problem problem)
 
static ProtoFunctiondeserialize (DeserializingStream &s)
 Deserialize with type disambiguation. More...
 

Static Public Attributes

static const std::string meta_doc
 Documentation. More...
 
static std::map< std::string, Plugin > solvers_
 Collection of available format plugins. More...
 
static const std::string infix_ = "graphmodel"
 Infix used to form plugin registration symbols (casadi_register_graphmodel_<name>) More...
 

Protected Member Functions

void initSingleton ()
 
void destroySingleton ()
 
shared_from_this ()
 Get a shared object from the current internal object. More...
 
const B shared_from_this () const
 Get a shared object from the current internal object. More...
 

Protected Attributes

onnx::ModelProto model_
 ONNX model protocol buffer. More...
 
std::map< std::string, casadi_int > dimension_overrides_
 Dimension overrides for symbolic dimensions. More...
 
bool has_model_ = false
 Whether a model has been loaded. More...
 
std::set< std::string > exported_functions_
 Track which functions have been exported as FunctionProto. More...
 
std::string casadi_real_ = "double"
 Real type for exported tensors: "double" (default) or "float". More...
 
std::vector< uint8_t > model_data_
 Raw model bytes (empty when constructed for export only) More...
 
bool verbose_
 Verbose – for debugging. More...
 
static const Options options_
 Options. More...
 
const Optionsget_options () const override
 Options. More...
 

Member Typedef Documentation

◆ Creator

typedef GraphModelInternal*(* casadi::GraphModelInternal::Creator) (const std::vector< uint8_t > &model_data)
inherited

Definition at line 58 of file graph_model_internal.hpp.

◆ RegFcn

typedef int(* casadi::PluginInterface< GraphModelInternal >::RegFcn) (Plugin *plugin)
inherited

Definition at line 73 of file plugin_interface.hpp.

◆ weak_ref_type

Definition at line 152 of file shared_object.hpp.

Constructor & Destructor Documentation

◆ Onnx()

casadi::Onnx::Onnx ( const std::vector< uint8_t > &  model_data)
explicit

Definition at line 59 of file onnx_model.cpp.

60  if (!model_data.empty()) load_bytes(model_data);
61  }
GraphModelInternal(const std::vector< uint8_t > &model_data)
Definition: graph_model.cpp:83
const std::vector< uint8_t > & model_data() const
Raw model bytes.
void load_bytes(const std::vector< uint8_t > &data)
Load a graph from serialized ONNX bytes.
Definition: onnx_model.cpp:73

References load_bytes(), and casadi::GraphModelInternal::model_data().

◆ ~Onnx()

casadi::Onnx::~Onnx ( )
override

Definition at line 63 of file onnx_model.cpp.

63  {
64  }

Member Function Documentation

◆ class_name()

std::string casadi::Onnx::class_name ( ) const
inlineoverridevirtual

Reimplemented from casadi::GraphModelInternal.

Definition at line 72 of file onnx_model.hpp.

72 { return "Onnx"; }

◆ construct()

void casadi::GraphModelInternal::construct ( const Dict opts)
inherited

Definition at line 96 of file graph_model.cpp.

96  {
97  for (auto&& op : opts) {
98  if (op.first == "verbose") verbose_ = op.second;
99  }
100  init(opts);
101  }
bool verbose_
Verbose – for debugging.
virtual void init(const Dict &opts)
Initialize.

References casadi::GraphModelInternal::init(), and casadi::GraphModelInternal::verbose_.

◆ create()

Function casadi::Onnx::create ( const std::string &  name)

Definition at line 413 of file onnx_import.cpp.

413  {
414  casadi_assert(has_model_, "No ONNX model loaded. Call load() first.");
415  return function_from_graph(model_.graph(), name);
416  }
onnx::ModelProto model_
ONNX model protocol buffer.
Definition: onnx_model.hpp:110
bool has_model_
Whether a model has been loaded.
Definition: onnx_model.hpp:116

References has_model_, and model_.

Referenced by import_symbolic().

◆ creator()

static GraphModelInternal* casadi::Onnx::creator ( const std::vector< uint8_t > &  model_data)
inlinestatic

Definition at line 67 of file onnx_model.hpp.

67  {
68  return new Onnx(model_data);
69  }
Onnx(const std::vector< uint8_t > &model_data)
Definition: onnx_model.cpp:59

Referenced by casadi::casadi_register_graphmodel_onnx().

◆ debug_repr()

std::string casadi::GenericSharedInternal< SharedObject , SharedObjectInternal >::debug_repr ( const Internal *  i) const
inherited

Definition at line 62 of file generic_shared_internal.hpp.

175  {
176  // Note: i != this because of something something multiple inheritance
177  return str( (casadi_int)(i)) + "/" + static_cast<const Internal*>(this)->class_name();
178  }
std::string str(const T &v)
String representation, any type.

◆ deps_version_check()

virtual void casadi::PluginInterface< GraphModelInternal >::deps_version_check ( const std::string &  stage) const
inlinevirtualinherited

Definition at line 112 of file plugin_interface.hpp.

112 {}

◆ deserialize()

static ProtoFunction* casadi::PluginInterface< GraphModelInternal >::deserialize ( DeserializingStream s)
inlinestaticinherited

Extra doc: https://github.com/casadi/casadi/wiki/L_rr

Definition at line 124 of file plugin_interface.hpp.

124  {
125  std::string class_name, plugin_name;
126  s.unpack("PluginInterface::plugin_name", plugin_name);
128  return deserialize(s);
129  }
static Deserialize plugin_deserialize(const std::string &pname)
Get the plugin deserialize_map.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
virtual const char * plugin_name() const=0
ProtoFunction *(* Deserialize)(DeserializingStream &)

◆ destroySingleton()

void casadi::GenericSharedInternal< SharedObject , SharedObjectInternal >::destroySingleton ( )
inlineprotectedinherited

Called in the destructor of singletons

Definition at line 77 of file generic_shared_internal.hpp.

77  {
78  static_cast<Internal*>(this)->count--;
79  }

◆ disp()

void casadi::GraphModelInternal::disp ( std::ostream &  stream,
bool  more 
) const
overridevirtualinherited

Implements casadi::SharedObjectInternal.

Definition at line 106 of file graph_model.cpp.

106  {
107  stream << "GraphModel(" << plugin_name() << ")";
108  }
const char * plugin_name() const override=0
Query plugin name.

References casadi::GraphModelInternal::plugin_name().

◆ export_symbolic()

std::vector< uint8_t > casadi::Onnx::export_symbolic ( const Function f,
const Dict opts 
)
overridevirtual

Implements casadi::GraphModelInternal.

Definition at line 125 of file onnx_model.cpp.

125  {
126  auto it = opts.find("casadi_real");
127  if (it != opts.end()) set_casadi_real(it->second.to_string());
128  load(f);
129  return save_bytes();
130  }
std::vector< uint8_t > save_bytes() const
Serialize the loaded graph/model to ONNX bytes.
Definition: onnx_model.cpp:79
void load(const Function &f)
Load a CasADi Function and convert to the ONNX representation.
Definition: onnx_export.cpp:77

References load(), and save_bytes().

◆ fill_metadata()

void casadi::Onnx::fill_metadata ( GraphBuilderInternal gb) const
overridevirtual

Implements casadi::GraphModelInternal.

Definition at line 107 of file onnx_model.cpp.

107  {
108  gb.clear_nodes();
109  const onnx::GraphProto& g = model_.graph();
110 
111  // Initializers are constants, not graph inputs (mirror ORT, which excludes them)
112  std::set<std::string> init_names;
113  for (int i = 0; i < g.initializer_size(); ++i) init_names.insert(g.initializer(i).name());
114 
115  for (int i = 0; i < g.input_size(); ++i)
116  if (!init_names.count(g.input(i).name())) gb.add_node(io_node(g.input(i), "input"));
117  for (int i = 0; i < g.output_size(); ++i) gb.add_node(io_node(g.output(i), "output"));
118  }
static Node io_node(const onnx::ValueInfoProto &vi, const std::string &io)
Definition: onnx_model.cpp:87

References casadi::GraphBuilderInternal::add_node(), casadi::GraphBuilderInternal::clear_nodes(), casadi::io_node(), and model_.

◆ get_options()

const Options& casadi::Onnx::get_options ( ) const
inlineoverridevirtual

Reimplemented from casadi::GraphModelInternal.

Definition at line 77 of file onnx_model.hpp.

77 { return options_; }
static const Options options_
Options.
Definition: onnx_model.hpp:76

◆ getCount()

Definition at line 60 of file generic_shared_internal.hpp.

205  {
206  return static_cast<const Internal*>(this)->count;
207  }

◆ getPlugin()

PluginInterface< GraphModelInternal >::Plugin & casadi::PluginInterface< GraphModelInternal >::getPlugin ( const std::string &  pname)
staticinherited

Definition at line 102 of file plugin_interface.hpp.

295  {
296 
297 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
298  std::lock_guard<std::mutex> lock(Derived::mutex_solvers_);
299 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
300 
301  // Check if the solver has been loaded
302  auto it=Derived::solvers_.find(pname);
303 
304  // Load the solver if needed
305  if (it==Derived::solvers_.end()) {
306  load_plugin(pname, true, false);
307  it=Derived::solvers_.find(pname);
308  }
309  casadi_assert_dev(it!=Derived::solvers_.end());
310  return it->second;
311  }
static Plugin load_plugin(const std::string &pname, bool register_plugin=true, bool needs_lock=true)
Load a plugin dynamically.

◆ has_plugin()

bool casadi::PluginInterface< GraphModelInternal >::has_plugin ( const std::string &  pname,
bool  verbose = false 
)
staticinherited

Definition at line 76 of file plugin_interface.hpp.

134  {
135 
136 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
137  std::lock_guard<std::mutex> lock(Derived::mutex_solvers_);
138 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
139 
140  // Quick return if available
141  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
142  return true;
143  }
144 
145  // Try loading the plugin
146  try {
147  (void)load_plugin(pname, false, false);
148  return true;
149  } catch (CasadiException& ex) {
150  if (verbose) {
151  casadi_warning(ex.what());
152  }
153  return false;
154  }
155  }
std::vector< casadi_int > find(const std::vector< T > &v)
find nonzeros

◆ import_symbolic()

Function casadi::Onnx::import_symbolic ( const GraphBuilderInternal gb,
const std::string &  name 
)
overridevirtual

Implements casadi::GraphModelInternal.

Definition at line 120 of file onnx_model.cpp.

120  {
121  for (auto&& b : gb.dim_bindings()) set_dimension(b.first, b.second);
122  return create(name);
123  }
Function create(const std::string &name)
Create a CasADi Function from the loaded ONNX graph.
void set_dimension(const std::string &name, casadi_int dim)
Set dimension for a symbolic variable.

References create(), casadi::GraphBuilderInternal::dim_bindings(), and set_dimension().

◆ init()

void casadi::Onnx::init ( const Dict opts)
overridevirtual

Reimplemented from casadi::GraphModelInternal.

Definition at line 66 of file onnx_model.cpp.

66  {
68  for (auto&& op : opts) {
69  if (op.first == "casadi_real") set_casadi_real(op.second.to_string());
70  }
71  }

References casadi::GraphModelInternal::init().

◆ initSingleton()

void casadi::GenericSharedInternal< SharedObject , SharedObjectInternal >::initSingleton ( )
inlineprotectedinherited

Called in the constructor of singletons to avoid that the counter reaches zero

Definition at line 71 of file generic_shared_internal.hpp.

71  {
72  casadi_assert_dev(static_cast<Internal*>(this)->count==0);
73  static_cast<Internal*>(this)->count++;
74  }

◆ instantiate()

GraphModelInternal * casadi::PluginInterface< GraphModelInternal >::instantiate ( const std::string &  fname,
const std::string &  pname,
Problem  problem 
)
staticinherited

Definition at line 106 of file plugin_interface.hpp.

317  {
318 
319  // Assert the plugin exists (needed for adaptors)
320  if (!has_plugin(pname, true)) {
321  casadi_error("Plugin '" + pname + "' is not found.");
322  }
323  return getPlugin(pname).creator(fname, problem);
324  }
static bool has_plugin(const std::string &pname, bool verbose=false)
Check if a plugin is available or can be loaded.
static Plugin & getPlugin(const std::string &pname)
Load and get the creator function.

◆ load()

void casadi::Onnx::load ( const Function f)

Definition at line 77 of file onnx_export.cpp.

77  {
78  model_.Clear();
79  exported_functions_.clear();
80  model_.set_ir_version(8);
81  model_.set_producer_name("CasADi");
82  model_.set_producer_version(CasadiMeta::version());
83 
84  onnx::OperatorSetIdProto* opset = model_.add_opset_import();
85  opset->set_domain(""); // default ONNX domain
86  opset->set_version(16); // opset 16: ScatterND reduction
87 
88  onnx::GraphProto* graph = model_.mutable_graph();
89  graph->set_name(f.name());
90  add_graph_inputs(graph, f);
91 
92  // work vector index -> the ONNX tensor name that produced it
93  std::map<casadi_int, std::string> work_to_onnx;
94 
95  casadi_int n_instr = f.n_instructions();
96 
97  // Pre-scan: count segments per output (multi-segment = horz/vert/diagcat output)
98  std::map<casadi_int, casadi_int> output_segment_count;
99  for (casadi_int k = 0; k < n_instr; ++k) {
100  if (f.instruction_id(k) == OP_OUTPUT) {
101  MX mx = f.instruction_MX(k);
102  Dict info = mx.info();
103  casadi_int output_idx = info["ind"];
104  output_segment_count[output_idx]++;
105  }
106  }
107 
108  // Track segment values for multi-segment outputs: output_idx -> (offset -> onnx_name)
109  std::map<casadi_int, std::map<casadi_int, std::string>> output_segment_values;
110  // and each segment's source sparsity, to extract its nonzero array on reassembly
111  std::map<casadi_int, std::map<casadi_int, Sparsity>> output_segment_sparsity;
112 
113  for (casadi_int k = 0; k < n_instr; ++k) {
114  casadi_int op = f.instruction_id(k);
115  std::vector<casadi_int> o = f.instruction_output(k);
116  std::vector<casadi_int> i = f.instruction_input(k);
117 
118  // Unique result name keyed on the instruction index
119  std::string node_output = "n" + std::to_string(k);
120 
121  // OP_OUTPUT is special: it supports multi-segment outputs (horz/vert/diagcat)
122  if (op == OP_OUTPUT) {
123  MX mx = f.instruction_MX(k);
124  Dict info = mx.info();
125  casadi_int output_idx = info["ind"];
126  casadi_int offset = info["offset"];
127 
128  std::string output_name = onnx_output_name(f, output_idx);
129  std::string input_onnx_name = work_to_onnx[i[0]];
130 
131  if (output_segment_count[output_idx] > 1) {
132  // Multi-segment output (horzcat-like): each segment provides a CONTIGUOUS run of the
133  // output's column-major nonzero array (offset = nnz offset). Record the source value and
134  // its sparsity; reassemble by concatenating the segments' nonzero arrays below.
135  output_segment_values[output_idx][offset] = input_onnx_name;
136  output_segment_sparsity[output_idx][offset] = mx.dep(0).sparsity();
137  } else {
138  // Single-segment output. A reshape can fold into the output (no OP_RESHAPE
139  // instruction): the source shape then differs from the declared output shape. A
140  // folded reshape densifies on import, so when the output pattern is non-dense, route
141  // the assembly through a temp and restore the exact pattern natively from the seed
142  // (no overlay).
143  MX dep = mx.dep(0);
144  Sparsity out_sp = f.sparsity_out(output_idx);
145  auto add_node = [graph]() -> onnx::NodeProto* { return graph->add_node(); };
146  bool folded = (dep.size1() != out_sp.size1() || dep.size2() != out_sp.size2());
147  if (folded && !out_sp.is_dense()) {
148  std::string tmp = "out_pre_" + std::to_string(k);
149  emit_output_node(graph, input_onnx_name, dep.size1(), dep.size2(), out_sp,
150  tmp, "out_rs_" + std::to_string(k));
151  emit_sparsity_restore(add_node, tmp,
152  Sparsity::dense(out_sp.size1(), out_sp.size2()), out_sp,
153  "outr_" + std::to_string(k), output_name);
154  } else {
155  emit_output_node(graph, input_onnx_name, dep.size1(), dep.size2(), out_sp,
156  output_name, "out_rs_" + std::to_string(k));
157  }
158  }
159  continue;
160  }
161 
162  if (process_operation(graph, f, op, k, i, o, work_to_onnx, node_output)) continue;
163 
164  // Operations not handled by process_operation
165  if (op == OP_CALL) {
166  Function called_func = f.instruction_MX(k).which_function();
167  if (is_map_function(called_func)) {
168  export_map(graph, called_func, i, o, work_to_onnx, node_output + "_out");
169  } else if (is_reduce_map_function(called_func)) {
170  export_reduce_map(graph, called_func, i, o, work_to_onnx, node_output + "_out");
171  } else if (is_if_else_function(called_func)) {
172  export_if(graph, called_func, i, o, work_to_onnx, node_output + "_out");
173  } else {
174  assert_not_control_flow(called_func);
175  export_call(graph, called_func, i, o, work_to_onnx, node_output + "_out");
176  }
177  continue;
178  }
179 
180  // Unknown/unsupported operation
181  casadi_error("ONNX export: unsupported operation code " +
182  std::to_string(op) + " at instruction " + std::to_string(k) +
183  ". The CasADi Function contains operations that cannot be exported to ONNX.");
184  }
185 
186  // Reassemble multi-segment outputs pseudo-dense. The block structure is one of:
187  // - horzcat (all blocks full-height, size1==R): columns concatenated -> a single ONNX Concat.
188  // Transpose-rep: CasADi horzcat (axis 1) is ONNX axis 0 (matches OP_HORZCAT dispatch).
189  // - vertcat (all blocks full-width, size2==C): rows concatenated -> a single ONNX Concat on
190  // the other axis. Transpose-rep: CasADi vertcat (axis 0) is ONNX axis 1
191  // (matches OP_VERTCAT).
192  // - diagcat (neither, genuine block-diagonal): Pad each DENSE block to (R,C) at its
193  // offset, Sum.
194  // The assembly imports as a DENSE block; when the output pattern is non-dense, restore it
195  // natively from the seed (no overlay) by routing the assembly through a temp.
196  auto seg_add_node = [graph]() -> onnx::NodeProto* { return graph->add_node(); };
197  for (const auto& kv : output_segment_values) {
198  casadi_int output_idx = kv.first;
199  const auto& segments = kv.second; // offset -> onnx_name, sorted by offset
200  const auto& seg_sp = output_segment_sparsity[output_idx];
201  casadi_int R = f.size1_out(output_idx), C = f.size2_out(output_idx);
202 
203  std::vector<std::string> names;
204  std::vector<casadi_int> brs, bcs;
205  bool all_full_height = true, all_full_width = true;
206  for (const auto& seg : segments) {
207  const Sparsity& sp = seg_sp.at(seg.first);
208  names.push_back(seg.second);
209  brs.push_back(sp.size1()); bcs.push_back(sp.size2());
210  if (sp.size1() != R) all_full_height = false; // not horzcat
211  if (sp.size2() != C) all_full_width = false; // not vertcat
212  }
213  Sparsity out_sp = f.sparsity_out(output_idx);
214  std::string oname = onnx_output_name(f, output_idx);
215  std::string uniq = "oseg" + std::to_string(output_idx);
216 
217  // Helper: write the assembly directly to oname when dense, else to a temp + restore.
218  auto finish = [&](const std::function<void(const std::string&)>& emit) -> void {
219  if (out_sp.is_dense()) {
220  emit(oname);
221  } else {
222  std::string tmp = oname + "_pre";
223  emit(tmp);
224  emit_sparsity_restore(seg_add_node, tmp, Sparsity::dense(R, C), out_sp,
225  uniq + "r", oname);
226  }
227  };
228 
229  if (all_full_height) {
230  // horzcat -> ONNX Concat axis 0
231  finish([&](const std::string& dst) {
232  onnx::NodeProto* cc = seg_add_node();
233  cc->set_op_type("Concat");
234  for (const auto& n : names) cc->add_input(n);
235  cc->add_output(dst);
236  add_int_attribute(cc, "axis", 0);
237  });
238  } else if (all_full_width) {
239  // vertcat -> ONNX Concat axis 1
240  finish([&](const std::string& dst) {
241  onnx::NodeProto* cc = seg_add_node();
242  cc->set_op_type("Concat");
243  for (const auto& n : names) cc->add_input(n);
244  cc->add_output(dst);
245  add_int_attribute(cc, "axis", 1);
246  });
247  } else {
248  // genuine block-diagonal (diagcat) -> Pad each block to (R,C) at its offset, then Sum
249  std::vector<casadi_int> row_off, col_off;
250  casadi_int ro = 0, co = 0;
251  for (casadi_int b = 0; b < static_cast<casadi_int>(names.size()); ++b) {
252  row_off.push_back(ro); col_off.push_back(co);
253  ro += brs[b]; co += bcs[b];
254  }
255  finish([&](const std::string& dst) {
256  emit_blockdiag(seg_add_node, names, row_off, col_off, brs, bcs, R, C, dst, uniq);
257  });
258  }
259  }
260 
261  // Fuse trailing rename Identities: a node of the form Identity(src) -> oname, where oname is a
262  // declared graph output and src is an internal node output used nowhere else, is a pure rename.
263  // Make the producing node write straight to oname and drop the Identity. Conservative: only
264  // when src is produced by exactly one node and consumed by exactly this Identity (so we never
265  // collapse
266  // an output that aliases an input, or a value feeding two outputs / another consumer).
267  {
268  std::set<std::string> output_names;
269  for (casadi_int i = 0; i < f.n_out(); ++i) output_names.insert(onnx_output_name(f, i));
270  // How many places consume each tensor name (as a node input).
271  std::map<std::string, int> consumers;
272  for (const auto& nd : graph->node())
273  for (const auto& in : nd.input()) consumers[in]++;
274  // Which node produces each tensor name, and is the name produced more than once.
275  std::map<std::string, int> producers;
276  for (const auto& nd : graph->node())
277  for (const auto& o : nd.output()) producers[o]++;
278 
279  auto* nodes = graph->mutable_node();
280  // Plan rewrites: src -> oname for fusable trailing Identities.
281  std::map<std::string, std::string> rename; // src -> oname
282  std::set<int> drop; // node indices (Identity) to remove
283  for (int n = 0; n < nodes->size(); ++n) {
284  const onnx::NodeProto& nd = nodes->Get(n);
285  if (nd.op_type() != "Identity" || nd.input_size() != 1 || nd.output_size() != 1) continue;
286  const std::string& src = nd.input(0);
287  const std::string& dst = nd.output(0);
288  if (!output_names.count(dst)) continue; // only fuse into graph outputs
289  if (output_names.count(src)) continue; // src is itself an output (e.g. aliased)
290  if (producers[src] != 1) continue; // src must be produced by a single node
291  if (consumers[src] != 1) continue; // and consumed only by this Identity
292  if (rename.count(src) || drop.count(n)) continue; // src already claimed by another output
293  rename[src] = dst;
294  drop.insert(n);
295  }
296  if (!drop.empty()) {
297  // Apply: rewrite producer outputs, then rebuild node list without the dropped Identities.
298  for (int n = 0; n < nodes->size(); ++n) {
299  if (drop.count(n)) continue;
300  onnx::NodeProto* nd = nodes->Mutable(n);
301  for (int o = 0; o < nd->output_size(); ++o) {
302  auto it = rename.find(nd->output(o));
303  if (it != rename.end()) nd->set_output(o, it->second);
304  }
305  }
306  google::protobuf::RepeatedPtrField<onnx::NodeProto> kept;
307  for (int n = 0; n < nodes->size(); ++n)
308  if (!drop.count(n)) kept.Add()->CopyFrom(nodes->Get(n));
309  nodes->Swap(&kept);
310  }
311  }
312 
313  // Add graph outputs (for main graph, use empty prefix to use function's output names)
314  add_graph_outputs(graph, f, "");
315 
316  // An output not produced by any node (e.g. an all-structural-zero output, whose MXFunction has
317  // no instructions) needs an explicit zero Constant so both ORT and the importer have a tensor.
318  // A non-dense pattern is planted directly as a sparse_value (all-zero) Constant -- the seed
319  // that imports to exactly that pattern; a dense output gets a plain dense zero Constant.
320  {
321  std::set<std::string> produced;
322  for (const auto& nd : graph->node())
323  for (const auto& o : nd.output()) produced.insert(o);
324  auto add_node = [graph]() -> onnx::NodeProto* { return graph->add_node(); };
325  for (casadi_int i = 0; i < f.n_out(); ++i) {
326  std::string oname = onnx_output_name(f, i);
327  if (!produced.count(oname)) {
328  const Sparsity& sp = f.sparsity_out(i);
329  if (sp.is_dense()) {
330  add_real_constant(add_node, oname,
331  std::vector<double>(sp.size1() * sp.size2(), 0.0),
332  {sp.size2(), sp.size1()});
333  } else {
334  add_sparse_constant(add_node, oname, DM(sp, 0.0));
335  }
336  }
337  }
338  }
339 
340  // Input sparsity seed: the ONNX graph value-flow is dense, so a non-dense CasADi input pattern
341  // is planted as a standard sparse_initializer (all-zero values) sharing the input name -- an
342  // optional input with a sparse zero default, readable by any ONNX tool. Import picks it up as a
343  // sparse MX and re-propagates sparsity natively. OUTPUT patterns need no overlay: each op
344  // restores its own output sparsity from seeds (emit_sparsity_restore), so they recover for
345  // free.
346  for (casadi_int i = 0; i < f.n_in(); ++i) {
347  if (!f.sparsity_in(i).is_dense()) {
348  fill_sparse_tensor(graph->add_sparse_initializer(), onnx_input_name(f, i),
349  DM(f.sparsity_in(i), 0.0));
350  }
351  }
352 
353  // If we exported any functions, add the casadi domain opset_import
354  if (!exported_functions_.empty()) {
355  onnx::OperatorSetIdProto* casadi_opset = model_.add_opset_import();
356  casadi_opset->set_domain("casadi");
357  casadi_opset->set_version(1);
358 
359  if (verbose_) {
360  uout() << " Exported " << exported_functions_.size()
361  << " function(s) to casadi domain" << std::endl;
362  }
363  }
364 
365  has_model_ = true;
366 
367  if (verbose_) {
368  uout() << "Converted CasADi Function to ONNX model: " << f.name() << std::endl;
369  uout() << " Instructions processed: " << n_instr << std::endl;
370  uout() << " ONNX nodes created: " << graph->node_size() << std::endl;
371  }
372  }
static const char * version()
Obtain the version number of CasADi.
Definition: casadi_meta.cpp:30
std::set< std::string > exported_functions_
Track which functions have been exported as FunctionProto.
Definition: onnx_model.hpp:119
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1028
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
std::string onnx_output_name(const Function &f, casadi_int i)
Definition: onnx_export.cpp:39
void add_int_attribute(onnx::NodeProto *node, const std::string &name, casadi_int value)
Add an integer attribute (e.g. axis) to a node.
Matrix< double > DM
Definition: dm_fwd.hpp:33
std::string onnx_input_name(const Function &f, casadi_int i)
Function input/output name, or a generated fallback when unnamed.
Definition: onnx_export.cpp:34
std::ostream & uout()
@ OP_OUTPUT
Definition: calculus.hpp:82
@ OP_CALL
Definition: calculus.hpp:88

References casadi::add_int_attribute(), casadi::C, casadi::Sparsity::dense(), casadi::MX::dep(), exported_functions_, has_model_, casadi::MX::info(), casadi::Function::instruction_id(), casadi::Function::instruction_input(), casadi::Function::instruction_MX(), casadi::Function::instruction_output(), casadi::Sparsity::is_dense(), model_, casadi::Function::n_in(), casadi::Function::n_instructions(), casadi::Function::n_out(), casadi::Function::name(), casadi::onnx_input_name(), casadi::onnx_output_name(), casadi::OP_CALL, casadi::OP_OUTPUT, casadi::GenericMatrix< MatType >::size1(), casadi::Sparsity::size1(), casadi::Function::size1_out(), casadi::GenericMatrix< MatType >::size2(), casadi::Sparsity::size2(), casadi::Function::size2_out(), casadi::MX::sparsity(), casadi::Function::sparsity_in(), casadi::Function::sparsity_out(), casadi::uout(), casadi::GraphModelInternal::verbose_, casadi::CasadiMeta::version(), and casadi::MX::which_function().

Referenced by export_symbolic().

◆ load_bytes()

void casadi::Onnx::load_bytes ( const std::vector< uint8_t > &  data)

Definition at line 73 of file onnx_model.cpp.

73  {
74  casadi_assert(model_.ParseFromArray(data.data(), static_cast<int>(data.size())),
75  "Failed to parse ONNX model from memory");
76  has_model_ = true;
77  }

References has_model_, and model_.

Referenced by Onnx().

◆ load_library()

handle_t casadi::PluginInterface< GraphModelInternal >::load_library ( const std::string &  libname,
std::string &  resultpath,
bool  global 
)
staticinherited

Definition at line 92 of file plugin_interface.hpp.

187  {
188 
189 #ifndef WITH_DL
190  casadi_error("WITH_DL option needed for dynamic loading");
191 #else // WITH_DL
192 
193  // Get the name of the shared library
194  std::string lib = std::string(CasadiMeta::shared_library_prefix()) + libname +
195  CasadiMeta::shared_library_suffix();
196 
197  // Build up search paths;
198  std::vector<std::string> search_paths = get_search_paths();
199  return open_shared_library(lib, search_paths, resultpath,
200  "PluginInterface::load_plugin", global);
201 
202 #endif // WITH_DL
203  }
std::vector< std::string > get_search_paths()
Definition: casadi_os.cpp:79

◆ load_plugin()

PluginInterface< GraphModelInternal >::Plugin casadi::PluginInterface< GraphModelInternal >::load_plugin ( const std::string &  pname,
bool  register_plugin = true,
bool  needs_lock = true 
)
staticinherited

Definition at line 88 of file plugin_interface.hpp.

208  {
209 
210 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
211  casadi::conditional_lock_guard<std::mutex> lock(Derived::mutex_solvers_, needs_lock);
212 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
213 
214  // Issue warning and quick return if already loaded
215  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
216  casadi_warning("PluginInterface: Solver " + pname + " is already in use. Ignored.");
217  return Plugin();
218  }
219 
220  // Logger singletons are lazily instantiated on first uout()/uerr() calls
221  // This instantation may lead to a data race with potential instatiations in plugin
222  // To be safe, trigger instantatin before any plugin loading
223  uout();
224  uerr();
225 
226 #ifndef WITH_DL
227  casadi_error("WITH_DL option needed for dynamic loading");
228 #else // WITH_DL
229  // Retrieve the registration function
230  RegFcn reg;
231 
232  // Load the dll
233  std::string regName = "casadi_register_" + Derived::infix_ + "_" + pname;
234 
235  std::string searchpath;
236  handle_t handle = load_library("casadi_" + Derived::infix_ + "_" + pname, searchpath,
237  false);
238 
239 #ifdef _WIN32
240 
241 #if __GNUC__
242 #pragma GCC diagnostic push
243 #pragma GCC diagnostic ignored "-Wcast-function-type"
244 #endif
245  reg = reinterpret_cast<RegFcn>(GetProcAddress(handle, TEXT(regName.c_str())));
246 #if __GNUC__
247 #pragma GCC diagnostic pop
248 #endif
249 
250 #else // _WIN32
251  // Reset error
252  dlerror();
253 
254  // Load creator
255  reg = reinterpret_cast<RegFcn>(dlsym(handle, regName.c_str()));
256 #endif // _WIN32
257  casadi_assert(reg!=nullptr,
258  "PluginInterface::load_plugin: no \"" + regName + "\" found in " + searchpath + ".");
259 
260  // Create a temporary struct
261  Plugin plugin = pluginFromRegFcn(reg);
262  // Register the plugin
263  if (register_plugin) {
264  registerPlugin(plugin, false);
265  }
266 
267  return plugin;
268 
269 #endif // WITH_DL
270  }
static handle_t load_library(const std::string &libname, std::string &resultpath, bool global)
Load a library dynamically.
static Plugin pluginFromRegFcn(RegFcn regfcn)
Instantiate a Plugin struct from a factory function.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
std::ostream & uerr()
void * handle_t
Definition: casadi_os.hpp:109

◆ model_data()

const std::vector<uint8_t>& casadi::GraphModelInternal::model_data ( ) const
inlineinherited

Definition at line 90 of file graph_model_internal.hpp.

90 { return model_data_; }
std::vector< uint8_t > model_data_
Raw model bytes (empty when constructed for export only)

Referenced by Onnx().

◆ plugin_deserialize()

Deserialize casadi::PluginInterface< GraphModelInternal >::plugin_deserialize ( const std::string &  pname)
staticinherited

Definition at line 82 of file plugin_interface.hpp.

165  {
166  Deserialize m = getPlugin(pname).deserialize;
167  casadi_assert(m, "Plugin \"" + pname + "\" does not support deserialize");
168  return m;
169  }

◆ plugin_name()

const char* casadi::Onnx::plugin_name ( ) const
inlineoverridevirtual

Implements casadi::GraphModelInternal.

Definition at line 71 of file onnx_model.hpp.

71 { return "onnx"; }

◆ plugin_options()

const Options & casadi::PluginInterface< GraphModelInternal >::plugin_options ( const std::string &  pname)
staticinherited

Definition at line 79 of file plugin_interface.hpp.

158  {
159  const Options *op = getPlugin(pname).options;
160  casadi_assert(op!=nullptr, "Plugin \"" + pname + "\" does not support options");
161  return *op;
162  }

◆ pluginFromRegFcn()

PluginInterface< GraphModelInternal >::Plugin casadi::PluginInterface< GraphModelInternal >::pluginFromRegFcn ( RegFcn  regfcn)
staticinherited

Definition at line 85 of file plugin_interface.hpp.

173  {
174  // Create a temporary struct
175  Plugin plugin;
176 
177  // Set the fields
178  int flag = regfcn(&plugin);
179  casadi_assert(flag==0, "Registration of plugin failed.");
180 
181  return plugin;
182  }

◆ registerPlugin() [1/2]

void casadi::PluginInterface< GraphModelInternal >::registerPlugin ( const Plugin &  plugin,
bool  needs_lock = true 
)
staticinherited

Definition at line 96 of file plugin_interface.hpp.

278  {
279 
280 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
281  casadi::conditional_lock_guard<std::mutex> lock(Derived::mutex_solvers_, needs_lock);
282 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
283 
284  // Check if the solver name is in use
285  typename std::map<std::string, Plugin>::iterator it=Derived::solvers_.find(plugin.name);
286  casadi_assert(it==Derived::solvers_.end(),
287  "Solver " + str(plugin.name) + " is already in use");
288 
289  // Add to list of solvers
290  Derived::solvers_[plugin.name] = plugin;
291  }

◆ registerPlugin() [2/2]

void casadi::PluginInterface< GraphModelInternal >::registerPlugin ( RegFcn  regfcn,
bool  needs_lock = true 
)
staticinherited

Definition at line 99 of file plugin_interface.hpp.

273  {
274  registerPlugin(pluginFromRegFcn(regfcn), needs_lock);
275  }

◆ save_bytes()

std::vector< uint8_t > casadi::Onnx::save_bytes ( ) const

Definition at line 79 of file onnx_model.cpp.

79  {
80  casadi_assert(has_model_, "No ONNX model loaded.");
81  std::string s;
82  casadi_assert(model_.SerializeToString(&s), "Failed to serialize ONNX model");
83  return std::vector<uint8_t>(s.begin(), s.end());
84  }

References has_model_, and model_.

Referenced by export_symbolic().

◆ serialize_type()

void casadi::PluginInterface< GraphModelInternal >::serialize_type ( SerializingStream s) const
inlineinherited

Extra doc: https://github.com/casadi/casadi/wiki/L_rq

Definition at line 117 of file plugin_interface.hpp.

117  {
118  s.pack("PluginInterface::plugin_name", std::string(plugin_name()));
119  }

◆ set_dimension()

void casadi::Onnx::set_dimension ( const std::string &  name,
casadi_int  dim 
)

Definition at line 71 of file onnx_utilities.cpp.

71  {
72  dimension_overrides_[name] = dim;
73 
74  if (verbose_) {
75  uout() << "Set dimension override: " << name << " = " << dim << std::endl;
76  }
77  }
std::map< std::string, casadi_int > dimension_overrides_
Dimension overrides for symbolic dimensions.
Definition: onnx_model.hpp:113

References dimension_overrides_, casadi::uout(), and casadi::GraphModelInternal::verbose_.

Referenced by import_symbolic().

◆ shared_from_this() [1/2]

B casadi::GenericSharedInternal< SharedObject , SharedObjectInternal >::shared_from_this ( )
inlineprotectedinherited

Definition at line 83 of file generic_shared_internal.hpp.

83  {
84  casadi_assert_dev(B::test_cast(static_cast<Internal*>(this)));
85  B ret;
86  ret.own(static_cast<Internal*>(this));
87  return ret;
88  }

◆ shared_from_this() [2/2]

const B casadi::GenericSharedInternal< SharedObject , SharedObjectInternal >::shared_from_this ( ) const
inlineprotectedinherited

Definition at line 92 of file generic_shared_internal.hpp.

92  {
93  casadi_assert_dev(B::test_cast(static_cast<const Internal*>(this)));
94  B ret;
95  ret.own(const_cast<Internal*>(static_cast<const Internal*>(this)));
96  return ret;
97  }

◆ shortname()

static std::string casadi::GraphModelInternal::shortname ( )
inlinestaticinherited

Definition at line 106 of file graph_model_internal.hpp.

106 { return "graphmodel"; }

◆ weak()

Extra doc: https://github.com/casadi/casadi/wiki/L_1ai

Definition at line 67 of file generic_shared_internal.hpp.

210  {
211 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
212  auto* w = weak_ref_.load(std::memory_order_acquire);
213  if (!w) {
214  auto* nw = new GenericWeakRef<Shared, Internal>(static_cast<Internal*>(this));
215  GenericWeakRef<Shared, Internal>* expected = nullptr;
216  if (weak_ref_.compare_exchange_strong(
217  expected, nw, std::memory_order_release, std::memory_order_acquire)) {
218  w = nw;
219  } else {
220  delete nw; // lost the race; another thread published first
221  w = expected;
222  }
223  }
224  return w;
225 #else
226  if (weak_ref_==nullptr) {
227  weak_ref_ = new GenericWeakRef<Shared, Internal>(static_cast<Internal*>(this));
228  }
229  return weak_ref_;
230 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
231  }

Member Data Documentation

◆ casadi_real_

std::string casadi::Onnx::casadi_real_ = "double"
protected

Definition at line 122 of file onnx_model.hpp.

◆ dimension_overrides_

std::map<std::string, casadi_int> casadi::Onnx::dimension_overrides_
protected

Definition at line 113 of file onnx_model.hpp.

Referenced by set_dimension().

◆ exported_functions_

std::set<std::string> casadi::Onnx::exported_functions_
protected

Definition at line 119 of file onnx_model.hpp.

Referenced by load().

◆ has_model_

bool casadi::Onnx::has_model_ = false
protected

Definition at line 116 of file onnx_model.hpp.

Referenced by create(), load(), load_bytes(), and save_bytes().

◆ infix_

const std::string casadi::GraphModelInternal::infix_ = "graphmodel"
staticinherited

Definition at line 103 of file graph_model_internal.hpp.

◆ meta_doc

const std::string casadi::Onnx::meta_doc
static
Initial value:
=
"ONNX backend for GraphModel: protobuf metadata, symbolic import and export.\n"

Definition at line 89 of file onnx_model.hpp.

Referenced by casadi::casadi_register_graphmodel_onnx().

◆ model_

onnx::ModelProto casadi::Onnx::model_
protected

Definition at line 110 of file onnx_model.hpp.

Referenced by create(), fill_metadata(), load(), load_bytes(), and save_bytes().

◆ model_data_

std::vector<uint8_t> casadi::GraphModelInternal::model_data_
protectedinherited

Definition at line 110 of file graph_model_internal.hpp.

◆ options_

const Options casadi::Onnx::options_
static
Initial value:
{{"casadi_real",
{OT_STRING, "Real type for exported tensors: 'double' (default) or 'float'"}}
}
}
static const Options options_
Options.

Definition at line 76 of file onnx_model.hpp.

Referenced by casadi::casadi_register_graphmodel_onnx().

◆ solvers_

std::map< std::string, GraphModelInternal::Plugin > casadi::GraphModelInternal::solvers_
staticinherited

Definition at line 96 of file graph_model_internal.hpp.

◆ verbose_

bool casadi::GraphModelInternal::verbose_
protectedinherited

The documentation for this class was generated from the following files: