Menu
Version 0.0.3
GradieX Documentation
Single-precision arithmetic, OpenMP simd pragmas on the hot loops, mini-batch gradient accumulation, and a derivative lookup that ties each activation and loss to its own derivative.
Everything builds. The engine compiles clean, the C++ suite passes 91 checks under AddressSanitizer and UBSan, and the Python package builds and passes its own suites. The bindings were ported to the float engine and gained a matplotlib plotting layer. See Build Status.
Build Status
| Target | Command | Result |
|---|---|---|
| C++ engine | clang++ -std=c++20 -O2 main.cpp -o main | Builds — 0 errors, 18 warnings under -Wall -Wextra |
| The demo | ./main | Runs — exit 0, and prints nothing; see below |
| C++ tests | ./tests/run_tests.sh | Passes — 91 checks, also clean under --asan |
| Python module | pip install . | Builds — gradiex-0.0.3, with numpy and matplotlib |
| Python tests | python3 python/test_bindings.py | Passes — bindings, guards, C++/Python parity |
| Plotting tests | python3 python/test_plotting.py | Passes — every diagram, headless |
The 18 warnings are 17 instances of 'restrict' type qualifier on return type has no effect plus one unused parameter. They are benign but load-bearing in an odd way — see The __restrict__ return qualifier. tests/run_tests.sh already passes -Wno-ignored-qualifiers.
main.cpp calls train() without the verbose argument, which now defaults to false, and it no longer calls the removed printAllLayers(). So the binary trains 10,000 epochs and prints nothing at all. Pass true as the sixth argument, or read lossBuffer, to see anything.
Getting Started
Header-only, one translation unit, as in v0.0.2:
clang++ -std=c++20 -O2 main.cpp -o main
./main
To get anything out of the #pragma omp simd directives, OpenMP must be enabled:
clang++ -std=c++20 -O2 -fopenmp-simd main.cpp -o main # Apple clang
g++-14 -std=c++20 -O3 -fopenmp main.cpp -o main # GCC
clang++ -std=c++20 -O3 -fopenmp main.cpp -o main # LLVM clang + libomp
-fopenmp-simdApple clang — the default c++ on macOS — rejects -fopenmp, but accepts -fopenmp-simd, which enables exactly the directives core/ uses and needs no runtime library. Without one of them the pragmas are silently ignored: no error, no warning, no vectorisation. The flag is worth up to 2.4× on wide layers — see SIMD Pragmas.
What Changed Since v0.0.2
| v0.0.2 | v0.0.3 | |
|---|---|---|
| Scalar type | double | float throughout the engine |
| Vectorisation | #pragma GCC unroll 4 | #pragma omp simd, with reduction clauses on the dot product |
| Gradients | assigned (=), applied immediately | accumulated (+=), then scaled and applied |
| Training | per-sample SGD | mini-batch SGD — Network(..., int batches) |
| Derivatives | passed in by the caller, unchecked | looked up from the forward function via fetchAssociatedDerivative |
Layer constructor | 7 arguments | 5 — the two derivatives are derived |
addHiddenLayer / addOutputLayer | 8 arguments | 6 |
computeLoss | always squared error, ignoring the configured loss | calls the configured loss — fixes v0.0.2 known bug 1 |
| Scratch pads | inputSize * width, only width used | width — fixes v0.0.2 known bug 7 |
| Activations | sigmoid, relu, leakyRelu, tanh, swish, gelu, softmax | the same, plus identity |
| Losses | mse, mae, huber, bce, cce, fused softmax+CCE | unchanged |
| Epoch logging | unconditional std::cout | verbose flag, default off |
| Removed | — | Layer::print, Network::printAllLayers, Network::runEpoch, Network::backwardPass |
| Commented out | — | Network::predict |
| Tests | present, several fail by design | ported — 91 checks, all passing |
| Python bindings | build and work | ported to float; training delegated to the engine |
| Plotting | — | seven matplotlib diagrams, light and dark |
| Shuffling | none | per-epoch, seeded, inside train() |
| Seeded init | none | initializers::setSeed / gradiex.seed |
The full v0.0.2 reference is at v0.0.2 → C++ Library, and the three-version comparison is on the versions page.
Plots
New in v0.0.3: the Python package ships a matplotlib layer. net.train(..., plot=True) draws the loss curve when training finishes, and seven further diagrams cover the architecture, the weights, the fit and the loss surface. Every one renders in light and dark.
2 → 16 → 16 → 1 network at 100 % test accuracy. The full gallery is on the Python Library page.Reference
- The C++ Library
- The header-only engine in
core/. Thefloatsignatures, the SIMD pragmas, gradient accumulation and batching, the derivative lookup, and what is still open. - The Python Library
- The
gradiexpackage: install, the API, what the binding guarantees the C++ API does not, and the plot gallery — loss curves, architecture diagrams, decision boundaries, weight maps and 3‑D loss landscapes. - Performance
- Benchmarks against v0.0.2 on a shared harness: 1.2–3.2× per sample, 1.7–5.5× with mini-batching, and what the
-fopenmp-simdflag is worth at each layer width.
If you are looking for something specific:
- What switching to
floatcosts - Whether the SIMD pragmas do anything
- Gradient accumulation and batching
- How derivatives are resolved
- Why two translation units will not link
- Known bugs and limitations
- The plot gallery
At a Glance
| Item | Value |
|---|---|
| Language | C++20 |
| Scalar type | float throughout the engine |
| Dependencies | None. OpenMP is optional and, when absent, silently inert. |
| Layout | Header-only; core/ compiles into the caller |
| Object model | Network → Layer → flat vectors |
| Training | Mini-batch stochastic gradient descent |
| Activations | identity, sigmoid, relu, leaky_relu, tanh, swish, gelu, softmax |
| Losses | mse, mae, huber, binary cross entropy, cross entropy, fused softmax + CCE |
| Initializers | zeros, xavier, he |
| Python binding | pybind11 — gradiex, with matplotlib plotting |
| Tests | 91 checks, passing under ASan and UBSan |
| Translation units | One only — two will not link; see ODR |
| Build status | Builds — 0 errors, 18 warnings |
| License | MIT |
Project State
v0.0.3 keeps v0.0.2's flat memory layout and changes what runs over it. Three things drive the release: halving the width of every scalar so twice as much fits in cache and a vector register holds eight lanes instead of four; annotating the hot loops so a compiler will vectorise them; and restoring mini-batching, which the v0.0.2 rewrite dropped and v0.0.1 had.
Two v0.0.2 defects are fixed along the way. computeLoss now calls the layer's configured loss instead of always summing squared error, and the scratch pads are sized width rather than inputSize * width. A third is addressed in design: fetchAssociatedDerivative derives the derivative from the forward function, so a caller can no longer pair sigmoid with reluDerivative. That lookup works for five of the eight activations — see Derivative Lookup.
What remains is reach rather than correctness. The engine builds and trains; python/ and tests/ were never touched, so the release has no automated verification at all — the gradient check, the zero-allocation assertion and the binding tests are all sitting in double-typed files that no longer compile. The new batching path has never been asserted against anything. Porting the suite is the highest-value work left; see Test Suite.
Fixed in v0.0.3: pyproject.toml and the extension both report 0.0.3, so gradiex.__version__ finally matches the directory it ships in. In 0.0.1/ and 0.0.2/ it still reads 0.0.1.