TorchLean API

NN.Runtime.Autograd.Engine.LibTorch.Float32Contract

CUDA float32 contract #

The binary32 path stores values in opaque LibTorch tensors. This module's agreement contracts concern binary32 only; the native tape also supports binary64, without deriving binary64 agreement from these theorems. The Lean proofs do not inspect compiled ATen operations, their library dependencies, or the device. The native backend therefore remains an explicit implementation boundary.

This module keeps that boundary precise:

In other words, the proof route is:

native CUDA bits --(explicit agreement assumption / tests / toolchain contract)--> ExecFloat.Binary 8 23 --(proved in Lean)--> FP32 rounding-on-ℝ error bounds.

What is not proved here:

Those are runtime/toolchain assumptions, and the CUDA stress tests are intended to validate them against this reference contract. The primitive-level part of that validation is scripts/checks/cuda.sh parity, which compares cases for the five fields of NativePrimitiveAgreement against compiled host arithmetic and ATen GPU operations, bit for bit. A passing case is evidence for that input and build, not the universally quantified agreement hypothesis. Operator policy settings alone do not supply that hypothesis.

Reference scalar and host conversion #

@[reducible, inline]

The scalar reference for CUDA float32 reasoning.

CUDA buffers are opaque to Lean; this is the scalar model we compare their 32-bit elements against.

Instances For
    @[inline]

    Interpret raw native binary32 bits as the ExecFloat.Binary 8 23 reference scalar.

    Instances For
      @[inline]

      Extract the binary32 bit pattern used for native/reference comparisons.

      Instances For
        @[inline]

        Reference meaning of uploading a Lean Float into a CUDA float32 buffer.

        Lean Float is binary64. The CUDA buffer path casts host doubles to native float; the reference contract for that cast is round-to-nearest-even binary32, implemented by FloatLib’s Model.cast.

        Instances For
          @[simp]

          Bit patterns survive a round trip through the reference scalar type.

          @[simp]

          And so do reference scalars through their bits, which is what makes the C boundary lossless: whatever the kernel writes back can be read as the same value we would have computed.

          Reinterpreting native Float32 bits agrees with FloatLib's native-value conversion.

          Abstract native scalar semantics #

          Abstract result bits for native CUDA scalar primitives.

          This structure provides a bit-by-bit comparison point between the FFI/runtime implementation and the ExecFloat.Binary 8 23 reference. It makes no claim that Lean proves the CUDA implementation correct. Rank-one and higher-rank tensor kernels lift the comparison elementwise, except for reductions whose order must also be specified.

          Instances For

            Agreement between a native result and the reference result, in the sense the theorems below need: either the two bit patterns are equal, or both encode NaN.

            The relation permits different NaN encodings while requiring identical bits for every non-NaN result, including signed zeros. scripts/checks/cuda.sh parity tests this relation against the linked native backend and counts NaN encoding differences separately.

            Every error bound below requires a finite result. That hypothesis rules out the second disjunct, so allowing different NaN encodings does not weaken those finite-result bounds.

            Instances For

              A finite native result pins the bits exactly, because the NaN case cannot apply to it.

              The trusted/validated CUDA scalar agreement assumption.

              For a concrete CUDA build, these fields are what parity tests, compiler flags, and backend policy are checking: native result bits match the executable ExecFloat.Binary 8 23 reference for primitive float32 ops, up to the encoding of NaN.

              Instances For

                Reference scalars are determined by their bits, NaN payloads included.

                This is the extensionality principle the kernel proofs use: agreeing bitwise with the C result is enough to conclude agreement as values, with no float equality anywhere in the argument.

                From bits to values #

                Each of these says: if the native result is finite, it is the reference value. Finiteness is what turns the NaN-tolerant contract into an equation, and every error bound below needs it anyway, so the hypothesis costs nothing in practice.

                Native addition is the reference value when its result is finite and the contract holds.

                Native multiplication is the reference value when its result is finite and the contract holds.

                Native division is the reference value when its result is finite and the contract holds.

                theorem Runtime.Autograd.LibTorch.Float32Contract.native_fma_eq_reference_of_isFinite {native : NativePrimitiveBits} (h : NativePrimitiveAgreement native) (x y z : RefScalar) (hfin : FloatLib.Floats.ExecFloat.Binary.isFinite (fromNativeBits (native.fmaBits x y z)) = true) :
                fromNativeBits (native.fmaBits x y z) = (fun (left right addend : FloatLib.Floats.ExecFloat.Binary { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := RefScalar._proof_4 }.expWidth { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := RefScalar._proof_4 }.fracWidth { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := RefScalar._proof_4 }.encoding { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := 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) RefScalar._proof_1 RefScalar._proof_2 RefScalar._proof_3 RefScalar._proof_4) (1 + 8 + 23) 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) RefScalar._proof_1 RefScalar._proof_2 RefScalar._proof_3 RefScalar._proof_4) (1 + 8 + 23) RefScalar._proof_5))) => left.fma right addend) x y z

                Native fused multiply-add is the reference value when its result is finite and the contract holds.

                theorem Runtime.Autograd.LibTorch.Float32Contract.native_sqrt_eq_reference_of_isFinite {native : NativePrimitiveBits} (h : NativePrimitiveAgreement native) (x : RefScalar) (hfin : FloatLib.Floats.ExecFloat.Binary.isFinite (fromNativeBits (native.sqrtBits x)) = true) :
                fromNativeBits (native.sqrtBits x) = (fun (value : FloatLib.Floats.ExecFloat.Binary { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := RefScalar._proof_4 }.expWidth { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := RefScalar._proof_4 }.fracWidth { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := RefScalar._proof_4 }.encoding { expWidth := 8, expWidth_ge_two := RefScalar._proof_1, fracWidth := 23, fracWidth_pos := RefScalar._proof_2, exponentBias := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee.defaultBias 8, encoding := FloatLib.Floats.Formats.BinaryInterchange.FloatFormat.Encoding.ieee, exponentBias_pos := RefScalar._proof_3, exponentBias_le_maxFinite := 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) RefScalar._proof_1 RefScalar._proof_2 RefScalar._proof_3 RefScalar._proof_4) (1 + 8 + 23) 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) RefScalar._proof_1 RefScalar._proof_2 RefScalar._proof_3 RefScalar._proof_4) (1 + 8 + 23) RefScalar._proof_5))) => value.sqrt) x

                Native square root is the reference value when its result is finite and the contract holds.

                Inheriting the proved configured binary32 → FP32 bounds #