94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
211 lines
7.3 KiB
Python
211 lines
7.3 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import re
|
|
from collections.abc import Iterable, Mapping
|
|
from types import MappingProxyType
|
|
from typing import Any, Optional
|
|
|
|
import torch
|
|
|
|
try:
|
|
from aiter.ops.triton.quant import dynamic_mxfp4_quant
|
|
except ImportError:
|
|
|
|
def raise_aiter_import_error(*args, **kwargs):
|
|
raise ImportError(
|
|
"Failed to import aiter. Make sure AITER is installed and accessible."
|
|
)
|
|
|
|
dynamic_mxfp4_quant = raise_aiter_import_error
|
|
from torch import nn
|
|
|
|
|
|
def deep_compare(dict1: Any, dict2: Any) -> bool:
|
|
if type(dict1) is not type(dict2):
|
|
return False
|
|
if isinstance(dict1, dict):
|
|
if dict1.keys() != dict2.keys():
|
|
return False
|
|
return all(deep_compare(dict1[k], dict2[k]) for k in dict1)
|
|
elif isinstance(dict1, list):
|
|
return set(dict1) == set(dict2)
|
|
else:
|
|
return dict1 == dict2
|
|
|
|
|
|
def should_ignore_layer(
|
|
layer_name: Optional[str],
|
|
ignore: Iterable[str],
|
|
fused_mapping: Mapping[str, list[str]] = MappingProxyType({}),
|
|
) -> bool:
|
|
if layer_name is None:
|
|
return False
|
|
|
|
# layer_name = model.layers.0.self_attn.qkv_proj
|
|
# proj_name = qkv_proj
|
|
proj_name = layer_name.split(".")[-1]
|
|
|
|
# Fused layers like gate_up_proj or qkv_proj will not be fused
|
|
# in the safetensors checkpoint. So, we convert the name
|
|
# from the fused version to unfused + check to make sure that
|
|
# each shard of the fused layer has the same scheme.
|
|
if proj_name in fused_mapping:
|
|
shard_proj_names = fused_mapping[proj_name]
|
|
|
|
# Convert fused_name --> [shard_names]
|
|
shard_names = [
|
|
layer_name.replace(proj_name, shard_proj_name)
|
|
for shard_proj_name in shard_proj_names
|
|
]
|
|
|
|
# Layer should be ignored if shards are ignored.
|
|
should_ignore_layer = None
|
|
for shard_name in shard_names:
|
|
should_ignore_shard = check_equal_or_regex_match(
|
|
layer_name=shard_name, targets=ignore
|
|
)
|
|
|
|
# If shard_idx=0, set layer ignore to match shard.
|
|
if should_ignore_layer is None:
|
|
should_ignore_layer = should_ignore_shard
|
|
|
|
# If shard_idx=1+ confirm scheme matches prior shards.
|
|
elif should_ignore_shard != should_ignore_layer:
|
|
raise ValueError(
|
|
f"Found different quantization schemes for "
|
|
f"{shard_proj_names} in {layer_name}. SGLang "
|
|
"requires all to use the same scheme."
|
|
)
|
|
|
|
# Unfused layers like down_proj and o_proj will match
|
|
# the safetensors checkpoint already.
|
|
else:
|
|
should_ignore_layer = check_equal_or_regex_match(
|
|
layer_name=layer_name, targets=ignore
|
|
)
|
|
|
|
assert should_ignore_layer is not None
|
|
|
|
return should_ignore_layer
|
|
|
|
|
|
def check_equal_or_regex_match(layer_name: str, targets: Iterable[str]) -> bool:
|
|
"""
|
|
Checks whether a layer_name is exactly equal or a regex match for
|
|
if target starts with 're:' to any target in list.
|
|
"""
|
|
for target in targets:
|
|
if _is_equal_or_regex_match(layer_name, target):
|
|
return True
|
|
return False
|
|
|
|
|
|
def _is_equal_or_regex_match(
|
|
value: str, target: str, check_contains: bool = False
|
|
) -> bool:
|
|
"""
|
|
Checks whether a value is exactly equal or a regex match for target
|
|
if target starts with 're:'. If check_contains is set to True,
|
|
additionally checks if the target string is contained within the value.
|
|
"""
|
|
|
|
if target.startswith("re:"):
|
|
pattern = target[3:]
|
|
if re.match(pattern, value):
|
|
return True
|
|
elif check_contains:
|
|
if target.lower() in value.lower():
|
|
return True
|
|
elif target == value:
|
|
return True
|
|
return False
|
|
|
|
|
|
# utility for tensor dims > 2 cases
|
|
def b_dynamic_mxfp4_quant(x):
|
|
h, b, d = x.shape
|
|
x, x_scales = dynamic_mxfp4_quant(x.reshape(-1, d))
|
|
return x.view(h, b, d // 2), x_scales.view(h, b, d // 32)
|
|
|
|
|
|
def mxfp4_to_f32(x, is_3d):
|
|
# 2 because we pack fp4 in uint8.
|
|
x = x.repeat_interleave(2, dim=-1)
|
|
if is_3d:
|
|
x[..., ::2] = x[..., ::2] & 0xF
|
|
x[..., 1::2] = x[..., 1::2] >> 4
|
|
else:
|
|
x[:, ::2] = x[:, ::2] & 0xF
|
|
x[:, 1::2] = x[:, 1::2] >> 4
|
|
|
|
mxfp4_list = [
|
|
0.0,
|
|
0.5,
|
|
1.0,
|
|
1.5,
|
|
2.0,
|
|
3.0,
|
|
4.0,
|
|
6.0,
|
|
-0.0,
|
|
-0.5,
|
|
-1.0,
|
|
-1.5,
|
|
-2.0,
|
|
-3.0,
|
|
-4.0,
|
|
-6.0,
|
|
]
|
|
mxfp4_in_f32 = torch.tensor(mxfp4_list, dtype=torch.float32, device="cuda")
|
|
return mxfp4_in_f32[x.long()]
|
|
|
|
|
|
def e8m0_to_f32(x):
|
|
# Per OCP MX-format v1.0: encoded 0..254 -> 2^(x-127); encoded 255 -> NaN.
|
|
# Detect the sentinel on the raw integer encoding, not on the float result
|
|
# (in float32, 2^128 overflows to +inf, so the old `x_f32 == 128` predicate
|
|
# both missed x=255 and wrongly NaN'd legitimate scale 128.0 at x=134).
|
|
x_f32 = 2 ** ((x.to(torch.float32)) - 127)
|
|
x_f32[x == 255] = float("nan")
|
|
return x_f32
|
|
|
|
|
|
def quark_post_load_weights(self_attn: nn.Module, w: torch.Tensor, quant_format: str):
|
|
if "mxfp4" in quant_format:
|
|
# when dtype is bf16, the processing flow is to dynamic quantize bf16 tensor to uint8 tensor
|
|
# do w_kc (bf16) first to get the w_kc(uint8) w_s_kc(uint8)
|
|
# and w_vc repeating the same procedure of w_kc to get w_vc(uint8) w_s_vc(uint8)
|
|
if w.dtype == torch.bfloat16:
|
|
w_kc, w_vc = w.unflatten(
|
|
0, (-1, self_attn.qk_nope_head_dim + self_attn.v_head_dim)
|
|
).split([self_attn.qk_nope_head_dim, self_attn.v_head_dim], dim=1)
|
|
w_kc, w_s_kc = b_dynamic_mxfp4_quant(w_kc.transpose(-2, -1))
|
|
w_kc = w_kc.transpose(-2, -1)
|
|
w_s_kc = w_s_kc.transpose(-2, -1)
|
|
w_vc, w_s_vc = b_dynamic_mxfp4_quant(w_vc)
|
|
w_s_kc = w_s_kc.transpose(1, 2).contiguous().transpose(1, 2)
|
|
w_s_vc = w_s_vc.contiguous().transpose(1, 2)
|
|
elif w.dtype == torch.uint8: # static quant for mxfp4
|
|
# when dtype is uint8, it means the w has been quantized to mxfp4 format
|
|
# but we must separate it to w_kc and w_vc.
|
|
# The quantized tensor size is only half of original tensor size
|
|
# and the scaling factor is 1/32, the transpose behavior will be not correct
|
|
# need to upcast it to fp32 to separate w to w_kc and w_vc
|
|
# to ensure the following transpose behavior is correct
|
|
# and then do mxfp4 quant again
|
|
w = mxfp4_to_f32(w, True).to(torch.bfloat16)
|
|
w_scales = self_attn.kv_b_proj.weight_scale.repeat_interleave(32, dim=-1)
|
|
w_scales = e8m0_to_f32(w_scales).to(torch.bfloat16)
|
|
w = w * w_scales
|
|
w_kc, w_vc = w.unflatten(
|
|
0, (-1, (self_attn.qk_nope_head_dim + self_attn.v_head_dim))
|
|
).split([self_attn.qk_nope_head_dim, self_attn.v_head_dim], dim=1)
|
|
w_kc, w_s_kc = b_dynamic_mxfp4_quant(w_kc.transpose(-2, -1))
|
|
w_kc = w_kc.transpose(-2, -1)
|
|
w_s_kc = w_s_kc.transpose(-2, -1)
|
|
w_vc, w_s_vc = b_dynamic_mxfp4_quant(w_vc)
|
|
w_s_kc = w_s_kc.transpose(1, 2).contiguous().transpose(1, 2)
|
|
w_s_vc = w_s_vc.contiguous().transpose(1, 2)
|
|
|
|
return w_kc, w_s_kc, w_vc, w_s_vc
|