From e8429b2fa0e83810342a7206c00e413bd52a0af6 Mon Sep 17 00:00:00 2001 From: Vaggelis Date: Mon, 24 Aug 2026 05:11:57 -0400 Subject: [PATCH] graphsurgeon: fold value_float constants as float32 Constant nodes carrying value_float or value_floats attributes were lowered by fold_constants() into float64 tensors because np.array defaults to double for Python floats. ONNX defines both attributes as float32, so the folded initializers had the wrong type and models produced from them were rejected by ONNX Runtime with a type error on consumers such as Add. Lower the two attributes explicitly as float32 and cover both with a regression test asserting dtype, shape and values of the folded constant. value_int and value_ints already land on int64 through the default path, which matches their ONNX types, so they are left alone. Test Plan: cd tools/onnx-graphsurgeon PYTHONPATH=$PWD python -m pytest "tests/ir/test_graph.py::TestFoldConstants::test_value_float_attrs_fold_as_float32" fails before the fix (initializer dtype DOUBLE), passes after PYTHONPATH=$PWD python -m pytest tests --ignore=tests/test_examples.py 281 passed, 1 skipped (test_examples excluded: its harness spawns bash with python3, which does not exist on Windows) Signed-off-by: Vaggelis --- tools/onnx-graphsurgeon/CHANGELOG.md | 8 +++++++ .../onnx_graphsurgeon/ir/graph.py | 3 +++ .../onnx-graphsurgeon/tests/ir/test_graph.py | 24 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/tools/onnx-graphsurgeon/CHANGELOG.md b/tools/onnx-graphsurgeon/CHANGELOG.md index 3d18b72d0..117af7fa8 100644 --- a/tools/onnx-graphsurgeon/CHANGELOG.md +++ b/tools/onnx-graphsurgeon/CHANGELOG.md @@ -3,6 +3,14 @@ Dates are in YYYY-MM-DD format. +## vNext + +### Fixed +- Fixed `Graph.fold_constants()` lowering `Constant` nodes specified with `value_float` or + `value_floats` into float64 (`DOUBLE`) constants. ONNX defines these attributes as float32, + and the folded constants now match, producing models that ONNX Runtime accepts. + + ## v0.6.2 (2026-05-21) ### Added diff --git a/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py b/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py index c7ce3b6ff..81609328f 100644 --- a/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py +++ b/tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py @@ -823,6 +823,9 @@ def should_exclude_node(node): continue elif isinstance(attr_val, Constant): arr = attr_val._values # Using ._values avoids copying + elif attr_name in ("value_float", "value_floats"): + # ONNX defines these attributes as float32 + arr = np.array(attr_val, dtype=np.float32) else: arr = np.array(attr_val) tensor.to_constant(arr) diff --git a/tools/onnx-graphsurgeon/tests/ir/test_graph.py b/tools/onnx-graphsurgeon/tests/ir/test_graph.py index 47ed2a2ef..3d8b0dc32 100644 --- a/tools/onnx-graphsurgeon/tests/ir/test_graph.py +++ b/tools/onnx-graphsurgeon/tests/ir/test_graph.py @@ -1360,6 +1360,30 @@ def test_with_invalid_nodes(self, foldable_with_invalid_node): tensor_map["c"].values == (np.ones(shape=(1, 3), dtype=np.float32) * 2) ) + @pytest.mark.parametrize( + "attrs", + [{"value_float": 1.5}, {"value_floats": [1.5, 2.5]}], + ) + def test_value_float_attrs_fold_as_float32(self, attrs): + # ONNX defines value_float and value_floats as float32 attributes. + graph = Graph(ir_version=10) + inp = Variable("input", shape=(2,), dtype=np.float32) + const_out = Variable("c") + graph.nodes.append(Node(op="Constant", attrs=attrs, outputs=[const_out])) + out = graph.add(inp, const_out, name="out") + graph.inputs = [inp] + graph.outputs = [out] + + graph.fold_constants().cleanup() + + assert len(graph.nodes) == 1 + folded = graph.nodes[0].inputs[1] + attr_name = "value_float" if "value_float" in attrs else "value_floats" + expected = np.array(attrs[attr_name], dtype=np.float32) + assert folded.dtype == np.float32 + assert folded.values.shape == expected.shape + assert np.array_equal(folded.values, expected) + def test_with_invalid_nodes_no_recursive(self, foldable_with_invalid_node): # No folding should take place without recursive partitioning original = foldable_with_invalid_node.copy()