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
4 changes: 2 additions & 2 deletions .azure-pipelines/scripts/ai_analysis/ai_analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def _call_copilot_cli(prompt: str, timeout: int, model: str, reasoning_effort: s
if ai_token:
env.setdefault("GITHUB_TOKEN", ai_token)
env.setdefault("GH_TOKEN", ai_token)
started = datetime.datetime.now(datetime.timezone.utc)
started = datetime.datetime.now(datetime.UTC)
returncode = None
stderr = ""
stdout = ""
Expand All @@ -169,7 +169,7 @@ def _call_copilot_cli(prompt: str, timeout: int, model: str, reasoning_effort: s
except (OSError, subprocess.TimeoutExpired) as e:
print(f"Warning: Copilot CLI call failed: {e}", file=sys.stderr)
stderr = str(e)
ended = datetime.datetime.now(datetime.timezone.utc)
ended = datetime.datetime.now(datetime.UTC)
parsed = _parse_copilot_json(stdout)
raw = parsed["answer"]
_append_trace(
Expand Down
2 changes: 1 addition & 1 deletion .azure-pipelines/scripts/compat_smoke_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
print(f"auto_round {auto_round.__version__} imported successfully (AutoRound={AutoRound.__name__})")

# Verify the console_scripts entry point was installed and is runnable.
result = subprocess.run(["auto-round", "--help"], capture_output=True, text=True)
result = subprocess.run(["auto-round", "--help"], capture_output=True, text=True, check=False)
if result.returncode != 0:
sys.stderr.write(result.stdout)
sys.stderr.write(result.stderr)
Expand Down
38 changes: 18 additions & 20 deletions .azure-pipelines/scripts/performance/check_performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Optional

logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)

LOG_DIR = Path("/auto-round/log_dir")
OUTPUT_BASE_DIR = Path("/auto-round/.azure-pipelines/scripts/performance")


@dataclass
class QuantMetrics:
tuning_time_s: Optional[float] = None
peak_ram_gb: Optional[float] = None
peak_vram_gb: Optional[float] = None
output_size_gb: Optional[float] = None
tuning_time_s: float | None = None
peak_ram_gb: float | None = None
peak_vram_gb: float | None = None
output_size_gb: float | None = None


def get_dir_size_gb(path: Path) -> float:
Expand All @@ -31,7 +31,7 @@ def parse_log_file(log_file: Path) -> QuantMetrics:
metrics = QuantMetrics()

if not log_file.exists():
logging.warning(f"Log file not found: {log_file}")
logger.warning(f"Log file not found: {log_file}")
return metrics

content = log_file.read_text(encoding="utf-8")
Expand All @@ -50,7 +50,7 @@ def parse_log_file(log_file: Path) -> QuantMetrics:
return metrics


def get_tuning_info() -> Dict[str, Dict[str, QuantMetrics]]:
def get_tuning_info() -> dict[str, dict[str, QuantMetrics]]:
summary = {}
model_list = ["Qwen/Qwen3-0.6B"]

Expand All @@ -60,7 +60,7 @@ def get_tuning_info() -> Dict[str, Dict[str, QuantMetrics]]:
log_file = LOG_DIR / f"perf_test_{test_mode}.log"
output_dir = OUTPUT_BASE_DIR / test_mode

logging.info(f"Processing {log_file}...")
logger.info(f"Processing {log_file}...")

metrics = parse_log_file(log_file)
metrics.output_size_gb = get_dir_size_gb(output_dir)
Expand All @@ -70,15 +70,13 @@ def get_tuning_info() -> Dict[str, Dict[str, QuantMetrics]]:
return summary


def compare_metric(
metric_name: str, current: Optional[float], baseline: Optional[float], tolerance: float = 0.1
) -> bool:
def compare_metric(metric_name: str, current: float | None, baseline: float | None, tolerance: float = 0.1) -> bool:
if current is None or baseline is None:
logging.error(f" [-] {metric_name}: Incomplete data (Current: {current}, Baseline: {baseline})")
logger.error(f" [-] {metric_name}: Incomplete data (Current: {current}, Baseline: {baseline})")
return False

if baseline == 0:
logging.warning(f" [!] {metric_name}: Baseline is 0, cannot calculate ratio.")
logger.warning(f" [!] {metric_name}: Baseline is 0, cannot calculate ratio.")
return False

ratio = current / baseline
Expand All @@ -87,10 +85,10 @@ def compare_metric(
msg = f" [*] {metric_name:<20}: Current = {current:<8} | Baseline = {baseline:<8} (Diff: {diff_percent:+.2f}%)"

if 1.0 - tolerance <= ratio <= 1.0 + tolerance:
logging.info(f"{msg} -> PASS")
logger.info(f"{msg} -> PASS")
return True
else:
logging.error(f"{msg} -> FAIL")
logger.error(f"{msg} -> FAIL")
return False


Expand All @@ -99,8 +97,8 @@ def check_performance():
all_passed = True

for model, modes in summary.items():
logging.info(f"\nEvaluating Model: {model}")
logging.info("-" * 60)
logger.info(f"\nEvaluating Model: {model}")
logger.info("-" * 60)

current: QuantMetrics = modes.get("current", QuantMetrics())
baseline: QuantMetrics = modes.get("baseline", QuantMetrics())
Expand All @@ -117,11 +115,11 @@ def check_performance():
if not compare_metric("Output Size (GB)", current.output_size_gb, baseline.output_size_gb, tolerance=0.01):
all_passed = False

logging.info("=" * 60)
logger.info("=" * 60)
if all_passed:
logging.info("✅ Performance check passed: All metrics are within acceptable limits.")
logger.info("✅ Performance check passed: All metrics are within acceptable limits.")
else:
logging.error("❌ Performance check failed: Current metrics exceed acceptable limits compared to baseline.")
logger.error("❌ Performance check failed: Current metrics exceed acceptable limits compared to baseline.")
sys.exit(1)


Expand Down
6 changes: 4 additions & 2 deletions .azure-pipelines/scripts/ut/collect_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,10 @@ def generate(self, test_type: str, results: list[TestResult]) -> None:
self._format_subheader(),
*(self._format_row(r) for r in results),
self.SEPARATOR,
f"Total: {stats['total']}, Passed: {stats['passed']}, "
f"Failed: {stats['failed']}, Skipped: {stats['skipped']}",
(
f"Total: {stats['total']}, Passed: {stats['passed']}, "
f"Failed: {stats['failed']}, Skipped: {stats['skipped']}"
),
self.SEPARATOR,
"",
]
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ repos:
exclude: ^auto_round/export/export_to_gguf/conversion/.*\.py$

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.20
rev: v0.16.9
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix, --no-cache]
Expand Down
14 changes: 7 additions & 7 deletions auto_round/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,20 @@
from .version import __version__

__all__ = [
"__version__",
"AWQConfig",
"AdamRoundConfig",
"AutoRound",
"AutoRoundLLM",
"AutoRoundMLLM",
"AutoRoundAdam",
"AutoRoundDiffusion",
"AutoRoundLLM",
"AutoRoundMLLM",
"AutoScheme",
"OptimizedRTNConfig",
"QuantizationScheme",
"RTNConfig",
"OptimizedRTNConfig",
"RotationConfig",
"SignRoundConfig",
"AdamRoundConfig",
"SignRoundV2Config",
"AWQConfig",
"RotationConfig",
"SpinQuantConfig",
"__version__",
]
2 changes: 1 addition & 1 deletion auto_round/algorithms/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __init__(self, config: Any = None) -> None:
self.config = config
# Name-mangled so subclasses cannot accidentally overwrite the run context.
self.__run_ctx: QuantizationRunContext | None = None
self.__block_forward_runner: "BlockForwardRunner | None" = None
self.__block_forward_runner: BlockForwardRunner | None = None

@classmethod
def from_config(cls, config: Any) -> "BaseAlgorithm":
Expand Down
14 changes: 7 additions & 7 deletions auto_round/algorithms/block_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Union
from typing import TYPE_CHECKING, Any

import torch

Expand Down Expand Up @@ -115,8 +115,8 @@ def __init__(
self,
batch_dim: int = 0,
batch_size: int = 8,
device: Union[str, "torch.device"] = "cpu",
cache_device: Union[str, "torch.device"] = "cpu",
device: str | torch.device = "cpu",
cache_device: str | torch.device = "cpu",
amp: bool = True,
amp_dtype: torch.dtype | None = None,
is_diffusion: bool = False,
Expand Down Expand Up @@ -144,7 +144,7 @@ def __init__(
# ── Factory ──────────────────────────────────────────────────────────────

@classmethod
def from_orchestrator(cls, orchestrator: "BaseOrchestrator", enable_torch_compile=True) -> "BlockForwardRunner":
def from_orchestrator(cls, orchestrator: BaseOrchestrator, enable_torch_compile=True) -> BlockForwardRunner:
"""Create from an orchestrator instance (called once at orchestrator init)."""
model_ctx = getattr(orchestrator, "model_context", None)
is_diffusion = getattr(model_ctx, "is_diffusion", False) if model_ctx else False
Expand All @@ -170,7 +170,7 @@ def __call__(self, *args, **kwargs) -> torch.Tensor:

def forward(
self,
block: "torch.nn.Module",
block: torch.nn.Module,
inputs: list[torch.Tensor] | dict,
input_others: dict,
indices: torch.Tensor | None = None,
Expand Down Expand Up @@ -310,7 +310,7 @@ def _count_samples(self, inputs: Any) -> int:
else:
return inputs.shape[self.batch_dim]

def _normalize_output(self, output: Any, block: "torch.nn.Module" = None) -> torch.Tensor:
def _normalize_output(self, output: Any, block: torch.nn.Module = None) -> torch.Tensor:
"""Normalize block output to a single tensor."""
if isinstance(output, torch.Tensor):
return output
Expand Down Expand Up @@ -343,7 +343,7 @@ def _normalize_output(self, output: Any, block: "torch.nn.Module" = None) -> tor
return first
raise TypeError(f"Block output[0] must be tensor, got {type(first).__name__}.")

def _get_output_dict(self, output: Any, block: "torch.nn.Module" = None) -> dict[str, torch.Tensor] | None:
def _get_output_dict(self, output: Any, block: torch.nn.Module = None) -> dict[str, torch.Tensor] | None:
if isinstance(output, torch.Tensor) or not isinstance(output, (tuple, list)):
return None
block_cls_name = block.__class__.__name__ if block is not None else None
Expand Down
30 changes: 14 additions & 16 deletions auto_round/algorithms/composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class BlockContext:
``ValueError`` with a user-readable message.
"""

model: "torch.nn.Module"
model: torch.nn.Module
block_names: list[str] # scheduling group; len > 1 when nblocks > 1
block_name: str # = block_names[0] for single-block; descriptive label for multi
block_index: int # 0-based index within the current all_blocks group
Expand Down Expand Up @@ -98,7 +98,7 @@ class AlgorithmComposer:
composer = AlgorithmComposer(configs, compressor=self)
"""

def __init__(self, configs: list, orchestrator: "BaseOrchestrator" = None) -> None:
def __init__(self, configs: list, orchestrator: BaseOrchestrator = None) -> None:
"""Build the pipeline from a list of algorithm config instances.

Resolution rules:
Expand Down Expand Up @@ -240,7 +240,7 @@ def __init__(self, configs: list, orchestrator: "BaseOrchestrator" = None) -> No

# ── Internal hook helpers (act_max calibration) ───────────────────────────

def _register_act_max_hooks(self, block: "torch.nn.Module") -> list:
def _register_act_max_hooks(self, block: torch.nn.Module) -> list:
"""Register per-module act_max tracking hooks for static activation quantization.

Returns a list of hook handles that the caller must remove when done.
Expand Down Expand Up @@ -301,7 +301,7 @@ def should_collect(name, module):
handles.append(module.register_forward_hook(collect_act_max))
return handles

def _get_fp_act_hooks(self, block: "torch.nn.Module") -> list:
def _get_fp_act_hooks(self, block: torch.nn.Module) -> list:
"""Register FP-input act_max + quantizer forward hooks."""
if not self.need_quanted_input():
# If having q_input, act_max will be collected in q_input forward hook,
Expand All @@ -312,13 +312,13 @@ def _get_fp_act_hooks(self, block: "torch.nn.Module") -> list:
handles.extend(self.block_quantizer.register_fp_input_forward_hooks(block))
return handles

def _get_q_act_hooks(self, block: "torch.nn.Module") -> list:
def _get_q_act_hooks(self, block: torch.nn.Module) -> list:
"""Register Q-input act_max + quantizer forward hooks."""
handles = self._register_act_max_hooks(block)
handles.extend(self.block_quantizer.register_qinput_forward_hooks(block))
return handles

def _attach_act_max_for_outside_layer(self, layer: "torch.nn.Module", fp_inputs, q_inputs) -> None:
def _attach_act_max_for_outside_layer(self, layer: torch.nn.Module, fp_inputs, q_inputs) -> None:
"""Compute and attach act_max for an outside-block layer from cached inputs.

Mirrors the hook-based act_max collection done for in-block layers, but
Expand All @@ -334,10 +334,10 @@ def _attach_act_max_for_outside_layer(self, layer: "torch.nn.Module", fp_inputs,
from auto_round.data_type.utils import reshape_pad_tensor_by_group_size

target_input = q_inputs or fp_inputs
act_group_size = getattr(layer, "act_group_size")
act_group_size = layer.act_group_size
if act_group_size is None:
act_group_size = layer.group_size
act_data_type = getattr(layer, "act_data_type")
act_data_type = layer.act_data_type
if act_data_type is None:
act_data_type = layer.data_type
is_act_nv_fp_flag = is_nv_fp(act_data_type) if act_data_type else False
Expand Down Expand Up @@ -368,9 +368,7 @@ def need_quanted_input(self):
for preprocessor in self.preprocessors:
if getattr(preprocessor, "enable_quanted_input", False):
return True
if getattr(self.block_quantizer, "enable_quanted_input", False):
return True
return False
return bool(getattr(self.block_quantizer, "enable_quanted_input", False))

def compress_embedding_layer(self):
return self.block_quantizer.quantize_embedding_layer()
Expand Down Expand Up @@ -512,7 +510,7 @@ def compress_block(

def compress_layer_outside_block(
self,
layer: "torch.nn.Module",
layer: torch.nn.Module,
fp_inputs=None,
q_inputs=None,
disable_opt_rtn=None, # TODO wenhuach rename this to search_init_scale
Expand All @@ -537,7 +535,7 @@ def compress_layer_outside_block(
if fp_inputs is not None:
from auto_round.compressors.utils import is_nv_fp

act_data_type = getattr(layer, "act_data_type")
act_data_type = layer.act_data_type
if act_data_type is None:
act_data_type = "fp"
act_dynamic = getattr(layer, "act_dynamic", True)
Expand Down Expand Up @@ -569,7 +567,7 @@ def members(self) -> list:
"""
return list(self.preprocessors) + list(self._rotation_members) + [self.block_quantizer]

def dispatch_block(self, block: "torch.nn.Module", input_ids, input_others: dict):
def dispatch_block(self, block: torch.nn.Module, input_ids, input_others: dict):
"""Dispatch block to device(s) via the pipeline's algorithms.

Iterates all members; if exactly one overrides the default dispatch_block,
Expand Down Expand Up @@ -597,7 +595,7 @@ def dispatch_block(self, block: "torch.nn.Module", input_ids, input_others: dict
return overriders[0].dispatch_block(block, input_ids, input_others)
return self.block_quantizer.dispatch_block(block, input_ids, input_others)

def prepare_run(self, composer: "AlgorithmComposer" = None):
def prepare_run(self, composer: AlgorithmComposer = None):
for alg in self.members():
alg.prepare_run(composer=self)

Expand Down Expand Up @@ -626,7 +624,7 @@ def _resolve_rotation_data_type(self) -> str:
return getattr(self.block_quantizer.config, "data_type", "mx_fp")
return "mx_fp"

def apply_model_transforms(self, model: "torch.nn.Module") -> "torch.nn.Module":
def apply_model_transforms(self, model: torch.nn.Module) -> torch.nn.Module:
"""Apply model-level pre-quantisation transforms (rotation) to *model*.

Generic entry point invoked once by the orchestrator before calibration
Expand Down
2 changes: 1 addition & 1 deletion auto_round/algorithms/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def dest(self) -> str:


class _MutuallyExclusiveParameterRegistry:
def __init__(self, registry: "AlgorithmParameterRegistry", group_id: int) -> None:
def __init__(self, registry: AlgorithmParameterRegistry, group_id: int) -> None:
self._registry = registry
self._group_id = group_id

Expand Down
2 changes: 0 additions & 2 deletions auto_round/algorithms/quantization/adam_round/adam.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ def _get_optimizer(self, optimizer):
optimizer = torch.optim.AdamW
elif isinstance(optimizer, str):
optimizer = getattr(torch.optim, optimizer)
else:
optimizer = optimizer
return optimizer

def _get_scaler(self):
Expand Down
Loading
Loading