Repository navigation
Description
Expected behavior
Chaining ONNX sequence operations—such as computing the length of a sequence produced by SplitToSequence via SequenceLength, or performing sequence operations like SequenceErase or SequenceInsert—should import successfully without crashing.
Actual behavior
When SequenceLength receives a sequence produced by an upstream operator (such as SplitToSequence), from_onnx crashes during node construction with:
Error converting operator SequenceLength, with inputs: [R.split(x, indices_or_sections=T.int64(6), ty=T.Tuple(R.Tensor((1,), dtype="float32"), ...))]
Traceback (most recent call last):
File "repro.py", line 14, in <module>
mod = from_onnx(model)
File "python/tvm/relax/frontend/onnx/onnx_frontend.py", line 6864, in from_onnx
return g.from_onnx(graph, opset)
File "python/tvm/relax/frontend/onnx/onnx_frontend.py", line 6406, in from_onnx
self._construct_nodes(graph)
File "python/tvm/relax/frontend/onnx/onnx_frontend.py", line 6606, in _construct_nodes
op = self._convert_operator(op_name, inputs, attr, self.opset)
File "python/tvm/relax/frontend/onnx/onnx_frontend.py", line 6703, in _convert_operator
sym = op_function(self.bb, inputs, attrs, [self._nodes, self._params])
File "python/tvm/relax/frontend/onnx/onnx_frontend.py", line 5766, in _impl_v11
return relax.const(len(inputs[0]), dtype="int64")
^^^^^^^^^^^^^^
TypeError: object of type 'Call' has no len()
Similarly, SequenceErase and SequenceInsert crash with the same error at lines 5715 and 5747:
- Line 5715:
seq_len = len(input_sequence)->TypeError: object of type 'Call' has no len() - Line 5747:
seq_len = len(input_sequence)->TypeError: object of type 'Call' has no len()
Environment
- TVM Version: 0.28.dev0 (commit
cbb1e12feonmain) - ONNX Version: 1.23.2
- Python Version: 3.12.2
- Operating System: Linux x86_64
Steps to reproduce
import onnx
from onnx import helper, TensorProto
from tvm.relax.frontend.onnx import from_onnx
split_node = helper.make_node("SplitToSequence", inputs=["x"], outputs=["seq"])
len_node = helper.make_node("SequenceLength", inputs=["seq"], outputs=["length"])
graph = helper.make_graph(
[split_node, len_node],
"seq_len_graph",
[helper.make_tensor_value_info("x", TensorProto.FLOAT, [6])],
[helper.make_tensor_value_info("length", TensorProto.INT64, [])],
)
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 11)])
mod = from_onnx(model)Root Cause Analysis
In python/tvm/relax/frontend/onnx/onnx_frontend.py:
class SequenceLength(OnnxOpConverter):
"""Operator converter for sequence length op."""
@classmethod
def _impl_v11(cls, bb, inputs, attr, params):
# Get length of input sequence
return relax.const(len(inputs[0]), dtype="int64")The converter assumes that inputs[0] is always a Python list or relax.Tuple (from SequenceConstruct). However, when the sequence is produced by an operator returning a tuple (like SplitToSequence), inputs[0] is an AST expression (relax.Call or relax.Var with a TupleType). Relax Call and Var AST nodes do not implement __len__, so calling len(inputs[0]) raises a TypeError.
Proposed Fix
In SequenceLength, inspect the type info (TupleType.fields) when inputs[0] is a Relax expression:
class SequenceLength(OnnxOpConverter):
"""Operator converter for sequence length op."""
@classmethod
def _impl_v11(cls, bb, inputs, attr, params):
seq = inputs[0]
if isinstance(seq, (list, tuple, relax.Tuple)):
seq_len = len(seq)
elif hasattr(seq.ty, "fields"):
seq_len = len(seq.ty.fields)
else:
raise NotImplementedError("Unable to determine static sequence length for SequenceLength")
return relax.const(seq_len, dtype="int64")The same helper should be used in SequenceErase and SequenceInsert to determine sequence length.
Triage
- needs-triage
- relax:frontend
Activity