The Problem

C developers who need automatic differentiation must either write ad‑hoc gradient code or pull in heavyweight C++/Python libraries. Both options add compile‑time bloat, external dependencies, and a steep learning curve for pure C projects.

What This Does

bare‑lm supplies a minimal autograd tensor engine written in pure C. The core implementation lives in src/bare.c (2 378 lines) and its public API in src/bare.h. Example programs in examples/ (e.g., xor.c, mnist.c) show a complete training loop using the API: tensor allocation (tensor_init), forward ops (linear_t, relu_t, sigmoid_t), loss (mseloss_t), backward pass (backward), gradient zeroing (zero_grad), and SGD update (sgd_step). The benchmark/ folder contains micro‑benchmarks (benchmark.c, mem_allocation.c) that exercise the same core functions for performance measurement.

How It Is Wired

Execution starts in the example programs (examples/xor.c, examples/attention.c, etc.). Each main creates a Memory arena, builds a ParameterList, and allocates input tensors via tensor_init (defined in bare.c). Forward ops (linear_t, relu_t, sigmoid_t, mseloss_t) allocate temporary tensors from the same arena; they call low‑level BLAS routines provided by OpenBLAS (linked at build time).

backward(mem, loss) builds a topological sort of the computation graph stored in the temporary arena and propagates gradients backward through the same set of functions. The gradient buffers belong to the parameter tensors, which are registered in the ParameterList when layers are created (create_linear). After gradients are consumed, sgd_step(pl, lr) updates every registered parameter in place.

All mutating effects are confined to the memory arenas (Memory *mem) and the parameter list; no file I/O or network calls occur inside the library. The only external dependency is the OpenBLAS library linked during compilation. src/bare.c therefore acts as a hub: every high‑level operation eventually calls into it, giving it the widest blast radius. The benchmark programs invoke the same functions but focus on timing and memory‑allocation patterns rather than training.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/bare-lm
cd bare-lm

# Install OpenBLAS (Ubuntu example)
sudo apt update && sudo apt install -y libopenblas-dev

# Build and install the library
make            # on Linux; on macOS use OPENBLAS_PREFIX if needed
sudo make install   # copies bare.h to /usr/local/include and libbare.{so,dylib} to /usr/local/lib

Compile an example (e.g., XOR) against the installed library:

cc examples/xor.c -lbare -lopenblas -o xor_demo
./xor_demo

The repository does not contain a separate configuration file; the only required runtime artifact is the OpenBLAS shared library.

Real‑World Use

A robotics control system written in C can embed bare‑lm to train a lightweight multilayer perceptron on‑board. The control loop would allocate a permanent Memory arena at startup, feed sensor vectors through linear_t/relu_t, compute a loss against a target trajectory, call backward, and update weights with sgd_step. All allocations stay within the pre‑allocated arena, eliminating dynamic‑memory fragmentation in real‑time code.

Code Health & Issues

  • HIGH – Cognitive load: deep nestingbenchmark/benchmark.c, examples/attention.c, src/bare.c have indentation depth 8. Flatten with early returns or guard clauses.
  • HIGH – Oversized filesrc/bare.c contains 2 338 lines. Split by logical responsibility (e.g., tensor ops, graph management, optimizer).
  • HIGH – Pin third‑party GitHub Actions.github/workflows/release.yml uses softprops/action-gh-release@v2. Replace the tag with a fixed 40‑character SHA and let Dependabot bump it.
  • LOW – Missing job timeout.github/workflows/release.yml declares no timeout-minutes. Add a reasonable bound to each job.
  • MEDIUM – No LICENSE file – The root lacks a license, leaving redistribution rights ambiguous. Add an appropriate open‑source license.

No committed secrets were found; CI is present via GitHub Actions; there is no Dockerfile or lockfile.

The Bottom Line

bare‑lm delivers a functional autograd engine for pure‑C projects with a straightforward build process, but the core source file is large and deeply nested, making maintenance risky. It is suitable for developers comfortable navigating C codebases and who can tolerate refactoring the monolithic bare.c for long‑term stability. Adding a license and tightening CI configuration would improve its readiness for broader adoption.