Imports
24.5. Relabel-to-Front
This section completes the push-relabel maximum-flow analysis begun in
CLRSLean.FourthEdition.Chapter_24.Section_24_4_Push_Relabel. There we
established the preflow model, the height function, and the two local
operations CLRS.Chapter26.push and CLRS.Chapter26.relabel, together
with the correctness certificate CLRS.Chapter26.maximal_of_no_overflow.
Here we count the operations.
The generic push-relabel algorithm repeatedly picks an overflowing vertex and
applies a push or a relabel. A run of the algorithm is a sequence of basic
operations (BasicOp), each either a relabel of an overflowing vertex or a
push from an overflowing vertex along an admissible edge. We count relabels,
saturating pushes, and nonsaturating pushes over a Run, and prove the three
CLRS counting bounds:
Main results:
-
BasicOp,Run: a single basic operation and a length-nrun of the generic algorithm. -
Run.height_mono: heights are nondecreasing across a run. -
Run.relabel_count_bound: at most2|V|²relabel operations total. -
Run.saturating_push_count_bound: at mostO(|V|·|E|)saturating pushes. -
Run.nonsaturating_push_count_bound: at mostO(|V|²(|V|+|E|))nonsaturating pushes, via the potentialΦ = Σ_{overflowing u} h(u). -
Run.generic_step_count_bound: the combinedO(V²E)bound on the number of basic operations.
These bounds hold for any run of the generic algorithm, so they apply to the
relabel-to-front schedule in particular. The relabel-to-front schedule is
stronger: its DISCHARGE procedure walks a per-vertex neighbor list and, on
relabeling a vertex, moves it to the front of the list L. That per-vertex
list discipline gives the sharper O(V³) bound:
-
RelabelToFrontRun: a run satisfying the discharge discipline (between two relabel operations, no vertex is the source of more than one nonsaturating push). -
RelabelToFrontRun.nonsaturating_push_count_bound: at most|V|·(relabels + 1)nonsaturating pushes, i.e.O(V³). -
RelabelToFrontRun.step_count_bound_V3: the relabel-to-front algorithm performs at most9|V|³basic operations.
Notation conventions used in this section:
-
φ: a preflow on the networkG -
h: a height functionV → ℕ -
V(asFintype.card V) : the number of vertices -
E(asnumEdges G) : the number of directed positive-capacity edges
set_option autoImplicit truenamespace CLRSnamespace Chapter26universe uopen Finset Classicalopen scoped BigOperatorsvariable {V : Type u} [Fintype V] [DecidableEq V] {G : FlowNetwork V}The number of edges
The set of directed edges (u,v) with positive capacity.
noncomputable def edgeSet (G : FlowNetwork V) : Finset (V × V) :=
(Finset.univ : Finset (V × V)).filter (fun e => 0 < G.c e.1 e.2)The number of directed edges (positive-capacity pairs).
noncomputable def numEdges (G : FlowNetwork V) : ℕ := (edgeSet G).cardBasic operations and runs
A single basic operation of the generic push-relabel algorithm: either a
relabel of an overflowing vertex u, or a push from an overflowing u along an
admissible residual edge (u,v). The preflow φ and height h are the state
before the operation.
inductive BasicOp (V : Type u) [Fintype V] [DecidableEq V] (G : FlowNetwork V) : Type u where
| relabel (φ : Preflow V G) (h : V → ℕ) (u : V) (hoverflow : φ.isOverflowing u)
(hres : ∃ v : V, φ.residualEdge u v)
(hpre : ∀ v, φ.residualEdge u v → h u ≤ h v) : BasicOp V G
| push (φ : Preflow V G) (h : V → ℕ) (u v : V) (hoverflow : φ.isOverflowing u)
(hres : φ.residualEdge u v) (hadm : h u = h v + 1) : BasicOp V Gnamespace BasicOpThe preflow before the operation.
The height function before the operation.
The preflow after the operation.
noncomputable def resultφ : BasicOp V G → Preflow V G
| .relabel φ _ _ _ _ _ => φ
| .push φ _ u v hoverflow hres _ => Chapter26.push φ u v hoverflow.1 hresThe height function after the operation.
noncomputable def resulth : BasicOp V G → V → ℕ
| .relabel φ h u _ hres _ => Chapter26.relabel φ h u hres
| .push _ h _ _ _ _ _ => hWhether the operation is a relabel.
Whether the operation is a relabel of the specific vertex u.
def relabelOf (u : V) : BasicOp V G → Bool
| .relabel _ _ w _ _ _ => decide (w = u)
| .push .. => false
Whether the operation is a push along the specific edge (u,v).
def pushOn (u v : V) : BasicOp V G → Bool
| .push _ _ a b _ _ _ => decide (a = u ∧ b = v)
| .relabel .. => false
Whether the operation is a saturating push (the residual capacity of the
edge is exhausted, i.e. cf(u,v) ≤ e(u) so δ = cf(u,v)).
noncomputable def isSaturatingPush : BasicOp V G → Bool
| .push φ _ u v _ _ _ => decide (φ.residualCapacity u v ≤ φ.excess u)
| .relabel .. => false
Whether the operation is a nonsaturating push (the excess is exhausted,
i.e. e(u) < cf(u,v) so δ = e(u)).
noncomputable def isNonsaturatingPush : BasicOp V G → Bool
| .push φ _ u v _ _ _ => decide (φ.excess u < φ.residualCapacity u v)
| .relabel .. => false
A saturating push along the specific edge (u,v).
noncomputable def saturatingPushOn (u v : V) : BasicOp V G → Bool
| .push φ _ a b _ _ _ => decide (a = u ∧ b = v ∧ φ.residualCapacity a b ≤ φ.excess a)
| .relabel .. => falseA single operation is exactly one of relabel, saturating push, or nonsaturating push.
lemma classification (o : BasicOp V G) :
(if o.isRelabel then 1 else 0) + (if o.isSaturatingPush then 1 else 0) +
(if o.isNonsaturatingPush then 1 else 0) = 1 := by
cases o with
| relabel φ h u ho hres hpre =>
simp [isRelabel, isSaturatingPush, isNonsaturatingPush]
| push φ h u v ho hres hadm =>
by_cases hsat : φ.residualCapacity u v ≤ φ.excess u
· simp [isRelabel, isSaturatingPush, isNonsaturatingPush, hsat]
· have hlt : φ.excess u < φ.residualCapacity u v := lt_of_not_ge hsat
simp [isRelabel, isSaturatingPush, isNonsaturatingPush, hsat, hlt]Relabels are characterized by the vertex they relabel.
lemma sum_relabelOf_eq_isRelabel (o : BasicOp V G) :
(∑ u : V, (if o.relabelOf u then 1 else 0 : ℕ)) = (if o.isRelabel then 1 else 0 : ℕ) := by
cases o with
| relabel φ h w ho hres hpre =>
simp only [relabelOf, isRelabel]
rw [Finset.sum_eq_single w]
· simp
· intro b _ hbw
simp [hbw.symm]
· intro hw
simp at hw
| push φ h u v ho hres hadm =>
simp [relabelOf, isRelabel]Saturating pushes are characterized by the edge they saturate.
lemma sum_saturatingPushOn_eq (o : BasicOp V G) :
(∑ e : V × V, (if o.saturatingPushOn e.1 e.2 then 1 else 0 : ℕ)) =
(if o.isSaturatingPush then 1 else 0 : ℕ) := by
cases o with
| relabel φ h u ho hres hpre =>
simp [saturatingPushOn, isSaturatingPush]
| push φ h u v ho hres hadm =>
by_cases hsat : φ.residualCapacity u v ≤ φ.excess u
· have hcard : ((Finset.univ.filter (fun e : V × V => u = e.1 ∧ v = e.2)).card) = 1 := by
have hf : (Finset.univ.filter (fun e : V × V => u = e.1 ∧ v = e.2)) = {(u, v)} := by
ext e
simp only [Finset.mem_filter, Finset.mem_univ, true_and, Finset.mem_singleton]
constructor
· intro h
exact Prod.ext h.1.symm h.2.symm
· intro h
cases h
exact ⟨rfl, rfl⟩
rw [hf]
simp
simp [saturatingPushOn, isSaturatingPush, hsat, Finset.sum_boole, hcard]
· simp [saturatingPushOn, isSaturatingPush, hsat]The height of any fixed vertex is nondecreasing across a single operation.
lemma height_mono (o : BasicOp V G) (u : V) : o.beforeh u ≤ o.resulth u := by
cases o with
| relabel φ h w ho hres hpre =>
by_cases hwu : w = u
· subst u
exact le_of_lt (relabel_height_increase φ h w hres hpre)
· simpa [beforeh, resulth, relabel_eq_of_ne φ h w hres (Ne.symm hwu)]
| push φ h u v ho hres hadm =>
simp [beforeh, resulth]A relabel operation strictly increases the height of the relabeled vertex.
lemma height_increase_of_relabel (o : BasicOp V G) {u : V} (hrel : o.relabelOf u = true) :
o.beforeh u < o.resulth u := by
cases o with
| relabel φ h w ho hres hpre =>
have hwu : w = u := by
simpa [relabelOf] using (of_decide_eq_true hrel)
subst u
exact relabel_height_increase φ h w hres hpre
| push φ h u v ho hres hadm =>
simp [relabelOf] at hrelend BasicOp
A run of the generic push-relabel algorithm: a sequence of n basic
operations with the associated preflows and height functions.
structure Run (V : Type u) [Fintype V] [DecidableEq V] (G : FlowNetwork V) (n : ℕ) where
φ : ℕ → Preflow V G
h : ℕ → V → ℕ
hvalid : ∀ i, IsValidHeight (φ i) (h i)
op : ∀ i, i < n → BasicOp V G
hop_beforeφ : ∀ i (hi : i < n), (op i hi).beforeφ = φ i
hop_beforeh : ∀ i (hi : i < n), (op i hi).beforeh = h i
hop_resultφ : ∀ i (hi : i < n), (op i hi).resultφ = φ (i + 1)
hop_resulth : ∀ i (hi : i < n), (op i hi).resulth = h (i + 1)namespace Runopen BasicOp (relabelOf pushOn saturatingPushOn isRelabel isSaturatingPush isNonsaturatingPush)
The operation at a Fin n step.
The number of relabel operations in a run.
noncomputable def numRelabels (R : Run V G n) : ℕ :=
∑ i : Fin n, (if (R.opFin i).isRelabel then 1 else 0)The number of saturating push operations in a run.
noncomputable def numSaturatingPushes (R : Run V G n) : ℕ :=
∑ i : Fin n, (if (R.opFin i).isSaturatingPush then 1 else 0)The number of nonsaturating push operations in a run.
noncomputable def numNonsaturatingPushes (R : Run V G n) : ℕ :=
∑ i : Fin n, (if (R.opFin i).isNonsaturatingPush then 1 else 0)
The number of relabel operations of a specific vertex u.
noncomputable def numRelabelsOf (R : Run V G n) (u : V) : ℕ :=
∑ i : Fin n, (if (R.opFin i).relabelOf u then 1 else 0)
The number of saturating pushes along a specific edge (u,v).
noncomputable def numSaturatingPushesOn (R : Run V G n) (u v : V) : ℕ :=
∑ i : Fin n, (if (R.opFin i).saturatingPushOn u v then 1 else 0)Height monotonicity
The height of any fixed vertex is nondecreasing across a single step.
lemma height_mono_step (R : Run V G n) (i : Fin n) (u : V) :
R.h i.1 u ≤ R.h (i.1 + 1) u := by
have hb := BasicOp.height_mono (R.opFin i) u
have hb' : (R.opFin i).beforeh u = R.h i.1 u := by
exact congrFun (R.hop_beforeh i.1 (Fin.isLt i)) u
have hr' : (R.opFin i).resulth u = R.h (i.1 + 1) u := by
exact congrFun (R.hop_resulth i.1 (Fin.isLt i)) u
rw [← hb', ← hr']
exact hb
Heights are nondecreasing across a run: i ≤ j implies h i u ≤ h j u.
lemma height_mono (R : Run V G n) {i j : ℕ} (hij : i ≤ j) (hj : j ≤ n) (u : V) :
R.h i u ≤ R.h j u := by
obtain ⟨d, rfl⟩ := Nat.exists_eq_add_of_le hij
induction d with
| zero => rfl
| succ d ih =>
have hle := ih
have hstep : R.h (i + d) u ≤ R.h (i + (d + 1)) u := by
have hi : i + d < n := by omega
simpa [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using
height_mono_step R ⟨i + d, hi⟩ u
omegaRelabel count bound
The height of u strictly increases between two distinct relabel steps of
u.
lemma height_strict_between_relabels_of (R : Run V G n) {i j : Fin n} (hij : i.1 < j.1)
{u : V} (hri : (R.opFin i).relabelOf u = true) (hrj : (R.opFin j).relabelOf u = true) :
R.h i.1 u < R.h j.1 u := by
have hinc : R.h i.1 u < R.h (i.1 + 1) u := by
have hb := BasicOp.height_increase_of_relabel (R.opFin i) hri
rw [show R.opFin i = R.op i.1 (Fin.isLt i) from rfl] at hb
rw [R.hop_beforeh i.1 (Fin.isLt i)] at hb
rw [R.hop_resulth i.1 (Fin.isLt i)] at hb
exact hb
have hmono : R.h (i.1 + 1) u ≤ R.h j.1 u := by
apply height_mono R
· omega
· omega
omega
Each vertex is relabeled at most 2|V| times.
theorem relabel_count_bound_of (R : Run V G n) (u : V) :
numRelabelsOf R u ≤ 2 * Fintype.card V := by
classical
let S : Finset (Fin n) :=
(Finset.univ : Finset (Fin n)).filter (fun i => (R.opFin i).relabelOf u = true)
have hcard : numRelabelsOf R u = S.card := by
rw [numRelabelsOf]
rw [Finset.sum_boole]
rfl
rw [hcard]
let f : Fin n → ℕ := fun i => R.h i.1 u
have hf_inj : Set.InjOn f (↑S : Set (Fin n)) := by
intro a ha b hb hfab
have ha' : (R.opFin a).relabelOf u = true := (Finset.mem_filter.mp ha).2
have hb' : (R.opFin b).relabelOf u = true := (Finset.mem_filter.mp hb).2
apply Fin.ext
apply le_antisymm
· by_contra hgt
have hlt : b.1 < a.1 := by omega
have hst := height_strict_between_relabels_of R hlt hb' ha'
unfold f at hfab
omega
· by_contra hgt
have hlt : a.1 < b.1 := by omega
have hst := height_strict_between_relabels_of R hlt ha' hb'
unfold f at hfab
omega
have hf_range : ∀ a : Fin n, a ∈ S → f a < 2 * Fintype.card V := by
intro a ha
have hrel' : (R.opFin a).relabelOf u = true := (Finset.mem_filter.mp ha).2
have hoverflow : (R.φ a.1).isOverflowing u := by
cases hop : R.opFin a with
| relabel φ h w ho hres hpre =>
have hwu : w = u := of_decide_eq_true (by simpa [relabelOf, hop] using hrel')
have hbφ : φ = R.φ a.1 := by
have h : (R.opFin a).beforeφ = R.φ a.1 := R.hop_beforeφ a.1 (Fin.isLt a)
simpa [BasicOp.beforeφ, hop] using h
subst u
simpa [hbφ] using ho
| push φ h u' v ho hres hadm =>
simp [relabelOf, hop] at hrel'
have hle := height_le_of_overflowing (R.φ a.1) (R.h a.1) (R.hvalid a.1) u
hoverflow.1 hoverflow.2.2
have hV : 0 < Fintype.card V := Fintype.card_pos_iff.mpr ⟨G.s⟩
unfold f
omega
calc
S.card = (S.image f).card := by
exact (Finset.card_image_iff.mpr hf_inj).symm
_ ≤ (Finset.range (2 * Fintype.card V)).card := by
apply Finset.card_le_card
intro x hx
rcases Finset.mem_image.mp hx with ⟨a, ha, rfl⟩
have hlt : f a < 2 * Fintype.card V := hf_range a (by simpa using ha)
exact Finset.mem_range.mpr hlt
_ = 2 * Fintype.card V := by simp
The total number of relabel operations is at most 2|V|².
theorem relabel_count_bound (R : Run V G n) :
numRelabels R ≤ 2 * Fintype.card V * Fintype.card V := by
have hsum : numRelabels R = ∑ u : V, numRelabelsOf R u := by
calc
numRelabels R = ∑ i : Fin n, (if (R.opFin i).isRelabel then 1 else 0) := rfl
_ = ∑ i : Fin n, ∑ u : V, (if (R.opFin i).relabelOf u then 1 else 0) := by
apply Finset.sum_congr rfl
intro i hi
exact (BasicOp.sum_relabelOf_eq_isRelabel (R.opFin i)).symm
_ = ∑ u : V, ∑ i : Fin n, (if (R.opFin i).relabelOf u then 1 else 0) := by
rw [Finset.sum_comm]
_ = ∑ u : V, numRelabelsOf R u := rfl
calc
numRelabels R = ∑ u : V, numRelabelsOf R u := hsum
_ ≤ ∑ u : V, (2 * Fintype.card V) := by
apply Finset.sum_le_sum
intro u _
exact relabel_count_bound_of R u
_ = Fintype.card V * (2 * Fintype.card V) := by simp [Finset.sum_const, nsmul_eq_mul]
_ = 2 * Fintype.card V * Fintype.card V := by rw [mul_comm]Saturating push count bound
The exact effect of pushBy on residual capacity.
lemma pushBy_residualCapacity_eq (φ : Preflow V G) (u v : V) (huv : u ≠ v) (δ : ℝ)
(hδ_nonneg : 0 ≤ δ) (hδ_le_excess : δ ≤ φ.excess u)
(hδ_le_residual : δ ≤ φ.residualCapacity u v) (a b : V) :
(pushBy φ u v huv δ hδ_nonneg hδ_le_excess hδ_le_residual).residualCapacity a b =
φ.residualCapacity a b - (if a = u ∧ b = v then δ else 0) +
(if a = v ∧ b = u then δ else 0) := by
unfold pushBy Preflow.residualCapacity Flow.edgeDelta
ring
The exact effect of push on residual capacity.
lemma push_residualCapacity_eq (φ : Preflow V G) (u v : V) (hu_ne_s : u ≠ G.s)
(hres : φ.residualEdge u v) (a b : V) :
(push φ u v hu_ne_s hres).residualCapacity a b =
φ.residualCapacity a b -
(if a = u ∧ b = v then min (φ.excess u) (φ.residualCapacity u v) else 0) +
(if a = v ∧ b = u then min (φ.excess u) (φ.residualCapacity u v) else 0) := by
unfold push
exact pushBy_residualCapacity_eq φ u v (residualEdge_ne φ hres)
(min (φ.excess u) (φ.residualCapacity u v))
(le_min (φ.hexcess_nonneg u hu_ne_s) (le_of_lt hres)) (min_le_left _ _)
(min_le_right _ _) a b
A push that does not run along the reverse edge (v,u) cannot increase the
residual capacity of (u,v).
lemma residualCapacity_le_of_not_reverse_push (φ : Preflow V G) (a b : V) (hu_ne_s : a ≠ G.s)
(hres : φ.residualEdge a b) (u v : V) (hnot : ¬ (a = v ∧ b = u)) :
(push φ a b hu_ne_s hres).residualCapacity u v ≤ φ.residualCapacity u v := by
rw [push_residualCapacity_eq φ a b hu_ne_s hres u v]
have hno_add : (if u = b ∧ v = a then min (φ.excess a) (φ.residualCapacity a b) else 0) = 0 := by
by_cases h : u = b ∧ v = a
· have hrev : a = v ∧ b = u := ⟨h.2.symm, h.1.symm⟩
exact (hnot hrev).elim
· simp [h]
rw [hno_add]
by_cases h : u = a ∧ v = b
· rcases h with ⟨hau, hbv⟩
subst u
subst v
have hmin_nonneg : 0 ≤ min (φ.excess a) (φ.residualCapacity a b) :=
le_min (φ.hexcess_nonneg a hu_ne_s) (le_of_lt hres)
simp only [if_true, and_self]
linarith
· simp [h]
A residual capacity that starts at 0 and becomes positive must at some
intermediate step be increased by a push along the reverse edge.
lemma exists_reverse_push_of_residual_recovery (R : Run V G n) {u v : V} {i j : ℕ}
(hij : i + 1 ≤ j) (hj : j ≤ n) (hzero : (R.φ (i + 1)).residualCapacity u v = 0)
(hpos : (R.φ j).residualCapacity u v > 0) :
∃ k : Fin n, i + 1 ≤ k.1 ∧ k.1 < j ∧ (R.opFin k).pushOn v u = true := by
classical
have hmain := Nat.le_induction (m := i + 1)
(P := fun t _ => t ≤ n →
(R.φ (i + 1)).residualCapacity u v = 0 →
(R.φ t).residualCapacity u v > 0 →
∃ k : Fin n, i + 1 ≤ k.1 ∧ k.1 < t ∧ (R.opFin k).pushOn v u = true)
(base := by
intro _ hzero hpos
linarith)
(succ := by
intro t ht ih ht_succ_n hzero hpos
by_cases hpred : (R.φ t).residualCapacity u v > 0
· obtain ⟨k, hk1, hk2, hk3⟩ := ih (by omega) hzero hpred
exact ⟨k, hk1, by omega, hk3⟩
· have hnonpos : (R.φ t).residualCapacity u v ≤ 0 := le_of_not_gt hpred
have hpush : (R.opFin ⟨t, by omega⟩).pushOn v u = true := by
cases hop : R.op t (by omega) with
| relabel φ h w ho hres hpre =>
exfalso
have hb : R.φ t = φ := by
simpa [BasicOp.beforeφ, hop] using (R.hop_beforeφ t (by omega)).symm
have hr : R.φ (t + 1) = φ := by
simpa [BasicOp.resultφ, hop] using (R.hop_resultφ t (by omega)).symm
have hpos' : (R.φ t).residualCapacity u v > 0 := by
rw [hb, ← hr]
exact hpos
linarith
| push φ h a b ho hres hadm =>
have hstep_pos : (R.φ (t + 1)).residualCapacity u v > 0 := by
have hr : (R.op t (by omega)).resultφ = R.φ (t + 1) :=
R.hop_resultφ t (by omega)
simpa [hop, BasicOp.resultφ, hr] using hpos
by_contra hnot
have hle : (R.φ (t + 1)).residualCapacity u v ≤
(R.φ t).residualCapacity u v := by
have hb : (R.op t (by omega)).beforeφ = R.φ t :=
R.hop_beforeφ t (by omega)
have hr : (R.op t (by omega)).resultφ = R.φ (t + 1) :=
R.hop_resultφ t (by omega)
have hnot' : ¬ (a = v ∧ b = u) := by
intro hab
apply hnot
change (R.op t (by omega)).pushOn v u = true
simp [pushOn, hop, hab]
rw [← hb, ← hr]
simpa [BasicOp.resultφ, BasicOp.beforeφ, hop] using
residualCapacity_le_of_not_reverse_push φ a b ho.1 hres u v hnot'
linarith
refine ⟨⟨t, by omega⟩, ?_, ?_, hpush⟩
· exact ht
· exact Nat.lt_succ_self t)
j hij hj hzero hpos
exact hmain
The height of u grows by at least two between two saturating pushes along
(u,v).
lemma saturating_height_increase (R : Run V G n) {u v : V} {i j : Fin n} (hij : i.1 < j.1)
(hsat_i : (R.opFin i).saturatingPushOn u v = true)
(hsat_j : (R.opFin j).saturatingPushOn u v = true) :
R.h i.1 u + 2 ≤ R.h j.1 u := by
have hadm_i : R.h i.1 u = R.h i.1 v + 1 := by
cases hop : R.opFin i with
| relabel φ h w ho hres hpre =>
simp [saturatingPushOn, hop] at hsat_i
| push φ h a b ho hres hadm =>
have hle : a = u ∧ b = v ∧ φ.residualCapacity a b ≤ φ.excess a :=
of_decide_eq_true (by simpa [saturatingPushOn, hop] using hsat_i)
rcases hle with ⟨ha, hb, _⟩
have hb_h : R.h i.1 = h := by
have h' : (R.opFin i).beforeh = R.h i.1 := R.hop_beforeh i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.beforeh, hop]
subst a; subst b
rw [hb_h]
exact hadm
have hzero : (R.φ (i.1 + 1)).residualCapacity u v = 0 := by
cases hop : R.opFin i with
| relabel φ h w ho hres hpre =>
simp [saturatingPushOn, hop] at hsat_i
| push φ h a b ho hres hadm =>
have hle : a = u ∧ b = v ∧ φ.residualCapacity a b ≤ φ.excess a :=
of_decide_eq_true (by simpa [saturatingPushOn, hop] using hsat_i)
rcases hle with ⟨ha, hb, hsat⟩
have hb_φ : R.φ i.1 = φ := by
have h' : (R.opFin i).beforeφ = R.φ i.1 := R.hop_beforeφ i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.beforeφ, hop]
have hr_φ : R.φ (i.1 + 1) = (R.opFin i).resultφ := (R.hop_resultφ i.1 (Fin.isLt i)).symm
subst a; subst b
rw [hr_φ]
simp [BasicOp.resultφ, hop, push_residualCapacity_eq φ u v ho.1 hres u v, hsat,
min_eq_right hsat, residualEdge_ne φ hres]
have hpos : (R.φ j.1).residualCapacity u v > 0 := by
cases hop : R.opFin j with
| relabel φ h w ho hres hpre =>
simp [saturatingPushOn, hop] at hsat_j
| push φ h a b ho hres hadm =>
have hle : a = u ∧ b = v := by
have h := of_decide_eq_true (by simpa [saturatingPushOn, hop] using hsat_j)
exact ⟨h.1, h.2.1⟩
have hb_φ : R.φ j.1 = φ := by
have h' : (R.opFin j).beforeφ = R.φ j.1 := R.hop_beforeφ j.1 (Fin.isLt j)
rw [← h']
simp [BasicOp.beforeφ, hop]
rcases hle with ⟨ha, hb⟩
subst a; subst b
rw [hb_φ]
exact hres
obtain ⟨k, hk1, hk2, hk3⟩ := exists_reverse_push_of_residual_recovery R (by omega : i.1 + 1 ≤ j.1)
(le_of_lt (Fin.isLt j)) hzero hpos
have hadm_k : R.h k.1 v = R.h k.1 u + 1 := by
cases hop : R.op k.1 (Fin.isLt k) with
| relabel φ h w ho hres hpre =>
simp [opFin, pushOn, hop] at hk3
| push φ h a b ho hres hadm =>
have hle : a = v ∧ b = u := of_decide_eq_true (by simpa [opFin, pushOn, hop] using hk3)
have hb_h : R.h k.1 = h := by
have h' : (R.op k.1 (Fin.isLt k)).beforeh = R.h k.1 := R.hop_beforeh k.1 (Fin.isLt k)
rw [← h']
simp [BasicOp.beforeh, hop]
rcases hle with ⟨ha, hb⟩
subst a; subst b
rw [hb_h]
exact hadm
have hadm_j : R.h j.1 u = R.h j.1 v + 1 := by
cases hop : R.opFin j with
| relabel φ h w ho hres hpre =>
simp [saturatingPushOn, hop] at hsat_j
| push φ h a b ho hres hadm =>
have hle : a = u ∧ b = v := by
have h := of_decide_eq_true (by simpa [saturatingPushOn, hop] using hsat_j)
exact ⟨h.1, h.2.1⟩
have hb_h : R.h j.1 = h := by
have h' : (R.opFin j).beforeh = R.h j.1 := R.hop_beforeh j.1 (Fin.isLt j)
rw [← h']
simp [BasicOp.beforeh, hop]
rcases hle with ⟨ha, hb⟩
subst a; subst b
rw [hb_h]
exact hadm
have h_iu_ku : R.h i.1 u ≤ R.h k.1 u := height_mono R (by omega) (by omega) u
have h_kv_jv : R.h k.1 v ≤ R.h j.1 v := height_mono R (by omega) (by omega) v
omega
Each edge admits at most 2|V| saturating pushes.
theorem saturating_push_count_bound_on (R : Run V G n) (u v : V) :
numSaturatingPushesOn R u v ≤ 2 * Fintype.card V := by
classical
let S : Finset (Fin n) :=
(Finset.univ : Finset (Fin n)).filter (fun i => (R.opFin i).saturatingPushOn u v = true)
have hcard : numSaturatingPushesOn R u v = S.card := by
rw [numSaturatingPushesOn]
rw [Finset.sum_boole]
rfl
rw [hcard]
let f : Fin n → ℕ := fun i => R.h i.1 u
have hf_inj : Set.InjOn f (↑S : Set (Fin n)) := by
intro a ha b hb hfab
have ha' : (R.opFin a).saturatingPushOn u v = true := (Finset.mem_filter.mp ha).2
have hb' : (R.opFin b).saturatingPushOn u v = true := (Finset.mem_filter.mp hb).2
apply Fin.ext
apply le_antisymm
· by_contra hgt
have hlt : b.1 < a.1 := by omega
have hst := saturating_height_increase R hlt hb' ha'
unfold f at hfab
omega
· by_contra hgt
have hlt : a.1 < b.1 := by omega
have hst := saturating_height_increase R hlt ha' hb'
unfold f at hfab
omega
have hf_range : ∀ a : Fin n, a ∈ S → f a < 2 * Fintype.card V := by
intro a ha
have hsat' : (R.opFin a).saturatingPushOn u v = true := (Finset.mem_filter.mp ha).2
have hoverflow : (R.φ a.1).isOverflowing u := by
cases hop : R.opFin a with
| relabel φ h w ho hres hpre =>
simp [saturatingPushOn, hop] at hsat'
| push φ h a' b ho hres hadm =>
have hle : a' = u := by
have h := of_decide_eq_true (by simpa [saturatingPushOn, hop] using hsat')
exact h.1
have hbφ : φ = R.φ a.1 := by
have h : (R.opFin a).beforeφ = R.φ a.1 := R.hop_beforeφ a.1 (Fin.isLt a)
simpa [BasicOp.beforeφ, hop] using h
subst u
simpa [hbφ] using ho
have hle := height_le_of_overflowing (R.φ a.1) (R.h a.1) (R.hvalid a.1) u
hoverflow.1 hoverflow.2.2
have hV : 0 < Fintype.card V := Fintype.card_pos_iff.mpr ⟨G.s⟩
unfold f
omega
calc
S.card = (S.image f).card := by
exact (Finset.card_image_iff.mpr hf_inj).symm
_ ≤ (Finset.range (2 * Fintype.card V)).card := by
apply Finset.card_le_card
intro x hx
rcases Finset.mem_image.mp hx with ⟨a, ha, rfl⟩
have hlt : f a < 2 * Fintype.card V := hf_range a (by simpa using ha)
exact Finset.mem_range.mpr hlt
_ = 2 * Fintype.card V := by simp
A saturating push on (u,v) requires some direction of {u,v} to have
positive capacity: either (u,v) itself, or the reverse edge (v,u) (a
saturating push may cancel flow on the reverse edge).
lemma saturating_pushOn_imp_cap (R : Run V G n) (u v : V) :
numSaturatingPushesOn R u v = 0 ∨ 0 < G.c u v ∨ 0 < G.c v u := by
classical
by_cases hcap_uv : 0 < G.c u v
· exact Or.inr (Or.inl hcap_uv)
· by_cases hcap_vu : 0 < G.c v u
· exact Or.inr (Or.inr hcap_vu)
· left
rw [numSaturatingPushesOn]
rw [Finset.sum_eq_zero]
intro i _
by_cases hsat : (R.opFin i).saturatingPushOn u v = true
· have hres : (R.φ i.1).residualEdge u v := by
cases hop : R.opFin i with
| relabel φ h w ho hres hpre =>
simp [saturatingPushOn, hop] at hsat
| push φ h a b ho hres hadm =>
have hle : a = u ∧ b = v := by
have h := of_decide_eq_true (by simpa [saturatingPushOn, hop] using hsat)
exact ⟨h.1, h.2.1⟩
have hbφ : φ = R.φ i.1 := by
have h : (R.opFin i).beforeφ = R.φ i.1 := R.hop_beforeφ i.1 (Fin.isLt i)
simpa [BasicOp.beforeφ, hop] using h
rcases hle with ⟨ha, hb⟩
subst a; subst b
simpa [hbφ] using hres
have hc_uv_nonneg : 0 ≤ G.c u v := G.hc_nonneg u v
have hc_vu_nonneg : 0 ≤ G.c v u := G.hc_nonneg v u
have hc_uv_zero : G.c u v = 0 := le_antisymm (le_of_not_gt hcap_uv) hc_uv_nonneg
have hc_vu_zero : G.c v u = 0 := le_antisymm (le_of_not_gt hcap_vu) hc_vu_nonneg
have hcap_vu : (R.φ i.1).f v u ≤ G.c v u := (R.φ i.1).hcapacity v u
have hskew : (R.φ i.1).f u v = -((R.φ i.1).f v u) := (R.φ i.1).hskew_symm u v
unfold Preflow.residualEdge Preflow.residualCapacity at hres
linarith
· simp [hsat]
The sum of f over univ, restricted by membership in s, equals the sum
over s.
private lemma sum_filter_univ_eq {s : Finset (V × V)} {f : V × V → ℕ} :
(∑ x : V × V, (if x ∈ s then f x else 0)) = ∑ x ∈ s, f x := by
classical
have hfilter : (Finset.univ : Finset (V × V)).filter (fun x => x ∈ s) = s := by
ext x
simp
simpa [hfilter] using (Finset.sum_filter (fun x => x ∈ s) f).symm
The sum of f over univ, restricted by swapped membership in s, equals
the sum of f ∘ swap over s.
private lemma sum_filter_univ_swap_eq {s : Finset (V × V)} {f : V × V → ℕ} :
(∑ x : V × V, (if x.swap ∈ s then f x else 0)) = ∑ x ∈ s, f x.swap := by
classical
have hfilter : (Finset.univ : Finset (V × V)).filter (fun x => x ∈ s) = s := by
ext x
simp
have h1 : (∑ x : V × V, (if x.swap ∈ s then f x else 0)) =
∑ x : V × V, (if x ∈ s then f x.swap else 0) := by
exact Equiv.sum_comp (Equiv.prodComm (α := V) (β := V)) (fun x => (if x ∈ s then f x.swap else 0))
have h2 : (∑ x : V × V, (if x ∈ s then f x.swap else 0)) = ∑ x ∈ s, f x.swap := by
simpa [hfilter] using (Finset.sum_filter (fun x => x ∈ s) (fun x => f x.swap)).symm
rw [h1, h2]
The total number of saturating pushes is at most 4|V|·|E|.
theorem saturating_push_count_bound (R : Run V G n) :
numSaturatingPushes R ≤ 4 * Fintype.card V * numEdges G := by
classical
have hsum : numSaturatingPushes R = ∑ e : V × V, numSaturatingPushesOn R e.1 e.2 := by
calc
numSaturatingPushes R = ∑ i : Fin n, (if (R.opFin i).isSaturatingPush then 1 else 0) := rfl
_ = ∑ i : Fin n, ∑ e : V × V, (if (R.opFin i).saturatingPushOn e.1 e.2 then 1 else 0) := by
apply Finset.sum_congr rfl
intro i hi
exact (BasicOp.sum_saturatingPushOn_eq (R.opFin i)).symm
_ = ∑ e : V × V, ∑ i : Fin n, (if (R.opFin i).saturatingPushOn e.1 e.2 then 1 else 0) := by
rw [Finset.sum_comm]
_ = ∑ e : V × V, numSaturatingPushesOn R e.1 e.2 := rfl
have hle : (∑ e : V × V, numSaturatingPushesOn R e.1 e.2) ≤
(∑ e ∈ edgeSet G, numSaturatingPushesOn R e.1 e.2) +
(∑ e ∈ edgeSet G, numSaturatingPushesOn R e.2 e.1) := by
calc
(∑ e : V × V, numSaturatingPushesOn R e.1 e.2) ≤
∑ e : V × V, ((if e ∈ edgeSet G then numSaturatingPushesOn R e.1 e.2 else 0) +
(if e.swap ∈ edgeSet G then numSaturatingPushesOn R e.1 e.2 else 0)) := by
apply Finset.sum_le_sum
intro e _
have h := saturating_pushOn_imp_cap R e.1 e.2
cases h with
| inl hz => simp [hz]
| inr hcap =>
cases hcap with
| inl h_uv =>
have he : e ∈ edgeSet G := Finset.mem_filter.mpr ⟨Finset.mem_univ e, h_uv⟩
simp [he]
| inr h_vu =>
have he : e.swap ∈ edgeSet G := Finset.mem_filter.mpr ⟨Finset.mem_univ e.swap, h_vu⟩
simp [he]
_ = (∑ e ∈ edgeSet G, numSaturatingPushesOn R e.1 e.2) +
(∑ e ∈ edgeSet G, numSaturatingPushesOn R e.2 e.1) := by
rw [Finset.sum_add_distrib]
congr 1
· exact sum_filter_univ_eq
· exact sum_filter_univ_swap_eq
calc
numSaturatingPushes R = ∑ e : V × V, numSaturatingPushesOn R e.1 e.2 := hsum
_ ≤ (∑ e ∈ edgeSet G, numSaturatingPushesOn R e.1 e.2) +
(∑ e ∈ edgeSet G, numSaturatingPushesOn R e.2 e.1) := hle
_ ≤ (∑ e ∈ edgeSet G, (2 * Fintype.card V)) +
(∑ e ∈ edgeSet G, (2 * Fintype.card V)) := by
apply Nat.add_le_add
· apply Finset.sum_le_sum
intro e _
exact saturating_push_count_bound_on R e.1 e.2
· apply Finset.sum_le_sum
intro e _
exact saturating_push_count_bound_on R e.2 e.1
_ = 4 * Fintype.card V * numEdges G := by
rw [numEdges, edgeSet]
simp only [Finset.sum_const, nsmul_eq_mul]
nlinarithPotential and nonsaturating push count bound
The set of overflowing vertices of a preflow.
noncomputable def overflowSet (φ : Preflow V G) : Finset V :=
(Finset.univ : Finset V).filter (fun u => φ.isOverflowing u)
The potential Φ = Σ_{overflowing u} h(u).
noncomputable def potential (φ : Preflow V G) (h : V → ℕ) : ℕ :=
∑ u ∈ overflowSet φ, h uSum of the single-edge skew update over the first argument.
private lemma edgeDelta_sum_first' (δ : ℝ) (u v a : V) :
(Finset.univ : Finset V).sum (fun x => Flow.edgeDelta δ u v x a) =
(if a = v then δ else 0) - (if a = u then δ else 0) := by
unfold Flow.edgeDelta
rw [Finset.sum_sub_distrib]
have hf : (Finset.univ : Finset V).sum (fun x => if x = u ∧ a = v then δ else 0) =
(if a = v then δ else 0) := by
by_cases hav : a = v
· subst a; simp
· simp [hav]
have hg : (Finset.univ : Finset V).sum (fun x => if x = v ∧ a = u then δ else 0) =
(if a = u then δ else 0) := by
by_cases hau : a = u
· subst a; simp
· simp [hau]
rw [hf, hg]
Telescoping: ∑ i in range n, (f (i+1) - f i) = f n - f 0.
private lemma sum_range_sub_eq (f : ℕ → ℤ) (n : ℕ) :
∑ i ∈ Finset.range n, (f (i + 1) - f i) = f n - f 0 := by
induction n with
| zero => simp
| succ n ih =>
rw [Finset.sum_range_succ, ih]
ring
The potential is bounded by 2|V|².
lemma potential_le (φ : Preflow V G) (h : V → ℕ) (hvalid : IsValidHeight φ h) :
potential φ h ≤ 2 * Fintype.card V * Fintype.card V := by
calc
potential φ h = ∑ u ∈ overflowSet φ, h u := rfl
_ ≤ ∑ u ∈ overflowSet φ, (2 * Fintype.card V) := by
apply Finset.sum_le_sum
intro u hu
have hoverflow : φ.isOverflowing u := (Finset.mem_filter.mp hu).2
have hle : h u ≤ 2 * Fintype.card V - 1 :=
height_le_of_overflowing φ h hvalid u hoverflow.1 hoverflow.2.2
omega
_ = (overflowSet φ).card * (2 * Fintype.card V) := by
simp [Finset.sum_const, nsmul_eq_mul]
_ ≤ Fintype.card V * (2 * Fintype.card V) := by
exact Nat.mul_le_mul_right (2 * Fintype.card V) (Finset.card_le_univ (overflowSet φ))
_ = 2 * Fintype.card V * Fintype.card V := by rw [mul_comm]The exact change of the potential under a relabel.
private lemma potential_relabel_eq (φ : Preflow V G) (h : V → ℕ) (u : V)
(hres : ∃ v : V, φ.residualEdge u v) (hoverflow : φ.isOverflowing u) :
((potential φ (relabel φ h u hres) : ℤ) - (potential φ h : ℤ)) =
((relabel φ h u hres u : ℕ) : ℤ) - (h u : ℤ) := by
unfold potential overflowSet
rw [Nat.cast_sum, Nat.cast_sum]
rw [← Finset.sum_sub_distrib]
rw [Finset.sum_eq_single u]
· intro b hb hbu
rw [relabel_eq_of_ne φ h u hres hbu]
simp
· intro hu
have : u ∈ (Finset.univ : Finset V).filter (fun u => φ.isOverflowing u) :=
Finset.mem_filter.mpr ⟨Finset.mem_univ u, hoverflow⟩
exact (hu this).elim
A push increases the potential by at most 2|V|.
lemma potential_push_le (φ : Preflow V G) (h : V → ℕ) (u v : V) (hu_ne_s : u ≠ G.s)
(hres : φ.residualEdge u v) (hadm : h u = h v + 1) (hvalid : IsValidHeight φ h)
(hoverflow : φ.isOverflowing u) :
((potential (push φ u v hu_ne_s hres) h : ℤ) - (potential φ h : ℤ)) ≤ 2 * Fintype.card V := by
let φ' := push φ u v hu_ne_s hres
have hsubset : overflowSet φ' ⊆ overflowSet φ ∪ {v} := by
intro a ha
have ha' : φ'.isOverflowing a := (Finset.mem_filter.mp ha).2
by_cases hav : a = v
· rw [Finset.mem_union]
right
simpa [hav]
· rw [Finset.mem_union]
left
apply Finset.mem_filter.mpr
constructor
· exact Finset.mem_univ a
· have hne_s : a ≠ G.s := ha'.1
have hne_t : a ≠ G.t := ha'.2.1
have hpos' : 0 < φ'.excess a := ha'.2.2
have hle_excess : φ'.excess a ≤ φ.excess a := by
unfold φ' push pushBy Preflow.excess netInflow
rw [Finset.sum_add_distrib]
rw [edgeDelta_sum_first' (min (∑ w : V, φ.f w u) (φ.residualCapacity u v)) u v a]
have hno_add : (if a = v then min (∑ w : V, φ.f w u) (φ.residualCapacity u v) else 0) = 0 := by
simp [hav]
rw [hno_add]
have hnonneg : 0 ≤ (if a = u then min (∑ w : V, φ.f w u) (φ.residualCapacity u v) else 0) := by
split
· exact le_min (φ.hexcess_nonneg u hu_ne_s) (le_of_lt hres)
· simp
linarith
exact ⟨hne_s, hne_t, lt_of_lt_of_le hpos' hle_excess⟩
have hdiff_le : ((potential φ' h : ℤ) - (potential φ h : ℤ)) ≤ (h v : ℤ) := by
unfold potential
have hsum_le : (∑ x ∈ overflowSet φ', h x) ≤ (∑ x ∈ overflowSet φ, h x) + h v := by
have hle_subset : (∑ x ∈ overflowSet φ', h x) ≤ ∑ x ∈ overflowSet φ ∪ {v}, h x :=
Finset.sum_le_sum_of_subset_of_nonneg hsubset (fun _ _ _ => Nat.zero_le _)
have hle_union : (∑ x ∈ overflowSet φ ∪ {v}, h x) ≤ (∑ x ∈ overflowSet φ, h x) + h v := by
have hdecomp : (∑ x ∈ overflowSet φ ∪ {v}, h x) =
(∑ x ∈ overflowSet φ, h x) + (∑ x ∈ ({v} : Finset V) \ overflowSet φ, h x) := by
rw [show overflowSet φ ∪ {v} = overflowSet φ ∪ (({v} : Finset V) \ overflowSet φ) by
ext x
by_cases hx : x ∈ overflowSet φ <;> simp [hx]]
rw [Finset.sum_union]
exact Finset.disjoint_sdiff
have hle_singleton : (∑ x ∈ ({v} : Finset V) \ overflowSet φ, h x) ≤ h v := by
have hsub : (∑ x ∈ ({v} : Finset V) \ overflowSet φ, h x) ≤ ∑ x ∈ ({v} : Finset V), h x :=
Finset.sum_le_sum_of_subset_of_nonneg Finset.sdiff_subset (fun _ _ _ => Nat.zero_le _)
simpa using hsub
rw [hdecomp]
exact Nat.add_le_add_left hle_singleton (∑ x ∈ overflowSet φ, h x)
exact le_trans hle_subset hle_union
have hz : ((∑ x ∈ overflowSet φ', h x : ℕ) : ℤ) ≤ ((∑ x ∈ overflowSet φ, h x : ℕ) : ℤ) + (h v : ℤ) := by
exact_mod_cast hsum_le
omega
have hv_le : (h v : ℤ) ≤ 2 * Fintype.card V := by
have hu_le : h u ≤ 2 * Fintype.card V - 1 :=
height_le_of_overflowing φ h hvalid u hoverflow.1 hoverflow.2.2
have hV : 0 < Fintype.card V := Fintype.card_pos_iff.mpr ⟨G.s⟩
have hadm' : (h u : ℤ) = (h v : ℤ) + 1 := by omega
have hu_le_z : (h u : ℤ) ≤ (2 * Fintype.card V : ℤ) := by
have h' : h u ≤ 2 * Fintype.card V := by omega
exact_mod_cast h'
nlinarith
nlinarithA nonsaturating push decreases the potential by at least one.
lemma potential_nonsaturating_push_le (φ : Preflow V G) (h : V → ℕ) (u v : V)
(hu_ne_s : u ≠ G.s) (hres : φ.residualEdge u v) (hadm : h u = h v + 1)
(hoverflow : φ.isOverflowing u) (hnonsat : φ.excess u < φ.residualCapacity u v) :
((potential (push φ u v hu_ne_s hres) h : ℤ) - (potential φ h : ℤ)) ≤ -1 := by
let φ' := push φ u v hu_ne_s hres
have hu_not : ¬ φ'.isOverflowing u := by
intro h
have hpos : 0 < φ'.excess u := h.2.2
have hexcess : φ'.excess u = 0 := by
unfold φ' push pushBy Preflow.excess netInflow
rw [Finset.sum_add_distrib]
rw [edgeDelta_sum_first' (min (∑ w : V, φ.f w u) (φ.residualCapacity u v)) u v u]
have hmin : min (∑ w : V, φ.f w u) (φ.residualCapacity u v) = ∑ w : V, φ.f w u := by
apply min_eq_left
exact le_of_lt hnonsat
rw [hmin]
simp [residualEdge_ne φ hres]
linarith
have hsubset : overflowSet φ' ⊆ (overflowSet φ \ {u}) ∪ {v} := by
intro a ha
have ha' : φ'.isOverflowing a := (Finset.mem_filter.mp ha).2
by_cases hav : a = v
· rw [Finset.mem_union]
right
simpa [hav]
· rw [Finset.mem_union]
left
apply Finset.mem_sdiff.mpr
constructor
· apply Finset.mem_filter.mpr
constructor
· exact Finset.mem_univ a
· have hne_s : a ≠ G.s := ha'.1
have hne_t : a ≠ G.t := ha'.2.1
have hpos' : 0 < φ'.excess a := ha'.2.2
have hle_excess : φ'.excess a ≤ φ.excess a := by
unfold φ' push pushBy Preflow.excess netInflow
rw [Finset.sum_add_distrib]
rw [edgeDelta_sum_first' (min (∑ w : V, φ.f w u) (φ.residualCapacity u v)) u v a]
have hno_add : (if a = v then min (∑ w : V, φ.f w u) (φ.residualCapacity u v) else 0) = 0 := by
simp [hav]
rw [hno_add]
have hnonneg : 0 ≤ (if a = u then min (∑ w : V, φ.f w u) (φ.residualCapacity u v) else 0) := by
split
· exact le_min (φ.hexcess_nonneg u hu_ne_s) (le_of_lt hres)
· simp
linarith
exact ⟨hne_s, hne_t, lt_of_lt_of_le hpos' hle_excess⟩
· intro hau
apply hu_not
exact (Finset.mem_singleton.mp hau) ▸ ha'
have hu_mem : u ∈ overflowSet φ := Finset.mem_filter.mpr ⟨Finset.mem_univ u, hoverflow⟩
have hdiff_le : ((potential φ' h : ℤ) - (potential φ h : ℤ)) ≤ -1 := by
unfold potential
have hsum_le : (∑ x ∈ overflowSet φ', h x) + h u ≤ (∑ x ∈ overflowSet φ, h x) + h v := by
have hle_subset : (∑ x ∈ overflowSet φ', h x) ≤ ∑ x ∈ (overflowSet φ \ {u}) ∪ {v}, h x :=
Finset.sum_le_sum_of_subset_of_nonneg hsubset (fun _ _ _ => Nat.zero_le _)
have hle_add : (∑ x ∈ overflowSet φ', h x) + h u ≤
(∑ x ∈ (overflowSet φ \ {u}) ∪ {v}, h x) + h u :=
Nat.add_le_add_right hle_subset (h u)
have hle_union : (∑ x ∈ (overflowSet φ \ {u}) ∪ {v}, h x) ≤
(∑ x ∈ overflowSet φ \ {u}, h x) + h v := by
have hdecomp : (∑ x ∈ (overflowSet φ \ {u}) ∪ {v}, h x) =
(∑ x ∈ overflowSet φ \ {u}, h x) + (∑ x ∈ ({v} : Finset V) \ (overflowSet φ \ {u}), h x) := by
rw [show (overflowSet φ \ {u}) ∪ {v} =
(overflowSet φ \ {u}) ∪ (({v} : Finset V) \ (overflowSet φ \ {u})) by
ext x
by_cases hx : x ∈ overflowSet φ \ {u} <;> simp [hx]]
rw [Finset.sum_union]
exact Finset.disjoint_sdiff
have hle_singleton : (∑ x ∈ ({v} : Finset V) \ (overflowSet φ \ {u}), h x) ≤ h v := by
have hsub : (∑ x ∈ ({v} : Finset V) \ (overflowSet φ \ {u}), h x) ≤
∑ x ∈ ({v} : Finset V), h x :=
Finset.sum_le_sum_of_subset_of_nonneg Finset.sdiff_subset (fun _ _ _ => Nat.zero_le _)
simpa using hsub
rw [hdecomp]
exact Nat.add_le_add_left hle_singleton (∑ x ∈ overflowSet φ \ {u}, h x)
have hsum_sdiff : (∑ x ∈ overflowSet φ \ {u}, h x) + h u = ∑ x ∈ overflowSet φ, h x := by
simpa using (Finset.sum_sdiff (Finset.singleton_subset_iff.mpr hu_mem) :
(∑ x ∈ overflowSet φ \ {u}, h x) + (∑ x ∈ ({u} : Finset V), h x) = ∑ x ∈ overflowSet φ, h x)
have hle_mid : (∑ x ∈ (overflowSet φ \ {u}) ∪ {v}, h x) + h u ≤
(∑ x ∈ overflowSet φ, h x) + h v := by
omega
exact le_trans hle_add hle_mid
have hz : ((∑ x ∈ overflowSet φ', h x : ℕ) : ℤ) + (h u : ℤ) ≤
((∑ x ∈ overflowSet φ, h x : ℕ) : ℤ) + (h v : ℤ) := by
exact_mod_cast hsum_le
have hadm' : (h u : ℤ) = (h v : ℤ) + 1 := by omega
omega
exact hdiff_le
The potential increases across a single step by at most the total height
increase plus 2|V| times the number of saturating pushes, minus one per
nonsaturating push.
private lemma potential_step_bound_tight (R : Run V G n) (i : Fin n) :
((potential (R.φ (i.1 + 1)) (R.h (i.1 + 1)) : ℤ) - (potential (R.φ i.1) (R.h i.1) : ℤ)) ≤
(∑ x : V, (((R.h (i.1 + 1) x : ℕ) : ℤ) - (R.h i.1 x : ℤ))) +
(2 * Fintype.card V : ℤ) * (if (R.opFin i).isSaturatingPush then 1 else 0) -
(if (R.opFin i).isNonsaturatingPush then 1 else 0) := by
cases hop : R.opFin i with
| relabel φ h u ho hres hpre =>
have hb_φ : φ = R.φ i.1 := by
have h' : (R.opFin i).beforeφ = R.φ i.1 := R.hop_beforeφ i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.beforeφ, hop]
have hb_h : h = R.h i.1 := by
have h' : (R.opFin i).beforeh = R.h i.1 := R.hop_beforeh i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.beforeh, hop]
have hr_φ : R.φ (i.1 + 1) = R.φ i.1 := by
have h' : (R.opFin i).resultφ = R.φ (i.1 + 1) := R.hop_resultφ i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.resultφ, hop, hb_φ]
let hres' : ∃ v : V, (R.φ i.1).residualEdge u v := by simpa [hb_φ] using hres
have hr_h : R.h (i.1 + 1) = Chapter26.relabel (R.φ i.1) (R.h i.1) u hres' := by
have h' : (R.opFin i).resulth = R.h (i.1 + 1) := R.hop_resulth i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.resulth, hop, hb_φ, hb_h]
have hdiff := potential_relabel_eq (R.φ i.1) (R.h i.1) u hres' (by simpa [hb_φ] using ho)
have hsum_eq : (∑ w : V, (((Chapter26.relabel (R.φ i.1) (R.h i.1) u hres' w : ℕ) : ℤ) - (R.h i.1 w : ℤ))) =
((Chapter26.relabel (R.φ i.1) (R.h i.1) u hres' u : ℕ) : ℤ) - (R.h i.1 u : ℤ) := by
rw [Finset.sum_eq_single u]
· intro b _ hbu
rw [relabel_eq_of_ne (R.φ i.1) (R.h i.1) u hres' hbu]
simp
· intro hu
simp at hu
rw [hr_φ, hr_h, hdiff, hsum_eq]
simp [isSaturatingPush, isNonsaturatingPush, hop]
| push φ h u v ho hres hadm =>
have hb_φ : φ = R.φ i.1 := by
have h' : (R.opFin i).beforeφ = R.φ i.1 := R.hop_beforeφ i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.beforeφ, hop]
have hb_h : h = R.h i.1 := by
have h' : (R.opFin i).beforeh = R.h i.1 := R.hop_beforeh i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.beforeh, hop]
have hr_φ : R.φ (i.1 + 1) = Chapter26.push (R.φ i.1) u v (by simpa [hb_φ] using ho.1)
(by simpa [hb_φ] using hres) := by
have h' : (R.opFin i).resultφ = R.φ (i.1 + 1) := R.hop_resultφ i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.resultφ, hop, hb_φ]
have hr_h : R.h (i.1 + 1) = R.h i.1 := by
have h' : (R.opFin i).resulth = R.h (i.1 + 1) := R.hop_resulth i.1 (Fin.isLt i)
rw [← h']
simp [BasicOp.resulth, hop, hb_h]
have hsum_zero : (∑ w : V, (((R.h i.1 w : ℕ) : ℤ) - (R.h i.1 w : ℤ))) = 0 := by
simp
by_cases hsat : φ.residualCapacity u v ≤ φ.excess u
· have hle := potential_push_le (R.φ i.1) (R.h i.1) u v (by simpa [hb_φ] using ho.1)
(by simpa [hb_φ] using hres) (by simpa [hb_h] using hadm) (R.hvalid i.1)
(by simpa [hb_φ] using ho)
have hnot_lt : ¬ φ.excess u < φ.residualCapacity u v := not_lt_of_ge hsat
rw [hr_φ, hr_h, hsum_zero]
simp [isSaturatingPush, isNonsaturatingPush, hop, hsat, hnot_lt]
nlinarith [hle]
· have hlt : φ.excess u < φ.residualCapacity u v := lt_of_not_ge hsat
have hle := potential_nonsaturating_push_le (R.φ i.1) (R.h i.1) u v (by simpa [hb_φ] using ho.1)
(by simpa [hb_φ] using hres) (by simpa [hb_h] using hadm) (by simpa [hb_φ] using ho)
(by simpa [hb_φ] using hlt)
rw [hr_φ, hr_h, hsum_zero]
simp [isSaturatingPush, isNonsaturatingPush, hop, hsat, hlt]
nlinarith [hle]
The total height increase across a run is at most 2|V| per vertex.
private lemma height_le_add_bound : ∀ (n : ℕ) (R : Run V G n) (u : V),
R.h n u ≤ R.h 0 u + 2 * Fintype.card V := by
intro n
induction n with
| zero =>
intro R u
omega
| succ m ih =>
intro R u
let R' : Run V G m := {
φ := R.φ
h := R.h
hvalid := R.hvalid
op := fun i hi => R.op i (Nat.lt_trans hi (Nat.lt_succ_self m))
hop_beforeφ := fun i hi => R.hop_beforeφ i (Nat.lt_trans hi (Nat.lt_succ_self m))
hop_beforeh := fun i hi => R.hop_beforeh i (Nat.lt_trans hi (Nat.lt_succ_self m))
hop_resultφ := fun i hi => R.hop_resultφ i (Nat.lt_trans hi (Nat.lt_succ_self m))
hop_resulth := fun i hi => R.hop_resulth i (Nat.lt_trans hi (Nat.lt_succ_self m)) }
have hb := ih R' u
change R.h m u ≤ R.h 0 u + 2 * Fintype.card V at hb
cases hop : R.op m (Nat.lt_succ_self m) with
| relabel φ h w ho hres hpre =>
by_cases hwu : w = u
· subst u
have hoverflow : (R.φ (m + 1)).isOverflowing w := by
have hrφ : R.φ (m + 1) = φ := by
simpa [BasicOp.resultφ, hop] using
(R.hop_resultφ m (Nat.lt_succ_self m)).symm
rw [hrφ]
exact ho
have hle' := height_le_of_overflowing (R.φ (m + 1)) (R.h (m + 1)) (R.hvalid (m + 1)) w
hoverflow.1 hoverflow.2.2
omega
· have hstep : R.h (m + 1) u = R.h m u := by
rw [← R.hop_resulth m (Nat.lt_succ_self m), ← R.hop_beforeh m (Nat.lt_succ_self m)]
simp [BasicOp.resulth, BasicOp.beforeh, hop, relabel_eq_of_ne φ h w hres (Ne.symm hwu)]
omega
| push φ h a b ho hres hadm =>
have hstep : R.h (m + 1) u = R.h m u := by
rw [← R.hop_resulth m (Nat.lt_succ_self m), ← R.hop_beforeh m (Nat.lt_succ_self m)]
simp [BasicOp.resulth, BasicOp.beforeh, hop]
omegaThe potential telescopes over a run.
private lemma potential_telescope (R : Run V G n) :
((potential (R.φ n) (R.h n) : ℤ) = (potential (R.φ 0) (R.h 0) : ℤ) +
∑ i ∈ Finset.range n, (((potential (R.φ (i + 1)) (R.h (i + 1)) : ℤ) -
(potential (R.φ i) (R.h i) : ℤ)))) := by
have h := sum_range_sub_eq (fun i => (potential (R.φ i) (R.h i) : ℤ)) n
omega
The total number of nonsaturating pushes is bounded by O(|V|²(|V|+|E|)).
theorem nonsaturating_push_count_bound (R : Run V G n) :
numNonsaturatingPushes R ≤ 12 * Fintype.card V * Fintype.card V * (Fintype.card V + numEdges G) := by
classical
let Vc := Fintype.card V
have htel := potential_telescope R
have hnonneg : 0 ≤ (potential (R.φ n) (R.h n) : ℤ) := by
exact Int.natCast_nonneg (potential (R.φ n) (R.h n))
have hinit_ℕ : potential (R.φ 0) (R.h 0) ≤ 2 * Vc * Vc := by
exact potential_le (R.φ 0) (R.h 0) (R.hvalid 0)
have hrel_inc_ℕ : (∑ u : V, (R.h n u - R.h 0 u)) ≤ 2 * Vc * Vc := by
have hbound : ∀ u, R.h n u - R.h 0 u ≤ 2 * Fintype.card V := by
intro u
have h : R.h n u ≤ R.h 0 u + 2 * Fintype.card V := height_le_add_bound n R u
omega
calc
(∑ u : V, (R.h n u - R.h 0 u)) ≤ ∑ u : V, (2 * Fintype.card V) := by
apply Finset.sum_le_sum
intro u _
exact hbound u
_ = Fintype.card V * (2 * Fintype.card V) := by simp [Finset.sum_const, nsmul_eq_mul]
_ = 2 * Vc * Vc := by dsimp [Vc]; rw [mul_comm]
have hsum_le : (∑ i ∈ Finset.range n,
(((potential (R.φ (i + 1)) (R.h (i + 1)) : ℤ) - (potential (R.φ i) (R.h i) : ℤ)))) ≤
(∑ u : V, (((R.h n u : ℕ) : ℤ) - (R.h 0 u : ℤ))) +
(2 * Vc : ℤ) * (numSaturatingPushes R : ℤ) -
(numNonsaturatingPushes R : ℤ) := by
have hfin : (∑ i : Fin n,
((potential (R.φ (i.1 + 1)) (R.h (i.1 + 1)) : ℤ) - (potential (R.φ i.1) (R.h i.1) : ℤ))) ≤
(∑ u : V, (((R.h n u : ℕ) : ℤ) - (R.h 0 u : ℤ))) +
(2 * Vc : ℤ) * (numSaturatingPushes R : ℤ) -
(numNonsaturatingPushes R : ℤ) := by
calc
(∑ i : Fin n,
((potential (R.φ (i.1 + 1)) (R.h (i.1 + 1)) : ℤ) - (potential (R.φ i.1) (R.h i.1) : ℤ))) ≤
∑ i : Fin n, ((∑ u : V, (((R.h (i.1 + 1) u : ℕ) : ℤ) - (R.h i.1 u : ℤ))) +
(2 * Vc : ℤ) * (if (R.opFin i).isSaturatingPush then 1 else 0) -
(if (R.opFin i).isNonsaturatingPush then 1 else 0)) := by
apply Finset.sum_le_sum
intro i _
exact potential_step_bound_tight R i
_ = (∑ u : V, (((R.h n u : ℕ) : ℤ) - (R.h 0 u : ℤ))) +
(2 * Vc : ℤ) * (∑ i : Fin n, (if (R.opFin i).isSaturatingPush then 1 else 0)) -
(∑ i : Fin n, (if (R.opFin i).isNonsaturatingPush then 1 else 0)) := by
rw [Finset.sum_sub_distrib, Finset.sum_add_distrib]
congr 1
congr 1
· rw [Finset.sum_comm]
apply Finset.sum_congr rfl
intro u _
have htel_u := sum_range_sub_eq (fun i => (R.h i u : ℤ)) n
rw [Finset.sum_fin_eq_sum_range]
exact (Finset.sum_congr rfl (fun x hx => by
simp [Finset.mem_range.mp hx])).trans htel_u
· rw [← Finset.mul_sum]
_ = (∑ u : V, (((R.h n u : ℕ) : ℤ) - (R.h 0 u : ℤ))) +
(2 * Vc : ℤ) * (numSaturatingPushes R : ℤ) -
(numNonsaturatingPushes R : ℤ) := by
simp [numSaturatingPushes, numNonsaturatingPushes]
have hfin' : (∑ i ∈ Finset.range n,
((potential (R.φ (i + 1)) (R.h (i + 1)) : ℤ) - (potential (R.φ i) (R.h i) : ℤ))) =
(∑ i : Fin n,
((potential (R.φ (i.1 + 1)) (R.h (i.1 + 1)) : ℤ) - (potential (R.φ i.1) (R.h i.1) : ℤ))) := by
rw [Finset.sum_fin_eq_sum_range]
apply Finset.sum_congr rfl
intro i hi
simp [Finset.mem_range.mp hi]
rw [hfin']
exact hfin
have hsum_cast : (∑ u : V, (((R.h n u : ℕ) : ℤ) - (R.h 0 u : ℤ))) =
((∑ u : V, (R.h n u - R.h 0 u) : ℕ) : ℤ) := by
rw [Nat.cast_sum]
apply Finset.sum_congr rfl
intro u _
have hmono : R.h 0 u ≤ R.h n u := height_mono R (Nat.zero_le n) (Nat.le_refl n) u
exact (Int.ofNat_sub hmono).symm
have hmain_ℤ : (numNonsaturatingPushes R : ℤ) ≤
(potential (R.φ 0) (R.h 0) : ℤ) +
(∑ u : V, (((R.h n u : ℕ) : ℤ) - (R.h 0 u : ℤ))) +
(2 * Vc : ℤ) * (numSaturatingPushes R : ℤ) := by
have hsum' := hsum_le
have : (potential (R.φ 0) (R.h 0) : ℤ) + (∑ i ∈ Finset.range n,
((potential (R.φ (i + 1)) (R.h (i + 1)) : ℤ) - (potential (R.φ i) (R.h i) : ℤ))) ≥ 0 := by
simpa [htel] using hnonneg
omega
have hmain_ℕ : numNonsaturatingPushes R ≤ 2 * Vc * Vc + (∑ u : V, (R.h n u - R.h 0 u)) +
2 * Vc * numSaturatingPushes R := by
have hinit_z : (potential (R.φ 0) (R.h 0) : ℤ) ≤ ((2 * Vc * Vc : ℕ) : ℤ) := by
exact_mod_cast hinit_ℕ
have hz : (numNonsaturatingPushes R : ℤ) ≤
((2 * Vc * Vc + (∑ u : V, (R.h n u - R.h 0 u)) + 2 * Vc * numSaturatingPushes R : ℕ) : ℤ) := by
rw [hsum_cast] at hmain_ℤ
have hcast : ((2 * Vc * numSaturatingPushes R : ℕ) : ℤ) = (2 * (Vc : ℤ)) * (numSaturatingPushes R : ℤ) := by
norm_num
have hcast_add : ((2 * Vc * Vc + (∑ u : V, (R.h n u - R.h 0 u)) + 2 * Vc * numSaturatingPushes R : ℕ) : ℤ) =
((2 * Vc * Vc : ℕ) : ℤ) + ((∑ u : V, (R.h n u - R.h 0 u) : ℕ) : ℤ) + ((2 * Vc * numSaturatingPushes R : ℕ) : ℤ) := by
simp only [Nat.cast_add]
nlinarith [hmain_ℤ, hinit_z, hcast, hcast_add]
exact_mod_cast hz
have hsat := saturating_push_count_bound R
have hV : 1 ≤ Vc := Fintype.card_pos_iff.mpr ⟨G.s⟩
dsimp [Vc] at hmain_ℕ hrel_inc_ℕ hsat ⊢
nlinarith [hmain_ℕ, hrel_inc_ℕ, hsat, hV]Every step is exactly one of relabel, saturating push, or nonsaturating push.
theorem op_count_decomp (R : Run V G n) :
n = numRelabels R + numSaturatingPushes R + numNonsaturatingPushes R := by
have hclass : (∑ i : Fin n, (1 : ℕ)) =
∑ i : Fin n, ((if (R.opFin i).isRelabel then 1 else 0) +
(if (R.opFin i).isSaturatingPush then 1 else 0) +
(if (R.opFin i).isNonsaturatingPush then 1 else 0)) := by
apply Finset.sum_congr rfl
intro i _
exact (BasicOp.classification (R.opFin i)).symm
calc
n = ∑ i : Fin n, (1 : ℕ) := by simp
_ = ∑ i : Fin n, ((if (R.opFin i).isRelabel then 1 else 0) +
(if (R.opFin i).isSaturatingPush then 1 else 0) +
(if (R.opFin i).isNonsaturatingPush then 1 else 0)) := hclass
_ = numRelabels R + numSaturatingPushes R + numNonsaturatingPushes R := by
rw [Finset.sum_add_distrib, Finset.sum_add_distrib]
simp [numRelabels, numSaturatingPushes, numNonsaturatingPushes]
The combined O(V²E) bound on the number of basic operations of the
generic push-relabel algorithm.
theorem generic_step_count_bound (R : Run V G n) :
n ≤ 12 * Fintype.card V * Fintype.card V * Fintype.card V +
12 * Fintype.card V * Fintype.card V * numEdges G +
4 * Fintype.card V * numEdges G + 4 * Fintype.card V * Fintype.card V := by
have hdecomp : n = numRelabels R + numSaturatingPushes R + numNonsaturatingPushes R :=
op_count_decomp R
rw [hdecomp]
have hr : numRelabels R ≤ 2 * Fintype.card V * Fintype.card V := relabel_count_bound R
have hs : numSaturatingPushes R ≤ 4 * Fintype.card V * numEdges G := saturating_push_count_bound R
have hn : numNonsaturatingPushes R ≤ 12 * Fintype.card V * Fintype.card V *
(Fintype.card V + numEdges G) := nonsaturating_push_count_bound R
nlinarithend RunThe relabel-to-front discharge order
namespace BasicOpThe active vertex of a basic operation: the relabeled vertex, or the source of the push.
end BasicOp
The number of directed edges is at most |V|².
theorem numEdges_le_card_mul_card (G : FlowNetwork V) :
numEdges G ≤ Fintype.card V * Fintype.card V := by
unfold numEdges edgeSet
calc
((Finset.univ : Finset (V × V)).filter (fun e => 0 < G.c e.1 e.2)).card
≤ (Finset.univ : Finset (V × V)).card := Finset.card_le_card (Finset.filter_subset _ _)
_ = Fintype.card V * Fintype.card V := by
rw [Finset.card_univ, Fintype.card_prod]
A relabel-to-front run is a run of the generic push-relabel algorithm that
respects the discharge discipline of CLRS §26.5. The DISCHARGE procedure
walks current[u] through a per-vertex neighbor list N[u] and, on exhausting
it, relabels u and moves it to the front of the list L. The observable
consequence of that list discipline is that between two relabel operations no
vertex is the source of more than one nonsaturating push, which is exactly the
condition recorded here. (A nonsaturating push is the final operation of a
DISCHARGE call, so the number of nonsaturating pushes equals the number of
DISCHARGE calls.)
The underlying generic run.
The discharge discipline: between two relabels, no vertex is the source of more than one nonsaturating push.
structure RelabelToFrontRun (V : Type u) [Fintype V] [DecidableEq V]
(G : FlowNetwork V) (n : ℕ) where run : Run V G n discharge_discipline :
∀ ⦃i j : ℕ⦄ (hi : i < n) (hj : j < n) (hij : i < j),
(run.op i hi).isNonsaturatingPush = true →
(run.op j hj).isNonsaturatingPush = true →
(∀ k (hk : k < n), i < k → k < j → (run.op k hk).isRelabel = false) →
BasicOp.opVertex (run.op i hi) ≠ BasicOp.opVertex (run.op j hj)namespace RelabelToFrontRunvariable {V : Type u} [Fintype V] [DecidableEq V] {G : FlowNetwork V} {n : ℕ}
The set of relabel operations occurring strictly before index i.
noncomputable def relabelsBeforeSet (R : Run V G n) (i : Fin n) : Finset (Fin n) :=
(Finset.univ : Finset (Fin n)).filter (fun k => (k : ℕ) < i.1 ∧ (R.opFin k).isRelabel = true)
The number of relabel operations occurring strictly before index i.
noncomputable def relabelsBefore (R : Run V G n) (i : Fin n) : ℕ :=
(relabelsBeforeSet R i).card
relabelsBefore counts a subset of all relabels, so it is at most the total
relabel count.
lemma relabelsBefore_le_numRelabels (R : Run V G n) (i : Fin n) :
relabelsBefore R i ≤ Run.numRelabels R := by
unfold relabelsBefore relabelsBeforeSet
rw [Run.numRelabels, Finset.sum_boole]
apply Finset.card_le_card
intro k hk
exact Finset.mem_filter.mpr ⟨Finset.mem_univ k, (Finset.mem_filter.mp hk).2.2⟩The before-set is monotone in the index.
lemma relabelsBeforeSet_mono (R : Run V G n) {i j : Fin n} (hij : i.1 ≤ j.1) :
relabelsBeforeSet R i ⊆ relabelsBeforeSet R j := by
intro k hk
have hk' := Finset.mem_filter.mp hk
exact Finset.mem_filter.mpr ⟨Finset.mem_univ k, ⟨lt_of_lt_of_le hk'.2.1 hij, hk'.2.2⟩⟩
If i < j have the same relabelsBefore value, no relabel occurs strictly
between them.
lemma no_relabel_between_of_relabelsBefore_eq (R : Run V G n) {i j : Fin n}
(hij : i.1 < j.1) (h : relabelsBefore R i = relabelsBefore R j) (k : Fin n)
(hik : i.1 < k.1) (hkj : k.1 < j.1) :
(R.opFin k).isRelabel = false := by
by_cases hkrel : (R.opFin k).isRelabel = true
· have hk_in_j : k ∈ relabelsBeforeSet R j := by
exact Finset.mem_filter.mpr ⟨Finset.mem_univ k, ⟨hkj, hkrel⟩⟩
have hk_not_in_i : k ∉ relabelsBeforeSet R i := by
intro hkin
have hlt := (Finset.mem_filter.mp hkin).2.1
omega
have hsubset : relabelsBeforeSet R i ⊆ relabelsBeforeSet R j :=
relabelsBeforeSet_mono R (Nat.le_of_lt hij)
have hproper : relabelsBeforeSet R i ⊂ relabelsBeforeSet R j :=
⟨hsubset, fun hsup => hk_not_in_i (hsup hk_in_j)⟩
have hlt_card : relabelsBefore R i < relabelsBefore R j := by
unfold relabelsBefore
exact Finset.card_lt_card hproper
omega
· have hfalse : (R.opFin k).isRelabel = false := by
cases hb : (R.opFin k).isRelabel <;> simp_all
exact hfalse
Between two relabels, two nonsaturating pushes with equal relabelsBefore
values discharge distinct vertices, so equal vertices force equal indices.
lemma opVertex_inj_of_nonsat (R : RelabelToFrontRun V G n) {i j : Fin n}
(hi : (R.run.opFin i).isNonsaturatingPush = true)
(hj : (R.run.opFin j).isNonsaturatingPush = true)
(hrb : relabelsBefore R.run i = relabelsBefore R.run j)
(hvertex : BasicOp.opVertex (R.run.opFin i) = BasicOp.opVertex (R.run.opFin j)) :
i = j := by
by_cases hij : i.1 = j.1
· exact Fin.ext hij
· exfalso
have hlt : i.1 < j.1 ∨ j.1 < i.1 := by omega
rcases hlt with hlt | hlt
· have hno := no_relabel_between_of_relabelsBefore_eq R.run hlt hrb
have hdisc := R.discharge_discipline (i := i.1) (j := j.1) (Fin.isLt i) (Fin.isLt j) hlt hi hj
(fun k hk hik hkj => hno ⟨k, hk⟩ (by simpa using hik) (by simpa using hkj))
exact hdisc hvertex
· have hrb' : relabelsBefore R.run j = relabelsBefore R.run i := hrb.symm
have hno := no_relabel_between_of_relabelsBefore_eq R.run hlt hrb'
have hdisc := R.discharge_discipline (i := j.1) (j := i.1) (Fin.isLt j) (Fin.isLt i) hlt hj hi
(fun k hk hik hkj => hno ⟨k, hk⟩ (by simpa using hik) (by simpa using hkj))
exact hdisc hvertex.symm
The relabel-to-front discharge order bounds the number of nonsaturating
pushes by (|V|·(relabels + 1)), i.e. O(V³).
theorem nonsaturating_push_count_bound (R : RelabelToFrontRun V G n) :
Run.numNonsaturatingPushes R.run ≤ (Run.numRelabels R.run + 1) * Fintype.card V := by
classical
let Rf := R.run
let S : Finset (Fin n) :=
(Finset.univ : Finset (Fin n)).filter (fun i => (Rf.opFin i).isNonsaturatingPush = true)
have hS : Run.numNonsaturatingPushes Rf = S.card := by
rw [Run.numNonsaturatingPushes, Finset.sum_boole]
rfl
rw [hS]
let f : Fin n → ℕ × V := fun i => (relabelsBefore Rf i, BasicOp.opVertex (Rf.opFin i))
have hf_inj : Set.InjOn f (↑S : Set (Fin n)) := by
intro i hi j hj hfij
have hi' : (Rf.opFin i).isNonsaturatingPush = true := (Finset.mem_filter.mp hi).2
have hj' : (Rf.opFin j).isNonsaturatingPush = true := (Finset.mem_filter.mp hj).2
have hrb : relabelsBefore Rf i = relabelsBefore Rf j := by
exact congrArg Prod.fst hfij
have hv : BasicOp.opVertex (Rf.opFin i) = BasicOp.opVertex (Rf.opFin j) := by
exact congrArg Prod.snd hfij
exact opVertex_inj_of_nonsat R hi' hj' hrb hv
have hcard_image : S.card = (S.image f).card := by
exact (Finset.card_image_iff.mpr hf_inj).symm
calc
S.card = (S.image f).card := hcard_image
_ ≤ ((Finset.range (Run.numRelabels Rf + 1)) ×ˢ (Finset.univ : Finset V)).card := by
apply Finset.card_le_card
intro x hx
rcases Finset.mem_image.mp hx with ⟨i, hiS, hxi⟩
rw [← hxi]
exact Finset.mem_product.mpr ⟨
Finset.mem_range.mpr (Nat.lt_succ_of_le (relabelsBefore_le_numRelabels Rf i)),
Finset.mem_univ (BasicOp.opVertex (Rf.opFin i))⟩
_ = (Run.numRelabels Rf + 1) * Fintype.card V := by
rw [Finset.card_product, Finset.card_range, Finset.card_univ]
The combined O(V²E) generic bound already bounds relabels and saturating
pushes; the discharge order additionally bounds nonsaturating pushes by
(|V|·(relabels + 1)). Assembled, this gives the sharper relabel-to-front
O(V³) bound on the number of basic operations.
theorem step_count_bound (R : RelabelToFrontRun V G n) :
n ≤ 2 * Fintype.card V * Fintype.card V + 4 * Fintype.card V * numEdges G +
(2 * Fintype.card V * Fintype.card V + 1) * Fintype.card V := by
have hdecomp := Run.op_count_decomp R.run
have hr : Run.numRelabels R.run ≤ 2 * Fintype.card V * Fintype.card V :=
Run.relabel_count_bound R.run
have hs : Run.numSaturatingPushes R.run ≤ 4 * Fintype.card V * numEdges G :=
Run.saturating_push_count_bound R.run
have hn : Run.numNonsaturatingPushes R.run ≤
(Run.numRelabels R.run + 1) * Fintype.card V := nonsaturating_push_count_bound R
nlinarith
The relabel-to-front algorithm performs at most 9|V|³ basic operations.
theorem step_count_bound_V3 (R : RelabelToFrontRun V G n) :
n ≤ 9 * Fintype.card V * Fintype.card V * Fintype.card V := by
have h := step_count_bound R
have hE : numEdges G ≤ Fintype.card V * Fintype.card V := numEdges_le_card_mul_card G
have hV : 1 ≤ Fintype.card V := Fintype.card_pos_iff.mpr ⟨G.s⟩
nlinarithend RelabelToFrontRunend Chapter26end CLRSDefinitions and proofs
CLRSLean.FourthEdition.Chapter_24.Section_24_5_Relabel_To_Front.Execution
An initialized relabel-to-front execution with persistent current-neighbor lists.
Initialization writes the complete flow table and height/excess/cursor tables. The scheduler scans inactive entries and rejected neighbors, pushes using cached excess and two indexed arc writes, and relabels with a counted minimum scan. A reversed processed prefix supports constant-cost discharge completion; moving it to the front uses a counted reverse-onto loop. Relabeling immediately moves the current vertex to the front, where its discharge continues.
The ordering, quiet-prefix, and skipped-neighbor invariants are preserved by the controller itself. Its operation trace therefore satisfies the native discharge discipline without an input certificate. The native cubic operation bound proves that the concrete fuelled run terminates; its returned preflow is a maximum flow.
The work theorem counts actual initialization writes, controller/cursor visits, minimum-scan visits, and moved list cells from this run. The scalar/indexed RAM charge assigns a fixed 32-unit allowance to each such event. Exact real arithmetic and indexed tables are primitives in this model; this is not a persistent Lean function-evaluator time bound, a mutable-array refinement, or a bit-complexity claim for arbitrary real capacities.
namespace CLRS.Chapter26.RelabelExecutionopen Finset Classicalset_option backward.isDefEq.respectTransparency falsevariable {V : Type*} [Fintype V] [DecidableEq V] {G : FlowNetwork V}noncomputable def initialFunction (G : FlowNetwork V) (u v : V) : ℝ :=
if u = G.s then G.c G.s v else if v = G.s then -G.c G.s u else 0theorem initialFunction_skew (G : FlowNetwork V) (u v : V) :
initialFunction G u v = -initialFunction G v u := by
by_cases hu : u = G.s <;> by_cases hv : v = G.s <;>
simp [initialFunction, hu, hv, G.hc_self]theorem initialFunction_capacity (G : FlowNetwork V) (u v : V) :
initialFunction G u v ≤ G.c u v := by
by_cases hu : u = G.s
· subst u; simp [initialFunction]
· by_cases hv : v = G.s
· subst v
simp only [initialFunction, if_neg hu, ↓reduceIte]
linarith [G.hc_nonneg G.s u, G.hc_nonneg u G.s]
· simpa [initialFunction, hu, hv] using G.hc_nonneg u vtheorem initialFunction_excess (G : FlowNetwork V) (u : V) (hu : u ≠ G.s) :
netInflow (initialFunction G) u = G.c G.s u := by
unfold netInflow
simp [initialFunction, hu]Indexed writes over an explicit finite enumeration. Counter increments occur at the same recursive nodes that install the returned cells.
def writeCells {ι α : Type*} [DecidableEq ι] (value : ι → α) (base : ι → α) :
List ι → (ι → α) × Nat
| [] => (base, 0)
| i :: is =>
let tail := writeCells value base is
(Function.update tail.1 i (value i), tail.2 + 1)@[simp] theorem writeCells_count {ι α : Type*} [DecidableEq ι]
(value base : ι → α) (is : List ι) : (writeCells value base is).2 = is.length := by
induction is with
| nil => rfl
| cons i is ih => simpa [writeCells] using ihtheorem writeCells_value {ι α : Type*} [DecidableEq ι]
(value base : ι → α) (is : List ι) (a : ι) :
(writeCells value base is).1 a = if a ∈ is then value a else base a := by
induction is with
| nil => simp [writeCells]
| cons i is ih => by_cases h : a = i <;> simp [writeCells, Function.update, h, ih]noncomputable def tabulate {ι α : Type*} [Fintype ι] [DecidableEq ι]
(value : ι → α) (default : α) : (ι → α) × Nat :=
writeCells value (fun _ => default) univ.toList@[simp] theorem tabulate_value {ι α : Type*} [Fintype ι] [DecidableEq ι]
(value : ι → α) (default : α) : (tabulate value default).1 = value := by
funext a; simp [tabulate, writeCells_value]@[simp] theorem tabulate_count {ι α : Type*} [Fintype ι] [DecidableEq ι]
(value : ι → α) (default : α) : (tabulate value default).2 = Fintype.card ι := by
simp [tabulate]noncomputable def initialCells (G : FlowNetwork V) : (V × V → ℝ) × Nat :=
tabulate (fun p => initialFunction G p.1 p.2) 0@[simp] theorem initialCells_value (G : FlowNetwork V) (u v : V) :
(initialCells G).1 (u,v) = initialFunction G u v := by simp [initialCells]
noncomputable def initialPreflow (G : FlowNetwork V) : Preflow V G where
f := fun u v => (initialCells G).1 (u,v)
hcapacity := by simpa using initialFunction_capacity G
hskew_symm := by simpa using initialFunction_skew G
hexcess_nonneg := by
intro u hu
simp only [initialCells_value]
rw [initialFunction_excess G u hu]
exact G.hc_nonneg G.s udef initialHeight (G : FlowNetwork V) (u : V) : Nat := if u = G.s then Fintype.card V else 0
theorem initial_valid (G : FlowNetwork V) : IsValidHeight (initialPreflow G) (initialHeight G) := by
refine ⟨by simp [initialHeight], by simp [initialHeight, Ne.symm G.hs_ne_t], ?_⟩
intro u v hr
by_cases hu : u = G.s
· subst u
have hz : (initialPreflow G).residualCapacity G.s v = 0 := by
simp [Preflow.residualCapacity, initialPreflow, initialFunction]
exact (show False from by change 0 < _ at hr; rw [hz] at hr; linarith).elim
· simp [initialHeight, hu]Push changes only its two endpoint excesses.
theorem push_excess (φ : Preflow V G) (u v : V) (hu : u ≠ G.s)
(hr : φ.residualEdge u v) (a : V) :
(push φ u v hu hr).excess a = φ.excess a +
(if a = v then min (φ.excess u) (φ.residualCapacity u v) else 0) -
(if a = u then min (φ.excess u) (φ.residualCapacity u v) else 0) := by
unfold push pushBy Preflow.excess netInflow
rw [Finset.sum_add_distrib]
have hed (δ : ℝ) : ∑ x : V, Flow.edgeDelta δ u v x a =
(if a = v then δ else 0) - (if a = u then δ else 0) := by
unfold Flow.edgeDelta
rw [Finset.sum_sub_distrib]
have hf : (∑ x : V, if x = u ∧ a = v then δ else 0) = (if a = v then δ else 0) := by
by_cases ha : a = v
· subst a; simp
· simp [ha]
have hg : (∑ x : V, if x = v ∧ a = u then δ else 0) = (if a = u then δ else 0) := by
by_cases ha : a = u
· subst a; simp
· simp [ha]
rw [hf, hg]
rw [hed]
ringtheorem push_valid (φ : Preflow V G) (h : V → Nat) (hv : IsValidHeight φ h)
(u v : V) (hu : φ.isOverflowing u) (hr : φ.residualEdge u v)
(ha : h u = h v + 1) : IsValidHeight (push φ u v hu.1 hr) h := by
unfold push
exact pushBy_validHeight φ h hv _ _ _ _ _ _ _ hatheorem push_new_admissible (φ : Preflow V G) (h : V → Nat)
(u v : V) (hu : φ.isOverflowing u) (hr : φ.residualEdge u v)
(ha : h u = h v + 1) (a b : V)
(hab : admissibleEdge (push φ u v hu.1 hr) h a b) : admissibleEdge φ h a b := by
refine ⟨?_, hab.2⟩
by_contra hn
have hnew := pushBy_new_residualEdge φ u v (residualEdge_ne φ hr)
(min (φ.excess u) (φ.residualCapacity u v))
(le_min (φ.hexcess_nonneg u hu.1) (le_of_lt hr))
(min_le_left _ _) (min_le_right _ _) hab.1 hn
rcases hnew with ⟨rfl, rfl⟩
have hh := hab.2
omegaA skipped neighbor stays ineligible until this source is relabeled.
def CursorInvariant (φ : Preflow V G) (h : V → Nat) (cursor : V → List V) : Prop :=
∀ u v, v ∉ cursor u → φ.residualEdge u v → h u ≤ h vtheorem cursor_push (φ : Preflow V G) (h : V → Nat) (cursor : V → List V)
(hc : CursorInvariant φ h cursor) (u v : V) (hu : φ.isOverflowing u)
(hr : φ.residualEdge u v) (ha : h u = h v + 1) :
CursorInvariant (push φ u v hu.1 hr) h cursor := by
intro a b hb hnew
by_cases hold : φ.residualEdge a b
· exact hc a b hb hold
· have hrev := pushBy_new_residualEdge φ u v (residualEdge_ne φ hr)
(min (φ.excess u) (φ.residualCapacity u v))
(le_min (φ.hexcess_nonneg u hu.1) (le_of_lt hr))
(min_le_left _ _) (min_le_right _ _) hnew hold
rcases hrev with ⟨rfl, rfl⟩
omega
theorem cursor_relabel (φ : Preflow V G) (h : V → Nat) (cursor : V → List V)
(hc : CursorInvariant φ h cursor) (u : V) (hr : ∃ v, φ.residualEdge u v)
(hpre : ∀ v, φ.residualEdge u v → h u ≤ h v) :
CursorInvariant φ (relabel φ h u hr) (Function.update cursor u univ.toList) := by
intro a b hb hab
by_cases ha : a = u
· subst a; simp at hb
· have hold : b ∉ cursor a := by simpa [Function.update, ha] using hb
have hle := hc a b hold hab
have hup := (relabel_height_increase φ h u hr hpre).le
rw [relabel_eq_of_ne φ h u hr ha]
by_cases hbu : b = u
· subst b; exact hle.trans hup
· rwa [relabel_eq_of_ne φ h u hr hbu]def Internal (G : FlowNetwork V) (u : V) : Prop := u ≠ G.s ∧ u ≠ G.tdef Ordered (φ : Preflow V G) (h : V → Nat) (L : List V) : Prop :=
L.Pairwise (fun a b => ¬ admissibleEdge φ h b a)structure Machine (G : FlowNetwork V) where
φ : Preflow V G
h : V → Nat
excessCache : V → ℝ
cache_correct : ∀ u, u ≠ G.s → excessCache u = φ.excess u
valid : IsValidHeight φ h
past : List V
todo : List V
nodup : (past.reverse ++ todo).Nodup
complete : ∀ v, v ∈ past.reverse ++ todo ↔ Internal G v
ordered : Ordered φ h (past.reverse ++ todo)
quiet : ∀ v ∈ past.reverse, ¬ φ.isOverflowing v
cursor : V → List V
cursor_bound : ∀ v, (cursor v).length ≤ Fintype.card V
skipped : CursorInvariant φ h cursordef Machine.done (s : Machine G) : List V := s.past.reversenoncomputable def selectInternal (G : FlowNetwork V) : List V → List V × Nat
| [] => ([], 0)
| u :: us =>
let tail := selectInternal G us
(if Internal G u then u :: tail.1 else tail.1, tail.2 + 1)@[simp] theorem selectInternal_value (G : FlowNetwork V) (xs : List V) :
(selectInternal G xs).1 = xs.filter (fun v => decide (Internal G v)) := by
induction xs with
| nil => rfl
| cons u us ih => by_cases hu : Internal G u <;> simp [selectInternal, hu, ih]@[simp] theorem selectInternal_count (G : FlowNetwork V) (xs : List V) :
(selectInternal G xs).2 = xs.length := by
induction xs with
| nil => rfl
| cons u us ih => simpa [selectInternal] using ih
noncomputable def initialMachine (G : FlowNetwork V) : Machine G where
φ := initialPreflow G
h := (tabulate (initialHeight G) 0).1
excessCache := (tabulate (G.c G.s) 0).1
cache_correct := by intro u hu; simpa [initialPreflow, Preflow.excess] using (initialFunction_excess G u hu).symm
valid := by simpa using initial_valid G
past := []
todo := (selectInternal G univ.toList).1
nodup := by simpa using (Finset.nodup_toList (univ : Finset V)).filter _
complete := by simp
ordered := by
simp only [List.reverse_nil, List.nil_append, selectInternal_value]
apply List.pairwise_iff_getElem.mpr
intro i j hi hj hij hadm
have hia : (univ.toList.filter (fun v => decide (Internal G v)))[i] ≠ G.s := by
have hm := List.getElem_mem hi
exact (of_decide_eq_true (List.mem_filter.mp hm).2).1
have hja : (univ.toList.filter (fun v => decide (Internal G v)))[j] ≠ G.s := by
have hm := List.getElem_mem hj
exact (of_decide_eq_true (List.mem_filter.mp hm).2).1
have he := hadm.2
simp only [tabulate_value] at he
change (if (univ.toList.filter (fun v => decide (Internal G v)))[j] = G.s then Fintype.card V else 0) =
(if (univ.toList.filter (fun v => decide (Internal G v)))[i] = G.s then Fintype.card V else 0) + 1 at he
rw [if_neg hja, if_neg hia] at he
omega
quiet := by simp
cursor := (tabulate (fun _ : V => (univ.toList : List V)) []).1
cursor_bound := by simp
skipped := by intro u v hv; simp at hvnoncomputable def credit (s : Machine G) : Nat :=
s.todo.length + ∑ v, (s.cursor v).lengththeorem credit_initial (G : FlowNetwork V) :
credit (initialMachine G) ≤ Fintype.card V + Fintype.card V * Fintype.card V := by
have hl := List.length_filter_le (fun v => decide (Internal G v)) (univ.toList : List V)
simp only [Finset.length_toList, Finset.card_univ] at hl
simpa [credit, initialMachine] using Nat.add_le_add_right hl (Fintype.card V * Fintype.card V)theorem ordered_push (s : Machine G) (u v : V) (hu : s.φ.isOverflowing u)
(hr : s.φ.residualEdge u v) (ha : s.h u = s.h v + 1) :
Ordered (push s.φ u v hu.1 hr) s.h (s.done ++ s.todo) := by
apply s.ordered.imp
intro a b hold hn
exact hold (push_new_admissible s.φ s.h u v hu hr ha b a hn)
theorem current_not_done (s : Machine G) {u : V} {us : List V} (ht : s.todo = u :: us) :
u ∉ s.done := by
have hn := s.nodup
rw [ht, List.nodup_append] at hn
intro hu
exact hn.2.2 u hu u (by simp) rfltheorem current_internal (s : Machine G) {u : V} {us : List V} (ht : s.todo = u :: us) :
Internal G u := (s.complete u).1 (by simp [ht])
theorem push_quiet_done (s : Machine G) {u : V} {us : List V} (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) :
∀ a ∈ s.done, ¬ (push s.φ u v hu.1 hr).isOverflowing a := by
intro a haD hover
have hau : a ≠ u := by intro h; subst a; exact current_not_done s ht haD
have hav : a ≠ v := by
intro h; subst a
have hord := List.pairwise_append.mp s.ordered
exact hord.2.2 v haD u (by simp [ht]) ⟨hr, ha⟩
have he : (push s.φ u v hu.1 hr).excess a = s.φ.excess a := by
rw [push_excess]; simp [hau, hav]
exact s.quiet a haD ⟨hover.1, hover.2.1, by simpa [he] using hover.2.2⟩noncomputable def skipCurrent (s : Machine G) (u : V) (us : List V)
(ht : s.todo = u :: us) (hq : ¬ s.φ.isOverflowing u) : Machine G :=
{ s with
past := u :: s.past
todo := us
nodup := by simpa [ht, Machine.done, List.reverse_cons, List.append_assoc] using s.nodup
complete := by intro v; simpa [ht, Machine.done, List.reverse_cons, List.append_assoc] using s.complete v
ordered := by simpa [ht, Machine.done, List.reverse_cons, List.append_assoc] using s.ordered
quiet := by
intro v hv
simp only [List.reverse_cons] at hv
rcases List.mem_append.mp hv with hv | hv
· exact s.quiet v hv
· simpa using (List.mem_singleton.mp hv) ▸ hq }theorem credit_skip (s : Machine G) (u : V) (us : List V)
(ht : s.todo = u :: us) (hq : ¬ s.φ.isOverflowing u) :
credit (skipCurrent s u us ht hq) + 1 = credit s := by
simp [credit, skipCurrent, ht]
omega
noncomputable def advanceCursor (s : Machine G) (u v : V) (vs : List V)
(hc : s.cursor u = v :: vs) (hn : ¬ admissibleEdge s.φ s.h u v) : Machine G :=
{ s with
cursor := Function.update s.cursor u vs
cursor_bound := by
intro a
by_cases ha : a = u
· subst a; simp only [Function.update_self]
have hb := s.cursor_bound u; rw [hc] at hb; simpa using Nat.le_trans (Nat.le_succ _) hb
· simpa [Function.update, ha] using s.cursor_bound a
skipped := by
intro a b hb hr
by_cases ha : a = u
· subst a
simp only [Function.update_self] at hb
by_cases hbv : b = v
· subst b
have hv := s.valid.2.2 u v hr
have hne : s.h u ≠ s.h v + 1 := fun he => hn ⟨hr, he⟩
omega
· apply s.skipped u b ?_ hr
simp [hc, hb, hbv]
· apply s.skipped a b ?_ hr
simpa [Function.update, ha] using hb }
theorem credit_advanceCursor (s : Machine G) (u v : V) (vs : List V)
(hc : s.cursor u = v :: vs) (hn : ¬ admissibleEdge s.φ s.h u v) :
credit (advanceCursor s u v vs hc hn) + 1 = credit s := by
unfold credit
have hfun : (fun a => ((advanceCursor s u v vs hc hn).cursor a).length) =
Function.update (fun a => (s.cursor a).length) u vs.length := by
funext a
by_cases ha : a = u <;> simp [advanceCursor, Function.update, ha]
rw [hfun, Finset.sum_update_of_mem (Finset.mem_univ u)]
have hs := Finset.sum_erase_add (univ : Finset V) (fun a => (s.cursor a).length) (mem_univ u)
rw [hc] at hs
simp only [List.length_cons] at hs
simp only [advanceCursor]
rw [Finset.sdiff_singleton_eq_erase]
omega
theorem ordered_relabel (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(hr : ∃ v, s.φ.residualEdge u v)
(hp : ∀ v, s.φ.residualEdge u v → s.h u ≤ s.h v) :
Ordered s.φ (relabel s.φ s.h u hr) (u :: (s.done ++ us)) := by
have hn : (u :: (s.done ++ us)).Nodup := List.perm_middle.nodup (by simpa [ht, Machine.done] using s.nodup)
have hnot := (List.nodup_cons.mp hn).1
have ho : Ordered s.φ s.h (s.done ++ us) := by
exact s.ordered.sublist (by rw [ht]; exact List.Sublist.append (List.Sublist.refl _) (List.sublist_cons_self _ _))
apply List.Pairwise.cons
· intro a ha hadm
have hau : a ≠ u := by intro he; subst a; exact hnot ha
have old := s.valid.2.2 a u hadm.1
have inc := relabel_height_increase s.φ s.h u hr hp
have eqn := hadm.2
rw [relabel_eq_of_ne s.φ s.h u hr hau] at eqn
omega
· apply List.Pairwise.imp_of_mem _ ho
intro a b ha hb hold hnew
have hau : a ≠ u := by intro he; subst a; exact hnot ha
have hbu : b ≠ u := by intro he; subst b; exact hnot hb
apply hold
refine ⟨hnew.1, ?_⟩
simpa only [relabel_eq_of_ne s.φ s.h u hr hau,
relabel_eq_of_ne s.φ s.h u hr hbu] using hnew.2A literal residual-neighbor scan: one candidate visit at every list cell.
noncomputable def scanMinimum (φ : Preflow V G) (h : V → Nat) (u : V) :
List V → WithTop Nat × Nat
| [] => (⊤, 0)
| v :: vs =>
let tail := scanMinimum φ h u vs
(if φ.residualEdge u v then min (h v : WithTop Nat) tail.1 else tail.1, tail.2 + 1)@[simp] theorem scanMinimum_visits (φ : Preflow V G) (h : V → Nat) (u : V) (vs : List V) :
(scanMinimum φ h u vs).2 = vs.length := by
induction vs with
| nil => rfl
| cons v vs ih => simpa [scanMinimum] using ihtheorem scanMinimum_value (φ : Preflow V G) (h : V → Nat) (u : V) (vs : List V) :
(scanMinimum φ h u vs).1 = ((vs.filter fun v => decide (φ.residualEdge u v)).map h).minimum := by
induction vs with
| nil => simp [scanMinimum]
| cons v vs ih => by_cases hv : φ.residualEdge u v <;> simp [scanMinimum, hv, ih, List.minimum_cons]noncomputable def scannedRelabel (φ : Preflow V G) (h : V → Nat) (u : V) : V → Nat :=
let least := (scanMinimum φ h u univ.toList).1.untopD 0
Function.update h u (1 + least)
theorem scannedRelabel_eq (φ : Preflow V G) (h : V → Nat) (u : V)
(hr : ∃ v, φ.residualEdge u v) : scannedRelabel φ h u = relabel φ h u hr := by
let S := ((univ : Finset V).filter (fun v => φ.residualEdge u v)).image h
have hS : S.Nonempty := by
obtain ⟨v, hv⟩ := hr
exact ⟨h v, mem_image.mpr ⟨v, mem_filter.mpr ⟨mem_univ _, hv⟩, rfl⟩⟩
have heq : (scanMinimum φ h u univ.toList).1 = (S.min' hS : WithTop Nat) := by
rw [scanMinimum_value]
apply (List.minimum_eq_coe_iff (m := S.min' hS)).mpr
constructor
· obtain ⟨v, hv, he⟩ := mem_image.mp (Finset.min'_mem S hS)
exact List.mem_map.mpr ⟨v, List.mem_filter.mpr ⟨by simp, by simpa using (mem_filter.mp hv).2⟩, he⟩
· intro a ha
obtain ⟨v, hv, rfl⟩ := List.mem_map.mp ha
exact Finset.min'_le S _ (mem_image.mpr ⟨v, mem_filter.mpr ⟨mem_univ _,
by simpa using (List.mem_filter.mp hv).2⟩, rfl⟩)
funext a
by_cases hau : a = u
· subst a
simp only [scannedRelabel, heq, Function.update_self]
calc
_ = 1 + S.min' hS := congrArg (1 + ·) (WithTop.untopD_coe 0 (S.min' hS))
_ = _ := by unfold relabel; simp only [↓reduceIte]; rfl
· simp [scannedRelabel, hau, relabel_eq_of_ne φ h u hr hau]Reverse the processed prefix directly onto the remaining suffix. Each visited cell performs one cons; no repeated append traversal is hidden in a discharge.
def reverseOnto {α : Type*} : List α → List α → List α × Nat
| [], acc => (acc, 0)
| a :: as, acc =>
let tail := reverseOnto as (a :: acc)
(tail.1, tail.2 + 1)@[simp] theorem reverseOnto_value {α : Type*} (xs acc : List α) :
(reverseOnto xs acc).1 = xs.reverse ++ acc := by
induction xs generalizing acc with
| nil => simp [reverseOnto]
| cons a as ih => simp [reverseOnto, ih, List.reverse_cons, List.append_assoc]@[simp] theorem reverseOnto_count {α : Type*} (xs acc : List α) :
(reverseOnto xs acc).2 = xs.length := by
induction xs generalizing acc with
| nil => rfl
| cons a as ih => simp [reverseOnto, ih]
noncomputable def relabelCurrent (s : Machine G) (u : V) (us : List V)
(ht : s.todo = u :: us) (hu : s.φ.isOverflowing u)
(hr : ∃ v, s.φ.residualEdge u v)
(hp : ∀ v, s.φ.residualEdge u v → s.h u ≤ s.h v) : Machine G where
φ := s.φ
h := scannedRelabel s.φ s.h u
excessCache := s.excessCache
cache_correct := s.cache_correct
valid := by rw [scannedRelabel_eq _ _ _ hr]; exact relabel_validHeight s.φ s.h s.valid u hu.1 hu.2.1 hr hp
past := []
todo := u :: (reverseOnto s.past us).1
nodup := by simpa only [List.reverse_nil, List.nil_append, reverseOnto_value, Machine.done] using List.perm_middle.nodup (by simpa [ht, Machine.done] using s.nodup)
complete := by intro a; simpa [ht, Machine.done, List.mem_append, or_assoc, or_left_comm] using s.complete a
ordered := by rw [scannedRelabel_eq _ _ _ hr]; simpa [Machine.done] using ordered_relabel s u us ht hr hp
quiet := by simp
cursor := Function.update s.cursor u univ.toList
cursor_bound := by intro a; by_cases ha : a = u <;> simp [Function.update, ha, s.cursor_bound]
skipped := by rw [scannedRelabel_eq _ _ _ hr]; exact cursor_relabel s.φ s.h s.cursor s.skipped u hr hp
theorem credit_relabel (s : Machine G) (u : V) (us : List V)
(ht : s.todo = u :: us) (hu : s.φ.isOverflowing u)
(hr : ∃ v, s.φ.residualEdge u v)
(hp : ∀ v, s.φ.residualEdge u v → s.h u ≤ s.h v) :
credit (relabelCurrent s u us ht hu hr hp) ≤ credit s + 2 * Fintype.card V := by
have hl := List.Nodup.length_le_card s.nodup
rw [ht] at hl
have hsum := Finset.sum_erase_add (univ : Finset V) (fun a => (s.cursor a).length) (mem_univ u)
have hfun : (fun a => ((relabelCurrent s u us ht hu hr hp).cursor a).length) =
Function.update (fun a => (s.cursor a).length) u (Fintype.card V) := by
funext a; by_cases ha : a = u <;> simp [relabelCurrent, Function.update, ha]
unfold credit
rw [hfun, Finset.sum_update_of_mem (mem_univ u), Finset.sdiff_singleton_eq_erase]
simp only [relabelCurrent, ht, List.length_cons, reverseOnto_value, List.length_append] at *
omegadef pushCells (f : V → V → ℝ) (u v : V) (δ : ℝ) : V → V → ℝ :=
Function.update (Function.update f u (Function.update (f u) v (f u v + δ)))
v (Function.update (f v) u (f v u - δ))
theorem pushCells_eq (f : V → V → ℝ) (u v : V) (huv : u ≠ v) (δ : ℝ) :
pushCells f u v δ = fun a b => f a b + Flow.edgeDelta δ u v a b := by
funext a b
by_cases hau : a = u <;> by_cases hav : a = v <;>
by_cases hbu : b = u <;> by_cases hbv : b = v <;>
simp_all [pushCells, Function.update, Flow.edgeDelta] <;> ringtheorem preflow_ext {φ ψ : Preflow V G} (h : φ.f = ψ.f) : φ = ψ := by
cases φ; cases ψ; cases h; rfl
noncomputable def cachedPush (s : Machine G) (u v : V) (hu : u ≠ G.s)
(hr : s.φ.residualEdge u v) : Preflow V G := by
let δ := min (s.excessCache u) (s.φ.residualCapacity u v)
let φ' := pushBy s.φ u v (residualEdge_ne s.φ hr) δ
(by dsimp [δ]; rw [s.cache_correct u hu]; exact le_min (s.φ.hexcess_nonneg u hu) hr.le)
(by dsimp [δ]; rw [s.cache_correct u hu]; exact min_le_left _ _)
(by dsimp [δ]; exact min_le_right _ _)
let f' := pushCells s.φ.f u v δ
have hf : f' = φ'.f := pushCells_eq s.φ.f u v (residualEdge_ne s.φ hr) δ
exact { f := f'
hcapacity := by rw [hf]; exact φ'.hcapacity
hskew_symm := by rw [hf]; exact φ'.hskew_symm
hexcess_nonneg := by rw [hf]; exact φ'.hexcess_nonneg }@[simp] theorem cachedPush_eq (s : Machine G) (u v : V) (hu : u ≠ G.s)
(hr : s.φ.residualEdge u v) : cachedPush s u v hu hr = push s.φ u v hu hr := by
apply preflow_ext
simp [cachedPush, push, pushBy, pushCells_eq _ _ _ (residualEdge_ne s.φ hr), s.cache_correct u hu]noncomputable def pushedCache (s : Machine G) (u v : V) : V → ℝ :=
let δ := min (s.excessCache u) (s.φ.residualCapacity u v)
Function.update (Function.update s.excessCache u (s.excessCache u - δ)) v (s.excessCache v + δ)
theorem pushedCache_correct (s : Machine G) (u v : V) (hu : u ≠ G.s)
(hr : s.φ.residualEdge u v) (a : V) (has : a ≠ G.s) :
pushedCache s u v a = (cachedPush s u v hu hr).excess a := by
rw [cachedPush_eq, push_excess]
have huv := residualEdge_ne s.φ hr
by_cases hau : a = u <;> by_cases hav : a = v <;>
simp_all [pushedCache, Function.update, s.cache_correct]
noncomputable def pushCurrent (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) : Machine G := by
let φ' := cachedPush s u v hu.1 hr
have hvalid : IsValidHeight φ' s.h := by simpa [φ'] using push_valid s.φ s.h s.valid u v hu hr ha
have hord : Ordered φ' s.h (s.done ++ s.todo) := by simpa [φ'] using ordered_push s u v hu hr ha
have hquiet : ∀ a ∈ s.done, ¬ φ'.isOverflowing a := by simpa [φ'] using push_quiet_done s ht v hu hr ha
have hskip : CursorInvariant φ' s.h s.cursor := by simpa [φ'] using cursor_push s.φ s.h s.cursor s.skipped u v hu hr ha
exact if hnCache : s.excessCache u < s.φ.residualCapacity u v then
let hn : s.φ.excess u < s.φ.residualCapacity u v := by simpa [s.cache_correct u hu.1] using hnCache
{ φ := φ', h := s.h, valid := hvalid
excessCache := pushedCache s u v
cache_correct := pushedCache_correct s u v hu.1 hr
past := u :: s.past, todo := us
nodup := by simpa [ht, Machine.done, List.reverse_cons, List.append_assoc] using s.nodup
complete := by intro a; simpa [ht, Machine.done, List.reverse_cons, List.append_assoc] using s.complete a
ordered := by simpa [ht, Machine.done, List.reverse_cons, List.append_assoc] using hord
quiet := by
intro a hm
simp only [List.reverse_cons] at hm
rcases List.mem_append.mp hm with hm | hm
· exact hquiet a hm
· have hau : a = u := List.mem_singleton.mp hm
subst a
intro hover
have hex : φ'.excess u = 0 := by
dsimp [φ']
rw [cachedPush_eq, push_excess]
simp [residualEdge_ne s.φ hr, min_eq_left hn.le]
have hx := hover.2.2
rw [hex] at hx
linarith
cursor := s.cursor, cursor_bound := s.cursor_bound, skipped := hskip }
else
{ φ := φ', h := s.h, valid := hvalid
excessCache := pushedCache s u v
cache_correct := pushedCache_correct s u v hu.1 hr
past := s.past, todo := s.todo, nodup := s.nodup, complete := s.complete,
ordered := hord, quiet := hquiet
cursor := s.cursor, cursor_bound := s.cursor_bound, skipped := hskip }theorem pushCurrent_φ (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) :
(pushCurrent s u us ht v hu hr ha).φ = push s.φ u v hu.1 hr := by
by_cases hn : s.φ.excess u < s.φ.residualCapacity u v <;> simp [pushCurrent, s.cache_correct u hu.1, hn]theorem pushCurrent_h (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) : (pushCurrent s u us ht v hu hr ha).h = s.h := by
by_cases hn : s.φ.excess u < s.φ.residualCapacity u v <;> simp [pushCurrent, s.cache_correct u hu.1, hn]theorem pushCurrent_done_mono (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) :
∀ a ∈ s.done, a ∈ (pushCurrent s u us ht v hu hr ha).done := by
by_cases hn : s.φ.excess u < s.φ.residualCapacity u v <;> intro a hm
all_goals simp only [Machine.done, List.mem_reverse] at hm
all_goals simp [pushCurrent, s.cache_correct u hu.1, hn, Machine.done, hm]theorem pushCurrent_nonsat_done (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) (hn : s.φ.excess u < s.φ.residualCapacity u v) :
u ∈ (pushCurrent s u us ht v hu hr ha).done := by simp [pushCurrent, s.cache_correct u hu.1, hn, Machine.done]theorem credit_push (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) :
credit (pushCurrent s u us ht v hu hr ha) ≤ credit s := by
by_cases hn : s.φ.excess u < s.φ.residualCapacity u v <;> simp [pushCurrent, s.cache_correct u hu.1, hn, credit, ht]One executed basic operation, including its preceding cursor/list scans.
structure BasicResult (s : Machine G) where
op : BasicOp V G
after : Machine G
beforeφ : op.beforeφ = s.φ
beforeh : op.beforeh = s.h
resultφ : op.resultφ = after.φ
resulth : op.resulth = after.h
scans : Nat
moves : Nat
minimumVisits : Nat
minimumVisits_eq : minimumVisits = Fintype.card V * (if op.isRelabel then 1 else 0)
scan_credit : scans + credit after ≤ credit s + 2 * Fintype.card V * (if op.isRelabel then 1 else 0)
move_bound : moves ≤ 2 * Fintype.card V * (if op.isRelabel then 1 else 0)
done_mono : op.isRelabel = false → ∀ a ∈ s.done, a ∈ after.done
nonsat_done : op.isNonsaturatingPush = true → op.opVertex ∈ after.done
source_fresh : op.opVertex ∉ s.donenoncomputable def relabelResult (s : Machine G) (u : V) (us : List V)
(ht : s.todo = u :: us) (hu : s.φ.isOverflowing u)
(hr : ∃ v, s.φ.residualEdge u v)
(hp : ∀ v, s.φ.residualEdge u v → s.h u ≤ s.h v) : BasicResult s where
op := .relabel s.φ s.h u hu hr hp
after := relabelCurrent s u us ht hu hr hp
beforeφ := rfl
beforeh := rfl
resultφ := rfl
resulth := (scannedRelabel_eq _ _ _ hr).symm
scans := 0
moves := (reverseOnto s.past us).2
minimumVisits := (scanMinimum s.φ s.h u univ.toList).2
minimumVisits_eq := by simp [BasicOp.isRelabel]
scan_credit := by simpa [BasicOp.isRelabel] using credit_relabel s u us ht hu hr hp
move_bound := by
have hl := List.Nodup.length_le_card s.nodup
simp only [List.length_append, List.length_reverse] at hl
simp only [BasicOp.isRelabel, ↓reduceIte, Nat.mul_one, reverseOnto_count]
omega
done_mono := by simp [BasicOp.isRelabel]
nonsat_done := by simp [BasicOp.isNonsaturatingPush]
source_fresh := current_not_done s ht
noncomputable def pushResult (s : Machine G) (u : V) (us : List V) (ht : s.todo = u :: us)
(v : V) (hu : s.φ.isOverflowing u) (hr : s.φ.residualEdge u v)
(ha : s.h u = s.h v + 1) : BasicResult s where
op := .push s.φ s.h u v hu hr ha
after := pushCurrent s u us ht v hu hr ha
beforeφ := rfl
beforeh := rfl
resultφ := (pushCurrent_φ s u us ht v hu hr ha).symm
resulth := (pushCurrent_h s u us ht v hu hr ha).symm
scans := 0
moves := 0
minimumVisits := 0
minimumVisits_eq := by simp [BasicOp.isRelabel]
scan_credit := by simpa [BasicOp.isRelabel] using credit_push s u us ht v hu hr ha
move_bound := by simp
done_mono := by intro _; exact pushCurrent_done_mono s u us ht v hu hr ha
nonsat_done := by
intro hn
have hn' : s.φ.excess u < s.φ.residualCapacity u v := by
simpa [BasicOp.isNonsaturatingPush] using hn
exact pushCurrent_nonsat_done s u us ht v hu hr ha hn'
source_fresh := current_not_done s htPull a result back across one actual administrative cursor/list step.
noncomputable def BasicResult.prependScan {s t : Machine G}
(hp : t.φ = s.φ) (hh : t.h = s.h)
(hd : ∀ a ∈ s.done, a ∈ t.done) (hc : credit t + 1 = credit s)
(r : BasicResult t) : BasicResult s where
op := r.op
after := r.after
beforeφ := r.beforeφ.trans hp
beforeh := r.beforeh.trans hh
resultφ := r.resultφ
resulth := r.resulth
scans := r.scans + 1
moves := r.moves
minimumVisits := r.minimumVisits
minimumVisits_eq := r.minimumVisits_eq
scan_credit := by have h := r.scan_credit; omega
move_bound := r.move_bound
done_mono := by intro hn a ha; exact r.done_mono hn a (hd a ha)
nonsat_done := r.nonsat_done
source_fresh := by intro hs; exact r.source_fresh (hd _ hs)structure Finished (s : Machine G) where
quiet : ∀ u, ¬ s.φ.isOverflowing u
scans : Nat
scan_bound : scans ≤ credit s
noncomputable def Finished.prependScan {s t : Machine G}
(hp : t.φ = s.φ) (hc : credit t + 1 = credit s) (r : Finished t) : Finished s where
quiet := by rw [← hp]; exact r.quiet
scans := r.scans + 1
scan_bound := by have h := r.scan_bound; omegaabbrev Next (s : Machine G) := Finished s ⊕ BasicResult sScan current neighbors and inactive list entries until a basic operation or a completed flow is reached. Administrative steps strictly consume credit.
noncomputable def next (s : Machine G) : Next s :=
match ht : s.todo with
| [] => .inl
{ quiet := by
intro u hu
have hm : u ∈ s.done ++ s.todo := (s.complete u).2 ⟨hu.1, hu.2.1⟩
have hd : u ∈ s.done := by simpa [ht] using hm
exact s.quiet u hd hu
scans := 0
scan_bound := Nat.zero_le _ }
| u :: us =>
if huCache : 0 < s.excessCache u then
let hu : s.φ.isOverflowing u := ⟨(current_internal s ht).1, (current_internal s ht).2, by
simpa [s.cache_correct u (current_internal s ht).1] using huCache⟩
match hc : s.cursor u with
| [] =>
let hr := exists_residualEdge_of_overflowing s.φ u hu.1 hu.2.2
let hp : ∀ v, s.φ.residualEdge u v → s.h u ≤ s.h v :=
fun v hv => s.skipped u v (by simp [hc]) hv
.inr (relabelResult s u us ht hu hr hp)
| v :: vs =>
if ha : admissibleEdge s.φ s.h u v then
.inr (pushResult s u us ht v hu ha.1 ha.2)
else
let t := advanceCursor s u v vs hc ha
match next t with
| .inl r => .inl (r.prependScan (s := s) (t := t) rfl (credit_advanceCursor s u v vs hc ha))
| .inr r => .inr (r.prependScan (s := s) (t := t) rfl rfl (fun _ hx => hx)
(credit_advanceCursor s u v vs hc ha))
else
let hu : ¬ s.φ.isOverflowing u := by
intro hover; exact huCache (by simpa [s.cache_correct u hover.1] using hover.2.2)
let t := skipCurrent s u us ht hu
match next t with
| .inl r => .inl (r.prependScan (s := s) (t := t) rfl (credit_skip s u us ht hu))
| .inr r => .inr (r.prependScan (s := s) (t := t) rfl rfl
(by intro a ha; simpa [Machine.done, t, skipCurrent, or_comm] using List.mem_cons_of_mem u (by simpa [Machine.done] using ha))
(credit_skip s u us ht hu))
termination_by credit s
decreasing_by
· have h := credit_advanceCursor s u v vs hc ha
omega
· have h := credit_skip s u us ht hu
omegainductive Trace : Machine G → Machine G → Nat → Type _
| nil (s : Machine G) : Trace s s 0
| cons {s t : Machine G} {n : Nat} (r : BasicResult s)
(tail : Trace r.after t n) : Trace s t (n + 1)namespace Tracenoncomputable def state {s t : Machine G} {n : Nat} (tr : Trace s t n) : Nat → Machine G :=
match tr with
| .nil s => fun _ => s
| .cons r tail => fun i => match i with | 0 => s | k + 1 => tail.state k@[simp] theorem state_zero {s t : Machine G} {n : Nat} (tr : Trace s t n) : tr.state 0 = s := by
cases tr <;> rfl@[simp] theorem state_end {s t : Machine G} {n : Nat} (tr : Trace s t n) : tr.state n = t := by
induction tr with
| nil => rfl
| cons r tail ih => exact ihnoncomputable def result {s t : Machine G} {n : Nat} (tr : Trace s t n)
(i : Nat) (hi : i < n) : BasicResult (tr.state i) :=
match tr with
| .nil _ => False.elim (by omega)
| .cons r tail => match i with
| 0 => r
| k + 1 => tail.result k (by omega)@[simp] theorem result_after {s t : Machine G} {n : Nat} (tr : Trace s t n)
(i : Nat) (hi : i < n) : (tr.result i hi).after = tr.state (i + 1) := by
induction tr generalizing i with
| nil => omega
| cons r tail ih =>
cases i with
| zero => simp [result, state]
| succ k => exact ih k (by omega)
noncomputable def toRun {s t : Machine G} {n : Nat} (tr : Trace s t n) : Run V G n where
φ i := (tr.state i).φ
h i := (tr.state i).h
hvalid i := (tr.state i).valid
op i hi := (tr.result i hi).op
hop_beforeφ i hi := (tr.result i hi).beforeφ
hop_beforeh i hi := (tr.result i hi).beforeh
hop_resultφ i hi := by rw [(tr.result i hi).resultφ, result_after]
hop_resulth i hi := by rw [(tr.result i hi).resulth, result_after]
theorem done_mono_interval {s t : Machine G} {n : Nat} (tr : Trace s t n)
{i j : Nat} (hij : i ≤ j) (hj : j ≤ n)
(hn : ∀ k (hk : k < n), i ≤ k → k < j → (tr.result k hk).op.isRelabel = false) :
∀ a ∈ (tr.state i).done, a ∈ (tr.state j).done := by
induction j with
| zero =>
have heq : i = 0 := by omega
subst i
exact fun _ h => h
| succ j ih =>
by_cases heq : i = j + 1
· subst i; exact fun _ h => h
· have hjn : j < n := by omega
have hmid := ih (by omega) (by omega) (by
intro k hk hik hkj; exact hn k hk hik (by omega))
intro a ha
have h := (tr.result j hjn).done_mono (hn j hjn (by omega) (by omega)) a (hmid a ha)
simpa using h
noncomputable def toRelabelToFrontRun {s t : Machine G} {n : Nat}
(tr : Trace s t n) : RelabelToFrontRun V G n where
run := tr.toRun
discharge_discipline := by
intro i j hi hj hij hni _ hnone heq
have hm := (tr.result i hi).nonsat_done hni
rw [result_after] at hm
have hmono := tr.done_mono_interval (i := i + 1) (j := j) (by omega) (by omega)
(by intro k hk hik hkj; exact hnone k hk (by omega) hkj)
have hin := hmono _ hm
have heq' : (tr.result i hi).op.opVertex = (tr.result j hj).op.opVertex := heq
rw [heq'] at hin
exact (tr.result j hj).source_fresh hintheorem length_bound {s t : Machine G} {n : Nat} (tr : Trace s t n) :
n ≤ 9 * Fintype.card V * Fintype.card V * Fintype.card V :=
tr.toRelabelToFrontRun.step_count_bound_V3noncomputable def scans {s t : Machine G} {n : Nat} : Trace s t n → Nat
| .nil _ => 0
| .cons r tail => r.scans + tail.scansnoncomputable def moves {s t : Machine G} {n : Nat} : Trace s t n → Nat
| .nil _ => 0
| .cons r tail => r.moves + tail.movesnoncomputable def relabels {s t : Machine G} {n : Nat} : Trace s t n → Nat
| .nil _ => 0
| .cons r tail => (if r.op.isRelabel then 1 else 0) + tail.relabelstheorem relabels_eq {s t : Machine G} {n : Nat} (tr : Trace s t n) :
tr.relabels = tr.toRun.numRelabels := by
induction tr with
| nil => simp [relabels, Run.numRelabels]
| cons r tail ih =>
simp only [relabels, Run.numRelabels, Fin.sum_univ_succ, Run.opFin, toRun, result]
congr 1theorem scans_credit {s t : Machine G} {n : Nat} (tr : Trace s t n) :
tr.scans + credit t ≤ credit s + 2 * Fintype.card V * tr.relabels := by
induction tr with
| nil => simp [scans, relabels]
| cons r tail ih =>
have h := r.scan_credit
simp only [scans, relabels, Nat.mul_add]
omegatheorem moves_bound {s t : Machine G} {n : Nat} (tr : Trace s t n) :
tr.moves ≤ 2 * Fintype.card V * tr.relabels := by
induction tr with
| nil => simp [moves, relabels]
| cons r tail ih =>
have h := r.move_bound
simp only [moves, relabels, Nat.mul_add]
omeganoncomputable def minimumVisits {s t : Machine G} {n : Nat} : Trace s t n → Nat
| .nil _ => 0
| .cons r tail => r.minimumVisits + tail.minimumVisitstheorem minimumVisits_eq {s t : Machine G} {n : Nat} (tr : Trace s t n) :
tr.minimumVisits = Fintype.card V * tr.relabels := by
induction tr with
| nil => simp [minimumVisits, relabels]
| cons r tail ih => simp [minimumVisits, relabels, r.minimumVisits_eq, ih, Nat.mul_add]end Tracestructure Execution (s : Machine G) (fuel : Nat) where
last : Machine G
steps : Nat
trace : Trace s last steps
terminal : Option (Finished last)
exhausted : terminal = none → steps = fuel
noncomputable def execute (s : Machine G) : (fuel : Nat) → Execution s fuel
| 0 => ⟨s, 0, .nil s, none, fun _ => rfl⟩
| fuel + 1 => match next s with
| .inl fin => ⟨s, 0, .nil s, some fin, by simp⟩
| .inr r =>
let tail := execute r.after fuel
{ last := tail.last
steps := tail.steps + 1
trace := .cons r tail.trace
terminal := tail.terminal
exhausted := by intro hn; rw [tail.exhausted hn] }def budget (V : Type*) [Fintype V] := 9 * Fintype.card V * Fintype.card V * Fintype.card V + 1
theorem execute_terminal (s : Machine G) : (execute s (budget V)).terminal ≠ none := by
intro hn
have h := (execute s (budget V)).trace.length_bound
rw [(execute s (budget V)).exhausted hn] at h
simp only [budget] at h
omeganoncomputable def completed (s : Machine G) : Finished (execute s (budget V)).last :=
(execute s (budget V)).terminal.get (Option.isSome_iff_ne_none.mpr (execute_terminal s))theorem completed_excess_zero (s : Machine G) (u : V) (hs : u ≠ G.s) (ht : u ≠ G.t) :
(execute s (budget V)).last.φ.excess u = 0 := by
have h := (completed s).quiet u
have hnonneg := (execute s (budget V)).last.φ.hexcess_nonneg u hs
by_contra hn
exact h ⟨hs, ht, lt_of_le_of_ne hnonneg (Ne.symm hn)⟩noncomputable def maximumFlow (s : Machine G) : Flow V G :=
(execute s (budget V)).last.φ.toFlow (completed_excess_zero s)theorem maximumFlow_isMaximal (s : Machine G) : (maximumFlow s).isMaximal :=
maximal_of_no_overflow _ _ (execute s (budget V)).last.valid (completed_excess_zero s)noncomputable def initializedFlow (G : FlowNetwork V) : Flow V G := maximumFlow (initialMachine G)theorem initialized_maximum_flow (G : FlowNetwork V) : (initializedFlow G).isMaximal :=
maximumFlow_isMaximal _Actual cursor advances/inactive-entry scans, plus terminal scans, list traversal on move-to-front, and a full vertex scan for each relabel. Pushes contribute one basic-operation unit. The scalar/indexed implementation expands these units below.
noncomputable def controllerWork (s : Machine G) : Nat :=
let r := execute s (budget V)
r.steps + r.trace.scans + (completed s).scans + r.trace.moves +
r.trace.minimumVisits
theorem controllerWork_bound (s : Machine G) :
controllerWork s ≤ 19 * Fintype.card V * Fintype.card V * Fintype.card V + credit s := by
let r := execute s (budget V)
have hn := r.trace.length_bound
have hscan := r.trace.scans_credit
have hfin := (completed s).scan_bound
have hmove := r.trace.moves_bound
have hrel : r.trace.relabels ≤ 2 * Fintype.card V * Fintype.card V := by
rw [Trace.relabels_eq]; exact r.trace.toRun.relabel_count_bound
change r.steps + r.trace.scans + (completed s).scans + r.trace.moves +
r.trace.minimumVisits ≤ _
rw [Trace.minimumVisits_eq]
change (completed s).scans ≤ credit r.last at hfin
nlinariththeorem initialized_controllerWork_le (G : FlowNetwork V) :
controllerWork (initialMachine G) ≤ 21 * Fintype.card V * Fintype.card V * Fintype.card V := by
have h := controllerWork_bound (initialMachine G)
have hc := credit_initial G
have hp : 0 < Fintype.card V := Fintype.card_pos_iff.mpr ⟨G.s⟩
have h1 : Fintype.card V ≤ Fintype.card V * Fintype.card V := Nat.le_mul_self _
have h2 : Fintype.card V * Fintype.card V ≤ Fintype.card V * Fintype.card V * Fintype.card V :=
Nat.le_mul_of_pos_right _ hp
nlinarithThe initialization's actual cell writes and internal-list candidate visits.
noncomputable def initializationWork (G : FlowNetwork V) : Nat :=
(initialCells G).2 + (tabulate (initialHeight G) 0).2 + (tabulate (G.c G.s) 0).2 +
(tabulate (fun _ : V => (univ.toList : List V)) []).2 + (selectInternal G univ.toList).2@[simp] theorem initializationWork_eq (G : FlowNetwork V) :
initializationWork G = Fintype.card V * Fintype.card V + 4 * Fintype.card V := by
simp [initializationWork, initialCells, Fintype.card_prod]
omegaScalar/indexed RAM charge: a fixed allowance of 32 primitive operations per cursor/list test, push/relabel dispatch, moved list cell, or minimum-scan cell. Indexed table lookup/update and exact real arithmetic are unit primitives. This is not a bound on persistent-function evaluation or machine bit complexity.
noncomputable def Trace.work {s t : Machine G} {n : Nat} : Trace s t n → Nat
| .nil _ => 0
| .cons r tail => 32 * (1 + r.scans + r.moves + r.minimumVisits) + tail.worktheorem Trace.work_eq {s t : Machine G} {n : Nat} (tr : Trace s t n) :
tr.work = 32 * (n + tr.scans + tr.moves + tr.minimumVisits) := by
induction tr with
| nil => simp [work, scans, moves, minimumVisits]
| cons r tail ih => simp only [work, scans, moves, minimumVisits, ih]; omegaThe returned measured construction-and-run charge. The last summand includes the actual scans that discover termination and its final empty-list test.
noncomputable def initializedWork (G : FlowNetwork V) : Nat :=
32 * initializationWork G + (execute (initialMachine G) (budget V)).trace.work +
32 * ((completed (initialMachine G)).scans + 1)
theorem initializedWork_eq (G : FlowNetwork V) : initializedWork G =
32 * (initializationWork G + controllerWork (initialMachine G) + 1) := by
unfold initializedWork controllerWork
rw [Trace.work_eq]
dsimp only
omega
theorem initialized_work_le_cubic (G : FlowNetwork V) :
initializedWork G ≤ 864 * Fintype.card V * Fintype.card V * Fintype.card V := by
rw [initializedWork_eq, initializationWork_eq]
have h := initialized_controllerWork_le G
have hp : 0 < Fintype.card V := Fintype.card_pos_iff.mpr ⟨G.s⟩
have h1 : Fintype.card V ≤ Fintype.card V * Fintype.card V := Nat.le_mul_self _
have h2 : Fintype.card V * Fintype.card V ≤ Fintype.card V * Fintype.card V * Fintype.card V :=
Nat.le_mul_of_pos_right _ hp
nlinarithOne concrete initialized run supplies both maximum flow and the cubic charge.
theorem initialized_correct_and_cost (G : FlowNetwork V) :
(initializedFlow G).isMaximal ∧
initializedWork G ≤ 864 * Fintype.card V * Fintype.card V * Fintype.card V :=
⟨initialized_maximum_flow G, initialized_work_le_cubic G⟩end CLRS.Chapter26.RelabelExecution