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.

2.6.9发布于 2026-09-14 01:46 UTC1,002star106fork同步于 2026-11-07 07:30 UTC

怎么上手

由 README 提炼,执行前请确认
pip install onnx2tf
硬件要求按 README 说明

README 未说明

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

把 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 做输入输出张量重命名

容易踩的坑

README 建议优先使用 LiteRT Torch 而非本工具;INT8 量化后精度可能因 SiLU/Swish 等激活函数和池化、卷积核尺寸而显著下降;工具不提供向 tflite 写入 label map 等元数据的功能

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本2.6.9
  1. 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 -cotof prints Matches for 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_tensor is 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, and min_abs_err_perm_1 silently 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.py only; 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 through apply_explicit_padding, which returns its argument unchanged unless padded is 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 feeds np.ones and 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 -cotof
    

    main (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=False
    

    this PR:

    perm=(0, 1, 2, 3) err=41.81199645996094     tf_out=(1, 4, 4, 8)
    perm=(0, 1, 3, 2)
    
  2. 2.6.82026-08-01 · 2026-08-01 04:28 UTC
    查看更新说明

    2.6.8

    Issue

    Conv.make_node() reads input_tensor_shape before the workaround that transposes an NCHW input to NHWC, and never refreshes it.

    The depthwise test compares group against input_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 a DepthwiseConv2D.

    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 Add whose input required the transpose).

    The ONNX model is valid — depthwise is Conv with group=C and weight [C, 1, K, K].

    Fix

    Refresh input_tensor_shape from input_tensor right before the depthwise test.

    What's Changed

    Full Changelog: https://github.com/PINTO0309/onnx2tf/compare/2.6.7...2.6.8

  3. 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_direct backend. 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

    ConversionSession now 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

  4. 2.6.62026-07-19 · 2026-07-19 01:04 UTC
    查看更新说明

    Summary

    This branch continues the staged refactoring of the TensorFlow-free flatbuffer_direct backend. 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.5 to 2.6.6 in 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.

    ConversionSession retains 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

  5. 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_direct refactor. 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.py is reduced from 25,647 lines on main to 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 -cotof outside 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:

    1. characterize the existing match, guard, ordering, statistics, metadata, and no-op behavior;
    2. add rejection, fan-out, boundary, dynamic-shape, quantization, and idempotence fixtures;
    3. correct mutation atomicity where a rejected candidate could leak changes;
    4. extract the implementation into one focused owner while retaining the original private lowerer symbol as a thin compatibility wrapper;
    5. 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

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    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

    Downloads GitHub Python PyPI CodeQL Model Convert Test Status DOI Ask DeepWiki

    tf_converter supported layers

    • https://github.com/onnx/onnx/blob/main/docs/Operators.md

    • :heavy_check_mark:: Supported :white_check_mark:: Partial support Help wanted: Pull Request are welcome

      OPStatus
      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_direct is the current default backend. Use --tflite_backend tf_converter only 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:

    1. ONNX graph preprocessing (tflite_builder.preprocess) and direct lowering (lower_onnx_to_ir)
    2. Direct FlatBuffer export (*_float32.tflite, *_float16.tflite, and optional quantized variants)
    3. 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.947s
    • flatbuffer_direct: ~0.239s
    • flatbuffer_direct was approximately 107x faster than tf_converter in 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_save with --output_h5, --output_keras_v3, or --output_tfv1_pb
    • --enable_auto_split_model with --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_direct is required for both
    • --flatbuffer_direct_output_saved_model cannot be combined with --disable_model_save
    • CUSTOM ops are rejected with an explicit error
    INT8 ONNXINT8 TFLite(LiteRT)
    • e.g. LiteRT only output

      onnx2tf \
      -i iat_llie_180x320.onnx \
      -tb flatbuffer_direct
      
    • e.g. Generate additional saved_model from LiteRT after LiteRT output

      onnx2tf \
      -i iat_llie_180x320.onnx \
      -tb flatbuffer_direct \
      -fdosm
      
    • e.g. Generate saved_model directly from an existing LiteRT (.tflite) file

      onnx2tf \
      -it iat_llie_180x320_float32.tflite \
      -tb flatbuffer_direct
      
    • e.g. Generate .h5 directly from an existing LiteRT (.tflite) file without tf_converter fallback

      onnx2tf \
      -it iat_llie_180x320_float32.tflite \
      -tb flatbuffer_direct \
      -oh5
      
    • e.g. Generate a PyTorch package directly from an existing LiteRT (.tflite) file

      onnx2tf \
      -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 \
      -cotof
      

      This 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.tflite
    • yolox_s_float16.tflite
    • yolox_s_accuracy_report.json (ONNX↔TFLite)
    • yolox_s_pytorch_accuracy_report.json (ONNX↔PyTorch)
    • yolox_s_accuracy_comparison_report.json
    • yolox_s_pytorch/
      • model.py
      • runtime.py
      • state_dict.pth
      • metadata.json
      • yolox_s_pytorch/yolox_s_jit.pt
      • yolox_s_pytorch/yolox_s_dynamo.onnx
      • yolox_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_pytorch are 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=True as 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.pth is load_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 layers table above.
    • Source of truth: onnx2tf/tflite_builder/op_registry.py and --report_op_coverage output.
    • 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_coverage for the latest numbers.

    Notes:

    • flatbuffer_direct supports 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_coverage and check *_op_coverage_report.json.
    ONNX OPTFLite OPKey constraints (flatbuffer_direct)
    AbsABS (or NEG + MAXIMUM for INT64)For INT64 input, lowered as NEG + MAXIMUM because TFLite ABS kernel does not support INT64
    AcosMUL + SUB + SQRT + ATAN2Input/output dtype must be FLOAT16 or FLOAT32
    AcoshSUB + ADD + SQRT + MUL + LOGInput/output dtype must be FLOAT16 or FLOAT32
    AddADD-
    AffineGridBATCH_MATMUL + TRANSPOSE + RESHAPEsize 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}
    AndLOGICAL_AND-
    ArgMaxARG_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
    ArgMinARG_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
    AttentionRESHAPE + TRANSPOSE + BATCH_MATMUL + MUL + SOFTMAX + CASTCanonical 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
    AsinMUL + SUB + SQRT + ATAN2Input/output dtype must be FLOAT16 or FLOAT32
    AsinhMUL + ADD + SQRT + LOGInput/output dtype must be FLOAT16 or FLOAT32
    AtanATAN2Input/output dtype must be FLOAT16 or FLOAT32
    AtanhADD + SUB + DIV + LOG + MULInput/output dtype must be FLOAT16 or FLOAT32
    AveragePoolAVERAGE_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
    BatchNormalizationMUL + ADDAll parameter inputs (scale, bias, mean, var) must be constant
    BernoulliSHAPE + RANDOM_UNIFORM + LESS (+ optional CAST)Input dtype must be FLOAT16/FLOAT32; output dtype must be BOOL or numeric
    BitShiftRIGHT_SHIFT (RIGHT) or MUL-based (LEFT)LHS/RHS must be integer tensors, direction must be LEFT or RIGHT; LEFT requires constant shift input
    BitwiseAndLOGICAL_ANDBOOL tensors only
    BitwiseNotLOGICAL_NOT / SUB + CASTInput dtype must be BOOL or integer
    BitwiseOrLOGICAL_ORBOOL tensors only
    BitwiseXorBITWISE_XORInput dtypes must match and be BOOL/integer
    BlackmanWindowCAST + SQUEEZE + RANGE + MUL + DIV + COS + SUB + ADD + MAXIMUMInput must be scalar-like rank-1 length-1 integer tensor; output dtype must be FLOAT16/FLOAT32
    CastCAST-
    CastLikeCAST-
    CeilCEIL-
    CeluMAXIMUM + MINIMUM + DIV + EXP + SUB + MUL + ADDInput/output dtype must be FLOAT16 or FLOAT32
    CenterCropPadSLICE + 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
    ClipRELU / RELU6 / MAXIMUM + MINIMUMGeneral constant clip ranges are supported via MAXIMUM/MINIMUM decomposition. ReLU fast-path: min=0,max=+inf; ReLU6 fast-path: min=0,max=6
    Col2ImRESHAPE + TRANSPOSE + TRANSPOSE_CONV + SLICE + CASTInput/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
    ConcatCONCATENATION-
    ConstantOfShapeCAST + FILLShape input must be rank-1 integer tensor; value attribute must be scalar (or omitted for zero-fill)
    ConvCONV_2D / DEPTHWISE_CONV_2D / CONV_3D2D: 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
    ConvIntegerCAST + SUB + PAD + CONV_2D / DEPTHWISE_CONV_2D + TRANSPOSEInput must be integer tensor, output dtype must be INT32/INT64, weights must be constant rank-4, and grouped conv must be regular/depthwise only
    ConvTransposeTRANSPOSE_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
    CompressNOT_EQUAL + WHERE + RESHAPE + CAST + GATHERCondition 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
    CosCOS-
    CoshSUB + EXP + ADD + MULInput/output dtype must be FLOAT16 or FLOAT32
    CumProdRANGE + 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
    DFTRESHAPE + 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]
    CumSumCUMSUMInput rank must be >=1; axis must be scalar constant (or attr) and in range; exclusive/reverse must be 0 or 1
    DeformConvPAD + RESHAPE + TRANSPOSE + SHAPE + RANGE + SQUEEZE + GATHER + FLOOR + MAXIMUM/MINIMUM + CAST + MUL + ADD + SUB + BATCH_MATMULConstrained 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
    DequantizeLinearDEQUANTIZEscale must be constant, zero_point (if provided) must be constant, per-axis axis must be in range
    DepthToSpaceDEPTH_TO_SPACE (DCR) / RESHAPE + TRANSPOSE + RESHAPE (CRD)Rank-4 only, blocksize > 1, mode in {DCR,CRD}
    DetGATHER + RESHAPE + MUL + SUB + ADDInput/output dtype must be FLOAT16/FLOAT32; builtin lowering currently supports static square 2x2 / 3x3 matrices only
    DivDIV 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
    DropoutRESHAPE (+ optional SHAPE + FILL for mask output)Inference-time no-op in flatbuffer_direct; inputs ratio/training_mode are ignored
    DynamicQuantizeLinearNEG + REDUCE_MAX + MINIMUM + MAXIMUM + SUB + DIV + ADD + CASTInput 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
    EinsumFULLY_CONNECTEDRank-2 matmul-style equation only (ij,jk->ik), rhs input must be constant weights
    EluELU-
    EqualEQUAL-
    ErfABS + SIGN + MUL + ADD + DIV + EXP + SUBInput/output dtype must be FLOAT16 or FLOAT32
    ExpEXP-
    ExpandRESHAPE + 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)
    EyeLikeRESHAPE (from const eye)Output must be rank-2 with fully static positive shape
    FlattenRESHAPEInput rank must be >= 1
    FloorFLOOR-
    FusedConvCONV_2D / DEPTHWISE_CONV_2D + fused activationSupports Relu/Tanh/Sigmoid/LeakyRelu/Clip/HardSigmoid activations with valid scalar params; convolution constraints follow builtin Conv/FusedConv validator
    FusedMatMulBATCH_MATMUL (+ optional MUL for alpha)Input rank >= 2, dtypes FLOAT16/FLOAT32 only, transA/transB must be 0 or 1, finite alpha required
    GatherGATHERbatch_dims=0 only
    GatherElementsCAST + RESHAPE + CONCATENATION + GATHER_NDData/indices ranks must match, output shape must equal indices shape, static positive output dims required, axis must be in range
    GatherNDCAST + GATHER_NDbatch_dims=0 only; indices must be integer type; indices last dim must be static positive and <= params_rank
    GeluGELU-
    GemmFULLY_CONNECTEDInput rank=2, weight rank=2 + constant, transA=0 only
    GreaterGREATER-
    GreaterOrEqualGREATER_EQUAL-
    GridSamplePAD + RESHAPE + TRANSPOSE + SQUEEZE + SLICE + ADD/SUB/MUL + FLOOR + MAXIMUM/MINIMUM + CAST + GATHERRank-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)
    GlobalAveragePoolMEANInput rank must be >=3
    GlobalLpPoolABS + POW + SUM + RESHAPE (+ optional CAST)Input rank must be >=3; input/output dtype must be FLOAT16/FLOAT32; p must be finite and > 0
    GlobalMaxPoolREDUCE_MAXInput rank must be >=3
    GroupNormalizationRESHAPE + 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
    GRUTRANSPOSE + SLICE + SQUEEZE + BATCH_MATMUL + ADD + MUL + SUB + LOGISTIC + TANH + RESHAPE + CONCATENATION + EXPAND_DIMSlayout=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
    HardmaxTRANSPOSE + ARG_MAX + ONE_HOTaxis must be in range; target axis size must be static positive
    HardSigmoidMUL + ADD + MAXIMUM + MINIMUMInput/output dtype must be FLOAT16 or FLOAT32
    HardSwishHARD_SWISHInput/output dtype must be FLOAT16 or FLOAT32
    HammingWindowCAST + SQUEEZE + RANGE + MUL + DIV + COS + SUB + MAXIMUMInput must be scalar-like rank-1 length-1 integer tensor; output dtype must be FLOAT16/FLOAT32
    HannWindowCAST + SQUEEZE + RANGE + MUL + DIV + COS + SUB + MAXIMUMInput must be scalar-like rank-1 length-1 integer tensor; output dtype must be FLOAT16/FLOAT32
    MelWeightMatrixconst-folded builtin tensor materializationAll 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
    IdentityRESHAPE-
    IfCONCATENATION + REDUCE_MAX + CAST + ADD + MUL + RESHAPE + NON_MAX_SUPPRESSION_V4/V5 + SLICE + GATHER + SHAPE + SUB + SELECT/SELECT_V2Built-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)
    InstanceNormalizationMEAN + SUB + MUL + MEAN + ADD + SQRT + DIV + MUL + ADDInput/output dtype must be FLOAT16 or FLOAT32; input rank must be >=3; scale and bias inputs must be constant
    IsInfABS + EQUAL / LESS + GREATER + LOGICAL_ANDInput dtype must be FLOAT16/FLOAT32; output dtype must be BOOL; detect_negative / detect_positive are honored
    IsNaNNOT_EQUALInput dtype must be FLOAT16/FLOAT32; output dtype must be BOOL
    MeanVarianceNormalizationMEAN + SUB + MUL + MEAN + ADD + SQRT + DIVInput/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
    InverseSLICE + 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
    LeakyReluLEAKY_RELUInput/output dtype must be FLOAT16 or FLOAT32
    LessLESS-
    LessOrEqualLESS_EQUAL-
    LogLOGInput/output dtype must be FLOAT16 or FLOAT32
    LogSoftmaxSOFTMAX + LOG (+ transpose in/out for non-last axis)axis must be in range (negative axis normalized)
    LoopWHILE (+ 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
    LpPoolABS + 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
    LpNormalizationL2_NORMALIZATIONp=2, axis=last only
    LRNLOCAL_RESPONSE_NORMALIZATION (+ transpose in/out)Input rank must be 4, size must be a positive odd integer
    LayerNormalizationMEAN + SUB + MUL + ADD + SQRT + DIV (+ optional CAST)axis and stash_type are honored; optional ONNX outputs (mean, inv_std_dev) are supported
    LSTMUNIDIRECTIONAL_SEQUENCE_LSTM / BIDIRECTIONAL_SEQUENCE_LSTM + REVERSE_V2 + SPLIT + SQUEEZE + SLICE + RESHAPE/EXPAND_DIMS + CONCATENATIONdirection 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
    MatMulBATCH_MATMUL (+ CAST/RESHAPE/SQUEEZE helpers)Supports standard rank>=2 matmul, vector lhs/rhs forms, vector dot, and scalar multiply patterns
    MatMulIntegerCAST + SUB + BATCH_MATMULA/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
    MaxMAXIMUM (chained for >2 inputs)At least 2 inputs
    MaxPoolMAX_POOL_2D2D only (rank=4), ceil_mode in {0,1}, zero pads or auto_pad=SAME_*
    MaxRoiPoolTRANSPOSE + SLICE + MAX_POOL_2D + CONCATENATIONCurrent 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
    MaxUnpoolCAST + RESHAPE + SCATTER_NDInput/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
    MeanADD + DIV (+ optional CAST)All inputs and output must be FLOAT16/FLOAT32
    NegativeLogLikelihoodLossTRANSPOSE + 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
    MinMINIMUM (chained for >2 inputs)At least 2 inputs
    MishEXP + ADD + LOG + TANH + MULInput/output dtype must be FLOAT16 or FLOAT32
    ModFLOOR_MODfmod=0 only
    MulMUL-
    MultiHeadAttentionRESHAPE + TRANSPOSE + BATCH_MATMUL + MUL + SOFTMAX + CASTnum_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
    NegNEG-
    NonMaxSuppressionNON_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.
    NonZeroNOT_EQUAL + WHERE + TRANSPOSE + CASTInput rank must be >=1; output rank must be 2
    NotLOGICAL_NOT-
    OneHotCAST + ADD + FLOOR_MOD + ONE_HOTdepth 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
    OptionalHasElementconst-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
    OrLOGICAL_OR-
    PadPAD / 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)
    PowPOWOutput dtype must be FLOAT16 or FLOAT32
    PReluPRELUslope must be constant (scalar or per-channel)
    QGemmFULLY_CONNECTEDInput 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}
    QLinearAddADDAll quantization params (a/b/c scale, a/b/c zero_point) must be constant
    QLinearAveragePoolDEQUANTIZE + TRANSPOSE + AVERAGE_POOL_2D + TRANSPOSE + QUANTIZEInput 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
    QLinearConcatDEQUANTIZE + CONCATENATION + QUANTIZEy scale/zero_point and each input triplet (x scale/zero_point) must be constant, input ranks must match, axis must be in range
    QLinearConvCONV_2D / DEPTHWISE_CONV_2DInput/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
    QLinearGlobalAveragePoolAVERAGE_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
    QLinearLeakyReluDEQUANTIZE + PRELU + QUANTIZEAll quantization params (x/y scale, x/y zero_point) must be constant
    QLinearMatMulFULLY_CONNECTEDInput rank=1 or 2, weight must be constant rank=2, all quantization params constant
    QLinearMulMULAll quantization params (a/b/c scale, a/b/c zero_point) must be constant
    QLinearSigmoidDEQUANTIZE + LOGISTIC + QUANTIZEAll quantization params (x scale/zero_point, y scale/zero_point) must be constant
    QLinearSoftmaxDEQUANTIZE + SOFTMAX + QUANTIZEAll quantization params (x/y scale, x/y zero_point) must be constant; axis must be last dimension
    QuantizeLinearQUANTIZEscale must be constant, zero_point (if provided) must be constant, per-axis axis must be in range
    RandomNormalRANDOM_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
    RandomNormalLikeSHAPE + 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
    RandomUniformRANDOM_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
    RandomUniformLikeSHAPE + 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
    RangeCAST + SQUEEZE + RANGEEach of start/limit/delta must be scalar-like rank-1 length-1 tensor
    ReciprocalDIVInput/output dtype must be FLOAT16 or FLOAT32
    ReduceL1ABS + SUMReduce axes must be constant when provided via input tensor
    ReduceL2MUL + SUM + SQRT + CASTReduce axes must be constant when provided via input tensor
    ReduceLogSumSUM + LOG (+ optional CAST)Input/output dtype must be FLOAT16/FLOAT32; reduce axes must be constant when provided via input tensor
    ReduceLogSumExpEXP + SUM + LOG (+ optional CAST)Input/output dtype must be FLOAT16/FLOAT32; reduce axes must be constant when provided via input tensor
    ReduceMaxREDUCE_MAXReduce axes must be constant when provided via input tensor
    ReduceMeanMEANReduce axes must be constant when provided via input tensor
    ReduceMinREDUCE_MINReduce axes must be constant when provided via input tensor
    ReduceProdREDUCE_PRODReduce axes must be constant when provided via input tensor
    ReduceSumSquareMUL + SUM (+ optional CAST)Input/output dtype must be FLOAT16/FLOAT32; reduce axes must be constant when provided via input tensor
    ReduceSumSUMReduce axes must be constant when provided via input tensor
    ReluRELU-
    ReshapeRESHAPEShape input must be constant
    ResizeRESIZE_NEAREST_NEIGHBOR / RESIZE_BILINEAR / (cubic) RESHAPE + BATCH_MATMUL + RESHAPE + BATCH_MATMULRank-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
    ReverseSequenceCAST + REVERSE_SEQUENCEInput rank must be >=2; seq_lengths must be rank-1 integer tensor; batch_axis/time_axis must be in range and different
    RoiAlignCAST + GATHER + PAD + RESHAPE + ADD/SUB/MUL/DIV + MAXIMUM/MINIMUM + FLOOR + TILE + AVERAGE_POOL_2D / MAX_POOL_2D + TRANSPOSEInput/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
    RotaryEmbeddingTRANSPOSE + SLICE + RESHAPE + CAST + MUL + SUB + ADD + CONCATENATIONCurrent 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
    RNNUNIDIRECTIONAL_SEQUENCE_RNN + REVERSE_V2 + CONCATENATION + TRANSPOSE + EXPAND_DIMS + SLICE + SQUEEZE + RESHAPEdirection 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
    RoundROUND-
    ScatterCAST + LESS + SELECT + SHAPE + GATHER + RANGE + RESHAPE + TILE + CONCATENATION + MUL + ADD + SUB + FILL + SCATTER_NDAlias of ScatterElements; reduction=none only; axis must be in range; indices dtype must be integer; updates/output dtype must match data dtype
    ScatterNDCAST + SHAPE + FILL + MUL + SCATTER_ND + SUB + ADDreduction=none only; data/updates/output dtypes must match (numeric), indices dtype must be integer, indices last dim must be static positive and <= data rank
    ScatterElementsCAST + LESS + SELECT + SHAPE + GATHER + RANGE + RESHAPE + TILE + CONCATENATION + MUL + ADD + SUB + FILL + SCATTER_NDreduction=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
    TensorScatterCAST + GATHER + RESHAPE + ADD (+ FLOOR_MOD for mode=circular) + CONCATENATION + FILL + MUL + SUB + SCATTER_NDdata/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
    SeluMAXIMUM + MINIMUM + EXP + SUB + MUL + ADDInput/output dtype must be FLOAT16 or FLOAT32
    ShapeSHAPE (+ SLICE for start/end)Output dtype must be INT32 or INT64; start/end slicing follows ONNX normalization
    ShrinkADD + SUB + LESS + GREATER + SELECT_V2 (+ optional CAST)Input/output dtype must be FLOAT16 or FLOAT32
    SigmoidLOGISTIC-
    SignSIGN-
    SinSIN-
    SinhSUB + EXP + MULInput/output dtype must be FLOAT16 or FLOAT32
    SizeSHAPE + REDUCE_PROD (+ optional CAST)Computes tensor element count via Shape -> ReduceProd; output dtype follows ONNX output type (INT32/INT64)
    StringNormalizerRESHAPE (no-op) / EQUAL + LOGICAL_OR + LOGICAL_NOT + WHERE + GATHER (+ EXPAND_DIMS for rank-2) / const-foldInput/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.
    SliceSLICE / STRIDED_SLICE / REVERSE_V2starts 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
    SoftmaxSOFTMAX (+ transpose in/out for non-last axis)axis must be in range (negative axis normalized)
    SoftmaxCrossEntropyLossTRANSPOSE + 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
    SoftplusEXP + ADD + LOGInput/output dtype must be FLOAT16 or FLOAT32
    SoftsignABS + ADD + DIVInput/output dtype must be FLOAT16 or FLOAT32
    STFTSLICE + 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]
    SpaceToDepthSPACE_TO_DEPTHblocksize > 1, rank=4 (NCHW)
    SplitSLICEaxis 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
    SqrtSQRT-
    SqueezeSQUEEZEAxes must be constant when provided via input tensor
    SubSUB-
    SumADD (chained for >2 inputs)At least 2 inputs
    TanSIN + COS + DIVInput/output dtype must be FLOAT16 or FLOAT32
    TanhTANH-
    ThresholdedReluGREATER + CAST + MULInput/output dtype must be FLOAT16 or FLOAT32
    TileCAST + TILEmultiples must be rank-1 integer tensor; if input rank is static, len(multiples) must match input rank; constant multiples must be non-negative
    TopKTOPK_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
    TransposeTRANSPOSEPermutation input must be constant
    TriluMUL / LOGICAL_ANDInput rank must be >=2; matrix dims must be static positive; optional k input must be constant
    UniqueCAST + FLOOR_MOD + UNIQUE + CONCATENATIONInput 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)
    UnsqueezeRESHAPEAxes 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
    UpsampleRESIZE_NEAREST_NEIGHBOR / RESIZE_BILINEAR / (cubic) RESHAPE + BATCH_MATMUL + RESHAPE + BATCH_MATMULLegacy 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
    WhereCAST + SELECTCondition input dtype must be BOOL or numeric
    XorNOT_EQUAL-
    ONNX OPDefault policyWhen enabled
    DeformConvbuiltin_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
    DynamicQuantizeLinearbuiltin_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
    Einsumbuiltin_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
    GridSamplebuiltin_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
    Ifbuiltin_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
    LogSoftmaxbuiltin_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
    Loopbuiltin_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
    LSTMbuiltin_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
    NonMaxSuppressionbuiltin_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
    QLinearConvbuiltin_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
    RoiAlignbuiltin_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
    Scanexplicit_error (custom_op_candidate_disabled)Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes
    ScatterElementsbuiltin_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
    SequenceAtexplicit_error (custom_op_candidate_disabled)Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes
    SequenceConstructexplicit_error (custom_op_candidate_disabled)Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes
    SequenceEraseexplicit_error (custom_op_candidate_disabled)Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes
    SequenceInsertexplicit_error (custom_op_candidate_disabled)Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes
    SequenceLengthexplicit_error (custom_op_candidate_disabled)Lowered to TFLite CUSTOM when --flatbuffer_direct_allow_custom_ops is enabled and allowlist passes
    TopKbuiltin_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
    Uniquebuiltin_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:

    • Einsum is now treated as builtin_supported when it matches builtin constraints; unsupported Einsum patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • QLinearConv is treated as builtin_supported for regular/depthwise patterns; unsupported grouped patterns may still fallback to CUSTOM when custom-op mode is enabled.
    • LogSoftmax is now treated as builtin_supported when builtin constraints pass; unsupported patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • LSTM is now treated as builtin_supported for constrained forward/reverse/bidirectional patterns; unsupported patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • NonMaxSuppression is now treated as builtin_supported when builtin constraints pass; unsupported patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • DynamicQuantizeLinear is now treated as builtin_supported for constrained float-input/uint8-output patterns; unsupported patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • If is now treated as builtin_supported for constrained patterns (NMS-guard, axis0 Add-branch, SequenceConstruct Add-branch, and nested ReduceMin/Add); unsupported patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • Loop is now treated as builtin_supported for constrained static-unroll/WHILE loop-carried patterns; unsupported patterns may still fallback to CUSTOM if custom-op mode is enabled.
    • StringNormalizer is now treated as builtin_supported under constrained runtime patterns (case_change_action=NONE, locale ''/en_US, rank1/2). Unsupported runtime patterns (e.g. LOWER/UPPER or case-insensitive stopword filtering) may still fallback to CUSTOM if custom-op mode is enabled.
    • StringNormalizer constant-input graphs are now folded at conversion time with string-constant buffer serialization support in flatbuffer_direct (including stopword filtering and LOWER/UPPER case conversion).
    • OptionalHasElement is now treated as builtin_supported for determinable-presence cases (non-optional inputs and Optional producer lineage), reducing ONNX_OPTIONALHASELEMENT custom-op fallbacks.
    • OneHot, MatMulInteger, Pow, Reciprocal, and Inverse are now treated as builtin_supported when builtin constraints pass.
    • ReduceMin is now treated as builtin_supported under builtin constraints.
    • Min and TopK are now treated as builtin_supported under builtin constraints, reducing ONNX_MIN / ONNX_TOPK custom-op fallbacks.
    • DepthToSpace and HardSwish are now treated as builtin_supported under builtin constraints (HardSwish is lowered directly as TFLite HARD_SWISH).
    • Pad builtin path now supports dynamic pads input (rank-1 length 2*rank) via `CAST + RES