topos 0.12.0

An autodiff compiler stack in Rust.
Documentation

topos

An autodiff compiler stack in Rust. Record a graph, inspect it, differentiate it, compile it, emit it. The spec is an immutable value and the state is yours. The network never moves; a training step is a pure data transform of caller-owned parameters.

The interpreter is the executable spec; everything faster must match it, bit for bit by default. The stack stays small enough to read. Every result is provable.

A typical compiler rewrites the program until it can run. Topos does not. The tape is written once; everything after it is a named way of reading the same spec. Fusion is an offer, not a rewrite. Emission writes the plan as text for an industrial backend — a sibling of describe, not a second compiler.

The design is in docs/vision.md. Constraints it assumes live in docs/principles/.

Record and train

use topos::{Detach, Tape, Tensor};

let (network, [w, x, y, loss]) = Tape::record(|tape| {
    let w = tape.parameter(0.0_f64);
    let x = tape.input(0.0);
    let y = tape.input(0.0);
    let error = w * x - y;
    [w, x, y, error * error].detach()
});
let mut parameters = network.parameters();

let samples = [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0)];
for step in 0..100 {
    let (sample_x, sample_y) = samples[step % samples.len()];
    let run = network.forward(
        &parameters,
        [(x, sample_x.into()), (y, sample_y.into())],
    );
    let gradients = run.backward(loss).parameters(&parameters);
    parameters = parameters.step(&gradients, |w, g| {
        w.clone() - g.clone() * Tensor::from(0.02)
    });
}

assert!((parameters.of(w).scalar() - 2.0).abs() < 1e-6);

The graph is recorded once. Every step feeds a sample and steps the parameters. Training never touches the network.

Inspect, compile, emit

use topos::{Detach, Tape};

let (network, [loss]) = Tape::record(|tape| {
    let w = tape.parameter(1.0_f64);
    [w * w].detach()
});

println!("{}", network.describe());
let plan = network.entry([loss]).lower();
println!("{}", plan.emit_stablehlo().expect("every operation lowers"));

Two rustdoc maps, nothing moves: topos::model to train, topos::compiler to inspect and emit.

As a dependency

cargo add topos

Write networks, losses, optimizers, and element types against the public surface. A hand-rolled layer behaves identically to a facade; a custom optimizer has the same standing as Adam. The opcode set, fusions, and backends stay in the crate — the core is closed on purpose.

Opt-in backends (accelerate, simd, metal, cuda) are documented in docs/acceleration.md. Vocabulary: docs/terminology.md. Notebooks: docs/notebooks.md. API: docs.rs/topos.

Examples

examples/ runs from a scalar chain to a transformer:

The name

A topos is a place — here, one where the whole compiler stack stays in view.

License

Licensed under either of MIT or Apache-2.0, at your option.