Library / SDK
codeplea/genann avatar
codeplea/genann

Genann: a two-file C99 neural network you compile into your own program

simple neural network library in C99

2,288 stars270 forksCZlib

At a glance

What is it?
Genann trains and runs feedforward networks from a single .c and .h pair, with backpropagation plus a contiguous weight array that other optimizers can search. It is a good fit when you want the network inside your binary and are willing to manage the training loop yourself.
Who is it for?
Adopt Genann if you need a small feedforward network compiled directly into a C program and you are prepared to write the data loading, shuffling and early-stopping logic yourself. Do not adopt it if you want convolutional or recurrent layers, an optimizer suite, or a training loop that handles regularization.
Can I use it commercially?
Yes. Zlib is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 39 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Genann solves: a network with no build system attached

Most neural network libraries arrive as a dependency graph. Genann arrives as genann.c and genann.h. The README states the library is self-contained in those two files and that using it means adding them to your project. There is no CMake target to find, no package manager step, no shared object to link at runtime. For a C program that already has a build, that is the whole integration story.

The target user is not someone training a model on a GPU cluster. It is someone who has a small supervised problem, a few dozen inputs at most, and a reason to keep the model inside the process: an embedded controller, a game, a signal classifier, a piece of numerical code that cannot pull in libtorch. The library implements feedforward networks with backpropagation, and the README lists its focus as being simple, fast, reliable and hackable, achieved by providing only the necessary functions and little extra. That last phrase is the honest description of the scope. Genann gives you allocation, a training step, a forward pass, and file I/O. Everything else is your code.

What genann_init actually allocates, and why the weight array matters

A network is created with genann_init(inputs, hidden_layers, hidden, outputs). The four integers fix the topology: input count, number of hidden layers, neurons per hidden layer, and output count. Note that hidden is a single integer, so every hidden layer has the same width. If you need a 64-32-16 stack, Genann will not express it.

The design decision worth understanding is memory layout. The README says a primary design goal was to store all network weights in one contiguous block, and that every genann struct exposes int total_weights and double *weight, where weight points to an array of total_weights doubles. That single array is what makes the library usable with optimizers Genann does not implement. Hill climbing, genetic algorithms and simulated annealing can all be pointed at the same buffer, because they only need to read and write a flat vector of doubles and then call genann_run() to score it. The README names those three methods explicitly and points at example2.c, which trains XOR by random hill climbing rather than backpropagation.

This is a real architectural choice, not a marketing line. Libraries that keep weights in per-layer structures force you to write adapters before any external optimizer can touch them.

The training and inference calls, and the activation escape hatch

genann_train(ann, inputs, desired_outputs, learning_rate) performs one backpropagation update on one sample. The README's quick example loops 300 epochs over 100 training points at a learning rate of 0.1, then calls genann_run() on a test input. genann_run() returns a pointer to an output array of length ann->outputs, owned by the network rather than by the caller.

Activation is configurable per network through two struct members, activation_hidden and activation_output. The README lists genann_act_sigmoid_cached, genann_act_tanh, genann_act_relu, genann_act_linear and genann_act_threshold. Sigmoid is the default. The constraint that follows is stated plainly: backpropagation knows the derivatives of the built-in activation functions only, so a custom function makes genann_train() assume the sigmoid derivative. That is a quiet failure mode. Training will still run and the loss will still move, but the gradient is wrong for your function. If you substitute an activation and want to keep using backpropagation, you are expected to modify the library. The alternative is to switch to one of the derivative-free search methods, which the README says work with any activation.

Persistence is handled by genann_read(FILE *) and genann_write(genann const *, FILE *), described as a text-based format. genann_copy() produces a deep copy of a network, which is useful when a search method needs to branch from a candidate.

Getting it compiling: two files, four examples, no build step required

There is no install command in the README. The instruction is to add genann.c and genann.h to your project and include the header:

#include "genann.h"

From there the minimal program is genann_init(2, 1, 3, 2) for two inputs, one hidden layer of three neurons and two outputs, followed by a training loop and a genann_free() call. The repository ships four example programs: example1.c trains XOR with backpropagation, example2.c trains XOR by random search, example3.c loads a network from a file and runs it, and example4.c trains on the Iris data set with backpropagation. Those four files cover the API surface; reading them is faster than reading the header.

Two hints in the README change how you prepare data. The first is that all functions are prefixed genann_, which keeps namespace collisions unlikely in a C project. The second is that the default sigmoid expects outputs between 0 and 1, and that inputs should be scaled to roughly plus or minus one. If you feed raw feature values in the thousands, you should not expect the default configuration to converge.

The README also states the code is thread-safe. That claim is about the library itself; it does not mean concurrent calls to genann_train() on the same network are safe. The struct holds mutable weight state, so sharing one network across threads without external synchronization would be a race. The README does not address that distinction, and it is worth being careful about.

Where Genann stops: fixed widths, one update per call, and no training loop

The example in the README carries its own warning: it is showing API usage, not good machine learning technique, and a real application would learn on the test data in a random order and monitor learning to prevent over-fitting. Genann does not do either of those things for you. There is no shuffle, no validation split, no early stopping, no regularization term, and no momentum or adaptive learning rate. genann_train() is one sample, one update. The outer loop, the epoch count, the learning rate schedule and the stopping criterion are all caller code.

The fixed hidden width is a second boundary. genann_init takes one hidden size, so architectures like 128-64-32 are out of reach without editing the library. There are no convolutional layers, no recurrent connections, no embeddings, and no batching. For the problems Genann targets that is fine; for sequence data or images it is disqualifying.

A third limitation is the training data interface itself. genann_train() takes raw double pointers, so normalization, encoding of categorical targets, and train/test splitting all live outside the library. The README's own guidance about scaling inputs to roughly plus or minus one is the only preprocessing advice it gives.

Genann against FANN and tinn: three different answers to the same question

The README recommends two alternatives, and the contrast is instructive. FANN is described as a heavier, more opinionated neural network library in C. Heavier here means it brings its own training machinery: the README does not enumerate FANN's features, but the framing is that Genann deliberately omits what FANN includes. If you want a library that owns the training loop, FANN is the direction to look, at the cost of a larger dependency.

The second alternative is tinn, described as an even smaller single-hidden-layer library. That is a narrower scope than Genann: one hidden layer, where Genann supports an arbitrary number of uniform-width hidden layers. If your problem genuinely fits one hidden layer, tinn is the smaller artifact; if you need depth, Genann is the one that can express it.

The README also mentions Peter van Rossum's Lightweight Neural Network, noting that despite the name it is heavier and has more features than Genann. So the positioning is consistent: Genann sits between tinn's single layer and the fuller feature sets of FANN and LWNN. The differentiator is not accuracy or speed claims, which the README does not make in numeric terms. It is the two-file footprint plus the contiguous weight array that lets you bolt on your own optimizer.

Licence, maintenance and what a v1.1.0 upgrade costs you

Genann is released under the zlib licence, which the README describes as free for nearly any use. That is a permissive licence with no copyleft obligation, and it is compatible with shipping inside a closed-source binary. This is a description of the licence text, not legal advice; if your organisation has a policy on permissive licences, run it past whoever owns that policy.

The release history shows v1.0.0 in September 2020, then a gap until v1.1.0 and v1.1.1 in August 2026. Two releases four days apart suggests v1.1.1 is a patch on top of the 1.1 line. The README does not include a changelog, so the release notes are the place to check what moved between 1.0.0 and 1.1.0 before you upgrade. Because the library is two files that you vendor into your own tree, upgrading is a file replacement plus a rebuild, but it is also a manual diff: if you have patched genann.c to add a custom activation derivative, as the README implies you may need to, that patch has to be reapplied. Budget for that each time you take a new version. The repository is not archived and the last push is dated August 2026, so the project is active, but the cadence is measured in years rather than weeks and the API surface is small enough that this is not a problem.

Who should compile Genann in, and who should walk away

Genann fits a specific shape of problem: a feedforward network with a modest number of inputs, a need to keep the model inside a C program with no external dependencies, and a developer willing to write the training loop. The contiguous weight array makes it unusually easy to try a genetic algorithm or hill climbing on the same network you would otherwise train with backpropagation, and example2.c demonstrates that path. If you are prototyping a small classifier or a function approximator and you want to read the entire implementation in one sitting, this is the library for that.

Walk away if you need convolutional or recurrent layers, non-uniform hidden layer widths, batching, or a built-in optimizer with momentum and adaptive rates. Walk away too if you want a library to manage over-fitting for you, because the README is explicit that the example does not do that and the library offers nothing to replace it.

Before adopting, verify three things against your own data. Confirm your inputs can be scaled into the range the README describes for the default sigmoid. Confirm that any activation you plan to set on activation_hidden or activation_output is one whose derivative genann_train() implements, or commit to a derivative-free search method instead. And confirm that genann_read() and genann_write() produce a format your deployment pipeline can actually consume, since the README describes it only as text-based and does not specify the layout.

Editorial conclusion

Adopt Genann if you need a small feedforward network compiled directly into a C program and you are prepared to write the data loading, shuffling and early-stopping logic yourself. Do not adopt it if you want convolutional or recurrent layers, an optimizer suite, or a training loop that handles regularization. Before committing, verify that your inputs fit the sigmoid range the README describes, that your chosen activation has a derivative genann_train() knows about, and that genann_read() can parse the file format you intend to ship.

Official sources

  1. codeplea/genann on GitHub
  2. License: Zlib
  3. Project website
  4. README
  5. Releases
Community notes

Community notes