hyperonnx 1.0.0__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/.github/workflows/lint.yml +3 -1
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/.github/workflows/unittest.yml +4 -2
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/.pre-commit-config.yaml +11 -17
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/PKG-INFO +17 -14
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/README.md +2 -2
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/README_CN.md +1 -1
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/__init__.py +1 -1
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/auto.py +8 -8
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/exporter/__init__.py +1 -2
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/exporter/dynamo.py +2 -2
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/exporter/torchscript.py +2 -3
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/exporter/utils.py +2 -2
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/function_rewriter.py +21 -24
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/hyper_export.py +25 -24
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/torch_export.py +2 -2
- hyperonnx-1.0.2/hyperonnx/transformers/__init__.py +10 -0
- hyperonnx-1.0.2/hyperonnx/transformers/attention.py +218 -0
- hyperonnx-1.0.2/hyperonnx/transformers/cache.py +173 -0
- {hyperonnx-1.0.0/hyperonnx → hyperonnx-1.0.2/hyperonnx/transformers}/patch.py +6 -6
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/typing.py +11 -10
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/pyproject.toml +50 -7
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/expoter/test_dynamo_build_onnxscript.py +3 -1
- hyperonnx-1.0.2/tests/expoter/test_non_pod_export.py +105 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/test_auto_trace_method.py +6 -10
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/test_compose_nodes_to_functions.py +4 -4
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/test_export_functions.py +44 -52
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/test_export_hyper_onnx.py +50 -36
- hyperonnx-1.0.2/tests/transformers/test_attention.py +126 -0
- hyperonnx-1.0.2/tests/transformers/test_static_cache.py +465 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/.github/words_bag.txt +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/.gitignore +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/LICENSE +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/docs/assets/qwen2_omni_vision.gif +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/docs/assets/r18-sample.gif +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/hyperonnx/utils.py +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/expoter/test_detach_module_outputs.py +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/expoter/test_dynamo_make_custom_op.py +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/expoter/test_dynamo_replace_custom_op.py +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/expoter/test_plain_output_tensors.py +0 -0
- {hyperonnx-1.0.0 → hyperonnx-1.0.2}/tests/test_fuse_constants_to_function.py +0 -0
|
@@ -11,6 +11,7 @@ jobs:
|
|
|
11
11
|
strategy:
|
|
12
12
|
matrix:
|
|
13
13
|
python-version: ["3.12"]
|
|
14
|
+
transformers-version: ["4.53.0", "5.1.0"]
|
|
14
15
|
steps:
|
|
15
16
|
- uses: actions/checkout@v5
|
|
16
17
|
with:
|
|
@@ -24,6 +25,7 @@ jobs:
|
|
|
24
25
|
node-version: lts/Jod
|
|
25
26
|
- name: pyright type check
|
|
26
27
|
run: |
|
|
27
|
-
uv sync --extra=
|
|
28
|
+
uv sync --dev --extra=cpu
|
|
29
|
+
uv pip install 'transformers==${{ matrix.transformers-version }}'
|
|
28
30
|
uv run pyright hyperonnx
|
|
29
31
|
uv run pre-commit run -s HEAD^ -o HEAD
|
|
@@ -12,6 +12,7 @@ jobs:
|
|
|
12
12
|
strategy:
|
|
13
13
|
matrix:
|
|
14
14
|
python-version: ["3.12"]
|
|
15
|
+
transformers-version: ["4.53.0", "5.1.0"]
|
|
15
16
|
steps:
|
|
16
17
|
- uses: actions/checkout@v5
|
|
17
18
|
- name: Install uv and set the Python version
|
|
@@ -20,7 +21,8 @@ jobs:
|
|
|
20
21
|
python-version: ${{ matrix.python-version }}
|
|
21
22
|
- name: Install dependencies
|
|
22
23
|
run: |
|
|
23
|
-
uv sync --extra=
|
|
24
|
+
uv sync --dev --extra=cpu -U
|
|
25
|
+
uv pip install 'transformers==${{ matrix.transformers-version }}'
|
|
24
26
|
- name: Run pytest and coverage
|
|
25
27
|
run: |
|
|
26
|
-
uv run pytest --cov=hyperonnx tests
|
|
28
|
+
uv run --dev --extra=cpu pytest --cov=hyperonnx tests
|
|
@@ -9,32 +9,26 @@ repos:
|
|
|
9
9
|
- id: check-toml
|
|
10
10
|
- id: check-yaml
|
|
11
11
|
- id: check-added-large-files
|
|
12
|
-
- repo: https://github.com/
|
|
13
|
-
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
# Ruff version.
|
|
14
|
+
rev: v0.15.8
|
|
14
15
|
hooks:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- id: black
|
|
16
|
+
# Run the linter.
|
|
17
|
+
- id: ruff-check
|
|
18
|
+
args: [--fix]
|
|
19
|
+
# Run the formatter.
|
|
20
|
+
- id: ruff-format
|
|
21
21
|
- repo: https://github.com/codespell-project/codespell
|
|
22
|
-
rev: v2.4.
|
|
22
|
+
rev: v2.4.2
|
|
23
23
|
hooks:
|
|
24
24
|
- id: codespell
|
|
25
25
|
args: [-w, -I=.github/words_bag.txt]
|
|
26
26
|
exclude: (\.(txt|diff|patch)$)
|
|
27
|
-
- repo: https://github.com/pycqa/flake8
|
|
28
|
-
rev: 7.3.0
|
|
29
|
-
hooks:
|
|
30
|
-
- id: flake8
|
|
31
|
-
additional_dependencies: [Flake8-pyproject]
|
|
32
|
-
args:
|
|
33
|
-
- '--ignore=W503,E203,E231,E241'
|
|
34
27
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
35
|
-
rev: v1.
|
|
28
|
+
rev: v1.19.1
|
|
36
29
|
hooks:
|
|
37
30
|
- id: mypy
|
|
38
31
|
args:
|
|
39
32
|
- '--ignore-missing-imports'
|
|
40
33
|
- '--exclude=tests.*\.py'
|
|
34
|
+
exclude: (^tests/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperonnx
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Copyright (C) 2025 The HYPERONNX Authors.
|
|
5
5
|
Author-email: Wenyi Tang <wenyitang@outlook.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -9,26 +9,29 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
9
9
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
10
10
|
Classifier: Topic :: Software Development
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: onnxifier>=2.0.
|
|
13
|
-
Requires-Dist: onnxscript
|
|
12
|
+
Requires-Dist: onnxifier>=2.0.1
|
|
13
|
+
Requires-Dist: onnxscript<0.6.0
|
|
14
14
|
Requires-Dist: onnx>=1.18.0
|
|
15
|
-
Requires-Dist: torch
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist: torchvision ; extra == "
|
|
15
|
+
Requires-Dist: torch<2.11 ; extra == "cpu"
|
|
16
|
+
Requires-Dist: torchvision ; extra == "cpu"
|
|
17
|
+
Requires-Dist: triton-windows ; extra == "cpu" and ( sys_platform == 'win32')
|
|
18
|
+
Requires-Dist: torch<2.11 ; extra == "cuda"
|
|
19
|
+
Requires-Dist: torchvision ; extra == "cuda"
|
|
20
|
+
Requires-Dist: triton-windows ; extra == "cuda" and ( sys_platform == 'win32')
|
|
21
|
+
Requires-Dist: torch<2.11 ; extra == "xpu"
|
|
22
|
+
Requires-Dist: torchvision ; extra == "xpu"
|
|
23
|
+
Requires-Dist: pytorch-triton-xpu ; extra == "xpu"
|
|
23
24
|
Project-URL: Docs, https://loseall.github.io/hyperonnx
|
|
24
25
|
Project-URL: Home, https://github.com/LoSealL/hyperonnx
|
|
25
|
-
Provides-Extra:
|
|
26
|
+
Provides-Extra: cpu
|
|
27
|
+
Provides-Extra: cuda
|
|
28
|
+
Provides-Extra: xpu
|
|
26
29
|
|
|
27
30
|
# 🚀 HYPER-ONNX
|
|
28
31
|
|
|
29
32
|
[中文](./README_CN.md)|[EN](./README.md)
|
|
30
33
|
|
|
31
|
-
Hyper-ONNX can export pytorch models (`nn.Module`) in a
|
|
34
|
+
Hyper-ONNX can export pytorch models (`nn.Module`) in a hierarchical manner. It can keep the module hier information and make a nested onnx graph. ✨
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
## 📦 Install
|
|
@@ -76,7 +79,7 @@ export_hyper_onnx(
|
|
|
76
79
|
|
|
77
80
|
```python
|
|
78
81
|
from hyperonnx import auto_trace_method
|
|
79
|
-
from hyperonnx.
|
|
82
|
+
from hyperonnx.transformers import patch_transformers
|
|
80
83
|
from transformers import (
|
|
81
84
|
GenerationConfig,
|
|
82
85
|
Qwen2_5OmniThinkerForConditionalGeneration,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[中文](./README_CN.md)|[EN](./README.md)
|
|
4
4
|
|
|
5
|
-
Hyper-ONNX can export pytorch models (`nn.Module`) in a
|
|
5
|
+
Hyper-ONNX can export pytorch models (`nn.Module`) in a hierarchical manner. It can keep the module hier information and make a nested onnx graph. ✨
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
## 📦 Install
|
|
@@ -50,7 +50,7 @@ export_hyper_onnx(
|
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
52
|
from hyperonnx import auto_trace_method
|
|
53
|
-
from hyperonnx.
|
|
53
|
+
from hyperonnx.transformers import patch_transformers
|
|
54
54
|
from transformers import (
|
|
55
55
|
GenerationConfig,
|
|
56
56
|
Qwen2_5OmniThinkerForConditionalGeneration,
|
|
@@ -50,7 +50,7 @@ export_hyper_onnx(
|
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
52
|
from hyperonnx import auto_trace_method
|
|
53
|
-
from hyperonnx.
|
|
53
|
+
from hyperonnx.transformers import patch_transformers
|
|
54
54
|
from transformers import (
|
|
55
55
|
GenerationConfig,
|
|
56
56
|
Qwen2_5OmniThinkerForConditionalGeneration,
|
|
@@ -15,13 +15,13 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import inspect
|
|
18
|
+
from collections.abc import Collection
|
|
18
19
|
from contextlib import contextmanager
|
|
19
20
|
from functools import wraps
|
|
20
21
|
from io import BytesIO
|
|
21
22
|
from os import PathLike
|
|
22
23
|
from pathlib import Path
|
|
23
24
|
from types import MethodType
|
|
24
|
-
from typing import Collection, Dict, List, Optional, Tuple
|
|
25
25
|
|
|
26
26
|
from torch import inference_mode
|
|
27
27
|
from torch.nn import Module
|
|
@@ -60,8 +60,8 @@ class AutoTraceMethod(Module):
|
|
|
60
60
|
|
|
61
61
|
def __init__(self, clsmethod: MethodType, expected_stages: int = 1):
|
|
62
62
|
super().__init__()
|
|
63
|
-
self.pos_args:
|
|
64
|
-
self.kwargs:
|
|
63
|
+
self.pos_args: list[tuple[AnyTensor, ...]] = []
|
|
64
|
+
self.kwargs: list[dict[str, AnyTensor]] = []
|
|
65
65
|
model = clsmethod.__self__
|
|
66
66
|
if not isinstance(model, Module):
|
|
67
67
|
for k, v in model.__dict__.items():
|
|
@@ -88,15 +88,15 @@ class AutoTraceMethod(Module):
|
|
|
88
88
|
self,
|
|
89
89
|
f: str | PathLike | BytesIO,
|
|
90
90
|
*,
|
|
91
|
-
input_names:
|
|
92
|
-
output_names:
|
|
91
|
+
input_names: list[str] | None = None,
|
|
92
|
+
output_names: list[str] | None = None,
|
|
93
93
|
opset_version: int = 19,
|
|
94
94
|
dynamo: bool = False,
|
|
95
95
|
external_data: bool = False,
|
|
96
|
-
hiera:
|
|
97
|
-
module_spec:
|
|
96
|
+
hiera: Collection[type[Module]] | None = None,
|
|
97
|
+
module_spec: dict[Module, ModuleSpec] | None = None,
|
|
98
98
|
do_optimization: bool = True,
|
|
99
|
-
external_directory:
|
|
99
|
+
external_directory: str | PathLike | None = None,
|
|
100
100
|
):
|
|
101
101
|
"""Export onnx model according to the traced data."""
|
|
102
102
|
if not self.pos_args and not self.kwargs:
|
|
@@ -15,7 +15,6 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from contextlib import contextmanager
|
|
18
|
-
from typing import Dict
|
|
19
18
|
|
|
20
19
|
from torch.nn import Module
|
|
21
20
|
|
|
@@ -26,7 +25,7 @@ from .torchscript import replace_duck_forward
|
|
|
26
25
|
|
|
27
26
|
@contextmanager
|
|
28
27
|
def replace_with_duck_module(
|
|
29
|
-
model: Module, dynamo: bool, module_spec:
|
|
28
|
+
model: Module, dynamo: bool, module_spec: dict[Module, ModuleSpec]
|
|
30
29
|
):
|
|
31
30
|
"""Replace the forward function of modules in `module_spec` with a duck type.
|
|
32
31
|
|
|
@@ -17,8 +17,8 @@ limitations under the License.
|
|
|
17
17
|
import inspect
|
|
18
18
|
from ast import AST
|
|
19
19
|
from collections import OrderedDict
|
|
20
|
+
from collections.abc import Callable
|
|
20
21
|
from contextlib import contextmanager
|
|
21
|
-
from typing import Callable, Dict
|
|
22
22
|
|
|
23
23
|
import onnxscript
|
|
24
24
|
import onnxscript.ir._schemas as schemas
|
|
@@ -257,7 +257,7 @@ def make_custom_op(module: Module, spec: ModuleSpec):
|
|
|
257
257
|
|
|
258
258
|
|
|
259
259
|
@contextmanager
|
|
260
|
-
def replace_with_custom_op(model: Module, module_spec:
|
|
260
|
+
def replace_with_custom_op(model: Module, module_spec: dict[Module, ModuleSpec]):
|
|
261
261
|
"""Replace the forward function of modules in `module_spec` with a duck type.
|
|
262
262
|
|
|
263
263
|
It's used to laterly replace the duck type with the embedded onnx functions.
|
|
@@ -16,7 +16,6 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
from contextlib import contextmanager
|
|
18
18
|
from functools import update_wrapper
|
|
19
|
-
from typing import Dict, List
|
|
20
19
|
|
|
21
20
|
import torch
|
|
22
21
|
from torch import Tensor
|
|
@@ -91,7 +90,7 @@ def make_duck_forward(module_spec: ModuleSpec):
|
|
|
91
90
|
|
|
92
91
|
|
|
93
92
|
@contextmanager
|
|
94
|
-
def replace_duck_forward(model: Module, module_spec:
|
|
93
|
+
def replace_duck_forward(model: Module, module_spec: dict[Module, ModuleSpec]):
|
|
95
94
|
"""Replace the forward function of modules in `module_spec` with a duck type.
|
|
96
95
|
|
|
97
96
|
It's used to laterly replace the duck type with the embedded onnx functions.
|
|
@@ -111,7 +110,7 @@ def replace_duck_forward(model: Module, module_spec: Dict[Module, ModuleSpec]):
|
|
|
111
110
|
output = spec.get("output")
|
|
112
111
|
return output
|
|
113
112
|
|
|
114
|
-
handles:
|
|
113
|
+
handles: list[RemovableHandle] = []
|
|
115
114
|
try:
|
|
116
115
|
for child in filter(lambda c: c in module_spec, model.modules()):
|
|
117
116
|
if module_spec[child]["status"] == ExportStatus.EXPORTED:
|
|
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
from collections.abc import Sequence
|
|
17
18
|
from itertools import chain
|
|
18
|
-
from typing import Sequence, Tuple
|
|
19
19
|
|
|
20
20
|
import torch
|
|
21
21
|
from onnxifier.logger import warning
|
|
@@ -24,7 +24,7 @@ from torch import Tensor
|
|
|
24
24
|
from ..typing import AnyTensor, ModuleSpec
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def plain_tensor_container(obj: AnyTensor) ->
|
|
27
|
+
def plain_tensor_container(obj: AnyTensor) -> tuple[Tensor, ...]:
|
|
28
28
|
"""Iteratively flatten an arbitrary output of Tensors into a tuple of Tensors.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Copyright (C)
|
|
2
|
+
Copyright (C) 2026 The HYPERONNX Authors.
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -17,11 +17,12 @@ limitations under the License.
|
|
|
17
17
|
# pylint: disable=arguments-differ
|
|
18
18
|
|
|
19
19
|
from collections import defaultdict
|
|
20
|
+
from collections.abc import Sequence
|
|
20
21
|
from contextlib import suppress
|
|
21
22
|
from copy import deepcopy
|
|
22
23
|
from hashlib import md5
|
|
23
24
|
from itertools import chain
|
|
24
|
-
from typing import
|
|
25
|
+
from typing import Literal, cast
|
|
25
26
|
|
|
26
27
|
import networkx as nx
|
|
27
28
|
import onnx
|
|
@@ -51,7 +52,7 @@ class ComposeOnnxAsFunctionRewriter(Rewriter):
|
|
|
51
52
|
|
|
52
53
|
def __init__(self, domain: str, module_spec: Sequence[ModuleSpec]):
|
|
53
54
|
super().__init__(SingleNodePattern().with_domain(domain))
|
|
54
|
-
self._specs:
|
|
55
|
+
self._specs: dict[str, ModuleSpec] = {
|
|
55
56
|
spec["type_name"]: spec for spec in module_spec
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -122,8 +123,8 @@ class ComposeOnnxAsFunctionRewriter(Rewriter):
|
|
|
122
123
|
# find the actual input with edges in the graph
|
|
123
124
|
# and remove dangled inputs
|
|
124
125
|
debugable_input_names = spec["input_names"]
|
|
125
|
-
dangled_input:
|
|
126
|
-
dangled_or_constant_unsafe:
|
|
126
|
+
dangled_input: list[str] = []
|
|
127
|
+
dangled_or_constant_unsafe: list[str] = []
|
|
127
128
|
if len(node.input) != len(debugable_input_names):
|
|
128
129
|
warning(
|
|
129
130
|
f"Number of inputs on {node.name} is different from spec. "
|
|
@@ -143,10 +144,7 @@ class ComposeOnnxAsFunctionRewriter(Rewriter):
|
|
|
143
144
|
debug(f"input unconnected to graph node: {input_name}")
|
|
144
145
|
dangled_or_constant_unsafe.append(input_name)
|
|
145
146
|
if len(dangled_input) < unused:
|
|
146
|
-
error(
|
|
147
|
-
f"Expect {unused} unused inputs but "
|
|
148
|
-
f"enumerated {len(dangled_input)}."
|
|
149
|
-
)
|
|
147
|
+
error(f"Expect {unused} unused inputs but enumerated {len(dangled_input)}.")
|
|
150
148
|
reminder = unused - len(dangled_input)
|
|
151
149
|
dangled_input.extend(dangled_or_constant_unsafe[:reminder])
|
|
152
150
|
for j in dangled_input:
|
|
@@ -159,8 +157,8 @@ class ComposeOnnxAsFunctionRewriter(Rewriter):
|
|
|
159
157
|
def _remove_unused_outputs(
|
|
160
158
|
self, graph: OnnxGraph, unused: int, node: NodeProto, spec: ModuleSpec
|
|
161
159
|
):
|
|
162
|
-
dangled_output:
|
|
163
|
-
dangled_output_unsafe:
|
|
160
|
+
dangled_output: list[str] = []
|
|
161
|
+
dangled_output_unsafe: list[str] = []
|
|
164
162
|
for i, output_name in enumerate(node.output):
|
|
165
163
|
if output_name in spec["unused_outputs"]:
|
|
166
164
|
dangled_output.append(output_name)
|
|
@@ -170,8 +168,7 @@ class ComposeOnnxAsFunctionRewriter(Rewriter):
|
|
|
170
168
|
dangled_output_unsafe.append(output_name)
|
|
171
169
|
if len(dangled_output) < unused:
|
|
172
170
|
error(
|
|
173
|
-
f"Expect {unused} unused outputs but "
|
|
174
|
-
f"enumerated {len(dangled_output)}."
|
|
171
|
+
f"Expect {unused} unused outputs but enumerated {len(dangled_output)}."
|
|
175
172
|
)
|
|
176
173
|
reminder = unused - len(dangled_output)
|
|
177
174
|
# try to drop `reminder` outputs from the last one
|
|
@@ -182,7 +179,7 @@ class ComposeOnnxAsFunctionRewriter(Rewriter):
|
|
|
182
179
|
if j in graph.outputs:
|
|
183
180
|
graph.remove_output(j)
|
|
184
181
|
|
|
185
|
-
def rewrite(self, graph: OnnxGraph, nodes:
|
|
182
|
+
def rewrite(self, graph: OnnxGraph, nodes: list[NodeProto]):
|
|
186
183
|
node = nodes[0]
|
|
187
184
|
if node.op_type not in self._specs:
|
|
188
185
|
raise KeyError(f"node spec is not found for {node.name}")
|
|
@@ -253,7 +250,7 @@ class ComposeNodesToFunctionsRewriter(Rewriter):
|
|
|
253
250
|
self.register_pre_hook(self._mark_isolated_nodes)
|
|
254
251
|
self._name = name
|
|
255
252
|
self._max_level = 0
|
|
256
|
-
self._level_processed:
|
|
253
|
+
self._level_processed: set[int] = set()
|
|
257
254
|
|
|
258
255
|
def _mark_isolated_nodes(self, graph: OnnxGraph) -> OnnxGraph:
|
|
259
256
|
# make a shallow copy of the graph
|
|
@@ -269,7 +266,7 @@ class ComposeNodesToFunctionsRewriter(Rewriter):
|
|
|
269
266
|
self._max_level = max(self._max_level, k + 1)
|
|
270
267
|
return graph
|
|
271
268
|
|
|
272
|
-
def _detect_cycles_in_graph(self, graph: OnnxGraph) ->
|
|
269
|
+
def _detect_cycles_in_graph(self, graph: OnnxGraph) -> list[list[str]]:
|
|
273
270
|
"""Comprehensive cycle detection in the entire graph."""
|
|
274
271
|
try:
|
|
275
272
|
cycles = list(nx.simple_cycles(graph))
|
|
@@ -278,7 +275,7 @@ class ComposeNodesToFunctionsRewriter(Rewriter):
|
|
|
278
275
|
return []
|
|
279
276
|
|
|
280
277
|
def _would_create_cycle(
|
|
281
|
-
self, graph: OnnxGraph, new_node: NodeProto, nodes_to_remove:
|
|
278
|
+
self, graph: OnnxGraph, new_node: NodeProto, nodes_to_remove: list[NodeProto]
|
|
282
279
|
) -> bool:
|
|
283
280
|
"""Check if adding a new node and removing old nodes would create a cycle."""
|
|
284
281
|
# Create a temporary graph by copying the original and removing nodes
|
|
@@ -307,7 +304,7 @@ class ComposeNodesToFunctionsRewriter(Rewriter):
|
|
|
307
304
|
return True # Assume cycle exists for safety
|
|
308
305
|
|
|
309
306
|
def _compose_subgraph(
|
|
310
|
-
self, graph: OnnxGraph, same_level_nodes:
|
|
307
|
+
self, graph: OnnxGraph, same_level_nodes: list[NodeProto], level: int
|
|
311
308
|
) -> None:
|
|
312
309
|
if len(same_level_nodes) == 0:
|
|
313
310
|
return # skip empty subgraph
|
|
@@ -388,7 +385,7 @@ class ComposeNodesToFunctionsRewriter(Rewriter):
|
|
|
388
385
|
if direction == "upstream" and i.op_type == "Constant":
|
|
389
386
|
continue
|
|
390
387
|
|
|
391
|
-
same_level_nodes:
|
|
388
|
+
same_level_nodes: list[NodeProto] = [i]
|
|
392
389
|
# Use BFS to collect neighbors to avoid recursive dependency issues
|
|
393
390
|
visited = {i.name}
|
|
394
391
|
queue = [i.name]
|
|
@@ -419,7 +416,7 @@ class ComposeNodesToFunctionsRewriter(Rewriter):
|
|
|
419
416
|
"""Compose downstream nodes into functions."""
|
|
420
417
|
self._compose_neighbors(graph, node, "downstream")
|
|
421
418
|
|
|
422
|
-
def rewrite(self, graph: OnnxGraph, nodes:
|
|
419
|
+
def rewrite(self, graph: OnnxGraph, nodes: list[NodeProto]):
|
|
423
420
|
node = nodes[0]
|
|
424
421
|
|
|
425
422
|
# Check for cycles before starting composition
|
|
@@ -474,10 +471,10 @@ class FuseConstantsToFunctionRewriter(Rewriter):
|
|
|
474
471
|
|
|
475
472
|
def __init__(self):
|
|
476
473
|
super().__init__(SingleNodePattern().with_domain(HYPER_DOMAIN))
|
|
477
|
-
self.users:
|
|
474
|
+
self.users: dict[str, int] = defaultdict(int)
|
|
478
475
|
self.register_pre_hook(self._collect_users)
|
|
479
476
|
|
|
480
|
-
def rewrite(self, graph: OnnxGraph, nodes:
|
|
477
|
+
def rewrite(self, graph: OnnxGraph, nodes: list[NodeProto]):
|
|
481
478
|
node = nodes[0]
|
|
482
479
|
func = graph.functions.get(node.op_type)
|
|
483
480
|
if func is None:
|
|
@@ -491,7 +488,7 @@ class FuseConstantsToFunctionRewriter(Rewriter):
|
|
|
491
488
|
node.op_type = func.name
|
|
492
489
|
self.users[func.name] += 1
|
|
493
490
|
|
|
494
|
-
consts:
|
|
491
|
+
consts: list[tuple[int, NodeProto]] = []
|
|
495
492
|
for i, cst in enumerate(self.get_input_nodes(node)):
|
|
496
493
|
if cst is not None and cst.op_type == "Constant":
|
|
497
494
|
consts.append((i, cst))
|
|
@@ -520,7 +517,7 @@ class EraseOutputTypesRewriter(Rewriter):
|
|
|
520
517
|
super().__init__(pattern=SingleNodePattern().with_name("__NO_MATCH__"))
|
|
521
518
|
self.register_pre_hook(self._clear_types)
|
|
522
519
|
self.register_post_hook(self._infer_types)
|
|
523
|
-
self.output_types:
|
|
520
|
+
self.output_types: list[onnx.TypeProto] = []
|
|
524
521
|
|
|
525
522
|
def _clear_types(self, graph: OnnxGraph):
|
|
526
523
|
for i in graph.output:
|
|
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
from collections.abc import Collection, Container
|
|
17
18
|
from contextlib import suppress
|
|
18
19
|
from inspect import signature
|
|
19
20
|
from io import BytesIO
|
|
@@ -21,7 +22,7 @@ from logging import Logger
|
|
|
21
22
|
from os import PathLike
|
|
22
23
|
from pathlib import Path
|
|
23
24
|
from tempfile import TemporaryDirectory
|
|
24
|
-
from typing import Any
|
|
25
|
+
from typing import Any
|
|
25
26
|
|
|
26
27
|
import onnx
|
|
27
28
|
from onnxifier import ONNXIFIER_OPSET, OnnxGraph, PassManager
|
|
@@ -48,8 +49,8 @@ from .typing import (
|
|
|
48
49
|
from .utils import HYPER_DOMAIN, OPTIMIZER_PASSES
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def _get_input_names(spec: ModuleSpec) ->
|
|
52
|
-
names:
|
|
52
|
+
def _get_input_names(spec: ModuleSpec) -> list[str]:
|
|
53
|
+
names: list[str] = []
|
|
53
54
|
params = spec["signature"].parameters
|
|
54
55
|
|
|
55
56
|
def _arg_to_name(name: str, args):
|
|
@@ -70,13 +71,13 @@ def _get_input_names(spec: ModuleSpec) -> List[str]:
|
|
|
70
71
|
return names
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
def _get_output_names(spec: ModuleSpec) -> None |
|
|
74
|
+
def _get_output_names(spec: ModuleSpec) -> None | list[str]:
|
|
74
75
|
output = spec.get("output")
|
|
75
76
|
if output is None:
|
|
76
77
|
return None
|
|
77
78
|
|
|
78
|
-
def _extract_names_from_dict(out_dict: dict) ->
|
|
79
|
-
names:
|
|
79
|
+
def _extract_names_from_dict(out_dict: dict) -> list[str]:
|
|
80
|
+
names: list[str] = []
|
|
80
81
|
for key, value in out_dict.items():
|
|
81
82
|
if isinstance(value, dict):
|
|
82
83
|
names.extend(_extract_names_from_dict(value))
|
|
@@ -99,7 +100,7 @@ def _get_output_names(spec: ModuleSpec) -> None | List[str]:
|
|
|
99
100
|
|
|
100
101
|
|
|
101
102
|
def make_hierarchical_hook(
|
|
102
|
-
hiera: Container[type[Module]], module_spec:
|
|
103
|
+
hiera: Container[type[Module]], module_spec: dict[Module, ModuleSpec], index: int
|
|
103
104
|
) -> HookCallback:
|
|
104
105
|
"""Make a forward hook to record spec of modules in `hiera`.
|
|
105
106
|
|
|
@@ -115,8 +116,8 @@ def make_hierarchical_hook(
|
|
|
115
116
|
|
|
116
117
|
def _hook(
|
|
117
118
|
module: Module,
|
|
118
|
-
args:
|
|
119
|
-
kwargs:
|
|
119
|
+
args: tuple[Tensor],
|
|
120
|
+
kwargs: dict[str, AnyTensor],
|
|
120
121
|
output: AnyTensor,
|
|
121
122
|
) -> None:
|
|
122
123
|
spec = module_spec[module]
|
|
@@ -140,12 +141,12 @@ def make_hierarchical_hook(
|
|
|
140
141
|
def trace_module_spec(
|
|
141
142
|
model: Module,
|
|
142
143
|
input_args: tuple,
|
|
143
|
-
kwargs:
|
|
144
|
+
kwargs: dict[str, AnyTensor] | None,
|
|
144
145
|
opset_version: int,
|
|
145
146
|
hiera: Container[type[Module]],
|
|
146
|
-
module_spec:
|
|
147
|
+
module_spec: dict[Module, ModuleSpec],
|
|
147
148
|
dynamo: bool = False,
|
|
148
|
-
) ->
|
|
149
|
+
) -> dict[Module, ModuleSpec]:
|
|
149
150
|
"""Register forward hooks to modules in the `hiera` and record forward
|
|
150
151
|
information to `module_spec`.
|
|
151
152
|
|
|
@@ -208,13 +209,13 @@ def _export_hiera(
|
|
|
208
209
|
dynamo: bool,
|
|
209
210
|
external_data: bool,
|
|
210
211
|
do_optimization: bool,
|
|
211
|
-
external_directory:
|
|
212
|
-
module_spec:
|
|
212
|
+
external_directory: str | PathLike | None,
|
|
213
|
+
module_spec: dict[Module, ModuleSpec],
|
|
213
214
|
hiera: Collection[type[Module]],
|
|
214
215
|
logger: Logger,
|
|
215
216
|
):
|
|
216
|
-
def _get_sub_spec(module: Module, spec:
|
|
217
|
-
child_spec:
|
|
217
|
+
def _get_sub_spec(module: Module, spec: dict[Module, ModuleSpec]):
|
|
218
|
+
child_spec: dict[Module, ModuleSpec] = default_module_spec()
|
|
218
219
|
for child in module.modules():
|
|
219
220
|
if child in spec:
|
|
220
221
|
child_spec[child] = spec[child]
|
|
@@ -308,16 +309,16 @@ def export_hyper_onnx( # noqa: C901
|
|
|
308
309
|
input_args: tuple,
|
|
309
310
|
f: str | PathLike | BytesIO,
|
|
310
311
|
*,
|
|
311
|
-
kwargs:
|
|
312
|
-
input_names:
|
|
313
|
-
output_names:
|
|
312
|
+
kwargs: dict[str, AnyTensor] | None = None,
|
|
313
|
+
input_names: list[str] | None = None,
|
|
314
|
+
output_names: list[str] | None = None,
|
|
314
315
|
opset_version: int = ONNXIFIER_OPSET.version,
|
|
315
316
|
dynamo: bool = False,
|
|
316
317
|
external_data: bool = False,
|
|
317
|
-
hiera:
|
|
318
|
-
module_spec:
|
|
318
|
+
hiera: Collection[type[Module]] | None = None,
|
|
319
|
+
module_spec: dict[Module, ModuleSpec] | None = None,
|
|
319
320
|
do_optimization: bool = True,
|
|
320
|
-
external_directory:
|
|
321
|
+
external_directory: str | PathLike | None = None,
|
|
321
322
|
**_: Any, # ignored options
|
|
322
323
|
) -> Any | None:
|
|
323
324
|
r"""Export a Pytorch module to ONNX format with hierarchical structure,
|
|
@@ -450,12 +451,12 @@ def export_hyper_onnx( # noqa: C901
|
|
|
450
451
|
else:
|
|
451
452
|
onnx_model = onnx.load_model_from_string(model_path.getvalue())
|
|
452
453
|
|
|
453
|
-
typenames:
|
|
454
|
+
typenames: list[str] = [module_spec[i]["type_name"] for i in module_spec]
|
|
454
455
|
for node in onnx_model.graph.node:
|
|
455
456
|
if node.op_type in typenames:
|
|
456
457
|
node.domain = HYPER_DOMAIN # add domain tag to run rewriter
|
|
457
458
|
graph = OnnxGraph(onnx_model)
|
|
458
|
-
passes:
|
|
459
|
+
passes: list[Any]
|
|
459
460
|
passes = [ComposeOnnxAsFunctionRewriter(HYPER_DOMAIN, tuple(module_spec.values()))]
|
|
460
461
|
if do_optimization:
|
|
461
462
|
passes.extend(OPTIMIZER_PASSES)
|
|
@@ -15,10 +15,10 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import warnings
|
|
18
|
-
from collections.abc import Callable
|
|
18
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
19
19
|
from inspect import signature
|
|
20
20
|
from os import PathLike
|
|
21
|
-
from typing import Any
|
|
21
|
+
from typing import Any
|
|
22
22
|
|
|
23
23
|
import torch
|
|
24
24
|
from torch.onnx import OperatorExportTypes
|