| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- package tests
- import (
- "math"
- "math/rand"
- "testing"
- "unsafe"
- "makarna/pkg/backend/cpu"
- "makarna/pkg/backend/cpu/nn"
- "makarna/pkg/quant"
- "makarna/pkg/tensor"
- )
- func maxAbsDiff(a, b []float32) float32 {
- max := float32(0)
- for i := range a {
- d := a[i] - b[i]
- if d < 0 {
- d = -d
- }
- if d > max {
- max = d
- }
- }
- return max
- }
- func mse(a, b []float32) float32 {
- var s float32
- for i := range a {
- d := a[i] - b[i]
- s += d * d
- }
- return s / float32(len(a))
- }
- func makeTestBlock(seed int64, scale float32) []float32 {
- r := rand.New(rand.NewSource(seed))
- out := make([]float32, 256)
- for i := range out {
- out[i] = (r.Float32()*2 - 1) * scale
- }
- return out
- }
- func TestHarness_CPU_QuantDequantRoundTrip(t *testing.T) {
- cases := []struct {
- name string
- seed int64
- scale float32
- mseMax float32
- maxAbsMax float32
- }{
- {name: "small", seed: 1, scale: 0.01, mseMax: 1e-4, maxAbsMax: 0.1},
- {name: "medium", seed: 2, scale: 1.0, mseMax: 5.0, maxAbsMax: 2.0},
- {name: "large", seed: 3, scale: 100.0, mseMax: 5e4, maxAbsMax: 200.0},
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- inp := makeTestBlock(tc.seed, tc.scale)
- q2 := quant.QuantizeQ2K(inp)
- q3 := quant.QuantizeQ3K(inp)
- q4 := quant.QuantizeQ4K(inp)
- q6 := quant.QuantizeQ6K(inp)
- q8 := quant.QuantizeQ8K(inp)
- if len(q2) != 84 {
- t.Fatalf("q2k size=%d", len(q2))
- }
- if len(q3) != 110 {
- t.Fatalf("q3k size=%d", len(q3))
- }
- if len(q4) != 144 {
- t.Fatalf("q4k size=%d", len(q4))
- }
- if len(q6) != 210 {
- t.Fatalf("q6k size=%d", len(q6))
- }
- if len(q8) != 292 {
- t.Fatalf("q8k size=%d", len(q8))
- }
- out2 := make([]float32, 256)
- out3 := make([]float32, 256)
- out4 := make([]float32, 256)
- out6 := make([]float32, 256)
- out8 := make([]float32, 256)
- tensor.DequantizeQ2_K((*tensor.BlockQ2_K)(unsafe.Pointer(&q2[0])), out2)
- tensor.DequantizeQ3_K((*tensor.BlockQ3_K)(unsafe.Pointer(&q3[0])), out3)
- tensor.DequantizeQ4_K((*tensor.BlockQ4_K)(unsafe.Pointer(&q4[0])), out4)
- tensor.DequantizeQ6_K((*tensor.BlockQ6_K)(unsafe.Pointer(&q6[0])), out6)
- tensor.DequantizeQ8_K((*tensor.BlockQ8_K)(unsafe.Pointer(&q8[0])), out8)
- outs := []struct {
- name string
- out []float32
- }{
- {name: "q2k", out: out2},
- {name: "q3k", out: out3},
- {name: "q4k", out: out4},
- {name: "q6k", out: out6},
- {name: "q8k", out: out8},
- }
- for _, o := range outs {
- for i, v := range o.out {
- if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) {
- t.Fatalf("%s invalid at %d: %f", o.name, i, v)
- }
- }
- m := mse(inp, o.out)
- mx := maxAbsDiff(inp, o.out)
- if m > tc.mseMax {
- t.Fatalf("%s mse=%f > %f", o.name, m, tc.mseMax)
- }
- if mx > tc.maxAbsMax {
- t.Fatalf("%s maxAbs=%f > %f", o.name, mx, tc.maxAbsMax)
- }
- }
- })
- }
- }
- func TestHarness_CPU_NNOpsSanity(t *testing.T) {
- dim := 32
- seq := 4
- r := rand.New(rand.NewSource(123))
- xData := make([]float32, seq*dim)
- wData := make([]float32, dim)
- for i := range xData {
- xData[i] = r.Float32()*2 - 1
- }
- for i := range wData {
- wData[i] = r.Float32()*2 - 1
- }
- x := cpu.NewTensor(tensor.Shape{seq, dim}, append([]float32(nil), xData...))
- w := cpu.NewTensor(tensor.Shape{dim}, append([]float32(nil), wData...))
- if err := nn.RMSNorm(x, w, 1e-5); err != nil {
- t.Fatalf("rmsnorm: %v", err)
- }
- for i, v := range x.DataFloat32() {
- if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) {
- t.Fatalf("rmsnorm invalid at %d: %f", i, v)
- }
- }
- headDim := 16
- numHeads := dim / headDim
- pos := make([]int, seq)
- for i := range pos {
- pos[i] = i
- }
- if err := nn.RoPE(x, pos, headDim, 10000); err != nil {
- t.Fatalf("rope: %v", err)
- }
- for i, v := range x.DataFloat32() {
- if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) {
- t.Fatalf("rope invalid at %d: %f", i, v)
- }
- }
- _ = numHeads
- row := cpu.NewTensor(tensor.Shape{dim}, append([]float32(nil), xData[:dim]...))
- if err := nn.Softmax(row); err != nil {
- t.Fatalf("softmax: %v", err)
- }
- var sum float32
- for i, v := range row.DataFloat32() {
- if v <= 0 || math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) {
- t.Fatalf("softmax invalid at %d: %f", i, v)
- }
- sum += v
- }
- if d := float32(math.Abs(float64(sum - 1))); d > 1e-4 {
- t.Fatalf("softmax sum=%f", sum)
- }
- }
|