Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Changelog
**Bug Fixes**

- Fix Megatron-Core HF importer to load fused ``TELayerNormColumnParallelLinear.layer_norm_weight`` from HF for GPT-family models (Qwen3 etc.) under ``--export-default-te-spec``. Importer now prefers per-context keys ``fused_input_layernorm`` / ``fused_pre_mlp_layernorm`` (fallback ``fused_norm`` for Nemotron-H backward compatibility); ``mcore_qwen.py`` provides the new rules. Without this fix, post-prune MMLU sat at chance.
- Fix ``QuantizeAlgorithmConfig`` instances being rejected with ``ValueError`` when passed as the ``algorithm`` in a quantization config (e.g. ``cfg["algorithm"] = mtq.MaxCalibConfig(...)``). Such instances are now accepted, consistent with the ``algorithm`` field's type annotation.

0.44 (2026-05-14)
^^^^^^^^^^^^^^^^^
Expand Down
6 changes: 4 additions & 2 deletions modelopt/torch/quantization/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"""This module contains the mode descriptor for the quantization mode."""

from abc import abstractmethod
from collections.abc import Callable
from collections.abc import Callable, Mapping

from modelopt.torch.opt.config import ModeloptBaseConfig
from modelopt.torch.opt.conversion import ModelLikeModule
Expand Down Expand Up @@ -374,7 +374,9 @@ def get_modelike_from_algo_cfg(algo_cfg: QuantizeAlgoCfgType) -> ModeConfigList:
return [get_modelike_from_algo_cfg(c)[0] for c in algo_cfg]
if algo_cfg is None or isinstance(algo_cfg, str):
algo_name, algo_cfg = algo_cfg, {}
elif isinstance(algo_cfg, dict):
elif isinstance(algo_cfg, Mapping):
# Normalize any mapping (incl. ModeloptBaseConfig instances) to a plain dict.
algo_cfg = dict(algo_cfg)
algo_name = algo_cfg["method"]
else:
raise ValueError(f"Invalid config type: {type(algo_cfg)}")
Expand Down
24 changes: 23 additions & 1 deletion tests/unit/torch/quantization/test_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@

from modelopt.torch.opt.config import ModeloptField
from modelopt.torch.opt.mode import _ModeRegistryCls
from modelopt.torch.quantization.config import QuantizeAlgorithmConfig
from modelopt.torch.quantization.config import (
MaxCalibConfig,
QuantizeAlgorithmConfig,
SmoothQuantCalibConfig,
)
from modelopt.torch.quantization.mode import (
BaseCalibrateModeDescriptor,
CalibrateModeRegistry,
QuantizeModeRegistry,
get_modelike_from_algo_cfg,
)


Expand Down Expand Up @@ -60,3 +65,20 @@ def config_class(self) -> QuantizeAlgorithmConfig:
@CalibrateModeRegistry.register_mode
class TestIncorrectCalibrateModeDescriptor:
pass


def test_get_modelike_from_algo_cfg_accepts_config_instance():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests look good. Thanks for adding them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll keep them in the revised version, with the dict assertion updated to reflect the Mapping pass-through.

"""Regression test for GitHub issue #201.

A ``QuantizeAlgorithmConfig`` instance passed as the ``algorithm`` config is
normalized to a dict and accepted, instead of raising ``ValueError``.
"""
mode_name, algo_cfg = get_modelike_from_algo_cfg(MaxCalibConfig(distributed_sync=False))[0]
assert mode_name == "max_calibrate"
assert isinstance(algo_cfg, dict)
assert algo_cfg["distributed_sync"] is False
MaxCalibConfig(**algo_cfg)

modes = get_modelike_from_algo_cfg([MaxCalibConfig(), SmoothQuantCalibConfig()])
assert [name for name, _ in modes] == ["max_calibrate", "smoothquant_calibrate"]
assert all(isinstance(cfg, dict) for _, cfg in modes)
14 changes: 13 additions & 1 deletion tests/unit/torch/quantization/test_quantize_cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import modelopt.torch.opt as mto
import modelopt.torch.quantization as mtq
from modelopt.torch.quantization.calib import MaxCalibrator
from modelopt.torch.quantization.config import QuantizerAttributeConfig
from modelopt.torch.quantization.config import MaxCalibConfig, QuantizerAttributeConfig
from modelopt.torch.quantization.conversion import set_quantizer_attributes_full
from modelopt.torch.quantization.nn.modules.tensor_quantizer import (
SequentialQuantizer,
Expand Down Expand Up @@ -167,6 +167,18 @@ def forward_loop():
mtq.quantize(model, mtq.INT8_DEFAULT_CFG, forward_loop=forward_loop)


def test_quantize_accepts_algo_config_instance_end_to_end():
"""Regression test for GitHub issue #201.

A ``QuantizeAlgorithmConfig`` instance set as ``config["algorithm"]`` must not
raise ``ValueError`` through the full quantize/calibrate flow.
"""
quant_config = copy.deepcopy(mtq.INT8_DEFAULT_CFG)
quant_config["algorithm"] = MaxCalibConfig(distributed_sync=False)
model = SimpleLinear()
quantize_model_and_forward(model, quant_config, [model.get_input() for _ in range(2)])


def test_custom_calib_config():
model_ref = SimpleLinear()
model_ref = mtq.quantize(
Expand Down