Files
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

313 lines
10 KiB
Python

#
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from onnx_graphsurgeon.logger import G_LOGGER
from onnx_graphsurgeon.ir.tensor import Tensor, Constant, Variable
from onnx_graphsurgeon.ir.graph import Graph
from onnx_graphsurgeon.ir.node import Node
from onnx_graphsurgeon.importers.onnx_importer import OnnxImporter
G_LOGGER.severity = G_LOGGER.ULTRA_VERBOSE
from collections import OrderedDict
import onnx.numpy_helper
from typing import List
import numpy as np
import onnx
import os
TEST_ROOT = os.path.realpath(os.path.dirname(__file__))
class Model(object):
def __init__(
self,
path: str,
inputs: List[Tensor],
outputs: List[Tensor],
nodes: List[Node],
opset: int = None,
):
self.path = path
self.inputs = inputs
self.outputs = outputs
self.nodes = nodes
self.opset = opset
def load(self):
return onnx.load(self.path)
def assert_equal(self, graph: Graph):
assert graph.inputs == self.inputs
G_LOGGER.debug("Graph inputs matched")
# Break down fields to make debugging failures easier.
for actual, expected in zip(graph.nodes, self.nodes):
def check_tensor_io(actensor, extensor):
def check_list(aclist, exlist):
G_LOGGER.debug(
"Actual node list: {:}\n\nExpected node list: {:}".format(
aclist, exlist
)
)
assert len(aclist) == len(exlist)
for acnode, exnode in zip(aclist, exlist):
assert acnode == exnode
G_LOGGER.debug("Checking tensor: {:} inputs".format(actensor.name))
check_list(actensor.inputs, extensor.inputs)
G_LOGGER.debug("Checking tensor: {:} outputs".format(actensor.name))
check_list(actensor.outputs, extensor.outputs)
G_LOGGER.debug(
"Actual Node: {:}\n\nExpected Node: {:}".format(actual, expected)
)
assert actual.op == expected.op
assert actual.inputs == expected.inputs
# Check I/O of input tensors
for acinp, exinp in zip(actual.inputs, expected.inputs):
check_tensor_io(acinp, exinp)
assert actual.outputs == expected.outputs
# Check I/O of output tensors
for acout, exout in zip(actual.outputs, expected.outputs):
check_tensor_io(acout, exout)
assert actual.name == expected.name
assert len(actual.attrs) == len(expected.attrs)
for (ackey, acval), (exkey, exval) in zip(
actual.attrs.items(), expected.attrs.items()
):
assert ackey == exkey
assert acval == exval
assert actual == expected
G_LOGGER.debug("Graph nodes matched")
assert graph.outputs == self.outputs
G_LOGGER.debug("Graph outputs matched")
def __str__(self):
return os.path.basename(self.path)
def identity_model():
path = os.path.join(TEST_ROOT, "models", "identity.onnx")
model = onnx.load(path)
x = Variable(name="x", dtype=np.float32, shape=(1, 1, 2, 2))
y = Variable(name="y", dtype=np.float32, shape=(1, 1, 2, 2))
node = Node(op="Identity", inputs=[x], outputs=[y])
return Model(
path, inputs=[x], outputs=[y], nodes=[node], opset=OnnxImporter.get_opset(model)
)
def dim_param_model():
path = os.path.join(TEST_ROOT, "models", "dim_param.onnx")
model = onnx.load(path)
x = Variable(name="Input:0", dtype=np.float32, shape=("dim0", 16, 128))
y = Variable(name="Output:0", dtype=np.float32, shape=("dim0", 16, 128))
node = Node(op="Identity", inputs=[x], outputs=[y])
return Model(
path, inputs=[x], outputs=[y], nodes=[node], opset=OnnxImporter.get_opset(model)
)
def lstm_model():
path = os.path.join(TEST_ROOT, "models", "lstm.onnx")
model = onnx.load(path)
onnx_graph = model.graph
def load_initializer(index: int) -> np.ndarray:
return onnx.numpy_helper.to_array(onnx_graph.initializer[index])
# Optional inputs are represented by empty tensors
X = Variable(name="X", dtype=np.float32, shape=(4, 3, 6))
W = Constant(name="W", values=load_initializer(0))
R = Constant(name="R", values=load_initializer(1))
B = Constant(name="B", values=load_initializer(2))
initial_c = Constant(name="initial_c", values=load_initializer(3))
Y = Variable(name="Y", dtype=np.float32, shape=(4, 1, 3, 5))
Y_h = Variable(name="Y_h", dtype=np.float32, shape=(1, 3, 5))
Y_c = Variable(name="Y_c", dtype=np.float32, shape=(1, 3, 5))
attrs = OrderedDict()
attrs["direction"] = "forward"
attrs["hidden_size"] = 5
node = Node(
op="LSTM",
attrs=attrs,
inputs=[X, W, R, B, Variable.empty(), Variable.empty(), initial_c],
outputs=[Y, Y_h, Y_c],
)
# Initializers will not be included in the graph inputs.
return Model(
path,
inputs=[X],
outputs=[Y, Y_h, Y_c],
nodes=[node],
opset=OnnxImporter.get_opset(model),
)
def scan_model():
path = os.path.join(TEST_ROOT, "models", "scan.onnx")
model = onnx.load(path)
# Body graph
sum_in = Variable(name="sum_in", dtype=np.float32, shape=(2,))
next = Variable(name="next", dtype=np.float32, shape=(2,))
sum_out = Variable(name="sum_out", dtype=np.float32, shape=(2,))
scan_out = Variable(name="scan_out", dtype=np.float32, shape=(2,))
body_nodes = [
Node(op="Add", inputs=[sum_in, next], outputs=[sum_out]),
Node(op="Identity", inputs=[sum_out], outputs=[scan_out]),
]
body_graph = Graph(
nodes=body_nodes,
inputs=[sum_in, next],
outputs=[sum_out, scan_out],
name="scan_body",
)
# Outer graph
inputs = [
Variable(name="initial", dtype=np.float32, shape=(2,)),
Variable(name="x", dtype=np.float32, shape=(3, 2)),
]
outputs = [
Variable(name="y", dtype=np.float32, shape=(2,)),
Variable(name="z", dtype=np.float32, shape=(3, 2)),
]
attrs = OrderedDict()
attrs["body"] = body_graph
attrs["num_scan_inputs"] = 1
scan_node = Node(op="Scan", inputs=inputs, outputs=outputs, attrs=attrs)
return Model(
path,
inputs=inputs,
outputs=outputs,
nodes=[scan_node],
opset=OnnxImporter.get_opset(model),
)
def initializer_is_output_model():
path = os.path.join(TEST_ROOT, "models", "initializer_is_output.onnx")
model = onnx.load(path)
X = Constant(name="X", values=np.ones((64, 64), dtype=np.float32))
return Model(
path, inputs=[], outputs=[X], nodes=[], opset=OnnxImporter.get_opset(model)
)
# Node includes a subgraph whose I/O names are the same as that of the node.
def nested_dup_names():
path = os.path.join(TEST_ROOT, "models", "nested_dup_names.onnx")
model = onnx.load(path)
# Inner
subgraph_inputs = [Variable("X", shape=(2, 2), dtype=np.float32)]
subgraph_outputs = [Variable("Y", shape=(2, 2), dtype=np.float32)]
subgraph_node = Node(
op="Identity", inputs=subgraph_inputs, outputs=subgraph_outputs
)
subgraph = Graph(
nodes=[subgraph_node], inputs=subgraph_inputs, outputs=subgraph_outputs
)
# Outer - problem happens if outer node has same I/O names as subgraph
inputs = [Variable("X", shape=(2, 2), dtype=np.float32)]
outputs = [Variable("Y", shape=(2, 2), dtype=np.float32)]
node = Node(op="Nested", inputs=inputs, outputs=outputs, attrs={"body": subgraph})
return Model(
path,
inputs=inputs,
outputs=outputs,
nodes=[node],
opset=OnnxImporter.get_opset(model),
)
def ext_weights():
path = os.path.join(TEST_ROOT, "models", "ext_weights.onnx")
model = onnx.load(path)
inputs = [Variable("input", shape=(1, 3), dtype=np.float32)]
outputs = [Variable("output", shape=(1, 3), dtype=np.float32)]
a = Constant("a", values=np.ones((1, 3), dtype=np.float32))
b = Constant("b", values=np.ones((1, 3), dtype=np.float32))
d = Constant("d", values=np.ones((1, 3), dtype=np.float32))
c = Variable("c")
e = Variable("e")
nodes = [
Node(op="Add", inputs=[a, b], outputs=[c]),
Node(op="Add", inputs=[c, d], outputs=[e]),
Node(op="Add", inputs=[inputs[0], e], outputs=outputs),
]
return Model(
path,
inputs=inputs,
outputs=outputs,
nodes=nodes,
opset=OnnxImporter.get_opset(model),
)
def const_foldable():
path = os.path.join(TEST_ROOT, "models", "const_foldable.onnx")
return Model(
path, inputs=None, outputs=None, nodes=None, opset=None
) # Only used for path.
def shape_cast_elision():
path = os.path.join(TEST_ROOT, "models", "shape_cast_elision.onnx")
return Model(
path, inputs=None, outputs=None, nodes=None, opset=None
) # Only used for path.
def sparse_nnz_model():
path = os.path.join(TEST_ROOT, "models", "sparse_nnz.onnx")
return Model(
path, inputs=None, outputs=None, nodes=None, opset=None
) # Only used for path.
def sparse_nnz_rank_model():
path = os.path.join(TEST_ROOT, "models", "sparse_nnz_rank.onnx")
return Model(
path, inputs=None, outputs=None, nodes=None, opset=None
) # Only used for path.