Back to Onnx

SKILL

.agents/skills/add-op/SKILL.md

1.22.04.4 KB
Original Source

Follow the full procedure in docs/AddNewOp.md.

Files to Modify

ComponentFile
Schema definitiononnx/defs/<domain>/defs.cc
Operator set registrationonnx/defs/operator_sets.h
Type/shape inferenceInline in schema via .TypeAndShapeInferenceFunction(...)
Function body (if applicable)Inline in schema via .FunctionBody(...)
Reference implementationonnx/reference/ops/op_<lowercase_name>.py
Node testsonnx/backend/test/case/node/<lowercase_name>.py
Shape inference testsonnx/test/shape_inference_test.py
Version converter adapter (if behavior changed)onnx/version_converter/adapters/<name>_<from>_<to>.h
Upgrade/downgrade testsonnx/test/version_converter/automatic_upgrade_test.py and automatic_downgrade_test.py

Domain subdirectories under onnx/defs/: math/, nn/, tensor/, logical/, reduction/, rnn/, sequence/, image/, text/, quantization/, controlflow/, optional/, traditionalml/, training/

Schema Registration Pattern

cpp
ONNX_OPERATOR_SET_SCHEMA(
    OperatorName,
    OPSET_VERSION,
    OpSchema()
        .SetDoc(OperatorName_verN_doc)
        .Input(0, "X", "Description", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
        .Output(0, "Y", "Description", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
        .Attr("attr_name", "Description", AttributeProto::FLOAT, default_value)
        .TypeConstraint("T", {"tensor(float)", "tensor(double)", ...}, "Description")
        .TypeAndShapeInferenceFunction(InferShapeForOperatorName)
        // Function body uses ONNX text format — see the onnxtxt skill for syntax/conventions.
        .FunctionBody(R"ONNX(
          {
            ...
          }
        )ONNX", FUNCTION_OPSET_VERSION));

Updating an Existing Operator

  1. Move current schema from defs.cc to old.cc in the same domain directory
  2. Create new version in defs.cc with the new opset version number
  3. Update onnx/defs/operator_sets.h
  4. Add a version converter adapter if behavior changed
  5. Add upgrade/downgrade tests

Avoiding duplication between defs.cc and old.cc

When moving a schema to old.cc, avoid significant code/documentation duplication:

  • Extract common logic (doc strings, type constraint lists, shape inference helpers) into shared functions in the domain's header or utils file.
  • Use parameterized helpers when versions differ only slightly (e.g., expanded type list, additional optional input).
  • Some duplication is acceptable when sharing would create overly complicated logic. Prefer clarity over DRY when the alternative makes either version harder to understand independently.

Code Style: Prefer Named Functions

Define shape inference functions and function body builders as separate named functions rather than inline lambdas within ONNX_OPERATOR_SET_SCHEMA. The macro expansion makes setting breakpoints on inline lambdas unreliable in debuggers.

cpp
// PREFERRED: named function — easy to set breakpoints
static void InferShapeForMyOp(InferenceContext& ctx) {
    propagateElemTypeFromInputToOutput(ctx, 0, 0);
    // ...
}

ONNX_OPERATOR_SET_SCHEMA(
    MyOp, 21,
    OpSchema()
        // ...
        .TypeAndShapeInferenceFunction(InferShapeForMyOp));

Writing Tests

For test fixtures, prefer the ONNX text format via onnx.parser / C++ OnnxParser. See the onnxtxt skill for the per-file recommendations table, body-subgraph idioms, the argument-order convention, and the unk__* materialization gotcha. (Empirical: PR #7962 cut ~58–70% of test LOC by switching.)

After Making Changes

bash
python onnx/defs/gen_doc.py
python onnx/backend/test/stat_coverage.py
python onnx/gen_proto.py  # only if proto changed
lintrunner -a --output oneline

References