TorchLean API

NN.Runtime.Autograd.Engine.LibTorch.KernelSpec

Pure specifications for CUDA float32 kernels #

This file is the proof layer companion to NN.Runtime.Autograd.Engine.LibTorch.*.

The LibTorch CUDA backend is an FFI boundary. These theorems describe explicit Lean functions; applying them to a compiled native operation requires a refinement contract. The interface has three layers:

  1. Pure Lean kernel specs in this file: row-major indexing, elementwise maps, fixed-order reductions, gather/scatter, and batched matmul are ordinary Lean functions over finite indices.
  2. Scalar float32 facts from Float32Contract: if native result bits match ExecFloat.Binary 8 23, then the existing configured binary32 → FP32-on-ℝ theorems apply.
  3. Native validation / trust boundary: LibTorch, its CUDA dependencies, compiler flags, GPU hardware, and driver behavior are validated by tests and documented assumptions, not proved by Lean.

This split is deliberate. It lets us prove the algorithm/indexing contracts that TorchLean owns, without claiming that the Lean kernel can inspect NVIDIA's compiler, runtime, or device ISA.

External references for the assumptions named here:

Flat row-major buffers #

@[reducible, inline]

A pure Lean view of a contiguous float32 buffer of length n.

Native LibTorch.Buffer is opaque and mutable outside Lean. FlatBuffer n is the spec counterpart: every valid linear index has a reference scalar value.

Instances For
    @[reducible, inline]

    A native-result buffer represented only by raw binary32 bits.

    Instances For

      Reinterpret a native bit buffer as reference ExecFloat.Binary 8 23 values.

      Instances For

        Total lookup for flat-buffer specs that decode indices from arithmetic.

        Well-formed kernel preconditions should make the in-bounds branch fire. The fallback keeps the spec total in Lean, and later layout proofs can discharge the bounds separately.

        Instances For

          Extract reference bits pointwise.

          Instances For
            @[simp]

            A buffer of reference scalars survives a round trip through its bits.

            @[simp]

            And a buffer of bits survives a round trip through reference scalars, so kernel specs may be stated on whichever side reads better and transported to the other.

            Elementwise kernels #

            Pure spec for a unary elementwise CUDA kernel.

            Instances For

              Pure spec for a binary elementwise CUDA kernel.

              Instances For

                Elementwise addition reference spec.

                Instances For

                  Elementwise multiplication reference spec.

                  Instances For

                    Elementwise division reference spec.

                    Instances For

                      Elementwise square-root reference spec.

                      Instances For

                        If every native result bit agrees with reference addition, the whole native elementwise-add buffer agrees extensionally with addSpec.

                        theorem Runtime.Autograd.LibTorch.KernelSpec.fromNativeBitsBuffer_eq_sqrtSpec_of_bits {n : ℕ} {bits : NativeBitsBuffer n} {x : FlatBuffer n} (hbits : ∀ (i : Fin n), bits i = Float32Contract.toNativeBits ((fun (value : FloatLib.Floats.ExecFloat.Binary { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.expWidth { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.fracWidth { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.encoding { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.exponentBias ⋯ ⋯ ⋯ ⋯ (FloatLib.Floats.Formats.BinaryInterchange.Configured.StoragePlan.forKnownWidth (FloatLib.Floats.ExecFloat.Binary.descriptor 8 23 FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee (FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8) Float32Contract.RefScalar._proof_1 Float32Contract.RefScalar._proof_2 Float32Contract.RefScalar._proof_3 Float32Contract.RefScalar._proof_4) (1 + 8 + 23) Float32Contract.RefScalar._proof_5) (FloatLib.Floats.Formats.BinaryInterchange.Configured.Code (FloatLib.Floats.Formats.BinaryInterchange.Configured.StoragePlan.forKnownWidth (FloatLib.Floats.ExecFloat.Binary.descriptor 8 23 FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee (FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8) Float32Contract.RefScalar._proof_1 Float32Contract.RefScalar._proof_2 Float32Contract.RefScalar._proof_3 Float32Contract.RefScalar._proof_4) (1 + 8 + 23) Float32Contract.RefScalar._proof_5))) => value.sqrt) (x i))) :

                        Elementwise square-root version of fromNativeBitsBuffer_eq_addSpec_of_bits.

                        theorem Runtime.Autograd.LibTorch.KernelSpec.native_sqrt_pointwise_abs_error_of_bits {n : ℕ} {bits : NativeBitsBuffer n} {x : FlatBuffer n} (hbits : ∀ (i : Fin n), bits i = Float32Contract.toNativeBits ((fun (value : FloatLib.Floats.ExecFloat.Binary { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.expWidth { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.fracWidth { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.encoding { expWidth := 8, expWidth_ge_two := Float32Contract.RefScalar._proof_1, fracWidth := 23, fracWidth_pos := Float32Contract.RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := Float32Contract.RefScalar._proof_3, exponentBias_le_maxFinite := Float32Contract.RefScalar._proof_4 }.exponentBias ⋯ ⋯ ⋯ ⋯ (FloatLib.Floats.Formats.BinaryInterchange.Configured.StoragePlan.forKnownWidth (FloatLib.Floats.ExecFloat.Binary.descriptor 8 23 FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee (FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8) Float32Contract.RefScalar._proof_1 Float32Contract.RefScalar._proof_2 Float32Contract.RefScalar._proof_3 Float32Contract.RefScalar._proof_4) (1 + 8 + 23) Float32Contract.RefScalar._proof_5) (FloatLib.Floats.Formats.BinaryInterchange.Configured.Code (FloatLib.Floats.Formats.BinaryInterchange.Configured.StoragePlan.forKnownWidth (FloatLib.Floats.ExecFloat.Binary.descriptor 8 23 FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee (FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8) Float32Contract.RefScalar._proof_1 Float32Contract.RefScalar._proof_2 Float32Contract.RefScalar._proof_3 Float32Contract.RefScalar._proof_4) (1 + 8 + 23) Float32Contract.RefScalar._proof_5))) => value.sqrt) (x i))) (i : Fin n) (hfin : FloatLib.Floats.ExecFloat.Binary.isFinite (Float32Contract.fromNativeBits (bits i)) = true) :

                        Pointwise real-error bound inherited by a native elementwise-square-root buffer after bit agreement.

                        Fixed-order reductions #

                        Sequential left-fold reduction over a flat buffer.

                        This is a deterministic algorithmic spec, not a claim about CUDA atomics. Native atomic reductions only refine this spec under an additional ordering/agreement assumption. The deterministic settings in Controls require repeatable algorithms; they do not establish this sequential order or discharge the agreement assumption.

                        Instances For

                          Explicit assumption package for a native reduction implementation.

                          Use this when a native CUDA reduction has been proved or validated to agree bitwise with reduceSumLeftSpec. A fixed reduction tree can be deterministic without matching this left fold; enabling deterministic algorithms alone does not supply this contract.

                          Instances For

                            Native fixed-order reduction inherits the reduceSumLeftSpec reference value.

                            Gather/scatter indexing #

                            Scatter-add a length-k value buffer into a length-n input buffer.

                            Repeated indices are accumulated in increasing source-index order. This mirrors the mathematical contract of scatter-add; a native parallel implementation must separately justify or validate its accumulation order when bitwise reproducibility matters.

                            Instances For

                              Batched row-major matrix multiplication #

                              Linear index for either BMM input, viewed as row-major (batch, rows, cols) storage.

                              Instances For

                                Decode a flat row-major output index for shape (batch, m, p).

                                Instances For
                                  def Runtime.Autograd.LibTorch.KernelSpec.bmmSpec (batch m n p : ℕ) (A : FlatBuffer (batch * m * n)) (B : FlatBuffer (batch * n * p)) :
                                  FlatBuffer (batch * m * p)

                                  Pure row-major batched matrix multiplication spec.

                                  For each output element C[b,i,j], this folds over k = 0..n-1 using ExecFloat.mul followed by ExecFloat.add. This fixes a specific accumulation order. LibTorch may use a different tree/FMA strategy, so bit-for-bit agreement with this spec is an explicit native contract, not a free theorem.

                                  Instances For
                                    structure Runtime.Autograd.LibTorch.KernelSpec.NativeBmmAgreement {batch m n p : ℕ} (nativeBits : NativeBitsBuffer (batch * m * p)) (A : FlatBuffer (batch * m * n)) (B : FlatBuffer (batch * n * p)) :

                                    Agreement assumption for a native BMM implementation.

                                    The scalar result bits must match bmmSpec at every output element. This is stronger than "numerically close": it is the bitwise contract needed to reuse exact ExecFloat.Binary 8 23 proofs.

                                    For a LibTorch implementation this assumption includes:

                                    • row-major TorchLean buffers are interpreted with the intended tensor dimensions;
                                    • the accumulation tree/FMA behavior agrees with the selected reference spec;
                                    • input and output strides match (batch,m,n), (batch,n,p), and (batch,m,p) row-major layout.
                                    Instances For
                                      theorem Runtime.Autograd.LibTorch.KernelSpec.native_bmm_eq_spec {batch m n p : ℕ} {nativeBits : NativeBitsBuffer (batch * m * p)} {A : FlatBuffer (batch * m * n)} {B : FlatBuffer (batch * n * p)} (h : NativeBmmAgreement nativeBits A B) :
                                      fromNativeBitsBuffer nativeBits = bmmSpec batch m n p A B

                                      Native BMM inherits the pure row-major bmmSpec when the bitwise agreement contract holds.