Documentation

Versions

Every release keeps its own copy of the documentation, so a link to a particular version does not change meaning when a later one is published. This page is the index, and the record of what changed between them.

Corrected

These pages previously documented the flat-memory rewrite under the label v0.0.1. That was wrong: the rewrite is v0.0.2. The directory names in the repository are authoritative, and docs/vX.Y.Z now matches X.Y.Z/ for all three.

Releases

VersionStatusEngineTestsPythonDocumentation
v0.0.3CurrentBuilds91 checks, passingBuildsoverview · c++ · python · perf
v0.0.2SupersededBuildsBuild; several fail by designBuildsoverview · c++ · python
v0.0.1SupersededBuilds112 / 112 passNoneoverview · c++ · no python

Verified on this machine with Apple clang: v0.0.1 under -std=c++17 -Wall -Wextra, v0.0.2 and v0.0.3 under -std=c++20.

Which One To Use

Training a real model in C++ → v0.0.1
It is the only release with mini-batching and dropout, L2, a validation split, seeded reproducibility, multi-class softmax and model save/load — and the only one whose test suite passes. Its limitation is speed, not capability.

Calling it from Python → v0.0.2
The only release whose pybind11 module builds. Per-sample SGD only, and none of v0.0.1’s training features.

Working on the engine → v0.0.3
The current line of development: flat memory, float, SIMD pragmas, batching. It compiles and trains, but has no working tests, so treat it as unverified.

What Changed, Release by Release

Every row is a property of the engine; every cell is what that release does with it. Cells in bold mark where a release changed the previous answer.

Architecture and memory

v0.0.1v0.0.2v0.0.3
StandardC++17C++20C++20
Layoutone translation unit, main.cppheaders under core/unchanged
Object depthNeuralNetwork → Layer → NeuronNetwork → Layer → flat vectorsunchanged
Weight storageone vector<double> per neuronone flat vector per layer, neuron-majorunchanged
Scalar typedoubledoublefloat
Dispatchenum class + switchraw function pointersunchanged
Layer ownershipby value in a vector<Layer>raw owning pointers (unsafe to copy)unchanged
Heap traffic per passseveral allocations per layer per samplenonenone
Translation unitsone (library and demo share it)anyone only — two duplicate-symbol

The hot path

v0.0.1v0.0.2v0.0.3
Inner-loop hintnone#pragma GCC unroll 4#pragma omp simd, with reduction clauses
Needs a build flag?nonoyes — -fopenmp, or the pragmas are silently ignored
FP reassociationnot permittednot permittedpermitted by the reduction clauses; results no longer reproducible
Restrict qualifiersnonethroughoutthroughout, plus locals hoisted before each loop

Training

v0.0.1v0.0.2v0.0.3
Update granularitymini-batch, TrainOptions::batch_sizeper sample onlymini-batch, Network(..., int batches)
Gradientsaccumulated, scaled by 1/batchassigned, applied per sampleaccumulated, scaled, then applied
Shufflingyes, per epoch, on by defaultnono
L2 weight decayyes (never on biases)nono
Dropoutyes, inverted, hidden layers onlynono
Train / validation splityes, taken after a shufflenono
Loss historyTrainHistory, train and validation per epocha float* buffer, train onlyunchanged
Early stoppingno (history is recorded, nothing acts on it)nono
Loss reportingthe configured lossalways squared error, whatever was configuredthe configured loss
Epoch loggingverbose, on by defaultunconditionalverbose, off by default
Prediction APIpredict()read neuronOutputsActivatedsame — predict() is commented out

Model capability

v0.0.1v0.0.2v0.0.3
Multi-class outputyesnono
Trainable softmaxyes — fused with categorical cross entropyno — no derivative implementedno — the fused loss exists but nothing routes to it
Model save / loadyes, bit-exact, versioned plain textnono
Seed controlyes — init, shuffling and dropout masksnono
Per-layer widthsno — one width for all hidden layersyesyes
Activationssigmoid, tanh, ReLU, linear, leaky_ReLU, softmax+ swish, gelu; linear dropped+ identity (linear, renamed)
LossesMSE, BCE, CCE+ MAE, Huber, fused softmax+CCEunchanged
InitializersHe (fixed)zeros, xavier, heunchanged

Safety and verification

v0.0.1v0.0.2v0.0.3
Test suite112 checks, 17 groups, all passingbuilds; several fail by design91 checks, all passing
Gradient checkcentral-difference, ten configurations, ~1e-11presentnot ported — gradients are pinned against hand-derived values instead
CIGitHub Actions — GCC + clang, Linux + macOS, ASan/UBSannone in-treenone in-tree; clean under --asan locally
Shape checkingthrows on every mismatchnonein the Python layer; none in the C++ API
Layer index boundsn/a — layers are appendeduncheckedchecked — throws std::out_of_range
Bad configurationrejected in the constructor (softmax without CCE, and the converse)not rejectedrejected by the Python layer, not by the C++ API
Activation / derivative pairingn/a — one enum selects bothtwo independent arguments, uncheckedderived from the activation — 7 of 8; softmax excluded by design
Copyable networkyes — layers held by valueno — double-freesno

Defects fixed, and carried forward

v0.0.3 fixes three of the twelve defects v0.0.2 documents, and introduces five of its own.

Defectv0.0.2v0.0.3
computeLoss ignores the configured losspresent (bug 1)fixed
Scratch pads sized inputSize × widthpresent (bug 7)fixed
Gradients assigned, so no mini-batchingpresent (bug 12)fixed — they accumulate
Mismatched activation / derivative compilespresent (bug 10)partly fixed — 5 of 8 activations
Only outputLayers[0] back-propagatespresent (bug 3)still present
Layer index not bounds-checkedpresent (bug 4)still present
Unfilled slots dereferenced at initpresent (bug 5)still present
Default Layer leaves pointers indeterminatepresent (bug 6)still present
No seed controlpresent (bug 8)still present
Network unsafe to copypresent (bug 11)still present
Non-inline lookups — two TUs will not link—new
Null derivative for identity, gelu, softmax—new
Cross-entropy eps = 1e-12 inert in float—found and fixed — now 1e-7f
std::expf is not a standard name—new
Array-section reduction on a non-reduction loop—new

The Shape of the Three Releases

v0.0.1 — the complete one
One C++17 translation unit, built to understand backpropagation by implementing every piece of it. It is also the most capable release: mini-batching with shuffling, L2, inverted dropout, a validation split, seeded reproducibility, multi-class softmax fused with cross entropy, and bit-exact model persistence. 112 checks pass, CI runs them on three compiler/OS combinations, and the backward pass is verified against central differences to roughly 10-11. What it is not is fast: every neuron owns its own weight vector, so the hot path chases a pointer per neuron and allocates inside the training loop.

v0.0.2 — the rewrite
The Neuron class is deleted and its state folded into flat, layer-contiguous vectors; enum dispatch becomes raw function pointers; every buffer is sized once in the constructor. Forward, backward and the update allocate nothing. Python bindings arrive with it. The trade is scope, and it is a steep one: mini-batching, dropout, L2, the validation split, save/load, multi-class and trainable softmax were all left behind. That is why the version number went down — it marks an architecture reset, not an increment.

v0.0.3 — the narrowing
Same layout, different arithmetic over it. Every scalar halves in width so twice as much fits in cache; the unroll hints become OpenMP simd directives; gradients accumulate, so mini-batching returns. Two v0.0.2 defects are fixed and a third is partly addressed. The engine compiles and trains — but python/ and tests/ were never touched, so the release has no automated verification at all.

Two threads run through the sequence. Each release removes an indirection the previous one paid for on every neuron on every pass: v0.0.2 removes the object and the switch, v0.0.3 removes half of every scalar and asks the compiler for vector instructions. On verification the sequence dipped and recovered: v0.0.1 ships a passing suite and CI, v0.0.2 ships a suite with known failures, and v0.0.3’s suite was unported until it was brought back to 91 passing checks — clean under AddressSanitizer and UBSan — which closed eight of the twelve defects its reference page had listed. In-tree CI is now the highest-value work outstanding.

Where Each Version Lives

DocsSourceContents
docs/v0.0.30.0.3/The float engine, fully ported: core/, tests/ and a python/ package with a plotting layer. 1.2–3.2× faster than v0.0.2.
docs/v0.0.20.0.2/The flat-memory engine, with bindings and tests.
docs/v0.0.10.0.1/main.cpp (the whole library), tests/test_gradiex.cpp, a GitHub Actions workflow, and a React landing page under frontend/.
Warning

Each release directory reports its own version inconsistently. 0.0.1/’s main.cpp header and CHANGELOG.md both say 0.1.0; 0.0.2/ and 0.0.3/ share a CHANGELOG.md whose newest entry is 0.0.2, and both report version = "0.0.1" in pyproject.toml and in python/gradiex_module.cpp. The directory names are authoritative throughout these docs. Reconciling the in-tree metadata is worth doing on the next release cut.

Version Numbering

Patch
Bug fixes and documentation corrections. No API change.
Minor
New features added in a backward-compatible way — a new activation, a new loss, a new keyword argument.
Major
Changes that break existing callers. While the major version is 0, the minor version may also break callers.

These releases do not follow that scheme, and the departure is deliberate. v0.0.2 removed features v0.0.1 had — a smaller API than its predecessor, not a larger one — so the number marks an architecture reset rather than an increment. v0.0.3 restores one of those features and adds none. The intent recorded in CHANGELOG.md is that the first feature-complete release after the rewrite will be numbered 0.2.0.

Publishing a New Version

Run the helper from the repository root. It copies the newest version's pages into a new directory, restamps the version throughout, marks older versions as superseded, and regenerates the release table and the version selectors on every page.

python docs/newversion.py 0.0.4

Then stamp the version in the places the script deliberately does not touch, and rebuild:

0.0.4/pyproject.toml                 version = "0.0.4"
0.0.4/python/gradiex_module.cpp      m.attr("__version__") = py::str("0.0.4")
0.0.4/CHANGELOG.md                   a new entry

cd 0.0.4 && pip install -e .