# Luminal: A Minimalist Machine Learning Framework
Luminal is a Rust-based machine learning framework, drawing inspiration from [Tinygrad](https://github.com/tinygrad/tinygrad).
Its key feature is the use of composable compilers, which simplify the process of adding support for new backends. Luminal’s design philosophy centers around a minimal core, ensuring maintainability.
At its heart, Luminal operates with just 11 primitive operators:
* **Unary**: `Log2`, `Exp2`, `Sin`, `Sqrt`, `Recip`
* **Binary**: `Add`, `Mul`, `Mod`, `LessThan`
* **Other**: `SumReduce`, `MaxReduce`, `Contiguous`
These ops are enough to support linear regression, convnets, transformers, etc.
You might think that such a small set of operations would severely limit the library’s capabilities.
However, Luminal overcomes this limitation through the strategic use of composable compilers.
These compilers allow us to extend the functionality of the core by adding new operations and optimizations in a modular and isolated manner.
For example, because the core library doesn’t handle specific devices, we can create compilers that replace the core operations with
equivalent operations optimized for different hardware, such as CUDA GPUs or TPUs. In the context of LuminAIR, we use compilers to target ZK circuits.
The beauty of this approach is that with only 11 core operations, the process of creating these specialized compilers becomes remarkably straightforward.
Therefore, our work with LuminAIR involves leveraging Luminal’s composable compilers to develop efficient compilers
specifically tailored for proving computational graphs using Zero Knowledge proofs.
# AIR: A Custom Algebraic Intermediate Representation Proved with Stwo
Stwo, developed by [Starkware](https://starkware.co/), represents the next generation of STARK provers.
It incorporates the innovative [Circle STARK protocol](https://eprint.iacr.org/2024/278), along with a variety of other optimizations.
Stwo unlocks the potential of the highly efficient prime field M31, benefiting the entire ZK-proof ecosystem.
We are leveraging Stwo framework to define constraints for our AIR.
LuminAIR designs a compiler that swaps each operation in the graph with its corresponding component in our AIR,
enabling the generation of C-STARK proofs for the computational graph.
While, in theory, we could support other ZK backends in the future by developing new compilers, our primary focus for now is on Stwo.
# Compilers
Source: https://luminair.gizatech.xyz/concepts/compilers
Core transformations of the computational graph.
A fundamental principle of LuminAIR is its reliance on ahead-of-time (AOT) compilation.
By pushing all computations to compile time, LuminAIR eliminates runtime overhead,
ensuring that every operation in the computational graph is static and optimized before execution.
When you write an expression like `x + y` in LuminAIR, no computation happens immediately. Instead:
* The operation is recorded in a directed acyclic computation graph.
* Actual computation occurs only when `graph.gen_trace()` is executed.
This approach, akin to lazy execution, allows LuminAIR to treat entire ML models as static computation graphs.
## Why AOT Compilation?
By fully representing ML models as static computation graphs, compilers have global knowledge of the entire model.
This allows for:
1. **Advanced Optimizations**: Compilers can perform tasks like operator fusion, backend-specific optimizations, and linking operations to their AIR equivalents.
2. **Separation of Concerns**: All complexity is pushed to compile time, leaving runtime execution lightweight and efficient.
## How LuminAIR Compilers Work
Compilers in LuminAIR are modular and stackable, meaning multiple compilers can be applied sequentially to transform the computation graph.
Each compiler focuses on a specific optimization task.
### Default Compilers Provided by LuminAIR
1. **GenericCompiler**
* Developed by [Luminal](https://github.com/jafioti/luminal), it applies backend-agnostic optimizations.
* Includes techniques like Common Subexpression Elimination [(CSE)](https://en.wikipedia.org/wiki/Common_subexpression_elimination). to eliminate redundant computations.
* Should always run before any specialized compilers.
2. **StwoCompiler**
* A specialized compiler designed for proving computational graphs using the [Stwo](https://github.com/starkware-libs/stwo) prover.
* Replaces operations in the graph with their equivalent components in the AIR.
`StwoCompiler` currently includes `PrimitiveCompiler`. It maps primitive operators (e.g., `Add`, `Mul`) to their corresponding AIR components.
```rust theme={null}
pub type StwoCompiler = (op::prim::PrimitiveCompiler,);
```
In the future, `StwoCompiler` will include additional sub-compilers, such as `FuseOpCompiler`, focuses on fusing multiple primitive operations into optimized composite operators.
### Customizing Compilers for Specific Use Cases
LuminAIR supports custom compilers, designed by users, that can be stacked alongside default ones to address specific use cases.
```rust theme={null}
cx.compile(<(GenericCompiler, MyCustomCompiler, StwoCompiler)>::default(), &mut e);
```
Here:
* `GenericCompiler` applies general optimizations.
* `MyCustomCompiler` implements user-defined transformations for a specific task.
* `StwoCompiler` prepares the graph for proof generation using the Stwo prover.
# GraphTensor
Source: https://luminair.gizatech.xyz/concepts/graph-tensor
The high-level interface for writing ML code, checked at compile time.
**GraphTensors** are a fundamental part of LuminAIR, serving as references to nodes within the computational graph.
They provide a high-level interface for defining machine learning models and operations, ensuring that all computations are checked at compile time.
# Creating a GraphTensor
To get started, you can create a `GraphTensor` by initializing a graph and adding tensor nodes:
```rust theme={null}
let mut cx = Graph::new(); // Initialize a new computational graph
let a: GraphTensor
While each AIR component verifies its local operation, it is equally important to ensure proper data flow between
nodes in the computational graph. Specifically, the output of one node must match the input of another node.
This consistency is enforced using the [LogUp](https://eprint.iacr.org/2022/1530?ref=blog.lambdaclass.com) lookup argument protocol,
which establishes a system of relations between tensor values.
**Output Yields** (Positive Multiplicity)
* When a node produces an output that will be consumed by other nodes, its multiplicity equals the number of consumers.
* This indicates that the value is “yielded” for use elsewhere in the graph.
**Input Consumes** (Negative Multiplicity)
* When a node receives an input from another node, its multiplicity is `-1`.
* This signifies that the value is “consumed” by the operation.
**Special Cases** (Zero Multiplicity)
* Graph Inputs (Initializers): Tensors that serve as initial inputs to the graph have zero multiplicity because they are not consumed by any prior operation.
* Graph Outputs (Final Results): Tensors that represent final outputs of the graph also have zero multiplicity since they are not yielded to subsequent operations.
# Operators
Source: https://luminair.gizatech.xyz/concepts/operators
Core Building Blocks of LuminAIR
In LuminAIR, operators represent the fundamental operations that a computational graph executes.
Each operator is associated with an AIR component, which ensures integrity during proof generation.
# Primitive Operators
LuminAIR adopts a RISC-style architecture for its operators.
LuminAIR supports a minimal set of 11 primitive operators.
These operators are sufficient to implement a wide range of machine learning models, such as linear regression, convolutional networks, and transformers.
| Operator | Status |
| ------------ | ------ |
| `Log2` | ✅ |
| `Exp2` | ✅ |
| `Sin` | ✅ |
| `Sqrt` | ✅ |
| `Recip` | ✅ |
| `Add` | ✅ |
| `Mul` | ✅ |
| `Mod` | ✅ |
| `LessThan` | ✅ |
| `SumReduce` | ✅ |
| `MaxReduce` | ✅ |
| `Contiguous` | ✅ |
These primitive operators are handled by the `PrimitiveCompiler`, a subset of the `StwoCompiler`.
Each operator is mapped to its corresponding AIR component during compilation, ensuring compatibility with the proof generation process.
# High-Level Operators
While the primitive operators cover a wide range of models, they may not always be the most efficient choice.
Complex operations in machine learning often require multiple primitive operators, leading to large graphs with many nodes. This can slow down both execution and proof generation.
To address this, LuminAIR plans to introduce fused operators in future phases (see [Roadmap](/more/roadmap)).
These fused operators will combine multiple primitives into single optimized operations, reducing graph complexity and improving performance.
The exact list of fused operators is still under development, but priority will be given to commonly used ML operations such as MatMul, SoftMax or ReLU.
# Prove & Verify
Source: https://luminair.gizatech.xyz/concepts/prove-verify
Ensuring Computational Integrity with LuminAIR.
LuminAIR’s primary objective is to cryptographically prove that a computational graph has been executed correctly.
This proof allows verifiers to validate the computation’s integrity using significantly fewer resources than re-executing the graph.
The process is powered by the `StwoCompiler` and the Stwo prover, enabling efficient proof generation and verification.
# What is Stwo?
[Stwo](https://github.com/starkware-libs/stwo) is a next-generation implementation of the [Circle STARK](https://eprint.iacr.org/2024/278) protocol, written in Rust.
Developed by Starkware, it is an ultra-efficient ZK prover designed for scalable succinct proofs. Stwo unlocks the potential of the highly efficient prime field M31, benefiting the entire ZK-proof ecosystem.
# How LuminAIR Leverages Stwo
The `StwoCompiler` plays a critical role in transforming and optimizing computational graphs.
It assigns each operator in the graph to its corresponding AIR component, preparing the graph for proof generation using the Stwo prover.
# LuminAIR Prover
The LuminAIR prover integrates directly with the Stwo library to:
1. Implement AIR components for each operator in the computational graph.
2. Generate an execution trace, which records the state of each operation during execution.
3. Produce succinct proofs.
### SIMD Backend for Parallelization
The LuminAIR prover currently supports a SIMD backend, allowing parallelized proof generation on devices equipped with SIMD capabilities.
For smaller traces or devices without SIMD support, a fallback to `CpuBackend` ensures compatibility.
### Future Plans: GPU Acceleration
In the roadmap, LuminAIR plans to support [Icicle-Stwo](https://github.com/ingonyama-zk/icicle-stwo), an implementation of Stwo by [Ingonyama](https://www.ingonyama.com/) that enables CUDA-based GPU acceleration.
This will significantly enhance proof generation speed for large-scale computation
# LuminAIR Verifier
The LuminAIR verifier, written in Rust, validates proofs generated by the prover. It ensures:
1. The LogUp sum is correct (Cf. [integrity section](/concepts/integrity)).
2. The proof satisfies all constraints defined by the AIR components.
### Future Plans
To expand its trustlessness, LuminAIR plans to implement additional verifier backends:
1. **WASM Verifier**:
A WebAssembly implementation will allow proofs to be verified directly in web browsers, enabling trustless verification by a client.
2. **Onchain Verification**:
* Implementing the verifier as a [Cairo](https://www.cairo-lang.org/) program verified onchain.
* Using [AVSs](https://app.eigenlayer.xyz/avs) like [Aligned Layer](https://alignedlayer.com/) to leverage Ethereum’s crypto-economic security.
# Shape Tracking
Source: https://luminair.gizatech.xyz/concepts/shape-tracking
Compile-time shape tracking
When working with a [GraphTensor](/concepts/graph-tensor) in LuminAIR, you may notice that its type includes a specific generic parameter.
This parameter encodes the tensor’s shape directly at the type level, enabling compile-time shape tracking.
# What Does the Shape Parameter Represent?
Consider the following example:
```rust theme={null}
let a: GraphTensor
This guide walks you through the steps to:
1. **Build** a computational graph using tensor operations.
2. **Compile** and execute the graph to generate execution traces.
3. **Prove** the computational graph using C-STARK proofs.
4. **Verify** the proof to ensure computation integrity.
## Step 1: Create a Computational Graph
```rust theme={null}
use luminair_graph::{graph::LuminairGraph, StwoCompiler};
use luminal::prelude::*;
fn main() -> Result<(), Box{model.description}