Skip to content

Commit 171acbb

Browse files
committed
nn: define neural network module API
1 parent b1ac4b7 commit 171acbb

1 file changed

Lines changed: 318 additions & 0 deletions

File tree

  • include/tensorlib

‎include/tensorlib/nn.h‎

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
#ifndef TENSORLIB_NN_H
2+
#define TENSORLIB_NN_H
3+
4+
/*
5+
* Neural-network layer declarations for TensorLib.
6+
*
7+
* This header deliberately contains declarations and ownership contracts
8+
* only. Implementations belong in a future nn/ source directory.
9+
*
10+
* The neural-network layer stores the persistent model structure:
11+
*
12+
* model -> modules -> parameters
13+
*
14+
* The dynamic Autograd graph is still created by calling ag_* operations
15+
* during each module forward pass. A module is not itself an Autograd node.
16+
*/
17+
18+
#include <stddef.h>
19+
#include <stdint.h>
20+
21+
#include "autograd.h"
22+
23+
24+
/* Forward declarations. */
25+
typedef struct nn_rng nn_rng;
26+
typedef struct nn_parameter nn_parameter;
27+
typedef struct nn_activation nn_activation;
28+
typedef struct nn_module nn_module;
29+
typedef struct nn_linear nn_linear;
30+
typedef struct nn_mlp nn_mlp;
31+
typedef struct nn_mlp_config nn_mlp_config;
32+
33+
34+
/*
35+
* Deterministic random-number generator state.
36+
*
37+
* The algorithm remains an implementation detail. The seed is public so
38+
* callers can reproduce parameter initialization and training experiments.
39+
*/
40+
struct nn_rng {
41+
uint64_t state;
42+
};
43+
44+
45+
/* Weight and bias initialization policies. */
46+
typedef enum {
47+
NN_INIT_ZERO,
48+
NN_INIT_XAVIER_UNIFORM,
49+
NN_INIT_XAVIER_NORMAL,
50+
NN_INIT_HE_UNIFORM,
51+
NN_INIT_HE_NORMAL
52+
} nn_init_kind;
53+
54+
55+
/*
56+
* A persistent trainable tensor.
57+
*
58+
* Ownership:
59+
* - The parameter owns value.
60+
* - value is an Autograd leaf with requires_grad == 1 when trainable.
61+
* - value->grad is allocated and accumulated by Autograd during backward.
62+
* - name is owned by the parameter and is used for diagnostics and state
63+
* serialization.
64+
*
65+
* A parameter is not an operation node and must have creator == NULL.
66+
*/
67+
struct nn_parameter {
68+
char* name;
69+
ag_tensor* value;
70+
int trainable;
71+
};
72+
73+
74+
/*
75+
* Activation callback.
76+
*
77+
* input is borrowed. The callback returns one owned ag_tensor reference.
78+
* The activation descriptor is passed to the callback so future
79+
* parameterized activations can use activation->context.
80+
*/
81+
typedef ag_tensor* (*nn_activation_forward_fn)(
82+
const nn_activation* activation,
83+
const ag_tensor* input
84+
);
85+
86+
87+
/*
88+
* Activation descriptor.
89+
*
90+
* Built-in activations are stateless and use context == NULL. A custom or
91+
* parameterized activation may store borrowed configuration in context; the
92+
* descriptor does not own that context.
93+
*/
94+
struct nn_activation {
95+
const char* name;
96+
nn_activation_forward_fn forward;
97+
const void* context;
98+
};
99+
100+
101+
/* Generic module callbacks. */
102+
typedef ag_tensor* (*nn_module_forward_fn)(
103+
const nn_module* module,
104+
const ag_tensor* input
105+
);
106+
107+
typedef void (*nn_module_destroy_fn)(nn_module* module);
108+
109+
110+
/*
111+
* Common base structure embedded as the first field of every module type.
112+
*
113+
* Ownership:
114+
* - A module owns its registered parameters.
115+
* - A module owns its registered child modules.
116+
* - The parameter and child arrays are implementation-managed dynamic
117+
* arrays.
118+
* - type_name is static or borrowed; name is owned by the module.
119+
*/
120+
struct nn_module {
121+
const char* type_name;
122+
char* name;
123+
124+
nn_module_forward_fn forward;
125+
nn_module_destroy_fn destroy;
126+
127+
nn_parameter** parameters;
128+
size_t parameter_count;
129+
size_t parameter_capacity;
130+
131+
nn_module** children;
132+
size_t child_count;
133+
size_t child_capacity;
134+
135+
int training;
136+
};
137+
138+
139+
/*
140+
* Fully connected layer:
141+
*
142+
* output = input @ transpose(weight) + bias
143+
*
144+
* PyTorch-style parameter layout:
145+
* weight: [out_features, in_features]
146+
* bias: [out_features]
147+
*
148+
* weight and bias are convenient aliases to parameters registered in base;
149+
* base remains the owner of the registered parameter objects.
150+
*/
151+
struct nn_linear {
152+
nn_module base;
153+
154+
nn_parameter* weight;
155+
nn_parameter* bias;
156+
157+
int in_features;
158+
int out_features;
159+
int use_bias;
160+
};
161+
162+
163+
/*
164+
* MLP construction settings.
165+
*
166+
* For hidden_sizes = {100} and hidden_count = 1, this creates:
167+
*
168+
* Linear(input_features, 100)
169+
* Linear(100, output_features)
170+
*
171+
* activations contains one descriptor per Linear layer, so its length must
172+
* be hidden_count + 1. An activation with forward == NULL means identity.
173+
* The config and its arrays are borrowed only during construction.
174+
*/
175+
struct nn_mlp_config {
176+
int input_features;
177+
178+
const int* hidden_sizes;
179+
size_t hidden_count;
180+
181+
int output_features;
182+
const nn_activation* activations;
183+
184+
int use_bias;
185+
186+
nn_init_kind weight_init;
187+
nn_init_kind bias_init;
188+
};
189+
190+
191+
/*
192+
* Generic multi-layer perceptron.
193+
*
194+
* Linear layers are owned by base.children. activations[i] is applied after
195+
* the i-th Linear layer. layer_count equals base.child_count.
196+
*/
197+
struct nn_mlp {
198+
nn_module base;
199+
200+
nn_activation* activations;
201+
size_t layer_count;
202+
};
203+
204+
205+
/*
206+
* Built-in activation descriptors.
207+
*
208+
* These return small descriptors by value. The returned descriptors do not
209+
* own resources and are safe to store inside nn_mlp::activations.
210+
*/
211+
nn_activation nn_activation_relu(void);
212+
nn_activation nn_activation_gelu(void);
213+
nn_activation nn_activation_sigmoid(void);
214+
nn_activation nn_activation_tanh(void);
215+
216+
/* Create a caller-defined activation descriptor. */
217+
nn_activation nn_activation_custom(
218+
const char* name,
219+
nn_activation_forward_fn forward,
220+
const void* context
221+
);
222+
223+
224+
/*
225+
* RNG API — declarations only; implementation is future work.
226+
*/
227+
void nn_rng_seed(nn_rng* rng, uint64_t seed);
228+
float nn_rng_uniform(nn_rng* rng, float min, float max);
229+
float nn_rng_normal(nn_rng* rng, float mean, float stddev);
230+
231+
232+
/*
233+
* Parameter API — declarations only; implementation is future work.
234+
*
235+
* The constructor allocates the tensor, initializes its storage, wraps it as
236+
* an Autograd leaf, and transfers ownership of the result to the caller.
237+
*/
238+
nn_parameter* nn_parameter_create(
239+
const char* name,
240+
int ndim,
241+
const int* dims,
242+
int trainable,
243+
nn_init_kind initializer,
244+
nn_rng* rng
245+
);
246+
247+
void nn_parameter_destroy(nn_parameter* parameter);
248+
249+
250+
/*
251+
* Module registration API — declarations only; implementation is future
252+
* work.
253+
*
254+
* Registration transfers ownership of parameter or child to module on
255+
* success. A failed registration leaves ownership with the caller.
256+
*/
257+
int nn_module_register_parameter(
258+
nn_module* module,
259+
nn_parameter* parameter
260+
);
261+
262+
int nn_module_register_child(
263+
nn_module* module,
264+
nn_module* child
265+
);
266+
267+
/* Recursively count and access parameters for optimizer/model traversal. */
268+
size_t nn_module_parameter_count(const nn_module* module);
269+
nn_parameter* nn_module_parameter_at(
270+
const nn_module* module,
271+
size_t index
272+
);
273+
274+
/* Execute a module's forward callback and return one owned output reference. */
275+
ag_tensor* nn_module_forward(
276+
const nn_module* module,
277+
const ag_tensor* input
278+
);
279+
280+
281+
/*
282+
* Linear-layer API — declarations only; implementation is future work.
283+
*/
284+
nn_linear* nn_linear_create(
285+
const char* name,
286+
int in_features,
287+
int out_features,
288+
int use_bias,
289+
nn_init_kind weight_init,
290+
nn_init_kind bias_init,
291+
nn_rng* rng
292+
);
293+
294+
void nn_linear_destroy(nn_linear* layer);
295+
296+
ag_tensor* nn_linear_forward(
297+
const nn_linear* layer,
298+
const ag_tensor* input
299+
);
300+
301+
302+
/*
303+
* MLP API — declarations only; implementation is future work.
304+
*/
305+
nn_mlp* nn_mlp_create(
306+
const char* name,
307+
const nn_mlp_config* config,
308+
nn_rng* rng
309+
);
310+
311+
void nn_mlp_destroy(nn_mlp* model);
312+
313+
ag_tensor* nn_mlp_forward(
314+
const nn_mlp* model,
315+
const ag_tensor* input
316+
);
317+
318+
#endif /* TENSORLIB_NN_H */

0 commit comments

Comments
 (0)