File size: 5,717 Bytes
6c2e79a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | /* Phase 2d: MLA attention in C, validated vs Python (attn_out.f32).
* Weights pre-dumped as f32 (weight-loading proven in Phase 1). Isolates the attention MATH.
* Build: gcc -O2 -o phase2d_mla phase2d_mla.c -lm
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define T 5
#define HID 6144
#define H 64
#define NOPE 128
#define ROPE 64
#define HD 192 /* qk_head_dim */
#define VD 128
#define QLORA 1536
#define KVLORA 512
#define QB_OUT 12288 /* H*HD */
#define KVB_OUT 16384 /* H*(NOPE+VD) */
#define O_IN 8192 /* H*VD */
#define SQ 2.0f
#define SKV 3.4641016151377544f
#define SCALE 0.07216878364870322f
#define EPS 1e-5f
static float* L(const char* n, long cnt) {
char p[512]; snprintf(p, sizeof p, "ref/%s.f32", n);
FILE* f = fopen(p, "rb"); if (!f) { perror(p); exit(1); }
float* b = malloc(cnt * 4);
if (fread(b, 4, cnt, f) != (size_t)cnt) { fprintf(stderr, "short %s\n", n); exit(1); }
fclose(f); return b;
}
/* out[M] = W[M,K] @ x[K] */
static void matvec(const float* W, const float* x, float* out, int M, int K) {
for (int i = 0; i < M; i++) { double a = 0; const float* w = W + (long)i * K;
for (int k = 0; k < K; k++) a += (double)w[k] * x[k]; out[i] = (float)a; }
}
static void rmsnorm(const float* x, const float* w, float* o, int d) {
double ss = 0; for (int i = 0; i < d; i++) ss += (double)x[i] * x[i];
float inv = 1.0f / sqrtf((float)(ss / d) + EPS);
for (int i = 0; i < d; i++) o[i] = x[i] * inv * w[i];
}
/* interleave (use_mla): [d] viewed as [d/2,2] transposed to [2,d/2] flattened */
static void interleave(const float* in, float* out, int d) {
int half = d / 2;
for (int i = 0; i < half; i++) { out[i] = in[2*i]; out[half + i] = in[2*i + 1]; }
}
/* rope in place on [ROPE] with cos/sin[ROPE]; rotate_half = [-x[half:], x[:half]] */
static void rope(float* t, const float* cos, const float* sin) {
int half = ROPE / 2; float tmp[ROPE];
for (int i = 0; i < ROPE; i++) {
float rh = (i < half) ? -t[half + i] : t[i - half];
tmp[i] = t[i] * cos[i] + rh * sin[i];
}
memcpy(t, tmp, ROPE * 4);
}
int main(void) {
float* qa=L("attnw_q_a_proj",(long)QLORA*HID); float* qaln=L("attnw_q_a_layernorm",QLORA);
float* qb=L("attnw_q_b_proj",(long)QB_OUT*QLORA);
float* kva=L("attnw_kv_a_proj_with_mqa",(long)(KVLORA+ROPE)*HID); float* kvaln=L("attnw_kv_a_layernorm",KVLORA);
float* kvb=L("attnw_kv_b_proj",(long)KVB_OUT*KVLORA); float* op=L("attnw_o_proj",(long)HID*O_IN);
float* cosT=L("mla_cos",(long)T*ROPE); float* sinT=L("mla_sin",(long)T*ROPE);
float* hn=L("attn_in",(long)T*HID); float* ref=L("attn_out",(long)T*HID);
/* per-token compute Q,K,V, then attention */
static float Q[H][T][HD], K[H][T][HD], V[H][T][VD];
for (int t = 0; t < T; t++) {
const float* h = hn + (long)t * HID;
float ql[QLORA], qln[QLORA], q[QB_OUT];
matvec(qa, h, ql, QLORA, HID); rmsnorm(ql, qaln, qln, QLORA); matvec(qb, qln, q, QB_OUT, QLORA);
float c[KVLORA + ROPE]; matvec(kva, h, c, KVLORA + ROPE, HID);
float kln[KVLORA]; rmsnorm(c, kvaln, kln, KVLORA);
for (int i = 0; i < KVLORA; i++) kln[i] *= SKV;
float kpass[KVB_OUT]; matvec(kvb, kln, kpass, KVB_OUT, KVLORA);
/* k_rot shared: interleave + rope once per token */
float krot[ROPE], krot_i[ROPE];
interleave(c + KVLORA, krot_i, ROPE); memcpy(krot, krot_i, ROPE*4);
rope(krot, cosT + t*ROPE, sinT + t*ROPE);
for (int hd = 0; hd < H; hd++) {
/* q for this head: q[hd*HD ..], split nope/rope, scale, interleave+rope the rope part */
const float* qh = q + hd * HD;
for (int i = 0; i < NOPE; i++) Q[hd][t][i] = qh[i] * SQ;
float qrot[ROPE], qrot_i[ROPE];
for (int i = 0; i < ROPE; i++) qrot[i] = qh[NOPE + i] * SQ;
interleave(qrot, qrot_i, ROPE); memcpy(qrot, qrot_i, ROPE*4);
rope(qrot, cosT + t*ROPE, sinT + t*ROPE);
for (int i = 0; i < ROPE; i++) Q[hd][t][NOPE + i] = qrot[i];
/* k = [k_nope | krot(shared)] , v */
const float* kp = kpass + hd * (NOPE + VD);
for (int i = 0; i < NOPE; i++) K[hd][t][i] = kp[i];
for (int i = 0; i < ROPE; i++) K[hd][t][NOPE + i] = krot[i];
for (int i = 0; i < VD; i++) V[hd][t][i] = kp[NOPE + i];
}
}
/* attention per head, causal */
static float ctx[T][O_IN]; /* [T, H*VD] */
for (int hd = 0; hd < H; hd++)
for (int qi = 0; qi < T; qi++) {
float sc[T]; float mx = -1e30f;
for (int ki = 0; ki <= qi; ki++) { double a = 0;
for (int d = 0; d < HD; d++) a += (double)Q[hd][qi][d] * K[hd][ki][d];
sc[ki] = (float)a * SCALE; if (sc[ki] > mx) mx = sc[ki]; }
double sum = 0; for (int ki = 0; ki <= qi; ki++) { sc[ki] = expf(sc[ki] - mx); sum += sc[ki]; }
for (int d = 0; d < VD; d++) { double a = 0;
for (int ki = 0; ki <= qi; ki++) a += sc[ki] * V[hd][ki][d];
ctx[qi][hd * VD + d] = (float)(a / sum); }
}
/* o_proj */
double dmax = 0;
for (int t = 0; t < T; t++) { float o[HID]; matvec(op, ctx[t], o, HID, O_IN);
for (int i = 0; i < HID; i++) { double d = fabs((double)o[i] - ref[(long)t*HID + i]); if (d > dmax) dmax = d; }
if (t == 0) { printf("out[0..8]: "); for (int i = 0; i < 8; i++) printf("%.4f ", o[i]); printf("\n"); }
}
printf("MLA attention max abs diff vs Python: %.3e\n", dmax);
printf("VERDICT: %s\n", dmax < 5e-2 ? "PASS — MLA attention matches Python" : "FAIL");
return dmax < 5e-2 ? 0 : 1;
}
|