cera 0.2.0

Rust-native LLM inference engine
Documentation
// Depthwise Conv1d with rolling buffer.
//
// For each channel:
//   out[ch] = sum_k(buffer[k, ch] * weight[ch, k]) + input[ch] * weight[ch, d_conv]
//
// Then updates rolling buffer: shift left, append input.
//
// Bind group 0:
//   @binding(0) input: array<f32>   (hidden_size, bx = b ⊙ x from in_proj)
//   @binding(1) buffer: array<f32>  (d_conv × hidden_size, rolling buffer, read-write)
//   @binding(2) weight: array<f32>  (hidden_size × kernel_size, conv weights)
//   @binding(3) output: array<f32>  (hidden_size, conv output, write)
//   @binding(4) params: vec4<u32>   (hidden_size, kernel_size, d_conv, 0)
//
// Dispatch: (ceil(hidden_size / 256), 1, 1)

@group(0) @binding(0) var<storage, read> input: array<f32>;
@group(0) @binding(1) var<storage, read_write> buffer: array<f32>;
@group(0) @binding(2) var<storage, read> weight: array<f32>;
@group(0) @binding(3) var<storage, read_write> output: array<f32>;
@group(0) @binding(4) var<storage, read> params: vec4<u32>;

@compute @workgroup_size(256, 1, 1)
fn conv1d_depthwise(@builtin(global_invocation_id) gid: vec3<u32>) {
    let ch = gid.x;
    let hidden_size = params.x;
    let kernel_size = params.y;
    let d_conv = params.z;

    if ch >= hidden_size { return; }

    // Convolution: sum over rolling buffer slots + current input
    var sum: f32 = 0.0;
    for (var k_idx = 0u; k_idx < d_conv; k_idx += 1u) {
        sum += buffer[k_idx * hidden_size + ch] * weight[ch * kernel_size + k_idx];
    }
    sum += input[ch] * weight[ch * kernel_size + d_conv];
    output[ch] = sum;

    // Update rolling buffer: shift left by one slot, append input
    // (This is sequential per-channel but each channel is independent)
    if d_conv > 1u {
        for (var k_idx = 0u; k_idx < d_conv - 1u; k_idx += 1u) {
            buffer[k_idx * hidden_size + ch] = buffer[(k_idx + 1u) * hidden_size + ch];
        }
    }
    if d_conv > 0u {
        buffer[(d_conv - 1u) * hidden_size + ch] = input[ch];
    }
}