@@ -1537,3 +1537,84 @@ def test_get_auto_quantize_config_emits_fused_expert_quantizer_names(with_persis
15371537 assert f"{ module_name } .down_proj_weight_quantizer" in quantizer_names
15381538 assert f"{ module_name } .weight_quantizer" not in quantizer_names
15391539
1540+
1541+ def test_mla_projections_share_one_group ():
1542+ """TRT-LLM fuses q_a_proj + kv_a_proj_with_mqa, so they must share one quant format."""
1543+
1544+ class _MLAAttention (torch .nn .Module ):
1545+ def __init__ (self ):
1546+ super ().__init__ ()
1547+ self .q_a_proj = torch .nn .Linear (32 , 32 )
1548+ self .kv_a_proj_with_mqa = torch .nn .Linear (32 , 32 )
1549+ self .o_proj = torch .nn .Linear (32 , 32 )
1550+
1551+ def forward (self , x ):
1552+ return self .o_proj (self .q_a_proj (x ) + self .kv_a_proj_with_mqa (x ))
1553+
1554+ class _MLABlock (torch .nn .Module ):
1555+ def __init__ (self ):
1556+ super ().__init__ ()
1557+ self .self_attn = _MLAAttention ()
1558+
1559+ def forward (self , x ):
1560+ return self .self_attn (x )
1561+
1562+ def get_input (self ):
1563+ return torch .randn (1 , 4 , 32 )
1564+
1565+ model = _MLABlock ()
1566+ mtq .auto_quantize (
1567+ model ,
1568+ constraints = {"effective_bits" : 8.0 },
1569+ quantization_formats = [mtq .INT8_DEFAULT_CFG ],
1570+ data_loader = [model .get_input () for _ in range (2 )],
1571+ forward_step = lambda model , batch : model (batch ),
1572+ loss_func = lambda output , data : output .sum (),
1573+ num_calib_steps = 2 ,
1574+ num_score_steps = 2 ,
1575+ method = "gradient" ,
1576+ )
1577+ hparam = model .self_attn .q_a_proj .get_hparam ("quant_recipe" )
1578+ assert model .self_attn .kv_a_proj_with_mqa .get_hparam ("quant_recipe" ) == hparam
1579+ assert model .self_attn .o_proj .get_hparam ("quant_recipe" ) != hparam
1580+
1581+
1582+ def test_mla_group_does_not_absorb_unfused_q_proj ():
1583+ """With q_lora_rank=None there is no q_a_proj; q_proj is NOT fused with kv_a_proj."""
1584+
1585+ class _MLAAttention (torch .nn .Module ):
1586+ def __init__ (self ):
1587+ super ().__init__ ()
1588+ self .q_proj = torch .nn .Linear (32 , 32 )
1589+ self .kv_a_proj_with_mqa = torch .nn .Linear (32 , 32 )
1590+ self .o_proj = torch .nn .Linear (32 , 32 )
1591+
1592+ def forward (self , x ):
1593+ return self .o_proj (self .q_proj (x ) + self .kv_a_proj_with_mqa (x ))
1594+
1595+ class _MLABlock (torch .nn .Module ):
1596+ def __init__ (self ):
1597+ super ().__init__ ()
1598+ self .self_attn = _MLAAttention ()
1599+
1600+ def forward (self , x ):
1601+ return self .self_attn (x )
1602+
1603+ def get_input (self ):
1604+ return torch .randn (1 , 4 , 32 )
1605+
1606+ model = _MLABlock ()
1607+ mtq .auto_quantize (
1608+ model ,
1609+ constraints = {"effective_bits" : 8.0 },
1610+ quantization_formats = [mtq .INT8_DEFAULT_CFG ],
1611+ data_loader = [model .get_input () for _ in range (2 )],
1612+ forward_step = lambda model , batch : model (batch ),
1613+ loss_func = lambda output , data : output .sum (),
1614+ num_calib_steps = 2 ,
1615+ num_score_steps = 2 ,
1616+ method = "gradient" ,
1617+ )
1618+ q_hparam = model .self_attn .q_proj .get_hparam ("quant_recipe" )
1619+ assert model .self_attn .kv_a_proj_with_mqa .get_hparam ("quant_recipe" ) != q_hparam
1620+ assert model .self_attn .o_proj .get_hparam ("quant_recipe" ) != q_hparam
0 commit comments