Skip to content

Python package ​

linnet-lang is a NumPy-only core that runs the compiler and exposes the program as typed objects, plus a backend per framework behind an extra.

Install ​

bash
uv add "linnet-lang[torch]"     # linnet.torch
uv add "linnet-lang[jax]"       # linnet.jax
uv add "linnet-lang[flax]"      # linnet.jax.load_nnx
uv add "linnet-lang[onnx]"      # linnet.onnx (add onnxruntime or onnxruntime-gpu to run models)
uv add "linnet-lang[nest]"      # linnet.nest

The wheels carry the linnet compiler. The package runs LINNET_BIN if it is set, then the wheel's compiler, then the one on PATH. In a checkout, uv sync --all-extras in python/linnet installs every backend and test dependency.

Modules ​

ModuleContents
linnetload_program, Program, compile_plan, read_arrays, read_bindings
linnet.ir, linnet.diagramtyped program, diagrams
linnet.nestNest model zoo
linnet.torch, linnet.jax, linnet.onnxbackends for PyTorch, JAX and Flax, ONNX
linnet.triton, linnet.hf, linnet.ggufintegrations: Triton Inference Server, Transformers (vLLM, SGLang, TGI), GGUF and Ollama
linnet.packingexamples packed into fixed-size batches as numpy arrays, for either framework
linnet.jax.traintraining in JAX
linnet.trainsupervised fine-tuning over packed sequences; linnet.train.grpo, reinforcement learning; linnet.train.dpo, preferences

Typed program ​

python
from linnet import load_program

program = load_program("examples/01-llama/src/lib.linnet", std_root="stdlib")
print(program)
linnet
llama::Model<Vocab: Dim, H: Dim, Heads: Dim, KvHeads: Dim, Inner: Dim, Layers: Dim, Batch: Dim, MaxSeq: Dim, T: Float = bf16>
  sub embedding: Embedding<Vocab, H, T>
  sub layers: [DecoderLayer<H, Heads, KvHeads, Inner, Batch, MaxSeq, T>; Layers]
  sub norm: RmsNorm<H, T>
  sub lm_head: Linear<H, Vocab, T>
  pub entry forward<B: Dim, S: Dim>(tokens: Tensor[B, S; i32]) -> Tensor[B, S, Vocab; T]
  pub entry decode(token: Tensor[Batch, 1; i32], pos: i32) -> Tensor[Batch, Vocab; T]
  ...

load_program reads linnet plan output into frozen linnet.ir dataclasses; nothing executes. Dimensions stay symbolic:

python
from linnet import ir

entry = program.entry("forward")
ir.format_signature(entry)
# 'pub entry forward<B: Dim, S: Dim>(tokens: Tensor[B, S; i32]) -> Tensor[B, S, Vocab; T]'

weight = next(e for e in program.manifest if e.path.endswith("k_proj.weight"))
ir.format_shape(weight.shape)        # 'KvHeads * (H / Heads), H'
weight.repeat                        # (DimSymbol(id=21, name='Layers'),)

for op in entry.body.walk():         # every operation, nested regions included
    if op.kind == "call":
        print(op.attrs["callee"], [ir.format_type(r.type) for r in op.results])
ClassFields or cases
Programroot, manifest, blocks, functions, constants
Functiongenerics, params, results, states, body (a Region of Ops)
Dimint, DimSymbol, PackSize, DimExpr(op, args) with add, mul, floordiv, mod, min, max
Unita Dim or a Pack (*S)
DTypea name such as "bf16", or DTypeVar
TypeScalarType, TensorType, TupleType, OptionalType, ArrayType, NamedType (block, struct, enum), ShapeType, UnitType

ir.substitute(type, ir.call_substitution(op)) rewrites a callee's types in the caller's generics. format_dim, format_shape, format_type and format_signature print Linnet syntax.

ir.bind_generics(program.root.generics, {"H": 64, ...}) gives the generics values (defaults fill the rest), and the Bindings it returns evaluate dimensions, shapes and dtypes: bindings.shape(weight.shape). Every backend runs from the same Program; compile_plan is load_program with the backends' options (functions=True for the module-level entries).

Diagrams ​

bash
python -m linnet.diagram examples/01-llama/src/lib.linnet --std stdlib \
    --entry forward --expand 1 -o forward.svg

linnet.diagram draws an entry as a dataflow graph, with each edge's inferred tensor type (T[B, S, H]).

OptionEffect
--expand Ninline sub-block calls N levels deep; 0 shows blocks only
--paramsa node per parameter read
--no-arithmeticelementwise operations folded into edges
--format svg|tikz|dotinferred from -o (.svg, .tex, .dot)
--theme light|darkSVG colours

diagram.build(program, "forward", expand=1) returns a Graph for to_svg, to_tikz or to_dot. TikZ output needs \usetikzlibrary{fit, arrows.meta}; DOT needs Graphviz.

Released under the MIT License.