LIFT — Language for Intelligent Frameworks and Technologies
Unified intermediate representation for AI and quantum computing.
LIFT is a Rust compiler framework built around a single SSA-based intermediate representation that treats tensor operations (AI/ML), quantum gates, and classical–quantum hybrid computation as equal citizens in the same graph. One pipeline handles all three: define → verify → optimise → analyse → predict → export.
Why LIFT?
AI models run on GPUs, quantum circuits run on QPUs, and hybrid classical-quantum workloads (VQE, QAOA, quantum machine learning) need both — but today they live in separate IRs and separate toolchains with no shared way to reason about them.
- One IR, two worlds — tensors and qubits share one SSA graph, so optimisation passes can reason across the classical/quantum boundary.
- Noise in the type system — every quantum gate carries T1/T2, fidelity, and crosstalk metadata, so the compiler accounts for noise at every stage instead of after the fact.
- Linear qubit types — the no-cloning theorem is enforced at compile time; reusing a qubit is a type error, not a runtime crash.
- Analysis before hardware runs — FLOPs, peak memory, circuit depth, estimated fidelity, and energy cost are computed statically. Budget violations halt compilation with an actionable error.
- One config file — a single
.lithreplaces the several configuration files typically scattered across separate frameworks.
Key Features
- 179 operations — 110 tensor ops (attention, convolutions, MoE, GNN, quantisation, diffusion), 48 quantum gates (Pauli, Clifford, parametric, IonQ-native), 21 hybrid ops (encoding, gradients, VQC/VQE/QAOA)
- 13 optimisation passes with preset
O0–O3pipelines, explicit-pass override, and per-pass enable/disable - Semantic verification — SSA, well-formedness, qubit linearity, and operation arity checked against dialect signatures
- Hardware-native gate decomposition and real qubit routing — BFS-based SWAP insertion over device topologies, transpilation to IBM/Rigetti/IonQ/Quantinuum native gate sets
- Non-adjacent gate cancellation, rotation merging, and generic tensor fusion
- 3 export backends — LLVM IR, ONNX (opset 21), OpenQASM 3.0 (all 48 gates)
- Cost modelling and performance prediction — roofline analysis, GPU/QPU device profiles, energy/carbon estimation
- Programmatic model generation — a
ModelBuilderRust API and alift-codegenbinary for defining models in code
Architecture
The pipeline reads left to right: Frontend → Core (with semantic verification) → Dialects → Optimise → Analyse → Export.
flowchart LR
subgraph Frontend["Frontend"]
LIF[".lif source"]
LITH[".lith config"]
CODGEN["lift-codegen / ModelBuilder"]
end
subgraph Core["Core + Verify"]
AST["lift-ast (lexer / parser)"]
IR["lift-core — SSA IR, verifier"]
end
subgraph Dialects["Dialects"]
TEN["lift-tensor (AI ops)"]
QUA["lift-quantum (gates, noise)"]
HYB["lift-hybrid (fusion)"]
end
subgraph Optimise["Optimise"]
CFG["lift-config (O0-O3)"]
OPT["lift-opt (13 passes)"]
end
subgraph Analyse["Analyse"]
SIM["lift-sim (FLOPs, memory)"]
PRED["lift-predict (roofline)"]
end
subgraph Export["Export"]
LLVM["LLVM IR"]
ONNX["ONNX"]
QASM["OpenQASM 3.0"]
end
LIF --> AST
LITH --> CFG
CODGEN --> AST
AST --> IR
IR --> TEN & QUA & HYB
IR --> OPT
CFG --> OPT
OPT --> SIM
SIM --> PRED
IR --> SIM
IR --> LLVM & ONNX & QASM
OPT --> LLVM & ONNX & QASM
classDef stage fill:#e8f0fe,stroke:#1a73e8,color:#174ea6;
class Frontend,Core,Dialects,Optimise,Analyse,Export stage;
Full architecture and the crate dependency graph: docs/LIFT_design.md.
Crates
All 13 are published to crates.io and versioned together.
| Crate | Description |
|---|---|
lift-core | SSA IR, type system, verifier, printer, pass manager, ModelBuilder |
lift-ast | Lexer, parser, IR builder for .lif source files |
lift-tensor | 110 tensor operations with shape inference and FLOP counting |
lift-quantum | 48 quantum gates, hardware providers, topology, noise, QEC |
lift-hybrid | 21 hybrid ops — encoding, gradients, variational algorithms |
lift-opt | 13 optimisation passes (classical, quantum, AI-specific) |
lift-sim | Cost models, energy estimation, reactive budgets |
lift-predict | Roofline-based performance prediction |
lift-import | ONNX, PyTorch FX, OpenQASM 3.0 importers |
lift-export | LLVM IR, ONNX, OpenQASM 3.0 exporters |
lift-config | .lith configuration file parser |
lift-cli | Command-line interface (installs as lift) |
lift-codegen | Programmatic model generation binary |
Docs for any crate: https://docs.rs/<crate-name>.
Quick Start
Requires Rust 1.80+ (rustup).
cargo install lift-cli # installs the `lift` binary
lift verify examples/phi3_mini.lif
lift analyse examples/phi3_mini.lif
lift optimise examples/phi3_mini.lif --config examples/phi3_optimize.lith
lift predict examples/phi3_mini.lif --device h100 --energy
lift predict examples/quantum_bell.lif --quantum superconducting
lift export examples/phi3_mini.lif --backend onnx --output model.onnx
Building from source instead: git clone this repo, then cargo build --release and substitute cargo run --release -p lift-cli -- for lift
above.
As a library
[dependencies]
lift-core = "0.4.8"
lift-ast = "0.4.8"
lift-opt = "0.4.8"
lift-export = "0.4.8"
#![allow(unused)] fn main() { use lift_ast::{Lexer, Parser, IrBuilder}; use lift_core::{Context, verifier, pass::PassManager}; let source = std::fs::read_to_string("model.lif").unwrap(); let tokens = Lexer::new(&source).tokenize().to_vec(); let program = Parser::new(tokens).parse().unwrap(); let mut ctx = Context::new(); IrBuilder::new().build_program(&mut ctx, &program).unwrap(); verifier::verify(&ctx).unwrap(); let mut pm = PassManager::new(); pm.add_pass(Box::new(lift_opt::Canonicalize)); pm.add_pass(Box::new(lift_opt::TensorFusion)); // ... 13 passes total — see docs/LIFT_Guide.md for the full pipeline pm.run_all(&mut ctx); let onnx = lift_export::OnnxExporter::new().export(&ctx).unwrap(); }
Defining models programmatically instead of writing .lif by hand: see
ModelBuilder in docs/LIFT_Guide.md or run cargo run --bin lift-codegen for a working end-to-end example.
Export Backends
- LLVM IR —
--backend llvm. Emits IR with cuBLAS/cuDNN runtime call sites for all 110 tensor operations. Currently textual (not yet executable) — see docs/CAPABILITIES.md. - ONNX —
--backend onnx. Protobuf text, opset 21, 70+ operations mapped to standard ONNX andcom.microsoftextensions (attention, MoE). Full op-mapping table in docs/LIFT_Guide.md. - OpenQASM 3.0 —
--backend qasm. All 48 gates, targeting IBM, Rigetti, IonQ, and Quantinuum native gate sets.
| Extension | Description |
|---|---|
.lif | LIFT IR source code |
.lith | Compilation configuration |
.ll | LLVM IR export |
.onnx | ONNX export (protobuf text) |
.qasm | OpenQASM 3.0 export |
Examples
See examples/ — hand-written models (phi3_mini.lif,
llama2_7b.lif, mistral_7b.lif, bert_base.lif, quantum_bell.lif) and
programmatically generated ones (cargo run --bin lift-codegen). Validate
the full pipeline end-to-end:
bash examples/validate_all.sh # 105 checks across every example and backend
Documentation
- 📖 Online book — the full documentation set, searchable
- LIFT_Guide.md — feature guide with code examples for every crate
- LIFT_Manual.md — user manual with real-world use cases
- LIFT_design.md — architecture and design
- DIALECTS.md — full dialect reference (tensor, quantum, hybrid)
- CAPABILITIES.md — honest capabilities, limits, and roadmap
- STRATEGY.md — who uses LIFT and why
- CHANGELOG.md — version history
Contributing
Contributions are welcome — see CONTRIBUTING.md for the development workflow and PR process. This project follows a Code of Conduct; see SECURITY.md to report a vulnerability.
Roadmap
flowchart LR
V3["v0.3 — IR, dialects, export"]
V4["v0.4 — O0-O3 pipeline, 13 passes, crates.io"]
V5["v0.5 — simulator, real backends, importers"]
V6["v0.6 — autodiff, Python bindings, v1.0"]
V3 --> V4 --> V5 --> V6
v0.4 (current) — optimisation pipeline with O0–O3 levels, semantic
verification, 13 passes, all crates on crates.io.
v0.5 (next) — state-vector quantum simulator, tensor interpreter, real LLVM lowering with cuBLAS/cuDNN calls, functional ONNX/PyTorch FX/OpenQASM importers, SABRE-style dynamic qubit re-placement.
v0.6 — automatic differentiation, PyO3 Python bindings, multi-file support, v1.0 release.
Details: docs/ROADMAP-v0.5.md.
License
Complete LIFT Framework Guide — All Features
LIFT — Language for Intelligent Frameworks and Technologies Unified intermediate representation for AI and quantum computing.
This document describes every feature of the LIFT framework, numbered and organised by crate. For each feature: what it does, how to use it, and which other features to combine it with.
Table of Contents
- General Architecture
- lift-core — IR Core
- lift-ast — Parsing the .lif Language
- lift-tensor — Tensor Operations (110 ops)
- lift-quantum — Quantum Gates and Noise (48 gates)
- lift-hybrid — Classical-Quantum Hybrid Computation
- lift-opt — Optimisation Passes (13 passes)
- lift-sim — Simulation and Cost Analysis
- lift-predict — Performance Prediction
- lift-import — Model Import
- lift-export — Backend Export (LLVM, ONNX, QASM)
- lift-config — Configuration (.lith)
- lift-cli — Command-Line Interface
- lift-codegen — Programmatic Model Generation
- Combinations and Complete Pipelines
- Concrete Examples
1. General Architecture
LIFT is a modular compiler composed of 14 crates organised in layers:
┌──────────┐
│ lift-cli │ ← User interface
└────┬─────┘
┌─────────────┼─────────────┐
│ │ │
┌──────┴──────┐ ┌────┴────┐ ┌──────┴──────┐
│ lift-import │ │lift-opt │ │ lift-export │
└──────┬──────┘ └────┬────┘ └──────┬──────┘
│ │ │
┌──────┴──────┐ ┌────┴────┐ ┌──────┴──────┐
│ lift-ast │ │lift-sim │ │lift-predict │
└──────┬──────┘ └────┬────┘ └──────┬──────┘
│ │ │
┌──────┴─────────────┴─────────────┴──────┐
│ lift-core │
├──────────┬──────────┬───────────────────┤
│lift-tensor│lift-quantum│ lift-hybrid │
└──────────┴──────────┴───────────────────┘
1.1 Compilation Pipeline
The standard workflow is:
Source (.lif) → Lexer → Parser → IR (SSA) → Verification → Optimisation → Simulation → Export
1.2 File Formats
| Extension | Description |
|---|---|
.lif | LIFT IR source code |
.lith | Compilation configuration |
.ll | LLVM IR export |
.onnx | ONNX export (protobuf text) |
.qasm | OpenQASM 3.0 export |
1.3 Adding LIFT as a Dependency
[dependencies]
lift-core = "0.4.8"
lift-ast = "0.4.8"
lift-tensor = "0.4.8"
lift-quantum = "0.4.8"
lift-hybrid = "0.4.8"
lift-opt = "0.4.8"
lift-sim = "0.4.8"
lift-predict = "0.4.8"
lift-import = "0.4.8"
lift-export = "0.4.8"
lift-config = "0.4.8"
2. lift-core — IR Core
The heart of the framework. Provides the SSA (Static Single Assignment) intermediate representation.
2.1 Context — The Central Container
#![allow(unused)] fn main() { use lift_core::Context; let mut ctx = Context::new(); }
The Context stores all IR data: values, operations, blocks, regions, functions, modules, interned strings, and types.
| Field | Description | Usage |
|---|---|---|
ctx.values | All SSA values | Each operation result is a unique value |
ctx.ops | All operations | Program instructions |
ctx.blocks | Basic blocks | Contain sequences of operations |
ctx.regions | Regions | Contain blocks (function bodies) |
ctx.modules | Modules | Compilation units |
ctx.strings | String interning | ctx.strings.intern("name") |
ctx.types | Type interning | Type deduplication |
ctx.dialects | Dialect registry | Populated with the core dialect by Context::new(); tensor/quantum/hybrid need explicit registration (see §2.7) |
Combine with: All other crates. The Context is the entry point for every pipeline.
2.2 Types — Type System
#![allow(unused)] fn main() { use lift_core::types::*; // Data types let fp32 = DataType::FP32; let fp16 = DataType::FP16; let bf16 = DataType::BF16; let int8 = DataType::INT8; let fp64 = DataType::FP64; // Dimensions (static or symbolic) let batch = Dimension::Constant(32); let seq = Dimension::Symbolic("seq_len".to_string()); // Tensor type info let tensor_info = TensorTypeInfo { shape: vec![Dimension::Constant(1), Dimension::Constant(784)], dtype: DataType::FP32, layout: MemoryLayout::Contiguous, }; // Size in bytes (element count × dtype size — TensorTypeInfo has no // size_bytes() method itself; lift-tensor's ShapeInference computes this // for a given op, see §4.2) let elems: usize = tensor_info.shape.iter().map(|d| d.static_value().unwrap_or(1)).product(); let bytes = elems * tensor_info.dtype.byte_size(); // 1*784*4 = 3136 }
Available data types:
| Type | Size | Usage |
|---|---|---|
FP64 | 8 bytes | High-precision scientific computing |
FP32 | 4 bytes | Standard training |
FP16 | 2 bytes | Fast inference |
BF16 | 2 bytes | Mixed-precision training (Google Brain) |
INT8 | 1 byte | Post-training quantisation |
INT32 | 4 bytes | Indices, counters |
BOOL | 1 byte | Masks |
Memory layouts: Contiguous, Strided.
2.3 Attributes — Operation Metadata
#![allow(unused)] fn main() { use lift_core::attributes::{Attribute, Attributes}; let mut attrs = Attributes::new(); // Different attribute types attrs.set("num_heads", Attribute::Integer(8)); attrs.set("dropout", Attribute::Float(0.1)); attrs.set("causal", Attribute::Bool(true)); // Reading let heads = attrs.get_integer("num_heads"); // Some(8) let drop = attrs.get_float("dropout"); // Some(0.1) let causal = attrs.get_bool("causal"); // Some(true) // Checking assert!(attrs.contains("num_heads")); assert_eq!(attrs.len(), 3); // Iteration for (key, val) in attrs.iter() { println!("{}: {:?}", key, val); } }
Combine with: lift-opt (passes read/write attributes), lift-export (exporters read attributes).
2.4 Verifier — Invariant Checking
#![allow(unused)] fn main() { use lift_core::verifier; let ctx = Context::new(); match verifier::verify(&ctx) { Ok(()) => println!("IR valid"), Err(errors) => { for e in &errors { eprintln!("Error: {}", e); } } } }
Checks:
- SSA: every value is defined exactly once
- Qubit linearity: every qubit is used exactly once
- Typing: type consistency between operations
- Structure: blocks, regions, terminators are correct
Combine with: Always use after import and after each optimisation pass.
2.5 Printer — IR Display
#![allow(unused)] fn main() { use lift_core::printer::print_ir; let ctx = Context::new(); let output = print_ir(&ctx); println!("{}", output); }
Produces a human-readable textual representation of the IR, useful for debugging.
2.6 Pass Manager
#![allow(unused)] fn main() { use lift_core::pass::{PassManager, Pass, PassResult, AnalysisCache}; let mut pm = PassManager::new(); pm.add_pass(Box::new(lift_opt::Canonicalize)); pm.add_pass(Box::new(lift_opt::DeadCodeElimination)); pm.add_pass(Box::new(lift_opt::TensorFusion)); let results = pm.run_all(&mut ctx); for (name, result) in &results { match result { PassResult::Changed => println!("{}: changed", name), PassResult::Unchanged => println!("{}: unchanged", name), PassResult::Error(e) => println!("{}: error: {}", name, e), PassResult::RolledBack => println!("{}: rolled back", name), } } }
Combine with: lift-opt (all 13 passes), lift-config (pass selection via configuration).
2.7 Dialect — Dialect System
#![allow(unused)] fn main() { use lift_core::dialect::{DialectRegistry, Dialect}; use lift_core::Context; // A standalone registry starts empty. let registry = DialectRegistry::new(); // Context::new() registers only the core dialect automatically // (via register_builtin_dialects). Tensor/quantum/hybrid need explicit // registration — this is what lift-cli's `verify` command does: let mut ctx = Context::new(); lift_tensor::dialect::register_tensor_dialect(&mut ctx.dialects); lift_quantum::dialect::register_quantum_dialect(&mut ctx.dialects); lift_hybrid::dialect::register_hybrid_dialect(&mut ctx.dialects); }
The three LIFT dialects:
- tensor: tensor operations (
tensor.matmul,tensor.relu, etc.) - quantum: quantum gates (
quantum.h,quantum.cx, etc.) - hybrid: hybrid operations (
hybrid.encode,hybrid.vqc_layer, etc.)
3. lift-ast — Parsing the .lif Language
3.1 Lexer — Tokenisation
#![allow(unused)] fn main() { use lift_ast::Lexer; let source = r#" dialect tensor module @mlp { func @forward(%x: tensor<1x784xf32>) -> tensor<1x10xf32> { %out = "tensor.relu"(%x) : (tensor<1x784xf32>) -> tensor<1x784xf32> return %out } } "#; let mut lexer = Lexer::new(source); let tokens = lexer.tokenize().to_vec(); assert!(lexer.errors().is_empty(), "Lexing errors: {:?}", lexer.errors()); }
3.2 Parser — Syntactic Analysis
#![allow(unused)] fn main() { use lift_ast::Parser; let mut parser = Parser::new(tokens); let program = parser.parse().expect("Parsing errors"); }
3.3 IrBuilder — IR Construction
#![allow(unused)] fn main() { use lift_ast::IrBuilder; use lift_core::Context; let mut ctx = Context::new(); let mut builder = IrBuilder::new(); builder.build_program(&mut ctx, &program).expect("IR construction errors"); }
3.4 Complete Parsing Pipeline
#![allow(unused)] fn main() { fn load_lif_file(path: &str) -> Result<Context, String> { let source = std::fs::read_to_string(path) .map_err(|e| format!("Read failed: {}", e))?; let mut lexer = Lexer::new(&source); let tokens = lexer.tokenize().to_vec(); if !lexer.errors().is_empty() { return Err(format!("Lexer errors: {:?}", lexer.errors())); } let mut parser = Parser::new(tokens); let program = parser.parse().map_err(|e| format!("Parser errors: {:?}", e))?; let mut ctx = Context::new(); let mut builder = IrBuilder::new(); builder.build_program(&mut ctx, &program)?; Ok(ctx) } }
Combine with: lift-core (Context), then lift-opt (optimisation), lift-sim (analysis), lift-export (compilation).
4. lift-tensor — Tensor Operations (110 ops)
4.1 Complete Operation List by Category
4.1.1 Basic Arithmetic (5 ops)
| # | Op | IR Name | Inputs | Description |
|---|---|---|---|---|
| 1 | Add | tensor.add | 2 | Element-wise addition |
| 2 | Sub | tensor.sub | 2 | Subtraction |
| 3 | Mul | tensor.mul | 2 | Element-wise multiplication |
| 4 | Div | tensor.div | 2 | Division |
| 5 | Neg | tensor.neg | 1 | Negation |
#![allow(unused)] fn main() { use lift_tensor::ops::TensorOp; let op = TensorOp::MatMul; println!("Nom: {}", op.name()); // "tensor.matmul" println!("Inputs: {:?}", op.num_inputs()); // (2, 2) println!("FLOPs: {}", op.flops_formula()); // "2*M*N*K" }
4.1.2 Linear Algebra (5 ops)
| # | Op | Inputs | Description |
|---|---|---|---|
| 6 | MatMul | 2 | Matrix multiplication |
| 7 | Linear | 3 | Linear layer (matmul + bias) |
| 8 | Embedding | 2 | Embedding lookup table |
| 9 | SparseMatMul | 2 | Sparse MatMul |
| 10 | SparseEmbedding | 2 | Sparse embedding lookup |
4.1.3 Activations (11 ops)
| # | Op | Description | FLOPs Formula |
|---|---|---|---|
| 11 | ReLU | max(0, x) | N |
| 12 | GeLU | Gaussian Error Linear Unit | ~8N |
| 13 | SiLU | x * sigmoid(x) (Swish) | ~8N |
| 14 | Sigmoid | 1/(1+exp(-x)) | N |
| 15 | Tanh | Hyperbolic tangent | N |
| 16 | Softmax | exp(x)/sum(exp(x)) | 5N |
| 17 | LeakyReLU | max(αx, x) | N |
| 18 | ELU | Exponential Linear Unit | N |
| 19 | Mish | x * tanh(softplus(x)) | ~8N |
| 20 | HardSwish | Swish approximation | ~8N |
| 21 | HardSigmoid | Sigmoid approximation | N |
#![allow(unused)] fn main() { assert!(TensorOp::ReLU.is_activation()); assert!(!TensorOp::MatMul.is_activation()); }
4.1.4 Normalisation (5 ops)
| # | Op | Inputs | Description |
|---|---|---|---|
| 22 | LayerNorm | 2-3 | Layer normalisation |
| 23 | RMSNorm | 2-3 | Root Mean Square Norm (LLaMA) |
| 24 | BatchNorm | 3-5 | Batch normalisation |
| 25 | GroupNorm | 2-3 | Group normalisation |
| 26 | InstanceNorm | 2-3 | Instance normalisation |
#![allow(unused)] fn main() { assert!(TensorOp::LayerNorm.is_normalisation()); }
4.1.5 Attention (8 ops)
| # | Op | Inputs | Description |
|---|---|---|---|
| 27 | Attention | 3-4 | Standard attention (Q, K, V, [mask]) |
| 28 | MultiHeadAttention | 3-4 | Multi-head |
| 29 | MultiQueryAttention | 3-4 | Multi-query (Llama) |
| 30 | GroupedQueryAttention | 3-4 | Grouped query (GQA) |
| 31 | FlashAttention | 3-4 | FlashAttention V2 (O(N) memory) |
| 32 | SlidingWindowAttention | 3-4 | Sliding window (Mistral) |
| 33 | CrossAttention | 3-4 | Cross-attention (encoder-decoder) |
| 34 | PagedAttention | 3-5 | Paged attention (vLLM) |
#![allow(unused)] fn main() { assert!(TensorOp::FlashAttention.is_attention()); }
4.1.6 Convolutions (6 ops)
| # | Op | Description |
|---|---|---|
| 35 | Conv2D | Convolution 2D standard |
| 36 | Conv1D | 1D convolution (audio, sequences) |
| 37 | Conv3D | 3D convolution (video, volumetric) |
| 38 | ConvTranspose2D | Transposed convolution (upsampling) |
| 39 | DepthwiseConv2D | Depthwise convolution (MobileNet) |
| 40 | DilatedConv2D | Dilated convolution (large receptive field) |
4.1.7 Pooling (4 ops)
| # | Op | Description |
|---|---|---|
| 41 | MaxPool2D | Max pooling 2D |
| 42 | AvgPool2D | Average pooling 2D |
| 43 | AdaptiveAvgPool2D | Adaptive average pooling |
| 44 | GlobalAvgPool | Global average pooling |
4.1.8 Shape Operations (13 ops)
| # | Op | Description | FLOPs |
|---|---|---|---|
| 45 | Reshape | Change shape | 0 |
| 46 | Transpose | Transpose | 0 |
| 47 | Concat | Concatenate | 0 |
| 48 | Split | Split | 0 |
| 49 | Gather | Advanced indexing | 0 |
| 50 | Scatter | Indexed write | 0 |
| 51 | Squeeze | Remove dim=1 | 0 |
| 52 | Unsqueeze | Add dim=1 | 0 |
| 53 | Permute | Permute dimensions | 0 |
| 54 | Expand | Broadcast expansion | 0 |
| 55 | Slice | Slice | 0 |
| 56 | Pad | Padding | 0 |
| 57 | Tile | Repeat | 0 |
#![allow(unused)] fn main() { assert!(TensorOp::Reshape.is_zero_flop()); }
4.1.9 Constants (5 ops)
| # | Op | Description |
|---|---|---|
| 58 | Constant | Constant tensor |
| 59 | Zeros | Zero tensor |
| 60 | Ones | Ones tensor |
| 61 | Arange | Sequence [0, 1, ..., n-1] |
| 62 | Full | Tensor filled with a value |
4.1.10 Recurrent (3 ops)
| # | Op | Description |
|---|---|---|
| 63 | LSTMCell | LSTM cell |
| 64 | GRUCell | GRU cell |
| 65 | RNNCell | Simple RNN cell |
4.1.11 Advanced Mathematics (11 ops)
| # | Op | Description |
|---|---|---|
| 66 | Einsum | Einstein notation |
| 67 | FFT | Fast Fourier Transform |
| 68 | IFFT | Inverse FFT |
| 69 | SVD | Singular Value Decomposition |
| 70 | Eig | Eigendecomposition |
| 71 | Solve | Linear system solver |
| 72 | TopK | Top-K values |
| 73 | Sort | Sort |
| 74 | Cumsum | Cumulative sum |
| 75 | Where | Element-wise conditional select |
| 76 | Clamp | Clamp values to a [min, max] range |
4.1.12 Quantisation (6 ops)
| # | Op | Description |
|---|---|---|
| 77 | Quantize | FP → INT8 |
| 78 | Dequantize | INT8 → FP |
| 79 | QuantizeInt4 | FP → INT4 |
| 80 | DequantizeInt4 | INT4 → FP |
| 81 | QuantizeFp8 | FP → FP8 |
| 82 | DequantizeFp8 | FP8 → FP |
4.1.13 Diffusion / Generative (3 ops)
| # | Op | Description |
|---|---|---|
| 83 | UNetDownBlock | U-Net down block |
| 84 | UNetUpBlock | U-Net up block |
| 85 | TimestepEmbedding | Timestep embedding (Stable Diffusion) |
4.1.14 GNN — Graph Neural Networks (2 ops)
| # | Op | Description |
|---|---|---|
| 86 | GNNMessagePassing | GNN message passing |
| 87 | GNNGlobalPooling | GNN global pooling |
4.1.15 MoE — Mixture of Experts (2 ops)
| # | Op | Description |
|---|---|---|
| 88 | MoEDispatch | Route to experts |
| 89 | MoECombine | Combine expert outputs |
4.1.16 Memory and Gradient (11 ops)
| # | Op | Description |
|---|---|---|
| 90 | Checkpoint | Gradient checkpointing (memory saving) |
| 91 | Offload | CPU offload (for large models) |
| 92 | GradAccumulate | Gradient accumulation |
| 93 | GradMatMul | MatMul gradient |
| 94 | GradReLU | ReLU gradient |
| 95 | GradSoftmax | Softmax gradient |
| 96 | GradLayerNorm | LayerNorm gradient |
| 97 | GradAttention | Attention gradient |
| 98 | GradConv2D | Conv2D gradient |
| 99 | GradLinear | Linear gradient |
| 100 | GradGeLU | GeLU gradient |
4.1.17 Parallelism (4 ops)
| # | Op | Description |
|---|---|---|
| 101 | ParallelSplit | Data parallel split |
| 102 | ParallelAllReduce | All-reduce across GPUs |
| 103 | PipelineSend | Pipeline parallel send |
| 104 | PipelineReceive | Pipeline parallel receive |
4.1.18 Fused Operations (6 ops)
| # | Op | Description | Gain |
|---|---|---|---|
| 105 | FusedMatMulBiasReLU | MatMul + Bias + ReLU | 1 kernel instead of 3 |
| 106 | FusedMatMulBias | MatMul + Bias | 1 kernel instead of 2 |
| 107 | FusedLinearGeLU | Linear + GeLU | Bandwidth gain |
| 108 | FusedAttentionLayerNorm | Attention + LayerNorm | Memory reduction |
| 109 | FusedLinearSiLU | Linear + SiLU | Bandwidth gain |
| 110 | FusedConvBatchNormReLU | Conv + BN + ReLU | Fast inference |
4.2 Shape Inference
#![allow(unused)] fn main() { use lift_core::types::*; use lift_tensor::ops::TensorOp; use lift_tensor::shape::ShapeInference; fn mk(shape: Vec<usize>, dtype: DataType) -> TensorTypeInfo { TensorTypeInfo { shape: shape.into_iter().map(Dimension::Constant).collect(), dtype, layout: MemoryLayout::Contiguous, } } // Shape inference let a = mk(vec![2, 3, 64], DataType::FP32); let b = mk(vec![2, 64, 128], DataType::FP32); let result = ShapeInference::infer_output_shape(&TensorOp::MatMul, &[&a, &b]).unwrap(); // result[0].shape = [2, 3, 128] // FLOP computation let flops = ShapeInference::compute_flops(&TensorOp::MatMul, &[&a, &b]); println!("FLOPs: {:?}", flops); // Some(49152) // Memory computation let mem = ShapeInference::compute_memory_bytes(&TensorOp::MatMul, &[&a, &b]); println!("Memory: {:?} bytes", mem); }
Combine with: lift-sim (cost model uses FLOPs), lift-predict (roofline prediction).
4.3 Useful Predicates
#![allow(unused)] fn main() { let op = TensorOp::FlashAttention; op.is_attention(); // true — attention variant? op.is_convolution(); // false — convolution? op.is_normalisation(); // false — normalisation? op.is_activation(); // false — activation? op.is_fused(); // false — fused operation? op.is_gradient(); // false — gradient operation? op.is_zero_flop(); // false — zero FLOPs (reshape, etc.)? op.num_inputs(); // (3, 4) — min/max number of inputs op.flops_formula(); // "2*B*H*(S^2*D + S*D^2)" }
5. lift-quantum — Quantum Gates and Noise (48 gates)
5.1 Quantum Gates
5.1.1 Standard 1-Qubit Gates (9 gates)
| # | Gate | IR Name | Type | Description |
|---|---|---|---|---|
| 1 | H | quantum.h | Clifford | Hadamard |
| 2 | X | quantum.x | Pauli | Quantum NOT (bit-flip) |
| 3 | Y | quantum.y | Pauli | Y rotation by π |
| 4 | Z | quantum.z | Pauli | Phase-flip |
| 5 | S | quantum.s | Clifford | Phase π/2 |
| 6 | Sdg | quantum.sdg | Clifford | S inverse |
| 7 | T | quantum.t | Non-Clifford | Phase π/4 (expensive for QEC) |
| 8 | Tdg | quantum.tdg | Non-Clifford | T inverse |
| 9 | SX | quantum.sx | Clifford | Square root of X |
5.1.2 Parametric 1-Qubit Gates (9 gates)
| # | Gate | Parameters | Description |
|---|---|---|---|
| 10 | RX | θ | Rotation around X |
| 11 | RY | θ | Rotation around Y |
| 12 | RZ | θ | Rotation around Z |
| 13 | P | φ | Phase gate |
| 14 | U1 | λ | U1 unitary gate |
| 15 | U2 | φ, λ | U2 unitary gate |
| 16 | U3 | θ, φ, λ | General unitary gate |
| 17 | Rx90 | — | Fixed RX(π/2) |
| 18 | Rx180 | — | Fixed RX(π) |
5.1.3 2-Qubit Gates (14 gates)
| # | Gate | Description | Native for |
|---|---|---|---|
| 19 | CX | CNOT | IBM |
| 20 | CZ | Controlled-Z | IBM, Rigetti |
| 21 | CY | Controlled-Y | — |
| 22 | SWAP | Qubit swap | — |
| 23 | ISWAP | iSWAP | Rigetti |
| 24 | ECR | Echoed Cross-Resonance | IBM Eagle |
| 25 | RZX | ZX rotation | IBM |
| 26 | XX | Ising XX | IonQ |
| 27 | YY | Ising YY | IonQ |
| 28 | ZZ | Ising ZZ | IonQ |
| 29 | CPhase | Controlled Phase | Rigetti |
| 30 | XY | XY interaction | Rigetti |
| 31 | CP | Controlled Phase | — |
| 32 | MS | Mølmer–Sørensen | IonQ |
5.1.4 3-Qubit and Multi-Control Gates (4 gates)
| # | Gate | Description |
|---|---|---|
| 33 | CCX | Toffoli (CCNOT) |
| 34 | CSWAP | Fredkin |
| 35 | MCX | Multi-controlled X |
| 36 | MCZ | Multi-controlled Z |
5.1.5 Special and Control Gates (10 gates)
| # | Gate | Description |
|---|---|---|
| 37 | GlobalPhase | Global phase |
| 38 | Delay | Delay (decoherence) |
| 39 | VirtualRZ | Virtual RZ (no physical cost) |
| 40 | IfElse | Classical conditional control |
| 41 | Measure | Measure 1 qubit |
| 42 | MeasureAll | Measure all qubits |
| 43 | Reset | Reset |
| 44 | Barrier | Barrier (prevents optimisation) |
| 45 | Init | Initialisation |
| 46 | ParamGate | Generic parametric gate |
5.1.6 IonQ Native Gates (2 gates)
| # | Gate | IR Name | Description |
|---|---|---|---|
| 47 | GPI | quantum.gpi | IonQ native single-qubit phase gate |
| 48 | GPI2 | quantum.gpi2 | IonQ native single-qubit phase gate (half-angle) |
#![allow(unused)] fn main() { use lift_quantum::gates::QuantumGate; let gate = QuantumGate::H; println!("Name: {}", gate.op_name()); // "quantum.h" println!("Qubits: {}", gate.num_qubits()); // 1 println!("Clifford: {}", gate.is_clifford()); // true println!("Parametric: {}", gate.is_parametric()); // false println!("Self-inverse: {}", gate.is_self_inverse()); // true // Look up a gate by its IR name let gate = QuantumGate::from_name("quantum.cx"); // Some(CX) }
5.2 Hardware Providers — Native Gate Sets
#![allow(unused)] fn main() { use lift_quantum::gates::{QuantumGate, Provider}; // Native gates per provider let ibm_basis = QuantumGate::native_basis(Provider::IbmEagle); let rigetti_basis = QuantumGate::native_basis(Provider::Rigetti); let ionq_basis = QuantumGate::native_basis(Provider::IonQ); let quant_basis = QuantumGate::native_basis(Provider::Quantinuum); }
| Provider | Native Gates |
|---|---|
IbmEagle | CX, RZ, SX, X |
IbmKyoto | ECR, RZ, SX, X |
Rigetti | CZ, RX, RZ |
IonQ | GPI, GPI2, MS |
Quantinuum | RZ, RX, ZZ |
Simulator | All gates |
Combine with: lift-opt::GateDecomposition (native gate transpilation) and lift-opt::RealRouting (actual SWAP insertion using this topology).
5.3 Device Topology — Hardware Topology
#![allow(unused)] fn main() { use lift_quantum::topology::DeviceTopology; // Predefined topologies let linear = DeviceTopology::linear(10); // Linear chain let grid = DeviceTopology::grid(3, 3); // 3x3 grid let hex = DeviceTopology::heavy_hex(27); // Heavy-hex IBM let ion = DeviceTopology::all_to_all(32); // All-to-all (trapped ions) let tree = DeviceTopology::tree(15); // Binary tree // Custom topology let custom = DeviceTopology::custom("my_chip", &[(0,1), (1,2), (2,3), (0,3)], 0.99); // Querying linear.are_connected(0, 1); // true linear.shortest_path(0, 4); // Some([0, 1, 2, 3, 4]) linear.swap_distance(0, 4); // Some(3) linear.avg_connectivity(); // average connectivity linear.diameter(); // graph diameter grid.neighbors(4); // neighbours of qubit 4 }
Combine with: lift-opt::LayoutMapping, lift-opt::NoiseAwareSchedule.
5.4 Noise Models
#![allow(unused)] fn main() { use lift_quantum::noise::{NoiseModel, GateNoise, CircuitNoise}; // Noise models let ideal = NoiseModel::Ideal; let depol = NoiseModel::Depolarizing { p: 0.01 }; let bitflip = NoiseModel::BitFlip { p: 0.001 }; let phaseflip = NoiseModel::PhaseFlip { p: 0.001 }; // Model fidelity let fidelity = depol.fidelity(); // 0.99 // Per-gate noise let gate_noise = GateNoise::with_depolarizing(0.999, 0.02); // Full circuit analysis let mut cn = CircuitNoise::new(); // ... noise accumulation println!("Total fidelity: {}", cn.total_fidelity); println!("2-qubit gates: {}", cn.two_qubit_count); }
5.5 Kraus Channels — Quantum Noise Channels
#![allow(unused)] fn main() { use lift_quantum::kraus::{ComplexMatrix, KrausChannel}; // Predefined noise channels let depol = KrausChannel::depolarizing(0.01, 1); // 1-qubit depolarising let amp = KrausChannel::amplitude_damping(0.02); // Amplitude damping let phase = KrausChannel::phase_damping(0.01); // Phase damping // Channel fidelity let fidelity = depol.average_gate_fidelity(); println!("Fidelity: {:.6}", fidelity); // Complex matrices let mut m = ComplexMatrix::identity(2); let dagger = m.dagger(); // Conjugate transpose let product = m.mul(&dagger).unwrap(); let trace = m.trace().unwrap(); }
Combine with: lift-sim::QuantumCostModel (circuit fidelity estimation), lift-opt::NoiseAwareSchedule.
5.6 QEC — Quantum Error Correction
#![allow(unused)] fn main() { use lift_quantum::qec::{QecCode, QecAnalysis}; // Available QEC codes let surface = QecCode::SurfaceCode { distance: 5 }; // 25 physical qubits/logical let steane = QecCode::SteaneCode; // 7 physical qubits/logical let shor = QecCode::ShorCode; // 9 physical qubits/logical let rep = QecCode::RepetitionCode { distance: 7 }; // 7 physical qubits let ldpc = QecCode::LdpcCode { n: 100, k: 10 }; // LDPC code // Code properties println!("Physical/logical: {}", surface.physical_per_logical()); // 25 println!("Distance: {}", surface.code_distance()); // 5 println!("Syndrome depth: {}", surface.syndrome_circuit_depth()); // 5 // Full QEC analysis let analysis = QecAnalysis::analyse( 10, // logical qubits 100, // circuit depth QecCode::SurfaceCode { distance: 5 }, 0.001, // physical error rate ); println!("Physical qubits: {}", analysis.physical_qubits); println!("Logical error rate: {:.2e}", analysis.logical_error_rate); println!("Overhead: {}", analysis.overhead_qubits); }
Combine with: lift-sim::QuantumCostModel, lift-predict (fidelity budget).
6. lift-hybrid — Classical-Quantum Hybrid Computation
6.1 Hybrid Operations (21 ops)
6.1.1 Encoding/Decoding (2 ops)
| # | Op | IR Name | Description |
|---|---|---|---|
| 1 | Encode | hybrid.encode | Encode classical data → qubits |
| 2 | Decode | hybrid.decode | Decode quantum measurements → classical |
6.1.2 Gradient Methods (6 ops)
| # | Op | IR Name | Evaluations | Exact? |
|---|---|---|---|---|
| 3 | ParameterShift | hybrid.parameter_shift | 2N | Yes |
| 4 | FiniteDifference | hybrid.finite_difference | N+1 | No |
| 5 | SPSA | hybrid.spsa | 2 | No |
| 6 | AdjointDifferentiation | hybrid.adjoint_diff | 1 | Yes |
| 7 | StochasticParameterShift | hybrid.stochastic_param_shift | 2 | No |
| 8 | JointGradient | hybrid.joint_gradient | Combined | — |
#![allow(unused)] fn main() { use lift_hybrid::gradient::GradientMethod; let method = GradientMethod::ParameterShift; let evals = method.circuit_evaluations(100); // 200 evaluations for 100 params assert!(method.is_exact()); // true }
6.1.3 Processing (4 ops)
| # | Op | Description |
|---|---|---|
| 9 | ClassicalPreprocess | Classical preprocessing |
| 10 | QuantumPostprocess | Quantum postprocessing |
| 11 | HybridForward | Hybrid forward pass |
| 12 | HybridBackward | Hybrid backward pass |
6.1.4 Variational Algorithms (4 ops)
| # | Op | Description | Usage |
|---|---|---|---|
| 13 | VqcLayer | Variational circuit layer | Quantum classification |
| 14 | VqeAnsatz | VQE ansatz | Quantum chemistry |
| 15 | QaoaLayer | QAOA layer | Combinatorial optimisation |
| 16 | QuantumKernel | Quantum kernel | Quantum machine learning |
6.1.5 Data Transfer (2 ops)
| # | Op | Description |
|---|---|---|
| 17 | GpuToQpu | GPU → QPU transfer |
| 18 | QpuToGpu | QPU → GPU transfer |
6.1.6 Co-Execution and Measurement (3 ops)
| # | Op | Description |
|---|---|---|
| 19 | CoExecute | Simultaneous classical+quantum execution |
| 20 | MeasureExpectation | Observable expectation value |
| 21 | MeasureSamples | Measurement sampling |
#![allow(unused)] fn main() { use lift_hybrid::ops::HybridOp; let op = HybridOp::VqcLayer; assert!(op.is_variational()); assert!(!op.is_gradient()); }
6.2 Encoding Strategies
#![allow(unused)] fn main() { use lift_hybrid::encoding::{EncodingStrategy, EncodingConfig}; let strategies = [ EncodingStrategy::AngleEncoding, // 1 qubit/feature, depth 1 EncodingStrategy::AmplitudeEncoding, // log2(n) qubits, depth n EncodingStrategy::BasisEncoding, // 1 qubit/feature, depth 1 EncodingStrategy::IQPEncoding, // 1 qubit/feature, depth 2n EncodingStrategy::HamiltonianEncoding, // 1 qubit/feature, depth n EncodingStrategy::KernelEncoding, // 1 qubit/feature, depth 3n ]; // Encoding configuration let config = EncodingConfig::new(EncodingStrategy::AmplitudeEncoding, 256); println!("Qubits required: {}", config.num_qubits); // 8 = log2(256) println!("Classical dimension: {}", config.classical_dim); // 256 }
| Strategy | Qubits | Depth | Best for |
|---|---|---|---|
| Angle | n | 1 | Few features |
| Amplitude | log₂(n) | n | Many features |
| Basis | n | 1 | Binary data |
| IQP | n | 2n | Quantum advantage |
| Hamiltonian | n | n | Physical simulation |
| Kernel | n | 3n | Quantum ML |
6.3 Gradient Configuration — Joint Gradient Setup
#![allow(unused)] fn main() { use lift_hybrid::gradient::{GradientMethod, JointGradientConfig}; let config = JointGradientConfig { classical_method: GradientMethod::Backprop, quantum_method: GradientMethod::ParameterShift, num_classical_params: 1000, num_quantum_params: 50, }; println!("Total evaluations: {}", config.total_evaluations()); // 1 (backprop) + 100 (2*50 parameter shift) = 101 }
6.4 Auxiliary Types
#![allow(unused)] fn main() { use lift_hybrid::ops::{AnsatzType, SyncPolicy, FeatureMap}; // Ansatz types for VQC let ansatz = AnsatzType::HardwareEfficient; // HardwareEfficient, StronglyEntangling, TwoLocal, UCCSD, Custom // Synchronisation policy let sync = SyncPolicy::Blocking; // Blocking, Asynchronous, Pipeline // Feature maps for quantum kernels let fm = FeatureMap::ZZFeatureMap; // ZZFeatureMap, PauliFeatureMap, AngleEncoding, AmplitudeEncoding }
7. lift-opt — Optimisation Passes (13 passes)
7.1 Classical Passes (5 passes)
7.1.1 Canonicalize — Canonical Form
#![allow(unused)] fn main() { use lift_opt::Canonicalize; use lift_core::pass::Pass; let pass = Canonicalize; // Reorders operations into canonical form // Normalises IR patterns to facilitate subsequent optimisations }
Usage: Always run first in the pipeline.
7.1.2 ConstantFolding — Constant Folding
#![allow(unused)] fn main() { use lift_opt::ConstantFolding; let pass = ConstantFolding; // Evaluates operations whose operands are all compile-time constants // Example: add(const(2), const(3)) → const(5) }
7.1.3 DeadCodeElimination — Dead Code Elimination
#![allow(unused)] fn main() { use lift_opt::DeadCodeElimination; let pass = DeadCodeElimination; // Removes operations whose results are never used // Respects operations with side effects (measurements, etc.) }
7.1.4 TensorFusion — Tensor Fusion
#![allow(unused)] fn main() { use lift_opt::TensorFusion; let pass = TensorFusion; // Fuses consecutive operations into fused operations // Example: MatMul + Bias + ReLU → FusedMatMulBiasReLU // Reduces memory accesses and kernel launches }
Combine with: Run after Canonicalize and ConstantFolding.
7.1.5 CommonSubexprElimination — Common Subexpression Elimination
#![allow(unused)] fn main() { use lift_opt::CommonSubexprElimination; let pass = CommonSubexprElimination; // Detects identical operations (same op, same operands) // Replaces duplicates with references to the first occurrence // Excludes operations with side effects }
7.2 Quantum Passes (3 passes)
7.2.1 GateCancellation — Gate Cancellation
#![allow(unused)] fn main() { use lift_opt::GateCancellation; let pass = GateCancellation; // Removes gate pairs that cancel out // Example: H H → identity, X X → identity // Respects qubit linearity invariants }
7.2.2 RotationMerge — Rotation Merging
#![allow(unused)] fn main() { use lift_opt::RotationMerge; let pass = RotationMerge; // Merges consecutive rotations on the same axis // Example: RZ(0.3) RZ(0.5) → RZ(0.8) // Removes identity rotations (angle ≈ 0) }
7.2.3 NoiseAwareSchedule — Noise-Aware Scheduling
#![allow(unused)] fn main() { use lift_opt::NoiseAwareSchedule; let pass = NoiseAwareSchedule; // Reorders quantum gates to minimise decoherence // Prioritises fast gates (1-qubit) before slow ones (2-qubit) // Respects SSA dependencies }
Combine with: lift-quantum::topology::DeviceTopology for the target topology.
7.3 Advanced AI Passes (3 passes)
7.3.1 FlashAttentionPass — FlashAttention Replacement
#![allow(unused)] fn main() { use lift_opt::FlashAttentionPass; let pass = FlashAttentionPass::default(); // threshold = 512 let pass_custom = FlashAttentionPass { seq_len_threshold: 1024 }; // Replaces tensor.attention with tensor.flash_attention // when sequence length exceeds the threshold // Reduces memory complexity from O(N²) to O(N) }
7.3.2 QuantisationPass — Quantisation Annotation
#![allow(unused)] fn main() { use lift_opt::QuantisationPass; use lift_opt::quantisation_pass::{QuantTarget, QuantMode}; let pass = QuantisationPass::default(); // INT8, Dynamic let pass_custom = QuantisationPass { target_dtype: QuantTarget::Fp8E4M3, mode: QuantMode::Static, }; // Annotates heavy operations (MatMul, Conv, Linear, Attention) // with quantisation metadata // Inserts Quantize/Dequantize pairs around annotated ops }
| Target | Size | Usage |
|---|---|---|
Int8 | 1 byte | Standard inference |
Int4 | 0.5 byte | Compressed LLMs (GPTQ, AWQ) |
Fp8E4M3 | 1 byte | H100 training |
Fp8E5M2 | 1 byte | H100 inference |
7.3.3 LayoutMapping — Qubit Mapping
#![allow(unused)] fn main() { use lift_opt::LayoutMapping; let pass = LayoutMapping; // Legacy annotation-only pass: marks 2-qubit gates whose qubits aren't // adjacent in the target topology with needs_swap = true. // Does NOT insert SWAP gates itself — that's RealRouting (§7.3.5), which // actually inserts quantum.swap ops along a BFS shortest path. }
Combine with: lift-quantum::topology::DeviceTopology.
7.3.4 GateDecomposition — Hardware-Native Gate Sets
#![allow(unused)] fn main() { use lift_opt::gate_decompose::GateDecomposition; use lift_quantum::gates::Provider; let pass = GateDecomposition::new(Provider::IbmEagle); // Lowers high-level gates to hardware-native gate sets, replacing the // original gate (not leaving it wired in alongside its decomposition): // H → RZ(π/2) SX RZ(π/2) // T/Tdg → RZ(±π/4) // S/Sdg → RZ(±π/2) // Y → RZ(π/2) X RZ(-π/2) // RX(θ) → RZ(π/2) SX RZ(π+θ) SX RZ(π/2) (verified against the closed-form // RX(θ) matrix up to global phase) // GateDecomposition::default() reads the provider from the "lift_provider" // op metadata key instead, falling back to Provider::Simulator (a no-op) // when it's unset — nothing in the CLI/config pipeline sets that key today, // so use GateDecomposition::new(provider) explicitly, as above. // Uses Context::insert_op_before to preserve SSA dominance }
Combine with: lift-config [quantum] provider key.
7.3.5 RealRouting — SWAP-Based Qubit Routing
#![allow(unused)] fn main() { use lift_opt::real_routing::RealRouting; use lift_quantum::topology::DeviceTopology; let pass = RealRouting::new(DeviceTopology::linear(8)); // Inserts actual quantum.swap operations so every 2-qubit gate // only acts on physically connected qubits. // Strategy: identity initial placement, BFS shortest-path routing, // logical↔physical placement maps updated after every swap. }
Combine with: lift-config [quantum] topology / num_qubits keys.
7.4 Optimisation Levels (O0-O3)
lift-config provides preset pipelines so you don't have to enumerate passes by hand:
| Level | Passes |
|---|---|
O0 | none |
O1 | canonicalize, constant-folding, dce |
O2 | O1 + cse, tensor-fusion |
O3 | all 13 passes (incl. gate-decomposition, real-routing) |
[optimisation]
level = "O3" # preset pipeline
passes = [] # (optional) explicit overrides the level
disabled_passes = ["cse"] # (optional) remove specific passes
max_iterations = 5
Explicit passes take priority over level; unknown pass names are reported by
OptimisationConfig::validate().
7.5 Recommended Optimisation Pipeline
#![allow(unused)] fn main() { use lift_core::PassManager; let mut pm = PassManager::new(); // Phase 1: Cleanup pm.add_pass(Box::new(lift_opt::Canonicalize)); pm.add_pass(Box::new(lift_opt::ConstantFolding)); pm.add_pass(Box::new(lift_opt::DeadCodeElimination)); pm.add_pass(Box::new(lift_opt::CommonSubexprElimination)); // Phase 2: Fusion (AI) pm.add_pass(Box::new(lift_opt::TensorFusion)); pm.add_pass(Box::new(lift_opt::FlashAttentionPass::default())); pm.add_pass(Box::new(lift_opt::QuantisationPass::default())); // Phase 3: Quantum pm.add_pass(Box::new(lift_opt::GateCancellation)); pm.add_pass(Box::new(lift_opt::RotationMerge)); pm.add_pass(Box::new(lift_opt::NoiseAwareSchedule)); pm.add_pass(Box::new(lift_opt::LayoutMapping)); // Phase 3b: Hardware targeting pm.add_pass(Box::new(lift_opt::gate_decompose::GateDecomposition::new(Provider::IbmEagle))); pm.add_pass(Box::new(lift_opt::real_routing::RealRouting::new(DeviceTopology::linear(8)))); // Phase 4: Final cleanup pm.add_pass(Box::new(lift_opt::DeadCodeElimination)); let results = pm.run_all(&mut ctx); }
8. lift-sim — Simulation and Cost Analysis
8.1 CostModel — Classical Cost Model
#![allow(unused)] fn main() { use lift_sim::cost::CostModel; // Predefined GPU profiles let a100 = CostModel::a100(); // 312 TFLOPS, 2039 GB/s let h100 = CostModel::h100(); // 989 TFLOPS, 3350 GB/s // Time estimation let flops = 2 * 1024 * 1024 * 1024_u64; let bytes = 4 * 1024 * 1024_u64; let compute_ms = a100.compute_time_ms(flops); // Compute time let memory_ms = a100.memory_time_ms(bytes); // Memory time let roofline_ms = a100.roofline_time_ms(flops, bytes); // Roofline model // Analysis let ai = a100.arithmetic_intensity(flops, bytes); // FLOPs/byte let bound = a100.is_compute_bound(flops, bytes); // true = compute-bound let fits = a100.fits_in_memory(bytes); // Fits in memory? let gpus = a100.num_gpus_needed(bytes); // GPUs needed }
8.2 QuantumCostModel — Quantum Cost Model
#![allow(unused)] fn main() { use lift_sim::cost::QuantumCostModel; // Quantum processor profiles let sc = QuantumCostModel::superconducting_default(); // IBM-like: 127 qubits let ion = QuantumCostModel::trapped_ion_default(); // IonQ-like: 32 qubits let atom = QuantumCostModel::neutral_atom_default(); // Atom-like: 256 qubits // Circuit fidelity estimation let fidelity = sc.circuit_fidelity(50, 20); // 50 1Q gates, 20 2Q gates println!("Fidelity: {:.6}", fidelity); // Circuit time let time_us = sc.circuit_time_us(50, 20, 5, 10); // 50 1Q, 20 2Q, 5 measurements, 10 depth println!("Time: {:.2} µs", time_us); // Decoherence fidelity let decoherence = sc.decoherence_fidelity(time_us); println!("Decoherence fidelity: {:.6}", decoherence); }
| Parameter | Superconducting | Trapped Ions | Neutral Atoms |
|---|---|---|---|
| 1Q time | 0.02 µs | 10 µs | 0.5 µs |
| 2Q time | 0.3 µs | 200 µs | 1.0 µs |
| 1Q fidelity | 99.9% | 99.99% | 99.9% |
| 2Q fidelity | 99% | 99.9% | 99.5% |
| T1 | 100 µs | 1 s | 5 ms |
| Qubits | 127 | 32 | 256 |
8.3 Budget — Resource Constraints
#![allow(unused)] fn main() { use lift_sim::cost::Budget; let budget = Budget { max_flops: Some(1_000_000_000_000), // 1 TFLOP max max_memory_bytes: Some(80_000_000_000), // 80 GB max_time_ms: Some(100.0), // 100 ms min_fidelity: Some(0.99), // 99% min fidelity max_circuit_depth: Some(1000), // 1000 layers max }; budget.check_flops(500_000_000_000).unwrap(); // OK budget.check_memory(40_000_000_000).unwrap(); // OK budget.check_fidelity(0.995).unwrap(); // OK }
8.4 EnergyModel — Energy and Carbon Estimation
#![allow(unused)] fn main() { use lift_sim::cost::EnergyModel; let model = EnergyModel::a100(); // Energy for 1 second of computation on 4 GPUs let joules = model.energy_joules(1000.0, 4); // Joules let kwh = model.energy_kwh(1000.0, 4); // kWh let carbon = model.carbon_grams(1000.0, 4); // grams CO₂ println!("Energy: {:.2} J", joules); println!("Carbon: {:.4} g CO₂", carbon); // Quantum energy (cryogenic refrigeration) let q_joules = model.quantum_energy_joules(100.0, 127); // 100 µs, 127 qubits }
8.5 ReactiveBudget — Dynamic Budget
#![allow(unused)] fn main() { use lift_sim::cost::{Budget, ReactiveBudget}; let budget = Budget { max_flops: Some(1_000_000), max_memory_bytes: Some(1_000_000), max_time_ms: Some(50.0), min_fidelity: Some(0.9), max_circuit_depth: None, }; let mut rb = ReactiveBudget::new(budget); // Consume resources incrementally rb.consume(100_000, 50_000, 5.0, 0.99); // flops, mem, time, fidelity rb.consume(200_000, 80_000, 10.0, 0.98); // Check remaining budget rb.check_remaining().unwrap(); // OK if within limits // Utilisation report let util = rb.utilisation(); println!("FLOPs used: {:.1}%", util.flop_ratio.unwrap() * 100.0); println!("Time used: {:.1}%", util.time_ratio.unwrap() * 100.0); // Remaining budget println!("Remaining FLOPs: {:?}", rb.remaining_flops()); println!("Remaining time: {:?} ms", rb.remaining_time_ms()); }
Combine with: lift-opt (stop optimisation if budget exhausted), lift-predict (verify prediction respects budget).
8.6 Module Analysis
#![allow(unused)] fn main() { use lift_sim::{analyze_module, analyze_quantum_ops}; let ctx = load_and_parse("model.lif").unwrap(); // Classical analysis let report = analyze_module(&ctx); println!("Total ops: {}", report.num_ops); println!("Tensor ops: {}", report.num_tensor_ops); println!("Quantum ops: {}", report.num_quantum_ops); println!("Hybrid ops: {}", report.num_hybrid_ops); println!("Total FLOPs: {}", report.total_flops); println!("Total memory: {} bytes", report.total_memory_bytes); println!("Peak memory: {} bytes", report.peak_memory_bytes); // Quantum analysis let quantum = analyze_quantum_ops(&ctx); println!("Qubits: {}", quantum.num_qubits_used); println!("Gates: {}", quantum.gate_count); println!("1Q gates: {}", quantum.one_qubit_gates); println!("2Q gates: {}", quantum.two_qubit_gates); println!("Measurements: {}", quantum.measurements); println!("Estimated fidelity: {:.6}", quantum.estimated_fidelity); }
9. lift-predict — Performance Prediction
#![allow(unused)] fn main() { use lift_predict::predict_performance; use lift_sim::{analyze_module, cost::CostModel}; let report = analyze_module(&ctx); let cost_model = CostModel::h100(); let prediction = predict_performance(&report, &cost_model); println!("Compute time: {:.4} ms", prediction.compute_time_ms); println!("Memory time: {:.4} ms", prediction.memory_time_ms); println!("Predicted time: {:.4} ms", prediction.predicted_time_ms); println!("Arithmetic intensity: {:.2} FLOP/byte", prediction.arithmetic_intensity); println!("Bottleneck: {}", prediction.bottleneck); // "compute" or "memory" }
Combine with: lift-sim (provides the analysis report and cost model).
10. lift-import — Model Import
10.1 ONNX Import
#![allow(unused)] fn main() { use lift_import::OnnxImporter; let importer = OnnxImporter::new(); let ctx = importer.import("model.onnx").expect("ONNX import failed"); }
10.2 PyTorch FX Import
#![allow(unused)] fn main() { use lift_import::PyTorchFxImporter; let importer = PyTorchFxImporter::new(); let ctx = importer.import("model_fx.json").expect("FX import failed"); }
10.3 OpenQASM 3.0 Import
#![allow(unused)] fn main() { use lift_import::OpenQasm3Importer; let importer = OpenQasm3Importer::new(); let ctx = importer.import("circuit.qasm").expect("QASM import failed"); }
Combine with: lift-core::verifier (verify imported IR), then lift-opt (optimise).
11. lift-export — Backend Export (LLVM, ONNX, QASM)
11.1 Export LLVM IR
#![allow(unused)] fn main() { use lift_export::LlvmExporter; let exporter = LlvmExporter::new(); let llvm_ir = exporter.export(&ctx).expect("LLVM export failed"); std::fs::write("output.ll", &llvm_ir).unwrap(); }
Produces LLVM IR with runtime function calls for tensor operations (cuBLAS/cuDNN backend).
11.2 Export ONNX
#![allow(unused)] fn main() { use lift_export::OnnxExporter; let exporter = OnnxExporter::new(); let onnx_text = exporter.export(&ctx).expect("ONNX export failed"); std::fs::write("output.onnx", &onnx_text).unwrap(); // Also available: JSON format let onnx_json = exporter.export_json(&ctx).expect("ONNX JSON export failed"); std::fs::write("output_onnx.json", &onnx_json).unwrap(); }
Produces ONNX protobuf text format at opset version 21, IR version 9. Compatible with:
- PyTorch, TensorFlow, TensorRT, ONNX Runtime
- Microsoft extension ops for attention and MoE
ONNX op mapping (70+ operations):
| LIFT Operation | ONNX Op | Domain |
|---|---|---|
tensor.matmul | MatMul | standard |
tensor.linear | Gemm | standard |
tensor.add / sub / mul / div | Add / Sub / Mul / Div | standard |
tensor.relu | Relu | standard |
tensor.gelu | Gelu | standard |
tensor.silu | Sigmoid + Mul | standard |
tensor.softmax | Softmax | standard |
tensor.layernorm | LayerNormalization | standard |
tensor.rmsnorm | SimplifiedLayerNormalization | com.microsoft |
tensor.batchnorm | BatchNormalization | standard |
tensor.conv2d | Conv | standard |
tensor.maxpool2d | MaxPool | standard |
tensor.avgpool2d | AveragePool | standard |
tensor.attention | Attention | com.microsoft |
tensor.grouped_query_attention | GroupQueryAttention | com.microsoft |
tensor.flash_attention | MultiHeadAttention | com.microsoft |
tensor.quantize | QuantizeLinear | standard |
tensor.dequantize | DequantizeLinear | standard |
tensor.moe_dispatch | MoE | com.microsoft |
tensor.reshape | Reshape | standard |
tensor.transpose | Transpose | standard |
tensor.concat | Concat | standard |
tensor.gather | Gather | standard |
tensor.squeeze / unsqueeze | Squeeze / Unsqueeze | standard |
tensor.clip / clamp | Clip | standard |
tensor.topk | TopK | standard |
tensor.where | Where | standard |
tensor.cumsum | CumSum | standard |
tensor.constant | Constant | standard |
tensor.zeros / ones | ConstantOfShape | standard |
tensor.einsum | Einsum | standard |
tensor.fft / ifft | DFT / IDFT | standard |
tensor.sparse_matmul | MatMul (sparse) | standard |
tensor.fused_matmul_bias_relu | FusedMatMulBiasRelu | com.microsoft |
tensor.fused_matmul_bias | FusedMatMulBias | com.microsoft |
tensor.fused_linear_gelu | FusedGemm | com.microsoft |
tensor.fused_linear_silu | FusedGemm | com.microsoft |
tensor.fused_conv_batchnorm_relu | FusedConvBatchNormRelu | com.microsoft |
tensor.fused_attention_layernorm | FusedAttention | com.microsoft |
Data type mapping:
| LIFT DataType | ONNX ElemType |
|---|---|
FP32 | 1 (FLOAT) |
FP64 | 11 (DOUBLE) |
FP16 | 10 (FLOAT16) |
BF16 | 16 (BFLOAT16) |
INT8 | 3 (INT8) |
INT32 | 6 (INT32) |
INT64 | 7 (INT64) |
BOOL | 9 (BOOL) |
11.3 Export OpenQASM 3.0
#![allow(unused)] fn main() { use lift_export::QasmExporter; let exporter = QasmExporter::new(); let qasm = exporter.export(&ctx).expect("QASM export failed"); std::fs::write("output.qasm", &qasm).unwrap(); }
Produces OpenQASM 3.0 executable on IBM Quantum, Rigetti, IonQ, Quantinuum.
Every one of the 48 QuantumGate variants has a match arm — 46 emit a real
gate instruction, and IfElse/ParamGate (control-flow and generic
wrappers, not literal gates) emit a descriptive comment instead. Qubit
indices are resolved by following each gate's actual SSA operand back to its
owning qubit, not assigned from a counter.
Combine with: lift-opt (optimise before export), lift-quantum::Provider (transpile to native gate set).
12. lift-config — Configuration (.lith)
12.1 .lith File Format
[target]
backend = "cuda"
device = "A100"
precision = "fp16"
[budget]
max_flops = 1000000000000
max_memory_bytes = 80000000000
max_time_ms = 100.0
min_fidelity = 0.99
[optimisation]
level = O2
max_iterations = 10
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = true
[quantum]
topology = "heavy_hex"
num_qubits = 127
shots = 4096
12.2 Programmatic Loading
#![allow(unused)] fn main() { use lift_config::{ConfigParser, LithConfig}; // From a file let source = std::fs::read_to_string("config.lith").unwrap(); let config = ConfigParser::new().parse(&source).unwrap(); // Default configuration let default = LithConfig::default(); // Backend: llvm, Level: O2, Passes: canonicalize, constant-folding, dce, tensor-fusion // With quantum let hybrid = LithConfig::default().with_quantum("heavy_hex", 127); }
12.3 Optimisation Levels
| Level | Passes | Usage |
|---|---|---|
O0 | None | Debug, verification |
O1 | Canonicalize, ConstantFolding, DCE | Fast compilation |
O2 | O1 + CSE, TensorFusion | Default — good trade-off |
O3 | All 13 passes (O2 + FlashAttention, Quantisation, GateCancellation, RotationMerge, NoiseAwareSchedule, LayoutMapping, GateDecomposition, RealRouting) | Maximum performance |
13. lift-cli — Command-Line Interface
13.1 Available Commands
13.1.1 lift verify — Verify a .lif file
lift verify model.lif
lift verify --verbose model.lif
Checks SSA invariants, qubit linearity, and typing.
13.1.2 lift analyse — Analyse a program
lift analyse model.lif
lift analyse model.lif --format json
Produces a report: op count, FLOPs, memory, quantum analysis.
13.1.3 lift print — Display the IR
lift print model.lif
Displays the IR in human-readable format.
13.1.4 lift optimise — Optimise
lift optimise model.lif
lift optimise model.lif --config config.lith --output optimised.lif
Applies the configured optimisation passes.
13.1.5 lift predict — Predict performance
lift predict model.lif --device a100
lift predict model.lif --device h100
Predicts execution time using the roofline model.
13.1.6 lift export — Export
lift export model.lif --backend llvm --output model.ll
lift export model.lif --backend onnx --output model.onnx
lift export quantum.lif --backend qasm --output circuit.qasm
Exports to LLVM IR, ONNX (opset 21), or OpenQASM 3.0.
14. lift-codegen — Programmatic Model Generation
The lift-codegen binary lets you define models directly from Rust code and automatically generate all export formats.
14.1 Running the Code Generator
cargo run --bin lift-codegen
This generates into examples/:
- 4
.lifmodels — Phi-3-mini, MLP, ResNet block, VQE circuit - 4
.llfiles — LLVM IR exports - 4
.onnxfiles — ONNX exports - 1
.qasmfile — OpenQASM export (for quantum models only) - 1
.lithconfig — H100 optimization configuration
Each model is automatically verified, analysed, optimised, and exported.
14.2 ModelBuilder API
#![allow(unused)] fn main() { use lift_core::model_builder::{ModelBuilder, tensor, tensor_2d, DataType}; let model = ModelBuilder::new("my_model") .function("forward") .param("x", tensor(&[1, 784], DataType::FP32)) .param("w", tensor_2d(784, 256, DataType::FP32)) .op("tensor.matmul", &["x", "w"], "h", tensor(&[1, 256], DataType::FP32)) .op("tensor.relu", &["h"], "out", tensor(&[1, 256], DataType::FP32)) .returns("out") .done(); // Write .lif source file model.write_lif("my_model.lif").unwrap(); // Build IR context for verification/analysis/export let ctx = model.build_context(); lift_core::verifier::verify(&ctx).unwrap(); }
14.3 Multi-Target Export from Code
#![allow(unused)] fn main() { let ctx = model.build_context(); // Optimise first let mut pm = PassManager::new(); pm.add_pass(Box::new(lift_opt::Canonicalize)); pm.add_pass(Box::new(lift_opt::ConstantFolding)); pm.add_pass(Box::new(lift_opt::DeadCodeElimination)); pm.add_pass(Box::new(lift_opt::TensorFusion)); pm.run_all(&mut ctx); // Export to all 3 backends let llvm_ir = lift_export::LlvmExporter::new().export(&ctx).unwrap(); let onnx_ir = lift_export::OnnxExporter::new().export(&ctx).unwrap(); std::fs::write("my_model.ll", &llvm_ir).unwrap(); std::fs::write("my_model.onnx", &onnx_ir).unwrap(); // Export QASM only if quantum ops present if ctx.ops.iter().any(|(_, op)| ctx.strings.resolve(op.name).starts_with("quantum.")) { let qasm_ir = lift_export::QasmExporter::new().export(&ctx).unwrap(); std::fs::write("my_model.qasm", &qasm_ir).unwrap(); } }
Combine with: All other crates. ModelBuilder is the programmatic entry point for defining models without .lif files.
15. Combinations and Complete Pipelines
15.1 Complete AI Pipeline (Transformer)
#![allow(unused)] fn main() { // 1. Import an ONNX model let ctx = OnnxImporter::new().import("bert.onnx")?; // 2. Verify verifier::verify(&ctx)?; // 3. Analyse let report = analyze_module(&ctx); // 4. Optimise let mut pm = PassManager::new(); pm.add_pass(Box::new(Canonicalize)); pm.add_pass(Box::new(ConstantFolding)); pm.add_pass(Box::new(DeadCodeElimination)); pm.add_pass(Box::new(CommonSubexprElimination)); pm.add_pass(Box::new(TensorFusion)); pm.add_pass(Box::new(FlashAttentionPass { seq_len_threshold: 512 })); pm.add_pass(Box::new(QuantisationPass { target_dtype: QuantTarget::Int8, mode: QuantMode::Dynamic, })); pm.add_pass(Box::new(DeadCodeElimination)); pm.run_all(&mut ctx); // 5. Predict performance let h100 = CostModel::h100(); let pred = predict_performance(&analyze_module(&ctx), &h100); // 6. Export to LLVM and ONNX let llvm = LlvmExporter::new().export(&ctx)?; let onnx = OnnxExporter::new().export(&ctx)?; std::fs::write("bert_optimised.ll", llvm)?; std::fs::write("bert_optimised.onnx", onnx)?; }
15.2 Complete Quantum Pipeline (Bell State)
#![allow(unused)] fn main() { // 1. Parse the circuit let ctx = load_lif_file("quantum_bell.lif")?; // 2. Analyse noise let quantum = analyze_quantum_ops(&ctx); let sc = QuantumCostModel::superconducting_default(); let fidelity = sc.circuit_fidelity( quantum.one_qubit_gates, quantum.two_qubit_gates ); // 3. QEC if needed if fidelity < 0.99 { let analysis = QecAnalysis::analyse(2, 5, QecCode::SurfaceCode { distance: 3 }, 0.001); println!("Physical qubits needed: {}", analysis.physical_qubits); } // 4. Optimise let mut pm = PassManager::new(); pm.add_pass(Box::new(GateCancellation)); pm.add_pass(Box::new(RotationMerge)); pm.add_pass(Box::new(NoiseAwareSchedule)); pm.add_pass(Box::new(LayoutMapping)); pm.run_all(&mut ctx); // 5. Export to QASM let qasm = QasmExporter::new().export(&ctx)?; std::fs::write("bell_optimised.qasm", qasm)?; }
15.3 Complete Hybrid Pipeline (VQE)
#![allow(unused)] fn main() { // 1. Configure encoding let encoding = EncodingConfig::new(EncodingStrategy::AngleEncoding, 4); // 2. Configure gradient let grad_config = JointGradientConfig { classical_method: GradientMethod::Backprop, quantum_method: GradientMethod::ParameterShift, num_classical_params: 100, num_quantum_params: 20, }; // 3. Reactive budget to control resources let budget = Budget { max_flops: Some(1_000_000_000), max_memory_bytes: Some(8_000_000_000), max_time_ms: Some(60_000.0), min_fidelity: Some(0.95), max_circuit_depth: Some(500), }; let mut rb = ReactiveBudget::new(budget); // 4. VQE optimisation loop for iteration in 0..100 { // Execute the quantum circuit rb.consume(10_000, 1_000, 0.5, 0.999); if rb.check_remaining().is_err() { println!("Budget exhausted at iteration {}", iteration); break; } let util = rb.utilisation(); println!("Iteration {}: FLOP {:.1}%, Time {:.1}%", iteration, util.flop_ratio.unwrap() * 100.0, util.time_ratio.unwrap() * 100.0 ); } // 5. Estimate carbon footprint let energy = EnergyModel::a100(); let carbon = energy.carbon_grams(rb.elapsed_ms, 1); println!("Carbon footprint: {:.4} g CO₂", carbon); }
15.4 Complete CLI Pipeline
# Verify, analyse, optimise, predict and export in one sequence
lift verify model.lif
lift analyse model.lif --format json > analysis.json
lift optimise model.lif --config production.lith --output optimised.lif
lift predict optimised.lif --device h100
lift export optimised.lif --backend llvm --output model.ll
lift export optimised.lif --backend onnx --output model.onnx
16. Concrete Examples
16.1 MLP (Multi-Layer Perceptron)
File tensor_mlp.lif:
#dialect tensor
module @mlp {
func @forward(%x: tensor<1x784xf32>, %w1: tensor<784x256xf32>,
%b1: tensor<256xf32>, %w2: tensor<256x10xf32>,
%b2: tensor<10xf32>) -> tensor<1x10xf32> {
%h1 = "tensor.matmul"(%x, %w1) : (tensor<1x784xf32>, tensor<784x256xf32>) -> tensor<1x256xf32>
%h2 = "tensor.add"(%h1, %b1) : (tensor<1x256xf32>, tensor<256xf32>) -> tensor<1x256xf32>
%h3 = "tensor.relu"(%h2) : (tensor<1x256xf32>) -> tensor<1x256xf32>
%h4 = "tensor.matmul"(%h3, %w2) : (tensor<1x256xf32>, tensor<256x10xf32>) -> tensor<1x10xf32>
%h5 = "tensor.add"(%h4, %b2) : (tensor<1x10xf32>, tensor<10xf32>) -> tensor<1x10xf32>
%out = "tensor.softmax"(%h5) : (tensor<1x10xf32>) -> tensor<1x10xf32>
return %out
}
}
16.2 Self-Attention (Transformer)
File attention.lif:
#dialect tensor
module @transformer {
func @self_attention(%q: tensor<1x128x64xf32>, %k: tensor<1x128x64xf32>,
%v: tensor<1x128x64xf32>, %norm_w: tensor<64xf32>)
-> tensor<1x128x64xf32> {
%attn = "tensor.attention"(%q, %k, %v) : (...) -> tensor<1x128x64xf32>
%normed = "tensor.layernorm"(%attn, %norm_w) : (...) -> tensor<1x128x64xf32>
return %normed
}
}
16.3 Bell State (Quantum)
File quantum_bell.lif:
#dialect quantum
module @bell_state {
func @bell(%q0: qubit, %q1: qubit) -> (qubit, qubit) {
%q2 = "quantum.h"(%q0) : (qubit) -> qubit
%q3, %q4 = "quantum.cx"(%q2, %q1) : (qubit, qubit) -> (qubit, qubit)
return %q3, %q4
}
}
16.4 Production Configuration
File production.lith:
[target]
backend = "cuda"
device = "H100"
precision = "fp16"
[budget]
max_flops = 1000000000000
max_memory_bytes = 80000000000
max_time_ms = 100.0
[optimisation]
level = O3
max_iterations = 20
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = true
[quantum]
topology = "heavy_hex"
num_qubits = 127
shots = 4096
Summary of Combinations by Task
| Task | Crates to combine |
|---|---|
| Train an LLM | lift-tensor + lift-opt (TensorFusion, FlashAttention) + lift-sim (CostModel) + lift-export (LLVM, ONNX) |
| Quantised inference | lift-tensor + lift-opt (QuantisationPass) + lift-predict + lift-export (LLVM, ONNX) |
| Quantum circuit | lift-quantum + lift-opt (GateCancellation, RotationMerge, LayoutMapping) + lift-export (QASM) |
| VQE / QAOA | lift-hybrid + lift-quantum + lift-opt (NoiseAwareSchedule) + lift-sim (QuantumCostModel) |
| Quantum ML | lift-hybrid (QuantumKernel, encoding) + lift-tensor + lift-quantum |
| Cost analysis | lift-sim (CostModel, EnergyModel) + lift-predict |
| QEC planning | lift-quantum (qec, topology) + lift-sim (QuantumCostModel) |
| Import/Optimise/Export | lift-import + lift-opt + lift-export (LLVM, ONNX, QASM) |
| Programmatic generation | lift-codegen + lift-core (ModelBuilder) + lift-export |
| Stable Diffusion | lift-tensor (UNet ops) + lift-opt (TensorFusion) + lift-export |
| GNN | lift-tensor (GNNMessagePassing, GNNGlobalPooling) + lift-opt + lift-export |
LIFT User Manual — Complete Usage Guide
LIFT — Language for Intelligent Frameworks and Technologies Version 0.4.8
This manual is the definitive reference for every use case of the LIFT compiler framework. It presents real-world problems, explains how LIFT solves them, and provides working code examples for each scenario.
Table of Contents
- What is LIFT and Why Does It Exist?
- Installation and Setup
- Core Concepts
- The
.lifSource Language - Use Case 1 — Neural Network Optimisation
- Use Case 2 — Transformer Attention and FlashAttention
- Use Case 3 — Quantum Circuit Design and Noise Analysis
- Use Case 4 — Hybrid Classical-Quantum (VQE)
- Use Case 5 — Model Import
- Use Case 6 — Performance Prediction
- Use Case 7 — Quantised Inference
- Use Case 8 — Backend Export (LLVM, ONNX, QASM)
- Use Case 9 — Energy and Carbon Estimation
- Use Case 10 — Device Topology and Routing
- Use Case 11 — Diffusion and GNN Models
- Use Case 12 — Budget-Constrained Compilation
- Use Case 13 — End-to-End Pipelines
- Configuration with
.lithFiles - CLI Reference
- Programmatic Model Generation
- Complete API Reference
- Troubleshooting
1. What is LIFT and Why Does It Exist?
1.1 The Problem
Modern computing faces a fragmentation crisis:
- AI/ML frameworks (PyTorch, TensorFlow, ONNX) produce models in incompatible formats with no unified optimisation pipeline.
- Quantum computing (Qiskit, Cirq, OpenQASM) uses entirely separate toolchains with no connection to classical compilation.
- Hybrid algorithms (VQE, QAOA, Quantum ML) require ad-hoc glue code between classical and quantum systems.
- Performance analysis is fragmented — different tools for GPU profiling, quantum fidelity, and cost modelling.
1.2 How LIFT Solves It
LIFT provides a single SSA-based intermediate representation spanning three dialects:
| Dialect | Domain | Operations |
|---|---|---|
| tensor | AI/ML | 110 ops: arithmetic, attention, convolution, normalisation, quantisation, GNN, diffusion |
| quantum | Quantum computing | 48 gates: Pauli, Clifford, parametric, multi-qubit; noise models, QEC, topology |
| hybrid | Classical-quantum | 21 ops: encoding, gradient methods, variational algorithms, GPU↔QPU transfer |
The unified pipeline: import → verify → analyse → optimise → predict → export.
1.3 Architecture
┌──────────┐
│ lift-cli │ ← User interface
└────┬─────┘
┌─────────────┼─────────────┐
┌──────┴──────┐ ┌────┴────┐ ┌──────┴──────┐
│ lift-import │ │lift-opt │ │ lift-export │
└──────┬──────┘ └────┬────┘ └──────┬──────┘
┌──────┴──────┐ ┌────┴────┐ ┌──────┴──────┐
│ lift-ast │ │lift-sim │ │lift-predict │
└──────┬──────┘ └────┬────┘ └──────┬──────┘
┌──────┴─────────────┴─────────────┴──────┐
│ lift-core │
├──────────┬──────────┬───────────────────┤
│lift-tensor│lift-quantum│ lift-hybrid │
└──────────┴──────────┴───────────────────┘
2. Installation and Setup
2.1 Prerequisites
- Rust 1.80+ — install via rustup
2.2 Build
git clone https://github.com/rustnew/Lift.git
cd Lift
cargo build --release
cargo test --workspace # 541 tests, all pass
2.3 Use as a Library
[dependencies]
lift-core = { path = "crates/lift-core" }
lift-ast = { path = "crates/lift-ast" }
lift-tensor = { path = "crates/lift-tensor" }
lift-quantum = { path = "crates/lift-quantum" }
lift-hybrid = { path = "crates/lift-hybrid" }
lift-opt = { path = "crates/lift-opt" }
lift-sim = { path = "crates/lift-sim" }
lift-predict = { path = "crates/lift-predict" }
lift-import = { path = "crates/lift-import" }
lift-export = { path = "crates/lift-export" }
lift-config = { path = "crates/lift-config" }
3. Core Concepts
3.1 SSA IR
LIFT uses Static Single Assignment — every value is defined exactly once:
%h1 = "tensor.matmul"(%x, %w) : (tensor<1x784xf32>, tensor<784x256xf32>) -> tensor<1x256xf32>
%h2 = "tensor.relu"(%h1) : (tensor<1x256xf32>) -> tensor<1x256xf32>
3.2 The Context
The Context is the central data structure holding all IR elements:
#![allow(unused)] fn main() { use lift_core::{Context, Attributes, Location}; use lift_core::types::{Dimension, DataType, MemoryLayout}; let mut ctx = Context::new(); // Create types let tensor_ty = ctx.make_tensor_type( vec![Dimension::Constant(1), Dimension::Constant(784)], DataType::FP32, MemoryLayout::Contiguous, ); let qubit_ty = ctx.make_qubit_type(); // Create block, add arguments let block = ctx.create_block(); let x = ctx.create_block_arg(block, tensor_ty); // Create operation let (op, results) = ctx.create_op( "tensor.relu", "tensor", vec![x], vec![tensor_ty], Attributes::new(), Location::unknown(), ); ctx.add_op_to_block(block, op); }
3.3 Linear Qubit Types
Problem: Qubits cannot be copied (no-cloning theorem). Classical IRs allow reuse, violating physics.
Solution: LIFT enforces linear types for qubits — each consumed exactly once:
#![allow(unused)] fn main() { let mut ctx = Context::new(); let qubit_ty = ctx.make_qubit_type(); let block = ctx.create_block(); let q0 = ctx.create_block_arg(block, qubit_ty); // First use — OK let (op1, _) = ctx.create_op("quantum.x", "quantum", vec![q0], vec![qubit_ty], Attributes::new(), Location::unknown()); ctx.add_op_to_block(block, op1); // Second use of same q0 — LINEARITY VIOLATION let (op2, _) = ctx.create_op("quantum.h", "quantum", vec![q0], vec![qubit_ty], Attributes::new(), Location::unknown()); ctx.add_op_to_block(block, op2); let result = lift_core::verifier::verify(&ctx); assert!(result.is_err()); // VerifyError::LinearityViolation }
3.4 Verification
The verifier checks SSA, dominance, and linearity:
#![allow(unused)] fn main() { use lift_core::verifier; match verifier::verify(&ctx) { Ok(()) => println!("IR is valid"), Err(errors) => { for err in &errors { eprintln!("Error: {}", err); } } } }
3.5 Printing the IR
#![allow(unused)] fn main() { use lift_core::printer::print_ir; let output = print_ir(&ctx); println!("{}", output); }
4. The .lif Source Language
4.1 Syntax
#dialect tensor
module @name {
func @function(%arg0: type0, %arg1: type1) -> return_type {
%result = "dialect.op"(%arg0, %arg1) {attr = value}
: (type0, type1) -> return_type
return %result
}
}
4.2 Types
| Type | Syntax | Example |
|---|---|---|
| Tensor | tensor<shape x dtype> | tensor<1x784xf32> |
| Qubit | qubit | qubit |
| Classical bit | bit | bit |
| Scalar | f32, f64, i32, i64 | f32 |
4.3 Parsing Programmatically
#![allow(unused)] fn main() { use lift_ast::{Lexer, Parser, IrBuilder}; use lift_core::Context; let source = std::fs::read_to_string("examples/tensor_mlp.lif").unwrap(); let tokens = Lexer::new(&source).tokenize().to_vec(); let program = Parser::new(tokens).parse().unwrap(); let mut ctx = Context::new(); IrBuilder::new().build_program(&mut ctx, &program).unwrap(); lift_core::verifier::verify(&ctx).unwrap(); }
5. Use Case 1 — Neural Network Optimisation
5.1 Problem
You have a Multi-Layer Perceptron (MLP) and want to:
- Represent it as LIFT IR
- Verify correctness
- Fuse MatMul + Bias + ReLU into a single kernel
- Measure FLOPs and memory
5.2 The MLP in .lif
File: examples/tensor_mlp.lif
#dialect tensor
module @mlp {
func @forward(%x: tensor<1x784xf32>, %w1: tensor<784x256xf32>, %b1: tensor<256xf32>,
%w2: tensor<256x10xf32>, %b2: tensor<10xf32>) -> tensor<1x10xf32> {
%h1 = "tensor.matmul"(%x, %w1) : (tensor<1x784xf32>, tensor<784x256xf32>) -> tensor<1x256xf32>
%h2 = "tensor.add"(%h1, %b1) : (tensor<1x256xf32>, tensor<256xf32>) -> tensor<1x256xf32>
%h3 = "tensor.relu"(%h2) : (tensor<1x256xf32>) -> tensor<1x256xf32>
%h4 = "tensor.matmul"(%h3, %w2) : (tensor<1x256xf32>, tensor<256x10xf32>) -> tensor<1x10xf32>
%h5 = "tensor.add"(%h4, %b2) : (tensor<1x10xf32>, tensor<10xf32>) -> tensor<1x10xf32>
%out = "tensor.softmax"(%h5) : (tensor<1x10xf32>) -> tensor<1x10xf32>
return %out
}
}
5.3 Build the IR Programmatically
#![allow(unused)] fn main() { use lift_core::{Context, Attributes, Location}; use lift_core::types::{Dimension, DataType, MemoryLayout}; let mut ctx = Context::new(); let input_ty = ctx.make_tensor_type( vec![Dimension::Constant(1), Dimension::Constant(784)], DataType::FP32, MemoryLayout::Contiguous, ); let w1_ty = ctx.make_tensor_type( vec![Dimension::Constant(784), Dimension::Constant(256)], DataType::FP32, MemoryLayout::Contiguous, ); let b1_ty = ctx.make_tensor_type( vec![Dimension::Constant(256)], DataType::FP32, MemoryLayout::Contiguous, ); let h1_ty = ctx.make_tensor_type( vec![Dimension::Constant(1), Dimension::Constant(256)], DataType::FP32, MemoryLayout::Contiguous, ); let block = ctx.create_block(); let x = ctx.create_block_arg(block, input_ty); let w1 = ctx.create_block_arg(block, w1_ty); let b1 = ctx.create_block_arg(block, b1_ty); // MatMul let (mm_op, mm_res) = ctx.create_op( "tensor.matmul", "tensor", vec![x, w1], vec![h1_ty], Attributes::new(), Location::unknown(), ); ctx.add_op_to_block(block, mm_op); // Add bias let (add_op, add_res) = ctx.create_op( "tensor.add", "tensor", vec![mm_res[0], b1], vec![h1_ty], Attributes::new(), Location::unknown(), ); ctx.add_op_to_block(block, add_op); // ReLU let (relu_op, _relu_res) = ctx.create_op( "tensor.relu", "tensor", vec![add_res[0]], vec![h1_ty], Attributes::new(), Location::unknown(), ); ctx.add_op_to_block(block, relu_op); lift_core::verifier::verify(&ctx).expect("Verification failed"); }
5.4 Tensor Fusion: Fuse MatMul + Bias + ReLU
Problem: Three separate GPU kernels waste memory bandwidth on intermediate results.
Solution: The TensorFusion pass detects matmul → add → relu and fuses them:
#![allow(unused)] fn main() { use lift_core::pass::PassManager; use lift_opt::{Canonicalize, ConstantFolding, TensorFusion, DeadCodeElimination}; let mut pm = PassManager::new(); pm.add_pass(Box::new(Canonicalize)); // x + 0 → x, x * 1 → x pm.add_pass(Box::new(ConstantFolding)); // fold constants at compile time pm.add_pass(Box::new(TensorFusion)); // matmul + bias + relu → fused pm.add_pass(Box::new(DeadCodeElimination)); // remove dead ops let results = pm.run_all(&mut ctx); for (name, result) in &results { println!(" {}: {:?}", name, result); } }
Before fusion:
%h1 = "tensor.matmul"(%x, %w1) : (...) -> tensor<1x256xf32>
%h2 = "tensor.add"(%h1, %b1) : (...) -> tensor<1x256xf32>
%h3 = "tensor.relu"(%h2) : (...) -> tensor<1x256xf32>
After fusion:
%h3 = "tensor.fused_matmul_bias_relu"(%x, %w1, %b1) : (...) -> tensor<1x256xf32>
5.5 Analyse Resource Usage
#![allow(unused)] fn main() { use lift_sim::analysis::analyze_module; let report = analyze_module(&ctx); println!("Total ops: {}", report.num_ops); println!("Tensor ops: {}", report.num_tensor_ops); println!("Total FLOPs: {}", report.total_flops); println!("Total memory: {} bytes", report.total_memory_bytes); println!("Peak memory: {} bytes", report.peak_memory_bytes); for (op_name, count) in &report.op_breakdown { println!(" {}: {}", op_name, count); } }
5.6 Shape Inference and FLOPs Counting
LIFT computes shapes and FLOPs for every tensor operation:
#![allow(unused)] fn main() { use lift_tensor::{TensorOp, ShapeInference}; use lift_core::types::{TensorTypeInfo, Dimension, DataType, MemoryLayout}; let a = TensorTypeInfo { shape: vec![Dimension::Constant(2), Dimension::Constant(3)], dtype: DataType::FP32, layout: MemoryLayout::Contiguous, }; let b = TensorTypeInfo { shape: vec![Dimension::Constant(3), Dimension::Constant(4)], dtype: DataType::FP32, layout: MemoryLayout::Contiguous, }; // Shape inference let output = ShapeInference::infer_output_shape(&TensorOp::MatMul, &[&a, &b]).unwrap(); // output[0].shape = [2, 4] // FLOPs: 2*M*N*K = 2*2*4*3 = 48 let flops = ShapeInference::compute_flops(&TensorOp::MatMul, &[&a, &b]); assert_eq!(flops, Some(48)); // Memory bytes: input A + input B + output let mem = ShapeInference::compute_memory_bytes(&TensorOp::MatMul, &[&a, &b]); println!("Memory: {:?} bytes", mem); }
5.7 All 110 Tensor Operations
| Category | Operations |
|---|---|
| Arithmetic | add, sub, mul, div, neg, matmul, linear, conv2d, embedding |
| Activations | relu, gelu, silu, sigmoid, softmax, tanh, leaky_relu, elu, mish, hard_swish, hard_sigmoid |
| Normalisation | layernorm, rmsnorm, batchnorm, groupnorm, instancenorm |
| Shape | reshape, transpose, concat, split, gather, scatter, squeeze, unsqueeze, permute, expand, slice, pad, tile |
| Constants | constant, zeros, ones, arange, full |
| Attention | attention, multi_head_attention, multi_query_attention, grouped_query_attention, flash_attention, sliding_window_attention, cross_attention, paged_attention |
| MoE | moe_dispatch, moe_combine |
| Convolution | conv1d, conv3d, conv_transpose2d, depthwise_conv2d, dilated_conv2d |
| Pooling | maxpool2d, avgpool2d, adaptive_avgpool2d, global_avgpool |
| Recurrent | lstm_cell, gru_cell, rnn_cell |
| Advanced Math | einsum, fft, ifft, svd, eig, solve, topk, sort, cumsum, where, clamp |
| Sparse | sparse_matmul, sparse_embedding |
| Quantisation | quantize, dequantize, quantize_int4, dequantize_int4, quantize_fp8, dequantize_fp8 |
| Diffusion | unet_down_block, unet_up_block, timestep_embedding |
| GNN | gnn_message_passing, gnn_global_pooling |
| Memory | checkpoint, offload, grad_accumulate |
| Gradient | grad_matmul, grad_relu, grad_softmax, grad_layernorm, grad_attention, grad_conv2d, grad_linear, grad_gelu |
| Parallelism | parallel_split, parallel_allreduce, pipeline_send, pipeline_receive |
| Fused | fused_matmul_bias_relu, fused_matmul_bias, fused_linear_gelu, fused_attention_layernorm, fused_linear_silu, fused_conv_batchnorm_relu |
6. Use Case 2 — Transformer Attention and FlashAttention
6.1 Problem
Transformers use self-attention which scales O(n²) in memory. For long sequences (>512 tokens), this becomes the bottleneck.
6.2 Attention in .lif
File: examples/attention.lif
#dialect tensor
module @transformer {
func @self_attention(%q: tensor<1x128x64xf32>, %k: tensor<1x128x64xf32>,
%v: tensor<1x128x64xf32>, %norm_w: tensor<64xf32>)
-> tensor<1x128x64xf32> {
%attn = "tensor.attention"(%q, %k, %v)
: (tensor<1x128x64xf32>, tensor<1x128x64xf32>, tensor<1x128x64xf32>)
-> tensor<1x128x64xf32>
%normed = "tensor.layernorm"(%attn, %norm_w)
: (tensor<1x128x64xf32>, tensor<64xf32>) -> tensor<1x128x64xf32>
return %normed
}
}
6.3 FlashAttention Pass
The FlashAttentionPass replaces tensor.attention with tensor.flash_attention when sequence length exceeds a threshold:
#![allow(unused)] fn main() { use lift_opt::FlashAttentionPass; use lift_core::pass::PassManager; let mut pm = PassManager::new(); pm.add_pass(Box::new(FlashAttentionPass { seq_len_threshold: 512 })); pm.run_all(&mut ctx); // tensor.attention → tensor.flash_attention // Same FLOPs, O(n) memory instead of O(n²) }
6.4 Attention Variants
| Operation | Architecture | Memory |
|---|---|---|
tensor.attention | Standard QKV | O(n²) |
tensor.multi_head_attention | GPT, BERT | O(n²) |
tensor.multi_query_attention | PaLM | O(n²) reduced |
tensor.grouped_query_attention | Llama 2 | O(n²) reduced |
tensor.flash_attention | FlashAttention | O(n) |
tensor.sliding_window_attention | Mistral | O(n×w) |
tensor.cross_attention | Encoder-decoder | O(n×m) |
tensor.paged_attention | vLLM KV cache | O(n) paged |
6.5 FLOPs Calculation
For attention: FLOPs = 4 × B × H × S² × D
#![allow(unused)] fn main() { use lift_tensor::{TensorOp, ShapeInference}; use lift_core::types::{TensorTypeInfo, Dimension, DataType, MemoryLayout}; let q = TensorTypeInfo { shape: vec![ Dimension::Constant(1), // batch Dimension::Constant(8), // heads Dimension::Constant(2048), // seq_len Dimension::Constant(64), // head_dim ], dtype: DataType::FP32, layout: MemoryLayout::Contiguous, }; let flops = ShapeInference::compute_flops(&TensorOp::Attention, &[&q, &q, &q]); println!("Attention FLOPs: {:?}", flops); // 4 × 1 × 8 × 2048 × 2048 × 64 ≈ 8.6 billion }
7. Use Case 3 — Quantum Circuit Design and Noise Analysis
7.1 Problem
Design a quantum circuit, understand which gates your hardware supports, model noise, and estimate fidelity before executing on real devices.
7.2 Bell State in .lif
File: examples/quantum_bell.lif
#dialect quantum
module @bell_state {
func @bell(%q0: qubit, %q1: qubit) -> (qubit, qubit) {
%q2 = "quantum.h"(%q0) : (qubit) -> qubit
%q3, %q4 = "quantum.cx"(%q2, %q1) : (qubit, qubit) -> (qubit, qubit)
return %q3, %q4
}
}
7.3 Build a Circuit Programmatically
#![allow(unused)] fn main() { use lift_core::{Context, Attributes, Location}; let mut ctx = Context::new(); let qubit_ty = ctx.make_qubit_type(); let block = ctx.create_block(); let q0 = ctx.create_block_arg(block, qubit_ty); let q1 = ctx.create_block_arg(block, qubit_ty); // Hadamard on q0 let (h_op, h_res) = ctx.create_op( "quantum.h", "quantum", vec![q0], vec![qubit_ty], Attributes::new(), Location::unknown(), ); ctx.add_op_to_block(block, h_op); // CNOT on (q0', q1) let (cx_op, cx_res) = ctx.create_op( "quantum.cx", "quantum", vec![h_res[0], q1], vec![qubit_ty, qubit_ty], Attributes::new(), Location::unknown(), ); ctx.add_op_to_block(block, cx_op); lift_core::verifier::verify(&ctx).expect("Valid circuit"); }
7.4 All 48 Quantum Gates
| Category | Gates |
|---|---|
| 1Q standard | H, X, Y, Z, S, Sdg, T, Tdg, SX |
| 1Q parametric | RX, RY, RZ, P, U1, U2, U3 |
| 1Q fixed | Rx90, Rx180 |
| 2Q standard | CX, CZ, CY, SWAP, ISWAP, ECR |
| 2Q parametric | RZX, XX, YY, ZZ, CPhase, XY, CP |
| IonQ native | GPI, GPI2, MS |
| 3Q | CCX (Toffoli), CSWAP (Fredkin) |
| Multi-controlled | MCX, MCZ |
| Measurement | Measure, MeasureAll, Reset, Barrier, Init |
| Special | GlobalPhase, Delay, VirtualRZ, IfElse, ParamGate |
7.5 Hardware-Native Gate Sets
#![allow(unused)] fn main() { use lift_quantum::QuantumGate; use lift_quantum::gates::Provider; // IBM Eagle/Kyoto native: {RZ, SX, X, CX, ECR} let ibm = QuantumGate::native_basis(Provider::IbmEagle); // Rigetti native: {RZ, RX, CZ, CPhase, XY} let rigetti = QuantumGate::native_basis(Provider::Rigetti); // IonQ native: {GPI, GPI2, MS} let ionq = QuantumGate::native_basis(Provider::IonQ); // Quantinuum native: {RZ, RX, RY, ZZ} let quantinuum = QuantumGate::native_basis(Provider::Quantinuum); for gate in ibm { println!("{} ({} qubit, parametric: {}, clifford: {})", gate.op_name(), gate.num_qubits(), gate.is_parametric(), gate.is_clifford()); } }
7.6 Noise Models
Problem: Real quantum hardware introduces errors. You need to model them before execution.
#![allow(unused)] fn main() { use lift_quantum::{NoiseModel, GateNoise, CircuitNoise}; // Depolarizing noise (1Q gate error p = 0.001) let noise_1q = NoiseModel::Depolarizing { p: 0.001 }; println!("1Q fidelity: {:.6}", noise_1q.fidelity()); // 0.999000 // Thermal relaxation let thermal = NoiseModel::ThermalRelaxation { t1_us: 100.0, t2_us: 80.0, gate_time_us: 0.3, }; println!("Thermal fidelity: {:.6}", thermal.fidelity()); // Composed noise let combined = noise_1q.compose(&thermal); println!("Combined fidelity: {:.6}", combined.fidelity()); // Track noise across a full circuit let mut circuit = CircuitNoise::new(); let g1q = GateNoise::with_depolarizing(0.999, 0.02); let g2q = GateNoise::with_depolarizing(0.99, 0.3); circuit.add_gate(&g1q, false); // H (1Q) circuit.add_gate(&g2q, true); // CX (2Q) println!("Circuit fidelity: {:.6}", circuit.total_fidelity); println!("Gate count: {}, 2Q gates: {}", circuit.gate_count, circuit.two_qubit_count); println!("Meets 99% threshold: {}", circuit.meets_threshold(0.99)); }
All noise models:
| Model | Parameter | Use Case |
|---|---|---|
Ideal | — | Simulation baseline |
Depolarizing { p } | Error probability | General gate errors |
AmplitudeDamping { gamma } | Decay rate | T1 relaxation |
PhaseDamping { gamma } | Dephasing rate | T2 dephasing |
BitFlip { p } | Flip probability | Classical-like errors |
PhaseFlip { p } | Phase flip prob | Z errors |
ThermalRelaxation { t1, t2, t } | Coherence times | Realistic hardware |
Kraus { operators } | Kraus matrices | Custom channels |
Composed(vec) | Multiple models | Layered noise |
7.7 Quantum Cost Model
#![allow(unused)] fn main() { use lift_sim::cost::QuantumCostModel; // Superconducting (IBM-like): fast but lower fidelity let sc = QuantumCostModel::superconducting_default(); // gate_time_1q: 0.02μs, gate_time_2q: 0.3μs, fidelity_1q: 0.999, fidelity_2q: 0.99 // Trapped-ion (IonQ-like): slow but very high fidelity let ti = QuantumCostModel::trapped_ion_default(); // gate_time_1q: 10μs, gate_time_2q: 200μs, fidelity_1q: 0.9999, fidelity_2q: 0.999 // Neutral-atom: fast with moderate fidelity, many qubits let na = QuantumCostModel::neutral_atom_default(); // gate_time_1q: 0.5μs, gate_time_2q: 1.0μs, fidelity_1q: 0.999, fidelity_2q: 0.995 // Compare fidelity for a 100-gate circuit (80×1Q + 20×2Q) println!("Superconducting: {:.6}", sc.circuit_fidelity(80, 20)); println!("Trapped-ion: {:.6}", ti.circuit_fidelity(80, 20)); println!("Neutral-atom: {:.6}", na.circuit_fidelity(80, 20)); }
7.8 Gate Optimisation Passes
#![allow(unused)] fn main() { use lift_opt::{GateCancellation, RotationMerge, NoiseAwareSchedule, RealRouting}; use lift_core::pass::PassManager; use lift_quantum::DeviceTopology; let mut pm = PassManager::new(); pm.add_pass(Box::new(GateCancellation)); // H·H → I, X·X → I pm.add_pass(Box::new(RotationMerge)); // Rz(a)·Rz(b) → Rz(a+b) pm.add_pass(Box::new(NoiseAwareSchedule)); // schedule to minimise noise pm.add_pass(Box::new(RealRouting::new(DeviceTopology::linear(4)))); // insert real SWAPs let results = pm.run_all(&mut ctx); for (name, result) in &results { println!(" {}: {:?}", name, result); } }
| Pass | What It Does |
|---|---|
GateCancellation | Cancels adjacent inverse gates (H·H, X·X, etc.) |
RotationMerge | Merges consecutive rotations: Rz(a)·Rz(b) → Rz(a+b) |
NoiseAwareSchedule | Reorders gates to place noisy 2Q gates on high-fidelity edges |
RealRouting | Maps logical qubits to physical qubits, inserting real quantum.swap ops (BFS shortest path) |
LayoutMapping | Legacy: only annotates non-adjacent 2-qubit gates with needs_swap = true — does not insert SWAPs itself. Use RealRouting instead. |
8. Use Case 4 — Hybrid Classical-Quantum (VQE)
8.1 Problem
VQE (Variational Quantum Eigensolver) is a hybrid algorithm requiring:
- Encoding classical data into quantum states
- Running a parametrised circuit (ansatz)
- Computing gradients of quantum parameters
- Iterating with a classical optimiser
8.2 Encoding Strategies
#![allow(unused)] fn main() { use lift_hybrid::encoding::{EncodingStrategy, EncodingConfig}; // Angle encoding: 1 qubit per feature, circuit depth 1 let angle = EncodingConfig::new(EncodingStrategy::AngleEncoding, 4); println!("Qubits: {}, depth: {}", angle.num_qubits, angle.strategy.circuit_depth(4)); // 4 qubits, depth 1 // Amplitude encoding: log2(N) qubits, depth N let amp = EncodingConfig::new(EncodingStrategy::AmplitudeEncoding, 16); println!("Qubits: {}, depth: {}", amp.num_qubits, amp.strategy.circuit_depth(16)); // 4 qubits, depth 16 // IQP encoding: N qubits, depth 2N let iqp = EncodingConfig::new(EncodingStrategy::IQPEncoding, 8); println!("Qubits: {}, depth: {}", iqp.num_qubits, iqp.strategy.circuit_depth(8)); // 8 qubits, depth 16 }
| Strategy | Qubits | Depth | Best For |
|---|---|---|---|
AngleEncoding | N | 1 | Small feature spaces |
AmplitudeEncoding | log₂(N) | N | Large feature spaces |
BasisEncoding | N | 1 | Binary data |
IQPEncoding | N | 2N | Quantum advantage proofs |
HamiltonianEncoding | N | N | Physics simulations |
KernelEncoding | N | 3N | Quantum kernel methods |
8.3 Gradient Methods
#![allow(unused)] fn main() { use lift_hybrid::gradient::GradientMethod; let num_params = 20; // Parameter shift: exact, 2 evaluations per parameter let ps = GradientMethod::ParameterShift; println!("Evals: {}, exact: {}", ps.circuit_evaluations(num_params), ps.is_exact()); // 40, true // SPSA: stochastic, only 2 evaluations total let spsa = GradientMethod::SPSA; println!("Evals: {}, exact: {}", spsa.circuit_evaluations(num_params), spsa.is_exact()); // 2, false // Adjoint: exact, 1 evaluation (best for simulators) let adj = GradientMethod::Adjoint; println!("Evals: {}, exact: {}", adj.circuit_evaluations(num_params), adj.is_exact()); // 1, true }
| Method | Evaluations | Exact | Best For |
|---|---|---|---|
ParameterShift | 2N | Yes | Hardware |
FiniteDifference | N+1 | No | Quick approximation |
SPSA | 2 | No | Many parameters |
Adjoint | 1 | Yes | Simulators |
Backprop | 1 | Yes | Classical parts |
8.4 Joint Gradient (Classical + Quantum)
#![allow(unused)] fn main() { use lift_hybrid::gradient::{GradientMethod, JointGradientConfig}; let config = JointGradientConfig { classical_method: GradientMethod::Backprop, quantum_method: GradientMethod::ParameterShift, num_classical_params: 1000, num_quantum_params: 20, }; println!("Total evaluations: {}", config.total_evaluations()); // 1 (backprop) + 40 (param shift) = 41 }
8.5 VQE Pipeline in .lif
#dialect tensor
#dialect quantum
#dialect hybrid
module @vqe {
func @step(%data: tensor<1x4xf32>, %q0: qubit, %q1: qubit) -> f32 {
// 1. Encode classical data
%encoded = "hybrid.encode"(%data) : (tensor<1x4xf32>) -> tensor<1x4xf32>
// 2. Variational ansatz
%q2 = "quantum.ry"(%q0) {angle = 0.5} : (qubit) -> qubit
%q3, %q4 = "quantum.cx"(%q2, %q1) : (qubit, qubit) -> (qubit, qubit)
%q5 = "quantum.rz"(%q3) {angle = 1.2} : (qubit) -> qubit
// 3. Measure expectation value
%energy = "hybrid.measure_expectation"(%q5) : (qubit) -> f32
// 4. Compute gradient
%grad = "hybrid.parameter_shift"(%energy) : (f32) -> f32
return %energy
}
}
8.6 All 21 Hybrid Operations
| Operation | Description |
|---|---|
hybrid.encode | Encode classical data into quantum state |
hybrid.decode | Decode quantum measurement to classical |
hybrid.parameter_shift | Gradient via parameter shift rule |
hybrid.finite_difference | Gradient via finite differences |
hybrid.spsa | Stochastic parameter shift approximation |
hybrid.adjoint_diff | Gradient via adjoint differentiation |
hybrid.stochastic_param_shift | Stochastic parameter shift |
hybrid.joint_gradient | Joint classical+quantum gradient |
hybrid.classical_preprocess | Classical preprocessing step |
hybrid.quantum_postprocess | Quantum postprocessing step |
hybrid.forward | Hybrid forward pass |
hybrid.backward | Hybrid backward pass |
hybrid.vqc_layer | Variational quantum circuit layer |
hybrid.vqe_ansatz | VQE ansatz circuit |
hybrid.qaoa_layer | QAOA mixer + cost layer |
hybrid.quantum_kernel | Quantum kernel evaluation |
hybrid.gpu_to_qpu | Transfer data GPU → QPU |
hybrid.qpu_to_gpu | Transfer data QPU → GPU |
hybrid.co_execute | Co-execute classical and quantum |
hybrid.measure_expectation | Measure observable expectation |
hybrid.measure_samples | Measure and return bit-strings |
9. Use Case 5 — Model Import
9.1 Problem
You have existing models in ONNX, PyTorch FX, or OpenQASM format and want to bring them into LIFT for unified optimisation and analysis.
These three importers are skeletons today (see docs/CAPABILITIES.md): they parse the source format enough to find the top-level node list, but they do not convert a single node into a LIFT operation — you get back a valid, empty module+function. The APIs below are real and tested; the conversion they're described as doing is the v0.5 roadmap item, not today's behaviour.
9.2 ONNX Import
#![allow(unused)] fn main() { use lift_import::OnnxImporter; use lift_core::Context; let json: serde_json::Value = serde_json::from_str( &std::fs::read_to_string("model.onnx.json").unwrap() ).unwrap(); let mut ctx = Context::new(); OnnxImporter::new() .import_from_json(&mut ctx, &json) .expect("ONNX import failed"); }
import_from_json takes an existing &mut Context and a pre-parsed
serde_json::Value (not a file path, and it doesn't return a Context).
9.3 PyTorch FX Import
#![allow(unused)] fn main() { use lift_import::PyTorchFxImporter; use lift_core::Context; let json: serde_json::Value = serde_json::from_str( &std::fs::read_to_string("model_fx.json").unwrap() ).unwrap(); let mut ctx = Context::new(); PyTorchFxImporter::new() .import_from_json(&mut ctx, &json) .expect("FX import failed"); }
9.4 OpenQASM 3.0 Import
#![allow(unused)] fn main() { use lift_import::OpenQasm3Importer; use lift_core::Context; use lift_core::pass::PassManager; let source = std::fs::read_to_string("circuit.qasm").unwrap(); let mut ctx = Context::new(); OpenQasm3Importer::new() .import_from_source(&mut ctx, &source) .expect("QASM import failed"); // Optimise the (currently empty) circuit let mut pm = PassManager::new(); pm.add_pass(Box::new(lift_opt::GateCancellation)); pm.add_pass(Box::new(lift_opt::RotationMerge)); pm.run_all(&mut ctx); }
import_from_source checks for a valid OPENQASM 3/OPENQASM 2 version
header and creates an empty circuit function — it does not yet parse gates.
9.5 Import → Analyse → Compare
A common workflow: import a model, analyse it, optimise, then compare before/after:
#![allow(unused)] fn main() { use lift_sim::analysis::analyze_module; // Before optimisation let report_before = analyze_module(&ctx); println!("Before: {} ops, {} FLOPs", report_before.num_ops, report_before.total_flops); // Run passes... pm.run_all(&mut ctx); // After optimisation let report_after = analyze_module(&ctx); println!("After: {} ops, {} FLOPs", report_after.num_ops, report_after.total_flops); println!("Ops reduced: {:.1}%", (1.0 - report_after.num_ops as f64 / report_before.num_ops as f64) * 100.0); }
10. Use Case 6 — Performance Prediction
10.1 Problem
Before running a model on expensive hardware, you need to know:
- How long will it take?
- Is it compute-bound or memory-bound?
- Will it fit in GPU memory?
- How many GPUs are needed?
10.2 Roofline Model (Classical)
#![allow(unused)] fn main() { use lift_sim::analysis::analyze_module; use lift_sim::cost::CostModel; use lift_predict::roofline::predict_performance; let report = analyze_module(&ctx); // NVIDIA A100 let a100 = CostModel::a100(); let pred_a100 = predict_performance(&report, &a100); println!("=== A100 Prediction ==="); println!("Compute time: {:.4} ms", pred_a100.compute_time_ms); println!("Memory time: {:.4} ms", pred_a100.memory_time_ms); println!("Predicted: {:.4} ms", pred_a100.predicted_time_ms); println!("Arithmetic intensity: {:.2} FLOP/byte", pred_a100.arithmetic_intensity); println!("Bottleneck: {}", pred_a100.bottleneck); // "compute" or "memory" // NVIDIA H100 let h100 = CostModel::h100(); let pred_h100 = predict_performance(&report, &h100); println!("\n=== H100 Prediction ==="); println!("Predicted: {:.4} ms", pred_h100.predicted_time_ms); println!("Speedup vs A100: {:.2}x", pred_a100.predicted_time_ms / pred_h100.predicted_time_ms); }
10.3 GPU Profiles
| Profile | TFLOPS (FP16) | Memory BW (GB/s) | VRAM | TDP |
|---|---|---|---|---|
CostModel::a100() | 312 | 2,039 | 80 GB | 400W |
CostModel::h100() | 989 | 3,350 | 80 GB | 700W |
10.4 Memory Fit and Multi-GPU Planning
#![allow(unused)] fn main() { let model = CostModel::a100(); let bytes = report.total_memory_bytes; println!("Model size: {:.2} GB", bytes as f64 / 1e9); println!("Fits in 1 GPU: {}", model.fits_in_memory(bytes)); println!("GPUs needed: {}", model.num_gpus_needed(bytes)); // Arithmetic intensity analysis let ai = model.arithmetic_intensity(report.total_flops, bytes); let ridge = model.flops_per_second / (model.memory_bandwidth_gb_s * 1e9); println!("Arithmetic intensity: {:.2} FLOP/byte", ai); println!("Ridge point: {:.2} FLOP/byte", ridge); println!("Regime: {}", if ai >= ridge { "compute-bound" } else { "memory-bound" }); }
10.5 Quantum Performance Prediction
#![allow(unused)] fn main() { use lift_predict::roofline::predict_quantum; use lift_sim::quantum_sim::QuantumAnalysis; use lift_sim::cost::QuantumCostModel; let analysis = QuantumAnalysis { num_qubits_used: 10, gate_count: 200, one_qubit_gates: 150, two_qubit_gates: 50, measurements: 10, circuit_depth: 30, estimated_fidelity: 0.92, ..Default::default() // covers three_qubit_gates and noise }; let sc = QuantumCostModel::superconducting_default(); let prediction = predict_quantum(&analysis, &sc, 0.01); // 1% precision println!("Estimated fidelity: {:.6}", prediction.estimated_fidelity); println!("Circuit time: {:.2} μs", prediction.circuit_time_us); println!("Shots for 1%% precision: {}", prediction.num_shots_for_precision); println!("Total execution: {:.2} ms", prediction.total_execution_time_ms); // Compare technologies let ti = QuantumCostModel::trapped_ion_default(); let pred_ti = predict_quantum(&analysis, &ti, 0.01); println!("\nTrapped-ion fidelity: {:.6} (vs {:.6} superconducting)", pred_ti.estimated_fidelity, prediction.estimated_fidelity); println!("Trapped-ion time: {:.2} ms (vs {:.2} ms)", pred_ti.total_execution_time_ms, prediction.total_execution_time_ms); }
11. Use Case 7 — Quantised Inference
11.1 Problem
FP32 models are too large and slow for deployment. You want INT8, INT4, or FP8 for faster inference.
11.2 Quantisation Operations
| Operation | Conversion |
|---|---|
tensor.quantize | FP32 → INT8 |
tensor.dequantize | INT8 → FP32 |
tensor.quantize_int4 | FP32 → INT4 |
tensor.dequantize_int4 | INT4 → FP32 |
tensor.quantize_fp8 | FP32 → FP8 |
tensor.dequantize_fp8 | FP8 → FP32 |
11.3 Quantised Inference in .lif
#dialect tensor
module @quantised_inference {
func @forward(%x: tensor<1x784xf32>,
%w1_q: tensor<784x256xi8>,
%b1: tensor<256xf32>) -> tensor<1x256xf32> {
// Dequantize INT8 weights to FP32
%w1 = "tensor.dequantize"(%w1_q) : (tensor<784x256xi8>) -> tensor<784x256xf32>
// Compute in FP32
%h1 = "tensor.matmul"(%x, %w1) : (tensor<1x784xf32>, tensor<784x256xf32>) -> tensor<1x256xf32>
%h2 = "tensor.add"(%h1, %b1) : (tensor<1x256xf32>, tensor<256xf32>) -> tensor<1x256xf32>
%out = "tensor.relu"(%h2) : (tensor<1x256xf32>) -> tensor<1x256xf32>
return %out
}
}
11.4 Automatic Quantisation Pass
The QuantisationPass annotates ops that are safe to quantise:
#![allow(unused)] fn main() { use lift_opt::QuantisationPass; use lift_core::pass::PassManager; let mut pm = PassManager::new(); pm.add_pass(Box::new(QuantisationPass)); pm.run_all(&mut ctx); }
11.5 Memory Savings
| Data Type | Bits | Size vs FP32 | Use Case |
|---|---|---|---|
| FP32 | 32 | 1× baseline | Training |
| FP16 / BF16 | 16 | 0.5× | Mixed-precision training |
| FP8 (E4M3) | 8 | 0.25× | H100 inference |
| INT8 | 8 | 0.25× | Server inference |
| INT4 | 4 | 0.125× | Edge/mobile inference |
| INT2 | 2 | 0.0625× | Extreme compression |
11.6 FP8 Formats
LIFT supports both FP8 variants:
#![allow(unused)] fn main() { use lift_tensor::ops::Fp8Format; // E4M3: 4 exponent, 3 mantissa — higher precision, smaller range // Best for: weights and activations in forward pass let e4m3 = Fp8Format::E4M3; // E5M2: 5 exponent, 2 mantissa — lower precision, larger range // Best for: gradients in backward pass let e5m2 = Fp8Format::E5M2; }
12. Use Case 8 — Backend Export (LLVM, ONNX, QASM)
12.1 Problem
After optimisation, you need to compile the IR to executable code for GPU/CPU or quantum hardware.
12.2 Export to LLVM IR
Skeleton today (see docs/CAPABILITIES.md): the exporter emits function signatures with each tensor op as an LLVM comment (
; tensor.matmul), not a real computation — no cuBLAS/cuDNN calls, no memory management.clang/llcwill happily compile the output, but the resulting binary does nothing; it isn't yet a path to a working executable.
#![allow(unused)] fn main() { use lift_export::LlvmExporter; let exporter = LlvmExporter::new(); let llvm_ir = exporter.export(&ctx).expect("LLVM export failed"); std::fs::write("output.ll", &llvm_ir).unwrap(); println!("Written {} bytes of LLVM IR", llvm_ir.len()); }
The output is syntactically valid LLVM IR, so tooling accepts it:
# Compiles cleanly — but runs as a no-op today, see the note above
clang -O3 output.ll -o model
# Or to object file
llc -O3 output.ll -filetype=obj -o model.o
12.3 Export to ONNX
#![allow(unused)] fn main() { use lift_export::OnnxExporter; let exporter = OnnxExporter::new(); let onnx = exporter.export(&ctx).expect("ONNX export failed"); std::fs::write("model.onnx", &onnx).unwrap(); // JSON format also available let onnx_json = exporter.export_json(&ctx).expect("ONNX JSON export failed"); std::fs::write("model_onnx.json", &onnx_json).unwrap(); }
The output is ONNX protobuf text format at opset version 21 — human-readable
and diffable, using the same operator set (standard ops plus Microsoft
extensions for attention/MoE/fused ops) that PyTorch, TensorFlow/tf2onnx,
TensorRT, and ONNX Runtime all understand. Most of those tools load the
binary protobuf .onnx format by default, though; export_json gives you
JSON, and text-to-binary conversion (e.g. via onnx.load+save in Python,
or protoc --encode) is a separate step this exporter doesn't do yet.
Key ONNX op mappings:
| LIFT Operation | ONNX Operator | Domain |
|---|---|---|
tensor.matmul | MatMul | standard |
tensor.linear | Gemm | standard |
tensor.relu | Relu | standard |
tensor.gelu | Gelu | standard |
tensor.softmax | Softmax | standard |
tensor.layernorm | LayerNormalization | standard |
tensor.rmsnorm | SimplifiedLayerNormalization | com.microsoft |
tensor.conv2d | Conv | standard |
tensor.attention | Attention | com.microsoft |
tensor.flash_attention | MultiHeadAttention | com.microsoft |
tensor.grouped_query_attention | GroupQueryAttention | com.microsoft |
tensor.quantize | QuantizeLinear | standard |
tensor.dequantize | DequantizeLinear | standard |
tensor.moe_dispatch | MoE | com.microsoft |
tensor.fused_matmul_bias_relu | FusedMatMul | com.microsoft |
| + 55 more operations |
12.4 Export to OpenQASM 3.0
#![allow(unused)] fn main() { use lift_export::QasmExporter; let exporter = QasmExporter::new(); let qasm = exporter.export(&ctx).expect("QASM export failed"); std::fs::write("circuit.qasm", &qasm).unwrap(); }
The output is standard OpenQASM 3.0 executable on:
- IBM Quantum (via Qiskit)
- Rigetti (via pyQuil)
- IonQ (via native API)
- Quantinuum (via TKET)
- Any OpenQASM 3.0 compatible platform
12.5 Full Export Pipeline
#![allow(unused)] fn main() { use lift_core::printer::print_ir; // Print human-readable IR (for debugging) let ir_text = print_ir(&ctx); std::fs::write("debug.lif", &ir_text).unwrap(); // Export to LLVM (for tensor/classical ops) let llvm = LlvmExporter::new().export(&ctx).expect("LLVM failed"); std::fs::write("model.ll", &llvm).unwrap(); // Export to ONNX (for PyTorch/TensorFlow/TensorRT interop) let onnx = OnnxExporter::new().export(&ctx).expect("ONNX failed"); std::fs::write("model.onnx", &onnx).unwrap(); // Export to QASM (for quantum ops) let qasm = QasmExporter::new().export(&ctx).expect("QASM failed"); std::fs::write("circuit.qasm", &qasm).unwrap(); }
13. Use Case 9 — Energy and Carbon Estimation
13.1 Problem
AI training and inference consume significant energy. You want to estimate the environmental impact before committing resources.
13.2 Classical Energy Model
#![allow(unused)] fn main() { use lift_sim::cost::{CostModel, EnergyModel}; use lift_sim::analysis::analyze_module; use lift_predict::roofline::predict_performance; let report = analyze_module(&ctx); let cost = CostModel::h100(); let prediction = predict_performance(&report, &cost); let energy = EnergyModel::h100(); // Single inference let joules = energy.energy_joules(prediction.predicted_time_ms, 1); let kwh = energy.energy_kwh(prediction.predicted_time_ms, 1); let co2 = energy.carbon_grams(prediction.predicted_time_ms, 1); println!("Single inference:"); println!(" Energy: {:.4} J ({:.8} kWh)", joules, kwh); println!(" CO₂: {:.6} g", co2); // Training: 8 GPUs for 72 hours let train_ms = 72.0 * 3600.0 * 1000.0; let train_kwh = energy.energy_kwh(train_ms, 8); let train_co2_kg = energy.carbon_grams(train_ms, 8) / 1000.0; println!("\nTraining (8× H100, 72h):"); println!(" Energy: {:.2} kWh", train_kwh); println!(" CO₂: {:.2} kg", train_co2_kg); println!(" Equivalent to: {:.0} km driven", train_co2_kg / 0.21); }
13.3 Energy Profiles
| Profile | GPU TDP | CPU TDP | Cooling PUE | CO₂ (g/kWh) |
|---|---|---|---|---|
EnergyModel::a100() | 400W | 250W | 1.1 | 400 (world avg) |
EnergyModel::h100() | 700W | 350W | 1.1 | 400 (world avg) |
13.4 Quantum Energy Estimation
#![allow(unused)] fn main() { let energy = EnergyModel::h100(); // Quantum circuit: dominated by cryogenic cooling let circuit_time_us = 100.0; let num_qubits = 127; let quantum_joules = energy.quantum_energy_joules(circuit_time_us, num_qubits); println!("Quantum energy: {:.4} J", quantum_joules); println!(" Cryogenics: ~25 kW (dilution refrigerator)"); println!(" Control electronics: ~{:.0} W ({} qubits × 10W)", num_qubits as f64 * 10.0, num_qubits); }
13.5 Compare Classical vs Quantum Energy
#![allow(unused)] fn main() { // Classical: matmul 1000×1000 on H100 let classical_time_ms = cost.compute_time_ms(2 * 1000 * 1000 * 1000); let classical_j = energy.energy_joules(classical_time_ms, 1); // Quantum: 100-gate circuit let quantum_j = energy.quantum_energy_joules(100.0, 50); println!("Classical (1000×1000 matmul): {:.4} J", classical_j); println!("Quantum (100-gate circuit): {:.4} J", quantum_j); println!("Note: Quantum energy is dominated by cryogenic overhead,"); println!("not by the computation itself."); }
14. Use Case 10 — Device Topology and Routing
14.1 Problem
Quantum hardware has limited connectivity — not all qubits can directly interact. Two-qubit gates between non-adjacent qubits require SWAP operations, increasing circuit depth and noise.
14.2 Built-in Topologies
#![allow(unused)] fn main() { use lift_quantum::DeviceTopology; // Linear chain (nearest-neighbour) let linear = DeviceTopology::linear(10); println!("Linear: {} qubits, {} edges, diameter {}", linear.num_qubits, linear.edges.len(), linear.diameter()); // 2D Grid (superconducting chips) let grid = DeviceTopology::grid(4, 4); println!("Grid 4×4: {} qubits, avg connectivity {:.2}", grid.num_qubits, grid.avg_connectivity()); // IBM Heavy-hex (Eagle/Heron processors) let heavy_hex = DeviceTopology::heavy_hex(127); println!("Heavy-hex: {} qubits, {} edges", heavy_hex.num_qubits, heavy_hex.edges.len()); // All-to-all (trapped-ion systems) let ion = DeviceTopology::all_to_all(32); println!("All-to-all: {} qubits, {} edges, diameter {}", ion.num_qubits, ion.edges.len(), ion.diameter()); // Binary tree let tree = DeviceTopology::tree(15); // Custom topology let custom = DeviceTopology::custom("my_chip", &[(0,1), (1,2), (2,3), (0,3), (1,3)], 0.995); }
| Topology | Constructor | Typical Hardware |
|---|---|---|
| Linear | linear(n) | Simple chains |
| Grid | grid(rows, cols) | Google Sycamore |
| Heavy-hex | heavy_hex(n) | IBM Eagle/Heron |
| All-to-all | all_to_all(n) | IonQ, Quantinuum |
| Tree | tree(n) | Hierarchical architectures |
| Custom | custom(name, edges, fidelity) | Any device |
14.3 Routing and SWAP Cost
#![allow(unused)] fn main() { let topo = DeviceTopology::grid(5, 5); // Are two qubits directly connected? println!("0↔1 connected: {}", topo.are_connected(0, 1)); // true println!("0↔6 connected: {}", topo.are_connected(0, 6)); // false // Find shortest path between qubits if let Some(path) = topo.shortest_path(0, 24) { println!("Path 0→24: {:?}", path); println!("SWAPs needed: {}", path.len() - 2); } // Number of SWAPs between any two qubits let swaps = topo.swap_distance(0, 24); println!("SWAP distance 0→24: {:?}", swaps); // Neighbours of a qubit println!("Neighbours of qubit 12: {:?}", topo.neighbors(12)); // Graph metrics println!("Diameter: {}", topo.diameter()); println!("Avg connectivity: {:.2}", topo.avg_connectivity()); }
14.4 Real Routing Pass
The RealRouting pass inserts real quantum.swap operations so every
2-qubit gate ends up on connected physical qubits, using BFS shortest paths
over your device's topology:
#![allow(unused)] fn main() { use lift_opt::RealRouting; use lift_core::pass::PassManager; let mut pm = PassManager::new(); pm.add_pass(Box::new(RealRouting::new(DeviceTopology::grid(5, 5)))); pm.run_all(&mut ctx); }
(LayoutMapping is an older, annotation-only pass — it flags non-adjacent
gates with needs_swap = true but never inserts a SWAP itself. RealRouting
does the actual routing and supersedes it.)
15. Use Case 11 — Diffusion and GNN Models
15.1 Diffusion Models (Stable Diffusion)
Problem: Diffusion models use UNet architectures with timestep conditioning and cross-attention. Standard tensor frameworks lack first-class support.
#dialect tensor
module @unet_step {
func @denoise(%x: tensor<1x4x64x64xf32>, %t: tensor<1xf32>,
%context: tensor<1x77x768xf32>) -> tensor<1x4x64x64xf32> {
// Timestep embedding
%t_emb = "tensor.timestep_embedding"(%t)
: (tensor<1xf32>) -> tensor<1x320xf32>
// UNet down block (conv + attention)
%d1 = "tensor.unet_down_block"(%x, %t_emb)
: (tensor<1x4x64x64xf32>, tensor<1x320xf32>) -> tensor<1x320x32x32xf32>
// Cross-attention with text context
%attn = "tensor.cross_attention"(%d1, %context, %context)
: (tensor<1x320x32x32xf32>, tensor<1x77x768xf32>, tensor<1x77x768xf32>)
-> tensor<1x320x32x32xf32>
// UNet up block (transpose conv + skip connections)
%u1 = "tensor.unet_up_block"(%attn, %t_emb)
: (tensor<1x320x32x32xf32>, tensor<1x320xf32>) -> tensor<1x4x64x64xf32>
return %u1
}
}
Diffusion-specific ops:
| Operation | Description |
|---|---|
tensor.timestep_embedding | Sinusoidal timestep encoding |
tensor.unet_down_block | Downsample with residual + attention |
tensor.unet_up_block | Upsample with skip connections |
15.2 Graph Neural Networks (GNN)
Problem: GNNs operate on irregular graph structures. Message passing between nodes requires specialised aggregation operations.
#dialect tensor
module @gcn {
func @forward(%nodes: tensor<100x64xf32>,
%edges: tensor<2x500xi64>,
%w: tensor<64x32xf32>) -> tensor<100x32xf32> {
// Message passing: aggregate neighbour features
%msg = "tensor.gnn_message_passing"(%nodes, %edges)
{aggregation = "mean"}
: (tensor<100x64xf32>, tensor<2x500xi64>) -> tensor<100x64xf32>
// Linear transform
%h = "tensor.matmul"(%msg, %w)
: (tensor<100x64xf32>, tensor<64x32xf32>) -> tensor<100x32xf32>
%out = "tensor.relu"(%h)
: (tensor<100x32xf32>) -> tensor<100x32xf32>
return %out
}
func @graph_classify(%nodes: tensor<100x32xf32>) -> tensor<1x32xf32> {
// Global pooling: graph-level representation
%graph = "tensor.gnn_global_pooling"(%nodes)
{aggregation = "mean"}
: (tensor<100x32xf32>) -> tensor<1x32xf32>
return %graph
}
}
GNN operations:
| Operation | Description | Aggregation |
|---|---|---|
tensor.gnn_message_passing | Neighbour feature aggregation | sum, mean, max, min |
tensor.gnn_global_pooling | Graph-level readout | sum, mean, max |
16. Use Case 12 — Budget-Constrained Compilation
16.1 Problem
You have hard resource constraints: maximum FLOPs, memory, time, or minimum quantum fidelity. You want to enforce these during compilation.
16.2 Static Budget
#![allow(unused)] fn main() { use lift_sim::cost::Budget; use lift_sim::analysis::analyze_module; let budget = Budget { max_flops: Some(1_000_000_000), // 1 GFLOP max_memory_bytes: Some(1_073_741_824), // 1 GB max_time_ms: Some(100.0), // 100 ms min_fidelity: Some(0.99), // 99% fidelity max_circuit_depth: Some(100), }; let report = analyze_module(&ctx); match budget.check_flops(report.total_flops) { Ok(()) => println!("FLOP budget OK"), Err(e) => println!("WARNING: {}", e), } match budget.check_memory(report.total_memory_bytes) { Ok(()) => println!("Memory budget OK"), Err(e) => println!("WARNING: {}", e), } }
16.3 Reactive Budget (Dynamic Tracking)
For iterative algorithms (VQE, QAOA) where resources are consumed over time:
#![allow(unused)] fn main() { use lift_sim::cost::{Budget, ReactiveBudget}; let budget = Budget { max_flops: Some(10_000_000_000), // 10 GFLOP max_memory_bytes: Some(4_294_967_296), // 4 GB max_time_ms: Some(5000.0), // 5 seconds min_fidelity: Some(0.90), // 90% fidelity max_circuit_depth: None, }; let mut tracker = ReactiveBudget::new(budget); for iteration in 0..100 { // Simulate consuming resources each iteration tracker.consume( 100_000_000, // 100M FLOPs 500_000_000, // 500MB memory 50.0, // 50ms 0.999, // fidelity factor ); match tracker.check_remaining() { Ok(()) => { let util = tracker.utilisation(); if iteration % 10 == 0 { println!("Iter {}: FLOP {:.0}%, time {:.0}%", iteration, util.flop_ratio.unwrap_or(0.0) * 100.0, util.time_ratio.unwrap_or(0.0) * 100.0, ); } } Err(e) => { println!("Budget exceeded at iteration {}: {}", iteration, e); break; } } } // Query remaining budget if let Some(remaining) = tracker.remaining_flops() { println!("Remaining FLOPs: {}", remaining); } if let Some(remaining) = tracker.remaining_time_ms() { println!("Remaining time: {:.2} ms", remaining); } }
17. Use Case 13 — End-to-End Pipelines
17.1 AI Pipeline: Import → Verify → Optimise → Predict → Export
#![allow(unused)] fn main() { use lift_import::OnnxImporter; use lift_core::{Context, verifier, pass::PassManager}; use lift_sim::analysis::analyze_module; use lift_sim::cost::CostModel; use lift_predict::roofline::predict_performance; use lift_export::LlvmExporter; // ── 1. Import (skeleton today — produces an empty module+function, // see docs/CAPABILITIES.md) ── let onnx_json: serde_json::Value = serde_json::from_str( &std::fs::read_to_string("model.onnx.json").unwrap() ).unwrap(); let mut ctx = Context::new(); OnnxImporter::new() .import_from_json(&mut ctx, &onnx_json) .expect("Import failed"); // ── 2. Verify ── verifier::verify(&ctx).expect("Verification failed"); // ── 3. Analyse (before) ── let before = analyze_module(&ctx); // ── 4. Optimise ── let mut pm = PassManager::new(); pm.add_pass(Box::new(lift_opt::Canonicalize)); pm.add_pass(Box::new(lift_opt::ConstantFolding)); pm.add_pass(Box::new(lift_opt::TensorFusion)); pm.add_pass(Box::new(lift_opt::FlashAttentionPass::default())); pm.add_pass(Box::new(lift_opt::CommonSubexprElimination)); pm.add_pass(Box::new(lift_opt::DeadCodeElimination)); for (name, result) in pm.run_all(&mut ctx) { println!(" {}: {:?}", name, result); } // ── 5. Analyse (after) ── let after = analyze_module(&ctx); println!("Ops: {} → {} ({:.1}% reduction)", before.num_ops, after.num_ops, (1.0 - after.num_ops as f64 / before.num_ops as f64) * 100.0); // ── 6. Predict ── let h100 = CostModel::h100(); let prediction = predict_performance(&after, &h100); println!("H100: {:.4} ms ({}-bound)", prediction.predicted_time_ms, prediction.bottleneck); // ── 7. Export ── let llvm = LlvmExporter::new().export(&ctx).expect("Export failed"); std::fs::write("model.ll", &llvm).unwrap(); println!("Exported {} bytes of LLVM IR", llvm.len()); }
17.2 Quantum Pipeline: Parse → Verify → Optimise → Predict → Export
#![allow(unused)] fn main() { use lift_ast::{Lexer, Parser, IrBuilder}; use lift_core::{Context, verifier, pass::PassManager}; use lift_quantum::DeviceTopology; use lift_sim::cost::QuantumCostModel; use lift_export::QasmExporter; // ── 1. Parse ── let source = std::fs::read_to_string("circuit.lif").unwrap(); let tokens = Lexer::new(&source).tokenize().to_vec(); let program = Parser::new(tokens).parse().unwrap(); let mut ctx = Context::new(); IrBuilder::new().build_program(&mut ctx, &program).unwrap(); // ── 2. Verify (SSA + linearity) ── verifier::verify(&ctx).expect("Circuit verification failed"); // ── 3. Optimise ── let mut pm = PassManager::new(); pm.add_pass(Box::new(lift_opt::GateCancellation)); pm.add_pass(Box::new(lift_opt::RotationMerge)); pm.add_pass(Box::new(lift_opt::NoiseAwareSchedule)); pm.add_pass(Box::new(lift_opt::RealRouting::new(DeviceTopology::linear(8)))); for (name, result) in pm.run_all(&mut ctx) { println!(" {}: {:?}", name, result); } // ── 4. Predict fidelity ── let sc = QuantumCostModel::superconducting_default(); let fidelity = sc.circuit_fidelity(30, 10); // 30×1Q + 10×2Q println!("Predicted fidelity: {:.6}", fidelity); // ── 5. Export to OpenQASM 3.0 ── let qasm = QasmExporter::new().export(&ctx).unwrap(); std::fs::write("circuit.qasm", &qasm).unwrap(); }
17.3 Hybrid Pipeline: VQE with Energy Estimation
#![allow(unused)] fn main() { use lift_core::{Context, Attributes, Location}; use lift_sim::cost::{CostModel, QuantumCostModel, EnergyModel, Budget, ReactiveBudget}; use lift_hybrid::gradient::GradientMethod; use lift_hybrid::encoding::{EncodingStrategy, EncodingConfig}; // ── Setup ── let encoding = EncodingConfig::new(EncodingStrategy::AngleEncoding, 4); let gradient = GradientMethod::ParameterShift; let num_params = 12; println!("Encoding: {} qubits, depth {}", encoding.num_qubits, encoding.strategy.circuit_depth(4)); println!("Gradient: {} evaluations per iteration", gradient.circuit_evaluations(num_params)); // ── Budget ── let budget = Budget { max_flops: None, max_memory_bytes: None, max_time_ms: Some(60_000.0), // 60 seconds min_fidelity: Some(0.80), max_circuit_depth: None, }; let mut tracker = ReactiveBudget::new(budget); // ── Cost models ── let qcm = QuantumCostModel::superconducting_default(); let energy = EnergyModel::a100(); // ── VQE loop ── let evals_per_iter = gradient.circuit_evaluations(num_params); let time_per_eval_us = qcm.circuit_time_us(10, 5, 1, 8); let fidelity_per_eval = qcm.circuit_fidelity(10, 5); for iter in 0..100 { let iter_time_ms = (evals_per_iter as f64 * time_per_eval_us) / 1000.0; tracker.consume(0, 0, iter_time_ms, fidelity_per_eval); if let Err(e) = tracker.check_remaining() { println!("Stopped at iteration {}: {}", iter, e); break; } } println!("Total time: {:.2} ms", tracker.elapsed_ms); println!("Final fidelity: {:.6}", tracker.current_fidelity); let total_energy_j = energy.quantum_energy_joules( tracker.elapsed_ms * 1000.0, encoding.num_qubits); println!("Energy: {:.2} J", total_energy_j); }
18. Configuration with .lith Files
18.1 Overview
LIFT uses .lith configuration files to control the compilation pipeline. The format is INI-like with [section] headers and key = value pairs. Comments use # or //.
18.2 Full .lith Example
# my_project.lith — LIFT compilation configuration
[target]
backend = "llvm" # llvm | qasm
device = "H100" # A100 | H100
precision = "fp16" # fp32 | fp16 | bf16 | fp8 | int8
[budget]
max_flops = 1000000000000 # 1 TFLOP
max_memory_bytes = 80000000000 # 80 GB
max_time_ms = 100.0 # 100 ms
min_fidelity = 0.95 # 95% quantum fidelity
[optimisation]
level = O3 # O0 | O1 | O2 | O3
max_iterations = 20
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = true
[quantum]
topology = "heavy_hex"
num_qubits = 127
shots = 4096
error_mitigation = "zne" # zero-noise extrapolation
18.3 Configuration Sections
| Section | Keys | Description |
|---|---|---|
| [target] | backend, device, precision | Compilation target |
| [budget] | max_flops, max_memory_bytes, max_time_ms, min_fidelity, max_circuit_depth | Resource constraints |
| [optimisation] | level, passes, disabled_passes, max_iterations | Pass pipeline control |
| [simulation] | shape_propagation, flop_counting, memory_analysis, noise_simulation | Analysis toggles |
| [quantum] | topology, num_qubits, error_mitigation, shots | Quantum device settings |
18.4 Optimisation Levels
| Level | Passes |
|---|---|
| O0 | No optimisation |
| O1 | Canonicalize, constant folding, DCE |
| O2 (default) | O1 + CSE, tensor fusion |
| O3 | All 13 passes — O2 + FlashAttention, quantisation, gate cancellation, rotation merge, noise-aware schedule, layout mapping, gate decomposition, real routing |
18.5 Loading Configuration Programmatically
#![allow(unused)] fn main() { use lift_config::{ConfigParser, LithConfig}; // From .lith file let source = std::fs::read_to_string("project.lith").unwrap(); let config = ConfigParser::new().parse(&source).expect("Config parse error"); println!("Backend: {}", config.target.backend); println!("Opt level: {:?}", config.optimisation.level); if let Some(q) = &config.quantum { println!("Quantum: {} qubits, {} topology", q.num_qubits, q.topology); } // From JSON let json = r#"{"target":{"backend":"qasm","device":null,"precision":"fp32"}, "budget":{"max_flops":null,"max_memory_bytes":null, "max_time_ms":null,"min_fidelity":0.99, "max_circuit_depth":null}, "optimisation":{"level":"O2","passes":["canonicalize","dce"], "disabled_passes":[],"max_iterations":10}, "simulation":{"enable_shape_propagation":true, "enable_flop_counting":true, "enable_memory_analysis":true, "enable_noise_simulation":true}, "quantum":{"topology":"grid","num_qubits":27, "error_mitigation":null,"shots":4096}}"#; let config = ConfigParser::new().parse_json(json).expect("JSON parse error"); }
18.6 Default Configuration
When no .lith file is provided, LIFT uses these defaults:
#![allow(unused)] fn main() { let config = LithConfig::default(); // target.backend = "llvm" // target.precision = "fp32" // optimisation.level = O2 // optimisation.passes = ["canonicalize", "constant-folding", "dce", "tensor-fusion"] // simulation: all enabled // quantum: None }
19. CLI Reference
19.1 Installation
After building with cargo build --release, the binary is at
target/release/lift-cli (the crate's package name — there's no [[bin]]
override to shorten it to lift, and the same is true after
cargo install lift-cli). Every lift <command> example below is exactly
what you'd run, substituting lift-cli for lift — or
cargo run --release -p lift-cli -- <command> from a source checkout, which
is what examples/validate_all.sh and this repo's other docs use.
19.2 Commands
lift verify — Verify a .lif file
Checks SSA invariants, type correctness, and qubit linearity.
lift verify examples/tensor_mlp.lif
Output:
Verification passed: examples/tensor_mlp.lif
Values: 11
Operations: 7
Blocks: 1
Regions: 1
# Verbose mode
lift -v verify examples/quantum_bell.lif
lift analyse — Analyse resource usage
Computes FLOPs, memory, gate counts, and fidelity estimates.
lift analyse examples/tensor_mlp.lif
Output:
=== LIFT Analysis Report ===
File: examples/tensor_mlp.lif
Operations: 7
Tensor ops: 6
Quantum ops: 0
Hybrid ops: 0
Compute:
Total FLOPs: 407.10 KFLOP
Total memory: 802.22 KiB
Peak memory: 801.22 KiB
Op breakdown:
tensor.matmul: 2
tensor.add: 2
core.return: 1
tensor.softmax: 1
tensor.relu: 1
JSON output:
lift analyse examples/tensor_mlp.lif --format json
lift print — Print human-readable IR
lift print examples/quantum_bell.lif
Output:
module @bell_state {
func @bell(%v0: qubit, %v1: qubit) -> (qubit, qubit) {
%v2 = "quantum.h"(%v0) : (qubit) -> qubit
%v3, %v4 = "quantum.cx"(%v2, %v1) : (qubit, qubit) -> (qubit, qubit)
"core.return"(%v3, %v4) : (qubit, qubit) -> ()
}
}
(The entry block's arguments are printed once, in the function signature —
not repeated as a separate ^bb0(...): label, since the .lif grammar has
no block-label syntax.)
lift optimise — Run optimisation passes
# Default passes (O2)
lift optimise examples/tensor_mlp.lif -o optimised.lif
# With custom config
lift optimise examples/tensor_mlp.lif --config project.lith -o optimised.lif
Output:
Optimisation results:
canonicalize -> unchanged
constant-folding -> unchanged
dce -> unchanged
common-subexpr-elimination -> unchanged
tensor-fusion -> changed
Output written to: optimised.lif
(O2, the default level, runs 5 passes: canonicalize, constant-folding, dce, cse, and tensor-fusion — see 18.4.)
lift predict — Predict performance
# Predict on A100 (default)
lift predict examples/tensor_mlp.lif
# Predict on H100
lift predict examples/tensor_mlp.lif --device h100
Output:
=== LIFT Performance Prediction ===
Device: H100
Compute time: 0.0000 ms
Memory time: 0.0002 ms
Predicted time: 0.0002 ms
Arithmetic intensity: 0.50 FLOP/byte
Bottleneck: memory
lift export — Export to backend
# Export to LLVM IR
lift export examples/tensor_mlp.lif --backend llvm -o model.ll
# Export to ONNX (opset 21)
lift export examples/tensor_mlp.lif --backend onnx -o model.onnx
# Export to OpenQASM 3.0
lift export examples/quantum_bell.lif --backend qasm -o circuit.qasm
# Print to stdout
lift export examples/quantum_bell.lif --backend qasm
19.3 Global Flags
| Flag | Description |
|---|---|
-v, --verbose | Enable debug-level logging |
--version | Print version |
--help | Print help |
20. Programmatic Model Generation
20.1 Using lift-codegen
The lift-codegen binary generates models from Rust code and exports all formats automatically:
cargo run --bin lift-codegen
Output:
╔═════════════════════════════════════════════════════════════╗
║ LIFT Code Generator — Models from Rust ║
╚═════════════════════════════════════════════════════════════╝
── Generating Phi-3-mini ──
[WRITE] examples/phi3_generated.lif (2703 bytes)
[VERIFY] OK — 20 ops, 32 values
[ANALYSE] FLOPs=54.43 GFLOP, Memory=1.23 GiB, Ops=20
[OPTIMISE] No changes
[EXPORT] examples/phi3_generated.ll (5918 bytes)
[EXPORT] examples/phi3_generated.onnx (10563 bytes)
── Generating VQE Circuit ──
[WRITE] examples/vqe_generated.lif (345 bytes)
[VERIFY] OK — 5 ops, 6 values
[ANALYSE] FLOPs=0 FLOP, Memory=0 B, Ops=5
[OPTIMISE] No changes
[EXPORT] examples/vqe_generated.ll (3248 bytes)
[EXPORT] examples/vqe_generated.onnx (2023 bytes)
[EXPORT] examples/vqe_generated.qasm (120 bytes)
20.2 ModelBuilder API
Define models programmatically without writing .lif files:
#![allow(unused)] fn main() { use lift_core::model_builder::{ModelBuilder, tensor, tensor_2d, DataType}; let model = ModelBuilder::new("my_mlp") .function("forward") .param("x", tensor(&[1, 784], DataType::FP32)) .param("w1", tensor_2d(784, 256, DataType::FP32)) .param("b1", tensor(&[256], DataType::FP32)) .op("tensor.matmul", &["x", "w1"], "h1", tensor(&[1, 256], DataType::FP32)) .op("tensor.add", &["h1", "b1"], "h2", tensor(&[1, 256], DataType::FP32)) .op("tensor.relu", &["h2"], "out", tensor(&[1, 256], DataType::FP32)) .returns("out") .done(); // Write .lif source (parseable by lift-cli) model.write_lif("my_mlp.lif").unwrap(); // Build IR context for full pipeline let ctx = model.build_context(); lift_core::verifier::verify(&ctx).unwrap(); // Export to all backends let llvm = lift_export::LlvmExporter::new().export(&ctx).unwrap(); let onnx = lift_export::OnnxExporter::new().export(&ctx).unwrap(); std::fs::write("my_mlp.ll", &llvm).unwrap(); std::fs::write("my_mlp.onnx", &onnx).unwrap(); }
21. Complete API Reference
21.1 Crate Overview
| Crate | Purpose |
|---|---|
| lift-core | IR foundation: Context, types, values, operations, blocks, regions, verifier, printer, pass manager |
| lift-ast | Lexer, parser, IR builder for .lif files |
| lift-tensor | Tensor operations (110), shape inference, FLOPs computation |
| lift-quantum | Quantum gates (48), noise models, topology, QEC codes, Kraus channels |
| lift-hybrid | Hybrid operations (21), encoding strategies, gradient methods |
| lift-opt | Optimisation passes (13): canonicalize, fusion, FlashAttention, gate cancellation, gate decomposition, real routing, etc. |
| lift-sim | Cost models (GPU + QPU), analysis reports, energy models, budgets |
| lift-predict | Roofline prediction (classical), quantum prediction (fidelity + shots) |
| lift-import | Importers: ONNX, PyTorch FX, OpenQASM 3.0 |
| lift-export | Exporters: LLVM IR, ONNX (opset 21), OpenQASM 3.0 |
| lift-config | .lith configuration parser |
| lift-cli | Command-line interface |
| lift-codegen | Programmatic model generation binary |
21.2 lift-core API
Context — central IR container:
| Method | Description |
|---|---|
Context::new() | Create empty IR context |
ctx.intern_string(s) → StringId | Intern a string |
ctx.resolve_string(id) → &str | Resolve interned string |
ctx.intern_type(ty) → TypeId | Intern a type |
ctx.resolve_type(id) → &CoreType | Resolve interned type |
ctx.make_integer_type(bits, signed) → TypeId | Create integer type |
ctx.make_float_type(bits) → TypeId | Create float type |
ctx.make_boolean_type() → TypeId | Create boolean type |
ctx.make_tensor_type(shape, dtype, layout) → TypeId | Create tensor type |
ctx.make_qubit_type() → TypeId | Create qubit type |
ctx.make_bit_type() → TypeId | Create classical bit type |
ctx.make_void_type() → TypeId | Create void type |
ctx.make_index_type() → TypeId | Create index type |
ctx.create_block() → BlockKey | Create a new block |
ctx.create_block_arg(block, ty) → ValueKey | Add block argument |
ctx.create_op(name, dialect, inputs, types, attrs, loc) → (OpKey, Vec<ValueKey>) | Create operation |
ctx.add_op_to_block(block, op) | Add op to block |
ctx.create_region() → RegionKey | Create a region |
ctx.create_module(name) → usize | Create a module |
ctx.snapshot() | Snapshot context state |
Verifier:
| Function | Description |
|---|---|
verifier::verify(&ctx) → Result<(), Vec<VerifyError>> | Verify the full IR |
Printer:
| Function | Description |
|---|---|
printer::print_ir(&ctx) → String | Print IR as text |
Pass Manager:
| Method | Description |
|---|---|
PassManager::new() | Create pass manager |
pm.add_pass(Box<dyn Pass>) | Register a pass |
pm.run_all(&mut ctx) → Vec<(String, PassResult)> | Run all passes |
Types:
| Type | Variants |
|---|---|
CoreType | Integer, Float, Boolean, Tuple, Function, Opaque, Void, Index |
TypeData | None, Tensor(TensorTypeInfo), Qubit, ClassicalBit, Hamiltonian, QuantumState |
DataType | FP32, FP16, BF16, FP64, INT8, INT16, INT32, INT64, UINT8, Bool |
Dimension | Constant(usize), Dynamic |
MemoryLayout | Contiguous, Strided, Blocked |
Attributes:
| Type | Variants |
|---|---|
Attribute | Integer(i64), Float(f64), String(StringId), Bool(bool), Type(TypeId), Array(Vec), Dict(HashMap) |
Attributes | .set(key, attr), .get(key), .get_integer(key), .get_float(key), .get_bool(key) |
21.3 lift-tensor API
| Item | Description |
|---|---|
TensorOp enum | 110 tensor operations |
TensorOp::name() → &str | Get string name |
TensorOp::from_name(s) → Option<TensorOp> | Parse from string |
TensorOp::num_inputs() → (usize, usize) | Min/max input count |
TensorOp::flops_formula() → &str | Theoretical FLOPs formula |
TensorOp::is_zero_flop() → bool | True for shape-only ops |
TensorOp::is_activation() → bool | True for activation ops |
TensorOp::is_attention() → bool | True for attention variants |
TensorOp::is_convolution() → bool | True for conv ops |
TensorOp::is_fused() → bool | True for fused kernels |
TensorOp::is_gradient() → bool | True for gradient ops |
ShapeInference::infer_output_shape(op, inputs) → Result<Vec<TensorTypeInfo>> | Infer output shapes |
ShapeInference::compute_flops(op, inputs) → Option<u64> | Count FLOPs |
ShapeInference::compute_memory_bytes(op, inputs) → Option<u64> | Estimate memory |
21.4 lift-quantum API
| Item | Description |
|---|---|
QuantumGate enum | 48 quantum gates |
QuantumGate::op_name() → &str | Get gate name (e.g. "quantum.h") |
QuantumGate::from_name(s) → Option<QuantumGate> | Parse from string |
QuantumGate::num_qubits() → usize | Gate arity |
QuantumGate::is_parametric() → bool | Requires angle parameters |
QuantumGate::is_self_inverse() → bool | G·G = I |
QuantumGate::is_clifford() → bool | In Clifford group |
QuantumGate::is_measurement() → bool | Measurement or control |
QuantumGate::is_entangling() → bool | Creates entanglement |
QuantumGate::native_basis(provider) → &[QuantumGate] | Hardware-native gates |
Provider enum | IbmEagle, IbmKyoto, Rigetti, IonQ, Quantinuum, Simulator |
NoiseModel enum | Ideal, Depolarizing, AmplitudeDamping, PhaseDamping, BitFlip, PhaseFlip, ThermalRelaxation, Kraus, Composed |
NoiseModel::fidelity() → f64 | Compute fidelity |
NoiseModel::compose(other) → NoiseModel | Chain noise models |
GateNoise::ideal() | Perfect gate |
GateNoise::with_depolarizing(f, t) | Gate with depolarizing noise |
CircuitNoise::new() | Track circuit-level noise |
CircuitNoise::add_gate(noise, is_2q) | Add gate to circuit |
CircuitNoise::meets_threshold(min) → bool | Check fidelity threshold |
DeviceTopology::linear(n) | Linear chain |
DeviceTopology::grid(r, c) | 2D grid |
DeviceTopology::heavy_hex(n) | IBM heavy-hex |
DeviceTopology::all_to_all(n) | Full connectivity |
DeviceTopology::tree(n) | Binary tree |
DeviceTopology::custom(name, edges, fid) | Custom topology |
topo.are_connected(q0, q1) → bool | Check edge |
topo.neighbors(q) → Vec<usize> | Get neighbours |
topo.shortest_path(from, to) → Option<Vec<usize>> | BFS path |
topo.swap_distance(from, to) → Option<usize> | SWAP count |
topo.diameter() → usize | Graph diameter |
topo.avg_connectivity() → f64 | Average degree |
21.5 lift-hybrid API
| Item | Description |
|---|---|
HybridOp enum | 21 hybrid operations |
HybridOp::op_name() → &str | Get op name |
HybridOp::from_name(s) → Option<HybridOp> | Parse from string |
HybridOp::is_gradient() → bool | Gradient op? |
HybridOp::is_variational() → bool | Variational algorithm? |
EncodingStrategy enum | AngleEncoding, AmplitudeEncoding, BasisEncoding, IQPEncoding, HamiltonianEncoding, KernelEncoding |
EncodingStrategy::qubits_required(dim) → usize | Qubits needed |
EncodingStrategy::circuit_depth(dim) → usize | Circuit depth |
EncodingConfig::new(strategy, dim) | Create config |
GradientMethod enum | ParameterShift, FiniteDifference, SPSA, Adjoint, Backprop |
GradientMethod::circuit_evaluations(n) → usize | Evaluations needed |
GradientMethod::is_exact() → bool | Exact gradient? |
JointGradientConfig | Combined classical+quantum gradients |
JointGradientConfig::total_evaluations() → usize | Total eval count |
AnsatzType enum | HardwareEfficient, StronglyEntangling, TwoLocal, UCCSD, Custom |
SyncPolicy enum | Blocking, Asynchronous, Pipeline |
FeatureMap enum | ZZFeatureMap, PauliFeatureMap, AngleEncoding, AmplitudeEncoding |
21.6 lift-opt Passes
| Pass | Name | Description |
|---|---|---|
Canonicalize | "canonicalize" | Simplify: x+0→x, x×1→x, reshape(reshape(x))→reshape(x) |
ConstantFolding | "constant-folding" | Evaluate constant expressions at compile time |
DeadCodeElimination | "dce" | Remove unused operations |
TensorFusion | "tensor-fusion" | Fuse matmul+bias+relu into single kernel |
GateCancellation | "gate-cancellation" | Cancel adjacent inverse gates (H·H→I) |
RotationMerge | "rotation-merge" | Merge rotations: Rz(a)·Rz(b)→Rz(a+b) |
FlashAttentionPass | "flash-attention" | Replace attention with FlashAttention when seq_len > threshold |
CommonSubexprElimination | "cse" | Eliminate duplicate computations |
QuantisationPass | "quantisation-pass" | Annotate quantisable operations |
NoiseAwareSchedule | "noise-aware-schedule" | Reorder gates for minimal noise |
LayoutMapping | "layout-mapping" | Legacy: annotate non-adjacent 2Q gates with needs_swap = true (no SWAP insertion) |
GateDecomposition | "gate-decomposition" | Replace non-native gates with the target provider's native set |
RealRouting | "real-routing" | Insert real quantum.swap ops (BFS) to satisfy topology connectivity |
21.7 lift-sim API
| Item | Description |
|---|---|
CostModel::a100() | NVIDIA A100 profile (312 TFLOPS, 2039 GB/s, 80GB) |
CostModel::h100() | NVIDIA H100 profile (989 TFLOPS, 3350 GB/s, 80GB) |
model.compute_time_ms(flops) → f64 | Compute-only time |
model.memory_time_ms(bytes) → f64 | Memory-only time |
model.roofline_time_ms(flops, bytes) → f64 | Roofline prediction |
model.arithmetic_intensity(flops, bytes) → f64 | FLOP/byte ratio |
model.is_compute_bound(flops, bytes) → bool | Compute or memory bound |
model.fits_in_memory(bytes) → bool | Fits in GPU VRAM |
model.num_gpus_needed(bytes) → usize | GPUs required |
QuantumCostModel::superconducting_default() | IBM-like QPU |
QuantumCostModel::trapped_ion_default() | IonQ-like QPU |
QuantumCostModel::neutral_atom_default() | Neutral-atom QPU |
qcm.circuit_fidelity(n_1q, n_2q) → f64 | Gate fidelity product |
qcm.circuit_time_us(n_1q, n_2q, n_meas, depth) → f64 | Execution time |
qcm.decoherence_fidelity(time_us) → f64 | Decoherence fidelity |
EnergyModel::a100() / ::h100() | Energy profiles |
energy.energy_joules(time_ms, gpus) → f64 | Energy in joules |
energy.energy_kwh(time_ms, gpus) → f64 | Energy in kWh |
energy.carbon_grams(time_ms, gpus) → f64 | CO₂ in grams |
energy.quantum_energy_joules(time_us, qubits) → f64 | Quantum energy |
Budget struct | Static resource constraints |
budget.check_flops(n) / check_memory(n) / check_fidelity(f) | Constraint checks |
ReactiveBudget::new(budget) | Dynamic budget tracker |
tracker.consume(flops, mem, time, fidelity) | Record usage |
tracker.check_remaining() → Result<()> | Check all constraints |
tracker.remaining_flops() / remaining_time_ms() | Remaining budget |
tracker.utilisation() → BudgetUtilisation | Usage ratios |
analyze_module(&ctx) → AnalysisReport | Full module analysis |
analyze_block(&ctx, block) → AnalysisReport | Single block analysis |
21.8 lift-predict API
| Item | Description |
|---|---|
predict_performance(report, cost_model) → RooflineResult | Classical roofline prediction |
predict_quantum(analysis, qcm, precision) → QuantumPrediction | Quantum performance prediction |
RooflineResult | .compute_time_ms, .memory_time_ms, .predicted_time_ms, .arithmetic_intensity, .is_compute_bound, .bottleneck |
QuantumPrediction | .estimated_fidelity, .circuit_time_us, .num_shots_for_precision, .total_execution_time_ms |
22. Troubleshooting
22.1 Common Verification Errors
| Error | Cause | Fix |
|---|---|---|
SSA violation: value used but not defined | Using a %name that was never created | Ensure all operands are defined before use |
SSA violation: value defined more than once | Two operations produce the same value | Use unique result names |
Dominance violation | Using a value before its defining op in block order | Reorder operations so definitions come before uses |
Type mismatch | Input types don't match operation signature | Check tensor shapes and data types |
Linearity violation: qubit consumed more than once | A qubit value used as input to two operations | Each qubit must be consumed exactly once |
Linearity violation: qubit not consumed (leaked) | A qubit is created but never used | Ensure all qubits are measured or returned |
Missing terminator | A block has no return or branch at the end | Add a terminator operation |
22.2 Common Parse Errors
| Error | Cause | Fix |
|---|---|---|
| Unexpected token | Syntax error in .lif file | Check operation format: %r = "dialect.op"(%args) : (types) -> type |
| Unknown type | Type name not recognised | Use tensor<...>, qubit, bit, f32, i64, bool |
| Unresolved dialect | Using an op without declaring the dialect | Add #dialect tensor, #dialect quantum, or #dialect hybrid at file top |
22.3 Optimisation Issues
| Issue | Cause | Fix |
|---|---|---|
| Fusion not applied | Pattern not matched (e.g. different order) | Ensure matmul → add → relu pattern is present |
| FlashAttention not applied | seq_len attribute missing or below threshold | Set seq_len attribute on attention ops, or lower threshold |
| Pass returns Error | IR is in invalid state | Run verify before optimisation |
22.4 Performance Debugging
#![allow(unused)] fn main() { // Check if compute-bound or memory-bound let model = CostModel::a100(); let report = analyze_module(&ctx); if model.is_compute_bound(report.total_flops, report.total_memory_bytes) { println!("Compute-bound: reduce FLOPs (quantise, prune, fuse)"); } else { println!("Memory-bound: reduce data movement (fusion, recomputation)"); } // Check per-op breakdown for (op, count) in &report.op_breakdown { println!(" {}: {} instances", op, count); } }
22.5 Quantum Debugging
#![allow(unused)] fn main() { use lift_quantum::{CircuitNoise, GateNoise}; // Track where fidelity drops let mut circuit = CircuitNoise::new(); let g1q = GateNoise::with_depolarizing(0.999, 0.02); let g2q = GateNoise::with_depolarizing(0.99, 0.3); // After each gate, check fidelity circuit.add_gate(&g1q, false); println!("After H: fidelity = {:.6}", circuit.total_fidelity); circuit.add_gate(&g2q, true); println!("After CX: fidelity = {:.6}", circuit.total_fidelity); // 2Q gates dominate fidelity loss! }
Appendix A — Summary of All Operations
| Dialect | Count | Categories |
|---|---|---|
| tensor | 110 | Arithmetic, activations, normalisation, shape, attention, convolution, pooling, recurrent, math, sparse, quantisation, diffusion, GNN, memory, gradient, parallelism, fused |
| quantum | 48 | 1Q standard, 1Q parametric, 1Q fixed, 2Q standard, 2Q parametric, IonQ native, 3Q, multi-controlled, measurement, special |
| hybrid | 21 | Encoding, gradient methods, processing, variational, data transfer, co-execution, measurement |
Total: 179 operations (110 + 48 + 21) across three dialects in a single unified IR.
Appendix B — Quick Reference Card
# Parse and verify
lift verify input.lif
# Analyse (FLOPs, memory, gates)
lift analyse input.lif
# Optimise with default passes
lift optimise input.lif -o optimised.lif
# Optimise with config
lift optimise input.lif --config project.lith -o optimised.lif
# Predict performance on H100
lift predict input.lif --device h100
# Export to LLVM
lift export input.lif --backend llvm -o model.ll
# Export to OpenQASM
lift export input.lif --backend qasm -o circuit.qasm
LIFT v0.4.8 — MIT License — https://github.com/rustnew/Lift
LIFT — Features, Capabilities, Limits, and Goals
Complete analysis based on the actual source code (67 Rust files, 13 crates, 541 tests).
Table of Contents
- Overview
- Processing Pipeline
- Implemented Features
- Partial Features
- Missing Features
- Current Limitations
- Analysis Accuracy
- What's Missing to Reach the Goals
- Roadmap
1. Overview
LIFT is a unified IR compiler for classical AI + quantum computing, written in Rust (13 crates):
| Crate | Role |
|---|---|
lift-core | Core: IR context, types, verifier, printer, pass manager |
lift-ast | Lexer, parser, builder for .lif files |
lift-tensor | 110 AI operations, shape inference, FLOP counting |
lift-quantum | 50+ quantum gates, noise, Kraus, QEC, topology |
lift-hybrid | 21 classical↔quantum operations |
lift-opt | 13 optimisation passes |
lift-sim | Static analysis, GPU/QPU cost models, energy |
lift-predict | Roofline prediction, quantum prediction |
lift-config | .lith file parser, O0-O3 level pipeline, quantum provider |
lift-import | ONNX, PyTorch FX, OpenQASM import (skeletons) |
lift-export | LLVM IR, ONNX (opset 21), OpenQASM 3.0 export |
lift-cli | CLI: verify, analyse, print, optimise, predict, export |
lift-codegen | Programmatic model generation, multi-format export |
lift-tests | 541 tests, 0 failures |
2. Processing Pipeline
.lif → Lexer → Parser → Builder → Context IR → Verification → Analysis → Optimisation → Export
Stage 1 — Lexer (COMPLETE)
Splits .lif text into tokens: keywords, #dialect directives, identifiers (@name/%var), literals, punctuation. Includes error handling.
Stage 2 — Parser (COMPLETE)
Builds the AST: dialect directives, modules, functions, operations with operands/attributes/type signatures. Tensor types (tensor<1x784xf32>), qubit, bit, hamiltonian. Error recovery.
Stage 3 — Builder (COMPLETE)
Converts the AST into internal IR inside the Context: SSA values, operations, blocks, regions, functions, modules.
Stage 4 — Context IR (COMPLETE)
Central structure with SlotMaps for values, ops, blocks, regions, types + StringInterner. Types: Integer (i1-i64), Float (f16-f64, fp8), Boolean, Void, Tuple, Function, Opaque (tensor, qubit, bit, hamiltonian).
Stage 5 — Verification (COMPLETE, 4 passes)
- SSA: every value defined exactly once, every use after its definition
- Well-formedness: no dangling references (ops ↔ values ↔ blocks ↔ regions)
- Linearity: every qubit consumed exactly once (no-cloning)
- Semantic: input arity of every operation checked against dialect signatures (core + tensor + quantum + hybrid), via
verify_semantics()/verify_with_dialects()
13 error types: UndefinedValue, MultipleDefinition, DominanceViolation, TypeMismatch, LinearityViolation, QubitLeaked, BranchLinearityMismatch, DanglingReference, MissingTerminator, OrphanedOperation, OrphanedBlock, InvalidOperation, SemanticError.
Stage 6 — Static Analysis (COMPLETE)
Produces: total_flops, total_memory_bytes, peak_memory, num_ops per dialect, op_breakdown. Quantum: qubits, 1Q/2Q/3Q gates, measurements, circuit_depth, estimated_fidelity, accumulated noise.
Stage 7 — Optimisation (13 passes)
| Pass | Type | Concrete action |
|---|---|---|
canonicalize | Tensor | Normalises patterns |
constant-folding | Tensor | Evaluates constants at compile time |
dce | General | Removes ops whose results are unused |
tensor-fusion | Tensor | Fuses matmul+add+relu → fused_matmul_bias_relu, linear+gelu → fused_linear_gelu, linear+silu → fused_linear_silu, conv2d+bn+relu (2 phases: ternary then binary) |
cse | General | Eliminates common subexpressions |
flash-attention | Tensor | Replaces attention → flash attention |
quantisation-pass | Tensor | Annotates for INT8/INT4 quantisation |
gate-cancellation | Quantum | Cancels H·H=I, X·X=I, S·Sdg=I, T·Tdg=I — including non-consecutive pairs (separated by commuting gates on other qubits, SSA chain verified) |
rotation-merge | Quantum | Merges Rz(a)·Rz(b) → Rz(a+b) — same, non-consecutive pairs |
noise-aware-schedule | Quantum | Reorders gates to minimise decoherence |
layout-mapping | Quantum | Annotates 2-qubit gates that need SWAPs |
gate-decomposition | Quantum | Decomposes H/T/Tdg/S/Sdg/Y/RX into the provider's native set (IBM, Rigetti, IonQ, Quantinuum), driven by [quantum] provider; the original gate is removed and replaced, not left in place alongside its decomposition |
real-routing | Quantum | Inserts real quantum.swap ops (BFS shortest path) to satisfy topology connectivity; tracks logical↔physical placement |
Level pipelines ([optimisation] level = O0|O1|O2|O3):
O0: no passesO1: canonicalize, constant-folding, dceO2: O1 + cse, tensor-fusionO3: all 13 passes, including gate-decomposition and real-routing
Explicit passes take priority over the level; disabled_passes removes passes; unknown passes trigger a warning (OptimisationConfig::validate()). All passes are reachable from the CLI.
Stage 8 — Prediction (COMPLETE)
- GPU roofline: compute_time_ms, memory_time_ms, bottleneck. A100 (312 TFLOPS) and H100 (989 TFLOPS) models.
- Quantum: fidelity, circuit_time_us, shots needed. 3 models: superconducting, trapped ion, neutral atom.
- Budget: checks max FLOPs, max memory, max time, min fidelity. ReactiveBudget for real-time tracking.
- Both are reachable from the CLI via
predict --energyandpredict --quantum <hardware>.
Stage 9 — Export (3 backends)
- LLVM IR: ops emitted as comments with cuBLAS/cuDNN runtime calls
- ONNX: protobuf text, opset 21, 70+ operations mapped (standard + com.microsoft)
- OpenQASM 3.0: all 48
QuantumGatevariants have a match arm (verified: no wildcard/unsupported fallback exists in the exporter) — 46 emit a real QASM gate instruction, andIfElse/ParamGate(control-flow/generic wrappers, not literal gates) emit a descriptive comment. Qubit indices are resolved by following each gate's actual SSA operand back to its owning qubit, not assigned from a counter
3. Implemented Features
3.1 Tensor Dialect — 110 operations
All 110 operations are defined in the TensorOp enum with name↔enum conversion, input count, classification. Working shape inference for: MatMul, Linear, Conv2D, Conv1D, DepthwiseConv2D, Attention, FlashAttention, MaxPool2D, GlobalAvgPool, BatchNorm, LayerNorm, RMSNorm, InstanceNorm, SparseMatMul, elementwise, ELU, LeakyReLU, Mish, HardSwish. Exact FLOP counting for MatMul, Linear, Conv2D, Attention, ReLU, elementwise, fused ops.
3.2 Quantum Dialect — 50+ gates
Gates: 9 standard 1Q + 7 parametric 1Q + 2 fixed-angle + 13 2Q gates + 2 3Q gates + 2 multi-controlled + 8 measurement/control + IonQ gates. Per-gate properties: num_qubits, is_parametric, is_self_inverse, is_clifford, is_entangling. 5 native sets (IBM, Rigetti, IonQ, Quantinuum, Simulator). Noise: GateNoise, CircuitNoise, KrausChannel (6 channels). Topology: linear, grid, heavy_hex, all_to_all, tree, custom + BFS. QEC: Surface, Steane, Shor, Repetition, LDPC.
3.3 Hybrid Dialect — 21 operations
Encode/Decode, 5 gradients, 4 variational algorithms, 2 transfers, 4 processing ops, CoExecute, 2 measurements. AnsatzType, SyncPolicy, FeatureMap, EncodingStrategy.
3.4 CLI — 6 commands
verify, analyse (text/JSON), print, optimise (with .lith), predict (A100/H100, --energy, --quantum), export (llvm/onnx/qasm).
3.5 Programmatic Generation — lift-codegen
lift-codegen binary: defines models from Rust via ModelBuilder, automatically generates .lif, .ll, .onnx, .qasm, .lith. 4 predefined models (Phi-3-mini, MLP, ResNet, VQE).
3.6 Energy Models
A100/H100 EnergyModel: energy in joules/kWh, CO2 grams, quantum energy (cryogenics). Connected to the CLI via predict --energy.
3.7 Tests — 541 tests, 0 failures
Types, operations, shapes, FLOPs, memory, gates, noise, topology, QEC, Kraus, benchmarks (GPT-2, LLaMA-7B, ResNet-50, BERT-base), O0-O3 pipeline, semantic verification, generic fusions, gate decomposition, non-consecutive cancellation/merge, real SWAP routing, printer/parser round-tripping. End-to-end validation: examples/validate_all.sh (105 checks, including all 13 passes).
4. Partial Features (code exists, incomplete)
4.1 LLVM IR Export — SKELETON
The exporter produces define void @func(ptr %arg0) { entry: ; tensor.matmul ret void }. Operations are emitted as comments, not real LLVM IR. No cuBLAS/cuDNN calls, no memory management.
4.2 ONNX Export — OPERATIONAL
The ONNX exporter produces protobuf text (opset 21) with 70+ operations mapped to standard ONNX and com.microsoft ops. Data types, shapes, and initializer nodes are generated. Missing: binary protobuf serialisation (currently text only), connected node graphs (nodes are emitted sequentially without explicit edges).
4.3 OpenQASM Export — all 48 gates handled, 2 as comments
Every QuantumGate variant has a match arm; 46 produce a real QASM gate instruction (including less-common ones like MCX, CSWAP, GPI/GPI2, XX/YY/ZZ). IfElse and ParamGate are control-flow/generic wrappers rather than fixed gates, so they emit a descriptive comment instead of a gate line. Gate order follows the real circuit order (block.ops) and qubit indices are resolved from each gate's actual operand chain, not a counter.
4.4 ONNX/PyTorch/QASM Import — SKELETONS
All 3 importers read the source format but create an empty module+function. No node/operation is actually converted into LIFT operations.
4.5 Layout Mapping — ANNOTATION ONLY
Adds needs_swap = true on non-adjacent 2Q gates. Does not actually insert SWAPs or route. (real-routing does the real work; layout-mapping remains a legacy annotation-only pass.)
4.6 Shape Inference — PARTIAL
Works for about 20 of the 110 operations. Missing: Conv3D, ConvTranspose2D, Reshape, Permute, Concat, Split, Slice, LSTM, GRU, RNN, FFT, SVD, Einsum, GNN, MoE, diffusion, quantisation, parallelism.
5. Missing Features
5.1 No Semantic Verification of Operand Shapes
The verifier checks SSA/well-formedness/linearity but does NOT check that tensor.matmul has 2 tensor inputs, that dimensions are compatible, that tensor.conv2d receives a 4D tensor, etc.
5.2 No Real Execution
LIFT cannot execute a program. It is purely an analysis compiler. There is no runtime, no interpreter, no GPU/QPU execution backend.
5.3 No Real Quantum Simulation
The quantum_sim module does static analysis (gate counting, fidelity estimation). It does NOT simulate quantum state (no state vector, no density matrix, no Monte Carlo simulation).
5.4 No Machine Code Generation
LLVM export does not produce executable code. This would require: lowering tensor operations to library calls (cuBLAS, cuDNN, oneDNN), memory management (allocation/deallocation), kernel scheduling, GPU launch code.
5.5 No Multi-File Support
A LIFT program is a single .lif file. No import/include system, no separate modules, no linking.
5.6 Limited Gate Decomposition Table
gate-decomposition correctly replaces the gates it knows about (H, T, Tdg, S, Sdg, Y, RX), but the decomposition table only covers those seven — every other non-native gate silently passes through unchanged, even when targeting hardware whose native set doesn't include it.
5.7 No GPU Scheduling
No placement of operations on CUDA streams, no compute/memory overlap, no operation parallelism.
5.8 No Automatic Differentiation
Gradient operations are declared (grad_matmul, grad_relu, etc.) but there is no autodiff system that automatically builds the backward graph from the forward graph.
5.9 No Data Handling
No data loading (datasets), no data loaders, no preprocessing. LIFT works purely on the computation graph.
6. Current Limitations
6.1 Structural Limitations
| Limitation | Impact |
|---|---|
| No execution | LIFT analyses but cannot execute a model |
| Skeleton export | Generated code (LLVM/QASM) is not executable as-is |
| Skeleton import | Cannot import a real ONNX/PyTorch model |
| No QC simulation | Fidelity estimated by formula, not real simulation |
6.2 Cost Model Limitations
- The roofline model is a coarse approximation: it does not account for cache effects, kernel launch latency, or compute/memory overlap
- The quantum model uses average default noise parameters, not the real properties of the target device
- Fidelity estimation assumes independent noise per gate (no spatial/temporal correlations)
6.3 Verifier Limitations
- No operand type checking (input types vs. signature)
- No dimension-compatibility checking (tensor shapes)
- No complete dominance checking (CFG)
- Linearity verification does not exhaustively handle conditional branches
- Semantic verification checks arity but not tensor dimensions
6.4 Optimiser Limitations
tensor-fusionrecognises 5 patterns (matmul+bias+relu, matmul+bias, linear+gelu/silu, conv+bn+relu) but not attention+softmax or layernorm fusionsgate-cancellation/rotation-mergedetect non-consecutive pairs via the SSA chain, but not cross patterns (e.g. H·Rz)noise-aware-schedulesorts by gate time, not a real constrained scheduling algorithmreal-routinginserts SWAPs (BFS) with an identity initial placement; no SABRE-style dynamic re-placement, no SWAP-direction correction for directionality
7. Analysis Accuracy
7.1 FLOP Counting
| Operation | Accuracy | Formula |
|---|---|---|
| MatMul (MxK × KxN) | Exact | 2 × M × K × N |
| MatMul batch (BxMxK × BxKxN) | Exact | 2 × B × M × K × N |
| Linear (MxK × KxN + N) | Exact | 2 × M × K × N + M × N |
| Conv2D | Exact | 2 × B × Cout × Hout × Wout × Cin × Kh × Kw |
| Attention | Exact | 2 × B × H × (S² × D + S × D²) |
| ReLU / elementwise | Exact | element count |
| Reshape, Transpose | Exact | 0 FLOPs (correct) |
| Fused ops | Exact | sum of components |
| LSTM, GRU, RNN | Not implemented | — |
| Conv3D, ConvTranspose | Not implemented | — |
| Einsum, FFT, SVD | Not implemented | — |
Overall accuracy: for pure Transformer models (GPT, BERT, LLaMA), FLOP-counting accuracy is excellent (error < 1%). For CNN models, it's good for Conv2D but misses other convolutions. For recurrent models (LSTM), FLOPs are not counted.
7.2 Memory Estimation
Computes element_count × byte_size(dtype) per tensor. Accurate for static memory but does not model: dynamically allocated intermediate activations, GPU memory fragmentation, workspace buffers (cuDNN), KV cache for LLM inference.
7.3 Time Prediction (Roofline)
| Aspect | Accuracy |
|---|---|
| Compute-bound vs. memory-bound identification | Good (standard cases) |
| Absolute time | Order of magnitude (2-5x error possible) |
| Cache effects | Not modelled |
| Kernel launch latency | Not modelled |
| Multi-GPU | Not modelled (assumes 1 GPU) |
| Compute/memory overlap | Not modelled |
7.4 Quantum Fidelity
Fidelity is estimated as the product of individual fidelities: F = ∏ f_gate × f_decoherence. This is an upper bound (real fidelity is often worse due to noise correlations, crosstalk, and readout errors).
8. What's Missing to Reach the Goals
LIFT's goal is: "Simulate → Predict → Optimise → Compile". Current state:
| Goal | State | What's missing |
|---|---|---|
| Simulate | 40% | Static analysis is solid, but no real execution simulation (no quantum state vector, no tensor interpreter) |
| Predict | 70% | GPU roofline OK, quantum prediction OK, but the model is too simplified (no cache, no multi-GPU, no scheduling) |
| Optimise | 70% | 13 passes wired in, O0-O3 pipeline, semantic verification, generic fusions, gate decomposition, real SWAP routing; missing a general rewrite graph and attention/layernorm fusions |
| Compile | 10% | LLVM/QASM export are skeletons, no real executable code |
8.1 To Reach Simulate (100%)
- Quantum state-vector simulator: multiply gate matrices onto a 2^n vector. Needed to validate quantum circuits.
- Tensor interpreter: execute tensor ops with real, numpy-like values. Needed to validate AI models.
- Monte Carlo simulation: to estimate the measurement distribution under noise.
8.2 To Reach Predict (100%)
- Refined cost model: incorporate launch latency, L2 cache effects, overlapped scheduling.
- Real hardware profiles: load real QPU properties (IBM Quantum calibration, per-qubit gate times).
- Multi-GPU: inter-GPU communication model (NVLink, PCIe).
- Advanced quantum prediction: correlated noise model, crosstalk, readout errors.
8.3 To Reach Optimise (100%)
- More fusion patterns: matmul+gelu, conv+bn+relu, attention+layernorm.
- Non-local gate cancellation: cancel pairs separated by operations on other qubits (commutation).
- Real routing: implement SABRE or A* for layout mapping with SWAP insertion.
- Broader gate decomposition: cover more than the current 7-gate table.
- Pattern-based rewrite system: allow declarative transformation rules.
8.4 To Reach Compile (100%)
- Tensor → LLVM lowering: generate real calls to cuBLAS/cuDNN/oneDNN.
- Memory management: GPU memory allocator (allocation, deallocation, reuse).
- Launch code: generate host code that orchestrates GPU kernels.
- Quantum backend: generate code for IBM Qiskit Runtime, Amazon Braket, or Google Cirq.
- Real import: convert real ONNX/PyTorch graphs into LIFT operations.
9. Roadmap (by priority)
Priority 1 — Quick fixes (low effort, immediate impact) — done
-
Wire all 13 passes into the CLI (
cmd_optimise, main.rs) -
Wire EnergyModel into the CLI (
predict --energy,--num-gpus) -
Wire predict_quantum into the CLI (
predict --quantum <hardware> --precision) -
crates/lift-demo/src/config.rspresent and compiling (cargo build -p lift-demo) -
Fix the printer/parser round trip (
optimise --outputproduced a.lifthe parser couldn't read back) -
Fix QASM qubit indexing (counter → real SSA operand chain), gate order (slotmap →
block.ops), and per-function qubit counting -
Fix
gate-decompositionleaving the original gate in place next to its own decomposition
Priority 2 — Functional Import/Export (medium effort, high impact)
- Real ONNX import: map ONNX nodes to TensorOp
- Real PyTorch FX import: map FX nodes to TensorOp
- Full QASM export: all 48 gates handled (done — see §4.3)
- Real QASM import: parse gates and create quantum operations
Priority 3 — Advanced Optimisation (medium effort)
- More tensor fusion patterns
- Non-local gate cancellation (commutation)
- Broader gate decomposition table
- Real routing (SABRE)
Priority 4 — Simulation (high effort)
- State-vector simulator (up to ~25 qubits)
- Simplified tensor interpreter
- Shape inference for the remaining 90 operations
Priority 5 — Real Compilation (very high effort)
- Tensor → LLVM lowering with cuBLAS calls
- GPU memory management
- Quantum backend (Qiskit/Braket)
Final Summary
| Metric | Value |
|---|---|
| Crates | 14 |
| Rust files | 67 |
| Tests | 541 (0 failures) |
| Defined operations | 179 (110 tensor + 48 quantum + 21 hybrid) |
| Optimisation passes | 13 (13 wired into the CLI) |
| Export backends | 3 (LLVM IR, ONNX opset 21, OpenQASM 3.0) |
| Cost models | 5 (A100, H100, superconducting, trapped ion, neutral atom) |
| QASM-exported gates | 48 / 48 (46 as real gates, 2 as comments) |
| ONNX-exported ops | 70+ / 110 |
| Functional imports | 0 / 3 |
| Execution possible | No |
| Real compilation | No |
LIFT is a solid, well-tested IR analysis and optimisation framework, with excellent dialect coverage (tensor, quantum, hybrid) and a clean architecture. Its strength is static analysis (FLOPs, memory, fidelity, noise, cost). The addition of ONNX export (opset 21) and the lift-codegen binary now makes it possible to generate models programmatically and export them to 3 backends (LLVM, ONNX, QASM). What it mainly lacks is the ability to actually execute code: LLVM export is a skeleton, imports are empty, and there is no runtime. To become a complete "Simulate → Predict → Optimise → Compile" compiler, it needs real lowering, functional imports, and a simulator.
This document is a complete and honest analysis of LIFT's current state.
LIFT Dialect Reference — Complete Guide
The definitive reference for every dialect, type, operation, and syntax rule in LIFT.
After reading this document you will be able to write, read, configure, and assemble any
.liffile for any model — classical AI, quantum circuits, or hybrid — without errors.
Table of Contents
- Part I — File Structure, Grammar, and Type System
- Part II — The
tensorDialect (Classical AI) - Part III — The
quantumDialect (Quantum Computing) - Part IV — The
hybridDialect (Classical + Quantum Bridge) - Part V — Configuration (
.lithFiles) - Part VI — Assembling Dialects Together
Part I — File Structure, Grammar, and Type System
1.1 The .lif File
Every LIFT program is a .lif text file with this structure:
#dialect tensor ← 1. Dialect declarations (one or more)
module @name { ← 2. Module
func @fn(%x: type) -> type { ← 3. Function
%y = "op"(%x) : (type) -> type ← 4. Operations
return %y ← 5. Return
}
}
Rules
- At least one
#dialectdirective at the top. - At least one
moduleblock. - Each module contains one or more
funcdeclarations. - Each function has parameters, optional return types, and a body of operations.
- The body ends with a
returnstatement.
1.2 Grammar Rules
Dialect Directive
#dialect tensor
#dialect quantum
#dialect hybrid
You can declare multiple dialects in one file.
Module
module @my_model {
...
}
Function
func @forward(%x: tensor<1x784xf32>, %w: tensor<784x256xf32>) -> tensor<1x256xf32> {
...
return %out
}
Multiple return types use parentheses:
func @bell(%q0: qubit, %q1: qubit) -> (qubit, qubit) {
...
return %q2, %q3
}
Operation (Assignment)
%result = "dialect.operation"(%input1, %input2) : (input_type1, input_type2) -> output_type
Multiple results:
%r1, %r2 = "quantum.cx"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
With attributes:
%y = "quantum.ry"(%q) {angle = 1.5708} : (qubit) -> qubit
1.3 Identifiers
| Prefix | Meaning | Example |
|---|---|---|
@ | Module or function name | @my_model, @forward |
% | SSA value (variable) | %x, %q0, %hidden |
^ | Block label | ^entry |
#dialect | Dialect directive | #dialect tensor |
SSA Rule
Every %name is assigned exactly once. No reassignment allowed.
1.4 The Type System
Tensor Types
tensor<shape x dtype>
| Example | Description |
|---|---|
tensor<4xf32> | 1D, 4 floats |
tensor<1x784xf32> | 2D, batch 1, 784 features |
tensor<1x3x224x224xf32> | 4D image: batch, channels, H, W |
tensor<Bx128x64xf16> | Symbolic batch dim, float16 |
Data Types (dtype)
| Syntax | Bits | Use Case |
|---|---|---|
f64 | 64 | Scientific computing |
f32 | 32 | Default training/inference |
f16 | 16 | Mixed precision |
bf16 | 16 | A100/H100 training |
fp8e4m3 | 8 | H100 FP8 inference |
fp8e5m2 | 8 | H100 FP8 training |
i64 | 64 | Large indices |
i32 | 32 | Indices |
i16 | 16 | Quantised weights |
i8 | 8 | INT8 quantisation |
i4 | 4 | INT4 quantisation |
i2 | 2 | Extreme quantisation |
u8 | 8 | Pixel values |
i1 | 1 | Booleans/masks |
index | 64 | Loop indices |
Quantum Types
| Syntax | Description | Rule |
|---|---|---|
qubit | A single qubit | Linear: consumed exactly once |
bit | Classical bit (measurement result) | Normal (non-linear) |
hamiltonian<N> | Hamiltonian on N qubits | Normal |
Scalar Types
f32, f64, i32, i64, bool, void, index
1.5 Attributes
Compile-time constants attached to operations:
%y = "tensor.conv2d"(%x, %w) {stride = 2, padding = 1} : ...
| Type | Example |
|---|---|
| Integer | stride = 2 |
| Float | rate = 0.5 |
| Boolean | training = true |
| String | mode = "same" |
| Array | kernel_size = [3, 3] |
1.6 Comments
// This is a comment (ignored by the parser)
Part II — The tensor Dialect (Classical AI)
Declare with #dialect tensor. Provides 107 operations for ML/AI.
2.1 Arithmetic (9)
| Operation | Syntax | Inputs | FLOPs |
|---|---|---|---|
| Add | "tensor.add" | 2 | N |
| Sub | "tensor.sub" | 2 | N |
| Mul | "tensor.mul" | 2 | N |
| Div | "tensor.div" | 2 | N |
| Neg | "tensor.neg" | 1 | N |
| MatMul | "tensor.matmul" | 2 | 2MNK |
| Linear | "tensor.linear" | 3 | 2MNK+N |
| Conv2D | "tensor.conv2d" | 2 | 2CoCiKhKwOhOw |
| Embedding | "tensor.embedding" | 2 | 0 (lookup) |
%out = "tensor.matmul"(%x, %w) : (tensor<1x784xf32>, tensor<784x256xf32>) -> tensor<1x256xf32>
%out = "tensor.linear"(%x, %w, %b) : (tensor<1x784xf32>, tensor<784x256xf32>, tensor<256xf32>) -> tensor<1x256xf32>
%feat = "tensor.conv2d"(%img, %k) : (tensor<1x3x224x224xf32>, tensor<64x3x7x7xf32>) -> tensor<1x64x112x112xf32>
2.2 Activations (11)
All: 1 input → 1 output, same shape.
| Operation | Syntax | Formula |
|---|---|---|
| ReLU | "tensor.relu" | max(0, x) |
| GeLU | "tensor.gelu" | x * Phi(x) |
| SiLU | "tensor.silu" | x * sigmoid(x) |
| Sigmoid | "tensor.sigmoid" | 1/(1+e^-x) |
| Softmax | "tensor.softmax" | e^xi / sum(e^xj) |
| Tanh | "tensor.tanh" | (e^x-e^-x)/(e^x+e^-x) |
| LeakyReLU | "tensor.leaky_relu" | max(alpha*x, x) |
| ELU | "tensor.elu" | x if x>0, alpha*(e^x-1) else |
| Mish | "tensor.mish" | x*tanh(softplus(x)) |
| HardSwish | "tensor.hard_swish" | x*relu6(x+3)/6 |
| HardSigmoid | "tensor.hard_sigmoid" | relu6(x+3)/6 |
%a = "tensor.relu"(%x) : (tensor<1x256xf32>) -> tensor<1x256xf32>
%p = "tensor.softmax"(%logits) : (tensor<1x10xf32>) -> tensor<1x10xf32>
Which to use: CNN → ReLU. Transformer/LLM → GeLU/SiLU. Classifier → Softmax/Sigmoid. Edge → HardSwish.
2.3 Normalisation (5)
| Operation | Syntax | Inputs | Use Case |
|---|---|---|---|
| LayerNorm | "tensor.layernorm" | 2-3 | Transformers |
| RMSNorm | "tensor.rmsnorm" | 2-3 | LLaMA, Mistral |
| BatchNorm | "tensor.batchnorm" | 3-5 | CNN training |
| GroupNorm | "tensor.groupnorm" | 2-3 | Diffusion |
| InstanceNorm | "tensor.instancenorm" | 2-3 | Style transfer |
// LayerNorm — Transformers (input + scale)
%n1 = "tensor.layernorm"(%x, %scale) : (tensor<1x128x64xf32>, tensor<64xf32>) -> tensor<1x128x64xf32>
// LayerNorm with bias — (input + scale + bias)
%n2 = "tensor.layernorm"(%x, %scale, %bias) : (tensor<1x128x64xf32>, tensor<64xf32>, tensor<64xf32>) -> tensor<1x128x64xf32>
// RMSNorm — LLaMA, Mistral (input + scale)
%n3 = "tensor.rmsnorm"(%x, %scale) : (tensor<1x128x4096xf32>, tensor<4096xf32>) -> tensor<1x128x4096xf32>
// BatchNorm — CNN training (input + scale + bias)
%n4 = "tensor.batchnorm"(%x, %scale, %bias) : (tensor<8x64x32x32xf32>, tensor<64xf32>, tensor<64xf32>) -> tensor<8x64x32x32xf32>
// GroupNorm — Diffusion models (input + scale)
%n5 = "tensor.groupnorm"(%x, %scale) : (tensor<1x256x32x32xf32>, tensor<256xf32>) -> tensor<1x256x32x32xf32>
// InstanceNorm — Style transfer (input + scale)
%n6 = "tensor.instancenorm"(%x, %scale) : (tensor<1x64x256x256xf32>, tensor<64xf32>) -> tensor<1x64x256x256xf32>
2.4 Shape Operations (13) — Zero FLOPs
| Operation | Syntax | Description |
|---|---|---|
| Reshape | "tensor.reshape" | Change shape |
| Transpose | "tensor.transpose" | Swap dims |
| Concat | "tensor.concat" | Join tensors |
| Split | "tensor.split" | Split tensor |
| Gather | "tensor.gather" | Index select |
| Scatter | "tensor.scatter" | Index assign |
| Squeeze | "tensor.squeeze" | Remove dim=1 |
| Unsqueeze | "tensor.unsqueeze" | Add dim=1 |
| Permute | "tensor.permute" | Reorder dims |
| Expand | "tensor.expand" | Broadcast |
| Slice | "tensor.slice" | Sub-tensor |
| Pad | "tensor.pad" | Add padding |
| Tile | "tensor.tile" | Repeat |
// Reshape: flatten a 4D image tensor to 2D
%flat = "tensor.reshape"(%x) : (tensor<1x3x224x224xf32>) -> tensor<1x150528xf32>
// Transpose: swap last two dimensions
%t = "tensor.transpose"(%x) : (tensor<128x64xf32>) -> tensor<64x128xf32>
// Concat: join two tensors along batch dimension
%cat = "tensor.concat"(%a, %b) : (tensor<1x128xf32>, tensor<1x128xf32>) -> tensor<2x128xf32>
// Squeeze: remove dimension of size 1
%sq = "tensor.squeeze"(%x) : (tensor<1x64x1x1xf32>) -> tensor<1x64xf32>
// Unsqueeze: add a batch dimension
%us = "tensor.unsqueeze"(%x) : (tensor<64xf32>) -> tensor<1x64xf32>
// Permute: reorder dimensions (e.g. NCHW → NHWC)
%p = "tensor.permute"(%x) : (tensor<1x3x224x224xf32>) -> tensor<1x224x224x3xf32>
// Slice: extract a sub-tensor
%sl = "tensor.slice"(%x) : (tensor<1x100x64xf32>) -> tensor<1x50x64xf32>
// Pad: add zero-padding
%padded = "tensor.pad"(%x) : (tensor<1x3x224x224xf32>) -> tensor<1x3x226x226xf32>
// Gather: index-based selection
%sel = "tensor.gather"(%x, %indices) : (tensor<1000x64xf32>, tensor<10xi32>) -> tensor<10x64xf32>
// Expand: broadcast a tensor
%exp = "tensor.expand"(%x) : (tensor<1x1x64xf32>) -> tensor<8x128x64xf32>
2.5 Attention (8)
All take 3-5 inputs (Q, K, V, optional mask). FLOPs = 2BH*(S²D + SD²).
| Operation | Syntax | Description |
|---|---|---|
| Attention | "tensor.attention" | Standard scaled dot-product |
| MultiHeadAttention | "tensor.multi_head_attention" | Standard Transformer |
| MultiQueryAttention | "tensor.multi_query_attention" | Shared K/V (fast inference) |
| GroupedQueryAttention | "tensor.grouped_query_attention" | LLaMA 2 style |
| FlashAttention | "tensor.flash_attention" | O(n) memory |
| SlidingWindowAttention | "tensor.sliding_window_attention" | Mistral |
| CrossAttention | "tensor.cross_attention" | Encoder-decoder |
| PagedAttention | "tensor.paged_attention" | vLLM paged KV |
// Standard scaled dot-product attention: Q, K, V
%attn = "tensor.attention"(%q, %k, %v) : (tensor<1x128x64xf32>, tensor<1x128x64xf32>, tensor<1x128x64xf32>) -> tensor<1x128x64xf32>
// Multi-head attention (standard Transformer)
%mha = "tensor.multi_head_attention"(%q, %k, %v) : (tensor<1x8x128x64xf32>, tensor<1x8x128x64xf32>, tensor<1x8x128x64xf32>) -> tensor<1x8x128x64xf32>
// Flash attention: same API, O(n) memory, for long sequences
%flash = "tensor.flash_attention"(%q, %k, %v) : (tensor<1x8x4096x64xf32>, tensor<1x8x4096x64xf32>, tensor<1x8x4096x64xf32>) -> tensor<1x8x4096x64xf32>
// Grouped-query attention (LLaMA 2 style): fewer K/V heads
%gqa = "tensor.grouped_query_attention"(%q, %k, %v) : (tensor<1x32x128x64xf32>, tensor<1x8x128x64xf32>, tensor<1x8x128x64xf32>) -> tensor<1x32x128x64xf32>
// Cross attention (encoder-decoder, e.g. translation)
%cross = "tensor.cross_attention"(%decoder_q, %encoder_k, %encoder_v) : (tensor<1x8x64x64xf32>, tensor<1x8x128x64xf32>, tensor<1x8x128x64xf32>) -> tensor<1x8x64x64xf32>
// Sliding window attention (Mistral): local context window
%swa = "tensor.sliding_window_attention"(%q, %k, %v) : (tensor<1x8x4096x64xf32>, tensor<1x8x4096x64xf32>, tensor<1x8x4096x64xf32>) -> tensor<1x8x4096x64xf32>
// Paged attention (vLLM): paged KV cache for efficient serving
%paged = "tensor.paged_attention"(%q, %k_cache, %v_cache) : (tensor<1x8x1x64xf32>, tensor<1x8x4096x64xf32>, tensor<1x8x4096x64xf32>) -> tensor<1x8x1x64xf32>
2.6 Convolution (6)
| Operation | Syntax | Use Case |
|---|---|---|
| Conv1D | "tensor.conv1d" | Audio, time series |
| Conv2D | "tensor.conv2d" | Images |
| Conv3D | "tensor.conv3d" | Video, medical 3D |
| ConvTranspose2D | "tensor.conv_transpose2d" | Upsampling |
| DepthwiseConv2D | "tensor.depthwise_conv2d" | MobileNet |
| DilatedConv2D | "tensor.dilated_conv2d" | Segmentation |
// Conv1D: audio processing [B, Cin, Length] * [Cout, Cin, K]
%audio_feat = "tensor.conv1d"(%audio, %kernel) : (tensor<1x1x16000xf32>, tensor<64x1x80xf32>) -> tensor<1x64x15921xf32>
// Conv2D: image feature extraction [B, Cin, H, W] * [Cout, Cin, Kh, Kw]
%img_feat = "tensor.conv2d"(%img, %kernel) : (tensor<1x3x224x224xf32>, tensor<64x3x7x7xf32>) -> tensor<1x64x112x112xf32>
// Conv3D: video processing [B, Cin, D, H, W] * [Cout, Cin, Kd, Kh, Kw]
%vid_feat = "tensor.conv3d"(%video, %kernel) : (tensor<1x3x16x112x112xf32>, tensor<64x3x3x3x3xf32>) -> tensor<1x64x14x110x110xf32>
// ConvTranspose2D: upsampling (decoder/generator)
%up = "tensor.conv_transpose2d"(%x, %kernel) : (tensor<1x64x16x16xf32>, tensor<64x32x4x4xf32>) -> tensor<1x32x32x32xf32>
// DepthwiseConv2D: MobileNet-style per-channel conv
%dw = "tensor.depthwise_conv2d"(%x, %kernel) : (tensor<1x64x32x32xf32>, tensor<64x1x3x3xf32>) -> tensor<1x64x32x32xf32>
// DilatedConv2D: large receptive field (segmentation)
%dilated = "tensor.dilated_conv2d"(%x, %kernel) : (tensor<1x64x64x64xf32>, tensor<64x64x3x3xf32>) -> tensor<1x64x64x64xf32>
2.7 Pooling (4)
| Operation | Syntax | Inputs |
|---|---|---|
| MaxPool2D | "tensor.maxpool2d" | 2 |
| AvgPool2D | "tensor.avgpool2d" | 2 |
| AdaptiveAvgPool2D | "tensor.adaptive_avgpool2d" | 1 |
| GlobalAvgPool | "tensor.global_avgpool" | 1 |
// MaxPool2D: downsample by taking maximum in each window
%p1 = "tensor.maxpool2d"(%x, %params) : (tensor<1x64x32x32xf32>, tensor<2xi32>) -> tensor<1x64x16x16xf32>
// AvgPool2D: downsample by averaging each window
%p2 = "tensor.avgpool2d"(%x, %params) : (tensor<1x64x32x32xf32>, tensor<2xi32>) -> tensor<1x64x16x16xf32>
// AdaptiveAvgPool2D: output always has fixed spatial size (e.g. 7x7)
%p3 = "tensor.adaptive_avgpool2d"(%x) : (tensor<1x512x14x14xf32>) -> tensor<1x512x7x7xf32>
// GlobalAvgPool: average all spatial dimensions → 1x1
%p4 = "tensor.global_avgpool"(%x) : (tensor<1x64x7x7xf32>) -> tensor<1x64x1x1xf32>
2.8 Recurrent (3)
| Operation | Syntax | FLOPs per step |
|---|---|---|
| LSTMCell | "tensor.lstm_cell" | 4*(in+hid)hid2 |
| GRUCell | "tensor.gru_cell" | 3*(in+hid)hid2 |
| RNNCell | "tensor.rnn_cell" | (in+hid)hid2 |
// LSTMCell: takes current input + previous hidden state, returns new hidden + cell state
%h_new, %c_new = "tensor.lstm_cell"(%x_t, %h_prev) : (tensor<1x128xf32>, tensor<1x256xf32>) -> (tensor<1x256xf32>, tensor<1x256xf32>)
// GRUCell: simpler than LSTM, single hidden state
%h_new = "tensor.gru_cell"(%x_t, %h_prev) : (tensor<1x128xf32>, tensor<1x256xf32>) -> tensor<1x256xf32>
// RNNCell: basic recurrent cell
%h_new = "tensor.rnn_cell"(%x_t, %h_prev) : (tensor<1x128xf32>, tensor<1x256xf32>) -> tensor<1x256xf32>
2.9 Advanced Math (11)
| Operation | Syntax | Complexity |
|---|---|---|
| Einsum | "tensor.einsum" | Varies |
| FFT | "tensor.fft" | O(n log n) |
| IFFT | "tensor.ifft" | O(n log n) |
| SVD | "tensor.svd" | O(mn min(m,n)) |
| Eig | "tensor.eig" | O(n³) |
| Solve | "tensor.solve" | O(n³) |
| TopK | "tensor.topk" | O(n log k) |
| Sort | "tensor.sort" | O(n log n) |
| Cumsum | "tensor.cumsum" | O(n) |
| Where | "tensor.where" | O(n) |
| Clamp | "tensor.clamp" | O(n) |
// Einsum: flexible tensor contraction (e.g. batch matmul)
%result = "tensor.einsum"(%a, %b) : (tensor<8x128x64xf32>, tensor<8x64x256xf32>) -> tensor<8x128x256xf32>
// FFT: Fast Fourier Transform (signal processing)
%freq = "tensor.fft"(%signal) : (tensor<1x1024xf32>) -> tensor<1x1024xf32>
// IFFT: Inverse FFT (frequency → time domain)
%time = "tensor.ifft"(%freq) : (tensor<1x1024xf32>) -> tensor<1x1024xf32>
// SVD: Singular Value Decomposition (compression, PCA)
%u = "tensor.svd"(%matrix) : (tensor<100x50xf32>) -> tensor<100x50xf32>
// TopK: get top-10 scores from 1000 classes
%top = "tensor.topk"(%scores) : (tensor<1x1000xf32>) -> tensor<1x10xf32>
// Sort: sort a tensor along last dimension
%sorted = "tensor.sort"(%x) : (tensor<1x100xf32>) -> tensor<1x100xf32>
// Cumsum: cumulative sum (prefix sum)
%cs = "tensor.cumsum"(%x) : (tensor<1x10xf32>) -> tensor<1x10xf32>
// Where: conditional selection (like numpy.where)
%selected = "tensor.where"(%cond, %a, %b) : (tensor<4xi1>, tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// Clamp: restrict values to [min, max] range
%clamped = "tensor.clamp"(%x, %min_val, %max_val) : (tensor<4xf32>, tensor<1xf32>, tensor<1xf32>) -> tensor<4xf32>
// Solve: solve linear system Ax = b
%solution = "tensor.solve"(%A, %b) : (tensor<64x64xf32>, tensor<64x1xf32>) -> tensor<64x1xf32>
// Eig: eigenvalue decomposition
%eigenvalues = "tensor.eig"(%matrix) : (tensor<32x32xf32>) -> tensor<32xf32>
2.10 Sparse (2)
"tensor.sparse_matmul", "tensor.sparse_embedding"
// SparseMatMul: sparse × dense matrix multiply (efficient for sparse models)
%result = "tensor.sparse_matmul"(%sparse_w, %x) : (tensor<10000x768xf32>, tensor<768x1xf32>) -> tensor<10000x1xf32>
// SparseEmbedding: sparse lookup (recommendation systems with huge vocab)
%emb = "tensor.sparse_embedding"(%sparse_ids, %table) : (tensor<1x50xi32>, tensor<1000000x128xf32>) -> tensor<1x50x128xf32>
2.11 Quantisation (6)
| Operation | Syntax | Direction |
|---|---|---|
| Quantize | "tensor.quantize" | f32 → i8 |
| Dequantize | "tensor.dequantize" | i8 → f32 |
| QuantizeInt4 | "tensor.quantize_int4" | f32 → i4 |
| DequantizeInt4 | "tensor.dequantize_int4" | i4 → f32 |
| QuantizeFp8 | "tensor.quantize_fp8" | f32 → fp8 |
| DequantizeFp8 | "tensor.dequantize_fp8" | fp8 → f32 |
// INT8 Quantisation: reduce model size 4x
%q8 = "tensor.quantize"(%weights) : (tensor<256x256xf32>) -> tensor<256x256xi8>
%dq8 = "tensor.dequantize"(%q8) : (tensor<256x256xi8>) -> tensor<256x256xf32>
// INT4 Quantisation: reduce model size 8x (GPTQ, AWQ style)
%q4 = "tensor.quantize_int4"(%weights) : (tensor<4096x4096xf32>) -> tensor<4096x4096xi4>
%dq4 = "tensor.dequantize_int4"(%q4) : (tensor<4096x4096xi4>) -> tensor<4096x4096xf32>
// FP8 Quantisation: H100 native format
%qfp8 = "tensor.quantize_fp8"(%weights) : (tensor<4096x4096xf32>) -> tensor<4096x4096xfp8e4m3>
%dqfp8 = "tensor.dequantize_fp8"(%qfp8) : (tensor<4096x4096xfp8e4m3>) -> tensor<4096x4096xf32>
2.12 Diffusion/Generative (3)
"tensor.unet_down_block" (2-3 in), "tensor.unet_up_block" (2-3 in), "tensor.timestep_embedding" (1 in)
// TimestepEmbedding: encode diffusion timestep as a vector
%t_emb = "tensor.timestep_embedding"(%timestep) : (tensor<1xi32>) -> tensor<1x256xf32>
// UNetDownBlock: encoder block of UNet (input + timestep embedding)
%down = "tensor.unet_down_block"(%x, %t_emb) : (tensor<1x64x64x64xf32>, tensor<1x256xf32>) -> tensor<1x128x32x32xf32>
// UNetUpBlock: decoder block of UNet (input + skip connection + timestep)
%up = "tensor.unet_up_block"(%x, %skip, %t_emb) : (tensor<1x128x32x32xf32>, tensor<1x128x32x32xf32>, tensor<1x256xf32>) -> tensor<1x64x64x64xf32>
2.13 GNN (2)
"tensor.gnn_message_passing" (2-3 in), "tensor.gnn_global_pooling" (1 in)
// GNNMessagePassing: propagate node features along edges
%h1 = "tensor.gnn_message_passing"(%nodes, %adj) : (tensor<50x16xf32>, tensor<50x50xf32>) -> tensor<50x16xf32>
// With edge features (3 inputs)
%h2 = "tensor.gnn_message_passing"(%nodes, %adj, %edge_feat) : (tensor<50x16xf32>, tensor<50x50xf32>, tensor<50x50x8xf32>) -> tensor<50x16xf32>
// GNNGlobalPooling: aggregate all node features into a single graph vector
%graph = "tensor.gnn_global_pooling"(%h2) : (tensor<50x16xf32>) -> tensor<1x16xf32>
2.14 MoE (2)
"tensor.moe_dispatch" (2-3 in), "tensor.moe_combine" (2-3 in)
// MoEDispatch: router sends tokens to top-k experts
%dispatched = "tensor.moe_dispatch"(%tokens, %router_logits) : (tensor<8x128x512xf32>, tensor<8x128x8xf32>) -> tensor<8x128x512xf32>
// MoECombine: merge expert outputs weighted by router
%combined = "tensor.moe_combine"(%expert_outputs, %router_weights) : (tensor<8x128x512xf32>, tensor<8x128x8xf32>) -> tensor<8x128x512xf32>
2.15 Constants (5) — Zero inputs
"tensor.constant", "tensor.zeros", "tensor.ones", "tensor.arange", "tensor.full"
// Zeros: create an all-zero tensor (e.g. initial hidden state)
%z = "tensor.zeros"() : () -> tensor<1x256xf32>
// Ones: create an all-one tensor (e.g. attention mask)
%mask = "tensor.ones"() : () -> tensor<1x128xi32>
// Arange: create [0, 1, 2, ..., 127] (e.g. position IDs)
%pos = "tensor.arange"() : () -> tensor<128xi32>
// Full: create a tensor filled with a specific value
%filled = "tensor.full"() : () -> tensor<1x64xf32>
// Constant: arbitrary constant tensor
%c = "tensor.constant"() : () -> tensor<3xf32>
2.16 Memory (3)
"tensor.checkpoint" (activation recompute), "tensor.offload" (to CPU), "tensor.grad_accumulate" (micro-batches)
// Checkpoint: recompute activations during backward instead of storing them
// Saves GPU memory at the cost of extra compute (critical for large models)
%ckpt = "tensor.checkpoint"(%activations) : (tensor<1x4096x4096xf32>) -> tensor<1x4096x4096xf32>
// Offload: move tensor from GPU to CPU memory (for very large models)
%offloaded = "tensor.offload"(%weights) : (tensor<8192x8192xf32>) -> tensor<8192x8192xf32>
// GradAccumulate: accumulate gradients over multiple micro-batches
// Used when actual batch doesn't fit in GPU memory
%acc = "tensor.grad_accumulate"(%grads) : (tensor<256x256xf32>) -> tensor<256x256xf32>
2.17 Gradient/Backward (8)
| Syntax | Forward Op |
|---|---|
"tensor.grad_matmul" | MatMul |
"tensor.grad_relu" | ReLU |
"tensor.grad_softmax" | Softmax |
"tensor.grad_layernorm" | LayerNorm |
"tensor.grad_attention" | Attention |
"tensor.grad_conv2d" | Conv2D |
"tensor.grad_linear" | Linear |
"tensor.grad_gelu" | GeLU |
// GradMatMul: backward pass for matrix multiplication
%grad_x = "tensor.grad_matmul"(%upstream_grad, %w) : (tensor<1x256xf32>, tensor<256x784xf32>) -> tensor<1x784xf32>
// GradReLU: backward pass for ReLU (zero where input was negative)
%grad_r = "tensor.grad_relu"(%upstream_grad, %relu_input) : (tensor<1x256xf32>, tensor<1x256xf32>) -> tensor<1x256xf32>
// GradSoftmax: backward pass for softmax
%grad_s = "tensor.grad_softmax"(%upstream_grad, %softmax_output) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
// GradLayerNorm: backward pass for layer normalisation
%grad_ln = "tensor.grad_layernorm"(%upstream_grad, %ln_input) : (tensor<1x128x64xf32>, tensor<1x128x64xf32>) -> tensor<1x128x64xf32>
// GradAttention: backward pass for attention
%grad_attn = "tensor.grad_attention"(%upstream_grad, %q, %k) : (tensor<1x128x64xf32>, tensor<1x128x64xf32>, tensor<1x128x64xf32>) -> tensor<1x128x64xf32>
// GradConv2D: backward pass for 2D convolution
%grad_conv = "tensor.grad_conv2d"(%upstream_grad, %conv_input) : (tensor<1x64x112x112xf32>, tensor<1x3x224x224xf32>) -> tensor<1x3x224x224xf32>
// GradLinear: backward pass for linear layer
%grad_lin = "tensor.grad_linear"(%upstream_grad, %w, %b) : (tensor<1x256xf32>, tensor<784x256xf32>, tensor<256xf32>) -> tensor<1x784xf32>
// GradGeLU: backward pass for GeLU activation
%grad_g = "tensor.grad_gelu"(%upstream_grad, %gelu_input) : (tensor<1x256xf32>, tensor<1x256xf32>) -> tensor<1x256xf32>
2.18 Parallelism (4)
"tensor.parallel_split", "tensor.parallel_allreduce", "tensor.pipeline_send", "tensor.pipeline_receive"
// ParallelSplit: split a batch across multiple GPUs (data parallelism)
%shard = "tensor.parallel_split"(%batch) : (tensor<32x128xf32>) -> tensor<8x128xf32>
// ParallelAllReduce: synchronise gradients across all GPUs
%synced = "tensor.parallel_allreduce"(%local_grad) : (tensor<256x256xf32>) -> tensor<256x256xf32>
// PipelineSend: send activation to the next pipeline stage (model parallelism)
%sent = "tensor.pipeline_send"(%activation) : (tensor<1x128x4096xf32>) -> tensor<1x128x4096xf32>
// PipelineReceive: receive activation from the previous pipeline stage
%recv = "tensor.pipeline_receive"(%placeholder) : (tensor<1x128x4096xf32>) -> tensor<1x128x4096xf32>
2.19 Fused Operations (6)
| Syntax | Equivalent |
|---|---|
"tensor.fused_matmul_bias_relu" | matmul+add+relu |
"tensor.fused_matmul_bias" | matmul+add |
"tensor.fused_linear_gelu" | linear+gelu |
"tensor.fused_attention_layernorm" | attention+layernorm |
"tensor.fused_linear_silu" | linear+silu |
"tensor.fused_conv_batchnorm_relu" | conv+bn+relu |
// FusedMatMulBiasReLU: 3 ops in 1 kernel (most common for MLP hidden layers)
%h = "tensor.fused_matmul_bias_relu"(%x, %w, %b) : (tensor<1x256xf32>, tensor<256x128xf32>, tensor<128xf32>) -> tensor<1x128xf32>
// FusedMatMulBias: matmul + bias only (no activation)
%h2 = "tensor.fused_matmul_bias"(%x, %w, %b) : (tensor<1x128xf32>, tensor<128x64xf32>, tensor<64xf32>) -> tensor<1x64xf32>
// FusedLinearGeLU: used in Transformer FFN (LLM inference)
%ffn = "tensor.fused_linear_gelu"(%x, %w, %b) : (tensor<1x128x4096xf32>, tensor<4096x16384xf32>, tensor<16384xf32>) -> tensor<1x128x16384xf32>
// FusedAttentionLayerNorm: attention + normalisation in one pass
%attn_ln = "tensor.fused_attention_layernorm"(%q, %k, %v, %scale) : (tensor<1x128x64xf32>, tensor<1x128x64xf32>, tensor<1x128x64xf32>, tensor<64xf32>) -> tensor<1x128x64xf32>
// FusedLinearSiLU: used in LLaMA/Mistral gate projections
%gate = "tensor.fused_linear_silu"(%x, %w, %b) : (tensor<1x128x4096xf32>, tensor<4096x11008xf32>, tensor<11008xf32>) -> tensor<1x128x11008xf32>
// FusedConvBatchNormReLU: standard CNN inference fusion
%feat = "tensor.fused_conv_batchnorm_relu"(%img, %w, %bn_s, %bn_b, %bn_m) : (tensor<1x3x224x224xf32>, tensor<64x3x7x7xf32>, tensor<64xf32>, tensor<64xf32>, tensor<64xf32>) -> tensor<1x64x112x112xf32>
Part III — The quantum Dialect (Quantum Computing)
Declare with #dialect quantum. Provides 50+ operations for quantum circuits.
Critical rule: all qubits follow the linearity rule — each qubit value must be consumed exactly once.
3.1 Standard 1-Qubit Gates (9)
All take 1 qubit, return 1 qubit: %q_out = "quantum.gate"(%q_in) : (qubit) -> qubit
| Gate | Syntax | Clifford | Self-Inverse | Description |
|---|---|---|---|---|
| Hadamard | "quantum.h" | Yes | Yes | Creates superposition |
| Pauli-X | "quantum.x" | Yes | Yes | Bit-flip |
| Pauli-Y | "quantum.y" | Yes | Yes | Y rotation |
| Pauli-Z | "quantum.z" | Yes | Yes | Phase-flip |
| S | "quantum.s" | Yes | No | sqrt(Z) |
| S† | "quantum.sdg" | Yes | No | S inverse |
| T | "quantum.t" | No | No | pi/8 gate |
| T† | "quantum.tdg" | No | No | T inverse |
| SX | "quantum.sx" | Yes | No | sqrt(X) |
%q1 = "quantum.h"(%q0) : (qubit) -> qubit
%q2 = "quantum.x"(%q1) : (qubit) -> qubit
%q3 = "quantum.t"(%q2) : (qubit) -> qubit
3.2 Parametric 1-Qubit Gates (7)
Take 1 qubit + angle attributes, return 1 qubit.
| Gate | Syntax | Parameters | Description |
|---|---|---|---|
| RX | "quantum.rx" | theta | X-axis rotation |
| RY | "quantum.ry" | theta | Y-axis rotation |
| RZ | "quantum.rz" | theta | Z-axis rotation |
| P | "quantum.p" | phi | Phase gate |
| U1 | "quantum.u1" | lambda | 1-param universal |
| U2 | "quantum.u2" | phi, lambda | 2-param universal |
| U3 | "quantum.u3" | theta, phi, lambda | 3-param universal (any 1Q gate) |
%q1 = "quantum.ry"(%q0) {angle = 1.5708} : (qubit) -> qubit
%q1 = "quantum.rz"(%q0) {angle = 0.785} : (qubit) -> qubit
%q1 = "quantum.u3"(%q0) {theta = 1.57, phi = 0.0, lambda = 3.14} : (qubit) -> qubit
3.3 Fixed-Angle 1-Qubit Gates (2)
| Gate | Syntax | Angle | Self-Inverse |
|---|---|---|---|
| Rx90 | "quantum.rx90" | pi/2 | No |
| Rx180 | "quantum.rx180" | pi | Yes |
// Rx90: fixed pi/2 rotation around X (commonly used in hardware)
%q1 = "quantum.rx90"(%q0) : (qubit) -> qubit
// Rx180: fixed pi rotation around X (equivalent to X gate)
%q2 = "quantum.rx180"(%q1) : (qubit) -> qubit
3.4 2-Qubit Gates (13)
All take 2 qubits, return 2 qubits: %a, %b = "quantum.gate"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
| Gate | Syntax | Native On | Parametric |
|---|---|---|---|
| CX (CNOT) | "quantum.cx" | IBM | No |
| CZ | "quantum.cz" | No | |
| CY | "quantum.cy" | — | No |
| SWAP | "quantum.swap" | — | No |
| iSWAP | "quantum.iswap" | No | |
| ECR | "quantum.ecr" | IBM Eagle | No |
| RZX | "quantum.rzx" | — | Yes |
| XX | "quantum.xx" | IonQ | Yes |
| YY | "quantum.yy" | — | Yes |
| ZZ | "quantum.zz" | Quantinuum | Yes |
| CP | "quantum.cp" | — | Yes |
| CPhase | "quantum.cphase" | Rigetti | Yes |
| XY | "quantum.xy" | Rigetti | Yes |
IonQ native gates (3)
| Gate | Syntax | Description |
|---|---|---|
| GPI | "quantum.gpi" | IonQ single-qubit gate |
| GPI2 | "quantum.gpi2" | IonQ single-qubit gate 2 |
| MS | "quantum.ms" | Mølmer-Sørensen (IonQ 2-qubit) |
// CX (CNOT): controlled NOT, fundamental entangling gate (IBM native)
%q2, %q3 = "quantum.cx"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// CZ: controlled-Z (Google Sycamore native)
%q2, %q3 = "quantum.cz"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// SWAP: exchange two qubit states
%q2, %q3 = "quantum.swap"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// ECR: echoed cross-resonance (IBM Eagle/Heron native)
%q2, %q3 = "quantum.ecr"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// ZZ: parametric Ising ZZ (Quantinuum native)
%q2, %q3 = "quantum.zz"(%q0, %q1) {angle = 0.5} : (qubit, qubit) -> (qubit, qubit)
// XX: parametric Ising XX (IonQ native)
%q2, %q3 = "quantum.xx"(%q0, %q1) {angle = 1.5708} : (qubit, qubit) -> (qubit, qubit)
// CP: controlled-phase gate (parametric)
%q2, %q3 = "quantum.cp"(%q0, %q1) {angle = 0.7854} : (qubit, qubit) -> (qubit, qubit)
// CPhase: Rigetti native controlled-phase
%q2, %q3 = "quantum.cphase"(%q0, %q1) {angle = 1.5708} : (qubit, qubit) -> (qubit, qubit)
// XY: Rigetti native XY interaction
%q2, %q3 = "quantum.xy"(%q0, %q1) {angle = 0.5} : (qubit, qubit) -> (qubit, qubit)
// iSWAP: imaginary SWAP (Google Sycamore)
%q2, %q3 = "quantum.iswap"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
3.5 3-Qubit Gates (2)
| Gate | Syntax | Description |
|---|---|---|
| CCX (Toffoli) | "quantum.ccx" | Controlled-Controlled-NOT |
| CSWAP (Fredkin) | "quantum.cswap" | Controlled-SWAP |
// CCX (Toffoli): 2 controls + 1 target, flips target if both controls are |1>
%a, %b, %c = "quantum.ccx"(%q0, %q1, %q2) : (qubit, qubit, qubit) -> (qubit, qubit, qubit)
// CSWAP (Fredkin): controlled swap, swaps q1/q2 if q0 is |1>
%d, %e, %f = "quantum.cswap"(%q3, %q4, %q5) : (qubit, qubit, qubit) -> (qubit, qubit, qubit)
3.6 Multi-Controlled Gates (2)
| Gate | Syntax | Qubits |
|---|---|---|
| MCX | "quantum.mcx" | N (variable) |
| MCZ | "quantum.mcz" | N (variable) |
// MCX with 4 qubits: 3 controls + 1 target
%a, %b, %c, %d = "quantum.mcx"(%q0, %q1, %q2, %q3) : (qubit, qubit, qubit, qubit) -> (qubit, qubit, qubit, qubit)
// MCZ with 3 qubits: 2 controls + 1 target
%e, %f, %g = "quantum.mcz"(%q4, %q5, %q6) : (qubit, qubit, qubit) -> (qubit, qubit, qubit)
3.7 Measurement and Control (8)
| Operation | Syntax | In | Out | Description |
|---|---|---|---|---|
| Measure | "quantum.measure" | 1 qubit | qubit | Measure qubit |
| MeasureAll | "quantum.measure_all" | N | N | Measure all |
| Reset | "quantum.reset" | 1 qubit | qubit | Reset to |0> |
| Barrier | "quantum.barrier" | N | — | Prevent reordering |
| Init | "quantum.init" | 1 qubit | qubit | Initialise register |
| Delay | "quantum.delay" | — | — | Time delay |
| VirtualRZ | "quantum.virtual_rz" | 1 qubit | qubit | Zero-cost virtual Z |
| IfElse | "quantum.if_else" | — | — | Classical conditional |
| ParamGate | "quantum.param_gate" | — | — | Generic parameterised gate |
%m = "quantum.measure"(%q0) : (qubit) -> qubit
%r = "quantum.reset"(%q0) : (qubit) -> qubit
%q1 = "quantum.virtual_rz"(%q0) {angle = 0.785} : (qubit) -> qubit
3.8 Hardware Native Gate Sets
Each provider has a fixed set of natively supported gates. All other gates are decomposed automatically.
| Provider | Native Gates |
|---|---|
| IBM Eagle / Kyoto | rz, sx, x, cx, ecr |
| Rigetti | rz, rx, cz, cphase, xy |
| IonQ | gpi, gpi2, ms |
| Quantinuum | rz, rx, ry, zz |
| Simulator | h, x, y, z, s, t, rx, ry, rz, cx, cz, ccx, swap |
Example — same entangling operation on different hardware:
// IBM Eagle: uses CX (CNOT) natively
%a, %b = "quantum.cx"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// Google Sycamore: uses CZ natively
%a, %b = "quantum.cz"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// IonQ: uses MS (Mølmer-Sørensen) natively
%a, %b = "quantum.ms"(%q0, %q1) : (qubit, qubit) -> (qubit, qubit)
// Quantinuum: uses ZZ natively
%a, %b = "quantum.zz"(%q0, %q1) {angle = 1.5708} : (qubit, qubit) -> (qubit, qubit)
// Rigetti: uses CPhase natively
%a, %b = "quantum.cphase"(%q0, %q1) {angle = 3.14159} : (qubit, qubit) -> (qubit, qubit)
Note: LIFT automatically decomposes non-native gates into the target hardware's native set during compilation. You can write using any gate and the compiler handles the rest.
3.9 Gate Properties
| Property | What it means | Checked by |
|---|---|---|
| num_qubits | Expected input count | Compile-time verification |
| is_parametric | Needs angle attributes | Attribute validation |
| is_self_inverse | G·G = Identity | Gate cancellation pass |
| is_clifford | Efficient classical simulation | Optimiser heuristics |
| is_entangling | Creates entanglement | Circuit analysis |
| is_measurement | Collapses state | Control flow analysis |
3.10 Qubit Linearity Rule
The most important rule in the quantum dialect.
Every qubit must be consumed exactly once:
// CORRECT
%q1 = "quantum.h"(%q0) : (qubit) -> qubit // q0 consumed → q1 produced
%q2, %q3 = "quantum.cx"(%q1, %q_b) : ... // q1 consumed
// ERROR: q0 used twice (no-cloning violation)
%q1 = "quantum.h"(%q0) : (qubit) -> qubit
%q2 = "quantum.x"(%q0) : (qubit) -> qubit // COMPILE ERROR
// ERROR: q1 never used (qubit leak)
%q1 = "quantum.h"(%q0) : (qubit) -> qubit
return // COMPILE ERROR
3.11 Complete Bell State Example
#dialect quantum
module @bell_state {
func @bell(%q0: qubit, %q1: qubit) -> (qubit, qubit) {
%q2 = "quantum.h"(%q0) : (qubit) -> qubit
%q3, %q4 = "quantum.cx"(%q2, %q1) : (qubit, qubit) -> (qubit, qubit)
return %q3, %q4
}
}
3.12 Complete GHZ State Example (3 qubits)
#dialect quantum
module @ghz {
func @ghz3(%q0: qubit, %q1: qubit, %q2: qubit) -> (qubit, qubit, qubit) {
%a = "quantum.h"(%q0) : (qubit) -> qubit
%b, %c = "quantum.cx"(%a, %q1) : (qubit, qubit) -> (qubit, qubit)
%d, %e = "quantum.cx"(%c, %q2) : (qubit, qubit) -> (qubit, qubit)
return %b, %d, %e
}
}
3.13 Complete Variational Circuit Example
#dialect quantum
module @variational {
func @layer(%q0: qubit, %q1: qubit) -> (qubit, qubit) {
// RY rotations (parametric)
%a = "quantum.ry"(%q0) {angle = 0.5} : (qubit) -> qubit
%b = "quantum.ry"(%q1) {angle = 1.2} : (qubit) -> qubit
// Entangling
%c, %d = "quantum.cx"(%a, %b) : (qubit, qubit) -> (qubit, qubit)
// More rotations
%e = "quantum.rz"(%c) {angle = 0.3} : (qubit) -> qubit
%f = "quantum.rz"(%d) {angle = 0.7} : (qubit) -> qubit
return %e, %f
}
}
Part IV — The hybrid Dialect (Classical + Quantum Bridge)
Declare with #dialect hybrid (usually combined with #dialect tensor and #dialect quantum). Provides 21 operations that bridge classical and quantum computing.
4.1 Encoding / Decoding (2)
| Operation | Syntax | Description |
|---|---|---|
| Encode | "hybrid.encode" | Classical tensor → quantum state |
| Decode | "hybrid.decode" | Quantum state → classical tensor |
%encoded = "hybrid.encode"(%data) {strategy = "angle"} : (tensor<1x4xf32>) -> qubit
%decoded = "hybrid.decode"(%qstate) : (qubit) -> tensor<1x4xf32>
Encoding Strategies
| Strategy | Attribute Value | Qubits for N features | Depth | Best For |
|---|---|---|---|---|
| Angle | "angle" | N | 1 | Small vectors (<20) |
| Amplitude | "amplitude" | ceil(log2(N)) | N | Large vectors |
| Basis | "basis" | N | 1 | Binary data |
| IQP | "iqp" | N | 2N | High expressivity |
| Hamiltonian | "hamiltonian" | N | N | Physics problems |
| Kernel | "kernel" | N | 3N | Quantum kernel methods |
4.2 Gradient Methods (6)
| Operation | Syntax | Evaluations | Exact |
|---|---|---|---|
| ParameterShift | "hybrid.parameter_shift" | 2N | Yes |
| FiniteDifference | "hybrid.finite_difference" | N+1 | No |
| SPSA | "hybrid.spsa" | 2 | No |
| AdjointDiff | "hybrid.adjoint_diff" | 1 | Yes |
| StochasticParamShift | "hybrid.stochastic_param_shift" | 2 | No |
| JointGradient | "hybrid.joint_gradient" | Variable | Mixed |
Which to choose
| Situation | Method |
|---|---|
| Few params (<50) | Parameter Shift |
| Many params (>100) | SPSA |
| Simulator only | Adjoint Diff |
| Mixed classical+quantum | Joint Gradient |
| Noisy hardware | Stochastic Parameter Shift |
// ParameterShift: exact gradient via 2 circuit evaluations per parameter
%grad1 = "hybrid.parameter_shift"(%expectation) : (tensor<1xf32>) -> tensor<1x16xf32>
// FiniteDifference: approximate gradient via N+1 evaluations
%grad2 = "hybrid.finite_difference"(%expectation) : (tensor<1xf32>) -> tensor<1x16xf32>
// SPSA: stochastic gradient, only 2 evaluations regardless of parameter count
%grad3 = "hybrid.spsa"(%expectation) : (tensor<1xf32>) -> tensor<1x16xf32>
// AdjointDiff: exact gradient in 1 evaluation (simulator only)
%grad4 = "hybrid.adjoint_diff"(%expectation) : (tensor<1xf32>) -> tensor<1x16xf32>
// JointGradient: use different methods for classical vs quantum parts
%grad5 = "hybrid.joint_gradient"(%hybrid_loss) : (tensor<1xf32>) -> tensor<1x32xf32>
4.3 Variational Algorithms (4)
| Operation | Syntax | Description |
|---|---|---|
| VqcLayer | "hybrid.vqc_layer" | Generic variational circuit layer |
| VqeAnsatz | "hybrid.vqe_ansatz" | VQE chemistry ansatz |
| QaoaLayer | "hybrid.qaoa_layer" | QAOA combinatorial optimisation |
| QuantumKernel | "hybrid.quantum_kernel" | Quantum kernel (SVM) |
Ansatz Types
| Type | Value | Use |
|---|---|---|
| HardwareEfficient | "hardware_efficient" | Near-term hardware |
| StronglyEntangling | "strongly_entangling" | Max expressivity |
| TwoLocal | "two_local" | General purpose |
| UCCSD | "uccsd" | Chemistry (VQE) |
| Custom | "custom" | User-defined |
%q_out = "hybrid.vqc_layer"(%q_in) {ansatz = "hardware_efficient", layers = 3} : (qubit) -> qubit
%q_out = "hybrid.vqe_ansatz"(%q_in) {ansatz = "uccsd"} : (qubit) -> qubit
%q_out = "hybrid.qaoa_layer"(%q_in) {gamma = 0.5, beta = 0.3} : (qubit) -> qubit
4.4 Data Transfer (2)
| Operation | Syntax | Direction |
|---|---|---|
| GpuToQpu | "hybrid.gpu_to_qpu" | GPU → QPU |
| QpuToGpu | "hybrid.qpu_to_gpu" | QPU → GPU |
%qubits = "hybrid.gpu_to_qpu"(%encoded) : (tensor<1x4xf32>) -> qubit
%results = "hybrid.qpu_to_gpu"(%measured) : (qubit) -> tensor<1x4xf32>
4.5 Processing (4)
| Operation | Syntax | Description |
|---|---|---|
| ClassicalPreprocess | "hybrid.classical_preprocess" | Pre-quantum classical processing |
| QuantumPostprocess | "hybrid.quantum_postprocess" | Post-quantum processing |
| HybridForward | "hybrid.forward" | Full hybrid forward pass |
| HybridBackward | "hybrid.backward" | Full hybrid backward pass |
// ClassicalPreprocess: transform classical data before quantum encoding
%prep = "hybrid.classical_preprocess"(%raw_data) : (tensor<1x100xf32>) -> tensor<1x8xf32>
// QuantumPostprocess: transform quantum measurement results
%post = "hybrid.quantum_postprocess"(%raw_measurement) : (tensor<4096xi32>) -> tensor<1x4xf32>
// HybridForward: execute the full classical+quantum forward pass
%fwd = "hybrid.forward"(%input) : (tensor<1x64xf32>) -> tensor<1x2xf32>
// HybridBackward: compute gradients through the full hybrid pipeline
%bwd = "hybrid.backward"(%loss) : (tensor<1xf32>) -> tensor<1x64xf32>
4.6 Co-Execution (1)
| Operation | Syntax | Description |
|---|---|---|
| CoExecute | "hybrid.co_execute" | Run GPU + QPU simultaneously |
Synchronisation Policies
| Policy | Value | Description |
|---|---|---|
| Blocking | "blocking" | GPU waits for QPU |
| Asynchronous | "async" | Independent execution |
| Pipeline | "pipeline" | Streaming tasks |
%result = "hybrid.co_execute"(%gpu_task, %qpu_task) {sync = "pipeline"} : (tensor<1x128xf32>, qubit) -> tensor<1x128xf32>
4.7 Measurement (2)
| Operation | Syntax | Output | Description |
|---|---|---|---|
| MeasureExpectation | "hybrid.measure_expectation" | scalar | Expectation value |
| MeasureSamples | "hybrid.measure_samples" | tensor | Raw shot results |
%val = "hybrid.measure_expectation"(%qubits) : (qubit) -> tensor<1xf32>
%samples = "hybrid.measure_samples"(%qubits) {shots = 4096} : (qubit) -> tensor<4096xi32>
4.8 Feature Maps (for quantum kernels)
| Feature Map | Description |
|---|---|
| ZZFeatureMap | ZZ interactions |
| PauliFeatureMap | Pauli products |
| AngleEncoding | Rotation encoding |
| AmplitudeEncoding | State amplitude |
// Quantum kernel with ZZ feature map: compute kernel value between two data points
%kernel_val = "hybrid.quantum_kernel"(%encoded_x1, %encoded_x2) {feature_map = "zz"} : (qubit, qubit) -> tensor<1x1xf32>
// Quantum kernel with Pauli feature map
%kernel_val2 = "hybrid.quantum_kernel"(%encoded_a, %encoded_b) {feature_map = "pauli"} : (qubit, qubit) -> tensor<1x1xf32>
4.9 Complete Hybrid Example — Medical Imaging (CNN + VQC)
#dialect tensor
#dialect quantum
#dialect hybrid
module @medical_hybrid {
func @classify(
%img: tensor<1x1x28x28xf32>,
%conv_w: tensor<16x1x3x3xf32>,
%fc_w: tensor<784x4xf32>,
%fc_b: tensor<4xf32>,
%q0: qubit, %q1: qubit, %q2: qubit, %q3: qubit
) -> tensor<1x2xf32> {
// Classical preprocessing: CNN feature extraction
%feat = "tensor.conv2d"(%img, %conv_w) : (tensor<1x1x28x28xf32>, tensor<16x1x3x3xf32>) -> tensor<1x16x26x26xf32>
%act = "tensor.relu"(%feat) : (tensor<1x16x26x26xf32>) -> tensor<1x16x26x26xf32>
%pool = "tensor.global_avgpool"(%act) : (tensor<1x16x26x26xf32>) -> tensor<1x16x1x1xf32>
%flat = "tensor.reshape"(%pool) : (tensor<1x16x1x1xf32>) -> tensor<1x16xf32>
// Reduce to 4 features for 4 qubits
%reduced = "tensor.linear"(%flat, %fc_w, %fc_b) : (tensor<1x16xf32>, tensor<16x4xf32>, tensor<4xf32>) -> tensor<1x4xf32>
// Encode into quantum state
%encoded = "hybrid.encode"(%reduced) {strategy = "angle"} : (tensor<1x4xf32>) -> qubit
// Quantum processing
%q_a = "hybrid.vqc_layer"(%encoded) {ansatz = "hardware_efficient", layers = 2} : (qubit) -> qubit
// Measure expectation values
%expectation = "hybrid.measure_expectation"(%q_a) : (qubit) -> tensor<1x2xf32>
// Classical postprocessing
%probs = "tensor.softmax"(%expectation) : (tensor<1x2xf32>) -> tensor<1x2xf32>
return %probs
}
}
4.10 Complete Hybrid Example — VQE for Chemistry
#dialect tensor
#dialect quantum
#dialect hybrid
module @vqe_molecule {
func @energy_estimation(
%params: tensor<1x16xf32>,
%q0: qubit, %q1: qubit, %q2: qubit, %q3: qubit
) -> tensor<1xf32> {
// Encode parameters into quantum state
%encoded = "hybrid.encode"(%params) {strategy = "amplitude"} : (tensor<1x16xf32>) -> qubit
// Apply VQE ansatz (UCCSD for chemistry)
%ansatz_out = "hybrid.vqe_ansatz"(%encoded) {ansatz = "uccsd"} : (qubit) -> qubit
// Measure energy expectation
%energy = "hybrid.measure_expectation"(%ansatz_out) : (qubit) -> tensor<1xf32>
// Compute gradient for parameter update
%grad = "hybrid.parameter_shift"(%energy) : (tensor<1xf32>) -> tensor<1x16xf32>
return %energy
}
}
4.11 Complete Hybrid Example — QAOA for Optimisation
#dialect tensor
#dialect quantum
#dialect hybrid
module @qaoa_portfolio {
func @optimise(
%gamma: tensor<1xf32>,
%beta: tensor<1xf32>,
%q0: qubit, %q1: qubit, %q2: qubit, %q3: qubit, %q4: qubit
) -> tensor<1xf32> {
// QAOA layer with problem-specific parameters
%q_out = "hybrid.qaoa_layer"(%q0) {gamma = 0.5, beta = 0.3} : (qubit) -> qubit
// Sample the result
%samples = "hybrid.measure_samples"(%q_out) {shots = 8192} : (qubit) -> tensor<8192xi32>
// Classical post-processing: evaluate cost function
%cost = "hybrid.quantum_postprocess"(%samples) : (tensor<8192xi32>) -> tensor<1xf32>
return %cost
}
}
Part V — Configuration (.lith Files)
The .lith file controls compilation, optimisation, budgets, and hardware targeting. It uses a simple INI format.
5.1 File Format
# Comment (ignored)
// Also a comment
[section_name]
key = value
key2 = "quoted value"
5.2 [target] Section
| Key | Type | Values | Default |
|---|---|---|---|
backend | string | llvm, onnx, qasm | llvm |
device | string | A100, H100, ibm_eagle, ibm_kyoto, rigetti, ionq, quantinuum | none |
precision | string | fp64, fp32, fp16, bf16 | fp32 |
[target]
backend = llvm
device = A100
precision = fp32
llvmbackend → exports to LLVM IR (CUDA PTX, x86-64, ARM)onnxbackend → exports to ONNX protobuf text (opset 21, PyTorch/TensorFlow/TensorRT interop)qasmbackend → exports to OpenQASM 3.0 (IBM Quantum, Amazon Braket, Azure Quantum)
5.3 [budget] Section
All fields are optional. Omitted fields impose no constraint.
| Key | Type | Description |
|---|---|---|
max_flops | u64 | Maximum FLOPs allowed |
max_memory_bytes | u64 | Maximum memory in bytes |
max_time_ms | f64 | Maximum execution time (ms) |
min_fidelity | f64 | Minimum quantum fidelity (0.0–1.0) |
max_circuit_depth | usize | Maximum quantum circuit depth |
[budget]
max_flops = 10000000000
max_memory_bytes = 80000000000
max_time_ms = 100.0
min_fidelity = 0.90
max_circuit_depth = 1000
5.4 [optimisation] Section
| Key | Type | Values | Default |
|---|---|---|---|
level | enum | O0, O1, O2, O3 | O2 |
max_iterations | usize | Any positive integer | 10 |
passes | comma-separated list | Any names from the table below | none (derived from level) |
disabled_passes | comma-separated list | Any names from the table below | none |
passes, when set, overrides level entirely. disabled_passes is always
subtracted from the effective list afterwards, whether it came from passes
or from level. Unknown pass names are dropped with a warning
(OptimisationConfig::validate()).
[optimisation]
level = O2
max_iterations = 10
Optimisation Levels
| Level | What it does |
|---|---|
| O0 | No optimisation |
| O1 | Canonicalize + Constant Folding + Dead Code Elimination |
| O2 | O1 + CSE + Tensor Fusion |
| O3 | O2 + Flash Attention + Quantisation Pass + Gate Cancellation + Rotation Merge + Noise-Aware Schedule + Layout Mapping + Gate Decomposition + Real Routing (all 13 passes) |
Default passes at O2
canonicalize, constant-folding, dce, cse, tensor-fusion
All available optimisation passes
| Pass | Dialect | Description |
|---|---|---|
canonicalize | All | Simplify operations to canonical forms |
constant-folding | Tensor | Evaluate constant expressions at compile time |
dce | All | Remove dead (unused) operations |
cse | All | Common Subexpression Elimination |
tensor-fusion | Tensor | Fuse adjacent tensor operations into single kernels |
flash-attention | Tensor | Replace standard attention with flash attention |
quantisation-pass | Tensor | Annotate compute-heavy ops for INT8/INT4/FP8 quantisation |
gate-cancellation | Quantum | Cancel adjacent (and non-consecutive) inverse gates (H·H=I, X·X=I, S·Sdg=I, T·Tdg=I) |
rotation-merge | Quantum | Merge consecutive (and non-consecutive) rotations (RZ(a)·RZ(b)=RZ(a+b)) |
noise-aware-schedule | Quantum | Schedule gates considering hardware noise |
layout-mapping | Quantum | Legacy pass: annotates non-adjacent 2-qubit gates with needs_swap = true. Does not insert SWAPs or route — that's real-routing's job. Not a SABRE implementation. |
gate-decomposition | Quantum | Replace H/T/Tdg/S/Sdg/Y/RX with the [quantum] provider's native gate set |
real-routing | Quantum | Insert real quantum.swap ops (BFS shortest path) so 2-qubit gates land on connected physical qubits |
5.5 [simulation] Section
| Key | Type | Default |
|---|---|---|
shape_propagation | bool | true |
flop_counting | bool | true |
memory_analysis | bool | true |
noise_simulation | bool | true |
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = true
5.6 [quantum] Section
Only needed for quantum or hybrid programs.
| Key | Type | Values | Default |
|---|---|---|---|
topology | string | grid, heavy_hex, all_to_all, linear, tree | linear |
num_qubits | usize | Any positive integer | 5 |
provider | string | ibm/ibm_eagle, ibm_kyoto, rigetti, ionq, quantinuum, simulator/sim | none (falls back to simulator, where every gate is native) |
error_mitigation | string | Mitigation strategy name | none |
shots | usize | Number of measurement shots | none |
provider drives the gate-decomposition pass's target native gate set and
real-routing's topology defaults; it's read here rather than from
[target].
[quantum]
topology = heavy_hex
num_qubits = 127
provider = ibm_kyoto
error_mitigation = zne
shots = 8192
Quantum Topologies
| Topology | Description | Provider |
|---|---|---|
linear | Qubits in a line | General |
grid | 2D grid | Google Sycamore |
heavy_hex | Heavy-hexagonal lattice | IBM Eagle/Heron |
all_to_all | Full connectivity | IonQ, Quantinuum |
tree | Tree structure | Custom |
5.7 Complete .lith Examples
Classical AI (GPU inference)
# config_gpu.lith — Optimised GPU inference
[target]
backend = llvm
device = A100
precision = fp16
[budget]
max_memory_bytes = 16000000000
max_time_ms = 50.0
[optimisation]
level = O3
max_iterations = 20
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = false
Quantum circuit (IBM hardware)
# config_ibm.lith — IBM Eagle quantum processor
[target]
backend = qasm
device = ibm_eagle
[budget]
min_fidelity = 0.85
max_circuit_depth = 500
[optimisation]
level = O3
max_iterations = 15
[simulation]
shape_propagation = false
flop_counting = false
memory_analysis = false
noise_simulation = true
[quantum]
topology = heavy_hex
num_qubits = 127
error_mitigation = zne
shots = 4096
Hybrid (GPU + QPU)
# config_hybrid.lith — Medical imaging hybrid
[target]
backend = llvm
device = A100
precision = fp32
[budget]
max_memory_bytes = 40000000000
max_time_ms = 10000.0
min_fidelity = 0.80
max_circuit_depth = 200
[optimisation]
level = O2
max_iterations = 10
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = true
[quantum]
topology = heavy_hex
num_qubits = 16
shots = 4096
Edge deployment (low power)
# config_edge.lith — Edge device deployment
[target]
backend = llvm
device = ARM
precision = fp16
[budget]
max_memory_bytes = 500000000
max_time_ms = 30.0
[optimisation]
level = O3
max_iterations = 30
[simulation]
shape_propagation = true
flop_counting = true
memory_analysis = true
noise_simulation = false
Part VI — Assembling Dialects Together
6.1 Single-Dialect Programs
Tensor only — MLP classifier
#dialect tensor
module @mlp {
func @forward(
%x: tensor<1x784xf32>,
%w1: tensor<784x256xf32>, %b1: tensor<256xf32>,
%w2: tensor<256x10xf32>, %b2: tensor<10xf32>
) -> tensor<1x10xf32> {
%h1 = "tensor.matmul"(%x, %w1) : (tensor<1x784xf32>, tensor<784x256xf32>) -> tensor<1x256xf32>
%h2 = "tensor.add"(%h1, %b1) : (tensor<1x256xf32>, tensor<256xf32>) -> tensor<1x256xf32>
%h3 = "tensor.relu"(%h2) : (tensor<1x256xf32>) -> tensor<1x256xf32>
%h4 = "tensor.matmul"(%h3, %w2) : (tensor<1x256xf32>, tensor<256x10xf32>) -> tensor<1x10xf32>
%h5 = "tensor.add"(%h4, %b2) : (tensor<1x10xf32>, tensor<10xf32>) -> tensor<1x10xf32>
%out = "tensor.softmax"(%h5) : (tensor<1x10xf32>) -> tensor<1x10xf32>
return %out
}
}
Tensor only — Transformer self-attention
#dialect tensor
module @transformer {
func @self_attention(
%q: tensor<1x128x64xf32>,
%k: tensor<1x128x64xf32>,
%v: tensor<1x128x64xf32>,
%norm_w: tensor<64xf32>
) -> tensor<1x128x64xf32> {
%attn = "tensor.attention"(%q, %k, %v) : (tensor<1x128x64xf32>, tensor<1x128x64xf32>, tensor<1x128x64xf32>) -> tensor<1x128x64xf32>
%normed = "tensor.layernorm"(%attn, %norm_w) : (tensor<1x128x64xf32>, tensor<64xf32>) -> tensor<1x128x64xf32>
return %normed
}
}
Tensor only — CNN for image classification
#dialect tensor
module @cnn {
func @forward(
%img: tensor<1x3x224x224xf32>,
%conv1_w: tensor<64x3x7x7xf32>,
%bn_s: tensor<64xf32>, %bn_b: tensor<64xf32>, %bn_m: tensor<64xf32>,
%fc_w: tensor<1024x1000xf32>, %fc_b: tensor<1000xf32>
) -> tensor<1x1000xf32> {
// Conv + BatchNorm + ReLU (fused)
%c1 = "tensor.fused_conv_batchnorm_relu"(%img, %conv1_w, %bn_s, %bn_b, %bn_m) : (tensor<1x3x224x224xf32>, tensor<64x3x7x7xf32>, tensor<64xf32>, tensor<64xf32>, tensor<64xf32>) -> tensor<1x64x112x112xf32>
// Pooling
%p1 = "tensor.global_avgpool"(%c1) : (tensor<1x64x112x112xf32>) -> tensor<1x64x1x1xf32>
%flat = "tensor.reshape"(%p1) : (tensor<1x64x1x1xf32>) -> tensor<1x64xf32>
// Classifier
%logits = "tensor.linear"(%flat, %fc_w, %fc_b) : (tensor<1x64xf32>, tensor<64x1000xf32>, tensor<1000xf32>) -> tensor<1x1000xf32>
%probs = "tensor.softmax"(%logits) : (tensor<1x1000xf32>) -> tensor<1x1000xf32>
return %probs
}
}
Tensor only — GNN
#dialect tensor
module @gnn {
func @forward(
%nodes: tensor<100x16xf32>,
%edges: tensor<100x100xf32>,
%w: tensor<16x16xf32>, %b: tensor<16xf32>
) -> tensor<1x16xf32> {
// Message passing
%h1 = "tensor.gnn_message_passing"(%nodes, %edges) : (tensor<100x16xf32>, tensor<100x100xf32>) -> tensor<100x16xf32>
%h2 = "tensor.relu"(%h1) : (tensor<100x16xf32>) -> tensor<100x16xf32>
// Second layer
%h3 = "tensor.gnn_message_passing"(%h2, %edges) : (tensor<100x16xf32>, tensor<100x100xf32>) -> tensor<100x16xf32>
// Global pooling
%graph = "tensor.gnn_global_pooling"(%h3) : (tensor<100x16xf32>) -> tensor<1x16xf32>
return %graph
}
}
Quantum only — Quantum Teleportation
#dialect quantum
module @teleportation {
func @teleport(%psi: qubit, %q1: qubit, %q2: qubit) -> (qubit, qubit, qubit) {
// Create Bell pair between q1 and q2
%a = "quantum.h"(%q1) : (qubit) -> qubit
%b, %c = "quantum.cx"(%a, %q2) : (qubit, qubit) -> (qubit, qubit)
// Bell measurement on psi and b
%d, %e = "quantum.cx"(%psi, %b) : (qubit, qubit) -> (qubit, qubit)
%f = "quantum.h"(%d) : (qubit) -> qubit
// Measure
%m1 = "quantum.measure"(%f) : (qubit) -> qubit
%m2 = "quantum.measure"(%e) : (qubit) -> qubit
return %m1, %m2, %c
}
}
Quantum only — Quantum Fourier Transform (3 qubits)
#dialect quantum
module @qft {
func @qft3(%q0: qubit, %q1: qubit, %q2: qubit) -> (qubit, qubit, qubit) {
// First qubit
%a = "quantum.h"(%q0) : (qubit) -> qubit
%b, %c = "quantum.cp"(%q1, %a) {angle = 1.5708} : (qubit, qubit) -> (qubit, qubit)
%d, %e = "quantum.cp"(%q2, %c) {angle = 0.7854} : (qubit, qubit) -> (qubit, qubit)
// Second qubit
%f = "quantum.h"(%b) : (qubit) -> qubit
%g, %h = "quantum.cp"(%d, %f) {angle = 1.5708} : (qubit, qubit) -> (qubit, qubit)
// Third qubit
%i = "quantum.h"(%g) : (qubit) -> qubit
// Swap first and last
%j, %k = "quantum.swap"(%e, %i) : (qubit, qubit) -> (qubit, qubit)
return %j, %h, %k
}
}
6.2 Multi-Dialect Programs
Tensor + Quantum — Feature extraction + quantum classification
#dialect tensor
#dialect quantum
#dialect hybrid
module @hybrid_classifier {
func @forward(
%img: tensor<1x1x28x28xf32>,
%w1: tensor<16x1x5x5xf32>,
%w2: tensor<256x4xf32>, %b2: tensor<4xf32>,
%q0: qubit, %q1: qubit, %q2: qubit, %q3: qubit
) -> tensor<1x2xf32> {
// ──── Stage 1: Classical (tensor dialect) ────
%conv = "tensor.conv2d"(%img, %w1) : (tensor<1x1x28x28xf32>, tensor<16x1x5x5xf32>) -> tensor<1x16x24x24xf32>
%act = "tensor.relu"(%conv) : (tensor<1x16x24x24xf32>) -> tensor<1x16x24x24xf32>
%pool = "tensor.adaptive_avgpool2d"(%act) : (tensor<1x16x24x24xf32>) -> tensor<1x16x4x4xf32>
%flat = "tensor.reshape"(%pool) : (tensor<1x16x4x4xf32>) -> tensor<1x256xf32>
%features = "tensor.linear"(%flat, %w2, %b2) : (tensor<1x256xf32>, tensor<256x4xf32>, tensor<4xf32>) -> tensor<1x4xf32>
// ──── Stage 2: Encoding (hybrid dialect) ────
%encoded = "hybrid.encode"(%features) {strategy = "angle"} : (tensor<1x4xf32>) -> qubit
// ──── Stage 3: Quantum circuit (quantum dialect via hybrid) ────
%processed = "hybrid.vqc_layer"(%encoded) {ansatz = "strongly_entangling", layers = 4} : (qubit) -> qubit
// ──── Stage 4: Measurement (hybrid dialect) ────
%raw = "hybrid.measure_expectation"(%processed) : (qubit) -> tensor<1x2xf32>
// ──── Stage 5: Post-processing (tensor dialect) ────
%probs = "tensor.softmax"(%raw) : (tensor<1x2xf32>) -> tensor<1x2xf32>
return %probs
}
}
Drug Discovery — GNN + VQE
#dialect tensor
#dialect quantum
#dialect hybrid
module @drug_discovery {
func @screen_molecule(
%atoms: tensor<50x16xf32>,
%bonds: tensor<50x50xf32>,
%q0: qubit, %q1: qubit, %q2: qubit, %q3: qubit
) -> tensor<1xf32> {
// Stage 1: GNN feature extraction
%h1 = "tensor.gnn_message_passing"(%atoms, %bonds) : (tensor<50x16xf32>, tensor<50x50xf32>) -> tensor<50x16xf32>
%h2 = "tensor.relu"(%h1) : (tensor<50x16xf32>) -> tensor<50x16xf32>
%h3 = "tensor.gnn_message_passing"(%h2, %bonds) : (tensor<50x16xf32>, tensor<50x50xf32>) -> tensor<50x16xf32>
%mol = "tensor.gnn_global_pooling"(%h3) : (tensor<50x16xf32>) -> tensor<1x16xf32>
// Stage 2: Reduce to qubit count and encode
%flat = "tensor.reshape"(%mol) : (tensor<1x16xf32>) -> tensor<1x16xf32>
%encoded = "hybrid.encode"(%flat) {strategy = "amplitude"} : (tensor<1x16xf32>) -> qubit
// Stage 3: VQE for energy calculation
%ansatz = "hybrid.vqe_ansatz"(%encoded) {ansatz = "uccsd"} : (qubit) -> qubit
// Stage 4: Energy measurement
%energy = "hybrid.measure_expectation"(%ansatz) : (qubit) -> tensor<1xf32>
return %energy
}
}
Quantum Finance — QAOA Portfolio Optimisation
#dialect tensor
#dialect quantum
#dialect hybrid
module @quantum_finance {
func @optimise_portfolio(
%returns: tensor<1x10xf32>,
%covariance: tensor<10x10xf32>,
%q0: qubit, %q1: qubit, %q2: qubit, %q3: qubit, %q4: qubit
) -> tensor<1x5xf32> {
// Classical: compute expected returns
%scores = "tensor.matmul"(%returns, %covariance) : (tensor<1x10xf32>, tensor<10x10xf32>) -> tensor<1x10xf32>
// Preprocess for quantum
%preprocessed = "hybrid.classical_preprocess"(%scores) : (tensor<1x10xf32>) -> tensor<1x5xf32>
// Encode
%encoded = "hybrid.encode"(%preprocessed) {strategy = "angle"} : (tensor<1x5xf32>) -> qubit
// QAOA optimisation
%qaoa_result = "hybrid.qaoa_layer"(%encoded) {gamma = 0.7, beta = 0.4} : (qubit) -> qubit
// Measure
%samples = "hybrid.measure_samples"(%qaoa_result) {shots = 8192} : (qubit) -> tensor<8192xi32>
// Post-process: extract best portfolio allocation
%allocation = "hybrid.quantum_postprocess"(%samples) : (tensor<8192xi32>) -> tensor<1x5xf32>
return %allocation
}
}
6.3 Multi-Function Modules
A module can contain multiple functions that call different dialects:
#dialect tensor
#dialect quantum
#dialect hybrid
module @full_pipeline {
// Classical preprocessing function
func @preprocess(%img: tensor<1x3x224x224xf32>, %w: tensor<64x3x7x7xf32>) -> tensor<1x64xf32> {
%c = "tensor.conv2d"(%img, %w) : (tensor<1x3x224x224xf32>, tensor<64x3x7x7xf32>) -> tensor<1x64x112x112xf32>
%a = "tensor.relu"(%c) : (tensor<1x64x112x112xf32>) -> tensor<1x64x112x112xf32>
%p = "tensor.global_avgpool"(%a) : (tensor<1x64x112x112xf32>) -> tensor<1x64x1x1xf32>
%f = "tensor.reshape"(%p) : (tensor<1x64x1x1xf32>) -> tensor<1x64xf32>
return %f
}
// Quantum processing function
func @quantum_layer(%q0: qubit, %q1: qubit) -> (qubit, qubit) {
%a = "quantum.ry"(%q0) {angle = 0.5} : (qubit) -> qubit
%b = "quantum.ry"(%q1) {angle = 1.0} : (qubit) -> qubit
%c, %d = "quantum.cx"(%a, %b) : (qubit, qubit) -> (qubit, qubit)
%e = "quantum.rz"(%c) {angle = 0.3} : (qubit) -> qubit
return %e, %d
}
// Hybrid pipeline function
func @classify(
%features: tensor<1x4xf32>,
%q0: qubit, %q1: qubit
) -> tensor<1x2xf32> {
%encoded = "hybrid.encode"(%features) {strategy = "angle"} : (tensor<1x4xf32>) -> qubit
%processed = "hybrid.vqc_layer"(%encoded) {ansatz = "hardware_efficient", layers = 2} : (qubit) -> qubit
%result = "hybrid.measure_expectation"(%processed) : (qubit) -> tensor<1x2xf32>
%probs = "tensor.softmax"(%result) : (tensor<1x2xf32>) -> tensor<1x2xf32>
return %probs
}
}
6.4 Common Patterns and Recipes
Pattern 1: Linear Layer (matmul + bias + activation)
%h = "tensor.matmul"(%x, %w) : (tensor<BxMxf32>, tensor<MxNxf32>) -> tensor<BxNxf32>
%b = "tensor.add"(%h, %bias) : (tensor<BxNxf32>, tensor<Nxf32>) -> tensor<BxNxf32>
%a = "tensor.relu"(%b) : (tensor<BxNxf32>) -> tensor<BxNxf32>
Or fused:
%a = "tensor.fused_matmul_bias_relu"(%x, %w, %bias) : (tensor<BxMxf32>, tensor<MxNxf32>, tensor<Nxf32>) -> tensor<BxNxf32>
Pattern 2: Transformer Block
%attn = "tensor.multi_head_attention"(%q, %k, %v) : ...
%res1 = "tensor.add"(%attn, %input) : ...
%norm1 = "tensor.layernorm"(%res1, %scale1) : ...
%ff1 = "tensor.linear"(%norm1, %w1, %b1) : ...
%act = "tensor.gelu"(%ff1) : ...
%ff2 = "tensor.linear"(%act, %w2, %b2) : ...
%res2 = "tensor.add"(%ff2, %norm1) : ...
%norm2 = "tensor.layernorm"(%res2, %scale2) : ...
Pattern 3: Bell Pair + Measurement
%h = "quantum.h"(%q0) : (qubit) -> qubit
%a, %b = "quantum.cx"(%h, %q1) : (qubit, qubit) -> (qubit, qubit)
%m0 = "quantum.measure"(%a) : (qubit) -> qubit
%m1 = "quantum.measure"(%b) : (qubit) -> qubit
Pattern 4: Variational Layer (rotation + entangling + rotation)
%r0 = "quantum.ry"(%q0) {angle = 0.5} : (qubit) -> qubit
%r1 = "quantum.ry"(%q1) {angle = 0.8} : (qubit) -> qubit
%e0, %e1 = "quantum.cx"(%r0, %r1) : (qubit, qubit) -> (qubit, qubit)
%f0 = "quantum.rz"(%e0) {angle = 0.3} : (qubit) -> qubit
%f1 = "quantum.rz"(%e1) {angle = 0.7} : (qubit) -> qubit
Pattern 5: Hybrid Pipeline (encode → process → measure → postprocess)
%enc = "hybrid.encode"(%data) {strategy = "angle"} : (tensor<...>) -> qubit
%proc = "hybrid.vqc_layer"(%enc) {ansatz = "hardware_efficient", layers = 3} : (qubit) -> qubit
%meas = "hybrid.measure_expectation"(%proc) : (qubit) -> tensor<...>
%out = "tensor.softmax"(%meas) : (tensor<...>) -> tensor<...>
Pattern 6: Quantisation for Edge Deployment
%q_weights = "tensor.quantize"(%weights) : (tensor<256x256xf32>) -> tensor<256x256xi8>
%output = "tensor.matmul"(%input, %q_weights) : ...
%dq = "tensor.dequantize"(%output) : (tensor<...xi8>) -> tensor<...xf32>
Pattern 7: Training with Gradient Accumulation
%fwd = "tensor.linear"(%x, %w, %b) : ...
%loss = "tensor.softmax"(%fwd) : ...
%grad = "tensor.grad_linear"(%loss, %w, %b) : ...
%acc = "tensor.grad_accumulate"(%grad) : ...
Pattern 8: Distributed Training
%split = "tensor.parallel_split"(%batch) : ...
%local = "tensor.matmul"(%split, %w) : ...
%synced = "tensor.parallel_allreduce"(%local) : ...
6.5 Error Checklist
Common mistakes and how to avoid them:
| Error | Cause | Fix |
|---|---|---|
Unknown operation: tensor.xxx | Typo in operation name | Check exact name in this reference |
Unknown operation: quantum.xxx | Missing #dialect quantum | Add #dialect quantum at top |
SSA violation | %name assigned twice | Use a new name for each result |
Linearity violation | Qubit used twice | Each qubit value consumed exactly once |
Qubit leaked | Qubit created but not consumed | Return or measure all qubits |
Wrong number of inputs | Operation got wrong operand count | Check input count in tables above |
Type mismatch | Tensor shapes incompatible | Verify shapes match (e.g. matmul: [M,K]×[K,N]) |
Missing type signature | No : (types) -> type | Always include type signature |
Missing #dialect | Using ops without declaring dialect | Add #dialect <name> at file top |
Attribute error | Parametric gate missing angle | Add {angle = ...} for RX, RY, RZ, etc. |
6.6 Quick Syntax Reference Card
┌─────────────────────────────────────────────────────┐
│ #dialect tensor / quantum / hybrid │
│ │
│ module @name { │
│ func @fn(%x: type, ...) -> type { │
│ %y = "dialect.op"(%x) {attrs} : (T) -> T │
│ %a, %b = "dialect.op"(%x, %y) : (T,T) -> (T,T) │
│ return %y │
│ } │
│ } │
├─────────────────────────────────────────────────────┤
│ TYPES: │
│ tensor<DxDxDxdtype> e.g. tensor<1x784xf32> │
│ qubit (linear — consumed once) │
│ bit (classical measurement) │
│ hamiltonian<N> (N-qubit operator) │
│ f32, i32, bool, void, index │
├─────────────────────────────────────────────────────┤
│ DTYPES: │
│ f64 f32 f16 bf16 fp8e4m3 fp8e5m2 │
│ i64 i32 i16 i8 i4 i2 u8 i1 index │
├─────────────────────────────────────────────────────┤
│ ATTRIBUTES: │
│ {key = 42, rate = 0.5, flag = true, s = "text"} │
│ {arr = [1, 2, 3]} │
├─────────────────────────────────────────────────────┤
│ DIALECTS: │
│ tensor: 96 ops (AI / ML) │
│ quantum: 50+ ops (quantum circuits) │
│ hybrid: 21 ops (classical ↔ quantum bridge) │
└─────────────────────────────────────────────────────┘
Appendix — Complete Operation Index
A.1 All Tensor Operations (96)
| # | Category | Syntax |
|---|---|---|
| 1 | Arithmetic | tensor.add |
| 2 | Arithmetic | tensor.sub |
| 3 | Arithmetic | tensor.mul |
| 4 | Arithmetic | tensor.div |
| 5 | Arithmetic | tensor.neg |
| 6 | Arithmetic | tensor.matmul |
| 7 | Arithmetic | tensor.linear |
| 8 | Arithmetic | tensor.conv2d |
| 9 | Arithmetic | tensor.embedding |
| 10 | Activation | tensor.relu |
| 11 | Activation | tensor.gelu |
| 12 | Activation | tensor.silu |
| 13 | Activation | tensor.sigmoid |
| 14 | Activation | tensor.softmax |
| 15 | Activation | tensor.tanh |
| 16 | Activation | tensor.leaky_relu |
| 17 | Activation | tensor.elu |
| 18 | Activation | tensor.mish |
| 19 | Activation | tensor.hard_swish |
| 20 | Activation | tensor.hard_sigmoid |
| 21 | Normalisation | tensor.layernorm |
| 22 | Normalisation | tensor.rmsnorm |
| 23 | Normalisation | tensor.batchnorm |
| 24 | Normalisation | tensor.groupnorm |
| 25 | Normalisation | tensor.instancenorm |
| 26 | Shape | tensor.reshape |
| 27 | Shape | tensor.transpose |
| 28 | Shape | tensor.concat |
| 29 | Shape | tensor.split |
| 30 | Shape | tensor.gather |
| 31 | Shape | tensor.scatter |
| 32 | Shape | tensor.squeeze |
| 33 | Shape | tensor.unsqueeze |
| 34 | Shape | tensor.permute |
| 35 | Shape | tensor.expand |
| 36 | Shape | tensor.slice |
| 37 | Shape | tensor.pad |
| 38 | Shape | tensor.tile |
| 39 | Attention | tensor.attention |
| 40 | Attention | tensor.multi_head_attention |
| 41 | Attention | tensor.multi_query_attention |
| 42 | Attention | tensor.grouped_query_attention |
| 43 | Attention | tensor.flash_attention |
| 44 | Attention | tensor.sliding_window_attention |
| 45 | Attention | tensor.cross_attention |
| 46 | Attention | tensor.paged_attention |
| 47 | Convolution | tensor.conv1d |
| 48 | Convolution | tensor.conv3d |
| 49 | Convolution | tensor.conv_transpose2d |
| 50 | Convolution | tensor.depthwise_conv2d |
| 51 | Convolution | tensor.dilated_conv2d |
| 52 | Pooling | tensor.maxpool2d |
| 53 | Pooling | tensor.avgpool2d |
| 54 | Pooling | tensor.adaptive_avgpool2d |
| 55 | Pooling | tensor.global_avgpool |
| 56 | Recurrent | tensor.lstm_cell |
| 57 | Recurrent | tensor.gru_cell |
| 58 | Recurrent | tensor.rnn_cell |
| 59 | Math | tensor.einsum |
| 60 | Math | tensor.fft |
| 61 | Math | tensor.ifft |
| 62 | Math | tensor.svd |
| 63 | Math | tensor.eig |
| 64 | Math | tensor.solve |
| 65 | Math | tensor.topk |
| 66 | Math | tensor.sort |
| 67 | Math | tensor.cumsum |
| 68 | Math | tensor.where |
| 69 | Math | tensor.clamp |
| 70 | Sparse | tensor.sparse_matmul |
| 71 | Sparse | tensor.sparse_embedding |
| 72 | Quantisation | tensor.quantize |
| 73 | Quantisation | tensor.dequantize |
| 74 | Quantisation | tensor.quantize_int4 |
| 75 | Quantisation | tensor.dequantize_int4 |
| 76 | Quantisation | tensor.quantize_fp8 |
| 77 | Quantisation | tensor.dequantize_fp8 |
| 78 | Generative | tensor.unet_down_block |
| 79 | Generative | tensor.unet_up_block |
| 80 | Generative | tensor.timestep_embedding |
| 81 | GNN | tensor.gnn_message_passing |
| 82 | GNN | tensor.gnn_global_pooling |
| 83 | MoE | tensor.moe_dispatch |
| 84 | MoE | tensor.moe_combine |
| 85 | Constants | tensor.constant |
| 86 | Constants | tensor.zeros |
| 87 | Constants | tensor.ones |
| 88 | Constants | tensor.arange |
| 89 | Constants | tensor.full |
| 90 | Memory | tensor.checkpoint |
| 91 | Memory | tensor.offload |
| 92 | Memory | tensor.grad_accumulate |
| 93 | Gradient | tensor.grad_matmul |
| 94 | Gradient | tensor.grad_relu |
| 95 | Gradient | tensor.grad_softmax |
| 96 | Gradient | tensor.grad_layernorm |
| 97 | Gradient | tensor.grad_attention |
| 98 | Gradient | tensor.grad_conv2d |
| 99 | Gradient | tensor.grad_linear |
| 100 | Gradient | tensor.grad_gelu |
| 101 | Parallelism | tensor.parallel_split |
| 102 | Parallelism | tensor.parallel_allreduce |
| 103 | Parallelism | tensor.pipeline_send |
| 104 | Parallelism | tensor.pipeline_receive |
| 105 | Fused | tensor.fused_matmul_bias_relu |
| 106 | Fused | tensor.fused_matmul_bias |
| 107 | Fused | tensor.fused_linear_gelu |
| 108 | Fused | tensor.fused_attention_layernorm |
| 109 | Fused | tensor.fused_linear_silu |
| 110 | Fused | tensor.fused_conv_batchnorm_relu |
A.2 All Quantum Operations (50+)
| # | Category | Syntax | Qubits |
|---|---|---|---|
| 1 | 1Q Standard | quantum.h | 1 |
| 2 | 1Q Standard | quantum.x | 1 |
| 3 | 1Q Standard | quantum.y | 1 |
| 4 | 1Q Standard | quantum.z | 1 |
| 5 | 1Q Standard | quantum.s | 1 |
| 6 | 1Q Standard | quantum.sdg | 1 |
| 7 | 1Q Standard | quantum.t | 1 |
| 8 | 1Q Standard | quantum.tdg | 1 |
| 9 | 1Q Standard | quantum.sx | 1 |
| 10 | 1Q Parametric | quantum.rx | 1 |
| 11 | 1Q Parametric | quantum.ry | 1 |
| 12 | 1Q Parametric | quantum.rz | 1 |
| 13 | 1Q Parametric | quantum.p | 1 |
| 14 | 1Q Parametric | quantum.u1 | 1 |
| 15 | 1Q Parametric | quantum.u2 | 1 |
| 16 | 1Q Parametric | quantum.u3 | 1 |
| 17 | 1Q Fixed | quantum.rx90 | 1 |
| 18 | 1Q Fixed | quantum.rx180 | 1 |
| 19 | 2Q | quantum.cx | 2 |
| 20 | 2Q | quantum.cz | 2 |
| 21 | 2Q | quantum.cy | 2 |
| 22 | 2Q | quantum.swap | 2 |
| 23 | 2Q | quantum.iswap | 2 |
| 24 | 2Q | quantum.ecr | 2 |
| 25 | 2Q | quantum.rzx | 2 |
| 26 | 2Q | quantum.xx | 2 |
| 27 | 2Q | quantum.yy | 2 |
| 28 | 2Q | quantum.zz | 2 |
| 29 | 2Q | quantum.cp | 2 |
| 30 | 2Q | quantum.cphase | 2 |
| 31 | 2Q | quantum.xy | 2 |
| 32 | IonQ | quantum.gpi | 1 |
| 33 | IonQ | quantum.gpi2 | 1 |
| 34 | IonQ | quantum.ms | 2 |
| 35 | 3Q | quantum.ccx | 3 |
| 36 | 3Q | quantum.cswap | 3 |
| 37 | Multi | quantum.mcx | N |
| 38 | Multi | quantum.mcz | N |
| 39 | Control | quantum.measure | 1 |
| 40 | Control | quantum.measure_all | N |
| 41 | Control | quantum.reset | 1 |
| 42 | Control | quantum.barrier | N |
| 43 | Control | quantum.init | 1 |
| 44 | Control | quantum.delay | 0 |
| 45 | Control | quantum.virtual_rz | 1 |
| 46 | Control | quantum.if_else | 0 |
| 47 | Special | quantum.global_phase | 0 |
| 48 | Special | quantum.param_gate | 0 |
A.3 All Hybrid Operations (21)
| # | Category | Syntax |
|---|---|---|
| 1 | Encoding | hybrid.encode |
| 2 | Encoding | hybrid.decode |
| 3 | Gradient | hybrid.parameter_shift |
| 4 | Gradient | hybrid.finite_difference |
| 5 | Gradient | hybrid.spsa |
| 6 | Gradient | hybrid.adjoint_diff |
| 7 | Gradient | hybrid.stochastic_param_shift |
| 8 | Gradient | hybrid.joint_gradient |
| 9 | Processing | hybrid.classical_preprocess |
| 10 | Processing | hybrid.quantum_postprocess |
| 11 | Processing | hybrid.forward |
| 12 | Processing | hybrid.backward |
| 13 | Variational | hybrid.vqc_layer |
| 14 | Variational | hybrid.vqe_ansatz |
| 15 | Variational | hybrid.qaoa_layer |
| 16 | Variational | hybrid.quantum_kernel |
| 17 | Transfer | hybrid.gpu_to_qpu |
| 18 | Transfer | hybrid.qpu_to_gpu |
| 19 | Execution | hybrid.co_execute |
| 20 | Measurement | hybrid.measure_expectation |
| 21 | Measurement | hybrid.measure_samples |
End of LIFT Dialect Reference.
Total operations documented: 110 tensor + 48 quantum + 21 hybrid = 179 operations.
This document is the complete, error-free, authoritative reference for all LIFT dialects, their syntax, configuration, and assembly.
LIFT — Strategic Business Guide
How companies, engineers, and researchers use LIFT to save money, ship faster, and win projects.
This document is not about how LIFT works internally. It is about what you gain by using it, in real projects, with real numbers.
Table of Contents
- Who Is This For
- The Cost of Not Using LIFT
- Healthcare and Medical Imaging
- Pharmaceutical and Drug Discovery
- Finance and Investment
- Manufacturing and Quality Control
- Energy and Sustainability
- Automotive and Autonomous Systems
- Cybersecurity and Fraud Detection
- Telecommunications and Networks
- Research Laboratories and Universities
- Consulting and AI Service Companies
- ROI Summary Table
- Getting Started
- Competitive Advantage
1. Who Is This For
| Role | What You Get From LIFT |
|---|---|
| CTO / VP Engineering | Cut infrastructure costs by 30-60%. Ship AI products 2-3x faster. Get energy reports for ESG compliance. |
| ML / AI Engineer | Stop juggling 5 frameworks. Write once, optimise automatically, deploy everywhere. |
| Quantum Computing Researcher | Run hybrid classical+quantum experiments without rewriting code for each hardware vendor. |
| Project Manager | Predictable budgets. Know compute cost, energy cost, and deployment time before writing production code. |
| Startup Founder | Compete with big tech on AI/quantum without a 50-person engineering team. |
| Data Scientist | Focus on the model, not the infrastructure. LIFT handles optimisation, export, and hardware targeting. |
2. The Cost of Not Using LIFT
Today, building an AI or hybrid AI+quantum product requires:
| Task | Without LIFT | Time Wasted |
|---|---|---|
| Model prototyping | Python + PyTorch | — |
| Optimising for GPU | TensorRT or ONNX Runtime (separate tool) | 2-4 weeks |
| Quantum circuit design | Qiskit or Cirq (separate language, separate team) | 4-8 weeks |
| Connecting classical + quantum | Custom glue code, no standard | 4-12 weeks |
| Performance estimation | Manual benchmarks on real hardware | 1-2 weeks per config |
| Energy/carbon reporting | Spreadsheets or guesswork | Ongoing |
| Deploying to production | Manual conversion to LLVM, CUDA, or OpenQASM | 2-6 weeks |
| Bug hunting (type errors, qubit leaks) | Runtime crashes, silent errors | Unpredictable |
Total overhead per project: 15-34 weeks of engineering time.
With LIFT, these tasks are eliminated or automated. The engineering team writes one .lif file and LIFT handles the rest.
What This Means in Money
| Team Size | Avg Engineer Salary (yearly) | 15-34 Weeks Overhead | Annual Savings With LIFT |
|---|---|---|---|
| 5 engineers | $120,000 | $173K - $392K | $170K - $390K / year |
| 10 engineers | $120,000 | $346K - $785K | $350K - $780K / year |
| 20 engineers | $120,000 | $692K - $1,570K | $690K - $1.5M / year |
These numbers do not include compute cost savings (see below).
3. Healthcare and Medical Imaging
The Opportunity
The global AI in healthcare market is projected at $187 billion by 2030. Hospitals and medical device companies need fast, accurate diagnostic tools.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| AI-Assisted Radiology | Classify chest X-rays, CT scans, MRIs automatically | Per-scan fee ($5-50) or SaaS to hospitals ($50K-500K/year) |
| Pathology Analysis | Analyse tissue samples at scale with CNN models | Per-slide analysis fee |
| Hybrid Quantum Diagnostics | Quantum-enhanced classifiers for rare disease detection on small datasets | Premium pricing for cutting-edge accuracy |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| 6 months to build and optimise a CNN pipeline | 6 weeks (auto-optimises, auto-exports) | 4.5 months faster to market |
| $15,000/month GPU cloud bill (unoptimised) | $6,000/month (60% compute reduction) | $108,000/year saved |
| Cannot offer quantum-enhanced diagnostics | Hybrid CNN+VQC ready out of the box | New product line, premium pricing |
| No energy reporting for hospital ESG | Automatic CO2 estimation per inference | Win contracts requiring sustainability reports |
Real-World Scenario
A medical imaging startup with 10 engineers:
- Before LIFT: 9-month dev cycle, $180K/year GPU costs, no quantum capability.
- After LIFT: 3-month dev cycle, $72K/year GPU costs, quantum-enhanced offering.
- Net gain year 1: $108K compute savings + $600K faster revenue (6 months earlier) + premium pricing on hybrid product.
4. Pharmaceutical and Drug Discovery
The Opportunity
Bringing a drug to market costs $2.6 billion on average and takes 10-15 years. Any acceleration is worth hundreds of millions.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Molecule Screening Platform | GNN rapid screening + quantum-precise energy calculation | License to pharma ($1M-10M/year) |
| Protein Binding Prediction | Predict drug-target protein binding | Per-molecule analysis fee |
| Drug Delivery Materials | Quantum simulation of nanoparticle properties | R&D partnerships |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| GNN + VQE = 2 separate pipelines, manual data transfer | Single pipeline, automatic encoding and transfer | 3-6 months saved |
| VQE runs until timeout, no budget control | Reactive budget stops on convergence, saves 40-70% quantum compute | $50K-200K/year quantum savings |
| No way to predict if quantum precision is sufficient | Fidelity and shot count predicted upfront | Avoid $10K-50K on failed experiments |
| Need separate quantum expertise team | One team writes classical + quantum together | Save 2-3 specialist salaries ($300K-500K/year) |
Real-World Scenario
A biotech company screening 100,000 molecules:
- Before LIFT: 6 months to build pipeline, $500K quantum costs, 3 quantum specialists.
- After LIFT: 2 months to build, $200K quantum costs, 1 quantum-aware engineer.
- Net gain: $300K quantum + $400K salaries + 4 months faster = first-to-patent advantage worth millions.
5. Finance and Investment
The Opportunity
Quant firms, banks, and asset managers spend billions on technology for portfolio optimisation, risk management, and fraud detection.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Quantum Portfolio Optimiser | Return prediction + QAOA asset selection under constraints | Performance fee or SaaS to asset managers |
| Real-Time Fraud Detection | Autoencoder + quantum anomaly detection | Per-transaction fee or enterprise license |
| Risk Simulation Engine | Hybrid classical+quantum Monte Carlo | License to banks ($500K-5M/year) |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| Manual integration of LSTM + QAOA, no latency guarantees | Automatic budget allocation (1 ms LSTM + 9 s QAOA within 10 s constraint) | Meet trading latency automatically |
| Fraud detection: 50 ms latency (too slow) | Optimised to < 10 ms with hybrid co-execution | Catch fraud in real-time, prevent $M losses |
| Quantum finance: experimental, unreliable | Fidelity prediction ensures usable results | Deploy quantum finance in production |
| Manual carbon footprint estimation | Automatic energy and CO2 reports | ESG compliance, zero extra effort |
Real-World Scenario
A quantitative hedge fund:
- Before LIFT: $2M/year engineering costs, experimental quantum results, 12-month dev cycles.
- After LIFT: $800K/year (smaller team, less integration), production-ready in 4 months.
- Net gain: $1.2M/year + faster alpha-generating strategies.
6. Manufacturing and Quality Control
The Opportunity
Smart manufacturing and Industry 4.0 require real-time AI. The market is worth $500 billion by 2030.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Visual Defect Detection | CNN on edge devices inspecting products on the assembly line | Per-unit license or embedded in cameras |
| Predictive Maintenance | Time-series AI predicting equipment failure | SaaS to factories ($100K-1M/year) |
| Supply Chain Optimiser | QAOA for logistics routing, scheduling, inventory | Per-optimisation fee or enterprise license |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| Edge model too large (200 MB) | Quantisation + fusion: 25-50 MB | Deploy on cheap hardware, save $500-2000/camera |
| Maintenance model: 200 ms inference | Optimised to 20 ms | Real-time alerts, prevent $50K-500K downtime |
| Heuristic solvers for supply chain | QAOA finds better discrete solutions | 5-15% logistics cost reduction |
| No visibility before deployment | Predict latency and memory on target device | Zero failed factory deployments |
Real-World Scenario
An industrial automation company deploying AI in 50 factories:
- Before LIFT: Custom optimisation per device, 3-month deployment, 30% failure rate.
- After LIFT: Auto-optimisation, 3-week deployment, < 5% failure rate.
- Net gain: 50 factories x $200K saved = $10M total savings.
7. Energy and Sustainability
The Opportunity
Energy companies need AI for grid optimisation, demand forecasting, and materials discovery. Governments mandate carbon reporting.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Grid Load Forecasting | Transformer models predicting demand 24-72 hours ahead | License to utilities ($200K-2M/year) |
| Battery Material Discovery | ML screening + VQE quantum simulation | R&D partnerships or IP licensing |
| Carbon-Aware AI | Models deployed with automatic energy and CO2 tracking | Compliance reporting service |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| Grid forecast: 500 ms inference, misses real-time | Optimised to 50 ms | Real-time grid management, prevent blackouts |
| Battery research: 2 years trial-and-error | VQE + ML screening: 6 months to candidates | 18 months faster R&D |
| Sustainability consultant: $100K/year | Auto-generated energy and CO2 data | $100K/year saved + better accuracy |
| Separate AI and quantum tools | Single workflow end-to-end | 50% less engineering time |
Real-World Scenario
An energy utility company:
- Before LIFT: $3M/year AI R&D, slow deployment, manual carbon reporting.
- After LIFT: $1.5M/year, automatic ESG compliance.
- Net gain: $1.5M/year + regulatory compliance + green energy advantage.
8. Automotive and Autonomous Systems
The Opportunity
Autonomous vehicles, drones, and robotics require edge AI with strict latency and power constraints. Market projected at $2 trillion by 2030.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Perception Pipeline | CNN for object detection, optimised for automotive GPUs | Embedded license per vehicle |
| Path Planning | Quantum-hybrid optimisation for real-time routing | SaaS or per-vehicle license |
| Sensor Fusion | Multi-modal AI: camera + LiDAR + radar | Component license to OEMs |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| Perception: 100 ms on Jetson (too slow for 30 FPS) | Quantisation + fusion: 30 ms | Meet safety certification requirements |
| Model needs 12 GB VRAM, target has 8 GB | LIFT predicts memory before deployment, auto-quantises | No hardware surprises, save $M in recalls |
| Each vehicle platform = separate optimisation | One .lif file, export to multiple targets | 80% less porting work |
| Power budget: 15W, model uses 25W | Energy estimation + optimisation: fits in 12W | Deploy on battery-powered systems |
Real-World Scenario
An autonomous vehicle company targeting 10,000 vehicles:
- Before LIFT: 12-month porting cycle per hardware platform, $500/vehicle in software optimisation costs.
- After LIFT: 2-month cycle, $50/vehicle.
- Net gain: $4.5M savings on 10,000 vehicles + 10 months faster to market.
9. Cybersecurity and Fraud Detection
The Opportunity
Cybercrime costs $10.5 trillion annually by 2025. Real-time threat detection is critical for banks, governments, and enterprises.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Anomaly Detection Engine | Autoencoder + quantum circuit for detecting unknown threats | Enterprise license ($200K-2M/year) |
| Transaction Monitoring | Real-time fraud detection for payment processors | Per-transaction fee (fractions of a cent, at scale = $M) |
| Network Intrusion Detection | Time-series AI monitoring network traffic patterns | SaaS to enterprises |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| Classical anomaly detection: misses novel attack patterns | Quantum feature space detects patterns invisible to classical models | Catch 15-30% more anomalies |
| Detection latency: 100 ms | Optimised hybrid pipeline: < 10 ms | Real-time response, prevent breaches |
| Separate classical + quantum dev teams | One integrated team | $300K-500K/year salary savings |
| Monthly false positive tuning | Better quantum feature separation = fewer false positives | 50% less analyst workload |
Real-World Scenario
A payment processor handling 10 million transactions/day:
- Before LIFT: 0.1% fraud loss ($100K/day), 100 ms detection, high false positive rate.
- After LIFT: 0.05% fraud loss ($50K/day), < 10 ms detection, 50% fewer false positives.
- Net gain: $50K/day fraud reduction = $18M/year.
10. Telecommunications and Networks
The Opportunity
5G and future 6G networks require AI-driven resource allocation, spectrum management, and network optimisation.
What You Build With LIFT
| Product | Description | Revenue Model |
|---|---|---|
| Spectrum Optimiser | QAOA for discrete frequency allocation | License to telecoms ($1M-10M/year) |
| Traffic Predictor | Transformer models for network load forecasting | SaaS to network operators |
| Edge Inference Engine | Optimised AI models for 5G edge nodes | Per-node license |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| Spectrum allocation: NP-hard, solved by heuristics | QAOA finds better discrete solutions | 8-20% better spectrum utilisation |
| Edge models too large for base stations | Auto-quantisation fits models in 256 MB | Deploy AI at the edge, new revenue stream |
| Separate AI and network optimisation teams | Single pipeline from model to edge deployment | 40% less engineering overhead |
| Performance unknown until field deployment | Predict latency on target hardware upfront | Zero field deployment failures |
Real-World Scenario
A telecom operator with 50,000 base stations:
- Before LIFT: $200/station annual AI cost, 5% spectrum waste.
- After LIFT: $100/station, 2% spectrum waste.
- Net gain: $5M/year savings + $30M/year revenue from better spectrum use.
11. Research Laboratories and Universities
The Opportunity
Researchers need to publish results faster, win grants, and transition from prototype to production. Quantum computing research is booming.
What You Build With LIFT
| Use Case | Description | Funding Outcome |
|---|---|---|
| Hybrid Algorithm Research | Test new quantum-classical algorithms without infrastructure hassle | More publications per year |
| Reproducible Experiments | One .lif file captures entire experiment (model + optimisation + hardware target) | Better reproducibility, higher citation count |
| Hardware Benchmarking | Compare performance across IBM, IonQ, Rigetti without rewriting | Comprehensive comparison papers |
| Student Training | Students learn AI + quantum in one unified framework | More skilled graduates, more industry partnerships |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| 3 months to set up experiment infrastructure | 1 week (LIFT handles everything) | 11 more weeks for actual research |
| Experiment results vary by framework version | Deterministic pipeline, reproducible results | Higher publication acceptance rate |
| Need access to 3 quantum platforms | Write once, export to IBM/IonQ/Rigetti/simulators | Broader comparison results |
| Grant proposal: "we will build custom tooling" | Grant proposal: "we use LIFT, proven framework" | Stronger proposals, higher funding rate |
Real-World Scenario
A quantum computing research lab:
- Before LIFT: 2 papers/year, 6 months per experiment setup, $200K/year in custom tooling.
- After LIFT: 5 papers/year, 1 month per setup, $20K/year.
- Net gain: 150% more publications + $180K/year savings + more competitive grant proposals.
12. Consulting and AI Service Companies
The Opportunity
AI consultancies and service companies build custom solutions for clients. Speed and reliability are competitive advantages.
What You Build With LIFT
| Service | Description | Revenue Model |
|---|---|---|
| Rapid AI Prototyping | Build client PoCs in days instead of months | Fixed-fee projects ($50K-500K) |
| Quantum Readiness Assessment | Show clients which of their problems benefit from quantum | Consulting fees ($10K-100K) |
| Production Deployment | Take client models from prototype to production with guaranteed performance | Retainer ($20K-200K/month) |
Why LIFT Makes You Profitable
| Without LIFT | With LIFT | Gain |
|---|---|---|
| PoC delivery: 3 months | PoC delivery: 3 weeks | 4x more projects per year |
| Deployment: "we think it will run in 50 ms" | Deployment: "LIFT predicts 47 ms on A100 with 99% confidence" | Win contracts with performance guarantees |
| Cannot offer quantum services (no expertise) | Hybrid ready out of the box | New service line, $M in revenue |
| Post-deployment support: many fire-fighting calls | Compile-time verification catches bugs early | 60% fewer support tickets |
Real-World Scenario
A 50-person AI consulting firm:
- Before LIFT: 8 projects/year, $200K average project, 20% overrun on timelines.
- After LIFT: 20 projects/year, $200K average, < 5% overrun.
- Net gain: $2.4M/year additional revenue + better client retention + quantum service line.
13. ROI Summary Table
| Industry | Annual Savings | Revenue Uplift | Time to Market | Payback Period |
|---|---|---|---|---|
| Healthcare | $108K-$500K compute | New quantum product line | 4.5 months faster | < 3 months |
| Pharma | $300K-$700K quantum + salaries | First-to-patent advantage | 4 months faster | < 6 months |
| Finance | $1.2M engineering | Faster alpha strategies | 8 months faster | < 2 months |
| Manufacturing | $10M (at scale) | Real-time quality product | 2.5 months faster per factory | < 1 month |
| Energy | $1.5M/year | ESG compliance contracts | 18 months faster R&D | < 4 months |
| Automotive | $4.5M (at scale) | Faster vehicle certification | 10 months faster | < 3 months |
| Cybersecurity | $18M/year fraud prevention | Premium detection service | Immediate | < 1 week |
| Telecom | $5M infra + $30M spectrum | Edge AI revenue stream | 6 months faster | < 2 months |
| Research | $180K/year tooling | 150% more publications | 5 months faster per paper | < 1 month |
| Consulting | Minimal direct | $2.4M additional revenue | 4x project throughput | < 1 month |
14. Getting Started — From Zero to First Project
Step 1: Identify Your Highest-Value Problem (Week 1)
Pick the problem that costs you the most money today:
- Slow model deployment? → LIFT auto-optimisation + export
- High compute costs? → LIFT quantisation + tensor fusion
- Exploring quantum? → LIFT hybrid pipeline
- ESG compliance pressure? → LIFT energy tracking
Step 2: Install and Run a Benchmark (Week 1)
# Install LIFT
cargo install lift-cli
# Run your first model
lift verify model.lif
lift analyse model.lif
lift optimise model.lif -o optimised.lif
lift predict optimised.lif --device a100
lift export optimised.lif --backend llvm -o model.ll
Step 3: Measure the Improvement (Week 2)
Compare LIFT output against your current pipeline:
- Model size (MB)
- Inference latency (ms)
- Memory usage (GB)
- Energy per inference (J)
- Development time (weeks)
Step 4: Scale to Production (Weeks 3-6)
- Integrate LIFT into your CI/CD pipeline
- Set budget constraints in
.lithconfig files - Auto-generate performance and energy reports
- Export to your target hardware (GPU, QPU, edge)
Step 5: Expand to Hybrid (Months 2-3)
- Add quantum components to suitable problems
- LIFT handles the classical-quantum bridge automatically
- Compare quantum vs classical results with the same tool
- Scale quantum experiments with reactive budgets
Team Skills Needed
| Role | Count | Skills |
|---|---|---|
| LIFT Lead Engineer | 1 | Familiar with LIFT syntax and pipeline |
| ML Engineers | 1-3 | Standard ML knowledge, LIFT handles the rest |
| Quantum-Aware Engineer | 0-1 | Basic quantum concepts (LIFT abstracts hardware details) |
| DevOps | 1 | CI/CD integration, LIFT CLI |
Total: 3-6 people replace a team of 10-15 using traditional tools.
15. Competitive Advantage
What Happens If Your Competitor Uses LIFT And You Do Not
| Dimension | Your Competitor (with LIFT) | You (without LIFT) |
|---|---|---|
| Time to market | 3 months | 9-12 months |
| Compute costs | 40-60% lower | Full price |
| Quantum capability | Production-ready | Experimental or none |
| ESG compliance | Automatic | Manual, expensive |
| Deployment reliability | Compile-time verified | Runtime crashes |
| Team size for same output | 5 engineers | 15 engineers |
| Hardware portability | GPU + QPU + Edge in one file | Separate codebase per target |
The Bottom Line
Companies using LIFT:
- Ship 2-4x faster because one tool replaces five.
- Spend 30-60% less on compute because 11 optimisation passes run automatically.
- Offer quantum-enhanced products without hiring a quantum physics team.
- Comply with ESG regulations without extra effort or consultants.
- Eliminate entire categories of bugs at compile time instead of in production.
- Scale to any hardware — GPU, QPU, edge — from the same source file.
The question is not whether you can afford to use LIFT. The question is whether you can afford not to.
LIFT
Language for Intelligent Frameworks and Technologies
The first Intermediate Representation built natively for both AI and Quantum Computing.
Simulate before you run. Compile once. Optimise everywhere.
Overview
LIFT is a unified compiler infrastructure that treats AI computation (tensors, gradients, attention) and quantum computation (qubits, gates, noise models) as first-class citizens in the same SSA-based intermediate representation. One .lif source file, one .lith config, one target pipeline: simulate, predict, optimise, compile.
That target is a work in progress, not today's state — see docs/CAPABILITIES.md for an honest, source-verified breakdown of what's real versus planned for each of the four stages. Predict and Optimise are solid; Simulate is static analysis only (no real execution yet); Compile produces text output, not executable code, for any target.
.lif source ──► LIFT-CORE (SSA IR) ──► SIMULATE ──► PREDICT ──► OPTIMISE ──► COMPILE
│ (static) │
┌───────────┼───────────┐ ┌────────────┼────────────┐
LIFT-TENSOR LIFT-QUANTUM LIFT-HYBRID OpenQASM 3 LLVM IR text ONNX
110 tensor 48 gates 21 hybrid (48/48 gates) (skeleton) (opset 21)
operations Kraus/QEC VQC/VQE ops CUDA PTX (planned)
Why LIFT?
No existing IR handles both AI and quantum in a single representation.
| Capability | MLIR | ONNX | OpenQASM | Qiskit | LIFT |
|---|---|---|---|---|---|
| AI tensor operations | Y | Y | - | - | Y |
| Quantum gate operations | - | - | Y | Y | Y |
| Unified AI + Quantum IR | - | - | - | ~ | Y |
| Noise as type-level attribute | - | - | - | - | Y |
| Linear qubit types (no-cloning) | - | - | - | - | Y |
| Budget enforcement before compile | - | - | - | - | Y |
| Single config for entire pipeline | - | - | - | - | Y |
| Performance prediction engine | - | - | - | - | Y |
Key: Y = implemented, ~ = partial, - = not supported
What makes LIFT unique
- One IR for AI + Quantum -- Both are equal citizens in the same SSA graph. Joint optimisation across classical and quantum operations.
- Noise in the type system -- Every quantum gate carries T1/T2, fidelity, crosstalk metadata. The compiler reasons about noise at every stage.
- Linear qubit types -- The no-cloning theorem enforced at compile time. Double-use of a qubit is a type error, not a runtime crash.
- Simulation-first compilation -- FLOP count, peak memory, circuit depth, expected fidelity, energy cost -- all computed before hardware runs. Budget violations halt compilation with actionable suggestions.
- One config language -- The
.lithfile replaces 6-8 separate configuration files across frameworks.
Architecture
USER .lif source | .lith config | lift(1) CLI
FRONTEND Lexer > Parser > AST > SSA Builder | Importers: ONNX, PyTorch FX, OpenQASM 3
DIALECTS LIFT-CORE | LIFT-TENSOR | LIFT-QUANTUM | LIFT-HYBRID
ANALYSIS Shape inference | FLOP count | Noise sim | Energy model | Roofline
PASSES TensorFusion FlashAttention GateCancellation RotationMerge LayoutMapping CSE ...
BACKENDS OpenQASM 3 (48/48 gates) | LLVM IR (skeleton) | ONNX (opset 21) | CUDA PTX, XLA (planned)
HARDWARE H100 / A100 / MI300 | IBM Kyoto / Rigetti / IonQ | TPU
Crate Map
| Crate | Purpose | Key contents |
|---|---|---|
lift-core | SSA IR foundation | Types, values, operations, blocks, regions, verifier, printer, pass manager |
lift-ast | Frontend | Lexer, parser, AST, IR builder for .lif files |
lift-tensor | AI dialect | 110 ops (attention, conv, pooling, MoE, quantisation, GNN, fused), shape inference |
lift-quantum | Quantum dialect | 48 gates (IBM/Rigetti/IonQ native), noise models, Kraus channels, QEC, topology |
lift-hybrid | Fusion dialect | 21 ops (VQC, VQE, QAOA), gradient methods, encoding strategies, GPU-QPU transfer |
lift-sim | Analysis engine | Cost models (A100/H100), quantum cost (superconducting/trapped-ion/neutral-atom), energy, carbon |
lift-predict | Prediction | Roofline model, budget enforcement |
lift-opt | Optimisation | 13 passes: DCE, constant fold, tensor fusion, flash attention, gate cancel, rotation merge, CSE, quantisation, noise-aware schedule, layout mapping, canonicalise, gate decomposition, real routing |
lift-import | Importers | ONNX, PyTorch FX, OpenQASM 3 |
lift-export | Backends | LLVM IR, ONNX (opset 21), OpenQASM 3 |
lift-config | Configuration | .lith parser and validator |
lift-cli | CLI | lift verify, lift analyse, lift print, lift optimise, lift predict, lift export |
lift-codegen | Codegen | Programmatic model generation, multi-format export |
Quick Start
# Install Rust 1.80+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Clone and build
git clone https://github.com/rustnew/Lift.git
cd Lift
cargo build --release
# Run tests (541 tests)
cargo test --workspace
# Install the `lift` CLI on your PATH (or use `cargo run -p lift-cli --` instead)
cargo install lift-cli
Example: Tensor Program
cat > hello.lif << 'EOF'
#dialect tensor
module @test {
func @forward(%x: tensor<4xf32>) -> tensor<4xf32> {
%out = "tensor.relu"(%x) : (tensor<4xf32>) -> tensor<4xf32>
return %out
}
}
EOF
lift verify hello.lif # Check IR well-formedness
lift analyse hello.lif # FLOPs, shapes, memory estimate
lift print hello.lif # Pretty-print the IR
Example: Quantum Circuit
#dialect quantum
module @bell {
func @bell_state(%q0: qubit, %q1: qubit) -> (bit, bit) {
%q2 = "quantum.h"(%q0) : (qubit) -> qubit
%q3, %q4 = "quantum.cx"(%q2, %q1) : (qubit, qubit) -> (qubit, qubit)
%b0 = "quantum.measure"(%q3) : (qubit) -> bit
%b1 = "quantum.measure"(%q4) : (qubit) -> bit
return %b0, %b1
}
}
(Every %name is assigned exactly once — SSA requires this. Reusing %q0 as
both the block argument and a gate result, as an earlier version of this
example did, fails lift verify with MultipleDefinition.)
The .lith Configuration
One file controls the entire compilation pipeline:
[target]
backend = "llvm"
device = "h100"
precision = "fp16"
[quantum]
provider = "ibm_kyoto"
topology = "heavy_hex"
num_qubits = 27
[optimisation]
level = O3
passes = canonicalize, tensor-fusion, gate-cancellation, gate-decomposition, real-routing
[budget]
max_memory_bytes = 80000000000
max_time_ms = 200.0
min_fidelity = 0.92
Optimisation Passes
All 13 passes are reachable from the CLI and from .lith's [optimisation] passes = ....
| Pass | Domain | Description |
|---|---|---|
| Canonicalise | All | Normalise IR to canonical form |
| Constant Folding | All | Evaluate compile-time constants |
| Dead Code Elimination | All | Remove unused operations |
| Common Subexpression Elimination | All | Deduplicate identical computations |
| Tensor Fusion | AI | Fuse MatMul+Bias+ReLU, Linear+GELU/SiLU, Conv+BN+ReLU chains |
| Flash Attention | AI | Replace standard attention with FlashAttention above a sequence-length threshold |
| Quantisation | AI | Annotate compute-heavy ops for INT8/INT4/FP8 quantisation |
| Gate Cancellation | Quantum | Cancel H·H=I, X·X=I, S·Sdg=I, T·Tdg=I, including non-consecutive pairs |
| Rotation Merge | Quantum | Merge Rz(a)·Rz(b) → Rz(a+b), including non-consecutive pairs |
| Noise-Aware Schedule | Quantum | Reorder gates to minimise decoherence |
| Layout Mapping | Quantum | Legacy pass: annotates non-adjacent 2-qubit gates with needs_swap = true — does not insert SWAPs itself |
| Gate Decomposition | Quantum | Replace H/T/Tdg/S/Sdg/Y/RX with the target provider's native gate set |
| Real Routing | Quantum | Insert real quantum.swap ops (BFS shortest path) so 2-qubit gates land on connected physical qubits |
Current Status
| Component | Status | Coverage |
|---|---|---|
lift-core | Stable | SSA IR, types, verifier, printer, pass manager |
lift-ast | Stable | Full lexer, parser, AST, IR builder |
lift-tensor | Stable | 110 operations, shape inference, FLOP counting |
lift-quantum | Stable | 48 gates, noise models, Kraus channels, QEC codes, topology |
lift-hybrid | Stable | 21 operations, gradient methods, encoding strategies |
lift-sim | Stable | Cost models, energy model, quantum simulation, budget tracking |
lift-predict | Stable | Roofline model, budget enforcement |
lift-opt | Stable | 13 optimisation passes |
lift-import | Skeleton | ONNX/PyTorch FX/OpenQASM 3 importers parse the source format but don't yet convert nodes into LIFT ops |
lift-export | Active | ONNX (opset 21) is operational; OpenQASM covers all 48 gates (46 as real instructions, 2 as comments); LLVM IR is a text skeleton (ops as comments) |
lift-config | Stable | .lith parser and types |
lift-cli | Stable | verify, analyse, print, optimise, predict, export |
lift-codegen | Stable | programmatic model generation, multi-format export |
Test suite: 541 tests, 100% pass rate across 14 crates.
Roadmap
| Phase | Target | Milestone |
|---|---|---|
| Core IR + Dialects | Done | SSA IR, tensor/quantum/hybrid dialects complete |
| Optimisation Passes | Done | 13 passes implemented and tested |
| Analysis Engine | Done | Cost models, energy, noise simulation |
| Functional Import/Export | Planned (v0.5) | Real ONNX/PyTorch FX/OpenQASM import; full 50+-gate OpenQASM export |
| Hardware Backends | Planned | CUDA PTX, native OpenQASM execution |
| Python Bindings | Planned | PyO3-based Python API |
| v1.0 Release | Q4 2026 | Full pipeline, benchmarks, arXiv paper |
Contributing
| Area | Difficulty | Description |
|---|---|---|
| CUDA PTX backend | Hard | GPU code generation for tensor ops |
| State vector simulator | Medium | Quantum circuit simulator (CPU + GPU) |
| Qiskit importer | Medium | Import Qiskit circuits into LIFT IR |
| API documentation | Easy | Rustdoc for all public items |
| Tutorials | Easy | Getting started guides and examples |
See CONTRIBUTING.md for code style and PR process.
Citation
@software{lift2025,
title = {LIFT: Language for Intelligent Frameworks and Technologies},
author = {LIFT Framework Contributors},
year = {2025},
url = {https://github.com/rustnew/Lift},
note = {Unified IR for AI and Quantum Computing}
}
License
MIT -- see LICENSE.
LIFT -- Because the future of computation is both intelligent and quantum, and it deserves a unified foundation.
LIFT v0.5 — Development Plan
Status: Active development target Scope: Execution engine, functional importers, real LLVM lowering Version bump: 0.5.0 (minor — new features, no breaking IR changes)
This plan breaks down the v0.5 milestone into concrete, independently mergeable work items. Each item lists the crates and files involved, the deliverable, and the acceptance criteria.
Overview
v0.5 moves LIFT from a static analysis compiler to an execution-capable compiler:
flowchart LR
A["Static analysis (v0.4)"] --> B["State-vector simulation (v0.5)"]
B --> C["Tensor interpreter (v0.5)"]
C --> D["Real LLVM lowering (v0.5)"]
D --> E["Functional importers (v0.5)"]
The four work streams are independent and can be developed in parallel.
Workstream 1 — State-vector quantum simulator
Target: simulate quantum circuits on CPU (up to ~25 qubits) to validate circuits before deploying to real QPUs.
Status today: crates/lift-sim/src/quantum_sim.rs performs static
analysis only (gate counts, depth, fidelity estimates). There is no numerical
simulation.
Tasks
| # | Task | File(s) | Acceptance |
|---|---|---|---|
| 1.1 | Amplitude vector type Vec<Complex64> with 2^N layout | crates/lift-sim/src/state.rs | State::new(num_qubits) allocates 2^N amplitudes |
| 1.2 | Gate matrix kernels (Pauli, Clifford, H, T, RX/RY/RZ, CNOT, SWAP) | crates/lift-sim/src/kernels.rs | Each gate applies correctly to an amplitude vector |
| 1.3 | Circuit executor — walk LIFT IR ops, apply gates in order | crates/lift-sim/src/executor.rs | Executes any quantum circuit expressed in lift-quantum dialect |
| 1.4 | Measurement with probability sampling | crates/lift-sim/src/measure.rs | measure(qubit) collapses state per Born rule |
| 1.5 | Noise channel application (depolarising, amplitude damping) | crates/lift-sim/src/noise.rs | Kraus operators applied to density matrix (mixed state mode) |
| 1.6 | CLI subcommand lift sim --quantum file.lif | crates/lift-cli/src/main.rs | Prints final state amplitudes + measurement counts |
Deliverable
lift sim --quantum examples/quantum_bell.lif prints:
Qubits: 2
State: |00⟩: 0.7071 |11⟩: 0.7071
Measurements (1024 shots): 00: 512, 11: 512
Workstream 2 — Tensor interpreter
Target: execute tensor ops with real values (numpy-like), enabling in-compiler evaluation of constant subgraphs.
Status today: no runtime values; the IR holds shapes/types only.
Tasks
| # | Task | File(s) | Acceptance |
|---|---|---|---|
| 2.1 | Runtime tensor value Tensor { data: Vec<f64>, shape: Vec<usize> } | crates/lift-sim/src/tensor.rs | Basic constructors and indexing |
| 2.2 | Core arithmetic kernels — add, sub, mul, div, matmul, broadcast | crates/lift-sim/src/tensor_ops.rs | Matches numpy semantics on shape mismatch |
| 2.3 | Reduction + reshape ops — sum, mean, max, reshape, transpose | crates/lift-sim/src/tensor_ops.rs | Correct output shapes |
| 2.4 | Dialect op → kernel dispatcher | crates/lift-sim/src/interp.rs | Every lift-tensor op maps to a kernel or errors clearly |
| 2.5 | CLI subcommand lift sim --tensor file.lif | crates/lift-cli/src/main.rs | Prints output tensors |
Deliverable
lift sim --tensor examples/tensor_mlp.lif evaluates the MLP forward pass and
prints each layer's output tensor.
Workstream 3 — Real LLVM IR lowering
Target: emit executable LLVM IR with cuBLAS/cuDNN runtime calls (GPU) and a fallback CPU path.
Status today: lift-export/src/llvm.rs emits a textual skeleton — module
declarations and function signatures, without real code generation.
Tasks
| # | Task | File(s) | Acceptance |
|---|---|---|---|
| 3.1 | Map LIFT tensor ops to cuBLAS calls (gemm, bias, relu fusion) | crates/lift-export/src/llvm.rs | matmul emits cublasSgemm |
| 3.2 | Map quantum measurement/shots to a runtime harness | crates/lift-export/src/llvm.rs | QPU bridge stubs generated |
| 3.3 | CPU fallback path (no GPU required to run) | crates/lift-export/src/llvm.rs | Emitted .ll compiles with clang |
| 3.4 | Verify emitted IR with llvm-as / lli in CI | .github/workflows/ci.yml | lli executes a trivial kernel |
Deliverable
lift export --backend llvm examples/phi3_mini.lif produces an .ll file that
compiles with clang and runs on CPU without a GPU.
Workstream 4 — Functional importers
Target: import ONNX, PyTorch FX, and OpenQASM 3 files into LIFT IR.
Status today: crates/lift-import/src/{onnx,pytorch,qasm}.rs are stubs —
error types and importer structs exist, but no parsing.
Tasks
| # | Task | File(s) | Acceptance |
|---|---|---|---|
| 4.1 | ONNX protobuf decoding (opset ≤ 21) | crates/lift-import/src/onnx.rs | Loads a real .onnx from examples/ |
| 4.2 | ONNX op → LIFT tensor op mapping | crates/lift-import/src/onnx.rs | Conv, Gemm, Relu, Softmax map correctly |
| 4.3 | OpenQASM 3 parser (grammar subset) | crates/lift-import/src/qasm.rs | Parses quantum_bell.lif-equivalent QASM |
| 4.4 | QASM gate → LIFT quantum op mapping | crates/lift-import/src/qasm.rs | H, CNOT, measure round-trip |
| 4.5 | PyTorch FX graph export ingestion | crates/lift-import/src/pytorch.rs | Reads a .fx.json graph |
| 4.6 | CLI subcommand lift import <file> | crates/lift-cli/src/main.rs | Imports and prints the IR |
Deliverable
lift import examples/phi3_generated.onnx produces a valid LIFT IR that passes
lift verify.
Testing strategy
- Every new kernel/simulator function gets unit tests in-crate.
- Round-trip tests: export
.qasm/.onnx→ import → verify. examples/validate_all.shextended withsimandimportsteps.- CI keeps
cargo fmt --check,clippy -D warnings,cargo test --workspace.
Suggested PR sequence
feat(sim): state-vector simulator— Workstream 1 (items 1.1–1.5)feat(cli): sim subcommands— items 1.6 + 2.5feat(sim): tensor interpreter— Workstream 2 (2.1–2.4)feat(import): ONNX importer— Workstream 4 (4.1–4.2)feat(import): OpenQASM importer— Workstream 4 (4.3–4.4)feat(export): real LLVM lowering— Workstream 3feat(import): PyTorch FX— Workstream 4 (4.5–4.6)
Each PR is independently mergeable and keeps main green.
Changelog
All notable changes to LIFT are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Planned (v0.5)
- State-vector quantum simulator (CPU, up to ~25 qubits)
- Tensor interpreter (numpy-like execution of tensor ops)
- Real LLVM IR lowering with cuBLAS/cuDNN runtime calls
- Functional importers — ONNX, PyTorch FX, OpenQASM 3 (currently stubs)
- SABRE-style dynamic qubit re-placement
Planned (v0.6)
- True automatic differentiation (backward graph construction)
- PyO3 Python bindings
- Multi-file support (
include/ linking) - v1.0 release — full pipeline, benchmarks, arXiv paper
[0.4.8] — 2026-08-26
Fixed
gate-cancellationfalsely cancelled a 2-qubit gate pair sharing only one wire (e.g.CX(q0,q1)thenCX(q0,q2)), and rewired only wire 0 on cancellation, leaving a dangling reference to a deleted value on any other wire. Cancellation now requires every wire to match and rewires all of them.noise-aware-scheduleunconditionally hoisted every non-quantum op (includingcore.return) before all quantum ops when reordering, discarding program order — dormant today since nothing yet writes differinggate_time_us, but would corrupt any circuit ending in a return the moment it does. Now preserves every non-quantum op's original position.dcedid not protectcore.call(a recognised, side-effecting core op) from removal when its result was unused.gate-decomposition:ibm_kyotoprovider metadata folded intoIbmEagleinstead ofIbmKyoto.- Tensor shape/FLOP inference (
infer_output_shape/compute_flops/compute_memory_bytes, now taking an optional attrs argument):- Conv1D/2D/3D and
DilatedConv2Dignored stride/padding/dilation entirely (alwaysin - kernel + 1), soDilatedConv2Dbehaved exactly like a plain Conv2D. Now readstride/padding/dilationattrs, withDilatedConv2Ddefaulting dilation to 2 so it differs from Conv2D even unconfigured. MaxPool2D/AvgPool2Dreturned the input shape unchanged instead of reducing spatial dimensions when given a kernel-shaped second input.compute_memory_bytesonly counted the output's bytes forMatMul/SparseMatMul; every other op (Conv*, activations, norms, ...) silently omitted the output from the memory-traffic total (understating real traffic by ~46% for a typical Conv2D).
- Conv1D/2D/3D and
- ONNX export:
tensor.siluexported as a bareSigmoidnode — computingsigmoid(x)instead ofSiLU(x) = x*sigmoid(x), wrong at every input, not an approximation. Now expands toSigmoid+Mul. - QASM export:
MCX/MCZhardcoded exactly 3 qubits, silently dropping every control qubit past the third. Now emits every input qubit. - Importers (QASM/ONNX/PyTorch FX): validated only top-level structure,
then returned
Ok(())with an empty module — silently discarding every gate/node, so a caller checking only theResultwould believe the import succeeded. Now return an explicit error, since none of the three perform real translation yet (tracked under Planned v0.5 above). - Verifier:
verify_ssaonly checked that a used value was defined somewhere in the context, with no ordering, so a use-before-def within a block passed verification. Added a dominance check scoped to each block's own program order. - Also independently re-verified that the #1 and #2 GitHub issue fixes (round-trip parsing, QASM qubit-index-from-operands) still hold, with no further changes needed there.
[0.4.7] — 2026-08-26
Changed
- Rewrote README.md for length and clarity (507 → 261 lines): merged two redundant intro paragraphs and two overlapping crate tables into one each, dropped a full ONNX op-mapping table that already lives in docs/LIFT_Guide.md and docs/LIFT_Manual.md, and cut a 12-line wall of near-identical CLI invocations down to the essentials. Every command and code sample that survived was re-tested against the current release, not just reviewed.
[0.4.6] — 2026-08-25
Fixed
cargo install lift-clinow installs a binary namedlift, notlift-cli. Every piece of documentation (README, the book, this changelog) has always shownlift verify ...— but the package had no[[bin]] nameoverride, so Cargo defaulted the binary to the package name. Added[[bin]] name = "lift"tocrates/lift-cli/Cargo.toml.examples/validate_all.shhardcodedcargo run --bin lift-cli --, which broke under the rename; fixed to--bin lift.
[0.4.5] — 2026-08-25
Fixed
- Printer/parser round trip —
optimise --output out.lifproduced a.liffile the parser could not read back (the printer emitted disconnected signature names plus a^bb0(...):block label with no grammar rule for it). The signature now prints the entry block's real argument names, and the redundant block header is no longer emitted. - QASM export qubit indexing — gates were numbered from a running counter
instead of their actual operand, so any two gates in a row could land on
different qubits and
CX's control/target could come out swapped. Qubit indices are now resolved by walking each operand's SSA def chain back to its owning qubit. Also fixed gate export order (was iterating the ops slotmap, which drifts once a pass frees a slot and a later pass reuses it; now walksblock.opsin program order) and per-function qubit counting (was summing qubit-typed block args across every function in the module). gate-decompositionno longer doubles the transformation — the pass built a native decomposition chain but left the original gate in the block, still wired to produce its own result, so e.g. decomposingTsilently producedRz(pi/4)followed by the still-presentT(i.e.S, notT's actual decomposition). The original gate's results are now redirected to the decomposition chain's output and the original op is removed.- RX decomposition sign error — the first
Rzin the nativeRX(theta)sequence had the wrong sign, soRX(0)compiled toZinstead of the identity, for every angle. Contributed by @cleitonaugusto (#4), verified independently against the closed-formRX(theta)matrix at 8 angles. - CLI
--versionwas hardcoded to"0.3.0"from an earlier release; now reads the real crate version viaCARGO_PKG_VERSION.
Added
predict --energy [--num-gpus N]— energy (J/kWh) and CO2 estimates, wiring the existingEnergyModelinto the CLI.predict --quantum <hardware> [--precision P]— quantum fidelity, shot count, and execution-time prediction (superconducting,trapped_ion,neutral_atom), wiring the existingpredict_quantuminto the CLI.CODE_OF_CONDUCT.md,SECURITY.md, and an issue-template chooser (.github/ISSUE_TEMPLATE/config.yml). Private vulnerability reporting is now enabled on the repository soSECURITY.md's instructions work.
Changed
- Removed 25 declared-but-unused dependencies across the workspace (found
with
cargo-machete, each verified by hand before removal). - Eliminated needless
Veccollects and redundant clones onlift-opt's hot paths (flash-attention, quantisation-pass, real-routing). lift-test/(root) moved tocrates/lift-demo/— it was the only workspace member outsidecrates/, and its name was one character from the unrelatedcrates/lift-testsintegration-test crate.- Consolidated secondary docs (
CAPABILITIES.md,DIALECTS.md,LIFT_design.md,LIFT_Guide.md,LIFT_Manual.md,PUBLISHING.md,STRATEGY.md) intodocs/;README.md,LICENSE,CHANGELOG.md, andCONTRIBUTING.mdstay at the root. - Translated
docs/CAPABILITIES.mdfrom French to English (it was the last fully-French document in the project) and corrected several claims that had gone stale since it was written, including two caught by this release's own fixes (QASM qubit indexing, gate-decomposition).
[0.4.4] — 2026-08-05
Changed
- Automated releases via crates.io Trusted Publishing (OIDC) — no API
token needed. All 13 crates configured with
rustnew/Liftworkflowpublish.yml; pushing av*tag publishes every crate in dependency order from CI (.github/workflows/publish.yml). - Version bump 0.4.3 → 0.4.4 across workspace and docs.
[0.4.3] — 2026-08-05
Changed
- Optimised crate descriptions for discoverability: every description now leads with "LIFT compiler", so the crates surface in crates.io searches for "compiler", "compiler framework", "quantum compiler", and "AI compiler".
- All 13 crates republished to crates.io at v0.4.3.
[0.4.2] — 2026-08-05
Fixed
- LICENSE now ships in every published crate package (was missing from crates.io tarballs because Cargo only auto-includes LICENSE files located in each package directory, not the workspace root).
- Repository field corrected to
rustnew/Liftin all published manifests (the GitHub rename fromLitf-IRhad not been propagated to crates.io). - Docs version references bumped to 0.4.2.
Changed
- All 13 crates republished to crates.io at v0.4.2.
[0.4.1] — 2026-08-05
Fixed
- Corrected op/gate counts in docs (110 tensor ops, 48 quantum gates, 21 hybrid ops).
- README examples now compile against the real API (
GateDecomposition::new(Provider::IbmKyoto),DataTypere-export frommodel_builder). - Repository references updated to
rustnew/Lift(renamed fromLitf-IR).
Changed
- Architecture diagrams moved to Mermaid (pipeline, dependency layers, roadmap).
- All 13 crates republished to crates.io at v0.4.1.
[0.4.0] — 2026-08-05
Added
- Optimisation levels
O0–O3with explicit-pass override and per-pass enable/disable. - Semantic verification (op arity vs dialect signatures).
- 13 optimisation passes including generic tensor fusion, hardware-native gate decomposition, real qubit routing (SWAP + BFS), non-adjacent gate cancellation & rotation merging.
- All 13 crates published to crates.io (first full workspace release).
[0.3.0] — 2026-04-30
Added
- Tensor / quantum / hybrid dialects.
- Cost modelling (FLOPs, memory, energy/carbon).
- Performance prediction (roofline analysis).
- Export backends (LLVM IR, ONNX, OpenQASM 3.0).
[0.2.1] — 2026-04-30
Changed
- Stability tuning.
[0.2.0] — 2026-03-31
Added
- Initial public release of the LIFT compiler framework.
- SSA-based intermediate representation.
- Tensor, quantum, and hybrid dialects.
- Core compiler infrastructure (types, values, operations, blocks, regions, verifier).
Contributing to LIFT
Thanks for your interest in contributing to LIFT — a unified intermediate representation for AI and quantum computing.
This guide covers the development workflow, project layout, and how to get your changes reviewed and merged.
Table of contents
- Development setup
- Project layout
- Building and testing
- Code style
- Validation
- Publishing
- Commit conventions
- Opening a pull request
Development setup
Requirements:
- Rust 1.80 or newer (see
rust-versioninCargo.toml) - Cargo (comes with Rust)
Clone and build:
git clone git@github.com:rustnew/Lift.git
cd Lift
cargo build --workspace
Project layout
LIFT is a Cargo workspace of 13 published crates, organised by dependency layer:
| Layer | Crates | Purpose |
|---|---|---|
| L0 — Foundation | lift-core, lift-config | SSA IR, verifier; O0–O3 pipeline config |
| L1 — Dialects & Frontend | lift-ast, lift-tensor, lift-quantum | lexer/parser; AI ops; quantum gates & noise |
| L2 — Analysis & I/O | lift-opt, lift-sim, lift-export, lift-import, lift-hybrid | passes; cost model; backends; importers; fusion |
| L3 — Prediction | lift-predict | roofline / performance prediction |
| L4 — Tools | lift-cli, lift-codegen | CLI; programmatic model generation |
lift-tests (publish = false) holds the integration test suite. lift-demo
(publish = false) is a standalone, end-to-end hybrid AI+quantum pipeline
walkthrough — useful as a worked example, not part of the library API.
Building and testing
# Build the whole workspace
cargo build --workspace
# Run all tests
cargo test --workspace
# Build a single crate
cargo build -p lift-core
Code style
- Run
rustfmt— CI enforcescargo fmt --all --check. - Run
clippywith warnings denied — CI enforcescargo clippy --all-targets -- -D warnings. - Keep changes minimal and focused on a single concern.
cargo fmt --all
cargo clippy --all-targets -- -D warnings
Validation
Before submitting, run the end-to-end validation script, which exercises the
full pipeline (verify → analyse → optimise → predict → export) across all
example models:
bash examples/validate_all.sh
This is also run in CI on every push to main and on pull requests.
Publishing
Releases are published to crates.io. The process:
-
Bump the version in
Cargo.toml([workspace.package] version) and update version references acrossREADME.mdand the docs (docs/LIFT_Guide.md,docs/LIFT_Manual.md,docs/LIFT_design.md,docs/DIALECTS.md). -
Update
CHANGELOG.md. -
Push a version tag — the
publishworkflow publishes all 13 crates automatically in dependency order (L0 → L1 → L2 → L3 → L4) using Trusted Publishing (OIDC, no API token):git tag v0.4.8 git push origin v0.4.8Trusted Publishing is configured per crate on crates.io (Settings → Trusted Publishing) for
rustnew/Lift, workflowpublish.yml. The workflow can also be triggered manually via the Actions tab (workflow_dispatch).crates.io does not allow overwriting a published version — a fix to an already-published release requires a new version bump.
-
Create a GitHub release:
gh release create v0.4.8 --title "..." --notes "..."
Manual fallback
If you need to publish outside CI (e.g. the very first release of a new crate), publish in dependency order with the API token:
cargo publish -p lift-core
cargo publish -p lift-config
# ... then L1, L2, L3, L4 ...
Commit conventions
Use conventional commit prefixes:
feat:— new featurefix:— bug fixdocs:— documentation onlychore:— maintenance (bumps, metadata, tooling)refactor:— code change that neither fixes a bug nor adds a featuretest:— adding or updating tests
Example: docs: add vision, roadmap, and layer-graph diagrams to README
Opening a pull request
- Fork the repository and create a feature branch.
- Make your changes, keeping them focused.
- Run
cargo fmt,cargo clippy,cargo test, andbash examples/validate_all.sh. - Push your branch and open a pull request against
main. - CI runs automatically (fmt, clippy, tests, validation). All checks must pass before merge.
Thank you for contributing to LIFT!
LIFT — Publishing & Visibility Tracker
This document tracks where LIFT is published and referenced across the Rust, AI, and quantum ecosystems, plus the channels still to pursue.
Published & live
| Channel | URL | Status |
|---|---|---|
| crates.io (13 crates) | https://crates.io/crates/lift-core | ✅ v0.4.8 |
| docs.rs (13 crates) | https://docs.rs/lift-core | ✅ |
| GitHub repo | https://github.com/rustnew/Lift | ✅ |
| GitHub Releases | https://github.com/rustnew/Lift/releases | ✅ 9 releases |
| GitHub Pages (docs book) | https://rustnew.github.io/Lift/ | ✅ |
| GitHub Discussions | https://github.com/rustnew/Lift/discussions | ✅ |
| crates.io Trusted Publishing | 13 crates → rustnew/Lift workflow publish.yml | ✅ configured |
Publishing is now secure: all 13 crates use Trusted Publishing (OIDC, no API token). Pushing a
v*tag triggers.github/workflows/publish.yml, which publishes every crate in dependency order. See CONTRIBUTING.md.
Pull requests submitted (awaiting merge)
| List | PR | Section |
|---|---|---|
| qosf/awesome-quantum-software | #178 | Quantum full-stack libraries + Quantum compilers (Rust) |
| merrymercy/awesome-tensor-compilers | #47 | Open Source Projects |
| rust-unofficial/awesome-rust | #2689 | Machine learning |
To do — other awesome lists
| List | Section | Status |
|---|---|---|
| invictvs-choi/awesome-quantum-compiler | — | Skipped — list is research-papers only, not open-source tools |
| zwang4/awesome-machine-learning-in-compilers | — | Skipped — list is "ML applied to compilers", not "ML compilers" |
Community announcements
Ready-to-post texts for each channel are in ANNOUNCEMENTS.md.
| Channel | Status |
|---|---|
| This Week in Rust | Text ready — submit via https://this-week-in-rust.org/ |
| users.rust-lang.org (Announcements) | Text ready |
| Reddit r/rust | Text ready |
| Reddit r/QuantumComputing | Text ready |
| Reddit r/MachineLearning | Text ready |
| Hacker News (Show HN) | Text ready |
| Lobste.rs | Reuse the HN/r/rust text |
| Rust Discord / Zulip | Reuse the announcement text |
To do — academic / long-term
| Channel | When | Notes |
|---|---|---|
| arXiv paper | v1.0 (Q4 2026) | Already in roadmap |
| Papers With Code | After arXiv | Link the repo |
| Quantum Open Source Foundation (QOSF) | Any time | Community + mentorship |
| Unitary Fund | Any time | Grants for open-source quantum projects |
Notes
- The crate name
liftis taken on crates.io (a DB migration tool, unrelated).lift-iris available if a standalone brand name is ever needed. - lib.rs indexes crates.io automatically; no manual submission needed.
Capabilities & readiness (v0.4.8) — truth check for marketing & reprise
Written 2026-08-05 (pause until ~2026-09). Keep this in sync with every release so the messaging never overpromises. Rule of thumb: announce what the code does today, not what the roadmap plans.
✅ Already usable today
| Area | Status | Audience |
|---|---|---|
| IR construction (modules, functions, blocks, ops, regions, values, types) | Solid | Compiler developers |
| Dialects: 110 tensor ops, 48 quantum gates, 21 hybrid ops (full types/API) | Real | API consumers |
| 13 optimisation passes (fusion, DCE, rewrites…) + pass framework | Real | Pass developers |
| IR verifier | Real | Program validation |
| Quantum analysis: circuit depth, estimated fidelity, depolarising noise | Real but static | Estimation only, no execution |
| Export: QASM (all 48 gates), ONNX (70+/110 ops, opset 21) | Real, partial coverage | Prototyping, QASM hardware runs |
| Export: LLVM-IR | Text skeleton, not executable | Not yet usable for real compilation |
❌ NOT yet usable (honest gaps — these are the v0.5/v0.6 plan)
| Gap | Impact |
|---|---|
No real simulator — quantum_sim.rs is static analysis, not state-vector simulation | Cannot run a circuit to get states/amplitudes |
| Importers are ~55-line skeletons (ONNX / PyTorch FX / QASM), not full parsers | Cannot load a real .onnx / .qasm file end-to-end |
| No real LLVM lowering — backend emits IR text, not executable bytecode | Cannot compile-and-run natively |
| No tensor execution — numpy-like interpreter is planned (v0.5) | Tensor ops do not compute yet |
🎯 One-line positioning (use in all marketing)
"Rust compiler framework: unified SSA IR for AI + quantum, 13 optimisation passes, O0-O3 pipelines, LLVM/ONNX/QASM backends."
This is accurate today. It is a framework (build compilers with it), not yet an end-to-end compiler you can feed a model/circuit into and run.
LIFT — Community Announcements
Ready-to-post announcement texts for each community channel. Each is tuned to the platform's tone and audience. Replace the placeholder links if needed.
Key facts (verified):
- 13 crates on crates.io (v0.4.8), docs on docs.rs
- 110 tensor ops, 48 quantum gates, 21 hybrid ops
- 13 optimisation passes, O0–O3 pipelines
- 3 backends: LLVM IR, ONNX (opset 21), OpenQASM 3.0
- Docs book: https://rustnew.github.io/Lift/
Reddit — r/rust (showcase)
Title: LIFT — a unified compiler framework for AI and quantum computing in Rust
Body:
I've been building LIFT, a compiler framework that treats AI and quantum computing as one problem instead of two.
The core idea: a single SSA intermediate representation where tensor ops, quantum gates, and classical-quantum hybrids are equal citizens. So you can optimise a hybrid VQE/QAOA workload and a transformer model in the same pipeline, with the same passes.
What it does today:
- 110 tensor ops — attention (Flash/Paged/GQA), MoE, quantisation, GNN, diffusion
- 48 quantum gates — with noise models, Kraus channels, QEC codes
- 13 optimisation passes — tensor fusion, FlashAttention replacement, gate cancellation, noise-aware scheduling, qubit routing (SWAP + BFS), gate decomposition
- O0–O3 pipelines with per-pass control
- 3 backends — LLVM IR, ONNX (opset 21), OpenQASM 3.0
- Cost modelling — FLOPs, memory, energy, roofline prediction before hardware runs
It's published as 13 crates on crates.io, with full docs.
- GitHub: https://github.com/rustnew/Lift
- crates.io: https://crates.io/crates/lift-core
- docs.rs: https://docs.rs/lift-core
- Docs book: https://rustnew.github.io/Lift/
Happy to hear feedback — especially from anyone working on MLIR, TVM, or quantum compilers. The roadmap (simulator, importers, real LLVM lowering) is open for contributions.
Reddit — r/QuantumComputing
Title: LIFT — a Rust compiler that unifies AI tensor and quantum circuit compilation
Body:
Sharing a project I've been working on: LIFT, a compiler framework with a single SSA IR that spans tensor operations and quantum gates.
For the quantum side, it includes:
- 48 quantum gates with noise models, Kraus channels, and QEC codes
- Noise-aware scheduling — the compiler reasons about T1/T2/fidelity at every stage
- Linear qubit types — the no-cloning theorem is enforced at compile time
- Qubit layout mapping and real qubit routing (SWAP + BFS)
- Hardware-native gate decomposition (IBM, Rigetti, IonQ, Quantinuum)
- OpenQASM 3.0 export
The differentiator: because AI tensors and quantum gates share one IR, hybrid classical-quantum workloads (VQE, QAOA, quantum chemistry) can be optimised jointly with the classical parts.
- GitHub: https://github.com/rustnew/Lift
- crates.io: https://crates.io/crates/lift-core
- Docs: https://rustnew.github.io/Lift/
The state-vector simulator and importers (Qiskit, OpenQASM) are on the roadmap.
Reddit — r/MachineLearning
Title: [P] LIFT — a Rust compiler framework for AI and quantum workloads
Body:
I've been working on LIFT, a compiler framework that unifies AI and quantum computation under one SSA intermediate representation.
The ML-relevant parts:
- 110 tensor ops including attention (Flash/Paged/GQA), MoE, quantisation, GNN, diffusion
- 13 optimisation passes including tensor fusion and FlashAttention replacement
- Cost modelling: FLOPs, peak memory, energy, and roofline prediction computed before hardware runs — budget violations halt compilation with suggestions
- ONNX (opset 21) export for PyTorch/TensorFlow/TensorRT interop
It's written in Rust and published as 13 crates.
- GitHub: https://github.com/rustnew/Lift
- crates.io: https://crates.io/crates/lift-core
- Docs: https://rustnew.github.io/Lift/
The tensor interpreter (numpy-like execution) and real LLVM lowering are on the roadmap. Feedback welcome.
Hacker News — Show HN
Title: Show HN: LIFT — a unified compiler for AI and quantum computing
Body:
I've been working on a compiler framework that treats AI and quantum computing as a single problem. LIFT uses one SSA intermediate representation where tensor ops, quantum gates, and classical-quantum hybrids are all first-class.
Why this matters: hybrid workloads (VQE, QAOA, quantum chemistry) need both classical and quantum compilation, but today they live in separate toolchains with separate IRs. LIFT lets you optimise them together.
Current state:
- 110 tensor ops, 48 quantum gates, 21 hybrid ops
- 13 optimisation passes, O0–O3 pipelines
- Noise-aware scheduling + linear qubit types (no-cloning enforced at compile time)
- Cost modelling before hardware runs (FLOPs, memory, energy, roofline)
- LLVM IR / ONNX / OpenQASM 3.0 backends
- 13 crates on crates.io, MIT licensed
Written in Rust. Docs: https://rustnew.github.io/Lift/
The roadmap (state-vector simulator, importers, real LLVM lowering) is open. Would love feedback from compiler folks — especially anyone who's worked with MLIR or quantum transpilers.
This Week in Rust — submission
Title: LIFT: a unified compiler framework for AI and quantum computing
Body:
LIFT is a Rust compiler framework with a single SSA intermediate representation spanning tensor operations, quantum gates, and classical-quantum hybrids. It ships 13 optimisation passes, O0–O3 pipelines, cost modelling, and LLVM IR / ONNX / OpenQASM 3.0 backends across 13 crates on crates.io.
users.rust-lang.org — Announcements
Title: LIFT — a unified compiler framework for AI and quantum computing
Body:
I'm announcing LIFT, a Rust compiler framework that unifies AI and quantum computation under a single SSA intermediate representation.
Highlights:
- 110 tensor ops, 48 quantum gates, 21 hybrid ops
- 13 optimisation passes, O0–O3 pipelines
- Noise-aware scheduling and linear qubit types
- Cost modelling (FLOPs, memory, energy, roofline) before hardware runs
- LLVM IR / ONNX / OpenQASM 3.0 backends
- 13 crates on crates.io, MIT licensed
Links:
- GitHub: https://github.com/rustnew/Lift
- crates.io: https://crates.io/crates/lift-core
- docs.rs: https://docs.rs/lift-core
- Docs book: https://rustnew.github.io/Lift/
Contributions welcome — see CONTRIBUTING.md.