// Finalize long Mamba-3 angle, dt and trapezoid derivatives.
//
// Each invocation owns one (batch, head) sequence. Angle derivatives are
// written per head so the normal matrix reduction path can combine them
// without cross-chunk atomics.
import storage;
import activations;
import attributes;
static const uint THREADS = 64u;
static const uint MAXA = 8u;
struct PushConstants {
uint dt_idx;
uint trap_idx;
uint angle_idx;
uint dgamma_idx;
uint dscale_idx;
uint dtheta_idx;
uint ddt_idx;
uint dtrap_idx;
uint danglehead_idx;
uint b;
uint l;
uint h;
uint a;
};
[[vk::push_constant]] PushConstants push;
[[vk::binding(0, 0)]] RWByteAddressBuffer storage_buffers[];
[kernel_name("mamba3_siso_chunk_finalize")]
[domain("ml")]
[variant("chunk16")]
[dtype("f32")]
[status("experimental")]
[shader("compute")]
[numthreads(THREADS, 1, 1)]
void main(uint3 dispatchId : SV_DispatchThreadID) {
uint bh = dispatchId.x;
if (bh >= push.b * push.h) return;
uint h = bh % push.h;
uint b = bh / push.h;
float suffix[MAXA];
for (uint k = 0u; k < push.a; ++k) suffix[k] = 0.0f;
for (uint rr = 0u; rr < push.l; ++rr) {
uint t = push.l - 1u - rr;
uint scalar = (b * push.l + t) * push.h + h;
uint thetaBase = (bh * push.l + t) * push.a;
uint angleBase = (b * push.l + t) * push.a;
float dtTheta = 0.0f;
float dt = load_f32(storage_buffers[push.dt_idx], scalar);
for (uint k = 0u; k < push.a; ++k) {
suffix[k] += load_f32(storage_buffers[push.dtheta_idx], thetaBase + k);
store_f32(storage_buffers[push.danglehead_idx], thetaBase + k, suffix[k] * dt);
dtTheta += suffix[k] * load_f32(storage_buffers[push.angle_idx], angleBase + k);
}
float trap = sigmoid(load_f32(storage_buffers[push.trap_idx], scalar));
float dGamma = load_f32(storage_buffers[push.dgamma_idx], scalar);
float dDt = dGamma * trap + dtTheta;
float dTrapRaw = dGamma * dt;
if (t > 0u) {
uint previous = scalar - push.h;
float incoming = load_f32(storage_buffers[push.dscale_idx], previous);
dDt += incoming * (1.0f - trap);
dTrapRaw -= incoming * dt;
}
store_f32(storage_buffers[push.ddt_idx], scalar, dDt);
store_f32(storage_buffers[push.dtrap_idx], scalar,
dTrapRaw * trap * (1.0f - trap));
}
}