[Bug][Relax][ONNX] A model-local Add function executes the built-in Add instead of its Sub body
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 25/100
Direzione di ricerca
Start in python/tvm/relax/frontend/onnx/onnx_frontend.py, where the node loop reads node.op_type and _convert_operator picks the built-in conversion map by name alone, ignoring node.domain and any model-local function. Run the repro script's local_function case, which should return 21 instead of 15, and compare with the inlined_control case. Done means local functions are either expanded from their body or explicitly rejected, with a regression test added.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
The smallest example I found is a one-node call to a local function.
I reduced a wrong-result case to a model-local function named Add in the domain example.local. Its body contains Sub, so inputs 18 and 3 must produce 15. Relax returns 21. Inlining that exact function before importing restores 15.
A runnable case
Save the code below as repro.py. With an LLVM-enabled TVM installation and the dependencies listed at the end, run OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 python repro.py.
from pathlib import Path
import hashlib
import numpy as np
import onnx
import onnxruntime as ort
import tvm
from onnx import TensorProto as T, helper as h, numpy_helper as nh
from onnx.reference import ReferenceEvaluator
from tvm import relax
from tvm.relax.frontend.onnx import from_onnx
def make(nodes, inputs, outputs, initializers=(), opset=18):
model = h.make_model(h.make_graph(nodes, "repro", inputs, outputs, list(initializers)),
opset_imports=[h.make_opsetid("", opset)], ir_version=10)
onnx.checker.check_model(model, full_check=True)
return model
def tvm_run(model, feeds):
mod = from_onnx(model, keep_params_in_input=False)
names = [value.name for value in model.graph.input]
executable = relax.build(mod, target="llvm", relax_pipeline="default")
result = relax.VirtualMachine(executable, tvm.cpu())["main"](
*[tvm.runtime.tensor(feeds[name]) for name in names])
return [result.numpy()] if hasattr(result, "numpy") else [v.numpy() for v in result]
def save(label, model, feeds):
folder = Path("artifacts") / label
folder.mkdir(parents=True, exist_ok=True)
onnx.checker.check_model(model, full_check=True)
onnx.save(model, folder / "model.onnx")
np.savez(folder / "inputs.npz", **feeds)
print(label, "model_sha256", hashlib.sha256((folder / "model.onnx").read_bytes()).hexdigest())
print(label, "inputs_sha256", hashlib.sha256((folder / "inputs.npz").read_bytes()).hexdigest())
def reference(model, feeds, expected, check_ort=True):
outputs = ReferenceEvaluator(model).run(None, feeds)
for a, b in zip(outputs, expected, strict=True):
assert a.shape == b.shape and a.dtype == b.dtype, (a.shape, a.dtype, b.shape, b.dtype)
np.testing.assert_allclose(a, b, rtol=1e-5, atol=1e-6)
if check_ort:
opts = ort.SessionOptions()
opts.intra_op_num_threads = opts.inter_op_num_threads = 1
opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL
outputs = ort.InferenceSession(model.SerializeToString(), opts,
providers=["CPUExecutionProvider"]).run(None, feeds)
for a, b in zip(outputs, expected, strict=True):
assert a.shape == b.shape and a.dtype == b.dtype
np.testing.assert_allclose(a, b, rtol=1e-5, atol=1e-6)
def error_line(error):
return type(error).__name__ + ": " + str(error).splitlines()[-1]
def same(actual, expected):
assert actual.shape == expected.shape and actual.dtype == expected.dtype
np.testing.assert_allclose(actual, expected, rtol=1e-5, atol=1e-6)
from onnx.inliner import inline_local_functions
function = h.make_function("example.local", "Add", ["a", "b"], ["out"],
[h.make_node("Sub", ["a", "b"], ["out"])], [h.make_opsetid("", 18)])
model = h.make_model(h.make_graph(
[h.make_node("Add", ["x", "z"], ["y"], domain="example.local")], "local_function",
[h.make_tensor_value_info(n, T.FLOAT, [1]) for n in ("x", "z")],
[h.make_tensor_value_info("y", T.FLOAT, [1])]),
opset_imports=[h.make_opsetid("", 18), h.make_opsetid("example.local", 1)],
functions=[function], ir_version=10)
feeds = {"x": np.array([18], dtype="float32"), "z": np.array([3], dtype="float32")}
expected = feeds["x"] - feeds["z"]
save("local_function", model, feeds)
reference(model, feeds, [expected])
actual = tvm_run(model, feeds)[0]
print("model-local Add body is Sub; expected", expected.tolist(), "TVM", actual.tolist())
assert actual.tolist() == [21.0]
inlined = inline_local_functions(model)
save("inlined_control", inlined, feeds)
same(tvm_run(inlined, feeds)[0], expected)
print("function-inlining control passed")
What I see
model-local Add body is Sub; expected [15.0] TVM [21.0]
function-inlining control passed
The expected/actual distinction is: Local function: 15; TVM: 21. Identical model after ONNX function inlining: TVM returns 15.
Why I think this is the relevant boundary
An ONNX function is resolved using its domain and name, with the model-local function body defining its computation. The witness passes full ONNX checking; ReferenceEvaluator and unoptimized ORT CPU both execute Sub and return 15.
The node construction loop obtains node.op_type, and _convert_operator selects the built-in conversion map using only that name. This silently substitutes a different function instead of respecting domain/body or explicitly declining unsupported functions.
Scope of this example
The preserved source and controls identify domain/body dispatch, rather than Relax closure lifetime or function-reference rewriting. Targeted searches did not identify an earlier report for this exact model-local function collision; no global novelty claim is made.
This self-contained script is a newly constructed minimal diagnostic from the same graph-testing investigation, not a byte-identical replay of an archived model. It needs only TVM, ONNX, NumPy and ONNX Runtime, downloads no files, and writes its generated models and inputs to artifacts/. The assertions check both the observed failure/wrong result and the normal controls; exit code zero means that diagnostic reproduced. No global novelty or current-main execution claim is made.
Reproduction setup
Linux x86_64 CPU/LLVM; Python 3.12.3; TVM 0.28.dev0; LLVM 18.1.3; ONNX 1.22.0; NumPy 2.5.4; ONNX Runtime 1.29.0 (CPUExecutionProvider).
The installed TVM binary's exact build source revision is UNKNOWN. Source links are a separate static inspection of main at 70ec7d5bf20326ef301ee3f06eb6aed51760331d; that revision was not built or executed for this report.
Loaded binary SHA256 identities:
libtvm_ffi.so:12072d0ccd9d5cb2c6dac80d42f4217ab50dbf795db03197000614a7a770a816libtvm_compiler.so:574a573fe994e5eda7352ddd8d685e64e45c7934bdeeaab57a5a272a77792ab1libtvm_runtime.so:4175916fc0ce6266c912629d2255094b94087425c6ab53ffd5e004d4c292c828libtvm_runtime_extra.so:79c93351fca12dd7851964890cea7da65dd2b2e0e64e5c28bfed56c878b70817
The code and inputs are small enough to recreate without an attachment. For an exact file check, repro.py has SHA256 47a5735855a75cc25bc03c41b0029f1dd24d179ee4a937af645143624375832e. Generated model/input checksums from this run:
artifacts/inlined_control/inputs.npz:25ae56865463aa811bec82cec1025975105523acf9ee32da8b290dfff420f868artifacts/inlined_control/model.onnx:f8eb546276aea5630f35253dba5c98d9465b46317f63d4351a96fedbe69b0a4aartifacts/local_function/inputs.npz:25ae56865463aa811bec82cec1025975105523acf9ee32da8b290dfff420f868artifacts/local_function/model.onnx:ebbf49c2d2e8a35742da88cbcb8dfc547c80d004d5315eedb6f4b093041573b4
I can follow up with a focused regression test and adjust the example if another supported lowering path is preferred.
- Lingua principale
- Python
- Stelle
- 13.9k
- Fork
- 4k
- Merge medio
- 7h 59m
- PR unite (30g)
- 159
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di apache/tvm
-
needs-triage type: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 80/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug] [Relax][ONNX] OneHot does not convert floating-point depth to int64Forse già presa @tandede l’ha presa 2 giorni fa. Apertaneeds-triage type: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
needs-triage type: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
needs-triage type: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug][Relax] ReorderTakeAfterMatmul fails on vector dot productsForse già presa @hiyufan l’ha presa 12 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
apache/tvm#20595 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Issue simili
-
documentation good first issue
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
MarketSquare/robotframework-browser#5479 ·
I maintainer di solito rispondono entro 1 giorno
-
Visual COBOL: missing 11.0 and 12.0, and 9.0 date differs from Rocket Software's lifecycle pageAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
endoflife-date/endoflife.date#11561 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
OCA/l10n-brazil#5602 ·
I maintainer di solito rispondono entro 1 giorno
-
documentation review-2.0.0
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dkuenzel/meshcore-home-gateway#106 ·
I maintainer di solito rispondono entro 1 giorno