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.

Structural Boolean shape equality agrees with propositional equality.

== on shapes is propositional equality.

!= on shapes is propositional disequality.

theorem NN.IR.Graph.option_bind_some_iff {β γ : Type} {x : Option β} {f : βOption γ} {v : γ} :
x >>= f = some v ∃ (a : β), x = some a f a = some v

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

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 : cExcept String (Spec.SomeTensor α)) (e : ¬cExcept 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 : cOkShape s t) (he : ¬cOkShape 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 α] [Context α] {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.

    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 α] [Context α] {i nOut : } {rest : Spec.Shape} {parents : Array (Spec.SomeTensor α)} {v : Spec.SomeTensor α} (h : evalConcatLeadingAxisFold i nOut rest parents = 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 rr.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.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_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_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_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.except_cases {ε β : Type} (x : Except ε β) :
      (∃ (e : ε), x = Except.error e) ∃ (a : β), x = Except.ok a

      Case analysis on an Except value stated as a disjunction of equations.

      theorem NN.IR.Graph.seq_ok {β γ : Type} {x : Except String β} {y : Except String γ} {v : γ} (h : (do let _ ← x y) = Except.ok v) :

      Sequencing a computation before y does not change a successful result of y.

      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 shapesshapes.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 jout[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

          The literal soundness statement: on a well-shaped graph, every evaluated node value has its declared shape.

          Note that hShapes is not needed for the conclusion, because evalNode normalizes each value to the declared shape (denoteAll_shape); its role is documented by denoteAllRaw_eq_denoteAll, which shows that on a checkShapes-accepted graph the normalization never changes anything.

          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.