Skip to content

Commit 6b6d2c0

Browse files
committed
refactor: move axis normalization into a shared util
`_compute_channel_mask` and `QuantizationGranularity._resolve_axis` both resolved a negative axis against the tensor rank. The shared helper lives in `torch_utils`. It resolves the axis and nothing else, so the range check stays where pruning already had it and the quantization path is unchanged.
1 parent fd66ff8 commit 6b6d2c0

4 files changed

Lines changed: 45 additions & 5 deletions

File tree

src/coreai_opt/_utils/torch_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ def flatten_tensors_to_list(obj: Any) -> list[torch.Tensor]:
7171
return []
7272

7373

74+
def normalize_axis(axis: int, ndim: int) -> int:
75+
"""Resolve a negative axis against a tensor rank.
76+
77+
A non-negative axis is returned unchanged. The caller must validate the range,
78+
since an axis below ``-ndim`` stays negative here.
79+
80+
Args:
81+
axis (int): Axis in standard Python style indexing.
82+
ndim (int): Rank of the tensor the axis refers to.
83+
84+
Returns:
85+
int: ``axis + ndim`` when axis is negative, otherwise axis unchanged.
86+
87+
"""
88+
return axis + ndim if axis < 0 else axis
89+
90+
7491
def get_module_name(model: torch.nn.Module, module: torch.nn.Module) -> str | None:
7592
"""Find the fully qualified name of a module within a model.
7693

src/coreai_opt/pruning/spec/prune.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
PartialConstructor as _PartialConstructor,
1818
with_args as _with_args,
1919
)
20+
from coreai_opt._utils.torch_utils import normalize_axis as _normalize_axis
2021
from coreai_opt.config.spec import CompressionSimulatorBase
2122

2223
from .scheme import ChannelStructured, PruningScheme
@@ -174,8 +175,7 @@ def _compute_channel_mask(
174175
raise ValueError(
175176
f"Invalid axis. Should be in range [{-weight.ndim}, {weight.ndim}), but got {axis}"
176177
)
177-
if axis < 0:
178-
axis += weight.ndim
178+
axis = _normalize_axis(axis, weight.ndim)
179179

180180
num_channels = weight.shape[axis]
181181
num_prune = math.floor(num_channels * sparsity)

src/coreai_opt/quantization/spec/granularity.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pydantic import BaseModel, ConfigDict, Field, model_serializer
1313

1414
from coreai_opt._utils.registry_utils import ConfigRegistryMixin as _ConfigRegistryMixin
15+
from coreai_opt._utils.torch_utils import normalize_axis as _normalize_axis
1516
from coreai_opt.quantization.spec.errors import _BlockSizeMismatchError
1617

1718

@@ -104,9 +105,7 @@ def _resolve_axis(granularity: QuantizationGranularity, tensor_ndim: int) -> int
104105
axis = granularity.axis
105106
if axis is None:
106107
return None
107-
if axis < 0:
108-
axis += tensor_ndim
109-
return axis
108+
return _normalize_axis(axis, tensor_ndim)
110109

111110

112111
@QuantizationGranularity.register("per_tensor")

tests/test_utils/test_torch_utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
mmap_module_state_dict,
1717
move_model_to_eval,
1818
move_model_to_train,
19+
normalize_axis,
1920
)
2021

2122

@@ -93,6 +94,29 @@ def test_normalize_module_fqn(raw: str, expected: str) -> None:
9394
assert normalize_module_fqn(raw) == expected
9495

9596

97+
class TestNormalizeAxis:
98+
"""Test normalize_axis resolution of negative axes."""
99+
100+
@staticmethod
101+
@pytest.mark.parametrize(
102+
("axis", "ndim", "expected"),
103+
[
104+
(0, 2, 0),
105+
(1, 2, 1),
106+
(-1, 2, 1),
107+
(-2, 2, 0),
108+
(-4, 4, 0),
109+
(-1, 1, 0),
110+
# Out of range passes through unchanged; the caller validates.
111+
(-5, 2, -3),
112+
(3, 2, 3),
113+
],
114+
)
115+
def test_resolves_to_non_negative(axis: int, ndim: int, expected: int) -> None:
116+
"""A negative axis resolves to its non-negative equivalent."""
117+
assert normalize_axis(axis, ndim) == expected
118+
119+
96120
class TestMmapModuleStateDict:
97121
"""Test mmap_module_state_dict serialization and reload."""
98122

0 commit comments

Comments
 (0)