Menu

Version 0.0.1

GradieX Documentation

A neural network library in a single C++17 translation unit, with no dependencies beyond the standard library. Mini-batching, dropout, L2, seeded reproducibility, multi-class softmax and bit-exact model persistence — verified by 112 checks.

A note on the number

This release is 0.0.1/ on disk, and these pages call it v0.0.1. Its own metadata disagrees: the main.cpp header and CHANGELOG.md both say 0.1.0. The directory name is authoritative here. Worth reconciling in the source, since v0.0.2’s changelog also refers back to this release as 0.0.1.

Getting Started

There is no build system and nothing to link. main.cpp is the library and the demo:

g++ -std=c++17 -O2 -Wall -Wextra -pedantic main.cpp -o gradiex
./gradiex

The demo runs two worked examples: XOR with per-sample SGD, and a three-class spiral classifier that trains with mini-batches, saves itself, reloads, and confirms the reloaded model predicts identically.

The test suite builds the same way:

g++ -std=c++17 -O2 -Wall -Wextra -pedantic tests/test_gradiex.cpp -o test_gradiex
./test_gradiex
Verified

Both build clean under -Wall -Wextra with Apple clang and C++17, and the suite reports 112 passed, 0 failed. The spiral classifier reaches 100% accuracy and its save/load round-trip is bit-for-bit identical across all 360 points.

Using the library from your own code is the same include:

#include "main.cpp"     // or copy the classes into a header

NeuralNetwork net(n_features, 2, 32, n_classes,
                  "ReLU", "softmax", "categorical_cross_entropy", 42);

TrainOptions o;
o.epochs = 300;  o.learning_rate = 0.05;  o.batch_size = 32;
o.l2 = 1e-4;     o.validation_split = 0.2;

TrainHistory h = net.train(X, Y, o);
net.save("model.gx");

Because the whole library lives in main.cpp alongside a main(), including it into another program means renaming that main. The test suite does exactly this — #define main gradiex_example_main before the include, #undef main after — so the tests exercise the real implementation with no duplicated source. v0.0.2 splits the engine into headers under core/, which removes the need for the trick.

Reference

The C++ Library
The whole engine: the Activation and Loss enums, Neuron, Layer, TrainOptions / TrainHistory, NeuralNetwork, the training loop, the model file format, and the invariants that must not be broken.

There are no Python bindings in this release; they arrive in v0.0.2.

If you are looking for something specific:

At a Glance

ItemValue
LanguageC++17
Scalar typedouble throughout
DependenciesNone (C++ standard library only)
LayoutOne translation unit — main.cpp, 1,206 lines
Python bindingNone
Object modelNeuralNetwork → Layer → Neuron
Dispatchenum class on the hot path; strings parsed once at the API boundary
TrainingMini-batch SGD with per-epoch shuffling
RegularizationL2 weight decay, inverted dropout, train/validation split
Activationssigmoid, tanh, ReLU, linear, leaky_ReLU, softmax (output only)
Lossesmean_squared_error, binary_cross_entropy, categorical_cross_entropy
InitializationHe, N(0, sqrt(2/fan_in)); biases zero
ReproducibilitySeeded — initialization, shuffling and dropout masks
PersistenceVersioned plain text, bit-exact round-trip
Tests112 checks across 17 groups, all passing
CIGitHub Actions — GCC and clang on Linux, clang on macOS, plus ASan/UBSan
ExtrasReact landing page under frontend/
Build statusClean under -Wall -Wextra -pedantic -Werror
LicenseMIT

The Web Frontend

v0.0.1 is the only release that ships a frontend/ directory. It is a React 19 + Vite + Tailwind landing page whose job is to describe the project and hand out a prebuilt Linux binary. It is distribution, not library API — nothing in main.cpp knows it exists.

cd frontend
npm install
npm run dev        # development server
npm run build      # production bundle into dist/
npm run preview    # serve the built bundle
npm run lint       # eslint

The download is a file-path contract rather than anything dynamic. Place the compiled binary at:

frontend/public/downloads/gradiex-linux-x86_64

and Vite copies it into dist/, where the page links it as /downloads/gradiex-linux-x86_64. The repository has a binary checked in at both paths. The CI workflow's release-binary job builds exactly that artifact with g++ -std=c++17 -O2 main.cpp -o gradiex-linux-x86_64 and uploads it, so the two halves line up — though nothing automated copies the artifact into frontend/public/downloads/; that step is manual.

For deployment the directory carries a Dockerfile (a Node build stage, then nginx serving dist/) and an nginx.conf. The React source is one 139-line src/App.jsx — a hero section, three feature cards and a three-step workflow list — plus src/App.css and the Vite entry point.

Warning

The landing-page copy advertises a “Readable Tensor API” and composing “layers and optimizers”. The library has no tensor type and one optimizer, plain SGD. If the page is going to be published, that copy is worth bringing in line with Not Supported.

Project State

v0.0.1 is the most complete release of GradieX by feature count, and the only one whose test suite passes. Everything the library claims is exercised: the backward pass is checked against central-difference numerical gradients across ten network configurations to roughly 10-11, mini-batch updates are asserted to equal the mean of the per-sample updates, save/load is asserted bit-exact, and seeded runs are asserted identical.

Its limitation is structural rather than behavioural. Every neuron owns its own std::vector<double> of weights and a second of gradients, so a forward pass chases one pointer per neuron and the weight matrix is scattered across the heap. Layer::forward and backward_local also allocate and return vectors by value, inside the training loop. That is the cost v0.0.2 was written to remove — and removing it is why v0.0.2 ships a smaller API than this one.

Two smaller constraints are worth knowing before building on it. Every hidden layer shares a single width fixed by the constructor, though load() reads each layer's shape independently, so a hand-written model file with varying widths loads and trains correctly. And TrainHistory reports best_validation_loss and best_epoch but nothing acts on them — there is no early stopping, so a caller watching for overfitting has to read the history itself.

What v0.0.2 Changed

v0.0.2 is a rewrite of the memory model, not of the math. It is also not a superset of this release — most of what v0.0.1 does above plain training was dropped and has been returning since.

v0.0.1v0.0.2
StandardC++17C++20
Layoutone translation unitheaders under core/
Object depthNeuralNetwork → Layer → NeuronNetwork → Layer → flat vectors
Weight storageone vector<double> per neuronone flat vector per layer
Dispatchenum class + switchraw function pointers
Heap traffic per passseveral allocations per layer per samplenone
Mini-batchingyesno — per-sample only
L2, dropout, validation splityesno
Model save / loadyes, bit-exactno
Multi-class / fused softmax + CCEyesno
Seed controlyesno
Python bindingsnonepybind11
Tests112 passingpresent, and several fail by design

The full picture across all three releases is on the versions page.