tico 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.
- tico/__init__.py +42 -0
- tico/config/__init__.py +4 -0
- tico/config/base.py +37 -0
- tico/config/factory.py +41 -0
- tico/config/v1.py +35 -0
- tico/experimental/__init__.py +1 -0
- tico/experimental/quantization/__init__.py +1 -0
- tico/experimental/quantization/algorithm/__init__.py +1 -0
- tico/experimental/quantization/algorithm/gptq/__init__.py +1 -0
- tico/experimental/quantization/algorithm/gptq/gptq.py +172 -0
- tico/experimental/quantization/algorithm/gptq/quant.py +153 -0
- tico/experimental/quantization/algorithm/gptq/quantizer.py +225 -0
- tico/experimental/quantization/algorithm/gptq/utils.py +65 -0
- tico/experimental/quantization/algorithm/pt2e/__init__.py +1 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/__init__.py +1 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/annotator.py +215 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/config.py +26 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/__init__.py +21 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/adaptive_avg_pool2d.py +65 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/add.py +57 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/conv2d.py +92 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/div.py +57 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/linear.py +94 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/mean.py +53 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/mul.py +57 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/relu6.py +53 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/rsqrt.py +53 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/op/sub.py +57 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/spec.py +47 -0
- tico/experimental/quantization/algorithm/pt2e/annotation/utils.py +88 -0
- tico/experimental/quantization/algorithm/pt2e/quantizer.py +78 -0
- tico/experimental/quantization/algorithm/pt2e/transformation/__init__.py +1 -0
- tico/experimental/quantization/algorithm/pt2e/transformation/convert_scalars_to_attrs.py +58 -0
- tico/experimental/quantization/algorithm/pt2e/utils.py +138 -0
- tico/experimental/quantization/algorithm/smoothquant/__init__.py +1 -0
- tico/experimental/quantization/algorithm/smoothquant/observer.py +78 -0
- tico/experimental/quantization/algorithm/smoothquant/quantizer.py +81 -0
- tico/experimental/quantization/algorithm/smoothquant/smooth_quant.py +164 -0
- tico/experimental/quantization/config.py +68 -0
- tico/experimental/quantization/evaluation/__init__.py +1 -0
- tico/experimental/quantization/evaluation/backend.py +20 -0
- tico/experimental/quantization/evaluation/evaluate.py +223 -0
- tico/experimental/quantization/evaluation/executor/__init__.py +1 -0
- tico/experimental/quantization/evaluation/executor/backend_executor.py +54 -0
- tico/experimental/quantization/evaluation/executor/circle_executor.py +75 -0
- tico/experimental/quantization/evaluation/executor/triv24_executor.py +128 -0
- tico/experimental/quantization/evaluation/metric.py +109 -0
- tico/experimental/quantization/evaluation/utils.py +185 -0
- tico/experimental/quantization/passes/__init__.py +1 -0
- tico/experimental/quantization/passes/fold_quant_ops.py +154 -0
- tico/experimental/quantization/passes/insert_quantize_on_dtype_mismatch.py +345 -0
- tico/experimental/quantization/passes/propagate_qparam_backward.py +91 -0
- tico/experimental/quantization/passes/propagate_qparam_forward.py +141 -0
- tico/experimental/quantization/passes/quantize_bias.py +123 -0
- tico/experimental/quantization/passes/remove_weight_dequant_op.py +177 -0
- tico/experimental/quantization/public_interface.py +108 -0
- tico/experimental/quantization/quantizer.py +71 -0
- tico/interpreter/__init__.py +1 -0
- tico/interpreter/infer.py +116 -0
- tico/interpreter/interpreter.py +93 -0
- tico/passes/__init__.py +1 -0
- tico/passes/cast_aten_where_arg_type.py +191 -0
- tico/passes/cast_mixed_type_args.py +187 -0
- tico/passes/const_prop_pass.py +307 -0
- tico/passes/convert_conv1d_to_conv2d.py +160 -0
- tico/passes/convert_layout_op_to_reshape.py +85 -0
- tico/passes/convert_repeat_to_expand_copy.py +89 -0
- tico/passes/convert_to_relu6.py +181 -0
- tico/passes/decompose_addmm.py +124 -0
- tico/passes/decompose_batch_norm.py +192 -0
- tico/passes/decompose_fake_quantize.py +134 -0
- tico/passes/decompose_fake_quantize_tensor_qparams.py +294 -0
- tico/passes/decompose_group_norm.py +275 -0
- tico/passes/decompose_grouped_conv2d.py +209 -0
- tico/passes/decompose_slice_scatter.py +169 -0
- tico/passes/extract_dtype_kwargs.py +122 -0
- tico/passes/fill_meta_val.py +57 -0
- tico/passes/fuse_leading_unsqueeze_reshape.py +112 -0
- tico/passes/fuse_redundant_reshape_to_mean.py +102 -0
- tico/passes/legalize_causal_mask_value.py +108 -0
- tico/passes/legalize_predefined_layout_operators.py +386 -0
- tico/passes/lower_pow2_to_mul.py +75 -0
- tico/passes/lower_to_resize_nearest_neighbor.py +235 -0
- tico/passes/lower_to_slice.py +230 -0
- tico/passes/merge_consecutive_cat.py +80 -0
- tico/passes/ops.py +78 -0
- tico/passes/remove_nop.py +84 -0
- tico/passes/remove_redundant_assert_nodes.py +51 -0
- tico/passes/remove_redundant_expand.py +66 -0
- tico/passes/remove_redundant_permute.py +122 -0
- tico/passes/remove_redundant_reshape.py +436 -0
- tico/passes/remove_redundant_slice.py +62 -0
- tico/passes/remove_redundant_to_copy.py +86 -0
- tico/passes/restore_linear.py +115 -0
- tico/passes/segment_index_select.py +145 -0
- tico/pt2_to_circle.py +105 -0
- tico/serialize/__init__.py +1 -0
- tico/serialize/circle_graph.py +319 -0
- tico/serialize/circle_mapping.py +177 -0
- tico/serialize/circle_serializer.py +240 -0
- tico/serialize/operators/__init__.py +28 -0
- tico/serialize/operators/hashable_opcode.py +43 -0
- tico/serialize/operators/node_visitor.py +80 -0
- tico/serialize/operators/op_abs.py +53 -0
- tico/serialize/operators/op_add.py +69 -0
- tico/serialize/operators/op_alias_copy.py +64 -0
- tico/serialize/operators/op_any.py +150 -0
- tico/serialize/operators/op_arange_start_step.py +61 -0
- tico/serialize/operators/op_argmax.py +62 -0
- tico/serialize/operators/op_avg_pool2d.py +192 -0
- tico/serialize/operators/op_bmm.py +62 -0
- tico/serialize/operators/op_cat.py +66 -0
- tico/serialize/operators/op_clamp.py +126 -0
- tico/serialize/operators/op_clone.py +71 -0
- tico/serialize/operators/op_constant_pad_nd.py +72 -0
- tico/serialize/operators/op_conv2d.py +186 -0
- tico/serialize/operators/op_copy.py +164 -0
- tico/serialize/operators/op_cos.py +59 -0
- tico/serialize/operators/op_cumsum.py +95 -0
- tico/serialize/operators/op_depthwise_conv2d.py +199 -0
- tico/serialize/operators/op_dequantize_per_channel.py +82 -0
- tico/serialize/operators/op_dequantize_per_tensor.py +64 -0
- tico/serialize/operators/op_div.py +62 -0
- tico/serialize/operators/op_embedding.py +60 -0
- tico/serialize/operators/op_eq.py +64 -0
- tico/serialize/operators/op_exp.py +60 -0
- tico/serialize/operators/op_expand.py +91 -0
- tico/serialize/operators/op_full.py +48 -0
- tico/serialize/operators/op_full_like.py +55 -0
- tico/serialize/operators/op_ge.py +54 -0
- tico/serialize/operators/op_gelu.py +59 -0
- tico/serialize/operators/op_gt.py +54 -0
- tico/serialize/operators/op_index.py +82 -0
- tico/serialize/operators/op_index_select.py +64 -0
- tico/serialize/operators/op_instance_norm.py +91 -0
- tico/serialize/operators/op_leaky_relu.py +60 -0
- tico/serialize/operators/op_linear.py +70 -0
- tico/serialize/operators/op_log.py +53 -0
- tico/serialize/operators/op_log1p.py +86 -0
- tico/serialize/operators/op_logical_and.py +63 -0
- tico/serialize/operators/op_logical_not.py +62 -0
- tico/serialize/operators/op_lt.py +61 -0
- tico/serialize/operators/op_max_dim.py +70 -0
- tico/serialize/operators/op_max_pool2d_with_indices.py +155 -0
- tico/serialize/operators/op_maximum.py +53 -0
- tico/serialize/operators/op_mean.py +66 -0
- tico/serialize/operators/op_minimum.py +53 -0
- tico/serialize/operators/op_mm.py +177 -0
- tico/serialize/operators/op_mul.py +99 -0
- tico/serialize/operators/op_ne.py +54 -0
- tico/serialize/operators/op_neg.py +59 -0
- tico/serialize/operators/op_permute.py +65 -0
- tico/serialize/operators/op_pow.py +141 -0
- tico/serialize/operators/op_prelu.py +54 -0
- tico/serialize/operators/op_quantize_per_tensor.py +79 -0
- tico/serialize/operators/op_reciprocal.py +64 -0
- tico/serialize/operators/op_relu.py +53 -0
- tico/serialize/operators/op_relu6.py +52 -0
- tico/serialize/operators/op_repeat.py +100 -0
- tico/serialize/operators/op_reshape.py +73 -0
- tico/serialize/operators/op_resize_nearest_neighbor.py +70 -0
- tico/serialize/operators/op_rsqrt.py +53 -0
- tico/serialize/operators/op_scalar_tensor.py +51 -0
- tico/serialize/operators/op_select_copy.py +65 -0
- tico/serialize/operators/op_sigmoid.py +56 -0
- tico/serialize/operators/op_sin.py +53 -0
- tico/serialize/operators/op_slice.py +155 -0
- tico/serialize/operators/op_softmax.py +100 -0
- tico/serialize/operators/op_split_with_sizes.py +99 -0
- tico/serialize/operators/op_sqrt.py +55 -0
- tico/serialize/operators/op_squeeze.py +73 -0
- tico/serialize/operators/op_sub.py +71 -0
- tico/serialize/operators/op_sum.py +63 -0
- tico/serialize/operators/op_tanh.py +54 -0
- tico/serialize/operators/op_to_copy.py +105 -0
- tico/serialize/operators/op_unsqueeze.py +66 -0
- tico/serialize/operators/op_view.py +74 -0
- tico/serialize/operators/op_where.py +82 -0
- tico/serialize/operators/utils.py +94 -0
- tico/serialize/pack.py +35 -0
- tico/serialize/quant_param.py +42 -0
- tico/utils/__init__.py +1 -0
- tico/utils/convert.py +296 -0
- tico/utils/define.py +35 -0
- tico/utils/diff_graph.py +181 -0
- tico/utils/errors.py +35 -0
- tico/utils/graph.py +282 -0
- tico/utils/logging.py +45 -0
- tico/utils/model.py +37 -0
- tico/utils/mx/__init__.py +1 -0
- tico/utils/mx/elemwise_ops.py +267 -0
- tico/utils/mx/formats.py +125 -0
- tico/utils/mx/mx_ops.py +270 -0
- tico/utils/padding.py +47 -0
- tico/utils/passes.py +76 -0
- tico/utils/register_custom_op.py +609 -0
- tico/utils/serialize.py +42 -0
- tico/utils/trace_decorators.py +101 -0
- tico/utils/utils.py +406 -0
- tico/utils/validate_args_kwargs.py +1149 -0
- tico-0.1.0.dist-info/LICENSE +241 -0
- tico-0.1.0.dist-info/METADATA +354 -0
- tico-0.1.0.dist-info/RECORD +206 -0
- tico-0.1.0.dist-info/WHEEL +5 -0
- tico-0.1.0.dist-info/entry_points.txt +3 -0
- tico-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
from torch.ao.quantization.quantizer import DerivedQuantizationSpec
|
21
|
+
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
24
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
25
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
26
|
+
QuantizationConfig,
|
27
|
+
)
|
28
|
+
from tico.utils.validate_args_kwargs import Conv2DArgs
|
29
|
+
|
30
|
+
|
31
|
+
@annot_spec.register_annotator(
|
32
|
+
[torch.ops.aten.conv2d.default, torch.ops.aten.conv2d.padding]
|
33
|
+
)
|
34
|
+
def _annotate_conv2d(
|
35
|
+
gm: torch.fx.GraphModule,
|
36
|
+
node: torch.fx.Node,
|
37
|
+
quantization_config: Optional[QuantizationConfig],
|
38
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
39
|
+
):
|
40
|
+
for node in gm.graph.nodes:
|
41
|
+
if node.op != "call_function" or node.target not in [
|
42
|
+
torch.ops.aten.conv2d.default,
|
43
|
+
torch.ops.aten.conv2d.padding,
|
44
|
+
]:
|
45
|
+
continue
|
46
|
+
if filter_fn and not filter_fn(node):
|
47
|
+
continue
|
48
|
+
if quant_utils.is_annotated(node):
|
49
|
+
continue
|
50
|
+
|
51
|
+
args = Conv2DArgs(*node.args, **node.kwargs) # type: ignore[arg-type]
|
52
|
+
input_ = args.input
|
53
|
+
weight = args.weight
|
54
|
+
bias = args.bias
|
55
|
+
|
56
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
57
|
+
weight_qspec = quant_utils.get_weight_qspec(quantization_config)
|
58
|
+
annot_utils.annotate_input_qspec_map(node, input_, input_act_qspec)
|
59
|
+
annot_utils.annotate_input_qspec_map(node, weight, weight_qspec)
|
60
|
+
nodes_to_mark_annotated = [input_, weight, node]
|
61
|
+
if bias:
|
62
|
+
|
63
|
+
def _derive_bias_qparams_from_act_and_weight_qparams(obs_or_fqs):
|
64
|
+
act_scale, _ = obs_or_fqs[0].calculate_qparams()
|
65
|
+
weight_scale, _ = obs_or_fqs[1].calculate_qparams()
|
66
|
+
bias_scale = act_scale * weight_scale
|
67
|
+
bias_zero_point = torch.zeros_like(bias_scale, dtype=torch.int32)
|
68
|
+
return bias_scale, bias_zero_point
|
69
|
+
|
70
|
+
bias_qspec = DerivedQuantizationSpec(
|
71
|
+
derived_from=[
|
72
|
+
(input_, node),
|
73
|
+
(weight, node),
|
74
|
+
],
|
75
|
+
derive_qparams_fn=_derive_bias_qparams_from_act_and_weight_qparams,
|
76
|
+
dtype=torch.int32,
|
77
|
+
quant_min=-(2**31),
|
78
|
+
quant_max=2**31 - 1,
|
79
|
+
qscheme=weight_qspec.qscheme,
|
80
|
+
ch_axis=0 if weight_qspec.qscheme == torch.per_channel_affine else None,
|
81
|
+
)
|
82
|
+
annot_utils.annotate_input_qspec_map(
|
83
|
+
node,
|
84
|
+
bias,
|
85
|
+
bias_qspec,
|
86
|
+
)
|
87
|
+
nodes_to_mark_annotated.append(bias)
|
88
|
+
|
89
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
90
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
91
|
+
|
92
|
+
annot_utils.mark_nodes_as_annotated(nodes_to_mark_annotated)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
24
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
25
|
+
QuantizationConfig,
|
26
|
+
)
|
27
|
+
from tico.utils.validate_args_kwargs import DivTensorArgs
|
28
|
+
|
29
|
+
|
30
|
+
@annot_spec.register_annotator([torch.ops.aten.div.Tensor])
|
31
|
+
def _annotate_div(
|
32
|
+
gm: torch.fx.GraphModule,
|
33
|
+
node: torch.fx.Node,
|
34
|
+
quantization_config: Optional[QuantizationConfig],
|
35
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
36
|
+
):
|
37
|
+
if node.op != "call_function" or node.target != torch.ops.aten.div.Tensor:
|
38
|
+
return
|
39
|
+
if filter_fn and not filter_fn(node):
|
40
|
+
return
|
41
|
+
if quant_utils.is_annotated(node):
|
42
|
+
return
|
43
|
+
|
44
|
+
args = DivTensorArgs(*node.args) # type: ignore[arg-type]
|
45
|
+
input = args.input
|
46
|
+
other = args.other
|
47
|
+
|
48
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
49
|
+
if isinstance(input, torch.fx.Node):
|
50
|
+
annot_utils.annotate_input_qspec_map(node, input, input_act_qspec)
|
51
|
+
if isinstance(other, torch.fx.Node):
|
52
|
+
annot_utils.annotate_input_qspec_map(node, other, input_act_qspec)
|
53
|
+
|
54
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
55
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
56
|
+
|
57
|
+
annot_utils.mark_nodes_as_annotated(node)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, List, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
from torch.ao.quantization.quantizer import DerivedQuantizationSpec
|
21
|
+
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
24
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
25
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
26
|
+
QuantizationConfig,
|
27
|
+
)
|
28
|
+
from tico.utils.validate_args_kwargs import LinearArgs
|
29
|
+
|
30
|
+
|
31
|
+
@annot_spec.register_annotator([torch.ops.aten.linear.default])
|
32
|
+
def _annotate_linear(
|
33
|
+
gm: torch.fx.GraphModule,
|
34
|
+
node: torch.fx.Node,
|
35
|
+
quantization_config: Optional[QuantizationConfig],
|
36
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
37
|
+
):
|
38
|
+
if node.op != "call_function" or node.target != torch.ops.aten.linear.default:
|
39
|
+
return
|
40
|
+
if filter_fn and not filter_fn(node):
|
41
|
+
return
|
42
|
+
if quant_utils.is_annotated(node):
|
43
|
+
return
|
44
|
+
|
45
|
+
args = LinearArgs(*node.args, **node.kwargs) # type: ignore[arg-type]
|
46
|
+
input_ = args.input
|
47
|
+
weight = args.weight
|
48
|
+
bias = args.bias
|
49
|
+
|
50
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
51
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
52
|
+
weight_qspec = quant_utils.get_weight_qspec(quantization_config)
|
53
|
+
bias_qspec = quant_utils.get_bias_qspec(quantization_config)
|
54
|
+
|
55
|
+
annot_utils.annotate_input_qspec_map(
|
56
|
+
node,
|
57
|
+
input_,
|
58
|
+
input_act_qspec,
|
59
|
+
)
|
60
|
+
annot_utils.annotate_input_qspec_map(
|
61
|
+
node,
|
62
|
+
weight,
|
63
|
+
weight_qspec,
|
64
|
+
)
|
65
|
+
nodes_to_mark_annotated = [node, weight]
|
66
|
+
if bias:
|
67
|
+
|
68
|
+
def _derive_bias_qparams_from_act_and_weight_qparams(obs_or_fqs):
|
69
|
+
act_scale, _ = obs_or_fqs[0].calculate_qparams()
|
70
|
+
weight_scale, _ = obs_or_fqs[1].calculate_qparams()
|
71
|
+
bias_scale = act_scale * weight_scale
|
72
|
+
bias_zero_point = torch.zeros_like(bias_scale, dtype=torch.int32)
|
73
|
+
return bias_scale, bias_zero_point
|
74
|
+
|
75
|
+
bias_qspec = DerivedQuantizationSpec(
|
76
|
+
derived_from=[
|
77
|
+
(input_, node),
|
78
|
+
(weight, node),
|
79
|
+
],
|
80
|
+
derive_qparams_fn=_derive_bias_qparams_from_act_and_weight_qparams,
|
81
|
+
dtype=torch.int32,
|
82
|
+
quant_min=-(2**31),
|
83
|
+
quant_max=2**31 - 1,
|
84
|
+
qscheme=weight_qspec.qscheme,
|
85
|
+
ch_axis=0 if weight_qspec.qscheme == torch.per_channel_affine else None,
|
86
|
+
)
|
87
|
+
annot_utils.annotate_input_qspec_map(
|
88
|
+
node,
|
89
|
+
bias,
|
90
|
+
bias_qspec,
|
91
|
+
)
|
92
|
+
nodes_to_mark_annotated.append(bias)
|
93
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
94
|
+
annot_utils.mark_nodes_as_annotated(nodes_to_mark_annotated)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
24
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
25
|
+
QuantizationConfig,
|
26
|
+
)
|
27
|
+
from tico.utils.validate_args_kwargs import MeanDimArgs
|
28
|
+
|
29
|
+
|
30
|
+
@annot_spec.register_annotator([torch.ops.aten.mean.dim])
|
31
|
+
def _annotate_mean(
|
32
|
+
gm: torch.fx.GraphModule,
|
33
|
+
node: torch.fx.Node,
|
34
|
+
quantization_config: Optional[QuantizationConfig],
|
35
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
36
|
+
):
|
37
|
+
if node.op != "call_function" or node.target != torch.ops.aten.mean.dim:
|
38
|
+
return
|
39
|
+
if filter_fn and not filter_fn(node):
|
40
|
+
return
|
41
|
+
if quant_utils.is_annotated(node):
|
42
|
+
return
|
43
|
+
|
44
|
+
args = MeanDimArgs(*node.args, **node.kwargs) # type: ignore[arg-type]
|
45
|
+
input = args.input
|
46
|
+
|
47
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
48
|
+
annot_utils.annotate_input_qspec_map(node, input, input_act_qspec)
|
49
|
+
|
50
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
51
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
52
|
+
|
53
|
+
annot_utils.mark_nodes_as_annotated(node)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
24
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
25
|
+
QuantizationConfig,
|
26
|
+
)
|
27
|
+
from tico.utils.validate_args_kwargs import MulTensorArgs
|
28
|
+
|
29
|
+
|
30
|
+
@annot_spec.register_annotator([torch.ops.aten.mul.Tensor])
|
31
|
+
def _annotate_mul(
|
32
|
+
gm: torch.fx.GraphModule,
|
33
|
+
node: torch.fx.Node,
|
34
|
+
quantization_config: Optional[QuantizationConfig],
|
35
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
36
|
+
):
|
37
|
+
if node.op != "call_function" or node.target != torch.ops.aten.mul.Tensor:
|
38
|
+
return
|
39
|
+
if filter_fn and not filter_fn(node):
|
40
|
+
return
|
41
|
+
if quant_utils.is_annotated(node):
|
42
|
+
return
|
43
|
+
|
44
|
+
args = MulTensorArgs(*node.args, **node.kwargs) # type: ignore[arg-type]
|
45
|
+
input = args.input
|
46
|
+
other = args.other
|
47
|
+
|
48
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
49
|
+
if isinstance(input, torch.fx.Node):
|
50
|
+
annot_utils.annotate_input_qspec_map(node, input, input_act_qspec)
|
51
|
+
if isinstance(other, torch.fx.Node):
|
52
|
+
annot_utils.annotate_input_qspec_map(node, other, input_act_qspec)
|
53
|
+
|
54
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
55
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
56
|
+
|
57
|
+
annot_utils.mark_nodes_as_annotated(node)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
24
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
25
|
+
QuantizationConfig,
|
26
|
+
)
|
27
|
+
from tico.utils.validate_args_kwargs import Relu6Args
|
28
|
+
|
29
|
+
|
30
|
+
@annot_spec.register_annotator([torch.ops.aten.relu6.default])
|
31
|
+
def _annotate_relu6(
|
32
|
+
gm: torch.fx.GraphModule,
|
33
|
+
node: torch.fx.Node,
|
34
|
+
quantization_config: Optional[QuantizationConfig],
|
35
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
36
|
+
):
|
37
|
+
if node.op != "call_function" or node.target != torch.ops.aten.relu6.default:
|
38
|
+
return
|
39
|
+
if filter_fn and not filter_fn(node):
|
40
|
+
return
|
41
|
+
if quant_utils.is_annotated(node):
|
42
|
+
return
|
43
|
+
|
44
|
+
args = Relu6Args(*node.args, **node.kwargs) # type: ignore
|
45
|
+
input = args.input
|
46
|
+
|
47
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
48
|
+
annot_utils.annotate_input_qspec_map(node, input, input_act_qspec)
|
49
|
+
|
50
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
51
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
52
|
+
|
53
|
+
annot_utils.mark_nodes_as_annotated(node)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
24
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
25
|
+
QuantizationConfig,
|
26
|
+
)
|
27
|
+
from tico.utils.validate_args_kwargs import RsqrtArgs
|
28
|
+
|
29
|
+
|
30
|
+
@annot_spec.register_annotator([torch.ops.aten.rsqrt.default])
|
31
|
+
def _annotate_rsqrt(
|
32
|
+
gm: torch.fx.GraphModule,
|
33
|
+
node: torch.fx.Node,
|
34
|
+
quantization_config: Optional[QuantizationConfig],
|
35
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
36
|
+
):
|
37
|
+
if node.op != "call_function" or node.target != torch.ops.aten.rsqrt.default:
|
38
|
+
return
|
39
|
+
if filter_fn and not filter_fn(node):
|
40
|
+
return
|
41
|
+
if quant_utils.is_annotated(node):
|
42
|
+
return
|
43
|
+
|
44
|
+
args = RsqrtArgs(*node.args, **node.kwargs) # type: ignore[arg-type]
|
45
|
+
input = args.input
|
46
|
+
|
47
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
48
|
+
annot_utils.annotate_input_qspec_map(node, input, input_act_qspec)
|
49
|
+
|
50
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
51
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
52
|
+
|
53
|
+
annot_utils.mark_nodes_as_annotated(node)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
22
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.utils as annot_utils
|
23
|
+
import tico.experimental.quantization.algorithm.pt2e.utils as quant_utils
|
24
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
25
|
+
QuantizationConfig,
|
26
|
+
)
|
27
|
+
from tico.utils.validate_args_kwargs import SubTensorArgs
|
28
|
+
|
29
|
+
|
30
|
+
@annot_spec.register_annotator([torch.ops.aten.sub.Tensor])
|
31
|
+
def _annotate_sub(
|
32
|
+
gm: torch.fx.GraphModule,
|
33
|
+
node: torch.fx.Node,
|
34
|
+
quantization_config: Optional[QuantizationConfig],
|
35
|
+
filter_fn: Optional[Callable[[torch.fx.Node], bool]] = None,
|
36
|
+
):
|
37
|
+
if node.op != "call_function" or node.target != torch.ops.aten.sub.Tensor:
|
38
|
+
return
|
39
|
+
if filter_fn and not filter_fn(node):
|
40
|
+
return
|
41
|
+
if quant_utils.is_annotated(node):
|
42
|
+
return
|
43
|
+
|
44
|
+
args = SubTensorArgs(*node.args) # type: ignore[arg-type]
|
45
|
+
input = args.input
|
46
|
+
other = args.other
|
47
|
+
|
48
|
+
input_act_qspec = quant_utils.get_input_act_qspec(quantization_config)
|
49
|
+
if isinstance(input, torch.fx.Node):
|
50
|
+
annot_utils.annotate_input_qspec_map(node, input, input_act_qspec)
|
51
|
+
if isinstance(other, torch.fx.Node):
|
52
|
+
annot_utils.annotate_input_qspec_map(node, other, input_act_qspec)
|
53
|
+
|
54
|
+
output_act_qspec = quant_utils.get_output_act_qspec(quantization_config)
|
55
|
+
annot_utils.annotate_output_qspec(node, output_act_qspec)
|
56
|
+
|
57
|
+
annot_utils.mark_nodes_as_annotated(node)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Callable, Dict, List, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
|
21
|
+
from tico.experimental.quantization.algorithm.pt2e.annotation.config import (
|
22
|
+
QuantizationConfig,
|
23
|
+
)
|
24
|
+
|
25
|
+
AnnotatorType = Callable[
|
26
|
+
[
|
27
|
+
torch.fx.GraphModule,
|
28
|
+
torch.fx.Node,
|
29
|
+
Optional[QuantizationConfig],
|
30
|
+
Optional[Callable[[torch.fx.Node], bool]],
|
31
|
+
],
|
32
|
+
None,
|
33
|
+
]
|
34
|
+
OP_TO_ANNOTATOR: Dict[torch._ops.OpOverload, AnnotatorType] = {}
|
35
|
+
OP_TO_SHARE_QUANT_SPEC: List[Callable] = [
|
36
|
+
torch.ops.aten.view_copy.default,
|
37
|
+
torch.ops.aten.view.default,
|
38
|
+
]
|
39
|
+
|
40
|
+
|
41
|
+
def register_annotator(target: List[torch._ops.OpOverload]):
|
42
|
+
def decorator(annotator: AnnotatorType):
|
43
|
+
for t in target:
|
44
|
+
OP_TO_ANNOTATOR[t] = annotator
|
45
|
+
return annotator
|
46
|
+
|
47
|
+
return decorator
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import List, Optional, TYPE_CHECKING
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import torch.fx
|
19
|
+
import torch
|
20
|
+
from torch.ao.quantization.quantizer import (
|
21
|
+
QuantizationAnnotation,
|
22
|
+
SharedQuantizationSpec,
|
23
|
+
)
|
24
|
+
|
25
|
+
import tico.experimental.quantization.algorithm.pt2e.annotation.spec as annot_spec
|
26
|
+
|
27
|
+
|
28
|
+
def annotate_input_qspec_map(node: torch.fx.Node, input_node: torch.fx.Node, qspec):
|
29
|
+
quantization_annotation: QuantizationAnnotation = node.meta.get(
|
30
|
+
"quantization_annotation", QuantizationAnnotation()
|
31
|
+
)
|
32
|
+
quantization_annotation.input_qspec_map[input_node] = qspec
|
33
|
+
node.meta["quantization_annotation"] = quantization_annotation
|
34
|
+
|
35
|
+
|
36
|
+
def annotate_output_qspec(node: torch.fx.Node, qspec):
|
37
|
+
quantization_annotation: QuantizationAnnotation = node.meta.get(
|
38
|
+
"quantization_annotation", QuantizationAnnotation()
|
39
|
+
)
|
40
|
+
quantization_annotation.output_qspec = qspec
|
41
|
+
node.meta["quantization_annotation"] = quantization_annotation
|
42
|
+
|
43
|
+
|
44
|
+
def mark_nodes_as_annotated(nodes: List[torch.fx.Node] | torch.fx.Node):
|
45
|
+
if isinstance(nodes, torch.fx.Node):
|
46
|
+
nodes = [nodes]
|
47
|
+
for node in nodes:
|
48
|
+
if node is not None:
|
49
|
+
if "quantization_annotation" not in node.meta:
|
50
|
+
node.meta["quantization_annotation"] = QuantizationAnnotation()
|
51
|
+
node.meta["quantization_annotation"]._annotated = True
|
52
|
+
|
53
|
+
|
54
|
+
def propagate_annotation_forward(model: torch.fx.GraphModule) -> None:
|
55
|
+
for n in model.graph.nodes:
|
56
|
+
if n.op != "call_function" or n.target not in annot_spec.OP_TO_SHARE_QUANT_SPEC:
|
57
|
+
continue
|
58
|
+
|
59
|
+
prev_node = n.args[0]
|
60
|
+
if not isinstance(prev_node, torch.fx.Node):
|
61
|
+
continue
|
62
|
+
|
63
|
+
quantization_annotation: Optional[QuantizationAnnotation] = prev_node.meta.get(
|
64
|
+
"quantization_annotation", None
|
65
|
+
)
|
66
|
+
if not quantization_annotation:
|
67
|
+
continue
|
68
|
+
|
69
|
+
output_qspec = quantization_annotation.output_qspec
|
70
|
+
if not output_qspec:
|
71
|
+
continue
|
72
|
+
|
73
|
+
# Make sure current node is not annotated
|
74
|
+
if (
|
75
|
+
"quantization_annotation" in n.meta
|
76
|
+
and n.meta["quantization_annotation"]._annotated
|
77
|
+
):
|
78
|
+
continue
|
79
|
+
|
80
|
+
shared_qspec = SharedQuantizationSpec(prev_node)
|
81
|
+
# Propagate the previous output_qspec to the current node
|
82
|
+
n.meta["quantization_annotation"] = QuantizationAnnotation(
|
83
|
+
input_qspec_map={
|
84
|
+
prev_node: shared_qspec,
|
85
|
+
},
|
86
|
+
output_qspec=shared_qspec,
|
87
|
+
_annotated=True,
|
88
|
+
)
|