TorchLean

4.3. GraphSpec🔗

GraphSpec is TorchLean's typed authoring DSL for architectures whose structure should be explicit: parameter layout, residual sharing, DAG structure, pure semantics, and executable lowering.

The earlier chapters cover the public model-building surface. This page is about the architecture boundary underneath it: the point where a model is still readable as a model, but already carries enough type information to support proofs and lowering.

4.3.1. Why GraphSpec Exists🔗

The TorchLean paper centers the project around a shared SSA/DAG IR with operation tags (NN.IR.Graph) used by runtime compilation and verification. GraphSpec is not a second copy of that IR. It solves a different problem:

  • NN.IR.Graph is the shared backend object for verification, inspection, and formal reasoning.

  • GraphSpec is the typed model-authoring language when the architecture itself should be a first-class mathematical object.

That distinction matters especially for residual networks, explicit sharing, and model families whose parameter interface should stay stable across lowerings.

4.3.2. What GraphSpec Adds🔗

The graph taxonomy belongs in Graphs and IR. The additional point here is more specific: GraphSpec gives architectures a typed authoring language before they are lowered into a runtime program or the shared op-level IR.

That authoring layer adds three things that a raw IR graph does not try to provide:

  • a parameter ABI in the type, so the order and shapes of trainable tensors are part of the model interface;

  • architecture-level sharing, so residual branches and reused intermediates can be written directly;

  • paired interpreters, so the same architecture object has a pure Spec meaning and an executable TorchLean program.

In short: GraphSpec records architecture facts before the model becomes a lower-level artifact.

4.3.3. Two Authoring Surfaces🔗

GraphSpec exposes two related surfaces because a linear chain and a general DAG with sharing are different authoring problems.

4.3.3.1. Sequential surface: Graph ps σ τ🔗

The sequential DSL is defined in NN.GraphSpec.Core API.

  • Graph ps σ τ means: a typed graph from input shape σ to output shape τ, with parameter tensor shapes ps.

  • Composition uses >>>, so straightforward pipelines read like architecture syntax rather than like a manually-assembled IR.

  • The core MLP examples live here.

This is the easiest GraphSpec surface for readers coming from nn.Sequential.

The type itself is worth reading carefully:

Graph ps σ τ

means:

  • σ is the input shape,

  • τ is the output shape,

  • ps is the ordered list of parameter tensor shapes.

For example, a dense layer from Vec inDim to Vec outDim has parameter shapes:

[Mat outDim inDim, Vec outDim]

That list is not just documentation. It is the parameter ABI of the graph: the pure interpreter and the executable compiler both expect parameters in that order.

4.3.3.2. DAG surface: DAG.Model ps ins τ🔗

The GraphSpec DAG API and GraphSpec DAG core define the shared-branch authoring surface.

  • It uses an SSA/A-normal-form term language with explicit binding and sharing.

  • It is the right surface for residual blocks, skip connections, and models such as ResNet-18.

  • GraphSpec's canonical "general model" type is this DAG Model.

For "use this intermediate twice" or "add a shortcut branch," the DAG layer is the important one.

4.3.4. Parameter ABI🔗

GraphSpec treats parameter layout as part of the architecture. Parameter shapes and ordering live in the type.

That gives the architecture layer two practical benefits:

  • parameter ABI is explicit and stable enough to reason about, and

  • the same model can be interpreted, lowered, and checked against a single declared parameter order.

Many downstream artifacts assume an exact parameter order, so GraphSpec makes that order part of the model interface instead of leaving it implicit in helper code.

4.3.5. Choosing The Right Surface🔗

Use the smallest surface that still says what you need to say.

  • Use nn.Sequential for ordinary tutorials and model training files.

  • Use sequential GraphSpec when the model is still a chain, but the parameter ABI and pure semantics should be explicit.

  • Use GraphSpec DAG models when the architecture has sharing, residual branches, or multiple inputs.

  • Use NN.IR.Graph when the consumer is a verifier, exporter, or graph level analysis pass.

This keeps each layer explicit: architecture authoring stays readable, while verifier-facing code still receives a precise op-level graph after lowering.

4.3.6. Two Semantics From One Model🔗

GraphSpec is valuable because it does not force an early choice between a clean mathematical object and an executable object.

For sequential graphs, the main names are:

  • NN.GraphSpec.Interp.spec

  • NN.GraphSpec.Compile.torchProgram

For DAG models, the corresponding names are:

  • NN.GraphSpec.Model.specFwd

  • NN.GraphSpec.Model.torchProgram

The pattern is the same in both cases:

  • the Spec semantics supplies a pure forward meaning in Lean,

  • the TorchLean compiler supplies an executable program in the runtime layer.

GraphSpec packages this semantic alignment at the architecture-authoring level.

The declarations worth recognizing in the infoview are:

#check NN.GraphSpec.Interp.spec
#check NN.GraphSpec.Compile.torchProgram
#check NN.GraphSpec.DAG.Model.specFwd
#check NN.GraphSpec.DAG.Model.torchProgram
#check NN.GraphSpec.Model.specFwd
#check NN.GraphSpec.Model.torchProgram
#check NN.GraphSpec.ToTorchLean.toSeq

4.3.7. Worked Micro-Example🔗

The smallest useful GraphSpec check sequence is the one from the README:

import NN.Entrypoint.GraphSpec

open NN
open NN.GraphSpec
open Spec

def g (inDim hidDim outDim : Nat) :=
  GraphSpec.Models.mlp inDim hidDim outDim

-- Pure semantics: parameters to input to output.
#check GraphSpec.Interp.spec (g 4 8 2)

-- Executable TorchLean program.
#check GraphSpec.Compile.torchProgram (g 4 8 2)

-- Optional lowering into the general DAG model representation.
#check GraphSpec.LowerToDAG.Graph.toDAGModelZeroInit (g 4 8 2)

The important lesson is not just that these names exist. The lesson is that one architecture can be read at three levels:

  • as typed authoring syntax,

  • as pure semantics,

  • and as executable runtime code.

4.3.8. Residual Sharing As A Tiny DAG🔗

A residual block is the first example where the DAG surface is more natural than a chain. The architecture says:

y = f(x)
z = y + x

The value x is used twice. In a purely sequential chain, that sharing is awkward because the input would have to be carried forward as an explicit side value. In a DAG model, the intermediate names are part of the authoring language.

This is the reason examples such as Models.residualLinear and Models.ResNet18.model use the DAG surface: their architecture contains skip connections, so shared values should be represented directly.

4.3.9. Lowering Paths🔗

GraphSpec supports several lowering paths. They are not competing APIs; they answer different questions about the same architecture.

4.3.9.1. Sequential Graph To DAG Model🔗

The relevant definitions are GraphSpec core and GraphSpec DAG core.

Important names:

  • LowerToDAG.Graph.toDAGTerm

  • LowerToDAG.Graph.toDAGModelZeroInit

  • LowerToDAG.Graph.toDAGModelDetInit?

This path turns a simple pipeline into GraphSpec's general DAG representation.

4.3.9.2. GraphSpec To TorchLean nn.Sequential🔗

The lowering API is GraphSpec to TorchLean.

Important name:

  • NN.GraphSpec.ToTorchLean.toSeq

This path is used by the runnable GraphSpec tutorial: author once in GraphSpec, then plug the lowered model into the same train.* API used elsewhere in the guide.

4.3.9.3. DAG Model To Runtime Model Zoo Wrappers🔗

The runtime examples to open are GraphSpec ResNet18 and runtime FNO1D.

These wrappers show how GraphSpec feeds the runtime-facing model zoo:

  • GraphSpec-backed resnet18Model, resnet18Program, resnet18InitParams

  • fno1d runtime wrappers for operator learning style work, which sit beside the GraphSpec-backed models in the broader model zoo

4.3.10. Model Zoo Landmarks🔗

For model definitions used in examples and demos, NN.GraphSpec.Models is the most convenient single import surface.

The current zoo intentionally includes both sequential and DAG-native models:

  • Models.mlp for the smallest sequential path,

  • Models.cnn2 and cnn2DAGModelZeroInit for "same model, different representation" comparisons,

  • Models.residualLinear for a small residual/shared example,

  • Models.ResNet18.model for a substantial DAG-authored architecture.

The GraphSpec README suggests this order:

  1. Models.mlp

  2. Models.cnn2

  3. Models.residualLinear

  4. Models.ResNet18.model

4.3.11. Reading Rule🔗

When reading a GraphSpec model, look for four things:

  1. the input and output shapes;

  2. the parameter-shape list;

  3. whether the model is sequential or DAG-native;

  4. which lowering or theorem cites it.

That is the information GraphSpec contributes beyond the public tutorial syntax. It is not another training API; it is the architecture layer that preserves parameter ABI and sharing before runtime or verification lowering.

4.3.12. Proof Alignment Checks🔗

GraphSpec also has a small but important proof side. The declarations below are compact alignment statements: they connect GraphSpec syntax to reference specs, deterministic parameter initialization, and the embedding of sequential primitives into DAG primitives.

The entrypoint NN.Entrypoint.GraphSpec re-exports the proof-facing names readers are most likely to check first:

#check NN.GraphSpec.Models.mlp_interp_eq_spec_mlp_forward
#check NN.GraphSpec.Models.mlp_detInitParams_eq_torchlean_linear_inits
#check NN.GraphSpec.Primitive.toDAGPrimOp_specFwd_eq

These are worth knowing about for two reasons:

  • they confirm that GraphSpec is meant to support theorem statements, not only code generation,

  • and they illustrate the "extend op by op" development style that also shows up in the verified runtime and verification layers.

4.3.13. Best First Path🔗

One concrete reading order:

  1. Read the GraphSpec README.

  2. Open the GraphSpec tutorial API.

  3. Run: lake exe torchlean graphspec --backend compiled

  4. Read the residual linear model API.

  5. Read the ResNet-18 GraphSpec API.

That path moves from "author a typed MLP and lower it into the training API" to "author a real residual DAG with explicit sharing."

4.3.14. Where It Fits In The System🔗

GraphSpec refines TorchLean's semantic model at the architecture boundary. In a Python codebase, architecture authoring and execution are often collapsed into the same object. GraphSpec separates them just enough to make semantics and proofs easier, without abandoning runnable models.

To inspect the implementation, start with the GraphSpec README, then move to the sequential and DAG cores, then to the model zoo. The GraphSpec to TorchLean API gives the lowering path into TorchLean Seq, and the GraphSpec TorchLean wrappers expose runtime model wrappers.

Next: Runtime and Autograd (execution), Graphs and IR (shared op-level graph), Verification (certificates and bounds).

4.3.15. References🔗

  • TorchLean paper: https://arxiv.org/abs/2602.22631

  • ResNet reference: He et al., "Deep Residual Learning for Image Recognition" (CVPR 2016). https://arxiv.org/abs/1512.03385