TorchLean API

NN.IR.OpContracts

Operation Contracts #

Shared operation contracts for NN.IR.Graph.

Several IR passes need to agree on the same small set of “shape contracts”:

The point of this file is to keep shape arithmetic out of individual passes. If an op has nontrivial shape behavior (concat, matmul, pooling, convolution, LayerNorm flattening, axis moves), define the contract here first and call it from inference/semantics instead of copying the formula.

Small shape utilities #

These helpers are used by multiple IR passes, especially Infer and Semantics.

The output shape of flattening a tensor of shape s to a 1D vector.

Instances For

    If s has rank at least two, return the shape obtained by swapping its first two axes.

    Example: (a, b, rest) becomes (b, a, rest).

    Instances For

      If s has shape (a, b, c) (rank three with scalar base), return (a, c, b).

      This is the common “transpose the last two axes” pattern for batched matrices.

      Instances For

        Generic contract helpers #

        These functions live outside any particular pass (Infer/Check/Semantics) so they can be reused without introducing import cycles.

        Check that an axis is in-bounds for a given shape.

        Instances For

          Check that a natural-number op parameter is nonzero.

          Instances For

            Reconstruct the proof object required by the typed tensor broadcast primitive.

            IR nodes store dynamic shapes, so every pass that accepts .broadcastTo must rebuild this witness instead of trusting that the declared input and output shapes are compatible.

            Instances For

              Compute the (seqLen, embedDim) pair used to interpret layernorm axis.

              TorchLean’s IR stores LayerNorm as an axis : Nat instead of a full normalized_shape tuple. We interpret this in the same way the PyTorch exporter does:

              normalized_shape = dims.drop axis

              That is, we normalize over the suffix of dimensions starting at axis. To reuse the current spec primitive (Spec.layerNorm), we flatten the input shape s into a 2D view:

              • seqLen = product of dimensions before axis (dims.take axis)
              • embedDim = product of dimensions from axis onward (dims.drop axis)

              Then we run 2D last-axis LayerNorm on a (seqLen × embedDim) tensor and reshape back.

              Instances For

                Check that axis refers to the last axis of s.

                This is a convenience predicate for passes/backends that restrict an op to last-axis behavior. For example, some verification bounds are implemented only for last-axis softmax/layernorm and use this check to fail fast with a readable error.

                Instances For

                  Compute the inverse of a permutation list.

                  If perm is a permutation of [0,1,...,r-1] (where r = perm.length), then the inverse inv satisfies $\mathrm{inv}[\mathrm{perm}[i]]=i$.

                  Instances For
                    def NN.IR.OpContracts.inversePerm.setOnce (perm : List ) (r : ) (xs : List (Option )) (axis j val : ) :
                    Instances For

                      Permutation (0-based axes) that moves axis to the last position, preserving the relative order of the other axes.

                      Example: rank four with axis set to 1 yields [0,2,3,1].

                      Instances For

                        Permutation (0-based axes) that moves axis to the first position, preserving the relative order of the other axes.

                        Example: rank four with axis set to 2 yields [2,0,1,3].

                        Instances For

                          Infer the output shape for matmul from the two parent shapes.

                          Supported cases:

                          • 2D: (m×n) · (n×p) → (m×p)
                          • limited 3D “batched matmul”: (b×m×n) · (b×n×p) → (b×m×p)
                          Instances For

                            Check that every concat parent has the expected rank.

                            Instances For

                              Infer leading-axis concat after the first input has fixed the common tail and initial dimension.

                              Instances For

                                Infer the output shape for concat from the parent shapes.

                                All parents must:

                                • have the same rank,
                                • agree on every dimension except axis, and
                                • have axis in bounds.

                                The output shape matches the parents except at axis, where the dimension is the sum of the input dimensions.

                                PyTorch analogy: torch.cat(xs, dim=axis) for a list xs of tensors.

                                Instances For

                                  Sliding-window shape arithmetic #

                                  Convolution and pooling preserve a leading channel axis, but their admissible padding domains are not identical. The contracts below share validation and traversal while retaining the correct output formula for each operation family.

                                  def NN.IR.OpContracts.slideOut (inLen k stride : ) :

                                  Output length for a 1D sliding-window op without padding: $\left\lfloor(\mathrm{in}-k)/\mathrm{stride}\right\rfloor+1$.

                                  Instances For
                                    def NN.IR.OpContracts.slideOutPad (inLen k stride padding : ) :

                                    Output length for a 1D sliding-window op with symmetric padding: ⌊(in + 2*pad - k)/stride⌋ + 1.

                                    Instances For
                                      def NN.IR.OpContracts.checkWindowFits (tag axis : String) (inLen k padding : ) :

                                      Reject sliding-window shapes where the kernel has no valid placement.

                                      Lean Nat subtraction saturates at zero, so $\mathrm{in}+2\,\mathrm{pad}-k$ would otherwise turn an invalid window into a plausible one-element output.

                                      Instances For
                                        def NN.IR.OpContracts.inferSlidingWindowDims (tag : String) (axisNames : List String) (inputs kernels strides paddings : List ) :

                                        Infer the output lengths of a channel-first sliding-window operation.

                                        The four lists describe the input length, kernel width, stride, and symmetric padding on each spatial axis. Their lengths must agree. Invalid kernels, strides, and windows are rejected before Nat subtraction can hide the error by saturating at zero.

                                        Instances For
                                          def NN.IR.OpContracts.inferPoolingDims (tag : String) (axisNames : List String) (inputs kernels strides paddings : List ) :

                                          Infer pooling output lengths while enforcing the same basic window checks as graph validation.

                                          Unlike convolution, pooling uses poolOutDim, which assigns an empty output to empty input axes and to padding outside the pooling domain.

                                          Instances For
                                            def NN.IR.OpContracts.inferPoolOutShape (tag : String) (axisNames : List String) (kernels strides paddings : List ) (parent : Spec.Shape) :

                                            Infer a channel-first pooling shape for an arbitrary number of spatial dimensions.

                                            Instances For
                                              def NN.IR.OpContracts.inferConvOutShape (tag : String) (inChannels outChannels : ) (axisNames : List String) (kernels strides paddings : List ) (parent : Spec.Shape) :

                                              Infer a channel-first convolution shape for an arbitrary number of spatial dimensions.

                                              The declared input-channel count is checked against the leading input axis. The output-channel count becomes the leading output axis; all remaining axes are inferred by inferSlidingWindowDims.

                                              Instances For
                                                def NN.IR.OpContracts.inferPool2dOutShape (tag : String) (kH kW stride padding : ) (parent : Spec.Shape) :

                                                Infer the output of the current two-dimensional pooling IR operators.

                                                Instances For
                                                  def NN.IR.OpContracts.inferConv2dOutShape (inC outC kH kW stride padding : ) (parent : Spec.Shape) :

                                                  Infer the output of the current two-dimensional convolution IR operator.

                                                  Instances For

                                                    Output shape for eval-mode BatchNorm2d on NCHW tensors.

                                                    Instances For