import pytest import torch from lmdeploy.pytorch.kernels.cuda.fused_lora import fused_lora class TestFusedLoRA: @pytest.fixture def dtype(self): yield torch.float16 @pytest.fixture def head_size(self): yield 32 @pytest.fixture def out_head_size(self): yield 16 @pytest.fixture def seq_lens(self, request): yield torch.tensor(request.param).cuda() @pytest.fixture def ranks(self): yield torch.tensor([2, 4]).cuda() @pytest.fixture def start_loc(self, seq_lens): yield seq_lens.cumsum(0) - seq_lens @pytest.fixture def input(self, seq_lens, head_size, dtype): total_len = seq_lens.sum() yield torch.rand(total_len, head_size, dtype=dtype).cuda() @pytest.fixture def adapter_ids(self, seq_lens, ranks): num_ranks = len(ranks) num_seqs = len(seq_lens) ret = torch.arange(0, num_seqs) % num_ranks ret = ret.cuda() yield ret @pytest.fixture def scaling(self, ranks): yield torch.arange(ranks.size(0)).cuda() + 1 @pytest.fixture def lora_a(self, ranks, head_size, dtype): out = [] for rank in ranks: w = torch.rand(head_size, rank, dtype=dtype).cuda() out.append(w) yield out @pytest.fixture def lora_b(self, ranks, out_head_size, dtype): out = [] for rank in ranks: w = torch.rand(rank, out_head_size, dtype=dtype).cuda() out.append(w) yield out @pytest.fixture def fused_lora_a(self, lora_a): yield torch.cat(lora_a, dim=1).t().contiguous() @pytest.fixture def fused_lora_b(self, lora_b): yield torch.cat(lora_b, dim=0).contiguous() @pytest.fixture def gt(self, input, start_loc, seq_lens, adapter_ids, lora_a, lora_b, scaling): out = [] for loc, s_len, r_id in zip(start_loc, seq_lens, adapter_ids): inp = input[loc:loc + s_len] l_a = lora_a[r_id] l_b = lora_b[r_id] s = scaling[r_id] out.append(inp @ l_a @ l_b * s) yield torch.cat(out) @pytest.mark.parametrize('seq_lens', [ (2, 4, 6, 8), (1, 1, 1, 1), ], indirect=True) def test_fused_lora(self, input, fused_lora_a, fused_lora_b, start_loc, seq_lens, adapter_ids, scaling, ranks, gt): max_seq_len = max(seq_lens).item() max_rank = max(ranks).item() rank_offset = ranks.cumsum(0) - ranks output = fused_lora( input, fused_lora_a, fused_lora_b, scaling=scaling, rank_start=rank_offset, ranks=ranks, seq_start=start_loc, seq_lens=seq_lens, adapter_ids=adapter_ids, max_rank=max_rank, max_seqlen=max_seq_len, ) torch.testing.assert_close(gt, output)