oarust 0.8.6

GPU-first semantic computing engine written in Rust
// Rankwise gate/RMSNorm/MIMO-out adjoint. One invocation owns (batch, head),
// producing deterministic parameter partials and dPre for the recurrent core.

import storage;
import activations;
import attributes;

static const uint MAXR = 8u;
static const uint MAXP = 128u;
static const uint MAXPN = 4096u;
static const uint MAXRP = MAXR * MAXP;

struct PushConstants {
	uint dout_idx, x_idx, z_idx, d_idx, mx_idx, mz_idx, mo_idx, norm_idx;
	uint q_idx, k_idx, hprev_idx, decay_idx, gamma_idx;
	uint dpre_idx, dz_idx, dmz_batch_idx, dmo_batch_idx, dnorm_batch_idx;
	uint b, l, h, p, n, r, has_z, has_d, has_norm;
	float eps;
};
[[vk::push_constant]] PushConstants push;
[[vk::binding(0, 0)]] RWByteAddressBuffer storage_buffers[];

[kernel_name("mamba3_mimo_backward_post")]
[domain("ml")]
[variant("post")]
[dtype("f32")]
[status("experimental")]
[shader("compute")]
[numthreads(1, 1, 1)]
void main(uint3 gid : SV_GroupID) {
	uint bh = gid.x;
	uint h = bh % push.h;
	uint b = bh / push.h;
	uint mimoBatch = bh * push.r * push.p;
	uint normBatch = bh * push.p;
	float u[MAXPN];
	float pre[MAXRP];
	for (uint i = 0u; i < push.r * push.p; ++i) {
		store_f32(storage_buffers[push.dmz_batch_idx], mimoBatch + i, 0.0f);
		store_f32(storage_buffers[push.dmo_batch_idx], mimoBatch + i, 0.0f);
	}
	for (uint p = 0u; p < push.p; ++p)
		store_f32(storage_buffers[push.dnorm_batch_idx], normBatch + p, 0.0f);
	float dh = push.has_d != 0u ? load_f32(storage_buffers[push.d_idx], h) : 0.0f;

	for (uint t = 0u; t < push.l; ++t) {
		uint scalar = (b * push.l + t) * push.h + h;
		uint xBase = scalar * push.p;
		float decay = load_f32(storage_buffers[push.decay_idx], scalar);
		float gamma = load_f32(storage_buffers[push.gamma_idx], scalar);
		for (uint p = 0u; p < push.p; ++p) {
			float xv = load_f32(storage_buffers[push.x_idx], xBase + p);
			for (uint n = 0u; n < push.n; ++n) {
				float value = 0.0f;
				for (uint r = 0u; r < push.r; ++r) {
					uint rp = (h * push.r + r) * push.p + p;
					uint qk = (((scalar * push.r + r) * push.n) + n);
					value += xv * load_f32(storage_buffers[push.mx_idx], rp)
						* load_f32(storage_buffers[push.k_idx], qk);
				}
				u[p * push.n + n] = value;
			}
			uint history = ((bh * push.l + t) * push.p + p) * push.n;
			for (uint r = 0u; r < push.r; ++r) {
				float value = 0.0f;
				for (uint n = 0u; n < push.n; ++n) {
					uint qk = (((scalar * push.r + r) * push.n) + n);
					float state = decay * load_f32(storage_buffers[push.hprev_idx], history + n)
						+ gamma * u[p * push.n + n];
					value += load_f32(storage_buffers[push.q_idx], qk) * state;
				}
				uint rp = (h * push.r + r) * push.p + p;
				pre[r * push.p + p] = value
					+ dh * xv * load_f32(storage_buffers[push.mx_idx], rp);
			}
		}

		for (uint r = 0u; r < push.r; ++r) {
			float inv = 1.0f;
			if (push.has_norm != 0u) {
				float sumSq = 0.0f;
				for (uint p = 0u; p < push.p; ++p) {
					float value = pre[r * push.p + p];
					sumSq += value * value;
				}
				inv = rsqrt(sumSq / float(push.p) + push.eps);
			}
			float normDot = 0.0f;
			for (uint p = 0u; p < push.p; ++p) {
				uint rp = (h * push.r + r) * push.p + p;
				float z = load_f32(storage_buffers[push.z_idx], xBase + p);
				float gateInput = z * load_f32(storage_buffers[push.mz_idx], rp);
				float gate = push.has_z != 0u ? silu(gateInput) : 1.0f;
				float weight = push.has_norm != 0u
					? load_f32(storage_buffers[push.norm_idx], h * push.p + p) : 1.0f;
				float core = pre[r * push.p + p] * inv * weight;
				float dout = load_f32(storage_buffers[push.dout_idx], xBase + p);
				float mo = load_f32(storage_buffers[push.mo_idx], rp);
				float dCore = dout * mo * gate;
				uint batchRp = mimoBatch + r * push.p + p;
				store_f32(storage_buffers[push.dmo_batch_idx], batchRp,
					load_f32(storage_buffers[push.dmo_batch_idx], batchRp) + dout * gate * core);
				if (push.has_z != 0u) {
					float du = dout * mo * core * silu_grad(gateInput);
					float dz = r == 0u ? 0.0f : load_f32(storage_buffers[push.dz_idx], xBase + p);
					store_f32(storage_buffers[push.dz_idx], xBase + p,
						dz + du * load_f32(storage_buffers[push.mz_idx], rp));
					store_f32(storage_buffers[push.dmz_batch_idx], batchRp,
						load_f32(storage_buffers[push.dmz_batch_idx], batchRp) + du * z);
				} else if (r == 0u) {
					store_f32(storage_buffers[push.dz_idx], xBase + p, 0.0f);
				}
				if (push.has_norm != 0u) {
					store_f32(storage_buffers[push.dnorm_batch_idx], normBatch + p,
						load_f32(storage_buffers[push.dnorm_batch_idx], normBatch + p)
						+ dCore * pre[r * push.p + p] * inv);
					normDot += dCore * weight * pre[r * push.p + p];
				}
				store_f32(storage_buffers[push.dpre_idx], (scalar * push.r + r) * push.p + p,
					dCore * inv * weight);
			}
			if (push.has_norm != 0u) {
				float correction = inv * inv * inv * normDot / float(push.p);
				for (uint p = 0u; p < push.p; ++p) {
					uint outIndex = (scalar * push.r + r) * push.p + p;
					store_f32(storage_buffers[push.dpre_idx], outIndex,
						load_f32(storage_buffers[push.dpre_idx], outIndex)
						- pre[r * push.p + p] * correction);
				}
			}
		}
	}
}