/* SPDX-License-Identifier: AGPL-3.0-or-later */ /* Copyright (C) 2026 David (Corey) Herrera */ /* * tensor_shim.c — minimal tm_tensor allocator so the REAL foundry optim.c * (optim/optim.c) can be linked standalone into the CUDA parity harness without * pulling in the full tensor module (which drags in cblas / metal / cuda). * * optim.c's Adam/AdamW path touches only: tm_zeros, tm_data_f32, tm_release, and * the struct fields data / grad / numel / shape / ndim / dtype. We implement * exactly those against the REAL tensor.h so the struct layout is identical to * what optim.c was compiled against. This is NOT a reimplementation of the * optimizer — adam_step() itself runs from the unmodified optim.c. */ #include "tensor/tensor.h" #include #include static size_t shim_dtype_size(tm_dtype d) { switch (d) { case TM_F64: case TM_I64: return 8; case TM_F16: case TM_BF16: return 2; default: return 4; /* F32 / I32 */ } } tm_tensor *tm_zeros(int64_t *shape, int ndim, tm_dtype dtype) { tm_tensor *t = (tm_tensor *)calloc(1, sizeof(tm_tensor)); if (!t) return NULL; t->ndim = ndim; int64_t n = 1; for (int i = 0; i < ndim; i++) { t->shape[i] = shape[i]; n *= shape[i]; } t->numel = n; t->dtype = dtype; t->requires_grad = false; t->refcount = 1; t->owns_data = true; t->data = calloc((size_t)n, shim_dtype_size(dtype)); return t; } /* Harness convenience: a tensor wrapping caller data (copied in). */ tm_tensor *shim_tensor_f32(const float *src, int64_t n) { int64_t shape[1] = { n }; tm_tensor *t = tm_zeros(shape, 1, TM_F32); if (src) memcpy(t->data, src, (size_t)n * sizeof(float)); return t; } float *tm_data_f32(tm_tensor *t) { return t ? (float *)t->data : NULL; } void tm_release(tm_tensor *t) { if (!t) return; if (t->owns_data && t->data) free(t->data); free(t); }