#include "tensor.h" #include #include #include #include #include /* ───────────────────────────────────────── LIFECYCLE ───────────────────────────────────────── */ Tensor tensor_create(int rows, int cols) { Tensor t; t.rows = rows; t.cols = cols; t.data = (float*)malloc(rows * cols * sizeof(float)); if (!t.data) { fprintf(stderr, "[TENSOR] malloc failed for %dx%d\n", rows, cols); exit(1); } return t; } Tensor tensor_zeros(int rows, int cols) { Tensor t = tensor_create(rows, cols); memset(t.data, 0, rows * cols * sizeof(float)); return t; } Tensor tensor_copy(const Tensor* t) { Tensor c = tensor_create(t->rows, t->cols); memcpy(c.data, t->data, t->rows * t->cols * sizeof(float)); return c; } void tensor_free(Tensor* t) { if (t->data) { free(t->data); t->data = NULL; } t->rows = 0; t->cols = 0; } /* ───────────────────────────────────────── RANDOM — Box-Muller gaussian ───────────────────────────────────────── */ static float randn_single(void) { static int has_spare = 0; static float spare; if (has_spare) { has_spare = 0; return spare; } float u, v, s; do { u = ((float)rand() / RAND_MAX) * 2.0f - 1.0f; v = ((float)rand() / RAND_MAX) * 2.0f - 1.0f; s = u * u + v * v; } while (s >= 1.0f || s == 0.0f); float mul = sqrtf(-2.0f * logf(s) / s); spare = v * mul; has_spare = 1; return u * mul; } void tensor_fill(Tensor* t, float val) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) t->data[i] = val; } void tensor_randn(Tensor* t, float std) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) t->data[i] = randn_single() * std; } void tensor_xavier(Tensor* t) { float std = sqrtf(2.0f / (float)(t->rows + t->cols)); tensor_randn(t, std); } /* ───────────────────────────────────────── ELEMENTWISE OPS ───────────────────────────────────────── */ void tensor_add(Tensor* dst, const Tensor* a, const Tensor* b) { int n = a->rows * a->cols; for (int i = 0; i < n; i++) dst->data[i] = a->data[i] + b->data[i]; } void tensor_add_inplace(Tensor* a, const Tensor* b) { int n = a->rows * a->cols; for (int i = 0; i < n; i++) a->data[i] += b->data[i]; } void tensor_sub(Tensor* dst, const Tensor* a, const Tensor* b) { int n = a->rows * a->cols; for (int i = 0; i < n; i++) dst->data[i] = a->data[i] - b->data[i]; } void tensor_mul_scalar(Tensor* t, float s) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) t->data[i] *= s; } void tensor_add_scalar(Tensor* t, float s) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) t->data[i] += s; } void tensor_elementwise_mul(Tensor* dst, const Tensor* a, const Tensor* b) { int n = a->rows * a->cols; for (int i = 0; i < n; i++) dst->data[i] = a->data[i] * b->data[i]; } /* ───────────────────────────────────────── MATRIX OPS ───────────────────────────────────────── */ /* dst = a @ b (a: MxK, b: KxN, dst: MxN) */ void tensor_matmul(Tensor* dst, const Tensor* a, const Tensor* b) { int M = a->rows, K = a->cols, N = b->cols; memset(dst->data, 0, M * N * sizeof(float)); for (int i = 0; i < M; i++) { for (int k = 0; k < K; k++) { float aik = a->data[i * K + k]; for (int j = 0; j < N; j++) { dst->data[i * N + j] += aik * b->data[k * N + j]; } } } } /* dst = a @ b.T (a: MxK, b: NxK, dst: MxN) */ void tensor_matmul_transB(Tensor* dst, const Tensor* a, const Tensor* b) { int M = a->rows, K = a->cols, N = b->rows; memset(dst->data, 0, M * N * sizeof(float)); for (int i = 0; i < M; i++) { for (int j = 0; j < N; j++) { float sum = 0.0f; for (int k = 0; k < K; k++) { sum += a->data[i * K + k] * b->data[j * K + k]; } dst->data[i * N + j] = sum; } } } void tensor_transpose(Tensor* dst, const Tensor* a) { for (int i = 0; i < a->rows; i++) for (int j = 0; j < a->cols; j++) dst->data[j * a->rows + i] = a->data[i * a->cols + j]; } /* ───────────────────────────────────────── ACTIVATIONS ───────────────────────────────────────── */ void tensor_relu(Tensor* dst, const Tensor* src) { int n = src->rows * src->cols; for (int i = 0; i < n; i++) dst->data[i] = src->data[i] > 0.0f ? src->data[i] : 0.0f; } void tensor_relu_inplace(Tensor* t) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) if (t->data[i] < 0.0f) t->data[i] = 0.0f; } /* GELU approximation: x * 0.5 * (1 + tanh(sqrt(2/pi)*(x + 0.044715*x^3))) */ void tensor_gelu_inplace(Tensor* t) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) { float x = t->data[i]; float inner = 0.7978845608f * (x + 0.044715f * x * x * x); t->data[i] = 0.5f * x * (1.0f + tanhf(inner)); } } void tensor_gelu(Tensor* dst, const Tensor* src) { int n = src->rows * src->cols; for (int i = 0; i < n; i++) { float x = src->data[i]; float inner = 0.7978845608f * (x + 0.044715f * x * x * x); dst->data[i] = 0.5f * x * (1.0f + tanhf(inner)); } } void tensor_softmax_rows(Tensor* t) { for (int i = 0; i < t->rows; i++) { float* row = t->data + i * t->cols; float max_val = row[0]; for (int j = 1; j < t->cols; j++) if (row[j] > max_val) max_val = row[j]; float sum = 0.0f; for (int j = 0; j < t->cols; j++) { row[j] = expf(row[j] - max_val); sum += row[j]; } for (int j = 0; j < t->cols; j++) row[j] /= sum; } } void tensor_softmax_inplace(Tensor* t, int len) { float max_val = t->data[0]; for (int i = 1; i < len; i++) if (t->data[i] > max_val) max_val = t->data[i]; float sum = 0.0f; for (int i = 0; i < len; i++) { t->data[i] = expf(t->data[i] - max_val); sum += t->data[i]; } for (int i = 0; i < len; i++) t->data[i] /= sum; } /* ───────────────────────────────────────── LAYER NORM ───────────────────────────────────────── */ void tensor_layernorm(Tensor* dst, const Tensor* src, const Tensor* w, const Tensor* b, int row, float eps) { int D = src->cols; float* x = src->data + row * D; float* out = dst->data + row * D; /* mean */ float mean = 0.0f; for (int i = 0; i < D; i++) mean += x[i]; mean /= D; /* variance */ float var = 0.0f; for (int i = 0; i < D; i++) { float diff = x[i] - mean; var += diff * diff; } var /= D; float inv_std = 1.0f / sqrtf(var + eps); for (int i = 0; i < D; i++) out[i] = (x[i] - mean) * inv_std * w->data[i] + b->data[i]; } /* ───────────────────────────────────────── HELPERS ───────────────────────────────────────── */ float tensor_get(const Tensor* t, int row, int col) { return t->data[row * t->cols + col]; } void tensor_set(Tensor* t, int row, int col, float val) { t->data[row * t->cols + col] = val; } void tensor_add_to(Tensor* t, int row, int col, float val) { t->data[row * t->cols + col] += val; } void tensor_embed(Tensor* dst, const Tensor* embed, const int* token_ids, int n_tokens) { int D = embed->cols; for (int i = 0; i < n_tokens; i++) { int id = token_ids[i]; memcpy(dst->data + i * D, embed->data + id * D, D * sizeof(float)); } } void tensor_add_row(Tensor* a, int a_row, const Tensor* b, int b_row) { int D = a->cols; float* ap = a->data + a_row * D; float* bp = b->data + b_row * D; for (int i = 0; i < D; i++) ap[i] += bp[i]; } void tensor_clip(Tensor* t, float min_val, float max_val) { int n = t->rows * t->cols; for (int i = 0; i < n; i++) { if (t->data[i] < min_val) t->data[i] = min_val; if (t->data[i] > max_val) t->data[i] = max_val; } } void tensor_print(const Tensor* t, const char* name, int max_rows) { int rows = t->rows < max_rows ? t->rows : max_rows; printf("[%s] shape=(%d,%d)\n", name, t->rows, t->cols); for (int i = 0; i < rows; i++) { printf(" row%d: ", i); int cols = t->cols < 8 ? t->cols : 8; for (int j = 0; j < cols; j++) printf("%.4f ", t->data[i * t->cols + j]); if (t->cols > 8) printf("..."); printf("\n"); } }