1111
1212from coreai_opt .quantization .spec .fake_quantize import FakeQuantizeImplBase
1313from coreai_opt .quantization .spec .granularity import (
14+ PerBlockGranularity ,
1415 PerChannelGranularity ,
1516 PerTensorGranularity ,
1617 QuantizationGranularity ,
@@ -267,8 +268,8 @@ def _shared_granularity_axis_is_safe(
267268 """Return True only if it's proven safe to keep ``fake_quant``'s
268269 granularity shared across ``op_node``; unproven cases default to unsafe.
269270
270- Per-channel activation quantization on a shared observer is assumed
271- unsafe by default. Each op category below has exactly one condition
271+ Per-channel and per-block activation quantization on a shared observer are
272+ assumed unsafe by default. Each op category below has exactly one condition
272273 under which it stops being safe, checked directly against that
273274 category rather than composing generic checks that apply to every op:
274275
@@ -284,10 +285,6 @@ def _shared_granularity_axis_is_safe(
284285 # No axis to violate — trivially safe.
285286 if isinstance (granularity , PerTensorGranularity ):
286287 return True
287- # Anything other than PerChannelGranularity (e.g. PerBlockGranularity)
288- # has no condition checked below, so it's not safe.
289- if not isinstance (granularity , PerChannelGranularity ):
290- return False
291288
292289 output_shape = op_node .meta ["val" ].shape
293290 input_shape = input_fq_node .all_input_nodes [0 ].meta ["val" ].shape
@@ -296,14 +293,19 @@ def _shared_granularity_axis_is_safe(
296293 # index, so there's no condition to check here.
297294 if len (input_shape ) != len (output_shape ):
298295 return False
299- axis = QuantizationGranularity ._resolve_axis (granularity , len (input_shape ))
300- if axis is None :
301- return False
296+
297+ if isinstance (granularity , PerBlockGranularity ):
298+ axes : tuple [int , ...] = tuple (range (len (input_shape )))
299+ else :
300+ axis = QuantizationGranularity ._resolve_axis (granularity , len (input_shape ))
301+ if axis is None :
302+ return False
303+ axes = (axis ,)
302304
303305 if op_node .target in _AXIS_RESIZING_ATEN_OPS :
304- return input_shape [axis ] == output_shape [axis ]
306+ return all ( input_shape [a ] == output_shape [a ] for a in axes )
305307 if op_node .target in _AXIS_REORDERING_ATEN_OPS :
306- return _op_preserves_axis_identity (op_node , axis )
308+ return all ( _op_preserves_axis_identity (op_node , a ) for a in axes )
307309 # flatten/reshape/view/unsqueeze, or an unrecognized future op: no known
308310 # single condition to prove safety, so default to unsafe.
309311 return False
0 commit comments