TorchLean API

NN.Tensor.Operations

Public Tensor Operations #

Shape-polymorphic lookup, axis operations, and list-shaped mapping and flattening helpers for TorchLean.Tensor. The implementations delegate to the canonical specification operations.

@[simp]
theorem TorchLean.Tensor.unstack_mapLeading {α : Type} [Storage α] {batch : } {inShape outShape : Shape} (f : Tensor α inShapeTensor α outShape) (xs : Tensor α (inShape.prependDim batch)) (i : Fin batch) :
(mapLeading [batch] f xs).unstack i = f (xs.unstack i)

Selecting one independently mapped batch row recovers the result for that row.

theorem TorchLean.Tensor.mapLeading_comp {α : Type} [Storage α] (leading : Shape) {s₁ s₂ s₃ : Shape} (f : Tensor α s₁Tensor α s₂) (g : Tensor α s₂Tensor α s₃) (xs : Tensor α (leading.concat s₁)) :
mapLeading leading (fun (x : Tensor α s₁) => g (f x)) xs = mapLeading leading g (mapLeading leading f xs)

Independent mapping commutes with composition across any leading shape.

def TorchLean.Tensor.cast {α : Type} [Storage α] {shape : Shape} (tensor : Tensor α shape) (target : Type) [Storage target] [ElementCast α target] :
Tensor target shape

Convert every tensor entry to a new element type while preserving its shape.

The target Storage instance selects the target physical representation.

Instances For
    def TorchLean.Tensor.add {α β γ : Type} [Storage α] [Storage β] [Storage γ] [Internal.PointwiseAdd α β γ] {shape : Shape} (left : Tensor α shape) (right : Tensor β shape) :
    Tensor γ shape

    Add equally shaped tensors, automatically promoting mixed element types.

    Instances For
      def TorchLean.Tensor.mul {α β γ : Type} [Storage α] [Storage β] [Storage γ] [Internal.PointwiseMul α β γ] {shape : Shape} (left : Tensor α shape) (right : Tensor β shape) :
      Tensor γ shape

      Multiply equally shaped tensors, automatically promoting mixed element types.

      Instances For
        def TorchLean.Tensor.sub {α β γ : Type} [Storage α] [Storage β] [Storage γ] [Internal.PointwiseSub α β γ] {shape : Shape} (left : Tensor α shape) (right : Tensor β shape) :
        Tensor γ shape

        Subtract equally shaped tensors, automatically promoting mixed element types.

        Instances For
          def TorchLean.Tensor.div {α β γ : Type} [Storage α] [Storage β] [Storage γ] [Internal.PointwiseDiv α β γ] {shape : Shape} (left : Tensor α shape) (right : Tensor β shape) :
          Tensor γ shape

          Divide equally shaped tensors, automatically promoting mixed element types.

          Instances For
            @[reducible, inline]
            abbrev TorchLean.Tensor.scale {α : Type} [Storage α] [Mul α] {shape : Shape} (tensor : Tensor α shape) (scalar : α) :
            Tensor α shape

            Multiply every tensor entry by one scalar. Public spelling of scaleSpec.

            Instances For
              def TorchLean.Tensor.clamp {α : Type} [Storage α] [Context α] {shape : Shape} (tensor : Tensor α shape) (minimum maximum : α) :
              Tensor α shape

              Clamp every tensor entry to the closed interval [minimum, maximum].

              Instances For
                def TorchLean.Tensor.square {α : Type} [Storage α] [Mul α] {shape : Shape} (tensor : Tensor α shape) :
                Tensor α shape

                Square every tensor entry.

                This is squareSpec under the weaker [Mul α] requirement; see square_eq_squareSpec.

                Instances For
                  theorem TorchLean.Tensor.square_eq_squareSpec {α : Type} [Storage α] [Context α] {shape : Shape} (tensor : Tensor α shape) :
                  tensor.square = tensor.squareSpec

                  Under a full scalar Context, the public square is the specification square.

                  def TorchLean.Tensor.matmul {α : Type} [Storage α] [Add α] [Mul α] [Zero α] {m n p : } (left : Tensor α [m, n]) (right : Tensor α [n, p]) :
                  Tensor α [m, p]

                  Multiply an m x n matrix by an n x p matrix.

                  Example:

                  -- `[2, 3] * [3, 2]` contracts the shared `3`. A mismatch is a type error, not a runtime message.
                  def left : Tensor Float [2, 3] := [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
                  
                  def right : Tensor Float [3, 2] := [[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]
                  
                  def product : Tensor Float [2, 2] := Tensor.matmul left right
                  
                  Instances For
                    def TorchLean.Tensor.matvec {α : Type} [Storage α] [Add α] [Mul α] [Zero α] {m n : } (matrix : Tensor α [m, n]) (vector : Tensor α [n]) :

                    Multiply an m x n matrix by an n-element vector.

                    Named after the BLAS level-2 operation the way matmul is named after level 3, so the three products read as a family at a call site: matmul, matvec, vecmat.

                    Example:

                    -- Matrix times column vector. It stays a separate name instead of an overload of `matmul` so the
                    -- shapes a reader should expect are visible at the call site, the way BLAS separates gemv.
                    def matrix : Tensor Float [2, 3] := [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
                    
                    def rowSums : Tensor Float [2] := Tensor.matvec matrix [1.0, 1.0, 1.0]
                    
                    Instances For
                      def TorchLean.Tensor.vecmat {α : Type} [Storage α] [Add α] [Mul α] [Zero α] {m n : } (vector : Tensor α [m]) (matrix : Tensor α [m, n]) :

                      Multiply an m-element row vector by an m x n matrix. See matvec for the naming.

                      Example:

                      -- Row vector times matrix: the same product read from the other side, with no transpose.
                      def matrix : Tensor Float [2, 3] := [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
                      
                      def columnSums : Tensor Float [3] := Tensor.vecmat [1.0, 1.0] matrix
                      
                      Instances For
                        def TorchLean.Tensor.relu {α : Type} [Storage α] [Zero α] [Max α] [BEq α] {shape : Shape} (tensor : Tensor α shape) :
                        Tensor α shape

                        Apply rectified linear activation pointwise.

                        Instances For
                          def TorchLean.Tensor.sigmoid {α : Type} [Storage α] [Context α] {shape : Shape} (tensor : Tensor α shape) :
                          Tensor α shape

                          Apply logistic sigmoid pointwise.

                          Instances For
                            def TorchLean.Tensor.tanh {α : Type} [Storage α] [Context α] {shape : Shape} (tensor : Tensor α shape) :
                            Tensor α shape

                            Apply hyperbolic tangent pointwise.

                            Instances For
                              def TorchLean.Tensor.Internal.linearAlongFinalAxis {α : Type} [Storage α] [Add α] [Mul α] [Zero α] {shape : Spec.Shape} {inputWidth outputWidth : } (endsWith : Spec.Shape.EndsWith.Proof inputWidth shape) (input : Tensor α shape) (weight : Tensor α [outputWidth, inputWidth]) (bias : Tensor α [outputWidth]) :
                              Tensor α (endsWith.replace outputWidth)

                              Apply an affine map to the innermost axis, recursing through the leading axes.

                              The EndsWith.Proof argument is what makes this total: it witnesses that shape really does end in inputWidth, and endsWith.replace outputWidth computes the result shape by swapping that last dimension. So a [batch, seq, in] input gives a [batch, seq, out] output with no reshaping.

                              Instances For
                                def TorchLean.Tensor.linear {α : Type} [Storage α] [Add α] [Mul α] [Zero α] {shape : Shape} {inputWidth outputWidth : } [endsWith : Shape.EndsWith inputWidth shape] (input : Tensor α shape) (weight : Tensor α [outputWidth, inputWidth]) (bias : Tensor α [outputWidth]) :
                                Tensor α (shape.replaceLast outputWidth)

                                Apply output = input * weightᵀ + bias along the final axis.

                                Every leading axis is preserved. The weight layout is [outputWidth, inputWidth], matching PyTorch's linear convention, and Lean infers inputWidth from the input tensor type.

                                Example:

                                -- The weight layout is `[outputWidth, inputWidth]`, as in PyTorch, and leading axes pass straight
                                -- through: a `[4, 3]` batch of rows comes back as `[4, 2]`.
                                def weight : Tensor Float [2, 3] := [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
                                
                                def bias : Tensor Float [2] := [0.5, -0.5]
                                
                                def project (rows : Tensor Float [4, 3]) : Tensor Float [4, 2] :=
                                  Tensor.linear rows weight bias
                                
                                Instances For
                                  @[inline]
                                  def TorchLean.Tensor.at {α : Type} [Storage α] {shape : Shape} (tensor : Tensor α shape) (coordinate : shape.Coord) :
                                  α

                                  Read one scalar at a statically valid coordinate.

                                  Instances For
                                    @[inline]
                                    def TorchLean.Tensor.set {α : Type} [Storage α] [Storage.Update α] {shape : Shape} (tensor : Tensor α shape) (coordinate : shape.Coord) (value : α) :
                                    Tensor α shape

                                    Replace one scalar at a statically valid coordinate.

                                    Instances For
                                      @[inline]
                                      def TorchLean.Tensor.modify {α : Type} [Storage α] [Storage.Update α] {shape : Shape} (tensor : Tensor α shape) (coordinate : shape.Coord) (f : αα) :
                                      Tensor α shape

                                      Transform one scalar at a statically valid coordinate.

                                      Instances For
                                        @[simp]
                                        theorem TorchLean.Tensor.at_set_self {α : Type} [Storage α] [Storage.Update α] {shape : Shape} (tensor : Tensor α shape) (coordinate : shape.Coord) (value : α) :
                                        (tensor.set coordinate value).at coordinate = value

                                        Reading a coordinate immediately after replacing it returns the new value.

                                        @[simp]
                                        theorem TorchLean.Tensor.at_modify_self {α : Type} [Storage α] [Storage.Update α] {shape : Shape} (tensor : Tensor α shape) (coordinate : shape.Coord) (f : αα) :
                                        (tensor.modify coordinate f).at coordinate = f (tensor.at coordinate)

                                        Reading a coordinate after modifying it returns the transformed old value.

                                        def TorchLean.Tensor.at? {α : Type} [Storage α] {shape : Shape} (tensor : Tensor α shape) (coordinates : Array ) :

                                        Read one scalar by runtime coordinates, returning none when validation fails.

                                        Instances For
                                          def TorchLean.Tensor.set? {α : Type} [Storage α] [Storage.Update α] {shape : Shape} (tensor : Tensor α shape) (coordinates : Array ) (value : α) :
                                          Option (Tensor α shape)

                                          Replace one scalar by runtime coordinates, returning none when validation fails.

                                          Instances For
                                            def TorchLean.Tensor.modify? {α : Type} [Storage α] [Storage.Update α] {shape : Shape} (tensor : Tensor α shape) (coordinates : Array ) (f : αα) :
                                            Option (Tensor α shape)

                                            Transform one scalar by runtime coordinates, returning none when validation fails.

                                            Instances For
                                              def TorchLean.Tensor.Internal.stackAtAxis {α : Type} [Storage α] {count : } {shape : Spec.Shape} (axis : ) (hAxis : axis shape.rank) :
                                              (Fin countTensor α shape)Tensor α (shape.insertAxis axis count)

                                              Stack count tensors along a chosen axis.

                                              The recursion peels leading dimensions until the insertion point is reached; the axis = 0 case is plain Tensor.dim. The final branch is impossible, since axis + 1 ≤ 0 cannot hold for a scalar shape, and grind discharges it from the arithmetic.

                                              Instances For
                                                def TorchLean.Tensor.stack {α : Type} [Storage α] {count : } {shape : Shape} (axis : ) (tensors : Fin countTensor α shape) (hAxis : axis shape.rank := by grind) :
                                                Tensor α (shape.insertAxis axis count)

                                                Stack equally shaped tensors along any valid insertion axis.

                                                Instances For
                                                  def TorchLean.Tensor.stackLeading {α : Type} [Storage α] {count : } {shape : Shape} (tensors : Fin countTensor α shape) :
                                                  Tensor α (shape.prependDim count)

                                                  Stack equally shaped tensors along a new leading axis.

                                                  Example:

                                                  -- Build a leading axis from an index function: three rows of width two become one `[3, 2]`.
                                                  def rows : Tensor Float [3, 2] :=
                                                    Tensor.stackLeading fun (row : Fin 3) =>
                                                      Tensor.ofFn fun (column : Fin 2) => (row.val + column.val).toFloat
                                                  
                                                  Instances For
                                                    @[simp]
                                                    theorem TorchLean.Tensor.unstack_stackLeading {α : Type} [Storage α] {count : } {shape : Shape} (tensors : Fin countTensor α shape) (index : Fin count) :
                                                    (stackLeading tensors).unstack index = tensors index

                                                    Selecting from a leading-axis stack recovers the selected tensor.

                                                    @[simp]
                                                    theorem TorchLean.Tensor.get_stackLeading {α : Type} [Storage α] {count : } {shape : Shape} (tensors : Fin countTensor α shape) (index : Fin count) :
                                                    get (stackLeading tensors) index = tensors index

                                                    Indexing a leading-axis stack recovers the selected tensor.

                                                    def TorchLean.Tensor.repeatAxis {α : Type} [Storage α] {shape : Shape} (axis count : ) (tensor : Tensor α shape) (hAxis : axis shape.rank := by grind) :
                                                    Tensor α (shape.insertAxis axis count)

                                                    Repeat a tensor along any newly inserted axis.

                                                    Instances For
                                                      def TorchLean.Tensor.repeatLeading {α : Type} [Storage α] {shape : Shape} (count : ) (tensor : Tensor α shape) :
                                                      Tensor α (shape.prependDim count)

                                                      Repeat a tensor along a new leading axis.

                                                      Instances For
                                                        @[simp]
                                                        theorem TorchLean.Tensor.unstack_repeatLeading {α : Type} [Storage α] {count : } {shape : Shape} (tensor : Tensor α shape) (index : Fin count) :
                                                        (repeatLeading count tensor).unstack index = tensor

                                                        Selecting from a leading-axis repetition recovers the repeated tensor.

                                                        @[simp]
                                                        theorem TorchLean.Tensor.get_repeatLeading {α : Type} [Storage α] {count : } {shape : Shape} (tensor : Tensor α shape) (index : Fin count) :
                                                        get (repeatLeading count tensor) index = tensor

                                                        Indexing a leading-axis repetition recovers the repeated tensor.

                                                        def TorchLean.Tensor.concat {α : Type} [Storage α] {leftCount rightCount : } {shape : Shape} (left : Tensor α (Spec.Shape.dim leftCount shape)) (right : Tensor α (Spec.Shape.dim rightCount shape)) :
                                                        Tensor α (Spec.Shape.dim (leftCount + rightCount) shape)

                                                        Concatenate tensors along their outermost axis.

                                                        This is concatAfter .scalar with a simpler index shape: .dim n shape instead of Spec.Shape.scalar.concat (.dim n suffix). The simpler form is what the @[simp] lemma get_concat_right below is stated against, and it is what makes a KV-cache append discharge by simp instead of by a shape rewrite, so the two are kept as separate definitions rather than merged into one with a defaulted argument.

                                                        Example:

                                                        -- Join on the leading axis: `2 + 3` rows that share the row shape `[2]`.
                                                        def top : Tensor Float [2, 2] := [[1.0, 2.0], [3.0, 4.0]]
                                                        
                                                        def bottom : Tensor Float [3, 2] := [[5.0, 6.0], [7.0, 8.0], [9.0, 10.0]]
                                                        
                                                        def stacked : Tensor Float [5, 2] := Tensor.concat top bottom
                                                        
                                                        Instances For
                                                          def TorchLean.Tensor.concatAfter {α : Type} [Storage α] (leading : Shape) {leftCount rightCount : } {suffix : Shape} (left : Tensor α (leading.concat (Spec.Shape.dim leftCount suffix))) (right : Tensor α (leading.concat (Spec.Shape.dim rightCount suffix))) :
                                                          Tensor α (leading.concat (Spec.Shape.dim (leftCount + rightCount) suffix))

                                                          Concatenate at the axis immediately following a known leading shape.

                                                          The After suffix matches flattenAfter below: both take the leading shape that is held fixed and act on the first axis past it. concatAxis was the older name, but "axis" suggested a Nat index in the PyTorch dim= sense, which is not what the argument is.

                                                          Example:

                                                          -- The same join one axis further in, which is how a batch axis is kept out of the way:
                                                          -- `[4, 2, 3]` and `[4, 5, 3]` become `[4, 7, 3]`.
                                                          def joined (left : Tensor Float [4, 2, 3]) (right : Tensor Float [4, 5, 3]) :
                                                              Tensor Float [4, 7, 3] :=
                                                            Tensor.concatAfter [4] left right
                                                          
                                                          Instances For
                                                            @[simp]
                                                            theorem TorchLean.Tensor.get_concat_right {α : Type} [Storage α] {leftCount rightCount : } {shape : Shape} (left : Tensor α (Spec.Shape.dim leftCount shape)) (right : Tensor α (Spec.Shape.dim rightCount shape)) (index : Fin rightCount) :
                                                            get (left.concat right) leftCount + index, = get right index

                                                            Selecting an entry from the right side of an outer-axis concatenation recovers that entry.

                                                            def TorchLean.Tensor.take {α : Type} [Storage α] {shape : Shape} (tensor : Tensor α shape) (axis count : ) [Shape.AxisInBounds axis shape] (hCount : count shape.axisSize axis := by simp [Spec.Shape.axisSize, Spec.Shape.getDim]) :
                                                            Tensor α (shape.replaceAxis axis count)

                                                            Keep the first count entries of any valid axis.

                                                            The bound is a proof obligation, not a runtime check, so slicing past the end of an axis cannot compile. The default tactic unfolds axisSize, which closes the goal whenever the shape is a literal; a caller with a symbolic shape passes its own proof, as the CIFAR crop in NN/Examples/Models/Common/RealData.lean does.

                                                            Example:

                                                            -- Keep the first two rows. The bound `2 <= 4` is discharged at the call site, so an out-of-range
                                                            -- count fails to compile instead of failing at runtime.
                                                            def firstRows (tensor : Tensor Float [4, 3]) : Tensor Float [2, 3] :=
                                                              Tensor.take tensor 0 2
                                                            
                                                            Instances For
                                                              def TorchLean.Tensor.flattenAfter {α : Type} [Storage α] [Inhabited α] (leading : Shape) {source : Shape} (tensor : Tensor α (leading.concat source)) :
                                                              Tensor α (leading.appendDim source.size)

                                                              Preserve leading and flatten every remaining axis into one row-major vector.

                                                              Example:

                                                              -- Keep the batch axis, flatten the rest: a batch of small images becomes a batch of vectors,
                                                              -- `[8, 3, 4, 4]` to `[8, 48]`.
                                                              def vectors (images : Tensor Float [8, 3, 4, 4]) : Tensor Float [8, 48] :=
                                                                Tensor.flattenAfter [8] images
                                                              
                                                              Instances For
                                                                def TorchLean.Tensor.flattenThenTake {α : Type} [Storage α] [Inhabited α] (leading : Shape) (count : ) {source : Shape} (hCount : count source.size) (tensor : Tensor α (leading.concat source)) :
                                                                Tensor α (leading.appendDim count)

                                                                Flatten after leading, then keep a checked prefix of each resulting vector.

                                                                Instances For