translate: turn an ONNX model back into Python code#

translate() converts an existing ModelProto (or GraphProto) into Python code that rebuilds an equivalent model. Two flavours are available:

  • api="onnx-compact" — a single nested onnx_light.onnx.helper expression (oh.make_model(oh.make_graph([...], ...))).

  • api="builder" — an incremental script driving the GraphBuilder (g.inp(...), g.init(...), g.op.<operator>(...), g.out(...), g.to_onnx(...)).

  • api="cpp" — a C++ function that rebuilds the model with GraphBuilder.

translate_header() returns the matching import header, so translate_header(api) + translate(model, api) is a fully runnable Python snippet. The example below builds a small model, prints both flavours and then executes the generated code to rebuild the model.

# sphinx_gallery_thumbnail_path = "_static/gallery_thumbnails/translate.png"

from __future__ import annotations

import numpy as np

import onnx_light.onnx as onnx
import onnx_light.onnx.defs as defs
import onnx_light.onnx.helper as oh
import onnx_light.onnx.numpy_helper as onh
from onnx_light.tools import translate, translate_header

# Built-in operator schemas are registered so the rebuilt models validate.
defs.register_onnx_operator_set_schema()

Build the model#

A tiny graph Y = Add(Mul(X, W), B) with two initializers so the translation exercises nodes, inputs/outputs and initializers.

model = oh.make_model(
    oh.make_graph(
        [oh.make_node("Mul", ["X", "W"], ["XW"]), oh.make_node("Add", ["XW", "B"], ["Y"])],
        "linear",
        [oh.make_tensor_value_info("X", onnx.TensorProto.FLOAT, ["N", 3])],
        [oh.make_tensor_value_info("Y", onnx.TensorProto.FLOAT, ["N", 3])],
        [
            onh.from_array(np.array([1.0, 2.0, 3.0], dtype=np.float32), name="W"),
            onh.from_array(np.array([0.5, 0.5, 0.5], dtype=np.float32), name="B"),
        ],
    ),
    opset_imports=[oh.make_opsetid("", 18)],
    ir_version=8,
)

onnx-compact flavour#

translate_header("onnx-compact") returns the imports and translate(model, api="onnx-compact") the nested oh.make_model expression.

compact_code = translate_header("onnx-compact") + translate(model, api="onnx-compact")
print("=== onnx-compact ===")
print(compact_code)
=== onnx-compact ===
import numpy as np
import ml_dtypes
import onnx_light.onnx as onnx
import onnx_light.onnx.helper as oh
import onnx_light.onnx.numpy_helper as onh
model = oh.make_model(
    oh.make_graph(
        [
            oh.make_node('Mul', ['X', 'W'], ['XW']),
            oh.make_node('Add', ['XW', 'B'], ['Y']),
        ],
        'linear',
        [
            oh.make_tensor_value_info('X', onnx.TensorProto.FLOAT, ('N', 3)),
        ],
        [
            oh.make_tensor_value_info('Y', onnx.TensorProto.FLOAT, ('N', 3)),
        ],
        [
            onh.from_array(np.array([1.0, 2.0, 3.0], dtype=np.float32), name='W'),
            onh.from_array(np.array([0.5, 0.5, 0.5], dtype=np.float32), name='B'),
        ],
    ),
    opset_imports=[oh.make_opsetid('', 18)],
    ir_version=8,
)

builder flavour#

The builder flavour rebuilds the same model step by step with the compact GraphBuilder API (g.inp, g.init, g.op and g.out).

builder_code = translate_header("builder") + translate(model, api="builder")
print("\n=== builder ===")
print(builder_code)
=== builder ===
import numpy as np
import ml_dtypes
import onnx_light.onnx as onnx
import onnx_light.onnx.helper as oh
import onnx_light.onnx.numpy_helper as onh
from onnx_light.onnx_core.graph_builder import GraphBuilder
g = GraphBuilder('linear')
g.set_opset_version('', 18)
g.inp('X', onnx.TensorProto.FLOAT, ('N', 3))
g.init(np.array([1.0, 2.0, 3.0], dtype=np.float32), name='W')
g.init(np.array([0.5, 0.5, 0.5], dtype=np.float32), name='B')
g.op.Mul('X', 'W', outputs=['XW'])
g.op.Add('XW', 'B', outputs=['Y'])
g.out('Y', onnx.TensorProto.FLOAT, ('N', 3))
model = g.to_onnx('model', ir_version=8)

C++ flavour#

The cpp flavour emits a standalone BuildModel function using the native GraphBuilder.

cpp_code = translate_header("cpp") + translate(model, api="cpp")
print("\n=== cpp ===")
print(cpp_code)
=== cpp ===
#include "onnx_core/builder/graph_builder.h"
#include "onnx_op/operator_sets.h"

#include <limits>
#include <string>
#include <vector>

namespace onnx_light = ONNX_LIGHT_NAMESPACE;
onnx_light::ModelProto BuildModel() {
  onnx_light::core::builder::GraphBuilder g(
      "linear", [](const std::string &op_type) {
        return onnx_light::onnx_op::GetAllOnnxOpSchemasWithHistory(op_type, false);
      });
  g.SetOpsetVersion("", 18);
  g.MakeInput("X", onnx_light::core::symbolic::TensorType::kFloat, onnx_light::core::symbolic::SymShape{onnx_light::core::symbolic::SymDim('N'), onnx_light::core::symbolic::SymDim(3)});
  onnx_light::TensorProto initializer_0;
  initializer_0.set_name("W");
  initializer_0.set_data_type(onnx_light::TensorProto::DataType::FLOAT);
  initializer_0.add_dims(3);
  initializer_0.set_raw_data(std::string({static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(128)), static_cast<char>(static_cast<unsigned char>(63)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(64)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(64)), static_cast<char>(static_cast<unsigned char>(64))}));
  g.MakeInitializer(initializer_0);
  onnx_light::TensorProto initializer_1;
  initializer_1.set_name("B");
  initializer_1.set_data_type(onnx_light::TensorProto::DataType::FLOAT);
  initializer_1.add_dims(3);
  initializer_1.set_raw_data(std::string({static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(63)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(63)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(0)), static_cast<char>(static_cast<unsigned char>(63))}));
  g.MakeInitializer(initializer_1);
  g.MakeNode("Mul", {"X", "W"}, {"XW"});
  g.MakeNode("Add", {"XW", "B"}, {"Y"});
  g.MakeOutput("Y", onnx_light::core::symbolic::TensorType::kFloat, onnx_light::core::symbolic::SymShape{onnx_light::core::symbolic::SymDim('N'), onnx_light::core::symbolic::SymDim(3)});
  return g.ToModel(8);
}

Round-trip#

The generated code is plain Python: executing it rebuilds an equivalent model. Here we run the builder snippet and check that the rebuilt graph has the same nodes as the original.

namespace: dict = {}
exec(builder_code, namespace)  # noqa: S102
rebuilt = namespace["model"]

original_ops = [node.op_type for node in model.graph.node]
rebuilt_ops = [node.op_type for node in rebuilt.graph.node]
print("\n=== round-trip ===")
print("original ops:", original_ops)
print("rebuilt ops :", rebuilt_ops)
assert original_ops == rebuilt_ops
=== round-trip ===
original ops: ['Mul', 'Add']
rebuilt ops : ['Mul', 'Add']

Total running time of the script: (0 minutes 0.011 seconds)

Related examples

pretty_onnx: shape info, shape tags, inplace and release annotations

pretty_onnx: shape info, shape tags, inplace and release annotations

Inspect and edit nodes while parsing or serializing with a node callback

Inspect and edit nodes while parsing or serializing with a node callback

Statistics on the weights of an ONNX model

Statistics on the weights of an ONNX model

Gallery generated by Sphinx-Gallery

Example last updated

Date:

2026-10-05