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,250 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import onnx
|
|
10
|
+
from onnx import helper, numpy_helper
|
|
11
|
+
from onnx.defs import OpSchema
|
|
12
|
+
|
|
13
|
+
from onnxscript import tensor
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from onnxscript import converter
|
|
17
|
+
|
|
18
|
+
# Conversions from python values to ONNX are used by both the script converter as well
|
|
19
|
+
# as the eager-mode runtime and both need to be consistent. The script converter converts
|
|
20
|
+
# python values into ONNX TensorProto, while the runtime converts python values into
|
|
21
|
+
# ONNXScript runtime's value-representation (based on Tensor).
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Utilities to convert a python value to TensorProto (for use by the script converter)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _py_type_to_onnx_type(pytype: type):
|
|
28
|
+
if pytype is bool:
|
|
29
|
+
return onnx.TensorProto.BOOL
|
|
30
|
+
if pytype is int:
|
|
31
|
+
return onnx.TensorProto.INT64
|
|
32
|
+
if pytype is float:
|
|
33
|
+
return onnx.TensorProto.FLOAT
|
|
34
|
+
if pytype is str:
|
|
35
|
+
return onnx.TensorProto.STRING
|
|
36
|
+
raise ValueError(f"Tensor element of type {pytype} not supported")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def pyvalue_to_onnx_tensor(tensor_name: str, pyvalue):
|
|
40
|
+
if isinstance(pyvalue, np.ndarray):
|
|
41
|
+
return numpy_helper.from_array(pyvalue, tensor_name)
|
|
42
|
+
if isinstance(pyvalue, list):
|
|
43
|
+
if len(pyvalue) == 0:
|
|
44
|
+
raise ValueError("Cannot convert an empty list to tensor")
|
|
45
|
+
pytype = type(pyvalue[0])
|
|
46
|
+
if not all(isinstance(e, pytype) for e in pyvalue):
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"Cannot convert an list with elements of different types to tensor"
|
|
49
|
+
)
|
|
50
|
+
return helper.make_tensor(
|
|
51
|
+
tensor_name,
|
|
52
|
+
_py_type_to_onnx_type(pytype),
|
|
53
|
+
[len(pyvalue)],
|
|
54
|
+
pyvalue,
|
|
55
|
+
)
|
|
56
|
+
onnx_type = _py_type_to_onnx_type(type(pyvalue))
|
|
57
|
+
if onnx_type is onnx.TensorProto.BOOL:
|
|
58
|
+
return helper.make_tensor(tensor_name, onnx_type, [], [int(pyvalue)])
|
|
59
|
+
if onnx_type is onnx.TensorProto.STRING:
|
|
60
|
+
return helper.make_tensor(tensor_name, onnx_type, [], vals=[pyvalue.encode("utf-8")])
|
|
61
|
+
|
|
62
|
+
return helper.make_tensor(tensor_name, onnx_type, [], [pyvalue])
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
_REPEATED_ATTRIBUTE_TYPES = frozenset(
|
|
66
|
+
{
|
|
67
|
+
onnx.AttributeProto.FLOATS,
|
|
68
|
+
onnx.AttributeProto.INTS,
|
|
69
|
+
onnx.AttributeProto.STRINGS,
|
|
70
|
+
onnx.AttributeProto.TENSORS,
|
|
71
|
+
onnx.AttributeProto.GRAPHS,
|
|
72
|
+
onnx.AttributeProto.SPARSE_TENSORS,
|
|
73
|
+
onnx.AttributeProto.TYPE_PROTOS,
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def pyvalue_to_onnx_attribute(
|
|
79
|
+
key: str,
|
|
80
|
+
value: Any,
|
|
81
|
+
name_generator: Callable[[], str],
|
|
82
|
+
attr_type: Optional[onnx.AttributeProto.AttributeType] = None,
|
|
83
|
+
) -> onnx.AttributeProto:
|
|
84
|
+
"""Helper function to create an ONNX AttributeProto.
|
|
85
|
+
|
|
86
|
+
This is a refinement of onnx.helper.make_attribute that works with ONNX Script
|
|
87
|
+
conventions for allowed types for attribute-values. In particular, it allows
|
|
88
|
+
* Empty lists as attribute values, provided the attribute type is specified
|
|
89
|
+
and is a list type.
|
|
90
|
+
* Scalar-values like 1.0 as well as lists like [1, -1] to be specified
|
|
91
|
+
when the attribute type is TensorProto by automatically converting the value
|
|
92
|
+
into a 0-D or 1-D tensor respectively.
|
|
93
|
+
"""
|
|
94
|
+
if isinstance(value, list) and not value:
|
|
95
|
+
# Empty list value:
|
|
96
|
+
if attr_type is None:
|
|
97
|
+
raise ValueError("Attribute type must be specified for empty list value.")
|
|
98
|
+
if attr_type not in _REPEATED_ATTRIBUTE_TYPES:
|
|
99
|
+
raise ValueError("Empty list value is only allowed for repeated attribute types.")
|
|
100
|
+
return onnx.AttributeProto(name=key, type=attr_type)
|
|
101
|
+
elif attr_type == onnx.AttributeProto.TENSOR and not isinstance(value, onnx.TensorProto):
|
|
102
|
+
return onnx.AttributeProto(
|
|
103
|
+
name=key, type=attr_type, t=pyvalue_to_onnx_tensor(name_generator(), value)
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
return onnx.helper.make_attribute(key, value)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# Utilities to convert python values into onnxscript tensors.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _promotable(x) -> bool:
|
|
113
|
+
"""Checks if a runtime parameter value needs to be promoted into an onnxscript value.
|
|
114
|
+
This is the runtime-equivalent of the promotion of literal constants into ONNX values
|
|
115
|
+
in the static converter.
|
|
116
|
+
"""
|
|
117
|
+
if isinstance(x, (bool, int, float)):
|
|
118
|
+
return True
|
|
119
|
+
if isinstance(x, list) and x:
|
|
120
|
+
# Note: This is meant to handle valid scenarios correctly. No attempt is
|
|
121
|
+
# made yet to capture all invalid usages in runtime mode.
|
|
122
|
+
return _promotable(x[0])
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _get_dtype(pyvalue):
|
|
127
|
+
"""Return np.dtype to use when converting a python value to an onnxscript tensor.
|
|
128
|
+
Note that int constants are treated as int64, as that is the common type in ONNX
|
|
129
|
+
for shape/index values.
|
|
130
|
+
"""
|
|
131
|
+
if isinstance(pyvalue, bool):
|
|
132
|
+
return np.bool_
|
|
133
|
+
elif isinstance(pyvalue, int):
|
|
134
|
+
return np.int64
|
|
135
|
+
elif isinstance(pyvalue, float):
|
|
136
|
+
return np.float32
|
|
137
|
+
elif isinstance(pyvalue, list):
|
|
138
|
+
if pyvalue:
|
|
139
|
+
# TODO: What to do about lists with mixed value types, like [1, 2.0]?
|
|
140
|
+
# Should at least produce an error/warning message.
|
|
141
|
+
return _get_dtype(pyvalue[0])
|
|
142
|
+
raise ValueError("Cannot determine target type for empty list")
|
|
143
|
+
raise TypeError(f"Value of unexpected type {type(pyvalue)}")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def cast_pyvalue_to_os_tensor(pyvalue, dtype=None):
|
|
147
|
+
"""Promotes python values into onnxscript tensors.
|
|
148
|
+
The optional argument dtype specifies the desired np.dtype of the tensor,
|
|
149
|
+
used only when a non-standard onnxscript-value is promoted into one.
|
|
150
|
+
"""
|
|
151
|
+
if _promotable(pyvalue):
|
|
152
|
+
if dtype is None:
|
|
153
|
+
dtype = _get_dtype(pyvalue)
|
|
154
|
+
return tensor.Tensor(np.array(pyvalue, dtype=dtype))
|
|
155
|
+
return pyvalue
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def cast_inputs(
|
|
159
|
+
get_type_info: Callable[[Any], Any],
|
|
160
|
+
cast: Callable[[Any, Any], Any],
|
|
161
|
+
op_schema: OpSchema | None,
|
|
162
|
+
args,
|
|
163
|
+
) -> tuple[Any, ...]:
|
|
164
|
+
"""Uses schema specification to support a limited form of auto-casting.
|
|
165
|
+
|
|
166
|
+
* Scalars are promoted to tensors.
|
|
167
|
+
* Further. they are cast to the required type when used in ops with other
|
|
168
|
+
tensor inputs that are required to be of same type.
|
|
169
|
+
Thus, in "A+1" or "Add(A, 1)", the value 1 will be converted to the same
|
|
170
|
+
type as A.
|
|
171
|
+
|
|
172
|
+
This is used by the converter in a static-mode, as well as by the eager-mode
|
|
173
|
+
execution in a dynamic-mode.
|
|
174
|
+
"""
|
|
175
|
+
if op_schema is None:
|
|
176
|
+
# Either an error or a custom op.
|
|
177
|
+
# No checks/casts in this case.
|
|
178
|
+
return tuple(cast(x, None) for x in args)
|
|
179
|
+
|
|
180
|
+
expected_inputs = op_schema.inputs
|
|
181
|
+
# We make two passes. In the first pass, we identify known type-bindings for
|
|
182
|
+
# type-variables: eg., {'T1' : np.float32, 'T2' : np.int32}.
|
|
183
|
+
# In the second pass, we use these bindings to cast scalar-values to
|
|
184
|
+
# tensors of appropriate types. The two passes are needed to handle cases
|
|
185
|
+
# like "Add(1, X)" where 1 must be cast to the same type as X.
|
|
186
|
+
type_bindings: dict[Optional[str], np.dtype] = {}
|
|
187
|
+
args_typevars: list[tuple[str, Optional[str]]] = []
|
|
188
|
+
for i, x in enumerate(args):
|
|
189
|
+
if i < len(expected_inputs):
|
|
190
|
+
expected = expected_inputs[i]
|
|
191
|
+
elif expected_inputs[-1].option == OpSchema.FormalParameterOption.Variadic:
|
|
192
|
+
expected = expected_inputs[-1]
|
|
193
|
+
if not expected.is_homogeneous:
|
|
194
|
+
args_typevars.append((x, None))
|
|
195
|
+
continue
|
|
196
|
+
else:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
f"Number of actual parameters {len(args)} "
|
|
199
|
+
f"exceeds number of formal parameters {len(expected_inputs)}."
|
|
200
|
+
)
|
|
201
|
+
typevar = expected.type_str
|
|
202
|
+
if "(" not in typevar:
|
|
203
|
+
# typevar is an identifier, like "T"
|
|
204
|
+
typeinfo = get_type_info(x)
|
|
205
|
+
if typeinfo is not None:
|
|
206
|
+
type_bindings[typevar] = typeinfo
|
|
207
|
+
args_typevars.append((x, typevar))
|
|
208
|
+
cast_args = [cast(x, type_bindings.get(typevar)) for x, typevar in args_typevars]
|
|
209
|
+
return tuple(cast_args)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def dynamic_cast_inputs(op_schema: OpSchema, args):
|
|
213
|
+
"""Used for autocast during eager-mode execution."""
|
|
214
|
+
|
|
215
|
+
def get_type_info(x):
|
|
216
|
+
return x.dtype if isinstance(x, tensor.Tensor) else None
|
|
217
|
+
|
|
218
|
+
return cast_inputs(get_type_info, cast_pyvalue_to_os_tensor, op_schema, args)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def static_cast_inputs(
|
|
222
|
+
converter_: converter.Converter,
|
|
223
|
+
op_schema: Optional[OpSchema],
|
|
224
|
+
args: Sequence[Optional[converter.Variable]],
|
|
225
|
+
) -> tuple[str, ...]:
|
|
226
|
+
"""Used for autocast during script-translation.
|
|
227
|
+
This is meant to transform expressions like "Add(X, 1)" to "Add(X, CastLike(1, X))"
|
|
228
|
+
Polymorphic constants (like 0 and 1) are cast to the type of other operands as needed.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
def get_type_info(x: Optional[converter.Variable]) -> Optional[converter.Variable]:
|
|
232
|
+
"""Returns x back if x can serve as the target-type for a cast (as the second
|
|
233
|
+
argument of CastLike) and None otherwise. In the expression "Add(X, 1), 1 is
|
|
234
|
+
castable, while X can serve as the target-type.
|
|
235
|
+
"""
|
|
236
|
+
return None if x is None or x.is_castable else x
|
|
237
|
+
|
|
238
|
+
def cast_like(
|
|
239
|
+
x: Optional[converter.Variable], y: Optional[converter.Variable]
|
|
240
|
+
) -> Optional[str]:
|
|
241
|
+
if x is None:
|
|
242
|
+
return None
|
|
243
|
+
if x.is_castable and y is not None:
|
|
244
|
+
# Polymorphic constant x is cast to the type of y:
|
|
245
|
+
x_cast = converter_.generate_unique_name(f"{x.name}_cast")
|
|
246
|
+
converter_.emit([x_cast], "CastLike", [x.name, y.name])
|
|
247
|
+
return x_cast
|
|
248
|
+
return x.name
|
|
249
|
+
|
|
250
|
+
return cast_inputs(get_type_info, cast_like, op_schema, args)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Utility for deprecating APIs."""
|
|
4
|
+
|
|
5
|
+
# Reference: https://github.com/pytorch/pytorch/blob/aed9bee0413dac190452fbfa9ab2a44b6e6843f5/torch/onnx/_deprecation.py
|
|
6
|
+
|
|
7
|
+
import functools
|
|
8
|
+
import textwrap
|
|
9
|
+
import warnings
|
|
10
|
+
from typing import Callable, TypeVar
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@functools.lru_cache(maxsize=1024)
|
|
16
|
+
def _warn_once(message: str):
|
|
17
|
+
"""Issue a FutureWarning only once per message."""
|
|
18
|
+
warnings.warn(message, category=FutureWarning, stacklevel=3)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def deprecated(since: str, removed_in: str, instructions: str) -> Callable[[T], T]:
|
|
22
|
+
"""Marks functions as deprecated.
|
|
23
|
+
|
|
24
|
+
It will result in a warning when the function is called and a note in the
|
|
25
|
+
docstring.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
since: The version when the function was first deprecated.
|
|
29
|
+
removed_in: The version when the function will be removed.
|
|
30
|
+
instructions: The action users should take.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A decorator that can be used to mark functions as deprecated.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def decorator(function):
|
|
37
|
+
@functools.wraps(function)
|
|
38
|
+
def wrapper(*args, **kwargs):
|
|
39
|
+
_warn_once(
|
|
40
|
+
f"'{function.__module__}.{function.__qualname__}' "
|
|
41
|
+
f"is deprecated in version {since} and will be "
|
|
42
|
+
f"removed in {removed_in}. Please {instructions}.",
|
|
43
|
+
)
|
|
44
|
+
return function(*args, **kwargs)
|
|
45
|
+
|
|
46
|
+
# Add a deprecation note to the docstring.
|
|
47
|
+
docstring = function.__doc__ or ""
|
|
48
|
+
|
|
49
|
+
# Add a note to the docstring.
|
|
50
|
+
deprecation_note = textwrap.dedent(
|
|
51
|
+
f"""\
|
|
52
|
+
.. deprecated:: {since}
|
|
53
|
+
Deprecated and will be removed in version {removed_in}.
|
|
54
|
+
Please {instructions}.
|
|
55
|
+
"""
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Split docstring at first occurrence of newline
|
|
59
|
+
summary_and_body = docstring.split("\n\n", 1)
|
|
60
|
+
|
|
61
|
+
if len(summary_and_body) > 1:
|
|
62
|
+
summary, body = summary_and_body
|
|
63
|
+
|
|
64
|
+
# Dedent the body. We cannot do this with the presence of the summary because
|
|
65
|
+
# the body contains leading whitespaces when the summary does not.
|
|
66
|
+
body = textwrap.dedent(body)
|
|
67
|
+
|
|
68
|
+
new_docstring_parts = [deprecation_note, "\n\n", summary, body]
|
|
69
|
+
else:
|
|
70
|
+
summary = summary_and_body[0]
|
|
71
|
+
|
|
72
|
+
new_docstring_parts = [deprecation_note, "\n\n", summary]
|
|
73
|
+
|
|
74
|
+
wrapper.__doc__ = "".join(new_docstring_parts)
|
|
75
|
+
|
|
76
|
+
return wrapper
|
|
77
|
+
|
|
78
|
+
return decorator
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Function for manipulating input parameters of an Op or a OnnxFunction."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import collections
|
|
8
|
+
from typing import Any, OrderedDict, Sequence
|
|
9
|
+
|
|
10
|
+
from onnxscript import values
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def separate_input_attributes_from_arguments(
|
|
14
|
+
param_schemas: Sequence[values.ParamSchema],
|
|
15
|
+
args,
|
|
16
|
+
kwargs,
|
|
17
|
+
fill_defaults: bool = True,
|
|
18
|
+
allow_extra_kwargs: bool = False,
|
|
19
|
+
) -> tuple[list[Any], OrderedDict[str, Any]]:
|
|
20
|
+
"""Separate Python args and kwargs into ONNX inputs and attributes.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
param_schemas: The parameter schemas of an Op or a OnnxFunction.
|
|
24
|
+
args: The Python positional arguments supplied by the caller.
|
|
25
|
+
kwargs: The Python keyword arguments supplied by the caller.
|
|
26
|
+
fill_defaults: Whether to fill the default values for attributes.
|
|
27
|
+
allow_extra_kwargs: Whether to allow extra keyword arguments.
|
|
28
|
+
When set to True, extra/unknown arguments will be ignored.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
A tuple of two elements:
|
|
32
|
+
- A list of ONNX inputs.
|
|
33
|
+
- An ordered dictionary of ONNX attribute names and values.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
TypeError: When allow_extra_kwargs is False and there are unknown kwargs.
|
|
37
|
+
TypeError: When a required input is not provided.
|
|
38
|
+
"""
|
|
39
|
+
# args, kwargs and param_schemas should be all in order
|
|
40
|
+
# user may not specify all inputs or attributes
|
|
41
|
+
|
|
42
|
+
all_param_names = {param.name for param in param_schemas}
|
|
43
|
+
extra_kwargs = set(kwargs).difference(all_param_names)
|
|
44
|
+
if extra_kwargs and not allow_extra_kwargs:
|
|
45
|
+
raise TypeError(f"Unexpected keyword arguments '{extra_kwargs}'")
|
|
46
|
+
|
|
47
|
+
onnx_inputs = []
|
|
48
|
+
onnx_attributes = collections.OrderedDict()
|
|
49
|
+
|
|
50
|
+
for i, param in enumerate(param_schemas):
|
|
51
|
+
if param.is_variadic_input:
|
|
52
|
+
# Exhaust all remaining args
|
|
53
|
+
onnx_inputs.extend(args[i:])
|
|
54
|
+
args = []
|
|
55
|
+
continue
|
|
56
|
+
if i < len(args):
|
|
57
|
+
if param.is_input:
|
|
58
|
+
onnx_inputs.append(args[i])
|
|
59
|
+
else:
|
|
60
|
+
onnx_attributes[param.name] = args[i]
|
|
61
|
+
elif param.name in kwargs:
|
|
62
|
+
if param.is_input:
|
|
63
|
+
onnx_inputs.append(kwargs[param.name])
|
|
64
|
+
else:
|
|
65
|
+
onnx_attributes[param.name] = kwargs[param.name]
|
|
66
|
+
elif (
|
|
67
|
+
param.is_attribute and param.default is not values._EmptyDefault # pylint: disable=protected-access
|
|
68
|
+
):
|
|
69
|
+
# User did not provide the attribute
|
|
70
|
+
if fill_defaults:
|
|
71
|
+
onnx_attributes[param.name] = param.default
|
|
72
|
+
elif param.required:
|
|
73
|
+
raise TypeError(f"Required input '{param}' was not provided")
|
|
74
|
+
|
|
75
|
+
return onnx_inputs, onnx_attributes
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def tag_arguments_with_param_schemas(
|
|
79
|
+
param_schemas: Sequence[values.ParamSchema],
|
|
80
|
+
args,
|
|
81
|
+
kwargs,
|
|
82
|
+
fill_defaults: bool = True,
|
|
83
|
+
allow_extra_kwargs: bool = False,
|
|
84
|
+
) -> tuple[list[tuple[Any, values.ParamSchema]], dict[str, tuple[Any, values.ParamSchema]]]:
|
|
85
|
+
"""Tag Python args and kwargs with matching ONNX ParamSchema.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
param_schemas: The parameter schemas of an Op or a OnnxFunction.
|
|
89
|
+
args: The Python positional arguments supplied by the caller.
|
|
90
|
+
kwargs: The Python keyword arguments supplied by the caller.
|
|
91
|
+
fill_defaults: Whether to fill the default values for attributes.
|
|
92
|
+
allow_extra_kwargs: Whether to allow extra keyword arguments.
|
|
93
|
+
When set to True, extra/unknown arguments will be ignored.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
A tuple of two elements:
|
|
97
|
+
- A list of tuple of Python positional argument and ParamSchema.
|
|
98
|
+
- An ordered dictionary of Python keyword argument names and tuple of argument
|
|
99
|
+
value and ParamSchema.
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
TypeError: When allow_extra_kwargs is False and there are unknown kwargs.
|
|
103
|
+
TypeError: When a required input is not provided.
|
|
104
|
+
"""
|
|
105
|
+
# args, kwargs and param_schemas should be all in order
|
|
106
|
+
# user may not specify all inputs or attributes
|
|
107
|
+
|
|
108
|
+
all_param_names = {param.name for param in param_schemas}
|
|
109
|
+
extra_kwargs = set(kwargs).difference(all_param_names)
|
|
110
|
+
if extra_kwargs and not allow_extra_kwargs:
|
|
111
|
+
raise TypeError(f"Unexpected keyword arguments '{extra_kwargs}'")
|
|
112
|
+
|
|
113
|
+
tagged_args: list[tuple[Any, values.ParamSchema]] = []
|
|
114
|
+
tagged_kwargs: dict[str, tuple[Any, values.ParamSchema]] = {}
|
|
115
|
+
|
|
116
|
+
for i, param in enumerate(param_schemas):
|
|
117
|
+
if param.is_variadic_input:
|
|
118
|
+
# Exhaust all remaining args
|
|
119
|
+
tagged_args.extend((arg, param) for arg in args[i:])
|
|
120
|
+
args = []
|
|
121
|
+
continue
|
|
122
|
+
if i < len(args):
|
|
123
|
+
tagged_args.append((args[i], param))
|
|
124
|
+
elif param.name in kwargs:
|
|
125
|
+
tagged_kwargs[param.name] = (kwargs[param.name], param)
|
|
126
|
+
elif param.default is not values._EmptyDefault: # pylint: disable=protected-access
|
|
127
|
+
# User did not provide the input/attribute
|
|
128
|
+
if fill_defaults:
|
|
129
|
+
tagged_kwargs[param.name] = (param.default, param)
|
|
130
|
+
elif param.required:
|
|
131
|
+
raise TypeError(f"Required input/attribute '{param}' was not provided")
|
|
132
|
+
|
|
133
|
+
return tagged_args, tagged_kwargs
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def turn_to_kwargs_to_avoid_ordering(
|
|
137
|
+
param_schemas: Sequence[values.ParamSchema],
|
|
138
|
+
inputs: list[Any],
|
|
139
|
+
attributes: dict[str, Any],
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""Return the inputs and attributes to the order of the function signature."""
|
|
142
|
+
for idx, param in enumerate(param_schemas):
|
|
143
|
+
if param.name not in attributes:
|
|
144
|
+
if param.is_variadic_input:
|
|
145
|
+
attributes[param.name] = inputs[idx:]
|
|
146
|
+
elif inputs:
|
|
147
|
+
attributes[param.name] = inputs.pop(0)
|
|
148
|
+
return attributes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""An internal wrapper for the beartype library.
|
|
4
|
+
|
|
5
|
+
Decorate a function with `@runtime_typing.checked` to enable runtime
|
|
6
|
+
type checking. The decorator is a no-op when the `beartype` library is not
|
|
7
|
+
installed.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import typing
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"checked",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
T = typing.TypeVar("T", bound=typing.Callable[..., typing.Any])
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from beartype import beartype as _beartype_decorator
|
|
21
|
+
from beartype import roar as _roar
|
|
22
|
+
|
|
23
|
+
checked = typing.cast(typing.Callable[[T], T], _beartype_decorator)
|
|
24
|
+
|
|
25
|
+
# Beartype warns when we import from typing because the types are deprecated
|
|
26
|
+
# in Python 3.9. But there will be a long time until we can move to using
|
|
27
|
+
# the native container types for type annotations (when 3.9 is the lowest
|
|
28
|
+
# supported version). So we silence the warning.
|
|
29
|
+
warnings.filterwarnings(
|
|
30
|
+
"ignore",
|
|
31
|
+
category=_roar.BeartypeDecorHintPep585DeprecationWarning,
|
|
32
|
+
)
|
|
33
|
+
except ImportError:
|
|
34
|
+
|
|
35
|
+
def checked(func: T) -> T: # type: ignore[no-redef]
|
|
36
|
+
return func
|
|
37
|
+
|
|
38
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
39
|
+
# Warn errors that are not import errors (unexpected).
|
|
40
|
+
warnings.warn(f"{e}", stacklevel=2)
|
|
41
|
+
|
|
42
|
+
def checked(func: T) -> T: # type: ignore[no-redef]
|
|
43
|
+
return func
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numbers
|
|
6
|
+
from typing import Optional, Sequence
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import onnx
|
|
10
|
+
import onnx.helper
|
|
11
|
+
|
|
12
|
+
from onnxscript import tensor
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def external_tensor(
|
|
16
|
+
name: str,
|
|
17
|
+
data_type: int,
|
|
18
|
+
dims: Sequence[int],
|
|
19
|
+
location: str,
|
|
20
|
+
offset: Optional[int] = None,
|
|
21
|
+
length: Optional[int] = None,
|
|
22
|
+
checksum: Optional[str] = None,
|
|
23
|
+
basepath: Optional[str] = None,
|
|
24
|
+
) -> onnx.TensorProto:
|
|
25
|
+
"""Create a TensorProto referencing externally stored tensor-data.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
name: name of the tensor
|
|
29
|
+
data_type: data type of tensor element
|
|
30
|
+
dims: shape of the tensor
|
|
31
|
+
location: location of the external file (relative path)
|
|
32
|
+
offset: offset in the file where the tensor-data starts
|
|
33
|
+
length: number of bytes containing the data
|
|
34
|
+
checksum: SHA1 digest of the file
|
|
35
|
+
basepath: basepath combined with location to form the full path
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
TensorProto
|
|
39
|
+
|
|
40
|
+
See https://github.com/onnx/onnx/blob/main/docs/ExternalData.md for more details.
|
|
41
|
+
"""
|
|
42
|
+
tensor_proto = onnx.TensorProto()
|
|
43
|
+
tensor_proto.name = name
|
|
44
|
+
tensor_proto.data_type = data_type
|
|
45
|
+
tensor_proto.dims.extend(dims)
|
|
46
|
+
tensor_proto.data_location = onnx.TensorProto.EXTERNAL
|
|
47
|
+
|
|
48
|
+
def add(k, v):
|
|
49
|
+
entry = tensor_proto.external_data.add()
|
|
50
|
+
entry.key = k
|
|
51
|
+
entry.value = str(v)
|
|
52
|
+
|
|
53
|
+
add("location", location)
|
|
54
|
+
if offset is not None:
|
|
55
|
+
add("offset", int(offset))
|
|
56
|
+
if length is not None:
|
|
57
|
+
add("length", int(length))
|
|
58
|
+
if checksum is not None:
|
|
59
|
+
add("checksum", checksum)
|
|
60
|
+
if basepath is not None:
|
|
61
|
+
add("basepath", basepath)
|
|
62
|
+
return tensor_proto
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def value_to_type_proto(val):
|
|
66
|
+
"""Return the ONNX type of a python-value."""
|
|
67
|
+
if isinstance(val, (np.ndarray, tensor.Tensor)):
|
|
68
|
+
elem_type = onnx.helper.np_dtype_to_tensor_dtype(val.dtype)
|
|
69
|
+
shape = val.shape
|
|
70
|
+
return onnx.helper.make_tensor_type_proto(elem_type, shape)
|
|
71
|
+
if isinstance(val, int):
|
|
72
|
+
return onnx.helper.make_tensor_type_proto(onnx.TensorProto.INT32, [])
|
|
73
|
+
if isinstance(val, (float, np.float32)):
|
|
74
|
+
return onnx.helper.make_tensor_type_proto(onnx.TensorProto.FLOAT, [])
|
|
75
|
+
if isinstance(val, list):
|
|
76
|
+
if len(val) > 0:
|
|
77
|
+
return onnx.helper.make_sequence_type_proto(value_to_type_proto(val[0]))
|
|
78
|
+
# Edge-case. Cannot determine a suitable ONNX type for an empty list.
|
|
79
|
+
# Should be using a typed-value instead.
|
|
80
|
+
# Treated as a sequence of tensors of float-type.
|
|
81
|
+
return onnx.helper.make_sequence_type_proto(
|
|
82
|
+
onnx.helper.make_tensor_type_proto(onnx.TensorProto.FLOAT, None)
|
|
83
|
+
)
|
|
84
|
+
if isinstance(val, numbers.Number):
|
|
85
|
+
nparray = np.array(val)
|
|
86
|
+
elem_type = onnx.helper.np_dtype_to_tensor_dtype(nparray.dtype)
|
|
87
|
+
return onnx.helper.make_tensor_type_proto(elem_type, [])
|
|
88
|
+
raise ValueError(f"Value of type {type(val)} is invalid as an ONNX input/output.")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def values_to_value_infos(name_values):
|
|
92
|
+
"""Create a list of ValueInfoProto from a list of (name, value) pairs,
|
|
93
|
+
skipping any None values.
|
|
94
|
+
"""
|
|
95
|
+
return [
|
|
96
|
+
onnx.helper.make_value_info(name, value_to_type_proto(val))
|
|
97
|
+
for (name, val) in name_values
|
|
98
|
+
if val is not None
|
|
99
|
+
]
|