Skip to the content.

Bug Zoo

Bug Zoo collects mistakes that are easy to miss in ordinary machine-learning tests. The program still runs and returns a tensor, loss, or token sequence, but the result no longer has the meaning the caller assumed.

Each example is deliberately small. It states the intended behavior as a Lean definition or theorem, shows where an implementation can depart from it, and identifies any runtime assumption that remains outside the proof. Together they cover attention, decoding, data boundaries, normalization, losses, compilation, floating point, and geometry.

Attention and Autoregressive Decoding

A causal mask should exclude future keys exactly. Replacing $-\infty$ with a large finite negative number only approximates that behavior and can fail when logits leave the expected range. AttentionMask.lean uses hard-mask semantics and proves that every strict-future attention weight is zero.

Incremental decoding introduces a different problem. A key/value cache must contain the same keys and values that full-sequence attention would have seen, in the same positions. KVCache.lean checks the append operation, while RoPEPosition.lean records the position assigned to the new token. These contracts isolate the two common off-by-one errors instead of hiding them inside a generation loop.

Data, Batches, and Normalization State

Tokenizer errors often appear much earlier than the model. A checkpoint may expect one vocabulary or special-token convention while the data loader supplies another. TokenizerBoundary.lean requires imported token ids to inhabit Fin vocabSize, making the vocabulary bound part of the object passed to the network.

Batching should normally change throughput, not the prediction for an individual sample. BatchInvariance.lean states that selecting a row from a batched reference run agrees with evaluating that row alone. This catches accidental reductions across the batch axis and state that leaks between samples.

Normalization has its own hidden state. Batch normalization uses learned affine parameters and running statistics at inference time; layer normalization has a degenerate one-feature case that is easy to mishandle. The normalization examples make the axes, epsilon placement, running state, and zero-gradient corner cases explicit.

Losses, Floating Point, and Compilation

Several examples concern computations that are mathematically familiar but numerically unsafe. Masking a quotient after division does not repair a division by zero, and a direct implementation of a logit loss can overflow even when its stable form is finite. AutogradDomain.lean and StableLoss.lean put the domain restriction and stable formula into the contract before reverse mode is considered.

Real-number proofs do not automatically describe a binary32 run. FloatBoundary.lean uses Lean’s logical Float32.Model and TorchLean’s independent bit-level executor to state the connection explicitly. CompilerBoundary.lean does the analogous job for optimized graphs: an accepted rewrite must preserve operations, shapes, dtypes, weights, and buffers.

The final geometry case starts from tensors exported by a detector. Lean recomputes camera projection and positive depth, then checks that the reported two-dimensional box encloses every projected corner. The detector remains an external producer; the enclosure claim does not.

Run the Examples

Build the complete collection with:

lake build NN.Examples.BugZoo.All

The cases with registered certificate checkers can also be run from the verification command:

lake exe verify -- camera-box3d-cert
lake exe verify -- all

The source files and the contracts they expose are listed below.

Source file Bug family Contract exposed
AttentionMask.lean Causal masks, mask polarity, finite sentinels standing in for $-\infty$ Future positions receive exactly zero attention weight under hard-mask semantics.
KVCache.lean Shifted or malformed key/value caches in autoregressive decoding The appended key/value vector is exactly the final cache entry.
RoPEPosition.lean Off-by-one or mismatched rotary/absolute positions Appending a token assigns the next sequence position.
TokenizerBoundary.lean Vocabulary-size and special-token mismatches Imported token ids inhabit Fin vocabSize.
BatchInvariance.lean Dynamic batching changing per-sample outputs Selecting one row from a batched reference run equals evaluating that row alone.
NormalizationState.lean BatchNorm formula/state mistakes Epsilon placement and eval-time running statistics are explicit objects.
LayerNormDegenerateAxis.lean One-feature LayerNorm corner cases The output is the bias, with zero input and scale-gradient contribution.
ConstantNormalizationSlice.lean Cancellation in normalization kernels on constant slices Affine normalization returns the bias and contributes zero scale gradient.
IgnoredLabelLoss.lean All-ignored cross-entropy reductions Ignored labels and the empty-reduction policy are named.
AutogradDomain.lean Masking after undefined division The safe graph records epsilon-protected division before masking.
StableLoss.lean Numerically unstable losses and domain-sensitive ops Logit losses use the stable log-softmax path.
ShapeAndBroadcast.lean Missing axes and silent broadcasts Dimension changes are explicit terms with shape evidence.
CompilerBoundary.lean Optimized graphs silently changing semantics Backend acceptance is a preservation obligation over ops, shapes, dtypes, weights, and buffers.
FloatBoundary.lean Real-valued reasoning applied to Float32 runs Lean’s logical Float32 model and TorchLean’s independent executor are connected by a named equivalence obligation.
Geometry3DProjection.lean Camera convention, depth, layout, and projection-box errors The checker recomputes projection, positive depth, and 2D box enclosure.

Two Checked Statements

Under hard-mask semantics, every strict-future key receives exactly zero attention weight:

theorem trueInfinityMask_future_attention_weight_zero :
  Spec.get2 (Spec.hardMaskedSoftmaxSpec scores (Spec.causalMask n)) i j = 0

Lean 4.33 defines core Float32 operations through Float32.Model. TorchLean proves that the model agrees with its independently implemented IEEE32Exec arithmetic:

theorem Float32Bridge.float32_isFinite_eq_ieee32 (a : _root_.Float32) :
    Float32.isFinite a = IEEE32Exec.isFinite (toIEEE32Exec a)

theorem Float32Bridge.toIEEE32Exec_add
    (a b : _root_.Float32) :
    toIEEE32Exec (a + b) =
      canonicalize (IEEE32Exec.add (toIEEE32Exec a) (toIEEE32Exec b))

Classification and addition need no finiteness premise. The addition theorem covers finite values, signed zeros, infinities, NaNs, underflow, overflow, and nearest-even rounding. NaNs are canonicalized before comparing bits because Lean stores one NaN representation while IEEE32Exec retains payload and sign information.