snapkitty
machine-learning
c
snapkitty-mlc / src /sk_matrix.c
SNAPKITTYWEST's picture
Upload folder using huggingface_hub
8b41169 verified
Raw History Blame Contribute Delete
8.94 kB
/* sk_matrix.c β€” SnapKitty MLC: matrix operations
*
* Authors: Ahmad Ali Parr, Jessica L. Williams (SNAPKITTYWEST)
* Source: SNAPKITTYAGENT9NOVA/MLC
* License: BSL-1.1 / AGPL-3.0 / MPL-2.0
*/
#define _CRT_SECURE_NO_WARNINGS
#include "../include/sk_defs.h"
#include "../include/sk_arena.h"
#include "../include/sk_random.h"
#include "../include/sk_matrix.h"
/* ── Lifecycle ────────────────────────────────────────────────────── */
sk_matrix* sk_mat_create(sk_arena* arena, u32 rows, u32 cols) {
sk_matrix* mat = SK_PUSH_STRUCT(arena, sk_matrix);
mat->rows = rows;
mat->cols = cols;
mat->data = SK_PUSH_ARRAY(arena, f32, (u64)rows * cols);
return mat;
}
sk_matrix* sk_mat_load(sk_arena* arena, u32 rows, u32 cols, const char* filename) {
sk_matrix* mat = sk_mat_create(arena, rows, cols);
FILE* f = fopen(filename, "rb");
if (!f) return mat;
fseek(f, 0, SEEK_END);
u64 size = (u64)ftell(f);
fseek(f, 0, SEEK_SET);
size = SK_MIN(size, sizeof(f32) * (u64)rows * cols);
fread(mat->data, 1, size, f);
fclose(f);
return mat;
}
b32 sk_mat_copy(sk_matrix* dst, const sk_matrix* src) {
if (dst->rows != src->rows || dst->cols != src->cols) return false;
memcpy(dst->data, src->data, sizeof(f32) * (u64)dst->rows * dst->cols);
return true;
}
/* ── Fill ─────────────────────────────────────────────────────────── */
void sk_mat_clear(sk_matrix* mat) {
memset(mat->data, 0, sizeof(f32) * (u64)mat->rows * mat->cols);
}
void sk_mat_fill(sk_matrix* mat, f32 x) {
u64 size = (u64)mat->rows * mat->cols;
for (u64 i = 0; i < size; i++) mat->data[i] = x;
}
void sk_mat_fill_rand(sk_matrix* mat, f32 lower, f32 upper) {
u64 size = (u64)mat->rows * mat->cols;
for (u64 i = 0; i < size; i++)
mat->data[i] = sk_prng_randf() * (upper - lower) + lower;
}
/* ── Reductions ───────────────────────────────────────────────────── */
void sk_mat_scale(sk_matrix* mat, f32 scale) {
u64 size = (u64)mat->rows * mat->cols;
for (u64 i = 0; i < size; i++) mat->data[i] *= scale;
}
f32 sk_mat_sum(const sk_matrix* mat) {
u64 size = (u64)mat->rows * mat->cols;
f32 sum = 0.0f;
for (u64 i = 0; i < size; i++) sum += mat->data[i];
return sum;
}
u64 sk_mat_argmax(const sk_matrix* mat) {
u64 size = (u64)mat->rows * mat->cols;
u64 max_i = 0;
for (u64 i = 1; i < size; i++)
if (mat->data[i] > mat->data[max_i]) max_i = i;
return max_i;
}
/* ── Element-wise arithmetic ──────────────────────────────────────── */
b32 sk_mat_add(sk_matrix* out, const sk_matrix* a, const sk_matrix* b) {
if (a->rows != b->rows || a->cols != b->cols) return false;
if (out->rows != a->rows || out->cols != a->cols) return false;
u64 size = (u64)out->rows * out->cols;
for (u64 i = 0; i < size; i++) out->data[i] = a->data[i] + b->data[i];
return true;
}
b32 sk_mat_sub(sk_matrix* out, const sk_matrix* a, const sk_matrix* b) {
if (a->rows != b->rows || a->cols != b->cols) return false;
if (out->rows != a->rows || out->cols != a->cols) return false;
u64 size = (u64)out->rows * out->cols;
for (u64 i = 0; i < size; i++) out->data[i] = a->data[i] - b->data[i];
return true;
}
/* ── Matrix multiply (four transpose variants) ───────────────────── */
static void _mat_mul_nn(sk_matrix* out, const sk_matrix* a, const sk_matrix* b) {
for (u64 i = 0; i < out->rows; i++)
for (u64 k = 0; k < a->cols; k++)
for (u64 j = 0; j < out->cols; j++)
out->data[j + i * out->cols] +=
a->data[k + i * a->cols] * b->data[j + k * b->cols];
}
static void _mat_mul_nt(sk_matrix* out, const sk_matrix* a, const sk_matrix* b) {
for (u64 i = 0; i < out->rows; i++)
for (u64 j = 0; j < out->cols; j++)
for (u64 k = 0; k < a->cols; k++)
out->data[j + i * out->cols] +=
a->data[k + i * a->cols] * b->data[k + j * b->cols];
}
static void _mat_mul_tn(sk_matrix* out, const sk_matrix* a, const sk_matrix* b) {
for (u64 k = 0; k < a->rows; k++)
for (u64 i = 0; i < out->rows; i++)
for (u64 j = 0; j < out->cols; j++)
out->data[j + i * out->cols] +=
a->data[i + k * a->cols] * b->data[j + k * b->cols];
}
static void _mat_mul_tt(sk_matrix* out, const sk_matrix* a, const sk_matrix* b) {
for (u64 i = 0; i < out->rows; i++)
for (u64 j = 0; j < out->cols; j++)
for (u64 k = 0; k < a->rows; k++)
out->data[j + i * out->cols] +=
a->data[i + k * a->cols] * b->data[k + j * b->cols];
}
b32 sk_mat_mul(
sk_matrix* out, const sk_matrix* a, const sk_matrix* b,
b8 zero_out, b8 transpose_a, b8 transpose_b
) {
u32 a_rows = transpose_a ? a->cols : a->rows;
u32 a_cols = transpose_a ? a->rows : a->cols;
u32 b_rows = transpose_b ? b->cols : b->rows;
u32 b_cols = transpose_b ? b->rows : b->cols;
if (a_cols != b_rows) return false;
if (out->rows != a_rows || out->cols != b_cols) return false;
if (zero_out) sk_mat_clear(out);
switch ((transpose_a << 1) | transpose_b) {
case 0: _mat_mul_nn(out, a, b); break;
case 1: _mat_mul_nt(out, a, b); break;
case 2: _mat_mul_tn(out, a, b); break;
case 3: _mat_mul_tt(out, a, b); break;
}
return true;
}
/* ── Activations ──────────────────────────────────────────────────── */
b32 sk_mat_relu(sk_matrix* out, const sk_matrix* in) {
if (out->rows != in->rows || out->cols != in->cols) return false;
u64 size = (u64)out->rows * out->cols;
for (u64 i = 0; i < size; i++) out->data[i] = SK_MAX(0, in->data[i]);
return true;
}
b32 sk_mat_softmax(sk_matrix* out, const sk_matrix* in) {
if (out->rows != in->rows || out->cols != in->cols) return false;
u64 size = (u64)out->rows * out->cols;
f32 sum = 0.0f;
for (u64 i = 0; i < size; i++) { out->data[i] = expf(in->data[i]); sum += out->data[i]; }
sk_mat_scale(out, 1.0f / sum);
return true;
}
b32 sk_mat_cross_entropy(sk_matrix* out, const sk_matrix* p, const sk_matrix* q) {
if (p->rows != q->rows || p->cols != q->cols) return false;
if (out->rows != p->rows || out->cols != p->cols) return false;
u64 size = (u64)p->rows * p->cols;
for (u64 i = 0; i < size; i++)
out->data[i] = p->data[i] == 0.0f ? 0.0f : p->data[i] * -logf(q->data[i]);
return true;
}
/* ── Gradient accumulation ────────────────────────────────────────── */
b32 sk_mat_relu_add_grad(sk_matrix* out, const sk_matrix* in, const sk_matrix* grad) {
if (out->rows != in->rows || out->cols != in->cols) return false;
if (out->rows != grad->rows || out->cols != grad->cols) return false;
u64 size = (u64)out->rows * out->cols;
for (u64 i = 0; i < size; i++)
out->data[i] += in->data[i] > 0.0f ? grad->data[i] : 0.0f;
return true;
}
b32 sk_mat_softmax_add_grad(
sk_matrix* out, const sk_matrix* softmax_out, const sk_matrix* grad
) {
if (softmax_out->rows != 1 && softmax_out->cols != 1) return false;
sk_arena_temp scratch = sk_arena_scratch_get(NULL, 0);
u32 size = SK_MAX(softmax_out->rows, softmax_out->cols);
sk_matrix* jacobian = sk_mat_create(scratch.arena, size, size);
for (u32 i = 0; i < size; i++)
for (u32 j = 0; j < size; j++)
jacobian->data[j + i * size] =
softmax_out->data[i] * ((i == j) - softmax_out->data[j]);
sk_mat_mul(out, jacobian, grad, 0, 0, 0);
sk_arena_scratch_release(scratch);
return true;
}
b32 sk_mat_cross_entropy_add_grad(
sk_matrix* p_grad, sk_matrix* q_grad,
const sk_matrix* p, const sk_matrix* q, const sk_matrix* grad
) {
if (p->rows != q->rows || p->cols != q->cols) return false;
u64 size = (u64)p->rows * p->cols;
if (p_grad) {
if (p_grad->rows != p->rows || p_grad->cols != p->cols) return false;
for (u64 i = 0; i < size; i++)
p_grad->data[i] += -logf(q->data[i]) * grad->data[i];
}
if (q_grad) {
if (q_grad->rows != q->rows || q_grad->cols != q->cols) return false;
for (u64 i = 0; i < size; i++)
q_grad->data[i] += -p->data[i] / q->data[i] * grad->data[i];
}
return true;
}