flash-qla 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. flash_qla/__init__.py +16 -0
  2. flash_qla/ops/__init__.py +7 -0
  3. flash_qla/ops/gated_delta_rule/__init__.py +7 -0
  4. flash_qla/ops/gated_delta_rule/chunk/__init__.py +325 -0
  5. flash_qla/ops/gated_delta_rule/chunk/blackwell/__init__.py +20 -0
  6. flash_qla/ops/gated_delta_rule/chunk/blackwell/cp_bwd.py +693 -0
  7. flash_qla/ops/gated_delta_rule/chunk/blackwell/cp_fwd.py +609 -0
  8. flash_qla/ops/gated_delta_rule/chunk/blackwell/fused_bwd.py +1319 -0
  9. flash_qla/ops/gated_delta_rule/chunk/blackwell/fused_fwd.py +908 -0
  10. flash_qla/ops/gated_delta_rule/chunk/blackwell/kkt_solve.py +323 -0
  11. flash_qla/ops/gated_delta_rule/chunk/blackwell/prepare_h.py +776 -0
  12. flash_qla/ops/gated_delta_rule/chunk/cp_context.py +322 -0
  13. flash_qla/ops/gated_delta_rule/chunk/hopper/__init__.py +20 -0
  14. flash_qla/ops/gated_delta_rule/chunk/hopper/cp_bwd.py +693 -0
  15. flash_qla/ops/gated_delta_rule/chunk/hopper/cp_fwd.py +609 -0
  16. flash_qla/ops/gated_delta_rule/chunk/hopper/fused_bwd.py +1148 -0
  17. flash_qla/ops/gated_delta_rule/chunk/hopper/fused_fwd.py +780 -0
  18. flash_qla/ops/gated_delta_rule/chunk/hopper/kkt_solve.py +323 -0
  19. flash_qla/ops/gated_delta_rule/chunk/hopper/prepare_h.py +675 -0
  20. flash_qla/ops/utils/__init__.py +11 -0
  21. flash_qla/ops/utils/cumsum.py +171 -0
  22. flash_qla/ops/utils/group_reduce.py +79 -0
  23. flash_qla/utils/__init__.py +24 -0
  24. flash_qla/utils/contiguous.py +41 -0
  25. flash_qla/utils/index.py +93 -0
  26. flash_qla/utils/math.py +101 -0
  27. flash_qla/utils/pack.py +83 -0
  28. flash_qla/utils/profiler.py +67 -0
  29. flash_qla-0.1.1.dist-info/METADATA +15 -0
  30. flash_qla-0.1.1.dist-info/RECORD +33 -0
  31. flash_qla-0.1.1.dist-info/WHEEL +5 -0
  32. flash_qla-0.1.1.dist-info/licenses/LICENSE +21 -0
  33. flash_qla-0.1.1.dist-info/top_level.txt +1 -0
flash_qla/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ # Copyright (c) 2026 The Qwen team, Alibaba Group.
2
+ # Licensed under The MIT License [see LICENSE for details]
3
+
4
+ __version__ = "0.1.1"
5
+
6
+ from flash_qla.ops.gated_delta_rule.chunk import (
7
+ chunk_gated_delta_rule_fwd,
8
+ chunk_gated_delta_rule_bwd,
9
+ chunk_gated_delta_rule,
10
+ )
11
+
12
+ __all__ = [
13
+ "chunk_gated_delta_rule_fwd",
14
+ "chunk_gated_delta_rule_bwd",
15
+ "chunk_gated_delta_rule",
16
+ ]
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2026 The Qwen team, Alibaba Group.
2
+ # Licensed under The MIT License [see LICENSE for details]
3
+
4
+ from .gated_delta_rule import chunk_gated_delta_rule
5
+
6
+
7
+ __all__ = ["chunk_gated_delta_rule"]
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2026 The Qwen team, Alibaba Group.
2
+ # Licensed under The MIT License [see LICENSE for details]
3
+
4
+ from .chunk import chunk_gated_delta_rule
5
+
6
+
7
+ __all__ = ["chunk_gated_delta_rule"]
@@ -0,0 +1,325 @@
1
+ # Copyright (c) 2026 The Qwen team, Alibaba Group.
2
+ # Licensed under The MIT License [see LICENSE for details]
3
+
4
+ import torch
5
+ import tilelang
6
+
7
+ from flash_qla.utils import l2norm_fwd, l2norm_bwd, prepare_chunk_offsets
8
+ from flash_qla.ops.utils import chunk_local_cumsum, group_reduce_vector
9
+
10
+ if tilelang.contrib.nvcc.get_target_compute_version() == "9.0":
11
+ from .hopper import fused_gdr_fwd, fused_gdr_bwd, fused_gdr_h, kkt_solve
12
+ from .hopper import get_warmup_chunks, get_warmup_chunks_bidi, correct_initial_states, correct_terminal_states
13
+ from .hopper.cp_bwd import fused_gdr_dh_ws as fused_gdr_dh
14
+ elif tilelang.contrib.nvcc.get_target_compute_version() == "10.0":
15
+ from .blackwell import fused_gdr_fwd, fused_gdr_bwd, fused_gdr_h, kkt_solve
16
+ from .blackwell import get_warmup_chunks, get_warmup_chunks_bidi, correct_initial_states, correct_terminal_states
17
+ from .blackwell.cp_bwd import fused_gdr_dh_ws as fused_gdr_dh
18
+ else:
19
+ raise ValueError("FlashQLA now support sm90 and sm100 only.")
20
+ from .cp_context import intra_card_cp_preprocess, intra_card_cp_preprocess_bwd, _calc_cp_seqs, _create_cu_seqlens
21
+
22
+ from flash_qla.utils import input_guard
23
+
24
+
25
+ def chunk_gated_delta_rule_fwd(
26
+ q: torch.Tensor,
27
+ k: torch.Tensor,
28
+ v: torch.Tensor,
29
+ g: torch.Tensor,
30
+ beta: torch.Tensor,
31
+ scale: float | None = None,
32
+ initial_state: torch.Tensor | None = None,
33
+ cu_seqlens: torch.LongTensor | None = None,
34
+ output_final_state: bool = True,
35
+ output_h: bool = False,
36
+ auto_cp: bool = True,
37
+ state_v_first: bool = False,
38
+ enable_fwd_cp_cache: bool = False,
39
+ ):
40
+ g = chunk_local_cumsum(g, chunk_size=64, cu_seqlens=cu_seqlens)
41
+ A = kkt_solve(
42
+ k=k,
43
+ b=beta,
44
+ cu_seqlens=cu_seqlens,
45
+ )
46
+ cp_cache = None
47
+ if auto_cp:
48
+ if enable_fwd_cp_cache:
49
+ initial_state, cu_seqlens, cp_seq_map, raw_cu_seqlens, cached_mt, cached_fallback_bwd, cached_num_warmup_bwd = (
50
+ intra_card_cp_preprocess(
51
+ k=k, v=v, a=A, g=g, b=beta,
52
+ raw_h0=initial_state,
53
+ raw_cu_seqlens=cu_seqlens,
54
+ state_v_first=state_v_first,
55
+ enable_fwd_cp_cache=True,
56
+ )
57
+ )
58
+ if cached_mt is not None:
59
+ cp_cache = (initial_state, cached_mt, cached_fallback_bwd, cached_num_warmup_bwd)
60
+ else:
61
+ initial_state, cu_seqlens, cp_seq_map, raw_cu_seqlens = (
62
+ intra_card_cp_preprocess(
63
+ k=k, v=v, a=A, g=g, b=beta,
64
+ raw_h0=initial_state,
65
+ raw_cu_seqlens=cu_seqlens,
66
+ state_v_first=state_v_first,
67
+ )
68
+ )
69
+ else:
70
+ cp_seq_map = None
71
+ raw_cu_seqlens = None
72
+ o, h, final_state = fused_gdr_fwd(
73
+ q=q,
74
+ k=k,
75
+ v=v,
76
+ a=A,
77
+ g=g,
78
+ b=beta,
79
+ scale=scale,
80
+ initial_state=initial_state,
81
+ output_final_state=output_final_state,
82
+ output_h=output_h,
83
+ output_o=True,
84
+ cu_seqlens=cu_seqlens,
85
+ cp_seq_map=cp_seq_map,
86
+ raw_cu_seqlens=raw_cu_seqlens,
87
+ state_v_first=state_v_first,
88
+ )
89
+ return g, A, o, h, final_state, cp_cache
90
+
91
+
92
+ def chunk_gated_delta_rule_bwd(
93
+ q: torch.Tensor,
94
+ k: torch.Tensor,
95
+ v: torch.Tensor,
96
+ g: torch.Tensor,
97
+ beta: torch.Tensor,
98
+ A: torch.Tensor,
99
+ do: torch.Tensor,
100
+ dht: torch.Tensor | None = None,
101
+ scale: float | None = None,
102
+ initial_state: torch.Tensor | None = None,
103
+ cu_seqlens: torch.LongTensor | None = None,
104
+ state_v_first: bool = False,
105
+ auto_cp: bool = False,
106
+ force_cp: int = 0,
107
+ cp_cache: tuple | None = None,
108
+ ):
109
+ batch_size, num_tokens, num_k_heads, _ = k.shape
110
+ _, _, H, _ = v.shape
111
+ chunk_size = A.shape[-1]
112
+
113
+ if auto_cp and fused_gdr_dh is not None:
114
+ h_initial_state, h_cu_seqlens, bwd_dht, bwd_cu_seqlens, seq_map_r2c, use_cp = (
115
+ intra_card_cp_preprocess_bwd(
116
+ k=k, v=v, a=A, g=g, b=beta, raw_h0=initial_state,
117
+ q=q, do=do, dht=dht, scale=scale,
118
+ raw_cu_seqlens=cu_seqlens,
119
+ state_v_first=state_v_first,
120
+ force_cp=force_cp,
121
+ cp_cache=cp_cache,
122
+ )
123
+ )
124
+ else:
125
+ h_initial_state = initial_state
126
+ h_cu_seqlens = cu_seqlens
127
+ bwd_dht = dht
128
+ bwd_cu_seqlens = cu_seqlens
129
+ seq_map_r2c = None
130
+ use_cp = False
131
+
132
+ h, _, _ = fused_gdr_h(
133
+ k=k, v=v, a=A, g=g, b=beta,
134
+ initial_state=h_initial_state,
135
+ output_final_state=False,
136
+ output_h=True,
137
+ cu_seqlens=h_cu_seqlens,
138
+ state_v_first=state_v_first,
139
+ )
140
+ dq, dk, dv, dg, db, dh0 = fused_gdr_bwd(
141
+ q=q, k=k, v=v, a=A, g=g, b=beta,
142
+ do=do, dht=bwd_dht, h=h, scale=scale,
143
+ cu_seqlens=bwd_cu_seqlens,
144
+ state_v_first=state_v_first,
145
+ )
146
+
147
+ if use_cp: # TODO store dh0 in fused_bwd kernel
148
+ dh0 = dh0[seq_map_r2c[:-1].long()] if dh0 is not None else None
149
+ elif initial_state is None:
150
+ dh0 = None
151
+
152
+ Hg, H = k.shape[-2], v.shape[-2]
153
+ if Hg < H:
154
+ dq = group_reduce_vector(dq, Hg)
155
+ dk = group_reduce_vector(dk, Hg)
156
+ assert dg.dtype == torch.float32, "dg should be fp32"
157
+ dg = chunk_local_cumsum(dg, chunk_size=64, reverse=True, cu_seqlens=cu_seqlens)
158
+ return dq, dk, dv, db, dg, dh0
159
+
160
+
161
+ class ChunkGatedDeltaRuleFunction(torch.autograd.Function):
162
+ @staticmethod
163
+ @input_guard
164
+ @torch.amp.custom_fwd(device_type="cuda")
165
+ def forward(
166
+ ctx,
167
+ q: torch.Tensor,
168
+ k: torch.Tensor,
169
+ v: torch.Tensor,
170
+ g: torch.Tensor,
171
+ beta: torch.Tensor,
172
+ scale: float | None = None,
173
+ initial_state: torch.Tensor | None = None,
174
+ output_final_state: bool = False,
175
+ cu_seqlens: torch.LongTensor | None = None,
176
+ state_v_first: bool = False,
177
+ auto_cp: bool = False,
178
+ use_qk_l2norm_in_kernel: bool = False,
179
+ enable_fwd_cp_cache: bool = False,
180
+ ):
181
+ q_rstd, k_rstd = None, None
182
+ if use_qk_l2norm_in_kernel:
183
+ q, q_rstd = l2norm_fwd(q)
184
+ k, k_rstd = l2norm_fwd(k)
185
+
186
+ g, A, o, _, final_state, cp_cache = chunk_gated_delta_rule_fwd(
187
+ q=q,
188
+ k=k,
189
+ v=v,
190
+ g=g,
191
+ beta=beta,
192
+ scale=scale,
193
+ initial_state=initial_state,
194
+ output_final_state=output_final_state,
195
+ output_h=False,
196
+ cu_seqlens=cu_seqlens,
197
+ state_v_first=state_v_first,
198
+ auto_cp=auto_cp,
199
+ enable_fwd_cp_cache=enable_fwd_cp_cache,
200
+ )
201
+
202
+ if cp_cache is not None:
203
+ cached_cp_h0, cached_mt, cached_fallback_bwd, cached_num_warmup_bwd = cp_cache
204
+ ctx.save_for_backward(
205
+ q, k, q_rstd, k_rstd, v, g, beta, A, initial_state, cu_seqlens,
206
+ cached_cp_h0, cached_mt, cached_fallback_bwd, cached_num_warmup_bwd,
207
+ )
208
+ ctx._cp_cache_count = 4
209
+ else:
210
+ ctx.save_for_backward(q, k, q_rstd, k_rstd, v, g, beta, A, initial_state, cu_seqlens)
211
+ ctx._cp_cache_count = 0
212
+ ctx.scale = scale
213
+ ctx.state_v_first = state_v_first
214
+ ctx.autocp = auto_cp
215
+ ctx.use_qk_l2norm_in_kernel = use_qk_l2norm_in_kernel
216
+ return o.to(q.dtype), final_state
217
+
218
+ @staticmethod
219
+ @input_guard
220
+ @torch.amp.custom_bwd(device_type="cuda")
221
+ def backward(ctx, do: torch.Tensor, dht: torch.Tensor):
222
+ if ctx._cp_cache_count == 4:
223
+ q, k, q_rstd, k_rstd, v, g, beta, A, initial_state, cu_seqlens, \
224
+ cached_cp_h0, cached_mt, cached_fallback_bwd, cached_num_warmup_bwd = ctx.saved_tensors
225
+ cp_cache = (cached_cp_h0, cached_mt, cached_fallback_bwd, cached_num_warmup_bwd)
226
+ else:
227
+ q, k, q_rstd, k_rstd, v, g, beta, A, initial_state, cu_seqlens = ctx.saved_tensors
228
+ cp_cache = None
229
+
230
+ dq, dk, dv, db, dg, dh0 = chunk_gated_delta_rule_bwd(
231
+ q=q,
232
+ k=k,
233
+ v=v,
234
+ g=g,
235
+ beta=beta,
236
+ A=A,
237
+ do=do,
238
+ dht=dht,
239
+ scale=ctx.scale,
240
+ initial_state=initial_state,
241
+ cu_seqlens=cu_seqlens,
242
+ state_v_first=ctx.state_v_first,
243
+ auto_cp=ctx.autocp,
244
+ cp_cache=cp_cache,
245
+ )
246
+
247
+ if ctx.use_qk_l2norm_in_kernel:
248
+ dq = l2norm_bwd(q, q_rstd, dq)
249
+ dk = l2norm_bwd(k, k_rstd, dk)
250
+
251
+ return (
252
+ dq.to(q),
253
+ dk.to(k),
254
+ dv.to(v),
255
+ dg.to(g),
256
+ db.to(beta),
257
+ None,
258
+ dh0 if initial_state is not None else None,
259
+ None,
260
+ None,
261
+ None,
262
+ None,
263
+ None,
264
+ None,
265
+ )
266
+
267
+
268
+ @torch.compiler.disable
269
+ def chunk_gated_delta_rule(
270
+ q: torch.Tensor,
271
+ k: torch.Tensor,
272
+ v: torch.Tensor,
273
+ g: torch.Tensor,
274
+ beta: torch.Tensor,
275
+ scale: float = None,
276
+ initial_state: torch.Tensor = None,
277
+ output_final_state: bool = False,
278
+ use_qk_l2norm_in_kernel: bool = False,
279
+ cu_seqlens: torch.LongTensor | None = None,
280
+ head_first: bool = False,
281
+ state_v_first: bool = False,
282
+ auto_cp: bool = False,
283
+ enable_fwd_cp_cache: bool = False,
284
+ ):
285
+ assert q.dtype == k.dtype == v.dtype
286
+ assert q.dtype != torch.float32, (
287
+ "ChunkGatedDeltaRuleFunction does not support float32. Please use bfloat16 or float16."
288
+ )
289
+ assert not head_first, "head_first=True is not supported."
290
+ assert v.shape[2] % k.shape[2] == 0, (
291
+ "num_qk_heads must be divisible to num_v_heads."
292
+ )
293
+
294
+ if cu_seqlens is not None:
295
+ if q.shape[0] != 1:
296
+ raise ValueError(
297
+ f"The batch size is expected to be 1 rather than {q.shape[0]} when using `cu_seqlens`."
298
+ f"Please flatten variable-length inputs before processing."
299
+ )
300
+ if initial_state is not None and initial_state.shape[0] != len(cu_seqlens) - 1:
301
+ raise ValueError(
302
+ f"The number of initial states is expected to be equal to the number of input sequences, "
303
+ f"i.e., {len(cu_seqlens) - 1} rather than {initial_state.shape[0]}."
304
+ )
305
+
306
+ if scale is None:
307
+ scale = k.shape[-1] ** -0.5
308
+
309
+ o, final_state = ChunkGatedDeltaRuleFunction.apply(
310
+ q,
311
+ k,
312
+ v,
313
+ g,
314
+ beta,
315
+ scale,
316
+ initial_state,
317
+ output_final_state,
318
+ cu_seqlens,
319
+ state_v_first,
320
+ auto_cp,
321
+ use_qk_l2norm_in_kernel,
322
+ enable_fwd_cp_cache,
323
+ )
324
+
325
+ return o, final_state
@@ -0,0 +1,20 @@
1
+ # Copyright (c) 2026 The Qwen team, Alibaba Group.
2
+ # Licensed under The MIT License [see LICENSE for details]
3
+
4
+ from .fused_fwd import fused_gdr_fwd
5
+ from .fused_bwd import fused_gdr_bwd
6
+ from .prepare_h import fused_gdr_h
7
+ from .kkt_solve import kkt_solve
8
+ from .cp_fwd import get_warmup_chunks, get_warmup_chunks_bidi, correct_initial_states, correct_terminal_states
9
+
10
+
11
+ __all__ = [
12
+ "fused_gdr_fwd",
13
+ "fused_gdr_bwd",
14
+ "fused_gdr_h",
15
+ "kkt_solve",
16
+ "get_warmup_chunks",
17
+ "get_warmup_chunks_bidi",
18
+ "correct_initial_states",
19
+ "correct_terminal_states",
20
+ ]