Skip to content

Commit f1fc652

Browse files
committed
add logic to preserve per-block granularity for shared observers
1 parent eb36dac commit f1fc652

3 files changed

Lines changed: 213 additions & 73 deletions

File tree

src/coreai_opt/quantization/_graph/_utils.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from coreai_opt.quantization.spec.fake_quantize import FakeQuantizeImplBase
1313
from 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

Comments
 (0)