| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- package compute
- import (
- "testing"
- "makarna/pkg/backend/cpu"
- "makarna/pkg/backend/device"
- "makarna/pkg/tensor"
- )
- func TestLinearCPU(t *testing.T) {
- // Input: [2, 3] @ Weight: [4, 3] = Output: [2, 4]
- input := cpu.NewTensor(tensor.Shape{2, 3}, []float32{
- 1, 2, 3,
- 4, 5, 6,
- })
- weight := cpu.NewTensor(tensor.Shape{4, 3}, []float32{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1,
- 1, 1, 1,
- })
- output := cpu.NewTensor(tensor.Shape{2, 4}, nil)
- ctx := NewContext(nil, 0) // nil dispatcher = CPU
- if err := Linear(ctx, input, weight, output); err != nil {
- t.Fatalf("Linear failed: %v", err)
- }
- expected := []float32{
- 1, 2, 3, 6, // row 0: [1,2,3] dot each weight row
- 4, 5, 6, 15, // row 1
- }
- outData := output.DataFloat32()
- for i, exp := range expected {
- if diff := outData[i] - exp; diff < -0.001 || diff > 0.001 {
- t.Errorf("output[%d] = %f, expected %f", i, outData[i], exp)
- }
- }
- }
- func TestRMSNorm(t *testing.T) {
- x := cpu.NewTensor(tensor.Shape{1, 4}, []float32{1, 2, 3, 4})
- w := cpu.NewTensor(tensor.Shape{4}, []float32{1, 1, 1, 1})
- ctx := NewContext(nil, 0)
- if err := RMSNorm(ctx, x, w, 1e-6); err != nil {
- t.Fatalf("RMSNorm failed: %v", err)
- }
- // Check output is normalized
- data := x.DataFloat32()
- var ss float32
- for _, v := range data {
- ss += v * v
- }
- rms := ss / 4
- // After RMSNorm, variance should be close to 1
- if rms < 0.9 || rms > 1.1 {
- t.Errorf("RMS after norm = %f, expected ~1.0", rms)
- }
- }
- func TestDeviceDispatcher(t *testing.T) {
- placements := []tensor.DevicePlacement{
- {Type: tensor.CUDA, GPU: 0},
- {Type: tensor.CUDA, GPU: 0},
- {Type: tensor.CPU, GPU: -1},
- {Type: tensor.CPU, GPU: -1},
- }
- dd := device.NewDeviceDispatcher(placements)
- if dd.NumGPULayers() != 2 {
- t.Errorf("NumGPULayers = %d, expected 2", dd.NumGPULayers())
- }
- if !dd.IsLayerOnGPU(0) {
- t.Error("Layer 0 should be on GPU")
- }
- if dd.IsLayerOnGPU(2) {
- t.Error("Layer 2 should be on CPU")
- }
- p := dd.LayerPlacement(1)
- if p.Type != tensor.CUDA {
- t.Errorf("Layer 1 placement = %v, expected CUDA", p.Type)
- }
- // Beyond bounds defaults to CPU
- p = dd.LayerPlacement(100)
- if p.Type != tensor.CPU {
- t.Errorf("Out of bounds placement = %v, expected CPU", p.Type)
- }
- }
- func TestContextPlacement(t *testing.T) {
- placements := []tensor.DevicePlacement{
- {Type: tensor.CUDA, GPU: 0},
- {Type: tensor.CPU, GPU: -1},
- }
- dd := device.NewDeviceDispatcher(placements)
- ctx0 := NewContext(dd, 0)
- if !ctx0.IsGPU() {
- t.Error("Context 0 should be GPU")
- }
- ctx1 := NewContext(dd, 1)
- if ctx1.IsGPU() {
- t.Error("Context 1 should be CPU")
- }
- // Nil dispatcher
- ctxNil := NewContext(nil, 0)
- if ctxNil.IsGPU() {
- t.Error("Nil dispatcher should default to CPU")
- }
- }
- func TestSwiGLU(t *testing.T) {
- gate := cpu.NewTensor(tensor.Shape{2}, []float32{0, 1})
- up := cpu.NewTensor(tensor.Shape{2}, []float32{2, 3})
- out := cpu.NewTensor(tensor.Shape{2}, nil)
- ctx := NewContext(nil, 0)
- if err := SwiGLU(ctx, gate, up, out); err != nil {
- t.Fatalf("SwiGLU failed: %v", err)
- }
- // SiLU(0) = 0, so out[0] = 0 * 2 = 0
- // SiLU(1) ≈ 0.731, so out[1] ≈ 0.731 * 3 ≈ 2.19
- data := out.DataFloat32()
- if data[0] != 0 {
- t.Errorf("out[0] = %f, expected 0", data[0])
- }
- if data[1] < 2.0 || data[1] > 2.5 {
- t.Errorf("out[1] = %f, expected ~2.2", data[1])
- }
- }
|