Fucina: a CPU-first tensor/autograd library in Zig
Hi all. For the past months I’ve been building Fucina (Italian for “forge”), a tensor/autograd library written in pure Zig 0.16. Whenever I try a new language, I somehow go through the same routine and doing the same thing "I’ll just play around with it a bit” and then, before I know it, I’m trying to build a whole library. And now Fucina is at the point where I almost like it, and it can be consumed as an ordinary package, so I’d like to show it here.
Per the forum’s policy, the disclosure: this project is developed using agentic coding tools, with me owning (“controlling”) the ideas, the architecture decisions, and also writing code first hand (I feel like I don’t really know how to write code the way I used to but I’m so glad Zig itself is written entirely by hand).
The part that I think is genuinely interesting for this forum is what comptime does to tensor code, even though I’ve already had feedback from Zig programmers who don’t really see the benefit at all, that is, axis names are part of the type: Tensor(.{ .batch, .in })
is a different type from Tensor(.{ .in, .batch })
, contraction is by axis name, the result’s tag set is computed at compile time, and a misaligned contraction is a compile error rather than a runtime crash. The tags (and the rank) exist only in the type system and compile away entirely:
fn forward(ctx: *ExecContext, m: *const Model, x: *const Tensor(.{ .batch, .in })) !Tensor(.{ .batch, .class }) {
var z1 = try x.dot(ctx, &m.w1, .in); // contract .in -> .{ .batch, .h1 }
defer z1.deinit();
// ...
}
Execution is eager (no explicit graph object), and the same forward code runs for inference and training. There is no C/C++ build system and no Python runtime dependency; BLAS providers and a Metal/CUDA GEMM (matmul operations) offload are opt-in build options.
Using it:
zig fetch --save git+https://github.com/matteo-grella/fucina#v0.1.0
To develop and test the library against real workloads I decided to implement LLM inference and serving (Qwen3 dense and MoE, DeepSeek, GLM, Gemma, and others), speech-to-text, TTS and a guitar amp simulator. Each is validated against its reference implementation. These applications are not the library itself, and they will eventually move to their own repos but so far has been very handy having all in a single repo.
There is also an entirely AI generated book-length course in the docs, “Forging Deep Learning in Zig”, where I asked an LLM pretending to rebuild the library from scratch reversing engeneering my development journey and the big REFERENCE (also this AI generated and updated).
The API is pre-1.0 and will surely change. Feedback I’d particularly value from this community are API ergonomics of the tagged tensor surface, the package consumption experience, anything that feels that feels not idiomatic Zig (I tried to follow the std) but also come up with a lot of things from my previous Go project, plus some influence from Fortran.
Also, I’d love feedback if you can execute and benchmark the implemented deep learning models, particularly LLMs, on your computers. Thank you!
The toolchain is pinned to 0.16.0 exactly.
Reference: Fucina Reference - Fucina