From ef02a9866f5139cf7420143c017ad3fc37d699a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Sun, 30 Nov 2025 11:45:23 +0100 Subject: [PATCH 1/5] add code rendering --- .../test_translate_classic.py | 96 +++++++++---------- onnx_array_api/tools/replace_constants.py | 1 - onnx_array_api/translate_api/base_emitter.py | 2 +- onnx_array_api/translate_api/inner_emitter.py | 51 ++++------ 4 files changed, 61 insertions(+), 89 deletions(-) diff --git a/_unittests/ut_translate_api/test_translate_classic.py b/_unittests/ut_translate_api/test_translate_classic.py index 864a2e2..e4859dc 100644 --- a/_unittests/ut_translate_api/test_translate_classic.py +++ b/_unittests/ut_translate_api/test_translate_classic.py @@ -2,17 +2,13 @@ import os from textwrap import dedent import numpy as np +import onnx +import onnx.helper as oh +import onnx.numpy_helper as onh from onnx import ModelProto, TensorProto, load from onnx.defs import onnx_opset_version from onnx.reference import ReferenceEvaluator from onnx.reference.op_run import OpRun -from onnx.helper import ( - make_tensor_value_info, - make_node, - make_graph, - make_model, - make_opsetid, -) from onnx.checker import check_model from onnx_array_api.ext_test_case import ExtTestCase from onnx_array_api.light_api import start @@ -23,19 +19,17 @@ class TestTranslateClassic(ExtTestCase): def test_check_code(self): - opset_imports = [ - make_opsetid("", 19), - ] + opset_imports = [oh.make_opsetid("", 19)] inputs = [] outputs = [] nodes = [] initializers = [] sparse_initializers = [] functions = [] - inputs.append(make_tensor_value_info("X", TensorProto.FLOAT, shape=[])) - nodes.append(make_node("Exp", ["X"], ["Y"])) - outputs.append(make_tensor_value_info("Y", TensorProto.FLOAT, shape=[])) - graph = make_graph( + inputs.append(oh.make_tensor_value_info("X", TensorProto.FLOAT, shape=[])) + nodes.append(oh.make_node("Exp", ["X"], ["Y"])) + outputs.append(oh.make_tensor_value_info("Y", TensorProto.FLOAT, shape=[])) + graph = oh.make_graph( nodes, "onename", inputs, @@ -43,7 +37,7 @@ def test_check_code(self): initializers, sparse_initializer=sparse_initializers, ) - model = make_model(graph, functions=functions, opset_imports=opset_imports) + model = oh.make_model(graph, functions=functions, opset_imports=opset_imports) check_model(model) def test_exp(self): @@ -60,7 +54,7 @@ def test_exp(self): expected = dedent( """ opset_imports = [ - make_opsetid('', 19), + oh.make_opsetid('', 19), ] inputs = [] outputs = [] @@ -68,7 +62,7 @@ def test_exp(self): initializers = [] sparse_initializers = [] functions = [] - inputs.append(make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Exp', @@ -76,8 +70,8 @@ def test_exp(self): ['Y'] ) ) - outputs.append(make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) - graph = make_graph( + outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + graph = oh.make_graph( nodes, 'light_api', inputs, @@ -85,7 +79,7 @@ def test_exp(self): initializers, sparse_initializer=sparse_initializers, ) - model = make_model( + model = oh.make_model( graph, functions=functions, opset_imports=opset_imports @@ -130,7 +124,7 @@ def test_transpose(self): expected = dedent( """ opset_imports = [ - make_opsetid('', 19), + oh.make_opsetid('', 19), ] inputs = [] outputs = [] @@ -139,12 +133,12 @@ def test_transpose(self): sparse_initializers = [] functions = [] initializers.append( - from_array( + onh.from_array( np.array([-1, 1], dtype=np.int64), name='r' ) ) - inputs.append(make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Reshape', @@ -160,8 +154,8 @@ def test_transpose(self): perm=[1, 0] ) ) - outputs.append(make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) - graph = make_graph( + outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + graph = oh.make_graph( nodes, 'light_api', inputs, @@ -169,7 +163,7 @@ def test_transpose(self): initializers, sparse_initializer=sparse_initializers, ) - model = make_model( + model = oh.make_model( graph, functions=functions, opset_imports=opset_imports @@ -199,7 +193,7 @@ def test_transpose_short(self): expected = dedent( """ opset_imports = [ - make_opsetid('', 19), + oh.make_opsetid('', 19), ] inputs = [] outputs = [] @@ -208,12 +202,12 @@ def test_transpose_short(self): sparse_initializers = [] functions = [] initializers.append( - from_array( + onh.from_array( np.array([-1, 1], dtype=np.int64), name='r' ) ) - inputs.append(make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Reshape', @@ -229,8 +223,8 @@ def test_transpose_short(self): perm=[1, 0] ) ) - outputs.append(make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) - graph = make_graph( + outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + graph = oh.make_graph( nodes, 'light_api', inputs, @@ -238,7 +232,7 @@ def test_transpose_short(self): initializers, sparse_initializer=sparse_initializers, ) - model = make_model( + model = oh.make_model( graph, functions=functions, opset_imports=opset_imports @@ -270,7 +264,7 @@ def test_topk_reverse(self): expected = dedent( """ opset_imports = [ - make_opsetid('', 19), + oh.make_opsetid('', 19), ] inputs = [] outputs = [] @@ -278,8 +272,8 @@ def test_topk_reverse(self): initializers = [] sparse_initializers = [] functions = [] - inputs.append(make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) - inputs.append(make_tensor_value_info('K', TensorProto.INT64, shape=[])) + inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('K', TensorProto.INT64, shape=[])) nodes.append( make_node_extended( 'TopK', @@ -290,9 +284,9 @@ def test_topk_reverse(self): sorted=1 ) ) - outputs.append(make_tensor_value_info('Values', TensorProto.FLOAT, shape=[])) - outputs.append(make_tensor_value_info('Indices', TensorProto.FLOAT, shape=[])) - graph = make_graph( + outputs.append(oh.make_tensor_value_info('Values', TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Indices', TensorProto.FLOAT, shape=[])) + graph = oh.make_graph( nodes, 'light_api', inputs, @@ -300,7 +294,7 @@ def test_topk_reverse(self): initializers, sparse_initializer=sparse_initializers, ) - model = make_model( + model = oh.make_model( graph, functions=functions, opset_imports=opset_imports @@ -338,8 +332,8 @@ def test_aionnxml(self): expected = dedent( """ opset_imports = [ - make_opsetid('', 19), - make_opsetid('ai.onnx.ml', 3), + oh.make_opsetid('', 19), + oh.make_opsetid('ai.onnx.ml', 3), ] inputs = [] outputs = [] @@ -348,12 +342,12 @@ def test_aionnxml(self): sparse_initializers = [] functions = [] initializers.append( - from_array( + onh.from_array( np.array([-1, 1], dtype=np.int64), name='r' ) ) - inputs.append(make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Reshape', @@ -370,8 +364,8 @@ def test_aionnxml(self): norm='MAX' ) ) - outputs.append(make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) - graph = make_graph( + outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + graph = oh.make_graph( nodes, 'light_api', inputs, @@ -379,7 +373,7 @@ def test_aionnxml(self): initializers, sparse_initializer=sparse_initializers, ) - model = make_model( + model = oh.make_model( graph, functions=functions, opset_imports=opset_imports @@ -402,9 +396,6 @@ def _run(cls, code): f"Compilation failed due to {e}\n---\n{cls._code_line(code)}\n---\n{e}" ) from e - import onnx - import onnx.helper - import onnx.numpy_helper import onnx_array_api.translate_api.make_helper import ml_dtypes @@ -430,10 +421,11 @@ def from_array_extended(tensor, name=None): return t globs = onnx.__dict__.copy() - globs.update(onnx.helper.__dict__) - globs.update(onnx.numpy_helper.__dict__) globs.update(onnx_array_api.translate_api.make_helper.__dict__) - globs.update(ml_dtypes.__dict__) + globs["np"] = np + globs["oh"] = oh + globs["onh"] = onh + globs["ml_dtypes"] = ml_dtypes globs["from_array_extended"] = from_array_extended locs = {} try: diff --git a/onnx_array_api/tools/replace_constants.py b/onnx_array_api/tools/replace_constants.py index daa4ca8..d1bbffe 100644 --- a/onnx_array_api/tools/replace_constants.py +++ b/onnx_array_api/tools/replace_constants.py @@ -52,7 +52,6 @@ def replace_initializer_by_constant_of_shape( ) ) dtype = cst.dtype - assert op_type != "Constant" new_nodes.append( make_node( op_type, diff --git a/onnx_array_api/translate_api/base_emitter.py b/onnx_array_api/translate_api/base_emitter.py index 0e98c76..3f34a4a 100644 --- a/onnx_array_api/translate_api/base_emitter.py +++ b/onnx_array_api/translate_api/base_emitter.py @@ -131,7 +131,7 @@ def render_attribute_value(self, value: Any) -> Tuple[List[str], str]: sdtype = repl.get(str(v.dtype), str(str(v.dtype))) package = "np" if hasattr(np, sdtype) else "ml_dtypes" return [], ( - f"from_array(np.array({v.tolist()}, dtype={package}.{sdtype}), " + f"onh.from_array(np.array({v.tolist()}, dtype={package}.{sdtype}), " f"name={value[0].name!r})" ) if isinstance(v, (int, float, list)): diff --git a/onnx_array_api/translate_api/inner_emitter.py b/onnx_array_api/translate_api/inner_emitter.py index 1044289..cc459ed 100644 --- a/onnx_array_api/translate_api/inner_emitter.py +++ b/onnx_array_api/translate_api/inner_emitter.py @@ -23,7 +23,7 @@ def render_attribute_value(self, value: Any) -> Tuple[List[str], str]: rows = tr.export(as_str=False, single_line=False) new_rows = [f"def _make_local_graph_{value[0].name}():"] for line in rows: - if "make_model" in line: + if "oh.make_model" in line: break new_rows.append(" " + line) new_rows.append(" return graph") @@ -35,10 +35,9 @@ def render_attribute_value(self, value: Any) -> Tuple[List[str], str]: def _make_attribute( self, name: str, attr_type: int, ref_attr_name: Optional[str] = None ) -> str: - if ref_attr_name is None: - raise NotImplementedError( - f"Cannot create attribute with name={name!r}, attr_type={attr_type}." - ) + assert ( + ref_attr_name is not None + ), f"Cannot create attribute with name={name!r}, attr_type={attr_type}." return ( f"make_ref_attribute(key={name!r}, attr_type={attr_type}, " f"ref_attr_name={ref_attr_name!r})" @@ -52,13 +51,13 @@ def _emit_start(self, **kwargs: Dict[str, Any]) -> List[str]: lines = ["opset_imports = ["] opsets = kwargs.get("opsets", {}) for k, v in opsets.items(): - lines.append(f" make_opsetid({k!r}, {v!r}),") + lines.append(f" oh.make_opsetid({k!r}, {v!r}),") lines.append("]") return lines def _emit_to_onnx_model(self, **kwargs: Dict[str, Any]) -> List[str]: lines = [ - "model = make_model(", + "model = oh.make_model(", " graph,", " functions=functions,", " opset_imports=opset_imports", @@ -80,7 +79,7 @@ def _emit_begin_graph(self, **kwargs: Dict[str, Any]) -> List[str]: def _emit_end_graph(self, **kwargs: Dict[str, Any]) -> List[str]: name = kwargs.get("name", "noname") lines = [ - "graph = make_graph(", + "graph = oh.make_graph(", " nodes,", f" {name!r},", " inputs,", @@ -95,18 +94,9 @@ def _emit_initializer(self, **kwargs: Dict[str, Any]) -> List[str]: name = kwargs["name"] value = kwargs["value"] repl = {"bool": "bool_", "object": "object_", "str": "str_"} - fra = "from_array" + fra = "onh.from_array" sdtype = repl.get(str(value.dtype), str(value.dtype)) - if sdtype.startswith("("): - from onnx.reference.custom_element_types import float8e4m3fn - - if sdtype == str(float8e4m3fn): - sdtype = "float8e4m3fn" - fra = "from_array_extended" - else: - raise NotImplementedError(f"Unexpected dtype={sdtype}.") - else: - sdtype = f"np.{sdtype}" if hasattr(np, sdtype) else f"ml_dtypes.{sdtype}" + sdtype = f"np.{sdtype}" if hasattr(np, sdtype) else f"ml_dtypes.{sdtype}" return [ "initializers.append(", @@ -123,17 +113,17 @@ def _emit_io(self, container: str, **kwargs: Dict[str, Any]) -> List[str]: shape = kwargs.get("shape", None) if elem_type and shape: return [ - f"{container}.append(make_tensor_value_info({name!r}, " + f"{container}.append(oh.make_tensor_value_info({name!r}, " f"TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, shape={shape!r}))" ] if elem_type: return [ - f"{container}.append(make_tensor_value_info({name!r}, " + f"{container}.append(oh.make_tensor_value_info({name!r}, " f"TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, shape=[]))" ] return [ - f"{container}.append(make_tensor_value_info({name!r}, " - f"TensorProto.UNDEFINED, []))" + f"{container}.append(oh.make_tensor_value_info({name!r}, " + f"onnx.TensorProto.UNDEFINED, []))" ] def _emit_input(self, **kwargs: Dict[str, Any]) -> List[str]: @@ -199,7 +189,7 @@ def _emit_function_attributes(self, **kwargs: Dict[str, Any]) -> List[str]: def _emit_end_function(self, **kwargs: Dict[str, Any]) -> List[str]: lines = [ "functions.append(", - " make_function(", + " oh.make_function(", " domain_f, ", " name_f, ", " inputs, ", @@ -223,18 +213,9 @@ def _emit_initializer(self, **kwargs: Dict[str, Any]) -> List[str]: name = kwargs["name"] value = kwargs["value"] repl = {"bool": "bool_", "object": "object_", "str": "str_"} - fra = "from_array" + fra = "onh.from_array" sdtype = repl.get(str(value.dtype), str(value.dtype)) - if sdtype.startswith("("): - from onnx.reference.custom_element_types import float8e4m3fn - - if sdtype == str(float8e4m3fn): - sdtype = "float8e4m3fn" - fra = "from_array_extended" - else: - raise NotImplementedError(f"Unexpected dtype={sdtype}.") - else: - sdtype = f"np.{sdtype}" if hasattr(np, sdtype) else f"ml_dtypes.{sdtype}" + sdtype = f"np.{sdtype}" if hasattr(np, sdtype) else f"ml_dtypes.{sdtype}" if value.size <= 16: return [ "initializers.append(", From c03d391a61f6652e705f5a6d8b3158083ba30035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Sun, 30 Nov 2025 11:46:04 +0100 Subject: [PATCH 2/5] =?UTF-8?q?ch=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOGS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index 07d9593..30b21c8 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -4,6 +4,7 @@ Change Logs 0.3.2 +++++ +* :pr:`104`: add code rendering when conveting a model into code * :pr:`103`: fix import issue with the latest onnx version * :pr:`101`: fix as_tensor in onnx_text_plot_tree From bc37c7b37538acf7ae049687c38fe7bf73995806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Sun, 30 Nov 2025 12:04:38 +0100 Subject: [PATCH 3/5] fix --- .../ut_light_api/test_backend_export.py | 7 +++ _unittests/ut_translate_api/test_translate.py | 42 ++++++++--------- .../test_translate_builder.py | 46 +++++++++---------- .../test_translate_classic.py | 32 ++++++------- onnx_array_api/translate_api/__init__.py | 34 ++++++++++++++ .../translate_api/builder_emitter.py | 6 ++- onnx_array_api/translate_api/inner_emitter.py | 8 ++-- onnx_array_api/translate_api/light_emitter.py | 13 ++++-- 8 files changed, 116 insertions(+), 72 deletions(-) diff --git a/_unittests/ut_light_api/test_backend_export.py b/_unittests/ut_light_api/test_backend_export.py index a19360b..2c581ec 100644 --- a/_unittests/ut_light_api/test_backend_export.py +++ b/_unittests/ut_light_api/test_backend_export.py @@ -4,12 +4,15 @@ from difflib import unified_diff import packaging.version as pv import numpy +import ml_dtypes from numpy.testing import assert_allclose from onnx.defs import onnx_opset_version import onnx.backend.base import onnx.backend.test import onnx.shape_inference import onnx.version_converter +import onnx.helper as oh +import onnx.numpy_helper as onh from onnx import ModelProto, TensorProto, __version__ as onnx_version from onnx.helper import ( make_function, @@ -94,6 +97,10 @@ def run( locs = { "np": numpy, + "ml_dtypes": ml_dtypes, + "onnx": onnx, + "oh": oh, + "onh": onh, "to_array": to_array, "to_array_extended": to_array_extended, "from_array": from_array, diff --git a/_unittests/ut_translate_api/test_translate.py b/_unittests/ut_translate_api/test_translate.py index 98629d8..4d95f8d 100644 --- a/_unittests/ut_translate_api/test_translate.py +++ b/_unittests/ut_translate_api/test_translate.py @@ -1,7 +1,7 @@ import unittest from textwrap import dedent import numpy as np -from onnx import ModelProto, TensorProto +import onnx from onnx.defs import onnx_opset_version from onnx.reference import ReferenceEvaluator from onnx_array_api.ext_test_case import ExtTestCase @@ -20,7 +20,7 @@ def test_event_type(self): def test_exp(self): onx = start(opset=19).vin("X").Exp().rename("Y").vout().to_onnx() - self.assertIsInstance(onx, ModelProto) + self.assertIsInstance(onx, onnx.ModelProto) self.assertIn("Exp", str(onx)) ref = ReferenceEvaluator(onx) a = np.arange(10).astype(np.float32) @@ -32,12 +32,12 @@ def test_exp(self): """ ( start(opset=19) - .vin('X', elem_type=TensorProto.FLOAT) + .vin('X', elem_type=onnx.TensorProto.FLOAT) .bring('X') .Exp() .rename('Y') .bring('Y') - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .to_onnx() )""" ).strip("\n") @@ -45,12 +45,12 @@ def test_exp(self): onx2 = ( start(opset=19) - .vin("X", elem_type=TensorProto.FLOAT) + .vin("X", elem_type=onnx.TensorProto.FLOAT) .bring("X") .Exp() .rename("Y") .bring("Y") - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .to_onnx() ) ref = ReferenceEvaluator(onx2) @@ -68,7 +68,7 @@ def test_transpose(self): .vout() .to_onnx() ) - self.assertIsInstance(onx, ModelProto) + self.assertIsInstance(onx, onnx.ModelProto) self.assertIn("Transpose", str(onx)) ref = ReferenceEvaluator(onx) a = np.arange(10).astype(np.float32) @@ -82,7 +82,7 @@ def test_transpose(self): start(opset=19) .cst(np.array([-1, 1], dtype=np.int64)) .rename('r') - .vin('X', elem_type=TensorProto.FLOAT) + .vin('X', elem_type=onnx.TensorProto.FLOAT) .bring('X', 'r') .Reshape() .rename('r0_0') @@ -90,7 +90,7 @@ def test_transpose(self): .Transpose(perm=[1, 0]) .rename('Y') .bring('Y') - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .to_onnx() )""" ).strip("\n") @@ -107,7 +107,7 @@ def test_topk_reverse(self): .vout() .to_onnx() ) - self.assertIsInstance(onx, ModelProto) + self.assertIsInstance(onx, onnx.ModelProto) ref = ReferenceEvaluator(onx) x = np.array([[0, 1, 2, 3], [9, 8, 7, 6]], dtype=np.float32) k = np.array([2], dtype=np.int64) @@ -120,15 +120,15 @@ def test_topk_reverse(self): """ ( start(opset=19) - .vin('X', elem_type=TensorProto.FLOAT) - .vin('K', elem_type=TensorProto.INT64) + .vin('X', elem_type=onnx.TensorProto.FLOAT) + .vin('K', elem_type=onnx.TensorProto.INT64) .bring('X', 'K') .TopK(axis=-1, largest=0, sorted=1) .rename('Values', 'Indices') .bring('Values') - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .bring('Indices') - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .to_onnx() )""" ).strip("\n") @@ -152,7 +152,7 @@ def test_export_if(self): .to_onnx() ) - self.assertIsInstance(onx, ModelProto) + self.assertIsInstance(onx, onnx.ModelProto) ref = ReferenceEvaluator(onx) x = np.array([[0, 1, 2, 3], [9, 8, 7, 6]], dtype=np.float32) k = np.array([2], dtype=np.int64) @@ -162,11 +162,11 @@ def test_export_if(self): code = translate(onx) selse = ( "g().cst(np.array([0], dtype=np.int64)).rename('Z')." - "bring('Z').vout(elem_type=TensorProto.FLOAT)" + "bring('Z').vout(elem_type=onnx.TensorProto.FLOAT)" ) sthen = ( "g().cst(np.array([1], dtype=np.int64)).rename('Z')." - "bring('Z').vout(elem_type=TensorProto.FLOAT)" + "bring('Z').vout(elem_type=onnx.TensorProto.FLOAT)" ) expected = dedent( f""" @@ -174,7 +174,7 @@ def test_export_if(self): start(opset=19) .cst(np.array([0.0], dtype=np.float32)) .rename('r') - .vin('X', elem_type=TensorProto.FLOAT) + .vin('X', elem_type=onnx.TensorProto.FLOAT) .bring('X') .ReduceSum(keepdims=1, noop_with_empty_axes=0) .rename('Xs') @@ -185,7 +185,7 @@ def test_export_if(self): .If(else_branch={selse}, then_branch={sthen}) .rename('W') .bring('W') - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .to_onnx() )""" ).strip("\n") @@ -210,7 +210,7 @@ def test_aionnxml(self): start(opset=19, opsets={'ai.onnx.ml': 3}) .cst(np.array([-1, 1], dtype=np.int64)) .rename('r') - .vin('X', elem_type=TensorProto.FLOAT) + .vin('X', elem_type=onnx.TensorProto.FLOAT) .bring('X', 'r') .Reshape() .rename('USE') @@ -218,7 +218,7 @@ def test_aionnxml(self): .ai.onnx.ml.Normalizer(norm='MAX') .rename('Y') .bring('Y') - .vout(elem_type=TensorProto.FLOAT) + .vout(elem_type=onnx.TensorProto.FLOAT) .to_onnx() )""" ).strip("\n") diff --git a/_unittests/ut_translate_api/test_translate_builder.py b/_unittests/ut_translate_api/test_translate_builder.py index b1ad394..184708b 100644 --- a/_unittests/ut_translate_api/test_translate_builder.py +++ b/_unittests/ut_translate_api/test_translate_builder.py @@ -2,7 +2,7 @@ from textwrap import dedent import numpy as np import onnx.helper as oh -from onnx import ModelProto, TensorProto +import onnx from onnx.checker import check_model from onnx.defs import onnx_opset_version from onnx.reference import ReferenceEvaluator @@ -22,7 +22,7 @@ def setUp(self): def test_exp(self): onx = start(opset=19, ir_version=10).vin("X").Exp().rename("Y").vout().to_onnx() - self.assertIsInstance(onx, ModelProto) + self.assertIsInstance(onx, onnx.ModelProto) self.assertIn("Exp", str(onx)) ref = ReferenceEvaluator(onx) a = np.arange(10).astype(np.float32) @@ -42,9 +42,9 @@ def light_api( return Y g = GraphBuilder({'': 19}, ir_version=10) - g.make_tensor_input("X", TensorProto.FLOAT, ()) + g.make_tensor_input("X", onnx.TensorProto.FLOAT, ()) light_api(g.op, "X") - g.make_tensor_output("Y", TensorProto.FLOAT, ()__SUFFIX__) + g.make_tensor_output("Y", onnx.TensorProto.FLOAT, ()__SUFFIX__) model = g.to_onnx() """ ) @@ -62,10 +62,10 @@ def light_api( return Y g2 = GraphBuilder({"": 19}) - g2.make_tensor_input("X", TensorProto.FLOAT, ("A",)) + g2.make_tensor_input("X", onnx.TensorProto.FLOAT, ("A",)) light_api(g2.op, "X") g2.make_tensor_output( - "Y", TensorProto.FLOAT, ("A",), is_dimension=False, indexed=False + "Y", onnx.TensorProto.FLOAT, ("A",), is_dimension=False, indexed=False ) onx2 = g2.to_onnx() @@ -99,9 +99,9 @@ def light_api( return Y g = GraphBuilder({'': 19}, ir_version=10) - g.make_tensor_input("X", TensorProto.FLOAT, ()) + g.make_tensor_input("X", onnx.TensorProto.FLOAT, ()) light_api(g.op, "X") - g.make_tensor_output("Y", TensorProto.FLOAT, ()__SUFFIX__) + g.make_tensor_output("Y", onnx.TensorProto.FLOAT, ()__SUFFIX__) model = g.to_onnx() """ ) @@ -122,16 +122,16 @@ def light_api( return Y g = GraphBuilder({"": 21}) - X = g.make_tensor_input("X", TensorProto.FLOAT, ()) + X = g.make_tensor_input("X", onnx.TensorProto.FLOAT, ()) light_api(g.op, X) - g.make_tensor_output("Y", TensorProto.FLOAT, ()) + g.make_tensor_output("Y", onnx.TensorProto.FLOAT, ()) model = g.to_onnx() self.assertNotEmpty(model) check_model(model) def test_exp_f(self): onx = start(opset=19, ir_version=10).vin("X").Exp().rename("Y").vout().to_onnx() - self.assertIsInstance(onx, ModelProto) + self.assertIsInstance(onx, onnx.ModelProto) self.assertIn("Exp", str(onx)) ref = ReferenceEvaluator(onx) a = np.arange(10).astype(np.float32) @@ -155,9 +155,9 @@ def light_api( def mm() -> "ModelProto": g = GraphBuilder({'': 19}, ir_version=10) - g.make_tensor_input("X", TensorProto.FLOAT, ()) + g.make_tensor_input("X", onnx.TensorProto.FLOAT, ()) light_api(g.op, "X") - g.make_tensor_output("Y", TensorProto.FLOAT, ()__SUFFIX__) + g.make_tensor_output("Y", onnx.TensorProto.FLOAT, ()__SUFFIX__) model = g.to_onnx() return model @@ -179,10 +179,10 @@ def light_api( return Y g2 = GraphBuilder({"": 19}) - g2.make_tensor_input("X", TensorProto.FLOAT, ("A",)) + g2.make_tensor_input("X", onnx.TensorProto.FLOAT, ("A",)) light_api(g2.op, "X") g2.make_tensor_output( - "Y", TensorProto.FLOAT, ("A",), is_dimension=False, indexed=False + "Y", onnx.TensorProto.FLOAT, ("A",), is_dimension=False, indexed=False ) onx2 = g2.to_onnx() @@ -216,11 +216,11 @@ def test_local_function(self): ], "example", [ - oh.make_tensor_value_info("X", TensorProto.FLOAT, [None, None]), - oh.make_tensor_value_info("A", TensorProto.FLOAT, [None, None]), - oh.make_tensor_value_info("B", TensorProto.FLOAT, [None, None]), + oh.make_tensor_value_info("X", onnx.TensorProto.FLOAT, [None, None]), + oh.make_tensor_value_info("A", onnx.TensorProto.FLOAT, [None, None]), + oh.make_tensor_value_info("B", onnx.TensorProto.FLOAT, [None, None]), ], - [oh.make_tensor_value_info("Y", TensorProto.FLOAT, None)], + [oh.make_tensor_value_info("Y", onnx.TensorProto.FLOAT, None)], ) onnx_model = oh.make_model( @@ -262,11 +262,11 @@ def make_custom_LinearRegression(g: "GraphBuilder"): def mm() -> "ModelProto": g = GraphBuilder({'': 14, 'custom': 1}, ir_version=10) - g.make_tensor_input("X", TensorProto.FLOAT, ('', '')) - g.make_tensor_input("A", TensorProto.FLOAT, ('', '')) - g.make_tensor_input("B", TensorProto.FLOAT, ('', '')) + g.make_tensor_input("X", onnx.TensorProto.FLOAT, ('', '')) + g.make_tensor_input("A", onnx.TensorProto.FLOAT, ('', '')) + g.make_tensor_input("B", onnx.TensorProto.FLOAT, ('', '')) example(g.op, "X", "A", "B") - g.make_tensor_output("Y", TensorProto.FLOAT, ()__SUFFIX__) + g.make_tensor_output("Y", onnx.TensorProto.FLOAT, ()__SUFFIX__) make_custom_LinearRegression(g) model = g.to_onnx() return model diff --git a/_unittests/ut_translate_api/test_translate_classic.py b/_unittests/ut_translate_api/test_translate_classic.py index e4859dc..c0d7954 100644 --- a/_unittests/ut_translate_api/test_translate_classic.py +++ b/_unittests/ut_translate_api/test_translate_classic.py @@ -26,9 +26,9 @@ def test_check_code(self): initializers = [] sparse_initializers = [] functions = [] - inputs.append(oh.make_tensor_value_info("X", TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info("X", onnx.TensorProto.FLOAT, shape=[])) nodes.append(oh.make_node("Exp", ["X"], ["Y"])) - outputs.append(oh.make_tensor_value_info("Y", TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info("Y", onnx.TensorProto.FLOAT, shape=[])) graph = oh.make_graph( nodes, "onename", @@ -62,7 +62,7 @@ def test_exp(self): initializers = [] sparse_initializers = [] functions = [] - inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', onnx.TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Exp', @@ -70,7 +70,7 @@ def test_exp(self): ['Y'] ) ) - outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Y', onnx.TensorProto.FLOAT, shape=[])) graph = oh.make_graph( nodes, 'light_api', @@ -138,7 +138,7 @@ def test_transpose(self): name='r' ) ) - inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', onnx.TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Reshape', @@ -154,7 +154,7 @@ def test_transpose(self): perm=[1, 0] ) ) - outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Y', onnx.TensorProto.FLOAT, shape=[])) graph = oh.make_graph( nodes, 'light_api', @@ -207,7 +207,7 @@ def test_transpose_short(self): name='r' ) ) - inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', onnx.TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Reshape', @@ -223,7 +223,7 @@ def test_transpose_short(self): perm=[1, 0] ) ) - outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Y', onnx.TensorProto.FLOAT, shape=[])) graph = oh.make_graph( nodes, 'light_api', @@ -272,8 +272,8 @@ def test_topk_reverse(self): initializers = [] sparse_initializers = [] functions = [] - inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) - inputs.append(oh.make_tensor_value_info('K', TensorProto.INT64, shape=[])) + inputs.append(oh.make_tensor_value_info('X', onnx.TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('K', onnx.TensorProto.INT64, shape=[])) nodes.append( make_node_extended( 'TopK', @@ -284,8 +284,8 @@ def test_topk_reverse(self): sorted=1 ) ) - outputs.append(oh.make_tensor_value_info('Values', TensorProto.FLOAT, shape=[])) - outputs.append(oh.make_tensor_value_info('Indices', TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Values', onnx.TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Indices', onnx.TensorProto.FLOAT, shape=[])) graph = oh.make_graph( nodes, 'light_api', @@ -347,7 +347,7 @@ def test_aionnxml(self): name='r' ) ) - inputs.append(oh.make_tensor_value_info('X', TensorProto.FLOAT, shape=[])) + inputs.append(oh.make_tensor_value_info('X', onnx.TensorProto.FLOAT, shape=[])) nodes.append( make_node_extended( 'Reshape', @@ -364,7 +364,7 @@ def test_aionnxml(self): norm='MAX' ) ) - outputs.append(oh.make_tensor_value_info('Y', TensorProto.FLOAT, shape=[])) + outputs.append(oh.make_tensor_value_info('Y', onnx.TensorProto.FLOAT, shape=[])) graph = oh.make_graph( nodes, 'light_api', @@ -405,13 +405,13 @@ def from_array_extended(tensor, name=None): dt == onnx.reference.custom_element_types.float8e4m3fn and dt.descr[0][0] == "e4m3fn" ): - to = TensorProto.FLOAT8E4M3FN + to = onnx.TensorProto.FLOAT8E4M3FN dt_to = np.uint8 elif ( dt == onnx.reference.custom_element_types.bfloat16 and dt.descr[0][0] == "bfloat16" ): - to = TensorProto.BFLOAT16 + to = onnx.TensorProto.BFLOAT16 dt_to = np.uint16 else: return onnx.numpy_helper.from_array(tensor, name) diff --git a/onnx_array_api/translate_api/__init__.py b/onnx_array_api/translate_api/__init__.py index a9a8932..8a0456f 100644 --- a/onnx_array_api/translate_api/__init__.py +++ b/onnx_array_api/translate_api/__init__.py @@ -1,9 +1,43 @@ +import textwrap from onnx import ModelProto from .translate import Translater from .inner_emitter import InnerEmitter, InnerEmitterShortInitializer from .builder_emitter import BuilderEmitter +def translate_header(api: str = "light"): + """Returns the necessary header for each api.""" + if api == "light": + return textwrap.dedent( + """ + import numpy as np + immort ml_dtypes + from onnx_array_api.light_api import start + from onnx_array_api.translate_api import translate + """ + ) + if api == "onnx": + return textwrap.dedent( + """ + import numpy as np + immort ml_dtypes + import onnx + import onnx.helper as oh + import onnx.numpy_helper as onh + from onnx_array_api.translate_api.make_helper import make_node_extended + """ + ) + if api == "builder": + return textwrap.dedent( + """ + import numpy as np + immort ml_dtypes + import onnx + from onnx_array_api.graph_api import GraphBuilder + """ + ) + + def translate(proto: ModelProto, single_line: bool = False, api: str = "light") -> str: """ Translates an ONNX proto into a code using :ref:`l-light-api` diff --git a/onnx_array_api/translate_api/builder_emitter.py b/onnx_array_api/translate_api/builder_emitter.py index c9462fd..d37d388 100644 --- a/onnx_array_api/translate_api/builder_emitter.py +++ b/onnx_array_api/translate_api/builder_emitter.py @@ -49,11 +49,13 @@ def _emit_to_onnx_model(self, **kwargs: Dict[str, Any]) -> List[str]: inps = ", ".join(["g.op", *[f'"{i}"' for i in self.inputs]]) inputs = [] for inp, stype, shape in self.inputs_full_: - inputs.append(f'g.make_tensor_input("{inp}", TensorProto.{stype}, {shape})') + inputs.append( + f'g.make_tensor_input("{inp}", onnx.TensorProto.{stype}, {shape})' + ) outputs = [] for inp, stype, shape in self.outputs_full_: outputs.append( - f'g.make_tensor_output("{inp}", TensorProto.{stype}, ' + f'g.make_tensor_output("{inp}", onnx.TensorProto.{stype}, ' f"{shape}, is_dimension=False, indexed=False)" ) rows = [ diff --git a/onnx_array_api/translate_api/inner_emitter.py b/onnx_array_api/translate_api/inner_emitter.py index cc459ed..da558a6 100644 --- a/onnx_array_api/translate_api/inner_emitter.py +++ b/onnx_array_api/translate_api/inner_emitter.py @@ -7,9 +7,7 @@ class InnerEmitter(BaseEmitter): - """ - Converts event into proper code. - """ + """Converts event into proper code.""" def render_attribute_value(self, value: Any) -> Tuple[List[str], str]: """ @@ -114,12 +112,12 @@ def _emit_io(self, container: str, **kwargs: Dict[str, Any]) -> List[str]: if elem_type and shape: return [ f"{container}.append(oh.make_tensor_value_info({name!r}, " - f"TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, shape={shape!r}))" + f"onnx.TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, shape={shape!r}))" ] if elem_type: return [ f"{container}.append(oh.make_tensor_value_info({name!r}, " - f"TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, shape=[]))" + f"onnx.TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, shape=[]))" ] return [ f"{container}.append(oh.make_tensor_value_info({name!r}, " diff --git a/onnx_array_api/translate_api/light_emitter.py b/onnx_array_api/translate_api/light_emitter.py index 983ca66..a76e33c 100644 --- a/onnx_array_api/translate_api/light_emitter.py +++ b/onnx_array_api/translate_api/light_emitter.py @@ -56,12 +56,13 @@ def _emit_input(self, **kwargs: Dict[str, Any]) -> List[str]: shape = kwargs.get("shape", None) if elem_type and shape: return [ - f"vin({name!r}, elem_type=TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, " - f"shape={shape!r})" + f"vin({name!r}, elem_type=onnx.TensorProto." + f"{ELEMENT_TYPE_NAME[elem_type]}, shape={shape!r})" ] if elem_type: return [ - f"vin({name!r}, elem_type=TensorProto.{ELEMENT_TYPE_NAME[elem_type]})" + f"vin({name!r}, elem_type=onnx.TensorProto." + f"{ELEMENT_TYPE_NAME[elem_type]})" ] return [f"vin({name!r})"] @@ -74,11 +75,13 @@ def _emit_output(self, **kwargs: Dict[str, Any]) -> List[str]: shape = kwargs.get("shape", None) if elem_type and shape: inst.append( - f"vout(elem_type=TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, " + f"vout(elem_type=onnx.TensorProto.{ELEMENT_TYPE_NAME[elem_type]}, " f"shape={shape!r})" ) elif elem_type: - inst.append(f"vout(elem_type=TensorProto.{ELEMENT_TYPE_NAME[elem_type]})") + inst.append( + f"vout(elem_type=onnx.TensorProto.{ELEMENT_TYPE_NAME[elem_type]})" + ) else: inst.append("vout()") return inst From 9b50e3a742567e1fec559dc29e738ceab973341c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Sun, 30 Nov 2025 12:21:11 +0100 Subject: [PATCH 4/5] upgrade version --- CHANGELOGS.rst | 6 +++++- _unittests/ut_translate_api/test_translate.py | 7 ++++++- _unittests/ut_xrun_doc/test_command_lines1.py | 2 +- onnx_array_api/__init__.py | 2 +- onnx_array_api/translate_api/__init__.py | 1 + 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOGS.rst b/CHANGELOGS.rst index 30b21c8..462c794 100644 --- a/CHANGELOGS.rst +++ b/CHANGELOGS.rst @@ -1,11 +1,15 @@ Change Logs =========== -0.3.2 +0.3.3 +++++ * :pr:`104`: add code rendering when conveting a model into code * :pr:`103`: fix import issue with the latest onnx version + +0.3.2 ++++++ + * :pr:`101`: fix as_tensor in onnx_text_plot_tree 0.3.1 diff --git a/_unittests/ut_translate_api/test_translate.py b/_unittests/ut_translate_api/test_translate.py index 4d95f8d..d80f5e3 100644 --- a/_unittests/ut_translate_api/test_translate.py +++ b/_unittests/ut_translate_api/test_translate.py @@ -6,7 +6,7 @@ from onnx.reference import ReferenceEvaluator from onnx_array_api.ext_test_case import ExtTestCase from onnx_array_api.light_api import start, g -from onnx_array_api.translate_api import translate +from onnx_array_api.translate_api import translate, translate_header from onnx_array_api.translate_api.base_emitter import EventType OPSET_API = min(19, onnx_opset_version() - 1) @@ -18,6 +18,11 @@ def test_event_type(self): EventType.to_str(EventType.INITIALIZER), "EventType.INITIALIZER" ) + def test_translate_header(self): + for f in ["light", "onnx", "builder"]: + translate_header(f) + self.assertRaise(lambda: translate_header("NONE"), ValueError) + def test_exp(self): onx = start(opset=19).vin("X").Exp().rename("Y").vout().to_onnx() self.assertIsInstance(onx, onnx.ModelProto) diff --git a/_unittests/ut_xrun_doc/test_command_lines1.py b/_unittests/ut_xrun_doc/test_command_lines1.py index 0503f55..aadeaff 100644 --- a/_unittests/ut_xrun_doc/test_command_lines1.py +++ b/_unittests/ut_xrun_doc/test_command_lines1.py @@ -69,7 +69,7 @@ def test_command_translate(self): main(args) code = st.getvalue() - self.assertIn("model = make_model(", code) + self.assertIn("model = oh.make_model(", code) args = ["translate", "-m", model_file, "-a", "light"] st = StringIO() diff --git a/onnx_array_api/__init__.py b/onnx_array_api/__init__.py index 9a56d5b..f3e0d56 100644 --- a/onnx_array_api/__init__.py +++ b/onnx_array_api/__init__.py @@ -2,5 +2,5 @@ APIs to create ONNX Graphs. """ -__version__ = "0.3.2" +__version__ = "0.3.3" __author__ = "Xavier Dupré" diff --git a/onnx_array_api/translate_api/__init__.py b/onnx_array_api/translate_api/__init__.py index 8a0456f..77cb6d1 100644 --- a/onnx_array_api/translate_api/__init__.py +++ b/onnx_array_api/translate_api/__init__.py @@ -36,6 +36,7 @@ def translate_header(api: str = "light"): from onnx_array_api.graph_api import GraphBuilder """ ) + raise ValueError(f"Unexpected value {api!r} for api.") def translate(proto: ModelProto, single_line: bool = False, api: str = "light") -> str: From dd76457d60e49e2ed216eb63be1cf09fc622a7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= Date: Sun, 30 Nov 2025 12:26:08 +0100 Subject: [PATCH 5/5] fix header --- onnx_array_api/translate_api/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/onnx_array_api/translate_api/__init__.py b/onnx_array_api/translate_api/__init__.py index 77cb6d1..fd60de1 100644 --- a/onnx_array_api/translate_api/__init__.py +++ b/onnx_array_api/translate_api/__init__.py @@ -11,7 +11,7 @@ def translate_header(api: str = "light"): return textwrap.dedent( """ import numpy as np - immort ml_dtypes + import ml_dtypes from onnx_array_api.light_api import start from onnx_array_api.translate_api import translate """ @@ -20,7 +20,7 @@ def translate_header(api: str = "light"): return textwrap.dedent( """ import numpy as np - immort ml_dtypes + import ml_dtypes import onnx import onnx.helper as oh import onnx.numpy_helper as onh @@ -31,7 +31,7 @@ def translate_header(api: str = "light"): return textwrap.dedent( """ import numpy as np - immort ml_dtypes + import ml_dtypes import onnx from onnx_array_api.graph_api import GraphBuilder """