[Bugifx][INC] Fix INC quantization method selection for non-quantized layers (#47237)

Signed-off-by: lvliang-intel <liang1.lv@intel.com> Co-authored-by: Kunshang Ji <kunshang.ji@intel.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

Liang Lv committed Sep 1, 2026 at 18:40 UTC 92ccd2c30679d53418399be71f8ff7c0afef9790
2 files changed +91 -2
tests/quantization/test_auto_round.py
+88 -1
@@ -333,12 +333,99 @@ def test_inc_model_prefix_early_exit() -> None:
333 },
334 }
335 )
336 + layer = object.__new__(LinearBase)
337
338 # get_quant_method checks model. prefix for unquantized early-exit
338 - result = config.get_quant_method(DummyLayer(), "layers.1.mlp.gate_proj")
339 + result = config.get_quant_method(layer, "layers.1.mlp.gate_proj")
340 assert isinstance(result, UnquantizedLinearMethod)
341
342
343 +def test_inc_get_quant_method_unquantized_parallel_lm_head_returns_unquantized() -> (
344 + None
345 +):
346 + """ParallelLMHead with bits >= 16 returns UnquantizedLinearMethod via the
347 + early-exit path (parallel_lm_head is explicitly handled alongside LinearBase).
348 + """
349 + config = make_config(extra_config={"lm_head": {"bits": 16}})
350 + layer = object.__new__(ParallelLMHead)
351 +
352 + method = config.get_quant_method(layer, "lm_head")
353 +
354 + assert isinstance(method, UnquantizedLinearMethod)
355 +
356 +
357 +def test_inc_get_quant_method_unquantized_unknown_layer_returns_none() -> None:
358 + """Regression test: non-Linear/non-MoE layers with bits >= 16 must return
359 + None from the early-exit path instead of being misclassified as linear.
360 + """
361 + config = make_config(extra_config={"layer": {"bits": 16}})
362 +
363 + method = config.get_quant_method(DummyLayer(), "layer")
364 +
365 + assert method is None
366 +
367 +
368 +@pytest.mark.parametrize(
369 + "layer_factory",
370 + [
371 + pytest.param(
372 + lambda: object.__new__(LinearBase),
373 + id="LinearBase",
374 + ),
375 + pytest.param(
376 + lambda: object.__new__(ParallelLMHead),
377 + id="ParallelLMHead",
378 + ),
379 + ],
380 +)
381 +def test_inc_get_quant_method_unquantized_layer_with_model_prefix(
382 + layer_factory,
383 +) -> None:
384 + """``model.``-prefixed extra_config entries trigger early exit for both
385 + LinearBase and ParallelLMHead layers."""
386 + config = make_config(extra_config={"model.layer": {"bits": 16}})
387 + layer = layer_factory()
388 +
389 + method = config.get_quant_method(layer, "layer")
390 +
391 + assert isinstance(method, UnquantizedLinearMethod)
392 +
393 +
394 +def test_inc_get_quant_method_unquantized_routed_experts_with_model_prefix(
395 + monkeypatch,
396 +) -> None:
397 + """``model.``-prefixed extra_config entries trigger early exit for
398 + RoutedExperts layers, returning UnquantizedFusedMoEMethod."""
399 +
400 + class DummyUnquantizedFusedMoEMethod:
401 + def __init__(self, moe_config) -> None:
402 + self.moe_config = moe_config
403 +
404 + monkeypatch.setattr(
405 + "vllm.model_executor.layers.quantization.inc.inc.UnquantizedFusedMoEMethod",
406 + DummyUnquantizedFusedMoEMethod,
407 + )
408 +
409 + config = make_config(extra_config={"model.moe_layer": {"bits": 16}})
410 + layer = object.__new__(RoutedExperts)
411 + layer.moe_config = None
412 +
413 + method = config.get_quant_method(layer, "moe_layer")
414 +
415 + assert isinstance(method, DummyUnquantizedFusedMoEMethod)
416 + assert method.moe_config is None
417 +
418 +
419 +def test_inc_get_quant_method_unknown_layer_with_model_prefix_returns_none() -> None:
420 + """``model.``-prefixed extra_config entries return None for unhandled
421 + layer types (non-Linear, non-MoE)."""
422 + config = make_config(extra_config={"model.unknown": {"bits": 16}})
423 +
424 + method = config.get_quant_method(DummyLayer(), "unknown")
425 +
426 + assert method is None
427 +
428 +
429 def test_inc_config_parser_regex_match() -> None:
430 config = make_config(
431 extra_config={
vllm/model_executor/layers/quantization/inc/inc.py
+3 -1
@@ -328,9 +328,11 @@ class INCConfig(QuantizationConfig):
328 if (
329 layer_name == prefix or layer_name == f"model.{prefix}"
330 ) and self.extra_config[layer_name].get("bits", 16) >= 16:
331 + if isinstance(layer, (LinearBase, ParallelLMHead)):
332 + return UnquantizedLinearMethod()
333 if isinstance(layer, RoutedExperts):
334 return UnquantizedFusedMoEMethod(layer.moe_config)
333 - return UnquantizedLinearMethod()
335 + return None
336
337 layer_config = self.config_parser.resolve(layer, prefix)
338 if not layer_config.quantized: