File size: 8,943 Bytes
8b41169 | 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | /* 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;
}
|