// Kernel-correctness test for the Metal backend: coli_metal_matmul vs CPU reference // (dequant->f32 MAC * per-row scale) for f32/int8/int4/int2 across real GLM shapes. #include "../backend_metal.h" #include #include #include #include #include enum { F32=0, I8=1, I4=2, I2=3 }; static void cpu_ref(int fmt, const void *W, const float *s, const float *x, float *y, int S, int I, int O) { const int8_t *q8 = (const int8_t*)W; const uint8_t *q4 = (const uint8_t*)W; const float *qf = (const float*)W; int rb4=(I+1)/2, rb2=(I+3)/4; for (int o=0;o>1)]; int v=(i&1)?(b>>4):(b&0xF); w=(float)(v-8); } else if (fmt==I2){ uint8_t b=q4[(size_t)o*rb2+(i>>2)]; int v=(b>>(2*(i&3)))&0x3; w=(float)(v-2); } else w=qf[(size_t)o*I+i]; acc += w*xr[i]; } y[(size_t)si*O+o]=acc*s[o]; } } static int run(int fmt, int O, int I, int S, const char *name) { int rb4=(I+1)/2, rb2=(I+3)/4; size_t wn = (fmt==I8)?(size_t)O*I : (fmt==I4)?(size_t)O*rb4 : (fmt==I2)?(size_t)O*rb2 : (size_t)O*I*sizeof(float); std::vector W(wn); std::vector Wf; srand(99); if (fmt==F32){ Wf.resize((size_t)O*I); for(auto&v:Wf) v=((rand()%2000)-1000)/1000.f; } else for(auto&b:W) b=(uint8_t)((fmt==I8)?((rand()%255)-127):(rand()&0xFF)); const void *Wp = (fmt==F32)?(const void*)Wf.data():(const void*)W.data(); std::vector s(O), x((size_t)S*I), yr((size_t)S*O), yg((size_t)S*O); for(auto&v:s) v=(fmt==F32)?1.0f:(0.01f+(rand()%100)/10000.f); for(auto&v:x) v=((rand()%2000)-1000)/1000.f; cpu_ref(fmt, Wp, s.data(), x.data(), yr.data(), S, I, O); ColiMetalTensor *t=nullptr; if (!coli_metal_matmul(&t, yg.data(), x.data(), Wp, s.data(), fmt, S, I, O)) { printf(" %-22s FAIL (matmul returned 0)\n", name); return 1; } double maxabs=0, ymax=0; for(size_t i=0;i<(size_t)S*O;i++){ maxabs=fmax(maxabs,fabs(yg[i]-yr[i])); ymax=fmax(ymax,fabs(yr[i])); } double nerr=maxabs/(ymax+1e-9); int ok = nerr < 1e-4; printf(" %-22s nerr=%.2e %s\n", name, nerr, ok?"ok":"*** MISMATCH"); coli_metal_tensor_free(t); return ok?0:1; } static float deq4(const uint8_t* w,int i){ uint8_t b=w[i>>1]; int v=(i&1)?(b>>4):(b&0xF); return (float)(v-8); } static size_t roundpg(size_t n){ size_t p=16384; return ((n+p-1)/p)*p; } // Validate coli_metal_moe_block against a CPU reference (gate/up/silu/down + weighted scatter-add). static int run_moe(const std::vector& nrv, const char* name) { const int D=6144, I=2048, fmt=2; int rbG=(D+1)/2, rbD=(I+1)/2, nb=(int)nrv.size(); int R=0; std::vector xoff(nb),nr(nrv); for(int e=0;e slab(nb), fslab(nb); std::vector g(nb),u(nb),d(nb); std::vector gs(nb),us(nb),ds(nb); size_t wlen=roundpg((size_t)I*rbG*2 + (size_t)D*rbD), flen=roundpg(((size_t)I*2+D)*sizeof(float)); for(int e=0;e xg((size_t)R*D); for(auto&v:xg) v=((rand()%2000)-1000)/1000.f; std::vector rows(R); std::vector rw(R); for(int gr=0;gr position 0 int S=1; // CPU reference std::vector refout((size_t)S*D,0.f), gg(I),uu(I),hh(D); for(int e=0;e gout((size_t)S*D,0.f); int ok = coli_metal_moe_block(nb,D,I,fmt,g.data(),u.data(),d.data(),gs.data(),us.data(),ds.data(), xg.data(),xoff.data(),nr.data(),rows.data(),rw.data(),gout.data(),S); double maxabs=0,ymax=0; for(size_t i=0;i