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