A Rust neural network framework for auditable, backend-agnostic NLP research
Rustral is a 24-crate Rust workspace for building, training, and deploying neural networks. It is designed around three commitments that are unusual in the deep learning framework landscape:
No hidden global state. Every forward pass receives an explicit ForwardCtx carrying the backend, training/inference mode, run ID, optional shape policy, and optional operation profiler. There is no silent model.train() / model.eval() toggle, no global tensor registry, and no implicit mode switching.
Backend-independent model definitions. Layers are written against the Backend and TensorOps traits. The same model code runs unchanged on a reference CPU backend (ndarray with SIMD), an optimized Candle backend (CUDA/Metal), and an experimental cross-platform WGPU backend (native WGSL compute shaders).
Reproducibility by construction. Every benchmark, NLP training run, and LLM inference run emits a schema-validated JSON manifest recording machine metadata, git SHA, dataset checksums, hyperparameters, and raw timing distributions with 95% confidence intervals. These manifests are validated in CI.
Rustral is the subject of an EMNLP 2026 systems paper (docs/paper/emnlp_paper.tex) that presents the first systematic 6-framework cross-framework operator benchmark and proposes Cross-Backend Consistency Regularization as a training-time methodology for preventing silent implementation drift.
Mean wall-clock ms on identical workloads (AMD EPYC 7702P, 64-core). Best per row in bold.
| Workload | Rustral | Candle | PyTorch | JAX | TensorFlow | ONNX |
|---|---|---|---|---|---|---|
| matmul 128×128 | 0.105 | 0.145 | 3.358 | 0.182 | 0.555 | 1.111 |
| matmul 512×512 | 5.619 | 1.716 | 4.529 | 0.936 | 1.714 | 1.963 |
| attention (d=64) | 0.026 | 0.024 | 0.089 | 0.103 | 0.438 | 0.038 |
| attention (d=256) | 0.456 | 0.563 | 0.169 | 0.587 | 1.113 | 0.127 |
| Workload | Rustral CUDA | PyTorch CUDA | Ratio |
|---|---|---|---|
| matmul 128×128 | 0.040 | 0.080 | 2.0× |
| attention (d=64) | 0.110 | 0.258 | 2.3× |
| Operation | Before | After | Speedup |
|---|---|---|---|
| LayerNorm (batch 1k) | 21.71 ms | 1.69 ms | 12.8× |
| CrossEntropyLoss | 1000+ ms | 460.5 ms | 2.2× |
| Parallel MHA | 200+ ms | 142.7 ms | 1.5× |
| Task | Rustral | PyTorch | Variance |
|---|---|---|---|
| SST-2 accuracy (smoke) | 0.5092 ± 0.0000 | 0.4897 ± 0.0200 | Rustral: zero |
| WikiText-2 perplexity (smoke) | 16,182 ± 149 | 21,516 ± 3,306 | PyTorch: 22× larger |
| SST-2 accuracy (paper-profile) | 0.765 ± 0.008 | 0.706 ± 0.015 | Rustral: ~2× lower variance |
| Metric | CPU | CUDA |
|---|---|---|
| GPT-2 first-token latency | 847 ms | 42 ms |
| GPT-2 tokens/sec | 4.2 | 58.3 |
| KV-cache decode step | 0.027 ms | — |
| LLaMA golden parity vs HF | ✅ <2×10⁻³ | ✅ |
Low wrapper cost on one matched path: Rustral's transformer pipeline matches raw Candle at 1.09 ms in the reported workload. This is evidence for that measured path, not a general no-overhead claim.
Operator-to-task transfer: 12.8× LayerNorm speedup becomes 1.41× at the NLP pipeline level because normalization is <10% of total FLOPs. Microbenchmark speedups systematically overstate end-to-end gains—a finding with implications for how systems papers should report performance.
Rustral is organized as a workspace of 24 focused crates:
Backends (ndarray CPU, Candle CPU/CUDA/Metal, WGPU)
↓
Core (Backend trait, TensorOps, ForwardCtx, Module, Parameter)
↓
Differentiation (reverse-mode Tape, fused losses)
↓
Modeling (Linear, Conv2d, LSTM, Transformer, LLaMA, MoE, GPT-2)
↓
Training (SGD/Adam/AdamW, DataLoader, TapeTrainer)
↓
Infrastructure (SafeTensors I/O, HF Hub, GGUF, Distributed, Metrics)
↓
Deployment (HTTP inference server, model zoo, ONNX export)
| Crate | Role |
|---|---|
rustral-core | Backend, TensorOps (20+ primitives), ForwardCtx, Parameter, Module, NamedParameters |
rustral-ndarray-backend | Reference CPU backend with SIMD (wide crate) and Rayon parallelism |
rustral-candle-backend | Optimized CPU/CUDA/Metal backend via Candle |
rustral-wgpu-backend | Experimental cross-platform GPU via native WGSL compute shaders |
rustral-autodiff | Reverse-mode Tape with explicit watching, fused losses, and operation profiling |
rustral-nn | Layers: Linear, Conv2d, LSTM, Transformer (encoder/decoder), LLaMA (RMS norm, RoPE, SwiGLU, GQA), MoE |
rustral-llm | GPT-2/LLaMA inference, KV-cache incremental decode, HuggingFace SafeTensors loading, CausalLm trait, greedy generation CLI |
rustral-optim | SGD, Adam, AdamW with NamedParameters visitor path and learning rate schedules |
rustral-data | Dataset / DataLoader with in-memory, streaming, and mmap backends |
rustral-io | SafeTensors save/load, sharded meta state dicts, strict validation |
rustral-runtime | TapeTrainer, inference pools, model I/O, NLP example orchestrator |
rustral-symbolic | Dependency graphs, spans, optimized subword tokenizers |
rustral-distributed | Single-process DP/TP/ZeRO simulation, ProcessGroup abstraction |
rustral-hf | HuggingFace Hub integration, snapshot pinning, local model scanning |
rustral-gguf | GGUF format header parsing |
rustral-bench | Schema-v2 JSON benchmark harness, 6-framework cross-comparison, Criterion microbenches |
rustral-metrics | JSONL/TensorBoard-style metrics writers |
rustral-autotuner | Kernel config search with ci_mode, fast(), and persistent cache |
rustral-tui | Live terminal dashboard with progress bars, loss sparklines, memory/leak monitoring |
rustral-inference-server | Axum HTTP service: /health, /v1/infer, /metrics, Docker |
rustral-model-zoo | Curated checkpoint registry with HF tensor-name mapping notes |
rustral-cuda-backend | Standalone CUDA backend (separate from Candle CUDA path) |
rustral-metal-backend | Standalone Metal backend (separate from Candle Metal path) |
rustral-onnx-export | Experimental ONNX Linear exporter |
The critical design invariant: model code depends only on the Backend and TensorOps traits, not on any concrete backend. Swap the backend constructor and the same model runs on CPU, CUDA, Metal, or WGPU.
The central design choice. Every forward pass receives:
pub struct ForwardCtx<'a, B: Backend> {
backend: &'a B,
mode: Mode, // Train or Inference
run_id: u64, // stable identifier
shape_policy: ShapePolicy,
profiler: Option<OperationProfiler>,
}
This eliminates an entire class of bugs. In PyTorch, calling model.train() sets a global flag; forgetting to toggle it produces silent correctness errors. In Rustral, the mode is carried through the call graph, and every operation that behaves differently in training vs. inference receives the mode explicitly. You cannot accidentally run inference with training-mode dropout.
# Clone and build
git clone https://github.com/skumyol/rustral.git && cd rustral
cargo build --workspace
# Run tests (formats, clippy, full suite — 700+ tests, 0 failures)
./run_tests.sh
# First example: XOR classification (proves the network learns)
cargo run -p rustral-runtime --features training --example tape_xor_classification
# SST-2 sentiment classifier (quick smoke test)
cargo run --release -p rustral-runtime --features training --example sst2_classifier -- --quick
# WikiText-2 language model
cargo run --release -p rustral-runtime --features training --example wikitext2_lm -- --quick
# Benchmark harness (CPU)
python3 scripts/bench/run_all.py --suite rustral --suite candle --repeats 3
# CUDA (requires nvcc + CUDA toolkit ≥ 12.0)
cargo run --release -p rustral-runtime --features "training,cuda" \
--example sst2_classifier -- --paper --d-model 256 --num-layers 4
# Force Candle CPU backend when CUDA JIT overhead dominates
RUSTRAL_FORCE_CPU=1 cargo run --release -p rustral-runtime --features "training,cuda" \
--example sst2_classifier -- --paper
# GPT-2 generation with metrics JSON
cargo run -p rustral-llm --features hf-tokenizers -- \
generate --model gpt2 --prompt "The capital of France is" --max-new-tokens 32
cargo run -p rustral-runtime --features training --example save_linear_artifact -- tiny_linear.safetensors
cargo run -p rustral-inference-server -- \
--artifact tiny_linear.safetensors --bind 127.0.0.1:8080 --in-features 1 --out-features 1 --bias
curl -s -X POST http://127.0.0.1:8080/v1/infer \
-H 'content-type: application/json' -d '{"input":[[0.25]]}'
| Backend | Hardware | Best For | Status |
|---|---|---|---|
rustral-ndarray-backend | CPU | Reference, correctness baselines | Stable |
rustral-candle-backend | CPU, CUDA, Metal | Production training, GPU benchmarks | Stable |
rustral-wgpu-backend | GPU (Vulkan/Metal/DX12) | Experiments, inference prototyping | Experimental |
// Same model code, three backends
use rustral_ndarray_backend::CpuBackend;
use rustral_candle_backend::CandleBackend;
use rustral_wgpu_backend::WgpuBackend;
let reference = run(&CpuBackend::default())?;
let optimized = run(&CandleBackend::cpu())?;
// let on_gpu = run(&CandleBackend::cuda(0)?)?;
Rustral favors explicit structure over framework magic.
| User need | Rustral solution |
|---|---|
| Build a layer without boilerplate | LinearBuilder / Conv2dConfig initialize backend-owned parameters |
| Run the same model on another backend | Model code depends on Backend, not a concrete tensor library |
| Know whether a pass is training or inference | ForwardCtx carries Mode::Train or Mode::Inference explicitly |
| Compose larger models from smaller pieces | Module gives each layer a typed forward contract |
| Inspect or replace internals | Autodiff, optimizers, runtime, data, backends are separate crates |
| Move toward deployment | Rust binaries embed the same model logic used in experiments |
| Stack | Best at | Rustral advantage |
|---|---|---|
| PyTorch | Ecosystem, pretrained models, notebooks | Rust-native deployment, explicit context, auditable internals |
| JAX | Transforms, compilation, accelerators | Simpler explicit systems model for Rust applications |
| Candle | Practical Rust tensor execution | Rustral can use Candle as a backend while adding typed modules, trainers, IO |
| Burn | High-level Rust DL ergonomics | Rustral stays small, inspectable, and backend-contract focused |
Rustral is the subject of a systems paper submitted to EMNLP 2026 (ARR May cycle). The paper:
| Artifact | Location |
|---|---|
| Paper (LaTeX) | docs/paper/emnlp_paper.tex |
| Bibliography | docs/paper/custom.bib |
| Supervisor briefing | docs/paper/professor-report-eml-2026.md |
| Experiments report | docs/paper/emnlp_experiments_report.md |
| Gap analysis | docs/paper/gap-analysis-may25.md |
| Deep research feedback | docs/paper/deep_research.md |
cargo fmt: Cleancargo clippy -D warnings: CleanRun locally:
./run_tests.sh
# or stricter:
cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings \
&& cargo test --workspace --exclude rustral-wgpu-backend
use rustral_core::{Backend, ForwardCtx, Mode, Module};
use rustral_nn::{Linear, LinearBuilder, Conv2d, Conv2dConfig};
struct MyModel<B: Backend> {
conv1: Conv2d<B>,
fc1: Linear<B>,
fc2: Linear<B>,
}
impl<B: Backend> MyModel<B> {
fn new(backend: &B) -> Self {
Self {
conv1: Conv2dConfig::new(32, 3, 3).build(backend),
fc1: LinearBuilder::new(32 * 7 * 7, 128).build(backend),
fc2: LinearBuilder::new(128, 10).build(backend),
}
}
fn forward(&self, image: &B::Tensor, ctx: &mut ForwardCtx<B>) -> B::Tensor {
let ops = ctx.backend().ops();
let x = self.conv1.forward(image.clone(), ctx).unwrap();
let x = ops.relu(&x).unwrap();
// ... reshape, fc1, relu, fc2 ...
x
}
}
fn run<B: Backend>(backend: &B) -> rustral_core::Result<B::Tensor> {
let model = TinyMlp::new(backend)?;
let input = backend.tensor_from_vec(vec![0.0, 1.0], &[1, 2])?;
let mut ctx = ForwardCtx::new(backend, Mode::Inference);
model.forward(input, &mut ctx)
}
let reference = run(&CpuBackend::default())?;
let fast_cpu = run(&CandleBackend::cpu())?;
use rustral_optim::{MixedPrecisionOptimizer, DType};
let optimizer = MixedPrecisionOptimizer::new(Adam::new(0.001))
.with_dtype(DType::Float16)
.with_loss_scale(1024.0);
The candle backend uses the candle-core library for highly optimized CPU execution, plus optional CUDA and Metal paths:
use rustral_candle_backend::CandleBackend;
let backend = CandleBackend::cpu();
// Or with CUDA (requires nvcc / CUDA toolkit):
// let backend = CandleBackend::cuda(0)?;
// Or with Metal on macOS:
// let backend = CandleBackend::metal(0)?;
On large linear layers, candle can be up to ~20x faster than the ndarray backend on CPU.
The benchmark crate also ships CUDA and Metal workload binaries:
cargo run --release -p rustral-bench --features cuda --bin rustral_workloads_cuda -- --repeats 5 --warmup 2
cargo run --release -p rustral-bench --features metal --bin rustral_workloads_metal -- --repeats 5 --warmup 2
# Linux + NVIDIA: CUDA backend tests + runtime (`training,cuda`) + bench JSON integration (`RUSTRAL_TEST_GPU=1`)
./scripts/run_gpu_tests.sh
use rustral_nn::{FlashAttention, SelfAttentionConfig};
let config = SelfAttentionConfig::new(768, 12);
let flash_attn = FlashAttention::new(&backend, config, 42)?;
use rustral_nn::{ExpertLayer, MoEConfig};
let config = MoEConfig::new(512, 64, 2048, 2);
let moe = ExpertLayer::new(&backend, config, 42)?;
rustral-runtime)| Example | Command |
|---|---|
| XOR classification | cargo run -p rustral-runtime --features training --example tape_xor_classification |
| Tape training demo | cargo run -p rustral-runtime --features training --example tape_train_demo |
| SST-2 classifier | cargo run --release -p rustral-runtime --features training --example sst2_classifier -- --quick |
| WikiText-2 LM | cargo run --release -p rustral-runtime --features training --example wikitext2_lm -- --quick |
| Mixed precision | cargo run -p rustral-runtime --features training --example mixed_precision_training |
rustral-nn)| Example | Command |
|---|---|
| XOR | cargo run -p rustral-nn --example xor |
| MNIST | cargo run -p rustral-nn --example mnist |
| BERT encoder | cargo run -p rustral-nn --example transformer_bert_encoder |
| GPT decoder | cargo run -p rustral-nn --example transformer_gpt_decoder |
| MoE training | cargo run -p rustral-nn --example moe_training |
| Example | Command |
|---|---|
| Benchmark | cargo run -p rustral-candle-backend --example benchmark |
| Document | What It Covers |
|---|---|
ARCHITECTURE.md | Crate map, design invariants, backend capabilities |
docs/master-plan.md | Feature roadmap, LLM bites, pre-submission quality gates |
docs/concepts.md | Tutorial and concept guide |
docs/api-signatures.md | Public API inventory |
EVALUATION.md | SST-2 and WikiText-2 methodology, metrics, reproducibility |
BENCHMARKS.md | Benchmark harness, schema v2, release snapshots |
docs/paper/emnlp_paper.tex | EMNLP 2026 submission |
docs/index.html | Single-page unified documentation |
| API Docs | cargo doc --open |
| Platform | CPU | CUDA | Metal | WGPU |
|---|---|---|---|---|
| Linux + NVIDIA | ✅ ndarray/Candle | ✅ Candle | — | ⚠️ Experimental |
| macOS | ✅ ndarray/Candle | — | ✅ Candle | ✅ Metal-native |
| Windows | ✅ ndarray/Candle | — | — | ✅ DX12-native |
Good first issues: add an example, improve documentation, add tests for edge cases, fix compiler warnings. See CONTRIBUTING.md.
MIT OR Apache-2.0 — you may use either.
Rustral draws ideas from PyTorch's eager execution, JAX's functional purity, and Rust's ownership model. It aims to stay transparent and hackable alongside crates like Candle and Burn.
177 commits
12 commits
Rust
81.9%
Python
8.4%
Shell
3.2%
TeX
2.8%
Cuda
1.2%
A Rust neural network framework for auditable, backend-agnostic NLP research
Rustral is a 24-crate Rust workspace for building, training, and deploying neural networks. It is designed around three commitments that are unusual in the deep learning framework landscape:
No hidden global state. Every forward pass receives an explicit ForwardCtx carrying the backend, training/inference mode, run ID, optional shape policy, and optional operation profiler. There is no silent model.train() / model.eval() toggle, no global tensor registry, and no implicit mode switching.
Backend-independent model definitions. Layers are written against the Backend and TensorOps traits. The same model code runs unchanged on a reference CPU backend (ndarray with SIMD), an optimized Candle backend (CUDA/Metal), and an experimental cross-platform WGPU backend (native WGSL compute shaders).
Reproducibility by construction. Every benchmark, NLP training run, and LLM inference run emits a schema-validated JSON manifest recording machine metadata, git SHA, dataset checksums, hyperparameters, and raw timing distributions with 95% confidence intervals. These manifests are validated in CI.
Rustral is the subject of an EMNLP 2026 systems paper (docs/paper/emnlp_paper.tex) that presents the first systematic 6-framework cross-framework operator benchmark and proposes Cross-Backend Consistency Regularization as a training-time methodology for preventing silent implementation drift.
Mean wall-clock ms on identical workloads (AMD EPYC 7702P, 64-core). Best per row in bold.
| Workload | Rustral | Candle | PyTorch | JAX | TensorFlow | ONNX |
|---|---|---|---|---|---|---|
| matmul 128×128 | 0.105 | 0.145 | 3.358 | 0.182 | 0.555 | 1.111 |
| matmul 512×512 | 5.619 | 1.716 | 4.529 | 0.936 | 1.714 | 1.963 |
| attention (d=64) | 0.026 | 0.024 | 0.089 | 0.103 | 0.438 | 0.038 |
| attention (d=256) | 0.456 | 0.563 | 0.169 | 0.587 | 1.113 | 0.127 |
| Workload | Rustral CUDA | PyTorch CUDA | Ratio |
|---|---|---|---|
| matmul 128×128 | 0.040 | 0.080 | 2.0× |
| attention (d=64) | 0.110 | 0.258 | 2.3× |
| Operation | Before | After | Speedup |
|---|---|---|---|
| LayerNorm (batch 1k) | 21.71 ms | 1.69 ms | 12.8× |
| CrossEntropyLoss | 1000+ ms | 460.5 ms | 2.2× |
| Parallel MHA | 200+ ms | 142.7 ms | 1.5× |
| Task | Rustral | PyTorch | Variance |
|---|---|---|---|
| SST-2 accuracy (smoke) | 0.5092 ± 0.0000 | 0.4897 ± 0.0200 | Rustral: zero |
| WikiText-2 perplexity (smoke) | 16,182 ± 149 | 21,516 ± 3,306 | PyTorch: 22× larger |
| SST-2 accuracy (paper-profile) | 0.765 ± 0.008 | 0.706 ± 0.015 | Rustral: ~2× lower variance |
| Metric | CPU | CUDA |
|---|---|---|
| GPT-2 first-token latency | 847 ms | 42 ms |
| GPT-2 tokens/sec | 4.2 | 58.3 |
| KV-cache decode step | 0.027 ms | — |
| LLaMA golden parity vs HF | ✅ <2×10⁻³ | ✅ |
Low wrapper cost on one matched path: Rustral's transformer pipeline matches raw Candle at 1.09 ms in the reported workload. This is evidence for that measured path, not a general no-overhead claim.
Operator-to-task transfer: 12.8× LayerNorm speedup becomes 1.41× at the NLP pipeline level because normalization is <10% of total FLOPs. Microbenchmark speedups systematically overstate end-to-end gains—a finding with implications for how systems papers should report performance.
Rustral is organized as a workspace of 24 focused crates:
Backends (ndarray CPU, Candle CPU/CUDA/Metal, WGPU)
↓
Core (Backend trait, TensorOps, ForwardCtx, Module, Parameter)
↓
Differentiation (reverse-mode Tape, fused losses)
↓
Modeling (Linear, Conv2d, LSTM, Transformer, LLaMA, MoE, GPT-2)
↓
Training (SGD/Adam/AdamW, DataLoader, TapeTrainer)
↓
Infrastructure (SafeTensors I/O, HF Hub, GGUF, Distributed, Metrics)
↓
Deployment (HTTP inference server, model zoo, ONNX export)
| Crate | Role |
|---|---|
rustral-core | Backend, TensorOps (20+ primitives), ForwardCtx, Parameter, Module, NamedParameters |
rustral-ndarray-backend | Reference CPU backend with SIMD (wide crate) and Rayon parallelism |
rustral-candle-backend | Optimized CPU/CUDA/Metal backend via Candle |
rustral-wgpu-backend | Experimental cross-platform GPU via native WGSL compute shaders |
rustral-autodiff | Reverse-mode Tape with explicit watching, fused losses, and operation profiling |
rustral-nn | Layers: Linear, Conv2d, LSTM, Transformer (encoder/decoder), LLaMA (RMS norm, RoPE, SwiGLU, GQA), MoE |
rustral-llm | GPT-2/LLaMA inference, KV-cache incremental decode, HuggingFace SafeTensors loading, CausalLm trait, greedy generation CLI |
rustral-optim | SGD, Adam, AdamW with NamedParameters visitor path and learning rate schedules |
rustral-data | Dataset / DataLoader with in-memory, streaming, and mmap backends |
rustral-io | SafeTensors save/load, sharded meta state dicts, strict validation |
rustral-runtime | TapeTrainer, inference pools, model I/O, NLP example orchestrator |
rustral-symbolic | Dependency graphs, spans, optimized subword tokenizers |
rustral-distributed | Single-process DP/TP/ZeRO simulation, ProcessGroup abstraction |
rustral-hf | HuggingFace Hub integration, snapshot pinning, local model scanning |
rustral-gguf | GGUF format header parsing |
rustral-bench | Schema-v2 JSON benchmark harness, 6-framework cross-comparison, Criterion microbenches |
rustral-metrics | JSONL/TensorBoard-style metrics writers |
rustral-autotuner | Kernel config search with ci_mode, fast(), and persistent cache |
rustral-tui | Live terminal dashboard with progress bars, loss sparklines, memory/leak monitoring |
rustral-inference-server | Axum HTTP service: /health, /v1/infer, /metrics, Docker |
rustral-model-zoo | Curated checkpoint registry with HF tensor-name mapping notes |
rustral-cuda-backend | Standalone CUDA backend (separate from Candle CUDA path) |
rustral-metal-backend | Standalone Metal backend (separate from Candle Metal path) |
rustral-onnx-export | Experimental ONNX Linear exporter |
The critical design invariant: model code depends only on the Backend and TensorOps traits, not on any concrete backend. Swap the backend constructor and the same model runs on CPU, CUDA, Metal, or WGPU.
The central design choice. Every forward pass receives:
pub struct ForwardCtx<'a, B: Backend> {
backend: &'a B,
mode: Mode, // Train or Inference
run_id: u64, // stable identifier
shape_policy: ShapePolicy,
profiler: Option<OperationProfiler>,
}
This eliminates an entire class of bugs. In PyTorch, calling model.train() sets a global flag; forgetting to toggle it produces silent correctness errors. In Rustral, the mode is carried through the call graph, and every operation that behaves differently in training vs. inference receives the mode explicitly. You cannot accidentally run inference with training-mode dropout.
# Clone and build
git clone https://github.com/skumyol/rustral.git && cd rustral
cargo build --workspace
# Run tests (formats, clippy, full suite — 700+ tests, 0 failures)
./run_tests.sh
# First example: XOR classification (proves the network learns)
cargo run -p rustral-runtime --features training --example tape_xor_classification
# SST-2 sentiment classifier (quick smoke test)
cargo run --release -p rustral-runtime --features training --example sst2_classifier -- --quick
# WikiText-2 language model
cargo run --release -p rustral-runtime --features training --example wikitext2_lm -- --quick
# Benchmark harness (CPU)
python3 scripts/bench/run_all.py --suite rustral --suite candle --repeats 3
# CUDA (requires nvcc + CUDA toolkit ≥ 12.0)
cargo run --release -p rustral-runtime --features "training,cuda" \
--example sst2_classifier -- --paper --d-model 256 --num-layers 4
# Force Candle CPU backend when CUDA JIT overhead dominates
RUSTRAL_FORCE_CPU=1 cargo run --release -p rustral-runtime --features "training,cuda" \
--example sst2_classifier -- --paper
# GPT-2 generation with metrics JSON
cargo run -p rustral-llm --features hf-tokenizers -- \
generate --model gpt2 --prompt "The capital of France is" --max-new-tokens 32
cargo run -p rustral-runtime --features training --example save_linear_artifact -- tiny_linear.safetensors
cargo run -p rustral-inference-server -- \
--artifact tiny_linear.safetensors --bind 127.0.0.1:8080 --in-features 1 --out-features 1 --bias
curl -s -X POST http://127.0.0.1:8080/v1/infer \
-H 'content-type: application/json' -d '{"input":[[0.25]]}'
| Backend | Hardware | Best For | Status |
|---|---|---|---|
rustral-ndarray-backend | CPU | Reference, correctness baselines | Stable |
rustral-candle-backend | CPU, CUDA, Metal | Production training, GPU benchmarks | Stable |
rustral-wgpu-backend | GPU (Vulkan/Metal/DX12) | Experiments, inference prototyping | Experimental |
// Same model code, three backends
use rustral_ndarray_backend::CpuBackend;
use rustral_candle_backend::CandleBackend;
use rustral_wgpu_backend::WgpuBackend;
let reference = run(&CpuBackend::default())?;
let optimized = run(&CandleBackend::cpu())?;
// let on_gpu = run(&CandleBackend::cuda(0)?)?;
Rustral favors explicit structure over framework magic.
| User need | Rustral solution |
|---|---|
| Build a layer without boilerplate | LinearBuilder / Conv2dConfig initialize backend-owned parameters |
| Run the same model on another backend | Model code depends on Backend, not a concrete tensor library |
| Know whether a pass is training or inference | ForwardCtx carries Mode::Train or Mode::Inference explicitly |
| Compose larger models from smaller pieces | Module gives each layer a typed forward contract |
| Inspect or replace internals | Autodiff, optimizers, runtime, data, backends are separate crates |
| Move toward deployment | Rust binaries embed the same model logic used in experiments |
| Stack | Best at | Rustral advantage |
|---|---|---|
| PyTorch | Ecosystem, pretrained models, notebooks | Rust-native deployment, explicit context, auditable internals |
| JAX | Transforms, compilation, accelerators | Simpler explicit systems model for Rust applications |
| Candle | Practical Rust tensor execution | Rustral can use Candle as a backend while adding typed modules, trainers, IO |
| Burn | High-level Rust DL ergonomics | Rustral stays small, inspectable, and backend-contract focused |
Rustral is the subject of a systems paper submitted to EMNLP 2026 (ARR May cycle). The paper:
| Artifact | Location |
|---|---|
| Paper (LaTeX) | docs/paper/emnlp_paper.tex |
| Bibliography | docs/paper/custom.bib |
| Supervisor briefing | docs/paper/professor-report-eml-2026.md |
| Experiments report | docs/paper/emnlp_experiments_report.md |
| Gap analysis | docs/paper/gap-analysis-may25.md |
| Deep research feedback | docs/paper/deep_research.md |
cargo fmt: Cleancargo clippy -D warnings: CleanRun locally:
./run_tests.sh
# or stricter:
cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings \
&& cargo test --workspace --exclude rustral-wgpu-backend
use rustral_core::{Backend, ForwardCtx, Mode, Module};
use rustral_nn::{Linear, LinearBuilder, Conv2d, Conv2dConfig};
struct MyModel<B: Backend> {
conv1: Conv2d<B>,
fc1: Linear<B>,
fc2: Linear<B>,
}
impl<B: Backend> MyModel<B> {
fn new(backend: &B) -> Self {
Self {
conv1: Conv2dConfig::new(32, 3, 3).build(backend),
fc1: LinearBuilder::new(32 * 7 * 7, 128).build(backend),
fc2: LinearBuilder::new(128, 10).build(backend),
}
}
fn forward(&self, image: &B::Tensor, ctx: &mut ForwardCtx<B>) -> B::Tensor {
let ops = ctx.backend().ops();
let x = self.conv1.forward(image.clone(), ctx).unwrap();
let x = ops.relu(&x).unwrap();
// ... reshape, fc1, relu, fc2 ...
x
}
}
fn run<B: Backend>(backend: &B) -> rustral_core::Result<B::Tensor> {
let model = TinyMlp::new(backend)?;
let input = backend.tensor_from_vec(vec![0.0, 1.0], &[1, 2])?;
let mut ctx = ForwardCtx::new(backend, Mode::Inference);
model.forward(input, &mut ctx)
}
let reference = run(&CpuBackend::default())?;
let fast_cpu = run(&CandleBackend::cpu())?;
use rustral_optim::{MixedPrecisionOptimizer, DType};
let optimizer = MixedPrecisionOptimizer::new(Adam::new(0.001))
.with_dtype(DType::Float16)
.with_loss_scale(1024.0);
The candle backend uses the candle-core library for highly optimized CPU execution, plus optional CUDA and Metal paths:
use rustral_candle_backend::CandleBackend;
let backend = CandleBackend::cpu();
// Or with CUDA (requires nvcc / CUDA toolkit):
// let backend = CandleBackend::cuda(0)?;
// Or with Metal on macOS:
// let backend = CandleBackend::metal(0)?;
On large linear layers, candle can be up to ~20x faster than the ndarray backend on CPU.
The benchmark crate also ships CUDA and Metal workload binaries:
cargo run --release -p rustral-bench --features cuda --bin rustral_workloads_cuda -- --repeats 5 --warmup 2
cargo run --release -p rustral-bench --features metal --bin rustral_workloads_metal -- --repeats 5 --warmup 2
# Linux + NVIDIA: CUDA backend tests + runtime (`training,cuda`) + bench JSON integration (`RUSTRAL_TEST_GPU=1`)
./scripts/run_gpu_tests.sh
use rustral_nn::{FlashAttention, SelfAttentionConfig};
let config = SelfAttentionConfig::new(768, 12);
let flash_attn = FlashAttention::new(&backend, config, 42)?;
use rustral_nn::{ExpertLayer, MoEConfig};
let config = MoEConfig::new(512, 64, 2048, 2);
let moe = ExpertLayer::new(&backend, config, 42)?;
rustral-runtime)| Example | Command |
|---|---|
| XOR classification | cargo run -p rustral-runtime --features training --example tape_xor_classification |
| Tape training demo | cargo run -p rustral-runtime --features training --example tape_train_demo |
| SST-2 classifier | cargo run --release -p rustral-runtime --features training --example sst2_classifier -- --quick |
| WikiText-2 LM | cargo run --release -p rustral-runtime --features training --example wikitext2_lm -- --quick |
| Mixed precision | cargo run -p rustral-runtime --features training --example mixed_precision_training |
rustral-nn)| Example | Command |
|---|---|
| XOR | cargo run -p rustral-nn --example xor |
| MNIST | cargo run -p rustral-nn --example mnist |
| BERT encoder | cargo run -p rustral-nn --example transformer_bert_encoder |
| GPT decoder | cargo run -p rustral-nn --example transformer_gpt_decoder |
| MoE training | cargo run -p rustral-nn --example moe_training |
| Example | Command |
|---|---|
| Benchmark | cargo run -p rustral-candle-backend --example benchmark |
| Document | What It Covers |
|---|---|
ARCHITECTURE.md | Crate map, design invariants, backend capabilities |
docs/master-plan.md | Feature roadmap, LLM bites, pre-submission quality gates |
docs/concepts.md | Tutorial and concept guide |
docs/api-signatures.md | Public API inventory |
EVALUATION.md | SST-2 and WikiText-2 methodology, metrics, reproducibility |
BENCHMARKS.md | Benchmark harness, schema v2, release snapshots |
docs/paper/emnlp_paper.tex | EMNLP 2026 submission |
docs/index.html | Single-page unified documentation |
| API Docs | cargo doc --open |
| Platform | CPU | CUDA | Metal | WGPU |
|---|---|---|---|---|
| Linux + NVIDIA | ✅ ndarray/Candle | ✅ Candle | — | ⚠️ Experimental |
| macOS | ✅ ndarray/Candle | — | ✅ Candle | ✅ Metal-native |
| Windows | ✅ ndarray/Candle | — | — | ✅ DX12-native |
Good first issues: add an example, improve documentation, add tests for edge cases, fix compiler warnings. See CONTRIBUTING.md.
MIT OR Apache-2.0 — you may use either.
Rustral draws ideas from PyTorch's eager execution, JAX's functional purity, and Rust's ownership model. It aims to stay transparent and hackable alongside crates like Candle and Burn.
177 commits
12 commits
Rust
81.9%
Python
8.4%
Shell
3.2%
TeX
2.8%
Cuda
1.2%