mingrad 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mingrad-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 axiol2056
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include mingrad/csrc *.c *.h
5
+ recursive-include tests *.c
mingrad-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.4
2
+ Name: mingrad
3
+ Version: 0.1.0
4
+ Summary: Motor de autograd minimalista en C, con bindings nativos a Python (sin ctypes/pybind11)
5
+ Author: axiol2056
6
+ License: MIT
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Programming Language :: C
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: author
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: license
20
+ Dynamic: license-file
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ # mingrad
25
+
26
+ Motor de autograd minimalista escrito en **C puro**, con bindings nativos a Python vía `Python.h` (sin `ctypes`, sin `pybind11`, sin Cython, sin NumPy como dependencia). Piénsalo como una base tipo PyTorch pero mucho más simple, pensada para servir de punto de partida para entrenar tus propios modelos sin depender de un framework grande.
27
+
28
+ Incluye tensores N-dimensionales, autograd con grafo dinámico, capas `Linear`, `Conv2D` y `Conv3D`, activaciones, losses (`MSE`, `CrossEntropy`), y un optimizador `SGD`.
29
+
30
+ ## Por qué existe
31
+
32
+ Para tener una base propia, entendible de punta a punta, sobre la que entrenar modelos (incluyendo datos volumétricos/3D) sin la complejidad interna de PyTorch/TensorFlow. En benchmarks propios contra NumPy+BLAS, mingrad resultó consistentemente más rápido (ver [Rendimiento](#rendimiento)) gracias a un diseño de acceso a memoria cache-friendly, sin necesidad de escribir SIMD a mano.
33
+
34
+ ## Instalación
35
+
36
+ Requiere un compilador de C (gcc/clang) y headers de desarrollo de Python (`python3-dev` / `python3-devel` según tu distro).
37
+
38
+ ```bash
39
+ pip install mingrad
40
+ ```
41
+
42
+ O desde el código fuente:
43
+
44
+ ```bash
45
+ git clone <repo>
46
+ cd mingrad
47
+ pip install .
48
+ ```
49
+
50
+ No hay dependencias de Python en tiempo de ejecución (ni NumPy, ni nada más).
51
+
52
+ ## Uso básico
53
+
54
+ ### Tensores y autograd
55
+
56
+ ```python
57
+ import mingrad as mg
58
+
59
+ mg.init(64 * 1024 * 1024) # reserva 64 MB de pool; llamar una sola vez, al principio
60
+
61
+ x = mg.Tensor([1.0, 2.0, 3.0, 4.0], shape=(2, 2), requires_grad=True)
62
+ y = mg.Tensor([5.0, 6.0, 7.0, 8.0], shape=(2, 2), requires_grad=True)
63
+
64
+ z = (x + y).sum()
65
+ z.backward()
66
+
67
+ print(x.grad) # [1.0, 1.0, 1.0, 1.0]
68
+
69
+ mg.shutdown()
70
+ ```
71
+
72
+ Operaciones disponibles sobre `Tensor`: `+`, `-`, `*`, `@` (matmul), `.relu()`, `.sigmoid()`, `.tanh()`, `.softmax()`, `.sum()`, `.mean()`, `.mse_loss(target)`, `.cross_entropy_loss(targets)`.
73
+
74
+ ### Entrenar un MLP
75
+
76
+ ```python
77
+ import mingrad as mg
78
+
79
+ mg.init(64 * 1024 * 1024)
80
+
81
+ # datos de ejemplo: X (lista plana, row-major), Y (índices de clase)
82
+ X, Y, N = load_my_dataset() # tu propio dataset
83
+
84
+ model = mg.Model()
85
+ model.add_linear(in_features=2, out_features=16).add_relu()
86
+ model.add_linear(in_features=16, out_features=3)
87
+
88
+ opt = mg.SGD(model.parameters(), lr=0.1)
89
+
90
+ for epoch in range(200):
91
+ x = mg.Tensor(X, shape=(N, 2))
92
+ logits = model(x)
93
+ loss = logits.cross_entropy_loss(Y)
94
+
95
+ model.zero_grad()
96
+ loss.backward()
97
+ opt.step()
98
+
99
+ if epoch % 20 == 0:
100
+ print(f"epoch {epoch}: loss={loss.data[0]:.4f}")
101
+
102
+ mg.shutdown()
103
+ ```
104
+
105
+ ### Conv2D (imágenes)
106
+
107
+ ```python
108
+ model = mg.Model()
109
+ model.add_conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
110
+ model.add_relu()
111
+
112
+ # input: (batch, in_channels, height, width), row-major
113
+ x = mg.Tensor(image_data, shape=(batch, 3, 32, 32))
114
+ out = model(x) # (batch, 16, 32, 32)
115
+ ```
116
+
117
+ ### Conv3D (datos volumétricos)
118
+
119
+ ```python
120
+ model = mg.Model()
121
+ model.add_conv3d(in_channels=1, out_channels=8, kernel_size=3, stride=1, padding=1)
122
+ model.add_relu()
123
+
124
+ # input: (batch, in_channels, depth, height, width), row-major
125
+ x = mg.Tensor(volume_data, shape=(batch, 1, 16, 16, 16))
126
+ out = model(x) # (batch, 8, 16, 16, 16)
127
+ ```
128
+
129
+ `kernel_size`, `stride` y `padding` aceptan un entero (mismo valor en todos los ejes) o una tupla explícita: `kernel_size=(3, 3)` para Conv2D, `kernel_size=(3, 3, 3)` para Conv3D.
130
+
131
+ ### Leer datos y gradientes
132
+
133
+ ```python
134
+ t.shape # tupla de ints
135
+ t.data # copia de los datos como list[float]
136
+ t.grad # copia del gradiente como list[float] (requiere requires_grad=True y backward() ya llamado)
137
+ ```
138
+
139
+ `.data` y `.grad` devuelven **copias** en cada acceso, no vistas — no hay interoperabilidad directa con NumPy/arrays en esta versión (ver limitaciones).
140
+
141
+ ## Rendimiento
142
+
143
+ mingrad usa un pool de memoria global (bump allocator, sin `malloc`/`free` por operación) y patrones de acceso a memoria cache-friendly (acceso contiguo, evitando iterar por columnas), auto-vectorizado por el compilador con `-O3`. No usa SIMD manual ni multithreading.
144
+
145
+ Benchmarks propios (forward + backward, batch representativo), comparado contra NumPy/BLAS puro y contra [nnetflow](https://pypi.org/project/nnetflow/) (motor de autograd en Python+NumPy):
146
+
147
+ | Escenario | mingrad vs referencia Python/NumPy |
148
+ |---|---|
149
+ | MLP denso (64→128→10), entrenamiento completo | ~7.8× más rápido |
150
+ | Conv2D (batch=8, 3×32×32→16 canales) | ~6.4× más rápido |
151
+ | Conv3D (batch=4, 2×16³→8 canales) | ~1.8× más rápido |
152
+
153
+ Los números exactos dependen de tu CPU y del tamaño de tus tensores; estos son solo un punto de referencia obtenido durante el desarrollo, no una garantía.
154
+
155
+ **Sobre `-march=native`**: por defecto, este paquete se compila con `-O3` sin `-march=native`. Es una decisión deliberada, no un descuido: durante el desarrollo confirmamos que `-march=native` (con AVX-512) causaba una **regresión de rendimiento de más de 4×** específicamente en Conv3D en el hardware de prueba, además del riesgo general de que un binario compilado con `-march=native` puede crashear (`SIGILL`) si se ejecuta en una CPU distinta a la que compiló. Si sabés que tu caso de uso se beneficia y tu hardware de build/deploy es el mismo, podés recompilar vos mismo con esa flag — pero medí antes y después, no asumas que es una mejora.
156
+
157
+ ## Limitaciones
158
+
159
+ Léelas antes de usar esto en algo importante.
160
+
161
+ - **El pool de memoria no libera tensores individuales.** No hay recolector de basura ni referencia-conteo por tensor: cada tensor que creás (incluyendo resultados intermedios de operaciones) ocupa espacio en el pool hasta que llamás `mg.reset_pool()` o `mg.shutdown()`. Un loop de entrenamiento largo sin resetear el pool eventualmente lo agota.
162
+
163
+ - **Si el pool se queda sin memoria, el proceso aborta (`SIGABRT`), no lanza una excepción Python.** Esto es intencional en el motor en C (preferimos fallar ruidosamente a corromper memoria), pero significa que **no podés capturarlo con `try/except`** — tu programa Python entero termina. Dimensioná `mg.init(pool_bytes)` con margen, y en producción considerá correr el entrenamiento en un subproceso si necesitás aislar este tipo de fallo.
164
+
165
+ - **`mg.reset_pool()` invalida todos los handles existentes sin avisar, y usar un handle viejo después de un reset puede devolver datos de OTRO tensor en silencio, sin ningún error.** Esto es una consecuencia de cómo se reciclan los índices de handles al hacer reset — no es un caso raro, lo reproducimos fácilmente durante el desarrollo. **Nunca conserves una referencia a un `Tensor`/`Model` creado antes de un `reset_pool()`.** Si necesitás resetear memoria entre epochs, hacelo solo cuando no haya ningún tensor vivo que te importe (por ejemplo, recreando el modelo también), o simplemente usá un pool lo bastante grande y no resetees.
166
+
167
+ - **No es thread-safe.** El estado interno (tabla de handles, último error, el pool mismo) es completamente global sin ningún lock. Usar mingrad desde más de un thread de Python al mismo tiempo es una condición de carrera, aunque el GIL de Python evite corrupción de memoria a nivel de bytecode individual.
168
+
169
+ - **`.data` y `.grad` devuelven copias en listas Python planas, no arrays de NumPy ni vistas de memoria.** Para tensores grandes esto tiene un costo notable de conversión en cada lectura. No hay soporte de `__array_interface__` ni buffer protocol en esta versión.
170
+
171
+ - **Sin GPU.** Todo corre en CPU.
172
+
173
+ - **Sin serialización de modelos.** No hay `save()`/`load()`; si necesitás persistir un modelo entrenado, tenés que extraer `model.parameters()` (cada uno con `.data`) y guardarlos vos mismo (por ejemplo a JSON), y reconstruir el modelo + cargar los datos a mano al volver a levantarlo.
174
+
175
+ - **Sin batching automático, sin DataLoader, sin manejo de datasets.** Vos armás los batches y se los pasás como listas planas.
176
+
177
+ - **`Model` solo cubre arquitecturas secuenciales** (una capa tras otra). No hay soporte para ramas, skip-connections (tipo ResNet), o cualquier grafo que no sea una cadena lineal. Para eso hay que operar directamente sobre `Tensor` y construir el grafo a mano con las operaciones sueltas (`+`, `@`, etc.).
178
+
179
+ - **Compilación probada en Linux con gcc.** `setup.py` intenta detectar Windows y usar flags de MSVC, pero no fue probado ahí. macOS con clang debería funcionar (misma familia de flags que gcc) pero tampoco fue verificado extensivamente.
180
+
181
+ - **Sin validación exhaustiva de shapes en todos los casos límite.** El motor de autograd fue validado contra gradiente numérico (diferencias finitas) para todas las operaciones sobre los casos de test incluidos, pero eso no cubre cada combinación posible de shapes, dtypes, o tamaños extremos.
182
+
183
+ ## Estructura del paquete
184
+
185
+ ```
186
+ mingrad/
187
+ ├── __init__.py # API Python ergonómica (Tensor, Model, SGD)
188
+ ├── _mingrad # módulo de extensión compilado (.so)
189
+ └── csrc/ # código fuente en C (motor + bindings)
190
+ ├── mingrad.h # API del motor en C puro
191
+ ├── mingrad_api.h/c # capa de handles, para exponer a otros lenguajes
192
+ ├── _mingrad.c # módulo de extensión Python (Python.h)
193
+ └── *.c # tensor, ops, losses, capas, conv2d/3d, optimizador
194
+ ```
195
+
196
+ Si preferís usar el motor directamente desde C (sin Python), `mingrad.h` es la API completa; `mingrad_api.h` es una capa simplificada basada en handles enteros, pensada originalmente para bindings pero utilizable también desde C directamente.
197
+
198
+ ## Tests
199
+
200
+ El motor de autograd fue validado con tests de gradiente numérico (diferencias finitas contra el gradiente analítico) para cada operación, incluyendo Conv2D y Conv3D con distintos stride/padding. Estos tests están en `tests/` y se compilan directo con gcc contra `mingrad/csrc/`, sin depender de Python. Los fuentes del motor no necesitan `Python.h` — solo `_mingrad.c` (el binding) lo requiere, así que se excluye de este build:
201
+
202
+ ```bash
203
+ gcc -std=c11 -O2 -Imingrad/csrc \
204
+ mingrad/csrc/arena.c mingrad/csrc/shape.c mingrad/csrc/tensor.c mingrad/csrc/ops.c \
205
+ mingrad/csrc/losses.c mingrad/csrc/layers.c mingrad/csrc/optim.c \
206
+ mingrad/csrc/conv2d.c mingrad/csrc/conv3d.c \
207
+ tests/test_grad_check.c -o test_grad_check -lm
208
+ ./test_grad_check
209
+ ```
210
+
211
+ ## Licencia
212
+
213
+ Ver [LICENSE](LICENSE).
@@ -0,0 +1,190 @@
1
+ # mingrad
2
+
3
+ Motor de autograd minimalista escrito en **C puro**, con bindings nativos a Python vía `Python.h` (sin `ctypes`, sin `pybind11`, sin Cython, sin NumPy como dependencia). Piénsalo como una base tipo PyTorch pero mucho más simple, pensada para servir de punto de partida para entrenar tus propios modelos sin depender de un framework grande.
4
+
5
+ Incluye tensores N-dimensionales, autograd con grafo dinámico, capas `Linear`, `Conv2D` y `Conv3D`, activaciones, losses (`MSE`, `CrossEntropy`), y un optimizador `SGD`.
6
+
7
+ ## Por qué existe
8
+
9
+ Para tener una base propia, entendible de punta a punta, sobre la que entrenar modelos (incluyendo datos volumétricos/3D) sin la complejidad interna de PyTorch/TensorFlow. En benchmarks propios contra NumPy+BLAS, mingrad resultó consistentemente más rápido (ver [Rendimiento](#rendimiento)) gracias a un diseño de acceso a memoria cache-friendly, sin necesidad de escribir SIMD a mano.
10
+
11
+ ## Instalación
12
+
13
+ Requiere un compilador de C (gcc/clang) y headers de desarrollo de Python (`python3-dev` / `python3-devel` según tu distro).
14
+
15
+ ```bash
16
+ pip install mingrad
17
+ ```
18
+
19
+ O desde el código fuente:
20
+
21
+ ```bash
22
+ git clone <repo>
23
+ cd mingrad
24
+ pip install .
25
+ ```
26
+
27
+ No hay dependencias de Python en tiempo de ejecución (ni NumPy, ni nada más).
28
+
29
+ ## Uso básico
30
+
31
+ ### Tensores y autograd
32
+
33
+ ```python
34
+ import mingrad as mg
35
+
36
+ mg.init(64 * 1024 * 1024) # reserva 64 MB de pool; llamar una sola vez, al principio
37
+
38
+ x = mg.Tensor([1.0, 2.0, 3.0, 4.0], shape=(2, 2), requires_grad=True)
39
+ y = mg.Tensor([5.0, 6.0, 7.0, 8.0], shape=(2, 2), requires_grad=True)
40
+
41
+ z = (x + y).sum()
42
+ z.backward()
43
+
44
+ print(x.grad) # [1.0, 1.0, 1.0, 1.0]
45
+
46
+ mg.shutdown()
47
+ ```
48
+
49
+ Operaciones disponibles sobre `Tensor`: `+`, `-`, `*`, `@` (matmul), `.relu()`, `.sigmoid()`, `.tanh()`, `.softmax()`, `.sum()`, `.mean()`, `.mse_loss(target)`, `.cross_entropy_loss(targets)`.
50
+
51
+ ### Entrenar un MLP
52
+
53
+ ```python
54
+ import mingrad as mg
55
+
56
+ mg.init(64 * 1024 * 1024)
57
+
58
+ # datos de ejemplo: X (lista plana, row-major), Y (índices de clase)
59
+ X, Y, N = load_my_dataset() # tu propio dataset
60
+
61
+ model = mg.Model()
62
+ model.add_linear(in_features=2, out_features=16).add_relu()
63
+ model.add_linear(in_features=16, out_features=3)
64
+
65
+ opt = mg.SGD(model.parameters(), lr=0.1)
66
+
67
+ for epoch in range(200):
68
+ x = mg.Tensor(X, shape=(N, 2))
69
+ logits = model(x)
70
+ loss = logits.cross_entropy_loss(Y)
71
+
72
+ model.zero_grad()
73
+ loss.backward()
74
+ opt.step()
75
+
76
+ if epoch % 20 == 0:
77
+ print(f"epoch {epoch}: loss={loss.data[0]:.4f}")
78
+
79
+ mg.shutdown()
80
+ ```
81
+
82
+ ### Conv2D (imágenes)
83
+
84
+ ```python
85
+ model = mg.Model()
86
+ model.add_conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
87
+ model.add_relu()
88
+
89
+ # input: (batch, in_channels, height, width), row-major
90
+ x = mg.Tensor(image_data, shape=(batch, 3, 32, 32))
91
+ out = model(x) # (batch, 16, 32, 32)
92
+ ```
93
+
94
+ ### Conv3D (datos volumétricos)
95
+
96
+ ```python
97
+ model = mg.Model()
98
+ model.add_conv3d(in_channels=1, out_channels=8, kernel_size=3, stride=1, padding=1)
99
+ model.add_relu()
100
+
101
+ # input: (batch, in_channels, depth, height, width), row-major
102
+ x = mg.Tensor(volume_data, shape=(batch, 1, 16, 16, 16))
103
+ out = model(x) # (batch, 8, 16, 16, 16)
104
+ ```
105
+
106
+ `kernel_size`, `stride` y `padding` aceptan un entero (mismo valor en todos los ejes) o una tupla explícita: `kernel_size=(3, 3)` para Conv2D, `kernel_size=(3, 3, 3)` para Conv3D.
107
+
108
+ ### Leer datos y gradientes
109
+
110
+ ```python
111
+ t.shape # tupla de ints
112
+ t.data # copia de los datos como list[float]
113
+ t.grad # copia del gradiente como list[float] (requiere requires_grad=True y backward() ya llamado)
114
+ ```
115
+
116
+ `.data` y `.grad` devuelven **copias** en cada acceso, no vistas — no hay interoperabilidad directa con NumPy/arrays en esta versión (ver limitaciones).
117
+
118
+ ## Rendimiento
119
+
120
+ mingrad usa un pool de memoria global (bump allocator, sin `malloc`/`free` por operación) y patrones de acceso a memoria cache-friendly (acceso contiguo, evitando iterar por columnas), auto-vectorizado por el compilador con `-O3`. No usa SIMD manual ni multithreading.
121
+
122
+ Benchmarks propios (forward + backward, batch representativo), comparado contra NumPy/BLAS puro y contra [nnetflow](https://pypi.org/project/nnetflow/) (motor de autograd en Python+NumPy):
123
+
124
+ | Escenario | mingrad vs referencia Python/NumPy |
125
+ |---|---|
126
+ | MLP denso (64→128→10), entrenamiento completo | ~7.8× más rápido |
127
+ | Conv2D (batch=8, 3×32×32→16 canales) | ~6.4× más rápido |
128
+ | Conv3D (batch=4, 2×16³→8 canales) | ~1.8× más rápido |
129
+
130
+ Los números exactos dependen de tu CPU y del tamaño de tus tensores; estos son solo un punto de referencia obtenido durante el desarrollo, no una garantía.
131
+
132
+ **Sobre `-march=native`**: por defecto, este paquete se compila con `-O3` sin `-march=native`. Es una decisión deliberada, no un descuido: durante el desarrollo confirmamos que `-march=native` (con AVX-512) causaba una **regresión de rendimiento de más de 4×** específicamente en Conv3D en el hardware de prueba, además del riesgo general de que un binario compilado con `-march=native` puede crashear (`SIGILL`) si se ejecuta en una CPU distinta a la que compiló. Si sabés que tu caso de uso se beneficia y tu hardware de build/deploy es el mismo, podés recompilar vos mismo con esa flag — pero medí antes y después, no asumas que es una mejora.
133
+
134
+ ## Limitaciones
135
+
136
+ Léelas antes de usar esto en algo importante.
137
+
138
+ - **El pool de memoria no libera tensores individuales.** No hay recolector de basura ni referencia-conteo por tensor: cada tensor que creás (incluyendo resultados intermedios de operaciones) ocupa espacio en el pool hasta que llamás `mg.reset_pool()` o `mg.shutdown()`. Un loop de entrenamiento largo sin resetear el pool eventualmente lo agota.
139
+
140
+ - **Si el pool se queda sin memoria, el proceso aborta (`SIGABRT`), no lanza una excepción Python.** Esto es intencional en el motor en C (preferimos fallar ruidosamente a corromper memoria), pero significa que **no podés capturarlo con `try/except`** — tu programa Python entero termina. Dimensioná `mg.init(pool_bytes)` con margen, y en producción considerá correr el entrenamiento en un subproceso si necesitás aislar este tipo de fallo.
141
+
142
+ - **`mg.reset_pool()` invalida todos los handles existentes sin avisar, y usar un handle viejo después de un reset puede devolver datos de OTRO tensor en silencio, sin ningún error.** Esto es una consecuencia de cómo se reciclan los índices de handles al hacer reset — no es un caso raro, lo reproducimos fácilmente durante el desarrollo. **Nunca conserves una referencia a un `Tensor`/`Model` creado antes de un `reset_pool()`.** Si necesitás resetear memoria entre epochs, hacelo solo cuando no haya ningún tensor vivo que te importe (por ejemplo, recreando el modelo también), o simplemente usá un pool lo bastante grande y no resetees.
143
+
144
+ - **No es thread-safe.** El estado interno (tabla de handles, último error, el pool mismo) es completamente global sin ningún lock. Usar mingrad desde más de un thread de Python al mismo tiempo es una condición de carrera, aunque el GIL de Python evite corrupción de memoria a nivel de bytecode individual.
145
+
146
+ - **`.data` y `.grad` devuelven copias en listas Python planas, no arrays de NumPy ni vistas de memoria.** Para tensores grandes esto tiene un costo notable de conversión en cada lectura. No hay soporte de `__array_interface__` ni buffer protocol en esta versión.
147
+
148
+ - **Sin GPU.** Todo corre en CPU.
149
+
150
+ - **Sin serialización de modelos.** No hay `save()`/`load()`; si necesitás persistir un modelo entrenado, tenés que extraer `model.parameters()` (cada uno con `.data`) y guardarlos vos mismo (por ejemplo a JSON), y reconstruir el modelo + cargar los datos a mano al volver a levantarlo.
151
+
152
+ - **Sin batching automático, sin DataLoader, sin manejo de datasets.** Vos armás los batches y se los pasás como listas planas.
153
+
154
+ - **`Model` solo cubre arquitecturas secuenciales** (una capa tras otra). No hay soporte para ramas, skip-connections (tipo ResNet), o cualquier grafo que no sea una cadena lineal. Para eso hay que operar directamente sobre `Tensor` y construir el grafo a mano con las operaciones sueltas (`+`, `@`, etc.).
155
+
156
+ - **Compilación probada en Linux con gcc.** `setup.py` intenta detectar Windows y usar flags de MSVC, pero no fue probado ahí. macOS con clang debería funcionar (misma familia de flags que gcc) pero tampoco fue verificado extensivamente.
157
+
158
+ - **Sin validación exhaustiva de shapes en todos los casos límite.** El motor de autograd fue validado contra gradiente numérico (diferencias finitas) para todas las operaciones sobre los casos de test incluidos, pero eso no cubre cada combinación posible de shapes, dtypes, o tamaños extremos.
159
+
160
+ ## Estructura del paquete
161
+
162
+ ```
163
+ mingrad/
164
+ ├── __init__.py # API Python ergonómica (Tensor, Model, SGD)
165
+ ├── _mingrad # módulo de extensión compilado (.so)
166
+ └── csrc/ # código fuente en C (motor + bindings)
167
+ ├── mingrad.h # API del motor en C puro
168
+ ├── mingrad_api.h/c # capa de handles, para exponer a otros lenguajes
169
+ ├── _mingrad.c # módulo de extensión Python (Python.h)
170
+ └── *.c # tensor, ops, losses, capas, conv2d/3d, optimizador
171
+ ```
172
+
173
+ Si preferís usar el motor directamente desde C (sin Python), `mingrad.h` es la API completa; `mingrad_api.h` es una capa simplificada basada en handles enteros, pensada originalmente para bindings pero utilizable también desde C directamente.
174
+
175
+ ## Tests
176
+
177
+ El motor de autograd fue validado con tests de gradiente numérico (diferencias finitas contra el gradiente analítico) para cada operación, incluyendo Conv2D y Conv3D con distintos stride/padding. Estos tests están en `tests/` y se compilan directo con gcc contra `mingrad/csrc/`, sin depender de Python. Los fuentes del motor no necesitan `Python.h` — solo `_mingrad.c` (el binding) lo requiere, así que se excluye de este build:
178
+
179
+ ```bash
180
+ gcc -std=c11 -O2 -Imingrad/csrc \
181
+ mingrad/csrc/arena.c mingrad/csrc/shape.c mingrad/csrc/tensor.c mingrad/csrc/ops.c \
182
+ mingrad/csrc/losses.c mingrad/csrc/layers.c mingrad/csrc/optim.c \
183
+ mingrad/csrc/conv2d.c mingrad/csrc/conv3d.c \
184
+ tests/test_grad_check.c -o test_grad_check -lm
185
+ ./test_grad_check
186
+ ```
187
+
188
+ ## Licencia
189
+
190
+ Ver [LICENSE](LICENSE).