Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3299eb3
feat(rrq): Phase 1 - Recurrent Residual Quantization (packed INT2 2+2…
luoyu-intel Sep 4, 2026
6afb9a7
feat(rrq): Phase 2 - generate_rrq_residual (incremental from existing…
luoyu-intel Sep 4, 2026
5c7c12e
feat(rrq): Phase 3 - add per-plane sign-SGD tuning
luoyu-intel Sep 4, 2026
0fd545f
fix(rrq): accumulate optimized residual prefix
luoyu-intel Sep 4, 2026
d0c60ea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 6, 2026
ed2f737
feat(rrq): match AutoRound per-plane quality + configurable mixed-pre…
luoyu-intel Sep 6, 2026
584e0ac
docs(rrq): add Recurrent Residual Quantization entry to README
luoyu-intel Sep 6, 2026
25efe2f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 6, 2026
254b3e9
Merge branch 'main' into feat/rrq-phase1
luoyu-intel Sep 7, 2026
0ba0b03
fix(cli): match shared argparse options before falling back to dest i…
luoyu-intel Sep 7, 2026
11597ed
perf(rrq): replace 4-plane matmul accumulation with single dequant+ma…
luoyu-intel Sep 7, 2026
2632c1a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 7, 2026
c851ced
fix(rrq): declare tunable fields as named __init__ params
luoyu-intel Sep 8, 2026
5da4987
Merge branch 'main' into feat/rrq-phase1
luoyu-intel Sep 8, 2026
ad15c7d
fix(export): include unquantized vision layers in AWQ modules_to_not_…
luoyu-intel Sep 8, 2026
31e0a14
refactor(rrq): minimize common-code changes to preserve main behavior
luoyu-intel Sep 8, 2026
4e78c2b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 8, 2026
6085da1
revert(export): restore AWQ _collect_modules_to_not_convert to main l…
luoyu-intel Sep 8, 2026
5c765bf
Apply batched suggestions from code review
luoyu-intel Sep 9, 2026
664510b
revert log info
luoyu-intel Sep 9, 2026
22e9fff
fix(rrq): raise ValueError for explicit model_free=True with RRQConfig
luoyu-intel Sep 9, 2026
f26d807
fix(rrq): enforce disable_opt_rtn=True in RRQConfig.check_config()
luoyu-intel Sep 9, 2026
389d93e
Refactor RRQ exports to sub-package entry points and add eval scripts
luoyu-intel Sep 10, 2026
0722945
test(rrq): add lm-eval accuracy test for RRQ W2A16 quantization
luoyu-intel Sep 10, 2026
533e29e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 10, 2026
3094057
docs(README): note that RRQ does not support production-level deploym…
luoyu-intel Sep 10, 2026
7a77c98
Merge branch 'main' into feat/rrq-phase1
a32543254 Sep 10, 2026
06f3d58
feat(rrq): hide RRQ from user-facing listings, auto-select via format
luoyu-intel Sep 10, 2026
74f3f00
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 10, 2026
30f2634
fix(rrq): auto-select W2A16 scheme and force disable_opt_rtn for RRQ
luoyu-intel Sep 10, 2026
8a53c1b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 10, 2026
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ auto_round_extension/ark/auto_round_kernel/build_*/
auto_round_extension/ark/build-*/
auto_round_extension/ark/auto_round_kernel/*.so
graphify-out/
rrq_output/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ See our papers [SignRoundV1](https://arxiv.org/pdf/2309.05516) and [SignRoundV2]


## 🆕 What's New
* [2026/09] We experimentally support **Recurrent Residual Quantization (RRQ)**, a progressive multi-precision representation that stacks INT2 residual planes on a standard INT2 base. A single checkpoint serves 2/4/6/8-bit — and per-layer mixed precision — selectable at load time without re-quantizing: [*Paper*](https://arxiv.org/abs/2608.04048).
Comment thread
luoyu-intel marked this conversation as resolved.
Outdated

* [2026/08] We experimentally support **algorithm composition** (e.g., `--algs awq,signround` or `--algs hadamard,awq,signround`) to improve accuracy [*Overview*](./docs/algorithm_combinations.md). We welcome any practical, deployable algorithms that are ready for real-world use. Feel free to submit a PR or leave a comment in Issues.

* [2026/08] AutoScheme WOQ deployment on vLLM is experimentally restored, thanks to Humming Kernel: [*vLLM PR*](https://github.com/vllm-project/vllm/pull/52890), [*Example Model*](https://huggingface.co/Intel/Qwen3.8-27B-bpw2.8-AutoRound). Note: shared layers must be configured per vLLM's fusion patterns.
Expand Down
2 changes: 2 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ AutoRound 是专为大语言模型(LLMs)和视觉-语言模型(VLMs)设

## 🆕 最新进展

* [2026/09] 我们实验性地支持 **递归残差量化(RRQ)**:一种渐进式多精度表示,在标准 INT2 base 之上叠加 INT2 残差平面。单个 checkpoint 即可在加载时选择 2/4/6/8-bit(以及逐层混合精度),无需重新量化:[*论文*](https://arxiv.org/abs/2608.04048)。

* [2026/08] 我们实验性地支持**算法组合**(例如 `--algs awq,signround` 或 `--algs hadamard,awq,signround`),以提升精度:[*总览*](./docs/algorithm_combinations_CN.md). 我们欢迎能真正落地的任何算法组合,欢迎提交 PR 或在 Issues 中留言。

* [2026/08] 感谢 Humming Kernel,AutoScheme WOQ 在 vLLM 上的部署已实验性恢复:[*vLLM PR*](https://github.com/vllm-project/vllm/pull/52890),[*示例模型*](https://huggingface.co/Intel/Qwen3.8-27B-bpw2.8-AutoRound)。注意:共享层需按 vLLM 的融合模式进行配置。
Expand Down
23 changes: 23 additions & 0 deletions auto_round/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
from auto_round.autoround import AutoRound, AutoRoundAdam, AutoRoundDiffusion, AutoRoundLLM, AutoRoundMLLM
from auto_round.algorithms.quantization.rtn.config import OptimizedRTNConfig, RTNConfig
from auto_round.algorithms.quantization.rrq.config import RRQConfig
from auto_round.algorithms.quantization.sign_round.config import (
AdamRoundConfig,
SignRoundConfig,
Expand Down Expand Up @@ -41,10 +42,32 @@
"QuantizationScheme",
"RTNConfig",
"OptimizedRTNConfig",
"RRQConfig",
"SignRoundConfig",
"AdamRoundConfig",
"SignRoundV2Config",
"AWQConfig",
"RotationConfig",
"SpinQuantConfig",
"load_rrq_model",
Comment thread
luoyu-intel marked this conversation as resolved.
Outdated
"generate_rrq_residual",
]


def __getattr__(name):
"""Lazy import for heavy submodules to avoid import cycles / slow startup.

Currently exposes RRQ-related entry points:
- ``load_rrq_model``: combined base + residual loading (``auto_round.inference.rrq_model``)
- ``generate_rrq_residual``: Phase 2 incremental residual generation
(``auto_round.export.export_to_autoround.export_to_rrq``)
"""
if name == "load_rrq_model":
from auto_round.inference.rrq_model import load_rrq_model

return load_rrq_model
if name == "generate_rrq_residual":
from auto_round.export.export_to_autoround.export_to_rrq import generate_rrq_residual

return generate_rrq_residual
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
29 changes: 29 additions & 0 deletions auto_round/algorithms/quantization/rrq/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (c) 2026 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Recurrent Residual Quantization (RRQ) algorithm.

RRQ quantizes weights into multiple sequential INT2 planes (base + residuals)
using recursive RTN. Each plane is independently quantized in the weight
domain, so the accumulated representation at any prefix is the best
achievable within that bit budget.

Phase 1: RTN only (no sign-SGD tuning).
Comment thread
luoyu-intel marked this conversation as resolved.
Phase 3: per-plane sign-SGD tuning with a frozen prefix.
"""

from auto_round.algorithms.quantization.rrq.config import RRQConfig
from auto_round.algorithms.quantization.rrq.quantizer import RRQRTNQuantizer, RRQSignRoundQuantizer

__all__ = ["RRQConfig", "RRQRTNQuantizer", "RRQSignRoundQuantizer"]
196 changes: 196 additions & 0 deletions auto_round/algorithms/quantization/rrq/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# Copyright (c) 2026 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Configuration for Recurrent Residual Quantization (RRQ).

RRQ quantizes each weight tensor into K sequential INT2 planes (1 base +
K-1 residual planes). The total effective bit-width is K * plane_bits.

By default RRQ follows AutoRound's default SignRound optimization settings
(``iters=200``, automatic learning rates, zero momentum, and min/max tuning).
Passing ``iters=0`` explicitly selects the RTN-only mode. Every plane uses the
same optimization settings; residual planes differ only in their frozen prefix
and target residual.
"""

from auto_round.algorithms.config import AlgorithmParameterRegistry
from auto_round.algorithms.quantization.rtn.config import RTNConfig
from auto_round.algorithms.registry import register_algorithm


class RRQConfig(RTNConfig):
"""Configuration for Recurrent Residual Quantization.

Fixed in this version:
- bits = 2 (per plane)
- data_type = "int"
- act_bits = 16 (weight-only)
- num_residual_planes = 3 (total planes = 4, effective bits 2/4/6/8)

With the default ``iters=200``, every plane uses the same SignRound
optimization parameters as ordinary AutoRound. Passing ``iters=0``
explicitly selects RTN-only quantization without calibration.
Comment thread
luoyu-intel marked this conversation as resolved.
Outdated
"""

#: Number of INT2 residual planes (after the base plane).
#: Total planes = 1 (base) + num_residual_planes.
num_residual_planes: int = 3

def __init__(
self,
*,
iters: int = 200,
lr: float | None = None,
minmax_lr: float | None = None,
momentum: float = 0.0,
lr_scheduler=None,
enable_minmax_tuning: bool = True,
gradient_accumulate_steps: int = 1,
enable_quanted_input: bool = True,
not_use_best_mse: bool = False,
dynamic_max_gap: int = -1,
enable_lfq: bool = False,
num_residual_planes: int = 3,
disable_opt_rtn: bool | None = True,
**kwargs,
):
# Enforce fixed values
if "bits" in kwargs and kwargs["bits"] != 2:
raise ValueError(f"RRQ only supports bits=2 per plane, got {kwargs['bits']}")
if "data_type" in kwargs and kwargs["data_type"] != "int":
raise ValueError(f"RRQ only supports data_type='int', got {kwargs['data_type']!r}")
if "act_bits" in kwargs and kwargs["act_bits"] != 16:
raise ValueError(f"RRQ is weight-only; act_bits must be 16, got {kwargs['act_bits']}")

# RRQ-tunable fields (not scheme fields). Declared as named parameters so
# the CLI field-acceptance contract (see test_cli_usage) holds; stored
# before super() so the inherited RTN/Quantization __init__ doesn't drop
# them. Match SignRoundConfig's AutoRound default. RTN remains available
# via an explicit ``iters=0``.
self._rrq_iters = iters
self._rrq_lr = lr
self._rrq_minmax_lr = minmax_lr
self._rrq_momentum = momentum
self._rrq_lr_scheduler = lr_scheduler
self._rrq_enable_minmax_tuning = enable_minmax_tuning
self._rrq_gradient_accumulate_steps = gradient_accumulate_steps
self._rrq_enable_quanted_input = enable_quanted_input
self._rrq_not_use_best_mse = not_use_best_mse
self._rrq_dynamic_max_gap = dynamic_max_gap
self._rrq_enable_lfq = enable_lfq

# Inject fixed values
kwargs.setdefault("bits", 2)
kwargs.setdefault("data_type", "int")
kwargs.setdefault("act_bits", 16)

# num_residual_planes is not a scheme field
if num_residual_planes not in (1, 3):
raise ValueError(f"RRQ supports num_residual_planes=1 or 3, got {num_residual_planes}")
if num_residual_planes <= 0:
raise ValueError("num_residual_planes must be positive")
self._num_residual_planes = num_residual_planes

# ``disable_opt_rtn`` stays True at the config level purely to keep RRQ
# routed to its own quantizer: an RTNConfig subclass with
# ``disable_opt_rtn=False`` is silently coerced to OptimizedRTNConfig by
# the AutoRound entry, which would drop every residual plane. The
# per-plane RTN quality is matched to standard AutoRound (opt-RTN)
# inside the quantizer instead (see RRQRTNQuantizer). Passed explicitly
# (not via ``**kwargs``) since it is a named parameter here.
super().__init__(disable_opt_rtn=disable_opt_rtn, **kwargs)
Comment thread
luoyu-intel marked this conversation as resolved.

self.iters = int(self._rrq_iters or 0)
self.lr = self._rrq_lr
self.minmax_lr = self._rrq_minmax_lr
self.momentum = self._rrq_momentum
self.lr_scheduler = self._rrq_lr_scheduler
self.enable_minmax_tuning = self._rrq_enable_minmax_tuning
self.gradient_accumulate_steps = self._rrq_gradient_accumulate_steps
self.enable_quanted_input = self._rrq_enable_quanted_input
self.not_use_best_mse = self._rrq_not_use_best_mse
self.dynamic_max_gap = self._rrq_dynamic_max_gap
self.enable_lfq = self._rrq_enable_lfq
# Both paths use block calibration data: SignRound needs it for tuning,
# and the RTN path needs it to collect the imatrix for imatrix-weighted
# opt-RTN (matching an ordinary AutoRound W2A16 OptimizedRTN base).
self.need_calib = True

@property
def total_planes(self) -> int:
"""Total number of INT2 planes (base + residual)."""
return 1 + self._num_residual_planes

@property
def total_bits(self) -> int:
"""Total effective bit-width of the full representation."""
return self.bits * self.total_planes

@classmethod
def register_args(cls, registry):
"""Register CLI args for RRQ (Phase 1 common args + optional tuning)."""
super().register_args(registry)
registry.add_argument(
"--iters",
field="iters",
default=200,
type=int,
help="Iterations of per-plane sign-SGD tuning. 0 explicitly selects "
"pure RTN; the default 200 matches AutoRound SignRound.",
)
registry.add_argument("--lr", field="lr", default=None, type=float, help="Learning rate for the RRQ tuning.")
registry.add_argument(
"--minmax_lr", field="minmax_lr", default=None, type=float, help="Learning rate for min-max tuning."
)
registry.add_argument(
"--momentum", field="momentum", default=0.0, type=float, help="Momentum for the RRQ optimizer."
)

def _lr_for_bits(self, bits):
"""Auto lr heuristic for sign-SGD tuning (mirrors SignRound)."""
if self.iters <= 0:
return None
# Match SignRoundConfig._lr_for_bits: low-bit layers get a higher lr
# when the iteration budget is large.
if self.iters >= 1000 and bits is not None and bits <= 3:
return 2.0 / self.iters
return 1.0 / self.iters

def compute_lr(self, bits):
"""Resolve the rounding lr for a layer bit-width."""
if self.lr is not None:
return self.lr
return self._lr_for_bits(bits)

def compute_minmax_lr(self, bits):
"""Resolve the min-max tuning lr for a layer bit-width."""
if self.minmax_lr is not None:
return self.minmax_lr
return self.compute_lr(bits)

def check_config(self) -> None:
super().check_config()
# RRQ-specific validation
assert self.bits == 2, "RRQ requires bits=2"
assert self.data_type == "int", "RRQ only supports int data_type"
assert self.act_bits == 16, "RRQ is weight-only"
if self.iters < 0:
raise ValueError("`iters` must be non-negative")


register_algorithm(
"rrq",
aliases=("rrq", "rrq_rtn"),
config_factory=RRQConfig,
summary="Recurrent Residual Quantization: INT2 base + 3 residual planes (2/4/6/8-bit).",
)
Loading
Loading