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.
- flash_qla/__init__.py +16 -0
- flash_qla/ops/__init__.py +7 -0
- flash_qla/ops/gated_delta_rule/__init__.py +7 -0
- flash_qla/ops/gated_delta_rule/chunk/__init__.py +325 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/__init__.py +20 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/cp_bwd.py +693 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/cp_fwd.py +609 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/fused_bwd.py +1319 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/fused_fwd.py +908 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/kkt_solve.py +323 -0
- flash_qla/ops/gated_delta_rule/chunk/blackwell/prepare_h.py +776 -0
- flash_qla/ops/gated_delta_rule/chunk/cp_context.py +322 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/__init__.py +20 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/cp_bwd.py +693 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/cp_fwd.py +609 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/fused_bwd.py +1148 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/fused_fwd.py +780 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/kkt_solve.py +323 -0
- flash_qla/ops/gated_delta_rule/chunk/hopper/prepare_h.py +675 -0
- flash_qla/ops/utils/__init__.py +11 -0
- flash_qla/ops/utils/cumsum.py +171 -0
- flash_qla/ops/utils/group_reduce.py +79 -0
- flash_qla/utils/__init__.py +24 -0
- flash_qla/utils/contiguous.py +41 -0
- flash_qla/utils/index.py +93 -0
- flash_qla/utils/math.py +101 -0
- flash_qla/utils/pack.py +83 -0
- flash_qla/utils/profiler.py +67 -0
- flash_qla-0.1.1.dist-info/METADATA +15 -0
- flash_qla-0.1.1.dist-info/RECORD +33 -0
- flash_qla-0.1.1.dist-info/WHEEL +5 -0
- flash_qla-0.1.1.dist-info/licenses/LICENSE +21 -0
- 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,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
|
+
]
|