Skip to content

[docs] Document rewriter pattern options #2406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Jun 19, 2025

This PR adds comprehensive documentation for the rewriter pattern options that were previously undocumented. The rewriter pattern system supports four key options for controlling pattern matching and replacement behavior:

New Documentation Added

_allow_other_inputs option

  • File: docs/tutorial/rewriter/allow_other_inputs.md
  • Purpose: Controls whether patterns can match nodes with additional inputs beyond those specified
  • Default: False (exact input matching)
  • Example: Matching Conv operations that may have optional bias inputs
def conv_pattern(op, input, weight):
    # Matches Conv with 2 or 3 inputs (weight + optional bias)
    return op.Conv(input, weight, _allow_other_inputs=True)

_domain option

  • File: docs/tutorial/rewriter/domain_option.md
  • Purpose: Specifies operator domains for pattern matching and replacement
  • Use cases: Domain-specific rewrites, migrating between operator domains
  • Example: Targeting operations from specific domains like "com.microsoft"
def custom_relu_pattern(op, input):
    # Only matches Relu from custom domain
    return op.Relu(input, _domain="custom.domain")

_outputs option

  • File: docs/tutorial/rewriter/outputs_option.md
  • Purpose: Specifies number and names of operation outputs
  • Formats: Integer count (_outputs=2) or named list (_outputs=["first", "second"])
  • Example: Handling multi-output operations like Split
def split_pattern(op, input):
    # Matches Split operations with exactly 2 outputs
    return op.Split(input, num_outputs=2, axis=0, _outputs=2)

Enhanced _allow_other_attributes documentation

  • File: docs/tutorial/rewriter/attributes.md (improved formatting)
  • Already documented: Controls whether patterns match nodes with additional attributes
  • Default: True (allows extra attributes)

Documentation Structure Improvements

  • Added "Pattern Options" section to main rewriter documentation
  • Integrated all option docs into the tutorial flow
  • Created working code examples for each option
  • Followed existing documentation patterns and style
  • All examples compile and run successfully
  • Documentation builds correctly with Sphinx

The documentation now provides complete coverage of all rewriter pattern options with practical examples showing real-world usage patterns.

Fixes #2405.

Warning

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • docs.python.org
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)
  • docs.scipy.org
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)
  • matplotlib.org
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)
  • numpy.org
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)
  • onnx.ai
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)
  • onnxruntime.ai
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)
  • pytorch.org
    • Triggering command: python -m sphinx docs dist/html -W -q (dns block)
    • Triggering command: python -m sphinx docs dist/html -q -E -j 1 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@Copilot Copilot AI changed the title [WIP] [docs] Document rewriter options [docs] Document rewriter pattern options Jun 19, 2025
@Copilot Copilot AI requested a review from justinchuby June 19, 2025 21:08
Copilot finished work on behalf of justinchuby June 19, 2025 21:08
@justinchuby justinchuby requested a review from gramalingam June 19, 2025 23:13
@justinchuby justinchuby marked this pull request as ready for review June 19, 2025 23:13
@justinchuby
Copy link
Collaborator

@gramalingam

@justinchuby justinchuby added topic: documentation Improvements or additions to documentation module: rewriter labels Jun 19, 2025
Copy link

codecov bot commented Jun 19, 2025

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
15730 3 15727 1699
View the top 3 failed test(s) by shortest run time
onnxscript.backend.onnx_export_test.TestOnnxBackEnd::test_export2python_produces_correct_onnx_script_model_1143_test_softmax_example
Stack Traces | 0.004s run time
onnxscript\backend\onnx_export_test.py:137: in extract_functions
    mod = importlib.import_module(import_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\hostedtoolcache\windows\Python\3.12.10\x64\Lib\importlib\__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E   ModuleNotFoundError: No module named 'tests.onnx_backend_test_code.test_softmax_example'

The above exception was the direct cause of the following exception:
.nox\test\Lib\site-packages\parameterized\parameterized.py:620: in standalone_func
    return func(*(a + p.args), **p.kwargs, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\backend\onnx_export_test.py:271: in test_export2python_produces_correct_onnx_script_model
    functions = extract_functions(backend_test.name, code, self.test_folder)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\backend\onnx_export_test.py:139: in extract_functions
    raise AssertionError(
E   AssertionError: Unable to import 'tests.onnx_backend_test_code.test_softmax_example' (e=No module named 'tests.onnx_backend_test_code.test_softmax_example') (file: 'D:\\a\\onnxscript\\onnxscript\\tests\\onnx_backend_test_code\\test_softmax_example.py', absolute path: 'D:\\a\\onnxscript\\onnxscript\\tests\\onnx_backend_test_code\\test_softmax_example.py', current folder: D:\a\onnxscript\onnxscript
E   ---- CONTENT --
E   import numpy
E   from onnx import TensorProto
E   from onnx.helper import make_tensor
E   from onnxscript import script, external_tensor
E   from onnxscript.values import Opset
E   from onnxscript.onnx_types import FLOAT
E   from onnxscript.onnx_opset import opset13
E   
E   @script()
E   def bck_test_softmax_example(x: FLOAT[1,3]) -> (FLOAT[1,3]):
E       y = opset13.Softmax(x)
E       return y
onnxscript.backend.onnx_export_test.TestOnnxBackEnd::test_export2python_produces_correct_onnx_script_model_0125_test_ai_onnx_ml_tree_ensemble_set_membership
Stack Traces | 0.009s run time
onnxscript/converter.py:460: in _eval_constant_expr
    return eval(cpl, self.globals, locals)  # pylint: disable=eval-used
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E   NameError: name 'nan' is not defined

The above exception was the direct cause of the following exception:
..../test_ort_nightly/lib/python3.11.../site-packages/parameterized/parameterized.py:620: in standalone_func
    return func(*(a + p.args), **p.kwargs, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/backend/onnx_export_test.py:271: in test_export2python_produces_correct_onnx_script_model
    functions = extract_functions(backend_test.name, code, self.test_folder)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/backend/onnx_export_test.py:137: in extract_functions
    mod = importlib.import_module(import_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.../Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1204: in _gcd_import
    ???
<frozen importlib._bootstrap>:1176: in _find_and_load
    ???
<frozen importlib._bootstrap>:1147: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:690: in _load_unlocked
    ???
..../test_ort_nightly/lib/python3.11.../_pytest/assertion/rewrite.py:186: in exec_module
    exec(co, module.__dict__)
tests/onnx_backend_test_code/test_ai_onnx_ml_tree_ensemble_set_membership.py:9: in <module>
    @script()
     ^^^^^^^^
onnxscript/main.py:94: in transform
    result = script_check(f_ast, opset, env, src, default_opset=default_opset)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/main.py:38: in script_check
    return convert.translate_function_def(f)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/converter.py:1452: in translate_function_def
    fn_ir = self._translate_function_def_common(stmt)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/converter.py:1439: in _translate_function_def_common
    self._translate_stmt(s, index_of_stmt=i)
onnxscript/converter.py:961: in _translate_stmt
    return self._translate_assign_stmt(node)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/converter.py:1048: in _translate_assign_stmt
    assign(lhs, rhs)
onnxscript/converter.py:992: in assign
    t = self._translate_expr(rhs, lhs).name
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/converter.py:546: in _translate_expr
    r = self._translate_call_expr(node)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/converter.py:825: in _translate_call_expr
    attrs = [
onnxscript/converter.py:826: in <listcomp>
    self._translate_attr(x, y, callee.op_schema.attributes[x])
onnxscript/converter.py:510: in _translate_attr
    val = self._eval_constant_expr(expr)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript/converter.py:462: in _eval_constant_expr
    raise NameError(
E   NameError: ERROR: Missing names, globals contains ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', '@py_builtins', '@pytest_ar', 'numpy', 'TensorProto', 'make_tensor', 'script', 'external_tensor', 'Opset', 'FLOAT', 'ai_onnx_ml5'], locals [].
E   at: Function 'bck_test_ai_onnx_ml_tree_ensemble_set_membership', line 3
E       Y = ai_onnx_ml5.TreeEnsemble(X, aggregate_function=1, leaf_targetids=[0, 1, 2, 3], leaf_weights=make_tensor("value", 1, dims=[4], vals=[1.0, 10.0, 1000.0, 100.0]), membership_values=make_tensor("value", 1, dims=[8], vals=[1.2000000476837158, 3.700000047683716, 8.0, 9.0, nan, 12.0, 7.0, nan]), n_targets=4, nodes_falseleafs=[1, 0, 1], nodes_falsenodeids=[2, 2, 3], nodes_featureids=[0, 0, 0], nodes_modes=make_tensor("value", 2, dims=[3], vals=[0, 6, 6]), nodes_splits=make_tensor("value", 1, dims=[3], vals=[11.0, 232344.0, nan]), nodes_trueleafs=[0, 1, 1], nodes_truenodeids=[1, 0, 1], post_transform=0, tree_roots=[0])
E                                                                                                                                                                                             ^
onnxscript.backend.onnx_export_test.TestOnnxBackEnd::test_export2python_produces_correct_onnx_script_model_0026_test_ai_onnx_ml_tree_ensemble_set_membership
Stack Traces | 0.015s run time
onnxscript\converter.py:460: in _eval_constant_expr
    return eval(cpl, self.globals, locals)  # pylint: disable=eval-used
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E   NameError: name 'nan' is not defined

The above exception was the direct cause of the following exception:
.nox\test_ort_nightly\Lib\site-packages\parameterized\parameterized.py:620: in standalone_func
    return func(*(a + p.args), **p.kwargs, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\backend\onnx_export_test.py:271: in test_export2python_produces_correct_onnx_script_model
    functions = extract_functions(backend_test.name, code, self.test_folder)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\backend\onnx_export_test.py:137: in extract_functions
    mod = importlib.import_module(import_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\hostedtoolcache\windows\Python\3.11.9\x64\Lib\importlib\__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1204: in _gcd_import
    ???
<frozen importlib._bootstrap>:1176: in _find_and_load
    ???
<frozen importlib._bootstrap>:1147: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:690: in _load_unlocked
    ???
.nox\test_ort_nightly\Lib\site-packages\_pytest\assertion\rewrite.py:186: in exec_module
    exec(co, module.__dict__)
tests\onnx_backend_test_code\test_ai_onnx_ml_tree_ensemble_set_membership.py:9: in <module>
    @script()
     ^^^^^^^^
onnxscript\main.py:94: in transform
    result = script_check(f_ast, opset, env, src, default_opset=default_opset)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\main.py:38: in script_check
    return convert.translate_function_def(f)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\converter.py:1452: in translate_function_def
    fn_ir = self._translate_function_def_common(stmt)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\converter.py:1439: in _translate_function_def_common
    self._translate_stmt(s, index_of_stmt=i)
onnxscript\converter.py:961: in _translate_stmt
    return self._translate_assign_stmt(node)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\converter.py:1048: in _translate_assign_stmt
    assign(lhs, rhs)
onnxscript\converter.py:992: in assign
    t = self._translate_expr(rhs, lhs).name
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\converter.py:546: in _translate_expr
    r = self._translate_call_expr(node)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\converter.py:825: in _translate_call_expr
    attrs = [
onnxscript\converter.py:826: in <listcomp>
    self._translate_attr(x, y, callee.op_schema.attributes[x])
onnxscript\converter.py:510: in _translate_attr
    val = self._eval_constant_expr(expr)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
onnxscript\converter.py:462: in _eval_constant_expr
    raise NameError(
E   NameError: ERROR: Missing names, globals contains ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', '@py_builtins', '@pytest_ar', 'numpy', 'TensorProto', 'make_tensor', 'script', 'external_tensor', 'Opset', 'FLOAT', 'ai_onnx_ml5'], locals [].
E   at: Function 'bck_test_ai_onnx_ml_tree_ensemble_set_membership', line 3
E       Y = ai_onnx_ml5.TreeEnsemble(X, aggregate_function=1, leaf_targetids=[0, 1, 2, 3], leaf_weights=make_tensor("value", 1, dims=[4], vals=[1.0, 10.0, 1000.0, 100.0]), membership_values=make_tensor("value", 1, dims=[8], vals=[1.2000000476837158, 3.700000047683716, 8.0, 9.0, nan, 12.0, 7.0, nan]), n_targets=4, nodes_falseleafs=[1, 0, 1], nodes_falsenodeids=[2, 2, 3], nodes_featureids=[0, 0, 0], nodes_modes=make_tensor("value", 2, dims=[3], vals=[0, 6, 6]), nodes_splits=make_tensor("value", 1, dims=[3], vals=[11.0, 232344.0, nan]), nodes_trueleafs=[0, 1, 1], nodes_truenodeids=[1, 0, 1], post_transform=0, tree_roots=[0])
E                                                                                                                                                                                             ^

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Copy link
Contributor

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lintrunner found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@Copilot Copilot AI requested a review from gramalingam June 20, 2025 18:55
Copilot finished work on behalf of gramalingam June 20, 2025 18:55
Copilot finished work on behalf of gramalingam June 20, 2025 18:59
@Copilot Copilot AI requested a review from gramalingam June 20, 2025 19:06
Copilot finished work on behalf of gramalingam June 20, 2025 19:06
@gramalingam gramalingam enabled auto-merge (squash) June 20, 2025 20:20
@gramalingam
Copy link
Collaborator

The Lint / Enforce style CI is failing due to some white space issues. Can you fix them?

Copy link
Contributor Author

Copilot AI commented Jun 20, 2025

The Lint / Enforce style CI is failing due to some white space issues. Can you fix them?

Fixed the whitespace issues by removing trailing whitespace and adding final newlines to all affected files. Commit 12f0fea.

@Copilot Copilot AI requested a review from gramalingam June 20, 2025 21:43
Copilot finished work on behalf of gramalingam June 20, 2025 21:43
@script()
def original_model(A: FLOAT[4, 4]) -> FLOAT[2, 4]:
# Split operation that produces 2 outputs
result1, result2 = opset18.Split(A, num_outputs=2, axis=0)

Check warning

Code scanning / lintrunner

PYLINT/W0612 Warning documentation

Unused variable 'result2' (unused-variable)
See unused-variable. To disable, use # pylint: disable=unused-variable
@script()
def original_model(A: FLOAT[4, 4]) -> FLOAT[2, 4]:
# Split operation that produces 2 outputs
result1, result2 = opset18.Split(A, num_outputs=2, axis=0)

Check warning

Code scanning / lintrunner

RUFF/RUF059 Warning documentation

Unpacked variable result2 is never used.
See https://docs.astral.sh/ruff/rules/unused-unpacked-variable
@justinchuby
Copy link
Collaborator

Please run lintrunner -a --all-files and fix issues.

Copy link
Contributor Author

Copilot AI commented Jun 20, 2025

Please run lintrunner -a --all-files and fix issues.

Fixed the linting issues by running lintrunner -a --all-files. The tool automatically corrected code formatting issues including line length and spacing. Commit 2a6889e.

Copilot finished work on behalf of justinchuby June 20, 2025 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module: rewriter topic: documentation Improvements or additions to documentation
Projects
Development

Successfully merging this pull request may close these issues.

[docs] Document rewriter options
3 participants