kernels-bot commited on
Commit
95d01a6
·
verified ·
1 Parent(s): b70db7c

Uploaded using `kernel-builder`.

Browse files
build/torch-rocm/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from . import layers
2
+ from .layers import LigerForCausalLMLoss, liger_rotary_pos_emb
3
+
4
+
5
+ __all__ = [
6
+ "layers",
7
+ # kept for BC
8
+ "LigerForCausalLMLoss",
9
+ "liger_rotary_pos_emb",
10
+ ]
build/torch-rocm/_ops.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def get_backend() -> str:
4
+ """Detect the backend by inspecting torch."""
5
+ import torch
6
+
7
+ if hasattr(torch, "neuron"):
8
+ # Needs to be sorted before specific Torch builds, since Neuron
9
+ # extension can be loaded into e.g. CUDA Torch builds.
10
+ return "neuron"
11
+ elif torch.version.cuda is not None:
12
+ return "cuda"
13
+ elif torch.version.hip is not None:
14
+ return "rocm"
15
+ elif torch.backends.mps.is_available():
16
+ return "metal"
17
+ elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
18
+ return "xpu"
19
+ else:
20
+ return "cpu"
21
+
22
+
23
+ def _find_ops_name() -> str:
24
+ kernel_name = "liger_kernels"
25
+ unique_id = "4d9f798"
26
+ backend = get_backend()
27
+ return f"_{kernel_name}_{backend}_{unique_id}"
28
+
29
+
30
+ _OPS_NAME = _find_ops_name()
31
+
32
+ ops = getattr(torch.ops, _OPS_NAME)
33
+
34
+ def add_op_namespace_prefix(op_name: str) -> str:
35
+ """
36
+ Prefix op by namespace.
37
+ """
38
+ return f"{_OPS_NAME}::{op_name}"
build/torch-rocm/cross_entropy.py ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import operator
2
+
3
+ from typing import Optional
4
+
5
+ import torch
6
+ import triton
7
+ import triton.language as tl
8
+
9
+ from .utils import compare_version
10
+ from .utils import element_mul_kernel
11
+ from .utils import is_hip
12
+ from .utils import infer_device
13
+ from .utils import is_npu_available
14
+
15
+ if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
16
+ try:
17
+ # typical import path with dispatch available
18
+ from triton.language.extra.libdevice import tanh
19
+ except ModuleNotFoundError:
20
+ # for working with NGC containers
21
+ from triton.language.extra.cuda.libdevice import tanh
22
+ else:
23
+ from triton.language.math import tanh
24
+
25
+
26
+ @triton.jit
27
+ def liger_cross_entropy_kernel(
28
+ X_ptr,
29
+ X_stride,
30
+ Y_ptr,
31
+ Y_stride,
32
+ weight_ptr,
33
+ loss_ptr,
34
+ z_loss_ptr,
35
+ loss_stride,
36
+ token_accuracy_ptr,
37
+ token_accuracy_stride,
38
+ predicted_tokens_ptr,
39
+ predicted_tokens_stride,
40
+ n_cols,
41
+ n_non_ignore,
42
+ sum_non_ignore_weight,
43
+ weight_sum,
44
+ ignore_index,
45
+ lse_square_scale: tl.constexpr,
46
+ label_smoothing: tl.constexpr,
47
+ reduction: tl.constexpr, # set it as constexpr since reduction is always known at compile time
48
+ softcap,
49
+ RETURN_Z_LOSS: tl.constexpr,
50
+ RETURN_TOKEN_ACCURACY: tl.constexpr,
51
+ RETURN_PREDICTED_TOKENS: tl.constexpr,
52
+ BLOCK_SIZE: tl.constexpr,
53
+ HAS_WEIGHT: tl.constexpr,
54
+ HAS_SOFTCAPPING: tl.constexpr,
55
+ HAS_GRADIENTS: tl.constexpr,
56
+ ):
57
+ """
58
+ This kernel computes both cross entropy loss and the gradient of the input.
59
+ We only consider hard label + mean reduction for now. Please refer to https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html for the math.
60
+
61
+ Parameters:
62
+ X_ptr: Pointer to input tensor.
63
+ X_stride (int): The stride of the input tensor.
64
+ Y_ptr: Pointer to target tensor.
65
+ Y_stride (int): The stride of the target tensor.
66
+ weight_ptr: Pointer to weight tensor.
67
+ loss_ptr: Pointer to tensor to store the loss.
68
+ z_loss_ptr: Pointer to tensor to store the z loss. No operation if RETURN_Z_LOSS is 0.
69
+ loss_stride (int): The stride of the loss tensor.
70
+ token_accuracy_ptr: Pointer to tensor to store the per-token accuracy. No operation if RETURN_TOKEN_ACCURACY is 0.
71
+ token_accuracy_stride (int): The stride of the token accuracy tensor.
72
+ n_cols (int): The number of columns in the input tensor.
73
+ n_non_ignore (float): The number of non-ignored elements in the batch.
74
+ sum_non_ignore_weight (float): The sum of non-ignored target's weights in the batch.
75
+ weight_sum (float): The sum of weight tensor.
76
+ ignore_index (int): The index to ignore in the target.
77
+ label_smoothing (float): The amount of smoothing when computing the loss, where 0.0 means no smoothing.
78
+ lse_square_scale (float): The scaler of (logsumexp(_input)) ^ 2 adding to the loss for the stability of training.
79
+ reduction (str): The string for the reduction to apply
80
+ softcap (float): The upper threshold for scaling logits to the range (-softcap, +softcap).
81
+ RETURN_Z_LOSS (int): The boolean value to decide whether to store z loss to z_loss_ptr or not. It must be 0 or 1.
82
+ RETURN_TOKEN_ACCURACY (int): The boolean value to decide whether to store per-token accuracy to token_accuracy_ptr or not. It must be 0 or 1.
83
+ BLOCK_SIZE (int): The block size for Triton operations.
84
+ HAS_WEIGHT (bool): The boolean value to determine whether assigning weight to each of the classes.
85
+ HAS_SOFTCAPPING (bool): The boolean value to determine whether applying soft-capping or not.
86
+ HAS_GRADIENTS (bool): The boolean value to determine whether calculating gradients in forward pass.
87
+ """
88
+
89
+ # https://github.com/triton-lang/triton/issues/1058
90
+ # If B*T*V is too large, program_id * stride will overflow out of int32, so we convert to int64
91
+ program_id = tl.program_id(0).to(tl.int64)
92
+
93
+ # 1. Load Y_ptr first because if the target is ignore_index, we can return right away
94
+ Y_ptr += program_id * Y_stride
95
+ y = tl.load(Y_ptr)
96
+
97
+ # 2. locate the start index
98
+ X_ptr += program_id * X_stride
99
+
100
+ if y == ignore_index:
101
+ # set all X_ptr as 0
102
+ for i in range(0, n_cols, BLOCK_SIZE):
103
+ X_offsets = i + tl.arange(0, BLOCK_SIZE)
104
+ tl.store(X_ptr + X_offsets, 0.0, mask=X_offsets < n_cols)
105
+ # For ignored tokens, set token accuracy to 0
106
+ if RETURN_TOKEN_ACCURACY:
107
+ token_accuracy_ptr += program_id * token_accuracy_stride
108
+ tl.store(token_accuracy_ptr, 0.0)
109
+ if RETURN_PREDICTED_TOKENS:
110
+ predicted_tokens_ptr += program_id * predicted_tokens_stride
111
+ tl.store(predicted_tokens_ptr, -1)
112
+ return
113
+
114
+ loss_ptr += program_id * loss_stride
115
+ if RETURN_Z_LOSS:
116
+ z_loss_ptr += program_id * loss_stride
117
+ if RETURN_TOKEN_ACCURACY:
118
+ token_accuracy_ptr += program_id * token_accuracy_stride
119
+ if RETURN_PREDICTED_TOKENS:
120
+ predicted_tokens_ptr += program_id * predicted_tokens_stride
121
+
122
+ if HAS_WEIGHT:
123
+ weight_y = tl.load(weight_ptr + y).cast(tl.float32)
124
+
125
+ # Online softmax: 2 loads + 1 store (compared with 3 loads + 1 store for the safe softmax)
126
+ # Refer to Algorithm 3 in the paper: https://arxiv.org/pdf/1805.02867
127
+
128
+ # 3. [Online softmax] first pass: find max + sum
129
+ m = float("-inf") # m is the max value. use the notation from the paper
130
+ d = 0.0 # d is the sum. use the notation from the paper
131
+ argmax_idx = 0 # Track the index of the maximum value for token accuracy / predicted tokens computation
132
+ ori_X_y = tl.load(X_ptr + y).cast(tl.float32) # we need to store the original value of X_y for the loss calculation
133
+ if HAS_SOFTCAPPING:
134
+ ori_X_y = softcap * tanh(ori_X_y / softcap)
135
+
136
+ # Label smoothing is a general case of normal cross entropy
137
+ # See the full derivation at https://github.com/linkedin/Liger-Kernel/pull/198#issue-2503665310
138
+ scaled_x_sum = 0.0
139
+ eps = label_smoothing / n_cols
140
+
141
+ for i in range(0, n_cols, BLOCK_SIZE):
142
+ X_offsets = i + tl.arange(0, BLOCK_SIZE)
143
+ X_block = tl.load(
144
+ X_ptr + X_offsets,
145
+ mask=X_offsets < n_cols,
146
+ other=float("-inf"),
147
+ # Ensure float32 precision for softmax calculation
148
+ ).cast(tl.float32)
149
+ if HAS_SOFTCAPPING:
150
+ X_block = softcap * tanh(X_block / softcap)
151
+ block_max = tl.max(X_block)
152
+
153
+ # Track argmax for accuracy / predicted tokens computation
154
+ if RETURN_TOKEN_ACCURACY or RETURN_PREDICTED_TOKENS:
155
+ # Find the index of the maximum value in this block
156
+ is_max_mask = X_block == block_max
157
+ # Mask out invalid indices with a value larger than n_cols
158
+ masked_offsets = tl.where(is_max_mask, X_offsets, n_cols)
159
+ # Get the first (smallest) index where max occurs
160
+ current_block_argmax_idx = tl.min(masked_offsets)
161
+
162
+ is_new_max = block_max > m
163
+ argmax_idx = tl.where(is_new_max, current_block_argmax_idx, argmax_idx)
164
+
165
+ if label_smoothing > 0:
166
+ # scale X beforehand to avoid overflow
167
+ if HAS_WEIGHT:
168
+ weight_block = tl.load(weight_ptr + X_offsets, mask=X_offsets < n_cols)
169
+ scaled_x_sum += tl.sum(tl.where(X_offsets < n_cols, -eps * X_block * weight_block, 0.0))
170
+ else:
171
+ scaled_x_sum += tl.sum(tl.where(X_offsets < n_cols, -eps * X_block, 0.0))
172
+ m_new = tl.maximum(m, block_max)
173
+ d = d * tl.exp(m - m_new) + tl.sum(tl.exp(X_block - m_new))
174
+ m = m_new
175
+
176
+ # log (sum(e^(X_i))) = log (sum(e ^ (max(X) * e ^ (X_i - max(X)))))
177
+ # = log (e^(max(X)) * sum(e ^ (X_i - max(X))))
178
+ # = max(X) + log (sum(e ^ (X_i - max(X)))) = m + log d
179
+ lse = m + tl.log(d)
180
+
181
+ # 4. [Online Softmax] Second pass: compute gradients
182
+ # For 'mean' reduction, gradients are normalized by number of non-ignored elements (N)
183
+ # dx_y = (softmax(x_y) - 1) / N
184
+ # dx_i = softmax(x_i) / N, i != y
185
+ # For label smoothing:
186
+ # dx_i = (softmax(x_i) - label_smoothing / V) / N, V = n_cols, i != y
187
+ # dx_y = (softmax(x_y) - label_smoothing / V - (1 - label_smoothing)) / N
188
+ # = dx_i - (1 - label_smoothing) / N
189
+ # With Z loss:
190
+ # dx_i = ((1 + 2 * lse_square_scale * lse) * softmax(x_i) - label_smoothing / V) / N, i != y
191
+ # dx_y = dx_i - (1 - label_smoothing) / N
192
+ # For 'sum' reduction, no normalization is applied:
193
+ # dx_y = softmax(x_y) - 1
194
+ # dx_i = softmax(x_i), for i ≠ y
195
+ if HAS_GRADIENTS:
196
+ for i in range(0, n_cols, BLOCK_SIZE):
197
+ X_offsets = i + tl.arange(0, BLOCK_SIZE)
198
+ X_block = tl.load(
199
+ X_ptr + X_offsets,
200
+ mask=X_offsets < n_cols,
201
+ other=float("-inf"),
202
+ # Ensure float32 precision for softmax calculation
203
+ ).cast(tl.float32)
204
+ if HAS_SOFTCAPPING:
205
+ intermediate = tanh(X_block / softcap)
206
+ X_block = softcap * intermediate
207
+
208
+ if not HAS_WEIGHT:
209
+ # softmax(x_i)
210
+ X_block = tl.exp(X_block - m) / d
211
+ # derivative of z-loss: 2 * lse_square_scale * lse * softmax(x_i)
212
+ X_block += 2 * lse_square_scale * lse * X_block
213
+ # smoothing term
214
+ X_block += -eps
215
+ # special handle dx_y
216
+ X_block = tl.where(X_offsets != y, X_block, X_block - (1 - label_smoothing))
217
+ # reduction scale
218
+ if reduction == "mean":
219
+ X_block = X_block / n_non_ignore
220
+ else:
221
+ weight_block = tl.load(weight_ptr + X_offsets, mask=X_offsets < n_cols)
222
+ softmax_X = tl.exp(X_block - m) / d
223
+ # derivative of original_loss
224
+ dloss_ori = (1 - label_smoothing) * softmax_X
225
+ # specially handle dx_y
226
+ dloss_ori = tl.where(X_offsets != y, dloss_ori, dloss_ori - (1 - label_smoothing))
227
+ dloss_ori = dloss_ori * weight_y
228
+ # derivative of smooth_loss
229
+ dloss_smooth = eps * (-weight_block + softmax_X * weight_sum)
230
+ # derivative of z-loss
231
+ dz_loss = 2 * lse_square_scale * lse * softmax_X
232
+ # reduction scale
233
+ if reduction == "mean":
234
+ dloss_ori = dloss_ori / sum_non_ignore_weight
235
+ dloss_smooth = dloss_smooth / sum_non_ignore_weight
236
+ # TODO: Implement weighted z_loss. Currently, z_loss is not scaled by weight.
237
+ dz_loss = dz_loss / n_non_ignore
238
+ # derivative of total_loss
239
+ X_block = dloss_ori + dloss_smooth + dz_loss
240
+
241
+ # chain rule softcapping
242
+ # d(softcap * tanh(x / softcap)) = (1 - tanh^2(x / softcap))
243
+ if HAS_SOFTCAPPING:
244
+ X_block = X_block * (1 - intermediate * intermediate)
245
+
246
+ tl.store(X_ptr + X_offsets, X_block, mask=X_offsets < n_cols)
247
+
248
+ # We need tl.debug_barrier() to ensure the new result of X_ptr is written as mentioned in
249
+ # https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/ops/cross_entropy.py#L34
250
+ tl.debug_barrier()
251
+
252
+ # 5. Calculate the loss
253
+
254
+ # loss = log (softmax(X_y)) = log ((e ^ (X_y - max(X)) / sum(e ^ (X - max(X))))
255
+ # = (X_y - max(X)) - log(sum(e ^ (X - max(X))))
256
+ # = X_y - m - log d = X_y - lse
257
+ # sum(e ^ (X - max(X))) must >= 1 because the max term is e ^ 0 = 1
258
+ # So we can safely calculate log (softmax(X_y)) without overflow
259
+ loss = lse - ori_X_y
260
+ if HAS_WEIGHT:
261
+ loss = weight_y * loss
262
+
263
+ # Original loss = H(q, p), with label smoothing regularization = H(q', p) and (label_smoothing / V) = eps
264
+ # H(q', p) = (1 - label_smoothing) * H(q, p) + label_smoothing * H(u, p)
265
+ # = (1 - label_smoothing) * H(q, p) + eps * sum(logsoftmax(x_i))
266
+ # By using m (global max of xi) and d (sum of e^(xi-m)), we can simplify as:
267
+ # = (1 - label_smoothing) * H(q, p) + (sum(-eps * x_i) + label_smoothing * (m + logd))
268
+ # Refer to H(q', p) in section 7 of the paper: https://arxiv.org/pdf/1512.00567
269
+ # pytorch: https://github.com/pytorch/pytorch/blob/2981534f54d49fa3a9755c9b0855e7929c2527f0/aten/src/ATen/native/LossNLL.cpp#L516
270
+ # See full derivation at https://github.com/linkedin/Liger-Kernel/pull/198#issuecomment-2333753087
271
+ if label_smoothing > 0:
272
+ if HAS_WEIGHT:
273
+ smooth_loss = scaled_x_sum + eps * lse * weight_sum
274
+ else:
275
+ smooth_loss = scaled_x_sum + label_smoothing * lse
276
+ loss = loss * (1 - label_smoothing) + smooth_loss
277
+
278
+ # An auxiliary loss, z_loss
279
+ # Refer to Page14 Loss function section in the paper PaLM: https://www.jmlr.org/papers/v24/22-1144.html
280
+ z_loss = lse_square_scale * lse * lse
281
+ # Normalize the loss by the number of non-ignored elements if reduction is "mean"
282
+ if reduction == "mean":
283
+ if HAS_WEIGHT:
284
+ loss = loss / sum_non_ignore_weight
285
+ else:
286
+ loss = loss / n_non_ignore
287
+ # TODO: Implement weighted z_loss. Currently, z_loss is not scaled by weight.
288
+ z_loss = z_loss / n_non_ignore
289
+ loss += z_loss
290
+
291
+ tl.store(loss_ptr, loss)
292
+ if RETURN_Z_LOSS:
293
+ tl.store(z_loss_ptr, z_loss)
294
+ if RETURN_TOKEN_ACCURACY:
295
+ # Store 1.0 if prediction is correct, 0.0 otherwise
296
+ is_correct = 1.0 if argmax_idx == y else 0.0
297
+ tl.store(token_accuracy_ptr, is_correct)
298
+ if RETURN_PREDICTED_TOKENS:
299
+ tl.store(predicted_tokens_ptr, argmax_idx)
300
+
301
+
302
+ # The hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576 https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
303
+ # However, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
304
+ # The optimal maximum block size depends on your hardware, your kernel, and your dtype
305
+ # the best size we found by manually tuning on xpu and npu.
306
+ if infer_device() == "xpu":
307
+ MAX_FUSED_SIZE = 4096
308
+ elif infer_device() == "npu":
309
+ MAX_FUSED_SIZE = 2048
310
+ else:
311
+ MAX_FUSED_SIZE = 65536 // 2
312
+
313
+
314
+ def cross_entropy_forward(
315
+ _input,
316
+ target,
317
+ weight,
318
+ ignore_index,
319
+ lse_square_scale,
320
+ label_smoothing,
321
+ reduction,
322
+ softcap,
323
+ return_z_loss,
324
+ return_token_accuracy=False,
325
+ return_predicted_tokens=False,
326
+ ):
327
+ assert isinstance(return_z_loss, bool), f"return_z_loss must be True or False. Got: {return_z_loss}"
328
+ assert isinstance(return_token_accuracy, bool), (
329
+ f"return_token_accuracy must be True or False. Got: {return_token_accuracy}"
330
+ )
331
+ assert isinstance(return_predicted_tokens, bool), (
332
+ f"return_predicted_tokens must be True or False. Got: {return_predicted_tokens}"
333
+ )
334
+
335
+ BT, V = _input.shape
336
+ n_rows = BT
337
+
338
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
339
+
340
+ # unreduced loss
341
+ loss_1d = torch.zeros(n_rows, dtype=_input.dtype, device=_input.device)
342
+ z_loss_1d = torch.zeros(n_rows, dtype=_input.dtype, device=_input.device) if return_z_loss else None
343
+ token_accuracy_1d = (
344
+ torch.zeros(n_rows, dtype=torch.float32, device=_input.device) if return_token_accuracy else None
345
+ )
346
+ predicted_tokens_1d = (
347
+ torch.full((n_rows,), -1, dtype=torch.int64, device=_input.device) if return_predicted_tokens else None
348
+ )
349
+
350
+ target_mask = target != ignore_index
351
+ n_non_ignore = target_mask.sum().item()
352
+ assert (target * target_mask).max() < _input.shape[-1], (
353
+ f"Target {target.max()} is out of bounds. Expected < {_input.shape[-1]}"
354
+ )
355
+ assert (target * target_mask).min() >= 0, f"Target {target.min()} is out of bounds. Expected >= 0"
356
+ sum_non_ignore_weight = n_non_ignore
357
+ weight_sum = 0.0
358
+ if weight is not None:
359
+ assert weight.shape[0] == V, f"If given, weight has to be a Tensor of size V. Got: {weight.shape}"
360
+ assert torch.is_floating_point(weight), (
361
+ f"If given, weight has to be a Tensor of floating point dtype. Got: {weight.dtype}"
362
+ )
363
+ sum_non_ignore_weight = torch.gather(weight, dim=0, index=target.masked_select(target_mask)).sum().item()
364
+ weight_sum = weight.sum().item()
365
+ # ensure weight is contiguous
366
+ if weight.stride(-1) != 1:
367
+ weight = weight.contiguous()
368
+
369
+ # ensure _input and target are contiguous in the last dimension
370
+ if _input.stride(-1) != 1:
371
+ _input = _input.contiguous()
372
+ if target.stride(-1) != 1:
373
+ target = target.contiguous()
374
+
375
+ # Here we use a trick to store X_ptr gradient in X_ptr so we can save memory
376
+ liger_cross_entropy_kernel[(n_rows,)](
377
+ X_ptr=_input,
378
+ X_stride=_input.stride(-2),
379
+ Y_ptr=target,
380
+ Y_stride=target.stride(-1), # always 1
381
+ weight_ptr=weight, # dummy if None
382
+ loss_ptr=loss_1d,
383
+ z_loss_ptr=z_loss_1d,
384
+ loss_stride=loss_1d.stride(-1), # always 1
385
+ token_accuracy_ptr=token_accuracy_1d,
386
+ token_accuracy_stride=token_accuracy_1d.stride(-1)
387
+ if return_token_accuracy
388
+ else 0, # always 1 if accuracy is enabled
389
+ predicted_tokens_ptr=predicted_tokens_1d,
390
+ predicted_tokens_stride=predicted_tokens_1d.stride(-1)
391
+ if return_predicted_tokens
392
+ else 0, # always 1 if predicted tokens is enabled
393
+ n_cols=V,
394
+ n_non_ignore=n_non_ignore,
395
+ sum_non_ignore_weight=sum_non_ignore_weight,
396
+ ignore_index=ignore_index,
397
+ weight_sum=weight_sum,
398
+ lse_square_scale=lse_square_scale,
399
+ label_smoothing=label_smoothing,
400
+ reduction=reduction,
401
+ softcap=softcap,
402
+ RETURN_Z_LOSS=return_z_loss,
403
+ RETURN_TOKEN_ACCURACY=return_token_accuracy,
404
+ RETURN_PREDICTED_TOKENS=return_predicted_tokens,
405
+ BLOCK_SIZE=BLOCK_SIZE,
406
+ HAS_WEIGHT=True if weight is not None else False,
407
+ HAS_SOFTCAPPING=True if softcap is not None else False,
408
+ HAS_GRADIENTS=_input.requires_grad,
409
+ # TODO: 32 seems to give the best performance
410
+ # Performance is quite sensitive to num_warps
411
+ num_warps=32 if not is_hip() else 16,
412
+ )
413
+
414
+ if reduction == "none":
415
+ loss = loss_1d
416
+ z_loss = z_loss_1d if return_z_loss else None
417
+ token_accuracy = token_accuracy_1d if return_token_accuracy else None
418
+ else:
419
+ loss = torch.sum(loss_1d)
420
+ z_loss = torch.sum(z_loss_1d) if return_z_loss else None
421
+ # For accuracy, we compute the mean across all non-ignored tokens
422
+ token_accuracy = torch.sum(token_accuracy_1d) / n_non_ignore if return_token_accuracy else None
423
+
424
+ predicted_tokens = predicted_tokens_1d if return_predicted_tokens else None
425
+
426
+ return loss, z_loss, token_accuracy, predicted_tokens, _input
427
+
428
+
429
+ def cross_entropy_backward(_input, grad_output):
430
+ # If cross entropy is the last layer, grad_output is 1.0. Skip the mul to save time
431
+ if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
432
+ pass
433
+ # If reduction is 'none'
434
+ elif grad_output.ndim > 0:
435
+ _input = _input * grad_output.unsqueeze(dim=1)
436
+ # If reduction is ['mean', 'sum'], grad_output is just a scalar
437
+ # We use a Triton kernel instead of a PyTorch operation because modifying inputs in-place
438
+ # for gradient storage and backward multiple times causes anomalies with PyTorch but not with Triton.
439
+ else:
440
+ BT, V = _input.shape
441
+ n_rows = BT
442
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
443
+
444
+ element_mul_kernel[(n_rows,)](
445
+ _input,
446
+ _input.stride(-2),
447
+ grad_output,
448
+ V,
449
+ BLOCK_SIZE=BLOCK_SIZE,
450
+ num_warps=32 if not is_hip() else 16,
451
+ )
452
+
453
+ return _input
454
+
455
+
456
+ class LigerCrossEntropyFunction(torch.autograd.Function):
457
+ """
458
+ This class implements a custom autograd function for the Liger Cross Entropy loss.
459
+ It overrides the forward and backward methods of the torch.autograd.Function class.
460
+ """
461
+
462
+ @staticmethod
463
+ def forward(
464
+ ctx,
465
+ _input: torch.Tensor,
466
+ target: torch.Tensor,
467
+ weight: Optional[torch.FloatTensor],
468
+ ignore_index: int = -100,
469
+ lse_square_scale: float = 0.0,
470
+ label_smoothing: float = 0.0,
471
+ reduction: str = "mean",
472
+ softcap: Optional[float] = None,
473
+ return_z_loss: bool = False,
474
+ return_token_accuracy: bool = False,
475
+ return_predicted_tokens: bool = False,
476
+ ):
477
+ """
478
+ The forward pass of the Liger Cross Entropy loss.
479
+
480
+ Parameters:
481
+ ctx : The context object.
482
+ _input (tensor): The input tensor of shape (BT, V) where B is batch size, T is sequence length, V is vocab size.
483
+ target (tensor): The target tensor of shape (BT) where each value is in [0, V-1].
484
+ weight(Tensor, optional): a manual rescaling weight given to each class. If given, has to be a Tensor of size V and floating point dtype
485
+ ignore_index (int): The index to ignore in the target.
486
+ lse_square_scale (float): The scaler of (logsumexp(_input)) ^ 2 adding to the loss for the stability of training.
487
+ label_smoothing (float): The amount of smoothing when computing the loss, where 0.0 means no smoothing.
488
+ reduction (str): The reduction to apply to the output: "none" | "mean | "sum".
489
+ softcap (Optional[float]): The upper threshold for scaling logits to the range (-softcap, +softcap).
490
+ return_z_loss (bool): When `return_z_loss` is `True`, returns (loss, z_loss, token_accuracy, predicted_tokens) instead of (loss, None, None, None). Default: `False`
491
+ return_token_accuracy (bool): When `return_token_accuracy` is `True`, computes and returns per-token accuracy without materializing logits. Default: `False`
492
+ return_predicted_tokens (bool): When `return_predicted_tokens` is `True`, returns per-token predicted class indices (argmax) without materializing logits. Default: `False`
493
+
494
+ Returns:
495
+ tuple: A tuple with the computed losses, accuracy, and predicted tokens: (loss, z_loss, token_accuracy, predicted_tokens). z_loss, token_accuracy, and predicted_tokens are None if not requested.
496
+ """
497
+ input_requires_grad = _input.requires_grad
498
+
499
+ loss, z_loss, token_accuracy, predicted_tokens, _input = cross_entropy_forward(
500
+ _input,
501
+ target,
502
+ weight,
503
+ ignore_index,
504
+ lse_square_scale,
505
+ label_smoothing,
506
+ reduction,
507
+ softcap,
508
+ return_z_loss,
509
+ return_token_accuracy,
510
+ return_predicted_tokens,
511
+ )
512
+ # TODO: investigation
513
+ # If we don't detach the _input tensor, the memory will double
514
+ # Not sure why but seems that there will be a time both grad and value exist but in different location
515
+ if input_requires_grad:
516
+ ctx.save_for_backward(_input.detach())
517
+ ctx.return_z_loss = return_z_loss
518
+ ctx.return_token_accuracy = return_token_accuracy
519
+ ctx.return_predicted_tokens = return_predicted_tokens
520
+
521
+ return loss, z_loss, token_accuracy, predicted_tokens
522
+
523
+ @staticmethod
524
+ def backward(ctx, grad_output, grad_output2, grad_output3, grad_output4):
525
+ """
526
+ The backward pass of the Liger Cross Entropy loss.
527
+
528
+ Parameters:
529
+ ctx : The context object with saved tensors.
530
+ grad_output (tensor): The tensor containing the gradient of the loss with respect to the output.
531
+ grad_output2 (tensor): No use. Gradient for z_loss (not used as z_loss is only for logging).
532
+ grad_output3 (tensor): No use. Gradient for token_accuracy (not used as token_accuracy is only for metrics).
533
+ grad_output4 (tensor): No use. Gradient for predicted_tokens (not used as predicted_tokens is only for metrics).
534
+ Returns:
535
+ tuple: A tuple with the gradients with respect to the inputs. The elements are tensors or None.
536
+ """
537
+ if ctx.return_z_loss:
538
+ del grad_output2 # z_loss is only for logging
539
+ if ctx.return_token_accuracy:
540
+ del grad_output3 # token_accuracy is only for metrics
541
+ if ctx.return_predicted_tokens:
542
+ del grad_output4 # predicted_tokens is only for metrics
543
+
544
+ (_input,) = ctx.saved_tensors
545
+ _input = cross_entropy_backward(_input, grad_output)
546
+ return (
547
+ _input,
548
+ None,
549
+ None,
550
+ None,
551
+ None,
552
+ None,
553
+ None,
554
+ None,
555
+ None,
556
+ None,
557
+ None,
558
+ )
build/torch-rocm/dyt.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import operator
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from .utils import compare_version
8
+ from .utils import ensure_contiguous
9
+ from .utils import get_npu_core_count
10
+ from .utils import infer_device
11
+ from .utils import is_npu_available
12
+
13
+ if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
14
+ try:
15
+ # typical import path with dispatch available
16
+ from triton.language.extra.libdevice import tanh
17
+ except ModuleNotFoundError:
18
+ # for working with NGC containers
19
+ from triton.language.extra.cuda.libdevice import tanh
20
+ else:
21
+ from triton.language.math import tanh
22
+
23
+
24
+ @triton.autotune(
25
+ configs=[
26
+ triton.Config({"BLOCK_N": bn}, num_stages=ns, num_warps=nw)
27
+ for bn in [1024, 2048, 4096]
28
+ for ns in [1, 2]
29
+ for nw in [4, 8, 16]
30
+ ],
31
+ key=["N"],
32
+ )
33
+ @triton.jit
34
+ def _dyt_fwd_kernel(X, Y, Alpha, Gamma, Beta, HAVE_BETA: tl.constexpr, N: tl.constexpr, BLOCK_N: tl.constexpr):
35
+ col = tl.cast(tl.program_id(0), tl.int64) * BLOCK_N + tl.arange(0, BLOCK_N)
36
+ mask = col < N
37
+ row_id = tl.cast(tl.program_id(1), tl.int64)
38
+
39
+ X += row_id * N
40
+ Y += row_id * N
41
+ alpha = tl.load(Alpha).to(tl.float32)
42
+
43
+ gamma = tl.load(Gamma + col, mask=mask, other=0.0).to(tl.float32)
44
+
45
+ x = tl.load(X + col, mask=mask, other=0.0).to(tl.float32)
46
+
47
+ tanh_x = tanh(alpha * x)
48
+ y = tanh_x * gamma
49
+ if HAVE_BETA:
50
+ beta = tl.load(Beta + col, mask=mask, other=0.0).to(tl.float32)
51
+ y += beta
52
+ tl.store(Y + col, y, mask=mask)
53
+
54
+
55
+ @triton.autotune(
56
+ configs=[
57
+ triton.Config({"BLOCK_N": bn}, num_stages=ns, num_warps=nw)
58
+ for bn in [1024, 2048, 4096]
59
+ for ns in [1, 2]
60
+ for nw in [4, 8, 16]
61
+ ],
62
+ key=["N"],
63
+ # DA is indexed by program_id(0), so different BLOCK_N configs write to
64
+ # different slot counts per SM. Autotune trials don't zero outputs between
65
+ # runs, so stale slots from a prior trial would leak into da.sum(). Reset
66
+ # DA between trials to isolate each config's writes.
67
+ reset_to_zero=["DA"],
68
+ )
69
+ @triton.jit
70
+ def _dyt_bwd_kernel(
71
+ DY, DX, DA, DG, DB, X, Alpha, Gamma, HAVE_BETA: tl.constexpr, M, N: tl.constexpr, BLOCK_N: tl.constexpr
72
+ ):
73
+ col = tl.cast(tl.program_id(0), tl.int64) * BLOCK_N + tl.arange(0, BLOCK_N)
74
+ mask = col < N
75
+ start_row_id = tl.cast(tl.program_id(1), tl.int64)
76
+
77
+ alpha = tl.load(Alpha).to(tl.float32)
78
+ da = 0.0
79
+ gamma = tl.load(Gamma + col, mask=mask, other=0.0).to(tl.float32)
80
+ dg = tl.zeros((BLOCK_N,), dtype=tl.float32)
81
+ if HAVE_BETA:
82
+ db = tl.zeros((BLOCK_N,), dtype=tl.float32)
83
+ for row_id in range(start_row_id, M, tl.num_programs(1)):
84
+ x = tl.load(X + row_id * N + col, mask=mask, other=0.0).to(tl.float32)
85
+ dy = tl.load(DY + row_id * N + col, mask=mask, other=0.0).to(tl.float32)
86
+ tanh_x = tanh(alpha * x)
87
+ if HAVE_BETA:
88
+ db += dy
89
+ dg += dy * tanh_x
90
+ tmp = (1 - tanh_x * tanh_x) * dy * gamma
91
+ da += tl.sum(x * tmp, 0)
92
+ dx = alpha * tmp
93
+ tl.store(DX + row_id * N + col, dx, mask=mask)
94
+
95
+ tl.store(DG + start_row_id * N + col, dg, mask=mask)
96
+ if HAVE_BETA:
97
+ tl.store(DB + start_row_id * N + col, db, mask=mask)
98
+ tl.store(DA + start_row_id * tl.cdiv(N, 512) + tl.program_id(0), da)
99
+
100
+
101
+ def liger_dyt_fwd(x, alpha, gamma, beta):
102
+ assert x.is_contiguous()
103
+ HAVE_BETA = True if beta is not None else False
104
+ input_shape = x.shape
105
+ x = x.view(-1, input_shape[-1])
106
+ M, N = x.shape
107
+
108
+ y = torch.empty_like(x)
109
+
110
+ grid = lambda meta: (triton.cdiv(N, meta["BLOCK_N"]), M)
111
+ _dyt_fwd_kernel[grid](
112
+ x,
113
+ y,
114
+ alpha,
115
+ gamma,
116
+ beta,
117
+ HAVE_BETA,
118
+ N,
119
+ )
120
+ return y.view(input_shape)
121
+
122
+
123
+ def liger_dyt_bwd(dy, x, alpha, gamma, beta):
124
+ assert dy.is_contiguous()
125
+ input_shape = x.shape
126
+ x = x.view(-1, input_shape[-1])
127
+ M, N = x.shape
128
+ HAVE_BETA = True if beta is not None else False
129
+
130
+ device = infer_device()
131
+ if device == "cuda":
132
+ NUM_SMS = torch.cuda.get_device_properties(x.device).multi_processor_count
133
+ elif device == "xpu":
134
+ NUM_SMS = torch.xpu.get_device_properties(x.device).gpu_subslice_count
135
+ elif device == "npu":
136
+ NUM_SMS = get_npu_core_count()
137
+ da = torch.zeros(NUM_SMS, triton.cdiv(N, 512), dtype=torch.float32, device=x.device)
138
+ dg = torch.empty(NUM_SMS, N, dtype=torch.float32, device=x.device)
139
+ db = torch.empty(NUM_SMS, N, dtype=torch.float32, device=x.device) if HAVE_BETA else None
140
+ dx = torch.empty_like(dy)
141
+
142
+ grid = lambda meta: (triton.cdiv(N, meta["BLOCK_N"]), NUM_SMS)
143
+ _dyt_bwd_kernel[grid](dy, dx, da, dg, db, x, alpha, gamma, HAVE_BETA, M, N)
144
+ if HAVE_BETA:
145
+ db = db.sum(0).to(x.dtype)
146
+ dg = dg.sum(0).to(gamma.dtype)
147
+ da = da.sum().to(x.dtype).unsqueeze(0)
148
+ return dx.view(input_shape), da, dg, db
149
+
150
+
151
+ class LigerDyTFunction(torch.autograd.Function):
152
+ @staticmethod
153
+ @ensure_contiguous
154
+ def forward(ctx, x, alpha, gamma, beta):
155
+ y = liger_dyt_fwd(x, alpha, gamma, beta)
156
+ ctx.save_for_backward(x, alpha, gamma, beta)
157
+ return y
158
+
159
+ @staticmethod
160
+ @ensure_contiguous
161
+ def backward(ctx, dy):
162
+ x, alpha, gamma, beta = ctx.saved_tensors
163
+ dx, dalpha, dgamma, dbeta = liger_dyt_bwd(dy, x, alpha, gamma, beta)
164
+ return dx, dalpha, dgamma, dbeta
build/torch-rocm/fused_linear_cross_entropy.py ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+
4
+ from .cross_entropy import liger_cross_entropy_kernel
5
+ from .utils import amp_custom_bwd
6
+ from .utils import amp_custom_fwd
7
+ from .utils import element_mul_kernel
8
+ from .utils import is_hip
9
+ from .utils import infer_device
10
+
11
+ # The hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576 https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
12
+ # However, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
13
+ # The optimal maximum block size depends on your hardware, your kernel, and your dtype
14
+ MAX_FUSED_SIZE = 2048 if infer_device() == "npu" else 65536 // 2
15
+
16
+
17
+ def fused_linear_cross_entropy_forward(
18
+ _input,
19
+ weight,
20
+ target,
21
+ ce_weight=None,
22
+ bias=None,
23
+ ignore_index=-100,
24
+ lse_square_scale=0.0,
25
+ label_smoothing=0.0,
26
+ reduction="mean",
27
+ softcap=None,
28
+ return_z_loss=False,
29
+ accum_dtype=None,
30
+ use_token_scaling=False,
31
+ return_token_accuracy=False,
32
+ return_predicted_tokens=False,
33
+ ):
34
+ assert isinstance(return_z_loss, bool), f"return_z_loss must be True or False. Got: {return_z_loss}"
35
+ assert isinstance(return_token_accuracy, bool), (
36
+ f"return_token_accuracy must be True or False. Got: {return_token_accuracy}"
37
+ )
38
+ assert isinstance(return_predicted_tokens, bool), (
39
+ f"return_predicted_tokens must be True or False. Got: {return_predicted_tokens}"
40
+ )
41
+ device = _input.device
42
+
43
+ input_requires_grad = _input.requires_grad
44
+
45
+ # inputs have shape: BT x H
46
+ # materialized activations will have shape: BT x V
47
+ # the increase in memory = BT x V
48
+ # reduction can be achieved by partitioning the number of tokens BT into smaller chunks.
49
+ # for ex: if we were to achieve the same memory consumption as BT x H, then the chunk size should be:
50
+ # inc_factor = (V+H-1)//H, chunk_size = (BT + inc_factor - 1)//inc_factor
51
+ # for ex: BT = 4096*4, V = 32000, H = 4096 ==> inc_factor = 8, chunk_size = 2048
52
+ BT, H = _input.shape
53
+ V = weight.shape[0]
54
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
55
+
56
+ inc_factor = triton.cdiv(V, H) # (V + H - 1) // H
57
+ chunk_size = triton.next_power_of_2(triton.cdiv(BT, inc_factor)) # (BT + inc_factor - 1) // inc_factor
58
+ num_chunks = triton.cdiv(BT, chunk_size) # (BT + chunk_size - 1) // chunk_size
59
+
60
+ grad_input = torch.zeros_like(_input, device=device)
61
+
62
+ # we use fp32 for loss and gradients accumulator
63
+ if input_requires_grad:
64
+ if accum_dtype is None:
65
+ grad_weight = torch.zeros_like(weight, device=device) if weight.requires_grad else None
66
+ grad_bias = torch.zeros_like(bias, device=device) if bias is not None else None
67
+ else:
68
+ grad_weight = torch.zeros_like(weight, dtype=accum_dtype, device=device) if weight.requires_grad else None
69
+ grad_bias = torch.zeros_like(bias, dtype=accum_dtype, device=device) if bias is not None else None
70
+ else:
71
+ grad_weight = None
72
+ grad_bias = None
73
+
74
+ loss_1d = torch.zeros(BT, dtype=torch.float32, device=device)
75
+ z_loss_1d = torch.zeros(BT, dtype=_input.dtype, device=_input.device) if return_z_loss else None
76
+ token_accuracy_1d = torch.zeros(BT, dtype=torch.float32, device=device) if return_token_accuracy else None
77
+ predicted_tokens_1d = torch.full((BT,), -1, dtype=torch.int64, device=device) if return_predicted_tokens else None
78
+
79
+ # TODO: evaluate how CUDA synchronization caused by .item() affects the speed
80
+ target_mask = target != ignore_index
81
+ total_n_non_ignore = target_mask.sum().item()
82
+ total_sum_non_ignore_ce_weight = total_n_non_ignore
83
+ ce_weight_sum = 0.0
84
+ if ce_weight is not None:
85
+ assert ce_weight.shape[0] == V, f"If given, weight has to be a Tensor of size V. Got: {ce_weight.shape}"
86
+ assert torch.is_floating_point(ce_weight), (
87
+ f"If given, weight has to be a Tensor of floating point dtype. Got: {ce_weight.dtype}"
88
+ )
89
+ total_sum_non_ignore_ce_weight = (
90
+ torch.gather(ce_weight, dim=0, index=target.masked_select(target_mask)).sum().item()
91
+ )
92
+ ce_weight_sum = ce_weight.sum().item()
93
+ if ce_weight.stride(-1) != 1:
94
+ ce_weight = ce_weight.contiguous()
95
+
96
+ for chunk_id in range(num_chunks):
97
+ start_idx = chunk_id * chunk_size
98
+ end_idx = min((chunk_id + 1) * chunk_size, BT)
99
+ _input_chunk = _input[start_idx:end_idx] # chunk_size x H
100
+
101
+ # when doing matmul, use the original precision
102
+ logits_chunk = _input_chunk @ weight.t() # chunk_size x V
103
+ if bias is not None:
104
+ logits_chunk = logits_chunk + bias
105
+
106
+ target_chunk = target[start_idx:end_idx] # chunk_size,
107
+
108
+ n_rows = logits_chunk.shape[0]
109
+
110
+ # Compute predicted probabilities for token scaling if needed
111
+ if use_token_scaling:
112
+ # Compute softmax probabilities for scaling
113
+ # We need to compute this before the cross entropy kernel modifies logits_chunk
114
+ logits_for_softmax = logits_chunk.detach().clone() # Detach to avoid gradient flow
115
+ if softcap is not None:
116
+ logits_for_softmax = softcap * torch.tanh(logits_for_softmax / softcap)
117
+
118
+ # Compute softmax to get predicted probabilities
119
+ probs = torch.softmax(logits_for_softmax, dim=-1)
120
+
121
+ # Get predicted probabilities for token scaling, handling ignored targets
122
+ valid_target_mask = target_chunk != ignore_index
123
+ valid_targets = target_chunk[valid_target_mask]
124
+
125
+ if len(valid_targets) > 0:
126
+ # Gather probabilities only for valid targets
127
+ valid_probs = probs[valid_target_mask]
128
+ pred_probs_valid = torch.gather(valid_probs, -1, valid_targets.unsqueeze(-1)).squeeze(-1)
129
+
130
+ # Create full tensor with zeros for ignored targets
131
+ pred_probs = torch.zeros_like(target_chunk, dtype=probs.dtype, device=probs.device)
132
+ pred_probs[valid_target_mask] = pred_probs_valid
133
+ else:
134
+ # All targets are ignored
135
+ pred_probs = torch.zeros_like(target_chunk, dtype=probs.dtype, device=probs.device)
136
+
137
+ # Store the scaling factors
138
+ scaling_factors = pred_probs.detach() # Detach to ensure no gradient flow
139
+
140
+ # unreduced loss
141
+ loss_1d_slice = loss_1d[start_idx:end_idx] # chunk_size,
142
+ z_loss_1d_slice = z_loss_1d[start_idx:end_idx] if return_z_loss else None
143
+ token_accuracy_1d_slice = token_accuracy_1d[start_idx:end_idx] if return_token_accuracy else None
144
+ predicted_tokens_1d_slice = predicted_tokens_1d[start_idx:end_idx] if return_predicted_tokens else None
145
+
146
+ # ensure _input and target are contiguous
147
+ logits_chunk = logits_chunk.contiguous()
148
+ target_chunk = target_chunk.contiguous()
149
+
150
+ # Here we calculate the gradient of logits_chunk in place so we can save memory.
151
+ liger_cross_entropy_kernel[(n_rows,)](
152
+ X_ptr=logits_chunk,
153
+ X_stride=logits_chunk.stride(-2),
154
+ Y_ptr=target_chunk,
155
+ Y_stride=target_chunk.stride(-1), # always 1
156
+ weight_ptr=ce_weight,
157
+ loss_ptr=loss_1d_slice,
158
+ z_loss_ptr=z_loss_1d_slice,
159
+ loss_stride=loss_1d_slice.stride(-1), # always 1
160
+ token_accuracy_ptr=token_accuracy_1d_slice,
161
+ token_accuracy_stride=token_accuracy_1d_slice.stride(-1)
162
+ if return_token_accuracy
163
+ else 0, # always 1 if accuracy is enabled
164
+ predicted_tokens_ptr=predicted_tokens_1d_slice,
165
+ predicted_tokens_stride=predicted_tokens_1d_slice.stride(-1)
166
+ if return_predicted_tokens
167
+ else 0, # always 1 if predicted tokens is enabled
168
+ n_cols=V,
169
+ n_non_ignore=total_n_non_ignore,
170
+ sum_non_ignore_weight=total_sum_non_ignore_ce_weight,
171
+ weight_sum=ce_weight_sum,
172
+ ignore_index=ignore_index,
173
+ lse_square_scale=lse_square_scale,
174
+ label_smoothing=label_smoothing,
175
+ reduction=reduction,
176
+ softcap=softcap,
177
+ RETURN_Z_LOSS=return_z_loss,
178
+ RETURN_TOKEN_ACCURACY=return_token_accuracy,
179
+ RETURN_PREDICTED_TOKENS=return_predicted_tokens,
180
+ HAS_WEIGHT=True if ce_weight is not None else False,
181
+ HAS_SOFTCAPPING=True if softcap is not None else False,
182
+ HAS_GRADIENTS=input_requires_grad,
183
+ BLOCK_SIZE=BLOCK_SIZE,
184
+ num_warps=32 if not is_hip() else 16,
185
+ )
186
+
187
+ # Apply token scaling if requested
188
+ if use_token_scaling:
189
+ loss_1d_slice = loss_1d_slice * scaling_factors
190
+ if return_z_loss:
191
+ z_loss_1d_slice = z_loss_1d_slice * scaling_factors
192
+
193
+ loss_1d[start_idx:end_idx] = loss_1d_slice
194
+ if return_z_loss:
195
+ z_loss_1d[start_idx:end_idx] = z_loss_1d_slice
196
+ if return_token_accuracy:
197
+ token_accuracy_1d[start_idx:end_idx] = token_accuracy_1d_slice
198
+ if return_predicted_tokens:
199
+ predicted_tokens_1d[start_idx:end_idx] = predicted_tokens_1d_slice
200
+ grad_logits_chunk = logits_chunk # chunk_size x V
201
+
202
+ # Apply token scaling to gradients if requested
203
+ if use_token_scaling:
204
+ # Expand scaling factors to match gradient dimensions
205
+ scaling_factors_expanded = scaling_factors.unsqueeze(-1) # chunk_size x 1
206
+ grad_logits_chunk = grad_logits_chunk * scaling_factors_expanded
207
+
208
+ if input_requires_grad:
209
+ grad_input[start_idx:end_idx] = grad_logits_chunk @ weight
210
+
211
+ if grad_weight is not None and input_requires_grad:
212
+ grad_weight += torch.mm(grad_logits_chunk.t(), _input_chunk).float()
213
+
214
+ if bias is not None and input_requires_grad:
215
+ torch.add(
216
+ input=grad_bias,
217
+ other=grad_logits_chunk.sum(dim=0),
218
+ out=grad_bias,
219
+ alpha=1.0,
220
+ )
221
+
222
+ # Need extra calculations for backward if reduction=='none'. Not supporting reduction='none' now.
223
+ # if reduction == "none":
224
+ # loss = loss_1d
225
+ # z_loss = z_loss_1d if return_z_loss else None
226
+
227
+ if reduction == "none":
228
+ # Return per-token losses
229
+ loss = loss_1d
230
+ z_loss = z_loss_1d if return_z_loss else None
231
+ token_accuracy = token_accuracy_1d if return_token_accuracy else None
232
+ else:
233
+ loss = torch.sum(loss_1d)
234
+ z_loss = torch.sum(z_loss_1d) if return_z_loss else None
235
+ # For accuracy, we compute the mean across all non-ignored tokens
236
+ token_accuracy = torch.sum(token_accuracy_1d) / total_n_non_ignore if return_token_accuracy else None
237
+
238
+ predicted_tokens = predicted_tokens_1d if return_predicted_tokens else None
239
+
240
+ # Cast back to original dtype
241
+ grad_weight = grad_weight.to(weight.dtype) if grad_weight is not None else None
242
+ grad_bias = grad_bias.to(bias.dtype) if grad_bias is not None else None
243
+
244
+ return loss, z_loss, token_accuracy, predicted_tokens, grad_input, grad_weight, grad_bias
245
+
246
+
247
+ def fused_linear_cross_entropy_backward(grad_output, grad_input, grad_weight, grad_bias):
248
+ # If cross entropy is the last layer, grad_output is 1.0. Skip the mul to save time
249
+ if not torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
250
+ # We use a Triton kernel instead of a PyTorch operation because modifying inputs in-place
251
+ # for gradient storage and backward multiple times causes anomalies with PyTorch but not with Triton.
252
+ BT, H = grad_input.shape
253
+ n_rows = BT
254
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(H))
255
+
256
+ element_mul_kernel[(n_rows,)](
257
+ grad_input,
258
+ grad_input.stride(-2),
259
+ grad_output,
260
+ H,
261
+ BLOCK_SIZE=BLOCK_SIZE,
262
+ num_warps=32 if not is_hip() else 16,
263
+ )
264
+
265
+ # handle grad_weight
266
+ if grad_weight is not None:
267
+ V, H = grad_weight.shape
268
+ n_rows = V
269
+
270
+ element_mul_kernel[(n_rows,)](
271
+ grad_weight,
272
+ grad_weight.stride(-2),
273
+ grad_output,
274
+ H,
275
+ BLOCK_SIZE=BLOCK_SIZE,
276
+ num_warps=32 if not is_hip() else 16,
277
+ )
278
+
279
+ if grad_bias is not None:
280
+ V = grad_bias.shape[0]
281
+ n_rows = V
282
+
283
+ element_mul_kernel[(n_rows,)](
284
+ grad_bias,
285
+ grad_bias.stride(-1),
286
+ grad_output,
287
+ 1,
288
+ BLOCK_SIZE=BLOCK_SIZE,
289
+ num_warps=32 if not is_hip() else 16,
290
+ )
291
+ return grad_input, grad_weight, grad_bias
292
+
293
+
294
+ class LigerFusedLinearCrossEntropyFunction(torch.autograd.Function):
295
+ @staticmethod
296
+ @amp_custom_fwd
297
+ def forward(
298
+ ctx,
299
+ _input,
300
+ weight,
301
+ target,
302
+ bias=None,
303
+ ce_weight=None,
304
+ ignore_index=-100,
305
+ lse_square_scale=0.0,
306
+ label_smoothing=0.0,
307
+ reduction="mean",
308
+ softcap=None,
309
+ return_z_loss: bool = False,
310
+ accum_dtype=None,
311
+ use_token_scaling: bool = False,
312
+ return_token_accuracy: bool = False,
313
+ return_predicted_tokens: bool = False,
314
+ ):
315
+ """
316
+ Fusing the last linear layer with cross-entropy loss
317
+ Reference: https://github.com/mgmalek/efficient_cross_entropy
318
+
319
+ Handle the forward and backward pass of the final linear layer via cross-entropy loss by avoiding
320
+ the materialization of the large logits tensor. Since Cross Entropy Loss is the last layer, we can
321
+ compute the gradient at the forward pass. By doing so, we don't have to store the _input and target
322
+ for the backward pass.
323
+
324
+ _input: (B*T, H) where B is batch size, T is sequence length, H is hidden dimension.
325
+ target: (B*T) where each value is in [0, V-1]
326
+ weight: (V, H) where V is the number of classes
327
+ bias: (V) where V is the number of classes
328
+ ce_weight: a manual rescaling weight given to each class. If given, has to be a Tensor of size V and floating point dtype
329
+ ignore_index: the index to ignore in the target
330
+ label_smoothing (float): The amount of smoothing when computing the loss, where 0.0 means no smoothing.
331
+ reduction: reduction to apply
332
+ accum_dtype (torch.dtype): the dtype of intermediate result buffers for weight and bias gradient accumulations.
333
+ Recommended to set `accum_dtype` to higher precision, e.g. `torch.float32`, if the training is unstable with original dtype. Default: `None`, performing accumulations in original dtype
334
+ use_token_scaling (bool): whether to scale each token's loss by its predicted probability (detached).
335
+ When True, each token's loss is multiplied by the model's predicted probability for that token's true class.
336
+ Default: False.
337
+ return_token_accuracy (bool): When `return_token_accuracy` is `True`, computes and returns per-token accuracy without materializing logits. Default: `False`
338
+ return_predicted_tokens (bool): When `return_predicted_tokens` is `True`, returns per-token predicted class indices (argmax) without materializing logits. Default: `False`
339
+ """
340
+
341
+ loss, z_loss, token_accuracy, predicted_tokens, grad_input, grad_weight, grad_bias = (
342
+ fused_linear_cross_entropy_forward(
343
+ _input=_input,
344
+ weight=weight,
345
+ target=target,
346
+ bias=bias,
347
+ ce_weight=ce_weight,
348
+ ignore_index=ignore_index,
349
+ lse_square_scale=lse_square_scale,
350
+ label_smoothing=label_smoothing,
351
+ reduction=reduction,
352
+ softcap=softcap,
353
+ return_z_loss=return_z_loss,
354
+ accum_dtype=accum_dtype,
355
+ use_token_scaling=use_token_scaling,
356
+ return_token_accuracy=return_token_accuracy,
357
+ return_predicted_tokens=return_predicted_tokens,
358
+ )
359
+ )
360
+ # downcast to dtype and store for backward
361
+ ctx.save_for_backward(
362
+ grad_input.detach(),
363
+ grad_weight.detach() if grad_weight is not None else None,
364
+ grad_bias.detach() if grad_bias is not None else None,
365
+ )
366
+ ctx.return_z_loss = return_z_loss
367
+ ctx.return_token_accuracy = return_token_accuracy
368
+ ctx.return_predicted_tokens = return_predicted_tokens
369
+ return loss, z_loss, token_accuracy, predicted_tokens
370
+
371
+ @staticmethod
372
+ @amp_custom_bwd
373
+ def backward(ctx, grad_output, grad_output2, grad_output3, grad_output4):
374
+ if ctx.return_z_loss:
375
+ del grad_output2 # z_loss is only for logging
376
+ if ctx.return_token_accuracy:
377
+ del grad_output3 # token_accuracy is only for metrics
378
+ if ctx.return_predicted_tokens:
379
+ del grad_output4 # predicted_tokens is only for metrics
380
+ (grad_input, grad_weight, grad_bias) = ctx.saved_tensors
381
+ grad_input, grad_weight, grad_bias = fused_linear_cross_entropy_backward(
382
+ grad_output, grad_input, grad_weight, grad_bias
383
+ )
384
+ return (
385
+ grad_input,
386
+ grad_weight,
387
+ None,
388
+ grad_bias,
389
+ None,
390
+ None,
391
+ None,
392
+ None,
393
+ None,
394
+ None,
395
+ None,
396
+ None,
397
+ None, # use_token_scaling
398
+ None, # return_token_accuracy
399
+ None, # return_predicted_tokens
400
+ )
build/torch-rocm/geglu.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import operator
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from .utils import calculate_settings
8
+ from .utils import compare_version
9
+ from .utils import ensure_contiguous
10
+ from .utils import is_npu_available
11
+
12
+ if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
13
+ try:
14
+ # typical import path with dispatch available
15
+ from triton.language.extra.libdevice import tanh
16
+ except ModuleNotFoundError:
17
+ # for working with NGC containers
18
+ from triton.language.extra.cuda.libdevice import tanh
19
+ else:
20
+ from triton.language.math import tanh
21
+
22
+
23
+ @triton.jit
24
+ def _geglu_tanh_forward_kernel(a, b, c, stride, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr):
25
+ program_id = tl.program_id(0).to(tl.int64)
26
+
27
+ # locate start index
28
+ a += program_id * stride
29
+ b += program_id * stride
30
+ c += program_id * stride
31
+
32
+ col_offsets = tl.arange(0, BLOCK_SIZE)
33
+ mask = col_offsets < n_cols
34
+ a_row = tl.load(a + col_offsets, mask=mask, other=0).to(tl.float32)
35
+ b_row = tl.load(b + col_offsets, mask=mask, other=0)
36
+
37
+ # tanh approximation form of GELU is computed with:
38
+ # 0.5 * a * (1 + tanh(sqrt(2 / pi) * (a + 0.044715 * a^3)))
39
+ sqrt_2_over_pi = 0.7978845608028654 # sqrt(2 / pi)
40
+ a_cubed = a_row * a_row * a_row
41
+ tanh_arg = sqrt_2_over_pi * (a_row + 0.044715 * a_cubed)
42
+ tanh_result = tanh(tanh_arg)
43
+ geglu_a = 0.5 * a_row * (1 + tanh_result)
44
+ c_row = geglu_a.cast(b_row.dtype) * b_row
45
+ tl.store(c + col_offsets, c_row, mask=mask)
46
+
47
+
48
+ @triton.jit
49
+ def _geglu_tanh_backward_kernel(dc, a, b, stride, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr):
50
+ program_id = tl.program_id(0).to(tl.int64)
51
+
52
+ # locate start index
53
+ dc += program_id * stride
54
+ a += program_id * stride
55
+ b += program_id * stride
56
+
57
+ col_offsets = tl.arange(0, BLOCK_SIZE)
58
+ mask = col_offsets < n_cols
59
+
60
+ dc_row = tl.load(dc + col_offsets, mask=mask, other=0)
61
+ a_row = tl.load(a + col_offsets, mask=mask, other=0).to(tl.float32)
62
+ b_row = tl.load(b + col_offsets, mask=mask, other=0)
63
+
64
+ # recomputation to save memory
65
+ sqrt_2_over_pi = 0.7978845608028654 # sqrt(2 / pi)
66
+ a_cubed = a_row * a_row * a_row
67
+ tanh_arg = sqrt_2_over_pi * (a_row + 0.044715 * a_cubed)
68
+ tanh_result = tanh(tanh_arg)
69
+ geglu_a = 0.5 * a_row * (1 + tanh_result)
70
+ geglu_a = geglu_a.to(dc_row.dtype).to(tl.float32)
71
+
72
+ db_row = dc_row.cast(tl.float32) * geglu_a
73
+
74
+ # Gradient w.r.t. a can be computed with:
75
+ # b * (0.5 * (1 + tanh(z)) + 0.5 * a * (1 - tanh(z)^2) * (sqrt(2/pi) * (1 + 3 * 0.044715 * a^2)))
76
+ # where z = sqrt(2/pi) * (a + 0.044715 * a^3)
77
+ term1 = 0.5 * (1 + tanh_result)
78
+ tanh_sq = tanh_result * tanh_result
79
+ term2 = 0.5 * a_row * (1 - tanh_sq) * (sqrt_2_over_pi * (1 + 3 * 0.044715 * a_row * a_row))
80
+ da_row = dc_row * b_row * (term1 + term2)
81
+
82
+ tl.store(a + col_offsets, da_row, mask=mask)
83
+ tl.store(b + col_offsets, db_row.to(dc_row.dtype), mask=mask)
84
+
85
+
86
+ def geglu_forward(a, b):
87
+ ori_shape = a.shape
88
+
89
+ n_cols = ori_shape[-1]
90
+ a = a.view(-1, n_cols)
91
+ b = b.view(-1, n_cols)
92
+ c = torch.empty_like(a)
93
+ n_rows = a.shape[0]
94
+
95
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
96
+
97
+ _geglu_tanh_forward_kernel[(n_rows,)](
98
+ a,
99
+ b,
100
+ c,
101
+ c.stride(-2),
102
+ n_cols=n_cols,
103
+ BLOCK_SIZE=BLOCK_SIZE,
104
+ num_warps=num_warps,
105
+ )
106
+ return a, b, c.view(*ori_shape)
107
+
108
+
109
+ def geglu_backward(a, b, dc):
110
+ ori_shape = dc.shape
111
+ n_cols = ori_shape[-1]
112
+ dc = dc.view(-1, n_cols)
113
+ n_rows = dc.shape[0]
114
+
115
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
116
+
117
+ _geglu_tanh_backward_kernel[(n_rows,)](
118
+ dc,
119
+ a,
120
+ b,
121
+ dc.stride(-2),
122
+ n_cols=n_cols,
123
+ BLOCK_SIZE=BLOCK_SIZE,
124
+ num_warps=num_warps,
125
+ )
126
+
127
+ return a.view(*ori_shape), b.view(*ori_shape)
128
+
129
+
130
+ class LigerGELUMulFunction(torch.autograd.Function):
131
+ @staticmethod
132
+ @ensure_contiguous
133
+ def forward(ctx, a, b):
134
+ a, b, c = geglu_forward(a, b)
135
+ ctx.save_for_backward(a, b)
136
+ return c
137
+
138
+ @staticmethod
139
+ @ensure_contiguous
140
+ def backward(ctx, dc):
141
+ a, b = ctx.saved_tensors
142
+ a, b = geglu_backward(a, b, dc)
143
+ return a, b
build/torch-rocm/group_norm.py ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import operator
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from .utils import compare_version
8
+ from .utils import ensure_contiguous
9
+ from .utils import infer_device
10
+ from .utils import is_npu_available
11
+
12
+ if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
13
+ try:
14
+ # typical import path with dispatch available
15
+ from triton.language.extra.libdevice import rsqrt
16
+ except ModuleNotFoundError:
17
+ # for working with NGC containers
18
+ from triton.language.extra.cuda.libdevice import rsqrt
19
+ else:
20
+ from triton.language.math import rsqrt
21
+
22
+ if infer_device() == "npu":
23
+ MAX_FUSED_SIZE = 16384 # 8192
24
+ else:
25
+ MAX_FUSED_SIZE = 65536
26
+
27
+
28
+ @triton.jit
29
+ def _group_norm_forward_kernel(
30
+ Y_ptr, # pointer to output, shape (n_rows, n_groups, hidden_size)
31
+ Y_row_stride, # stride of each row in output
32
+ Y_col_stride, # stride of each column in output
33
+ X_ptr, # pointer to input, shape (n_rows, n_groups, hidden_size)
34
+ X_row_stride, # stride of each row in input
35
+ X_col_stride, # stride of each column in input
36
+ Mean_ptr, # pointer to mean, shape (n_rows, n_groups)
37
+ Mean_row_stride, # stride of each row in mean
38
+ Mean_col_stride, # stride of each column in mean
39
+ RSTD_ptr, # pointer to rstd, shape (n_rows, n_groups)
40
+ RSTD_row_stride, # stride of each row in rstd
41
+ RSTD_col_stride, # stride of each column in rstd
42
+ W_ptr, # pointer to W
43
+ B_ptr, # pointer to B
44
+ hidden_size, # hidden size of X
45
+ channels_per_group, # the number of channels per group
46
+ eps,
47
+ BLOCK_SIZE: tl.constexpr,
48
+ ):
49
+ """
50
+ References:
51
+ https://nn.labml.ai/normalization/group_norm/index.html
52
+ """
53
+ batch_idx = tl.program_id(0)
54
+ group_idx = tl.program_id(1)
55
+
56
+ X_ptr += batch_idx * X_row_stride + group_idx * X_col_stride
57
+ Y_ptr += batch_idx * Y_row_stride + group_idx * Y_col_stride
58
+
59
+ block_range = tl.arange(0, BLOCK_SIZE)
60
+
61
+ # Compute mean and variance using the online algorithm
62
+ s = 0.0
63
+ squared_sum = 0.0
64
+ for i in tl.range(0, hidden_size, BLOCK_SIZE):
65
+ hidden_size_offsets = i + block_range
66
+ mask = hidden_size_offsets < hidden_size
67
+ X = tl.load(X_ptr + hidden_size_offsets, mask=mask, other=0.0)
68
+ s += tl.sum(X)
69
+ # X**2
70
+ squared_sum += tl.sum(X * X)
71
+
72
+ m = s / hidden_size
73
+
74
+ # variance = E[X**2] - E[X]**2
75
+ variance = (squared_sum / hidden_size) - (m * m)
76
+
77
+ # 1/std
78
+ rstd = rsqrt(variance + eps)
79
+
80
+ # Normalize — flat loop over full hidden_size (not per-channel)
81
+ # This avoids the nested channel × per_channel_hidden loop where
82
+ # BLOCK_SIZE >> hidden_size_per_channel causes massive padding waste.
83
+ hidden_size_per_channel = hidden_size // channels_per_group
84
+ for i in tl.range(0, hidden_size, BLOCK_SIZE):
85
+ hidden_size_offsets = i + block_range
86
+ mask = hidden_size_offsets < hidden_size
87
+ X = tl.load(X_ptr + hidden_size_offsets, mask=mask, other=m)
88
+ # Determine which channel each element belongs to, then load W/B
89
+ local_channel = hidden_size_offsets // hidden_size_per_channel
90
+ global_channel = group_idx * channels_per_group + local_channel
91
+ W = tl.load(W_ptr + global_channel, mask=mask)
92
+ B = tl.load(B_ptr + global_channel, mask=mask)
93
+ Y = (X - m) * rstd * W + B
94
+ tl.store(Y_ptr + hidden_size_offsets, Y, mask=mask)
95
+
96
+ tl.store(Mean_ptr + batch_idx * Mean_row_stride + group_idx * Mean_col_stride, m)
97
+ tl.store(RSTD_ptr + batch_idx * RSTD_row_stride + group_idx * RSTD_col_stride, rstd)
98
+
99
+
100
+ @triton.jit
101
+ def _group_norm_backward_kernel(
102
+ X_ptr, # pointer to input, shape (n_rows, n_channels, hidden_size)
103
+ X_row_stride, # stride of each row in input
104
+ X_col_stride, # stride of each column in input
105
+ W_ptr, # pointer to weights, shape (n_channels)
106
+ Mean_ptr, # pointer to mean, shape (n_rows, n_groups)
107
+ Mean_ptr_row_stride, # stride of each column in mean
108
+ Mean_ptr_col_stride, # stride of each column in mean
109
+ RSTD_ptr, # pointer to rstd, shape (n_rows, n_groups)
110
+ DX_ptr, # pointer to input grad, shape (n_rows, n_groups, hidden_size)
111
+ DW_ptr, # pointer to weights grad, shape (n_channels)
112
+ DB_ptr, # pointer to bias grad, shape (n_channels)
113
+ UPSTREAM_ptr, # pointer to output grad, shape (n_rows, n_channels, hidden_size)
114
+ hidden_size: tl.constexpr, # hidden size
115
+ channels_per_group: tl.constexpr, # number of groups in group norm
116
+ BLOCK_SIZE: tl.constexpr,
117
+ dtype: tl.constexpr,
118
+ ):
119
+ """
120
+ References:
121
+ https://nn.labml.ai/normalization/group_norm/index.html
122
+ https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
123
+
124
+ The backprop equations are the same for group_norm and layer_norm
125
+ the only difference here is that we load the Mean, Rstd corresponding to the
126
+ group we're computing gradients for and the mean and rstd are computed over n-channels
127
+ so the total number of elements we compute the mean over is num_channels_per_group * hidden_size
128
+
129
+ We also need to load the Weights corresponding to the current channel to compute the gradients.
130
+ """
131
+ batch_idx = tl.program_id(0)
132
+ group_idx = tl.program_id(1)
133
+
134
+ # Move the pointers to the correct batch
135
+ X_ptr += batch_idx * X_row_stride
136
+ DX_ptr += batch_idx * X_row_stride
137
+ UPSTREAM_ptr += batch_idx * X_row_stride
138
+
139
+ # Mean and rstd are the same shape so have the same strides
140
+ mean = tl.load(Mean_ptr + batch_idx * Mean_ptr_row_stride + group_idx * Mean_ptr_col_stride)
141
+ rstd = tl.load(RSTD_ptr + batch_idx * Mean_ptr_row_stride + group_idx * Mean_ptr_col_stride)
142
+
143
+ c1 = 0.0
144
+ c2 = 0.0
145
+ block_range = tl.arange(0, BLOCK_SIZE)
146
+
147
+ # We need to compute the sum terms of the backprop equations across all channels in the group
148
+ for channel_idx in range(group_idx * channels_per_group, (group_idx + 1) * channels_per_group):
149
+ dW = 0.0
150
+ dB = 0.0
151
+ # Move the pointers to the correct channel
152
+ W = tl.load(W_ptr + channel_idx)
153
+ for i in tl.range(0, hidden_size, BLOCK_SIZE):
154
+ hidden_size_offsets = i + block_range
155
+ mask = hidden_size_offsets < hidden_size
156
+ X = tl.load(
157
+ X_ptr + channel_idx * X_col_stride + hidden_size_offsets,
158
+ mask=mask,
159
+ other=0.0,
160
+ )
161
+ UPSTREAM_grad = tl.load(
162
+ UPSTREAM_ptr + channel_idx * X_col_stride + hidden_size_offsets,
163
+ mask=mask,
164
+ other=0.0,
165
+ )
166
+
167
+ x_hat = (X - mean) * rstd
168
+ dW += tl.sum(UPSTREAM_grad * x_hat)
169
+ dB += tl.sum(UPSTREAM_grad)
170
+
171
+ wdy = W * UPSTREAM_grad
172
+ c1 += tl.sum(x_hat * wdy)
173
+ c2 += tl.sum(wdy)
174
+
175
+ # Need to ensure additions to the same channel are atomic
176
+ tl.atomic_add(DW_ptr + channel_idx, dW.to(dtype))
177
+ tl.atomic_add(DB_ptr + channel_idx, dB.to(dtype))
178
+
179
+ N = hidden_size * channels_per_group
180
+ c1 = c1 / N
181
+ c2 = c2 / N
182
+
183
+ for channel_idx in tl.range(group_idx * channels_per_group, (group_idx + 1) * channels_per_group):
184
+ # Move the pointers to the correct channel
185
+ W = tl.load(W_ptr + channel_idx)
186
+ for i in range(0, hidden_size, BLOCK_SIZE):
187
+ hidden_size_offsets = i + block_range
188
+ mask = hidden_size_offsets < hidden_size
189
+ X = tl.load(
190
+ X_ptr + channel_idx * X_col_stride + hidden_size_offsets,
191
+ mask=mask,
192
+ other=0.0,
193
+ )
194
+ UPSTREAM_grad = tl.load(
195
+ UPSTREAM_ptr + channel_idx * X_col_stride + hidden_size_offsets,
196
+ mask=mask,
197
+ other=0.0,
198
+ )
199
+
200
+ x_hat = (X - mean) * rstd
201
+ wdy = W * UPSTREAM_grad
202
+ dx = (wdy - (x_hat * c1 + c2)) * rstd
203
+ tl.store(DX_ptr + channel_idx * X_col_stride + hidden_size_offsets, dx, mask=mask)
204
+
205
+
206
+ def group_norm_forward(X, num_channels, num_groups, W, B, eps):
207
+ shape = X.shape
208
+ batch_size = shape[0]
209
+ channels_per_group = num_channels // num_groups
210
+ # Reshape X so that the mean and std are computed across the groups
211
+ X = X.view(batch_size, num_groups, -1).contiguous()
212
+ hidden_size = X.shape[-1]
213
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(hidden_size))
214
+ Y = torch.empty((batch_size, num_groups, hidden_size), dtype=X.dtype, device=X.device)
215
+ Mean = torch.zeros((batch_size, num_groups), dtype=X.dtype, device=X.device)
216
+ RSTD = torch.zeros((batch_size, num_groups), dtype=X.dtype, device=X.device)
217
+
218
+ _group_norm_forward_kernel[(batch_size, num_groups)](
219
+ Y,
220
+ Y.stride(0),
221
+ Y.stride(1),
222
+ X,
223
+ X.stride(0),
224
+ X.stride(1),
225
+ Mean,
226
+ Mean.stride(0),
227
+ Mean.stride(1),
228
+ RSTD,
229
+ RSTD.stride(0),
230
+ RSTD.stride(1),
231
+ W,
232
+ B,
233
+ hidden_size,
234
+ channels_per_group,
235
+ eps,
236
+ BLOCK_SIZE=BLOCK_SIZE,
237
+ )
238
+ # Return tensors in the original shape
239
+ return Y.view(*shape), X.view(*shape), Mean, RSTD, BLOCK_SIZE
240
+
241
+
242
+ def group_norm_backward(dY, X, W, B, Mean, RSTD, num_channels, num_groups):
243
+ shape = dY.shape
244
+ batch_size = shape[0]
245
+ hidden_size = dY.shape[-1]
246
+ channels_per_group = num_channels // num_groups
247
+ dY = dY.view(batch_size, num_groups, -1)
248
+ DX = torch.empty(
249
+ (batch_size, num_groups, hidden_size * channels_per_group),
250
+ dtype=X.dtype,
251
+ device=X.device,
252
+ )
253
+ DW = torch.zeros((num_channels), dtype=W.dtype, device=W.device)
254
+ DB = torch.zeros((num_channels), dtype=B.dtype, device=B.device)
255
+ triton_dtype = tl.float32 if X.dtype == torch.float32 else tl.bfloat16
256
+
257
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(hidden_size))
258
+ _group_norm_backward_kernel[(batch_size, num_groups)](
259
+ X,
260
+ X.stride(0),
261
+ X.stride(1),
262
+ W,
263
+ Mean,
264
+ Mean.stride(0),
265
+ Mean.stride(1),
266
+ RSTD,
267
+ DX,
268
+ DW,
269
+ DB,
270
+ dY,
271
+ hidden_size,
272
+ channels_per_group,
273
+ BLOCK_SIZE=BLOCK_SIZE,
274
+ dtype=triton_dtype,
275
+ )
276
+
277
+ # Return tensors in the original shape
278
+ return DX.view(*shape), DW, DB
279
+
280
+
281
+ class LigerGroupNormFunction(torch.autograd.Function):
282
+ @staticmethod
283
+ @ensure_contiguous
284
+ def forward(
285
+ ctx,
286
+ X,
287
+ affine_scaling_weight,
288
+ affine_shifting_bias,
289
+ num_channels,
290
+ num_groups,
291
+ eps,
292
+ ):
293
+ Y, X, Mean, RSTD, BLOCK_SIZE = group_norm_forward(
294
+ X,
295
+ num_channels,
296
+ num_groups,
297
+ affine_scaling_weight,
298
+ affine_shifting_bias,
299
+ eps,
300
+ )
301
+ ctx.num_channels = num_channels
302
+ ctx.num_groups = num_groups
303
+ ctx.save_for_backward(X, affine_scaling_weight, affine_shifting_bias, Mean, RSTD)
304
+ return Y
305
+
306
+ @staticmethod
307
+ @ensure_contiguous
308
+ def backward(ctx, dY):
309
+ X, W, B, Mean, RSTD = ctx.saved_tensors
310
+ DX, DW, DB = group_norm_backward(dY, X, W, B, Mean, RSTD, ctx.num_channels, ctx.num_groups)
311
+ return DX, DW, DB, None, None, None
build/torch-rocm/jsd.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from .utils import ensure_contiguous
8
+ from .utils import infer_device
9
+
10
+
11
+ @triton.jit
12
+ def _jsd_kernel(
13
+ X_ptr, # input in logspace, X = log Q
14
+ X_stride,
15
+ Y_ptr, # ground truth in logspace, Y = log P
16
+ Y_stride,
17
+ loss_ptr,
18
+ loss_stride,
19
+ dX_ptr,
20
+ dX_stride,
21
+ label_ptr,
22
+ beta: tl.constexpr,
23
+ n_non_ignore: int,
24
+ ignore_index: tl.constexpr,
25
+ n_cols,
26
+ BLOCK_SIZE: tl.constexpr,
27
+ HAS_LABEL: tl.constexpr,
28
+ ):
29
+ # JSD(P || Q) = (KL(P || M) + KL(Q || M)) / 2, M = (1/2) * (P + Q) = (1/2) * (e ^ Y + e ^ X)
30
+ # = sum(P * log P + Q * log Q - 2 * M * log M) / 2
31
+ # = sum(e ^ Y * Y + e ^ X * X - 2 * M * log M) / 2
32
+ # grad_x_i = 0.5 * Q * (X - log_M)
33
+ pid = tl.program_id(0).to(tl.int64)
34
+ X_ptr += pid * X_stride
35
+ dX_ptr += pid * dX_stride
36
+ Y_ptr += pid * Y_stride
37
+ loss_ptr += pid * loss_stride
38
+ label_ptr += pid
39
+
40
+ if HAS_LABEL:
41
+ label = tl.load(label_ptr)
42
+ if label == ignore_index:
43
+ for i in range(0, n_cols, BLOCK_SIZE):
44
+ offsets = i + tl.arange(0, BLOCK_SIZE)
45
+ tl.store(dX_ptr + offsets, 0.0, mask=offsets < n_cols)
46
+ return
47
+
48
+ for i in range(0, n_cols, BLOCK_SIZE):
49
+ offsets = i + tl.arange(0, BLOCK_SIZE)
50
+ mask = offsets < n_cols
51
+ X = tl.load(X_ptr + offsets, mask=mask, other=float("-inf")).to(tl.float32)
52
+ Y = tl.load(Y_ptr + offsets, mask=mask, other=float("-inf")).to(tl.float32)
53
+
54
+ if beta == 0.0: # forward KL
55
+ Y_max = tl.max(Y, axis=0)
56
+ Y_shifted = Y - Y_max
57
+ Y_prob = tl.exp(Y_shifted) * tl.exp(Y_max) # Compensate for the shift
58
+ loss = Y_prob * (Y - X)
59
+ dX = -Y_prob
60
+ elif beta == 1.0: # reverse KL
61
+ X_max = tl.max(X, axis=0)
62
+ X_shifted = X - X_max
63
+ X_prob = tl.exp(X_shifted) * tl.exp(X_max) # Compensate for the shift
64
+ loss = X_prob * (X - Y)
65
+ dX = loss + X_prob
66
+ else:
67
+ max_val = tl.maximum(tl.max(X, axis=0), tl.max(Y, axis=0))
68
+ X_shifted = X - max_val
69
+ Y_shifted = Y - max_val
70
+
71
+ # Pre-compute exp(max_val) since it's used twice
72
+ exp_max = tl.exp(max_val)
73
+
74
+ # Compute exp terms with compensation
75
+ Q = tl.exp(X_shifted) * exp_max # = exp(X)
76
+ P = tl.exp(Y_shifted) * exp_max # = exp(Y)
77
+
78
+ # Pre-compute common terms
79
+ beta_P = beta * P
80
+ one_minus_beta_Q = (1 - beta) * Q
81
+ M = beta_P + one_minus_beta_Q
82
+ log_M = tl.log(M) # No need to compensate as M is already in original scale
83
+
84
+ loss = beta_P * Y + one_minus_beta_Q * X - M * log_M
85
+ dX = one_minus_beta_Q * (X - log_M)
86
+
87
+ # Pre-compute scaling factor
88
+ scale = 1.0 / n_non_ignore
89
+ loss = loss * scale
90
+ dX = dX * scale
91
+
92
+ tl.store(loss_ptr + offsets, loss, mask=mask)
93
+ tl.store(dX_ptr + offsets, dX, mask=mask)
94
+
95
+
96
+ MAX_FUSED_SIZE = 4096 if infer_device() == "xpu" else 65536
97
+
98
+
99
+ def jsd_forward(_input, target, shift_labels, beta, ignore_index, has_label):
100
+ BT, V = _input.shape
101
+ n_rows = BT
102
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
103
+ # non reduction loss
104
+ loss = torch.zeros(_input.shape, dtype=torch.float32, device=_input.device)
105
+ dX = torch.empty_like(_input)
106
+
107
+ if has_label:
108
+ n_non_ignore = (shift_labels != ignore_index).sum().item()
109
+ else:
110
+ n_non_ignore = BT
111
+
112
+ _jsd_kernel[(n_rows,)](
113
+ X_ptr=_input, # input in logspace, X = log Q
114
+ X_stride=_input.stride(-2),
115
+ Y_ptr=target, # ground truth in logspace, Y = log P
116
+ Y_stride=target.stride(-2),
117
+ loss_ptr=loss,
118
+ loss_stride=loss.stride(-2),
119
+ dX_ptr=dX,
120
+ dX_stride=dX.stride(-2),
121
+ label_ptr=(shift_labels if has_label else torch.empty(1, device=_input.device)), # dummy ptr if no label
122
+ beta=beta,
123
+ n_non_ignore=n_non_ignore,
124
+ ignore_index=ignore_index,
125
+ n_cols=V,
126
+ BLOCK_SIZE=BLOCK_SIZE,
127
+ HAS_LABEL=has_label,
128
+ )
129
+
130
+ loss = torch.sum(loss)
131
+ return loss.to(_input.dtype), dX
132
+
133
+
134
+ def jsd_backward(dX, grad_output):
135
+ # If jsd is the last layer, grad_output is 1.0. Skip the mul to save time
136
+ if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
137
+ return dX
138
+ else:
139
+ return grad_output * dX
140
+
141
+
142
+ class LigerJSDFunction(torch.autograd.Function):
143
+ r"""
144
+ This class implements the forward and backward pass for the generalized Jensen-Shannon Divergence.
145
+ .. math::
146
+ JSD(\beta)(P || Q)
147
+ = \beta * KLDiv(P || (\beta * P + (1 - \beta) * Q)) + (1 - \beta) * KLDiv(Q || (\beta * P + (1 - \beta) * Q))
148
+
149
+ .. note::
150
+ As all the other losses in PyTorch, this function expects the first argument,
151
+ :attr:`_input`, to be the predictions, the output of the student model, in log-space
152
+ and the second, :attr:`target`, to be the observations, the output of the teacher model, in log-space.
153
+ This differs from the standard mathematical notation :math:`JSD(P || Q)` where
154
+ :math:`P` denotes the teacher model and :math:`Q` denotes the student model.
155
+ """
156
+
157
+ @staticmethod
158
+ @ensure_contiguous
159
+ def forward(
160
+ ctx,
161
+ _input: torch.Tensor,
162
+ target: torch.Tensor,
163
+ shift_labels: Optional[torch.Tensor] = None,
164
+ beta: float = 0.5,
165
+ ignore_index: int = -100,
166
+ ) -> torch.Tensor:
167
+ """
168
+ Args:
169
+ _input (torch.Tensor): predict values with shape (BT, V) in logspace
170
+ target (torch.Tensor): ground truth values with shape (BT, V) in logspace
171
+ shift_labels (Optional[torch.LongTensor]): indicator of next predicted vocab with shape (BT) where each value is in [0, V-1].
172
+ beta (float): coefficient beta of generalized JSD in the interval [0, 1]. It implements forward/reverse KL when beta equals 0 and 1 respectively. Default: `0.5`
173
+ ignore_index (int): the index to ignore. Default: -100
174
+
175
+ Returns:
176
+ loss (torch.Tensor): generalized JSD
177
+ """
178
+ has_label = False
179
+ if shift_labels is not None:
180
+ assert shift_labels.shape == (_input.shape[0],), (
181
+ f"the shape of shift_labels must be (BT,). Got: {shift_labels.shape}"
182
+ )
183
+ shift_labels = shift_labels.contiguous()
184
+ has_label = True
185
+
186
+ loss, dX = jsd_forward(_input, target, shift_labels, beta, ignore_index, has_label)
187
+ ctx.save_for_backward(dX)
188
+ return loss
189
+
190
+ @staticmethod
191
+ @ensure_contiguous
192
+ def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
193
+ (dX,) = ctx.saved_tensors
194
+ dX = jsd_backward(dX, grad_output)
195
+ return (
196
+ dX,
197
+ None,
198
+ None,
199
+ None,
200
+ None,
201
+ )
build/torch-rocm/kl_div.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Literal
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from .utils import ensure_contiguous
8
+ from .utils import is_hip
9
+ from .utils import infer_device
10
+
11
+
12
+ def get_num_warps(BLOCK_SIZE):
13
+ num_warps = 4
14
+ if BLOCK_SIZE >= 32768:
15
+ num_warps = 32 if not is_hip() else 16
16
+ elif BLOCK_SIZE >= 8192:
17
+ num_warps = 16
18
+ elif BLOCK_SIZE >= 2048:
19
+ num_warps = 8
20
+
21
+ return num_warps
22
+
23
+
24
+ if infer_device() == "xpu":
25
+ MAX_FUSED_SIZE = 8192
26
+ elif infer_device() == "npu":
27
+ MAX_FUSED_SIZE = 8192
28
+ else:
29
+ MAX_FUSED_SIZE = 65536 // 4 # 65536 // 4 or 8 works the best
30
+
31
+ REDUCTION_LITERAL = Literal["none", "sum", "mean", "batchmean"]
32
+
33
+ _REDUCTION_MODE_NONE: tl.constexpr = tl.constexpr(0)
34
+ _REDUCTION_MODE_SUM: tl.constexpr = tl.constexpr(1)
35
+ _REDUCTION_MODE_MEAN: tl.constexpr = tl.constexpr(2)
36
+ _REDUCTION_MODE_BATCHMEAN: tl.constexpr = tl.constexpr(3)
37
+
38
+ _str_to_reduction_mode = {
39
+ "none": _REDUCTION_MODE_NONE.value,
40
+ "sum": _REDUCTION_MODE_SUM.value,
41
+ "mean": _REDUCTION_MODE_MEAN.value,
42
+ "batchmean": _REDUCTION_MODE_BATCHMEAN.value,
43
+ }
44
+
45
+
46
+ @triton.jit
47
+ def _kldiv_kernel_forward(
48
+ y_ptr, # [B, S], prediction ptr, the kernel expects the prediction in log-space
49
+ y_stride, # int, prediction stride
50
+ gt_ptr, # [B, S], ground truth ptr
51
+ gt_stride, # int, ground truth stride
52
+ loss_ptr, # [B] or [B, S] if reduction == _REDUCTION_MODE_NONE, output ptr
53
+ loss_stride, # int, output stride
54
+ n_cols, # int, number of columns in the input tensor
55
+ eps,
56
+ BLOCK_SIZE: tl.constexpr,
57
+ log_target: tl.constexpr = False,
58
+ reduction: tl.constexpr = _REDUCTION_MODE_BATCHMEAN,
59
+ ):
60
+ pid = tl.program_id(0).to(tl.int64)
61
+ y_ptr += pid * y_stride
62
+ gt_ptr += pid * gt_stride
63
+ loss_ptr += pid * loss_stride
64
+
65
+ base_offsets = tl.arange(0, BLOCK_SIZE)
66
+
67
+ loss_sum = 0.0
68
+ for i in range(0, n_cols, BLOCK_SIZE):
69
+ offsets = i + base_offsets
70
+ mask = offsets < n_cols
71
+ y = tl.load(y_ptr + offsets, mask=mask, other=0.0)
72
+ y_true = tl.load(gt_ptr + offsets, mask=mask, other=0.0)
73
+
74
+ # KL(y_true || y) = y_true * (log(y_true) - log(y))
75
+ # We compute KL(y_true || y) with y in the log-space
76
+ if not log_target:
77
+ loss = y_true * (tl.log(tl.maximum(y_true, eps)) - y)
78
+ else:
79
+ loss = tl.exp(y_true) * (y_true - y)
80
+
81
+ if reduction == _REDUCTION_MODE_NONE:
82
+ tl.store(loss_ptr + offsets, loss, mask=mask)
83
+ else:
84
+ loss_sum += tl.sum(loss, axis=0)
85
+
86
+ if reduction != _REDUCTION_MODE_NONE:
87
+ tl.store(loss_ptr, loss_sum)
88
+
89
+
90
+ @triton.jit
91
+ def _kldiv_kernel_backward(
92
+ target_ptr,
93
+ target_stride,
94
+ new_grads_ptr,
95
+ new_grads_stride,
96
+ n_cols,
97
+ BLOCK_SIZE: tl.constexpr,
98
+ log_target: tl.constexpr = False,
99
+ ):
100
+ pid = tl.program_id(0).to(tl.int64)
101
+
102
+ target_ptr += pid * target_stride
103
+ new_grads_ptr += pid * new_grads_stride
104
+
105
+ offsets = tl.arange(0, BLOCK_SIZE)
106
+ mask = offsets < n_cols
107
+
108
+ for i in range(0, n_cols, BLOCK_SIZE):
109
+ offsets = i + tl.arange(0, BLOCK_SIZE)
110
+ mask = offsets < n_cols
111
+
112
+ target = tl.load(target_ptr + offsets, mask=mask, other=0.0)
113
+
114
+ if not log_target:
115
+ res = target * -1
116
+ else:
117
+ res = -tl.exp(target)
118
+
119
+ tl.store(new_grads_ptr + offsets, res, mask=mask)
120
+
121
+
122
+ def kldiv_forward_triton(y_pred, y_true, log_target, reduction, eps): # [BT, V]
123
+ BT, V = y_pred.shape
124
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
125
+ num_warps = 32 if infer_device() == "xpu" else get_num_warps(BLOCK_SIZE)
126
+
127
+ grid = (BT,)
128
+ reduction = _str_to_reduction_mode[reduction]
129
+
130
+ out_size = (BT, V) if reduction == _REDUCTION_MODE_NONE.value else (BT,)
131
+ output_tensor = torch.zeros(out_size, device=y_pred.device, dtype=torch.float32)
132
+
133
+ _kldiv_kernel_forward[grid](
134
+ y_pred,
135
+ y_pred.stride(0),
136
+ y_true,
137
+ y_true.stride(0),
138
+ output_tensor,
139
+ output_tensor.stride(0),
140
+ V,
141
+ eps=eps,
142
+ BLOCK_SIZE=BLOCK_SIZE,
143
+ num_warps=num_warps,
144
+ log_target=log_target,
145
+ reduction=reduction,
146
+ )
147
+
148
+ # calculated according to the reduction mode same as in Pytorch. In the later versions, `mean` will be changed to the same behavior as `batchmean`
149
+ # https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html
150
+ # https://github.com/pytorch/pytorch/blob/d7b57c4d63edb42e1deeeba9497fcb5f1f748ff2/torch/nn/functional.py#L3372
151
+ if reduction == _REDUCTION_MODE_BATCHMEAN.value:
152
+ return output_tensor.sum() / BT
153
+ elif reduction == _REDUCTION_MODE_SUM.value:
154
+ return output_tensor.sum(dim=0)
155
+ elif reduction == _REDUCTION_MODE_MEAN.value:
156
+ return output_tensor.sum() / (BT * V)
157
+ else:
158
+ return output_tensor
159
+
160
+
161
+ def kldiv_backward_triton(target, grad_output, new_grads, log_target):
162
+ BT, V = target.shape
163
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
164
+ num_warps = 32 if infer_device() == "xpu" else get_num_warps(BLOCK_SIZE)
165
+
166
+ grid = (BT,)
167
+
168
+ # We store the gradients in-place in the input tensor
169
+ _kldiv_kernel_backward[grid](
170
+ target,
171
+ target.stride(0),
172
+ new_grads,
173
+ new_grads.stride(0),
174
+ V,
175
+ BLOCK_SIZE=BLOCK_SIZE,
176
+ num_warps=num_warps,
177
+ log_target=log_target,
178
+ )
179
+
180
+ # If cross entropy is the last layer, grad_output is 1.0. Skip the mul then.
181
+ if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
182
+ return new_grads
183
+
184
+ return new_grads * grad_output
185
+
186
+
187
+ class LigerKLDivLossFunction(torch.autograd.Function):
188
+ """
189
+ Class implementing the forward and backward pass for the KL Divergence Loss using Triton, as defined by the following formula:
190
+ ```python
191
+ if log_target:
192
+ loss = target.exp() * (target - input)
193
+ else:
194
+ loss = target * (target.log() - input)
195
+ ```,
196
+ then the loss is reduced according to the `reduction` parameter.
197
+ as defined in the PyTorch documentation: https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html
198
+ """
199
+
200
+ @staticmethod
201
+ @ensure_contiguous
202
+ def forward(
203
+ ctx,
204
+ y_pred: torch.Tensor,
205
+ y_true: torch.Tensor,
206
+ reduction: REDUCTION_LITERAL = "batchmean",
207
+ log_target: bool = False,
208
+ eps: float = 1e-10,
209
+ ) -> torch.Tensor:
210
+ """A forward pass for the KL Divergence Loss.
211
+
212
+ Args:
213
+ ctx: Torch autograd context
214
+ y_pred (torch.Tensor): A tensor of shape (BT, V) containing the predicted values, expected to be log-probabilities.
215
+ y_true (torch.Tensor): A tensor of shape (BT, V) containing the target values, expected to be either probabilities or log-probabilities, depending on the value of `log_target`.
216
+ reduction (REDUCTION_LITERAL, optional): Reduction to be used. Defaults to "batchmean".
217
+ log_target (bool, optional): If set to true, expects the ground truth to already be log-probabilities. Defaults to False.
218
+ eps: (float, optional): A small value to avoid division by zero. Defaults to 1e-10.
219
+
220
+ Returns:
221
+ torch.Tensor: The computed KL Divergence Loss, with shape (BT, V) if `reduction` is "none", else a scalar.
222
+ """
223
+ ctx.save_for_backward(y_true)
224
+ ctx.reduction = reduction
225
+ ctx.log_target = log_target
226
+ return kldiv_forward_triton(y_pred, y_true, log_target=log_target, reduction=reduction, eps=eps)
227
+
228
+ @staticmethod
229
+ @ensure_contiguous
230
+ def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
231
+ """A backward pass for the KL Divergence Loss.
232
+
233
+ Args:
234
+ ctx: Torch autograd context
235
+ grad_output (torch.Tensor): The gradient of the loss with respect to the output.
236
+
237
+ Returns:
238
+ tuple[torch.Tensor, None, None, None, None]: The gradient of the loss with respect to the inputs and None for the other arguments of the forward method.
239
+ """
240
+ (y_true,) = ctx.saved_tensors
241
+
242
+ new_grads = torch.empty_like(y_true)
243
+
244
+ derivative = kldiv_backward_triton(y_true, grad_output, new_grads, ctx.log_target)
245
+
246
+ if ctx.reduction == "batchmean":
247
+ derivative = derivative / y_true.shape[0]
248
+ elif ctx.reduction == "sum" or ctx.reduction == "none":
249
+ pass
250
+ elif ctx.reduction == "mean":
251
+ derivative = derivative / (y_true.shape[0] * y_true.shape[1])
252
+
253
+ return (
254
+ derivative,
255
+ None,
256
+ None,
257
+ None,
258
+ None,
259
+ )
build/torch-rocm/layer_norm.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import operator
3
+
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+
8
+ from .utils import calculate_settings
9
+ from .utils import compare_version
10
+ from .utils import ensure_contiguous
11
+ from .utils import get_npu_core_count
12
+ from .utils import set_large_grf_mode
13
+ from .utils import is_npu_available
14
+
15
+ if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
16
+ try:
17
+ # typical import path with dispatch available
18
+ from triton.language.extra.libdevice import rsqrt
19
+ except ModuleNotFoundError:
20
+ # for working with NGC containers
21
+ from triton.language.extra.cuda.libdevice import rsqrt
22
+ else:
23
+ from triton.language.math import rsqrt
24
+
25
+
26
+ @triton.jit
27
+ def _layer_norm_forward_kernel(
28
+ Y_ptr, # pointer to output, shape (n_rows, n_cols)
29
+ Y_row_stride, # stride of each row in output
30
+ X_ptr, # pointer to input, shape (n_rows, n_cols)
31
+ X_row_stride, # stride of each row in input
32
+ W_ptr, # pointer to weights, shape (n_cols,)
33
+ W_row_stride, # stride of each row in weights
34
+ B_ptr, # pointer to bias, shape (n_cols,)
35
+ B_row_stride, # stride of each row in bias
36
+ Mean_ptr, # pointer to mean, shape (n_rows,)
37
+ Mean_row_stride, # stride of each row in mean
38
+ RSTD_ptr, # pointer to rstd, shape (n_rows,)
39
+ RSTD_row_stride, # stride of each row in rstd
40
+ n_cols,
41
+ eps,
42
+ BLOCK_SIZE: tl.constexpr,
43
+ ):
44
+ """
45
+ References:
46
+ https://arxiv.org/abs/1607.06450
47
+ https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
48
+ """
49
+ row_idx = tl.program_id(0).to(tl.int64)
50
+ col_offsets = tl.arange(0, BLOCK_SIZE)
51
+ mask = col_offsets < n_cols
52
+
53
+ # Pre-load weights and bias in fp32 to avoid repeated conversions
54
+ W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0.0)
55
+ B_row = tl.load(B_ptr + col_offsets, mask=mask, other=0.0)
56
+ W_f32 = W_row.to(tl.float32)
57
+ B_f32 = B_row.to(tl.float32)
58
+
59
+ # Calculate pointers for this row
60
+ row_X_ptr = X_ptr + row_idx * X_row_stride
61
+ row_Y_ptr = Y_ptr + row_idx * Y_row_stride
62
+ row_Mean_ptr = Mean_ptr + row_idx * Mean_row_stride
63
+ row_RSTD_ptr = RSTD_ptr + row_idx * RSTD_row_stride
64
+
65
+ # Load input data and convert to fp32 for numerical stability
66
+ X_row = tl.load(row_X_ptr + col_offsets, mask=mask, other=0.0)
67
+ X_f32 = X_row.to(tl.float32)
68
+
69
+ # Compute statistics in fp32 for numerical stability
70
+ mean = tl.sum(X_f32, axis=0) / n_cols
71
+ X_centered = X_f32 - mean
72
+ # Apply mask to variance calculation to exclude contributions from masked elements
73
+ X_centered_masked = tl.where(mask, X_centered, 0.0)
74
+ var = tl.sum(X_centered_masked * X_centered_masked, axis=0) / n_cols
75
+ rstd = rsqrt(var + eps)
76
+
77
+ # Store statistics (convert back to original dtype only once)
78
+ tl.store(row_Mean_ptr, mean.to(X_row.dtype))
79
+ tl.store(row_RSTD_ptr, rstd.to(X_row.dtype))
80
+
81
+ # Fused normalization and affine transformation
82
+ # Y = (X - mean) * rstd * W + B = X_centered * rstd * W + B
83
+ Y_f32 = X_centered * rstd * W_f32 + B_f32
84
+
85
+ # Store output (single conversion back to original dtype)
86
+ tl.store(row_Y_ptr + col_offsets, Y_f32.to(X_row.dtype), mask=mask)
87
+
88
+
89
+ @triton.jit
90
+ def _layer_norm_backward_kernel(
91
+ X_ptr, # pointer to input, shape (n_rows, n_cols)
92
+ stride_x, # stride of each row in input
93
+ W_ptr, # pointer to weights, shape (n_cols,)
94
+ Mean_ptr, # pointer to mean, shape (n_rows,)
95
+ stride_mean, # stride of each row in mean
96
+ RSTD_ptr, # pointer to rstd, shape (n_rows,)
97
+ stride_rstd, # stride of each row in rstd
98
+ DX_ptr, # pointer to input grad, shape (n_rows, n_cols)
99
+ stride_dx, # stride of each row in input grad
100
+ DW_ptr, # pointer to weights grad, shape (n_cols,)
101
+ stride_dw, # stride of each row in weights grad
102
+ DB_ptr, # pointer to bias grad, shape (n_cols,)
103
+ stride_db, # stride of each row in bias grad
104
+ DY_ptr, # pointer to output grad, shape (n_rows, n_cols)
105
+ stride_dy, # stride of each row in output grad
106
+ n_rows,
107
+ n_cols,
108
+ rows_per_program: tl.constexpr,
109
+ BLOCK_SIZE: tl.constexpr,
110
+ ):
111
+ """
112
+ References:
113
+ https://arxiv.org/abs/1607.06450
114
+ https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
115
+ """
116
+ row_block_id = tl.program_id(0).to(tl.int64)
117
+ row_start = row_block_id * rows_per_program
118
+ row_end = min((row_block_id + 1) * rows_per_program, n_rows)
119
+ cols = tl.arange(0, BLOCK_SIZE)
120
+ mask = cols < n_cols
121
+
122
+ dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
123
+ db_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
124
+
125
+ # Pre-load weights once (same optimization as forward pass)
126
+ w = tl.load(W_ptr + cols, mask=mask, other=0.0)
127
+ w_f32 = w.to(tl.float32)
128
+
129
+ for row_idx in range(row_start, row_end):
130
+ # Calculate pointers for this specific row
131
+ row_X_ptr = X_ptr + row_idx * stride_x
132
+ row_DX_ptr = DX_ptr + row_idx * stride_dx
133
+ row_DY_ptr = DY_ptr + row_idx * stride_dy
134
+ row_Mean_ptr = Mean_ptr + row_idx * stride_mean
135
+ row_RSTD_ptr = RSTD_ptr + row_idx * stride_rstd
136
+
137
+ # Load data for this row
138
+ x = tl.load(row_X_ptr + cols, mask=mask, other=0.0)
139
+ dy = tl.load(row_DY_ptr + cols, mask=mask, other=0.0)
140
+ mean = tl.load(row_Mean_ptr)
141
+ rstd = tl.load(row_RSTD_ptr)
142
+
143
+ # Convert to fp32 for numerical stability
144
+ x_f32 = x.to(tl.float32)
145
+ dy_f32 = dy.to(tl.float32)
146
+ mean_f32 = mean.to(tl.float32)
147
+ rstd_f32 = rstd.to(tl.float32)
148
+
149
+ # Compute backward pass for this row
150
+ x_hat = (x_f32 - mean_f32) * rstd_f32
151
+ wdy = w_f32 * dy_f32
152
+ c1 = tl.sum(x_hat * wdy, axis=0) / n_cols
153
+ c2 = tl.sum(wdy, axis=0) / n_cols
154
+ dx = (wdy - (x_hat * c1 + c2)) * rstd_f32
155
+
156
+ # Store input gradient
157
+ tl.store(row_DX_ptr + cols, dx, mask=mask)
158
+
159
+ # Accumulate weight and bias gradients for this thread block's assigned rows
160
+ dw = dy_f32 * x_hat
161
+ db = dy_f32
162
+ dW_row += dw
163
+ db_row += db
164
+
165
+ tl.store(DW_ptr + row_block_id * stride_dw + cols, dW_row, mask=mask)
166
+ tl.store(DB_ptr + row_block_id * stride_db + cols, db_row, mask=mask)
167
+
168
+
169
+ def layer_norm_forward(X, W, B, eps):
170
+ """
171
+ Args:
172
+ X: Input tensor of shape (..., hidden_size)
173
+ W: Weight tensor of shape (hidden_size,)
174
+ B: Bias tensor of shape (hidden_size,)
175
+ eps: Small constant for numerical stability
176
+
177
+ Returns:
178
+ Tuple of (output, input, mean, rstd, block_size, num_warps)
179
+ """
180
+ shape = X.shape
181
+ dim = shape[-1]
182
+ X = X.view(-1, dim)
183
+ n_rows, n_cols = X.shape
184
+
185
+ # Calculate optimal block size and warp configuration
186
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
187
+
188
+ # Allocate output tensors
189
+ Y = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
190
+ Mean = torch.empty(n_rows, dtype=X.dtype, device=X.device)
191
+ RSTD = torch.empty(n_rows, dtype=X.dtype, device=X.device)
192
+
193
+ # Validate input dimensions
194
+ if X.shape[1] != W.shape[0]:
195
+ raise ValueError(
196
+ f"Incompatible dimensions: input feature size (X.shape[1]={X.shape[1]}) "
197
+ f"must match weight size (W.shape[0]={W.shape[0]})"
198
+ )
199
+
200
+ # XPU-specific optimization
201
+ kernel_args = {}
202
+ if X.device.type == "xpu":
203
+ set_large_grf_mode(kernel_args)
204
+
205
+ # Launch kernel with one thread block per row for optimal performance
206
+ grid = (n_rows,)
207
+ _layer_norm_forward_kernel[grid](
208
+ Y,
209
+ Y.stride(0),
210
+ X,
211
+ X.stride(0),
212
+ W,
213
+ W.stride(0),
214
+ B,
215
+ B.stride(0),
216
+ Mean,
217
+ Mean.stride(0),
218
+ RSTD,
219
+ RSTD.stride(0),
220
+ n_cols,
221
+ eps,
222
+ BLOCK_SIZE=BLOCK_SIZE,
223
+ num_warps=num_warps,
224
+ **kernel_args,
225
+ )
226
+
227
+ return Y.view(*shape), X, Mean, RSTD, BLOCK_SIZE, num_warps
228
+
229
+
230
+ def layer_norm_backward(dY, X, W, B, Mean, RSTD):
231
+ """
232
+ Args:
233
+ dY: Gradient of output
234
+ X: Input tensor
235
+ W: Weight tensor
236
+ B: Bias tensor
237
+ Mean: Pre-computed mean
238
+ RSTD: Pre-computed reciprocal standard deviation
239
+
240
+ Returns:
241
+ Tuple of (input_grad, weight_grad, bias_grad)
242
+ """
243
+ shape = dY.shape
244
+ dim = shape[-1]
245
+ dY = dY.view(-1, dim)
246
+ n_rows, n_cols = dY.shape
247
+
248
+ sm_count = 1
249
+ if X.device.type == "cuda":
250
+ sm_count = torch.cuda.get_device_properties(X.device).multi_processor_count
251
+ elif X.device.type == "xpu":
252
+ sm_count = torch.xpu.get_device_properties(X.device).gpu_eu_count
253
+ elif X.device.type == "npu":
254
+ sm_count = get_npu_core_count()
255
+
256
+ # fp32 for numerical stability especially.
257
+ _DW = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
258
+ _DB = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
259
+
260
+ # Calculate optimal block size and warp configuration
261
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
262
+ if n_cols > BLOCK_SIZE:
263
+ raise RuntimeError(f"Feature dimension {n_cols} exceeds maximum supported size of {BLOCK_SIZE}.")
264
+ rows_per_program = math.ceil(n_rows / sm_count)
265
+ grid = (sm_count,)
266
+
267
+ # Allocate gradient tensors
268
+ DX = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
269
+
270
+ kernel_args = {"num_warps": num_warps}
271
+ # XPU-specific optimization
272
+ if X.device.type == "xpu":
273
+ kernel_args.update({"num_warps": 32, "num_stages": 4})
274
+ set_large_grf_mode(kernel_args)
275
+
276
+ # Launch kernel with one thread block per row for optimal performance
277
+ _layer_norm_backward_kernel[grid](
278
+ X,
279
+ X.stride(0),
280
+ W,
281
+ Mean,
282
+ Mean.stride(0),
283
+ RSTD,
284
+ RSTD.stride(0),
285
+ DX,
286
+ DX.stride(0),
287
+ _DW,
288
+ _DW.stride(0),
289
+ _DB,
290
+ _DB.stride(0),
291
+ dY,
292
+ dY.stride(0),
293
+ n_rows,
294
+ n_cols,
295
+ rows_per_program=rows_per_program,
296
+ BLOCK_SIZE=BLOCK_SIZE,
297
+ **kernel_args,
298
+ )
299
+
300
+ DX = DX.view(*shape)
301
+ DW = _DW.sum(dim=0).to(W.dtype)
302
+ DB = _DB.sum(dim=0).to(B.dtype)
303
+
304
+ return DX, DW, DB
305
+
306
+
307
+ class LigerLayerNormFunction(torch.autograd.Function):
308
+ @staticmethod
309
+ @ensure_contiguous
310
+ def forward(ctx, X, W, B, eps):
311
+ Y, X, Mean, RSTD, BLOCK_SIZE, num_warps = layer_norm_forward(X, W, B, eps)
312
+ ctx.save_for_backward(X, W, B, Mean, RSTD)
313
+ return Y
314
+
315
+ @staticmethod
316
+ @ensure_contiguous
317
+ def backward(ctx, dY):
318
+ X, W, B, Mean, RSTD = ctx.saved_tensors
319
+ DX, DW, DB = layer_norm_backward(dY, X, W, B, Mean, RSTD)
320
+ return DX, DW, DB, None
build/torch-rocm/layers.py ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ from dataclasses import dataclass
3
+ from typing import Optional
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+ from .fused_linear_cross_entropy import LigerFusedLinearCrossEntropyFunction
9
+ from .geglu import LigerGELUMulFunction
10
+ from .rms_norm import LigerRMSNormFunction
11
+ from .rope import LigerRopeFunction
12
+ from .swiglu import LigerSiLUMulFunction
13
+ from .tiled_mlp import apply_tiled_mlp
14
+
15
+
16
+ # NOTE: Not compile-friendly --> large deviations to the original implementation under compile
17
+ class LigerRMSNorm(nn.Module):
18
+ weight: nn.Parameter
19
+ variance_epsilon: float
20
+
21
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
22
+ return LigerRMSNormFunction.apply(
23
+ hidden_states,
24
+ self.weight,
25
+ self.variance_epsilon,
26
+ 0,
27
+ "llama",
28
+ True,
29
+ None,
30
+ )
31
+
32
+ def extra_repr(self):
33
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
34
+
35
+
36
+ class LigerLinear(nn.Module):
37
+ weight: nn.Parameter
38
+ bias: nn.Parameter | None
39
+
40
+ can_torch_compile = True
41
+
42
+ def forward(self, input: torch.Tensor) -> torch.Tensor:
43
+ def _forward(module, x):
44
+ return nn.functional.linear(x, module.weight, module.bias)
45
+
46
+ compute_params = [p for p in self.parameters() if p.requires_grad]
47
+
48
+ return apply_tiled_mlp(
49
+ fn=_forward,
50
+ mlp_module=self,
51
+ x=input,
52
+ num_shards=None,
53
+ compute_params=compute_params,
54
+ )
55
+
56
+
57
+ class LigerSwiGLUMLP(nn.Module):
58
+ gate_proj: nn.Linear
59
+ up_proj: nn.Linear
60
+ down_proj: nn.Linear
61
+
62
+ can_torch_compile = True
63
+
64
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
65
+ return self.down_proj(LigerSiLUMulFunction.apply(self.gate_proj(x), self.up_proj(x)))
66
+
67
+
68
+ class LigerGEGLUMLP(nn.Module):
69
+ gate_proj: nn.Linear
70
+ up_proj: nn.Linear
71
+ down_proj: nn.Linear
72
+
73
+ can_torch_compile = True
74
+
75
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
76
+ return self.down_proj(LigerGELUMulFunction.apply(self.gate_proj(x), self.up_proj(x)))
77
+
78
+
79
+ class LigerTiledSwiGLUMLP(nn.Module):
80
+ gate_proj: nn.Linear
81
+ up_proj: nn.Linear
82
+ down_proj: nn.Linear
83
+
84
+ can_torch_compile = True
85
+
86
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
87
+ def _mlp_forward(module, x):
88
+ """Internal MLP forward function for tiled computation."""
89
+ gate = module.gate_proj(x)
90
+ up = module.up_proj(x)
91
+ return module.down_proj(LigerSiLUMulFunction.apply(gate, up))
92
+
93
+ compute_params = [p for p in self.parameters() if p.requires_grad]
94
+
95
+ return apply_tiled_mlp(
96
+ fn=_mlp_forward,
97
+ mlp_module=self,
98
+ x=x,
99
+ num_shards=None,
100
+ compute_params=compute_params,
101
+ )
102
+
103
+
104
+ class LigerTiledGEGLUMLP(nn.Module):
105
+ gate_proj: nn.Linear
106
+ up_proj: nn.Linear
107
+ down_proj: nn.Linear
108
+
109
+ can_torch_compile = True
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ def _mlp_forward(module, x):
113
+ """Internal MLP forward function for tiled computation."""
114
+ gate = module.gate_proj(x)
115
+ up = module.up_proj(x)
116
+ return module.down_proj(LigerGELUMulFunction.apply(gate, up))
117
+
118
+ compute_params = [p for p in self.parameters() if p.requires_grad]
119
+
120
+ return apply_tiled_mlp(
121
+ fn=_mlp_forward,
122
+ mlp_module=self,
123
+ x=x,
124
+ num_shards=None,
125
+ compute_params=compute_params,
126
+ )
127
+
128
+
129
+ def liger_rotary_pos_emb(
130
+ q: torch.Tensor,
131
+ k: torch.Tensor,
132
+ cos: torch.Tensor,
133
+ sin: torch.Tensor,
134
+ unsqueeze_dim: int = 1,
135
+ ) -> tuple[torch.Tensor, torch.Tensor]:
136
+ """Apply standard rotary positional embedding to ``q`` and ``k``."""
137
+ return LigerRopeFunction.apply(q, k, cos, sin, None, unsqueeze_dim)
138
+
139
+
140
+ @dataclass
141
+ class CrossEntropyOutput:
142
+ loss: torch.Tensor
143
+ z_loss: Optional[torch.Tensor] = None
144
+ token_accuracy: Optional[torch.Tensor] = None
145
+ predicted_tokens: Optional[torch.Tensor] = None
146
+
147
+
148
+ def liger_fused_linear_cross_entropy(
149
+ input: torch.Tensor,
150
+ weight: torch.Tensor,
151
+ target: torch.Tensor,
152
+ bias: Optional[torch.Tensor] = None,
153
+ ce_weight: Optional[torch.Tensor] = None,
154
+ ignore_index: int = -100,
155
+ lse_square_scale: float = 0.0,
156
+ label_smoothing: float = 0.0,
157
+ reduction: str = "mean",
158
+ softcap: Optional[float] = None,
159
+ return_z_loss: bool = False,
160
+ accum_dtype: Optional[torch.dtype] = None,
161
+ use_token_scaling: bool = False,
162
+ return_token_accuracy: bool = False,
163
+ return_predicted_tokens: bool = False,
164
+ ):
165
+ loss, z_loss, token_accuracy, predicted_tokens = LigerFusedLinearCrossEntropyFunction.apply(
166
+ input,
167
+ weight,
168
+ target,
169
+ bias,
170
+ ce_weight,
171
+ ignore_index,
172
+ lse_square_scale,
173
+ label_smoothing,
174
+ reduction,
175
+ softcap,
176
+ return_z_loss,
177
+ accum_dtype,
178
+ use_token_scaling,
179
+ return_token_accuracy,
180
+ return_predicted_tokens,
181
+ )
182
+ if not return_z_loss and not return_token_accuracy and not return_predicted_tokens:
183
+ return loss
184
+ return CrossEntropyOutput(
185
+ loss=loss,
186
+ z_loss=z_loss,
187
+ token_accuracy=token_accuracy,
188
+ predicted_tokens=predicted_tokens,
189
+ )
190
+
191
+
192
+ # NOTE: We have this intentional graph break as we encounter issues such as IMAs and Cublas errors.
193
+ # We know that this is an optimized kernel already so there is less ways to
194
+ # fuse it either way; we rely on torch compile to go through the base model to optimize.
195
+ @torch.compiler.disable
196
+ def LigerForCausalLMLoss(
197
+ logits: None, # to match transformers signature
198
+ labels: torch.Tensor,
199
+ vocab_size: int, # to match transformers signature
200
+ num_items_in_batch: Optional[int] = None,
201
+ ignore_index: int = -100,
202
+ shift_labels: Optional[torch.Tensor] = None,
203
+ hidden_states: torch.Tensor | None = None,
204
+ lm_head_weight: torch.Tensor | None = None,
205
+ lm_head_bias: torch.Tensor | None = None,
206
+ **kwargs,
207
+ ):
208
+ # To match signature we hide these behind the kwargs but we expect a few kwargs to exist
209
+ hidden_size = kwargs.pop("hidden_size", None)
210
+ final_logit_softcapping = kwargs.pop("final_logit_softcapping", None)
211
+
212
+ if hidden_size is None or hidden_states is None or lm_head_weight is None:
213
+ raise ValueError(
214
+ f"`LigerForCausalLMLoss` requires the LLM's weight (found `{lm_head_weight is not None}`),"
215
+ f"the last hidden state (found `{hidden_states is not None}`), and the `hidden_size`"
216
+ f"(found `{hidden_size is not None}`). Please make sure to pass the necessary kwargs."
217
+ )
218
+
219
+ applicable_params = inspect.signature(liger_fused_linear_cross_entropy).parameters
220
+ kwargs = {k: v for k, v in kwargs.items() if k in applicable_params}
221
+
222
+ if shift_labels is None:
223
+ labels = nn.functional.pad(labels, (0, 1), value=ignore_index)
224
+ shift_labels = labels[..., 1:].contiguous()
225
+
226
+ hidden_states = hidden_states.view(-1, hidden_size)
227
+ shift_labels = shift_labels.view(-1).to(hidden_states.device)
228
+
229
+ reduction = "sum" if num_items_in_batch is not None else "mean"
230
+ loss = liger_fused_linear_cross_entropy(
231
+ hidden_states,
232
+ lm_head_weight,
233
+ shift_labels,
234
+ bias=lm_head_bias,
235
+ reduction=reduction,
236
+ ignore_index=ignore_index,
237
+ softcap=final_logit_softcapping,
238
+ return_token_accuracy=False,
239
+ return_predicted_tokens=False,
240
+ **kwargs,
241
+ )
242
+
243
+ if reduction == "sum":
244
+ loss = loss / num_items_in_batch
245
+
246
+ return loss
247
+
248
+
249
+ # Add torch compile support for functions - in this case it's to allow this to be used
250
+ # but the function itself will not be compiled (see the note at the function)
251
+ liger_rotary_pos_emb.can_torch_compile = True
252
+ LigerForCausalLMLoss.can_torch_compile = True
253
+
254
+
255
+ class liger_rotary_pos_emb_layer(nn.Module):
256
+ can_torch_compile = True
257
+
258
+ def forward(
259
+ self,
260
+ q: torch.Tensor,
261
+ k: torch.Tensor,
262
+ cos: torch.Tensor,
263
+ sin: torch.Tensor,
264
+ unsqueeze_dim: int = 1,
265
+ ) -> tuple[torch.Tensor, torch.Tensor]:
266
+ """Apply standard rotary positional embedding to ``q`` and ``k``."""
267
+ return LigerRopeFunction.apply(q, k, cos, sin, None, unsqueeze_dim)
268
+
269
+
270
+ class LigerForCausalLMLossLayer(nn.Module):
271
+ can_torch_compile = True
272
+
273
+ # NOTE: We have this intentional graph break as we encounter issues such as IMAs and Cublas errors.
274
+ # We know that this is an optimized kernel already so there is less ways to
275
+ # fuse it either way; we rely on torch compile to go through the base model to optimize.
276
+ @torch.compiler.disable
277
+ def forward(
278
+ self,
279
+ logits: None, # to match transformers signature
280
+ labels: torch.Tensor,
281
+ vocab_size: int, # to match transformers signature
282
+ num_items_in_batch: Optional[int] = None,
283
+ ignore_index: int = -100,
284
+ shift_labels: Optional[torch.Tensor] = None,
285
+ hidden_states: torch.Tensor | None = None,
286
+ lm_head_weight: torch.Tensor | None = None,
287
+ lm_head_bias: torch.Tensor | None = None,
288
+ **kwargs,
289
+ ):
290
+ # To match signature we hide these behind the kwargs but we expect a few kwargs to exist
291
+ hidden_size = kwargs.pop("hidden_size", None)
292
+ final_logit_softcapping = kwargs.pop("final_logit_softcapping", None)
293
+
294
+ if hidden_size is None or hidden_states is None or lm_head_weight is None:
295
+ raise ValueError(
296
+ f"`LigerForCausalLMLoss` requires the LLM's weight (found `{lm_head_weight is not None}`),"
297
+ f"the last hidden state (found `{hidden_states is not None}`), and the `hidden_size`"
298
+ f"(found `{hidden_size is not None}`). Please make sure to pass the necessary kwargs."
299
+ )
300
+
301
+ applicable_params = inspect.signature(liger_fused_linear_cross_entropy).parameters
302
+ kwargs = {k: v for k, v in kwargs.items() if k in applicable_params}
303
+
304
+ if shift_labels is None:
305
+ labels = nn.functional.pad(labels, (0, 1), value=ignore_index)
306
+ shift_labels = labels[..., 1:].contiguous()
307
+
308
+ hidden_states = hidden_states.view(-1, hidden_size)
309
+ shift_labels = shift_labels.view(-1).to(hidden_states.device)
310
+
311
+ reduction = "sum" if num_items_in_batch is not None else "mean"
312
+ loss = liger_fused_linear_cross_entropy(
313
+ hidden_states,
314
+ lm_head_weight,
315
+ shift_labels,
316
+ bias=lm_head_bias,
317
+ reduction=reduction,
318
+ ignore_index=ignore_index,
319
+ softcap=final_logit_softcapping,
320
+ return_token_accuracy=False,
321
+ return_predicted_tokens=False,
322
+ **kwargs,
323
+ )
324
+
325
+ if reduction == "sum":
326
+ loss = loss / num_items_in_batch
327
+
328
+ return loss
329
+
330
+
331
+ __all__ = [
332
+ "LigerRMSNorm",
333
+ "LigerLinear",
334
+ "LigerSwiGLUMLP",
335
+ "LigerGEGLUMLP",
336
+ "LigerTiledSwiGLUMLP",
337
+ "LigerTiledGEGLUMLP",
338
+ "liger_rotary_pos_emb",
339
+ "liger_rotary_pos_emb_layer",
340
+ "LigerForCausalLMLoss",
341
+ "LigerForCausalLMLossLayer",
342
+ ]
build/torch-rocm/liger_kernels/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch-rocm/metadata.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "liger-kernels",
3
+ "id": "_liger_kernels_rocm_4d9f798",
4
+ "version": 3,
5
+ "license": "BSD-2-Clause",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "rocm"
9
+ },
10
+ "digest": {
11
+ "algorithm": "sha256",
12
+ "files": {
13
+ "__init__.py": "DSZMiK0xOBiMb0JdCc2K9QH4Z6lV7L8D/fTowstf/og=",
14
+ "_ops.py": "pdeXrc8M9KCvCpe0mtloEEFT3iJWZab9CMNsgT2CK10=",
15
+ "cross_entropy.py": "LsQN0OI8VHqgUObWF0qqTgNA9z7SYMaZNb2I7FIYnO4=",
16
+ "dyt.py": "806Tai0T+Yi7Ctf5YGmtwAc87MUjlzjsLky7b1T+oWM=",
17
+ "fused_linear_cross_entropy.py": "yjtZHWyBcXCBJ57NbbrQ1wMmTr9nARqDBrdjXVYpR74=",
18
+ "geglu.py": "Lmw0I38IBIHkNROTf9V3bP6PZLktCWCqNAxzVRaipbk=",
19
+ "group_norm.py": "xdY/tCr4rHOb4PhD827259XB7QDb22z2psUKzTYTKBE=",
20
+ "jsd.py": "CftyIN36AU63e1GdtuA7hNhs66I3TjS9lttcFriBnGA=",
21
+ "kl_div.py": "VQG/89mefLifPeVEv+1O8hmsXgGujMLA0JXDwO5QvtU=",
22
+ "layer_norm.py": "INpxK7Ac27f+eckcMfOOSgZErzDhNF5iGt207eV9aoM=",
23
+ "layers.py": "+50F9xmnpQXbU1K/lZxdJwbnTotkksJlhoLOWHjo+/M=",
24
+ "liger_kernels/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY=",
25
+ "qwen2vl_mrope.py": "3GExhYpLgB4VUtyZyjRk8XjEur3W4EWF6HQ67ML5vBU=",
26
+ "rms_norm.py": "GLzIZjDP74QxaeKQ5B/VDXKTGxD6Xo9cVaW/iqmsc4c=",
27
+ "rope.py": "v+7JHRrv+5ImoROkpKfl30WwWI4qTa2tAl7zQeB4ml4=",
28
+ "swiglu.py": "TLhyvpr7nbVbMqWbx8uz+iU2GxS6q0QrVxWAd1eQCBo=",
29
+ "tiled_mlp.py": "PJw2R8YdHHxQRSwbFJWjcHxgzssEqsRWqyogkY8pKR8=",
30
+ "tvd.py": "VbWUtpJ79E2atqjBoVyTSO785aS+idHiQBIrJwifDAw=",
31
+ "utils.py": "Y8wQsplz6ed7oPmBfX7yGGcX74VB3t/e5VFvYv/X7wU="
32
+ }
33
+ },
34
+ "provenance": {
35
+ "kernel-builder": {
36
+ "version": "0.17.0-dev0",
37
+ "sha": "81580bb92577f2f7228661ca2a221fc052375709",
38
+ "dirty": false
39
+ },
40
+ "kernel": {
41
+ "sha": "4d9f798b95ed280b770213048b8d3f41509d0c9e",
42
+ "dirty": false
43
+ }
44
+ }
45
+ }
build/torch-rocm/metadata.json.sigstore ADDED
@@ -0,0 +1 @@
 
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHTDCCBtGgAwIBAgIUeKKjysjZHPTkcjmFb3CHS1TysN0wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwNzE0MTIwMjUwWhcNMjYwNzE0MTIxMjUwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMw0GrkZBBP9oqnpDe5wf8q41n6WefaLaklEXON/6Xd4W4HwqUuA5A2sreVnQq1EjKZd4nZvn0yIbO+G/5kGEfKOCBfAwggXsMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUtZx+9iAL0iQdwe1N8MZBeaC2LFIwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wawYDVR0RAQH/BGEwX4ZdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoNGQ5Zjc5OGI5NWVkMjgwYjc3MDIxMzA0OGI4ZDNmNDE1MDlkMGM5ZTATBgorBgEEAYO/MAEEBAVCdWlsZDArBgorBgEEAYO/MAEFBB1odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMG0GCisGAQQBg78wAQkEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoNGQ5Zjc5OGI5NWVkMjgwYjc3MDIxMzA0OGI4ZDNmNDE1MDlkMGM5ZTAbBgorBgEEAYO/MAELBA0MC3NlbGYtaG9zdGVkMEAGCisGAQQBg78wAQwEMgwwaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MDgGCisGAQQBg78wAQ0EKgwoNGQ5Zjc5OGI5NWVkMjgwYjc3MDIxMzA0OGI4ZDNmNDE1MDlkMGM5ZTAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAaBgorBgEEAYO/MAEPBAwMCjEwNzE0NzU1MjkwLgYKKwYBBAGDvzABEAQgDB5odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2UwGAYKKwYBBAGDvzABEQQKDAgyNTcyMDc0MzBtBgorBgEEAYO/MAESBF8MXWh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS8uZ2l0aHViL3dvcmtmbG93cy9idWlsZC55YW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAETBCoMKDRkOWY3OThiOTVlZDI4MGI3NzAyMTMwNDhiOGQzZjQxNTA5ZDBjOWUwIQYKKwYBBAGDvzABFAQTDBF3b3JrZmxvd19kaXNwYXRjaDBkBgorBgEEAYO/MAEVBFYMVGh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS9hY3Rpb25zL3J1bnMvMjkzMzA2MjM4NjUvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzBGBgorBgEEAYO/MAEYBDgMNnJlcG86aHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHk6cmVmOnJlZnMvaGVhZHMvbWFpbjCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABn2CCKbEAAAQDAEcwRQIgMoVU3dtBnGlxMMb3sOTM8i/rOdiqaQ37x0tz9NOZa+MCIQCvrHRw6fB5k7rokMe6iDv+n9siBeeEDI5Helc4t8PNfzAKBggqhkjOPQQDAwNpADBmAjEAuazcBiTcv8K6MyCbxJkO8Q1WX752+NAYF6uQhREhOlfLY1Htf2AgU5YzxWwv72nSAjEAsyIagTrwrpp0hqNOQI9ik7Cl4rMqprpQ90wKXEz5+urqVmetTmkd7YsMArDzriDo"}, "tlogEntries":[{"logIndex":"2167855782", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"hashedrekord", "version":"0.0.1"}, "integratedTime":"1784030571", "inclusionPromise":{"signedEntryTimestamp":"MEQCIFPwhR+59GwljQ+ZxD1rhaNTV3Sh4vPwQeU/9JrQoO2xAiBPUFa1Dck8XvuXhEpd/t74KU9P4qaGczgDzl//4hAk8w=="}, "inclusionProof":{"logIndex":"2045951520", "rootHash":"Re/Ph87BOM7jhvV4VE+3WsyQb72tumWdlqTxAOe0qEM=", "treeSize":"2045951527", "hashes":["nUla+MAuN3xIPh3Fw90o+gJvejDEBaNrSVpncG6U3kM=", "dEagCZIabgWBH6ViR5uwn8xXp3WdvaR3vEy+Q8US9b8=", "4tMZ/2/4pBxGx11gMAczICVOHyul021sgz6LWWWRpIQ=", "m+Vk8U0/4EcekuSwkXtYOJcYVvuBCDXI7XChKnMHSec=", "iMBom1ahASM6ZAxymZoy+ISJTz5W3t4BNlJ+0yiYrnc=", "IuHNjKNGzZgw8Ec1vaQ4yxMC8m5mfRHL/sO7dTeEGIM=", "T/vXn6bo+SCijMlRZwcNRxHpWcvInutRlkDvk1Y7oEM=", "TXWJIUMj+J24/w5Nh86FuokZClxMAnvB6sia+ArBfNI=", "pfPn/gRvLHfxa/LNybYhqG78qHZ4QD3PI+zQ35oYweM=", "sMaCrs8erYNQ48jT2FC56F0RAbpTkuSXZ+cs1zcSObw=", "zGam2EiRYwS9IjemppqM/acVfU15hw3eAqn9Fb8+8vY=", "qQGtNWYnFxNQ1OmqNBR2y92EFrgo5lyaKNQLaE/Dl1c=", "/UAkaaq9D8BPMw5qLAxaf+ur4Sfbv3FfLWB2M/py3wM=", "56+1wf4OgrBZEzfW9n13hPfm0bRvMRgerPv4TPE+3Ho=", "mTSsSZdzVhRgljy9CvDq3GfjxxeOTHrwgpOWK4rRj3Q=", "5sxNZDoxEj6DMmQATisX9bQXdFmeRHYfC8BjyDIIgec=", "Rq/A2aTC9e54ldNjcpsJ26rX/h8JlN6ZHxgw7yEa6C0=", "+/VZ56MsIPxMiyLAodzKXo5TEWdQp36z89qLhpzloAo=", "daxmZaajRpZV+JxHiOYZhJBiSKN5ucqjh2WnGbHhirw=", "DOCeoSMovIvLExkhIvisow9AuNXgeWs4ECkyR6EcqYU="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n2045951527\nRe/Ph87BOM7jhvV4VE+3WsyQb72tumWdlqTxAOe0qEM=\n\n— rekor.sigstore.dev wNI9ajBEAiAxviZPcApj0ZJ1BUlQmvWCHwh/I05iSQg0PsjajUUu5wIgfTmpusfaD0Tu2YE2XuUcR3X1uFqLW7goWine86qQDnQ=\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiNmJkMTIwYTVhOGNkYWE0MmI5MTkxYWYwYzBlZDQzNDMxMDE5OWU2Y2RkZDg1YzFhNTE4NTAxODE3Yzc2NDliIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNHMmE1Y2pEL09zY3UrdlpxWjVnbXB6S2xVTGhXZHhyRmVSSE00dlRJa0p3SWhBSyttWkE1WlBZUGwrZnJUSFdLYk5TZUxFaFh5TjdzZ3VCNmpMMWxBTEhHTCIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaFVSRU5EUW5SSFowRjNTVUpCWjBsVlpVdExhbmx6YWxwSVVGUnJZMnB0Um1JelEwaFRNVlI1YzA0d2QwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDU2UlRCTlZFbDNUV3BWZDFkb1kwNU5hbGwzVG5wRk1FMVVTWGhOYWxWM1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZOZHpCSGNtdGFRa0pRT1c5eGJuQkVaVFYzWmpoeE5ERnVObGRsWm1GTVlXdHNSVmdLVDA0dk5saGtORmMwU0hkeFZYVkJOVUV5YzNKbFZtNVJjVEZGYWt0YVpEUnVXblp1TUhsSllrOHJSeTgxYTBkRlprdFBRMEptUVhkbloxaHpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlYwV25nckNqbHBRVXd3YVZGa2QyVXhUamhOV2tKbFlVTXlURVpKZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJGM1dVUldVakJTUVZGSUwwSkhSWGRZTkZwa1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKb01Wb3laSEJpYldSdFdWZE9iQXBNTW5Sc1kyMDFiR0pJVFhSWk1qbDBZbGhXZFdGWVVqVk1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1rb3hZVmQ0YTB4dWJHaGlWM2hCQ21OdFZtMWplVGx2V2xkR2EyTjVPWFJaVjJ4MVRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpBS1lWYzVkV041Tlc1aFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NIZFpTMHQzV1VKQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWN3cGlNMlJtV2tkc2VtTkhSakJaTW1kM1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOU9SMUUxV21wak5VOUhTVFZPVjFaclRXcG5kMWxxWXpOTlJFbDRDazE2UVRCUFIwazBXa1JPYlU1RVJURk5SR3hyVFVkTk5WcFVRVlJDWjI5eVFtZEZSVUZaVHk5TlFVVkZRa0ZXUTJSWGJITmFSRUZ5UW1kdmNrSm5SVVVLUVZsUEwwMUJSVVpDUWpGdlpGZGtibUZYTlc1YWJVWnFXbE01Y2xwWVNuVmFWM2g2VEZkT2RtSlhNVEZpYld3d1pWUkJaRUpuYjNKQ1owVkZRVmxQTHdwTlFVVkhRa0U1ZVZwWFducE1NbWhzV1ZkU2Vrd3lNV2hoVnpSM1QzZFpTMHQzV1VKQ1FVZEVkbnBCUWtOQlVYUkVRM1J2WkVoU2QyTjZiM1pNTTFKMkNtRXlWblZNYlVacVpFZHNkbUp1VFhWYU1td3dZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVY3dSME5wYzBkQlVWRkNaemM0ZDBGUmEwVUtXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5YURGYU1tUndZbTFrYlZsWFRteE1NblJzWTIwMWJHSklUWFJaTWpsMFlsaFdkUXBoV0ZJMVRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2VERKS01XRlhlR3RNYm14b1lsZDRRV050Vm0xamVUbHZXbGRHYTJONU9YUlpWMngxQ2sxRVowZERhWE5IUVZGUlFtYzNPSGRCVVc5RlMyZDNiMDVIVVRWYWFtTTFUMGRKTlU1WFZtdE5hbWQzV1dwak0wMUVTWGhOZWtFd1QwZEpORnBFVG0wS1RrUkZNVTFFYkd0TlIwMDFXbFJCWWtKbmIzSkNaMFZGUVZsUEwwMUJSVXhDUVRCTlF6Tk9iR0pIV1hSaFJ6bDZaRWRXYTAxRlFVZERhWE5IUVZGUlFncG5OemgzUVZGM1JVMW5kM2RoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlUxRVowZERhWE5IUVZGUlFtYzNPSGRCVVRCRlMyZDNiMDVIVVRWYWFtTTFUMGRKTlU1WFZtdE5hbWQzV1dwak0wMUVTWGdLVFhwQk1FOUhTVFJhUkU1dFRrUkZNVTFFYkd0TlIwMDFXbFJCWmtKbmIzSkNaMFZGUVZsUEwwMUJSVTlDUWtWTlJETktiRnB1VFhaaFIxWm9Xa2hOZGdwaVYwWndZbXBCWVVKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhkTlEycEZkMDU2UlRCT2VsVXhUV3ByZDB4bldVdExkMWxDUWtGSFJIWjZRVUpGUVZGbkNrUkNOVzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWM1IwRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVc0tSRUZuZVU1VVkzbE5SR013VFhwQ2RFSm5iM0pDWjBWRlFWbFBMMDFCUlZOQ1JqaE5XRmRvTUdSSVFucFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVid3BrVjJSdVlWYzFibHB0Um1wYVV6bHlXbGhLZFZwWGVIcE1WMDUyWWxjeE1XSnRiREJsVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxwQ21SWGJITmFRelUxV1ZjeGMxRklTbXhhYmsxMllVZFdhRnBJVFhaaVYwWndZbXBCTkVKbmIzSkNaMFZGUVZsUEwwMUJSVlJDUTI5TlMwUlNhMDlYV1RNS1QxUm9hVTlVVm14YVJFazBUVWRKTTA1NlFYbE5WRTEzVGtSb2FVOUhVWHBhYWxGNFRsUkJOVnBFUW1wUFYxVjNTVkZaUzB0M1dVSkNRVWRFZG5wQlFncEdRVkZVUkVKR00ySXpTbkphYlhoMlpERTVhMkZZVG5kWldGSnFZVVJDYTBKbmIzSkNaMFZGUVZsUEwwMUJSVlpDUmxsTlZrZG9NR1JJUW5wUGFUaDJDbG95YkRCaFNGWnBURzFPZG1KVE9XOWtWMlJ1WVZjMWJscHRSbXBhVXpseVdsaEtkVnBYZUhwTVYwNTJZbGN4TVdKdGJEQmxVemxvV1ROU2NHSXlOWG9LVEROS01XSnVUWFpOYW10NlRYcEJNazFxVFRST2FsVjJXVmhTTUZwWE1YZGtTRTEyVFZSQlYwSm5iM0pDWjBWRlFWbFBMMDFCUlZkQ1FXZE5RbTVDTVFwWmJYaHdXWHBDUjBKbmIzSkNaMFZGUVZsUEwwMUJSVmxDUkdkTlRtNUtiR05IT0RaaFNGWnVXakpzZFZveVdtaFpNbFYyWVRKV2VXSnRWbk5qZVRGcUNtSXlNWFJrVnpWd1pFaHJObU50Vm0xUGJrcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRMEpwWjFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT0VKSWIwRUtaVUZDTWtGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtNHlRME5MWWtWQlFVRlJSQXBCUldOM1VsRkpaMDF2VmxVelpIUkNia2RzZUUxTllqTnpUMVJOT0drdmNrOWthWEZoVVRNM2VEQjBlamxPVDFwaEswMURTVkZEZG5KSVVuYzJaa0kxQ21zM2NtOXJUV1UyYVVSMksyNDVjMmxDWldWRlJFazFTR1ZzWXpSME9GQk9abnBCUzBKblozRm9hMnBQVUZGUlJFRjNUbkJCUkVKdFFXcEZRWFZoZW1NS1FtbFVZM1k0U3paTmVVTmllRXByVHpoUk1WZFlOelV5SzA1QldVWTJkVkZvVWtWb1QyeG1URmt4U0hSbU1rRm5WVFZaZW5oWGQzWTNNbTVUUVdwRlFRcHplVWxoWjFSeWQzSndjREJvY1U1UFVVazVhV3MzUTJ3MGNrMXhjSEp3VVRrd2QwdFlSWG8xSzNWeWNWWnRaWFJVYld0a04xbHpUVUZ5UkhweWFVUnZDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ=="}], "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyjADAgEAMIICwQYJKoZIhvcNAQcCoIICsjCCAq4CAQMxDTALBglghkgBZQMEAgEwgbcGCyqGSIb3DQEJEAEEoIGnBIGkMIGhAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQg/3UMGV8fwrsfiQEAzqbUSlYc87EUEy8wrOfhaOM5NwACFBwa0r74wyBkjdrXOXL45py8exMrGA8yMDI2MDcxNDEyMDI1MVowAwIBAaAypDAwLjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MRUwEwYDVQQDEwxzaWdzdG9yZS10c2GgADGCAdwwggHYAgEBMFEwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZAIUOhNULwyQYe68wUMvy4qOiyojiwwwCwYJYIZIAWUDBAIBoIH8MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwNzE0MTIwMjUxWjAvBgkqhkiG9w0BCQQxIgQgC5Qp2/4m1NXACJdcq25HSjgek+k/yuqiVNfBceqic8kwgY4GCyqGSIb3DQEJEAIvMX8wfTB7MHkEIIX5J7wHq2LKw7RDVsEO/IGyxog/2nq55thw2dE6zQW3MFUwPaQ7MDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAoGCCqGSM49BAMCBGgwZgIxALBGy+9QLpTAvIjqpcf3WnWsVHfTrPKGAClJboW2OzfnuQ9wBO4Qi9+XIsmEIuGPdwIxAKbZ0fUnZ4D5AG3RMAvGaeLMf2WAwCkkT+7PEpjMl7uXe2luLTuwr+SqoAxPJxE2KA=="}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"tr0SClqM2qQrkZGvDA7UNDEBmebN3YXBpRhQGBfHZJs="}, "signature":"MEYCIQCG2a5cjD/Oscu+vZqZ5gmpzKlULhWdxrFeRHM4vTIkJwIhAK+mZA5ZPYPl+frTHWKbNSeLEhXyN7sguB6jL1lALHGL"}}
build/torch-rocm/qwen2vl_mrope.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+
5
+
6
+ @triton.jit
7
+ def _triton_qwen2vl_mrope(
8
+ q_ptr,
9
+ k_ptr,
10
+ cos,
11
+ sin,
12
+ sl,
13
+ bs: tl.constexpr,
14
+ n_qh: tl.constexpr,
15
+ n_kh: tl.constexpr,
16
+ hd: tl.constexpr,
17
+ pad_n_qh: tl.constexpr,
18
+ pad_n_kh: tl.constexpr,
19
+ pad_hd: tl.constexpr,
20
+ mrope_section_t: tl.constexpr,
21
+ mrope_section_h: tl.constexpr,
22
+ BLOCK_SIZE: tl.constexpr,
23
+ BACKWARD_PASS: tl.constexpr = False,
24
+ ):
25
+ pid = tl.program_id(0)
26
+
27
+ # locate start address
28
+ q_ptr = q_ptr + pid * (n_qh * hd)
29
+ k_ptr = k_ptr + pid * (n_kh * hd)
30
+
31
+ # ####################################################################
32
+ # get the cos(mθ_{i...d/2}) and sin(mθ_{i...d/2}) for token position
33
+ # m of this program instance
34
+ # ####################################################################
35
+
36
+ # 1. program instances are laid out in a 1D vector of size bsz * seq_len, which
37
+ # effectively represents a 2D grid of size [bsz, seq_len] with seq_len dimension
38
+ # being the fastest changing dimension. Thus we can simply do pid // sl to get the batch index
39
+ # and pid % sl to get the sequence index.
40
+ # 2. We only need the left half of cos and sin matrix because the right half is just
41
+ # a clone of the left half.
42
+ t_end = mrope_section_t
43
+ h_end = t_end + mrope_section_h
44
+
45
+ t_cos = cos + pid * hd
46
+ h_cos = t_cos + bs * sl * hd
47
+ w_cos = h_cos + bs * sl * hd
48
+ t_sin = sin + pid * hd
49
+ h_sin = t_sin + bs * sl * hd
50
+ w_sin = h_sin + bs * sl * hd
51
+
52
+ cos_offsets = tl.arange(0, pad_hd // 2)
53
+ t_mask = cos_offsets < t_end
54
+ h_mask = (t_end <= cos_offsets) & (cos_offsets < h_end)
55
+ w_mask = (h_end <= cos_offsets) & (cos_offsets < hd // 2)
56
+ t_cos_row = tl.load(t_cos + cos_offsets, mask=t_mask, other=0)
57
+ h_cos_row = tl.load(h_cos + cos_offsets, mask=h_mask, other=0)
58
+ w_cos_row = tl.load(w_cos + cos_offsets, mask=w_mask, other=0)
59
+ t_sin_row = tl.load(t_sin + cos_offsets, mask=t_mask, other=0)
60
+ h_sin_row = tl.load(h_sin + cos_offsets, mask=h_mask, other=0)
61
+ w_sin_row = tl.load(w_sin + cos_offsets, mask=w_mask, other=0)
62
+ cos_row = t_cos_row + h_cos_row + w_cos_row
63
+ sin_row = t_sin_row + h_sin_row + w_sin_row
64
+
65
+ # ####################################################################
66
+ # Load the left and right half of q and k for the current
67
+ # program instance (i.e. for the current token) separately
68
+ # ####################################################################
69
+ # left half of the head
70
+ first_half_q_offsets = tl.arange(0, pad_n_qh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
71
+ first_half_k_offsets = tl.arange(0, pad_n_kh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
72
+ first_q_mask = (tl.arange(0, pad_n_qh)[:, None] < n_qh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
73
+ first_k_mask = (tl.arange(0, pad_n_kh)[:, None] < n_kh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
74
+ q_tile_1 = tl.load(q_ptr + first_half_q_offsets, mask=first_q_mask, other=0).to(sin_row.dtype)
75
+ k_tile_1 = tl.load(k_ptr + first_half_k_offsets, mask=first_k_mask, other=0).to(sin_row.dtype)
76
+
77
+ # right half of the head
78
+ second_half_q_offsets = first_half_q_offsets + (hd // 2)
79
+ second_half_k_offsets = first_half_k_offsets + (hd // 2)
80
+ second_q_mask = first_q_mask
81
+ second_k_mask = first_k_mask
82
+ q_tile_2 = tl.load(q_ptr + second_half_q_offsets, mask=second_q_mask, other=0).to(sin_row.dtype)
83
+ k_tile_2 = tl.load(k_ptr + second_half_k_offsets, mask=second_k_mask, other=0).to(sin_row.dtype)
84
+
85
+ if not BACKWARD_PASS:
86
+ # y = [x1, x2] * [cos, cos] + [-x2, x1] * [sin, sin]
87
+ new_q_tile_1 = q_tile_1 * cos_row - q_tile_2 * sin_row
88
+ tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
89
+ new_q_tile_2 = q_tile_2 * cos_row + q_tile_1 * sin_row
90
+ tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
91
+
92
+ new_k_tile_1 = k_tile_1 * cos_row - k_tile_2 * sin_row
93
+ tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
94
+ new_k_tile_2 = k_tile_2 * cos_row + k_tile_1 * sin_row
95
+ tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
96
+ else:
97
+ # with some math, we can get:
98
+ # dy = [dx1, dx2] * [cos, cos] + [-dx2, dx1] * [-sin, -sin]
99
+ new_q_tile_1 = q_tile_1 * cos_row + q_tile_2 * sin_row
100
+ tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
101
+ new_q_tile_2 = q_tile_2 * cos_row - q_tile_1 * sin_row
102
+ tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
103
+
104
+ new_k_tile_1 = k_tile_1 * cos_row + k_tile_2 * sin_row
105
+ tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
106
+ new_k_tile_2 = k_tile_2 * cos_row - k_tile_1 * sin_row
107
+ tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
108
+
109
+
110
+ def qwen2vl_mrope_forward(q, k, cos, sin, mrope_section):
111
+ # transpose it back to the physical shape because Triton looks at the physical storage
112
+ # note: q and k are incontiguous before the transformation and will become contiguous after transpose
113
+ q = q.transpose(1, 2)
114
+ k = k.transpose(1, 2)
115
+
116
+ batch_size, seq_len, n_q_head, head_dim = q.shape
117
+ n_kv_head = k.shape[2]
118
+ pad_hd = triton.next_power_of_2(head_dim)
119
+ pad_n_q_head = triton.next_power_of_2(n_q_head)
120
+ pad_n_kv_head = triton.next_power_of_2(n_kv_head)
121
+ BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
122
+
123
+ n_row = batch_size * seq_len
124
+
125
+ # ensure tensors passed into the kernel are contiguous. It will be no-op if they are already contiguous
126
+ q = q.contiguous()
127
+ k = k.contiguous()
128
+ cos = cos.contiguous()
129
+ sin = sin.contiguous()
130
+
131
+ _triton_qwen2vl_mrope[(n_row,)](
132
+ q,
133
+ k,
134
+ cos,
135
+ sin,
136
+ seq_len,
137
+ batch_size,
138
+ n_q_head,
139
+ n_kv_head,
140
+ head_dim,
141
+ pad_n_q_head,
142
+ pad_n_kv_head,
143
+ pad_hd,
144
+ mrope_section[0],
145
+ mrope_section[1],
146
+ BLOCK_SIZE=BLOCK_SIZE,
147
+ BACKWARD_PASS=False,
148
+ )
149
+ return q.transpose(1, 2), k.transpose(1, 2), cos, sin
150
+
151
+
152
+ def qwen2vl_mrope_backward(dq, dk, cos, sin, mrope_section):
153
+ dq = dq.transpose(1, 2)
154
+ dk = dk.transpose(1, 2)
155
+
156
+ batch_size, seq_len, n_q_head, head_dim = dq.shape
157
+ n_kv_head = dk.shape[2]
158
+ pad_hd = triton.next_power_of_2(head_dim)
159
+ pad_n_q_head = triton.next_power_of_2(n_q_head)
160
+ pad_n_kv_head = triton.next_power_of_2(n_kv_head)
161
+ BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
162
+
163
+ n_row = batch_size * seq_len
164
+
165
+ # ensure dq and dk are contiguous
166
+ dq = dq.contiguous()
167
+ dk = dk.contiguous()
168
+
169
+ # backward is similar to forward except swapping few ops
170
+ _triton_qwen2vl_mrope[(n_row,)](
171
+ dq,
172
+ dk,
173
+ cos,
174
+ sin,
175
+ seq_len,
176
+ batch_size,
177
+ n_q_head,
178
+ n_kv_head,
179
+ head_dim,
180
+ pad_n_q_head,
181
+ pad_n_kv_head,
182
+ pad_hd,
183
+ mrope_section[0],
184
+ mrope_section[1],
185
+ BLOCK_SIZE=BLOCK_SIZE,
186
+ BACKWARD_PASS=True,
187
+ )
188
+ return dq.transpose(1, 2), dk.transpose(1, 2)
189
+
190
+
191
+ class LigerQwen2VLMRopeFunction(torch.autograd.Function):
192
+ """
193
+ Triton implementation of the Qwen2VL Multimodal Rotary Positional Embedding (M-RoPE) operation.
194
+
195
+ Please find the corresponding HuggingFace implementation here:
196
+ https://github.com/huggingface/transformers/blob/main/src/transformers/models/qwen2_vl/modeling_qwen2_vl.py
197
+ """
198
+
199
+ @staticmethod
200
+ def forward(ctx, q, k, cos, sin, mrope_section, unsqueeze_dim=1):
201
+ """
202
+ q size: (bsz, n_q_head, seq_len, head_dim)
203
+ k size: (bsz, n_kv_head, seq_len, head_dim)
204
+ cos size: (3, bsz, seq_len, head_dim)
205
+ sin size: (3, bsz, seq_len, head_dim)
206
+ """
207
+ q, k, cos, sin = qwen2vl_mrope_forward(q, k, cos, sin, mrope_section)
208
+ ctx.save_for_backward(cos, sin)
209
+ ctx.mrope_section = mrope_section
210
+ return q, k
211
+
212
+ def backward(ctx, dq, dk):
213
+ """
214
+ dq size: (bsz, n_q_head, seq_len, head_dim)
215
+ dk size: (bsz, n_kv_head, seq_len, head_dim)
216
+ cos size: (3, bsz, seq_len, head_dim)
217
+ sin size: (3, bsz, seq_len, head_dim)
218
+ """
219
+ cos, sin = ctx.saved_tensors
220
+ mrope_section = ctx.mrope_section
221
+ dq, dk = qwen2vl_mrope_backward(dq, dk, cos, sin, mrope_section)
222
+ return dq, dk, None, None, None, None
build/torch-rocm/rms_norm.py ADDED
@@ -0,0 +1,654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file incorporates code from Unsloth licensed under the Apache License, Version 2.0.
3
+ See the original Unsloth repository at https://github.com/unslothai/unsloth.
4
+
5
+ The following line
6
+ https://github.com/linkedin/Liger-Kernel/blob/7382a8761f9af679482b968f9348013d933947c7/src/liger_kernel/ops/rms_norm.py#L30
7
+ is based on code from Unsloth, located at:
8
+ https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/rms_layernorm.py#L22
9
+
10
+ Modifications made by Yanning Chen, 2024.
11
+ """
12
+
13
+ import math
14
+ import operator
15
+
16
+ import torch
17
+ import triton
18
+ import triton.language as tl
19
+
20
+ from .utils import calculate_settings
21
+ from .utils import compare_version
22
+ from .utils import ensure_contiguous
23
+ from .utils import get_npu_core_count
24
+ from .utils import set_large_grf_mode
25
+ from .utils import torch_to_triton_dtype
26
+ from .utils import is_npu_available
27
+
28
+ if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
29
+ try:
30
+ # typical import path with dispatch available
31
+ from triton.language.extra.libdevice import rsqrt
32
+ except ModuleNotFoundError:
33
+ # for working with NGC containers
34
+ from triton.language.extra.cuda.libdevice import rsqrt
35
+ else:
36
+ from triton.language.math import rsqrt
37
+
38
+
39
+ _CASTING_MODE_NONE: tl.constexpr = tl.constexpr(-1)
40
+ _CASTING_MODE_LLAMA: tl.constexpr = tl.constexpr(0)
41
+ _CASTING_MODE_GEMMA: tl.constexpr = tl.constexpr(1)
42
+
43
+
44
+ @triton.jit
45
+ def _rms_norm_forward_kernel(
46
+ Y_ptr,
47
+ Y_row_stride,
48
+ X_ptr,
49
+ X_row_stride,
50
+ W_ptr,
51
+ W_row_stride,
52
+ RSTD_ptr,
53
+ RSTD_row_stride,
54
+ n_cols,
55
+ eps,
56
+ offset,
57
+ casting_mode: tl.constexpr, # constexpr so the `if` blocks can be optimized out
58
+ elementwise_affine: tl.constexpr,
59
+ BLOCK_SIZE: tl.constexpr,
60
+ ):
61
+ """
62
+ y_i = (x_i / (RMS)) * (offset + wi), RMS = sqrt(sum(x_i^2) / N)
63
+
64
+ Reference:
65
+ 1. https://triton-lang.org/main/getting-started/tutorials/05-layer-norm.html
66
+ 2. https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/rms_layernorm.py#L22
67
+ 3. https://arxiv.org/pdf/1910.07467
68
+ """
69
+
70
+ row_idx = tl.program_id(0).to(tl.int64)
71
+ col_offsets = tl.arange(0, BLOCK_SIZE)
72
+ mask = col_offsets < n_cols
73
+
74
+ y_base = Y_ptr + row_idx * Y_row_stride
75
+ x_base = X_ptr + row_idx * X_row_stride
76
+ rstd_base = RSTD_ptr + row_idx * RSTD_row_stride
77
+
78
+ X_row = tl.load(x_base + col_offsets, mask=mask, other=0)
79
+ X_row_dtype = X_row.dtype
80
+ if elementwise_affine:
81
+ W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0)
82
+
83
+ # On Llama, only rstd is computed on fp32
84
+ if casting_mode == _CASTING_MODE_LLAMA:
85
+ X_row = X_row.to(tl.float32)
86
+
87
+ # Gemma computes everything on fp32, and then casts back the output to the original dtype
88
+ if casting_mode == _CASTING_MODE_GEMMA:
89
+ if elementwise_affine:
90
+ W_row = W_row.to(tl.float32)
91
+ X_row = X_row.to(tl.float32)
92
+
93
+ if casting_mode == _CASTING_MODE_NONE:
94
+ eps = eps.to(X_row_dtype)
95
+ offset = offset.to(X_row_dtype)
96
+
97
+ mean_square = tl.sum(X_row * X_row, axis=0) / n_cols
98
+ rstd = rsqrt(mean_square + eps)
99
+
100
+ # We can save time by caching rms with minimal memory overhead
101
+ # because rms is much smaller compared to X_row, as rms is for each row.
102
+ # However, on the computation side, it can save 4 operations (*, sum, /, sqrt).
103
+ tl.store(rstd_base, rstd)
104
+
105
+ X_row = X_row * rstd
106
+
107
+ # On Llama, the multiplication with the weight is done on the original dtype
108
+ if casting_mode == _CASTING_MODE_LLAMA:
109
+ X_row = X_row.to(X_row_dtype)
110
+
111
+ if elementwise_affine:
112
+ Y_row = X_row * (offset + W_row)
113
+ else:
114
+ Y_row = X_row
115
+
116
+ if casting_mode == _CASTING_MODE_GEMMA:
117
+ Y_row = Y_row.to(X_row_dtype)
118
+
119
+ tl.store(y_base + col_offsets, Y_row, mask=mask)
120
+
121
+
122
+ @triton.jit
123
+ def _rms_norm_backward_kernel(
124
+ dY_ptr,
125
+ dY_row_stride,
126
+ dX_ptr,
127
+ dX_row_stride,
128
+ X_ptr,
129
+ X_row_stride,
130
+ X_dtype: tl.constexpr,
131
+ W_ptr,
132
+ W_row_stride,
133
+ RSTD_ptr,
134
+ RSTD_row_stride,
135
+ dW_ptr,
136
+ dW_row_stride,
137
+ n_rows,
138
+ n_cols,
139
+ offset,
140
+ rows_per_program,
141
+ casting_mode: tl.constexpr,
142
+ elementwise_affine: tl.constexpr,
143
+ BLOCK_SIZE: tl.constexpr,
144
+ ):
145
+ """
146
+ dx = (1 / RMS) * [dy * (w + offset - (1 / N) * (1 / RMS^2) * ((dy * (w + offset)) dot x) * x]. * means element-wise multiplication, whileas dot means dot product
147
+ dw = sum(dy * (x / RMS)). summation over BxT dimension
148
+ """
149
+
150
+ row_block_id = tl.program_id(0).to(tl.int64)
151
+ row_start = row_block_id * rows_per_program
152
+ row_end = min((row_block_id + 1) * rows_per_program, n_rows)
153
+ col_offsets = tl.arange(0, BLOCK_SIZE)
154
+ mask = col_offsets < n_cols
155
+
156
+ if elementwise_affine:
157
+ dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
158
+
159
+ if elementwise_affine:
160
+ W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0.0)
161
+ W_row = W_row + offset
162
+
163
+ for row_idx in range(row_start, row_end):
164
+ dy_base = dY_ptr + row_idx * dY_row_stride
165
+ dx_base = dX_ptr + row_idx * dX_row_stride
166
+
167
+ x_base = X_ptr + row_idx * X_row_stride
168
+ rstd_base = RSTD_ptr + row_idx * RSTD_row_stride
169
+
170
+ dY_row = tl.load(dy_base + col_offsets, mask=mask, other=0.0)
171
+ X_row = tl.load(x_base + col_offsets, mask=mask, other=0.0)
172
+
173
+ # Get cached rms
174
+ rstd_row = tl.load(rstd_base)
175
+
176
+ X_row = X_row.to(tl.float32)
177
+
178
+ # Different bacward graphs for different casting modes
179
+ if casting_mode == _CASTING_MODE_LLAMA:
180
+ if elementwise_affine:
181
+ m = (dY_row * W_row).to(tl.float32)
182
+ else:
183
+ m = dY_row.to(tl.float32)
184
+
185
+ elif casting_mode == _CASTING_MODE_GEMMA:
186
+ dY_row = dY_row.to(tl.float32)
187
+ if elementwise_affine:
188
+ m = dY_row * W_row
189
+ else:
190
+ m = dY_row
191
+ else:
192
+ if elementwise_affine:
193
+ m = dY_row * W_row
194
+ else:
195
+ m = dY_row
196
+
197
+ dX_row = rstd_row * m
198
+
199
+ dX_row += (rstd_row) * (-(1 / n_cols) * rstd_row * rstd_row * tl.sum(m * X_row, axis=0) * X_row)
200
+
201
+ if elementwise_affine:
202
+ # calculate the gradient of W
203
+ if casting_mode == _CASTING_MODE_LLAMA:
204
+ dW_row += dY_row * (X_row * rstd_row).to(X_dtype)
205
+ else:
206
+ # here X_row is already in fp32 (see previous if block)
207
+ dW_row += dY_row * (X_row * rstd_row)
208
+
209
+ tl.store(dx_base + col_offsets, dX_row.to(X_dtype), mask=mask)
210
+
211
+ if elementwise_affine:
212
+ tl.store(dW_ptr + row_block_id * dW_row_stride + col_offsets, dW_row, mask=mask)
213
+
214
+
215
+ @triton.jit
216
+ def _block_rms_norm_forward_kernel(
217
+ Y_ptr,
218
+ Y_row_stride,
219
+ X_ptr,
220
+ X_row_stride,
221
+ W_ptr,
222
+ W_row_stride,
223
+ RSTD_ptr,
224
+ RSTD_row_stride,
225
+ n_rows,
226
+ n_cols,
227
+ eps,
228
+ offset,
229
+ casting_mode: tl.constexpr, # constexpr so the `if` blocks can be optimized out
230
+ elementwise_affine: tl.constexpr,
231
+ BLOCK_SIZE: tl.constexpr,
232
+ BLOCK_ROW: tl.constexpr,
233
+ ):
234
+ """
235
+ y_i = (x_i / (RMS)) * (offset + wi), RMS = sqrt(sum(x_i^2) / N)
236
+
237
+ Reference:
238
+ 1. https://triton-lang.org/main/getting-started/tutorials/05-layer-norm.html
239
+ 2. https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/rms_layernorm.py#L22
240
+ 3. https://arxiv.org/pdf/1910.07467
241
+ """
242
+
243
+ row_idx = tl.program_id(0) * BLOCK_ROW + tl.arange(0, BLOCK_ROW)
244
+ col_offsets = tl.arange(0, BLOCK_SIZE)
245
+ row_mask = row_idx < n_rows
246
+ col_mask = col_offsets < n_cols
247
+
248
+ X_row = tl.load(
249
+ X_ptr + row_idx[:, None] * X_row_stride + col_offsets[None, :],
250
+ mask=row_mask[:, None] & col_mask[None, :],
251
+ other=0,
252
+ )
253
+ X_row_dtype = X_row.dtype
254
+ if elementwise_affine:
255
+ W_row = tl.load(W_ptr + col_offsets, mask=col_mask, other=0)
256
+
257
+ # On Llama, only rstd is computed on fp32
258
+ if casting_mode == _CASTING_MODE_LLAMA:
259
+ X_row = X_row.to(tl.float32)
260
+
261
+ # Gemma computes everything on fp32, and then casts back the output to the original dtype
262
+ if casting_mode == _CASTING_MODE_GEMMA:
263
+ if elementwise_affine:
264
+ W_row = W_row.to(tl.float32)
265
+ X_row = X_row.to(tl.float32)
266
+
267
+ if casting_mode == _CASTING_MODE_NONE:
268
+ eps = eps.to(X_row_dtype)
269
+ offset = offset.to(X_row_dtype)
270
+
271
+ mean_square = tl.sum(X_row * X_row, axis=1) / n_cols
272
+ rstd = rsqrt(mean_square + eps)
273
+
274
+ # We can save time by caching rms with minimal memory overhead
275
+ # because rms is much smaller compared to X_row, as rms is for each row.
276
+ # However, on the computation side, it can save 4 operations (*, sum, /, sqrt).
277
+ tl.store(RSTD_ptr + row_idx * RSTD_row_stride, rstd, row_mask)
278
+
279
+ X_row = X_row * rstd[:, None]
280
+
281
+ # On Llama, the multiplication with the weight is done on the original dtype
282
+ if casting_mode == _CASTING_MODE_LLAMA:
283
+ X_row = X_row.to(X_row_dtype)
284
+
285
+ if elementwise_affine:
286
+ Y_row = X_row * (offset + W_row)[None, :]
287
+ else:
288
+ Y_row = X_row
289
+
290
+ if casting_mode == _CASTING_MODE_GEMMA:
291
+ Y_row = Y_row.to(X_row_dtype)
292
+
293
+ tl.store(
294
+ Y_ptr + row_idx[:, None] * Y_row_stride + col_offsets[None, :],
295
+ Y_row,
296
+ mask=row_mask[:, None] & col_mask[None, :],
297
+ )
298
+
299
+
300
+ @triton.jit
301
+ def _block_rms_norm_backward_kernel(
302
+ dY_ptr,
303
+ dY_row_stride,
304
+ dX_ptr,
305
+ dX_row_stride,
306
+ X_ptr,
307
+ X_row_stride,
308
+ X_dtype: tl.constexpr,
309
+ W_ptr,
310
+ W_row_stride,
311
+ RSTD_ptr,
312
+ RSTD_row_stride,
313
+ dW_ptr,
314
+ dW_row_stride,
315
+ n_rows,
316
+ n_cols,
317
+ offset,
318
+ casting_mode: tl.constexpr,
319
+ elementwise_affine: tl.constexpr,
320
+ BLOCK_SIZE: tl.constexpr,
321
+ BLOCK_ROW: tl.constexpr,
322
+ ):
323
+ """
324
+ dx = (1 / RMS) * [dy * (w + offset - (1 / N) * (1 / RMS^2) * ((dy * (w + offset)) dot x) * x]. * means element-wise multiplication, whileas dot means dot product
325
+ dw = sum(dy * (x / RMS)). summation over BxT dimension
326
+ """
327
+
328
+ pid = tl.program_id(0).cast(tl.int64)
329
+ NUM_SMS = tl.num_programs(0)
330
+
331
+ col_offsets = tl.arange(0, BLOCK_SIZE)
332
+ col_mask = col_offsets < n_cols
333
+
334
+ if elementwise_affine:
335
+ dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
336
+
337
+ W_row = tl.load(W_ptr + col_offsets, mask=col_mask, other=0.0)
338
+ W_row = W_row + offset
339
+
340
+ for start in range(pid * BLOCK_ROW, n_rows, NUM_SMS * BLOCK_ROW):
341
+ row_idx = start + tl.arange(0, BLOCK_ROW)
342
+ row_mask = row_idx < n_rows
343
+ dY_row = tl.load(
344
+ dY_ptr + row_idx[:, None] * dY_row_stride + col_offsets[None, :],
345
+ mask=row_mask[:, None] & col_mask[None, :],
346
+ other=0.0,
347
+ )
348
+ X_row = tl.load(
349
+ X_ptr + row_idx[:, None] * X_row_stride + col_offsets[None, :],
350
+ mask=row_mask[:, None] & col_mask[None, :],
351
+ other=0.0,
352
+ )
353
+
354
+ # Get cached rms
355
+ rstd_row = tl.load(RSTD_ptr + row_idx * RSTD_row_stride, row_mask)
356
+
357
+ X_row = X_row.to(tl.float32)
358
+
359
+ # Different bacward graphs for different casting modes
360
+ if casting_mode == _CASTING_MODE_LLAMA:
361
+ if elementwise_affine:
362
+ m = (dY_row * W_row[None, :]).to(tl.float32)
363
+ else:
364
+ m = dY_row.to(tl.float32)
365
+
366
+ elif casting_mode == _CASTING_MODE_GEMMA:
367
+ dY_row = dY_row.to(tl.float32)
368
+ if elementwise_affine:
369
+ m = dY_row * W_row[None, :]
370
+ else:
371
+ m = dY_row
372
+ else:
373
+ if elementwise_affine:
374
+ m = dY_row * W_row[None, :]
375
+ else:
376
+ m = dY_row
377
+
378
+ dX_row = rstd_row[:, None] * m
379
+
380
+ dX_row += (rstd_row[:, None]) * (
381
+ -(1 / n_cols) * (rstd_row * rstd_row * tl.sum(m * X_row, axis=1))[:, None] * X_row
382
+ )
383
+
384
+ if elementwise_affine:
385
+ if casting_mode == _CASTING_MODE_LLAMA:
386
+ # TODO(tcc): use tl.sum(..., dtype=tl.float32) once we upgrade to triton>=3.3.0
387
+ dW_row += tl.sum((dY_row * (X_row * rstd_row[:, None]).to(X_dtype)).to(tl.float32), 0)
388
+ else:
389
+ # here X_row is already in fp32 (see previous if block)
390
+ dW_row += tl.sum(dY_row * (X_row * rstd_row[:, None]), 0)
391
+
392
+ tl.store(
393
+ dX_ptr + row_idx[:, None] * dX_row_stride + col_offsets[None, :],
394
+ dX_row,
395
+ mask=row_mask[:, None] & col_mask[None, :],
396
+ )
397
+
398
+ if elementwise_affine:
399
+ tl.store(dW_ptr + pid * dW_row_stride + col_offsets, dW_row, mask=col_mask)
400
+
401
+
402
+ _str_to_casting_mode = {
403
+ "llama": _CASTING_MODE_LLAMA.value,
404
+ "gemma": _CASTING_MODE_GEMMA.value,
405
+ "none": _CASTING_MODE_NONE.value,
406
+ }
407
+
408
+
409
+ def rms_norm_forward(X, W, eps, offset, casting_mode, row_mode):
410
+ if not isinstance(casting_mode, int):
411
+ assert casting_mode in _str_to_casting_mode, f"Invalid casting mode: {casting_mode}"
412
+ casting_mode = _str_to_casting_mode[casting_mode]
413
+ else:
414
+ assert casting_mode in _str_to_casting_mode.values(), f"Invalid casting mode: {casting_mode}"
415
+
416
+ shape = X.shape
417
+ dim = shape[-1]
418
+ X = X.view(-1, dim)
419
+ n_rows, n_cols = X.shape
420
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
421
+
422
+ Y = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
423
+ # RSTD is to cache rstd for each row
424
+ # RSTD is always computed/stored in fp32 if we are using Llama or Gemma casting mode
425
+ rstd_dtype = torch.float32 if casting_mode in (_CASTING_MODE_LLAMA.value, _CASTING_MODE_GEMMA.value) else X.dtype
426
+ RSTD = torch.empty(n_rows, dtype=rstd_dtype, device=X.device)
427
+
428
+ if W is not None:
429
+ # Check constraints.
430
+ assert X.shape[1] == W.shape[0], (
431
+ "Incompatible hidden size dimension between tensor1.shape[1] and tensor2.shape[0]"
432
+ )
433
+ elementwise_affine = True
434
+ else:
435
+ elementwise_affine = False
436
+
437
+ # XPU-specific optimization
438
+ kernel_args = {}
439
+ if X.device.type == "xpu":
440
+ set_large_grf_mode(kernel_args)
441
+ if BLOCK_SIZE > 256 or n_rows < 4096 * 8 or row_mode:
442
+ _rms_norm_forward_kernel[(n_rows,)](
443
+ Y,
444
+ Y.stride(0),
445
+ X,
446
+ X.stride(0),
447
+ W,
448
+ W.stride(0) if elementwise_affine else 0,
449
+ RSTD,
450
+ RSTD.stride(0),
451
+ n_cols,
452
+ eps,
453
+ offset,
454
+ casting_mode,
455
+ elementwise_affine=elementwise_affine,
456
+ BLOCK_SIZE=BLOCK_SIZE,
457
+ num_warps=num_warps,
458
+ **kernel_args, # XPU-specific optimization
459
+ )
460
+ else:
461
+ BLOCK_ROW = 16
462
+ kernel_args["BLOCK_ROW"] = BLOCK_ROW
463
+ _block_rms_norm_forward_kernel[(triton.cdiv(n_rows, BLOCK_ROW),)](
464
+ Y,
465
+ Y.stride(0),
466
+ X,
467
+ X.stride(0),
468
+ W,
469
+ W.stride(0) if elementwise_affine else 0,
470
+ RSTD,
471
+ RSTD.stride(0),
472
+ n_rows,
473
+ n_cols,
474
+ eps,
475
+ offset,
476
+ casting_mode,
477
+ elementwise_affine=elementwise_affine,
478
+ BLOCK_SIZE=BLOCK_SIZE,
479
+ num_warps=num_warps,
480
+ **kernel_args, # XPU-specific optimization
481
+ )
482
+ return Y.view(*shape), X, RSTD, BLOCK_SIZE, num_warps, casting_mode
483
+
484
+
485
+ def rms_norm_backward(dY, X, W, RSTD, offset, casting_mode, BLOCK_SIZE, num_warps, in_place, row_mode):
486
+ shape = dY.shape
487
+ dim = shape[-1]
488
+ dY = dY.view(-1, dim)
489
+ n_rows, n_cols = dY.shape
490
+
491
+ sm_count = 1
492
+ if X.device.type == "cuda":
493
+ sm_count = torch.cuda.get_device_properties(X.device).multi_processor_count
494
+ elif X.device.type == "xpu":
495
+ sm_count = torch.xpu.get_device_properties(X.device).gpu_eu_count
496
+ elif X.device.type == "npu":
497
+ sm_count = get_npu_core_count()
498
+
499
+ if W is not None:
500
+ # fp32 for numerical stability especially.
501
+ _dW = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
502
+ elementwise_affine = True
503
+ else:
504
+ _dW = None
505
+ elementwise_affine = False
506
+
507
+ if n_cols > BLOCK_SIZE:
508
+ raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
509
+ rows_per_program = math.ceil(n_rows / sm_count)
510
+ grid = (sm_count,)
511
+
512
+ if in_place is True:
513
+ dX = dY
514
+ else:
515
+ dX = torch.zeros_like(dY)
516
+
517
+ # XPU-specific optimization
518
+ kernel_args = {}
519
+ if X.device.type == "xpu":
520
+ set_large_grf_mode(kernel_args)
521
+
522
+ if BLOCK_SIZE > 256 or n_rows < 4096 * 8 or row_mode:
523
+ _rms_norm_backward_kernel[grid](
524
+ dY,
525
+ dY.stride(0),
526
+ dX,
527
+ dX.stride(0),
528
+ X,
529
+ X.stride(0),
530
+ torch_to_triton_dtype[X.dtype],
531
+ W,
532
+ W.stride(0) if elementwise_affine else 0,
533
+ RSTD,
534
+ RSTD.stride(0),
535
+ _dW,
536
+ _dW.stride(0) if elementwise_affine else 0,
537
+ n_rows,
538
+ n_cols,
539
+ offset,
540
+ rows_per_program,
541
+ casting_mode,
542
+ elementwise_affine=elementwise_affine,
543
+ BLOCK_SIZE=BLOCK_SIZE,
544
+ num_warps=num_warps,
545
+ **kernel_args, # XPU-specific optimization
546
+ )
547
+ else:
548
+ BLOCK_ROW = 16
549
+ kernel_args["BLOCK_ROW"] = BLOCK_ROW
550
+ _block_rms_norm_backward_kernel[grid](
551
+ dY,
552
+ dY.stride(0),
553
+ dX,
554
+ dX.stride(0),
555
+ X,
556
+ X.stride(0),
557
+ torch_to_triton_dtype[X.dtype],
558
+ W,
559
+ W.stride(0) if elementwise_affine else 0,
560
+ RSTD,
561
+ RSTD.stride(0),
562
+ _dW,
563
+ _dW.stride(0) if elementwise_affine else 0,
564
+ n_rows,
565
+ n_cols,
566
+ offset,
567
+ casting_mode,
568
+ elementwise_affine=elementwise_affine,
569
+ BLOCK_SIZE=BLOCK_SIZE,
570
+ num_warps=num_warps,
571
+ **kernel_args, # XPU-specific optimization
572
+ )
573
+ dX = dX.view(*shape)
574
+
575
+ if elementwise_affine:
576
+ dW = _dW.sum(dim=0).to(W.dtype)
577
+ else:
578
+ dW = None
579
+
580
+ return dX, dW
581
+
582
+
583
+ class LigerRMSNormFunction(torch.autograd.Function):
584
+ """
585
+ Performs RMSNorm (Root Mean Square Normalization), which normalizes the input tensor `X` using the
586
+ weight tensor `W`, with an optional offset and casting mode.
587
+
588
+ Some models use an 'offset' to shift the weight tensor `W` by a constant value. For example, Gemma
589
+ uses an offset of 1.0, so the computation becomes `(X / RMS(X)) * (W + 1.0)` instead of the usual
590
+ `(X / RMS(X)) * W`. You can pass the offset value as an argument to the forward function.
591
+
592
+ In addition, different models cast their inputs at different places during RMSNorm computation. For
593
+ example, Gemma casts everything to fp32 nefore starting the computation, while Llama casts only the
594
+ inverse RMS to fp32. You can specify the casting mode using the `casting_mode` argument. We currently
595
+ support the following casting modes (they match HuggingFace Transformers' implementations):
596
+ - 'llama': matches the Llama implementation, where only the inverse RMS is computed on fp32.
597
+ - 'gemma': matches the Gemma implementation, where everything is cast to fp32, then computed, then cast back to the original dtype.
598
+ - 'none': no casting is done. The computation is done in the original dtype. This saves memory and is slightly faster, but has more error w.r.t. the original implementation.
599
+
600
+ `in_place` option means whether to in_place modify dY to store dX. This is default to `True` to save memory. However, under certain cases, it can produce incorrect inputs.
601
+ For example, gemma2 uses two rmsnorm sequentially with residual in between. The resesidual part needs dY so it cannot be modified in-place.
602
+ Therefore, for the patching of RMSNorm in gemma2, we set `in_place` to `False`
603
+ """
604
+
605
+ @staticmethod
606
+ @ensure_contiguous
607
+ def forward(ctx, X, W, eps, offset=0.0, casting_mode="llama", in_place=True, row_mode=None):
608
+ """
609
+ X: (B, T, H) or (BxT, H)
610
+ W: (H,)
611
+ """
612
+ if isinstance(X, torch.distributed.tensor.DTensor):
613
+ # Input tensor is output of a tensor parallel module and
614
+ # needs to be gathered to a local tensor to compute
615
+ # RMSE layer norm on each TP worker.
616
+ # TODO: support CP.
617
+ X = X.full_tensor()
618
+
619
+ Y, X, RSTD, BLOCK_SIZE, num_warps, casting_mode = rms_norm_forward(X, W, eps, offset, casting_mode, row_mode)
620
+ ctx.offset = offset
621
+ ctx.casting_mode = casting_mode
622
+ ctx.in_place = in_place
623
+ ctx.row_mode = row_mode
624
+ ctx.BLOCK_SIZE = BLOCK_SIZE
625
+ ctx.num_warps = num_warps
626
+ ctx.elementwise_affine = W is not None
627
+ if W is not None:
628
+ ctx.save_for_backward(X, W, RSTD)
629
+ else:
630
+ ctx.save_for_backward(X, RSTD)
631
+ return Y
632
+
633
+ @staticmethod
634
+ @ensure_contiguous
635
+ def backward(ctx, dY):
636
+ """
637
+ Y: (B, T, H) or (BxT, H)
638
+ """
639
+ if ctx.elementwise_affine:
640
+ X, W, RSTD = ctx.saved_tensors
641
+ else:
642
+ X, RSTD = ctx.saved_tensors
643
+ W = None
644
+
645
+ if isinstance(dY, torch.distributed.tensor.DTensor):
646
+ # Gradients are output of a tensor parallel module and
647
+ # needs to be gathered to a local tensor for computing RMSE layer.
648
+ # TODO: support CP.
649
+ dY = dY.full_tensor()
650
+
651
+ dX, dW = rms_norm_backward(
652
+ dY, X, W, RSTD, ctx.offset, ctx.casting_mode, ctx.BLOCK_SIZE, ctx.num_warps, ctx.in_place, ctx.row_mode
653
+ )
654
+ return dX, dW, None, None, None, None, None
build/torch-rocm/rope.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+
5
+
6
+ @triton.jit
7
+ def _triton_rope(
8
+ q_ptr,
9
+ q_row_stride,
10
+ k_ptr,
11
+ k_row_stride,
12
+ cos,
13
+ cos_row_stride,
14
+ sin,
15
+ sin_row_stride,
16
+ sl,
17
+ bs: tl.constexpr,
18
+ cos_bs: tl.constexpr,
19
+ n_qh: tl.constexpr,
20
+ n_kh: tl.constexpr,
21
+ hd: tl.constexpr,
22
+ pad_n_qh: tl.constexpr,
23
+ pad_n_kh: tl.constexpr,
24
+ pad_hd: tl.constexpr,
25
+ BLOCK_SIZE: tl.constexpr,
26
+ BACKWARD_PASS: tl.constexpr = False,
27
+ ):
28
+ # q size: (bsz, seq_len, num_q_heads, head_dim)
29
+ # q stride: (seq_len * num_q_heads * head_dim, num_q_heads * head_dim, head_dim, 1)
30
+ # k size: (bsz, seq_len, num_kv_heads, head_dim)
31
+ # k stride: (seq_len * num_kv_heads * head_dim, num_kv_heads * head_dim, head_dim, 1)
32
+
33
+ # cos size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
34
+ # stride: (seq_len * head_dim, head_dim, 1)
35
+ pid = tl.program_id(0).to(tl.int64)
36
+
37
+ # locate start address
38
+ q_ptr = q_ptr + pid * q_row_stride
39
+ k_ptr = k_ptr + pid * k_row_stride
40
+
41
+ # ####################################################################
42
+ # get the cos(mθ_{i...d/2}) and sin(mθ_{i...d/2}) for token position
43
+ # m of this program instance
44
+ # ####################################################################
45
+
46
+ # 1. program instances are laid out in a 1D vector of size bsz * seq_len, which
47
+ # effectively represents a 2D grid of size [bsz, seq_len] with seq_len dimension
48
+ # being the fastest changing dimension. Thus we can simply do pid // sl to get the batch index
49
+ # and pid % sl to get the sequence index.
50
+ # 2. We only need the left half of cos and sin matrix because the right half is just
51
+ # a clone of the left half.
52
+ batch_idx = pid // sl
53
+ cos_row_idx = pid % sl
54
+ cos = cos + tl.where(
55
+ cos_bs == 1,
56
+ cos_row_idx * cos_row_stride,
57
+ batch_idx * (sl * cos_row_stride) + cos_row_idx * cos_row_stride,
58
+ )
59
+ sin = sin + tl.where(
60
+ cos_bs == 1,
61
+ cos_row_idx * sin_row_stride,
62
+ batch_idx * (sl * sin_row_stride) + cos_row_idx * sin_row_stride,
63
+ )
64
+
65
+ cos_offsets = tl.arange(0, pad_hd // 2)
66
+ cos_mask = cos_offsets < hd // 2
67
+ cos_row = tl.load(cos + cos_offsets, mask=cos_mask, other=0)
68
+ sin_row = tl.load(sin + cos_offsets, mask=cos_mask, other=0)
69
+
70
+ # ####################################################################
71
+ # Load the left and right half of q and k for the current
72
+ # program instance (i.e. for the current token) separately
73
+ # ####################################################################
74
+ # left half of the head
75
+ first_half_q_offsets = tl.arange(0, pad_n_qh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
76
+ first_half_k_offsets = tl.arange(0, pad_n_kh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
77
+ first_q_mask = (tl.arange(0, pad_n_qh)[:, None] < n_qh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
78
+ first_k_mask = (tl.arange(0, pad_n_kh)[:, None] < n_kh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
79
+ q_tile_1 = tl.load(q_ptr + first_half_q_offsets, mask=first_q_mask, other=0).to(sin_row.dtype)
80
+ k_tile_1 = tl.load(k_ptr + first_half_k_offsets, mask=first_k_mask, other=0).to(sin_row.dtype)
81
+
82
+ # right half of the head
83
+ second_half_q_offsets = first_half_q_offsets + (hd // 2)
84
+ second_half_k_offsets = first_half_k_offsets + (hd // 2)
85
+ second_q_mask = first_q_mask
86
+ second_k_mask = first_k_mask
87
+ q_tile_2 = tl.load(q_ptr + second_half_q_offsets, mask=second_q_mask, other=0).to(sin_row.dtype)
88
+ k_tile_2 = tl.load(k_ptr + second_half_k_offsets, mask=second_k_mask, other=0).to(sin_row.dtype)
89
+
90
+ if not BACKWARD_PASS:
91
+ # y = [x1, x2] * [cos, cos] + [-x2, x1] * [sin, sin]
92
+ new_q_tile_1 = q_tile_1 * cos_row - q_tile_2 * sin_row
93
+ tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
94
+ new_q_tile_2 = q_tile_2 * cos_row + q_tile_1 * sin_row
95
+ tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
96
+
97
+ new_k_tile_1 = k_tile_1 * cos_row - k_tile_2 * sin_row
98
+ tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
99
+ new_k_tile_2 = k_tile_2 * cos_row + k_tile_1 * sin_row
100
+ tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
101
+ else:
102
+ # with some math, we can get:
103
+ # dy = [dx1, dx2] * [cos, cos] + [-dx2, dx1] * [-sin, -sin]
104
+ new_q_tile_1 = q_tile_1 * cos_row + q_tile_2 * sin_row
105
+ tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
106
+ new_q_tile_2 = q_tile_2 * cos_row - q_tile_1 * sin_row
107
+ tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
108
+
109
+ new_k_tile_1 = k_tile_1 * cos_row + k_tile_2 * sin_row
110
+ tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
111
+ new_k_tile_2 = k_tile_2 * cos_row - k_tile_1 * sin_row
112
+ tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
113
+
114
+
115
+ def rope_forward(q, k, cos, sin):
116
+ # transpose it back to the physical shape because Triton looks at the physical storage
117
+ # note: q and k are incontiguous before the transformation and will become contiguous after transpose
118
+ q = q.transpose(1, 2)
119
+ k = k.transpose(1, 2)
120
+
121
+ batch_size, seq_len, n_q_head, head_dim = q.shape
122
+ n_kv_head = k.shape[2]
123
+ pad_hd = triton.next_power_of_2(head_dim)
124
+ pad_n_q_head = triton.next_power_of_2(n_q_head)
125
+ pad_n_kv_head = triton.next_power_of_2(n_kv_head)
126
+ BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
127
+
128
+ n_row = batch_size * seq_len
129
+
130
+ # ensure tensors passed into the kernel are contiguous. It will be no-op if they are already contiguous
131
+ q = q.contiguous()
132
+ k = k.contiguous()
133
+ cos = cos.contiguous()
134
+ sin = sin.contiguous()
135
+ cos_batch_size = cos.shape[0]
136
+
137
+ _triton_rope[(n_row,)](
138
+ q,
139
+ q.stride(1),
140
+ k,
141
+ k.stride(1),
142
+ cos,
143
+ cos.stride(-2),
144
+ sin,
145
+ sin.stride(-2),
146
+ seq_len,
147
+ batch_size,
148
+ cos_batch_size,
149
+ n_q_head,
150
+ n_kv_head,
151
+ head_dim,
152
+ pad_n_q_head,
153
+ pad_n_kv_head,
154
+ pad_hd,
155
+ BLOCK_SIZE=BLOCK_SIZE,
156
+ BACKWARD_PASS=False,
157
+ )
158
+ return q.transpose(1, 2), k.transpose(1, 2), cos, sin
159
+
160
+
161
+ def rope_backward(dq, dk, cos, sin):
162
+ dq = dq.transpose(1, 2)
163
+ dk = dk.transpose(1, 2)
164
+
165
+ batch_size, seq_len, n_q_head, head_dim = dq.shape
166
+ cos_batch_size = cos.shape[0]
167
+ n_kv_head = dk.shape[2]
168
+ pad_hd = triton.next_power_of_2(head_dim)
169
+ pad_n_q_head = triton.next_power_of_2(n_q_head)
170
+ pad_n_kv_head = triton.next_power_of_2(n_kv_head)
171
+ BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
172
+
173
+ n_row = batch_size * seq_len
174
+
175
+ # ensure dq and dk are contiguous
176
+ dq = dq.contiguous()
177
+ dk = dk.contiguous()
178
+
179
+ # backward is similar to forward except swapping few ops
180
+ _triton_rope[(n_row,)](
181
+ dq,
182
+ dq.stride(1),
183
+ dk,
184
+ dk.stride(1),
185
+ cos,
186
+ cos.stride(-2),
187
+ sin,
188
+ sin.stride(-2),
189
+ seq_len,
190
+ batch_size,
191
+ cos_batch_size,
192
+ n_q_head,
193
+ n_kv_head,
194
+ head_dim,
195
+ pad_n_q_head,
196
+ pad_n_kv_head,
197
+ pad_hd,
198
+ BLOCK_SIZE=BLOCK_SIZE,
199
+ BACKWARD_PASS=True,
200
+ )
201
+ return dq.transpose(1, 2), dk.transpose(1, 2)
202
+
203
+
204
+ class LigerRopeFunction(torch.autograd.Function):
205
+ """
206
+ Triton implementation of the Rotary Positional Embedding (RoPE) operation. Please note that
207
+ this implements the HuggingFace Llama & Mistral version, whose rotation matrix is slightly different
208
+ than the original RoPE paper.
209
+
210
+ Please find the corresponding HuggingFace implementation here:
211
+ https://github.com/huggingface/transformers/blob/v4.40.2/src/transformers/models/llama/modeling_llama.py#L184
212
+
213
+ For more details about the rotation matrix used here, please refer to:
214
+ https://discuss.huggingface.co/t/is-llama-rotary-embedding-implementation-correct/44509/2
215
+ """
216
+
217
+ @staticmethod
218
+ def forward(ctx, q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
219
+ """
220
+ q size: (bsz, n_q_head, seq_len, head_dim)
221
+ k size: (bsz, n_kv_head, seq_len, head_dim)
222
+ cos size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
223
+ sin size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
224
+ """
225
+ q, k, cos, sin = rope_forward(q, k, cos, sin)
226
+ ctx.save_for_backward(cos, sin)
227
+ return q, k
228
+
229
+ def backward(ctx, dq, dk):
230
+ """
231
+ dq size: (bsz, n_q_head, seq_len, head_dim)
232
+ dk size: (bsz, n_kv_head, seq_len, head_dim)
233
+ cos size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
234
+ sin size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
235
+ """
236
+
237
+ cos, sin = ctx.saved_tensors
238
+ dq, dk = rope_backward(dq, dk, cos, sin)
239
+ return dq, dk, None, None, None, None
build/torch-rocm/swiglu.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+
5
+ from .utils import calculate_settings
6
+ from .utils import ensure_contiguous
7
+
8
+
9
+ @triton.jit
10
+ def silu(x):
11
+ return x * tl.sigmoid(x)
12
+
13
+
14
+ @triton.jit
15
+ def _swiglu_forward_kernel(
16
+ a_ptr, b_ptr, c_ptr, stride, gate_multiplier, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr
17
+ ):
18
+ program_id = tl.program_id(0).to(tl.int64)
19
+
20
+ # locate start index
21
+ a_ptr += program_id * stride
22
+ b_ptr += program_id * stride
23
+ c_ptr += program_id * stride
24
+
25
+ col_offsets = tl.arange(0, BLOCK_SIZE)
26
+ mask = col_offsets < n_cols
27
+
28
+ # sigmoid requires type float32
29
+ a_row = tl.load(a_ptr + col_offsets, mask=mask, other=0).to(tl.float32) * gate_multiplier
30
+ b_row = tl.load(b_ptr + col_offsets, mask=mask, other=0)
31
+ c_row = silu(a_row).cast(b_row.dtype) * b_row
32
+ tl.store(c_ptr + col_offsets, c_row, mask=mask)
33
+
34
+
35
+ @triton.jit
36
+ def _swiglu_backward_kernel(
37
+ dc_ptr, a_ptr, b_ptr, stride, gate_multiplier, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr
38
+ ):
39
+ program_id = tl.program_id(0).to(tl.int64)
40
+
41
+ # locate start index
42
+ dc_ptr += program_id * stride
43
+ a_ptr += program_id * stride
44
+ b_ptr += program_id * stride
45
+
46
+ col_offsets = tl.arange(0, BLOCK_SIZE)
47
+ mask = col_offsets < n_cols
48
+
49
+ dc_row = tl.load(dc_ptr + col_offsets, mask=mask, other=0)
50
+ # sigmoid requires type float32
51
+ a_row = tl.load(a_ptr + col_offsets, mask=mask, other=0).to(tl.float32) * gate_multiplier
52
+ b_row = tl.load(b_ptr + col_offsets, mask=mask, other=0)
53
+
54
+ # recomputation to save memory. a_row already holds a * gate_multiplier.
55
+ sig_a = tl.sigmoid(a_row)
56
+ silu_a = a_row * sig_a
57
+ db_row = dc_row * silu_a
58
+ # chain rule pulls an extra factor of gate_multiplier through the pre-activation scaling
59
+ da_row = dc_row * (silu_a * (1 - sig_a) + sig_a) * b_row * gate_multiplier
60
+
61
+ tl.store(a_ptr + col_offsets, da_row, mask=mask)
62
+ tl.store(b_ptr + col_offsets, db_row, mask=mask)
63
+
64
+
65
+ def swiglu_forward(a, b, gate_multiplier: float = 1.0):
66
+ ori_shape = a.shape
67
+
68
+ n_cols = ori_shape[-1]
69
+ a = a.view(-1, n_cols)
70
+ b = b.view(-1, n_cols)
71
+ c = torch.empty_like(a)
72
+ n_rows = a.shape[0]
73
+
74
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
75
+
76
+ _swiglu_forward_kernel[(n_rows,)](
77
+ a,
78
+ b,
79
+ c,
80
+ c.stride(-2),
81
+ float(gate_multiplier),
82
+ n_cols=n_cols,
83
+ BLOCK_SIZE=BLOCK_SIZE,
84
+ num_warps=num_warps,
85
+ )
86
+ return a, b, c.view(*ori_shape)
87
+
88
+
89
+ def swiglu_backward(a, b, dc, gate_multiplier: float = 1.0):
90
+ ori_shape = dc.shape
91
+ n_cols = ori_shape[-1]
92
+ dc = dc.view(-1, n_cols)
93
+ n_rows = dc.shape[0]
94
+
95
+ BLOCK_SIZE, num_warps = calculate_settings(n_cols)
96
+
97
+ _swiglu_backward_kernel[(n_rows,)](
98
+ dc,
99
+ a,
100
+ b,
101
+ dc.stride(-2),
102
+ float(gate_multiplier),
103
+ n_cols=n_cols,
104
+ BLOCK_SIZE=BLOCK_SIZE,
105
+ num_warps=num_warps,
106
+ )
107
+ return a.view(*ori_shape), b.view(*ori_shape)
108
+
109
+
110
+ class LigerSiLUMulFunction(torch.autograd.Function):
111
+ @staticmethod
112
+ @ensure_contiguous
113
+ def forward(ctx, a, b, gate_multiplier: float = 1.0, down_multiplier: float = 1.0):
114
+ gate_multiplier = float(gate_multiplier)
115
+ down_multiplier = float(down_multiplier)
116
+ ctx.gate_multiplier = gate_multiplier
117
+ ctx.down_multiplier = down_multiplier
118
+
119
+ if isinstance(a, torch.distributed.tensor.DTensor) or isinstance(b, torch.distributed.tensor.DTensor):
120
+ device_mesh, placements = (
121
+ (a.device_mesh, a.placements)
122
+ if isinstance(a, torch.distributed.tensor.DTensor)
123
+ else (b.device_mesh, b.placements)
124
+ )
125
+
126
+ # Assume that full tensors are gathered before and identical across
127
+ # the associated process groups.
128
+ if not isinstance(a, torch.distributed.tensor.DTensor):
129
+ a = torch.distributed.tensor.distribute_tensor(a, device_mesh=device_mesh, placements=placements)
130
+ if not isinstance(b, torch.distributed.tensor.DTensor):
131
+ b = torch.distributed.tensor.distribute_tensor(b, device_mesh=device_mesh, placements=placements)
132
+ a_local, b_local, c_local = swiglu_forward(a.to_local(), b.to_local(), gate_multiplier)
133
+ if down_multiplier != 1.0:
134
+ c_local = c_local * down_multiplier
135
+ ctx.save_for_backward(a_local, b_local)
136
+ ctx.dtensor_metadata = (device_mesh, placements)
137
+ return torch.distributed.tensor.DTensor.from_local(c_local, device_mesh, placements)
138
+ else:
139
+ a, b, c = swiglu_forward(a, b, gate_multiplier)
140
+ if down_multiplier != 1.0:
141
+ c = c * down_multiplier
142
+ ctx.save_for_backward(a, b)
143
+ ctx.dtensor_metadata = None
144
+ return c
145
+
146
+ @staticmethod
147
+ @ensure_contiguous
148
+ def backward(ctx, dc):
149
+ a, b = ctx.saved_tensors
150
+ gate_multiplier = ctx.gate_multiplier
151
+ down_multiplier = ctx.down_multiplier
152
+
153
+ if ctx.dtensor_metadata is not None:
154
+ device_mesh, placements = ctx.dtensor_metadata
155
+
156
+ # Assume that full tensors are gathered before and identical across
157
+ # the associated process groups.
158
+ dc_local = (
159
+ dc.to_local()
160
+ if isinstance(dc, torch.distributed.tensor.DTensor)
161
+ else torch.distributed.tensor.distribute_tensor(dc, device_mesh=device_mesh, placements=placements)
162
+ )
163
+ if down_multiplier != 1.0:
164
+ dc_local = dc_local * down_multiplier
165
+ a_local, b_local = swiglu_backward(a, b, dc_local, gate_multiplier)
166
+ return (
167
+ torch.distributed.tensor.DTensor.from_local(a_local, device_mesh, placements),
168
+ torch.distributed.tensor.DTensor.from_local(b_local, device_mesh, placements),
169
+ None,
170
+ None,
171
+ )
172
+
173
+ if down_multiplier != 1.0:
174
+ dc = dc * down_multiplier
175
+ a, b = swiglu_backward(a, b, dc, gate_multiplier)
176
+ return a, b, None, None
build/torch-rocm/tiled_mlp.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+
3
+ from typing import Callable
4
+ from typing import List
5
+ from typing import Optional
6
+
7
+ import torch
8
+ import torch.distributed as dist
9
+
10
+ from .utils import ensure_contiguous
11
+
12
+
13
+ class LigerTiledMLPFunction(torch.autograd.Function):
14
+ """
15
+ Based on DeepSpeed's TiledMLP:
16
+ https://github.com/deepspeedai/DeepSpeed/blob/v0.18.2/deepspeed/runtime/sequence_parallel/ulysses_sp.py#L838
17
+
18
+ Perform a tiled MLP computation to massively reduce memory usage needed to compute MLP
19
+ when using very long sequence lengths.
20
+
21
+ This module re-computes `forward` in the `backward`. So the `forward` occurs twice each iteration.
22
+ And if you're using activation checkpointing it then occurs thrice.
23
+
24
+ Args:
25
+ fn: the function to call on sharded inputs (e.g., mlp.forward)
26
+ mlp_module: the MLP nn.Module object
27
+ x: the input to MLP.forward (hidden_states)
28
+ shards: how many shards to use
29
+ compute_params: a list of weights engaged in the compute
30
+
31
+ Returns:
32
+ the computed hidden_states
33
+ """
34
+
35
+ @staticmethod
36
+ @ensure_contiguous
37
+ def forward(
38
+ ctx,
39
+ fn: Callable,
40
+ mlp_module: torch.nn.Module,
41
+ x: torch.Tensor,
42
+ shards: int,
43
+ compute_params: Optional[List[torch.nn.Parameter]] = None,
44
+ ) -> torch.Tensor:
45
+ ctx.fn = fn
46
+ ctx.mlp_module = mlp_module
47
+ ctx.shards = shards
48
+ ctx.compute_params = compute_params
49
+ ctx.save_for_backward(x)
50
+
51
+ # x.shape could be [bs, seqlen, hidden_size] or [seqlen, hidden_size] (moe experts)
52
+ x_shards = list(torch.chunk(x, chunks=shards, dim=-2))
53
+ with torch.no_grad():
54
+ output_shards = [fn(mlp_module, x_shard) for x_shard in x_shards]
55
+ output_unsharded = torch.cat(output_shards, dim=-2)
56
+
57
+ return output_unsharded
58
+
59
+ @staticmethod
60
+ @ensure_contiguous
61
+ def backward(ctx, *grads) -> tuple:
62
+ fn = ctx.fn
63
+ (x,) = ctx.saved_tensors
64
+ mlp_module = ctx.mlp_module
65
+ shards = ctx.shards
66
+ compute_params = ctx.compute_params
67
+
68
+ grad_output = grads[0]
69
+
70
+ x_requires_grad = x.requires_grad
71
+ x = x.detach()
72
+ # detach() unsets x.requires_grad, so restore it
73
+ x.requires_grad_(x_requires_grad)
74
+
75
+ # x.shape could be [bs, seqlen, in_features] or [seqlen, in_features] (moe experts)
76
+ in_features = x.shape[-1]
77
+ out_features = grad_output.shape[-1]
78
+ x_shape_orig = x.shape
79
+
80
+ # flatten bs+seqlen to avoid having stride issues when narrowing into seqlen w/ bs>1
81
+ # NOTE: input and output feature dimensions may differ, e.g.
82
+ # Linear(in_features, out_features), so flatten x and grad_output with their own last dims.
83
+ x = x.view(-1, in_features)
84
+ incoming_grad = grad_output.view(-1, out_features)
85
+ x_grad = torch.zeros_like(x)
86
+
87
+ x_shards = list(torch.chunk(x, chunks=shards, dim=0))
88
+ incoming_grad_shards = list(torch.chunk(incoming_grad, chunks=shards, dim=0))
89
+
90
+ # ZeRO-3 partitioned parameters carry a ds_id; collect them once so the per-shard loop only
91
+ # flips the ready flag. Parameters on other backends have no ds_id and are left untouched.
92
+ ds_params = [p for p in compute_params if hasattr(p, "ds_id")] if compute_params else []
93
+
94
+ for i, x_shard in enumerate(x_shards):
95
+ x_shard.requires_grad_(x_requires_grad)
96
+
97
+ # if seqlen is not exactly divisible by shards the last step will be shorter than shard_step
98
+ shard_step = x_shards[i].shape[0]
99
+ shard_offset = i * x_shards[0].shape[0]
100
+
101
+ x_shard.grad = x_grad.narrow(0, shard_offset, shard_step).view_as(x_shard)
102
+ incoming_grad_shard = incoming_grad_shards[i]
103
+
104
+ # Defer DeepSpeed's reduction until the last shard has accumulated into param.grad; the flag
105
+ # is read by ZeRO's hook during each shard's backward, so it must be set per shard.
106
+ for param in ds_params:
107
+ param.ds_grad_is_ready = i + 1 == len(x_shards)
108
+
109
+ with torch.enable_grad():
110
+ output = fn(mlp_module, x_shard)
111
+ torch.autograd.backward(output, incoming_grad_shard)
112
+
113
+ # unflatten
114
+ x_grad = x_grad.view(x_shape_orig)
115
+
116
+ return (None, None, x_grad, None, None)
117
+
118
+
119
+ def apply_tiled_mlp(
120
+ fn: Callable,
121
+ mlp_module: torch.nn.Module,
122
+ x: torch.Tensor,
123
+ num_shards: Optional[int] = None,
124
+ compute_params: Optional[List[torch.nn.Parameter]] = None,
125
+ ) -> torch.Tensor:
126
+ """
127
+ Apply tiled MLP computation for memory efficiency.
128
+
129
+ Args:
130
+ fn: the function to call on sharded inputs (e.g., lambda module, x: module(x))
131
+ mlp_module: the MLP nn.Module object
132
+ x: the input tensor with shape [bs, seqlen, hidden_size] or [seqlen, hidden_size]
133
+ num_shards: number of shards to use. If None, automatically calculated as ceil(seqlen / hidden_size)
134
+ compute_params: list of parameters for DeepSpeed ZeRO optimization
135
+ """
136
+ if num_shards is None:
137
+ # x.shape could be [bs, seqlen, hidden_size] or [seqlen, hidden_size]
138
+ hidden_size = x.shape[-1]
139
+ seqlen = x.shape[-2]
140
+ num_shards = math.ceil(seqlen / hidden_size)
141
+
142
+ # Ensure num_shards is at least 1
143
+ num_shards = max(1, num_shards)
144
+
145
+ # All ranks must run the same number of shards: a sharded-parameter backend (DeepSpeed ZeRO-3, FSDP)
146
+ # gathers weights inside each shard's recompute, so a rank that runs fewer shards stops participating
147
+ # in those collectives and deadlocks the others. Harmonize on the per-rank maximum.
148
+ if dist.is_available() and dist.is_initialized() and dist.get_world_size() > 1:
149
+ num_shards_tensor = torch.tensor(num_shards, device=x.device)
150
+ dist.all_reduce(num_shards_tensor, op=dist.ReduceOp.MAX)
151
+ num_shards = int(num_shards_tensor.item())
152
+
153
+ return LigerTiledMLPFunction.apply(
154
+ fn,
155
+ mlp_module,
156
+ x,
157
+ num_shards,
158
+ compute_params,
159
+ )
build/torch-rocm/tvd.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Literal
2
+ from typing import Optional
3
+
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+
8
+ from .utils import ensure_contiguous
9
+
10
+ MAX_FUSED_SIZE = 65536 // 4
11
+
12
+ REDUCTION_LITERAL = Literal["none", "sum", "mean", "batchmean"]
13
+
14
+ _REDUCTION_MODE_NONE = tl.constexpr(0)
15
+ _REDUCTION_MODE_SUM = tl.constexpr(1)
16
+ _REDUCTION_MODE_MEAN = tl.constexpr(2)
17
+ _REDUCTION_MODE_BATCHMEAN = tl.constexpr(3)
18
+
19
+ _str_to_reduction_mode = {
20
+ "none": _REDUCTION_MODE_NONE.value,
21
+ "sum": _REDUCTION_MODE_SUM.value,
22
+ "mean": _REDUCTION_MODE_MEAN.value,
23
+ "batchmean": _REDUCTION_MODE_BATCHMEAN.value,
24
+ }
25
+
26
+
27
+ def get_num_warps(BLOCK_SIZE):
28
+ num_warps = 4
29
+ if BLOCK_SIZE >= 32768:
30
+ num_warps = 32
31
+ elif BLOCK_SIZE >= 8192:
32
+ num_warps = 16
33
+ elif BLOCK_SIZE >= 2048:
34
+ num_warps = 8
35
+
36
+ return num_warps
37
+
38
+
39
+ @triton.jit
40
+ def _tv_distance_kernel(
41
+ p_ptr,
42
+ p_stride,
43
+ q_ptr,
44
+ q_stride,
45
+ loss_ptr,
46
+ loss_stride,
47
+ grads_ptr,
48
+ grads_stride,
49
+ label_ptr,
50
+ ignore_index: tl.constexpr,
51
+ n_cols,
52
+ scale, # pre-computed reduction scale for gradients (fused into kernel)
53
+ BLOCK_SIZE: tl.constexpr,
54
+ HAS_LABEL: tl.constexpr,
55
+ reduction: tl.constexpr = _REDUCTION_MODE_BATCHMEAN,
56
+ ):
57
+ pid = tl.program_id(0).to(tl.int64)
58
+ p_ptr += pid * p_stride
59
+ q_ptr += pid * q_stride
60
+ loss_ptr += pid * loss_stride
61
+ grads_ptr += pid * grads_stride
62
+ label_ptr += pid
63
+
64
+ base_offsets = tl.arange(0, BLOCK_SIZE)
65
+
66
+ if HAS_LABEL:
67
+ label = tl.load(label_ptr)
68
+ if label == ignore_index:
69
+ for i in range(0, n_cols, BLOCK_SIZE):
70
+ offsets = i + base_offsets
71
+ mask = offsets < n_cols
72
+ tl.store(grads_ptr + offsets, 0.0, mask=mask)
73
+ if reduction == _REDUCTION_MODE_NONE:
74
+ tl.store(loss_ptr + offsets, 0.0, mask=mask)
75
+ return
76
+
77
+ loss_sum = 0.0
78
+ for i in range(0, n_cols, BLOCK_SIZE):
79
+ offsets = i + base_offsets
80
+ mask = offsets < n_cols
81
+
82
+ p = tl.load(p_ptr + offsets, mask=mask, other=0.0)
83
+ q = tl.load(q_ptr + offsets, mask=mask, other=0.0)
84
+
85
+ # TVD(P || Q) = 0.5 * |P - Q|
86
+ tv_loss = 0.5 * tl.abs(p - q)
87
+
88
+ # Fuse reduction scaling into gradient computation (eliminates separate Python division)
89
+ grad_res = tl.where(p > q, 0.5 * scale, -0.5 * scale)
90
+
91
+ tl.store(grads_ptr + offsets, grad_res, mask=mask)
92
+
93
+ if reduction == _REDUCTION_MODE_NONE:
94
+ tl.store(loss_ptr + offsets, tv_loss, mask=mask)
95
+ else:
96
+ loss_sum += tl.sum(tv_loss, axis=0)
97
+
98
+ if reduction != _REDUCTION_MODE_NONE:
99
+ # Fuse reduction scaling into loss (same scale as gradients; avoids Python division)
100
+ tl.store(loss_ptr, loss_sum * scale)
101
+
102
+
103
+ def tv_distance_forward_triton(p, q, shift_labels, reduction, ignore_index, has_label):
104
+ BT, V = p.shape
105
+
106
+ BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
107
+ num_warps = get_num_warps(BLOCK_SIZE)
108
+
109
+ grid = (BT,)
110
+
111
+ reduction = _str_to_reduction_mode[reduction]
112
+
113
+ out_size = (BT, V) if reduction == _REDUCTION_MODE_NONE.value else (BT,)
114
+ output_tensor = torch.zeros(out_size, device=p.device, dtype=torch.float32)
115
+ grads = torch.empty_like(p)
116
+
117
+ n_non_ignore = (shift_labels != ignore_index).sum().item() if has_label else BT
118
+
119
+ # Pre-compute gradient scale factor (fused into kernel to avoid separate division)
120
+ if reduction == _REDUCTION_MODE_BATCHMEAN.value:
121
+ scale = 1.0 / n_non_ignore
122
+ elif reduction == _REDUCTION_MODE_MEAN.value:
123
+ scale = 1.0 / (n_non_ignore * V)
124
+ else:
125
+ scale = 1.0
126
+
127
+ _tv_distance_kernel[grid](
128
+ p,
129
+ p.stride(0),
130
+ q,
131
+ q.stride(0),
132
+ output_tensor,
133
+ output_tensor.stride(0),
134
+ grads,
135
+ grads.stride(0),
136
+ shift_labels if has_label else torch.empty(1, device=p.device),
137
+ ignore_index,
138
+ V,
139
+ scale,
140
+ BLOCK_SIZE=BLOCK_SIZE,
141
+ HAS_LABEL=has_label,
142
+ num_warps=num_warps,
143
+ reduction=reduction,
144
+ )
145
+
146
+ # Loss and gradients are already scaled inside the kernel — no separate division needed
147
+ if reduction in (_REDUCTION_MODE_BATCHMEAN.value, _REDUCTION_MODE_MEAN.value):
148
+ return output_tensor.sum(), grads
149
+ elif reduction == _REDUCTION_MODE_SUM.value:
150
+ return output_tensor.sum(dim=0), grads
151
+ else:
152
+ return output_tensor, grads
153
+
154
+
155
+ def tvd_backward_triton(grad_output, grads):
156
+ # If cross entropy is the last layer, grad_output is 1.0. Skip the mul then.
157
+ if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
158
+ return grads
159
+
160
+ return grads * grad_output
161
+
162
+
163
+ class LigerTVDLossFunction(torch.autograd.Function):
164
+ """
165
+ Class implementing the forward and backward pass for the Total Variation Distance Loss using Triton.
166
+ """
167
+
168
+ @staticmethod
169
+ @ensure_contiguous
170
+ def forward(
171
+ ctx,
172
+ p: torch.Tensor,
173
+ q: torch.Tensor,
174
+ shift_labels: Optional[torch.Tensor] = None,
175
+ reduction: REDUCTION_LITERAL = "batchmean",
176
+ ignore_index: int = -100,
177
+ ) -> torch.Tensor:
178
+ """A forward pass for the Total Variation Distance Loss.
179
+
180
+ Args:
181
+ ctx: Torch autograd context
182
+ p (torch.Tensor): A tensor of shape (BT, V) containing the first distribution.
183
+ q (torch.Tensor): A tensor of shape (BT, V) containing the second distribution.
184
+ shift_labels (Optional[torch.Tensor]): A tensor of shape (BT,) containing the labels.
185
+ reduction (REDUCTION_LITERAL, optional): The reduction method to be applied. Defaults to "batchmean".
186
+ ignore_index (int, optional): The index to ignore during loss calculation. Defaults to -100.
187
+
188
+ Returns:
189
+ torch.Tensor: The computed Total Variation Distance Loss.
190
+ """
191
+ has_label = False
192
+ if shift_labels is not None:
193
+ assert shift_labels.shape == (p.shape[0],), (
194
+ f"the shape of shift_labels must be (BT,). Got: {shift_labels.shape}"
195
+ )
196
+ shift_labels = shift_labels.contiguous()
197
+ has_label = True
198
+
199
+ loss, grads = tv_distance_forward_triton(p, q, shift_labels, reduction, ignore_index, has_label)
200
+ ctx.save_for_backward(grads)
201
+ return loss
202
+
203
+ @staticmethod
204
+ @ensure_contiguous
205
+ def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
206
+ """A backward pass for the Total Variation Distance Loss.
207
+
208
+ Args:
209
+ ctx: Torch autograd context
210
+ grad_output (torch.Tensor): The gradient of the loss with respect to the output.
211
+
212
+ Returns:
213
+ tuple[torch.Tensor, None, None, None, None]: The gradient of the loss with respect to the inputs.
214
+ """
215
+ (grads,) = ctx.saved_tensors
216
+ grads = tvd_backward_triton(grad_output, grads)
217
+
218
+ return grads, None, None, None, None
build/torch-rocm/utils.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file incorporates code from Unsloth licensed under the Apache License, Version 2.0.
3
+ See the original Unsloth repository at https://github.com/unslothai/unsloth.
4
+
5
+ The following line
6
+ https://github.com/linkedin/Liger-Kernel/blob/7382a8761f9af679482b968f9348013d933947c7/src/liger_kernel/ops/utils.py#L23
7
+ is based on code from Unsloth, located at:
8
+ https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/utils.py#L43
9
+
10
+ Modifications made by Yanning Chen, 2024.
11
+ """
12
+
13
+ import functools
14
+ import importlib
15
+ import operator
16
+
17
+ from typing import Callable
18
+
19
+ import torch
20
+ import triton
21
+ import triton.language as tl
22
+
23
+ from packaging.version import Version
24
+
25
+
26
+ def is_npu_available() -> bool:
27
+ """Detect Ascend NPU availability."""
28
+ try:
29
+ from transformers.utils import is_torch_npu_available
30
+
31
+ return is_torch_npu_available()
32
+ except Exception:
33
+ return False
34
+
35
+
36
+ def infer_device():
37
+ """
38
+ Get current device name based on available devices
39
+ """
40
+ if torch.cuda.is_available(): # Works for both Nvidia and AMD
41
+ return "cuda"
42
+ # Use Ascend NPU if available (torch.npu)
43
+ elif is_npu_available():
44
+ return "npu"
45
+ # XPU (Intel) if available
46
+ elif torch.xpu.is_available():
47
+ return "xpu"
48
+ else:
49
+ return "cpu"
50
+
51
+
52
+ def is_hip() -> bool:
53
+ return torch.version.hip is not None
54
+
55
+
56
+ def ensure_contiguous(fn):
57
+ @functools.wraps(fn)
58
+ def wrapper(ctx, *args, **kwargs):
59
+ def maybe_to_contiguous(x):
60
+ return x.contiguous() if isinstance(x, torch.Tensor) else x
61
+
62
+ args = [maybe_to_contiguous(arg) for arg in args]
63
+ kwargs = {k: maybe_to_contiguous(v) for k, v in kwargs.items()}
64
+ return fn(ctx, *args, **kwargs)
65
+
66
+ return wrapper
67
+
68
+
69
+ def calculate_settings(n):
70
+ # reference: https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/utils.py#L43
71
+
72
+ MAX_FUSED_SIZE = 65536
73
+ BLOCK_SIZE = triton.next_power_of_2(n)
74
+ if BLOCK_SIZE > MAX_FUSED_SIZE:
75
+ raise RuntimeError(
76
+ f"Cannot launch Triton kernel since n = {n} exceeds the recommended Triton blocksize = {MAX_FUSED_SIZE}."
77
+ )
78
+
79
+ num_warps = 4
80
+ if BLOCK_SIZE >= 32768:
81
+ num_warps = 32 if not is_hip() else 16
82
+ elif BLOCK_SIZE >= 8192:
83
+ num_warps = 16
84
+ elif BLOCK_SIZE >= 2048:
85
+ num_warps = 8
86
+ return BLOCK_SIZE, num_warps
87
+
88
+
89
+ def compare_version(package: str, operator: Callable, target: str):
90
+ try:
91
+ pkg = importlib.import_module(package)
92
+ except ImportError:
93
+ return False
94
+ pkg_version = Version(pkg.__version__)
95
+ return operator(pkg_version, Version(target))
96
+
97
+
98
+ def get_amp_custom_fwd_bwd() -> Callable:
99
+ device = infer_device()
100
+ if compare_version("torch", operator.ge, "2.4.0"):
101
+ return (
102
+ functools.partial(torch.amp.custom_fwd, device_type=device),
103
+ functools.partial(torch.amp.custom_bwd, device_type=device),
104
+ )
105
+ if hasattr(torch, "npu") and getattr(torch.npu, "amp", None) is not None:
106
+ return torch.npu.amp.custom_fwd, torch.npu.amp.custom_bwd
107
+ return torch.cuda.amp.custom_fwd, torch.cuda.amp.custom_bwd
108
+
109
+
110
+ amp_custom_fwd, amp_custom_bwd = get_amp_custom_fwd_bwd()
111
+
112
+
113
+ torch_to_triton_dtype = {
114
+ torch.float32: tl.float32,
115
+ torch.float16: tl.float16,
116
+ torch.bfloat16: tl.bfloat16,
117
+ }
118
+
119
+
120
+ @triton.jit
121
+ def element_mul_kernel(
122
+ X_ptr,
123
+ X_stride,
124
+ grad_output_ptr,
125
+ n_cols,
126
+ BLOCK_SIZE: tl.constexpr,
127
+ ):
128
+ """
129
+ This function multiplies each element of the tensor pointed by X_ptr with the value pointed by grad_output_ptr.
130
+ The multiplication is performed in-place on the tensor pointed by X_ptr.
131
+
132
+ Parameters:
133
+ X_ptr: Pointer to the input tensor.
134
+ X_stride (int): The stride of the input tensor.
135
+ grad_output_ptr: Pointer to the gradient output value.
136
+ n_cols (int): The number of columns in the input tensor.
137
+ BLOCK_SIZE (int): The block size for Triton operations.
138
+ """
139
+
140
+ # Get the program ID and convert it to int64 to avoid overflow
141
+ program_id = tl.program_id(0).to(tl.int64)
142
+
143
+ # Locate the start index
144
+ X_ptr += program_id * X_stride
145
+
146
+ # Load the gradient output value
147
+ grad_output = tl.load(grad_output_ptr)
148
+
149
+ # Perform the element-wise multiplication
150
+ for i in range(0, n_cols, BLOCK_SIZE):
151
+ X_offsets = i + tl.arange(0, BLOCK_SIZE)
152
+ X_block = tl.load(X_ptr + X_offsets, mask=X_offsets < n_cols)
153
+ tl.store(X_ptr + X_offsets, X_block * grad_output, mask=X_offsets < n_cols)
154
+
155
+
156
+ def get_npu_core_count(default: int = 20) -> int:
157
+ """Return NPU vector core count.
158
+ Fallback to `default` if Triton runtime or NPU device is unavailable.
159
+ """
160
+ try:
161
+ utils = triton.runtime.driver.active.utils
162
+ props = utils.get_device_properties(0)
163
+ return int(props.get("num_vectorcore", default))
164
+ except Exception:
165
+ return default
166
+
167
+
168
+ def set_large_grf_mode(kernel_args: dict):
169
+ """Set large GRF mode for XPU devices."""
170
+ # On XPU triton installed along with pytorch-xpu will be called `pytorch-triton-xpu`,
171
+ # triton XPU installed from source will be called `triton`.
172
+ if compare_version("pytorch-triton-xpu", operator.ge, "3.6.0") or compare_version("triton", operator.ge, "3.6.0"):
173
+ kernel_args["grf_mode"] = "256"
174
+ else:
175
+ # API was changed in https://github.com/intel/intel-xpu-backend-for-triton/pull/5430
176
+ kernel_args["grf_mode"] = "large"