TorchLean API

NN.IR.ShapeSoundness

Shape Soundness #

Shape inference (Infer.nodeOutShape, Graph.checkShapes) and the reference semantics (Graph.evalNodeRaw, Graph.denoteAll) are two matches over OpKind. This file proves that they agree: on a graph accepted by checkShapes, the value the evaluator computes for every node already has the shape inference assigns to it, so the declared-shape normalization performed by evalNode never rejects a node.

The results are organized as one lemma per operator family, followed by an induction over the node array that runs Graph.inferShapesFrom and Graph.denoteAllFrom in lockstep.

Except helpers #

theorem NN.IR.Graph.pure_eq_ok {ε β : Type} (a : β) :

pure a in the Except monad is .ok a.

theorem NN.IR.Graph.bind_ok_iff {ε β γ : Type} {x : Except ε β} {f : β → Except ε γ} {v : γ} :
x >>= f = Except.ok v ↔ ∃ (a : β), x = Except.ok a ∧ f a = Except.ok v

A bind in the Except monad succeeds exactly when both stages succeed.

theorem NN.IR.Graph.map_ok_iff {ε β γ : Type} {f : β → γ} {x : Except ε β} {v : γ} :
Except.map f x = Except.ok v ↔ ∃ (a : β), x = Except.ok a ∧ f a = v

Except.map succeeds exactly when its argument succeeds.

theorem NN.IR.Graph.ok_bind {ε β γ : Type} (a : β) (f : β → Except ε γ) :
Except.ok a >>= f = f a

Binding a success value applies the continuation.

theorem NN.IR.Graph.error_bind {ε β γ : Type} (e : ε) (f : β → Except ε γ) :

Binding an error propagates the error.

Every success value of an Except computation has shape s.

Instances For

    Errors have no success value.

    theorem NN.IR.Graph.okShape_ok {α : Type} [TorchLean.Storage α] (s : Spec.Shape) (t : TorchLean.Tensor α s) :
    OkShape s (Except.ok { shape := s, tensor := t })

    A success value tagged with s has shape s.

    theorem NN.IR.Graph.okShape_bind {α : Type} [TorchLean.Storage α] {β : Type} (s : Spec.Shape) (x : Except String β) (f : β → Except String (Spec.SomeTensor α)) (hf : ∀ (a : β), OkShape s (f a)) :
    OkShape s (x >>= f)

    Binding preserves the shape invariant of the continuation.

    theorem NN.IR.Graph.okShape_dite {α : Type} [TorchLean.Storage α] (s : Spec.Shape) (c : Prop) [Decidable c] (t : c → Except String (Spec.SomeTensor α)) (e : ¬c → Except String (Spec.SomeTensor α)) (ht : ∀ (h : c), OkShape s (t h)) (he : ∀ (h : ¬c), OkShape s (e h)) :
    OkShape s (if h : c then t h else e h)

    A dependent conditional preserves the shape invariant of both branches.

    theorem NN.IR.Graph.okShape_ite {α : Type} [TorchLean.Storage α] (s : Spec.Shape) (c : Prop) [Decidable c] (t e : Except String (Spec.SomeTensor α)) (ht : c → OkShape s t) (he : ¬c → OkShape s e) :
    OkShape s (if c then t else e)

    A conditional preserves the shape invariant of both branches.

    Decoder characterizations #

    theorem NN.IR.Graph.shape_eq_of_expectShape_ok {α : Type} [TorchLean.Storage α] {expected : Spec.Shape} {v : Spec.SomeTensor α} {t : TorchLean.Tensor α expected} (h : expectShape expected v = Except.ok t) :
    v.shape = expected

    A successful expectShape certifies the stored shape tag.

    theorem NN.IR.Graph.getParentValue_ok {α : Type} [TorchLean.Storage α] {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {pid : ℕ} {pv : Spec.SomeTensor α} (h : getParentValue vals i n pid = Except.ok pv) :
    vals[pid]? = some pv

    A successful parent read is an in-bounds array lookup.

    theorem NN.IR.Graph.unaryParentId_ok {i : ℕ} {n : Node} {pid : ℕ} (h : unaryParentId i n = Except.ok pid) :

    The unary decoder succeeds exactly on a one-element parent array.

    The binary decoder succeeds exactly on a two-element parent array.

    The unary inference decoder returns the sole parent shape.

    theorem NN.IR.Graph.expectBinaryParents_ok {tag : String} {ps : Array Spec.Shape} {s₁ s₂ : Spec.Shape} (h : Infer.expectBinaryParents tag ps = Except.ok (s₁, s₂)) :
    ps.size = 2 ∧ ps[0]? = some s₁ ∧ ps[1]? = some s₂

    The binary inference decoder returns both parent shapes.

    The same-shape inference rule returns the first parent's shape.

    Parent-shape correspondence #

    def NN.IR.Graph.ParentShapesOf {α : Type} [TorchLean.Storage α] (vals : Array (Spec.SomeTensor α)) (parents : Array ℕ) (parentShapes : Array Spec.Shape) :

    parentShapes lists the shapes of the already evaluated values at a node's parent ids.

    This is the interface between the shape table maintained by Graph.inferShapesFrom and the value table maintained by Graph.denoteAllFrom: whenever the evaluator can read parent k, inference saw exactly that parent's shape at position k.

    Instances For
      theorem NN.IR.Graph.unary_parent_shape {α : Type} [TorchLean.Storage α] {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} (hParents : ParentShapesOf vals n.parents parentShapes) {pid : ℕ} (hPid : unaryParentId i n = Except.ok pid) {pv : Spec.SomeTensor α} (hVal : getParentValue vals i n pid = Except.ok pv) {tag : String} {s : Spec.Shape} (hInfer : Infer.expectUnaryParent tag parentShapes = Except.ok s) :
      s = pv.shape

      The unary parent shape read by inference is the shape of the value read by the evaluator.

      theorem NN.IR.Graph.binary_parent_shapes {α : Type} [TorchLean.Storage α] {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} (hParents : ParentShapesOf vals n.parents parentShapes) {a b : ℕ} (hIds : binaryParentIds i n = Except.ok (a, b)) {av bv : Spec.SomeTensor α} (hA : getParentValue vals i n a = Except.ok av) (hB : getParentValue vals i n b = Except.ok bv) {tag : String} {s₁ s₂ : Spec.Shape} (hInfer : Infer.expectBinaryParents tag parentShapes = Except.ok (s₁, s₂)) :
      s₁ = av.shape ∧ s₂ = bv.shape

      The binary parent shapes read by inference are the shapes of the values read by the evaluator.

      Operator families #

      Each lemma below fixes one operator (or family) and shows that the raw evaluator value has the inferred shape. The hypotheses are the same throughout: the parent-shape correspondence, a successful inference, and a successful raw evaluation. permute, transpose, and conv are the exception (see evalNodeRaw_shape_declared).

      theorem NN.IR.Graph.inferBatchNormEvalOutShape_ok {channelAxis channels : ℕ} {s r : Spec.Shape} (h : OpContracts.inferBatchNormEvalOutShape channelAxis channels s = Except.ok r) :
      r = s

      The eval-mode BatchNorm contract returns the parent shape.

      theorem NN.IR.Graph.evalBatchNorm_ok_shape {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {id channelAxis channels : ℕ} {x y : Spec.SomeTensor α} (h : evalBatchNorm payload id channelAxis channels x = Except.ok y) :

      Eval-mode BatchNorm preserves the shape of its input.

      theorem NN.IR.Graph.evalConcatLeadingAxisFold_ok_shape {α : Type} [TorchLean.Storage α] {i nOut : ℕ} {rest : Spec.Shape} {parents : Array (Spec.SomeTensor α)} {v : Spec.SomeTensor α} {permuted : Bool} (h : evalConcatLeadingAxisFold i nOut rest parents permuted = Except.ok v) :
      v.shape = Spec.Shape.dim nOut rest

      Leading-axis concat produces the declared leading extent over the shared tail.

      theorem NN.IR.Graph.list_mapM_ok {β γ : Type} {f : β → Except String γ} {l : List β} {r : List γ} :
      List.mapM f l = Except.ok r → r.length = l.length ∧ ∀ (k : ℕ) (hk : k < l.length) (hr : k < r.length), f l[k] = Except.ok r[k]

      A successful list mapM in Except succeeds pointwise.

      theorem NN.IR.Graph.array_mapM_ok {β γ : Type} {f : β → Except String γ} {as : Array β} {bs : Array γ} (h : Array.mapM f as = Except.ok bs) :
      bs.size = as.size ∧ ∀ (k : ℕ) (hk : k < as.size) (hb : k < bs.size), f as[k] = Except.ok bs[k]

      A successful array mapM in Except succeeds pointwise.

      theorem NN.IR.Graph.evalNodeRaw_shape_binary_elementwise {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind ∈ [OpKind.add, OpKind.sub, OpKind.mulElem, OpKind.maxElem, OpKind.minElem]) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      Binary elementwise operations (add, sub, mulElem, maxElem, minElem).

      theorem NN.IR.Graph.evalNodeRaw_shape_source {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind = OpKind.input ∨ (∃ (s : Spec.Shape), n.kind = OpKind.const s) ∨ (∃ (seed : ℕ), n.kind = OpKind.randUniform seed) ∨ ∃ (seed : ℕ), n.kind = OpKind.bernoulliMask seed) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      Source nodes (input, const, randUniform, bernoulliMask): inference returns the shape the evaluator tags its result with.

      theorem NN.IR.Graph.evalNodeRaw_shape_unary_elementwise {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind ∈ [OpKind.abs, OpKind.sqrt, OpKind.inv, OpKind.relu, OpKind.tanh, OpKind.sigmoid, OpKind.silu, OpKind.softplus, OpKind.exp, OpKind.sin, OpKind.cos, OpKind.detach]) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      Unary shape-preserving operations that only read one parent at the declared shape.

      theorem NN.IR.Graph.evalNodeRaw_shape_topKMask {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} {k : ℕ} (hk : n.kind = OpKind.topKMask k) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      topKMask: shape preserving, reading one parent at the declared shape.

      theorem NN.IR.Graph.evalNodeRaw_shape_log {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind = OpKind.log) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      log: like the other unary operations, plus the data-dependent positivity check.

      theorem NN.IR.Graph.evalNodeRaw_shape_normalization {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : (∃ (axis : ℕ), n.kind = OpKind.softmax axis) ∨ (∃ (mask : HardMask), n.kind = OpKind.hardMaskedSoftmax mask) ∨ ∃ (axis : ℕ), n.kind = OpKind.layernorm axis) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      softmax, hardMaskedSoftmax, and layernorm: shape preserving after their axis, mask, or normalized-suffix validation.

      theorem NN.IR.Graph.evalNodeRaw_shape_reduction {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : (∃ (axis : ℕ), n.kind = OpKind.reduceSum axis) ∨ (∃ (axis : ℕ), n.kind = OpKind.reduceMean axis) ∨ n.kind = OpKind.sum) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      Axis reductions (reduceSum, reduceMean) and the full reduction sum.

      theorem NN.IR.Graph.evalNodeRaw_shape_shape_op {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : (∃ (s₁ : Spec.Shape) (s₂ : Spec.Shape), n.kind = OpKind.broadcastTo s₁ s₂) ∨ (∃ (inS : Spec.Shape) (outS : Spec.Shape), n.kind = OpKind.reshape inS outS) ∨ ∃ (s : Spec.Shape), n.kind = OpKind.flatten s) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      Pure shape operations (broadcastTo, reshape, flatten) tag their result with the shape written in the operation.

      theorem NN.IR.Graph.evalNodeRaw_shape_linear_loss {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind = OpKind.linear ∨ n.kind = OpKind.mseLoss) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      linear and mseLoss: the evaluator tags its result with the declared shape, respectively the scalar shape, and inference returns the same.

      theorem NN.IR.Graph.evalNodeRaw_shape_matmul {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind = OpKind.matmul) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      matmul: both passes decompose the operand shapes with OpContracts.matmulDims.

      theorem NN.IR.Graph.evalNodeRaw_shape_scaled_matmul {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : n.kind = OpKind.scaledMatmul) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      A scalar scale operand changes a contraction's arithmetic, not its output shape.

      theorem NN.IR.Graph.evalNodeRaw_shape_pool {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : (∃ (config : WindowConfig), n.kind = OpKind.maxPool config) ∨ ∃ (config : WindowConfig), n.kind = OpKind.avgPool config) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      maxPool and avgPool: both passes plan the pooled suffix with OpContracts.planPool.

      theorem NN.IR.Graph.evalNodeRaw_shape_batchNorm {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : ∃ (channelAxis : ℕ) (channels : ℕ), n.kind = OpKind.batchNormEval channelAxis channels) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      batchNormEval: the payload-backed evaluator preserves the parent shape, which is also what the contract returns.

      theorem NN.IR.Graph.evalNodeRaw_shape_concat {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hk : ∃ (axis : ℕ), n.kind = OpKind.concat axis) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      concat: the evaluator recomputes OpContracts.inferConcatOutShape on the parent values and insists that it equals the declared shape, which is what it produces.

      theorem NN.IR.Graph.evalNodeRaw_shape_declared {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {v : Spec.SomeTensor α} (hk : (∃ (perm : Array ℕ), n.kind = OpKind.permute perm) ∨ (∃ (a₁ : ℕ) (a₂ : ℕ), n.kind = OpKind.transpose a₁ a₂) ∨ ∃ (config : ConvConfig), n.kind = OpKind.conv config) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :

      permute, transpose, and conv compare the shape they realize against the declared outShape, so a successful raw evaluation has the declared shape. Relating that realized shape to the inference rule (Shape.permute?, respectively the convolution contract) is not needed for soundness because checkShapes separately forces the inferred shape to be the declared one.

      theorem NN.IR.Graph.evalNodeRaw_shape_slice {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {v : Spec.SomeTensor α} {axis start length : ℕ} (hk : n.kind = OpKind.slice axis start length) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :

      slice: the evaluator checks the sliced shape against the declared one.

      theorem NN.IR.Graph.evalPointwise_ok {α : Type} [TorchLean.Storage α] {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {v : Spec.SomeTensor α} {op : Spec.PointwiseOp} {meaning : Option (Spec.PointwiseFn α op.arity)} (h : evalPointwise vals i n op meaning = Except.ok v) :
      v.shape = n.outShape ∧ ∃ (h0 : 0 < n.parents.size) (pv : Spec.SomeTensor α), vals[n.parents[0]]? = some pv ∧ pv.shape = n.outShape

      A successful pointwise evaluation read its first parent at the declared shape, and its value has that shape.

      theorem NN.IR.Graph.evalNodeRaw_shape_pointwise {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} {op : Spec.PointwiseOp} (hk : n.kind = OpKind.pointwise op) (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :
      v.shape = inferred

      Pointwise operators: inference returns the shape of the first parent, which the evaluator reads at the declared shape, and the result has that shape too.

      theorem NN.IR.Graph.evalNodeRaw_shape_of_infer {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {parentShapes : Array Spec.Shape} {inferred : Spec.Shape} {v : Spec.SomeTensor α} (hParents : ParentShapesOf vals n.parents parentShapes) (hInfer : Infer.nodeOutShape n parentShapes = Except.ok inferred) (hDecl : inferred = n.outShape) (hEval : evalNodeRaw payload input vals i n = Except.ok v) :

      Node-level soundness of shape inference.

      If inference assigns the declared shape to a node (as Graph.checkShapes requires) and the node's parents carry the shapes inference saw, then the raw evaluator value already has the declared shape.

      Graph-level soundness #

      theorem NN.IR.Graph.denoteAll_ok_from {α : Type} [TorchLean.Storage α] [Context α] {g : Graph} {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} (h : g.denoteAll payload input = Except.ok vals) :
      g.denoteAllFrom payload input 0 #[] = Except.ok vals

      A successful denoteAll is a successful denoteAllFrom started at node 0.

      theorem NN.IR.Graph.getNode_ok {g : Graph} {i : ℕ} {n : Node} (h : g.getNode i = Except.ok n) :

      A successful checked lookup returns the node stored at the requested index.

      The normalized value of a node has the declared shape.

      Normalization is the identity on a value that already has the declared shape.

      theorem NN.IR.Graph.evalNode_ok_shape {α : Type} [TorchLean.Storage α] [Context α] {payload : Payload α} {input : Spec.SomeTensor α} {vals : Array (Spec.SomeTensor α)} {i : ℕ} {n : Node} {v : Spec.SomeTensor α} (h : evalNode payload input vals i n = Except.ok v) :

      Every value produced by evalNode has the declared shape of its node.

      theorem NN.IR.Graph.lookupParentShapes_ok {inferred : Array Spec.Shape} {pids : List ℕ} {shapes : List Spec.Shape} :
      lookupParentShapes inferred pids = some shapes → shapes.length = pids.length ∧ ∀ (k : ℕ) (hk : k < pids.length) (hs : k < shapes.length), inferred[pids[k]]? = some shapes[k]

      A successful parent lookup returns one already inferred shape per parent id.

      theorem NN.IR.Graph.parentShapesOf_of_lookup {α : Type} [TorchLean.Storage α] {vals : Array (Spec.SomeTensor α)} {inferred : Array Spec.Shape} (hSize : vals.size = inferred.size) (hShapes : ∀ (j : ℕ) (hj : j < inferred.size), vals[j].shape = inferred[j]) {parents : Array ℕ} {shapes : List Spec.Shape} (hLookup : lookupParentShapes inferred parents.toList = some shapes) :
      ParentShapesOf vals parents shapes.toArray

      The shapes looked up by inference are the shapes of the corresponding evaluated values.

      @[irreducible]
      def NN.IR.Graph.denoteAllRawFrom {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (i : ℕ) (vals : Array (Spec.SomeTensor α)) :

      Evaluate nodes i, i+1, ... with evalNodeRaw, that is, without the per-node declared-shape normalization performed by evalNode.

      This is a proof-only reference evaluator: denoteAllRawFrom_eq_denoteAllFrom shows that on a graph accepted by checkShapes it computes the same table as denoteAllFrom.

      Instances For

        denoteAll without the per-node declared-shape normalization.

        Instances For
          theorem NN.IR.Graph.denoteAllRawFrom_eq_denoteAllFrom {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (i : ℕ) (vals : Array (Spec.SomeTensor α)) (inferred inferredAll : Array Spec.Shape) (hInfer : g.inferShapesFrom i inferred = Except.ok inferredAll) (hVals : vals.size = i) (hInferred : inferred.size = i) (hShapes : ∀ (j : ℕ) (hj : j < inferred.size), vals[j].shape = inferred[j]) :
          g.denoteAllRawFrom payload input i vals = g.denoteAllFrom payload input i vals

          Lockstep induction: if shape inference accepts the nodes from i on, starting from a table of inferred shapes that matches the evaluated prefix, then raw and normalized evaluation agree from i on.

          theorem NN.IR.Graph.denoteAllRaw_eq_denoteAll {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (hShapes : g.checkShapes = Except.ok ()) :
          g.denoteAllRaw payload input = g.denoteAll payload input

          On a graph accepted by checkShapes, the per-node declared-shape normalization is redundant: evaluating with evalNodeRaw and with evalNode produce the same result.

          theorem NN.IR.Graph.denoteAllFrom_prefix {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (i : ℕ) (vals out : Array (Spec.SomeTensor α)) (h : g.denoteAllFrom payload input i vals = Except.ok out) (j : ℕ) (hj : j < vals.size) :
          out[j]? = some vals[j]

          Values already in the table are untouched by evaluating further nodes.

          theorem NN.IR.Graph.denoteAllFrom_ok_shapes {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (i : ℕ) (vals out : Array (Spec.SomeTensor α)) (h : g.denoteAllFrom payload input i vals = Except.ok out) (hSize : vals.size = i) (hi : i ≤ g.nodes.size) :
          out.size = g.nodes.size ∧ ∀ (j : ℕ) (hj : j < g.nodes.size) (hjo : j < out.size), i ≤ j → out[j].shape = g.nodes[j].outShape

          Evaluation from i fills exactly the remaining nodes, each with its declared shape.

          theorem NN.IR.Graph.denoteAll_shape {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (vals : Array (Spec.SomeTensor α)) (h : g.denoteAll payload input = Except.ok vals) :
          vals.size = g.nodes.size ∧ ∀ (i : ℕ) (hi : i < g.nodes.size) (hiv : i < vals.size), vals[i].shape = g.nodes[i].outShape

          Successful evaluation produces exactly one value per node, each with its declared shape.

          This needs no shape-inference hypothesis: evalNode checks each result's declared shape. The stronger correspondence denoteAllRaw_eq_denoteAll shows that on a graph accepted by checkShapes, these checks never change or reject a raw result.

          theorem NN.IR.Graph.checkShapes_sound {α : Type} [TorchLean.Storage α] [Context α] (g : Graph) (payload : Payload α) (input : Spec.SomeTensor α) (vals : Array (Spec.SomeTensor α)) (hShapes : g.checkShapes = Except.ok ()) (hEval : g.denoteAll payload input = Except.ok vals) (i : ℕ) (hi : i < g.nodes.size) (hiv : i < vals.size) :

          Shape inference is sound for the reference semantics: if checkShapes accepts a graph and the graph evaluates, every node value has the shape inference assigned to it, namely the declared one.