README 建议优先使用 LiteRT Torch 而非本工具;INT8 量化后精度可能因 SiLU/Swish 等激活函数和池化、卷积核尺寸而显著下降;工具不提供向 tflite 写入 label map 等元数据的功能
onnx2tf
PINTO0309MIT活跃维护把 ONNX 模型一键转成 TFLite、TensorFlow、PyTorch 原生代码或 TorchScript,省去跨框架部署时手写转换脚本的麻烦。
A tool for converting ONNX files to LiteRT/TFLite/TensorFlow, PyTorch native code (nn.Module), TorchScript (.pt), state_dict (.pt), Exported Program (.pt2), and Dynamo ONNX. It also supports direct conversion from LiteRT to PyTorch.
把 ONNX 模型转成 TFLite、TensorFlow 或 PyTorch 代码,方便部署到不同推理框架
把 ONNX 模型一键转成 TFLite、TensorFlow、PyTorch 原生代码或 TorchScript,省去跨框架部署时手写转换脚本的麻烦。
核心能力
- 将 ONNX 转换为 LiteRT/TFLite/TensorFlow 模型
- 导出 PyTorch 原生 nn.Module 代码与 TorchScript/state_dict/Exported Program
- 支持从 LiteRT 直接反向转换到 PyTorch
- 覆盖大量 ONNX 算子,含量化算子与 RNN/Attention 类
- 可对转换后的 tflite 做输入输出张量重命名
容易踩的坑
- 2.6.9最新正式版2026-09-14 · 2026-09-14 01:46 UTC
1. Content and background
With
--tflite_backend tf_converter, a Conv whose input has C == H == W and explicit padding can come out wrong while-cotofprintsMatchesfor every OP.When all non-batch axes of a Conv input have the same size, strict mode picks the input layout by trying every permutation that keeps the batch axis: transpose the validation data from the previous layer, run a dummy Conv on it, compare with the ONNX dummy inference, keep the smallest error. With explicit pads that can't be expressed as SAME,
input_tensoris padded before that search but the validation data is not. For a 3x3 stride-2 Conv on 8x8 every candidate outputs (1,3,3,8) against ONNX's (1,8,4,4), all of them score the sentinel error, andmin_abs_err_perm_1silently stays at identity. If the input is still in ONNX order, e.g. straight out of a Reshape, the Conv uses the wrong axis as channels. The per-OP check does not catch it because it tolerates layout ambiguity when the dims are equal.2. Summary of corrections
onnx2tf/ops/Conv.pyonly; the padding decision is unchanged. I keep a reference to the unpadded tensor, apply the winning permutation to that, and pad afterwards. Each transposed validation candidate gets the same padding before the dummy Conv is built. Both go throughapply_explicit_padding, which returns its argument unchanged unlesspaddedis set, so every other path behaves exactly as before. Ties still resolve to identity (strict less-than, identity is the first candidate).3. Before/After
Smallest model graph that shows it:
X(1,512) -> Add(const) -> Reshape(1,8,8,8) -> Conv(k3, s2, pads=[1,1,1,1]) -> Y(1,8,4,4). The Reshape leaves the Conv input in ONNX order; the Add makes the input non-constant, since the dummy inference feedsnp.onesand a constant input gives every permutation the same output.import numpy as np, onnx from onnx import TensorProto, helper, numpy_helper rng = np.random.default_rng(0); N = 8 init = [numpy_helper.from_array(rng.standard_normal((N, N, 3, 3)).astype(np.float32), "W"), numpy_helper.from_array(rng.standard_normal((N,)).astype(np.float32), "B"), numpy_helper.from_array(rng.standard_normal((N*N*N,)).astype(np.float32), "bias512"), numpy_helper.from_array(np.array([1, N, N, N], dtype=np.int64), "shape")] g = helper.make_graph( [helper.make_node("Add", ["X", "bias512"], ["A"]), helper.make_node("Reshape", ["A", "shape"], ["R"]), helper.make_node("Conv", ["R", "W", "B"], ["Y"], kernel_shape=[3,3], strides=[2,2], pads=[1,1,1,1])], "conv_padded_same_axes", [helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, N*N*N])], [helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, N, N//2, N//2])], init) m = helper.make_model(g, opset_imports=[helper.make_opsetid("", 13)]); m.ir_version = 8 onnx.save(m, "conv_padded_same_axes.onnx")onnx2tf -i conv_padded_same_axes.onnx -o out -tb tf_converter -nuo -cotofmain (d6cd7159), with a temporary print in the search loop:
perm=(0, 1, 2, 3) err=9223372036854775807 tf_out=(1, 3, 3, 8) onnx=(1, 8, 4, 4) perm=(0, 1, 3, 2) err=9223372036854775807 tf_out=(1, 3, 3, 8) onnx=(1, 8, 4, 4) perm=(0, 2, 1, 3) err=9223372036854775807 tf_out=(1, 3, 3, 8) onnx=(1, 8, 4, 4) perm=(0, 2, 3, 1) err=9223372036854775807 tf_out=(1, 3, 3, 8) onnx=(1, 8, 4, 4) perm=(0, 3, 1, 2) err=9223372036854775807 tf_out=(1, 3, 3, 8) onnx=(1, 8, 4, 4) perm=(0, 3, 2, 1) err=9223372036854775807 tf_out=(1, 3, 3, 8) onnx=(1, 8, 4, 4) chosen perm=[0, 1, 2, 3] INFO: onnx_output_name: Y shape: (1, 8, 4, 4) dtype: float32 validate_result: Matches ONNX/TFLite output check complete! (out/conv_padded_same_axes_accuracy_report.json) trigger=-cotof(auto) source=tf_converter target=float32 max_abs=57.625 rmse=16.7022 cosine=0.154714 pass=Falsethis PR:
perm=(0, 1, 2, 3) err=41.81199645996094 tf_out=(1, 4, 4, 8) perm=(0, 1, 3, 2) - 2.6.82026-08-01 · 2026-08-01 04:28 UTC
查看更新说明
2.6.8
Issue
Conv.make_node()readsinput_tensor_shapebefore the workaround that transposes an NCHW input to NHWC, and never refreshes it.The depthwise test compares
groupagainstinput_tensor_shape[-1]. For a transposed input this compares the channel count against a spatial dimension, the test fails, and the node falls through to the grouped-conv path — producing a filter of shape[C, K, K, 1]instead of aDepthwiseConv2D.Symptom: in a MobileNet/ConvNeXt-style network only the first depthwise conv of each stage converts correctly (it consumes a conv output directly), while the following ones are silently degraded (they consume a residual
Addwhose input required the transpose).The ONNX model is valid — depthwise is
Convwithgroup=Cand weight[C, 1, K, K].Fix
Refresh
input_tensor_shapefrominput_tensorright before the depthwise test.What's Changed
- Fix depthwise Conv detection after NCHW->NHWC input transpose (Legacy converter) by @AL3708 in https://github.com/PINTO0309/onnx2tf/pull/959
Full Changelog: https://github.com/PINTO0309/onnx2tf/compare/2.6.7...2.6.8
- 2.6.72026-07-20 · 2026-07-20 07:30 UTC
查看更新说明
2.6.7
Summary
This pull request continues the characterize-first refactoring of the TensorFlow-free
flatbuffer_directbackend. It restructures late shape, layout, topology, fallback, and terminal-cleanup orchestration without changing public conversion policy or using newly recorded evidence to alter graph-rewrite control flow.The main outcome is a smaller and more reviewable central lowerer whose phase boundaries are explicit, whose repeated orchestration sequences have focused owners, and whose observation-only results are retained in a bounded conversion-session store under stable phase IDs.
The branch also includes the full sequential Tier 0–4 TFLite/native-PyTorch regression evidence for the final checkpoint and updates the package version metadata to 2.6.7.
Why this change is needed
The direct FlatBuffer path has accumulated many interacting shape, layout, topology, compatibility, and model-recovery rules. Even when each rule is locally correct, several structural properties made maintenance difficult:
- adjacent pass calls were coordinated directly inside the central lowerer;
- repeated sort/layout-refresh and shape/reconciliation sequences had multiple owners;
- useful mutation and validation results were sometimes discarded or kept in unconsumed local dictionaries;
- phase identity was implicit in source position rather than represented by a stable ID;
- guard-skipped work was difficult to distinguish from work that ran and produced no mutation;
- terminal invariants and fallback recovery boundaries were spread across a very large function;
- changing ownership could easily invalidate structural assumptions in distant tests.
This branch makes those contracts explicit before attempting more aggressive scan elimination or control-flow optimization.
Architecture changes
Bounded phase-result storage
ConversionSessionnow owns a bounded, conversion-local phase-result store.- Results are recorded under 128 stable phase IDs.
- Each phase has one deterministic owner expression.
- Stored values are compact counters or bounded summaries rather than graph-sized snapshots.
- The store copies mappings so later caller mutation cannot silently change recorded evidence.
- “not invoked” remains distinct from “invoked with zero mutations”.
- No recorded counter is consumed to skip or reorder production work in this PR.
This removes observation-only lowerer locals while giving future optimization work a stable basis for differential testing.
Shared topology and shape owners
Repeated multi-step sequences now have focused pass-module owners, including:
- topology sort followed by logical-layout refresh;
- complete static-shape reconciliation followed by topology restoration;
- terminal topology sort and logical-layout validation;
- bounded primary and fallback topology checkpoints;
- safe transpose reduction, dynamic reshape resolution, and terminal expand/squeeze reconciliation evidence.
The owners preserve the original arguments, guards, ordering, cycle behavior, metadata updates, and temporary-state lifetime.
Explicit late and terminal orchestration
Large groups of previously adjacent lowerer calls were extracted into narrowly scoped orchestration owners. The extracted families include:
- QLinear and quantized layout recovery;
- attention/QKV, BatchMatMul, window, and reshape-layout recovery;
- binary adapter and broadcast convergence;
- pre-Concat, Slice/Concat, channel-slice, and terminal bridge cleanup;
- late Conv1D/decoder and Pad/InstanceNorm recovery;
- fallback norm, precision, unbound-input, and recurrent-alias repair;
- SiNet CSP attention, residual, HardSwish/SE, and terminal recovery;
- final-input dynamic-shape, final boundary, and terminal stabilization cleanup;
- prune-aware summaries for PRELU, Conv-input, affine, hard-activation, SPP, and normalization clusters.
These owners return the same child mappings or
- 2.6.62026-07-19 · 2026-07-19 01:04 UTC
查看更新说明
Summary
This branch continues the staged refactoring of the TensorFlow-free
flatbuffer_directbackend. It makes pass ownership, mutation evidence, graph index reuse, reconciliation decisions, and terminal validation substantially more explicit while preserving the existing conversion policy and public interfaces.The central goal of this checkpoint is not to introduce a new converter or to change the order of the existing compatibility rules. It is to make the current behavior safer to evolve:
- pass results are retained instead of being silently discarded;
- graph-stability decisions use complete, typed mutation evidence;
- stable paths avoid redundant full-graph reconciliation scans;
- repeated adjacent cleanup passes share one live
ModelIRGraphIndex; - terminal layout validation observes the graph after the applicable repairs;
- focused owners and contracts isolate changes from unrelated model families;
- every production change follows a characterize-first regression workflow.
The release metadata is updated consistently from
2.6.5to2.6.6in the package, project metadata, lockfile, and documented container tags.Motivation
The direct FlatBuffer path has accumulated many interacting layout, shape, quantization, recovery, and compatibility rules. Historically, a number of those rules were invoked through raw calls in the central lowerer, returned useful information that callers discarded, rebuilt producer/consumer state at nearby boundaries, or triggered reconciliation even when the preceding owner made no graph mutation.
Those patterns make apparently local changes difficult to reason about. A counter can be incomplete because cleanup happened outside the counter, a zero result can be mistaken for graph stability, or two adjacent passes can independently rescan the same graph. This branch establishes explicit contracts for these boundaries before using them for conservative scan elimination or index sharing.
Detailed changes
Efficient diagnostic bookkeeping
run_model_ir_pass_group()no longer scans the complete and growing diagnostic history for every emitted event. One scan initializes the global event count, maximum group sequence, and per-pass invocation counts. Those counters are then updated as events are appended.ConversionSessionretains this state across pass groups. Ordinary caller-owned lists remain supported through a safe one-scan fallback. The external diagnostic fields, numbering semantics, list identity, skip/cycle behavior, and invariant-failure reporting are unchanged.Explicit pass results and mutation evidence
Late, very-late, terminal, recovery, attention, quantized, binary-layout, Conv1D, and safety-fallback boundaries now return or stage fixed-schema result dictionaries instead of dropping child results. Multi-pass orchestration preserves child order and shared pass-state scopes.
Observation-only results remain intentionally unconsumed when their counters are not yet safe control-flow inputs. Where a stability decision is made, the owner contract accounts for every relevant mutation, including cleanup-only tensor pruning. Non-mutating iteration counters are excluded from mutation summaries.
This gives later work auditable evidence without adding ModelIR copies, fingerprints, or extra graph walks.
Complete static-shape reconciliation accounting
Static-shape reconciliation can now opt in to a complete mutation count. In addition to ordinary tensor-shape updates, it records operator-option changes, constant shape-parameter writes, and direct tensor metadata updates performed during the existing fixed-point walk.
The default result schema remains compatible. Only characterized call sites request the additional evidence, and their original guards, pruning behavior, layout synchronization, and pass order are preserved.
This complete evidence is retained across primary, fallba
- 2.6.52026-07-17 · 2026-07-17 14:16 UTC
查看更新说明
2.6.5
Summary
This pull request is the sixth staged checkpoint of the
flatbuffer_directrefactor. It moves the remaining large groups of already-supported ModelIR compatibility logic out of the central ONNX lowerer and into focused, characterized pass and orchestration modules.The central implementation in
lower_from_onnx2tf.pyis reduced from 25,647 lines onmainto 5,585 lines. The branch adds 79 focused pass/orchestration modules and updates or adds 92 test files. The large net addition is primarily the extracted owners, synthetic characterization fixtures, architecture contracts, and the recorded Tier 0-4 regression evidence; it is not a second conversion pipeline.The main goals of this checkpoint are:
- make pass ownership explicit and local;
- preserve the existing CLI, Python API, artifact formats, pass order, and conversion policy;
- prevent partially applied rewrites when a late guard rejects a match;
- reuse graph indexes, layout state, diagnostics, and pass state instead of reconstructing equivalent context in nested helpers;
- keep direct TFLite conversion and
-cotofoutside the TensorFlow import boundary; - provide a reproducible, sequential Tier 0-4 regression result for both TFLite and native PyTorch output.
No dependency was added. Project and container metadata are synchronized at version 2.6.5.
Why this change is needed
The direct FlatBuffer backend accumulated shape repair, layout recovery, quantization compatibility, fusion, and late cleanup rules in one central module. Many rules were individually valid, but their implicit ordering and shared mutation made them difficult to understand or modify safely. A guard that failed after an early tensor/layout mutation could leave the graph in a partially rewritten state, and nested recovery helpers repeatedly assembled nearly identical ModelIR/LayoutState/diagnostic contexts.
This branch follows a deliberately conservative sequence for each family:
- characterize the existing match, guard, ordering, statistics, metadata, and no-op behavior;
- add rejection, fan-out, boundary, dynamic-shape, quantization, and idempotence fixtures;
- correct mutation atomicity where a rejected candidate could leak changes;
- extract the implementation into one focused owner while retaining the original private lowerer symbol as a thin compatibility wrapper;
- verify owner/wrapper ModelIR fingerprints and architecture boundaries.
This separates ownership without attempting a broad semantic rewrite at the same time.
Main improvements
1. Focused pass ownership
The branch extracts shape, layout, quantization, attention, binary, affine, Concat, and terminal recovery behavior into op-family-oriented modules under
onnx2tf/tflite_builder/passes/.Representative extracted families include:
- static Squeeze and shape-signature sanitization;
- dynamic boundary-signature realignment;
- ExpandDims/Squeeze-to-Reshape compatibility;
- exact and singleton rank-four binary layout adaptation;
- channelwise broadcast-constant and stale binary-adapter repair;
- Conv/Pool output passthrough and Conv/Mul/Add affine folding;
- mixed-layout Concat input repair and legacy pre-Concat recovery;
- Concat/Mul/Add, nested-Concat, Mean/Reshape, and StridedSlice/Pad/Concat bridge recovery;
- Softmax/Transpose canonicalization and Reshape/Transpose collapse;
- BatchMatMul affine input, SE, adjoint, and attention preprojection recovery;
- attention Gather cleanup and QKV reshape compatibility;
- elementwise round-trip, fan-out, residual affine, and terminal PReLU/FC recovery;
- QLinear SiLU, QLinear Concat/Conv, QDQ bridge, quantized Swish, and other quantized recovery orchestration;
- late and terminal recovery clusters for Slice/Concat, affine/Concat/Split, normalization/attention, channel shuffle, SPP, hard activations, and singleton reshape patterns.
The o
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
onnx2tf
A tool for converting ONNX files to LiteRT/TFLite/TensorFlow, PyTorch native code (nn.Module), TorchScript (.pt), state_dict (.pt), Exported Program (.pt2), and Dynamo ONNX. It also supports direct conversion from LiteRT to PyTorch.
You should use LiteRT Torch rather than onnx2tf. https://github.com/google-ai-edge/litert-torch and https://github.com/google-ai-edge/ai-edge-quantizer
tf_converter supported layers
-
:heavy_check_mark:: Supported :white_check_mark:: Partial support Help wanted: Pull Request are welcome
OP Status Abs :heavy_check_mark: Acosh :heavy_check_mark: Acos :heavy_check_mark: Add :heavy_check_mark: AffineGrid :heavy_check_mark: And :heavy_check_mark: ArgMax :heavy_check_mark: ArgMin :heavy_check_mark: Asinh :heavy_check_mark: Asin :heavy_check_mark: Atanh :heavy_check_mark: Atan :heavy_check_mark: Attention :heavy_check_mark: AveragePool :heavy_check_mark: BatchNormalization :heavy_check_mark: Bernoulli :heavy_check_mark: BitShift :heavy_check_mark: BitwiseAnd :heavy_check_mark: BitwiseNot :heavy_check_mark: BitwiseOr :heavy_check_mark: BitwiseXor :heavy_check_mark: BlackmanWindow :heavy_check_mark: Cast :heavy_check_mark: Ceil :heavy_check_mark: Celu :heavy_check_mark: CenterCropPad :heavy_check_mark: Clip :heavy_check_mark: Col2Im :white_check_mark: Compress :heavy_check_mark: ConcatFromSequence :heavy_check_mark: Concat :heavy_check_mark: ConstantOfShape :heavy_check_mark: Constant :heavy_check_mark: Conv :heavy_check_mark: ConvInteger :white_check_mark: ConvTranspose :heavy_check_mark: Cosh :heavy_check_mark: Cos :heavy_check_mark: CumProd :heavy_check_mark: CumSum :heavy_check_mark: DeformConv :white_check_mark: DepthToSpace :heavy_check_mark: Det :heavy_check_mark: DequantizeLinear :heavy_check_mark: DFT :white_check_mark: Div :heavy_check_mark: Dropout :heavy_check_mark: DynamicQuantizeLinear :heavy_check_mark: Einsum :heavy_check_mark: Elu :heavy_check_mark: Equal :heavy_check_mark: Erf :heavy_check_mark: Expand :heavy_check_mark: Exp :heavy_check_mark: EyeLike :heavy_check_mark: Flatten :heavy_check_mark: Floor :heavy_check_mark: FusedConv :heavy_check_mark: GatherElements :heavy_check_mark: GatherND :heavy_check_mark: Gather :heavy_check_mark: Gelu :heavy_check_mark: Gemm :heavy_check_mark: GlobalAveragePool :heavy_check_mark: GlobalLpPool :heavy_check_mark: GlobalMaxPool :heavy_check_mark: GreaterOrEqual :heavy_check_mark: Greater :heavy_check_mark: GridSample :white_check_mark: GroupNormalization :heavy_check_mark: GRU :heavy_check_mark: HammingWindow :white_check_mark: HannWindow :white_check_mark: Hardmax :heavy_check_mark: HardSigmoid :heavy_check_mark: HardSwish :heavy_check_mark: Identity :heavy_check_mark: If :heavy_check_mark: ImageDecoder :white_check_mark: Input :heavy_check_mark: InstanceNormalization :heavy_check_mark: Inverse :heavy_check_mark: IsInf :heavy_check_mark: IsNaN :heavy_check_mark: LayerNormalization :heavy_check_mark: LeakyRelu :heavy_check_mark: LessOrEqual :heavy_check_mark: Less :heavy_check_mark: Log :heavy_check_mark: LogSoftmax :heavy_check_mark: Loop :heavy_check_mark: LpNormalization :heavy_check_mark: LpPool :heavy_check_mark: LRN :heavy_check_mark: LSTM :heavy_check_mark: MatMul :heavy_check_mark: MatMulInteger :heavy_check_mark: MaxPool :heavy_check_mark: Max :heavy_check_mark: MaxRoiPool :heavy_check_mark: MaxUnpool :heavy_check_mark: Mean :heavy_check_mark: MeanVarianceNormalization :heavy_check_mark: MelWeightMatrix :heavy_check_mark: Min :heavy_check_mark: Mish :heavy_check_mark: Mod :heavy_check_mark: Mul :heavy_check_mark: Multinomial :heavy_check_mark: Neg :heavy_check_mark: NegativeLogLikelihoodLoss :heavy_check_mark: NonMaxSuppression :heavy_check_mark: NonZero :heavy_check_mark: Optional :heavy_check_mark: OptionalGetElement :heavy_check_mark: OptionalHasElement :heavy_check_mark: Not :heavy_check_mark: OneHot :heavy_check_mark: Or :heavy_check_mark: Pad :heavy_check_mark: Pow :heavy_check_mark: PRelu :heavy_check_mark: QLinearAdd :heavy_check_mark: QLinearAveragePool :heavy_check_mark: QLinearConcat :heavy_check_mark: QLinearConv :heavy_check_mark: QGemm :heavy_check_mark: QLinearGlobalAveragePool :heavy_check_mark: QLinearLeakyRelu :heavy_check_mark: QLinearMatMul :heavy_check_mark: QLinearMul :heavy_check_mark: QLinearSigmoid :heavy_check_mark: QLinearSoftmax :heavy_check_mark: QuantizeLinear :heavy_check_mark: RandomNormalLike :heavy_check_mark: RandomNormal :heavy_check_mark: RandomUniformLike :heavy_check_mark: RandomUniform :heavy_check_mark: Range :heavy_check_mark: Reciprocal :heavy_check_mark: ReduceL1 :heavy_check_mark: ReduceL2 :heavy_check_mark: ReduceLogSum :heavy_check_mark: ReduceLogSumExp :heavy_check_mark: ReduceMax :heavy_check_mark: ReduceMean :heavy_check_mark: ReduceMin :heavy_check_mark: ReduceProd :heavy_check_mark: ReduceSum :heavy_check_mark: ReduceSumSquare :heavy_check_mark: RegexFullMatch :heavy_check_mark: Relu :heavy_check_mark: Reshape :heavy_check_mark: Resize :heavy_check_mark: ReverseSequence :heavy_check_mark: RNN :heavy_check_mark: RoiAlign :heavy_check_mark: RotaryEmbedding :heavy_check_mark: Round :heavy_check_mark: ScaleAndTranslate :heavy_check_mark: Scatter :heavy_check_mark: ScatterElements :heavy_check_mark: ScatterND :heavy_check_mark: Scan :heavy_check_mark: Selu :heavy_check_mark: SequenceAt :heavy_check_mark: SequenceConstruct :heavy_check_mark: SequenceEmpty :heavy_check_mark: SequenceErase :heavy_check_mark: SequenceInsert :heavy_check_mark: SequenceLength :heavy_check_mark: Shape :heavy_check_mark: Shrink :heavy_check_mark: Sigmoid :heavy_check_mark: Sign :heavy_check_mark: Sinh :heavy_check_mark: Sin :heavy_check_mark: Size :heavy_check_mark: Slice :heavy_check_mark: Softmax :heavy_check_mark: SoftmaxCrossEntropyLoss :heavy_check_mark: Softplus :heavy_check_mark: Softsign :heavy_check_mark: SpaceToDepth :heavy_check_mark: Split :heavy_check_mark: SplitToSequence :heavy_check_mark: Sqrt :heavy_check_mark: Squeeze :heavy_check_mark: STFT :white_check_mark: StringConcat :heavy_check_mark: StringNormalizer :heavy_check_mark: StringSplit :heavy_check_mark: Sub :heavy_check_mark: Sum :heavy_check_mark: Tan :heavy_check_mark: Tanh :heavy_check_mark: TensorScatter :heavy_check_mark: TfIdfVectorizer :white_check_mark: ThresholdedRelu :heavy_check_mark: Tile :heavy_check_mark: TopK :heavy_check_mark: Transpose :heavy_check_mark: Trilu :heavy_check_mark: Unique :heavy_check_mark: Unsqueeze :heavy_check_mark: Upsample :heavy_check_mark: Where :heavy_check_mark: Xor :heavy_check_mark:
flatbuffer_direct execution path
flatbuffer_direct is now the default backend. It is faster and has a higher success rate than tf_converter for the supported direct path. The simplest conversion command now outputs only a LiteRT model by default, but if you add --flatbuffer_direct_output_saved_model, it will also output a saved_model. Unlike the legacy tf_converter path, this SavedModel is built from the LiteRT-side ModelIR.
[!IMPORTANT]
flatbuffer_directis the current default backend. Use--tflite_backend tf_converteronly when you explicitly need the legacy TensorFlow Lite Converter compatibility path.
With the default flatbuffer_direct backend, onnx2tf uses a direct fast path for both ONNX input and -it/--input_tflite_file_path input:
- ONNX graph preprocessing (
tflite_builder.preprocess) and direct lowering (lower_onnx_to_ir) - Direct FlatBuffer export (
*_float32.tflite,*_float16.tflite, and optional quantized variants) - Optional direct reports/evaluation (
*_op_coverage_report.json, tensor correspondence, ONNX/TFLite check)
In this fast path, the per-node TensorFlow conversion (op.make_node() over all ONNX nodes) is skipped.
This removes the long debug traces such as:
INFO: <index> / <total>INFO: onnx_op_type: ...INFO: tf_op_type: ...
Measured example (same model, float32 TFLite write stage):
tf_converter:~24.947sflatbuffer_direct:~0.239sflatbuffer_directwas approximately 107x faster thantf_converterin this case.
Actual speedup depends on model structure, enabled options, and runtime environment.
Direct export can also generate TF-side artifacts without falling back to tf_converter:
--output_h5--output_keras_v3--output_tfv1_pb--flatbuffer_direct_output_pytorch
These artifacts are generated from an internal SavedModel bridge built from float32 ModelIR. If direct export fails, conversion stops with an explicit error.
-inimc / -onimc also stay on the direct path in flatbuffer_direct.
For ONNX input and -it input, these options crop the imported/lowered ModelIR
at the specified boundary tensor names instead of splitting the ONNX graph.
-dgc, -ebu, and -eru also stay on the direct path in flatbuffer_direct.
For ONNX input they are applied during lowering or as post-lowering ModelIR rewrites.
For -it input they are applied to imported ModelIR before SavedModel bridge,
split planning, or rewritten TFLite export.
If the requested rewrite cannot be applied safely, conversion stops with an explicit error.
-me also stays on the direct path in flatbuffer_direct.
For ONNX MeanVarianceNormalization, direct lowering uses primitive builtin ops
and applies mvn_epsilon to the internal variance + epsilon term without
falling back to tf_converter.
--disable_model_save also stays on the direct path. In flatbuffer_direct, it means the conversion can still run internal validation and temporary staging, but no final artifacts are left in the requested output directory.
Invalid combinations are rejected explicitly:
--disable_model_savewith--output_h5,--output_keras_v3, or--output_tfv1_pb--enable_auto_split_modelwith--output_h5,--output_keras_v3, or--output_tfv1_pb
SavedModel direct export from flatbuffer_direct ModelIR is available with
--flatbuffer_direct_output_saved_model.
PyTorch package direct export is available with
--flatbuffer_direct_output_pytorch.
These options have the following constraints:
--tflite_backend flatbuffer_directis required for both--flatbuffer_direct_output_saved_modelcannot be combined with--disable_model_saveCUSTOMops are rejected with an explicit error
| INT8 ONNX | INT8 TFLite(LiteRT) |
|---|---|
-
e.g. LiteRT only output
onnx2tf \ -i iat_llie_180x320.onnx \ -tb flatbuffer_direct -
e.g. Generate additional
saved_modelfrom LiteRT after LiteRT outputonnx2tf \ -i iat_llie_180x320.onnx \ -tb flatbuffer_direct \ -fdosm -
e.g. Generate
saved_modeldirectly from an existing LiteRT (.tflite) fileonnx2tf \ -it iat_llie_180x320_float32.tflite \ -tb flatbuffer_direct -
e.g. Generate
.h5directly from an existing LiteRT (.tflite) file withouttf_converterfallbackonnx2tf \ -it iat_llie_180x320_float32.tflite \ -tb flatbuffer_direct \ -oh5 -
e.g. Generate a PyTorch package directly from an existing LiteRT (
.tflite) fileonnx2tf \ -it iat_llie_180x320_float32.tflite \ -o tmp_iat_llie_180x320_from_tflite \ -tb flatbuffer_direct \ -fdopt -
e.g. Compare the input LiteRT model and the generated PyTorch package with the same seeded inputs
onnx2tf \ -it iat_llie_180x320_float32.tflite \ -o tmp_iat_llie_180x320_from_tflite \ -tb flatbuffer_direct \ -fdopt \ -cotofThis outputs:
iat_llie_180x320_float32_pytorch/iat_llie_180x320_float32_pytorch_accuracy_report.json(TFLite↔PyTorch)iat_llie_180x320_float32_accuracy_comparison_report.json
[Ultra experimental] PyTorch export example (yolox_s.onnx)
flatbuffer_direct can emit a native PyTorch package together with optional
TorchScript, Dynamo ONNX, and ExportedProgram artifacts in one run.
Generate all PyTorch-side artifacts plus TFLite and accuracy reports:
onnx2tf \
-i yolox_s.onnx \
-o tmp_yolox_s \
-tb flatbuffer_direct \
-cotof \
-fdopt \
-fdots \
-fdodo \
-fdoep
The output directory contains:
yolox_s_float32.tfliteyolox_s_float16.tfliteyolox_s_accuracy_report.json(ONNX↔TFLite)yolox_s_pytorch_accuracy_report.json(ONNX↔PyTorch)yolox_s_accuracy_comparison_report.jsonyolox_s_pytorch/model.pyruntime.pystate_dict.pthmetadata.jsonyolox_s_pytorch/yolox_s_jit.ptyolox_s_pytorch/yolox_s_dynamo.onnxyolox_s_pytorch/yolox_s_ep.pt2
The generated PyTorch package is a normal torch.nn.Module package. You can
load it and run eager inference directly:
import sys
import torch
sys.path.append("tmp_yolox_s")
from yolox_s_pytorch import load_model
model = load_model(device="cpu", eval_mode=True)
x = torch.zeros((1, 3, 640, 640), dtype=torch.float32)
with torch.no_grad():
output = model(x)
print("input :", tuple(x.shape))
print("output:", tuple(output.shape))
print(model.forward_named(x).keys())
Expected output for the current yolox_s package:
input : (1, 3, 640, 640)
output: (1, 8400, 85)
dict_keys(['output'])
You can also load the bundled state_dict.pth explicitly with standard
PyTorch APIs:
import sys
from pathlib import Path
import torch
sys.path.append("tmp_yolox_s")
from yolox_s_pytorch.model import Model
package_dir = Path("tmp_yolox_s/yolox_s_pytorch")
model = Model(load_weights=False, eval_mode=True)
state_dict = torch.load(package_dir / "state_dict.pth", map_location="cpu")
model.load_state_dict(state_dict, strict=True)
The generated state_dict.pth is saved in load_state_dict-compatible format
for native PyTorch packages.
For native packages, raw torch.onnx.export(..., dynamo=True) and raw
torch.export.save(torch.export.export(...)) are intended to produce the same
graph structure as the helper-generated *_dynamo.onnx and *_ep.pt2.
Example: raw torch.onnx.export
from pathlib import Path
import importlib
import logging
import sys
import torch
package_dir = Path("tmp_yolox_s/yolox_s_pytorch").resolve()
sys.path.insert(0, str(package_dir.parent))
pkg = importlib.import_module(package_dir.name)
model = pkg.load_model(device="cpu", eval_mode=True)
model.eval()
example_inputs = (torch.randn(1, 3, 640, 640),)
logging.getLogger("torch.onnx._internal.exporter._registration").setLevel(logging.ERROR)
with torch.no_grad():
torch.onnx.export(
model,
example_inputs,
str(package_dir / "raw_dynamo.onnx"),
dynamo=True,
input_names=model.input_names,
output_names=model.output_names,
)
Example: raw torch.export.save
from pathlib import Path
import importlib
import sys
import torch
package_dir = Path("tmp_yolox_s/yolox_s_pytorch").resolve()
sys.path.insert(0, str(package_dir.parent))
pkg = importlib.import_module(package_dir.name)
model = pkg.load_model(device="cpu", eval_mode=True)
model.eval()
example_inputs = (torch.randn(1, 3, 640, 640),)
with torch.no_grad():
exported_program = torch.export.export(model, example_inputs)
torch.export.save(
exported_program,
str(package_dir / "raw_exported_program.pt2"),
)
Both examples require a concrete example input shape. For dynamic public inputs,
use the same concrete shape/data that you would provide to -fdodo or -fdoep
via --shape_hints, --test_data_nhwc_path, or -cind.
[!CAUTION] Native PyTorch packages generated by
--flatbuffer_direct_output_pytorchare intended primarily for inference, not for training as-is.Detailed reasons:
- The exporter is designed to preserve inference behavior of the converted TFLite/ModelIR graph, not to reconstruct the original training-time PyTorch model semantics.
- The generated graph may include inference-oriented rewrites such as layout normalization, constant folding, reshapes/transposes inserted for runtime compatibility, and decomposition into primitive ops. These are correct for forward inference but are not guaranteed to be ideal or even stable for gradient-based optimization.
- Some generated models include postprocessing or task-specific inference logic such as
argmax,non_max_suppression, score filtering, indexing-heavy tensor selection, or shape-control branches. These are typically non-differentiable or poor training targets.- Native export may emit helper paths that are semantically equivalent for inference but were not designed with training ergonomics in mind, such as strict shape/layout alignment, bridge shims for 1D/2D/3D convolution compatibility, or graph-local runtime helpers.
- Fallback-backed packages (
tflite,saved_model,string_normalizer) are wrappers around non-PyTorch execution backends and therefore should be treated as inference-only.- Even native packages are emitted with
eval_mode=Trueas the normal usage path. The exporter does not currently guarantee training-safe reconstruction of modules such as normalization layers, recurrent state handling, or control-flow-heavy blocks in the same form expected by optimizers and schedulers.state_dict.pthisload_state_dict-compatible for native packages, but compatibility of weight loading does not imply that the generated module is a faithful training architecture suitable for fine-tuning.Practical guidance:
- Use generated PyTorch packages for inference validation, packaging, and side-by-side output comparison.
- If you want to train or fine-tune a model, treat the generated package as a reference implementation only, and rebuild or simplify the architecture into a training-oriented PyTorch model before optimization.
- Scope: ONNX ops listed in the ``tf_converter
supported layerstable above. - Source of truth:
onnx2tf/tflite_builder/op_registry.pyand--report_op_coverageoutput. - Current summary:
- Listed ONNX ops in the builtin table below:
192 - Policy counts are generated in
*_op_coverage_report.json(schema_policy_counts). - Check each conversion run with
--report_op_coveragefor the latest numbers.
- Listed ONNX ops in the builtin table below:
Notes:
flatbuffer_directsupports only a subset of ONNX ops as TFLite builtins.- Some ops are conditionally supported (rank/attribute/constant-input constraints).
- For model-specific results, use
--report_op_coverageand check*_op_coverage_report.json.
| ONNX OP | TFLite OP | Key constraints (flatbuffer_direct) |
|---|---|---|
| Abs | ABS (or NEG + MAXIMUM for INT64) | For INT64 input, lowered as NEG + MAXIMUM because TFLite ABS kernel does not support INT64 |
| Acos | MUL + SUB + SQRT + ATAN2 | Input/output dtype must be FLOAT16 or FLOAT32 |
| Acosh | SUB + ADD + SQRT + MUL + LOG | Input/output dtype must be FLOAT16 or FLOAT32 |
| Add | ADD | - |
| AffineGrid | BATCH_MATMUL + TRANSPOSE + RESHAPE | size input must be constant rank-1 length 4 or 5 with static positive values; theta must be rank-3 float tensor with shape [N,2,3] or [N,3,4]; output shape must match size; align_corners in {0,1} |
| And | LOGICAL_AND | - |
| ArgMax | ARG_MAX (+ optional RESHAPE for keepdims) | axis must be in range, keepdims must be 0 or 1, select_last_index=0, output dtype must be INT32 or INT64 |
| ArgMin | ARG_MIN (+ optional RESHAPE for keepdims) | axis must be in range, keepdims must be 0 or 1, select_last_index=0, output dtype must be INT32 or INT64 |
| Attention | RESHAPE + TRANSPOSE + BATCH_MATMUL + MUL + SOFTMAX + CAST | Canonical 3-input form only (query/key/value); single output only; q_num_heads == kv_num_heads > 0; is_causal=0; qk_matmul_output_mode=0; softcap=0; rank-3 float tensors only |
| Asin | MUL + SUB + SQRT + ATAN2 | Input/output dtype must be FLOAT16 or FLOAT32 |
| Asinh | MUL + ADD + SQRT + LOG | Input/output dtype must be FLOAT16 or FLOAT32 |
| Atan | ATAN2 | Input/output dtype must be FLOAT16 or FLOAT32 |
| Atanh | ADD + SUB + DIV + LOG + MUL | Input/output dtype must be FLOAT16 or FLOAT32 |
| AveragePool | AVERAGE_POOL_2D (+ optional PAD/PADV2 + divisor correction DIV) | 2D only (rank=4), ceil_mode in {0,1}, count_include_pad in {0,1}. Supports auto_pad in {NOTSET,VALID,SAME_*,SAME_LOWER} and explicit pads. For count_include_pad=0 with non-zero effective pads, a correction path (AVERAGE_POOL_2D on mask + DIV) is applied |
| BatchNormalization | MUL + ADD | All parameter inputs (scale, bias, mean, var) must be constant |
| Bernoulli | SHAPE + RANDOM_UNIFORM + LESS (+ optional CAST) | Input dtype must be FLOAT16/FLOAT32; output dtype must be BOOL or numeric |
| BitShift | RIGHT_SHIFT (RIGHT) or MUL-based (LEFT) | LHS/RHS must be integer tensors, direction must be LEFT or RIGHT; LEFT requires constant shift input |
| BitwiseAnd | LOGICAL_AND | BOOL tensors only |
| BitwiseNot | LOGICAL_NOT / SUB + CAST | Input dtype must be BOOL or integer |
| BitwiseOr | LOGICAL_OR | BOOL tensors only |
| BitwiseXor | BITWISE_XOR | Input dtypes must match and be BOOL/integer |
| BlackmanWindow | CAST + SQUEEZE + RANGE + MUL + DIV + COS + SUB + ADD + MAXIMUM | Input must be scalar-like rank-1 length-1 integer tensor; output dtype must be FLOAT16/FLOAT32 |
| Cast | CAST | - |
| CastLike | CAST | - |
| Ceil | CEIL | - |
| Celu | MAXIMUM + MINIMUM + DIV + EXP + SUB + MUL + ADD | Input/output dtype must be FLOAT16 or FLOAT32 |
| CenterCropPad | SLICE + PAD (+ optional RESHAPE passthrough) | Target shape input must be constant rank-1; axes must be in range and length must match target shape; output rank must match input rank; string dtype unsupported in builtin path |
| Clip | RELU / RELU6 / MAXIMUM + MINIMUM | General constant clip ranges are supported via MAXIMUM/MINIMUM decomposition. ReLU fast-path: min=0,max=+inf; ReLU6 fast-path: min=0,max=6 |
| Col2Im | RESHAPE + TRANSPOSE + TRANSPOSE_CONV + SLICE + CAST | Input/output dtype must be FLOAT16/FLOAT32; input/output ranks must be 3/4; image_shape and block_shape must be constant 2-elements; static positive dimensions required |
| Concat | CONCATENATION | - |
| ConstantOfShape | CAST + FILL | Shape input must be rank-1 integer tensor; value attribute must be scalar (or omitted for zero-fill) |
| Conv | CONV_2D / DEPTHWISE_CONV_2D / CONV_3D | 2D: rank=4, constant weights, grouped conv only regular/depthwise, zero pads or auto_pad=SAME_*. 3D: rank=5, constant rank-5 weights, group=1, strides/dilations length=3, auto_pad in {NOTSET,VALID,SAME_UPPER} (SAME_LOWER unsupported); explicit pads are handled via VALID+pad/crop path |
| ConvInteger | CAST + SUB + PAD + CONV_2D / DEPTHWISE_CONV_2D + TRANSPOSE | Input must be integer tensor, output dtype must be INT32/INT64, weights must be constant rank-4, and grouped conv must be regular/depthwise only |
| ConvTranspose | TRANSPOSE_CONV / CONV_3D_TRANSPOSE (+ optional ADD bias; 1D uses EXPAND_DIMS/SQUEEZE shim) | 1D/2D/3D supported (1D: input rank=3 + weight rank=3 const, 2D: input rank=4 + weight rank=4 const, 3D: input rank=5 + weight rank=5 const), group=1, dilations must be all-ones, and output_padding must satisfy 0 <= output_padding < stride (1D len=1, 2D len=2, 3D len=3). auto_pad supports SAME_*, VALID, and NOTSET; explicit non-zero pads are handled by post-crop when output shape is static |
| Compress | NOT_EQUAL + WHERE + RESHAPE + CAST + GATHER | Condition input must be rank-1 BOOL or integer tensor; axis may be omitted (flattened output) or be in range; when axis is specified and static, condition length must match that axis dimension |
| Cos | COS | - |
| Cosh | SUB + EXP + ADD + MUL | Input/output dtype must be FLOAT16 or FLOAT32 |
| CumProd | RANGE + LESS/LESS_EQUAL + RESHAPE + TILE + FILL + SELECT_V2 + REDUCE_PROD (+ optional REVERSE_V2) | Input/output dtype must be FLOAT16 or FLOAT32; input shape must be fully static positive; axis must be scalar constant (or attr) and in range; exclusive/reverse must be 0 or 1 |
| DFT | RESHAPE + BATCH_MATMUL + CONCATENATION (+ optional CAST) | Current builtin path supports real input [..., N, 1] only with onesided=1, inverse=0, and transform axis=rank-2; input/output shapes must be static positive; optional dft_length and axis inputs must be constant scalars; output shape must be [..., N//2 + 1, 2] |
| CumSum | CUMSUM | Input rank must be >=1; axis must be scalar constant (or attr) and in range; exclusive/reverse must be 0 or 1 |
| DeformConv | PAD + RESHAPE + TRANSPOSE + SHAPE + RANGE + SQUEEZE + GATHER + FLOOR + MAXIMUM/MINIMUM + CAST + MUL + ADD + SUB + BATCH_MATMUL | Constrained 2D float path only: input/output/offset/mask rank=4, input/offset/output(/mask) dtype FLOAT16/FLOAT32, weights and optional bias must be constant, kernel/channel/spatial dims must be static positive, and builtin lowering is limited to group=1 and offset_group=1 for LiteRT runtime safety. Grouped patterns remain custom-op candidates |
| DequantizeLinear | DEQUANTIZE | scale must be constant, zero_point (if provided) must be constant, per-axis axis must be in range |
| DepthToSpace | DEPTH_TO_SPACE (DCR) / RESHAPE + TRANSPOSE + RESHAPE (CRD) | Rank-4 only, blocksize > 1, mode in {DCR,CRD} |
| Det | GATHER + RESHAPE + MUL + SUB + ADD | Input/output dtype must be FLOAT16/FLOAT32; builtin lowering currently supports static square 2x2 / 3x3 matrices only |
| Div | DIV or MUL (when divisor is constant reciprocal) | For non-floating outputs, lowered as CAST -> MUL(reciprocal) -> CAST to preserve output dtype without using unsupported integer DIV paths |
| Dropout | RESHAPE (+ optional SHAPE + FILL for mask output) | Inference-time no-op in flatbuffer_direct; inputs ratio/training_mode are ignored |
| DynamicQuantizeLinear | NEG + REDUCE_MAX + MINIMUM + MAXIMUM + SUB + DIV + ADD + CAST | Input dtype must be FLOAT16/FLOAT32, output dtypes must be Y=UINT8, Y_Scale=FLOAT16/FLOAT32, Y_ZeroPoint=UINT8; scale/zero-point outputs must be scalar |
| Einsum | FULLY_CONNECTED | Rank-2 matmul-style equation only (ij,jk->ik), rhs input must be constant weights |
| Elu | ELU | - |
| Equal | EQUAL | - |
| Erf | ABS + SIGN + MUL + ADD + DIV + EXP + SUB | Input/output dtype must be FLOAT16 or FLOAT32 |
| Exp | EXP | - |
| Expand | RESHAPE + MUL (broadcast via const ones) | Output shape must be statically known, non-negative, and broadcast-compatible with input shape (current direct lowering uses static RESHAPE + MUL) |
| EyeLike | RESHAPE (from const eye) | Output must be rank-2 with fully static positive shape |
| Flatten | RESHAPE | Input rank must be >= 1 |
| Floor | FLOOR | - |
| FusedConv | CONV_2D / DEPTHWISE_CONV_2D + fused activation | Supports Relu/Tanh/Sigmoid/LeakyRelu/Clip/HardSigmoid activations with valid scalar params; convolution constraints follow builtin Conv/FusedConv validator |
| FusedMatMul | BATCH_MATMUL (+ optional MUL for alpha) | Input rank >= 2, dtypes FLOAT16/FLOAT32 only, transA/transB must be 0 or 1, finite alpha required |
| Gather | GATHER | batch_dims=0 only |
| GatherElements | CAST + RESHAPE + CONCATENATION + GATHER_ND | Data/indices ranks must match, output shape must equal indices shape, static positive output dims required, axis must be in range |
| GatherND | CAST + GATHER_ND | batch_dims=0 only; indices must be integer type; indices last dim must be static positive and <= params_rank |
| Gelu | GELU | - |
| Gemm | FULLY_CONNECTED | Input rank=2, weight rank=2 + constant, transA=0 only |
| Greater | GREATER | - |
| GreaterOrEqual | GREATER_EQUAL | - |
| GridSample | PAD + RESHAPE + TRANSPOSE + SQUEEZE + SLICE + ADD/SUB/MUL + FLOOR + MAXIMUM/MINIMUM + CAST + GATHER | Rank-4/5 with static positive dims; mode=bilinear; padding_mode in {zeros,border}; align_corners in {0,1}. When grid is graph input, keep shape metadata (e.g. -kat grid) |
| GlobalAveragePool | MEAN | Input rank must be >=3 |
| GlobalLpPool | ABS + POW + SUM + RESHAPE (+ optional CAST) | Input rank must be >=3; input/output dtype must be FLOAT16/FLOAT32; p must be finite and > 0 |
| GlobalMaxPool | REDUCE_MAX | Input rank must be >=3 |
| GroupNormalization | RESHAPE + MEAN + SUB + MUL + ADD + SQRT + DIV (+ optional CAST) | Input/output dtype must be FLOAT16/FLOAT32; scale/bias must be constant length=C; num_groups > 0 and must divide channel dim; channel/spatial dims must be static positive |
| GRU | TRANSPOSE + SLICE + SQUEEZE + BATCH_MATMUL + ADD + MUL + SUB + LOGISTIC + TANH + RESHAPE + CONCATENATION + EXPAND_DIMS | layout=0; direction in {forward, reverse, bidirectional}; sequence_lens unsupported; W/R must be constant rank-3; linear_before_reset in {0,1}; activations [Sigmoid,Tanh]; clip=0 |
| Hardmax | TRANSPOSE + ARG_MAX + ONE_HOT | axis must be in range; target axis size must be static positive |
| HardSigmoid | MUL + ADD + MAXIMUM + MINIMUM | Input/output dtype must be FLOAT16 or FLOAT32 |
| HardSwish | HARD_SWISH | Input/output dtype must be FLOAT16 or FLOAT32 |
| HammingWindow | CAST + SQUEEZE + RANGE + MUL + DIV + COS + SUB + MAXIMUM | Input must be scalar-like rank-1 length-1 integer tensor; output dtype must be FLOAT16/FLOAT32 |
| HannWindow | CAST + SQUEEZE + RANGE + MUL + DIV + COS + SUB + MAXIMUM | Input must be scalar-like rank-1 length-1 integer tensor; output dtype must be FLOAT16/FLOAT32 |
| MelWeightMatrix | const-folded builtin tensor materialization | All five inputs must be constant scalars; output dtype must be FLOAT16/FLOAT32; output shape is [dft_length // 2 + 1, num_mel_bins]; requires 0 <= lower_edge_hertz < upper_edge_hertz <= sample_rate / 2 |
| Identity | RESHAPE | - |
| If | CONCATENATION + REDUCE_MAX + CAST + ADD + MUL + RESHAPE + NON_MAX_SUPPRESSION_V4/V5 + SLICE + GATHER + SHAPE + SUB + SELECT/SELECT_V2 | Built-in lowering supports constrained patterns: NMS-guard pattern (empty then-branch + NMS else-branch), axis0 Add-branch pattern (single Add per branch, same trailing dims, different static first dim), SequenceConstruct Add-branch pattern (branch-local Constant/Add with terminal SequenceConstruct), and nested ReduceMin/Add pattern (else-branch ReduceMin/Greater with nested Add/Add If). In control-flow branch lowering, dynamic-condition If is additionally supported when both branches are single-output initializer-only constants (lowered via Where) |
| InstanceNormalization | MEAN + SUB + MUL + MEAN + ADD + SQRT + DIV + MUL + ADD | Input/output dtype must be FLOAT16 or FLOAT32; input rank must be >=3; scale and bias inputs must be constant |
| IsInf | ABS + EQUAL / LESS + GREATER + LOGICAL_AND | Input dtype must be FLOAT16/FLOAT32; output dtype must be BOOL; detect_negative / detect_positive are honored |
| IsNaN | NOT_EQUAL | Input dtype must be FLOAT16/FLOAT32; output dtype must be BOOL |
| MeanVarianceNormalization | MEAN + SUB + MUL + MEAN + ADD + SQRT + DIV | Input/output dtype must be FLOAT16 or FLOAT32; mvn_epsilon is applied directly in builtin lowering; default axes follow ONNX channel-first semantics and rank <3 reduces over axis 0 |
| Inverse | SLICE + MUL + SUB + ADD + NEG + CONCATENATION + DIV (+ optional CAST in/out) | Input/output dtype must be FLOAT16 or FLOAT32; input rank must be >=2; matrix last dimensions must resolve to square 2x2 or 3x3 |
| LeakyRelu | LEAKY_RELU | Input/output dtype must be FLOAT16 or FLOAT32 |
| Less | LESS | - |
| LessOrEqual | LESS_EQUAL | - |
| Log | LOG | Input/output dtype must be FLOAT16 or FLOAT32 |
| LogSoftmax | SOFTMAX + LOG (+ transpose in/out for non-last axis) | axis must be in range (negative axis normalized) |
| Loop | WHILE (+ subgraph-local ADD/LESS/LOGICAL_AND/RESHAPE and lowered body ops) | Built-in lowering supports either static-unroll patterns (constant trip_count/cond, loop-carried outputs only) or WHILE patterns with loop-carried outputs only (no scan outputs). max_trip_count input dtype must be INT32 or INT64 |
| LpPool | ABS + POW + AVERAGE_POOL_2D + MUL + RESHAPE (+ optional CAST) | Rank-4 only; input/output dtype must be FLOAT16/FLOAT32; kernel_shape/strides/dilations must be 2D; dilations=[1,1]; p must be finite and > 0; non-zero pads require count_include_pad=1 |
| LpNormalization | L2_NORMALIZATION | p=2, axis=last only |
| LRN | LOCAL_RESPONSE_NORMALIZATION (+ transpose in/out) | Input rank must be 4, size must be a positive odd integer |
| LayerNormalization | MEAN + SUB + MUL + ADD + SQRT + DIV (+ optional CAST) | axis and stash_type are honored; optional ONNX outputs (mean, inv_std_dev) are supported |
| LSTM | UNIDIRECTIONAL_SEQUENCE_LSTM / BIDIRECTIONAL_SEQUENCE_LSTM + REVERSE_V2 + SPLIT + SQUEEZE + SLICE + RESHAPE/EXPAND_DIMS + CONCATENATION | direction in {forward,reverse,bidirectional}, layout=0, input_forget=0; W/R must be constant rank-3 with num_directions matching direction; optional B must be constant shape [num_directions, 8*hidden_size]; initial_h/initial_c are optional (when provided, shape must be [num_directions, batch, hidden]; runtime tensor inputs are supported); sequence_lens and peephole input P unsupported; projection (R.shape[2] != hidden_size) unsupported |
| MatMul | BATCH_MATMUL (+ CAST/RESHAPE/SQUEEZE helpers) | Supports standard rank>=2 matmul, vector lhs/rhs forms, vector dot, and scalar multiply patterns |
| MatMulInteger | CAST + SUB + BATCH_MATMUL | A/B input rank must be >=2 (rank=1 placeholder allowed), A/B dtypes must be integer tensor types (INT8/UINT8/INT16/UINT16/INT32), output dtype must be INT32/INT64; optional zero-point inputs must be scalar/1D and shape-compatible |
| Max | MAXIMUM (chained for >2 inputs) | At least 2 inputs |
| MaxPool | MAX_POOL_2D | 2D only (rank=4), ceil_mode in {0,1}, zero pads or auto_pad=SAME_* |
| MaxRoiPool | TRANSPOSE + SLICE + MAX_POOL_2D + CONCATENATION | Current builtin path supports rank-4 input/output only with constant rois; input/output dtype must be FLOAT16/FLOAT32; all shapes must be static positive; pooled_shape must be length-2 positive and match output spatial dims; output channels must match input channels; output batch must equal the number of constant rois |
| MaxUnpool | CAST + RESHAPE + SCATTER_ND | Input/indices/output must be rank-4; input and indices shapes must match; input/output dtype must match and indices must be integer; output shape must be static positive with matching batch/channel; kernel_shape and strides must be length-2 positive; zero pads only; optional output_shape input must be a constant length-4 tensor matching the graph output shape |
| Mean | ADD + DIV (+ optional CAST) | All inputs and output must be FLOAT16/FLOAT32 |
| NegativeLogLikelihoodLoss | TRANSPOSE + CAST + EQUAL + SELECT_V2 + ONE_HOT + MUL + SUM + SUB (+ optional GATHER/MEAN/DIV) | Input/output dtype must be FLOAT16/FLOAT32; target dtype must be integer; input rank must be >=2 with static positive class dim at axis 1; optional weight must be rank-1 float tensor of length C; reduction in {none,sum,mean}; ignore_index supported |
| Min | MINIMUM (chained for >2 inputs) | At least 2 inputs |
| Mish | EXP + ADD + LOG + TANH + MUL | Input/output dtype must be FLOAT16 or FLOAT32 |
| Mod | FLOOR_MOD | fmod=0 only |
| Mul | MUL | - |
| MultiHeadAttention | RESHAPE + TRANSPOSE + BATCH_MATMUL + MUL + SOFTMAX + CAST | num_heads > 0, unidirectional=0, query/key/value must be rank-3 same dtype (FLOAT16/FLOAT32), hidden dims must be static positive and divisible by num_heads |
| Neg | NEG | - |
| NonMaxSuppression | NON_MAX_SUPPRESSION_V4/V5 + SLICE + GATHER + SUB + CAST + RESHAPE + CONCATENATION (+ optional ARG_MAX + REDUCE_MAX) | Rank-3 boxes/scores only; center_point_box=0; currently batch=1; boxes last dim must be 4; static positive num_boxes; scores_shape[2] == boxes_shape[1]; optional thresholds/max_output must be scalar constants; output dtype must be INT32 or INT64; when --output_nms_with_argmax is disabled, class dim must be static positive (class dim >1 is supported via class-wise NMS). --switch_nms_version (-snms) selects V4 or V5. |
| NonZero | NOT_EQUAL + WHERE + TRANSPOSE + CAST | Input rank must be >=1; output rank must be 2 |
| Not | LOGICAL_NOT | - |
| OneHot | CAST + ADD + FLOOR_MOD + ONE_HOT | depth input must be constant scalar and >0; values input must be constant 2-element tensor [off_value,on_value]; normalized axis must be in range |
| OptionalHasElement | const-fold (BOOL scalar) | Built-in lowering supports determinable-presence cases only: non-optional tensor inputs are folded to true; inputs produced by Optional (empty/value) are folded to false/true; runtime-optional graph inputs are not supported in builtin path |
| Or | LOGICAL_OR | - |
| Pad | PAD / PADV2 / MIRROR_PAD (+ dynamic pads bridge: CAST + RESHAPE + TRANSPOSE) | mode in {constant,reflect}; reflect is lowered to MIRROR_PAD(REFLECT). pads may be constant or dynamic rank-1 tensor of length 2*rank (integer type, internally cast to INT32). For mode=constant, constant zero is lowered as PAD; constant non-zero is lowered as PADV2 (non-quantized tensors only) |
| Pow | POW | Output dtype must be FLOAT16 or FLOAT32 |
| PRelu | PRELU | slope must be constant (scalar or per-channel) |
| QGemm | FULLY_CONNECTED | Input rank=1 or 2, weight must be constant rank=2, bias must be constant, quantization params must be constant, transA=0, transB in {0,1} |
| QLinearAdd | ADD | All quantization params (a/b/c scale, a/b/c zero_point) must be constant |
| QLinearAveragePool | DEQUANTIZE + TRANSPOSE + AVERAGE_POOL_2D + TRANSPOSE + QUANTIZE | Input rank=4 only, all quantization params (x scale/zero_point, y scale/zero_point) must be constant, kernel_shape/strides must be 2D, dilations=[1,1], ceil_mode in {0,1} (ceil_mode=1 has stricter pad/auto_pad constraints), and count_include_pad=0 |
| QLinearConcat | DEQUANTIZE + CONCATENATION + QUANTIZE | y scale/zero_point and each input triplet (x scale/zero_point) must be constant, input ranks must match, axis must be in range |
| QLinearConv | CONV_2D / DEPTHWISE_CONV_2D | Input/output rank=4, weight must be constant rank=4, all quantization params constant, group conv only regular/depthwise (depthwise detection uses group and weight shape), optional bias must be constant |
| QLinearGlobalAveragePool | AVERAGE_POOL_2D (preferred) / DEQUANTIZE + MEAN + QUANTIZE (fallback) | All quantization params (x scale/zero_point, y scale/zero_point) must be constant, input rank >= 3, channels_last must be 0 or 1. Quantized AVERAGE_POOL_2D path is used for rank-4 with static spatial dims and per-tensor quantization |
| QLinearLeakyRelu | DEQUANTIZE + PRELU + QUANTIZE | All quantization params (x/y scale, x/y zero_point) must be constant |
| QLinearMatMul | FULLY_CONNECTED | Input rank=1 or 2, weight must be constant rank=2, all quantization params constant |
| QLinearMul | MUL | All quantization params (a/b/c scale, a/b/c zero_point) must be constant |
| QLinearSigmoid | DEQUANTIZE + LOGISTIC + QUANTIZE | All quantization params (x scale/zero_point, y scale/zero_point) must be constant |
| QLinearSoftmax | DEQUANTIZE + SOFTMAX + QUANTIZE | All quantization params (x/y scale, x/y zero_point) must be constant; axis must be last dimension |
| QuantizeLinear | QUANTIZE | scale must be constant, zero_point (if provided) must be constant, per-axis axis must be in range |
| RandomNormal | RANDOM_STANDARD_NORMAL (+ optional MUL + ADD + CAST) | shape attribute must be present and non-empty; output dtype must be FLOAT16/FLOAT32; seed is mapped to TFLite random options when provided |
| RandomNormalLike | SHAPE + RANDOM_STANDARD_NORMAL (+ optional MUL + ADD + CAST) | Rank inferred from input shape; output dtype must be supported numeric type (FLOAT16/FLOAT32/INT*/UINT*). seed is mapped to TFLite random options when provided |
| RandomUniform | RANDOM_UNIFORM (+ optional MUL + ADD + CAST) | shape attribute must be present and non-empty; output dtype must be FLOAT16/FLOAT32; seed is mapped to TFLite random options when provided |
| RandomUniformLike | SHAPE + RANDOM_UNIFORM (+ optional MUL + ADD + CAST) | Input rank is used only to materialize the runtime shape; output dtype must be FLOAT16/FLOAT32; seed is mapped to TFLite random options when provided |
| Range | CAST + SQUEEZE + RANGE | Each of start/limit/delta must be scalar-like rank-1 length-1 tensor |
| Reciprocal | DIV | Input/output dtype must be FLOAT16 or FLOAT32 |
| ReduceL1 | ABS + SUM | Reduce axes must be constant when provided via input tensor |
| ReduceL2 | MUL + SUM + SQRT + CAST | Reduce axes must be constant when provided via input tensor |
| ReduceLogSum | SUM + LOG (+ optional CAST) | Input/output dtype must be FLOAT16/FLOAT32; reduce axes must be constant when provided via input tensor |
| ReduceLogSumExp | EXP + SUM + LOG (+ optional CAST) | Input/output dtype must be FLOAT16/FLOAT32; reduce axes must be constant when provided via input tensor |
| ReduceMax | REDUCE_MAX | Reduce axes must be constant when provided via input tensor |
| ReduceMean | MEAN | Reduce axes must be constant when provided via input tensor |
| ReduceMin | REDUCE_MIN | Reduce axes must be constant when provided via input tensor |
| ReduceProd | REDUCE_PROD | Reduce axes must be constant when provided via input tensor |
| ReduceSumSquare | MUL + SUM (+ optional CAST) | Input/output dtype must be FLOAT16/FLOAT32; reduce axes must be constant when provided via input tensor |
| ReduceSum | SUM | Reduce axes must be constant when provided via input tensor |
| Relu | RELU | - |
| Reshape | RESHAPE | Shape input must be constant |
| Resize | RESIZE_NEAREST_NEIGHBOR / RESIZE_BILINEAR / (cubic) RESHAPE + BATCH_MATMUL + RESHAPE + BATCH_MATMUL | Rank-4 only. nearest/linear: builtin resize path (limited attr combinations), parameters must be constant scales/sizes or dynamic rank-1 integer sizes (INT32/INT64). cubic: strict ONNX cubic decomposition (no FlexResizeBicubic), supports coordinate_transformation_mode in {align_corners, asymmetric, half_pixel, pytorch_half_pixel} and honors cubic_coeff_a/exclude_outside; requires static input C/H/W and static output H/W. Batch dimension is preserved through the cubic decomposition path |
| ReverseSequence | CAST + REVERSE_SEQUENCE | Input rank must be >=2; seq_lengths must be rank-1 integer tensor; batch_axis/time_axis must be in range and different |
| RoiAlign | CAST + GATHER + PAD + RESHAPE + ADD/SUB/MUL/DIV + MAXIMUM/MINIMUM + FLOOR + TILE + AVERAGE_POOL_2D / MAX_POOL_2D + TRANSPOSE | Input/output rank=4 only; rois rank=2 ([...,4]), batch_indices rank=1 integer; input C/H/W must be static positive; mode in {avg,max}; coordinate_transformation_mode in {half_pixel,output_half_pixel}; output_height/output_width must be positive |
| RotaryEmbedding | TRANSPOSE + SLICE + RESHAPE + CAST + MUL + SUB + ADD + CONCATENATION | Current builtin path supports rank-4 input/output only, interleaved=0, and no position_ids input; all tensor dtypes must be FLOAT16/FLOAT32 with output dtype matching input; shapes must be static positive; cos/sin must be rank-2 with shape [seq_len, rotary_embedding_dim/2]; rotary_embedding_dim must be even and <= head_size |
| RNN | UNIDIRECTIONAL_SEQUENCE_RNN + REVERSE_V2 + CONCATENATION + TRANSPOSE + EXPAND_DIMS + SLICE + SQUEEZE + RESHAPE | direction in {forward,reverse,bidirectional}, layout=0; sequence_lens unsupported; W/R must be constant rank-3 with num_directions matching direction; optional B must be constant shape [num_directions, 2*hidden_size]; optional initial_h shape must be [num_directions, batch, hidden] (runtime tensor inputs are supported); activations in {tanh,relu,sigmoid}; clip=0 |
| Round | ROUND | - |
| Scatter | CAST + LESS + SELECT + SHAPE + GATHER + RANGE + RESHAPE + TILE + CONCATENATION + MUL + ADD + SUB + FILL + SCATTER_ND | Alias of ScatterElements; reduction=none only; axis must be in range; indices dtype must be integer; updates/output dtype must match data dtype |
| ScatterND | CAST + SHAPE + FILL + MUL + SCATTER_ND + SUB + ADD | reduction=none only; data/updates/output dtypes must match (numeric), indices dtype must be integer, indices last dim must be static positive and <= data rank |
| ScatterElements | CAST + LESS + SELECT + SHAPE + GATHER + RANGE + RESHAPE + TILE + CONCATENATION + MUL + ADD + SUB + FILL + SCATTER_ND | reduction=none only; data/indices/updates must have same rank; axis must be in range; indices dtype must be integer; updates/output dtype must match data dtype; output shape must match data shape |
| TensorScatter | CAST + GATHER + RESHAPE + ADD (+ FLOOR_MOD for mode=circular) + CONCATENATION + FILL + MUL + SUB + SCATTER_ND | data/updates/output rank must match and updates shape must be static positive; axis must be in range; mode in {linear,circular}; optional write_indices must be rank-1 integer tensor with length >= updates.shape[0]; output dtype must match data dtype; mode=circular requires static positive axis dim |
| Selu | MAXIMUM + MINIMUM + EXP + SUB + MUL + ADD | Input/output dtype must be FLOAT16 or FLOAT32 |
| Shape | SHAPE (+ SLICE for start/end) | Output dtype must be INT32 or INT64; start/end slicing follows ONNX normalization |
| Shrink | ADD + SUB + LESS + GREATER + SELECT_V2 (+ optional CAST) | Input/output dtype must be FLOAT16 or FLOAT32 |
| Sigmoid | LOGISTIC | - |
| Sign | SIGN | - |
| Sin | SIN | - |
| Sinh | SUB + EXP + MUL | Input/output dtype must be FLOAT16 or FLOAT32 |
| Size | SHAPE + REDUCE_PROD (+ optional CAST) | Computes tensor element count via Shape -> ReduceProd; output dtype follows ONNX output type (INT32/INT64) |
| StringNormalizer | RESHAPE (no-op) / EQUAL + LOGICAL_OR + LOGICAL_NOT + WHERE + GATHER (+ EXPAND_DIMS for rank-2) / const-fold | Input/output dtype must be STRING, locale must be '' or en_US. Runtime path supports only case_change_action=NONE (or empty). For non-constant input, stopword filtering is supported only when is_case_sensitive=1 and input rank is 1 or 2 (rank=2 follows current onnx2tf behavior and processes the first row). Constant-input path is folded at conversion time and supports LOWER/UPPER, case-insensitive matching, and empty-result fallback ("") semantics. |
| Slice | SLICE / STRIDED_SLICE / REVERSE_V2 | starts must be constant input/attr. ends is usually constant input/attr; dynamic ends is supported only for rank-1 axis-0 prefix slice (start=0, step=1). steps=0 unsupported. Negative steps are supported only for full-axis reverse pattern (start=-1, very negative end, step=-1) via REVERSE_V2 |
| Softmax | SOFTMAX (+ transpose in/out for non-last axis) | axis must be in range (negative axis normalized) |
| SoftmaxCrossEntropyLoss | TRANSPOSE + SOFTMAX + LOG + CAST + EQUAL + SELECT_V2 + ONE_HOT + MUL + SUM + SUB (+ optional GATHER/MEAN/DIV) | Input/output dtype must be FLOAT16/FLOAT32; labels dtype must be integer; input rank must be >=2 with static positive class dim at axis 1; optional weight must be rank-1 float tensor of length C; reduction in {none,sum,mean}; optional output[1] log-prob tensor is supported |
| Softplus | EXP + ADD + LOG | Input/output dtype must be FLOAT16 or FLOAT32 |
| Softsign | ABS + ADD + DIV | Input/output dtype must be FLOAT16 or FLOAT32 |
| STFT | SLICE + MUL + RESHAPE + BATCH_MATMUL + CONCATENATION (+ optional CAST) | Current builtin path supports rank-2 signal input only with onesided=1; frame_step, window, and frame_length inputs must be constant; shapes must be static positive with signal_length >= frame_length; window length must equal frame_length; output shape must be [batch, num_frames, frame_length//2 + 1, 2] |
| SpaceToDepth | SPACE_TO_DEPTH | blocksize > 1, rank=4 (NCHW) |
| Split | SLICE | axis must be in range; explicit split sizes (input/attr) must be constant and count must match outputs; without explicit split sizes, axis dim must be known and divisible by output count |
| Sqrt | SQRT | - |
| Squeeze | SQUEEZE | Axes must be constant when provided via input tensor |
| Sub | SUB | - |
| Sum | ADD (chained for >2 inputs) | At least 2 inputs |
| Tan | SIN + COS + DIV | Input/output dtype must be FLOAT16 or FLOAT32 |
| Tanh | TANH | - |
| ThresholdedRelu | GREATER + CAST + MUL | Input/output dtype must be FLOAT16 or FLOAT32 |
| Tile | CAST + TILE | multiples must be rank-1 integer tensor; if input rank is static, len(multiples) must match input rank; constant multiples must be non-negative |
| TopK | TOPK_V2 (+ optional TRANSPOSE + NEG + CAST + SQUEEZE) | Input rank must be >=1; input dtype must be FLOAT16/FLOAT32; axis must be in range; largest must be 0 or 1; sorted must be 1; k must be scalar-like ([] or [1]) and integer dtype; indices output dtype must be INT32 or INT64 |
| Transpose | TRANSPOSE | Permutation input must be constant |
| Trilu | MUL / LOGICAL_AND | Input rank must be >=2; matrix dims must be static positive; optional k input must be constant |
| Unique | CAST + FLOOR_MOD + UNIQUE + CONCATENATION | Input dtype must be integer; output[0] dtype must be integer; sorted must be 0 or 1; when axis is specified, only axis=0 is supported and input must be rank-2 with static positive second dimension; builtin path supports output[0] only (other outputs must be unused) |
| Unsqueeze | RESHAPE | Axes must be constant and unique. Axis normalization follows output-rank semantics (output_rank = input_rank + len(axes)), so opset8-style patterns such as input_rank=2, axes=[2,3] are supported |
| Upsample | RESIZE_NEAREST_NEIGHBOR / RESIZE_BILINEAR / (cubic) RESHAPE + BATCH_MATMUL + RESHAPE + BATCH_MATMUL | Legacy alias of Resize lowered by the same builder. Rank-4 only; supports constrained nearest/linear builtin resize and constrained cubic decomposition path. Parameter input follows Upsample 2-input form (scales/sizes) with the same constant/dynamic integer constraints as Resize |
| Where | CAST + SELECT | Condition input dtype must be BOOL or numeric |
| Xor | NOT_EQUAL | - |
| ONNX OP | Default policy | When enabled |
|---|---|---|
| DeformConv | builtin_supported on constrained standard 2D float pattern (group=1, offset_group=1); otherwise explicit_error (custom_op_candidate_disabled) | Grouped or otherwise unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| DynamicQuantizeLinear | builtin_supported on constrained float-input/uint8-output pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| Einsum | builtin_supported on constrained equations; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported equations can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| GridSample | builtin_supported on constrained rank-4/5 bilinear pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported GridSample patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| If | builtin_supported on constrained patterns (NMS-guard, axis0 Add-branch, SequenceConstruct Add-branch, and nested ReduceMin/Add); otherwise explicit_error (custom_op_candidate_disabled) | Unsupported If patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| LogSoftmax | builtin_supported on constrained axis pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| Loop | builtin_supported on constrained patterns (static-unroll / WHILE loop-carried forms); otherwise explicit_error (custom_op_candidate_disabled) | Unsupported Loop patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| LSTM | builtin_supported on constrained forward/reverse/bidirectional pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| NonMaxSuppression | builtin_supported on constrained rank-3 boxes/scores pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| QLinearConv | builtin_supported on constrained regular/depthwise pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported grouped patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| RoiAlign | builtin_supported on constrained pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported RoiAlign patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| Scan | explicit_error (custom_op_candidate_disabled) | Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| ScatterElements | builtin_supported on constrained pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported ScatterElements patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| SequenceAt | explicit_error (custom_op_candidate_disabled) | Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| SequenceConstruct | explicit_error (custom_op_candidate_disabled) | Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| SequenceErase | explicit_error (custom_op_candidate_disabled) | Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| SequenceInsert | explicit_error (custom_op_candidate_disabled) | Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| SequenceLength | explicit_error (custom_op_candidate_disabled) | Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| TopK | builtin_supported on constrained float-input/scalar-k pattern; otherwise explicit_error (custom_op_candidate_disabled) | Unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
| Unique | builtin_supported on constrained integer pattern (output[0]-only); otherwise explicit_error (custom_op_candidate_disabled) | Unsupported patterns can be lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes |
Notes:
Einsumis now treated asbuiltin_supportedwhen it matches builtin constraints; unsupportedEinsumpatterns may still fallback toCUSTOMif custom-op mode is enabled.QLinearConvis treated asbuiltin_supportedfor regular/depthwise patterns; unsupported grouped patterns may still fallback toCUSTOMwhen custom-op mode is enabled.LogSoftmaxis now treated asbuiltin_supportedwhen builtin constraints pass; unsupported patterns may still fallback toCUSTOMif custom-op mode is enabled.LSTMis now treated asbuiltin_supportedfor constrained forward/reverse/bidirectional patterns; unsupported patterns may still fallback toCUSTOMif custom-op mode is enabled.NonMaxSuppressionis now treated asbuiltin_supportedwhen builtin constraints pass; unsupported patterns may still fallback toCUSTOMif custom-op mode is enabled.DynamicQuantizeLinearis now treated asbuiltin_supportedfor constrained float-input/uint8-output patterns; unsupported patterns may still fallback toCUSTOMif custom-op mode is enabled.Ifis now treated asbuiltin_supportedfor constrained patterns (NMS-guard, axis0 Add-branch, SequenceConstruct Add-branch, and nested ReduceMin/Add); unsupported patterns may still fallback toCUSTOMif custom-op mode is enabled.Loopis now treated asbuiltin_supportedfor constrained static-unroll/WHILE loop-carried patterns; unsupported patterns may still fallback toCUSTOMif custom-op mode is enabled.StringNormalizeris now treated asbuiltin_supportedunder constrained runtime patterns (case_change_action=NONE, locale''/en_US, rank1/2). Unsupported runtime patterns (e.g.LOWER/UPPERor case-insensitive stopword filtering) may still fallback toCUSTOMif custom-op mode is enabled.StringNormalizerconstant-input graphs are now folded at conversion time with string-constant buffer serialization support in flatbuffer_direct (including stopword filtering andLOWER/UPPERcase conversion).OptionalHasElementis now treated asbuiltin_supportedfor determinable-presence cases (non-optional inputs andOptionalproducer lineage), reducingONNX_OPTIONALHASELEMENTcustom-op fallbacks.OneHot,MatMulInteger,Pow,Reciprocal, andInverseare now treated asbuiltin_supportedwhen builtin constraints pass.ReduceMinis now treated asbuiltin_supportedunder builtin constraints.MinandTopKare now treated asbuiltin_supportedunder builtin constraints, reducingONNX_MIN/ONNX_TOPKcustom-op fallbacks.DepthToSpaceandHardSwishare now treated asbuiltin_supportedunder builtin constraints (HardSwishis lowered directly as TFLiteHARD_SWISH).Padbuiltin path now supports dynamicpadsinput (rank-1 length2*rank) via `CAST + RES