Documentation

LeanMlir.Proofs.Foundation.ParamGradNodes

ParamGradNodes — each batched parameter gradient node is a loss derivative #

GradNodesB states each emitted parameter gradient node as its layer's parameter Jacobian contracted with an arbitrary output cotangent. Here the cotangent is the gradient of a scalar G at the layer's output (HasGradAt), and the node becomes ∂G/∂θ with the layer's parameter varied: one lemma per node kind, shared by every net, each concluding HasGradAt in the parameter (the loss is differentiable there and the node is its gradient).

The BatchNorm γ/β nodes are stated in the transposed [C, N·H·W] layout at the reassocB index; their lemmas re-sum the Jacobian over that permutation (bnLAPerm). A conv or depthwise bias that the render reads with the β op (EfficientNet-B0's) is biasBeta_hasGradAt: the bias enters as a channel broadcast (*_bias_split), so the channel sum the β node computes is its derivative.

theorem Proofs.GradNodeB.cInB_eq_batchMapBackward {N ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (b : Vec oc) (x : Vec (N * (ic * h * w))) (dy : Vec (N * (oc * h * w))) :

cInB — the emitted conv input-cotangent — is the batched conv VJP's backward, at any saved input.

theorem Proofs.GradNodeB.cStridedInB_eq_batchMapBackward {N ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (b : Vec oc) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (dy : Vec (N * (oc * h * w))) :

cStridedInB — the emitted strided-conv input-cotangent — is the batched strided conv VJP's backward, at any saved input.

theorem Proofs.GradNodeB.dInB_eq_batchMapBackward {N c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (b : Vec c) (x dy : Vec (N * (c * h * w))) :

dInB — the emitted depthwise input-cotangent — is the batched depthwise VJP's backward.

theorem Proofs.GradNodeB.dStridedInB_eq_batchMapBackward {N c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (b : Vec c) (x : Vec (N * (c * (2 * h) * (2 * w)))) (dy : Vec (N * (c * h * w))) :

dStridedInB — the emitted (symmetric) strided depthwise input-cotangent — is the batched strided depthwise VJP's backward, at any saved input.

theorem Proofs.GradNodeB.dStridedXlaInB_eq_batchMapBackward {N c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (b : Vec c) (x : Vec (N * (c * (2 * h) * (2 * w)))) (dy : Vec (N * (c * h * w))) :

dStridedXlaInB — the emitted XLA-SAME strided depthwise input-cotangent — is the batched strided depthwise VJP's backward, at any saved input.

theorem Proofs.GradNodeB.hasGradAt_bnBatchLA {N c h w : ℕ} (ε : ℝ) (hε : 0 < ε) (γ β : Vec c) (z : Vec (N * (c * h * w))) {G : Vec (N * (c * h * w)) → Vec 1} {dy : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.bnBatchLA N c h w ε γ β z) dy) :
HasGradAt (fun (z' : Vec (N * (c * h * w))) => G (StableHLO.bnBatchLA N c h w ε γ β z')) z (BackLinks.bnInB N c h w ε γ z dy)

Back through batch BN: the gradient at its input is bnInB of the gradient at its output.

theorem Proofs.GradNodeB.hasGradAt_relu {n : ℕ} (x : Vec n) (hs : ∀ (k : Fin n), x k ≠ 0) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (relu n x) dy) :
HasGradAt (fun (u : Vec n) => G (relu n u)) x (BackLinks.reluMaskB n x dy)

Back through relu, off its kink: reluMaskB.

theorem Proofs.GradNodeB.hasGradAt_relu6 {n : ℕ} (x : Vec n) (hs : ∀ (k : Fin n), x k ≠ 0 ∧ x k ≠ 6) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (relu6 n x) dy) :
HasGradAt (fun (u : Vec n) => G (relu6 n u)) x (BackLinks.relu6MaskB n x dy)

Back through relu6, off both kinks: relu6MaskB.

theorem Proofs.GradNodeB.hasGradAt_addConst {n : ℕ} (u v : Vec n) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (fun (i : Fin n) => u i + v i) dy) :
HasGradAt (fun (u' : Vec n) => G fun (i : Fin n) => u' i + v i) u dy

Back through u ↦ u + v with the skip v held fixed: the gradient passes unchanged. This is how a residual block's body sees the loss once one of its parameters varies.

theorem Proofs.GradNodeB.hasGradAt_constAdd {n : ℕ} (v u : Vec n) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (fun (i : Fin n) => v i + u i) dy) :
HasGradAt (fun (u' : Vec n) => G fun (i : Fin n) => v i + u' i) u dy

…and through u ↦ v + u, the fixed branch on the left (a projected skip seen from the body).

theorem Proofs.HasGradAt.residual_body {n : ℕ} (L : StableHLO.CertLayer n n) (v : Vec n) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (L.residual.fwd v) dy) :
HasGradAt (fun (u : Vec n) => G fun (i : Fin n) => u i + v i) (L.fwd v) dy

At a residual layer the loss read at the BODY output is u ↦ G (u + v): the skip is a constant once a body parameter varies, so its gradient there is still the block-output cotangent.

theorem Proofs.GradNodeB.hasGradAt_cast {n m : ℕ} (e : n = m) (x : Vec n) {G : Vec m → Vec 1} {dy : Vec m} (hG : HasGradAt G (fun (j : Fin m) => x (Fin.cast ⋯ j)) dy) :
HasGradAt (fun (u : Vec n) => G fun (j : Fin m) => u (Fin.cast ⋯ j)) x fun (i : Fin n) => dy (Fin.cast e i)

Back through a relabelling Fin n ≃ Fin m along n = m (MobileNetV4's head reads [N, c] as [N, c, 1, 1]): the cotangent is read back along the same cast.

theorem Proofs.StableHLO.CertLayer.hasGradAt_comp {m n : ℕ} (L : CertLayer m n) (x : Vec m) (hx : L.ok x) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (L.fwd x) dy) :
HasGradAt (fun (y : Vec m) => G (L.fwd y)) x ((L.vjp x hx).backward dy)

Back through a certified layer, at a point it certifies: the layer's own backward.

theorem Proofs.GradNodeB.hasGradAt_bnBackB {N c h w : ℕ} (ε : ℝ) (hε : 0 < ε) (γ β : Vec c) (z : Vec (N * (c * h * w))) {G : Vec (N * (c * h * w)) → Vec 1} {dy : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.bnBatchLA N c h w ε γ β z) dy) :
HasGradAt (fun (z' : Vec (N * (c * h * w))) => G (StableHLO.bnBatchLA N c h w ε γ β z')) z (BackLinks.bnBackB N c h w ε hε γ β z dy)

Back through batch BN, at the certified backward's own spelling bnBackB (EfficientNet-B0's tie threads this form; the ResNets and MobileNets emit bnInB).

theorem Proofs.GradNodeB.hasGradAt_swish {n : ℕ} (x : Vec n) {G : Vec n → Vec 1} {dy : Vec n} (hG : HasGradAt G (swish n x) dy) :
HasGradAt (fun (u : Vec n) => G (swish n u)) x (BackLinks.swBackB n x dy)

Back through swish: swBackB. Swish has no kink, so there is no smoothness hypothesis.

theorem Proofs.GradNodeB.hasGradAt_conv {N ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (b : Vec oc) (x : Vec (N * (ic * h * w))) {G : Vec (N * (oc * h * w)) → Vec 1} {dy : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConv W b) x) dy) :
HasGradAt (fun (y : Vec (N * (ic * h * w))) => G (StableHLO.batchMap N (flatConv W b) y)) x (BackLinks.cInB N W b dy)

Back through a batched conv: cInB.

theorem Proofs.GradNodeB.hasGradAt_convStrided {N ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (b : Vec oc) (x : Vec (N * (ic * (2 * h) * (2 * w)))) {G : Vec (N * (oc * h * w)) → Vec 1} {dy : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConvStride2 W b) x) dy) :
HasGradAt (fun (y : Vec (N * (ic * (2 * h) * (2 * w)))) => G (StableHLO.batchMap N (flatConvStride2 W b) y)) x (BackLinks.cStridedInB N W b dy)

Back through a batched symmetric strided conv: cStridedInB.

theorem Proofs.GradNodeB.hasGradAt_depthwise {N c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (b : Vec c) (x : Vec (N * (c * h * w))) {G : Vec (N * (c * h * w)) → Vec 1} {dy : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseFlat W b) x) dy) :
HasGradAt (fun (y : Vec (N * (c * h * w))) => G (StableHLO.batchMap N (depthwiseFlat W b) y)) x (BackLinks.dInB N W b dy)

Back through a batched depthwise: dInB.

theorem Proofs.GradNodeB.hasGradAt_depthwiseStrided {N c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (b : Vec c) (x : Vec (N * (c * (2 * h) * (2 * w)))) {G : Vec (N * (c * h * w)) → Vec 1} {dy : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseStride2Flat W b) x) dy) :
HasGradAt (fun (y : Vec (N * (c * (2 * h) * (2 * w)))) => G (StableHLO.batchMap N (depthwiseStride2Flat W b) y)) x (BackLinks.dStridedInB N W b dy)

Back through a batched symmetric strided depthwise: dStridedInB.

theorem Proofs.GradNodeB.hasGradAt_depthwiseStridedXla {N c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (b : Vec c) (x : Vec (N * (c * (2 * h) * (2 * w)))) {G : Vec (N * (c * h * w)) → Vec 1} {dy : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseStride2FlatXla W b) x) dy) :
HasGradAt (fun (y : Vec (N * (c * (2 * h) * (2 * w)))) => G (StableHLO.batchMap N (depthwiseStride2FlatXla W b) y)) x (BackLinks.dStridedXlaInB N W b dy)

Back through a batched XLA-SAME strided depthwise: dStridedXlaInB (MobileNetV2's and EfficientNet-B0's stride-2 bodies).

theorem Proofs.GradNodeB.convW_hasGradAt {N ic oc h w kH kW : ℕ} (xN cotN : String) (b : Vec oc) (x : Vec (N * (ic * h * w))) (W : Kernel4 oc ic kH kW) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConv W b) x) cot) :

Conv weight node = ∇_W G.

theorem Proofs.GradNodeB.convB_hasGradAt {N ic oc h w kH kW : ℕ} (cotN : String) (W : Kernel4 oc ic kH kW) (x : Vec (N * (ic * h * w))) (b : Vec oc) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConv W b) x) cot) :

Conv bias node = ∇_b G.

theorem Proofs.GradNodeB.flatConvStride2_weight_differentiable {ic oc h w kH kW : ℕ} (b : Vec oc) (y : Vec (ic * (2 * h) * (2 * w))) :
Differentiable ℝ fun (θ : Vec (oc * ic * kH * kW)) => flatConvStride2 (Kernel4.unflatten θ) b y
theorem Proofs.GradNodeB.flatConvStride2_bias_differentiable {ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (y : Vec (ic * (2 * h) * (2 * w))) :
Differentiable ℝ fun (θ : Vec oc) => flatConvStride2 W θ y
theorem Proofs.GradNodeB.convStridedW_hasGradAt {N ic oc h w kH kW : ℕ} (xN cotN : String) (b : Vec oc) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (W : Kernel4 oc ic kH kW) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConvStride2 W b) x) cot) :

Stride-2 (symmetric) conv weight node = ∇_W G.

theorem Proofs.GradNodeB.convStridedB_hasGradAt {N ic oc h w kH kW : ℕ} (cotN : String) (W : Kernel4 oc ic kH kW) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (b : Vec oc) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConvStride2 W b) x) cot) :

Stride-2 (symmetric) conv bias node = ∇_b G.

theorem Proofs.GradNodeB.convStridedXlaW_hasGradAt {N ic oc h w kH kW : ℕ} (xN cotN : String) (b : Vec oc) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (W : Kernel4 oc ic kH kW) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConvStride2Xla W b) x) cot) :

XLA-SAME strided conv weight node = ∇_W G (MobileNetV2's and B0's stem).

theorem Proofs.GradNodeB.convStridedXlaB_hasGradAt {N ic oc h w kH kW : ℕ} (cotN : String) (W : Kernel4 oc ic kH kW) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (b : Vec oc) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (flatConvStride2Xla W b) x) cot) :

XLA-SAME strided conv bias node = ∇_b G.

theorem Proofs.GradNodeB.depthwiseW_hasGradAt {N c h w kH kW : ℕ} (xN cotN : String) (b : Vec c) (x : Vec (N * (c * h * w))) (W : DepthwiseKernel c kH kW) {G : Vec (N * (c * h * w)) → Vec 1} {cot : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseFlat W b) x) cot) :

Depthwise weight node = ∇_W G.

theorem Proofs.GradNodeB.depthwiseB_hasGradAt {N c h w kH kW : ℕ} (cotN : String) (W : DepthwiseKernel c kH kW) (x : Vec (N * (c * h * w))) (b : Vec c) {G : Vec (N * (c * h * w)) → Vec 1} {cot : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseFlat W b) x) cot) :

Depthwise bias node = ∇_b G.

theorem Proofs.GradNodeB.depthwiseStridedW_hasGradAt {N c h w kH kW : ℕ} (xN cotN : String) (b : Vec c) (x : Vec (N * (c * (2 * h) * (2 * w)))) (W : DepthwiseKernel c kH kW) {G : Vec (N * (c * h * w)) → Vec 1} {cot : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseStride2Flat W b) x) cot) :

Symmetric strided depthwise weight node = ∇_W G (MobileNetV4's dw_mid at its three downsampling rows).

theorem Proofs.GradNodeB.depthwiseStridedXlaW_hasGradAt {N c h w kH kW : ℕ} (xN cotN : String) (b : Vec c) (x : Vec (N * (c * (2 * h) * (2 * w)))) (W : DepthwiseKernel c kH kW) {G : Vec (N * (c * h * w)) → Vec 1} {cot : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseStride2FlatXla W b) x) cot) :

XLA-SAME strided depthwise weight node = ∇_W G.

theorem Proofs.GradNodeB.depthwiseStridedXlaB_hasGradAt {N c h w kH kW : ℕ} (cotN : String) (W : DepthwiseKernel c kH kW) (x : Vec (N * (c * (2 * h) * (2 * w)))) (b : Vec c) {G : Vec (N * (c * h * w)) → Vec 1} {cot : Vec (N * (c * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (depthwiseStride2FlatXla W b) x) cot) :

XLA-SAME strided depthwise bias node = ∇_b G.

theorem Proofs.GradNodeB.denseW_hasGradAt {N a c : ℕ} (xN cotN : String) (x : Vec (N * a)) (W : Mat a c) (b : Vec c) {G : Vec (N * c) → Vec 1} {cot : Vec (N * c)} (hG : HasGradAt G (StableHLO.batchMap N (dense W b) x) cot) :

Dense weight node = ∇_W G, at the flat Mat.flatten index finProdFinEquiv (i, j).

theorem Proofs.GradNodeB.dense_bias_differentiable {a c : ℕ} (W : Mat a c) (x : Vec a) :
Differentiable ℝ fun (b' : Vec c) => dense W b' x
theorem Proofs.GradNodeB.denseB_hasGradAt {N a c : ℕ} (cotN : String) (W : Mat a c) (x₀ : Vec a) (x : Vec (N * a)) (b : Vec c) {G : Vec (N * c) → Vec 1} {cot : Vec (N * c)} (hG : HasGradAt G (StableHLO.batchMap N (dense W b) x) cot) :

Dense bias node = ∇_b G. The node's statement carries one activation x₀ for every example; the bias Jacobian is the identity whatever the activation, so any x₀ serves.

noncomputable def Proofs.GradNodeB.bnLAPerm (N oc h w : ℕ) :
Fin (N * (oc * h * w)) ≃ Fin (oc * (N * (h * w)))

The permutation bnBatchLA reads its per-channel core through: network index J ↦ the [C, N·H·W] cell bnchwBackIdx (J at the mul_assoc cast). bnBatchLA … v J is bnPerChannelFlat … (bnchwFwd … (reassocB … v)) (bnLAPerm … J) by rfl.

Equations
  • One or more equations did not get rendered due to their size.
Instances For
    theorem Proofs.GradNodeB.bnPerChannelFlat_gamma_differentiable (oc m : ℕ) (ε : ℝ) (β : Vec oc) (v : Vec (oc * m)) :
    Differentiable ℝ fun (γ' : Vec oc) => bnPerChannelFlat oc m ε γ' β v
    theorem Proofs.GradNodeB.bnPerChannelFlat_beta_differentiable (oc m : ℕ) (ε : ℝ) (γ : Vec oc) (v : Vec (oc * m)) :
    Differentiable ℝ fun (β' : Vec oc) => bnPerChannelFlat oc m ε γ β' v
    theorem Proofs.GradNodeB.bnGamma_hasGradAt {N oc h w : ℕ} (vN epsStr cotN : String) (ε : ℝ) (γ β : Vec oc) (v : Vec (N * (oc * h * w))) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.bnBatchLA N oc h w ε γ β v) cot) :
    HasGradAt (fun (θ : Vec oc) => G (StableHLO.bnBatchLA N oc h w ε θ β v)) γ (StableHLO.den (StableHLO.SHlo.bnGammaGradB vN epsStr ε (BackLinks.reassocB N oc h w v) (StableHLO.SHlo.operand cotN (BackLinks.reassocB N oc h w cot))))

    BatchNorm γ node = ∇_γ G, at the reassocB index the render's node reads.

    theorem Proofs.GradNodeB.bnBeta_hasGradAt {N oc h w : ℕ} (cotN : String) (ε : ℝ) (γ β : Vec oc) (v : Vec (N * (oc * h * w))) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.bnBatchLA N oc h w ε γ β v) cot) :
    HasGradAt (fun (θ : Vec oc) => G (StableHLO.bnBatchLA N oc h w ε γ θ v)) β (StableHLO.den (StableHLO.SHlo.operand cotN (BackLinks.reassocB N oc h w cot)).bnBetaGradB)

    BatchNorm β node = ∇_β G, at the reassocB index.

    theorem Proofs.GradNodeB.pdiv_bias_of_split {a oc h w : ℕ} (per : Vec oc → Vec a → Vec (oc * h * w)) (hsplit : ∀ (θ : Vec oc) (y : Vec a), per θ y = fun (k : Fin (oc * h * w)) => per 0 y k + broadcastFlat oc h w θ k) (y : Vec a) (b : Vec oc) (o : Fin oc) (j : Fin (oc * h * w)) :
    pdiv (fun (θ : Vec oc) => per θ y) b o j = if o = flatChannel oc h w j then 1 else 0

    A channel-broadcast bias's Jacobian is the channel indicator.

    theorem Proofs.GradNodeB.biasBeta_hasGradAt {N a oc h w : ℕ} (cotN : String) (per : Vec oc → Vec a → Vec (oc * h * w)) (hsplit : ∀ (θ : Vec oc) (y : Vec a), per θ y = fun (k : Fin (oc * h * w)) => per 0 y k + broadcastFlat oc h w θ k) (x : Vec (N * a)) (b : Vec oc) {G : Vec (N * (oc * h * w)) → Vec 1} {cot : Vec (N * (oc * h * w))} (hG : HasGradAt G (StableHLO.batchMap N (per b) x) cot) :
    HasGradAt (fun (θ : Vec oc) => G (StableHLO.batchMap N (per θ) x)) b (StableHLO.den (StableHLO.SHlo.operand cotN (BackLinks.reassocB N oc h w cot)).bnBetaGradB)

    A channel-broadcast bias, read by the β node, = ∇_b G. For any per-example op whose bias enters as per 0 y + broadcast b, the emitted bnBetaGradB on the op's output cotangent is the loss gradient in the bias.

    theorem Proofs.GradNodeB.flatConv_bias_split {ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (θ : Vec oc) (y : Vec (ic * h * w)) :
    flatConv W θ y = fun (k : Fin (oc * h * w)) => flatConv W 0 y k + broadcastFlat oc h w θ k

    A conv's bias is a channel broadcast.

    theorem Proofs.GradNodeB.depthwiseFlat_bias_split {c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (θ : Vec c) (y : Vec (c * h * w)) :
    depthwiseFlat W θ y = fun (k : Fin (c * h * w)) => depthwiseFlat W 0 y k + broadcastFlat c h w θ k

    A depthwise conv's bias is a channel broadcast.

    theorem Proofs.GradNodeB.depthwiseStride2Flat_bias_split {c h w kH kW : ℕ} (W : DepthwiseKernel c kH kW) (θ : Vec c) (y : Vec (c * (2 * h) * (2 * w))) :
    depthwiseStride2Flat W θ y = fun (k : Fin (c * h * w)) => depthwiseStride2Flat W 0 y k + broadcastFlat c h w θ k

    A symmetric strided depthwise conv's bias is a channel broadcast: decimation keeps channels.

    theorem Proofs.GradNodeB.flatConvStride2Xla_bias_split {ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (θ : Vec oc) (y : Vec (ic * (2 * h) * (2 * w))) :
    flatConvStride2Xla W θ y = fun (k : Fin (oc * h * w)) => flatConvStride2Xla W 0 y k + broadcastFlat oc h w θ k

    An XLA-SAME strided conv's bias is a channel broadcast.

    theorem Proofs.GradNodeB.flatConvStride4_bias_split {ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (θ : Vec oc) (y : Vec (ic * (2 * (2 * h)) * (2 * (2 * w)))) :
    flatConvStride4 W θ y = fun (k : Fin (oc * h * w)) => flatConvStride4 W 0 y k + broadcastFlat oc h w θ k

    The stride-4 patchify conv's bias is a channel broadcast: both decimations keep channels.

    theorem Proofs.GradNodeB.pdiv_flatConvStride4_bias_eq_conv2d {ic oc h w kH kW : ℕ} (W : Kernel4 oc ic kH kW) (x : Vec (ic * (2 * (2 * h)) * (2 * (2 * w)))) (y : Tensor3 ic h w) (b : Vec oc) (o : Fin oc) (j : Fin (oc * h * w)) :
    pdiv (fun (b' : Vec oc) => flatConvStride4 W b' x) b o j = pdiv (fun (b' : Vec oc) => (conv2d W b' y).flatten) b o j

    The stride-4 stem's bias Jacobian is a stride-1 conv's, at any input. Both are the channel indicator (pdiv_bias_of_split), so a node the render emits as a stride-1 bias reduce at the output resolution is the patchify conv's bias gradient at the real image x.