PyTorch graph capture #
This module generates a Python script that captures an nn.Module and writes a shared
tensor-graph artifact. The Lean importer checks it before evaluation:
PyTorch nn.Module
--torch.export / FX capture-->
TorchLean graph JSON (`torchlean.ir.v2`)
--Import.IR.parseProgram-->
NN.IR.Program
PyTorch handles capture and shape propagation. The generated script translates supported
operations into NN.IR.OpKind and reports an error for unsupported operations or configurations.
The importer then checks the graph's edges, shapes, and operation attributes.
Tensor entries also retain their dtypes. Binary constants and linear parameters carry complete
interchange words for Import.IR.parseProgram and its typed payload loader. Conversions and
pointwise tensor promotions become explicit cast nodes, two of them when an integer, Boolean or
binary64 value becomes binary16 or bfloat16, which c10::convert makes from binary32. Pointwise
broadcasting is explicit too.
Metadata assertions and assumptions needed by a lowering are retained as capture_constraints.
For example, partial top-k routing assumes finite scores and a strict gap at the cutoff;
capturing one example does not establish those conditions for every input. The CPU reference
interpreter does not validate these assumptions or physical device and layout constraints.
example may be one tensor or a tuple of positional tensors. Argument order comes from
torch.export's graph signature, not from parameter placeholder order. Unused user arguments
remain in the interface; dead parameter values and dead intermediate nodes may be omitted.
The artifact lists every input in input_ids, in argument order.
Returned containers are flattened to tensor leaves; non-tensor leaves are rejected.
Operator matching uses exact callable or ATen identities. For example, torch.log can become an
IR .log node, while torch.log1p needs a different formula and is rejected. Similar names and
matching output shapes are insufficient to identify the computation.
Pointwise operators from the core ATen vocabulary (neg, rsqrt, erf, gelu, div, pow and
clamp_min with a scalar bound) become IR pointwise nodes in the arithmetic of the vectorized loops
of PyTorch's CPU kernels.
Operands are converted as PyTorch converts them, Python scalars included, and binary16 and bfloat16
values are widened to binary32 where those loops compute in binary32. A Python scalar operand of
add, sub or mul becomes a constant in the same way. For bfloat16 and binary16,
a scalar exponent of pow is rounded to the tensor's dtype before PyTorch's dispatch compares it.
Exponent 3 is x * x * x, not a rounded cube. The frontend-lowering section of
NN/IR/README.md states the rules and the elements PyTorch computes differently.
The script is assembled from one definition per Python section so each piece can be read and
changed on its own. Every "kind" string it writes comes from NN.Runtime.PyTorch.Wire, the
same table the Lean importer parses with.
References:
torch.export:https://docs.pytorch.org/docs/stable/user_guide/torch_compiler/export.htmltorch.fx:https://docs.pytorch.org/docs/stable/fx.html
Options for the generated PyTorch graph-capture script.
- functionName : String
Name of the Python helper function emitted into the script.
- preferTorchExport : Bool
If true, use
torch.export.exportfirst and fall back to FX symbolic tracing. - includeDebugTargets : Bool
If true, include raw PyTorch target strings in each node for debugging.
Instances For
Python sections #
Each section is an array of Python lines ending in one blank separator line. Indentation is applied here so the sections concatenate into a valid module.
Imports and the artifact format marker.
Instances For
Shape readers for FX node metadata plus the getitem/getattr target tests.
Instances For
Kind entries for tuple-valued FX nodes; nn.MultiheadAttention carries its payload.
Instances For
Model loading, target naming, FX node reference collection, and spatial tuple parsing.
Instances For
Parameter payload serialization for linear, attention, and convolution nodes.
Instances For
Flatten-versus-reshape selection and axis normalization.
Instances For
Python dictionary from pointwise plan names to their wire kinds.
Instances For
Pointwise operators: recognition, validation, and emission as IR nodes.
_pointwise_plan identifies the operator, its attributes and its operands, which may be Python
scalars. _arithmetic_kind does the same for add, sub and mul with a scalar operand, and
the dtype conversion rule plans a conversion. _emit_pointwise appends the nodes the vectorized
loops of PyTorch's CPU kernels compute: scalar constants, conversions to the common dtype and to
the computation type, explicit broadcasts, the reduced-precision division and multiplication paths
for a one-element right operand, and PyTorch's dispatch of pow with a scalar exponent.
Instances For
_lower_kind rule for pointwise operators, checked before every other rule.
Instances For
Opening of _lower_kind: target name, argument, and axis extraction shared by all rules.
Instances For
Mixture-of-experts routing written as torch.zeros_like(p).scatter(-1, torch.topk(p, k, dim=-1).indices, 1.0), captured as one top_k_mask node over p. TorchLean's mask keeps ties for
the lower index; torch.topk documents no tie order. For 0 < k < width, agreement assumes finite
scores and different k-th and k + 1-th largest entries in each row. The artifact records these
conditions as unchecked capture_constraints; keeping no entries or every entry needs neither
condition. The zeros and the mask must have p's dtype, since the mask node inherits it;
any other dtype is left to the ordinary operator checks, which reject a
zeros_like dtype override. The topk call, its index projection and the zeros exist only to
build the mask and are not exported.
Instances For
_lower_kind rules for call_module nodes, keyed on the nn.Module subclass.
Instances For
_lower_kind rules for elementwise aten/functional targets that carry no payload.
A dtype conversion and arithmetic with a Python scalar operand return a plan for
_emit_pointwise, which writes the conversions and constants PyTorch computes.
Instances For
_lower_kind rules for reductions, softmax, and shape operations.
Instances For
_lower_kind rules for functional layers with payloads: layer norm, linear, batch norm.
Instances For
_lower_kind rules for functional convolution and pooling, then the tuple/fallback cases.
Instances For
The complete _lower_kind function.
Instances For
Check captured operations before dropping unused values.
FX records x.relu_(); return x with an unused result for the ReLU call. Skipping that call before
checking its contract would erase the update to x. Unsupported calls therefore fail even when
their results have no users.
Instances For
Graph capture: torch.export when preferred and lowerable, otherwise FX symbolic tracing.
Instances For
The exported entry point: walk the captured graph and write the artifact.
Instances For
Command-line main for the generated script.
Instances For
Emit a Python script that captures a PyTorch module and writes TorchLean graph JSON.
The generated script expects a Python file containing a zero-argument model constructor or class:
python export_torchlean_graph.py my_model.py MyModel out_graph.json --example-shape 1,4
The first implementation target is the shared IR subset: elementwise ops, matmul, reductions,
reshape/permute/flatten/concat, softmax, layernorm, and simple pooling/conv metadata when PyTorch
exposes enough static arguments. Linear layers can appear either as aten.linear or as lower-level
matmul/add depending on PyTorch's graph capture.