Upload folder using huggingface_hub
Browse files- vllm-patches/A_wna16_marlin.py +537 -0
- vllm-patches/B_int_wna16.py +917 -0
- vllm-patches/C_config.py +1436 -0
- vllm-patches/PATCHES.md +32 -0
- vllm-patches/launch_m3_awq.sh +30 -0
vllm-patches/A_wna16_marlin.py
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
| 3 |
+
|
| 4 |
+
from typing import Any
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from compressed_tensors.quantization import (
|
| 8 |
+
QuantizationArgs,
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
from vllm.logger import init_logger
|
| 12 |
+
from vllm.model_executor.layers.fused_moe import (
|
| 13 |
+
RoutedExperts,
|
| 14 |
+
SharedExperts,
|
| 15 |
+
)
|
| 16 |
+
from vllm.model_executor.layers.fused_moe.config import (
|
| 17 |
+
FusedMoEConfig,
|
| 18 |
+
FusedMoEQuantConfig,
|
| 19 |
+
)
|
| 20 |
+
from vllm.model_executor.layers.fused_moe.oracle.int_wna16 import (
|
| 21 |
+
WNA16MoEBackend,
|
| 22 |
+
convert_to_wna16_moe_kernel_format,
|
| 23 |
+
make_wna16_moe_kernel,
|
| 24 |
+
make_wna16_moe_quant_config,
|
| 25 |
+
select_wna16_moe_backend,
|
| 26 |
+
)
|
| 27 |
+
from vllm.model_executor.layers.quantization.compressed_tensors.compressed_tensors_moe import ( # noqa E501
|
| 28 |
+
CompressedTensorsMoEMethod,
|
| 29 |
+
)
|
| 30 |
+
from vllm.model_executor.layers.quantization.compressed_tensors.schemes.compressed_tensors_wNa16 import ( # noqa
|
| 31 |
+
WNA16_SUPPORTED_TYPES_MAP,
|
| 32 |
+
WNA16_ZP_SUPPORTED_TYPES_MAP,
|
| 33 |
+
)
|
| 34 |
+
from vllm.model_executor.layers.quantization.utils.marlin_utils import (
|
| 35 |
+
get_marlin_input_dtype,
|
| 36 |
+
marlin_make_workspace_new,
|
| 37 |
+
)
|
| 38 |
+
from vllm.model_executor.layers.quantization.utils.quant_utils import (
|
| 39 |
+
QuantKey,
|
| 40 |
+
kInt4Static32GroupScale,
|
| 41 |
+
kInt4StaticGroupScale,
|
| 42 |
+
kInt8StaticGroupScale,
|
| 43 |
+
)
|
| 44 |
+
from vllm.model_executor.utils import replace_parameter, set_weight_attrs
|
| 45 |
+
|
| 46 |
+
logger = init_logger(__name__)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class CompressedTensorsWNA16MarlinMoEMethod(CompressedTensorsMoEMethod):
|
| 50 |
+
def __init__(
|
| 51 |
+
self,
|
| 52 |
+
weight_quant: QuantizationArgs,
|
| 53 |
+
input_quant: QuantizationArgs | None,
|
| 54 |
+
moe: FusedMoEConfig,
|
| 55 |
+
layer_name: str | None = None,
|
| 56 |
+
):
|
| 57 |
+
super().__init__(moe)
|
| 58 |
+
self.weight_quant = weight_quant
|
| 59 |
+
self.input_quant = input_quant
|
| 60 |
+
self.symmetric = weight_quant.symmetric
|
| 61 |
+
# Extract properties from weight_quant
|
| 62 |
+
self.num_bits = weight_quant.num_bits
|
| 63 |
+
self.packed_factor = 32 // weight_quant.num_bits
|
| 64 |
+
self.strategy = weight_quant.strategy
|
| 65 |
+
self.group_size = weight_quant.group_size
|
| 66 |
+
self.actorder = weight_quant.actorder
|
| 67 |
+
|
| 68 |
+
self.quant_type = (
|
| 69 |
+
WNA16_SUPPORTED_TYPES_MAP[self.num_bits]
|
| 70 |
+
if self.symmetric
|
| 71 |
+
else WNA16_ZP_SUPPORTED_TYPES_MAP[self.num_bits]
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
self.marlin_input_dtype = get_marlin_input_dtype(layer_name)
|
| 75 |
+
|
| 76 |
+
if self.num_bits == 4:
|
| 77 |
+
if self.group_size == 32:
|
| 78 |
+
scale = kInt4Static32GroupScale
|
| 79 |
+
else:
|
| 80 |
+
scale = kInt4StaticGroupScale
|
| 81 |
+
elif self.num_bits == 8:
|
| 82 |
+
assert self.group_size == -1
|
| 83 |
+
scale = kInt8StaticGroupScale
|
| 84 |
+
else:
|
| 85 |
+
raise ValueError(
|
| 86 |
+
"CompressedTensorsWNA16MarlinMoEMethod only supports int4 and int8 now."
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
weight_key = QuantKey(self.quant_type, scale, symmetric=self.symmetric)
|
| 90 |
+
|
| 91 |
+
# Select WNA16 MoE backend via oracle.
|
| 92 |
+
self.wna16_backend, self.experts_cls = select_wna16_moe_backend(
|
| 93 |
+
config=self.moe,
|
| 94 |
+
weight_key=weight_key,
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
def get_weight_shape(
|
| 98 |
+
self,
|
| 99 |
+
weight_name: str,
|
| 100 |
+
num_experts: int,
|
| 101 |
+
hidden_size: int,
|
| 102 |
+
intermediate_size_per_partition: int,
|
| 103 |
+
num_groups_w2: int | None = None,
|
| 104 |
+
num_groups_w13: int | None = None,
|
| 105 |
+
) -> tuple[int, int, int]:
|
| 106 |
+
"""
|
| 107 |
+
Get the shape of the weight based on the weight name, number of experts
|
| 108 |
+
hidden size, intermediate size per partition, number of groups for w2,
|
| 109 |
+
and number of groups for w13. Pass in num_groups_w2 and num_groups_w13
|
| 110 |
+
for weight scales/zero_points.
|
| 111 |
+
"""
|
| 112 |
+
if weight_name in ("w13_scale", "w13_zp"):
|
| 113 |
+
assert num_groups_w13 is not None, (
|
| 114 |
+
"num_groups_w13 must be provided for weight scales/zero_points"
|
| 115 |
+
)
|
| 116 |
+
if weight_name in ("w2_scale", "w2_zp"):
|
| 117 |
+
assert num_groups_w2 is not None, (
|
| 118 |
+
"num_groups_w2 must be provided for weight scales/zero_points"
|
| 119 |
+
)
|
| 120 |
+
w13_num_shards = 2 if self.moe.is_act_and_mul else 1
|
| 121 |
+
is_flashinfer = self.wna16_backend == WNA16MoEBackend.FLASHINFER_TRTLLM
|
| 122 |
+
shape_map = {
|
| 123 |
+
"w13_weight": {
|
| 124 |
+
"Flashinfer": (
|
| 125 |
+
num_experts,
|
| 126 |
+
w13_num_shards * intermediate_size_per_partition,
|
| 127 |
+
hidden_size // self.packed_factor,
|
| 128 |
+
),
|
| 129 |
+
"Marlin": (
|
| 130 |
+
num_experts,
|
| 131 |
+
hidden_size // self.packed_factor,
|
| 132 |
+
w13_num_shards * intermediate_size_per_partition,
|
| 133 |
+
),
|
| 134 |
+
},
|
| 135 |
+
"w13_scale": {
|
| 136 |
+
"Flashinfer": (
|
| 137 |
+
num_experts,
|
| 138 |
+
w13_num_shards * intermediate_size_per_partition,
|
| 139 |
+
num_groups_w13,
|
| 140 |
+
),
|
| 141 |
+
"Marlin": (
|
| 142 |
+
num_experts,
|
| 143 |
+
num_groups_w13,
|
| 144 |
+
w13_num_shards * intermediate_size_per_partition,
|
| 145 |
+
),
|
| 146 |
+
},
|
| 147 |
+
"w13_zp": {
|
| 148 |
+
"Marlin": (
|
| 149 |
+
num_experts,
|
| 150 |
+
num_groups_w13,
|
| 151 |
+
w13_num_shards
|
| 152 |
+
* intermediate_size_per_partition
|
| 153 |
+
// self.packed_factor,
|
| 154 |
+
),
|
| 155 |
+
},
|
| 156 |
+
"w2_weight": {
|
| 157 |
+
"Flashinfer": (
|
| 158 |
+
num_experts,
|
| 159 |
+
hidden_size,
|
| 160 |
+
intermediate_size_per_partition // self.packed_factor,
|
| 161 |
+
),
|
| 162 |
+
"Marlin": (
|
| 163 |
+
num_experts,
|
| 164 |
+
intermediate_size_per_partition // self.packed_factor,
|
| 165 |
+
hidden_size,
|
| 166 |
+
),
|
| 167 |
+
},
|
| 168 |
+
"w2_scale": {
|
| 169 |
+
"Flashinfer": (num_experts, hidden_size, num_groups_w2),
|
| 170 |
+
"Marlin": (num_experts, num_groups_w2, hidden_size),
|
| 171 |
+
},
|
| 172 |
+
"w2_zp": {
|
| 173 |
+
"Marlin": (
|
| 174 |
+
num_experts,
|
| 175 |
+
num_groups_w2,
|
| 176 |
+
hidden_size // self.packed_factor,
|
| 177 |
+
),
|
| 178 |
+
},
|
| 179 |
+
}
|
| 180 |
+
backend_key = "Flashinfer" if is_flashinfer else "Marlin"
|
| 181 |
+
return shape_map[weight_name][backend_key]
|
| 182 |
+
|
| 183 |
+
def create_weights(
|
| 184 |
+
self,
|
| 185 |
+
layer: torch.nn.Module,
|
| 186 |
+
num_experts: int,
|
| 187 |
+
hidden_size: int,
|
| 188 |
+
intermediate_size_per_partition: int,
|
| 189 |
+
params_dtype: torch.dtype,
|
| 190 |
+
**extra_weight_attrs,
|
| 191 |
+
):
|
| 192 |
+
intermediate_size_full = extra_weight_attrs.pop("intermediate_size_full")
|
| 193 |
+
|
| 194 |
+
# Will transpose the loaded weight along the
|
| 195 |
+
# intermediate and hidden dim sizes. Will
|
| 196 |
+
# shard for TP along the transposed dims
|
| 197 |
+
is_transposed = self.wna16_backend != WNA16MoEBackend.FLASHINFER_TRTLLM
|
| 198 |
+
extra_weight_attrs.update(
|
| 199 |
+
{"is_transposed": is_transposed, "quant_method": self.strategy}
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
w13_weight = torch.nn.Parameter(
|
| 203 |
+
torch.empty(
|
| 204 |
+
*self.get_weight_shape(
|
| 205 |
+
"w13_weight",
|
| 206 |
+
num_experts,
|
| 207 |
+
hidden_size,
|
| 208 |
+
intermediate_size_per_partition,
|
| 209 |
+
),
|
| 210 |
+
dtype=torch.int32,
|
| 211 |
+
),
|
| 212 |
+
requires_grad=False,
|
| 213 |
+
)
|
| 214 |
+
layer.register_parameter("w13_weight_packed", w13_weight)
|
| 215 |
+
set_weight_attrs(w13_weight, extra_weight_attrs)
|
| 216 |
+
|
| 217 |
+
w2_weight = torch.nn.Parameter(
|
| 218 |
+
torch.empty(
|
| 219 |
+
*self.get_weight_shape(
|
| 220 |
+
"w2_weight",
|
| 221 |
+
num_experts,
|
| 222 |
+
hidden_size,
|
| 223 |
+
intermediate_size_per_partition,
|
| 224 |
+
),
|
| 225 |
+
dtype=torch.int32,
|
| 226 |
+
),
|
| 227 |
+
requires_grad=False,
|
| 228 |
+
)
|
| 229 |
+
layer.register_parameter("w2_weight_packed", w2_weight)
|
| 230 |
+
set_weight_attrs(w2_weight, extra_weight_attrs)
|
| 231 |
+
|
| 232 |
+
# In the case where we have actorder/g_idx,
|
| 233 |
+
# we do not partition the w2 scales
|
| 234 |
+
load_full_w2 = self.actorder and self.group_size != -1
|
| 235 |
+
w2_scales_size = (
|
| 236 |
+
intermediate_size_full if load_full_w2 else intermediate_size_per_partition
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
self.is_k_full = (not self.actorder) or (
|
| 240 |
+
intermediate_size_per_partition == intermediate_size_full
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
if self.strategy == "channel":
|
| 244 |
+
num_groups_w2 = num_groups_w13 = 1
|
| 245 |
+
self.group_size = -1
|
| 246 |
+
else:
|
| 247 |
+
num_groups_w2 = w2_scales_size // self.group_size
|
| 248 |
+
num_groups_w13 = hidden_size // self.group_size
|
| 249 |
+
|
| 250 |
+
layer.num_groups_w13 = num_groups_w13
|
| 251 |
+
layer.num_groups_w2 = num_groups_w2
|
| 252 |
+
|
| 253 |
+
w13_scale = torch.nn.Parameter(
|
| 254 |
+
torch.ones(
|
| 255 |
+
*self.get_weight_shape(
|
| 256 |
+
"w13_scale",
|
| 257 |
+
num_experts,
|
| 258 |
+
hidden_size,
|
| 259 |
+
intermediate_size_per_partition,
|
| 260 |
+
num_groups_w13=num_groups_w13,
|
| 261 |
+
),
|
| 262 |
+
dtype=params_dtype,
|
| 263 |
+
),
|
| 264 |
+
requires_grad=False,
|
| 265 |
+
)
|
| 266 |
+
layer.register_parameter("w13_weight_scale", w13_scale)
|
| 267 |
+
set_weight_attrs(w13_scale, extra_weight_attrs)
|
| 268 |
+
|
| 269 |
+
w2_scale = torch.nn.Parameter(
|
| 270 |
+
torch.ones(
|
| 271 |
+
*self.get_weight_shape(
|
| 272 |
+
"w2_scale",
|
| 273 |
+
num_experts,
|
| 274 |
+
hidden_size,
|
| 275 |
+
intermediate_size_per_partition,
|
| 276 |
+
num_groups_w2=num_groups_w2,
|
| 277 |
+
),
|
| 278 |
+
dtype=params_dtype,
|
| 279 |
+
),
|
| 280 |
+
requires_grad=False,
|
| 281 |
+
)
|
| 282 |
+
layer.register_parameter("w2_weight_scale", w2_scale)
|
| 283 |
+
set_weight_attrs(w2_scale, extra_weight_attrs)
|
| 284 |
+
set_weight_attrs(w2_scale, {"load_full_w2": load_full_w2})
|
| 285 |
+
|
| 286 |
+
if not self.symmetric:
|
| 287 |
+
w13_zp = torch.nn.Parameter(
|
| 288 |
+
torch.zeros(
|
| 289 |
+
*self.get_weight_shape(
|
| 290 |
+
"w13_zp",
|
| 291 |
+
num_experts,
|
| 292 |
+
hidden_size,
|
| 293 |
+
intermediate_size_per_partition,
|
| 294 |
+
num_groups_w13=num_groups_w13,
|
| 295 |
+
),
|
| 296 |
+
dtype=torch.int32,
|
| 297 |
+
),
|
| 298 |
+
requires_grad=False,
|
| 299 |
+
)
|
| 300 |
+
layer.register_parameter("w13_weight_zero_point", w13_zp)
|
| 301 |
+
set_weight_attrs(w13_zp, extra_weight_attrs)
|
| 302 |
+
|
| 303 |
+
w2_zp = torch.nn.Parameter(
|
| 304 |
+
torch.zeros(
|
| 305 |
+
*self.get_weight_shape(
|
| 306 |
+
"w2_zp",
|
| 307 |
+
num_experts,
|
| 308 |
+
hidden_size,
|
| 309 |
+
intermediate_size_per_partition,
|
| 310 |
+
num_groups_w2=num_groups_w2,
|
| 311 |
+
),
|
| 312 |
+
dtype=torch.int32,
|
| 313 |
+
),
|
| 314 |
+
requires_grad=False,
|
| 315 |
+
)
|
| 316 |
+
layer.register_parameter("w2_weight_zero_point", w2_zp)
|
| 317 |
+
set_weight_attrs(w2_zp, extra_weight_attrs)
|
| 318 |
+
|
| 319 |
+
w2_weight_shape = torch.nn.Parameter(
|
| 320 |
+
torch.empty(num_experts, 2), requires_grad=False
|
| 321 |
+
)
|
| 322 |
+
layer.register_parameter("w2_weight_shape", w2_weight_shape)
|
| 323 |
+
set_weight_attrs(w2_weight_shape, extra_weight_attrs)
|
| 324 |
+
w13_weight_shape = torch.nn.Parameter(
|
| 325 |
+
torch.empty(num_experts, 2), requires_grad=False
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
layer.register_parameter("w13_weight_shape", w13_weight_shape)
|
| 329 |
+
set_weight_attrs(w13_weight_shape, extra_weight_attrs)
|
| 330 |
+
|
| 331 |
+
w13_g_idx = torch.nn.Parameter(
|
| 332 |
+
torch.empty(
|
| 333 |
+
num_experts,
|
| 334 |
+
hidden_size,
|
| 335 |
+
dtype=torch.int32,
|
| 336 |
+
),
|
| 337 |
+
requires_grad=False,
|
| 338 |
+
)
|
| 339 |
+
layer.register_parameter("w13_weight_g_idx", w13_g_idx)
|
| 340 |
+
set_weight_attrs(w13_g_idx, extra_weight_attrs)
|
| 341 |
+
|
| 342 |
+
w2_g_idx = torch.nn.Parameter(
|
| 343 |
+
torch.empty(
|
| 344 |
+
num_experts,
|
| 345 |
+
intermediate_size_per_partition,
|
| 346 |
+
dtype=torch.int32,
|
| 347 |
+
),
|
| 348 |
+
requires_grad=False,
|
| 349 |
+
)
|
| 350 |
+
layer.register_parameter("w2_weight_g_idx", w2_g_idx)
|
| 351 |
+
set_weight_attrs(w2_g_idx, extra_weight_attrs)
|
| 352 |
+
|
| 353 |
+
w13_g_idx_sort_indices = torch.nn.Parameter(
|
| 354 |
+
torch.empty(
|
| 355 |
+
num_experts,
|
| 356 |
+
hidden_size,
|
| 357 |
+
dtype=torch.int32,
|
| 358 |
+
),
|
| 359 |
+
requires_grad=False,
|
| 360 |
+
)
|
| 361 |
+
layer.register_parameter("w13_g_idx_sort_indices", w13_g_idx_sort_indices)
|
| 362 |
+
set_weight_attrs(w13_g_idx_sort_indices, extra_weight_attrs)
|
| 363 |
+
|
| 364 |
+
w2_g_idx_sort_indices = torch.nn.Parameter(
|
| 365 |
+
torch.empty(
|
| 366 |
+
num_experts,
|
| 367 |
+
intermediate_size_per_partition,
|
| 368 |
+
dtype=torch.int32,
|
| 369 |
+
),
|
| 370 |
+
requires_grad=False,
|
| 371 |
+
)
|
| 372 |
+
layer.register_parameter("w2_g_idx_sort_indices", w2_g_idx_sort_indices)
|
| 373 |
+
set_weight_attrs(w2_g_idx_sort_indices, extra_weight_attrs)
|
| 374 |
+
|
| 375 |
+
layer.a13_scale = None
|
| 376 |
+
layer.a2_scale = None
|
| 377 |
+
|
| 378 |
+
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
| 379 |
+
# Process weights using the shared oracle infrastructure
|
| 380 |
+
is_flashinfer = self.wna16_backend == WNA16MoEBackend.FLASHINFER_TRTLLM
|
| 381 |
+
(
|
| 382 |
+
w13_qweight,
|
| 383 |
+
w2_qweight,
|
| 384 |
+
w13_scales,
|
| 385 |
+
w2_scales,
|
| 386 |
+
w13_g_idx_processed,
|
| 387 |
+
w2_g_idx_processed,
|
| 388 |
+
w13_g_idx_sort_indices,
|
| 389 |
+
w2_g_idx_sort_indices,
|
| 390 |
+
w13_qzeros,
|
| 391 |
+
w2_qzeros,
|
| 392 |
+
w13_input_global_scale,
|
| 393 |
+
w2_input_global_scale,
|
| 394 |
+
_, # w13_bias
|
| 395 |
+
_, # w2_bias
|
| 396 |
+
) = convert_to_wna16_moe_kernel_format(
|
| 397 |
+
backend=self.wna16_backend,
|
| 398 |
+
layer=layer,
|
| 399 |
+
quant_config=self.weight_quant,
|
| 400 |
+
input_dtype=self.marlin_input_dtype,
|
| 401 |
+
w13=layer.w13_weight_packed,
|
| 402 |
+
w2=layer.w2_weight_packed,
|
| 403 |
+
w13_scale=layer.w13_weight_scale,
|
| 404 |
+
w2_scale=layer.w2_weight_scale,
|
| 405 |
+
w13_g_idx=layer.w13_weight_g_idx,
|
| 406 |
+
w2_g_idx=layer.w2_weight_g_idx,
|
| 407 |
+
w13_qzeros=getattr(layer, "w13_weight_zero_point", None),
|
| 408 |
+
w2_qzeros=getattr(layer, "w2_weight_zero_point", None),
|
| 409 |
+
)
|
| 410 |
+
|
| 411 |
+
# Replace common parameters
|
| 412 |
+
replace_parameter(layer, "w13_weight_packed", w13_qweight)
|
| 413 |
+
replace_parameter(layer, "w2_weight_packed", w2_qweight)
|
| 414 |
+
replace_parameter(layer, "w13_weight_scale", w13_scales)
|
| 415 |
+
replace_parameter(layer, "w2_weight_scale", w2_scales)
|
| 416 |
+
|
| 417 |
+
if not self.symmetric:
|
| 418 |
+
replace_parameter(layer, "w13_weight_zero_point", w13_qzeros)
|
| 419 |
+
replace_parameter(layer, "w2_weight_zero_point", w2_qzeros)
|
| 420 |
+
|
| 421 |
+
# Marlin-specific parameters (not needed for Flashinfer)
|
| 422 |
+
if not is_flashinfer:
|
| 423 |
+
replace_parameter(layer, "w13_weight_g_idx", w13_g_idx_processed)
|
| 424 |
+
replace_parameter(layer, "w2_weight_g_idx", w2_g_idx_processed)
|
| 425 |
+
replace_parameter(layer, "w13_g_idx_sort_indices", w13_g_idx_sort_indices)
|
| 426 |
+
replace_parameter(layer, "w2_g_idx_sort_indices", w2_g_idx_sort_indices)
|
| 427 |
+
|
| 428 |
+
# Register input global scales if present
|
| 429 |
+
if w13_input_global_scale is not None:
|
| 430 |
+
layer.register_parameter(
|
| 431 |
+
"w13_input_global_scale",
|
| 432 |
+
torch.nn.Parameter(w13_input_global_scale, requires_grad=False),
|
| 433 |
+
)
|
| 434 |
+
if w2_input_global_scale is not None:
|
| 435 |
+
layer.register_parameter(
|
| 436 |
+
"w2_input_global_scale",
|
| 437 |
+
torch.nn.Parameter(w2_input_global_scale, requires_grad=False),
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
layer.workspace = marlin_make_workspace_new(
|
| 441 |
+
layer.w13_weight_g_idx.device, 4
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
+
# Alias packed weights to w13_weight/w2_weight for the modular kernel interface
|
| 445 |
+
layer.w13_weight = layer.w13_weight_packed
|
| 446 |
+
layer.w2_weight = layer.w2_weight_packed
|
| 447 |
+
|
| 448 |
+
assert self.experts_cls is not None
|
| 449 |
+
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
|
| 450 |
+
assert self.moe_quant_config is not None
|
| 451 |
+
|
| 452 |
+
# Add Marlin-specific arguments
|
| 453 |
+
marlin_args: dict[str, Any] = {}
|
| 454 |
+
if not is_flashinfer:
|
| 455 |
+
marlin_args = {
|
| 456 |
+
"w13_g_idx": layer.w13_weight_g_idx,
|
| 457 |
+
"w2_g_idx": layer.w2_weight_g_idx,
|
| 458 |
+
"w13_g_idx_sort_indices": layer.w13_g_idx_sort_indices,
|
| 459 |
+
"w2_g_idx_sort_indices": layer.w2_g_idx_sort_indices,
|
| 460 |
+
"is_k_full": self.is_k_full,
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
self.moe_kernel = make_wna16_moe_kernel(
|
| 464 |
+
moe_quant_config=self.moe_quant_config,
|
| 465 |
+
moe_config=self.moe,
|
| 466 |
+
experts_cls=self.experts_cls,
|
| 467 |
+
routing_tables=layer._expert_routing_tables(),
|
| 468 |
+
**marlin_args,
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
def get_fused_moe_quant_config(
|
| 472 |
+
self, layer: torch.nn.Module
|
| 473 |
+
) -> FusedMoEQuantConfig | None:
|
| 474 |
+
return make_wna16_moe_quant_config(
|
| 475 |
+
w1_scale=layer.w13_weight_scale,
|
| 476 |
+
w2_scale=layer.w2_weight_scale,
|
| 477 |
+
group_size=self.group_size,
|
| 478 |
+
num_bits=self.num_bits,
|
| 479 |
+
w1_zp=getattr(layer, "w13_weight_zero_point", None),
|
| 480 |
+
w2_zp=getattr(layer, "w2_weight_zero_point", None),
|
| 481 |
+
# SwiGLU/swigluoai gate params live on the layer; plumb them into the
|
| 482 |
+
# quant config so the fused activation (swigluoai_uninterleave on
|
| 483 |
+
# MiniMax-M3) receives gemm1_clamp_limit/alpha/beta. Mirrors the
|
| 484 |
+
# fp8/nvfp4/mxfp8 CT MoE methods which already do this.
|
| 485 |
+
gemm1_clamp_limit=getattr(layer, "swiglu_limit", None),
|
| 486 |
+
gemm1_alpha=getattr(layer, "swiglu_alpha", None),
|
| 487 |
+
gemm1_beta=getattr(layer, "swiglu_beta", None),
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
def apply_monolithic(
|
| 491 |
+
self,
|
| 492 |
+
layer: RoutedExperts,
|
| 493 |
+
x: torch.Tensor,
|
| 494 |
+
router_logits: torch.Tensor,
|
| 495 |
+
input_ids: torch.Tensor | None = None,
|
| 496 |
+
) -> torch.Tensor:
|
| 497 |
+
assert self.is_monolithic
|
| 498 |
+
assert self.moe_kernel is not None
|
| 499 |
+
return self.moe_kernel.apply_monolithic(
|
| 500 |
+
x,
|
| 501 |
+
layer.w13_weight,
|
| 502 |
+
layer.w2_weight,
|
| 503 |
+
router_logits,
|
| 504 |
+
activation=layer.activation,
|
| 505 |
+
global_num_experts=layer.global_num_experts,
|
| 506 |
+
expert_map=layer.expert_map,
|
| 507 |
+
apply_router_weight_on_input=layer.apply_router_weight_on_input,
|
| 508 |
+
num_expert_group=layer.num_expert_group,
|
| 509 |
+
topk_group=layer.topk_group,
|
| 510 |
+
e_score_correction_bias=layer.e_score_correction_bias,
|
| 511 |
+
routed_scaling_factor=layer.routed_scaling_factor,
|
| 512 |
+
)
|
| 513 |
+
|
| 514 |
+
def apply(
|
| 515 |
+
self,
|
| 516 |
+
layer: RoutedExperts,
|
| 517 |
+
x: torch.Tensor,
|
| 518 |
+
topk_weights: torch.Tensor,
|
| 519 |
+
topk_ids: torch.Tensor,
|
| 520 |
+
shared_experts: SharedExperts | None,
|
| 521 |
+
shared_experts_input: torch.Tensor | None,
|
| 522 |
+
) -> torch.Tensor:
|
| 523 |
+
assert not self.is_monolithic
|
| 524 |
+
assert self.moe_kernel is not None
|
| 525 |
+
return self.moe_kernel.apply(
|
| 526 |
+
x,
|
| 527 |
+
layer.w13_weight,
|
| 528 |
+
layer.w2_weight,
|
| 529 |
+
topk_weights,
|
| 530 |
+
topk_ids,
|
| 531 |
+
activation=layer.activation,
|
| 532 |
+
global_num_experts=layer.global_num_experts,
|
| 533 |
+
expert_map=layer.expert_map,
|
| 534 |
+
apply_router_weight_on_input=layer.apply_router_weight_on_input,
|
| 535 |
+
shared_experts=shared_experts,
|
| 536 |
+
shared_experts_input=shared_experts_input,
|
| 537 |
+
)
|
vllm-patches/B_int_wna16.py
ADDED
|
@@ -0,0 +1,917 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
| 3 |
+
import sys
|
| 4 |
+
from enum import Enum
|
| 5 |
+
from typing import Any
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
from compressed_tensors.quantization import (
|
| 9 |
+
QuantizationArgs,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
import vllm._custom_ops as ops
|
| 13 |
+
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
|
| 14 |
+
from vllm.logger import init_logger
|
| 15 |
+
from vllm.model_executor.layers.fused_moe.config import (
|
| 16 |
+
FusedMoEConfig,
|
| 17 |
+
FusedMoEQuantConfig,
|
| 18 |
+
int4_w4a16_moe_quant_config,
|
| 19 |
+
int8_w8a16_moe_quant_config,
|
| 20 |
+
)
|
| 21 |
+
from vllm.model_executor.layers.fused_moe.experts.marlin_moe import (
|
| 22 |
+
BatchedMarlinExperts,
|
| 23 |
+
MarlinExperts,
|
| 24 |
+
MarlinExpertsBase,
|
| 25 |
+
)
|
| 26 |
+
from vllm.model_executor.layers.fused_moe.experts.trtllm_mxint4_moe import (
|
| 27 |
+
TrtLlmMxint4ExpertsMonolithic,
|
| 28 |
+
)
|
| 29 |
+
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
|
| 30 |
+
from vllm.model_executor.layers.quantization.utils.marlin_utils import (
|
| 31 |
+
marlin_act_int8_process_scales,
|
| 32 |
+
marlin_moe_permute_scales,
|
| 33 |
+
marlin_permute_bias,
|
| 34 |
+
moe_awq_to_marlin_zero_points,
|
| 35 |
+
moe_packed_to_marlin_zero_points,
|
| 36 |
+
)
|
| 37 |
+
from vllm.model_executor.layers.quantization.utils.quant_utils import (
|
| 38 |
+
QuantKey,
|
| 39 |
+
)
|
| 40 |
+
from vllm.platforms import current_platform
|
| 41 |
+
|
| 42 |
+
logger = init_logger(__name__)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class WNA16MoEBackend(Enum):
|
| 46 |
+
MARLIN = "MARLIN"
|
| 47 |
+
BATCHED_MARLIN = "BATCHED_MARLIN"
|
| 48 |
+
FLASHINFER_TRTLLM = "FLASHINFER_TRTLLM"
|
| 49 |
+
XPU = "XPU"
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def backend_to_kernel_cls(
|
| 53 |
+
backend: WNA16MoEBackend,
|
| 54 |
+
) -> list[type[mk.FusedMoEExperts]]:
|
| 55 |
+
"""Return the experts class for the given backend, or None for NONE."""
|
| 56 |
+
if backend == WNA16MoEBackend.MARLIN:
|
| 57 |
+
return [MarlinExperts]
|
| 58 |
+
elif backend == WNA16MoEBackend.BATCHED_MARLIN:
|
| 59 |
+
return [BatchedMarlinExperts]
|
| 60 |
+
elif backend == WNA16MoEBackend.FLASHINFER_TRTLLM:
|
| 61 |
+
return [TrtLlmMxint4ExpertsMonolithic]
|
| 62 |
+
elif backend == WNA16MoEBackend.XPU:
|
| 63 |
+
from vllm.model_executor.layers.fused_moe.experts.xpu_moe import (
|
| 64 |
+
XPUExpertsWNA16,
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
return [XPUExpertsWNA16]
|
| 68 |
+
else:
|
| 69 |
+
raise ValueError(f"Unknown WNA16 MoE backend: {backend.value}")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def _get_priority_backends() -> list[WNA16MoEBackend]:
|
| 73 |
+
"""
|
| 74 |
+
Get available backends in priority order based on platform and config.
|
| 75 |
+
"""
|
| 76 |
+
if current_platform.is_xpu():
|
| 77 |
+
return [WNA16MoEBackend.XPU]
|
| 78 |
+
|
| 79 |
+
_AVAILABLE_BACKENDS = [
|
| 80 |
+
WNA16MoEBackend.FLASHINFER_TRTLLM,
|
| 81 |
+
WNA16MoEBackend.MARLIN,
|
| 82 |
+
WNA16MoEBackend.BATCHED_MARLIN,
|
| 83 |
+
]
|
| 84 |
+
return _AVAILABLE_BACKENDS
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def select_wna16_moe_backend(
|
| 88 |
+
config: FusedMoEConfig,
|
| 89 |
+
weight_key: QuantKey,
|
| 90 |
+
) -> tuple[WNA16MoEBackend, type[mk.FusedMoEExperts]]:
|
| 91 |
+
"""Select the WNA16 MoE backend.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
config: the shared ``FusedMoEConfig`` for this layer.
|
| 95 |
+
weight_key: The QuantKey describing the weight quantization.
|
| 96 |
+
Must have int4 or int8 type.
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
A tuple of (``WNA16MoEBackend``, experts class or ``None``).
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
activation_format = (
|
| 103 |
+
mk.FusedMoEActivationFormat.BatchedExperts
|
| 104 |
+
if config.moe_parallel_config.use_batched_activation_format
|
| 105 |
+
else mk.FusedMoEActivationFormat.Standard
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
def _make_log_backend(backend: WNA16MoEBackend):
|
| 109 |
+
return f"Using '{backend.value}' WNA16 MoE backend."
|
| 110 |
+
|
| 111 |
+
def _make_log_unsupported(backend: WNA16MoEBackend, reason: str | None) -> str:
|
| 112 |
+
if reason:
|
| 113 |
+
return (
|
| 114 |
+
f"WNA16 MoE backend '{backend.value}' does not support the "
|
| 115 |
+
f"deployment configuration since {reason}."
|
| 116 |
+
)
|
| 117 |
+
return (
|
| 118 |
+
f"WNA16 MoE backend '{backend.value}' does not support the "
|
| 119 |
+
"deployment configuration."
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
def _return_or_raise(
|
| 123 |
+
backend: WNA16MoEBackend,
|
| 124 |
+
config: FusedMoEConfig,
|
| 125 |
+
weight_key: QuantKey | None,
|
| 126 |
+
activation_key: QuantKey | None,
|
| 127 |
+
activation_format: mk.FusedMoEActivationFormat,
|
| 128 |
+
) -> tuple[WNA16MoEBackend, type[mk.FusedMoEExperts]]:
|
| 129 |
+
reason: str | None = None
|
| 130 |
+
for k_cls in backend_to_kernel_cls(backend):
|
| 131 |
+
supported, reason = k_cls.is_supported_config(
|
| 132 |
+
k_cls, config, weight_key, activation_key, activation_format
|
| 133 |
+
)
|
| 134 |
+
if supported:
|
| 135 |
+
logger.info_once(_make_log_backend(backend), scope="local")
|
| 136 |
+
return backend, k_cls
|
| 137 |
+
raise ValueError(_make_log_unsupported(backend, reason))
|
| 138 |
+
|
| 139 |
+
# Select kernels in order of backend.
|
| 140 |
+
AVAILABLE_BACKENDS = _get_priority_backends()
|
| 141 |
+
|
| 142 |
+
for backend in AVAILABLE_BACKENDS:
|
| 143 |
+
activation_key = None # always BF16 activation for WNA16 MoE
|
| 144 |
+
for k_cls in backend_to_kernel_cls(backend):
|
| 145 |
+
supported, reason = k_cls.is_supported_config(
|
| 146 |
+
k_cls, config, weight_key, activation_key, activation_format
|
| 147 |
+
)
|
| 148 |
+
if supported:
|
| 149 |
+
logger.info_once(_make_log_backend(backend), scope="local")
|
| 150 |
+
return backend, k_cls
|
| 151 |
+
else:
|
| 152 |
+
logger.debug_once(_make_log_unsupported(backend, reason), scope="local")
|
| 153 |
+
|
| 154 |
+
raise NotImplementedError(
|
| 155 |
+
"No WNA16 MoE backend supports the deployment configuration."
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def make_wna16_moe_quant_config(
|
| 160 |
+
w1_scale: torch.Tensor,
|
| 161 |
+
w2_scale: torch.Tensor,
|
| 162 |
+
group_size: int,
|
| 163 |
+
num_bits: int,
|
| 164 |
+
w1_zp: torch.Tensor | None = None,
|
| 165 |
+
w2_zp: torch.Tensor | None = None,
|
| 166 |
+
w1_bias: torch.Tensor | None = None,
|
| 167 |
+
w2_bias: torch.Tensor | None = None,
|
| 168 |
+
a1_gscale: torch.Tensor | None = None,
|
| 169 |
+
a2_gscale: torch.Tensor | None = None,
|
| 170 |
+
gemm1_clamp_limit: float | None = None,
|
| 171 |
+
gemm1_alpha: float | None = None,
|
| 172 |
+
gemm1_beta: float | None = None,
|
| 173 |
+
) -> FusedMoEQuantConfig:
|
| 174 |
+
"""Create the FusedMoEQuantConfig for 4 or 8-bit WNA16 MoE."""
|
| 175 |
+
if num_bits == 4:
|
| 176 |
+
return int4_w4a16_moe_quant_config(
|
| 177 |
+
w1_scale=w1_scale,
|
| 178 |
+
w2_scale=w2_scale,
|
| 179 |
+
w1_zp=w1_zp,
|
| 180 |
+
w2_zp=w2_zp,
|
| 181 |
+
w1_bias=w1_bias,
|
| 182 |
+
w2_bias=w2_bias,
|
| 183 |
+
block_shape=[0, group_size],
|
| 184 |
+
a1_gscale=a1_gscale,
|
| 185 |
+
a2_gscale=a2_gscale,
|
| 186 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 187 |
+
gemm1_alpha=gemm1_alpha,
|
| 188 |
+
gemm1_beta=gemm1_beta,
|
| 189 |
+
)
|
| 190 |
+
else:
|
| 191 |
+
assert num_bits == 8
|
| 192 |
+
return int8_w8a16_moe_quant_config(
|
| 193 |
+
w1_scale=w1_scale,
|
| 194 |
+
w2_scale=w2_scale,
|
| 195 |
+
w1_zp=w1_zp,
|
| 196 |
+
w2_zp=w2_zp,
|
| 197 |
+
w1_bias=w1_bias,
|
| 198 |
+
w2_bias=w2_bias,
|
| 199 |
+
block_shape=[0, group_size],
|
| 200 |
+
a1_gscale=a1_gscale,
|
| 201 |
+
a2_gscale=a2_gscale,
|
| 202 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 203 |
+
gemm1_alpha=gemm1_alpha,
|
| 204 |
+
gemm1_beta=gemm1_beta,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def make_wna16_moe_kernel(
|
| 209 |
+
moe_quant_config: FusedMoEQuantConfig,
|
| 210 |
+
moe_config: FusedMoEConfig,
|
| 211 |
+
experts_cls: type[mk.FusedMoEExperts],
|
| 212 |
+
is_k_full: bool = False,
|
| 213 |
+
w13_g_idx: torch.Tensor | None = None,
|
| 214 |
+
w2_g_idx: torch.Tensor | None = None,
|
| 215 |
+
w13_g_idx_sort_indices: torch.Tensor | None = None,
|
| 216 |
+
w2_g_idx_sort_indices: torch.Tensor | None = None,
|
| 217 |
+
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
|
| 218 |
+
) -> mk.FusedMoEKernel:
|
| 219 |
+
from vllm.model_executor.layers.fused_moe.all2all_utils import (
|
| 220 |
+
maybe_make_prepare_finalize,
|
| 221 |
+
)
|
| 222 |
+
from vllm.model_executor.layers.fused_moe.experts.xpu_moe import (
|
| 223 |
+
XPUExpertsWNA16,
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
# Currently, we only support TrtLlmMxint4ExpertsMonolithic, MarlinExperts
|
| 227 |
+
# and BatchedMarlinExperts
|
| 228 |
+
assert experts_cls in (
|
| 229 |
+
MarlinExperts,
|
| 230 |
+
BatchedMarlinExperts,
|
| 231 |
+
TrtLlmMxint4ExpertsMonolithic,
|
| 232 |
+
XPUExpertsWNA16,
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
is_monolithic = experts_cls.is_monolithic()
|
| 236 |
+
|
| 237 |
+
prepare_finalize = maybe_make_prepare_finalize(
|
| 238 |
+
moe=moe_config,
|
| 239 |
+
quant_config=moe_quant_config,
|
| 240 |
+
routing_tables=routing_tables,
|
| 241 |
+
allow_new_interface=True,
|
| 242 |
+
use_monolithic=is_monolithic,
|
| 243 |
+
)
|
| 244 |
+
assert prepare_finalize is not None
|
| 245 |
+
|
| 246 |
+
logger.info_once("Using %s", prepare_finalize.__class__.__name__, scope="local")
|
| 247 |
+
|
| 248 |
+
extra_args: dict[str, Any] = {}
|
| 249 |
+
if issubclass(experts_cls, MarlinExpertsBase):
|
| 250 |
+
extra_args = {
|
| 251 |
+
"w13_g_idx": w13_g_idx,
|
| 252 |
+
"w2_g_idx": w2_g_idx,
|
| 253 |
+
"w13_g_idx_sort_indices": w13_g_idx_sort_indices,
|
| 254 |
+
"w2_g_idx_sort_indices": w2_g_idx_sort_indices,
|
| 255 |
+
"is_k_full": is_k_full,
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
if experts_cls is XPUExpertsWNA16:
|
| 259 |
+
assert (
|
| 260 |
+
prepare_finalize.activation_format == mk.FusedMoEActivationFormat.Standard
|
| 261 |
+
), (
|
| 262 |
+
"XPUExpertsWNA16 only supports the Standard activation format; "
|
| 263 |
+
"xpu_fused_moe(is_int4=True) does not implement BatchedExperts."
|
| 264 |
+
)
|
| 265 |
+
experts: mk.FusedMoEExperts = XPUExpertsWNA16(
|
| 266 |
+
moe_config=moe_config,
|
| 267 |
+
quant_config=moe_quant_config,
|
| 268 |
+
)
|
| 269 |
+
elif (
|
| 270 |
+
prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts
|
| 271 |
+
):
|
| 272 |
+
max_num_tokens = prepare_finalize.max_num_tokens_per_rank()
|
| 273 |
+
assert max_num_tokens is not None
|
| 274 |
+
experts = experts_cls(
|
| 275 |
+
max_num_tokens=max_num_tokens,
|
| 276 |
+
num_dispatchers=prepare_finalize.num_dispatchers(),
|
| 277 |
+
moe_config=moe_config,
|
| 278 |
+
quant_config=moe_quant_config,
|
| 279 |
+
**extra_args,
|
| 280 |
+
)
|
| 281 |
+
else:
|
| 282 |
+
experts = experts_cls(
|
| 283 |
+
moe_config=moe_config,
|
| 284 |
+
quant_config=moe_quant_config,
|
| 285 |
+
**extra_args,
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
return mk.FusedMoEKernel(
|
| 289 |
+
prepare_finalize,
|
| 290 |
+
experts,
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
# ---------------------------------------------------------------------------
|
| 295 |
+
# Per-backend weight post-processing
|
| 296 |
+
# ---------------------------------------------------------------------------
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def _process_weights_flashinfer(
|
| 300 |
+
w13_qweight: torch.Tensor,
|
| 301 |
+
w2_qweight: torch.Tensor,
|
| 302 |
+
w13_scales: torch.Tensor,
|
| 303 |
+
w2_scales: torch.Tensor,
|
| 304 |
+
w13_g_idx: torch.Tensor,
|
| 305 |
+
w2_g_idx: torch.Tensor,
|
| 306 |
+
w13_bias: torch.Tensor | None = None,
|
| 307 |
+
w2_bias: torch.Tensor | None = None,
|
| 308 |
+
) -> tuple[
|
| 309 |
+
torch.Tensor, # w13_qweight
|
| 310 |
+
torch.Tensor, # w2_qweight
|
| 311 |
+
torch.Tensor, # w13_scales
|
| 312 |
+
torch.Tensor, # w2_scales
|
| 313 |
+
torch.Tensor, # w13_g_idx
|
| 314 |
+
torch.Tensor, # w2_g_idx
|
| 315 |
+
torch.Tensor | None, # w13_g_idx_sort_indices
|
| 316 |
+
torch.Tensor | None, # w2_g_idx_sort_indices
|
| 317 |
+
torch.Tensor | None, # w13_qzeros
|
| 318 |
+
torch.Tensor | None, # w2_qzeros
|
| 319 |
+
torch.Tensor | None, # w13_input_global_scale
|
| 320 |
+
torch.Tensor | None, # w2_input_global_scale
|
| 321 |
+
torch.Tensor | None, # w13_bias
|
| 322 |
+
torch.Tensor | None, # w2_bias
|
| 323 |
+
]:
|
| 324 |
+
"""Flashinfer (TRT-LLM MXINT4) weight post-processing.
|
| 325 |
+
|
| 326 |
+
Steps
|
| 327 |
+
-----
|
| 328 |
+
1. Transform weights/scales via ``prepare_static_weights_for_trtllm_mxint4_moe``.
|
| 329 |
+
2. Return transformed tensors, passing through g_idx/bias unchanged.
|
| 330 |
+
"""
|
| 331 |
+
from vllm.model_executor.layers.quantization.utils.flashinfer_mxint4_moe import (
|
| 332 |
+
prepare_static_weights_for_trtllm_mxint4_moe,
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
dict_weights_mxint4 = prepare_static_weights_for_trtllm_mxint4_moe(
|
| 336 |
+
w13_qweight,
|
| 337 |
+
w13_scales,
|
| 338 |
+
w2_qweight,
|
| 339 |
+
w2_scales,
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
return (
|
| 343 |
+
dict_weights_mxint4["gemm1_weights"],
|
| 344 |
+
dict_weights_mxint4["gemm2_weights"],
|
| 345 |
+
dict_weights_mxint4["gemm1_scales"],
|
| 346 |
+
dict_weights_mxint4["gemm2_scales"],
|
| 347 |
+
w13_g_idx,
|
| 348 |
+
w2_g_idx,
|
| 349 |
+
None,
|
| 350 |
+
None,
|
| 351 |
+
None,
|
| 352 |
+
None,
|
| 353 |
+
None,
|
| 354 |
+
None,
|
| 355 |
+
w13_bias,
|
| 356 |
+
w2_bias,
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def _process_weights_marlin(
|
| 361 |
+
layer: torch.nn.Module,
|
| 362 |
+
input_dtype: torch.dtype | None,
|
| 363 |
+
num_bits: int,
|
| 364 |
+
pack_factor: int,
|
| 365 |
+
group_size: int,
|
| 366 |
+
actorder: str | None,
|
| 367 |
+
w13_qweight: torch.Tensor,
|
| 368 |
+
w2_qweight: torch.Tensor,
|
| 369 |
+
w13_scales: torch.Tensor,
|
| 370 |
+
w2_scales: torch.Tensor,
|
| 371 |
+
w13_g_idx: torch.Tensor,
|
| 372 |
+
w2_g_idx: torch.Tensor,
|
| 373 |
+
w13_qzeros: torch.Tensor | None = None,
|
| 374 |
+
w2_qzeros: torch.Tensor | None = None,
|
| 375 |
+
w13_bias: torch.Tensor | None = None,
|
| 376 |
+
w2_bias: torch.Tensor | None = None,
|
| 377 |
+
) -> tuple[
|
| 378 |
+
torch.Tensor, # w13_qweight
|
| 379 |
+
torch.Tensor, # w2_qweight
|
| 380 |
+
torch.Tensor, # w13_scales
|
| 381 |
+
torch.Tensor, # w2_scales
|
| 382 |
+
torch.Tensor, # w13_g_idx
|
| 383 |
+
torch.Tensor, # w2_g_idx
|
| 384 |
+
torch.Tensor, # w13_g_idx_sort_indices
|
| 385 |
+
torch.Tensor, # w2_g_idx_sort_indices
|
| 386 |
+
torch.Tensor | None, # w13_qzeros
|
| 387 |
+
torch.Tensor | None, # w2_qzeros
|
| 388 |
+
torch.Tensor | None, # w13_input_global_scale
|
| 389 |
+
torch.Tensor | None, # w2_input_global_scale
|
| 390 |
+
torch.Tensor | None, # w13_bias
|
| 391 |
+
torch.Tensor | None, # w2_bias
|
| 392 |
+
]:
|
| 393 |
+
"""Standard Marlin weight post-processing shared by MARLIN and
|
| 394 |
+
BATCHED_MARLIN backends.
|
| 395 |
+
|
| 396 |
+
Steps
|
| 397 |
+
-----
|
| 398 |
+
1. Optional FP8 preprocessing of packed weights / scales.
|
| 399 |
+
2. Sort / reset g_idx tensors for act-order handling.
|
| 400 |
+
3. Repack weights via ``gptq_marlin_moe_repack``.
|
| 401 |
+
4. Permute scales (and optionally extract INT8 global scales).
|
| 402 |
+
5. Permute bias tensors.
|
| 403 |
+
"""
|
| 404 |
+
is_a_8bit = input_dtype is not None and input_dtype.itemsize == 1
|
| 405 |
+
|
| 406 |
+
marlin_w13_qweight: torch.Tensor
|
| 407 |
+
marlin_w2_qweight: torch.Tensor
|
| 408 |
+
marlin_w13_scales: torch.Tensor
|
| 409 |
+
marlin_w2_scales: torch.Tensor
|
| 410 |
+
w13_g_idx_sort_indices: torch.Tensor | None = None
|
| 411 |
+
w2_g_idx_sort_indices: torch.Tensor | None = None
|
| 412 |
+
w13_input_global_scale: torch.Tensor | None = None
|
| 413 |
+
w2_input_global_scale: torch.Tensor | None = None
|
| 414 |
+
w13_bias_out: torch.Tensor | None = None
|
| 415 |
+
w2_bias_out: torch.Tensor | None = None
|
| 416 |
+
|
| 417 |
+
# --- FP8 weight / scale adjustment ---
|
| 418 |
+
if input_dtype == torch.float8_e4m3fn:
|
| 419 |
+
# NOTE: for non-zp quantization format only
|
| 420 |
+
marlin_w13_qweight = ops.marlin_int4_fp8_preprocess(w13_qweight, inplace=False)
|
| 421 |
+
marlin_w2_qweight = ops.marlin_int4_fp8_preprocess(w2_qweight, inplace=False)
|
| 422 |
+
marlin_w13_scales = w13_scales.data * 512
|
| 423 |
+
marlin_w2_scales = w2_scales.data * 512
|
| 424 |
+
else:
|
| 425 |
+
marlin_w13_qweight = w13_qweight
|
| 426 |
+
marlin_w2_qweight = w2_qweight
|
| 427 |
+
marlin_w13_scales = w13_scales
|
| 428 |
+
marlin_w2_scales = w2_scales
|
| 429 |
+
|
| 430 |
+
# --- Process act_order (g_idx) ---
|
| 431 |
+
if actorder == "group":
|
| 432 |
+
num_experts = w13_g_idx.shape[0]
|
| 433 |
+
w13_g_idx_sort_indices = torch.empty_like(w13_g_idx)
|
| 434 |
+
w2_g_idx_sort_indices = torch.empty_like(w2_g_idx)
|
| 435 |
+
w13_sorted_g_idx = torch.empty_like(w13_g_idx)
|
| 436 |
+
w2_sorted_g_idx = torch.empty_like(w2_g_idx)
|
| 437 |
+
for e in range(num_experts):
|
| 438 |
+
w13_g_idx_sort_indices[e] = torch.argsort(w13_g_idx[e]).to(torch.int32)
|
| 439 |
+
w2_g_idx_sort_indices[e] = torch.argsort(w2_g_idx[e]).to(torch.int32)
|
| 440 |
+
w13_sorted_g_idx[e] = w13_g_idx[e][w13_g_idx_sort_indices[e]]
|
| 441 |
+
w2_sorted_g_idx[e] = w2_g_idx[e][w2_g_idx_sort_indices[e]]
|
| 442 |
+
w13_g_idx = w13_sorted_g_idx
|
| 443 |
+
w2_g_idx = w2_sorted_g_idx
|
| 444 |
+
else:
|
| 445 |
+
num_experts = w13_g_idx.shape[0]
|
| 446 |
+
device = w13_g_idx.device
|
| 447 |
+
w13_g_idx = torch.nn.Parameter(
|
| 448 |
+
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
| 449 |
+
requires_grad=False,
|
| 450 |
+
)
|
| 451 |
+
w2_g_idx = torch.nn.Parameter(
|
| 452 |
+
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
| 453 |
+
requires_grad=False,
|
| 454 |
+
)
|
| 455 |
+
w13_g_idx_sort_indices = torch.nn.Parameter(
|
| 456 |
+
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
| 457 |
+
requires_grad=False,
|
| 458 |
+
)
|
| 459 |
+
w2_g_idx_sort_indices = torch.nn.Parameter(
|
| 460 |
+
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
| 461 |
+
requires_grad=False,
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
# --- Repack weights ---
|
| 465 |
+
marlin_w13_qweight = ops.gptq_marlin_moe_repack(
|
| 466 |
+
marlin_w13_qweight,
|
| 467 |
+
w13_g_idx_sort_indices,
|
| 468 |
+
marlin_w13_qweight.shape[1] * pack_factor,
|
| 469 |
+
marlin_w13_qweight.shape[2],
|
| 470 |
+
num_bits,
|
| 471 |
+
is_a_8bit=is_a_8bit,
|
| 472 |
+
)
|
| 473 |
+
marlin_w2_qweight = ops.gptq_marlin_moe_repack(
|
| 474 |
+
marlin_w2_qweight,
|
| 475 |
+
w2_g_idx_sort_indices,
|
| 476 |
+
marlin_w2_qweight.shape[1] * pack_factor,
|
| 477 |
+
marlin_w2_qweight.shape[2],
|
| 478 |
+
num_bits,
|
| 479 |
+
is_a_8bit=is_a_8bit,
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
# --- Permute scales ---
|
| 483 |
+
marlin_w13_scales = marlin_moe_permute_scales(
|
| 484 |
+
s=marlin_w13_scales,
|
| 485 |
+
size_k=layer.intermediate_size_per_partition,
|
| 486 |
+
size_n=marlin_w13_scales.shape[2],
|
| 487 |
+
group_size=group_size,
|
| 488 |
+
is_a_8bit=is_a_8bit,
|
| 489 |
+
)
|
| 490 |
+
group_size_or_pack_factor = group_size if group_size != -1 else pack_factor
|
| 491 |
+
marlin_w2_scales = marlin_moe_permute_scales(
|
| 492 |
+
s=marlin_w2_scales,
|
| 493 |
+
size_k=marlin_w2_scales.shape[1] * group_size_or_pack_factor,
|
| 494 |
+
size_n=marlin_w2_scales.shape[2],
|
| 495 |
+
group_size=group_size,
|
| 496 |
+
is_a_8bit=is_a_8bit,
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
if input_dtype == torch.int8:
|
| 500 |
+
if layer.num_groups_w13 > 1:
|
| 501 |
+
marlin_w13_scales, w13_input_global_scale = marlin_act_int8_process_scales(
|
| 502 |
+
marlin_w13_scales
|
| 503 |
+
)
|
| 504 |
+
if layer.num_groups_w2 > 1:
|
| 505 |
+
marlin_w2_scales, w2_input_global_scale = marlin_act_int8_process_scales(
|
| 506 |
+
marlin_w2_scales
|
| 507 |
+
)
|
| 508 |
+
|
| 509 |
+
# --- Permute zero points ---
|
| 510 |
+
if w13_qzeros is not None and w2_qzeros is not None:
|
| 511 |
+
w13_qzeros = moe_packed_to_marlin_zero_points(
|
| 512 |
+
w13_qzeros,
|
| 513 |
+
size_k=w13_qzeros.shape[1],
|
| 514 |
+
size_n=w13_qzeros.shape[2] * pack_factor,
|
| 515 |
+
num_bits=num_bits,
|
| 516 |
+
is_a_8bit=is_a_8bit,
|
| 517 |
+
)
|
| 518 |
+
w2_qzeros = moe_packed_to_marlin_zero_points(
|
| 519 |
+
w2_qzeros,
|
| 520 |
+
size_k=w2_qzeros.shape[1],
|
| 521 |
+
size_n=w2_qzeros.shape[2] * pack_factor,
|
| 522 |
+
num_bits=num_bits,
|
| 523 |
+
is_a_8bit=is_a_8bit,
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
# --- Permute bias ---
|
| 527 |
+
if w13_bias is not None:
|
| 528 |
+
w13_bias_out = marlin_permute_bias(w13_bias)
|
| 529 |
+
if w2_bias is not None:
|
| 530 |
+
w2_bias_out = marlin_permute_bias(w2_bias)
|
| 531 |
+
|
| 532 |
+
return (
|
| 533 |
+
marlin_w13_qweight,
|
| 534 |
+
marlin_w2_qweight,
|
| 535 |
+
marlin_w13_scales,
|
| 536 |
+
marlin_w2_scales,
|
| 537 |
+
w13_g_idx,
|
| 538 |
+
w2_g_idx,
|
| 539 |
+
w13_g_idx_sort_indices,
|
| 540 |
+
w2_g_idx_sort_indices,
|
| 541 |
+
w13_qzeros,
|
| 542 |
+
w2_qzeros,
|
| 543 |
+
w13_input_global_scale,
|
| 544 |
+
w2_input_global_scale,
|
| 545 |
+
w13_bias_out,
|
| 546 |
+
w2_bias_out,
|
| 547 |
+
)
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
def _process_awq_weights_marlin(
|
| 551 |
+
layer: torch.nn.Module,
|
| 552 |
+
weight_bits: int,
|
| 553 |
+
pack_factor: int,
|
| 554 |
+
group_size: int,
|
| 555 |
+
input_dtype: torch.dtype | None,
|
| 556 |
+
w13_qweight: torch.Tensor,
|
| 557 |
+
w2_qweight: torch.Tensor,
|
| 558 |
+
w13_scales: torch.Tensor,
|
| 559 |
+
w2_scales: torch.Tensor,
|
| 560 |
+
w13_qzeros: torch.Tensor,
|
| 561 |
+
w2_qzeros: torch.Tensor,
|
| 562 |
+
w13_bias: torch.Tensor | None = None,
|
| 563 |
+
w2_bias: torch.Tensor | None = None,
|
| 564 |
+
) -> tuple[
|
| 565 |
+
torch.Tensor, # w13_qweight
|
| 566 |
+
torch.Tensor, # w2_qweight
|
| 567 |
+
torch.Tensor, # w13_scales
|
| 568 |
+
torch.Tensor, # w2_scales
|
| 569 |
+
torch.Tensor | None, # w13_g_idx
|
| 570 |
+
torch.Tensor | None, # w2_g_idx
|
| 571 |
+
torch.Tensor | None, # w13_g_idx_sort_indices
|
| 572 |
+
torch.Tensor | None, # w2_g_idx_sort_indices
|
| 573 |
+
torch.Tensor | None, # w13_qzeros
|
| 574 |
+
torch.Tensor | None, # w2_qzeros
|
| 575 |
+
torch.Tensor | None, # w13_input_global_scale
|
| 576 |
+
torch.Tensor | None, # w2_input_global_scale
|
| 577 |
+
torch.Tensor | None, # w13_bias
|
| 578 |
+
torch.Tensor | None, # w2_bias
|
| 579 |
+
]:
|
| 580 |
+
"""AWQ-specific Marlin weight post-processing.
|
| 581 |
+
|
| 582 |
+
AWQ checkpoints use a different packing order than GPTQ, so they need
|
| 583 |
+
AWQ-specific weight repacking and zero-point conversion before Marlin runs.
|
| 584 |
+
"""
|
| 585 |
+
num_experts = w13_qweight.shape[0]
|
| 586 |
+
device = w13_qweight.device
|
| 587 |
+
is_a_8bit = input_dtype is not None and input_dtype.itemsize == 1
|
| 588 |
+
w13_input_global_scale: torch.Tensor | None = None
|
| 589 |
+
w2_input_global_scale: torch.Tensor | None = None
|
| 590 |
+
w13_bias_out: torch.Tensor | None = None
|
| 591 |
+
w2_bias_out: torch.Tensor | None = None
|
| 592 |
+
|
| 593 |
+
if input_dtype == torch.float8_e4m3fn:
|
| 594 |
+
ops.marlin_int4_fp8_preprocess(
|
| 595 |
+
w13_qweight.view(-1, w13_qweight.size(2)),
|
| 596 |
+
w13_qzeros.view(-1, w13_qzeros.size(2)),
|
| 597 |
+
inplace=True,
|
| 598 |
+
)
|
| 599 |
+
ops.marlin_int4_fp8_preprocess(
|
| 600 |
+
w2_qweight.view(-1, w2_qweight.size(2)),
|
| 601 |
+
w2_qzeros.view(-1, w2_qzeros.size(2)),
|
| 602 |
+
inplace=True,
|
| 603 |
+
)
|
| 604 |
+
w13_scales = w13_scales.data * 512
|
| 605 |
+
w2_scales = w2_scales.data * 512
|
| 606 |
+
|
| 607 |
+
w13_g_idx_sort_indices = torch.nn.Parameter(
|
| 608 |
+
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
| 609 |
+
requires_grad=False,
|
| 610 |
+
)
|
| 611 |
+
w2_g_idx_sort_indices = torch.nn.Parameter(
|
| 612 |
+
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
| 613 |
+
requires_grad=False,
|
| 614 |
+
)
|
| 615 |
+
|
| 616 |
+
marlin_w13_qweight = ops.awq_marlin_moe_repack(
|
| 617 |
+
w13_qweight,
|
| 618 |
+
w13_g_idx_sort_indices,
|
| 619 |
+
size_k=w13_qweight.shape[1],
|
| 620 |
+
size_n=w13_qweight.shape[2] * pack_factor,
|
| 621 |
+
num_bits=weight_bits,
|
| 622 |
+
is_a_8bit=is_a_8bit,
|
| 623 |
+
)
|
| 624 |
+
marlin_w2_qweight = ops.awq_marlin_moe_repack(
|
| 625 |
+
w2_qweight,
|
| 626 |
+
w2_g_idx_sort_indices,
|
| 627 |
+
size_k=w2_qweight.shape[1],
|
| 628 |
+
size_n=w2_qweight.shape[2] * pack_factor,
|
| 629 |
+
num_bits=weight_bits,
|
| 630 |
+
is_a_8bit=is_a_8bit,
|
| 631 |
+
)
|
| 632 |
+
|
| 633 |
+
marlin_w13_scales = marlin_moe_permute_scales(
|
| 634 |
+
s=w13_scales,
|
| 635 |
+
size_k=layer.intermediate_size_per_partition,
|
| 636 |
+
size_n=w13_scales.shape[2],
|
| 637 |
+
group_size=group_size,
|
| 638 |
+
is_a_8bit=is_a_8bit,
|
| 639 |
+
)
|
| 640 |
+
if input_dtype == torch.int8 and layer.num_groups_w13 > 1:
|
| 641 |
+
marlin_w13_scales, w13_input_global_scale = marlin_act_int8_process_scales(
|
| 642 |
+
marlin_w13_scales
|
| 643 |
+
)
|
| 644 |
+
|
| 645 |
+
marlin_w2_scales = marlin_moe_permute_scales(
|
| 646 |
+
s=w2_scales,
|
| 647 |
+
size_k=layer.intermediate_size_per_partition,
|
| 648 |
+
size_n=w2_scales.shape[2],
|
| 649 |
+
group_size=group_size,
|
| 650 |
+
is_a_8bit=is_a_8bit,
|
| 651 |
+
)
|
| 652 |
+
if input_dtype == torch.int8 and layer.num_groups_w2 > 1:
|
| 653 |
+
marlin_w2_scales, w2_input_global_scale = marlin_act_int8_process_scales(
|
| 654 |
+
marlin_w2_scales
|
| 655 |
+
)
|
| 656 |
+
|
| 657 |
+
marlin_w13_qzeros = moe_awq_to_marlin_zero_points(
|
| 658 |
+
w13_qzeros,
|
| 659 |
+
size_k=w13_qzeros.shape[1],
|
| 660 |
+
size_n=w13_qzeros.shape[2] * pack_factor,
|
| 661 |
+
num_bits=weight_bits,
|
| 662 |
+
is_a_8bit=is_a_8bit,
|
| 663 |
+
)
|
| 664 |
+
marlin_w2_qzeros = moe_awq_to_marlin_zero_points(
|
| 665 |
+
w2_qzeros,
|
| 666 |
+
size_k=w2_qzeros.shape[1],
|
| 667 |
+
size_n=w2_qzeros.shape[2] * pack_factor,
|
| 668 |
+
num_bits=weight_bits,
|
| 669 |
+
is_a_8bit=is_a_8bit,
|
| 670 |
+
)
|
| 671 |
+
|
| 672 |
+
if w13_bias is not None:
|
| 673 |
+
w13_bias_out = marlin_permute_bias(w13_bias)
|
| 674 |
+
if w2_bias is not None:
|
| 675 |
+
w2_bias_out = marlin_permute_bias(w2_bias)
|
| 676 |
+
|
| 677 |
+
return (
|
| 678 |
+
marlin_w13_qweight,
|
| 679 |
+
marlin_w2_qweight,
|
| 680 |
+
marlin_w13_scales,
|
| 681 |
+
marlin_w2_scales,
|
| 682 |
+
None,
|
| 683 |
+
None,
|
| 684 |
+
w13_g_idx_sort_indices,
|
| 685 |
+
w2_g_idx_sort_indices,
|
| 686 |
+
marlin_w13_qzeros,
|
| 687 |
+
marlin_w2_qzeros,
|
| 688 |
+
w13_input_global_scale,
|
| 689 |
+
w2_input_global_scale,
|
| 690 |
+
w13_bias_out,
|
| 691 |
+
w2_bias_out,
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
|
| 695 |
+
def _process_weights_xpu(
|
| 696 |
+
layer: torch.nn.Module,
|
| 697 |
+
quant_config: QuantizationConfig,
|
| 698 |
+
w13_qweight: torch.Tensor,
|
| 699 |
+
w2_qweight: torch.Tensor,
|
| 700 |
+
w13_scales: torch.Tensor,
|
| 701 |
+
w2_scales: torch.Tensor,
|
| 702 |
+
w13_bias: torch.Tensor | None = None,
|
| 703 |
+
w2_bias: torch.Tensor | None = None,
|
| 704 |
+
) -> tuple[
|
| 705 |
+
torch.Tensor, # w13_qweight
|
| 706 |
+
torch.Tensor, # w2_qweight
|
| 707 |
+
torch.Tensor, # w13_scales
|
| 708 |
+
torch.Tensor, # w2_scales
|
| 709 |
+
torch.Tensor | None, # w13_bias
|
| 710 |
+
torch.Tensor | None, # w2_bias
|
| 711 |
+
]:
|
| 712 |
+
"""Repack GPTQ-format INT4 MoE weights into the layout
|
| 713 |
+
`vllm_xpu_kernels.fused_moe_interface.xpu_fused_moe(is_int4=True)` expects:
|
| 714 |
+
|
| 715 |
+
w13: [E, 2*N, K] int4 (uint8 storage [E, 2*N, K // 2])
|
| 716 |
+
w13_scales: [E, 2*N, K // group_size] params_dtype
|
| 717 |
+
w2: [E, K, N] int4 (uint8 storage [E, K, N // 2])
|
| 718 |
+
w2_scales: [E, K, N // group_size] params_dtype
|
| 719 |
+
|
| 720 |
+
Input GPTQ layout from FusedMoE.weight_loader:
|
| 721 |
+
w13: [E, K // 8, 2*N] int32 (8 nibbles per int32 along the input dim)
|
| 722 |
+
w13_scales: [E, K // group_size, 2*N] params_dtype
|
| 723 |
+
w2: [E, N // 8, K] int32
|
| 724 |
+
w2_scales: [E, N // group_size, K] params_dtype
|
| 725 |
+
|
| 726 |
+
Transpose dim 1 ↔ dim 2 then view int32 → uint8 to recover sequential
|
| 727 |
+
int4-packed bytes along the input dim. Each packed int32 holds 8 nibbles
|
| 728 |
+
`(n7<<28)|(n6<<24)|...|(n1<<4)|n0` in ascending K order; on a
|
| 729 |
+
little-endian host the int32→uint8 view exposes them as bytes
|
| 730 |
+
`[n1<<4|n0, n3<<4|n2, n5<<4|n4, n7<<4|n6]`, i.e. two nibbles per byte
|
| 731 |
+
with the lower nibble = lower input-K index. xpu_fused_moe(is_int4=True)
|
| 732 |
+
expects this convention; on a big-endian host the byte order reverses
|
| 733 |
+
and the kernel would silently miscompute, so we hard-fail.
|
| 734 |
+
"""
|
| 735 |
+
del layer, quant_config # unused — kept for parity with the marlin helper
|
| 736 |
+
|
| 737 |
+
if sys.byteorder != "little":
|
| 738 |
+
raise NotImplementedError(
|
| 739 |
+
"_process_weights_xpu requires a little-endian host: the GPTQ "
|
| 740 |
+
"int32 → uint8 nibble repack relies on LE byte ordering."
|
| 741 |
+
)
|
| 742 |
+
|
| 743 |
+
w13_xpu = w13_qweight.transpose(1, 2).contiguous().view(torch.uint8)
|
| 744 |
+
w2_xpu = w2_qweight.transpose(1, 2).contiguous().view(torch.uint8)
|
| 745 |
+
w13_scales_xpu = w13_scales.transpose(1, 2).contiguous()
|
| 746 |
+
w2_scales_xpu = w2_scales.transpose(1, 2).contiguous()
|
| 747 |
+
|
| 748 |
+
return (
|
| 749 |
+
w13_xpu,
|
| 750 |
+
w2_xpu,
|
| 751 |
+
w13_scales_xpu,
|
| 752 |
+
w2_scales_xpu,
|
| 753 |
+
w13_bias,
|
| 754 |
+
w2_bias,
|
| 755 |
+
)
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
def convert_to_wna16_moe_kernel_format(
|
| 759 |
+
backend: WNA16MoEBackend,
|
| 760 |
+
layer: torch.nn.Module,
|
| 761 |
+
quant_config: QuantizationConfig | QuantizationArgs | None,
|
| 762 |
+
input_dtype: torch.dtype | None,
|
| 763 |
+
w13: torch.Tensor,
|
| 764 |
+
w2: torch.Tensor,
|
| 765 |
+
w13_scale: torch.Tensor,
|
| 766 |
+
w2_scale: torch.Tensor,
|
| 767 |
+
w13_g_idx: torch.Tensor | None = None,
|
| 768 |
+
w2_g_idx: torch.Tensor | None = None,
|
| 769 |
+
w13_qzeros: torch.Tensor | None = None,
|
| 770 |
+
w2_qzeros: torch.Tensor | None = None,
|
| 771 |
+
w13_bias: torch.Tensor | None = None,
|
| 772 |
+
w2_bias: torch.Tensor | None = None,
|
| 773 |
+
) -> tuple[
|
| 774 |
+
torch.Tensor, # w13_qweight
|
| 775 |
+
torch.Tensor, # w2_qweight
|
| 776 |
+
torch.Tensor, # w13_scales
|
| 777 |
+
torch.Tensor, # w2_scales
|
| 778 |
+
torch.Tensor | None, # w13_g_idx
|
| 779 |
+
torch.Tensor | None, # w2_g_idx
|
| 780 |
+
torch.Tensor | None, # w13_g_idx_sort_indices
|
| 781 |
+
torch.Tensor | None, # w2_g_idx_sort_indices
|
| 782 |
+
torch.Tensor | None, # w13_qzeros
|
| 783 |
+
torch.Tensor | None, # w2_qzeros
|
| 784 |
+
torch.Tensor | None, # w13_input_global_scale
|
| 785 |
+
torch.Tensor | None, # w2_input_global_scale
|
| 786 |
+
torch.Tensor | None, # w13_bias
|
| 787 |
+
torch.Tensor | None, # w2_bias
|
| 788 |
+
]:
|
| 789 |
+
"""Dispatch weight post-processing to the appropriate per-backend handler.
|
| 790 |
+
|
| 791 |
+
To add a new backend, implement a ``_process_weights_<name>`` helper and
|
| 792 |
+
add a branch here.
|
| 793 |
+
|
| 794 |
+
Args:
|
| 795 |
+
backend: the selected ``WNA16MoEBackend``.
|
| 796 |
+
layer: the ``FusedMoE`` layer whose parameters are being prepared.
|
| 797 |
+
quant_config: the ``QuantizationConfig`` for this layer.
|
| 798 |
+
input_dtype: optional activation dtype, usually should be 16 bit.
|
| 799 |
+
"""
|
| 800 |
+
if backend in (
|
| 801 |
+
WNA16MoEBackend.MARLIN,
|
| 802 |
+
WNA16MoEBackend.BATCHED_MARLIN,
|
| 803 |
+
):
|
| 804 |
+
from vllm.model_executor.layers.quantization.auto_gptq import (
|
| 805 |
+
AutoGPTQConfig,
|
| 806 |
+
)
|
| 807 |
+
from vllm.model_executor.layers.quantization.awq_marlin import (
|
| 808 |
+
AWQMarlinConfig,
|
| 809 |
+
)
|
| 810 |
+
|
| 811 |
+
if isinstance(quant_config, AWQMarlinConfig):
|
| 812 |
+
if w13_qzeros is None or w2_qzeros is None:
|
| 813 |
+
raise ValueError("AWQ Marlin MoE requires zero-point tensors.")
|
| 814 |
+
|
| 815 |
+
weight_bits = quant_config.weight_bits
|
| 816 |
+
pack_factor = quant_config.pack_factor
|
| 817 |
+
group_size = quant_config.group_size
|
| 818 |
+
|
| 819 |
+
return _process_awq_weights_marlin(
|
| 820 |
+
layer,
|
| 821 |
+
weight_bits,
|
| 822 |
+
pack_factor,
|
| 823 |
+
group_size,
|
| 824 |
+
input_dtype,
|
| 825 |
+
w13,
|
| 826 |
+
w2,
|
| 827 |
+
w13_scale,
|
| 828 |
+
w2_scale,
|
| 829 |
+
w13_qzeros,
|
| 830 |
+
w2_qzeros,
|
| 831 |
+
w13_bias,
|
| 832 |
+
w2_bias,
|
| 833 |
+
)
|
| 834 |
+
elif isinstance(quant_config, AutoGPTQConfig):
|
| 835 |
+
num_bits = quant_config.quant_type.size_bits
|
| 836 |
+
pack_factor = quant_config.pack_factor
|
| 837 |
+
group_size = quant_config.group_size
|
| 838 |
+
actorder = "group" if quant_config.desc_act else None
|
| 839 |
+
elif isinstance(quant_config, QuantizationArgs):
|
| 840 |
+
num_bits = quant_config.num_bits
|
| 841 |
+
pack_factor = 32 // quant_config.num_bits
|
| 842 |
+
group_size = quant_config.group_size
|
| 843 |
+
actorder = quant_config.actorder
|
| 844 |
+
else:
|
| 845 |
+
raise TypeError(
|
| 846 |
+
"Marlin WNA16 MoE backend requires AutoGPTQConfig, AWQMarlinConfig or "
|
| 847 |
+
f"QuantizationArgs, got {type(quant_config).__name__}."
|
| 848 |
+
)
|
| 849 |
+
if w13_g_idx is None or w2_g_idx is None:
|
| 850 |
+
raise ValueError("GPTQ Marlin MoE requires g_idx tensors.")
|
| 851 |
+
return _process_weights_marlin(
|
| 852 |
+
layer,
|
| 853 |
+
input_dtype,
|
| 854 |
+
num_bits,
|
| 855 |
+
pack_factor,
|
| 856 |
+
group_size,
|
| 857 |
+
actorder,
|
| 858 |
+
w13,
|
| 859 |
+
w2,
|
| 860 |
+
w13_scale,
|
| 861 |
+
w2_scale,
|
| 862 |
+
w13_g_idx,
|
| 863 |
+
w2_g_idx,
|
| 864 |
+
w13_qzeros,
|
| 865 |
+
w2_qzeros,
|
| 866 |
+
w13_bias,
|
| 867 |
+
w2_bias,
|
| 868 |
+
)
|
| 869 |
+
elif backend == WNA16MoEBackend.FLASHINFER_TRTLLM:
|
| 870 |
+
return _process_weights_flashinfer(
|
| 871 |
+
w13,
|
| 872 |
+
w2,
|
| 873 |
+
w13_scale,
|
| 874 |
+
w2_scale,
|
| 875 |
+
w13_g_idx,
|
| 876 |
+
w2_g_idx,
|
| 877 |
+
w13_bias,
|
| 878 |
+
w2_bias,
|
| 879 |
+
)
|
| 880 |
+
elif backend == WNA16MoEBackend.XPU:
|
| 881 |
+
assert quant_config is not None
|
| 882 |
+
(
|
| 883 |
+
w13_xpu,
|
| 884 |
+
w2_xpu,
|
| 885 |
+
w13_scale_xpu,
|
| 886 |
+
w2_scale_xpu,
|
| 887 |
+
w13_bias_out,
|
| 888 |
+
w2_bias_out,
|
| 889 |
+
) = _process_weights_xpu(
|
| 890 |
+
layer,
|
| 891 |
+
quant_config,
|
| 892 |
+
w13,
|
| 893 |
+
w2,
|
| 894 |
+
w13_scale,
|
| 895 |
+
w2_scale,
|
| 896 |
+
w13_bias,
|
| 897 |
+
w2_bias,
|
| 898 |
+
)
|
| 899 |
+
empty = torch.empty((0,), dtype=torch.int32, device=w13.device)
|
| 900 |
+
return (
|
| 901 |
+
w13_xpu,
|
| 902 |
+
w2_xpu,
|
| 903 |
+
w13_scale_xpu,
|
| 904 |
+
w2_scale_xpu,
|
| 905 |
+
empty, # w13_g_idx
|
| 906 |
+
empty, # w2_g_idx
|
| 907 |
+
empty, # w13_g_idx_sort_indices
|
| 908 |
+
empty, # w2_g_idx_sort_indices
|
| 909 |
+
None, # w13_qzeros — sym int4 on XPU has none; kernel does uint4b8→s4
|
| 910 |
+
None, # w2_qzeros
|
| 911 |
+
None, # w13_input_global_scale
|
| 912 |
+
None, # w2_input_global_scale
|
| 913 |
+
w13_bias_out,
|
| 914 |
+
w2_bias_out,
|
| 915 |
+
)
|
| 916 |
+
else:
|
| 917 |
+
raise ValueError(f"Unsupported wna16 MoE backend: {backend.value}")
|
vllm-patches/C_config.py
ADDED
|
@@ -0,0 +1,1436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
from enum import IntEnum
|
| 5 |
+
from typing import Union
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
from vllm.config import ParallelConfig, SchedulerConfig
|
| 10 |
+
from vllm.config.kernel import MoEBackend
|
| 11 |
+
from vllm.distributed import get_dp_group, get_pcp_group, get_tensor_model_parallel_rank
|
| 12 |
+
from vllm.logger import init_logger
|
| 13 |
+
from vllm.model_executor.layers.fused_moe.activation import MoEActivation
|
| 14 |
+
from vllm.model_executor.layers.quantization.utils.ocp_mx_utils import (
|
| 15 |
+
OCP_MX_DTYPES,
|
| 16 |
+
OCP_MX_Scheme,
|
| 17 |
+
)
|
| 18 |
+
from vllm.model_executor.layers.quantization.utils.quant_utils import GroupShape
|
| 19 |
+
from vllm.platforms import current_platform
|
| 20 |
+
from vllm.utils.import_utils import has_triton_kernels
|
| 21 |
+
from vllm.utils.math_utils import cdiv
|
| 22 |
+
|
| 23 |
+
logger = init_logger(__name__)
|
| 24 |
+
|
| 25 |
+
if has_triton_kernels():
|
| 26 |
+
try:
|
| 27 |
+
from triton_kernels.matmul_ogs import PrecisionConfig
|
| 28 |
+
except (ImportError, AttributeError) as e:
|
| 29 |
+
logger.error(
|
| 30 |
+
"Failed to import Triton kernels. Please make sure your triton "
|
| 31 |
+
"version is compatible. Error: %s",
|
| 32 |
+
e,
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _get_config_dtype_str(
|
| 37 |
+
dtype: torch.dtype,
|
| 38 |
+
use_fp8_w8a8: bool = False,
|
| 39 |
+
use_fp8_w8a16: bool = False,
|
| 40 |
+
use_int8_w8a16: bool = False,
|
| 41 |
+
use_int4_w4a16: bool = False,
|
| 42 |
+
ocp_mx_scheme: str | None = None,
|
| 43 |
+
) -> str | None:
|
| 44 |
+
"""
|
| 45 |
+
Return a string used to construct the filename that contains the
|
| 46 |
+
tuning info for a particular quantization scheme. See
|
| 47 |
+
try_get_optimal_moe_config in fused_moe.py.
|
| 48 |
+
"""
|
| 49 |
+
if use_fp8_w8a8:
|
| 50 |
+
return "fp8_w8a8"
|
| 51 |
+
elif use_fp8_w8a16:
|
| 52 |
+
return "fp8_w8a16"
|
| 53 |
+
elif use_int8_w8a16:
|
| 54 |
+
return "int8_w8a16"
|
| 55 |
+
elif use_int4_w4a16:
|
| 56 |
+
return "int4_w4a16"
|
| 57 |
+
elif ocp_mx_scheme is not None:
|
| 58 |
+
# The output of this function is passed to `try_get_optimal_moe_config`,
|
| 59 |
+
# and as we only simulate OCP MX execution in fused_moe for now,
|
| 60 |
+
# we will NOT look for `*,dtype=w_mxfp4_a_mxfp4.json` for now.
|
| 61 |
+
return None
|
| 62 |
+
elif dtype == torch.float:
|
| 63 |
+
# avoiding cases where kernel fails when float32 MoE
|
| 64 |
+
# use fp16/bfloat16 configs
|
| 65 |
+
return "float32"
|
| 66 |
+
return None
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def _quant_flags_to_group_shape(
|
| 70 |
+
quant_dtype: torch.dtype | str | None,
|
| 71 |
+
per_act_token_quant: bool,
|
| 72 |
+
per_out_ch_quant: bool,
|
| 73 |
+
block_shape: list[int] | None,
|
| 74 |
+
) -> tuple[GroupShape | None, GroupShape | None]:
|
| 75 |
+
"""
|
| 76 |
+
Convert MoE quantization flags into more generic GroupShapes.
|
| 77 |
+
"""
|
| 78 |
+
a_shape: GroupShape | None
|
| 79 |
+
w_shape: GroupShape | None
|
| 80 |
+
if block_shape is not None:
|
| 81 |
+
assert not per_act_token_quant
|
| 82 |
+
assert not per_out_ch_quant
|
| 83 |
+
# TODO(bnell): this is not quite right for activations since first
|
| 84 |
+
# dim should be 1.
|
| 85 |
+
a_shape = GroupShape(row=block_shape[0], col=block_shape[1])
|
| 86 |
+
w_shape = GroupShape(row=block_shape[0], col=block_shape[1])
|
| 87 |
+
else:
|
| 88 |
+
w_shape = None
|
| 89 |
+
a_shape = None if quant_dtype is None else GroupShape.PER_TENSOR
|
| 90 |
+
|
| 91 |
+
if per_act_token_quant:
|
| 92 |
+
a_shape = GroupShape.PER_TOKEN
|
| 93 |
+
|
| 94 |
+
if per_out_ch_quant:
|
| 95 |
+
w_shape = GroupShape.PER_TOKEN
|
| 96 |
+
|
| 97 |
+
return a_shape, w_shape
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# The type of method in top-K routing
|
| 101 |
+
# Please keep this in sync with the counterpart defined in https://github.com/flashinfer-ai/flashinfer/blob/main/include/flashinfer/trtllm/fused_moe/runner.h
|
| 102 |
+
class RoutingMethodType(IntEnum):
|
| 103 |
+
# Default: Softmax -> TopK
|
| 104 |
+
Default = (0,)
|
| 105 |
+
# Renormalize: TopK -> Softmax
|
| 106 |
+
Renormalize = (1,)
|
| 107 |
+
# DeepSeekV3: Sigmoid -> RoutingBiasAdd -> Top2 in group -> Top4 groups
|
| 108 |
+
# -> Top8 experts from the Top4 groups
|
| 109 |
+
DeepSeekV3 = (2,)
|
| 110 |
+
# Llama4: Top1 -> Sigmoid
|
| 111 |
+
Llama4 = (3,)
|
| 112 |
+
# RenormalizeNaive: Softmax -> TopK -> Renormalize
|
| 113 |
+
RenormalizeNaive = (4,)
|
| 114 |
+
# TopK: TopK (no softmax)
|
| 115 |
+
TopK = (5,)
|
| 116 |
+
# SigmoidRenorm: Sigmoid -> TopK -> Renormalize (divide by sum of top-K)
|
| 117 |
+
SigmoidRenorm = (6,)
|
| 118 |
+
# MiniMax2: Sigmoid + Bias -> TopK -> ScaledSumNormalize
|
| 119 |
+
# (routeScale=1.0, epsilon=1e-20)
|
| 120 |
+
MiniMax2 = (7,)
|
| 121 |
+
# Sigmoid: Sigmoid -> TopK (no renormalization)
|
| 122 |
+
Sigmoid = (8,)
|
| 123 |
+
# Unspecified
|
| 124 |
+
Unspecified = (9,)
|
| 125 |
+
# other routing types (not passed to FlashInfer kernels)
|
| 126 |
+
# Deepseek V4 -> sqrtsoftplus + Bias + Normalize
|
| 127 |
+
DeepseekV4 = (100,)
|
| 128 |
+
Custom = (101,)
|
| 129 |
+
Simulated = (102,)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def get_routing_method_type(
|
| 133 |
+
scoring_func: str,
|
| 134 |
+
top_k: int,
|
| 135 |
+
renormalize: bool,
|
| 136 |
+
num_expert_group: int | None,
|
| 137 |
+
has_e_score_bias: bool,
|
| 138 |
+
routed_scaling_factor: float | None = 1.0,
|
| 139 |
+
) -> RoutingMethodType:
|
| 140 |
+
if scoring_func == "sqrtsoftplus":
|
| 141 |
+
# DeepSeek V4 uses sqrtsoftplus routing with optional routing bias
|
| 142 |
+
# and top-k renormalization.
|
| 143 |
+
if renormalize:
|
| 144 |
+
return RoutingMethodType.DeepseekV4
|
| 145 |
+
else:
|
| 146 |
+
return RoutingMethodType.Unspecified
|
| 147 |
+
|
| 148 |
+
if has_e_score_bias:
|
| 149 |
+
if scoring_func == "sigmoid":
|
| 150 |
+
if not renormalize:
|
| 151 |
+
return RoutingMethodType.Unspecified
|
| 152 |
+
if (num_expert_group or 0) > 0:
|
| 153 |
+
return RoutingMethodType.DeepSeekV3
|
| 154 |
+
if routed_scaling_factor in (None, 1.0):
|
| 155 |
+
return RoutingMethodType.MiniMax2
|
| 156 |
+
return RoutingMethodType.Unspecified
|
| 157 |
+
else:
|
| 158 |
+
return RoutingMethodType.Unspecified
|
| 159 |
+
|
| 160 |
+
if scoring_func == "sigmoid":
|
| 161 |
+
if renormalize:
|
| 162 |
+
return RoutingMethodType.SigmoidRenorm
|
| 163 |
+
return RoutingMethodType.Sigmoid
|
| 164 |
+
|
| 165 |
+
if scoring_func == "softmax":
|
| 166 |
+
if renormalize:
|
| 167 |
+
return RoutingMethodType.RenormalizeNaive
|
| 168 |
+
else:
|
| 169 |
+
return RoutingMethodType.Default
|
| 170 |
+
|
| 171 |
+
return RoutingMethodType.Unspecified
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
@dataclass
|
| 175 |
+
class FusedMoEQuantDesc:
|
| 176 |
+
"""
|
| 177 |
+
A quantization descriptor for fused MoE ops. This class can describe
|
| 178 |
+
either activations or weights.
|
| 179 |
+
"""
|
| 180 |
+
|
| 181 |
+
# The quantized type of this parameters. None means unquantized or
|
| 182 |
+
# already quantized.
|
| 183 |
+
# TODO (bnell): use scalar_type instead of Union.
|
| 184 |
+
dtype: torch.dtype | str | None = None
|
| 185 |
+
|
| 186 |
+
# A field that describes the quantization group shape, from quant_utils.py.
|
| 187 |
+
# * (-1, -1) for per-tensor quantization
|
| 188 |
+
# * (1, -1) for per-row quantization
|
| 189 |
+
# * (-1, 1) for per-column quantization
|
| 190 |
+
# * (128, 128) for 128x128 deepseek style block quantization
|
| 191 |
+
# * (1, 128) for deepseek style activation quantization
|
| 192 |
+
# (i.e. per-token-per-group)
|
| 193 |
+
shape: GroupShape | None = None
|
| 194 |
+
|
| 195 |
+
# Quantization scales.
|
| 196 |
+
# TODO(bnell): maybe put PrecisionConfigs in subclass of QuantDesc?
|
| 197 |
+
scale: Union[torch.Tensor, "PrecisionConfig", None] = None
|
| 198 |
+
|
| 199 |
+
# Quantization alphas or gscales, used for nvfp4 types.
|
| 200 |
+
# W4A8 FP8: used for per-channel scales
|
| 201 |
+
# TODO(bnell): put some of these in subclasses
|
| 202 |
+
alpha_or_gscale: torch.Tensor | None = None
|
| 203 |
+
|
| 204 |
+
# Zero points for int4/int8 types
|
| 205 |
+
zp: torch.Tensor | None = None
|
| 206 |
+
|
| 207 |
+
# Biases for GPT triton MoE
|
| 208 |
+
bias: torch.Tensor | None = None
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
# TODO(bnell): have subclasses for specific moe methods?
|
| 212 |
+
# e.g. for specific arguments bias, precision, etc.
|
| 213 |
+
@dataclass
|
| 214 |
+
class FusedMoEQuantConfig:
|
| 215 |
+
"""
|
| 216 |
+
The FusedMoEQuantConfig contains all the quantization parameters for
|
| 217 |
+
a single FusedMoEMethodBase operation. It consists of four
|
| 218 |
+
FusedMoEQuantDescs, one for each activation and set of weights.
|
| 219 |
+
|
| 220 |
+
Each FusedMoEMethodBase must implement a get_fused_moe_quant_config
|
| 221 |
+
method to construct a FusedMoEQuantConfig for use with that class.
|
| 222 |
+
|
| 223 |
+
FusedMoEQuant configs are only used for modular kernels, fused_experts
|
| 224 |
+
(from fused_moe.py), cutlass_moe_fp[48], rocm_aiter_fused_experts and
|
| 225 |
+
triton_kernel_moe_forward. Other MoE methods can ignore the
|
| 226 |
+
FusedMoEQuantConfig (for now) and hardcode it to None.
|
| 227 |
+
|
| 228 |
+
There are currently some restrictions on what can be expressed:
|
| 229 |
+
- Most MoE ops only support similar quantization strategies for
|
| 230 |
+
each parameter, e.g. both weights must have the same GroupShape
|
| 231 |
+
and both activations must share the same GroupShape. One exception to
|
| 232 |
+
this is the cutlass moe which allows per channel quantization on the
|
| 233 |
+
outputs. Note: this restrictions are not always rigorously checked.
|
| 234 |
+
- Not all fused MoE functions support all the parameters, e.g. zero points,
|
| 235 |
+
global scales, alphas and biases are not universally supported.
|
| 236 |
+
- Fully general GroupShapes are not allowed. Activations only support
|
| 237 |
+
per token, per tensor or K-blocked.
|
| 238 |
+
- Weights are not required to have a GroupShape since they have already
|
| 239 |
+
been quantized.
|
| 240 |
+
|
| 241 |
+
Other notes:
|
| 242 |
+
- PrecisionConfigs are specific to GPT OSS Triton.
|
| 243 |
+
- As a follow up it would probably make sense to subclass FusedMoEQuantDesc
|
| 244 |
+
or FusedMoEQuantConfig for particular FusedMoEMethodBase subclasses
|
| 245 |
+
so that only the required quantization parameters are used/stored.
|
| 246 |
+
"""
|
| 247 |
+
|
| 248 |
+
# TODO(bnell) make sure a1_scales/a2_scales don't interfere with chunking
|
| 249 |
+
_a1: FusedMoEQuantDesc
|
| 250 |
+
_a2: FusedMoEQuantDesc
|
| 251 |
+
_w1: FusedMoEQuantDesc
|
| 252 |
+
_w2: FusedMoEQuantDesc
|
| 253 |
+
is_scale_swizzled: bool = True
|
| 254 |
+
|
| 255 |
+
# MXFP4-specific TRTLLM parameters for SwiGLU activation clamping.
|
| 256 |
+
# These correspond to gemm1_alpha, gemm1_beta, gemm1_clamp_limit
|
| 257 |
+
# in TrtLlmMxfp4ExpertsBase.
|
| 258 |
+
gemm1_alpha: float | None = None
|
| 259 |
+
gemm1_beta: float | None = None
|
| 260 |
+
gemm1_clamp_limit: float | None = None
|
| 261 |
+
|
| 262 |
+
mx_alignment: int = 0
|
| 263 |
+
|
| 264 |
+
def __post_init__(self):
|
| 265 |
+
assert not self.per_act_token_quant or self.block_shape is None, (
|
| 266 |
+
"illegal quantization"
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
#
|
| 270 |
+
# Convenience accessors for various properties.
|
| 271 |
+
#
|
| 272 |
+
|
| 273 |
+
@property
|
| 274 |
+
def quant_dtype(self) -> torch.dtype | str | None:
|
| 275 |
+
return self._a1.dtype
|
| 276 |
+
|
| 277 |
+
@property
|
| 278 |
+
def weight_quant_dtype(self) -> torch.dtype | str | None:
|
| 279 |
+
return self._w1.dtype
|
| 280 |
+
|
| 281 |
+
@property
|
| 282 |
+
def is_quantized(self) -> bool:
|
| 283 |
+
return self.quant_dtype is not None
|
| 284 |
+
|
| 285 |
+
@property
|
| 286 |
+
def is_per_act_token(self) -> bool:
|
| 287 |
+
return self._a1.shape == GroupShape.PER_TOKEN
|
| 288 |
+
|
| 289 |
+
@property
|
| 290 |
+
def per_act_token_quant(self) -> bool:
|
| 291 |
+
return self._a1.shape == GroupShape.PER_TOKEN
|
| 292 |
+
|
| 293 |
+
@property
|
| 294 |
+
def per_out_ch_quant(self) -> bool:
|
| 295 |
+
return self._w1.shape == GroupShape.PER_TOKEN
|
| 296 |
+
|
| 297 |
+
@property
|
| 298 |
+
def is_per_tensor(self) -> bool:
|
| 299 |
+
return self._a1.shape == GroupShape.PER_TENSOR
|
| 300 |
+
|
| 301 |
+
@property
|
| 302 |
+
def block_shape(self) -> list[int] | None:
|
| 303 |
+
if (
|
| 304 |
+
self._a1.shape is not None
|
| 305 |
+
and self._a1.shape != GroupShape.PER_TENSOR
|
| 306 |
+
and self._a1.shape != GroupShape.PER_TOKEN
|
| 307 |
+
):
|
| 308 |
+
return [self._a1.shape.row, self._a1.shape.col]
|
| 309 |
+
else:
|
| 310 |
+
return None
|
| 311 |
+
|
| 312 |
+
@property
|
| 313 |
+
def is_block_quantized(self) -> bool:
|
| 314 |
+
return self.block_shape is not None
|
| 315 |
+
|
| 316 |
+
@property
|
| 317 |
+
def a1_scale(self) -> torch.Tensor | None:
|
| 318 |
+
assert self._a1.scale is None or isinstance(self._a1.scale, torch.Tensor)
|
| 319 |
+
return self._a1.scale
|
| 320 |
+
|
| 321 |
+
@property
|
| 322 |
+
def a1_gscale(self) -> torch.Tensor | None:
|
| 323 |
+
return self._a1.alpha_or_gscale
|
| 324 |
+
|
| 325 |
+
@property
|
| 326 |
+
def a2_scale(self) -> torch.Tensor | None:
|
| 327 |
+
assert self._a2.scale is None or isinstance(self._a2.scale, torch.Tensor)
|
| 328 |
+
return self._a2.scale
|
| 329 |
+
|
| 330 |
+
@property
|
| 331 |
+
def a2_gscale(self) -> torch.Tensor | None:
|
| 332 |
+
return self._a2.alpha_or_gscale
|
| 333 |
+
|
| 334 |
+
@property
|
| 335 |
+
def w1_scale(self) -> torch.Tensor | None:
|
| 336 |
+
assert self._w1.scale is None or isinstance(self._w1.scale, torch.Tensor)
|
| 337 |
+
return self._w1.scale
|
| 338 |
+
|
| 339 |
+
@property
|
| 340 |
+
def w1_zp(self) -> torch.Tensor | None:
|
| 341 |
+
return self._w1.zp
|
| 342 |
+
|
| 343 |
+
@property
|
| 344 |
+
def w1_bias(self) -> torch.Tensor | None:
|
| 345 |
+
return self._w1.bias
|
| 346 |
+
|
| 347 |
+
@property
|
| 348 |
+
def w1_precision(self) -> "PrecisionConfig | None":
|
| 349 |
+
assert self._w1.scale is None or isinstance(self._w1.scale, PrecisionConfig)
|
| 350 |
+
return self._w1.scale
|
| 351 |
+
|
| 352 |
+
@property
|
| 353 |
+
def g1_alphas(self) -> torch.Tensor | None:
|
| 354 |
+
return self._w1.alpha_or_gscale
|
| 355 |
+
|
| 356 |
+
@property
|
| 357 |
+
def w2_scale(self) -> torch.Tensor | None:
|
| 358 |
+
assert self._w2.scale is None or isinstance(self._w2.scale, torch.Tensor)
|
| 359 |
+
return self._w2.scale
|
| 360 |
+
|
| 361 |
+
@property
|
| 362 |
+
def w2_zp(self) -> torch.Tensor | None:
|
| 363 |
+
return self._w2.zp
|
| 364 |
+
|
| 365 |
+
@property
|
| 366 |
+
def w2_bias(self) -> torch.Tensor | None:
|
| 367 |
+
return self._w2.bias
|
| 368 |
+
|
| 369 |
+
@property
|
| 370 |
+
def w2_precision(self) -> "PrecisionConfig | None":
|
| 371 |
+
assert self._w2.scale is None or isinstance(self._w2.scale, PrecisionConfig)
|
| 372 |
+
return self._w2.scale
|
| 373 |
+
|
| 374 |
+
@property
|
| 375 |
+
def g2_alphas(self) -> torch.Tensor | None:
|
| 376 |
+
return self._w2.alpha_or_gscale
|
| 377 |
+
|
| 378 |
+
@property
|
| 379 |
+
def use_fp8_w8a8(self) -> bool:
|
| 380 |
+
return self.quant_dtype == current_platform.fp8_dtype()
|
| 381 |
+
|
| 382 |
+
@property
|
| 383 |
+
def use_int8_w8a8(self) -> bool:
|
| 384 |
+
return self.quant_dtype == torch.int8
|
| 385 |
+
|
| 386 |
+
@property
|
| 387 |
+
def use_int8_w8a16(self) -> bool:
|
| 388 |
+
return self._a1.dtype is None and self._w1.dtype == torch.int8
|
| 389 |
+
|
| 390 |
+
@property
|
| 391 |
+
def use_fp8_w8a16(self) -> bool:
|
| 392 |
+
return self._a1.dtype is None and self._w1.dtype == current_platform.fp8_dtype()
|
| 393 |
+
|
| 394 |
+
@property
|
| 395 |
+
def use_int4_w4a16(self) -> bool:
|
| 396 |
+
return self._a1.dtype is None and self._w1.dtype == "int4"
|
| 397 |
+
|
| 398 |
+
@property
|
| 399 |
+
def use_nvfp4_w4a16(self) -> bool:
|
| 400 |
+
return self._a1.dtype is None and self._w1.dtype == "nvfp4"
|
| 401 |
+
|
| 402 |
+
@property
|
| 403 |
+
def ocp_mx_scheme(self) -> str | None:
|
| 404 |
+
if not hasattr(self, "_ocp_mx_scheme"):
|
| 405 |
+
if (self._a1.dtype is not None and not isinstance(self._a1.dtype, str)) or (
|
| 406 |
+
self._w1.dtype is not None and not isinstance(self._w1.dtype, str)
|
| 407 |
+
):
|
| 408 |
+
self._ocp_mx_scheme = None
|
| 409 |
+
else:
|
| 410 |
+
ocp_mx_scheme = OCP_MX_Scheme.from_quant_dtype(
|
| 411 |
+
self._a1.dtype, self._w1.dtype
|
| 412 |
+
)
|
| 413 |
+
|
| 414 |
+
if ocp_mx_scheme is not None:
|
| 415 |
+
ocp_mx_scheme = ocp_mx_scheme.value
|
| 416 |
+
|
| 417 |
+
self._ocp_mx_scheme = ocp_mx_scheme
|
| 418 |
+
|
| 419 |
+
return self._ocp_mx_scheme
|
| 420 |
+
|
| 421 |
+
@property
|
| 422 |
+
def use_mxfp4_w4a16(self) -> bool:
|
| 423 |
+
return self._a1.dtype is None and self._w1.dtype == "mxfp4"
|
| 424 |
+
|
| 425 |
+
@property
|
| 426 |
+
def use_mxfp4_w4a4(self) -> bool:
|
| 427 |
+
return self._a1.dtype == "mxfp4" and self._w1.dtype == "mxfp4"
|
| 428 |
+
|
| 429 |
+
@property
|
| 430 |
+
def use_nvfp4_w4a4(self) -> bool:
|
| 431 |
+
return self.quant_dtype == "nvfp4"
|
| 432 |
+
|
| 433 |
+
@property
|
| 434 |
+
def use_mxfp4_w4a8(self) -> bool:
|
| 435 |
+
return self._a1.dtype == "fp8" and self._w1.dtype == "mxfp4"
|
| 436 |
+
|
| 437 |
+
def config_name(self, dtype: torch.dtype) -> str | None:
|
| 438 |
+
"""
|
| 439 |
+
Return a string used to construct the filename that contains the
|
| 440 |
+
tuning info for a particular quantization scheme. See
|
| 441 |
+
try_get_optimal_moe_config in fused_moe.py.
|
| 442 |
+
"""
|
| 443 |
+
return _get_config_dtype_str(
|
| 444 |
+
use_fp8_w8a8=self.use_fp8_w8a8,
|
| 445 |
+
use_fp8_w8a16=self.use_fp8_w8a16,
|
| 446 |
+
use_int8_w8a16=self.use_int8_w8a16,
|
| 447 |
+
use_int4_w4a16=self.use_int4_w4a16,
|
| 448 |
+
ocp_mx_scheme=self.ocp_mx_scheme,
|
| 449 |
+
dtype=dtype,
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
def scale_shape(
|
| 453 |
+
self,
|
| 454 |
+
max_tokens: int,
|
| 455 |
+
hidden_dim: int,
|
| 456 |
+
) -> tuple[int, int] | None:
|
| 457 |
+
"""
|
| 458 |
+
Construct the proper activation scale shape for this
|
| 459 |
+
config.
|
| 460 |
+
"""
|
| 461 |
+
if self.is_quantized:
|
| 462 |
+
if self.is_block_quantized:
|
| 463 |
+
assert self.block_shape is not None
|
| 464 |
+
_, block_k = self.block_shape
|
| 465 |
+
k_tiles = cdiv(hidden_dim, block_k)
|
| 466 |
+
return (max_tokens, k_tiles)
|
| 467 |
+
elif self.is_per_act_token:
|
| 468 |
+
return (max_tokens, 1)
|
| 469 |
+
else:
|
| 470 |
+
return (1, 1)
|
| 471 |
+
else:
|
| 472 |
+
return None
|
| 473 |
+
|
| 474 |
+
def batched_scale_shape(
|
| 475 |
+
self,
|
| 476 |
+
num_experts: int,
|
| 477 |
+
max_tokens: int,
|
| 478 |
+
hidden_dim: int,
|
| 479 |
+
) -> tuple[int, int, int] | None:
|
| 480 |
+
"""
|
| 481 |
+
Construct the proper activation batched scale shape for this
|
| 482 |
+
config, e.g. (num experts, *scale_shape).
|
| 483 |
+
"""
|
| 484 |
+
if self.is_quantized:
|
| 485 |
+
scale_shape = self.scale_shape(max_tokens, hidden_dim)
|
| 486 |
+
assert scale_shape is not None
|
| 487 |
+
return (num_experts, *scale_shape)
|
| 488 |
+
else:
|
| 489 |
+
return None
|
| 490 |
+
|
| 491 |
+
@staticmethod
|
| 492 |
+
def make(
|
| 493 |
+
quant_dtype: torch.dtype | str | None = None,
|
| 494 |
+
per_act_token_quant: bool = False,
|
| 495 |
+
per_out_ch_quant: bool = False,
|
| 496 |
+
block_shape: list[int] | None = None,
|
| 497 |
+
w1_scale: Union[torch.Tensor, "PrecisionConfig", None] = None,
|
| 498 |
+
w2_scale: Union[torch.Tensor, "PrecisionConfig", None] = None,
|
| 499 |
+
a1_scale: torch.Tensor | None = None,
|
| 500 |
+
a2_scale: torch.Tensor | None = None,
|
| 501 |
+
g1_alphas: torch.Tensor | None = None,
|
| 502 |
+
g2_alphas: torch.Tensor | None = None,
|
| 503 |
+
a1_gscale: torch.Tensor | None = None,
|
| 504 |
+
a2_gscale: torch.Tensor | None = None,
|
| 505 |
+
w1_bias: torch.Tensor | None = None,
|
| 506 |
+
w2_bias: torch.Tensor | None = None,
|
| 507 |
+
w1_zp: torch.Tensor | None = None,
|
| 508 |
+
w2_zp: torch.Tensor | None = None,
|
| 509 |
+
weight_dtype: torch.dtype | str | None = None,
|
| 510 |
+
is_scale_swizzled: bool = True,
|
| 511 |
+
gemm1_alpha: float | None = None,
|
| 512 |
+
gemm1_beta: float | None = None,
|
| 513 |
+
gemm1_clamp_limit: float | None = None,
|
| 514 |
+
) -> "FusedMoEQuantConfig":
|
| 515 |
+
"""
|
| 516 |
+
General builder function for a FusedMoEQuantConfig.
|
| 517 |
+
- quant_dtype: Optional quantization type. None if activations are
|
| 518 |
+
unquantized or quantized prior to calling. Note: "nvfp4", "mxfp4",
|
| 519 |
+
"mxfp6_e3m2", "mxfp6_e2m3" are the only valid string values
|
| 520 |
+
for quant_dtype.
|
| 521 |
+
- per_act_token_quant: Activations have per token quantization.
|
| 522 |
+
- per_out_ch_quant: Outputs have per channel quantization. (only
|
| 523 |
+
for cutlass).
|
| 524 |
+
- block_shape: Optional block size for block-wise quantization.
|
| 525 |
+
Incompatible with per_act_token and per_out_ch quant.
|
| 526 |
+
- w1_scale: Optional scale to be used for w1.
|
| 527 |
+
- w2_scale: Optional scale to be used for w2.
|
| 528 |
+
- a1_scale: Optional scale to be used for a1.
|
| 529 |
+
- a2_scale: Optional scale to be used for a2.
|
| 530 |
+
- g1_alphas: Optional global quantization scales for w1 (for nvfp4).
|
| 531 |
+
Optional per-channel scales for w1 (for W4A8 FP8).
|
| 532 |
+
Optional dq scale i.e. w_scale * a_scale (for W8A8 fp8).
|
| 533 |
+
- g2_alphas: Optional global quantization scales for w2 (for nvfp4).
|
| 534 |
+
Optional per-channel scales for w2 (for W4A8 FP8).
|
| 535 |
+
Optional dq scale i.e. w_scale * a_scale (for W8A8 fp8).
|
| 536 |
+
- a1_gscale: Optional global quantization scales for a1 (1.0 /a2_scale).
|
| 537 |
+
- a2_gscale: Optional global quantization scales for a2 (1.0 /a2_scale).
|
| 538 |
+
|
| 539 |
+
- w1_bias: Optional biases for w1 (GPT OSS Triton).
|
| 540 |
+
- w2_bias: Optional biases for w1 (GPT OSS Triton).
|
| 541 |
+
- w1_zp: Optional w1 zero points for int4/int8 quantization.
|
| 542 |
+
- w2_zp: Optional w2 zero points for int4/int8 quantization.
|
| 543 |
+
- is_scale_swizzled: Whether the activation scale-factor layout is
|
| 544 |
+
swizzled. Pass through to the underlying quantization kernel for
|
| 545 |
+
dtypes that distinguish layouts (nvfp4, mxfp8). Defaults to True.
|
| 546 |
+
- gemm1_alpha: Optional MXFP4 TRTLLM SwiGLU alpha parameter.
|
| 547 |
+
- gemm1_beta: Optional MXFP4 TRTLLM SwiGLU beta parameter.
|
| 548 |
+
- gemm1_clamp_limit: Optional MXFP4 TRTLLM SwiGLU clamp limit.
|
| 549 |
+
"""
|
| 550 |
+
assert not isinstance(quant_dtype, str) or quant_dtype in {
|
| 551 |
+
"nvfp4",
|
| 552 |
+
"mxfp4",
|
| 553 |
+
"mxfp6_e3m2",
|
| 554 |
+
"mxfp6_e2m3",
|
| 555 |
+
"mxfp8",
|
| 556 |
+
}
|
| 557 |
+
assert not isinstance(weight_dtype, str) or weight_dtype in {
|
| 558 |
+
"nvfp4",
|
| 559 |
+
"mxfp4",
|
| 560 |
+
"mxfp6_e3m2",
|
| 561 |
+
"mxfp6_e2m3",
|
| 562 |
+
"int4",
|
| 563 |
+
"mxfp8",
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
if weight_dtype is None:
|
| 567 |
+
weight_dtype = quant_dtype
|
| 568 |
+
|
| 569 |
+
a_shape, w_shape = _quant_flags_to_group_shape(
|
| 570 |
+
quant_dtype, per_act_token_quant, per_out_ch_quant, block_shape
|
| 571 |
+
)
|
| 572 |
+
quant_config = FusedMoEQuantConfig(
|
| 573 |
+
_a1=FusedMoEQuantDesc(quant_dtype, a_shape, a1_scale, a1_gscale),
|
| 574 |
+
_a2=FusedMoEQuantDesc(quant_dtype, a_shape, a2_scale, a2_gscale),
|
| 575 |
+
_w1=FusedMoEQuantDesc(
|
| 576 |
+
weight_dtype, w_shape, w1_scale, g1_alphas, w1_zp, w1_bias
|
| 577 |
+
),
|
| 578 |
+
_w2=FusedMoEQuantDesc(
|
| 579 |
+
weight_dtype, w_shape, w2_scale, g2_alphas, w2_zp, w2_bias
|
| 580 |
+
),
|
| 581 |
+
is_scale_swizzled=is_scale_swizzled,
|
| 582 |
+
gemm1_alpha=gemm1_alpha,
|
| 583 |
+
gemm1_beta=gemm1_beta,
|
| 584 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 585 |
+
)
|
| 586 |
+
assert quant_config.per_act_token_quant == per_act_token_quant
|
| 587 |
+
assert quant_config.per_out_ch_quant == per_out_ch_quant
|
| 588 |
+
assert quant_config.block_shape == block_shape
|
| 589 |
+
return quant_config
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
def fp8_w8a8_moe_quant_config(
|
| 593 |
+
w1_scale: torch.Tensor,
|
| 594 |
+
w2_scale: torch.Tensor,
|
| 595 |
+
a1_scale: torch.Tensor | None = None,
|
| 596 |
+
a2_scale: torch.Tensor | None = None,
|
| 597 |
+
w1_bias: torch.Tensor | None = None,
|
| 598 |
+
w2_bias: torch.Tensor | None = None,
|
| 599 |
+
per_act_token_quant: bool = False,
|
| 600 |
+
per_out_ch_quant: bool = False,
|
| 601 |
+
block_shape: list[int] | None = None,
|
| 602 |
+
a1_gscale: torch.Tensor | None = None,
|
| 603 |
+
a2_gscale: torch.Tensor | None = None,
|
| 604 |
+
g1_alphas: torch.Tensor | None = None,
|
| 605 |
+
g2_alphas: torch.Tensor | None = None,
|
| 606 |
+
gemm1_clamp_limit: float | None = None,
|
| 607 |
+
) -> FusedMoEQuantConfig:
|
| 608 |
+
"""
|
| 609 |
+
Construct a quant config for fp8 activations and fp8 weights.
|
| 610 |
+
"""
|
| 611 |
+
return FusedMoEQuantConfig.make(
|
| 612 |
+
current_platform.fp8_dtype(),
|
| 613 |
+
w1_scale=w1_scale,
|
| 614 |
+
g1_alphas=g1_alphas,
|
| 615 |
+
w2_scale=w2_scale,
|
| 616 |
+
g2_alphas=g2_alphas,
|
| 617 |
+
w1_bias=w1_bias,
|
| 618 |
+
w2_bias=w2_bias,
|
| 619 |
+
a1_scale=a1_scale,
|
| 620 |
+
a1_gscale=a1_gscale,
|
| 621 |
+
a2_scale=a2_scale,
|
| 622 |
+
a2_gscale=a2_gscale,
|
| 623 |
+
per_act_token_quant=per_act_token_quant,
|
| 624 |
+
per_out_ch_quant=per_out_ch_quant,
|
| 625 |
+
block_shape=block_shape,
|
| 626 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 627 |
+
)
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
def int8_w8a8_moe_quant_config(
|
| 631 |
+
w1_scale: torch.Tensor,
|
| 632 |
+
w2_scale: torch.Tensor,
|
| 633 |
+
a1_scale: torch.Tensor | None,
|
| 634 |
+
a2_scale: torch.Tensor | None,
|
| 635 |
+
w1_bias: torch.Tensor | None = None,
|
| 636 |
+
w2_bias: torch.Tensor | None = None,
|
| 637 |
+
per_act_token_quant: bool = False,
|
| 638 |
+
) -> FusedMoEQuantConfig:
|
| 639 |
+
"""
|
| 640 |
+
Construct a quant config for int8 activations and int8 weights.
|
| 641 |
+
"""
|
| 642 |
+
return FusedMoEQuantConfig.make(
|
| 643 |
+
torch.int8,
|
| 644 |
+
w1_scale=w1_scale,
|
| 645 |
+
w2_scale=w2_scale,
|
| 646 |
+
a1_scale=a1_scale,
|
| 647 |
+
a2_scale=a2_scale,
|
| 648 |
+
w1_bias=w1_bias,
|
| 649 |
+
w2_bias=w2_bias,
|
| 650 |
+
per_act_token_quant=per_act_token_quant,
|
| 651 |
+
per_out_ch_quant=False,
|
| 652 |
+
block_shape=None,
|
| 653 |
+
)
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
def gptq_marlin_moe_quant_config(
|
| 657 |
+
w1_scale: torch.Tensor,
|
| 658 |
+
w2_scale: torch.Tensor,
|
| 659 |
+
weight_bits: int,
|
| 660 |
+
group_size: int,
|
| 661 |
+
w1_zp: torch.Tensor | None = None,
|
| 662 |
+
w2_zp: torch.Tensor | None = None,
|
| 663 |
+
w1_bias: torch.Tensor | None = None,
|
| 664 |
+
w2_bias: torch.Tensor | None = None,
|
| 665 |
+
):
|
| 666 |
+
"""
|
| 667 |
+
Construct a quant config for gptq marlin quantization.
|
| 668 |
+
"""
|
| 669 |
+
from vllm.model_executor.layers.quantization.utils.quant_utils import GroupShape
|
| 670 |
+
|
| 671 |
+
w_shape = None if group_size == -1 else GroupShape(row=1, col=group_size)
|
| 672 |
+
|
| 673 |
+
# Activations are NOT quantized for GPTQ (fp16/bf16)
|
| 674 |
+
a_shape = w_shape # Same as weight shape for alignment
|
| 675 |
+
|
| 676 |
+
# Determine weight dtype
|
| 677 |
+
if weight_bits == 4:
|
| 678 |
+
weight_dtype = "int4"
|
| 679 |
+
elif weight_bits == 8:
|
| 680 |
+
weight_dtype = torch.int8
|
| 681 |
+
else:
|
| 682 |
+
raise ValueError(f"Unsupported weight_bits: {weight_bits}")
|
| 683 |
+
|
| 684 |
+
return FusedMoEQuantConfig(
|
| 685 |
+
_a1=FusedMoEQuantDesc(dtype=None, shape=a_shape),
|
| 686 |
+
_a2=FusedMoEQuantDesc(dtype=None, shape=a_shape),
|
| 687 |
+
_w1=FusedMoEQuantDesc(weight_dtype, w_shape, w1_scale, None, w1_zp, w1_bias),
|
| 688 |
+
_w2=FusedMoEQuantDesc(weight_dtype, w_shape, w2_scale, None, w2_zp, w2_bias),
|
| 689 |
+
)
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
def mxfp4_w4a16_moe_quant_config(
|
| 693 |
+
w1_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 694 |
+
w2_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 695 |
+
w1_bias: torch.Tensor | None = None,
|
| 696 |
+
w2_bias: torch.Tensor | None = None,
|
| 697 |
+
gemm1_alpha: float | None = None,
|
| 698 |
+
gemm1_beta: float | None = None,
|
| 699 |
+
gemm1_clamp_limit: float | None = None,
|
| 700 |
+
) -> FusedMoEQuantConfig:
|
| 701 |
+
"""
|
| 702 |
+
Construct a quant config for unquantized activations and mxfp4 weights.
|
| 703 |
+
"""
|
| 704 |
+
return FusedMoEQuantConfig(
|
| 705 |
+
_a1=FusedMoEQuantDesc(),
|
| 706 |
+
_a2=FusedMoEQuantDesc(),
|
| 707 |
+
_w1=FusedMoEQuantDesc("mxfp4", None, w1_scale, None, None, w1_bias),
|
| 708 |
+
_w2=FusedMoEQuantDesc("mxfp4", None, w2_scale, None, None, w2_bias),
|
| 709 |
+
gemm1_alpha=gemm1_alpha,
|
| 710 |
+
gemm1_beta=gemm1_beta,
|
| 711 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 712 |
+
)
|
| 713 |
+
|
| 714 |
+
|
| 715 |
+
def mxfp4_mxfp8_moe_quant_config(
|
| 716 |
+
w1_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 717 |
+
w2_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 718 |
+
a1_scale: torch.Tensor | None = None,
|
| 719 |
+
a2_scale: torch.Tensor | None = None,
|
| 720 |
+
w1_bias: torch.Tensor | None = None,
|
| 721 |
+
w2_bias: torch.Tensor | None = None,
|
| 722 |
+
block_shape: list[int] | None = None,
|
| 723 |
+
gemm1_alpha: float | None = None,
|
| 724 |
+
gemm1_beta: float | None = None,
|
| 725 |
+
gemm1_clamp_limit: float | None = None,
|
| 726 |
+
mx_alignment: int = 0,
|
| 727 |
+
is_scale_swizzled: bool = True,
|
| 728 |
+
) -> FusedMoEQuantConfig:
|
| 729 |
+
"""
|
| 730 |
+
Construct a quant config for mxfp4 activations and mxfp4 weights.
|
| 731 |
+
"""
|
| 732 |
+
return FusedMoEQuantConfig(
|
| 733 |
+
_a1=FusedMoEQuantDesc("mxfp8"),
|
| 734 |
+
_a2=FusedMoEQuantDesc("mxfp8"),
|
| 735 |
+
_w1=FusedMoEQuantDesc("mxfp4", None, w1_scale, None, None, w1_bias),
|
| 736 |
+
_w2=FusedMoEQuantDesc("mxfp4", None, w2_scale, None, None, w2_bias),
|
| 737 |
+
gemm1_alpha=gemm1_alpha,
|
| 738 |
+
gemm1_beta=gemm1_beta,
|
| 739 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 740 |
+
mx_alignment=mx_alignment,
|
| 741 |
+
is_scale_swizzled=is_scale_swizzled,
|
| 742 |
+
)
|
| 743 |
+
|
| 744 |
+
|
| 745 |
+
def mxfp4_w4a8_moe_quant_config(
|
| 746 |
+
w1_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 747 |
+
w2_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 748 |
+
a1_scale: torch.Tensor | None = None,
|
| 749 |
+
a2_scale: torch.Tensor | None = None,
|
| 750 |
+
w1_bias: torch.Tensor | None = None,
|
| 751 |
+
w2_bias: torch.Tensor | None = None,
|
| 752 |
+
block_shape: list[int] | None = None,
|
| 753 |
+
gemm1_clamp_limit: float | None = None,
|
| 754 |
+
) -> FusedMoEQuantConfig:
|
| 755 |
+
"""
|
| 756 |
+
Construct a quant config for fp8 activations and mxfp4 weights.
|
| 757 |
+
"""
|
| 758 |
+
return FusedMoEQuantConfig(
|
| 759 |
+
_a1=FusedMoEQuantDesc("fp8", None, a1_scale, None, None, None),
|
| 760 |
+
_a2=FusedMoEQuantDesc("fp8", None, a2_scale, None, None, None),
|
| 761 |
+
_w1=FusedMoEQuantDesc("mxfp4", None, w1_scale, None, None, w1_bias),
|
| 762 |
+
_w2=FusedMoEQuantDesc("mxfp4", None, w2_scale, None, None, w2_bias),
|
| 763 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 764 |
+
)
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
def ocp_mx_moe_quant_config(
|
| 768 |
+
quant_dtype: str,
|
| 769 |
+
w1_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 770 |
+
w2_scale: Union[torch.Tensor, "PrecisionConfig"],
|
| 771 |
+
weight_dtype: str | None = None,
|
| 772 |
+
a1_scale: torch.Tensor | None = None,
|
| 773 |
+
a2_scale: torch.Tensor | None = None,
|
| 774 |
+
w1_bias: torch.Tensor | None = None,
|
| 775 |
+
w2_bias: torch.Tensor | None = None,
|
| 776 |
+
block_shape: list[int] | None = None,
|
| 777 |
+
gemm1_alpha: float | None = None,
|
| 778 |
+
gemm1_beta: float | None = None,
|
| 779 |
+
gemm1_clamp_limit: float | None = None,
|
| 780 |
+
) -> FusedMoEQuantConfig:
|
| 781 |
+
"""
|
| 782 |
+
Construct a quant config for mxfp4 activations and mxfp4 weights.
|
| 783 |
+
"""
|
| 784 |
+
assert quant_dtype in OCP_MX_DTYPES
|
| 785 |
+
return FusedMoEQuantConfig.make(
|
| 786 |
+
quant_dtype=quant_dtype,
|
| 787 |
+
weight_dtype=weight_dtype,
|
| 788 |
+
w1_scale=w1_scale,
|
| 789 |
+
w2_scale=w2_scale,
|
| 790 |
+
a1_scale=a1_scale,
|
| 791 |
+
a2_scale=a2_scale,
|
| 792 |
+
w1_bias=w1_bias,
|
| 793 |
+
w2_bias=w2_bias,
|
| 794 |
+
per_act_token_quant=False,
|
| 795 |
+
per_out_ch_quant=False,
|
| 796 |
+
block_shape=block_shape,
|
| 797 |
+
gemm1_alpha=gemm1_alpha,
|
| 798 |
+
gemm1_beta=gemm1_beta,
|
| 799 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 800 |
+
)
|
| 801 |
+
|
| 802 |
+
|
| 803 |
+
def nvfp4_moe_quant_config(
|
| 804 |
+
g1_alphas: torch.Tensor,
|
| 805 |
+
g2_alphas: torch.Tensor,
|
| 806 |
+
a1_gscale: torch.Tensor,
|
| 807 |
+
a2_gscale: torch.Tensor,
|
| 808 |
+
w1_scale: torch.Tensor,
|
| 809 |
+
w2_scale: torch.Tensor,
|
| 810 |
+
w1_bias: torch.Tensor | None = None,
|
| 811 |
+
w2_bias: torch.Tensor | None = None,
|
| 812 |
+
is_scale_swizzled: bool = True,
|
| 813 |
+
gemm1_clamp_limit: float | None = None,
|
| 814 |
+
) -> FusedMoEQuantConfig:
|
| 815 |
+
"""
|
| 816 |
+
Construct a quant config for mxfp4 activations and nvp4 weights.
|
| 817 |
+
"""
|
| 818 |
+
return FusedMoEQuantConfig.make(
|
| 819 |
+
"nvfp4",
|
| 820 |
+
w1_scale=w1_scale,
|
| 821 |
+
w2_scale=w2_scale,
|
| 822 |
+
w1_bias=w1_bias,
|
| 823 |
+
w2_bias=w2_bias,
|
| 824 |
+
a1_gscale=a1_gscale,
|
| 825 |
+
a2_gscale=a2_gscale,
|
| 826 |
+
g1_alphas=g1_alphas,
|
| 827 |
+
g2_alphas=g2_alphas,
|
| 828 |
+
per_act_token_quant=False,
|
| 829 |
+
per_out_ch_quant=False,
|
| 830 |
+
block_shape=None,
|
| 831 |
+
is_scale_swizzled=is_scale_swizzled,
|
| 832 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 833 |
+
)
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
def mxfp4_moe_quant_config(
|
| 837 |
+
w1_scale: torch.Tensor,
|
| 838 |
+
w2_scale: torch.Tensor,
|
| 839 |
+
) -> FusedMoEQuantConfig:
|
| 840 |
+
"""
|
| 841 |
+
Construct a quant config for MXFP4 x MXFP4 MoE.
|
| 842 |
+
MXFP4 uses block scaling only (E8M0 scales, 32-element groups), with no
|
| 843 |
+
separate alphas / global activation scales in this config.
|
| 844 |
+
"""
|
| 845 |
+
return FusedMoEQuantConfig.make(
|
| 846 |
+
"mxfp4",
|
| 847 |
+
w1_scale=w1_scale,
|
| 848 |
+
w2_scale=w2_scale,
|
| 849 |
+
per_act_token_quant=False,
|
| 850 |
+
per_out_ch_quant=False,
|
| 851 |
+
block_shape=None,
|
| 852 |
+
)
|
| 853 |
+
|
| 854 |
+
|
| 855 |
+
def nvfp4_w4a16_moe_quant_config(
|
| 856 |
+
g1_alphas: torch.Tensor,
|
| 857 |
+
g2_alphas: torch.Tensor,
|
| 858 |
+
w1_scale: torch.Tensor,
|
| 859 |
+
w2_scale: torch.Tensor,
|
| 860 |
+
) -> FusedMoEQuantConfig:
|
| 861 |
+
"""
|
| 862 |
+
Construct a quant config for 16-but activations and nvp4 weights.
|
| 863 |
+
"""
|
| 864 |
+
return FusedMoEQuantConfig.make(
|
| 865 |
+
quant_dtype=None,
|
| 866 |
+
w1_scale=w1_scale,
|
| 867 |
+
w2_scale=w2_scale,
|
| 868 |
+
g1_alphas=g1_alphas,
|
| 869 |
+
g2_alphas=g2_alphas,
|
| 870 |
+
weight_dtype="nvfp4",
|
| 871 |
+
)
|
| 872 |
+
|
| 873 |
+
|
| 874 |
+
def int4_w4a16_moe_quant_config(
|
| 875 |
+
w1_scale: torch.Tensor,
|
| 876 |
+
w2_scale: torch.Tensor,
|
| 877 |
+
w1_zp: torch.Tensor | None = None,
|
| 878 |
+
w2_zp: torch.Tensor | None = None,
|
| 879 |
+
w1_bias: torch.Tensor | None = None,
|
| 880 |
+
w2_bias: torch.Tensor | None = None,
|
| 881 |
+
block_shape: list[int] | None = None,
|
| 882 |
+
a1_gscale: torch.Tensor | None = None,
|
| 883 |
+
a2_gscale: torch.Tensor | None = None,
|
| 884 |
+
gemm1_alpha: float | None = None,
|
| 885 |
+
gemm1_beta: float | None = None,
|
| 886 |
+
gemm1_clamp_limit: float | None = None,
|
| 887 |
+
) -> FusedMoEQuantConfig:
|
| 888 |
+
"""
|
| 889 |
+
Construct a quant config for 16-bit float activations and int4 weights.
|
| 890 |
+
"""
|
| 891 |
+
group_shape = GroupShape(*block_shape) if block_shape is not None else None
|
| 892 |
+
return FusedMoEQuantConfig(
|
| 893 |
+
_a1=FusedMoEQuantDesc(shape=group_shape, alpha_or_gscale=a1_gscale),
|
| 894 |
+
_a2=FusedMoEQuantDesc(shape=group_shape, alpha_or_gscale=a2_gscale),
|
| 895 |
+
_w1=FusedMoEQuantDesc("int4", group_shape, w1_scale, None, w1_zp, w1_bias),
|
| 896 |
+
_w2=FusedMoEQuantDesc("int4", group_shape, w2_scale, None, w2_zp, w2_bias),
|
| 897 |
+
gemm1_alpha=gemm1_alpha,
|
| 898 |
+
gemm1_beta=gemm1_beta,
|
| 899 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 900 |
+
)
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
def fp8_w8a16_moe_quant_config(
|
| 904 |
+
w1_scale: torch.Tensor,
|
| 905 |
+
w2_scale: torch.Tensor,
|
| 906 |
+
w1_bias: torch.Tensor | None = None,
|
| 907 |
+
w2_bias: torch.Tensor | None = None,
|
| 908 |
+
block_shape: list[int] | None = None,
|
| 909 |
+
gemm1_alpha: float | None = None,
|
| 910 |
+
gemm1_beta: float | None = None,
|
| 911 |
+
gemm1_clamp_limit: float | None = None,
|
| 912 |
+
) -> FusedMoEQuantConfig:
|
| 913 |
+
"""
|
| 914 |
+
Construct a quant config for 16-bit float activations and fp8 weights.
|
| 915 |
+
"""
|
| 916 |
+
group_shape = GroupShape(*block_shape) if block_shape is not None else None
|
| 917 |
+
fp8_dtype = current_platform.fp8_dtype()
|
| 918 |
+
return FusedMoEQuantConfig(
|
| 919 |
+
_a1=FusedMoEQuantDesc(),
|
| 920 |
+
_a2=FusedMoEQuantDesc(),
|
| 921 |
+
_w1=FusedMoEQuantDesc(
|
| 922 |
+
fp8_dtype,
|
| 923 |
+
group_shape,
|
| 924 |
+
w1_scale,
|
| 925 |
+
None,
|
| 926 |
+
None,
|
| 927 |
+
w1_bias,
|
| 928 |
+
),
|
| 929 |
+
_w2=FusedMoEQuantDesc(
|
| 930 |
+
fp8_dtype,
|
| 931 |
+
group_shape,
|
| 932 |
+
w2_scale,
|
| 933 |
+
None,
|
| 934 |
+
None,
|
| 935 |
+
w2_bias,
|
| 936 |
+
),
|
| 937 |
+
gemm1_alpha=gemm1_alpha,
|
| 938 |
+
gemm1_beta=gemm1_beta,
|
| 939 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 940 |
+
)
|
| 941 |
+
|
| 942 |
+
|
| 943 |
+
def int8_w8a16_moe_quant_config(
|
| 944 |
+
w1_scale: torch.Tensor,
|
| 945 |
+
w2_scale: torch.Tensor,
|
| 946 |
+
w1_zp: torch.Tensor | None = None,
|
| 947 |
+
w2_zp: torch.Tensor | None = None,
|
| 948 |
+
w1_bias: torch.Tensor | None = None,
|
| 949 |
+
w2_bias: torch.Tensor | None = None,
|
| 950 |
+
block_shape: list[int] | None = None,
|
| 951 |
+
a1_gscale: torch.Tensor | None = None,
|
| 952 |
+
a2_gscale: torch.Tensor | None = None,
|
| 953 |
+
gemm1_alpha: float | None = None,
|
| 954 |
+
gemm1_beta: float | None = None,
|
| 955 |
+
gemm1_clamp_limit: float | None = None,
|
| 956 |
+
) -> FusedMoEQuantConfig:
|
| 957 |
+
"""
|
| 958 |
+
Construct a quant config for 16-bit float activations and int8 weights.
|
| 959 |
+
"""
|
| 960 |
+
group_shape = GroupShape(*block_shape) if block_shape is not None else None
|
| 961 |
+
return FusedMoEQuantConfig(
|
| 962 |
+
_a1=FusedMoEQuantDesc(shape=group_shape, alpha_or_gscale=a1_gscale),
|
| 963 |
+
_a2=FusedMoEQuantDesc(shape=group_shape, alpha_or_gscale=a2_gscale),
|
| 964 |
+
_w1=FusedMoEQuantDesc(torch.int8, group_shape, w1_scale, None, w1_zp, w1_bias),
|
| 965 |
+
_w2=FusedMoEQuantDesc(torch.int8, group_shape, w2_scale, None, w2_zp, w2_bias),
|
| 966 |
+
gemm1_alpha=gemm1_alpha,
|
| 967 |
+
gemm1_beta=gemm1_beta,
|
| 968 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 969 |
+
)
|
| 970 |
+
|
| 971 |
+
|
| 972 |
+
def int4_w4afp8_moe_quant_config(
|
| 973 |
+
w1_scale: torch.Tensor,
|
| 974 |
+
w2_scale: torch.Tensor,
|
| 975 |
+
g1_alphas: torch.Tensor,
|
| 976 |
+
g2_alphas: torch.Tensor,
|
| 977 |
+
per_act_token_quant: bool = False,
|
| 978 |
+
per_out_ch_quant: bool = False,
|
| 979 |
+
block_shape: list[int] | None = None,
|
| 980 |
+
) -> FusedMoEQuantConfig:
|
| 981 |
+
"""
|
| 982 |
+
Construct a quant config for fp8 activations and int4 weights.
|
| 983 |
+
"""
|
| 984 |
+
return FusedMoEQuantConfig.make(
|
| 985 |
+
torch.float8_e4m3fn, # quant dtype for activations
|
| 986 |
+
w1_scale=w1_scale,
|
| 987 |
+
w2_scale=w2_scale,
|
| 988 |
+
g1_alphas=g1_alphas,
|
| 989 |
+
g2_alphas=g2_alphas,
|
| 990 |
+
per_act_token_quant=per_act_token_quant,
|
| 991 |
+
per_out_ch_quant=per_out_ch_quant,
|
| 992 |
+
block_shape=block_shape,
|
| 993 |
+
weight_dtype="int4", # weight dtype for weights
|
| 994 |
+
)
|
| 995 |
+
|
| 996 |
+
|
| 997 |
+
def biased_moe_quant_config(
|
| 998 |
+
w1_bias: torch.Tensor | None,
|
| 999 |
+
w2_bias: torch.Tensor | None,
|
| 1000 |
+
gemm1_alpha: float | None = None,
|
| 1001 |
+
gemm1_beta: float | None = None,
|
| 1002 |
+
gemm1_clamp_limit: float | None = None,
|
| 1003 |
+
) -> FusedMoEQuantConfig:
|
| 1004 |
+
"""
|
| 1005 |
+
Construct a quant config for unquantized activations with biases.
|
| 1006 |
+
|
| 1007 |
+
gemm1_alpha/gemm1_beta/gemm1_clamp_limit carry the SwiGLU gate params
|
| 1008 |
+
through to the fused activation kernel (e.g. swigluoai_uninterleave).
|
| 1009 |
+
"""
|
| 1010 |
+
return FusedMoEQuantConfig(
|
| 1011 |
+
_a1=FusedMoEQuantDesc(),
|
| 1012 |
+
_a2=FusedMoEQuantDesc(),
|
| 1013 |
+
_w1=FusedMoEQuantDesc(bias=w1_bias),
|
| 1014 |
+
_w2=FusedMoEQuantDesc(bias=w2_bias),
|
| 1015 |
+
gemm1_alpha=gemm1_alpha,
|
| 1016 |
+
gemm1_beta=gemm1_beta,
|
| 1017 |
+
gemm1_clamp_limit=gemm1_clamp_limit,
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
|
| 1021 |
+
# A FusedMoEQuantConfig constant for an unquantized MoE op.
|
| 1022 |
+
FUSED_MOE_UNQUANTIZED_CONFIG: FusedMoEQuantConfig = FusedMoEQuantConfig.make()
|
| 1023 |
+
|
| 1024 |
+
|
| 1025 |
+
@dataclass
|
| 1026 |
+
class FusedMoEParallelConfig:
|
| 1027 |
+
tp_size: int
|
| 1028 |
+
pcp_size: int
|
| 1029 |
+
dp_size: int
|
| 1030 |
+
ep_size: int
|
| 1031 |
+
tp_rank: int
|
| 1032 |
+
pcp_rank: int
|
| 1033 |
+
dp_rank: int
|
| 1034 |
+
ep_rank: int
|
| 1035 |
+
sp_size: int
|
| 1036 |
+
|
| 1037 |
+
use_ep: bool # whether to use EP or not
|
| 1038 |
+
all2all_backend: str # all2all backend for MoE communication
|
| 1039 |
+
enable_eplb: bool # whether to enable expert load balancing
|
| 1040 |
+
|
| 1041 |
+
@property
|
| 1042 |
+
def is_sequence_parallel(self) -> bool:
|
| 1043 |
+
return self.sp_size > 1
|
| 1044 |
+
|
| 1045 |
+
@property
|
| 1046 |
+
def use_all2all_kernels(self):
|
| 1047 |
+
return self.dp_size > 1 and self.use_ep
|
| 1048 |
+
|
| 1049 |
+
@property
|
| 1050 |
+
def use_deepep_ht_kernels(self):
|
| 1051 |
+
return (
|
| 1052 |
+
self.use_all2all_kernels
|
| 1053 |
+
and self.all2all_backend == "deepep_high_throughput"
|
| 1054 |
+
)
|
| 1055 |
+
|
| 1056 |
+
@property
|
| 1057 |
+
def use_deepep_ll_kernels(self):
|
| 1058 |
+
return self.use_all2all_kernels and self.all2all_backend == "deepep_low_latency"
|
| 1059 |
+
|
| 1060 |
+
@property
|
| 1061 |
+
def use_fi_nvl_two_sided_kernels(self):
|
| 1062 |
+
return self.use_all2all_kernels and (
|
| 1063 |
+
self.all2all_backend == "flashinfer_all2allv"
|
| 1064 |
+
or self.all2all_backend == "flashinfer_nvlink_two_sided"
|
| 1065 |
+
)
|
| 1066 |
+
|
| 1067 |
+
@property
|
| 1068 |
+
def use_fi_nvl_one_sided_kernels(self):
|
| 1069 |
+
return (
|
| 1070 |
+
self.use_all2all_kernels
|
| 1071 |
+
and self.all2all_backend == "flashinfer_nvlink_one_sided"
|
| 1072 |
+
)
|
| 1073 |
+
|
| 1074 |
+
@property
|
| 1075 |
+
def use_batched_activation_format(self):
|
| 1076 |
+
return self.use_deepep_ll_kernels or self.use_nixl_ep_kernels
|
| 1077 |
+
|
| 1078 |
+
@property
|
| 1079 |
+
def needs_round_robin_routing_tables(self):
|
| 1080 |
+
return self.use_deepep_ll_kernels or self.use_nixl_ep_kernels
|
| 1081 |
+
|
| 1082 |
+
@property
|
| 1083 |
+
def use_ag_rs_all2all_kernels(self):
|
| 1084 |
+
return (
|
| 1085 |
+
self.use_all2all_kernels
|
| 1086 |
+
and self.all2all_backend == "allgather_reducescatter"
|
| 1087 |
+
)
|
| 1088 |
+
|
| 1089 |
+
@property
|
| 1090 |
+
def use_mori_kernels(self):
|
| 1091 |
+
return self.use_all2all_kernels and self.all2all_backend in (
|
| 1092 |
+
"mori_high_throughput",
|
| 1093 |
+
"mori_low_latency",
|
| 1094 |
+
)
|
| 1095 |
+
|
| 1096 |
+
@property
|
| 1097 |
+
def use_nixl_ep_kernels(self):
|
| 1098 |
+
return self.use_all2all_kernels and self.all2all_backend == "nixl_ep"
|
| 1099 |
+
|
| 1100 |
+
@property
|
| 1101 |
+
def use_deepep_v2_kernels(self):
|
| 1102 |
+
return self.use_all2all_kernels and self.all2all_backend == "deepep_v2"
|
| 1103 |
+
|
| 1104 |
+
@staticmethod
|
| 1105 |
+
def flatten_tp_across_dp_and_pcp(
|
| 1106 |
+
tp_size: int, dp_size: int, dp_rank: int, pcp_size: int, pcp_rank: int
|
| 1107 |
+
) -> tuple[int, int]:
|
| 1108 |
+
tp_rank = 0 if tp_size == 1 else get_tensor_model_parallel_rank()
|
| 1109 |
+
# There are actually dp_size * pcp_size * tp_size devices.
|
| 1110 |
+
# Update tp_size and tp_rank so we shard across all devices.
|
| 1111 |
+
flatten_tp_size = dp_size * pcp_size * tp_size
|
| 1112 |
+
flatten_tp_rank = dp_rank * pcp_size * tp_size + pcp_rank * tp_size + tp_rank
|
| 1113 |
+
return flatten_tp_size, flatten_tp_rank
|
| 1114 |
+
|
| 1115 |
+
@staticmethod
|
| 1116 |
+
def make(
|
| 1117 |
+
tp_size_: int,
|
| 1118 |
+
pcp_size_: int,
|
| 1119 |
+
dp_size_: int,
|
| 1120 |
+
sp_size_: int,
|
| 1121 |
+
vllm_parallel_config: ParallelConfig,
|
| 1122 |
+
) -> "FusedMoEParallelConfig":
|
| 1123 |
+
"""
|
| 1124 |
+
Determine MoE parallel configuration. Based on the input `tp_size_`,
|
| 1125 |
+
`dp_size_` and vllm's parallel config, determine what
|
| 1126 |
+
level's of parallelism to use in the fused moe layer.
|
| 1127 |
+
|
| 1128 |
+
Args:
|
| 1129 |
+
tp_size_ (int): `tp_size` passed into the FusedMoE constructor.
|
| 1130 |
+
pcp_size_ (int): `pcp_size` passed into the FusedMoE constructor.
|
| 1131 |
+
dp_size_ (int): `dp_size` passed into the FusedMoE constructor.
|
| 1132 |
+
vllm_parallel_config (ParallelConfig): vLLM's parallel config
|
| 1133 |
+
object which contains the `enable_expert_parallel` flag.
|
| 1134 |
+
|
| 1135 |
+
Examples:
|
| 1136 |
+
When there is no parallelism requested,
|
| 1137 |
+
i.e. `tp_size_` = `pcp_size_` = `dp_size_` = 1, we simply return the sizes
|
| 1138 |
+
unaltered and the ranks set to 0.
|
| 1139 |
+
|
| 1140 |
+
Expert Parallelism is considered only when either `dp_size_`, `pcp_size_` or
|
| 1141 |
+
`tp_size_` is non trivial.
|
| 1142 |
+
|
| 1143 |
+
Note that PCP serves the same function as DP here.
|
| 1144 |
+
|
| 1145 |
+
When TP = 2, DP(PCP) = 1 and EP = False, the configuration on different
|
| 1146 |
+
devices:
|
| 1147 |
+
|
| 1148 |
+
- device 0 : TP = {2, 0} DP = {1, 0} EP = {1, 0} //
|
| 1149 |
+
legend : {size, rank}
|
| 1150 |
+
- device 1 : TP = {2, 1} DP = {1, 0} EP = {1, 0}
|
| 1151 |
+
- Comment : Tensors are sharded across 2 devices.
|
| 1152 |
+
|
| 1153 |
+
When TP = 1, DP(PCP) = 2 and EP = False, the configuration on different
|
| 1154 |
+
devices:
|
| 1155 |
+
|
| 1156 |
+
- device 0 : TP = {2, 0} DP = {2, 0} EP = {1, 0}
|
| 1157 |
+
- device 1 : TP = {2, 1} DP = {2, 1} EP = {1, 0}
|
| 1158 |
+
- Comment: There are 2 engine instances and the tensors are sharded
|
| 1159 |
+
across 2 decvices.
|
| 1160 |
+
|
| 1161 |
+
When TP = 2, DP(PCP) = 2 and EP = False, the configuration on different
|
| 1162 |
+
devices:
|
| 1163 |
+
|
| 1164 |
+
- device 0: TP = {4, 0} DP = {2, 0} EP = {1, 0}
|
| 1165 |
+
- device 1: TP = {4, 1} DP = {2, 0} EP = {1, 0}
|
| 1166 |
+
- device 2: TP = {4, 2} DP = {2, 1} EP = {1, 0}
|
| 1167 |
+
- device 3: TP = {4, 3} DP = {2, 1} EP = {1, 0}
|
| 1168 |
+
- Comment: There are 2 engine instances and the tensors are sharded
|
| 1169 |
+
across 4 devices.
|
| 1170 |
+
|
| 1171 |
+
When, TP = 2, DP(PCP) = 1 and EP = True, the configuration on different
|
| 1172 |
+
devices:
|
| 1173 |
+
|
| 1174 |
+
- device 0: TP = {1, 0} DP = {1, 0} EP = {2, 0}
|
| 1175 |
+
- device 1: TP = {1, 0} DP = {1, 0} EP = {2, 1}
|
| 1176 |
+
- Comment: The experts are split between the 2 devices.
|
| 1177 |
+
|
| 1178 |
+
When, TP = 1, DP(PCP) = 2 and EP = True, the configuration on different
|
| 1179 |
+
devices:
|
| 1180 |
+
|
| 1181 |
+
- device 0: TP = {1, 0} DP = {2, 0} EP = {2, 0}
|
| 1182 |
+
- device 1: TP = {1, 0} DP = {2, 1} EP = {2, 1}
|
| 1183 |
+
- Comment: There are 2 engine instances and the experts are split
|
| 1184 |
+
between the 2 devices.
|
| 1185 |
+
|
| 1186 |
+
When TP = 2, DP(PCP) = 2 and EP = True, the configuration on different
|
| 1187 |
+
devices:
|
| 1188 |
+
|
| 1189 |
+
- device 0: TP = {1, 0} DP = {2, 0} EP = {4, 0}
|
| 1190 |
+
- device 1: TP = {1, 0} DP = {2, 0} EP = {4, 1}
|
| 1191 |
+
- device 2: TP = {1, 0} DP = {2, 1} EP = {4, 2}
|
| 1192 |
+
- device 3: TP = {1, 0} DP = {2, 1} EP = {4, 3}
|
| 1193 |
+
- Comment: There are 2 engine instances and the experts are split
|
| 1194 |
+
between the 4 devices.
|
| 1195 |
+
"""
|
| 1196 |
+
use_ep = (
|
| 1197 |
+
dp_size_ * pcp_size_ * tp_size_ > 1
|
| 1198 |
+
and vllm_parallel_config.enable_expert_parallel
|
| 1199 |
+
)
|
| 1200 |
+
|
| 1201 |
+
dp_size = dp_size_
|
| 1202 |
+
dp_rank = get_dp_group().rank_in_group if dp_size > 1 else 0
|
| 1203 |
+
pcp_size = pcp_size_
|
| 1204 |
+
pcp_rank = get_pcp_group().rank_in_group if pcp_size > 1 else 0
|
| 1205 |
+
tp_size, tp_rank = FusedMoEParallelConfig.flatten_tp_across_dp_and_pcp(
|
| 1206 |
+
tp_size_, dp_size_, dp_rank, pcp_size_, pcp_rank
|
| 1207 |
+
)
|
| 1208 |
+
|
| 1209 |
+
if not use_ep:
|
| 1210 |
+
return FusedMoEParallelConfig(
|
| 1211 |
+
tp_size=tp_size,
|
| 1212 |
+
tp_rank=tp_rank,
|
| 1213 |
+
pcp_size=pcp_size,
|
| 1214 |
+
pcp_rank=pcp_rank,
|
| 1215 |
+
dp_size=dp_size,
|
| 1216 |
+
dp_rank=dp_rank,
|
| 1217 |
+
ep_size=1,
|
| 1218 |
+
ep_rank=0,
|
| 1219 |
+
sp_size=sp_size_,
|
| 1220 |
+
use_ep=False,
|
| 1221 |
+
all2all_backend=vllm_parallel_config.all2all_backend,
|
| 1222 |
+
enable_eplb=vllm_parallel_config.enable_eplb,
|
| 1223 |
+
)
|
| 1224 |
+
# DP + EP / TP + EP / DP + TP + EP
|
| 1225 |
+
assert use_ep
|
| 1226 |
+
# In EP, each device owns a set of experts fully. There is no tensor
|
| 1227 |
+
# parallel update tp_size, tp_rank, ep_size and ep_rank to reflect that.
|
| 1228 |
+
ep_size = tp_size
|
| 1229 |
+
ep_rank = tp_rank
|
| 1230 |
+
return FusedMoEParallelConfig(
|
| 1231 |
+
tp_size=1,
|
| 1232 |
+
tp_rank=0,
|
| 1233 |
+
pcp_size=pcp_size,
|
| 1234 |
+
pcp_rank=pcp_rank,
|
| 1235 |
+
dp_size=dp_size,
|
| 1236 |
+
dp_rank=dp_rank,
|
| 1237 |
+
ep_size=ep_size,
|
| 1238 |
+
ep_rank=ep_rank,
|
| 1239 |
+
sp_size=sp_size_,
|
| 1240 |
+
use_ep=True,
|
| 1241 |
+
all2all_backend=vllm_parallel_config.all2all_backend,
|
| 1242 |
+
enable_eplb=vllm_parallel_config.enable_eplb,
|
| 1243 |
+
)
|
| 1244 |
+
|
| 1245 |
+
@classmethod
|
| 1246 |
+
def make_no_parallel(cls) -> "FusedMoEParallelConfig":
|
| 1247 |
+
"""For usage in CI/CD and testing."""
|
| 1248 |
+
return FusedMoEParallelConfig(
|
| 1249 |
+
tp_size=1,
|
| 1250 |
+
tp_rank=0,
|
| 1251 |
+
pcp_size=1,
|
| 1252 |
+
pcp_rank=0,
|
| 1253 |
+
dp_size=1,
|
| 1254 |
+
dp_rank=0,
|
| 1255 |
+
ep_size=1,
|
| 1256 |
+
ep_rank=0,
|
| 1257 |
+
sp_size=1,
|
| 1258 |
+
use_ep=False,
|
| 1259 |
+
all2all_backend="allgather_reducescatter",
|
| 1260 |
+
enable_eplb=False,
|
| 1261 |
+
)
|
| 1262 |
+
|
| 1263 |
+
|
| 1264 |
+
# Adapted from pplx-kernels tests/all_to_all_utils.py
|
| 1265 |
+
@dataclass
|
| 1266 |
+
class FusedMoEConfig:
|
| 1267 |
+
num_experts: int
|
| 1268 |
+
experts_per_token: int
|
| 1269 |
+
hidden_dim: int
|
| 1270 |
+
intermediate_size: int
|
| 1271 |
+
num_local_experts: int
|
| 1272 |
+
num_logical_experts: int
|
| 1273 |
+
activation: MoEActivation
|
| 1274 |
+
device: torch.device | str
|
| 1275 |
+
routing_method: RoutingMethodType
|
| 1276 |
+
moe_parallel_config: FusedMoEParallelConfig
|
| 1277 |
+
|
| 1278 |
+
# The activation type.
|
| 1279 |
+
in_dtype: torch.dtype
|
| 1280 |
+
|
| 1281 |
+
# Defaults to in_dtype if not specified.
|
| 1282 |
+
router_logits_dtype: torch.dtype | None = None
|
| 1283 |
+
|
| 1284 |
+
# Defaults to hidden_dim if not specified.
|
| 1285 |
+
hidden_dim_unpadded: int | None = None
|
| 1286 |
+
# Defaults to intermediate_size_per_partition if not specified.
|
| 1287 |
+
intermediate_size_per_partition_unpadded: int | None = None
|
| 1288 |
+
|
| 1289 |
+
moe_backend: MoEBackend = "auto"
|
| 1290 |
+
max_num_tokens: int = SchedulerConfig.DEFAULT_MAX_NUM_BATCHED_TOKENS_FOR_BATCHED_DP
|
| 1291 |
+
has_bias: bool = False
|
| 1292 |
+
is_lora_enabled: bool = False
|
| 1293 |
+
|
| 1294 |
+
# SwiGLU clamp limit. When set, backends that do not implement the clamp
|
| 1295 |
+
# are filtered out by `FusedMoEExperts.is_supported_config` so the oracle
|
| 1296 |
+
# cannot silently select one and drop the clamp.
|
| 1297 |
+
swiglu_limit: float | None = None
|
| 1298 |
+
swiglu_alpha: float | None = None
|
| 1299 |
+
swiglu_beta: float | None = None
|
| 1300 |
+
|
| 1301 |
+
max_capture_size: int = 0
|
| 1302 |
+
|
| 1303 |
+
# Set by __post_init__
|
| 1304 |
+
intermediate_size_per_partition: int = -1
|
| 1305 |
+
rocm_aiter_fmoe_enabled: bool = False
|
| 1306 |
+
aiter_fmoe_shared_expert_enabled: bool = False
|
| 1307 |
+
|
| 1308 |
+
def __post_init__(self):
|
| 1309 |
+
from vllm._aiter_ops import rocm_aiter_ops
|
| 1310 |
+
|
| 1311 |
+
tp_size = self.moe_parallel_config.tp_size
|
| 1312 |
+
assert self.intermediate_size % tp_size == 0
|
| 1313 |
+
self.intermediate_size_per_partition = self.intermediate_size // tp_size
|
| 1314 |
+
|
| 1315 |
+
if self.dp_size > 1:
|
| 1316 |
+
logger.debug_once(
|
| 1317 |
+
"Using FusedMoEConfig::max_num_tokens=%d", self.max_num_tokens
|
| 1318 |
+
)
|
| 1319 |
+
|
| 1320 |
+
assert self.max_num_tokens > 0
|
| 1321 |
+
|
| 1322 |
+
if self.router_logits_dtype is None:
|
| 1323 |
+
self.router_logits_dtype = self.in_dtype
|
| 1324 |
+
|
| 1325 |
+
if self.hidden_dim_unpadded is None:
|
| 1326 |
+
self.hidden_dim_unpadded = self.hidden_dim
|
| 1327 |
+
if self.intermediate_size_per_partition_unpadded is None:
|
| 1328 |
+
self.intermediate_size_per_partition_unpadded = (
|
| 1329 |
+
self.intermediate_size_per_partition
|
| 1330 |
+
)
|
| 1331 |
+
|
| 1332 |
+
if self.is_act_and_mul:
|
| 1333 |
+
self.rocm_aiter_fmoe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
|
| 1334 |
+
self.aiter_fmoe_shared_expert_enabled = (
|
| 1335 |
+
rocm_aiter_ops.is_fusion_moe_shared_experts_enabled()
|
| 1336 |
+
)
|
| 1337 |
+
|
| 1338 |
+
if self.use_mori_kernels:
|
| 1339 |
+
assert self.rocm_aiter_fmoe_enabled, (
|
| 1340 |
+
"Mori needs to be used with aiter fused_moe for now."
|
| 1341 |
+
)
|
| 1342 |
+
assert not self.aiter_fmoe_shared_expert_enabled, (
|
| 1343 |
+
"Mori does not support fusion shared expert now. "
|
| 1344 |
+
"Turn it off by setting VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS=0"
|
| 1345 |
+
)
|
| 1346 |
+
|
| 1347 |
+
if not self.is_act_and_mul and not (
|
| 1348 |
+
current_platform.is_cuda_alike() or current_platform.is_xpu()
|
| 1349 |
+
):
|
| 1350 |
+
raise NotImplementedError(
|
| 1351 |
+
"is_act_and_mul=False is supported only for CUDA, XPU and ROCm for now"
|
| 1352 |
+
)
|
| 1353 |
+
|
| 1354 |
+
@property
|
| 1355 |
+
def is_act_and_mul(self) -> bool:
|
| 1356 |
+
return self.activation.is_gated
|
| 1357 |
+
|
| 1358 |
+
@property
|
| 1359 |
+
def tp_size(self):
|
| 1360 |
+
return self.moe_parallel_config.tp_size
|
| 1361 |
+
|
| 1362 |
+
@property
|
| 1363 |
+
def dp_size(self):
|
| 1364 |
+
return self.moe_parallel_config.dp_size
|
| 1365 |
+
|
| 1366 |
+
@property
|
| 1367 |
+
def pcp_size(self):
|
| 1368 |
+
return self.moe_parallel_config.pcp_size
|
| 1369 |
+
|
| 1370 |
+
@property
|
| 1371 |
+
def ep_size(self):
|
| 1372 |
+
return self.moe_parallel_config.ep_size
|
| 1373 |
+
|
| 1374 |
+
@property
|
| 1375 |
+
def sp_size(self):
|
| 1376 |
+
return self.moe_parallel_config.sp_size
|
| 1377 |
+
|
| 1378 |
+
@property
|
| 1379 |
+
def is_sequence_parallel(self):
|
| 1380 |
+
return self.moe_parallel_config.is_sequence_parallel
|
| 1381 |
+
|
| 1382 |
+
@property
|
| 1383 |
+
def tp_rank(self):
|
| 1384 |
+
return self.moe_parallel_config.tp_rank
|
| 1385 |
+
|
| 1386 |
+
@property
|
| 1387 |
+
def dp_rank(self):
|
| 1388 |
+
return self.moe_parallel_config.dp_rank
|
| 1389 |
+
|
| 1390 |
+
@property
|
| 1391 |
+
def pcp_rank(self):
|
| 1392 |
+
return self.moe_parallel_config.pcp_rank
|
| 1393 |
+
|
| 1394 |
+
@property
|
| 1395 |
+
def ep_rank(self):
|
| 1396 |
+
return self.moe_parallel_config.ep_rank
|
| 1397 |
+
|
| 1398 |
+
@property
|
| 1399 |
+
def use_ep(self):
|
| 1400 |
+
return self.moe_parallel_config.use_ep
|
| 1401 |
+
|
| 1402 |
+
@property
|
| 1403 |
+
def use_deepep_ht_kernels(self):
|
| 1404 |
+
return self.moe_parallel_config.use_deepep_ht_kernels
|
| 1405 |
+
|
| 1406 |
+
@property
|
| 1407 |
+
def use_deepep_ll_kernels(self):
|
| 1408 |
+
return self.moe_parallel_config.use_deepep_ll_kernels
|
| 1409 |
+
|
| 1410 |
+
@property
|
| 1411 |
+
def use_mori_kernels(self):
|
| 1412 |
+
return self.moe_parallel_config.use_mori_kernels
|
| 1413 |
+
|
| 1414 |
+
@property
|
| 1415 |
+
def use_fi_nvl_two_sided_kernels(self):
|
| 1416 |
+
return self.moe_parallel_config.use_fi_nvl_two_sided_kernels
|
| 1417 |
+
|
| 1418 |
+
@property
|
| 1419 |
+
def use_fi_nvl_one_sided_kernels(self):
|
| 1420 |
+
return self.moe_parallel_config.use_fi_nvl_one_sided_kernels
|
| 1421 |
+
|
| 1422 |
+
@property
|
| 1423 |
+
def use_ag_rs_all2all_kernels(self):
|
| 1424 |
+
return self.moe_parallel_config.use_ag_rs_all2all_kernels
|
| 1425 |
+
|
| 1426 |
+
@property
|
| 1427 |
+
def use_nixl_ep_kernels(self):
|
| 1428 |
+
return self.moe_parallel_config.use_nixl_ep_kernels
|
| 1429 |
+
|
| 1430 |
+
@property
|
| 1431 |
+
def use_deepep_v2_kernels(self):
|
| 1432 |
+
return self.moe_parallel_config.use_deepep_v2_kernels
|
| 1433 |
+
|
| 1434 |
+
@property
|
| 1435 |
+
def needs_round_robin_routing_tables(self):
|
| 1436 |
+
return self.moe_parallel_config.needs_round_robin_routing_tables
|
vllm-patches/PATCHES.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Serving MiniMax-M3-AWQ-int4 on vLLM (A100 / Ampere)
|
| 2 |
+
|
| 3 |
+
This is an **int4 AWQ** quant (group-size 128, routed-experts-only, GPTQ-mse refinement,
|
| 4 |
+
Gemma-norm fold) of MiniMax-M3. It needs **three small vLLM patches** to serve coherently:
|
| 5 |
+
they make the WNA16-Marlin fused-MoE kernel honor M3s clamped SwiGLU and dequantize the
|
| 6 |
+
int4 packing correctly. **Stock vLLM will load the weights but emit incoherent output.**
|
| 7 |
+
|
| 8 |
+
## Requirements
|
| 9 |
+
- Base image: `vllm/vllm-openai:minimax-m3`
|
| 10 |
+
- `--block-size 128` is **mandatory** (M3 MSA sparse-attention index-cache alignment).
|
| 11 |
+
- 8x A100 80GB (TP8) for long context; smaller TP works at reduced context (e.g. 2x TP4 ~ up to 64K).
|
| 12 |
+
|
| 13 |
+
## Patches (mount read-only over the image)
|
| 14 |
+
Inside the container, `V=/usr/local/lib/python3.12/dist-packages/vllm`:
|
| 15 |
+
|
| 16 |
+
| file in this folder | mount target |
|
| 17 |
+
|---|---|
|
| 18 |
+
| `A_wna16_marlin.py` | `$V/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16_marlin.py` |
|
| 19 |
+
| `B_int_wna16.py` | `$V/model_executor/layers/fused_moe/oracle/int_wna16.py` |
|
| 20 |
+
| `C_config.py` | `$V/model_executor/layers/fused_moe/config.py` |
|
| 21 |
+
|
| 22 |
+
## Launch (TP8)
|
| 23 |
+
See `launch_m3_awq.sh`. Core flags: `--tensor-parallel-size 8 --block-size 128
|
| 24 |
+
--enable-expert-parallel --max-model-len 262144 --gpu-memory-utilization 0.95
|
| 25 |
+
--reasoning-parser minimax_m3 --tool-call-parser minimax_m3 --enable-auto-tool-choice
|
| 26 |
+
--trust-remote-code`.
|
| 27 |
+
|
| 28 |
+
## Notes
|
| 29 |
+
- KV dtype = bf16/auto. **fp8-KV is NOT available on A100** for this model: block-128 + fp8
|
| 30 |
+
routes to a Blackwell-only FlashInfer (trtllm-gen) kernel and fails at init.
|
| 31 |
+
- Sampling: `temperature 1.0, top_p 0.95, top_k 40`.
|
| 32 |
+
- The patches are modified Apache-2.0 vLLM source files.
|
vllm-patches/launch_m3_awq.sh
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# M3 AWQ-int4 at TP8 — identical to the proven RTN launch, only the model mount differs.
|
| 3 |
+
set -u
|
| 4 |
+
V=/usr/local/lib/python3.12/dist-packages/vllm
|
| 5 |
+
P=/srv/models/m3-clamp-patch
|
| 6 |
+
MODEL=${1:-/srv/quant/m3-awq-int4-v2}
|
| 7 |
+
docker rm -f vllm-m3-awq 2>/dev/null
|
| 8 |
+
docker run -d --name vllm-m3-awq \
|
| 9 |
+
--init \
|
| 10 |
+
--gpus all \
|
| 11 |
+
--ipc=host --shm-size=16g \
|
| 12 |
+
-p 127.0.0.1:8005:8000 \
|
| 13 |
+
-e HF_HUB_OFFLINE=1 \
|
| 14 |
+
-v $MODEL:/model:ro \
|
| 15 |
+
-v $P/A_wna16_marlin.py:$V/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16_marlin.py:ro \
|
| 16 |
+
-v $P/B_int_wna16.py:$V/model_executor/layers/fused_moe/oracle/int_wna16.py:ro \
|
| 17 |
+
-v $P/C_config.py:$V/model_executor/layers/fused_moe/config.py:ro \
|
| 18 |
+
vllm/vllm-openai:minimax-m3 \
|
| 19 |
+
--model /model --served-model-name m3 \
|
| 20 |
+
--tensor-parallel-size 8 \
|
| 21 |
+
--block-size 128 \
|
| 22 |
+
--enable-expert-parallel \
|
| 23 |
+
--enable-prefix-caching \
|
| 24 |
+
--max-model-len 262144 \
|
| 25 |
+
--max-num-seqs 64 \
|
| 26 |
+
--gpu-memory-utilization 0.95 \
|
| 27 |
+
--safetensors-load-strategy=prefetch \
|
| 28 |
+
--reasoning-parser minimax_m3 --tool-call-parser minimax_m3 --enable-auto-tool-choice \
|
| 29 |
+
--trust-remote-code
|
| 30 |
+
echo "launched: $(docker ps --filter name=vllm-m3-awq --format '{{.Names}} {{.Status}}')"
|