A lightweight neural network library written in C11 for embedded systems.
cTensor is a compact tensor computation library designed for small client-side devices, such as mobile phones and microcontrollers. The library implements automatic differentiation and dynamic compute graph functionality, allowing for efficient training and deployment of neural networks on resource-constrained devices.
This library was developed as part of GSoC 2025 and has been successfully validated on ARM Cortex-M3 microcontrollers, achieving 90% classification accuracy on the Iris dataset in a bare-metal environment.
- Lightweight C11 Implementation: Minimal dependencies for wide compatibility
- Automatic Differentiation Framework: Complete gradient computation with backward pass
- Dynamic Compute Graph: Efficient computation flow with gradient tracking
- Pool-based Memory Management: Efficient memory allocation system for embedded devices
- Basic Arithmetic: add, subtract, multiply, divide, power (both tensor-tensor and tensor-scalar)
- Unary Operations: negation, absolute value, square, reciprocal
- Matrix Operations: matrix multiplication, transpose
- Mathematical Functions: logarithm, exponential, sine, cosine, tangent
- Shape Operations: unsqueeze, detach
- Broadcasting: Element-wise broadcasting for operations on tensors with different shapes
- Sum: All elements or along specific dimension
- Mean: All elements or along specific dimension
- Max/Min: All elements or along dimension with indices
- Argmax: Find indices of maximum values
- Layers: Linear (fully connected) layer
- Activation Functions: ReLU, Sigmoid, Tanh, ELU, SELU, Softmax
- Loss Functions: Cross-entropy, Softmax Cross-entropy, MSE, MAE, Huber Loss
- Weight Initialization: Glorot/Xavier initialization
- SGD: Stochastic Gradient Descent with momentum
- Adam: Adaptive moment estimation
- RMSProp: Root Mean Square Propagation
- AdaGrad: Adaptive Gradient Algorithm
- Features: Weight decay support for all optimizers
- Gradient Clipping: By norm, value, range, positive/negative values
- Evaluation Mode: Disable gradient computation for inference
- Dataset Utilities: Normalization, shuffling
cTensor has been successfully deployed and tested on:
- ARM Cortex-M3 (STM32F103ZE) using Keil MDK simulation
- Task: Neural network classification on Iris dataset
- Result: 90% accuracy matching desktop performance
- Complete validation project:cTensor_Cortex_SIM
- C Compiler with C11 support (GCC, Clang)
- CMake (3.10+) for build configuration
- Math library (automatically linked on non-Windows systems)
On Windows:
build.batOn Linux/macOS:
mkdir -p build &&cd build
cmake ..
cmake --build .cd ..On Linux/macOS:
./build_g.shOn Windows with GCC:
gcc -std=c11 -Iinclude -O0 -Wfatal-errors -g -DDEBUG -lm src/nn.c src/operator.c src/basic.c src/iris_dataset.c src/context.c src/pool.c src/utils.c src/common/vector.c src/optimizer/sgd.c src2/main.c -o mainand run main.exe from root directory
cTensor uses a custom test framework. To run the tests:
# Build the test executable with CMake
mkdir -p build &&cd build
cmake ..
cmake --build .# Run the tests
./cten_exeFor detailed testing information, refer to Testing Documentation.
Here's a complete example training a neural network on the Iris dataset:
#include"cten.h"#include<stdio.h>intmain() {
// Initialize cTensor librarycten_initilize();
// Load the Iris datasetconstfloat (*X)[4];
constint*y;
intnum_samples=load_iris_dataset(&X, &y);
// Create network parametersTensorShapehidden_shape= {4, 10, 0, 0}; // 4 inputs -> 10 hidden unitsTensorShapeoutput_shape= {10, 3, 0, 0}; // 10 hidden -> 3 classes// Initialize network parameters with Glorot initializationTensorW1=Glorot_init(hidden_shape, true);
Tensorb1=Tensor_zeros((TensorShape){1, 10, 0, 0}, true);
TensorW2=Glorot_init(output_shape, true);
Tensorb2=Tensor_zeros((TensorShape){1, 3, 0, 0}, true);
// Setup optimizerTensorparams[4] = {W1, b1, W2, b2};
optim_sgd*optimizer=optim_sgd_new(4, params);
optim_sgd_config(optimizer, 0.01f, 0.9f);
// Training loop// ...cten_finalize();
return0;
}// Basic tensor creationTensorTensor_new(TensorShapeshape, boolrequires_grad);
TensorTensor_zeros(TensorShapeshape, boolrequires_grad);
TensorTensor_ones(TensorShapeshape, boolrequires_grad);
// Tensor manipulationTensorTensor_transpose(Tensorself);
TensorTensor_detach(Tensorself);
TensorTensor_unsqueeze(Tensorself, intdim);
// Element accessfloatTensor_get(Tensorself, inti, intj, intk, intl);
voidTensor_set(Tensorself, inti, intj, intk, intl, floatvalue);
// BackpropagationvoidTensor_backward(Tensorself, Tensorgrad);// Element-wise operations with tensorsTensorTensor_add(Tensorself, Tensorother);
TensorTensor_sub(Tensorself, Tensorother);
TensorTensor_mul(Tensorself, Tensorother);
TensorTensor_div(Tensorself, Tensorother);
TensorTensor_pow(Tensorself, Tensorother);
// Element-wise operations with scalarsTensorTensor_addf(Tensorself, floatother);
TensorTensor_subf(Tensorself, floatother);
TensorTensor_mulf(Tensorself, floatother);
TensorTensor_divf(Tensorself, floatother);
TensorTensor_powf(Tensorself, floatother);
// Matrix operationsTensorTensor_matmul(Tensorself, Tensorother);
// Unary operationsTensorTensor_neg(Tensorself);
TensorTensor_abs(Tensorself);
TensorTensor_square(Tensorself);
TensorTensor_reciprocal(Tensorself);// Logarithmic and exponentialTensornn_log(Tensorself);
Tensornn_exp(Tensorself);
// Trigonometric functionsTensornn_sin(Tensorself);
Tensornn_cos(Tensorself);
Tensornn_tan(Tensorself);// Reduction operations (with macro dispatch)TensorTensor_sum(Tensorself); // Sum all elementsTensorTensor_sum(Tensorself, intdim); // Sum along dimensionTensorTensor_mean(Tensorself); // Mean of all elementsTensorTensor_mean(Tensorself, intdim); // Mean along dimensionTensorTensor_max(Tensorself); // Max of all elementsTensorMaxMinResultTensor_max(Tensorself, intdim); // Max along dimensionTensorTensor_min(Tensorself); // Min of all elementsTensorMaxMinResultTensor_min(Tensorself, intdim); // Min along dimension// Argmax operationvoidTensor_argmax(Tensorself, int*out);// Neural network layersTensornn_linear(Tensorinput, Tensorweight, Tensorbias);
// Activation functionsTensornn_relu(Tensorinput);
Tensornn_sigmoid(Tensorinput);
Tensornn_tanh(Tensorinput);
Tensornn_elu(Tensorself, floatalpha);
Tensornn_selu(Tensorself);
Tensornn_softmax(Tensorinput, intdim);
// Loss functionsTensornn_crossentropy(Tensory_true, Tensory_pred);
Tensornn_softmax_crossentropy(Tensory_true, Tensorlogits);
Tensornn_mse_loss(Tensory_true, Tensory_pred);
Tensornn_mae_loss(Tensory_true, Tensory_pred);
Tensornn_huber_loss(Tensory_true, Tensory_pred, floatdelta);
// Weight initializationTensorGlorot_init(TensorShapeshape, boolrequires_grad);// SGD Optimizeroptim_sgd*optim_sgd_new(intn_params, Tensor*params, floatweight_decay);
voidoptim_sgd_config(optim_sgd*self, floatlr, floatmomentum);
voidoptim_sgd_zerograd(optim_sgd*self);
voidoptim_sgd_step(optim_sgd*self);
// Adam Optimizeroptim_adam*optim_adam_new(intn_params, Tensor*params, floatlr, floatβ1, floatβ2, floatε, floatweight_decay);
voidoptim_adam_zerograd(optim_adam*self);
voidoptim_adam_step(optim_adam*self);
// RMSProp Optimizeroptim_rmsprop*optim_rmsprop_new(intn_params, Tensor*params, floatlr, floatβ, floatε, floatweight_decay);
voidoptim_rmsprop_zerograd(optim_rmsprop*self);
voidoptim_rmsprop_step(optim_rmsprop*self);
// AdaGrad Optimizeroptim_adagrad*optim_adagrad_new(intn_params, Tensor*params, floatlr, floatε, floatweight_decay);
voidoptim_adagrad_zerograd(optim_adagrad*self);
voidoptim_adagrad_step(optim_adagrad*self);// Gradient clipping functionsvoidcten_clip_grad_norm(Tensor*params, intn_params, floatmax_norm);
voidcten_clip_grad_value(Tensor*params, intn_params, floatmax_value);
voidcten_clip_grad_value_range(Tensor*params, intn_params, floatmin_value, floatmax_value);
voidcten_clip_grad_positive(Tensor*params, intn_params, floatmax_value);
voidcten_clip_grad_negative(Tensor*params, intn_params, floatmin_value);// TensorShape utilitiesintTensorShape_numel(TensorShapeshape);
intTensorShape_dim(TensorShapeshape);
intTensorShape_asdim(TensorShapeshape, intdim);
intTensorShape_tostring(TensorShapeshape, char*buf, intsize);
// Dataset utilitiesintload_iris_dataset(constfloat (**X)[4], constint**y);
voidTensor_normalize_dataset(constfloat (*X)[4], float (*X_norm)[4], intn_samples, intn_train_samples, intn_features);
voidTensor_shuffle_dataset(constfloat (*X)[4], constint*y, float (*X_shuffled)[4], int*y_shuffled, intn_samples, intn_features);
// Evaluation modevoidcten_begin_eval();
boolcten_is_eval();
voidcten_end_eval();
// Broadcastingboolcten_elemwise_broadcast(Tensor*a, Tensor*b);
Tensorreduce_gradient_for_broadcasting(Tensorgrad, TensorShapeoriginal_shape, TensorShapebroadcasted_shape);cTensor uses a pool-based memory management system to efficiently handle tensor allocations:
voidcten_initilize();
voidcten_finalize();
voidcten_begin_malloc(PoolIdid);
voidcten_end_malloc();
voidcten_free(PoolIdid);cTensor/
├── include/ # Header files defining the API
│ └── cten.h # Complete API header
├── src/ # Core implementation files
│ ├── basic.c # Basic tensor operations
│ ├── nn.c # Neural network primitives
│ ├── operator.c # Mathematical operators
│ ├── context.c # Memory management
│ ├── utils.c # Utility functions
│ ├── optimizer/ # Optimizer implementations
│ └── ...
├── src2/ # Example applications
│ └── main.c # Iris dataset example
└── tests/ # Test suite
| Category | Components | Status |
|---|---|---|
| Core Structs | Tensor, GradNode, TensorMaxMinResult | ✅ |
| Autograd | Tensor_backward, requires_grad, detach | ✅ |
| Tensor Creation | Tensor_new, zeros, ones, Glorot_init | ✅ |
| Binary Operations | add, sub, mul, div, pow, matmul | ✅ |
| Unary Operations | neg, abs, square, reciprocal | ✅ |
| Math Functions | log, exp, sin, cos, tan | ✅ |
| Aggregations | sum, mean, max, min (with indices) | ✅ |
| Search/Sort | argmax | ✅ |
| Shape Operations | transpose, unsqueeze | ✅ |
| NN Layers | nn_linear | ✅ |
| Activations | ReLU, Sigmoid, Tanh, ELU, SELU, Softmax | ✅ |
| Loss Functions | CrossEntropy, MSE, MAE, Huber | ✅ |
| Optimizers | SGD, Adam, RMSProp, AdaGrad | ✅ |
| Training Utils | Gradient Clipping, Evaluation Mode, Weight Decay | ✅ |
Contributions to cTensor are welcome! Key areas for contribution include:
- Performance Optimization: Benchmarking and SIMD implementations
- Advanced Layers: Convolutional and recurrent neural network layers
- Documentation: Examples, tutorials, and API documentation improvements
- Testing: Expanding test coverage and validation on different platforms
This project was developed during Google Summer of Code 2025 by Advait Gaur under the mentorship of PrimedErwin, Anurag Bhat, and blueloveTH. The project successfully transformed cTensor from a basic prototype into a functional deep learning framework suitable for embedded applications.
This project is licensed under the MIT License - see the LICENSE file for details.