Course outline

PyTorch / Storage, restoration, and transformed execution / Lesson 17

Export and dynamic-shape contracts

Describe the supported tensor program and input domain rather than treating one captured example as unrestricted portability.

About 4 min readContent reviewed
On this page

Lesson goals

  • Distinguish an exported tensor graph from an arbitrary Python program.
  • Specify static dimensions and bounded dynamic dimensions.
  • Validate behavior and compatibility within the declared export contract.

Export captures a constrained tensor computation

torch.export.export takes a module and example arguments and produces an ExportedProgram under the documented capture and shape assumptions. It is not the same as saving a state_dict or optimizing a Python callable with torch.compile. The graph, state, signature, constraints, and supported operators have their own meaning. Export does not promise to preserve every arbitrary Python side effect or data structure as an unconstrained future program. Inspect the specific pinned API, including strict versus non-strict capture, instead of assuming a successful ordinary forward proves exportability or a universal deployment target.

Make the dynamic input domain explicit

The illustrative pointwise module computes 2*x+1 with a fixed feature width of two. A named Dim declares batch sizes from two through four while the example batch has size two. The derived new-input result has shape (3,2) and each element is three. strict=True is explicitly requested rather than inherited from an evolving default. The example deliberately avoids zero- and one-size specialization questions and makes no promise about inputs outside its domain. A dynamic dimension declaration is a constraint contract, not a command that makes incompatible branches or reshape assumptions disappear.

python Illustrative CPU PyTorch 2.14 API program; torch unavailable, expected output derived
import torch
from torch import nn
class Scale(nn.Module):
    def forward(self,x):
        return 2*x+1
sample = torch.ones(2,2,dtype=torch.float64,device="cpu")
batch = torch.export.Dim("batch",min=2,max=4)
program = torch.export.export(Scale(),(sample,),dynamic_shapes={"x":{0:batch}},strict=True)
new_input = torch.ones(3,2,dtype=torch.float64,device="cpu")
output = program.module()(new_input)
print("shape", tuple(output.shape))
print("first row", output[0].tolist())
Expected output
shape (3, 2)
first row [3.0, 3.0]

The pointwise function has an explicitly bounded dynamic batch and fixed feature width. No export capture, constraint inference, exported-module execution, or serialization was performed.

Shape reasoning must match the actual model

An operation can require equality between dimensions, divisibility, a minimum size, or a fixed feature count. Export can report a conflict between those inferred requirements and the user’s declared dynamic specification. Read and justify the needed relationship rather than copying a suggested fix that changes the supported task silently. Tensor-value-dependent Python branching and unsupported operators need their own supported reformulation or explicit limits. A graph with constrained shapes does not automatically validate units, category mappings, label availability, or meaningful numeric ranges. Keep semantic input checks beside the captured-program contract.

A local guard illustrates scope without implementing export

The standard-library companion accepts a rectangular batch with two features and sizes two through four, rejects five, and computes the same pointwise arithmetic. Its custom error message is part of that local validator only. It does not simulate ExportedProgram runtime guards or prove that every shape in the range was captured correctly. In a supported framework environment, compare ordinary and exported behavior on representative valid sizes, boundary sizes, invalid shapes, relevant dtypes, and the intended module mode. Validate outputs and downstream decisions with a justified comparison rule.

python Executed deterministic standard-library companion
def validate(rows):
    if not 2 <= len(rows) <= 4:
        raise ValueError("batch outside 2..4")
    if any(len(row) != 2 for row in rows):
        raise ValueError("expected two features")
    return [[2*value+1 for value in row] for row in rows]
output = validate([[1.0,1.0]]*3)
print("shape", (len(output),len(output[0])))
try:
    validate([[1.0,1.0]]*5)
except ValueError as error:
    print(str(error))
Expected output
shape (3, 2)
batch outside 2..4

The executed local validator checks a declared shape domain and arithmetic, not torch.export guards, captured operators, or a portable exported artifact.

Portability and artifact loading need their own evidence

A captured graph and a saved export artifact depend on supported format, operators, runtime, and version assumptions. Do not infer long-term or cross-version compatibility from one successful capture or from a filename extension. Preserve architecture, preprocessing, state identity, shape contract, mode, and known-input evidence appropriate to the consumer. The PyTorch 2.14 program here is illustrative because torch is unavailable; no export API, dynamic-shape solver, graph verifier, artifact save/load, deployment backend, or exported module was executed. Only the bounded list validator and arithmetic companion were run.

Common errors and fixes

An example batch is treated as proof of all future sizes

Why it happens
Static and dynamic shape assumptions were not declared or checked.
How to fix it
Specify and test the actual supported input domain.
Prevent it next time
Include valid boundary sizes and invalid shape cases.

A suggested constraint fix silently narrows the product task

Why it happens
A capture error was resolved without reviewing required coverage.
How to fix it
Reconcile model assumptions and intended input requirements explicitly.
Prevent it next time
Record shape-contract changes as interface changes.

An exported file is assumed universally portable

Why it happens
Runtime, operator, and format compatibility were omitted.
How to fix it
Test the documented consumer environment and loading policy.
Prevent it next time
Preserve version assumptions and known-input behavior with the artifact.

Check your understanding

1. Why does the example declare batch 2..4 rather than claim any positive batch?

Show worked solution

It states a bounded teaching domain and avoids inferring unrestricted behavior from one sample. The pointwise arithmetic is simple, but the exported contract still requires supported capture and verification. Another domain should be declared and tested deliberately rather than assumed.

2. Would accepting shape (3,2) establish that the input features use the intended units?

Show worked solution

No. Shape constraints describe dimensions, not measurement meaning. A consumer must preserve feature order, units, preprocessing, and output semantics separately. The local companion proves only its own shape checks and arithmetic and does not execute an exported PyTorch graph.

Key takeaways

  • Export represents a supported program under explicit assumptions.
  • Dynamic dimensions define a constrained domain, not unlimited generality.
  • Behavior, semantics, artifact trust, and portability require separate checks.

Official references