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
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
import dataclasses
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Dict, Mapping, Optional, Sequence, Set
|
|
9
|
+
|
|
10
|
+
import onnx
|
|
11
|
+
import onnx.defs
|
|
12
|
+
|
|
13
|
+
import onnxscript
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
_ONNX_DTYPE_TO_ONNX_TENSOR_TYPE_STR: Mapping[int, str] = {
|
|
18
|
+
onnx.TensorProto.FLOAT: "tensor(float)",
|
|
19
|
+
onnx.TensorProto.UINT8: "tensor(uint8)",
|
|
20
|
+
onnx.TensorProto.INT8: "tensor(int8)",
|
|
21
|
+
onnx.TensorProto.UINT16: "tensor(uint16)",
|
|
22
|
+
onnx.TensorProto.INT16: "tensor(int16)",
|
|
23
|
+
onnx.TensorProto.INT32: "tensor(int32)",
|
|
24
|
+
onnx.TensorProto.INT64: "tensor(int64)",
|
|
25
|
+
onnx.TensorProto.STRING: "tensor(string)",
|
|
26
|
+
onnx.TensorProto.BOOL: "tensor(bool)",
|
|
27
|
+
onnx.TensorProto.FLOAT16: "tensor(float16)",
|
|
28
|
+
onnx.TensorProto.DOUBLE: "tensor(double)",
|
|
29
|
+
onnx.TensorProto.COMPLEX64: "tensor(complex64)",
|
|
30
|
+
onnx.TensorProto.COMPLEX128: "tensor(complex128)",
|
|
31
|
+
onnx.TensorProto.UINT32: "tensor(uint32)",
|
|
32
|
+
onnx.TensorProto.UINT64: "tensor(uint64)",
|
|
33
|
+
onnx.TensorProto.BFLOAT16: "tensor(bfloat16)",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_ONNX_ATTR_TYPE_TO_ONNX_TENSOR_TYPE_STR: Mapping[int, str] = {
|
|
37
|
+
onnx.AttributeProto.UNDEFINED: "undefined",
|
|
38
|
+
onnx.AttributeProto.FLOAT: "tensor(float)",
|
|
39
|
+
onnx.AttributeProto.INT: "tensor(int64)",
|
|
40
|
+
onnx.AttributeProto.STRING: "tensor(string)",
|
|
41
|
+
onnx.AttributeProto.FLOATS: "tensor(float)",
|
|
42
|
+
onnx.AttributeProto.INTS: "tensor(int64)",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TypeConstraint:
|
|
47
|
+
"""Type constraint shared by multiple values."""
|
|
48
|
+
|
|
49
|
+
name: str
|
|
50
|
+
type_strs: Set[str]
|
|
51
|
+
values: Set[Value]
|
|
52
|
+
|
|
53
|
+
def __init__(self, name: str, type_strs: Set[str]):
|
|
54
|
+
self.name = name
|
|
55
|
+
self.type_strs = type_strs
|
|
56
|
+
self.values = set()
|
|
57
|
+
|
|
58
|
+
def merge_type_constraint(self, other: TypeConstraint):
|
|
59
|
+
"""Merge two type constraints.
|
|
60
|
+
|
|
61
|
+
Typically this process tightens the type constraints.
|
|
62
|
+
|
|
63
|
+
For example, consider two values with different existing type constraints:
|
|
64
|
+
|
|
65
|
+
input1: TypeConstraint(name="input1", type_strs={"tensor(float)", "tensor(int64)"})
|
|
66
|
+
input2: TypeConstraint(name="input2", type_strs={"tensor(float)", "tensor(int32)"})
|
|
67
|
+
|
|
68
|
+
Now consider they are two inputs to `Add` node, where the op schema is:
|
|
69
|
+
|
|
70
|
+
inputs: [T, T]
|
|
71
|
+
outputs: [T]
|
|
72
|
+
T: ['tensor(uint8)', ..., 'tensor(float)', 'tensor(int64)', 'tensor(int32)']
|
|
73
|
+
|
|
74
|
+
`input1` and `input2` must now bound to the same type constraint, hence the result is
|
|
75
|
+
the intersection of the two existing type constraints with the op schema type constraint:
|
|
76
|
+
|
|
77
|
+
input1: TypeConstraint(name="input1.input2", type_strs={"tensor(float)"})
|
|
78
|
+
input2: TypeConstraint(name="input1.input2", type_strs={"tensor(float)"})
|
|
79
|
+
"""
|
|
80
|
+
if self is other:
|
|
81
|
+
return
|
|
82
|
+
self.type_strs.intersection_update(other.type_strs)
|
|
83
|
+
self.values = self.values.union(other.values)
|
|
84
|
+
for meta in self.values:
|
|
85
|
+
meta.type_constraint = self
|
|
86
|
+
self.name = f"{self.name}.{other.name}"
|
|
87
|
+
|
|
88
|
+
def bind_value(self, value: Value):
|
|
89
|
+
"""Bind a value to this type constraint.
|
|
90
|
+
|
|
91
|
+
If the value does not have a type constraint, set it to this type constraint.
|
|
92
|
+
Otherwise, merge the two type constraints.
|
|
93
|
+
"""
|
|
94
|
+
if value.type_constraint is not None:
|
|
95
|
+
self.merge_type_constraint(value.type_constraint)
|
|
96
|
+
else:
|
|
97
|
+
self.values.add(value)
|
|
98
|
+
value.type_constraint = self
|
|
99
|
+
|
|
100
|
+
def __repr__(self):
|
|
101
|
+
value_names = [value.name for value in self.values]
|
|
102
|
+
return f"TypeConstraint(name={self.name}, type_strs={self.type_strs}, values={value_names})"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Value:
|
|
106
|
+
"""Represents a value in the graph. Associated with a TypeConstraint."""
|
|
107
|
+
|
|
108
|
+
def __init__(self, name: str):
|
|
109
|
+
self.type_constraint: Optional[TypeConstraint] = None
|
|
110
|
+
self.name: str = name
|
|
111
|
+
|
|
112
|
+
def merge_type_constraint(self, other: Value):
|
|
113
|
+
if other.type_constraint is not None:
|
|
114
|
+
other.type_constraint.bind_value(self)
|
|
115
|
+
elif self.type_constraint is not None:
|
|
116
|
+
self.type_constraint.bind_value(other)
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"Cannot merge two values without type constraints. {self} and {other}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def __repr__(self) -> str:
|
|
123
|
+
return f"Value(name={self.name}, type_constraint={self.type_constraint})"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclasses.dataclass
|
|
127
|
+
class OnnxFunctionTypeConstraints:
|
|
128
|
+
input_type_constraints: Dict[str, Optional[TypeConstraint]]
|
|
129
|
+
output_type_constraints: Dict[str, Optional[TypeConstraint]]
|
|
130
|
+
intermediate_type_constraints: Dict[str, Optional[TypeConstraint]]
|
|
131
|
+
|
|
132
|
+
def __repr__(self):
|
|
133
|
+
repr_strs = [
|
|
134
|
+
"Type Constraints:",
|
|
135
|
+
" Inputs: ",
|
|
136
|
+
]
|
|
137
|
+
repr_strs += [
|
|
138
|
+
f" {name}: {type_constraint.name}"
|
|
139
|
+
if type_constraint is not None
|
|
140
|
+
else f" {name}: None"
|
|
141
|
+
for name, type_constraint in self.input_type_constraints.items()
|
|
142
|
+
]
|
|
143
|
+
repr_strs += [
|
|
144
|
+
" Outputs: ",
|
|
145
|
+
]
|
|
146
|
+
repr_strs += [
|
|
147
|
+
f" {name}: {type_constraint.name}"
|
|
148
|
+
if type_constraint is not None
|
|
149
|
+
else f" {name}: None"
|
|
150
|
+
for name, type_constraint in self.output_type_constraints.items()
|
|
151
|
+
]
|
|
152
|
+
repr_strs += [
|
|
153
|
+
" Type Constraints: ",
|
|
154
|
+
]
|
|
155
|
+
# Trick to get unique type constraints but maintain the order.
|
|
156
|
+
ordered_unique_type_constraints = dict.fromkeys(self.input_type_constraints.values())
|
|
157
|
+
ordered_unique_type_constraints.update(
|
|
158
|
+
dict.fromkeys(self.output_type_constraints.values())
|
|
159
|
+
)
|
|
160
|
+
repr_strs += [
|
|
161
|
+
f" {type_constraint.name}: {type_constraint.type_strs}"
|
|
162
|
+
for type_constraint in ordered_unique_type_constraints
|
|
163
|
+
if type_constraint is not None
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
if self.intermediate_type_constraints:
|
|
167
|
+
repr_strs += [" Intermediate Values: "]
|
|
168
|
+
repr_strs += [
|
|
169
|
+
f" {name}: {type_constraint.name}"
|
|
170
|
+
if type_constraint is not None
|
|
171
|
+
else f" {name}: None"
|
|
172
|
+
for name, type_constraint in self.intermediate_type_constraints.items()
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
repr_strs += [
|
|
176
|
+
" Intermediate Type Constraints: ",
|
|
177
|
+
]
|
|
178
|
+
ordered_unique_type_constraints = dict.fromkeys(
|
|
179
|
+
self.intermediate_type_constraints.values()
|
|
180
|
+
)
|
|
181
|
+
repr_strs += [
|
|
182
|
+
f" {type_constraint.name}: {type_constraint.type_strs}"
|
|
183
|
+
for type_constraint in ordered_unique_type_constraints
|
|
184
|
+
if type_constraint is not None
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
return "\n".join(repr_strs)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class TypeConstraintDeducer:
|
|
191
|
+
def __init__(self, onnx_function: onnxscript.OnnxFunction):
|
|
192
|
+
self.onnx_function = onnx_function
|
|
193
|
+
self.values: Dict[str, Value] = {}
|
|
194
|
+
|
|
195
|
+
def type_constraints(self, signature_only: bool = True) -> OnnxFunctionTypeConstraints:
|
|
196
|
+
"""Retrieve deduced type constraints for the ONNX function."""
|
|
197
|
+
if not self.values:
|
|
198
|
+
raise ValueError("Must call deduce() first")
|
|
199
|
+
|
|
200
|
+
input_type_constraints = {
|
|
201
|
+
name: self.values[name].type_constraint for name in self.function_proto.input
|
|
202
|
+
}
|
|
203
|
+
output_type_constraints = {
|
|
204
|
+
name: self.values[name].type_constraint for name in self.function_proto.output
|
|
205
|
+
}
|
|
206
|
+
intermediate_type_constraints = (
|
|
207
|
+
{}
|
|
208
|
+
if signature_only
|
|
209
|
+
else {name: value.type_constraint for name, value in self.values.items()}
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Rename type constraints to T0, T1, T2, ...
|
|
213
|
+
seen_type_constraints: Set[TypeConstraint] = set()
|
|
214
|
+
for type_constraint in (
|
|
215
|
+
*input_type_constraints.values(),
|
|
216
|
+
*output_type_constraints.values(),
|
|
217
|
+
*intermediate_type_constraints.values(),
|
|
218
|
+
):
|
|
219
|
+
if type_constraint is not None and type_constraint not in seen_type_constraints:
|
|
220
|
+
type_constraint.name = f"T{len(seen_type_constraints)}"
|
|
221
|
+
seen_type_constraints.add(type_constraint)
|
|
222
|
+
|
|
223
|
+
return OnnxFunctionTypeConstraints(
|
|
224
|
+
input_type_constraints, output_type_constraints, intermediate_type_constraints
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def deduce(self, signature_only: bool = True) -> OnnxFunctionTypeConstraints:
|
|
228
|
+
"""Deduce type constraints for all values in the graph.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
signature_only: If True, return deduce type constraints only for function signature.
|
|
232
|
+
Otherwise, return deduced type constraints for all values in the graph.
|
|
233
|
+
"""
|
|
234
|
+
self.values = {}
|
|
235
|
+
self.function_proto = self.onnx_function.to_function_proto()
|
|
236
|
+
self.opset_version = self.function_proto.opset_import[0].version
|
|
237
|
+
|
|
238
|
+
param_schemas = self.onnx_function.param_schemas()
|
|
239
|
+
for param_schema in param_schemas:
|
|
240
|
+
if param_schema.is_input:
|
|
241
|
+
self.values[param_schema.name] = Value(param_schema.name)
|
|
242
|
+
|
|
243
|
+
for node in self.function_proto.node:
|
|
244
|
+
self._process_node(node)
|
|
245
|
+
|
|
246
|
+
return self.type_constraints(signature_only)
|
|
247
|
+
|
|
248
|
+
def _bind_signature(
|
|
249
|
+
self,
|
|
250
|
+
node: onnx.NodeProto,
|
|
251
|
+
param_names: Sequence[str],
|
|
252
|
+
param_schemas: Sequence[onnx.defs.OpSchema.FormalParameter],
|
|
253
|
+
op_type_constraints: Dict[str, TypeConstraint],
|
|
254
|
+
is_output: bool = False,
|
|
255
|
+
):
|
|
256
|
+
param_schemas = list(param_schemas)
|
|
257
|
+
# If the last parameter is variadic, duplicate it to match the number of parameters.
|
|
258
|
+
if (
|
|
259
|
+
len(param_schemas) < len(param_names)
|
|
260
|
+
and param_schemas[-1].option == onnx.defs.OpSchema.FormalParameterOption.Variadic
|
|
261
|
+
):
|
|
262
|
+
param_schemas += [param_schemas[-1]] * (len(param_names) - len(param_schemas))
|
|
263
|
+
for name, schema in zip(param_names, param_schemas):
|
|
264
|
+
if is_output:
|
|
265
|
+
if name in self.values:
|
|
266
|
+
raise ValueError(f"Output {name} already exists.")
|
|
267
|
+
value = Value(name)
|
|
268
|
+
self.values[name] = value
|
|
269
|
+
else:
|
|
270
|
+
if not name:
|
|
271
|
+
# Skip optional inputs
|
|
272
|
+
continue
|
|
273
|
+
value = self.values[name]
|
|
274
|
+
if (new_type_constraint := op_type_constraints.get(schema.type_str)) is None:
|
|
275
|
+
# parameter is annotated with type string instead of type constraint.
|
|
276
|
+
# Create individual type constraint.
|
|
277
|
+
new_type_constraint = TypeConstraint(
|
|
278
|
+
name=f"T_{name}",
|
|
279
|
+
type_strs={schema.type_str},
|
|
280
|
+
)
|
|
281
|
+
if not schema.is_homogeneous:
|
|
282
|
+
# Parameter is not homogeneous, this appears with variadic parameters.
|
|
283
|
+
# Meaning the type constraint is not shared amongst them.
|
|
284
|
+
# Creating a type constraint copy.
|
|
285
|
+
new_type_constraint = TypeConstraint(
|
|
286
|
+
name=f"{schema.name}_{name}",
|
|
287
|
+
type_strs=new_type_constraint.type_strs,
|
|
288
|
+
)
|
|
289
|
+
prev_value_constraint = copy.deepcopy(value.type_constraint)
|
|
290
|
+
new_type_constraint.bind_value(value)
|
|
291
|
+
if prev_value_constraint is not None and len(
|
|
292
|
+
prev_value_constraint.type_strs
|
|
293
|
+
) > len(new_type_constraint.type_strs):
|
|
294
|
+
logger.info(
|
|
295
|
+
"Type constraint is tightened due to binding %s with parameter %s in node %s(%s)",
|
|
296
|
+
value.name,
|
|
297
|
+
schema.name,
|
|
298
|
+
node.op_type,
|
|
299
|
+
node.name,
|
|
300
|
+
)
|
|
301
|
+
logger.info(" %s", prev_value_constraint.type_strs)
|
|
302
|
+
logger.info("->")
|
|
303
|
+
logger.info(" %s", new_type_constraint.type_strs)
|
|
304
|
+
|
|
305
|
+
def _perform_extra_type_constraint_tightening(self, node: onnx.NodeProto):
|
|
306
|
+
if node.op_type == "If":
|
|
307
|
+
# Step into subgraph for more type constraint deduction.
|
|
308
|
+
subgraph_attr_names = ("then_branch", "else_branch")
|
|
309
|
+
for attr in node.attribute:
|
|
310
|
+
if attr.name not in subgraph_attr_names:
|
|
311
|
+
continue
|
|
312
|
+
subgraph = attr.g
|
|
313
|
+
for subgraph_node in subgraph.node:
|
|
314
|
+
self._process_node(subgraph_node)
|
|
315
|
+
for subgraph_output, node_output_name in zip(subgraph.output, node.output):
|
|
316
|
+
# Type constraint must agree between "then" and "else" branch.
|
|
317
|
+
self.values[node_output_name].merge_type_constraint(
|
|
318
|
+
self.values[subgraph_output.name]
|
|
319
|
+
)
|
|
320
|
+
elif node.op_type in ("Loop", "Scan"):
|
|
321
|
+
# Step into subgraph for more type constraint deduction.
|
|
322
|
+
raise NotImplementedError("Loop/Scan is not supported yet!")
|
|
323
|
+
elif node.op_type == "Constant":
|
|
324
|
+
# Constant type is static and can be inferred from attribute.
|
|
325
|
+
# Tighten the type constraint.
|
|
326
|
+
tensor_attr = node.attribute[0]
|
|
327
|
+
type_constraint_name = f"Constant_{node.name}"
|
|
328
|
+
if tensor_attr.type == onnx.AttributeProto.TENSOR:
|
|
329
|
+
type_constraint = TypeConstraint(
|
|
330
|
+
type_constraint_name,
|
|
331
|
+
{_ONNX_DTYPE_TO_ONNX_TENSOR_TYPE_STR[tensor_attr.t.data_type]},
|
|
332
|
+
)
|
|
333
|
+
else:
|
|
334
|
+
type_constraint = TypeConstraint(
|
|
335
|
+
type_constraint_name,
|
|
336
|
+
{_ONNX_ATTR_TYPE_TO_ONNX_TENSOR_TYPE_STR[tensor_attr.type]},
|
|
337
|
+
)
|
|
338
|
+
type_constraint.bind_value(self.values[node.output[0]])
|
|
339
|
+
elif node.op_type == "Cast":
|
|
340
|
+
to_attr = node.attribute[0]
|
|
341
|
+
if not to_attr.ref_attr_name:
|
|
342
|
+
# Cast to a static type.
|
|
343
|
+
# Tighten the type constraint
|
|
344
|
+
type_constraint = TypeConstraint(
|
|
345
|
+
f"Cast_{node.name}",
|
|
346
|
+
{_ONNX_DTYPE_TO_ONNX_TENSOR_TYPE_STR[node.attribute[0].i]},
|
|
347
|
+
)
|
|
348
|
+
type_constraint.bind_value(self.values[node.output[0]])
|
|
349
|
+
|
|
350
|
+
def _process_node(self, node: onnx.NodeProto):
|
|
351
|
+
if node.domain and node.domain != "onnx":
|
|
352
|
+
raise NotImplementedError("Nested function is not supported yet.")
|
|
353
|
+
|
|
354
|
+
if node.op_type in ("Loop", "Scan"):
|
|
355
|
+
# Step into subgraph for more type constraint deduction.
|
|
356
|
+
raise NotImplementedError("Loop/Scan is not supported yet!")
|
|
357
|
+
|
|
358
|
+
# Creating new type constraints from op schema
|
|
359
|
+
op_schema = onnx.defs.get_schema(
|
|
360
|
+
node.op_type, max_inclusive_version=self.opset_version, domain=node.domain
|
|
361
|
+
)
|
|
362
|
+
op_type_constraints = {
|
|
363
|
+
ts.type_param_str: TypeConstraint(
|
|
364
|
+
name=f"{ts.type_param_str}_{node.name}", type_strs=set(ts.allowed_type_strs)
|
|
365
|
+
)
|
|
366
|
+
for ts in op_schema.type_constraints
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
# Binding new type constraints to input values.
|
|
370
|
+
self._bind_signature(node, node.input, op_schema.inputs, op_type_constraints)
|
|
371
|
+
# Creating new values for outputs, and bind with type constraints.
|
|
372
|
+
self._bind_signature(
|
|
373
|
+
node,
|
|
374
|
+
node.output,
|
|
375
|
+
op_schema.outputs,
|
|
376
|
+
op_type_constraints,
|
|
377
|
+
is_output=True,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Postprocess a few special cases
|
|
381
|
+
self._perform_extra_type_constraint_tightening(node)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def deduce_type_constraints(
|
|
385
|
+
onnx_function: onnxscript.OnnxFunction, signature_only: bool = True
|
|
386
|
+
) -> OnnxFunctionTypeConstraints:
|
|
387
|
+
"""Deduce type constraints for an ONNX function.
|
|
388
|
+
|
|
389
|
+
* Expects Tensors to be pre-annotated as `TensorType`.
|
|
390
|
+
* Expects Attributes to be pre-annotated correctly.
|
|
391
|
+
* Produces the least strict type constraints allowed by deducing from OpSchema in FunctionProto.
|
|
392
|
+
* Produces explanation for type constraint tightening.
|
|
393
|
+
* `TracedOnnxFunction` is not planned.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
onnx_function: ONNX function to generate type constraints for.
|
|
397
|
+
signature_only: Whether to only generate type constraints for the function signature.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Type constraints for the ONNX function.
|
|
401
|
+
"""
|
|
402
|
+
logger.info("Deducing type constraints for %s", onnx_function.name)
|
|
403
|
+
return TypeConstraintDeducer(onnx_function).deduce(signature_only=signature_only)
|