Skip to content

Forward reduce_range and nodes_to_quantize in the static QDQ config path - #2755

Open
Shreya Boyane (Sonica-B) wants to merge 1 commit into
microsoft:mainfrom
Sonica-B:fix/static-qdq-dropped-options
Open

Shreya Boyane (Sonica-B) wants to merge 1 commit into
microsoft:mainfrom
Sonica-B:fix/static-qdq-dropped-options

Conversation

@Sonica-B

Copy link
Copy Markdown

Describe your changes

OnnxStaticQuantization silently ignores reduce_range and nodes_to_quantize on its default path (quant_format="QDQ", prepare_qdq_config=True, non-QNN EP, onnxruntime >= 1.21).

On that path, get_static_run_config calls onnxruntime's get_qdq_config(...) and then uses the members of the returned StaticQuantConfig as the quantize_static arguments. Two user settings get lost on the way:

  • reduce_range is a parameter of get_qdq_config, but Olive never passes it, so StaticQuantConfig keeps its default reduce_range=False.
  • Neither get_qdq_config nor get_qnn_qdq_config has a nodes_to_quantize parameter, so StaticQuantConfig.nodes_to_quantize stays None and every node of the selected op types is quantized.

With prepare_qdq_config=False both settings work, so the pass does mean to support them. The pass-configuration docs example (docs/source/how-to/configure-workflows/pass-configuration.md) searches over reduce_range for OnnxQuantization; on the default path both search points currently produce the same model.

flowchart LR
    subgraph Before
        A1[pass config<br/>reduce_range, nodes_to_quantize] --> B1["get_qdq_config(...)<br/>no reduce_range passed"]
        B1 --> C1["StaticQuantConfig<br/>reduce_range=False<br/>nodes_to_quantize=None"]
        C1 --> D1[quantize_static]
    end
    subgraph After
        A2[pass config<br/>reduce_range, nodes_to_quantize] --> B2["get_qdq_config(..., reduce_range)"]
        B2 --> C2[StaticQuantConfig members]
        A2 -- "nodes_to_quantize copied" --> C2
        C2 --> D2[quantize_static]
    end
Loading

Changes in olive/passes/onnx/quantization.py:

  • pass reduce_range to get_qdq_config;
  • on the QNN path, log a warning when reduce_range is set, since get_qnn_qdq_config has no such parameter (QNN behavior is otherwise unchanged);
  • copy a user-set nodes_to_quantize onto the final run config, for both the QNN and non-QNN helpers.

Reproduction: a two-MatMul ONNX model (x -> MatMul(W1) -> Relu -> MatMul(W2)), OnnxStaticQuantization with precision=int8, weight_symmetric=True, everything else default. The script prints the largest absolute value in each quantized weight (127 is the full symmetric int8 range, 64 is the 7-bit reduce_range range).

Before (main @ 1fce2a6):

{}                                                           -> W1: int8 max|q|=127, W2: int8 max|q|=127
{'reduce_range': True}                                       -> W1: int8 max|q|=127, W2: int8 max|q|=127
{'reduce_range': True, 'prepare_qdq_config': False}          -> W1: int8 max|q|=64, W2: int8 max|q|=64
{'nodes_to_quantize': ['mm1']}                               -> W1: int8 max|q|=127, W2: int8 max|q|=127
{'nodes_to_quantize': ['mm1'], 'prepare_qdq_config': False}  -> W1: int8 max|q|=127, W2: not quantized (float)

After:

{}                                                           -> W1: int8 max|q|=127, W2: int8 max|q|=127
{'reduce_range': True}                                       -> W1: int8 max|q|=64, W2: int8 max|q|=64
{'reduce_range': True, 'prepare_qdq_config': False}          -> W1: int8 max|q|=64, W2: int8 max|q|=64
{'nodes_to_quantize': ['mm1']}                               -> W1: int8 max|q|=127, W2: not quantized (float)
{'nodes_to_quantize': ['mm1'], 'prepare_qdq_config': False}  -> W1: int8 max|q|=127, W2: not quantized (float)

Tests (test/passes/onnx/test_quantization.py):

  • test_nodes_and_ops gains a nodes_to_quantize case for both QNN and non-QNN and checks that nodes_to_quantize reaches quantize_static;
  • new test_static_quantization_applies_reduce_range_when_prepare_qdq_config runs real static quantization and checks that the quantized weights span 127 without reduce_range and 64 with it.

On main the three new cases fail (assert set() == {'node_linear'} twice, assert 127 == 64); with the fix the file passes (16 passed). Validated on Windows 11 with CPU torch (Python 3.11.9, onnxruntime 1.31.0, onnx 1.20.1, torch 2.14.1+cpu); test_add_metadata.py, test_split_model.py, test_quantize_embedding.py, test/engine/test_engine.py and test/cli/test_cli.py also pass there. CI will confirm on the other platforms. The QNN EP itself was not run; the QNN path is covered by the mocked test_nodes_and_ops[True-*] cases.

Checklist before requesting a review

  • Add unit tests for this change.
  • Make sure all tests can pass.
  • Update documents if necessary. (No doc change needed; the documented options now take effect.)
  • Lint and apply fixes to your code by running lintrunner -a
  • Is this a user-facing change? If yes, give a description of this change to be included in the release notes.

Release note: OnnxStaticQuantization / OnnxQuantization now apply reduce_range and nodes_to_quantize when prepare_qdq_config=True (the default for QDQ). Before this change both were ignored on that path. Configs that set reduce_range: true will now produce 7-bit weights, as documented.

(Optional) Issue link

None; found while reading the static quantization path.

With quant_format="QDQ" and prepare_qdq_config=True (both defaults),
OnnxStaticQuantization builds its run config from get_qdq_config (or
get_qnn_qdq_config for the QNN EP) and uses the returned
StaticQuantConfig in place of the pass config. reduce_range was never
passed to get_qdq_config, and neither helper accepts nodes_to_quantize,
so both settings were silently dropped: weights were quantized to the
full 8-bit range and every node of the selected op types was quantized.

Pass reduce_range to get_qdq_config, warn when it is set for the QNN EP
(get_qnn_qdq_config has no such parameter), and copy nodes_to_quantize
onto the final run config.
Copilot AI balanced review requested due to automatic review settings October 26, 2026 05:06
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

This branch has not been deployed

No deployments
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