Documentation

LeanMlir.Proofs.Architectures.ConvIndex

Conv and max-pool index facts — the flat ↔ tensor index vocabulary #

The flat-index plumbing every conv-net proof reads tensors through (t3Idx and its flat-sum sum_t3), and the 2×2 max-pool's window facts: the pool with its routing frozen (poolGatherFlat, which agrees with the pool at its selection) is ℓ1-contractive (poolGatherFlat_l1_contract), and a selection margin beyond 2δ freezes the argmax (MaxPool2MarginQ). The conv reads its input through the zero-padded window convPad and its kernel through the flat index k4Idx; cells with identical padded patches are twins (ConvPatchEq, and ConvPatchEq2 two convs deep), equal under every kernel and bias (conv2d_eq_of_convPatchEq). The conv as a dense layer with weight sharing and its float forward are in ConvFloat.

@[reducible]
def Proofs.t3Idx {c h w : ℕ} (ci : Fin c) (hi : Fin h) (wi : Fin w) :
Fin (c * h * w)

Flat index of a Tensor3 coordinate (the suite's row-major layout).

Note: on Lean ≥ 4.33 the proofs below fail without @[reducible]: t3Idx_def folds the raw encoding into the ite CONDITION below, but simp does not rewrite inside the Decidable INSTANCE argument, so the goal carries a folded condition over an unfolded instance and every ite_eq_left/ite_eq_right here fails to match. basisVec, which produces that ite, is @[reducible] for the same reason.

Equations
Instances For
    theorem Proofs.unflatten_t3Idx {c h w : ℕ} (v : Vec (c * h * w)) (ci : Fin c) (hi : Fin h) (wi : Fin w) :
    Tensor3.unflatten v ci hi wi = v (t3Idx ci hi wi)

    t3Idx reads back through Tensor3.unflatten.

    theorem Proofs.flatten_t3Idx {c h w : ℕ} (T : Tensor3 c h w) (ci : Fin c) (hi : Fin h) (wi : Fin w) :
    T.flatten (t3Idx ci hi wi) = T ci hi wi

    Tensor3.flatten reads off at a t3Idx.

    theorem Proofs.sum_t3 {c h w : ℕ} (f : Fin (c * h * w) → ℝ) :
    ∑ k : Fin (c * h * w), f k = ∑ ci : Fin c, ∑ hi : Fin h, ∑ wi : Fin w, f (t3Idx ci hi wi)

    A flat sum is the triple tensor sum.

    theorem Proofs.t3Idx_surj {c h w : ℕ} (k : Fin (c * h * w)) :
    ∃ (ci : Fin c) (hi : Fin h) (wi : Fin w), k = t3Idx ci hi wi

    Every flat spatial index is a t3Idx — lets a per-cell bound be lifted to the whole flattened conv-output vector (∀ k), the form relu_close / maxPoolFlat_close / dense_close consume.

    def Proofs.winRowEquiv (h : ℕ) :
    Fin h × Fin 2 ≃ Fin (2 * h)

    The window-cell parameterization (out-row, sub-row) ↦ in-row is a bijection — pooled windows tile the rows.

    Equations
    • One or more equations did not get rendered due to their size.
    Instances For
      def Proofs.winColEquiv (w : ℕ) :
      Fin w × Fin 2 ≃ Fin (2 * w)

      Column version of winRowEquiv.

      Equations
      • One or more equations did not get rendered due to their size.
      Instances For
        theorem Proofs.sum_window_cells {h w : ℕ} (g : Fin (2 * h) → Fin (2 * w) → ℝ) :
        ∑ ho : Fin h, ∑ wo : Fin w, ∑ ab : Fin 2 × Fin 2, g (winRowInv ho ab.1) (winColInv wo ab.2) = ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), g hi wi

        Summing a function over all window cells of all windows is summing it over the whole spatial grid — the 2×2 stride-2 windows partition the input.

        noncomputable def Proofs.poolGatherFlat {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) :
        Vec (c * (2 * h) * (2 * w)) → Vec (c * h * w)

        The 2×2 window gather at selection σ (windowGather at the 2×2 windows), flattened like maxPoolFlat: pooled entry (ci, ho, wo) reads window cell σ ci ho wo.

        Equations
        Instances For
          theorem Proofs.poolGatherFlat_apply {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (u : Vec (c * (2 * h) * (2 * w))) (ci : Fin c) (ho : Fin h) (wo : Fin w) :
          poolGatherFlat σ u (t3Idx ci ho wo) = u (t3Idx ci (winRowInv ho (σ ci ho wo).1) (winColInv wo (σ ci ho wo).2))
          noncomputable def Proofs.poolGatherCLM {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) :
          Vec (c * (2 * h) * (2 * w)) →L[ℝ] Vec (c * h * w)

          The 2×2 gather as a continuous linear map: each output reads one input coordinate.

          Equations
          Instances For
            theorem Proofs.poolGatherCLM_coe {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) :
            theorem Proofs.poolGatherFlat_differentiable {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) :
            theorem Proofs.pdiv_poolGatherFlat {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (u : Vec (c * (2 * h) * (2 * w))) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) (co : Fin c) (ho : Fin h) (wo : Fin w) :
            pdiv (poolGatherFlat σ) u (t3Idx ci hi wi) (t3Idx co ho wo) = if co = ci ∧ ho = winRow hi ∧ wo = winCol wi ∧ σ co ho wo = (winRowMod hi, winColMod wi) then 1 else 0

            The gather's Jacobian: input (ci, hi, wi) feeds pooled (co, ho, wo) exactly when it is that window's selected cell.

            theorem Proofs.poolGatherFlat_l1_contract {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (u v : Vec (c * (2 * h) * (2 * w))) :
            ∑ q : Fin (c * h * w), |poolGatherFlat σ u q - poolGatherFlat σ v q| ≤ ∑ k : Fin (c * (2 * h) * (2 * w)), |u k - v k|

            The gather is ℓ1-contractive: each pooled entry reads a distinct input cell (the 2×2 windows partition the input).

            theorem Proofs.maxPoolFlat_eq_poolGatherFlat {c h w : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (u : Vec (c * (2 * h) * (2 * w))) (hdom : ∀ (ci : Fin c) (ho : Fin h) (wo : Fin w) (cd : Fin 2 × Fin 2), u (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) ≤ u (t3Idx ci (winRowInv ho (σ ci ho wo).1) (winColInv wo (σ ci ho wo).2))) :

            The flat pool is the flat gather at a dominating selection.

            theorem Proofs.lt_of_lt_gap_of_close {xa xb ya yb δ : ℝ} (hlt : 2 * δ < xb - xa) (ha : |ya - xa| ≤ δ) (hb : |yb - xb| ≤ δ) :
            ya < yb

            Strict order survives δ-perturbations across a 2δ gap.

            def Proofs.MaxPool2MarginQ {c h w : ℕ} (δ : ℝ) (x : Tensor3 c (2 * h) (2 * w)) :

            Quantitative pool-selection margin: in every 2×2 window, a cell that dominates the window is more than 2δ above every other cell of it. The quantitative form of MaxPool2Smooth — a perturbation of at most δ per entry can neither tie the max nor move it to another cell, so the pool's argmax routing freezes. The other cells may tie with each other. The pool peer of the ReLU margin a·D < |zⱼ|.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              theorem Proofs.MaxPool2MarginQ.smooth_of_close {c h w : ℕ} {δ : ℝ} {x y : Tensor3 c (2 * h) (2 * w)} (hm : MaxPool2MarginQ δ x) (hclose : ∀ (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), |y ci hi wi - x ci hi wi| ≤ δ) :

              Every point within δ of a margined point is smooth (every window's max is attained once).

              theorem Proofs.MaxPool2MarginQ.smooth {c h w : ℕ} {δ : ℝ} (hδ0 : 0 ≤ δ) {x : Tensor3 c (2 * h) (2 * w)} (hm : MaxPool2MarginQ δ x) :

              A margined point is itself smooth.

              theorem Proofs.MaxPool2MarginQ.isArgmax_iff {c h w : ℕ} {δ : ℝ} {x y : Tensor3 c (2 * h) (2 * w)} (hm : MaxPool2MarginQ δ x) (hclose : ∀ (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), |y ci hi wi - x ci hi wi| ≤ δ) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) :
              MaxPool2IsArgmax y ci hi wi ↔ MaxPool2IsArgmax x ci hi wi

              The argmax freezes: within δ of a margined point, every window's argmax cell is the same as at the margined point.

              @[reducible, inline]
              abbrev Proofs.MaxPool2MarginQUpTo {c h w : ℕ} (δ : ℝ) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (x : Tensor3 c (2 * h) (2 * w)) :

              Quantitative pool margin up to twins (2×2), on the PRE-activation: WindowMarginUpTo at the 2×2 windows. Every window is dead (all cells ≤ 0), or a cell dominating it is more than 2δ above every other cell except its T-twins. Unlike MaxPool2MarginQ, a window of equal cells qualifies when its cells are twins: the descent rungs take T to relate cells that read identical input patches, which are equal for every value of the moving parameter. That is the case real MNIST forces (a constant background patch makes a window's cells equal for every kernel); see scripts/probes/mnist_pool_twin_probe.py.

              Equations
              Instances For
                theorem Proofs.MaxPool2MarginQ.to_marginQUpTo {c h w : ℕ} {δ : ℝ} {x : Tensor3 c (2 * h) (2 * w)} (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hm : MaxPool2MarginQ δ fun (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) => max (x ci hi wi) 0) :

                The post-ReLU MaxPool2MarginQ gives the pre-activation MaxPool2MarginQUpTo, with any twins: the dominating pre-activation cell is positive unless the window is dead, and below a positive cell the pre-activation gaps are at least the post-ReLU ones.

                theorem Proofs.MaxPool2MarginQ.to_marginQUpTo_flat {c h w : ℕ} {δ : ℝ} {x : Tensor3 c (2 * h) (2 * w)} (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hm : MaxPool2MarginQ δ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) x.flatten))) :

                MaxPool2MarginQ.to_marginQUpTo in the flat spelling the descent rungs use, the post-ReLU tensor as unflatten ∘ relu ∘ flatten.

                noncomputable def Proofs.convPad {ic h w : ℕ} (kH kW : ℕ) (x : Tensor3 ic h w) (c : Fin ic) (kh : Fin kH) (kw : Fin kW) (hi : Fin h) (wi : Fin w) :

                The padded input read that multiplies kernel entry (·, c, kh, kw) at output position (hi, wi) — names the dite inside conv2d so the affine-in-the-kernel structure can be stated. Depends on the input only, never the kernel.

                Equations
                • One or more equations did not get rendered due to their size.
                Instances For
                  theorem Proofs.conv2d_eq_convPad {ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (b : Vec oc) (x : Tensor3 ic h w) (o : Fin oc) (hi : Fin h) (wi : Fin w) :
                  conv2d W b x o hi wi = b o + ∑ c : Fin ic, ∑ kh : Fin kH, ∑ kw : Fin kW, W o c kh kw * convPad kH kW x c kh kw hi wi

                  conv2d through convPad: bias plus the kernel-linear form.

                  theorem Proofs.abs_convPad_le {ic h w kH kW : ℕ} (x : Tensor3 ic h w) {a : ℝ} (ha : 0 ≤ a) (hx : ∀ (c : Fin ic) (i : Fin h) (j : Fin w), |x c i j| ≤ a) (c : Fin ic) (kh : Fin kH) (kw : Fin kW) (hi : Fin h) (wi : Fin w) :
                  |convPad kH kW x c kh kw hi wi| ≤ a

                  Padded reads are bounded by the input bound (out-of-bounds reads are zero).

                  def Proofs.k4Idx {oc ic kH kW : ℕ} (o : Fin oc) (c : Fin ic) (kh : Fin kH) (kw : Fin kW) :
                  Fin (oc * ic * kH * kW)

                  Flat index of a Kernel4 entry (the suite's row-major layout).

                  Equations
                  Instances For
                    theorem Proofs.unflatten_k4Idx {oc ic kH kW : ℕ} (v : Vec (oc * ic * kH * kW)) (o : Fin oc) (c : Fin ic) (kh : Fin kH) (kw : Fin kW) :
                    Kernel4.unflatten v o c kh kw = v (k4Idx o c kh kw)

                    k4Idx reads back through Kernel4.unflatten.

                    theorem Proofs.flatten_k4Idx {oc ic kH kW : ℕ} (W : Kernel4 oc ic kH kW) (o : Fin oc) (c : Fin ic) (kh : Fin kH) (kw : Fin kW) :
                    W.flatten (k4Idx o c kh kw) = W o c kh kw

                    Kernel4.flatten reads off at a k4Idx — the forward peer of unflatten_k4Idx, lifting a per-entry kernel bound to the flattened vector.

                    theorem Proofs.k4Idx_surj {oc ic kH kW : ℕ} (idx : Fin (oc * ic * kH * kW)) :
                    ∃ (o : Fin oc) (c : Fin ic) (kh : Fin kH) (kw : Fin kW), idx = k4Idx o c kh kw

                    Every flat kernel index is a k4Idx — lets the abstract ∀ idx gradient accuracy be discharged per (o,cc,kh,kw) by cnn_conv2_grad_close.

                    theorem Proofs.sum_abs_k4 {oc ic kH kW : ℕ} (e : Vec (oc * ic * kH * kW)) :
                    ∑ idx : Fin (oc * ic * kH * kW), |e idx| = ∑ o : Fin oc, ∑ c : Fin ic, ∑ kh : Fin kH, ∑ kw : Fin kW, |e (k4Idx o c kh kw)|

                    The output-channel slabs tile the kernel: summing the slab masses over the output channels recovers the total ℓ1 mass.

                    theorem Proofs.sum_abs_kernel_slab_le {oc ic kH kW : ℕ} (e : Vec (oc * ic * kH * kW)) (o : Fin oc) :
                    ∑ c : Fin ic, ∑ kh : Fin kH, ∑ kw : Fin kW, |e (k4Idx o c kh kw)| ≤ ∑ idx : Fin (oc * ic * kH * kW), |e idx|

                    The ℓ1 mass of one output-channel slab is at most the total ℓ1 mass — the conv analogue of a dense column being part of the flat parameter vector.

                    theorem Proofs.t3Idx_def {c h w : ℕ} (ci : Fin c) (hi : Fin h) (wi : Fin w) :

                    Folds the raw finProdFinEquiv encoding back into t3Idx.

                    def Proofs.ConvPatchEq {ic h w : ℕ} (kH kW : ℕ) (x : Tensor3 ic h w) (p q : Fin h × Fin w) :

                    Twin cells of a conv input: positions p and q whose zero-padded kH×kW input patches are identical in every input channel. The conv output is then the same at p and q for EVERY kernel and bias (conv2d_eq_of_convPatchEq), so a tie between them is a tie along any step on the conv's parameters. A flat image region gives such pairs.

                    Equations
                    Instances For
                      theorem Proofs.ConvPatchEq.symm {ic h w kH kW : ℕ} {x : Tensor3 ic h w} {p q : Fin h × Fin w} (hpq : ConvPatchEq kH kW x p q) :
                      ConvPatchEq kH kW x q p
                      theorem Proofs.ConvPatchEq.trans {ic h w kH kW : ℕ} {x : Tensor3 ic h w} {p q u : Fin h × Fin w} (hpq : ConvPatchEq kH kW x p q) (hqu : ConvPatchEq kH kW x q u) :
                      ConvPatchEq kH kW x p u
                      theorem Proofs.ConvPatchEq.of_zero {ic h w kH kW : ℕ} {x : Tensor3 ic h w} {p q : Fin h × Fin w} (hp : ∀ (cc : Fin ic) (kh : Fin kH) (kw : Fin kW), convPad kH kW x cc kh kw p.1 p.2 = 0) (hq : ∀ (cc : Fin ic) (kh : Fin kH) (kw : Fin kW), convPad kH kW x cc kh kw q.1 q.2 = 0) :
                      ConvPatchEq kH kW x p q

                      Two cells whose patches are entirely zero are twins: the form a blank image region gives, and the one a concrete instance discharges cell by cell.

                      theorem Proofs.conv2d_eq_of_convPatchEq {ic oc h w kH kW : ℕ} {x : Tensor3 ic h w} {p q : Fin h × Fin w} (hpq : ConvPatchEq kH kW x p q) (W : Kernel4 oc ic kH kW) (b : Vec oc) (o : Fin oc) :
                      conv2d W b x o p.1 p.2 = conv2d W b x o q.1 q.2

                      Twin cells have equal conv outputs, for every kernel and bias.

                      def Proofs.ConvPatchEq2 {ic h w : ℕ} (kH kW : ℕ) (x : Tensor3 ic h w) (p q : Fin h × Fin w) :

                      Two-layer twins: positions p and q whose two-conv receptive fields are identical. For every offset of the outer kH×kW window, both reads fall in the zero padding, or both land on cells whose inner patches of x are identical (ConvPatchEq). Then the outer conv's input patches at p and q agree for every inner kernel and bias (convPatchEq_relu_conv), so a tie between the outer outputs there is a tie along any step on the inner conv's parameters. A flat image region away from the border gives such pairs.

                      Equations
                      • One or more equations did not get rendered due to their size.
                      Instances For
                        theorem Proofs.convPatchEq_relu_conv {ic oc h w kH kW : ℕ} {x : Tensor3 ic h w} {p q : Fin h × Fin w} (hpq : ConvPatchEq2 kH kW x p q) (W : Kernel4 oc ic kH kW) (b : Vec oc) :
                        ConvPatchEq kH kW (Tensor3.unflatten (relu (oc * h * w) (conv2d W b x).flatten)) p q

                        Two-layer twins are twins of the outer conv's input, for every inner kernel and bias.

                        theorem Proofs.MaxPool2MarginQ.poolBack_close {c h w : ℕ} {δ : ℝ} {x y : Tensor3 c (2 * h) (2 * w)} (hm : MaxPool2MarginQ δ x) (hclose : ∀ (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), |y ci hi wi - x ci hi wi| ≤ δ) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) {ay ax e : ℝ} (ha : |ay - ax| ≤ e) :
                        |(if MaxPool2IsArgmax y ci hi wi then ay else 0) - if MaxPool2IsArgmax x ci hi wi then ax else 0| ≤ e

                        Float pool-backward closeness. Under the pool margin the float post-relu argmax matches the real one (isArgmax_iff), so the pool's backward selector 𝟙[(ci,hi,wi) is its window's argmax]·(pooled cotangent) differs from the certified one only through the pooled cotangent value — an indicator pass-through (indicator ∈ {0,1}), the pool peer of reluMask_close. The two cotangent values ay (float) / ax (real) enter only via their closeness |ay − ax| ≤ e.