topos

Why it is shaped this way

The element is the seam

The graph is always tensors. A scalar is rank 0. The open plug is a number, not a second engine.

The graph is always tensors. A scalar is rank 0. The open plug is a number, not a second engine.

Why#

A stack that grew up teaching on scalars is tempted to keep two graph kinds: a scalar engine for the page, a tensor engine for the models, and a trait wide enough that both inhabit it. The scalar impls then spend their lives asserting rank, ignoring shape arguments, and reimplementing unfold as a no-op. The core gets harder to read than the math requires.

The named seam people should plug is a new number — a research float, an interval, a dual — not a reimplementation of windowing. If the engine is generic over "payloads that might be tensors," every new number pays the tensor tax again.

The idea#

the spec over f64, f32, and Bf16 is the same text: true
loss over f64   3.7912668765691295
loss over f32   3.791267
loss over Bf16  3.78125
Plate 1. One recording, three numbers. The spec is the same text over every element; only the bits a run answers differ, and each element's bits are its own.

One graph kind. Every node is a tensor. Rank 0 is a scalar: the shape column already says so, and the payload does not need a second sort. Recording can still look scalar — a rank-0 literal, arithmetic that reads like a line — because a scalar is a tensor, not because the engine is.

The type every public phase is generic over is the element: arithmetic, the identities, the accumulator, the elementary maps, and the optional hooks a backend may offer. Implement those on a number and the tensor machinery, the derivative rules, and the engine come along. A new element never reimplements unfold.

The recordable vocabulary is not a payload kind. It is the opcode set, with exactly two readings: compute over tensor buffers, or record the same operations as nodes. Forward math is inherent to tensors; the rules never need it. That cut is what lets a new element inherit reverse mode without writing a rule.

Backends, emission, and notebooks already speak an element contract — precision, literals, displays. The seam is that contract, named once.

Consequences#

  • There is no scalar graph. Teaching examples are rank-0 tensors.
  • Plugging the seam is implementing a number. It is not an engine fork and not a new opcode.
  • Shape lives on the node, inferred at record time. The element does not carry a second shape.
  • Transcendentals and comparisons that the maps need live on the element. Windowing, gathers, and folds do not.
  • An element that does not hook a backend runs on the interpreter. That is the default, not a failure.
  • Emission and display are further contracts on the same number, not a second payload axis.

Not this#

  • A parallel scalar IR "for pedagogy."
  • Making the recordable vocabulary a thing a new float must implement method-by-method.
  • Fused executors on the element. Those are plan-tier kernel faces, not arithmetic.
  • Treating layout, storage, or rank as part of the number.

See Vision for named seams, and Recorded reverse mode for the two readings of the recordable vocabulary.

Spelled today#

Tape<E> / Network<E> for E: Element. Element is Differentiable plus Elementary. Built-ins: f32, f64, Bf16. Recordable is the vocabulary a rule may call (Tensor computes, Trace records). Emittable and Sample are further element contracts. reference is how an out-of-tree element grades a kernel. This section may rot; the rest must not.