Skip to content
Standard Library

Standard Library

Naso Standard Library

The Naso standard library (std) provides essential modules for systems programming, quantum computing, polyhedral compilation, formal verification, and memory management.

Module Overview

ModuleDescriptionKey Types
std::quantumQuantum gates, qubit management, measurementQubit, QubitArray[N], QReg
std::polyPolyhedral loop transformations, affine mapsPolyDomain, AffineMap, Schedule
std::smtEmbedded Z3 SMT solver interfaceSolver, Expr, Model, Sort
std::memLinear memory management, allocationLinearBox, LinearVec, qalloc, qfree
std::ioClassical I/O, serializationstdin, stdout, serialize, deserialize
std::mathLinear algebra, numerical routinesMatrix, Vector, fft, linalg

std::quantum

The quantum module provides types and operations for quantum computing with linear type guarantees.

Types

TypeQuantityDescription
Qubit[1]Single linear qubit — must be consumed exactly once
QubitArray[N][1]Fixed-size array of N linear qubits
QReg[N][1]Quantum register with N qubits (alias for QubitArray[N])
Cbit[*]Classical bit (measurement result)
CReg[N][*]Classical register of N bits

Core Gates

FunctionSignatureDescription
qallocfn() -> [1] QubitAllocate a fresh qubit in |0⟩ state
qfreefn(q: [1] Qubit)Deallocate qubit (must be in |0⟩)
hadamardfn(inout q: [1] Qubit)Apply H gate: |0⟩ → ( |0⟩ + |1⟩ )/√2
pauli_xfn(inout q: [1] Qubit)Apply X gate: |0⟩ ↔ |1⟩
pauli_yfn(inout q: [1] Qubit)Apply Y gate
pauli_zfn(inout q: [1] Qubit)Apply Z gate: phase flip
rxfn(inout q: [1] Qubit, theta: f64)Rotation around X axis
ryfn(inout q: [1] Qubit, theta: f64)Rotation around Y axis
rzfn(inout q: [1] Qubit, theta: f64)Rotation around Z axis
cnotfn(inout ctl: [1] Qubit, inout tgt: [1] Qubit)Controlled-NOT
czfn(inout ctl: [1] Qubit, inout tgt: [1] Qubit)Controlled-Z
swapfn(inout a: [1] Qubit, inout b: [1] Qubit)SWAP gate
measurefn(inout q: [1] Qubit) -> [1] CbitMeasure in Z basis, consumes qubit
measure_xfn(inout q: [1] Qubit) -> [1] CbitMeasure in X basis
resetfn(inout q: [1] Qubit) -> [0] ProofReset to |0⟩ (requires proof of known state)

Example: Bell State Preparation

use std::quantum::{qalloc, qfree, hadamard, cnot, measure};

fn create_bell_pair() -> [2] Qubit {
    let q0 = qalloc();
    let q1 = qalloc();
    hadamard(inout q0);
    cnot(inout q0, inout q1);
    [q0, q1] // Return tuple of linear qubits
}

fn measure_bell(inout pair: [2] Qubit) -> [2] Cbit {
    let [q0, q1] = pair; // Destructure
    [measure(inout q0), measure(inout q1)]
}

Example: GHZ State (N-qubit Entanglement)

fn ghz_state(n: usize) -> [N] Qubit
where [N] = [n] {
    let mut reg = QReg::new(n); // [1] QReg[N]
    hadamard(inout reg[0]);
    for i in 1..n {
        cnot(inout reg[0], inout reg[i]);
    }
    reg.into_array() // [1] QubitArray[N]
}

Example: Quantum Teleportation Protocol

fn teleport(inout psi: [1] Qubit, inout alice: [1] Qubit, inout bob: [1] Qubit) 
    -> ([1] Cbit, [1] Cbit, [1] Qubit) {
    
    // Create Bell pair between Alice and Bob
    hadamard(inout alice);
    cnot(inout alice, inout bob);
    
    // Bell measurement on psi and Alice's qubit
    cnot(inout psi, inout alice);
    hadamard(inout psi);
    let m1 = measure(inout psi);    // Consumes psi
    let m2 = measure(inout alice);  // Consumes alice
    
    // Conditional corrections on Bob's qubit (classical control)
    // In QASM: if (m1) x bob; if (m2) z bob;
    // Here modeled as proof obligation:
    // m1=true → apply X to bob; m2=true → apply Z to bob
    
    (m1, m2, bob) // Return classical bits and Bob's qubit
}

std::poly

The polyhedral module provides utilities for working with polyhedral loop transformations and static analysis.

Types

TypeDescription
PolyDomainRepresents a polyhedral iteration domain (Z-polyhedron)
AffineMapRepresents an affine transformation: x ↦ Ax + b
ScheduleTime-space mapping for loop nests
DependenceData dependence relation between statements

Domain Construction

FunctionSignatureDescription
domainfn(bounds: &[Range]) -> PolyDomainCreate domain from loop bounds
intersectfn(a: PolyDomain, b: PolyDomain) -> PolyDomainDomain intersection
projectfn(dom: PolyDomain, dims: &[usize]) -> PolyDomainProject onto subset of dimensions

Transformations

FunctionSignatureDescription
tilefn(dom: PolyDomain, factors: &[usize]) -> PolyDomainTile domain for cache blocking
parallelfn(dom: PolyDomain) -> PolyDomainMark domain for parallel execution
vectorizefn(dom: PolyDomain, width: usize) -> PolyDomainVectorize innermost loop
skewfn(dom: PolyDomain, factor: isize) -> PolyDomainSkew transformation for wavefront
interchangefn(dom: PolyDomain, i: usize, j: usize) -> PolyDomainLoop interchange
fusefn(a: PolyDomain, b: PolyDomain) -> PolyDomainLoop fusion

Schedule Construction

use std::poly::{domain, tile, parallel, vectorize, Schedule};

fn build_schedule() -> Schedule {
    let dom = domain(&[0..N, 0..M, 0..P]);
    
    // Tile for L1 cache (32x32 tiles)
    let tiled = tile(dom, &[32, 32, 1]);
    
    // Parallelize outer tile loops
    let par = parallel(tiled);
    
    // Vectorize innermost loop (AVX-512: width=8 for f64)
    let vec = vectorize(par, 8);
    
    Schedule::from_domain(vec)
}

Example: Tiled Matrix Multiplication with Schedule

use std::poly::{domain, tile, parallel, vectorize, Schedule};

fn matmul_tiled(A: [N][M] f64, B: [M][P] f64) -> [N][P] f64 {
    // Compiler uses this schedule for code generation
    #[schedule(build_schedule())]
    fn matmul_kernel(A: [N][M] f64, B: [M][P] f64) -> [N][P] f64 {
        let C = [[0.0; P]; N];
        for i in 0..N {
            for j in 0..P {
                for k in 0..M {
                    C[i][j] += A[i][k] * B[k][j];
                }
            }
        }
        C
    }
    matmul_kernel(A, B)
}

Example: Stencil with Wavefront Parallelization

use std::poly::{domain, skew, parallel, Schedule};

fn stencil_3d_schedule() -> Schedule {
    let dom = domain(&[1..N-1, 1..M-1, 1..O-1]);
    
    // Skew i-j plane for wavefront parallelism
    let skewed = skew(dom, 1); // factor=1 for i+j = constant
    
    // Parallelize skewed dimension
    let par = parallel(skewed);
    
    Schedule::from_domain(par)
}

std::smt

The SMT module provides an interface to the embedded Z3 solver for compile-time verification.

Types

TypeDescription
SolverZ3 solver context (linear — must be consumed)
ExprSymbolic expression (quantifier-free)
SortSMT sort: Bool, Int, Real, BitVec[N], Array
ModelSatisfying assignment (if check returns Sat)
ProofUnsatisfiability proof (if check returns Unsat)

Functions

FunctionSignatureDescription
new_solverfn() -> [1] SolverCreate new Z3 context
declare_constfn(s: &mut Solver, name: &str, sort: Sort) -> ExprDeclare uninterpreted constant
assertfn(s: &mut Solver, e: Expr)Assert constraint
checkfn(s: &Solver) -> CheckResultCheck satisfiability
get_modelfn(s: &Solver) -> Option<Model>Get model if satisfiable
get_prooffn(s: &Solver) -> Option<Proof>Get unsat proof
expr_fromfn<T: IntoExpr>(v: T) -> ExprConvert Naso value to SMT expr

CheckResult Enum

enum CheckResult {
    Sat,      // Constraints satisfiable
    Unsat,    // Constraints unsatisfiable
    Unknown,  // Solver cannot determine (timeout/incomplete)
}

Example: Array Bounds Verification

use std::smt::{new_solver, assert, check, expr_from, Sort};

fn safe_get(arr: [*] [N] f64, idx: usize) -> f64 {
    let mut solver = new_solver();
    let idx_e = expr_from(idx);
    let n_e = expr_from(N);
    
    // Prove 0 <= idx < N
    assert(&mut solver, idx_e >= expr_from(0));
    assert(&mut solver, idx_e < n_e);
    
    match check(&solver) {
        CheckResult::Sat => {
            // Bounds proven at compile time — no runtime check needed
            arr[idx]
        }
        CheckResult::Unsat => {
            compile_error!("Index out of bounds: solver proved idx >= N");
        }
        CheckResult::Unknown => {
            // Fallback: insert runtime check
            if idx < N { arr[idx] } else { panic!("Index out of bounds") }
        }
    }
}

Example: Linear Arithmetic Verification

use std::smt::{new_solver, assert, check, declare_const, Sort, expr_from};

fn verify_invariant(x: i32, y: i32) -> [0] Proof {
    let mut solver = new_solver();
    
    // Declare symbolic versions
    let x_s = declare_const(&mut solver, "x", Sort::Int);
    let y_s = declare_const(&mut solver, "y", Sort::Int);
    
    // Precondition: x > 0 && y > 0
    assert(&mut solver, x_s > expr_from(0));
    assert(&mut solver, y_s > expr_from(0));
    
    // Operation: z = x + y
    let z_s = x_s + y_s;
    
    // Postcondition: z > x && z > y
    assert(&mut solver, z_s > x_s);
    assert(&mut solver, z_s > y_s);
    
    // This will be checked at compile time
    match check(&solver) {
        CheckResult::Sat => (), // Proven
        CheckResult::Unsat => compile_error!("Invariant violated"),
        CheckResult::Unknown => compile_error!("Verification incomplete"),
    }
}

Example: Quantifier-Free Bitvector Reasoning

use std::smt::{new_solver, assert, check, declare_const, Sort, expr_from};

fn verify_overflow(a: u32, b: u32) -> [0] Proof {
    let mut solver = new_solver();
    let a_s = declare_const(&mut solver, "a", Sort::BitVec(32));
    let b_s = declare_const(&mut solver, "b", Sort::BitVec(32));
    
    // Check if a + b overflows (unsigned)
    let sum = a_s.bvadd(b_s);
    let overflow = sum.bvult(a_s); // sum < a iff overflow
    
    assert(&mut solver, overflow);
    
    // If SAT, there exist inputs causing overflow
    match check(&solver) {
        CheckResult::Sat => {
            compile_warn!("Potential unsigned overflow detected");
        }
        CheckResult::Unsat => {
            // Proven no overflow for any inputs
        }
        CheckResult::Unknown => {}
    }
}

std::mem

Memory management with linear types and quantum allocation.

Types

TypeDescription
LinearBox<T>[1] owning pointer — unique ownership
LinearVec<T>[1] owning vector — linear buffer
SharedBox<T>[*] reference-counted (classical only)

Functions

FunctionSignatureDescription
allocfn<T>(val: T) -> [1] LinearBox<T>Allocate on heap with linear ownership
deallocfn<T>(box: [1] LinearBox<T>)Deallocate (consumes box)
qallocfn() -> [1] QubitQuantum allocation (from std::quantum)
qfreefn(q: [1] Qubit)Quantum deallocation
vec_from_rawfn(ptr: *mut T, len: usize, cap: usize) -> [1] LinearVec<T>Construct from raw parts
vec_into_rawfn(vec: [1] LinearVec<T>) -> (*mut T, usize, usize)Destructure to raw parts

Example: Linear Buffer Management

use std::mem::{alloc, dealloc, LinearBox};

fn process_data(data: [1] LinearBox<[N] f64>) -> [1] LinearBox<[N] f64> {
    // Exclusive mutable access to buffer
    let mut buf = data; // Move ownership
    
    for i in 0..N {
        buf[i] = buf[i] * 2.0; // In-place mutation
    }
    
    buf // Return ownership
}

fn main() {
    let boxed = alloc([0.0; 1024]); // [1] LinearBox<[1024] f64>
    let result = process_data(boxed); // boxed moved, result returned
    dealloc(result); // Explicit deallocation (consumes result)
}

std::io

Classical I/O and serialization for host interaction.

Functions

FunctionSignatureDescription
printfn(s: &str)Print to stdout
printlnfn(s: &str)Print with newline
read_linefn() -> StringRead line from stdin
serializefn<T: Serialize>(val: &T) -> Vec<u8>Serialize to bytes
deserializefn<T: Deserialize>(bytes: &[u8]) -> Result<T>Deserialize from bytes

Example: Host-Device Communication

use std::io::{println, serialize, deserialize};

#[derive(Serialize, Deserialize)]
struct KernelConfig {
    grid_size: usize,
    block_size: usize,
    precision: Precision,
}

fn launch_kernel(config: KernelConfig) {
    let bytes = serialize(&config);
    // Send to device driver...
    println!("Launching kernel: {:?}", config);
}

std::math

Linear algebra and numerical routines optimized via polyhedral compilation.

Types

TypeDescription
Matrix<N, M>[N][M] f64 — compile-time sized matrix
Vector<N>[N] f64 — compile-time sized vector

Functions

FunctionSignatureDescription
matmulfn(A: [N][M] f64, B: [M][P] f64) -> [N][P] f64Matrix multiplication
transposefn(A: [N][M] f64) -> [M][N] f64Matrix transpose
dotfn(a: [N] f64, b: [N] f64) -> f64Dot product
norm2fn(v: [N] f64) -> f64L2 norm
fftfn(v: [N] Complex) -> [N] ComplexFast Fourier Transform (N power of 2)
svdfn(A: [N][M] f64) -> (U, S, Vt)Singular Value Decomposition

Example: FFT with Polyhedral Optimization

use std::math::fft;

fn frequency_analysis(signal: [1024] f64) -> [1024] f64 {
    // Convert to complex
    let mut complex: [1024] Complex = [Complex::new(0.0, 0.0); 1024];
    for i in 0..1024 {
        complex[i] = Complex::new(signal[i], 0.0);
    }
    
    // FFT with automatic polyhedral scheduling
    let spectrum = fft(complex);
    
    // Compute magnitude spectrum
    let mut mag: [1024] f64 = [0.0; 1024];
    for i in 0..1024 {
        mag[i] = spectrum[i].re * spectrum[i].re + spectrum[i].im * spectrum[i].im;
    }
    mag
}

Prelude Imports

The following are automatically available in every Naso file:

// Quantities
[0], [1], [*], [N]

// Core types
bool, int, f64, f32, u8..u64, i8..i64, usize, isize
Vec<T>, Option<T>, Result<T, E>

// Quantum
Qubit, QReg, qalloc, qfree, hadamard, cnot, measure

// Memory
LinearBox, LinearVec, alloc, dealloc

// SMT
Solver, Expr, Sort, Model, new_solver, assert, check