CUDA Autograd Tape #
Shape-erased CUDA tape machinery for float32 Cuda.Buffer values. This is the GPU/runtime analogue
of the CPU autograd tape: it records node ids, runtime shapes, parent links, and backward callbacks,
then performs reverse-mode accumulation over buffers.
Pure error monad for the CUDA tape. Mirrors Engine/Core.
Instances For
Runtime, shape-erased CUDA buffer.
This plays the same role as Runtime.AnyTensor in the CPU tape: it pairs runtime Shape
metadata with an opaque Cuda.Buffer handle.
- s : Spec.Shape
Runtime shape metadata for the buffer.
- buf : Buffer
Device buffer (float32). Length is expected to be
Spec.Shape.size s.
Instances For
Number of scalar elements as a UInt32, with every individual shape dimension checked too.
Instances For
Allocate a zero-filled buffer for a given shape.
Instances For
Check that runtime shape metadata agrees with the native CUDA buffer length.
AnyBuffer is intentionally shape-erased, so its structure alone cannot enforce this invariant.
Call this at boundaries that accept buffers assembled outside the typed tensor conversion path,
before any kernel uses shape-derived indexing.
Instances For
Accumulate two AnyBuffer values by elementwise addition, with a dynamic shape check.
This is used by backprop to sum gradient contributions in DAGs. The addition runs only after the shape metadata agree and both native buffer lengths match that shape.
Instances For
CUDA tape node representing one recorded computation step.
Fields mirror the CPU Engine/Core node:
valueholds the forward buffer.parentsare tape node ids.backwardis a local VJP rule producing parent gradient contributions.
Optional node name for debugging/pretty-printing.
- value : AnyBuffer
Forward value computed at this node.
- requires_grad : Bool
Whether reverse-mode propagation should visit this node.
Parent node ids (dependencies) in the tape.
Forward workspace buffers retained only because this node's backward closure may need them.
The eager runtime releases these buffers explicitly after backprop consumes the tape, so long CUDA training loops do not wait on Lean external-object finalizers for large intermediate allocations.
Local VJP rule for this node.
Given an upstream cotangent for
value, return a list of(parentId, parentCotangent)contributions (one per parent, usually).
Instances For
CUDA autograd tape: a grow-only array of nodes. Node ids are array indices.
Tape nodes in evaluation order (id = index).
Instances For
Number of nodes stored in the tape.
Instances For
Add a leaf node (no parents).
PyTorch comparison: a tensor that enters the graph as a leaf (e.g. input or parameter value).
This low-level constructor records value without validating its external buffer. Code that wraps
an externally supplied Buffer must call AnyBuffer.validate first; tape operations validate
their operands again when they retrieve values.
Instances For
Require that an upstream gradient matches an expected runtime shape.
This is used inside backward closures to validate/cast the incoming cotangent. Both the shape tag and native buffer length are checked before a VJP reads the cotangent.
Instances For
Generic constructor for unary ops.
You provide:
forward : Buffer → Bufferbackward : Buffer → Buffer → Buffer(VJP; given inputxand upstreamdLdy, returndLdx)
Shapes are explicit and checked dynamically.
Instances For
Generic constructor for binary ops.
You provide:
forward : Buffer → Buffer → Bufferbackward : Buffer → Buffer → Buffer → (Buffer × Buffer)(VJP; given inputsa,b, and upstreamdLdy, return(dLda, dLdb))
Shapes are explicit and checked dynamically.
Instances For
Internal helper: add a gradient contribution g into the dense gradient array at id.
This checks:
idis a valid node id,- the parent requires gradients,
- the contribution shape matches the parent's value shape,
then accumulates via
AnyBuffer.add.
When the parent does not require gradients, the contribution is not stored anywhere. We still have
to release its device buffer. The release is threaded through the existing zero slot with
releaseThen, so the cleanup is part of the returned gradient array instead of a dead pure call.
Instances For
One reverse-mode backprop step at a single node id, updating the dense gradient array.
The incoming array is total: every tape node has a gradient buffer, initialized to zero unless a later node has already contributed to it. That total representation is convenient for the CUDA runtime because every slot has a concrete device buffer that can be released deterministically.
Instances For
Reverse-mode accumulation over the first n nodes in reverse order.
The recursion visits n-1, n-2, ..., 0; using n = t.size runs the full tape. We keep this as a
structural loop rather than a list fold so proof layer callers can reason about one node step at a
time.
Instances For
Reverse-mode accumulation starting from an explicit dense gradient array.
This expects grads0.size = t.size. The function is useful for callers that already seeded
multiple outputs or want to run a custom cotangent initialization. Before traversal, every slot is
checked against its node's shape and native buffer length.
Instances For
Run one dense reverse step only when id has been reached from the selected output, and mark the
parents that receive concrete VJP contributions. The gradient array remains total so consumed CUDA
buffers continue to follow the ownership discipline in addGradAll.
Instances For
Reverse-mode accumulation that returns a dense gradient buffer for every node id.
All gradients are initialized to zeros (using each node's runtime Shape), then the output node is
seeded and only nodes reached by VJP propagation are executed. Disconnected slots stay explicit
zero buffers; their backward closures are not called.
Instances For
Sparse gradients
Training only needs cotangents for parameter leaves. The sparse traversal below releases each activation cotangent immediately after its local VJP has propagated it and retains owned copies only for node ids selected by the caller.
Device gradients retained for selected tape node ids.
Instances For
Release every buffer owned by a sparse gradient map.
Instances For
Insert or accumulate an owned VJP contribution into the sparse gradient map.
CUDA backward rules return fresh contribution buffers. The sparse map copies a first contribution so its retained values have uniform ownership, then retires the consumed contribution immediately.
Instances For
Run reverse mode while retaining gradients only for node ids accepted by retain.
This function consumes seed. The returned buffers are owned by the map and must be released with
releaseSparseGrads after the optimizer has consumed them. All other activation gradients are
retired during the traversal.