Skip to content

Validate non-empty cfg when enabling quantizers in quant_cfg#1192

Open
shengliangxu wants to merge 11 commits intomainfrom
shengliangx/early-quant-cfg-validation
Open

Validate non-empty cfg when enabling quantizers in quant_cfg#1192
shengliangxu wants to merge 11 commits intomainfrom
shengliangx/early-quant-cfg-validation

Conversation

@shengliangxu
Copy link
Copy Markdown
Collaborator

@shengliangxu shengliangxu commented Apr 7, 2026

What does this PR do?

An entry with enable=True and an empty or invalid cfg (e.g., cfg={}, cfg=[], or cfg=42) would pass normalize_quant_cfg_list but later crash in set_quantizer_by_cfg when QuantizerAttributeConfig(enable=True) was constructed with no actual quantizer attributes.

Add early validation in normalize_quant_cfg_list to reject such entries at config time rather than surfacing a confusing Pydantic ValidationError deep in the conversion pipeline.

Testing

new unit tests added

Summary by CodeRabbit

  • Bug Fixes
    • Stricter validation for quantizer entries: enabled quantizers must have a non-empty dict or non-empty list of non-empty dicts; empty or invalid cfg values now raise a clearer error. Normalization still preserves explicit enable/disable flags.
  • Tests
    • Added unit tests covering rejection of empty/invalid configs when enabled and acceptance when disabled, and ensuring normalized output preserves disabled state.

…g_list

An entry with enable=True and an empty or invalid cfg (e.g., cfg={},
cfg=[], or cfg=42) would pass normalize_quant_cfg_list but later crash
in set_quantizer_by_cfg when QuantizerAttributeConfig(enable=True) was
constructed with no actual quantizer attributes.

Add early validation in normalize_quant_cfg_list to reject such entries
at config time rather than surfacing a confusing Pydantic ValidationError
deep in the conversion pipeline.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu requested a review from a team as a code owner April 7, 2026 22:23
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e144137f-d5d3-4606-becd-48c7e8bdf085

📥 Commits

Reviewing files that changed from the base of the PR and between 2eab7ce and 76d66dd.

📒 Files selected for processing (2)
  • modelopt/torch/quantization/config.py
  • tests/unit/torch/quantization/test_config_validation.py
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/torch/quantization/test_config_validation.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • modelopt/torch/quantization/config.py

📝 Walkthrough

Walkthrough

normalize_quant_cfg_list now validates each entry before canonicalization: if enable (implicit or explicit) is true, cfg must be a non-empty dict or list; otherwise a ValueError is raised. Docstring and error text were updated and unit tests were added for enabled/disabled/invalid cfg cases.

Changes

Cohort / File(s) Summary
Quantization Config Validation
modelopt/torch/quantization/config.py
Added pre-canonicalization validation in normalize_quant_cfg_list to reject entries where enable resolves to true but cfg is missing, empty, or not a dict/list; updated docstring and ValueError wording to reflect the "enabled quantizer must have a valid configuration" rule.
Config Validation Tests
tests/unit/torch/quantization/test_config_validation.py
Added tests asserting enabled entries with cfg={}, cfg=[], or non-dict/list cfg (e.g., 42) raise ValueError, and that disabled entries accept empty cfg while preserving enable=False after normalization.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Validate non-empty cfg when enabling quantizers in quant_cfg' directly describes the main change: adding validation to reject enabled quantizers with empty or invalid configurations.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
Security Anti-Patterns ✅ Passed Pull request contains only configuration validation enhancements with no security anti-patterns, unsafe deserialization, code execution, or new dependencies.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch shengliangx/early-quant-cfg-validation

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://NVIDIA.github.io/Model-Optimizer/pr-preview/pr-1192/

Built to branch gh-pages at 2026-04-09 20:36 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/unit/torch/quantization/test_config_validation.py (1)

166-205: Add regression cases for invalid list elements in enabled cfg

Nice coverage for empty/invalid top-level cfg; please also add cases for cfg=[{}] and cfg=[42] with enable=True to lock in early rejection of malformed sequential configs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/torch/quantization/test_config_validation.py` around lines 166 -
205, Add two regression tests to ensure invalid list elements inside an enabled
cfg are rejected: create tests similar to existing ones that call
normalize_quant_cfg_list with [{"quantizer_name": "*weight_quantizer", "cfg":
[{}], "enable": True}] and [{"quantizer_name": "*weight_quantizer", "cfg": [42],
"enable": True}] and assert they raise ValueError matching "non-empty dict or
list"; place them alongside the other test_error_* cases referencing the
normalize_quant_cfg_list function so malformed sequential configs are explicitly
covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@modelopt/torch/quantization/config.py`:
- Around line 1669-1680: The validation currently only checks the top-level cfg
container; update the check in the block using entry/cfg/enable so that when
enable is True and cfg is a list you also validate every element: require each
element to be a dict and non-empty (reject non-dict items like 42 and empty
dicts like {}), raising the same ValueError (include raw) if any element is
invalid; keep the existing dict-or-list check for top-level cfg and add the
per-item check (refer to variables entry, cfg, enable and the
QuantizerAttributeConfig-related error path) so malformed list payloads are
rejected early.

---

Nitpick comments:
In `@tests/unit/torch/quantization/test_config_validation.py`:
- Around line 166-205: Add two regression tests to ensure invalid list elements
inside an enabled cfg are rejected: create tests similar to existing ones that
call normalize_quant_cfg_list with [{"quantizer_name": "*weight_quantizer",
"cfg": [{}], "enable": True}] and [{"quantizer_name": "*weight_quantizer",
"cfg": [42], "enable": True}] and assert they raise ValueError matching
"non-empty dict or list"; place them alongside the other test_error_* cases
referencing the normalize_quant_cfg_list function so malformed sequential
configs are explicitly covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6ff10f3d-df75-4a3b-bb2b-efac0193fc02

📥 Commits

Reviewing files that changed from the base of the PR and between af2fe24 and dcd2d3b.

📒 Files selected for processing (2)
  • modelopt/torch/quantization/config.py
  • tests/unit/torch/quantization/test_config_validation.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
modelopt/torch/quantization/config.py (1)

1680-1691: ⚠️ Potential issue | 🟠 Major

Enabled list cfg is still under-validated at item level.

Line 1686 validates only the top-level container. Cases like cfg=[42] or cfg=[{}] still pass this block and can fail later, which weakens the “early rejection” goal.

Suggested fix
             cfg = entry.get("cfg")
             enable = entry.get("enable", True)
             if enable and cfg is not None:
-                if not isinstance(cfg, (dict, list)) or len(cfg) == 0:
+                if isinstance(cfg, dict):
+                    is_invalid = len(cfg) == 0
+                elif isinstance(cfg, list):
+                    is_invalid = len(cfg) == 0 or any(
+                        not isinstance(item, dict) or len(item) == 0 for item in cfg
+                    )
+                else:
+                    is_invalid = True
+                if is_invalid:
                     raise ValueError(
                         f"Invalid quant_cfg entry: {raw!r} — 'cfg' must be a non-empty dict "
-                        "or list when enabling a quantizer. Either provide quantizer "
-                        "attributes in 'cfg' or remove 'cfg' and set 'enable' explicitly."
+                        "or a non-empty list of non-empty dicts when enabling a quantizer. "
+                        "Either provide quantizer attributes in 'cfg' or remove 'cfg' and "
+                        "set 'enable' explicitly."
                     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modelopt/torch/quantization/config.py` around lines 1680 - 1691, The current
validation only checks that cfg is a non-empty dict or list but doesn’t validate
list elements, allowing invalid entries like cfg=[42] or cfg=[{}]; update the
validation in the block handling entry.get("cfg")/enable to iterate when cfg is
a list and ensure each element is a non-empty dict with the expected structure
(i.e., dict type and len(item) > 0), and raise a ValueError (including raw) if
any element is not a non-empty dict; also keep the existing non-list branch to
ensure a dict cfg is non-empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@modelopt/torch/quantization/config.py`:
- Around line 1680-1691: The current validation only checks that cfg is a
non-empty dict or list but doesn’t validate list elements, allowing invalid
entries like cfg=[42] or cfg=[{}]; update the validation in the block handling
entry.get("cfg")/enable to iterate when cfg is a list and ensure each element is
a non-empty dict with the expected structure (i.e., dict type and len(item) >
0), and raise a ValueError (including raw) if any element is not a non-empty
dict; also keep the existing non-list branch to ensure a dict cfg is non-empty.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: db068ac8-55d1-4e78-b057-191388182afc

📥 Commits

Reviewing files that changed from the base of the PR and between dcd2d3b and 210a1be.

📒 Files selected for processing (1)
  • modelopt/torch/quantization/config.py

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.20%. Comparing base (9b4f43a) to head (7868853).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1192      +/-   ##
==========================================
- Coverage   75.42%   66.20%   -9.22%     
==========================================
  Files         353      353              
  Lines       40603    40612       +9     
==========================================
- Hits        30623    26887    -3736     
- Misses       9980    13725    +3745     
Flag Coverage Δ
unit 55.12% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@meenchen meenchen left a comment

Choose a reason for hiding this comment

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

1. Correctness

[SUGGESTION] Error message could include the actual cfg type for easier debugging
When the validation rejects an invalid cfg, the error shows the raw entry but not the specific type/value that failed. For quick diagnosis, consider including it:

raise ValueError(
    f"Invalid quant_cfg entry: {raw!r} — 'cfg' must be a non-empty dict "
    f"or list when enabling a quantizer (got {type(cfg).__name__}: {cfg!r}). "
    "Either provide quantizer attributes in 'cfg' or set 'enable' explicitly."
)

2. Edge Case

[QUESTION] cfg=None explicitly provided with enable=True
The validation skips when cfg is None, which is correct for the default path (cfg not provided). But {"quantizer_name": "*", "cfg": None, "enable": True} also passes — is this a valid config downstream, or should it be caught here too?

shengliangxu and others added 2 commits April 8, 2026 16:17
@shengliangxu
Copy link
Copy Markdown
Collaborator Author

shengliangxu commented Apr 8, 2026

1. Correctness

[SUGGESTION] Error message could include the actual cfg type for easier debugging When the validation rejects an invalid cfg, the error shows the raw entry but not the specific type/value that failed. For quick diagnosis, consider including it:

raise ValueError(
    f"Invalid quant_cfg entry: {raw!r} — 'cfg' must be a non-empty dict "
    f"or list when enabling a quantizer (got {type(cfg).__name__}: {cfg!r}). "
    "Either provide quantizer attributes in 'cfg' or set 'enable' explicitly."
)

resolved

2. Edge Case

[QUESTION] cfg=None explicitly provided with enable=True The validation skips when cfg is None, which is correct for the default path (cfg not provided). But {"quantizer_name": "*", "cfg": None, "enable": True} also passes — is this a valid config downstream, or should it be caught here too?

This is intentionally allowed — it's the canonical way to express "enable this quantizer with default/inherited settings" (same as {"quantizer_name": "*", "enable": True} with no cfg). The code path entry.setdefault("cfg", None) normalizes missing cfg to None, so explicitly passing cfg=None is equivalent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants