base_IIXIV / fla /modules /conv /triton /kernels.py
mainline777's picture
Duplicate from silx-ai/Quasar-Preview
41865df
Raw
History Blame Contribute Delete
22.6 kB
import torch
import triton
import triton.language as tl
from einops import rearrange
from fla.utils import IS_AMD, autotune_cache_kwargs, input_guard
NUM_WARPS_AUTOTUNE = [2, 4, 8, 16] if IS_AMD else [4, 8, 16, 32]
STATIC_WARPS = 32 if not IS_AMD else 16
@triton.heuristics({
'HAS_WEIGHT': lambda args: args['weight'] is not None,
'HAS_BIAS': lambda args: args['bias'] is not None,
'HAS_RESIDUAL': lambda args: args['residual'] is not None,
'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
})
@triton.autotune(
configs=[
triton.Config({'BD': BD}, num_warps=num_warps)
for BD in [16, 32, 64, 128]
for num_warps in NUM_WARPS_AUTOTUNE
],
key=['D', 'W', 'NB'],
**autotune_cache_kwargs,
)
@triton.jit
def causal_conv1d_fwd_kernel(
x,
y,
weight,
bias,
residual,
cu_seqlens,
initial_state,
chunk_indices,
B,
T,
stride_x_n,
stride_x_t,
stride_x_d,
D: tl.constexpr,
W: tl.constexpr,
BT: tl.constexpr,
BW: tl.constexpr,
BD: tl.constexpr,
NB: tl.constexpr,
ACTIVATION: tl.constexpr,
HAS_WEIGHT: tl.constexpr,
HAS_BIAS: tl.constexpr,
HAS_RESIDUAL: tl.constexpr,
USE_INITIAL_STATE: tl.constexpr,
IS_VARLEN: tl.constexpr,
):
i_d, i_t, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2)
if IS_VARLEN:
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
T = eos - bos
p_x = x + bos * stride_x_t
else:
i_n = i_b
bos, eos = (i_b * T).to(tl.int64), (i_b * T + T).to(tl.int64)
p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
o_d = i_d * BD + tl.arange(0, BD)
o_w = tl.arange(0, BW) + W - BW
m_d = o_d < D
m_w = o_w >= 0
if HAS_WEIGHT:
# [BD, BW]
b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0).to(tl.float32)
b_y = tl.zeros((BT, BD), dtype=tl.float32)
if not USE_INITIAL_STATE:
for i_w in tl.static_range(-W + 1, 1):
p_yi = tl.make_block_ptr(p_x, (T, D), (stride_x_t, stride_x_d), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
# [BT, BD]
b_yi = tl.load(p_yi, boundary_check=(0, 1)).to(tl.float32)
if HAS_WEIGHT:
b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
b_y += b_yi
elif i_t * BT >= W:
# to make Triton compiler happy, we need to copy codes
for i_w in tl.static_range(-W + 1, 1):
p_yi = tl.make_block_ptr(p_x, (T, D), (stride_x_t, stride_x_d), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
# [BT, BD]
b_yi = tl.load(p_yi, boundary_check=(0, 1)).to(tl.float32)
if HAS_WEIGHT:
b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
b_y += b_yi
else:
o_t = i_t * BT + tl.arange(0, BT)
for i_w in tl.static_range(-W + 1, 1):
o_x = o_t + i_w
m_x = ((o_x >= 0) & (o_x < T))[:, None] & m_d
m_c = ((o_x + W >= 0) & (o_x < 0))[:, None] & m_d
b_yi = tl.load(
p_x + o_x[:, None] * stride_x_t + o_d * stride_x_d,
mask=m_x,
other=0
).to(tl.float32)
b_yi += tl.load(initial_state + i_n * D*W + o_d * W + (o_x + W)[:, None], mask=m_c, other=0).to(tl.float32)
if HAS_WEIGHT:
b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
b_y += b_yi
if HAS_BIAS:
b_y += tl.load(bias + o_d, mask=m_d).to(tl.float32)
if ACTIVATION == 'swish' or ACTIVATION == 'silu':
b_y = b_y * tl.sigmoid(b_y)
if HAS_RESIDUAL:
p_residual = tl.make_block_ptr(residual + bos * D, (T, D), (D, 1), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
b_residual = tl.load(p_residual, boundary_check=(0, 1))
b_y += b_residual
p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
tl.store(p_y, tl.cast(b_y, dtype=p_y.dtype.element_ty, fp_downcast_rounding='rtne'), boundary_check=(0, 1))
@triton.heuristics({
'HAS_WEIGHT': lambda args: args['dw'] is not None,
'HAS_BIAS': lambda args: args['db'] is not None,
'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
'USE_FINAL_STATE': lambda args: args['dht'] is not None,
'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
})
@triton.autotune(
configs=[
triton.Config({'BD': BD}, num_warps=num_warps)
for BD in [16, 32, 64, 128]
for num_warps in [4, 8, 16, 32]
],
key=['D', 'W', 'NB'],
**autotune_cache_kwargs,
)
@triton.jit
def causal_conv1d_bwd_kernel(
x,
y,
weight,
initial_state,
dht,
dy,
dx,
dw,
db,
cu_seqlens,
chunk_indices,
B,
T,
stride_x_n, # x batch stride
stride_x_t, # x time stride
stride_x_d, # x dim stride
stride_dx_n, # dx batch stride
stride_dx_t, # dx time stride
stride_dx_d, # dx dim stride
D: tl.constexpr,
W: tl.constexpr,
BT: tl.constexpr,
BW: tl.constexpr,
BD: tl.constexpr,
NB: tl.constexpr,
ACTIVATION: tl.constexpr,
HAS_WEIGHT: tl.constexpr,
HAS_BIAS: tl.constexpr,
USE_INITIAL_STATE: tl.constexpr,
USE_FINAL_STATE: tl.constexpr,
IS_VARLEN: tl.constexpr,
):
i_d, i_t, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2)
if IS_VARLEN:
i_tg = i_t
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
T = eos - bos
p_x = x + bos * stride_x_t
else:
i_tg = i_b * tl.num_programs(1) + i_t
i_n = i_b
bos, eos = (i_b * T).to(tl.int64), (i_b * T + T).to(tl.int64)
p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
o_d = i_d * BD + tl.arange(0, BD)
o_w = tl.arange(0, BW) + W - BW
m_d = o_d < D
m_w = o_w >= 0
if HAS_WEIGHT:
p_x = tl.make_block_ptr(p_x, (T, D), (stride_x_t, stride_x_d), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
b_x = tl.load(p_x, boundary_check=(0, 1))
# [BD, BW]
b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0)
b_dx = tl.zeros((BT, BD), dtype=tl.float32)
if HAS_BIAS:
b_db = tl.zeros((BD,), dtype=tl.float32)
if not USE_FINAL_STATE and not USE_INITIAL_STATE:
for i_w in tl.static_range(0, W):
p_dy = tl.make_block_ptr(dy + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
# [BT, BD]
b_dy = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
if ACTIVATION == 'swish' or ACTIVATION == 'silu':
p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
b_y = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
b_ys = tl.sigmoid(b_y)
b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
b_wdy = b_dy
if HAS_WEIGHT:
# [BT, BD]
b_wdy = b_wdy * tl.sum(b_w * (o_w == (W - i_w - 1)), 1)
# [BD]
b_dw = tl.sum(b_dy * b_x, 0)
tl.store(dw + i_tg * D*W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
if HAS_BIAS and i_w == 0:
b_db += tl.sum(b_dy, 0)
b_dx += b_wdy
elif i_t * BT >= W:
# to make Triton compiler happy, we need to copy codes
for i_w in tl.static_range(0, W):
p_dy = tl.make_block_ptr(dy + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
# [BT, BD]
b_dy = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
if ACTIVATION == 'swish' or ACTIVATION == 'silu':
p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
b_y = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
b_ys = tl.sigmoid(b_y)
b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
b_wdy = b_dy
if HAS_WEIGHT:
# [BT, BD]
b_wdy = b_wdy * tl.sum(b_w * (o_w == (W - i_w - 1)), 1)
# [BD]
b_dw = tl.sum(b_dy * b_x, 0)
tl.store(dw + i_tg * D*W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
if HAS_BIAS and i_w == 0:
b_db += tl.sum(b_dy, 0)
b_dx += b_wdy
else:
# which may use initial state
o_t = i_t * BT + tl.arange(0, BT)
for i_w in tl.static_range(0, W):
p_dy = tl.make_block_ptr(dy + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
b_dy_shift = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
if ACTIVATION == 'swish' or ACTIVATION == 'silu':
p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
b_y_shift = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
b_ys = tl.sigmoid(b_y_shift)
b_dy_shift = b_dy_shift * b_ys * (1 + b_y_shift * (1 - b_ys))
if HAS_WEIGHT:
# gradient comes from x:sum_t dy[t+i_w] * x[t]
b_dw = tl.sum(b_dy_shift * b_x, 0)
# index of cache:c = W - i_w + t
if USE_INITIAL_STATE:
mask_head_rows = (o_t < i_w) & (o_t < T)
# dy_head = dy[t]
b_dy_head = tl.load(dy + bos * D + o_t[:, None] * D + o_d, mask=(mask_head_rows[:, None] & m_d[None, :]),
other=0.0).to(tl.float32)
if ACTIVATION == 'swish' or ACTIVATION == 'silu':
# use y[t] (not y[t+i_w])
b_y_head = tl.load(y + bos * D + o_t[:, None] * D + o_d,
mask=(mask_head_rows[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
b_ys_head = tl.sigmoid(b_y_head)
b_dy_head = b_dy_head * b_ys_head * (1 + b_y_head * (1 - b_ys_head))
o_c = W - i_w + o_t
# index 0 is padding 0
mask_c = (mask_head_rows & (o_c >= 1) & (o_c < W))
b_xc = tl.load(initial_state + i_n * D * W + o_d[None, :] * W + o_c[:, None],
mask=(mask_c[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
# add the gradient comes from initial_state
b_dw += tl.sum(b_dy_head * b_xc, 0)
tl.store(dw + i_tg * D * W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
if HAS_BIAS and i_w == 0:
b_db += tl.sum(b_dy_shift, 0)
b_wdy = b_dy_shift if not HAS_WEIGHT else (b_dy_shift * tl.sum(b_w * (o_w == (W - i_w - 1)), 1))
b_dx += b_wdy
if HAS_BIAS:
b_db = tl.cast(b_db, dtype=db.dtype.element_ty, fp_downcast_rounding='rtne')
tl.store(db + i_tg * D + o_d, b_db, mask=m_d)
if USE_FINAL_STATE:
if i_t * BT + BT >= T-W:
start_tok = max(0, T - (W - 1))
offset = i_t * BT + tl.arange(0, BT)
tok_idx = offset - start_tok
mask = (offset >= start_tok) & (offset < T)
w_idx = 1 + tok_idx
dht_off = i_n * D * W + o_d[None, :] * W + w_idx[:, None]
b_dht = tl.load(dht + dht_off, mask=mask[:, None] & m_d[None, :], other=0.).to(tl.float32)
b_dx += b_dht
if IS_VARLEN:
p_dx = dx + bos * stride_dx_t
else:
p_dx = dx + tl.cast(i_b, tl.int64) * stride_dx_n
p_dx = tl.make_block_ptr(p_dx, (T, D), (stride_dx_t, stride_dx_d), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
tl.store(p_dx, tl.cast(b_dx, dtype=p_dx.dtype.element_ty, fp_downcast_rounding='rtne'), boundary_check=(0, 1))
@triton.heuristics({
'USE_INITIAL_STATE': lambda args: args['cache'] is not None,
'HAS_WEIGHT': lambda args: args['weight'] is not None,
'HAS_BIAS': lambda args: args['bias'] is not None,
'HAS_RESIDUAL': lambda args: args['residual'] is not None,
})
@triton.jit
def causal_conv1d_update_kernel(
x,
cache,
residual,
y,
weight,
bias,
stride_x_n, # batch stride
stride_x_d, # dim stride
stride_y_n, # batch stride
stride_y_d, # dim stride
D: tl.constexpr,
W: tl.constexpr,
BD: tl.constexpr,
BW: tl.constexpr,
ACTIVATION: tl.constexpr,
USE_INITIAL_STATE: tl.constexpr,
HAS_WEIGHT: tl.constexpr,
HAS_BIAS: tl.constexpr,
HAS_RESIDUAL: tl.constexpr,
):
i_d, i_n = tl.program_id(0), tl.program_id(1)
o_d = i_d * BD + tl.arange(0, BD)
o_w = tl.arange(0, BW)
m_d = o_d < D
m_w = o_w < W
# [BD]
b_x = tl.load(x + i_n * stride_x_n + o_d * stride_x_d, mask=m_d, other=0).to(tl.float32)
b_cache = tl.zeros((BD, BW), dtype=tl.float32)
if USE_INITIAL_STATE:
# 2. Shift Cache (Read [1:])
p_cache_read = tl.make_block_ptr(
cache + i_n * D*W,
shape=(D, W),
strides=(W, 1),
offsets=(i_d * BD, 1),
block_shape=(BD, BW),
order=(1, 0)
)
b_cache = tl.load(p_cache_read, boundary_check=(0, 1)).to(tl.float32)
# 3. Fill x to the last position
m_update = o_w == (W - 1)
b_cache = tl.where(m_update[None, :], b_x[:, None], b_cache)
if HAS_WEIGHT:
b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0)
b_y = tl.sum(b_cache * b_w, 1)
else:
b_y = tl.sum(b_cache, 1)
if HAS_BIAS:
b_y += tl.load(bias + o_d, mask=m_d)
if ACTIVATION == 'swish' or ACTIVATION == 'silu':
b_y = b_y * tl.sigmoid(b_y)
if HAS_RESIDUAL:
b_y += tl.load(residual + i_n * D + o_d, mask=m_d, other=0)
tl.store(y + i_n * stride_y_n + o_d * stride_y_d, tl.cast(b_y,
dtype=y.dtype.element_ty, fp_downcast_rounding='rtne'), mask=m_d)
if USE_INITIAL_STATE:
p_cache_write = tl.make_block_ptr(
cache + i_n * D*W,
shape=(D, W),
strides=(W, 1),
offsets=(i_d * BD, 0),
block_shape=(BD, BW),
order=(1, 0)
)
tl.store(p_cache_write, tl.cast(b_cache, dtype=cache.dtype.element_ty,
fp_downcast_rounding='rtne'), boundary_check=(0, 1))
@triton.heuristics({
'USE_ACTIVATION': lambda args: args['y'] is not None,
'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
})
@triton.jit
def compute_dh0_kernel(
dy,
y,
weight,
dh0,
cu_seqlens,
stride_dy_n,
stride_dy_t,
T,
D: tl.constexpr,
W: tl.constexpr,
BD: tl.constexpr,
USE_ACTIVATION: tl.constexpr,
IS_VARLEN: tl.constexpr,
):
"""
Compute dh0 (gradient w.r.t. initial_state) in a separate kernel.
This avoids Triton compiler bugs on some architectures (e.g., GB200).
Grid: (cdiv(D, BD), N)
"""
i_d, i_n = tl.program_id(0), tl.program_id(1)
# Get sequence boundaries
if IS_VARLEN:
bos = tl.load(cu_seqlens + i_n).to(tl.int64)
eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
seq_len = eos - bos
# For varlen, dy is [1, total_T, D], offset by bos
dy_base = dy + bos * stride_dy_t
else:
seq_len = T
# For non-varlen, dy is [B, T, D], offset by i_n * stride_dy_n
dy_base = dy + tl.cast(i_n, tl.int64) * stride_dy_n
o_d = i_d * BD + tl.arange(0, BD)
m_d = o_d < D
# For each i_w in [1, W), compute dh0[i_n, :, i_w]
for i_w in tl.static_range(1, W):
b_dh0 = tl.zeros([BD], dtype=tl.float32)
# Accumulate contributions from t = 0 to min(i_w, seq_len) - 1
for t in tl.static_range(0, W - 1):
if t < i_w:
w_idx = i_w - 1 - t
# Load dy[t, :] relative to dy_base
p_dy = dy_base + t * stride_dy_t + o_d
m_t = (t < seq_len) & m_d
b_dy = tl.load(p_dy, mask=m_t, other=0).to(tl.float32)
if USE_ACTIVATION:
if IS_VARLEN:
p_y = y + bos * stride_dy_t + t * stride_dy_t + o_d
else:
p_y = y + tl.cast(i_n, tl.int64) * stride_dy_n + t * stride_dy_t + o_d
b_y = tl.load(p_y, mask=m_t, other=0).to(tl.float32)
b_ys = tl.sigmoid(b_y)
b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
# Get weight[:, w_idx]
b_w_col = tl.load(weight + o_d * W + w_idx, mask=m_d, other=0).to(tl.float32)
# Accumulate
b_dh0 += tl.where(m_t, b_dy * b_w_col, 0)
# Store dh0[i_n, :, i_w]
p_dh0 = dh0 + i_n * D * W + o_d * W + i_w
tl.store(p_dh0, b_dh0.to(dh0.dtype.element_ty), mask=m_d)
@triton.heuristics({
'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
})
@triton.jit
def causal_conv1d_states_fwd_kernel(
x,
initial_state,
final_state,
cu_seqlens,
T,
D,
W,
stride_x_n,
stride_x_t,
stride_x_d,
BD: tl.constexpr,
BW: tl.constexpr,
USE_INITIAL_STATE: tl.constexpr,
IS_VARLEN: tl.constexpr,
):
i_d, i_n = tl.program_id(0), tl.program_id(1)
# o_d Shape: [BD]
o_d = i_d * BD + tl.arange(0, BD)
m_d = o_d < D
if IS_VARLEN:
bos = tl.load(cu_seqlens + i_n).to(tl.int64)
eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
seq_len = (eos - bos).to(tl.int32)
p_x = x + bos * stride_x_t
else:
seq_len = T
p_x = x + tl.cast(i_n, tl.int64) * stride_x_n
p_x = tl.make_block_ptr(p_x, (seq_len, D), (stride_x_t, stride_x_d), (seq_len - BW, i_d * BD), (BW, BD), (1, 0))
# b_x Shape: [BW, BD]
b_x = tl.load(p_x, boundary_check=(0, 1), padding_option="zero").to(tl.float32)
if USE_INITIAL_STATE:
if seq_len < BW:
o_c = W - (BW - seq_len) + tl.arange(0, BW)
m_c = (o_c >= 0) & (o_c < W)
p_init = initial_state + i_n * D*W + o_d[None, :] * W + o_c[:, None]
mask_init = m_d[None, :] & m_c[:, None]
b_cache = tl.load(p_init, mask=mask_init, other=0)
b_x += b_cache
# final_state: [N, D, W] (Channel Major inside sample)
# o_w Shape: [BW]
o_w = W - BW + tl.arange(0, BW)
# o_d[:, None] -> [BD, 1]
# o_w[None, :] -> [1, BW]
# p_final Shape -> [BD, BW]
p_final = final_state + tl.cast(i_n, tl.int64) * D*W + o_d[:, None] * W + o_w[None, :]
# m_final Shape -> [BD, BW]
m_final = m_d[:, None] & (o_w[None, :] >= 0)
tl.store(p_final, tl.trans(b_x).to(final_state.dtype.element_ty), mask=m_final)
@input_guard(no_guard_contiguous=["x"])
def causal_conv1d_update_states(
x: torch.Tensor,
state_len: int,
initial_state: torch.Tensor | None = None,
cu_seqlens: torch.Tensor | None = None,
) -> torch.Tensor:
if cu_seqlens is not None:
N = len(cu_seqlens) - 1
if x.dim() == 2:
stride_x_n = 0
stride_x_t, stride_x_d = x.stride()
T = x.shape[0]
else:
stride_x_n = x.stride(0)
stride_x_t, stride_x_d = x.stride(1), x.stride(2)
T = x.shape[1]
D = x.shape[-1]
else:
B, T, D = x.shape
N = B
stride_x_n, stride_x_t, stride_x_d = x.stride()
W = state_len
final_state = torch.empty(N, D, W, dtype=x.dtype, device=x.device)
BD = min(triton.next_power_of_2(D), 256)
BW = triton.next_power_of_2(W)
grid = (triton.cdiv(D, BD), N)
causal_conv1d_states_fwd_kernel[grid](
x=x,
initial_state=initial_state,
final_state=final_state,
cu_seqlens=cu_seqlens,
T=T,
D=D,
W=W,
stride_x_n=stride_x_n,
stride_x_t=stride_x_t,
stride_x_d=stride_x_d,
BW=BW,
BD=BD,
)
return final_state
@input_guard(no_guard_contiguous=["x"])
def causal_conv1d_update(
x: torch.Tensor,
cache: torch.Tensor,
residual: torch.Tensor | None = None,
weight: torch.Tensor | None = None,
bias: torch.Tensor | None = None,
activation: str | None = None,
) -> torch.Tensor:
shape = x.shape
if weight is not None and x.shape[-1] != weight.shape[0]:
x = rearrange(x, 'b t ... -> b t (...)')
D = x.shape[-1]
N = x.numel() // D
W = weight.shape[1] if weight is not None else None
BD = 8
BW = triton.next_power_of_2(W)
if x.dim() == 2:
# Case: (N, D)
stride_x_n = x.stride(0)
stride_x_d = x.stride(1)
elif x.dim() == 3 and x.shape[0] == 1:
# Case: (1, N, D) -> Time=1, Batch=N, Dim=D
# Batch 在 dim 1
stride_x_n = x.stride(1)
stride_x_d = x.stride(2)
elif x.dim() == 3:
# Case: (N, 1, D) -> Batch=N, Time=1, Dim=D
# Batch 在 dim 0
stride_x_n = x.stride(0)
stride_x_d = x.stride(2)
else:
# Fallback / Error case
raise ValueError(f"Unsupported input shape: {x.shape}")
y = torch.empty_like(x, memory_format=torch.contiguous_format)
if y.dim() == 2:
stride_y_n, stride_y_d = y.stride(0), y.stride(1)
elif y.dim() == 3 and y.shape[0] == 1:
stride_y_n, stride_y_d = y.stride(1), y.stride(2)
elif y.dim() == 3:
stride_y_n, stride_y_d = y.stride(0), y.stride(2)
def grid(meta): return (triton.cdiv(D, meta['BD']), N)
causal_conv1d_update_kernel[grid](
x=x,
cache=cache,
residual=residual,
y=y,
weight=weight,
bias=bias,
stride_x_n=stride_x_n,
stride_x_d=stride_x_d,
stride_y_n=stride_y_n,
stride_y_d=stride_y_d,
D=D,
W=W,
BD=BD,
BW=BW,
ACTIVATION=activation,
num_warps=STATIC_WARPS,
)
return y.view(shape), cache