ML library for your hardware
- Eager-ish Execution β tensor operations fuse into kernels as you write them; when fusion is no longer possible, the kernel executes. For one off computations.
- Tape Mode β wrap loops in a
Tapefor lazy graph building, autograd and egraph-based fusion optimization. For repeated computations. - CrossβPlatform Backends β native codegen for C, CUDA, OpenCL and SPIR-V.
- Full LinearβAlgebra Coverage β mirrors the PyTorch ops API (matmul, convolutions, pooling, reductions, indexing, etc.) by stacking ops. Stack more ops yourself to get more op coverage, zyx auto fuses and optimizes it.
- Immutable Tensors β tensors cannot be modified in place, preventing backβprop errors common in PyTorch (
RuntimeError: a tensor was modified in place). - Explicit Tape β you control what is recorded via
Tape; no need fortorch.no_grad()or requires_grad semantics. - Everything is diff β every tensor in tape can be differentiated w.r.t. any other tensor in tape.
- Lazy Device Loading β tensors load from their current memory pool (disk, another device) into the compute device only when needed.
- Parallel Pipelining β kernels allocate across heterogeneous devices (GPU, CPU, WebGPU) in a pipelined fashion via the scheduler automatcially. e-graph tries all options, picks the fastest measured path.
- Small Footprint β compiled library is only a few MB with two dependencies (
libloading,nanoserde) and std. This means for all models, a few MB binary runs (and trains) them on all backends. Training and deployment can freely use the same API.
import zyx
x = zyx.Tensor.randn(2, 3)
y = zyx.Tensor.uniform_(2, 3, from_=-1.0, to_=1.0)
z = x.relu() + y.tanh()
print(z.shape())
# Autograd with tape
tape = zyx.Tape([x, y])
result = x.relu() * y
grads = tape.gradient(result, [x, y])| Crate | Description |
|---|---|
zyx |
Core tensor library with eager-ish fusion and tape-based autodiff |
zyx-nn |
Neural network layers (Linear, Conv2d, Attention, etc.) and #[derive(Module)] |
zyx-optim |
Optimizers (SGD, Adam, AdamW, RMSprop) |
# from crates.io
cargo add zyx zyx-nn zyx-optim
# from PyPI
pip install zyx-pyA training loop with a two-layer network, using Tape for autograd and optimizations:
use zyx::{Tensor, DType, Tape};
use zyx_nn::{Linear, Module};
use zyx_optim::SGD;
#[derive(Module)]
struct SimpleNet {
linear1: Linear,
linear2: Linear,
}
impl SimpleNet {
fn new(dtype: DType) -> Result<Self, zyx::ZyxError> {
Ok(Self {
linear1: Linear::new(784, 128, true, dtype)?,
linear2: Linear::new(128, 10, true, dtype)?,
})
}
fn forward(&self, x: &Tensor) -> Tensor {
let x = self.linear1.forward(x).unwrap().relu();
self.linear2.forward(&x).unwrap()
}
}
fn main() -> Result<(), zyx::ZyxError> {
let mut model = SimpleNet::new(DType::F32)?;
let mut optim = SGD::default();
let x = Tensor::randn([64, 784], DType::F32)?;
let target = Tensor::randn([64, 10], DType::F32)?;
for epoch in 0..100 {
let tape = Tape::new(&model)?;
let output = model.forward(&x);
let loss = output.mse_loss(&target)?;
let grads = tape.gradient(&loss, &model);
optim.update(&mut model, grads);
tape.realize(&model)?;
}
Ok(())
}Hand-optimize kernels for peak performance using hardware-specific features (e.g. tensor cores):
use zyx::kernel::{Kernel, Scope, MemLayout, DeviceId};
use zyx::{DType, Tensor};
fn main() -> Result<(), zyx::ZyxError> {
let mut kernel = Kernel::new(DeviceId::AUTO);
let n = 4;
let inp = kernel.define(DType::F32, Scope::Global, true, n);
let gidx = kernel.gidx(0, n);
let loaded = kernel.load(inp, gidx, MemLayout::Scalar);
let doubled = kernel.add(loaded, loaded);
let out = kernel.define(DType::F32, Scope::Global, false, n);
kernel.store(out, doubled, gidx, MemLayout::Scalar);
let compiled = kernel.compile()?;
let x = Tensor::from([1.0f32, 2.0, 3.0, 4.0]);
let result = compiled.forward(&[&x], [n]);
let data: Vec<f32> = result.try_into().unwrap();
assert_eq!(data, vec![2.0, 4.0, 6.0, 8.0]);
Ok(())
}See the WMMA matmul example for a tensor-core matmul example.
flowchart LR
A["Tensor Graph"] --> B["Fusion and Device Schedule Search"]
B --> C["Unified Kernel IR"]
C --> D["Backend Code / Assembly"]
Outside a tape, tensor operations fuse eagerly into kernels as you call them. Inside a tape, a lazy graph is built and analyzed for fusion opportunities during realization. The fused operations are lowered to a unified intermediate representation, then compiled to native code (OpenCL, CUDA, C, SPIR-V, etc.) for the target backend. Tape egraph compares fusion schemes and device allocations.
| Feature | zyx | PyTorch | TensorFlow | JAX |
|---|---|---|---|---|
| Execution Model | Eager-ish fusion (default) + Tape (lazy + autograd) | Eager by default | Eager by default | Functional + XLA |
| Gradient Recording | Explicit Tape |
Implicit, requires no_grad() |
Implicit, tf.function | Explicit + jit |
| Tensor Mutability | Immutable (no in-place errors) | Mutable (risk of back-prop failures) | Mutable | Immutable |
| Kernel Fusion | Automatic, all backends | Manual (torch.jit) | Manual (XLA) | Manual (XLA) |
| Disk I/O | Lazy loading parallel to compute | Typically blocking | Blocking | Blocking |
| Device Pipelining | Built-in heterogeneous pipelining | Manual to(device) calls |
Manual device placement | Manual device placement |
| Compilation | Just-in-time | Pre-compiled + jit | Pre-compiled | Just-in-time |
| Import Time | ~1ms | ~2s | ~3s | ~0.5s |
| Wheel Size | ~5MB (includes all backends) | hundreds of MB |
- C - C codegen (clang/gcc)
- CUDA
- OpenCL
- Vulkan - SPIR-V codegen
- WGPU - SPIR-V codegen, feature:
wgpu
- Status: Stable API with active performance optimization
- License: LGPL-3.0-only (all crates)
- Rust Version: stable rust >= 1.88.0
- Platforms: Linux (primary), macOS, Windows (planned)
- Architecture Book - How zyx works under the hood
- Contributing - How to contribute, code style, and PR workflow
- Configuration - Hardware device selection, autotune settings, backend config
- Environment Variables - Debug flags
- API Reference - Complete API documentation
- Examples - MNIST, RNN and others
- Issues - Bug reports and feature requests