onnx_model.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_model.hpp"
26 #include <casadi/core/graph_builder_internal.hpp>
27 
28 namespace casadi {
29 
30  // Defined in casadi core (onnx.cpp): human-readable name of an ONNX element-type enum
31  std::string onnx_dtype_name(casadi_int elem_type);
32 
33  extern "C"
34  int CASADI_GRAPHMODEL_ONNX_EXPORT
35  casadi_register_graphmodel_onnx(GraphModelInternal::Plugin* plugin) {
36  plugin->creator = Onnx::creator;
37  plugin->name = "onnx";
38  plugin->doc = Onnx::meta_doc.c_str();
39  plugin->version = CASADI_VERSION;
40  plugin->options = &Onnx::options_;
41  return 0;
42  }
43 
44  extern "C"
45  void CASADI_GRAPHMODEL_ONNX_EXPORT casadi_load_graphmodel_onnx() {
47  }
48 
49  const std::string Onnx::meta_doc =
50  "ONNX backend for GraphModel: protobuf metadata, symbolic import and export.\n";
51 
52  const Options Onnx::options_
54  {{"casadi_real",
55  {OT_STRING, "Real type for exported tensors: 'double' (default) or 'float'"}}
56  }
57  };
58 
59  Onnx::Onnx(const std::vector<uint8_t>& model_data) : GraphModelInternal(model_data) {
60  if (!model_data.empty()) load_bytes(model_data);
61  }
62 
64  }
65 
66  void Onnx::init(const Dict& opts) {
68  for (auto&& op : opts) {
69  if (op.first == "casadi_real") set_casadi_real(op.second.to_string());
70  }
71  }
72 
73  void Onnx::load_bytes(const std::vector<uint8_t>& data) {
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  }
78 
79  std::vector<uint8_t> Onnx::save_bytes() const {
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  }
85 
86  // Build a Node descriptor from a graph input/output ValueInfo
87  static Node io_node(const onnx::ValueInfoProto& vi, const std::string& io) {
88  Node n;
89  n.name = vi.name();
90  n.io = io;
91  const onnx::TypeProto::Tensor& tt = vi.type().tensor_type();
92  n.dtype = onnx_dtype_name(tt.elem_type());
93  const onnx::TensorShapeProto& sh = tt.shape();
94  for (int k = 0; k < sh.dim_size(); ++k) {
95  const auto& d = sh.dim(k);
96  if (d.has_dim_value()) {
97  n.dimension.push_back(static_cast<casadi_int>(d.dim_value()));
98  n.dim_params.push_back("");
99  } else { // symbolic or unspecified -> dynamic
100  n.dimension.push_back(-1);
101  n.dim_params.push_back(d.has_dim_param() ? d.dim_param() : "");
102  }
103  }
104  return n;
105  }
106 
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  }
119 
120  Function Onnx::import_symbolic(const GraphBuilderInternal& gb, const std::string& name) {
121  for (auto&& b : gb.dim_bindings()) set_dimension(b.first, b.second);
122  return create(name);
123  }
124 
125  std::vector<uint8_t> Onnx::export_symbolic(const Function& f, const Dict& opts) {
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  }
131 
132 } // namespace casadi
Function object.
Definition: function.hpp:60
Internal class for GraphBuilder.
void clear_nodes()
Drop all tensor descriptors (called by a backend before re-filling)
void add_node(const Node &n)
Append a tensor descriptor (called by a backend during fill_metadata)
const std::map< std::string, casadi_int > & dim_bindings() const
Base interface for format-specific graph-model backends.
static const Options options_
Options.
const std::vector< uint8_t > & model_data() const
Raw model bytes.
virtual void init(const Dict &opts)
Initialize.
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)
Definition: onnx_model.cpp:125
static const std::string meta_doc
Documentation.
Definition: onnx_model.hpp:89
static const Options options_
Options.
Definition: onnx_model.hpp:76
Function create(const std::string &name)
Create a CasADi Function from the loaded ONNX graph.
void load_bytes(const std::vector< uint8_t > &data)
Load a graph from serialized ONNX bytes.
Definition: onnx_model.cpp:73
~Onnx() override
Definition: onnx_model.cpp:63
static GraphModelInternal * creator(const std::vector< uint8_t > &model_data)
Plugin factory.
Definition: onnx_model.hpp:67
std::vector< uint8_t > save_bytes() const
Serialize the loaded graph/model to ONNX bytes.
Definition: onnx_model.cpp:79
onnx::ModelProto model_
ONNX model protocol buffer.
Definition: onnx_model.hpp:110
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)
Definition: onnx_model.cpp:120
void init(const Dict &opts) override
Initialize.
Definition: onnx_model.cpp:66
bool has_model_
Whether a model has been loaded.
Definition: onnx_model.hpp:116
void load(const Function &f)
Load a CasADi Function and convert to the ONNX representation.
Definition: onnx_export.cpp:77
Onnx(const std::vector< uint8_t > &model_data)
Definition: onnx_model.cpp:59
void set_dimension(const std::string &name, casadi_int dim)
Set dimension for a symbolic variable.
void fill_metadata(GraphBuilderInternal &gb) const override
Populate a builder's Node metadata from the parsed model.
Definition: onnx_model.cpp:107
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
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, ......
static Node io_node(const onnx::ValueInfoProto &vi, const std::string &io)
Definition: onnx_model.cpp:87
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_GRAPHMODEL_ONNX_EXPORT casadi_register_graphmodel_onnx(GraphModelInternal::Plugin *plugin)
Definition: onnx_model.cpp:35
void CASADI_GRAPHMODEL_ONNX_EXPORT casadi_load_graphmodel_onnx()
Definition: onnx_model.cpp:45
Metadata for one graph tensor (graph input or output)
std::vector< casadi_int > dimension
Declared shape, -1 for dynamic dimensions.
std::string name
Tensor name.
std::string io
"input" or "output"
std::string dtype
Element type name (FLOAT, INT64, ...)
std::vector< std::string > dim_params
Symbolic name per axis ("" if static)