Skip to content

Commit 50529ee

Browse files
authored
Fix gemma4_31b CUDA tests for int4/int6 superscale quant (#20829)
The superscale change (per-256-weight fp16 scale step) added a 'K must be a multiple of 256' constraint and a new 'steps' arg to CudaDp4aPlanarInt6Tensor. Update the two stale gemma4_31b test files that were not migrated: - test_pipeline.py: bump shared TINY_CONFIG hidden_size 128 -> 256 so int4 linear K is divisible by 256 (fixes test_cuda_pipeline.py). - test_pack_cuda.py: use the 6-arg int6 constructor (encode scale codes + per-256 steps) and bump too-small K dims (128/64 -> 256).
1 parent f3daa57 commit 50529ee

2 files changed

Lines changed: 32 additions & 18 deletions

File tree

examples/models/gemma4_31b/quant/tests/test_pack_cuda.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import torch
2020
import torch.nn as nn
2121
from executorch.backends.cuda.dp4a_planar_int6_tensor import (
22+
_encode_int8_per_super,
2223
CudaDp4aPlanarInt6Tensor,
2324
pack_int6,
2425
unpack_int6,
@@ -145,8 +146,18 @@ def _make_int6(self, N, K, gs=16):
145146
q = torch.randint(-32, 32, (N, K), dtype=torch.int8)
146147
scale = (torch.rand(N, K // gs) * 0.1 + 0.01).to(torch.bfloat16)
147148
ql, qh = pack_int6(q)
148-
t = CudaDp4aPlanarInt6Tensor(ql, qh, scale, [1, gs], torch.Size([N, K]))
149-
return t, q, scale
149+
# The tensor stores int8 scale *codes* + a per-256-super-block [N, K/256]
150+
# fp16 step (scale = code * step[:, g // (256 // gs)]); encode here and
151+
# return the effective scale so the reference dequant matches the kernel.
152+
scale_codes, steps = _encode_int8_per_super(scale.float(), gs)
153+
n_super = steps.shape[1]
154+
gps = (K // gs) // n_super
155+
step_g = steps.to(torch.bfloat16).repeat_interleave(gps, dim=1)
156+
eff_scale = (scale_codes.to(torch.bfloat16) * step_g).to(torch.bfloat16)
157+
t = CudaDp4aPlanarInt6Tensor(
158+
ql, qh, scale_codes, steps, [1, gs], torch.Size([N, K])
159+
)
160+
return t, q, eff_scale
150161

151162
def _make_q6k_gguf(self, N, nb=1):
152163
"""Build a synthetic q6_k ExportableGGUFTensor (see test_gguf.py).
@@ -179,7 +190,7 @@ def test_pack_unpack_roundtrip(self):
179190
self.assertTrue(torch.equal(q_rt, q))
180191

181192
def test_dequantize_equals_q_scale(self):
182-
t, q, scale = self._make_int6(32, 128, gs=16)
193+
t, q, scale = self._make_int6(32, 256, gs=16)
183194
ref = q.to(torch.bfloat16) * scale.to(torch.bfloat16).repeat_interleave(
184195
16, dim=-1
185196
)
@@ -348,8 +359,8 @@ def setUp(self):
348359

349360
def test_mixed_precision(self):
350361
torch.manual_seed(0)
351-
w4 = torch.randn(64, 128, dtype=torch.bfloat16)
352-
w8 = torch.randn(64, 128, dtype=torch.bfloat16)
362+
w4 = torch.randn(64, 256, dtype=torch.bfloat16)
363+
w8 = torch.randn(64, 256, dtype=torch.bfloat16)
353364
q4 = quantize_weight(
354365
w4,
355366
QuantConfig(bits=4, group_size=32, symmetric=False, method="min_max"),
@@ -361,15 +372,15 @@ def test_mixed_precision(self):
361372
with torch.device("meta"):
362373
model = nn.ModuleDict(
363374
{
364-
"q_proj": nn.Linear(128, 64, bias=False),
365-
"v_proj": nn.Linear(128, 64, bias=False),
375+
"q_proj": nn.Linear(256, 64, bias=False),
376+
"v_proj": nn.Linear(256, 64, bias=False),
366377
}
367378
)
368379
pack_model(
369380
model, {"q_proj.weight": q4, "v_proj.weight": q8}, DEFAULT_CUDA_PACKERS
370381
)
371382
model.cuda()
372-
x = torch.randn(1, 128, dtype=torch.bfloat16, device="cuda")
383+
x = torch.randn(1, 256, dtype=torch.bfloat16, device="cuda")
373384

374385
ref4 = torch.nn.functional.linear(x, w4.cuda())
375386
out4 = model.q_proj(x)
@@ -389,7 +400,7 @@ def test_mixed_precision(self):
389400

390401
def test_load_and_pack_from_disk(self):
391402
torch.manual_seed(0)
392-
weight = torch.randn(64, 128, dtype=torch.bfloat16)
403+
weight = torch.randn(64, 256, dtype=torch.bfloat16)
393404
config = QuantConfig(bits=4, group_size=32, symmetric=False, method="min_max")
394405
q = quantize_weight(weight, config)
395406

@@ -405,27 +416,27 @@ def test_load_and_pack_from_disk(self):
405416
with torch.device("meta"):
406417
model = nn.ModuleDict(
407418
{
408-
"proj": nn.Linear(128, 64, bias=False),
419+
"proj": nn.Linear(256, 64, bias=False),
409420
"norm": nn.LayerNorm(64, bias=False),
410421
}
411422
)
412423
load_and_pack_for_cuda(path, model)
413424

414-
self.assertEqual(model.proj.weight.shape, torch.Size([64, 128]))
425+
self.assertEqual(model.proj.weight.shape, torch.Size([64, 256]))
415426
self.assertEqual(model.norm.weight.shape, torch.Size([64]))
416427

417428
model.proj.cuda()
418-
x = torch.randn(1, 128, dtype=torch.bfloat16, device="cuda")
429+
x = torch.randn(1, 256, dtype=torch.bfloat16, device="cuda")
419430
ref = torch.nn.functional.linear(x, weight.cuda())
420431
out = model.proj(x)
421432
rel_error = (out.float() - ref.float()).abs().mean() / ref.float().abs().mean()
422433
self.assertLess(rel_error.item(), 0.15)
423434

424435
def test_pack_one_quantized(self):
425436
config = QuantConfig(bits=4, group_size=32, symmetric=False, method="min_max")
426-
q = quantize_weight(torch.randn(64, 128, dtype=torch.bfloat16), config)
437+
q = quantize_weight(torch.randn(64, 256, dtype=torch.bfloat16), config)
427438
with torch.device("meta"):
428-
model = nn.ModuleDict({"proj": nn.Linear(128, 64, bias=False)})
439+
model = nn.ModuleDict({"proj": nn.Linear(256, 64, bias=False)})
429440
pack_one(model, "proj.weight", q, DEFAULT_CUDA_PACKERS)
430441
self.assertNotEqual(model.proj.weight.device.type, "meta")
431442

@@ -463,13 +474,13 @@ def __init__(self):
463474

464475
def test_missing_weight_detected(self):
465476
config = QuantConfig(bits=4, group_size=32, symmetric=False, method="min_max")
466-
q = quantize_weight(torch.randn(32, 64, dtype=torch.bfloat16), config)
477+
q = quantize_weight(torch.randn(32, 256, dtype=torch.bfloat16), config)
467478

468479
with torch.device("meta"):
469480
model = nn.ModuleDict(
470481
{
471-
"a": nn.Linear(64, 32, bias=False),
472-
"b": nn.Linear(64, 32, bias=False),
482+
"a": nn.Linear(256, 32, bias=False),
483+
"b": nn.Linear(256, 32, bias=False),
473484
}
474485
)
475486
with self.assertRaises(RuntimeError) as ctx:

examples/models/gemma4_31b/tests/test_pipeline.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@
4646

4747
TINY_CONFIG = Gemma4_31BConfig(
4848
vocab_size=256,
49-
hidden_size=128,
49+
# hidden_size must be a multiple of 256: CUDA int4/int6 superscale packing
50+
# uses a per-256-weight scale step, so in-features (K) must be divisible by
51+
# 256. Keeps the CUDA pipeline tests on the same shared fixture.
52+
hidden_size=256,
5053
intermediate_size=256,
5154
num_hidden_layers=6,
5255
num_attention_heads=4,

0 commit comments

Comments
 (0)