// Activation function WGSL kernels: SiLU (Swish) and its backward pass. // Used in the gating mechanism of the Mamba Mixer Block. export const ACTIVATIONS_WGSL: string = /* wgsl */` struct ActParams { num_elements : u32, }; @group(0) @binding(0) var p : ActParams; @group(0) @binding(1) var x : array; @group(0) @binding(2) var y : array; // SiLU(x) = x * sigmoid(x) @compute @workgroup_size(256, 1, 1) fn silu_forward( @builtin(global_invocation_id) gid : vec3, ) { let i = gid.x; if (i >= p.num_elements) { return; } let v = x[i]; y[i] = v / (1.0 + exp(-v)); } // RMSNorm forward: y = x / rms(x) * weight // Requires separate uniform for rms norm params. struct RMSNormParams { num_rows : u32, // number of vectors (batch * seq_len) dim : u32, // feature dimension eps : f32, }; @group(0) @binding(0) var rms_p : RMSNormParams; @group(0) @binding(1) var rms_x : array; @group(0) @binding(2) var rms_w : array; // scale (dim,) @group(0) @binding(3) var rms_y : array; @group(0) @binding(4) var rms_inv : array; // cache 1/rms per row @compute @workgroup_size(64, 1, 1) fn rmsnorm_forward( @builtin(global_invocation_id) gid : vec3, ) { let row = gid.x; if (row >= rms_p.num_rows) { return; } let D = rms_p.dim; let base = row * D; var sq_sum: f32 = 0.0; for (var i: u32 = 0u; i < D; i = i + 1u) { let v = rms_x[base + i]; sq_sum = sq_sum + v * v; } let inv_rms = 1.0 / sqrt(sq_sum / f32(D) + rms_p.eps); rms_inv[row] = inv_rms; for (var i: u32 = 0u; i < D; i = i + 1u) { rms_y[base + i] = rms_x[base + i] * inv_rms * rms_w[i]; } } `; // ---- Softmax (row-wise with optional causal mask) ---- // Standalone softmax used by AttentionBlock for the score matrix. // Dispatch: (L, H, B) — one workgroup per (row, head, batch). // This version is a simple sequential-within-workgroup implementation; // for large L prefer the cooperative version in attention.ts. export const SOFTMAX_FORWARD_WGSL: string = /* wgsl */` struct SoftmaxParams { rows : u32, // L cols : u32, // L causal : u32, // 1 = apply causal mask, 0 = full softmax }; @group(0) @binding(0) var sp : SoftmaxParams; @group(0) @binding(1) var data : array; @compute @workgroup_size(1, 1, 1) fn softmax_forward_simple(@builtin(global_invocation_id) gid: vec3) { let row = gid.x; let head = gid.y; let bat = gid.z; if (row >= sp.rows) { return; } let L = sp.cols; let base = bat * sp.rows * L + head * L * L + row * L; let lim = select(L, row + 1u, sp.causal == 1u); var max_val = -1e38; for (var c = 0u; c < lim; c = c + 1u) { if (data[base + c] > max_val) { max_val = data[base + c]; } } var sum_exp = 0.0; for (var c = 0u; c < lim; c = c + 1u) { let e = exp(data[base + c] - max_val); data[base + c] = e; sum_exp = sum_exp + e; } let inv = 1.0 / (sum_exp + 1e-12); for (var c = 0u; c < lim; c = c + 1u) { data[base + c] = data[base + c] * inv; } // Zero out masked positions for (var c = lim; c < L; c = c + 1u) { data[base + c] = 0.0; } } `; export const SOFTMAX_BACKWARD_WGSL: string = /* wgsl */` struct SoftmaxParams { rows : u32, cols : u32, causal : u32, }; @group(0) @binding(0) var sp : SoftmaxParams; @group(0) @binding(1) var p : array; // post-softmax probs @group(0) @binding(2) var dp : array; // upstream gradient @group(0) @binding(3) var dx : array; // output gradient @compute @workgroup_size(1, 1, 1) fn softmax_backward(@builtin(global_invocation_id) gid: vec3) { let row = gid.x; let head = gid.y; let bat = gid.z; if (row >= sp.rows) { return; } let L = sp.cols; let base = bat * sp.rows * L + head * L * L + row * L; let lim = select(L, row + 1u, sp.causal == 1u); // dot = sum_i p[i] * dp[i] var dot = 0.0; for (var i = 0u; i < lim; i = i + 1u) { dot = dot + p[base + i] * dp[base + i]; } for (var i = 0u; i < lim; i = i + 1u) { dx[base + i] = p[base + i] * (dp[base + i] - dot); } } `; // ---- Backward for SiLU ---- export const ACTIVATIONS_BACKWARD_WGSL: string = /* wgsl */` struct ActParams { num_elements : u32, }; @group(0) @binding(0) var p : ActParams; @group(0) @binding(1) var x : array; @group(0) @binding(2) var dy : array; @group(0) @binding(3) var dx : array; // d/dx [x * sigmoid(x)] = sigmoid(x) + x * sigmoid(x) * (1 - sigmoid(x)) // = silu(x)/x + sigmoid(x) * (1 - sigmoid(x)) * x // simplified: sigmoid(x) * (1 + x*(1 - sigmoid(x))) @compute @workgroup_size(256, 1, 1) fn silu_backward( @builtin(global_invocation_id) gid : vec3, ) { let i = gid.x; if (i >= p.num_elements) { return; } let v = x[i]; let sig = 1.0 / (1.0 + exp(-v)); dx[i] = dy[i] * sig * (1.0 + v * (1.0 - sig)); } `;