onnxscript 0.1.0__py3-none-any.whl
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.
- onnxscript/__init__.py +131 -0
- onnxscript/_framework_apis/__init__.py +3 -0
- onnxscript/_framework_apis/torch_2_5.py +117 -0
- onnxscript/_framework_apis/torch_2_6.py +45 -0
- onnxscript/_internal/__init__.py +0 -0
- onnxscript/_internal/analysis.py +229 -0
- onnxscript/_internal/ast_utils.py +64 -0
- onnxscript/_internal/autocast.py +250 -0
- onnxscript/_internal/deprecation.py +78 -0
- onnxscript/_internal/param_manipulation.py +148 -0
- onnxscript/_internal/runtime_typing.py +43 -0
- onnxscript/_internal/utils.py +99 -0
- onnxscript/_internal/version_utils.py +118 -0
- onnxscript/_legacy_ir/__init__.py +341 -0
- onnxscript/_legacy_ir/visitor.py +937 -0
- onnxscript/_thirdparty/asciichartpy.py +313 -0
- onnxscript/backend/__init__.py +2 -0
- onnxscript/backend/onnx_backend.py +303 -0
- onnxscript/backend/onnx_export.py +885 -0
- onnxscript/converter.py +1470 -0
- onnxscript/evaluator.py +619 -0
- onnxscript/function_libs/tools/torch_lib/deduce_type_constraints.py +403 -0
- onnxscript/function_libs/tools/torch_lib/generate_aten_signatures.py +333 -0
- onnxscript/function_libs/tools/torch_lib/generate_prims_signatures.py +331 -0
- onnxscript/function_libs/torch_lib/__init__.py +12 -0
- onnxscript/function_libs/torch_lib/_constants.py +5 -0
- onnxscript/function_libs/torch_lib/_flags.py +58 -0
- onnxscript/function_libs/torch_lib/graph_building/__init__.py +56 -0
- onnxscript/function_libs/torch_lib/graph_building/_graph_building_ir.py +723 -0
- onnxscript/function_libs/torch_lib/graph_building/_graph_building_torch.py +1125 -0
- onnxscript/function_libs/torch_lib/ops/__init__.py +27 -0
- onnxscript/function_libs/torch_lib/ops/common.py +80 -0
- onnxscript/function_libs/torch_lib/ops/core.py +8935 -0
- onnxscript/function_libs/torch_lib/ops/fft.py +385 -0
- onnxscript/function_libs/torch_lib/ops/linalg.py +399 -0
- onnxscript/function_libs/torch_lib/ops/nested.py +25 -0
- onnxscript/function_libs/torch_lib/ops/nn.py +2713 -0
- onnxscript/function_libs/torch_lib/ops/prims.py +850 -0
- onnxscript/function_libs/torch_lib/ops/quantized_decomposed.py +63 -0
- onnxscript/function_libs/torch_lib/ops/sparse.py +23 -0
- onnxscript/function_libs/torch_lib/ops/special.py +387 -0
- onnxscript/function_libs/torch_lib/ops/vision.py +25 -0
- onnxscript/function_libs/torch_lib/registration.py +151 -0
- onnxscript/function_libs/torch_lib/tensor_typing.py +74 -0
- onnxscript/ir/__init__.py +153 -0
- onnxscript/ir/_convenience.py +447 -0
- onnxscript/ir/_core.py +3119 -0
- onnxscript/ir/_display.py +49 -0
- onnxscript/ir/_enums.py +163 -0
- onnxscript/ir/_graph_comparison.py +23 -0
- onnxscript/ir/_io.py +97 -0
- onnxscript/ir/_linked_list.py +276 -0
- onnxscript/ir/_metadata.py +44 -0
- onnxscript/ir/_name_authority.py +72 -0
- onnxscript/ir/_polyfill.py +25 -0
- onnxscript/ir/_protocols.py +598 -0
- onnxscript/ir/_schemas.py +548 -0
- onnxscript/ir/_tape.py +120 -0
- onnxscript/ir/_type_casting.py +106 -0
- onnxscript/ir/convenience.py +32 -0
- onnxscript/ir/external_data.py +396 -0
- onnxscript/ir/passes/__init__.py +33 -0
- onnxscript/ir/passes/_pass_infra.py +172 -0
- onnxscript/ir/serde.py +1620 -0
- onnxscript/ir/tensor_adapters.py +122 -0
- onnxscript/ir/traversal.py +82 -0
- onnxscript/irbuilder.py +542 -0
- onnxscript/main.py +167 -0
- onnxscript/onnx_opset/__init__.py +232 -0
- onnxscript/onnx_opset/_impl/opset1.py +4100 -0
- onnxscript/onnx_opset/_impl/opset10.py +1227 -0
- onnxscript/onnx_opset/_impl/opset11.py +4013 -0
- onnxscript/onnx_opset/_impl/opset12.py +1078 -0
- onnxscript/onnx_opset/_impl/opset13.py +3924 -0
- onnxscript/onnx_opset/_impl/opset14.py +999 -0
- onnxscript/onnx_opset/_impl/opset15.py +604 -0
- onnxscript/onnx_opset/_impl/opset16.py +1255 -0
- onnxscript/onnx_opset/_impl/opset17.py +561 -0
- onnxscript/onnx_opset/_impl/opset18.py +1803 -0
- onnxscript/onnx_opset/_impl/opset19.py +1942 -0
- onnxscript/onnx_opset/_impl/opset2.py +218 -0
- onnxscript/onnx_opset/_impl/opset20.py +675 -0
- onnxscript/onnx_opset/_impl/opset21.py +1976 -0
- onnxscript/onnx_opset/_impl/opset22.py +2588 -0
- onnxscript/onnx_opset/_impl/opset3.py +199 -0
- onnxscript/onnx_opset/_impl/opset4.py +77 -0
- onnxscript/onnx_opset/_impl/opset5.py +84 -0
- onnxscript/onnx_opset/_impl/opset6.py +944 -0
- onnxscript/onnx_opset/_impl/opset7.py +1243 -0
- onnxscript/onnx_opset/_impl/opset8.py +444 -0
- onnxscript/onnx_opset/_impl/opset9.py +1485 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml1.py +974 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml2.py +112 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml3.py +308 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml4.py +129 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml5.py +158 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_preview_training1.py +577 -0
- onnxscript/onnx_types.py +229 -0
- onnxscript/optimizer/__init__.py +39 -0
- onnxscript/optimizer/_constant_folding.py +1083 -0
- onnxscript/optimizer/_inliner.py +312 -0
- onnxscript/optimizer/_legacy/_optimizer.py +98 -0
- onnxscript/optimizer/_legacy/_remove_unused_proto.py +144 -0
- onnxscript/optimizer/_legacy/_simple_function_folding.py +243 -0
- onnxscript/optimizer/_legacy/constant_folding.py +293 -0
- onnxscript/optimizer/_legacy/evaluator.py +439 -0
- onnxscript/optimizer/_optimizer.py +61 -0
- onnxscript/optimizer/_remove_unused.py +106 -0
- onnxscript/optimizer/_remove_unused_function.py +72 -0
- onnxscript/py.typed +1 -0
- onnxscript/rewriter/__init__.py +56 -0
- onnxscript/rewriter/_ir_utils.py +111 -0
- onnxscript/rewriter/broadcast_to_matmul.py +180 -0
- onnxscript/rewriter/cast_constant_of_shape.py +50 -0
- onnxscript/rewriter/collapse_slices.py +141 -0
- onnxscript/rewriter/erfgelu.py +27 -0
- onnxscript/rewriter/function_rule.py +232 -0
- onnxscript/rewriter/gemm_to_matmul_add.py +21 -0
- onnxscript/rewriter/generic_pattern.py +700 -0
- onnxscript/rewriter/llama_rule_sets.py +287 -0
- onnxscript/rewriter/no_op.py +56 -0
- onnxscript/rewriter/onnxruntime/__init__.py +50 -0
- onnxscript/rewriter/onnxruntime/bfloat16_utils/bfloat16_converter.py +99 -0
- onnxscript/rewriter/onnxruntime/fused_matmul_rule_sets.py +177 -0
- onnxscript/rewriter/onnxruntime/group_normalization_merge_silu.py +64 -0
- onnxscript/rewriter/onnxruntime/instance_to_group_normalization.py +155 -0
- onnxscript/rewriter/onnxruntime/softmax.py +63 -0
- onnxscript/rewriter/onnxruntime/transformers/__init__.py +21 -0
- onnxscript/rewriter/onnxruntime/transformers/biassplitgelu.py +31 -0
- onnxscript/rewriter/onnxruntime/transformers/fastgelu.py +29 -0
- onnxscript/rewriter/onnxruntime/transformers/layernorm.py +47 -0
- onnxscript/rewriter/onnxruntime/transformers/multihead_attention.py +715 -0
- onnxscript/rewriter/ort_fusions/__init__.py +9 -0
- onnxscript/rewriter/ort_fusions/_core.py +28 -0
- onnxscript/rewriter/ort_fusions/_smollm_1.py +253 -0
- onnxscript/rewriter/ort_fusions/_smollm_2.py +467 -0
- onnxscript/rewriter/ort_fusions/_test_models.py +122 -0
- onnxscript/rewriter/ort_fusions/_test_utils.py +42 -0
- onnxscript/rewriter/ort_fusions/cos_sin_cache.py +154 -0
- onnxscript/rewriter/ort_fusions/gqa.py +156 -0
- onnxscript/rewriter/ort_fusions/mha.py +198 -0
- onnxscript/rewriter/ort_fusions/rms_normalization.py +95 -0
- onnxscript/rewriter/ort_fusions/rotary_embedding.py +64 -0
- onnxscript/rewriter/ort_fusions/sdpa.py +75 -0
- onnxscript/rewriter/ort_fusions/skip_normalization.py +46 -0
- onnxscript/rewriter/pattern.py +1714 -0
- onnxscript/rewriter/testing.py +77 -0
- onnxscript/sourceinfo.py +59 -0
- onnxscript/tensor.py +227 -0
- onnxscript/testing/__init__.py +482 -0
- onnxscript/tools/__init__.py +4 -0
- onnxscript/tools/benchmark/__init__.py +23 -0
- onnxscript/tools/benchmark/benchmark_helpers.py +783 -0
- onnxscript/tools/benchmark/benchmark_run.py +140 -0
- onnxscript/tools/benchmark/export_model.py +207 -0
- onnxscript/tools/benchmark/export_model_batch.py +146 -0
- onnxscript/tools/memory_peak.py +244 -0
- onnxscript/tools/training_helper.py +50 -0
- onnxscript/tools/transformers_models/__init__.py +190 -0
- onnxscript/tools/transformers_models/llama.py +168 -0
- onnxscript/tools/transformers_models/mistral.py +238 -0
- onnxscript/tools/transformers_models/phi.py +248 -0
- onnxscript/tools/transformers_models/phi3.py +259 -0
- onnxscript/type_annotation.py +281 -0
- onnxscript/utils/__init__.py +0 -0
- onnxscript/utils/evaluation_utils.py +56 -0
- onnxscript/utils/timing_utils.py +33 -0
- onnxscript/utils/utils.py +84 -0
- onnxscript/values.py +790 -0
- onnxscript/version_converter/__init__.py +21 -0
- onnxscript/version_converter/_version_converter.py +314 -0
- onnxscript-0.1.0.dist-info/LICENSE +21 -0
- onnxscript-0.1.0.dist-info/METADATA +370 -0
- onnxscript-0.1.0.dist-info/RECORD +176 -0
- onnxscript-0.1.0.dist-info/WHEEL +5 -0
- onnxscript-0.1.0.dist-info/top_level.txt +1 -0
onnxscript/__init__.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"script",
|
|
6
|
+
"graph",
|
|
7
|
+
"ir",
|
|
8
|
+
"optimizer",
|
|
9
|
+
"rewriter",
|
|
10
|
+
"export_onnx_lib",
|
|
11
|
+
"OnnxFunction",
|
|
12
|
+
"TracedOnnxFunction",
|
|
13
|
+
"proto2python",
|
|
14
|
+
"external_tensor",
|
|
15
|
+
"BFLOAT16",
|
|
16
|
+
"FLOAT16",
|
|
17
|
+
"FLOAT8E4M3FN",
|
|
18
|
+
"FLOAT8E4M3FNUZ",
|
|
19
|
+
"FLOAT8E5M2",
|
|
20
|
+
"FLOAT8E5M2FNUZ",
|
|
21
|
+
"FLOAT",
|
|
22
|
+
"DOUBLE",
|
|
23
|
+
"INT8",
|
|
24
|
+
"INT16",
|
|
25
|
+
"INT32",
|
|
26
|
+
"INT64",
|
|
27
|
+
"UINT8",
|
|
28
|
+
"UINT16",
|
|
29
|
+
"UINT32",
|
|
30
|
+
"UINT64",
|
|
31
|
+
"BOOL",
|
|
32
|
+
"STRING",
|
|
33
|
+
"COMPLEX64",
|
|
34
|
+
"COMPLEX128",
|
|
35
|
+
"opset1",
|
|
36
|
+
"opset2",
|
|
37
|
+
"opset3",
|
|
38
|
+
"opset4",
|
|
39
|
+
"opset5",
|
|
40
|
+
"opset6",
|
|
41
|
+
"opset7",
|
|
42
|
+
"opset8",
|
|
43
|
+
"opset9",
|
|
44
|
+
"opset10",
|
|
45
|
+
"opset11",
|
|
46
|
+
"opset12",
|
|
47
|
+
"opset13",
|
|
48
|
+
"opset14",
|
|
49
|
+
"opset15",
|
|
50
|
+
"opset16",
|
|
51
|
+
"opset17",
|
|
52
|
+
"opset18",
|
|
53
|
+
"opset19",
|
|
54
|
+
"opset20",
|
|
55
|
+
"opset_ai_onnx_ml1",
|
|
56
|
+
"opset_ai_onnx_ml2",
|
|
57
|
+
"opset_ai_onnx_ml3",
|
|
58
|
+
"opset_ai_onnx_ml4",
|
|
59
|
+
"DEBUG",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
import importlib.metadata
|
|
63
|
+
|
|
64
|
+
from .backend.onnx_export import export2python as proto2python
|
|
65
|
+
from .main import export_onnx_lib, graph, script
|
|
66
|
+
|
|
67
|
+
# isort: off
|
|
68
|
+
from .onnx_opset import (
|
|
69
|
+
opset1,
|
|
70
|
+
opset2,
|
|
71
|
+
opset3,
|
|
72
|
+
opset4,
|
|
73
|
+
opset5,
|
|
74
|
+
opset6,
|
|
75
|
+
opset7,
|
|
76
|
+
opset8,
|
|
77
|
+
opset9,
|
|
78
|
+
opset10,
|
|
79
|
+
opset11,
|
|
80
|
+
opset12,
|
|
81
|
+
opset13,
|
|
82
|
+
opset14,
|
|
83
|
+
opset15,
|
|
84
|
+
opset16,
|
|
85
|
+
opset17,
|
|
86
|
+
opset18,
|
|
87
|
+
opset19,
|
|
88
|
+
opset20,
|
|
89
|
+
opset_ai_onnx_ml1,
|
|
90
|
+
opset_ai_onnx_ml2,
|
|
91
|
+
opset_ai_onnx_ml3,
|
|
92
|
+
opset_ai_onnx_ml4,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
from .onnx_types import (
|
|
96
|
+
BFLOAT16,
|
|
97
|
+
FLOAT16,
|
|
98
|
+
FLOAT8E4M3FN,
|
|
99
|
+
FLOAT8E4M3FNUZ,
|
|
100
|
+
FLOAT8E5M2,
|
|
101
|
+
FLOAT8E5M2FNUZ,
|
|
102
|
+
FLOAT,
|
|
103
|
+
DOUBLE,
|
|
104
|
+
INT8,
|
|
105
|
+
INT16,
|
|
106
|
+
INT32,
|
|
107
|
+
INT64,
|
|
108
|
+
UINT8,
|
|
109
|
+
UINT16,
|
|
110
|
+
UINT32,
|
|
111
|
+
UINT64,
|
|
112
|
+
BOOL,
|
|
113
|
+
STRING,
|
|
114
|
+
COMPLEX64,
|
|
115
|
+
COMPLEX128,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# isort: on
|
|
119
|
+
|
|
120
|
+
from . import ir, optimizer, rewriter
|
|
121
|
+
from ._internal.utils import external_tensor
|
|
122
|
+
from .values import OnnxFunction, TracedOnnxFunction
|
|
123
|
+
|
|
124
|
+
# Set DEBUG to True to enable additional debug checks
|
|
125
|
+
DEBUG: bool = False
|
|
126
|
+
|
|
127
|
+
try: # noqa: SIM105
|
|
128
|
+
__version__ = importlib.metadata.version("onnxscript")
|
|
129
|
+
except importlib.metadata.PackageNotFoundError:
|
|
130
|
+
# package is not installed
|
|
131
|
+
pass
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Stable APIs for PyTorch 2.5."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"check_model",
|
|
9
|
+
"convert_version",
|
|
10
|
+
"get_torchlib_ops",
|
|
11
|
+
"optimize",
|
|
12
|
+
"save_model_with_external_data",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
import dataclasses
|
|
16
|
+
import os
|
|
17
|
+
import pathlib
|
|
18
|
+
from typing import Callable
|
|
19
|
+
|
|
20
|
+
from onnxscript import ir, optimizer, version_converter
|
|
21
|
+
from onnxscript.function_libs.torch_lib import registration
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclasses.dataclass(frozen=True)
|
|
25
|
+
class _OnnxFunctionMeta:
|
|
26
|
+
"""A wrapper of onnx-script function with additional metadata.
|
|
27
|
+
|
|
28
|
+
qualified_name: The qualified name of the aten operator.
|
|
29
|
+
function: The onnx-script function.
|
|
30
|
+
domain: The domain of the function.
|
|
31
|
+
name: The name of the function.
|
|
32
|
+
is_complex: Whether the function is a complex function.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
qualified_name: str
|
|
36
|
+
function: Callable
|
|
37
|
+
domain: str
|
|
38
|
+
name: str
|
|
39
|
+
is_complex: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def optimize(model: ir.Model) -> ir.Model:
|
|
43
|
+
"""Optimize the model."""
|
|
44
|
+
# Internal flag. Will go away.
|
|
45
|
+
enabled = os.getenv("TORCH_ONNX_ENABLE_OPTIMIZATION") == "1"
|
|
46
|
+
if enabled:
|
|
47
|
+
optimizer.optimize_ir(model)
|
|
48
|
+
return model
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def convert_version(model: ir.Model, target_version: int) -> ir.Model:
|
|
52
|
+
"""Convert the model to the specified ONNX opset version."""
|
|
53
|
+
# Internal flag. Will go away.
|
|
54
|
+
enabled = os.getenv("TORCH_ONNX_ENABLE_VERSION_CONVERSION") == "1"
|
|
55
|
+
if enabled:
|
|
56
|
+
version_converter.convert_version(model, target_version)
|
|
57
|
+
return model
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def check_model(model: ir.Model) -> None:
|
|
61
|
+
"""Check the model."""
|
|
62
|
+
|
|
63
|
+
del model # Unused yet
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def save_model_with_external_data(model: ir.Model, model_path: str | os.PathLike) -> None:
|
|
67
|
+
"""Save the model with external data. The model is unchanged after saving."""
|
|
68
|
+
|
|
69
|
+
# TODO(#1835): Decide if we want to externalize large attributes as well
|
|
70
|
+
for value in model.graph.initializers.values():
|
|
71
|
+
if value.const_value is None:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
"The model contains uninitialized initializer values. "
|
|
74
|
+
"Please make sure all initializer values are initialized."
|
|
75
|
+
)
|
|
76
|
+
destination_path = pathlib.Path(model_path)
|
|
77
|
+
data_path = f"{destination_path.name}.data"
|
|
78
|
+
|
|
79
|
+
ir.save(model, model_path, external_data=data_path)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_torchlib_ops() -> list[_OnnxFunctionMeta]:
|
|
83
|
+
# Trigger op registration
|
|
84
|
+
from onnxscript.function_libs.torch_lib import ( # pylint: disable=import-outside-toplevel
|
|
85
|
+
ops,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
del ops # Unused
|
|
89
|
+
|
|
90
|
+
torchlib_registry = registration.default_registry
|
|
91
|
+
function_metas = []
|
|
92
|
+
|
|
93
|
+
for qualified_name, aten_overloads_func in torchlib_registry.items():
|
|
94
|
+
if qualified_name.startswith("internal::"):
|
|
95
|
+
# Skip the custom defined internal functions
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
for overload_func in aten_overloads_func.overloads:
|
|
99
|
+
function_meta = _OnnxFunctionMeta(
|
|
100
|
+
qualified_name=qualified_name,
|
|
101
|
+
function=overload_func,
|
|
102
|
+
domain=overload_func.function_ir.domain,
|
|
103
|
+
name=overload_func.name,
|
|
104
|
+
is_complex=False,
|
|
105
|
+
)
|
|
106
|
+
function_metas.append(function_meta)
|
|
107
|
+
for complex_func in aten_overloads_func.complex:
|
|
108
|
+
function_meta = _OnnxFunctionMeta(
|
|
109
|
+
qualified_name=qualified_name,
|
|
110
|
+
function=complex_func,
|
|
111
|
+
domain=complex_func.function_ir.domain,
|
|
112
|
+
name=complex_func.name,
|
|
113
|
+
is_complex=True,
|
|
114
|
+
)
|
|
115
|
+
function_metas.append(function_meta)
|
|
116
|
+
|
|
117
|
+
return function_metas
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Stable APIs for PyTorch 2.6."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"check_model",
|
|
9
|
+
"convert_version",
|
|
10
|
+
"get_torchlib_ops",
|
|
11
|
+
"optimize",
|
|
12
|
+
"save_model_with_external_data",
|
|
13
|
+
"torchlib_opset",
|
|
14
|
+
]
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
from onnxscript import ir, optimizer
|
|
18
|
+
from onnxscript._framework_apis.torch_2_5 import (
|
|
19
|
+
check_model,
|
|
20
|
+
convert_version,
|
|
21
|
+
get_torchlib_ops,
|
|
22
|
+
save_model_with_external_data,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from onnxscript.onnx_opset._impl.opset18 import Opset18
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def optimize(model: ir.Model) -> ir.Model:
|
|
30
|
+
"""Optimize the model."""
|
|
31
|
+
optimizer.optimize_ir(model)
|
|
32
|
+
return model
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def torchlib_opset() -> Opset18:
|
|
36
|
+
"""Return the default opset for torchlib."""
|
|
37
|
+
import onnxscript # pylint: disable=import-outside-toplevel
|
|
38
|
+
|
|
39
|
+
return onnxscript.opset18 # type: ignore
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def torchlib_opset_version() -> int:
|
|
43
|
+
"""Return the default opset version for torchlib."""
|
|
44
|
+
|
|
45
|
+
return torchlib_opset().version
|
|
File without changes
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
from typing import Any, Optional, Sequence, Set
|
|
7
|
+
|
|
8
|
+
from onnxscript import sourceinfo
|
|
9
|
+
from onnxscript._internal import ast_utils
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_loop_var(for_stmt: ast.For, formatter: sourceinfo.Formatter) -> str:
|
|
13
|
+
if not isinstance(for_stmt.target, ast.Name):
|
|
14
|
+
raise TypeError(formatter(for_stmt, "For loop target must be a single variable."))
|
|
15
|
+
return for_stmt.target.id
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _used_vars(expr: Optional[ast.expr]) -> Set[str]:
|
|
19
|
+
"""Return set of all variables used, including function names, in an expression."""
|
|
20
|
+
if expr is None:
|
|
21
|
+
return set()
|
|
22
|
+
if isinstance(expr, ast.Name):
|
|
23
|
+
return {expr.id}
|
|
24
|
+
result = set()
|
|
25
|
+
if isinstance(expr, ast.Call):
|
|
26
|
+
# The callee-expression is not visited
|
|
27
|
+
children = expr.args
|
|
28
|
+
for keyword in expr.keywords:
|
|
29
|
+
if isinstance(keyword.value, ast.Name):
|
|
30
|
+
result.add(keyword.value.id)
|
|
31
|
+
else:
|
|
32
|
+
children = ast.iter_child_nodes(expr) # type: ignore[assignment]
|
|
33
|
+
for c in children:
|
|
34
|
+
result = result | _used_vars(c)
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _lhs_vars(lhs: ast.expr) -> Set[str]:
|
|
39
|
+
"""Return set of assigned variables in the lhs of an assignment statement."""
|
|
40
|
+
|
|
41
|
+
def get_id(e):
|
|
42
|
+
assert isinstance(e, ast.Name), "Only simple assignments supported."
|
|
43
|
+
return e.id
|
|
44
|
+
|
|
45
|
+
if isinstance(lhs, ast.Tuple):
|
|
46
|
+
return {get_id(x) for x in lhs.elts}
|
|
47
|
+
return {get_id(lhs)}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def assigned_vars(
|
|
51
|
+
stmt: ast.stmt | list[ast.stmt], formatter: sourceinfo.Formatter
|
|
52
|
+
) -> Set[str]:
|
|
53
|
+
"""Return the set of all variables that may be assigned to in an execution of input stmt
|
|
54
|
+
or sequence of statements.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def assigned_in_block(block: Sequence[ast.stmt]) -> Set[str]:
|
|
58
|
+
result: set[Any] = set()
|
|
59
|
+
for s in block:
|
|
60
|
+
result = result | assigned_vars(s, formatter)
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
if isinstance(stmt, ast.Assign):
|
|
64
|
+
return _lhs_vars(stmt.targets[0])
|
|
65
|
+
if isinstance(stmt, ast.AnnAssign):
|
|
66
|
+
return _lhs_vars(stmt.target)
|
|
67
|
+
if isinstance(stmt, ast.Return):
|
|
68
|
+
return set()
|
|
69
|
+
if isinstance(stmt, ast.If):
|
|
70
|
+
return assigned_in_block(stmt.body) | assigned_in_block(stmt.orelse)
|
|
71
|
+
if isinstance(stmt, ast.For):
|
|
72
|
+
return assigned_in_block(stmt.body) | {_get_loop_var(stmt, formatter)}
|
|
73
|
+
if isinstance(stmt, ast.While):
|
|
74
|
+
return assigned_in_block(stmt.body)
|
|
75
|
+
if isinstance(stmt, list):
|
|
76
|
+
return assigned_in_block(stmt)
|
|
77
|
+
if isinstance(stmt, ast.Break):
|
|
78
|
+
return set()
|
|
79
|
+
if ast_utils.is_print_call(stmt):
|
|
80
|
+
return set()
|
|
81
|
+
if ast_utils.is_doc_string(stmt):
|
|
82
|
+
return set()
|
|
83
|
+
error_message = formatter(stmt, f"Unsupported statement type {type(stmt)!r}.")
|
|
84
|
+
raise ValueError(error_message)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def do_liveness_analysis(fun: ast.FunctionDef, formatter: sourceinfo.Formatter):
|
|
88
|
+
"""Perform liveness analysis of the given function-ast. The results of the
|
|
89
|
+
analysis are stored directly with each statement-ast `s` as attributes `s.live_in`
|
|
90
|
+
and `s.live_out`.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def visit(stmt: ast.stmt, live_out: Set[str]) -> Set[str]:
|
|
94
|
+
stmt.live_out = live_out # type: ignore[attr-defined]
|
|
95
|
+
live = do_visit(stmt, live_out)
|
|
96
|
+
stmt.live_in = live # type: ignore[attr-defined]
|
|
97
|
+
return live
|
|
98
|
+
|
|
99
|
+
def do_visit(stmt: ast.stmt, live_out: Set[str]) -> Set[str]:
|
|
100
|
+
def visitBlock(block: Sequence[ast.stmt], live_out: Set[str]) -> Set[str]:
|
|
101
|
+
for s in reversed(block):
|
|
102
|
+
live_out = visit(s, live_out)
|
|
103
|
+
return live_out
|
|
104
|
+
|
|
105
|
+
if isinstance(stmt, ast.Assign):
|
|
106
|
+
return live_out.difference(_lhs_vars(stmt.targets[0])) | _used_vars(stmt.value)
|
|
107
|
+
if isinstance(stmt, ast.AnnAssign):
|
|
108
|
+
return live_out.difference(_lhs_vars(stmt.target)) | _used_vars(stmt.value)
|
|
109
|
+
if isinstance(stmt, ast.Return):
|
|
110
|
+
return _used_vars(stmt.value)
|
|
111
|
+
if isinstance(stmt, ast.If):
|
|
112
|
+
live1 = visitBlock(stmt.body, live_out)
|
|
113
|
+
live2 = visitBlock(stmt.orelse, live_out)
|
|
114
|
+
return live1 | live2 | _used_vars(stmt.test)
|
|
115
|
+
if isinstance(stmt, ast.For):
|
|
116
|
+
p_loop_var = _get_loop_var(stmt, formatter)
|
|
117
|
+
prev = None
|
|
118
|
+
curr = live_out
|
|
119
|
+
while curr != prev:
|
|
120
|
+
prev = curr
|
|
121
|
+
curr = visitBlock(stmt.body, prev).difference({p_loop_var})
|
|
122
|
+
return curr
|
|
123
|
+
if isinstance(stmt, ast.While):
|
|
124
|
+
cond_vars = _used_vars(stmt.test)
|
|
125
|
+
prev = None
|
|
126
|
+
curr = live_out | cond_vars
|
|
127
|
+
while curr != prev:
|
|
128
|
+
prev = curr
|
|
129
|
+
curr = visitBlock(stmt.body, prev) | cond_vars
|
|
130
|
+
return curr
|
|
131
|
+
if isinstance(stmt, ast.Break):
|
|
132
|
+
# The following is sufficient for the current restricted usage, where
|
|
133
|
+
# a (conditional) break is allowed only as the last statement of a loop.
|
|
134
|
+
# Break statements in the middle of the loop, however, will require
|
|
135
|
+
# a generalization.
|
|
136
|
+
return live_out
|
|
137
|
+
if ast_utils.is_doc_string(stmt):
|
|
138
|
+
return live_out
|
|
139
|
+
if isinstance(stmt, ast.FunctionDef):
|
|
140
|
+
return live_out
|
|
141
|
+
if ast_utils.is_print_call(stmt):
|
|
142
|
+
return live_out
|
|
143
|
+
raise ValueError(formatter(stmt, f"Unsupported statement type {type(stmt)!r}."))
|
|
144
|
+
|
|
145
|
+
assert isinstance(fun, ast.FunctionDef)
|
|
146
|
+
live: set[Any] = set()
|
|
147
|
+
for s in reversed(fun.body):
|
|
148
|
+
live = visit(s, live)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def exposed_uses(stmts: Sequence[ast.stmt], formatter: sourceinfo.Formatter):
|
|
152
|
+
"""Return the set of variables that are used before being defined by given block.
|
|
153
|
+
In essence, this identifies the "inputs" to a given code-block.
|
|
154
|
+
For example, consider the following code-block:
|
|
155
|
+
::
|
|
156
|
+
|
|
157
|
+
x = x + 10
|
|
158
|
+
y = 20
|
|
159
|
+
z = x + y
|
|
160
|
+
x = 30
|
|
161
|
+
|
|
162
|
+
The exposed_uses of this code-block is { x }. The value of z is not used within
|
|
163
|
+
the block. Even though the value of y is used within the block, it is assigned
|
|
164
|
+
a value before it is used. However, in contrast, the incoming value of x is used
|
|
165
|
+
(in the first statement). Hence x is included in the exposed_uses.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
def visitBlock(block: Sequence[ast.stmt], live_out: Set[str]) -> Set[str]:
|
|
169
|
+
for stmt in reversed(block):
|
|
170
|
+
live_out = visit(stmt, live_out)
|
|
171
|
+
return live_out
|
|
172
|
+
|
|
173
|
+
def visit(stmt: ast.stmt, live_out: Set[str]) -> Set[str]:
|
|
174
|
+
if isinstance(stmt, ast.Assign):
|
|
175
|
+
return live_out.difference(_lhs_vars(stmt.targets[0])) | _used_vars(stmt.value)
|
|
176
|
+
if isinstance(stmt, ast.AnnAssign):
|
|
177
|
+
return live_out.difference(_lhs_vars(stmt.target)) | _used_vars(stmt.value)
|
|
178
|
+
if isinstance(stmt, ast.Return):
|
|
179
|
+
return _used_vars(stmt.value)
|
|
180
|
+
if isinstance(stmt, ast.If):
|
|
181
|
+
live1 = visitBlock(stmt.body, live_out)
|
|
182
|
+
live2 = visitBlock(stmt.orelse, live_out)
|
|
183
|
+
return (live1 | live2) | _used_vars(stmt.test)
|
|
184
|
+
if ast_utils.is_print_call(stmt):
|
|
185
|
+
return live_out
|
|
186
|
+
if ast_utils.is_doc_string(stmt):
|
|
187
|
+
return live_out
|
|
188
|
+
if isinstance(stmt, ast.For):
|
|
189
|
+
# Analysis assumes loop may execute zero times. Results can be improved
|
|
190
|
+
# for loops that execute at least once.
|
|
191
|
+
loop_var_set = {_get_loop_var(stmt, formatter)}
|
|
192
|
+
used_after_loop = live_out.difference(loop_var_set)
|
|
193
|
+
used_inside_loop = visitBlock(stmt.body, set()).difference(loop_var_set)
|
|
194
|
+
used_in_loop_header = _used_vars(stmt.iter)
|
|
195
|
+
return used_inside_loop | used_in_loop_header | used_after_loop
|
|
196
|
+
if isinstance(stmt, ast.While):
|
|
197
|
+
# Analysis assumes loop may execute zero times. Results can be improved
|
|
198
|
+
# for loops that execute at least once.
|
|
199
|
+
used_inside_loop = visitBlock(stmt.body, set())
|
|
200
|
+
used_in_loop_header = _used_vars(stmt.test)
|
|
201
|
+
return used_inside_loop | used_in_loop_header | live_out
|
|
202
|
+
if isinstance(stmt, ast.Break):
|
|
203
|
+
# Currently, we assume that break statements are only allowed as the last
|
|
204
|
+
# statement in a loop, as "if cond: break".
|
|
205
|
+
return live_out
|
|
206
|
+
if isinstance(stmt, ast.FunctionDef):
|
|
207
|
+
if stmt.name in live_out:
|
|
208
|
+
live_out.remove(stmt.name)
|
|
209
|
+
live_out = live_out | outer_scope_variables(stmt, formatter)
|
|
210
|
+
return live_out
|
|
211
|
+
raise ValueError(formatter(stmt, f"Unsupported statement type {type(stmt)!r}."))
|
|
212
|
+
|
|
213
|
+
return visitBlock(stmts, set())
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def outer_scope_variables(fun: ast.FunctionDef, formatter: sourceinfo.Formatter):
|
|
217
|
+
"""Return the set of outer-scope variables used in a nested function.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
fun: The function-ast to analyze.
|
|
221
|
+
formatter: The formatter object.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
A set of variable names (strings).
|
|
225
|
+
"""
|
|
226
|
+
assert isinstance(fun, ast.FunctionDef)
|
|
227
|
+
used_vars_ = exposed_uses(fun.body, formatter)
|
|
228
|
+
inputs = [x.arg for x in fun.args.args]
|
|
229
|
+
return used_vars_.difference(inputs)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Utilities for working with Python ASTs."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
import inspect
|
|
9
|
+
import sys
|
|
10
|
+
import textwrap
|
|
11
|
+
from typing import Callable
|
|
12
|
+
|
|
13
|
+
PY_VERSION_GE_39 = sys.version_info >= (3, 9)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_src_and_ast(func: Callable, /) -> tuple[str, ast.FunctionDef]:
|
|
17
|
+
try:
|
|
18
|
+
src = inspect.getsource(func)
|
|
19
|
+
except OSError as e:
|
|
20
|
+
raise RuntimeError(
|
|
21
|
+
f"Decorator script does not work on dynamically compiled function {func.__name__}."
|
|
22
|
+
) from e
|
|
23
|
+
src = textwrap.dedent(src)
|
|
24
|
+
top_level_ast = ast.parse(src)
|
|
25
|
+
assert isinstance(top_level_ast, ast.Module)
|
|
26
|
+
assert len(top_level_ast.body) == 1
|
|
27
|
+
f_ast = top_level_ast.body[0]
|
|
28
|
+
assert isinstance(f_ast, ast.FunctionDef)
|
|
29
|
+
return src, f_ast
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def normalize_subscript_expr(expr: ast.Subscript):
|
|
33
|
+
# Normalizes the representation of a subscripted expression, handling python version
|
|
34
|
+
# differences as well as variations between A[x] (single-index) and A[x, y] (multiple indices)
|
|
35
|
+
# Returns a list of expressions, denoting the indices, after stripping the extraneous "Index"
|
|
36
|
+
# wrapper present in python versions before 3.9
|
|
37
|
+
index_expr = expr.slice
|
|
38
|
+
if PY_VERSION_GE_39:
|
|
39
|
+
if isinstance(index_expr, ast.Tuple):
|
|
40
|
+
return index_expr.elts # multiple indices
|
|
41
|
+
else:
|
|
42
|
+
return [index_expr] # single index
|
|
43
|
+
else:
|
|
44
|
+
if isinstance(index_expr, ast.ExtSlice):
|
|
45
|
+
indices = index_expr.dims # type: ignore[attr-defined]
|
|
46
|
+
else:
|
|
47
|
+
indices = [index_expr] # single slice-index
|
|
48
|
+
return [x.value if isinstance(x, ast.Index) else x for x in indices] # type: ignore[attr-defined]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_print_call(stmt: ast.stmt) -> bool:
|
|
52
|
+
"""Return True if the statement is a call to the print function."""
|
|
53
|
+
if isinstance(stmt, ast.Expr):
|
|
54
|
+
if isinstance(stmt.value, ast.Call):
|
|
55
|
+
if isinstance(stmt.value.func, ast.Name):
|
|
56
|
+
return stmt.value.func.id == "print"
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def is_doc_string(stmt: ast.stmt) -> bool:
|
|
61
|
+
"""Return True if the statement is a docstring."""
|
|
62
|
+
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant):
|
|
63
|
+
return isinstance(stmt.value.value, str)
|
|
64
|
+
return False
|