Skip to content

Commit 7bb34e1

Browse files
authored
Reland "Arm backend: Make composable_quantizer default"
Differential Revision: D112610852 Pull Request resolved: #21018
1 parent abdc92d commit 7bb34e1

13 files changed

Lines changed: 222 additions & 108 deletions

backends/arm/_passes/normalize_while_initial_args_pass.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _connect_to_output(
6262
(placeholder,),
6363
)
6464
cloned_placeholders.append(clone)
65-
clone.meta = placeholder.meta
65+
clone.meta = placeholder.meta.copy()
6666
output_node = body_module.graph.output_node()
6767
output_values = output_node.args[0]
6868
if not isinstance(output_values, tuple):

backends/arm/public_api_manifests/api_manifest_running.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ signature = "EthosUPartitioner.register_custom_partition_op(self, op: torch._ops
6262

6363
[python.EthosUQuantizer]
6464
kind = "class"
65-
signature = "EthosUQuantizer(compile_spec: 'EthosUCompileSpec', use_composable_quantizer: 'bool' = False) -> 'None'"
65+
signature = "EthosUQuantizer(compile_spec: 'EthosUCompileSpec', use_composable_quantizer: 'bool' = True) -> 'None'"
6666

6767
[python.EthosUQuantizer.annotate]
6868
kind = "function"
@@ -150,7 +150,7 @@ signature = "VgfPartitioner.register_custom_partition_op(self, op: torch._ops.Op
150150

151151
[python.VgfQuantizer]
152152
kind = "class"
153-
signature = "VgfQuantizer(compile_spec: 'VgfCompileSpec', use_composable_quantizer: 'bool' = False) -> 'None'"
153+
signature = "VgfQuantizer(compile_spec: 'VgfCompileSpec', use_composable_quantizer: 'bool' = True) -> 'None'"
154154

155155
[python.VgfQuantizer.annotate]
156156
kind = "function"

backends/arm/quantizer/arm_quantizer.py

Lines changed: 110 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -493,21 +493,23 @@ class TOSAQuantizer(Quantizer):
493493
"""Manage quantization annotations for TOSA-compatible backends.
494494
495495
.. warning::
496-
Setting ``use_composable_quantizer=True`` enables an experimental API
497-
surface that may change without notice.
496+
The composable quantizer is now the default implementation. Setting
497+
``use_composable_quantizer=False`` is deprecated and will be removed in
498+
two minor releases.
498499
499500
"""
500501

501502
def __init__(
502503
self,
503504
compile_spec_or_tosa_spec,
504-
use_composable_quantizer: bool = False,
505+
use_composable_quantizer: bool = True,
505506
) -> None:
506507
"""Create a TOSA quantizer from a TOSA spec or Arm compile spec.
507508
508509
.. warning::
509-
Setting ``use_composable_quantizer=True`` enables an experimental
510-
API surface that may change without notice.
510+
The composable quantizer is now the default implementation.
511+
Setting ``use_composable_quantizer=False`` is deprecated and will
512+
be removed in two minor releases.
511513
512514
"""
513515
self.use_composable_quantizer = use_composable_quantizer
@@ -519,10 +521,45 @@ def __init__(
519521
self.quantizer = _TOSAQuantizerV2(compile_spec_or_tosa_spec)
520522
else:
521523
logger.info(
522-
"Using default quantizer in the arm backend. This quantizer is planned to be replaced by the composable quantizer implementation in the future, see https://github.com/pytorch/executorch/issues/17701"
524+
"Using deprecated legacy quantizer implementation in the arm backend. Setting use_composable_quantizer=False will be removed in two minor releases. See https://github.com/pytorch/executorch/issues/17701"
523525
)
524526
self.quantizer = _TOSAQuantizerV1(compile_spec_or_tosa_spec)
525527

528+
@staticmethod
529+
def _validate_optional_quantization_config(
530+
config_name: str, value: object, value_description: str = "value"
531+
) -> None:
532+
if value is not None and not isinstance(value, QuantizationConfig):
533+
raise TypeError(
534+
f"{config_name} {value_description} must be "
535+
"QuantizationConfig or None, "
536+
f"got {type(value).__name__}."
537+
)
538+
539+
@staticmethod
540+
def _validate_config_dict(
541+
config_name: str,
542+
value: object,
543+
is_valid_key: Callable[[object], bool],
544+
key_description: str,
545+
) -> Dict[Any, Optional[QuantizationConfig]]:
546+
if not isinstance(value, dict):
547+
raise TypeError(
548+
f"{config_name} must be a dict, got {type(value).__name__}."
549+
)
550+
551+
for key, quantization_config in value.items():
552+
if not is_valid_key(key):
553+
raise TypeError(
554+
f"{config_name} keys must be {key_description}, "
555+
f"got {type(key).__name__}."
556+
)
557+
TOSAQuantizer._validate_optional_quantization_config(
558+
config_name, quantization_config, "values"
559+
)
560+
561+
return value
562+
526563
@property
527564
def tosa_spec(self):
528565
"""Return the TOSA specification used by the active quantizer."""
@@ -540,12 +577,11 @@ def global_config(self):
540577

541578
@global_config.setter
542579
def global_config(self, value: Optional[QuantizationConfig]) -> None:
580+
self._validate_optional_quantization_config("global_config", value)
543581
if isinstance(self.quantizer, _TOSAQuantizerV1):
544582
self.quantizer.global_config = value
545583
else:
546-
raise NotImplementedError(
547-
"Composable quantizer does not allow setting global_config directly. Please use set_global() instead."
548-
)
584+
self.quantizer.set_global(value)
549585

550586
@property
551587
def io_config(self):
@@ -559,12 +595,12 @@ def io_config(self):
559595

560596
@io_config.setter
561597
def io_config(self, value: Optional[QuantizationConfig]) -> None:
598+
self._validate_optional_quantization_config("io_config", value)
562599
if isinstance(self.quantizer, _TOSAQuantizerV1):
563600
self.quantizer.io_config = value
564601
else:
565-
raise NotImplementedError(
566-
"Composable quantizer does not allow setting io_config directly. Please use set_io() instead."
567-
)
602+
self.quantizer.clear_io_config()
603+
self.quantizer.set_io(value)
568604

569605
@property
570606
def module_type_config(self):
@@ -580,12 +616,18 @@ def module_type_config(self):
580616
def module_type_config(
581617
self, value: Dict[Callable, Optional[QuantizationConfig]]
582618
) -> None:
619+
module_type_config = self._validate_config_dict(
620+
"module_type_config",
621+
value,
622+
callable,
623+
"callable",
624+
)
583625
if isinstance(self.quantizer, _TOSAQuantizerV1):
584-
self.quantizer.module_type_config = value
626+
self.quantizer.module_type_config = module_type_config
585627
else:
586-
raise NotImplementedError(
587-
"Composable quantizer does not allow setting module_type_config directly. Please use set_module_type() instead."
588-
)
628+
self.quantizer.clear_module_type_config()
629+
for module_type, quantization_config in module_type_config.items():
630+
self.quantizer.set_module_type(module_type, quantization_config)
589631

590632
@property
591633
def module_name_config(self):
@@ -601,12 +643,18 @@ def module_name_config(self):
601643
def module_name_config(
602644
self, value: Dict[str, Optional[QuantizationConfig]]
603645
) -> None:
646+
module_name_config = self._validate_config_dict(
647+
"module_name_config",
648+
value,
649+
lambda key: isinstance(key, str),
650+
"str",
651+
)
604652
if isinstance(self.quantizer, _TOSAQuantizerV1):
605-
self.quantizer.module_name_config = value
653+
self.quantizer.module_name_config = module_name_config
606654
else:
607-
raise NotImplementedError(
608-
"Composable quantizer does not allow setting module_name_config directly. Please use set_module_name() instead."
609-
)
655+
self.quantizer.clear_module_name_config()
656+
for module_name, quantization_config in module_name_config.items():
657+
self.quantizer.set_module_name(module_name, quantization_config)
610658

611659
def set_global(
612660
self, quantization_config: Optional[QuantizationConfig]
@@ -1131,6 +1179,30 @@ def quantizers(self, value: List[Quantizer]) -> None:
11311179
"""Update quantizers without accessing self._quantizers directly."""
11321180
self._quantizers = value
11331181

1182+
def _remove_quantizers_by_node_finder_type(
1183+
self, node_finder_types: type[NodeFinder] | tuple[type[NodeFinder], ...]
1184+
) -> None:
1185+
self._quantizers = [
1186+
quantizer
1187+
for quantizer in self._quantizers
1188+
if not (
1189+
isinstance(quantizer, PatternQuantizer)
1190+
and isinstance(quantizer.node_finder, node_finder_types)
1191+
)
1192+
]
1193+
1194+
def clear_module_type_config(self) -> _TOSAQuantizerV2:
1195+
self._remove_quantizers_by_node_finder_type(ModuleTypeNodeFinder)
1196+
return self
1197+
1198+
def clear_module_name_config(self) -> _TOSAQuantizerV2:
1199+
self._remove_quantizers_by_node_finder_type(ModuleNameNodeFinder)
1200+
return self
1201+
1202+
def clear_io_config(self) -> _TOSAQuantizerV2:
1203+
self._remove_quantizers_by_node_finder_type((InputNodeFinder, OutputNodeFinder))
1204+
return self
1205+
11341206
def annotate(self, model):
11351207
reporter = QuantizerReporter(self.quantizers, "FINAL QUANTIZATION REPORT")
11361208
model = super().annotate(model)
@@ -1284,20 +1356,25 @@ class EthosUQuantizer(TOSAQuantizer):
12841356
"""Quantizer supported by the Arm Ethos-U backend.
12851357
12861358
.. warning::
1287-
Setting ``use_composable_quantizer=True`` enables an experimental API
1288-
surface that may change without notice.
1359+
The composable quantizer is now the default implementation. Setting
1360+
``use_composable_quantizer=False`` is deprecated and will be removed in
1361+
two minor releases.
12891362
12901363
Args:
12911364
compile_spec (EthosUCompileSpec): Backend compile specification for
12921365
Ethos-U targets.
1293-
use_composable_quantizer (bool): Whether to use the composable quantizer implementation. See https://github.com/pytorch/executorch/issues/17701" for details.
1366+
use_composable_quantizer (bool): Whether to use the composable
1367+
quantizer implementation. Setting this to ``False`` is deprecated
1368+
and will be removed in two minor releases. See
1369+
[issue #17701](https://github.com/pytorch/executorch/issues/17701)
1370+
for details.
12941371
12951372
"""
12961373

12971374
def __init__(
12981375
self,
12991376
compile_spec: EthosUCompileSpec,
1300-
use_composable_quantizer: bool = False,
1377+
use_composable_quantizer: bool = True,
13011378
) -> None:
13021379
super().__init__(compile_spec, use_composable_quantizer)
13031380

@@ -1306,19 +1383,24 @@ class VgfQuantizer(TOSAQuantizer):
13061383
"""Quantizer supported by the Arm Vgf backend.
13071384
13081385
.. warning::
1309-
Setting ``use_composable_quantizer=True`` enables an experimental API
1310-
surface that may change without notice.
1386+
The composable quantizer is now the default implementation. Setting
1387+
``use_composable_quantizer=False`` is deprecated and will be removed in
1388+
two minor releases.
13111389
13121390
Args:
13131391
compile_spec (VgfCompileSpec): Backend compile specification for Vgf
13141392
targets.
1315-
use_composable_quantizer (bool): Whether to use the composable quantizer implementation. See https://github.com/pytorch/executorch/issues/17701" for details.
1393+
use_composable_quantizer (bool): Whether to use the composable
1394+
quantizer implementation. Setting this to ``False`` is deprecated
1395+
and will be removed in two minor releases. See
1396+
[issue #17701](https://github.com/pytorch/executorch/issues/17701)
1397+
for details.
13161398
13171399
"""
13181400

13191401
def __init__(
13201402
self,
13211403
compile_spec: VgfCompileSpec,
1322-
use_composable_quantizer: bool = False,
1404+
use_composable_quantizer: bool = True,
13231405
) -> None:
13241406
super().__init__(compile_spec, use_composable_quantizer)

backends/arm/quantizer/quantization_config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ def get_output_act_qspec(
356356
357357
If node is a pooling or upsample operator, returns a shared quantization spec.
358358
If no weight spec is configured, return ``None``.
359-
If node is a `to.dtype` operator, returns a fixed quantization spec if the input is integer and the output is floating-point.
359+
If node is a `to.dtype` operator, returns a fixed quantization spec if the input is integer and the output is float32.
360360
361361
"""
362362

@@ -391,6 +391,7 @@ def get_output_act_qspec(
391391
isinstance(input_val, torch.Tensor)
392392
and isinstance(output_val, torch.Tensor)
393393
and CastCheck.is_integer_to_float(input_val.dtype, output_val.dtype)
394+
and output_val.dtype == torch.float32
394395
):
395396
return FixedQParamsQuantizationSpec(
396397
dtype=input_val.dtype,

backends/arm/test/misc/test_quant_custom_meta.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,6 @@ def test_quantized_to_float_transition_tosa_INT_FP(fp_extension: bool):
105105
)
106106
pipeline.quantizer.set_module_type(torch.nn.Sigmoid, None) # type: ignore
107107
pipeline.quantizer.set_module_type(torch.nn.Conv1d, None) # type: ignore
108+
pipeline.quantizer.set_io(None) # type: ignore
108109

109110
pipeline.run()

0 commit comments

Comments
 (0)