TorchLean

7.6. BugZoo Catalog🔗

Imagine reviewing a causal-attention optimization. The output shape is right, the benchmark is faster, and several sampled prompts look normal. The missing question is semantic: can any future token receive attention weight? BugZoo begins at moments like this. It takes failures seen in frameworks, compilers, deployment tools, and serving systems and asks what small object or proposition would have made the intended behavior explicit before the failure reached production.

The BugZoo catalog API and BugZoo overview focus on the TorchLean fragment itself: once a computation enters a typed TorchLean spec, shape changes, masks, token bounds, finite domain choices, stateful normalization parameters, and backend semantics become named objects that can be checked.

Every file is small for a reason. A theorem such as “strict-future attention weight is zero” is easier to reuse and harder to overstate than a large demo that merely happens to produce plausible tokens. The motivating incident explains why the contract matters; the Lean declaration says exactly what was checked.

7.6.1. Compile The Catalog🔗

All entries are imported by NN/Examples/BugZoo/All.lean. Compile them together with:

lake env lean NN/Examples/BugZoo/All.lean

A successful command is silent. It means every definition and theorem in the catalog elaborated; it does not mean that every external framework implementation satisfies those contracts.

All.lean is also the completeness boundary for the maintained catalog: an example file not imported there is not covered by this compile command. The contracts describe TorchLean reference objects; external framework conformance requires a separate importer, refinement theorem, or explicit assumption.

For a more interactive pass, create BugZooAudit.lean:

import NN.Examples.BugZoo.AttentionMask
import NN.Examples.BugZoo.ShapeAndBroadcast

#check NN.Examples.BugZoo.AttentionMask.exactMaskedLogit_blocked_exp_zero
#check NN.Examples.BugZoo.AttentionMask.trueInfinityMask_future_attention_weight_zero
#check NN.Examples.BugZoo.ShapeAndBroadcast.addSingletonBatch
#check NN.Examples.BugZoo.ShapeAndBroadcast.broadcastRowToMatrix_firstRow

Open the file in the Lean Infoview. The attention theorem quantifies over every strict-future position j>i; the shape theorem exposes the singleton batch insertion and the proof-carrying broadcast as different operations. These are the contracts. The motivating PyTorch snippets in the source comments explain the bug family, but are not imported as trusted evidence.

7.6.2. Example Anatomy🔗

A good BugZoo example has four parts:

  • the pattern in the framework that goes wrong;

  • the TorchLean object that names the intended behavior;

  • the theorem, structure, or definition that marks the checked boundary;

  • the external conformance obligation or unsupported scope that remains outside the checked claim.

Most ML bugs here are semantic rather than syntactic. The program often still returns a tensor. The loss may still be a scalar. A compiled graph may still run. An LLM server may still emit tokens. BugZoo asks whether those tensors and tokens still mean what the user thought they meant.

The common contract shape is:

\text{bug pattern} \;\leadsto\; \text{TorchLean object} \;\leadsto\; \text{checked claim}

The examples stay short so the semantic invariant is visible. Each case isolates the condition that would have made the original kind of bug harder to miss.

Some representative contract shapes:

Example

Contract shape

Attention mask

j>i\Rightarrow A_{ij}=0

Batch invariance

\operatorname{select}(\operatorname{mapBatch}(f,X),i)=f(X_i)

Tokenizer boundary

token ids inhabit Fin vocabSize

KV cache

appended key/value appears at the final slot

Float boundary

runtime Float32 agrees with IEEE32Exec under a named agreement

Compiler boundary

target output equals source output

Stable loss

logits path uses log-softmax semantics

Ignored labels

inactive labels contribute zero

LayerNorm degenerate axis

zero-variance normalization follows an explicit epsilon policy

3D projection

camera projection exposes depth and denominator preconditions

7.6.3. The Examples🔗

7.6.3.1. Shape And Broadcast🔗

NN.Examples.BugZoo.ShapeAndBroadcast source records one of TorchLean's core design choices: shapes belong in the object, not as an afterthought. The example uses a missing batch dimension and a reduce then broadcast pattern as its running examples. In NumPy style tensor libraries, a reduced vector can silently expand back across a matrix and still produce a plausible loss. In TorchLean, ordinary elementwise operations require the same shape, and explicit broadcasting carries Shape.CanBroadcastTo evidence.

The local contract is concrete: addSingletonBatch names the batch insertion, reduceRows names the reduction, and broadcastRowToMatrix names the expansion. The theorem broadcastRowToMatrix_firstRow shows the preferred style: if a dimension is added, removed, or reintroduced, the documentation and proof script can point at a term that did it.

The empirical motivation is tensor shape fault work such as SFData, plus numerical bug studies that found bad reductions and accidental broadcasting in real DL programs.

The contract shape is:

\operatorname{add} : \operatorname{Tensor}(\alpha,s)\to \operatorname{Tensor}(\alpha,s)\to \operatorname{Tensor}(\alpha,s)

and explicit broadcast operations carry evidence that the source shape can be broadcast to the target shape.

7.6.3.2. Stable Loss🔗

NN.Examples.BugZoo.StableLoss source is about losses that look mathematically harmless but fail numerically. The classic sketch is softmax followed by log: if a probability rounds to zero, the log path can produce infinities and downstream NaNs. TorchLean keeps two APIs separate. Logits should use crossEntropyLogitsSpec, which unfolds through logSoftmaxSpec; probability inputs use the clipped probability form crossEntropySpec.

The checked hooks are small. crossEntropyLogits_uses_logSoftmax says that the logits loss really takes the stable logits path. crossEntropyProbabilities_clips_before_log says that the probability path clamps before log. safeDivSpec_unfold gives division with domain assumptions an explicit node protected by epsilon rather than hiding it in an optimizer or backend.

This example is motivated by TensorFuzz, which targeted rare numerical failures, and by empirical studies of numerical bugs involving log, sqrt, division, exp, and reductions.

7.6.3.3. Ignored Labels🔗

NN.Examples.BugZoo.IgnoredLabelLoss source turns a corner case that is easy to dismiss into a reduction contract. PyTorch issue #75181 reported an ignore_index case where all labels were ignored and the result was nan.

TorchLean exposes the policy instead of copying every branch of a framework kernel. labelContribution false loss = 0 states that ignored labels contribute no scalar loss, and the example's helper for empty reductions names one policy for the all-ignored case. The bug appears when an empty active set reaches an unnamed backend reduction.

The policy is the definition:

\operatorname{labelContribution}(active,loss) = \begin{cases} loss, & active\\ 0, & \neg active \end{cases}

The all ignored case is then a declared reduction policy, not an accidental division by zero.

7.6.3.4. Autograd Domain🔗

NN.Examples.BugZoo.AutogradDomain source follows PyTorch's own autograd note about division by zero. If a graph computes x/0 and masks the bad value afterward, the forward result may look hidden while the backward graph still contains the undefined operation.

TorchLean's example names the difference between "divide first, mask later" and "safe divide, then mask." maskAfterSafeDiv records safedivSpec before the mask, and maskAfterSafeDiv_uses_epsilon_denominator unfolds to division by \mathrm{denominator}+\varepsilon. The contrast definition, unsafeDivThenMask, stays in the file so importers and reviewers can see the risky graph shape rather than treating every masked expression as safe.

7.6.3.5. Attention Mask🔗

NN.Examples.BugZoo.AttentionMask source is the catalog entry for causal mask semantics. Attention masks fail by polarity, layout, fake negative infinity, fully masked rows, and interactions with API flags. PyTorch has had relevant reports for MultiheadAttention, including is_causal=True being ignored when need_weights=True and fully masked heads producing NaNs when weights are requested.

The example connects the runtime convention to the math. Lean's real numbers do not contain literal negative infinity, so the file first uses EReal to state the exact fact \exp(-\infty)=0. TorchLean's ordinary attention spec then uses the equivalent hard masked softmax numerator. The theorem trueInfinityMask_future_attention_weight_zero says that strict future positions receive exactly zero attention mass under the causal mask. That property is the one needed when reasoning about autoregressive output causality.

The spec also makes fully blocked rows total: if every mask entry in a row is false, the complete weight row is zero. This is distinct from first forming a vector of negative infinities and then applying an implementation whose normalization may produce NaN on that row.

In formula form:

j>i \quad\Longrightarrow\quad \operatorname{attentionWeight}_{causal}(i,j)=0

This example also points back to the original transformer paper, Attention Is All You Need.

7.6.3.6. Compiler Boundary🔗

NN.Examples.BugZoo.CompilerBoundary source is the wrong-code example. It is about optimized graphs that run, return tensors, and are nevertheless not the same computation as the source graph. NNSmith found compiler bugs across TVM, TensorRT, ONNXRuntime, and PyTorch, FreeFuzz found framework/API bugs by mining real snippets, and a recent PyTorch compiler correctness study focuses directly on silent torch.compile wrong outputs.

The local object is the SemanticBoundary structure. It has a source evaluator, a target evaluator, an implementation relation, and a preservation field. This compact shape is the reusable claim behind heavier IR compiler correctness theorems: accepted target code must agree with the source semantics on every input. A runtime check can make us more confident, but it is not the same kind of artifact as a semantic boundary.

The reusable statement is:

\operatorname{implements}(source,target) \quad\Longrightarrow\quad \forall x,\; target(x)=source(x)

7.6.3.7. Float Boundary🔗

NN.Examples.BugZoo.FloatBoundary source is where we refuse to let proofs over real numbers silently masquerade as float32 deployment guarantees. Floating point verification attacks show why that matters: a property proved over reals can be invalidated by finite precision, exceptional values, fused operations, denorm behavior, or changed reduction order. The example cites Jia and Rinard's warning paper.

TorchLean's answer is explicit modeling. IEEE32Exec is the executable bit level float32 model. Runtime Float32 primitives are not transparent to the Lean kernel, so the theorem runtimeFloat32_add_rewrites_to_ieee32 requires the named assumption RuntimeFloat32FiniteMatchesIEEE32Exec, together with finite-input and finite-result hypotheses. We built that friction on purpose. If a proof uses the float32 model, the boundary says where runtime conformance entered.

The boundary is therefore visible in the theorem shape:

\operatorname{RuntimeFloat32FiniteMatchesIEEE32Exec} \quad\Longrightarrow\quad \operatorname{runtimeAdd}(x,y) = \operatorname{IEEE32Exec.add}(x,y)

7.6.3.8. Normalization State🔗

NN.Examples.BugZoo.NormalizationState source covers BatchNorm style bugs where the formula or state is wrong but the layer still emits a tensor. CRADLE reported a BatchNorm epsilon placement issue across backends, while LEMON found BatchNormalization moving stat bugs and BatchNorm layers that produce NaNs.

The example splits the concern in two. First, normalizeCore_scalar_uses_variance_plus_epsilon shows that TorchLean's scalar normalization puts epsilon inside the variance term before square root. Second, RunningStats packages inference time mean and variance as explicit inputs. Train/eval mode bugs are often state bugs. If running statistics are ambient mutable framework state, the proof cannot see them. If they are arguments to batchNormEvalWithStats, the state boundary is visible.

7.6.3.9. LayerNorm Degenerate Axis🔗

NN.Examples.BugZoo.LayerNormDegenerateAxis source records a related but different normalization bug family. LayerNorm can receive an axis with one element or a constant slice. The tensor output still has the expected shape, but the variance term is zero and the implementation's epsilon convention determines whether the result is finite and meaningful.

TorchLean's contract is to expose the denominator policy instead of burying it in a fused kernel. The invariant is small:

\operatorname{denom}=\sqrt{\operatorname{variance}+\epsilon}

and the degenerate case should be governed by the same explicit formula. That gives later kernels a reference behavior to match.

7.6.3.10. Constant Normalization Slice🔗

NN.Examples.BugZoo.ConstantNormalizationSlice source keeps the constant-slice case visible. A constant row or channel slice is not exotic; it appears in padding-heavy batches, masked tokens, uniform images, and clipped signals. If the normalization implementation accidentally assumes positive variance, a constant slice can produce a division by zero, NaN, or a backend-specific branch.

The contract is the same discipline used throughout BugZoo: name the slice, name the variance, and name the epsilon-protected normalization result. An optimized kernel can then be tested or proved against the reference object.

7.6.3.11. Batch Invariance🔗

NN.Examples.BugZoo.BatchInvariance source is about serving systems that change outputs depending on which other requests share a batch. This can come from dynamic batching, kernel selection, reduction order, and scheduling details even when user randomness is off. The catalog points at recent LLM serving discussions and studies: Thinking Machines on inference nondeterminism and an LLM inference engine bug study.

The checked reference semantics is mapBatch: apply the same function to each example independently to each row. The theorem mapBatch_select_eq_single says that selecting one row from the batched result equals evaluating that row alone. That theorem is the semantic target a runtime path should refine; any explicit float32 tolerance, reduction-order difference, or serving-system batching policy belongs in a separate runtime assumption.

7.6.3.12. KV Cache🔗

NN.Examples.BugZoo.KVCache source models cache accounting in autoregressive inference. LLM engines fail through shifted caches, wrong cache slots, config/shape mismatches, resource scheduling, and interaction with positions or tokenizers. The broader source trail is the LLM inference engine bug study.

The local contract is exact: a cache append operation preserves existing entries and puts the newly decoded key/value in the final slot. That sounds obvious, but it is the kind of invariant that becomes fragile when a serving system combines paged attention, batching, and mutable buffers. The BugZoo example says what the reference operation means before any external cache manager is trusted to implement it.

7.6.3.13. RoPE Position🔗

NN.Examples.BugZoo.RoPEPosition source pairs naturally with the KV cache example. Rotary position embeddings make position accounting part of the model's meaning. A decode position off by one can be hard to notice because the tensor shapes still line up and the model still produces tokens.

The file introduces PositionSchedule and appendNextPosition. The theorem appendNextPosition_last states that the newly appended token gets exactly the next sequence index. The schedule is explicit rather than derived from ambient mutable state. It follows the same design move as the normalization example: if the state affects semantics, it should appear in the object we inspect.

7.6.3.14. Tokenizer Boundary🔗

NN.Examples.BugZoo.TokenizerBoundary source marks the boundary before tensors even reach the model. Tokenizer/config mismatches can disagree about vocabulary size, padding, EOS, or special token IDs while the neural network code itself looks ordinary. The LLM inference engine bug study lists tokenizer/config bugs as a real serving class.

TorchLean's current contract is small but valuable: token IDs inside the typed fragment can be represented as Fin vocabSize. padId_in_vocab and tokenAt_in_vocab are almost tautological, which means token IDs outside the vocabulary are no longer a late runtime condition once data has crossed into this representation. The remaining producer step is the importer or tokenizer bridge that constructs those Fin values from external bytes.

7.6.3.15. Geometry 3D Projection🔗

NN.Examples.BugZoo.Geometry3DProjection source shows why BugZoo is not limited to language-model incidents. Vision and robotics pipelines often project 3D points to image coordinates with a camera matrix. The formula is familiar:

(u,v)=\left(\frac{x}{z},\frac{y}{z}\right)

up to intrinsics and coordinate conventions. The bug family is also familiar: depth is zero or has the wrong sign, coordinate frames are swapped, or the denominator convention is implicit. A tensor can still be emitted, but the geometry claim no longer matches the camera model.

The TorchLean response is to expose projection preconditions and the safe division boundary. A verified perception or control argument should not inherit an unspoken "all points have valid positive depth" assumption from preprocessing code.

7.6.4. Use A Contract In A Real Review🔗

Return to the causal-attention optimization from the opening. Start with the reference theorem in AttentionMask: for every query row, a strict-future key has exactly zero weight. If the optimized path changes layouts, use the shape and broadcast examples to make that rearrangement explicit. If it crosses into a native compiler or float kernel, the compiler and Float32 examples show the two additional obligations: preserve the source computation and state where runtime arithmetic is assumed to match the reference semantics.

Test both sides of the boundary. A past or current token should be able to contribute; a future token must not. A fully blocked row should follow the declared zero-row policy instead of producing an accidental NaN. Compile All.lean after adding the focused example so the maintained catalog actually imports it.

The result is narrower and auditable: one recognizable failure becomes an explicit contract, one negative control, and one visible conformance obligation. The same pattern turns unstable losses into domain-aware specs, state bugs into explicit arguments, tokenizer and cache mistakes into import or append contracts, and wrong code into a semantic-preservation question.