Data parallelism: the gradient mean, and what function trained #
Every *dp* artifact in verified_mlir/ is ONE program run on R replicas. Per parameter,
after the gradient node and before the optimizer tail, emitGradAllReduce emits
stablehlo.all_reduce(add) over replica_groups = [[0..R-1]] followed by a divide by R.
That text is emitted OUTSIDE the SHlo AST and is a declared trusted carve-out, so every tie
in the repo — r34_net_tiedB, mnv2_net_tiedB, efficientnet_net_tiedG — is stated at the
PER-REPLICA gradient node and says so in its own header. This file is the ℝ-level half of
closing that disclaimer: piece 1 of planning/archive/proofs_tier_to_paper_nets.md §4d.
What is proved #
lossGrad_meanLoss— the gradient of a mean of losses is the mean of their gradients. Linearity ofpdiv, and the only analysis in the file.- ⭐
dpMeanGrad_eq_grad_meanLoss— the all-reduced mean gradient(1/R) Σ_r g_rIS∇((1/R) Σ_r L_r). This is what the collective computes, named as a gradient of something. It holds for any per-replica losses whatever, batch-coupled or not. - ⭐⭐
meanLoss_shard/dpMeanGrad_eq_globalBatchGrad_of_perExample— when the replica loss is the MEAN OVER ITS SLICE of a per-example loss (no batch coupling: ConvNeXt, ViT, every inference-BN forward), the mean of theRreplica losses is literally the mean over the global batch ofR·Nexamples, so the DP step is the single-device step at batchR·N. Stated at an arbitrary sharde : Fin R × Fin N ≃ Fin (R*N)— WHICH examples land on which replica does not enter, only that together they are the batch. The contiguous shard the DP shim cuts is thefinProdFinEquivinstance. - ⛔
dpMeanGrad_ne_globalBatchGrad— and for a batch-coupled loss that is FALSE, at an explicit two-replica witness. A training-mode BatchNorm reads a nonlinear function of its own slice's statistics;bnToyLossis the smallest thing with that shape. So a batch-BN net trained data-parallel did NOT minimise the batch-R·Nloss, anddpMeanGrad_eq_grad_meanLossis the honest statement of what it did minimise.dpToyShard_eq_batchis the lemma that makes it a witness rather than a comparison of two unrelated datasets. - ⭐⭐
dpIterate_lockstep/dpIterate_eq_meanLossTrain— the lockstep induction. Identical initial parameters and an identical (all-reduced) update keep theRstate copies equal at every step, sonsteps of theR-replica system arensteps of ORDINARY single-device training on the mean loss. That is the propertyVerifiedTrain.leanrelies on when it checkpoints from replica 0.
What is NOT claimed #
⚠ Nothing here is about the emitted all_reduce. den (allReduceMeanF R g) = (1/R) Σ_r den (g r)
is §4d piece 2, an SHlo constructor with a den, a pretty and a parser case, and it waits on
4c's batched chains. Until it lands a tie composes with these lemmas only through the reader.
⚠ BatchNorm statistics are per replica. Nothing all-reduces μ/var, which is why N in the
batch-BN tiers is the PER-CARD batch and why dpMeanGrad_ne_globalBatchGrad is not a curiosity.
⚠ That every replica starts from the same parameters, that the checkpoint is read from one
replica, and that replica_groups names all R devices are the DRIVER's (VerifiedTrain.lean,
ffi/pjrt_ffi.c, PJRT_REPLICAS), not theorems here. dpIterate_lockstep takes the shared start
as a HYPOTHESIS — it says what follows from it, not that the driver establishes it. The
*-dp-check gates are the empirical evidence for that half.
⭐ WHICH examples land where is the one piece of that which does become a theorem, and only for
half the nets. dpMeanGrad_eq_globalBatchGrad_of_perExample binds the shard, so for a net with
no batch coupling the partition is provably irrelevant and all the driver has to get right is that
the slices cover the batch. ⛔ For a batch-BN net the partition changes the function, and nothing
here recovers it.
pdiv_const_smul, the scalar-multiple rule this file needed, is Tensor.lean's (moved 2026-09-08).
A scalar loss's gradient, in the Vec 1-lifted spelling pdiv reads. SmoothedLossCot
writes this out at every use; it is worth a name here because the whole file is about
rearranging sums of them.
Equations
- Proofs.lossGrad L θ i = Proofs.pdiv (fun (θ' : Proofs.Vec P) (x : Fin 1) => L θ') θ i 0
Instances For
Differentiability of a scalar loss, in the same lifted spelling.
Equations
- Proofs.LossDifferentiableAt L θ = DifferentiableAt ℝ (fun (θ' : Proofs.Vec P) (x : Fin 1) => L θ') θ
Instances For
A mean of losses. Used at two different index meanings and deliberately ONE definition:
over replicas it is the function data parallelism minimises, and over examples it is the
batch mean a single device minimises. That those coincide under no batch coupling is
meanLoss_shard, and it is the content of §4d piece 1.
Equations
- Proofs.meanLoss L θ = 1 / ↑M * ∑ m : Fin M, L m θ
Instances For
A mean of losses is differentiable where its summands are.
⭐ The gradient of a mean is the mean of the gradients. Linearity of pdiv, and the
only analysis in this file: pdiv_const_smul pulls the 1/M out and pdiv_finset_sum
splits the sum. Everything downstream is this lemma read at two index meanings.
⭐⭐ What the collective computes, named as a gradient. The all-reduced mean of the R
per-replica gradients IS the gradient of the mean of the R per-replica losses.
No hypothesis on the losses beyond differentiability — in particular this holds for a
training-mode BatchNorm net, where each L r genuinely depends on the whole of replica
r's slice. That is the point: it names what a data-parallel run minimises without
claiming that function is the global-batch loss (dpMeanGrad_ne_globalBatchGrad).
⭐⭐ Sharding a per-example loss is invisible to the mean. If replica r's loss is the
mean over its own N examples, then the mean of the R replica losses is the mean over
all R·N examples — as FUNCTIONS, before any derivative is taken.
Stated at an arbitrary e, so which examples land on which replica does not enter: the
contiguous slices the DP shim cuts (finProdFinEquiv), the interleave ds.shard produces
for the sharded producers, and any other partition all give the same theorem.
⭐⭐ For a net with no batch coupling the data-parallel step IS the single-device step at
the global batch R·N. meanLoss_shard under lossGrad.
"No batch coupling" is the hypothesis's shape, not a side condition: it is spelled by
giving replica r the loss meanLoss (fun n => ℓ (e (r, n))) — a mean of per-example
terms — which a training-mode BatchNorm net's replica loss is not.
The contiguous shard — replica r owns examples [N·r, N·r + N) — as an instance. This is
the split VerifiedTrain.lean's DP path cuts (elems / replicas).
A loss that reads a NONLINEAR function of its own slice's batch statistic — the shape training-mode BatchNorm gives every replica loss in this repo, reduced to the smallest thing that still has it. Linear in the parameter, so the gradient is a constant and the arithmetic is visible; QUADRATIC in the slice mean, which is the coupling.
Equations
- Proofs.bnToyLoss xs θ = Proofs.sliceMean xs ^ 2 * θ 0
Instances For
Replica r's slice in the witness: one example, of value 2r.
Equations
- Proofs.dpToyShard r x✝ = 2 * ↑↑r
Instances For
The global batch those two slices make: {0, 2}, whose mean is 1.
Equations
- Proofs.dpToyBatch k = 2 * ↑↑k
Instances For
⚠ The two shards ARE the global batch, under the contiguous split — replica r owns
example r. Without this the witness below would be comparing two unrelated datasets and
would prove nothing; a comparison against a re-derivation tests the re-derivation.
⛔⛔ With batch coupling the previous section is FALSE, and here is the witness.
Two replicas, one example each — slices {0} and {2}, global batch {0, 2}. The
data-parallel mean gradient is (0² + 2²)/2 = 2; the gradient of the global-batch loss is
1² = 1.
So a training-BN net trained data-parallel did NOT take a step on the batch-R·N loss, at
any learning rate and however small the gradients. What it took a step on is the mean of
the R per-replica batch-BN losses, which is dpMeanGrad_eq_grad_meanLoss — a different
function, and the only honest answer to "what trained". ⚠ The witness needs no BatchNorm:
ANY nonlinear read of a per-slice statistic separates the two, which is why the split is
structural rather than a property of the normalisation's formula.
⭐⭐ The positive twin of dpMeanGrad_ne_globalBatchGrad: with SYNCHRONISED statistics
the data-parallel step IS the single-device step at the global batch R·N.
Write the global-batch gradient as a mean of per-example terms, (1/(R·N)) Σ_m c m — the
shared-weight batch sum every *GradB node denotes, at the cotangent the global backward
delivers to example m. Under sync-BN each c m depends on the WHOLE batch through the
all-reduced statistics; that is allowed, and it is what separates this from
dpMeanGrad_eq_globalBatchGrad_of_perExample, whose c m had to be a per-example loss's
own gradient. What sync-BN buys (P1–P3, PerChannelBN.lean / DataParallelSync.lean) is
that replica r's backward is the shard-r block of the global one, so replica r's
gradient node is (1/N) Σ_n c (e (r, n)) — ITS examples' terms of the global sum. Then the
collective's mean over R of those is the global mean: meanLoss_shard's arithmetic, for
vectors.
⛔ Per-replica BatchNorm fails exactly here: replica r's cotangents are then NOT the
shard-r block of any global backward, and the witness above is the two-example case.
The contiguous shard — replica r owns examples [N·r, N·r + N) — as an instance, the
split VerifiedTrain.lean's DP path cuts.
One data-parallel step, as shipped. Every replica computes its LOCAL gradient from its OWN state copy and its own slice; the collective averages them; every replica applies the same optimizer tail to its own state.
S is the replica's whole state — parameters and optimizer moments — because the tail is
stateful (Adam's two moments, momentum's velocity, EMA's shadow). Only the gradient is
all-reduced, which is exactly what emitGradAllReduce emits.
Equations
- Proofs.dpStep grad tail st r = tail (st r) (Proofs.dpMean fun (s : Fin R) => grad s (st s))
Instances For
The single-device step the R replicas are running in lockstep: one state, and the
DP-mean gradient.
Equations
- Proofs.dpSingleStep grad tail st = tail st (Proofs.dpMean fun (s : Fin R) => grad s st)
Instances For
From a shared state, one DP step lands every replica on the same state again — the induction
step, and it is definitional: the all-reduced gradient does not depend on r, and neither
does the state the tail is applied to.
⭐⭐ The lockstep induction. Identical initial states and an identical (all-reduced)
update keep the R copies equal at every step, so n steps of the R-replica system are
n steps of the single-device one. This is the property VerifiedTrain.lean relies on
when it checkpoints from replica 0 — the checkpoint is not "replica 0's answer", it is
every replica's.
⚠ The shared start is a HYPOTHESIS (the fun _ => st on the left). That the driver
broadcasts it, and that replica_groups names all R devices, is calling logic and the
*-dp-check gates are its evidence.
⭐⭐ What function trained, in one statement. When each replica's gradient is the
certified gradient of its own loss, one data-parallel step is one ordinary step on the
MEAN of the R per-replica losses.
⭐⭐ n steps of R replicas ARE n steps of single-device training on the mean loss.
The lockstep induction and the gradient mean, composed — the closing statement of §4d
piece 1, and the one that answers the disclaimer every tie in the repo carries.
⚠ Differentiability is asked for at EVERY point, not just at θ, because the trajectory
passes through states the statement cannot name. For a relu net that is the one place this
file is stronger than it needs to be: the honest weakening is differentiability along the
trajectory, and it costs a mutual induction the payoff does not justify.