Capture: What is the Wengert list (tape), and how does it underlie the autograd graph implementations in PyTorch, JAX, and TensorFlow?
This capture answers a question flagged as a follow-on from claim-linnainmaa-reverse-mode-single-pass and the reverse-mode-AD/backpropagation correspondence work: what exactly is a "Wengert list" or "tape," and how does that one historical idea show up — in three visibly different engineering forms — inside PyTorch, TensorFlow, and JAX.
Core question: What is a Wengert list (tape), and how does it underlie autograd in PyTorch, JAX, and TensorFlow?
Short answer, established below claim by claim: A Wengert list is the recorded sequence of elementary operations (an "evaluation trace") produced when a composite function is broken into intermediate steps — the substrate both forward- and reverse-mode automatic differentiation operate over. The term traces to Robert Wengert's 1964 paper, though Wengert's own method was forward-mode; the "tape" terminology and its specific use for storing a forward pass to replay backward is a later attachment to his name. All three major deep-learning frameworks implement some version of "record the forward computation, then walk it backward," but they reify the idea as three structurally different objects: PyTorch's dynamically rebuilt object graph, TensorFlow's explicitly named tf.GradientTape, and JAX's jaxpr — a functional intermediate representation produced by tracing, which is a "tape" only in a loose conceptual sense and not in JAX's own vocabulary.
Claim 1: A Wengert list is the evaluation trace of elementary operations produced by decomposing a function into intermediate variables — the basic representation AD techniques operate on
Claim type: definitional Sourcing floor: Tier 3–4 acceptable; achieved Tier 1 (read directly from the primary PDF)
Baydin, Pearlmutter, Radul & Siskind's JMLR survey introduces the term directly, describing the computation y = f(x₁,x₂) = ln(x₁) + x₁x₂ − sin(x₂) broken into intermediate variables vᵢ:
"On the left hand side of Table 2 we see the representation of the computation y = f(x1, x2) = ln(x1) + x1x2 − sin(x2) as an evaluation trace of elementary operations—also called a Wengert list (Wengert, 1964). We adopt the three-part notation used by Griewank and Walther (2008), where a function f : Rⁿ → Rᵐ is constructed using intermediate variables vi such that... variables vi−n = xi... are the input variables, variables vi... are the working (intermediate) variables, and variables ym−i = vl−i... are the output variables."
Innes's Zygote paper (MLSys 2020) gives the same object a slightly broader label, explicitly equating three names for it:
"This form can be viewed as a simple programming language; it is often referred to as a Wengert list, tape or graph (Bartholomew-Biggs et al., 2000)."
And states directly that this structure is the shared substrate of all reverse-mode AD implementations:
"This section outlines our proposed derivative transform for SSA form code, building on the Wengert list transformation that all reverse-mode ADs use at a minimum."
Provenance:
- source: Baydin, Pearlmutter, Radul & Siskind, "Automatic Differentiation in Machine Learning: a Survey," JMLR 18 (2018), pp. 1–43. https://jmlr.org/papers/volume18/17-468/17-468.pdf. Tier 1. Read directly as PDF.
- corroborating source: Innes, "Sense & Sensitivities: The Path to General-Purpose Algorithmic Differentiation," Proceedings of the 3rd MLSys Conference (2020). https://proceedings.mlsys.org/paper_files/paper/2020/file/b422680f3db0986ddd7f8f126baaf0fa-Paper.pdf. Tier 1. Read directly as PDF.
- related existing notes: claim-linnainmaa-reverse-mode-single-pass
Claim 2: The term traces to Robert Wengert's 1964 paper, which described forward-mode differentiation — the "tape" data structure that reverse-mode AD tools carry the name "Wengert list" for a mode Wengert himself did not publish
Claim type: historical — escalated to Tier 1–2 given the specificity of the attribution (which technique Wengert actually published); achieved Tier 1 Sourcing floor: Tier 3–4 acceptable for uncontested historical claims, but this one is escalated per the rubric because it is a load-bearing, slightly counterintuitive point (the list is named for someone who published the other mode of AD)
Baydin et al. state the origin and the mode directly, in their dedicated history section:
"Ideas underlying AD date back to the 1950s (Nolan, 1953; Beda et al., 1959). Forward mode AD as a general method for evaluating partial derivatives was essentially discovered by Wengert (1964). It was followed by a period of relatively low activity, until interest in the field was revived in the 1980s mostly through the work of Griewank (1989)..."
They separately identify reverse mode's automatic-implementation origin as later and distinct:
"Speelpenning (1980) subsequently introduced reverse mode AD as we know it, in the sense that he gave the first implementation that was actually automatic, accepting a specification of a computational process written in a general-purpose programming language and automatically performing the reverse mode transformation."
Innes's Zygote paper states the same forward/reverse split in its opening sentence:
"Its forward (Wengert, 1964) and later reverse (Speelpenning, 1980) modes were first developed for scientific computing, in languages like Fortran."
Wikipedia's "Automatic differentiation" article states the naming-mismatch point more explicitly than either Tier 1 source does — worth recording as a pointer, not as the evidentiary basis, per the sourcing floor: "a data structure known as a 'tape' or a Wengert list (however, Wengert published forward accumulation, not reverse accumulation..." This is consistent with, but not needed to establish, the Tier 1 finding above.
Provenance:
- source: Baydin et al. (2018), Section 3.3 "Origins of AD and Backpropagation," pp. 13–14. Tier 1. Read directly as PDF.
- corroborating source: Innes (2020), p. 1 (Introduction). Tier 1. Read directly as PDF.
- pointer/corroborating source (not load-bearing): Wikipedia, "Automatic differentiation," https://en.wikipedia.org/wiki/Automatic_differentiation, accessed 2026-07-06. Tier 4.
- bibliographic identity of the original paper (uncontested, definitional — Tier 3–4 sufficient): Robert E. Wengert, "A simple automatic derivative evaluation program," Communications of the ACM 7(8):463–464, 1964 — confirmed directly in both Tier 1 sources' own reference lists.
Claim 3: The Wengert list/tape operates as a two-phase mechanism — forward pass records the computation and its dependencies; backward pass replays that record in reverse, propagating adjoints via the chain rule — and its storage cost scales with the number of recorded operations
Claim type: technical-mechanism (with an embedded quantitative/scaling claim) Sourcing floor: Tier 1–2 required; achieved Tier 1
Baydin et al. describe the two-phase structure directly:
"In reverse mode AD, derivatives are computed in the second phase of a two-phase process. In the first phase, the original function code is run forward, populating intermediate variables vi and recording the dependencies in the computational graph through a bookkeeping procedure. In the second phase, derivatives are calculated by propagating adjoints v̄i in reverse, from the outputs to the inputs."
They state the storage-scaling cost of keeping this record directly:
"The advantages of reverse mode AD, however, come with the cost of increased storage requirements growing (in the worst case) in proportion to the number of operations in the evaluated function."
Innes's paper independently states the same scaling point when contrasting reverse mode with forward mode:
"...Julia's forward-mode AD (Revels et al., 2016) has constant memory overhead (compared to reverse mode's tape, linear in the number of instructions executed) and has minimal time overhead..."
As a concrete illustration of what "recording onto a tape, then playing it back" means in an actual implementation (not one of the three target frameworks, but the generic mechanism they all descend from), Baydin et al. describe ADOL-C, a C++ AD tool built on operator overloading:
"ADOL-C requires the use of AD-enabled types for variables, and records arithmetic operations on variables in tape data structures, which can subsequently be 'played back' during reverse mode AD computations."
Provenance:
- source: Baydin et al. (2018), Section 3.2 "Reverse Mode" (p. 12) and Section 5.3 "Operator Overloading" (p. 26). Tier 1. Read directly as PDF.
- corroborating source: Innes (2020), Section 3.4 "Mixed-Mode AD," p. 6–7. Tier 1. Read directly as PDF.
- related existing notes: claim-linnainmaa-reverse-mode-single-pass
Claim 4: PyTorch implements the tape/Wengert-list idea as a dynamic, "define-by-run" computational graph — a DAG of tensors and gradient functions built by operator overloading as the program actually executes, and rebuilt from scratch on every forward pass
Claim type: technical-mechanism Sourcing floor: Tier 1–2 required; achieved Tier 1
Baydin et al. place PyTorch (alongside autograd and Chainer) in the "define-by-run"/"dynamic computational graph" lineage, contrasted with the older "define-and-run" graph-building style:
"...the terms define-by-run and dynamic computational graph refer to the general-purpose AD capability available in newer PyTorch-like systems where a model is a regular program in the host programming language, whose execution dynamically constructs a computational graph on-the-fly that can freely change in each iteration."
PyTorch's own official documentation describes the mechanism in matching terms. From the official tutorial "A Gentle Introduction to torch.autograd":
"autograd keeps a record of data (tensors) & all executed operations (along with the resulting new tensors) in a directed acyclic graph (DAG)... In a forward pass, autograd does two things simultaneously: run the requested operation to compute a resulting tensor, and maintain the operation's gradient function in the DAG."
From the official "Autograd mechanics" notes:
"autograd records a graph recording all of the operations that created the data as you execute operations, giving you a directed acyclic graph whose leaves are the input tensors and roots are the output tensors... By tracing this graph from roots to leaves, you can automatically compute the gradients using the chain rule."
"DAGs are dynamic in PyTorch... the graph is recreated from scratch at every iteration, and this is exactly what allows for using arbitrary Python control flow statements."
Provenance:
- source: Baydin et al. (2018), Section 4.2, p. 18. Tier 1. Read directly as PDF.
- corroborating sources: PyTorch official docs — "A Gentle Introduction to torch.autograd," https://docs.pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html, and "Autograd mechanics," https://docs.pytorch.org/docs/2.12/notes/autograd.html. Both Tier 1 (official project documentation), fetched directly, accessed 2026-07-06.
Claim 5: TensorFlow implements the tape concept explicitly and by name — tf.GradientTape "records" operations executed in its context during the forward pass onto a tape object, then the backward pass traverses that recorded tape in reverse to compute gradients
Claim type: technical-mechanism Sourcing floor: Tier 1–2 required; achieved Tier 1
TensorFlow's official guide, "Introduction to gradients and automatic differentiation," states the mechanism directly and is the one of the three frameworks that uses the word "tape" as its own vocabulary, not just as a description supplied by outside commentators:
"TensorFlow 'records' relevant operations executed inside the context of a tf.GradientTape onto a 'tape.'"
"To differentiate automatically, TensorFlow needs to remember what operations happen in what order during the forward pass."
"TensorFlow then uses that tape to compute the gradients of a 'recorded' computation using reverse mode differentiation."
"Then, during the backward pass, TensorFlow traverses this list of operations in reverse order to compute gradients."
Historical nuance worth flagging: Baydin et al.'s 2018 survey characterizes TensorFlow, at the time of that paper's writing, as belonging to the other camp — the "define-and-run"/"static computational graph" systems (grouped with Theano, Caffe, and CNTK), contrasted directly with PyTorch's dynamic-graph approach: "In mainstream frameworks including Theano (Bastien et al., 2012), TensorFlow (Abadi et al., 2016), Caffe (Jia et al., 2014), and CNTK (Seide and Agarwal, 2016) the user first constructs a model as a computational graph using a domain-specific mini language, which then gets interpreted by the framework during execution." This reflects TensorFlow's original (TF1.x) graph-then-session execution model. The tf.GradientTape mechanism quoted above is TensorFlow's later, eager-execution-era, explicitly tape-named successor — the two descriptions are not in tension, they describe the same project at different points in its history, roughly bridged by the "TensorFlow eager" transition that Baydin et al. cite in passing elsewhere in the same paper (Agrawal et al., 2019, "Tensorflow eager: A multi-stage, python-embedded dsl for machine learning").
Provenance:
- source: TensorFlow official guide, "Introduction to gradients and automatic differentiation," https://www.tensorflow.org/guide/autodiff. Tier 1 (official project documentation), fetched directly, accessed 2026-07-06.
- corroborating/contextual source: Baydin et al. (2018), Section 4.2, p. 17–18. Tier 1. Read directly as PDF.
Claim 6: JAX does not implement a literal "tape" object; it traces the Python function into a jaxpr — a functional, statically-typed intermediate representation — and defines its forward- and reverse-mode transformations over that IR rather than over a mutable recorded operation list
Claim type: technical-mechanism Sourcing floor: Tier 1–2 required; achieved Tier 1
JAX's own internals documentation defines the object its transformations actually operate on:
"Jaxprs are JAX's internal intermediate representation (IR) of programs. They are explicitly typed, functional, first-order, and in algebraic normal form (ANF)."
"Conceptually, one can think of JAX transformations, such as jax.jit() or jax.grad(), as first trace-specializing the Python function to be transformed into a small and well-behaved intermediate form that is then interpreted with transformation-specific interpretation rules."
As a direct check on whether JAX's own documentation uses "tape" language at all, "Autodidax: JAX core from scratch" — JAX's own from-first-principles walkthrough of how its autodiff is implemented — was searched in full for the literal word "tape." It does not appear anywhere in that document, across sections covering both forward-mode (JVP) and reverse-mode (VJP/partial evaluation into a jaxpr) differentiation.
Innes's Zygote paper independently and directly discusses JAX's design in these terms, describing it as a "staged" approach distinct from tape-recording:
"In a recent system like JAX (Frostig et al., 2018), the Python interpreter can be viewed as taking on the role of compiler. Like Julia's abstract interpreter, it evaluates code with partial information (types) in order to statically resolve polymorphic methods, and then takes a back seat for program runtime."
"But the trace need not be a pure Wengert list, and 'staging' more of the host language's constructs (for example, control flow) into the trace makes this look increasingly like source-to-source AD. Many state-of-the-art ML systems, which originally took very different approaches to AD, are converging towards these staged programming approaches (Agrawal et al., 2019; PyTorch Team, 2018; Frostig et al., 2018)."
Provenance:
- source: JAX official docs, "JAX internals: The jaxpr language," https://docs.jax.dev/en/latest/jaxpr.html. Tier 1 (official project documentation), fetched directly, accessed 2026-07-06.
- corroborating source (absence-of-term check): JAX official docs, "Autodidax: JAX core from scratch," https://docs.jax.dev/en/latest/autodidax.html. Tier 1, fetched directly and searched in full, accessed 2026-07-06.
- corroborating source: Innes (2020), Section 1.1 "Convergence in AD Design," pp. 1–2. Tier 1. Read directly as PDF.
Synthesis: one idea, three different reifications
Across all three frameworks, the same underlying idea persists: record the sequence of elementary operations performed on the way to computing an output (a Wengert list, in Wengert's and Baydin et al.'s terminology), then traverse that record backward, applying the chain rule at each step to propagate adjoints. What differs is what kind of object each framework actually builds to hold that record:
- PyTorch builds a mutable, object-based DAG of tensors and
grad_fngradient functions, assembled by operator overloading as ordinary Python code executes, and discarded and rebuilt from scratch on every forward pass (claim-linnainmaa-reverse-mode-single-pass's single-backward-sweep structure, reified at Python-object granularity). - TensorFlow builds an explicitly named
tf.GradientTapeobject — the one of the three that uses "tape" as its own vocabulary, not an outside description — which records operations during awithblock and is played back in reverse when a gradient is requested. - JAX builds no literal tape at all in its own documentation's own words; it traces a function into a
jaxpr, a functional, immutable intermediate representation, and definesjvp/vjpas transformations over that representation. Per Innes's independent, direct description, this can be understood as "forward tape + backward player" in conceptual structure — but that framing is a secondary gloss (Tier 1, but not JAX's own language), not JAX's own self-description, which never uses the word "tape."
None of the three claims above required stretching any single source past what it actually says; the interesting finding is closer to a gap than a discovery — the "Wengert list" label is doing real historical work (it names the shared ancestor concept) while doing comparatively little descriptive work for at least one of the three frameworks it is sometimes casually applied to.
Further leads
- Griewank's 2012 Documenta Mathematica history paper ("Who Invented the Reverse Mode of Differentiation?"), already used elsewhere in the vault for the Linnainmaa cluster, was not re-consulted here for the Wengert-specific naming question; it would be worth checking directly whether Griewank himself comments on the "Wengert list" label being retroactively attached to a reverse-mode data structure, since this capture currently rests that specific framing on Wikipedia (Tier 4, non-load-bearing) rather than a primary source.
- ADOL-C's own documentation/papers (Walther and Griewank, 2012) were not read directly here — only described via Baydin et al.'s secondary summary — and would let a future capture upgrade the "tape data structures... played back" quote to a source about ADOL-C's own self-description rather than a survey's description of it.
- The TensorFlow "eager execution" transition (Agrawal et al., 2019, "TensorFlow Eager: A multi-stage, Python-embedded DSL for machine learning") is cited only in passing here via Baydin et al.'s bibliography; reading that paper directly would sharpen exactly when and how TensorFlow moved from the "define-and-run" graph style Baydin et al. describe to the tape-based
tf.GradientTapestyle documented today. - Innes's Zygote/MLSys paper is a substantial, previously-unused-in-this-vault Tier 1 primary source on AD implementation design generally (SSA-form differentiation, control-flow handling, checkpointing, complex/higher-order differentiation) — worth a dedicated note or two beyond the Wengert-list-specific material extracted here.