// 1D Causal Convolution WGSL Kernel // Implements a depthwise 1D causal convolution over the sequence dimension. // "Causal" means the output at position t only depends on positions <= t, // which is enforced by left-padding with (kernel_size - 1) zeros. // // Forward: y[b, t, d] = sum_{k=0}^{K-1} weight[d, k] * x[b, t-k, d] // where x[b, t', d] = 0 for t' < 0 (causal padding) // // The `groups` uniform is included for Mamba-2/3 compatibility where conv runs // over a fused (x, B, C) buffer. For standard Mamba-1 usage set groups = 1. // The kernel math is identical; the field is reserved for future grouped // weight-sharing variants. export const CONV1D_FORWARD_WGSL = /* wgsl */` struct ConvParams { seq_len : u32, // L d_channels : u32, // D (number of depthwise channels in this call) kernel_size : u32, // K (typically 4) batch : u32, // B groups : u32, // number of channel groups (1 = standard depthwise) }; @group(0) @binding(0) var params : ConvParams; // x (B, L, D) – input @group(0) @binding(1) var x : array; // weight (D, K) – depthwise conv weights @group(0) @binding(2) var weight : array; // bias (D,) – optional bias (zeros if unused) @group(0) @binding(3) var bias : array; // y (B, L, D) – output @group(0) @binding(4) var y : array; // Dispatch: (ceil(L/16), ceil(D/16), B) @compute @workgroup_size(16, 16, 1) fn conv1d_forward( @builtin(global_invocation_id) gid : vec3, ) { let L = params.seq_len; let D = params.d_channels; let K = params.kernel_size; let B = params.batch; let t = gid.x; // time position let d = gid.y; // channel let b = gid.z; // batch if (t >= L || d >= D || b >= B) { return; } var acc: f32 = 0.0; // Causal: convolve over k = 0..K-1, reading position (t - k) for (var k: u32 = 0u; k < K; k = k + 1u) { let w_idx = d * K + k; let w_val = weight[w_idx]; // t - k: use causal zero-padding for t < k if (t >= k) { let src = b * L * D + (t - k) * D + d; acc = acc + w_val * x[src]; } // else: zero-padding contributes 0 } acc = acc + bias[d]; let out = b * L * D + t * D + d; y[out] = acc; } `; // ---- Backward kernel for 1D convolution ---- export const CONV1D_BACKWARD_WGSL = /* wgsl */` struct ConvParams { seq_len : u32, d_channels : u32, kernel_size : u32, batch : u32, }; @group(0) @binding(0) var params : ConvParams; @group(0) @binding(1) var x : array; @group(0) @binding(2) var weight : array; @group(0) @binding(3) var dy : array; @group(0) @binding(4) var dx : array; @group(0) @binding(5) var dweight : array; @group(0) @binding(6) var dbias : array; // Dispatch: (ceil(L/16), ceil(D/16), B) – computes dx @compute @workgroup_size(16, 16, 1) fn conv1d_backward_dx( @builtin(global_invocation_id) gid : vec3, ) { let L = params.seq_len; let D = params.d_channels; let K = params.kernel_size; let B = params.batch; let t = gid.x; let d = gid.y; let b = gid.z; if (t >= L || d >= D || b >= B) { return; } var grad: f32 = 0.0; // dx[b, t, d] = sum_{k=0}^{K-1} dy[b, t+k, d] * weight[d, k] for (var k: u32 = 0u; k < K; k = k + 1u) { let tp = t + k; if (tp < L) { let dy_idx = b * L * D + tp * D + d; let w_idx = d * K + k; grad = grad + dy[dy_idx] * weight[w_idx]; } } let dx_idx = b * L * D + t * D + d; dx[dx_idx] = grad; } // Dispatch: (K, D, 1) – accumulates dweight over (B, L) @compute @workgroup_size(1, 1, 1) fn conv1d_backward_dw( @builtin(global_invocation_id) gid : vec3, ) { let L = params.seq_len; let D = params.d_channels; let K = params.kernel_size; let B = params.batch; let k = gid.x; let d = gid.y; if (k >= K || d >= D) { return; } var grad_w: f32 = 0.0; var grad_b: f32 = 0.0; for (var b: u32 = 0u; b < B; b = b + 1u) { for (var t: u32 = 0u; t < L; t = t + 1u) { let dy_idx = b * L * D + t * D + d; let dy_val = dy[dy_idx]; if (t >= k) { let x_idx = b * L * D + (t - k) * D + d; grad_w = grad_w + dy_val * x[x_idx]; } if (k == 0u) { grad_b = grad_b + dy_val; } } } dweight[d * K + k] = grad_w; if (k == 0u) { dbias[d] = grad_b; } } `;