Imports
import CLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.NetworkFlow
import CLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.ShortestPath
import CLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.MaximumFlow
import CLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.MinimumCostFlow
import CLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.MulticommodityFlow29.2. Formulating Problems as Linear Programs
This section contains the four textbook formulations: shortest path, maximum flow, minimum-cost flow, and multicommodity flow.
Implementation details
namespace CLRSnamespace Chapter29end Chapter29end CLRSDefinitions and proofs
CLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.NetworkFlow
29.2: Common finite-network definitions
CLRS writes its flow linear programs with one nonnegative variable f u v
for every ordered pair of vertices. A missing edge is represented by capacity
zero. This file records that shared finite-network vocabulary, together with
the finite-standard-form encoding helpers used by every §29.2 formulation.
namespace CLRSnamespace Chapter29open Finsetopen scoped BigOperatorsA finite directed capacitated network. Capacities are total; assigning capacity zero to nonedges gives exactly the convention used in CLRS §29.2.
structure FlowNetwork (V : Type*) [Fintype V] where
source : V
sink : V
source_ne_sink : source ≠ sink
capacity : V → V → ℝ
capacity_nonnegative : ∀ u v, 0 ≤ capacity u vnamespace FlowNetworkvariable {V : Type*} [Fintype V] (N : FlowNetwork V)Total flow leaving a vertex.
def outflow (f : V → V → ℝ) (u : V) : ℝ := ∑ v, f u vTotal flow entering a vertex.
def inflow (f : V → V → ℝ) (u : V) : ℝ := ∑ v, f v uNet flow leaving a vertex.
Flow conservation at one vertex.
The nonnegativity and capacity inequalities shared by the flow LPs.
def IsCapacityFeasible (f : V → V → ℝ) : Prop :=
(∀ u v, 0 ≤ f u v) ∧ ∀ u v, f u v ≤ N.capacity u vA feasible single-commodity flow: capacity constraints everywhere and conservation away from the source and sink.
def IsFlow (f : V → V → ℝ) : Prop :=
N.IsCapacityFeasible f ∧
∀ u, u ≠ N.source → u ≠ N.sink → ConservesAt f uend FlowNetworkFinite standard-form encoding helpers
These helpers reindex semantic vectors over a finite type into the Fin-indexed
vectors used by StandardLP, and prove the sum identities that the
assignment bridges rely on.
namespace FinEncodingvariable {ι : Type*} [Fintype ι]
Lift a semantic vector over a finite type to the Fin-indexed vector of the
same coordinates.
noncomputable def lift (x : ι → ℝ) : Fin (Fintype.card ι) → ℝ :=
x ∘ (Fintype.equivFin ι).symm
Project a Fin-indexed vector back to a semantic vector.
noncomputable def proj (x : Fin (Fintype.card ι) → ℝ) : ι → ℝ :=
x ∘ (Fintype.equivFin ι)Lifting then projecting recovers the original vector.
A finite sum over Fin (card ι) reindexes to a sum over ι.
theorem sum_reindex {M : Type*} [AddCommMonoid M] (f : Fin (Fintype.card ι) → M) :
(∑ i : Fin (Fintype.card ι), f i) = ∑ a : ι, f (Fintype.equivFin ι a) := by
exact (Equiv.sum_comp (Fintype.equivFin ι) f).symm
The indicator sum that collapses an ι-indicator to its selected coordinate.
theorem sum_indicator [DecidableEq ι] (a : ι) (x : ι → ℝ) :
(∑ j : Fin (Fintype.card ι),
(if (Fintype.equivFin ι).symm j = a then 1 else 0) * lift x j) = x a := by
calc
(∑ j : Fin (Fintype.card ι),
(if (Fintype.equivFin ι).symm j = a then 1 else 0) * lift x j)
= ∑ b : ι, (if b = a then 1 else 0) * x b := by
rw [sum_reindex]
simp [lift]
_ = x a := by
rw [Finset.sum_eq_single a]
· simp
· intro b _ hb; simp [hb]
· intro h; exact False.elim (h (Finset.mem_univ a))
A direct Fin-indicator sum (no reindexing): the coefficient is nonzero
exactly at the selected coordinate.
theorem sum_indicator_fin {n : ℕ} (e : Fin n) (x : Fin n → ℝ) :
(∑ j : Fin n, (if j = e then 1 else 0) * x j) = x e := by
rw [Finset.sum_eq_single e]
· simp
· intro b _ hb; simp [hb]
· intro h; exact False.elim (h (Finset.mem_univ e))
Fin.addCases reduces on a left (castAdd) index.
theorem addCases_castAdd {m n : ℕ} {C : Type u} (left : Fin m → C) (right : Fin n → C) (e : Fin m) :
Fin.addCases left right (Fin.castAdd n e) = left e := by
simp [Fin.addCases]
Fin.addCases reduces on a right (natAdd) index.
theorem addCases_natAdd {m n : ℕ} {C : Type u} (left : Fin m → C) (right : Fin n → C) (v : Fin n) :
Fin.addCases left right (Fin.natAdd m v) = right v := by
simp [Fin.addCases]
Lift a binary vector to the Fin-indexed vector over ordered pairs.
noncomputable def lift₂ {α β : Type*} [Fintype α] [Fintype β] (x : α → β → ℝ) :
Fin (Fintype.card (α × β)) → ℝ :=
lift (Function.uncurry x)
Project a Fin-indexed vector over ordered pairs back to a binary vector.
noncomputable def proj₂ {α β : Type*} [Fintype α] [Fintype β]
(x : Fin (Fintype.card (α × β)) → ℝ) : α → β → ℝ :=
Function.curry (proj x)Sum of an indicator on the first coordinate over all ordered pairs.
theorem sum_indicator_fst {α β : Type*} [Fintype α] [Fintype β] [DecidableEq α]
(a : α) (x : α → β → ℝ) :
(∑ j : Fin (Fintype.card (α × β)),
(if ((Fintype.equivFin (α × β)).symm j).1 = a then 1 else 0) * lift₂ x j) =
∑ b : β, x a b := by
calc
(∑ j : Fin (Fintype.card (α × β)),
(if ((Fintype.equivFin (α × β)).symm j).1 = a then 1 else 0) * lift₂ x j)
= ∑ p : α × β, (if p.1 = a then 1 else 0) * x p.1 p.2 := by
rw [sum_reindex]
simp [lift₂, lift, Function.uncurry]
_ = ∑ b : β, x a b := by
rw [Fintype.sum_prod_type]
rw [Finset.sum_eq_single a]
· simp
· intro c _ hc; simp [hc]
· intro h; exact False.elim (h (Finset.mem_univ a))Sum of an indicator on the second coordinate over all ordered pairs.
theorem sum_indicator_snd {α β : Type*} [Fintype α] [Fintype β] [DecidableEq β]
(a : β) (x : α → β → ℝ) :
(∑ j : Fin (Fintype.card (α × β)),
(if ((Fintype.equivFin (α × β)).symm j).2 = a then 1 else 0) * lift₂ x j) =
∑ b : α, x b a := by
calc
(∑ j : Fin (Fintype.card (α × β)),
(if ((Fintype.equivFin (α × β)).symm j).2 = a then 1 else 0) * lift₂ x j)
= ∑ p : α × β, (if p.2 = a then 1 else 0) * x p.1 p.2 := by
rw [sum_reindex]
simp [lift₂, lift, Function.uncurry]
_ = ∑ b : α, x b a := by
rw [Fintype.sum_prod_type]
rw [Finset.sum_comm]
rw [Finset.sum_eq_single a]
· simp
· intro c _ hc; simp [hc]
· intro h; exact False.elim (h (Finset.mem_univ a))end FinEncodingend Chapter29end CLRSCLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.ShortestPath
29.2: Shortest paths as a linear program
For a source s and target t, CLRS maximizes d t, subject to
d s = 0 and d v ≤ d u + w u v on every edge. Thus every feasible
d t is a lower bound on every s-to-t walk; an attained
bound is optimal.
This module also records the finite standard-form encoding: the general-form
program is reindexed into a concrete StandardLP (one variable per
vertex), and feasibility and the objective value are proved to be preserved.
namespace CLRSnamespace Chapter29namespace ShortestPathLPopen Chapter24open Matrixopen scoped BigOperatorsvariable {V : Type*} [Fintype V] [DecidableEq V]Feasibility constraints of the CLRS shortest-path LP.
def IsFeasible (G : WeightedGraph V) (s : V) (d : V → ℝ) : Prop :=
d s = 0 ∧ ∀ u v, (u, v) ∈ G.edges → d v ≤ d u + G.w u v
Optimality for the maximization objective d t.
def IsOptimal (G : WeightedGraph V) (s t : V) (d : V → ℝ) : Prop :=
IsFeasible G s d ∧ ∀ e, IsFeasible G s e → e t ≤ d tEvery feasible potential is a lower bound on the weight of every walk from the source. This is the core correctness property of the formulation.
theorem feasible_le_walkWeight {G : WeightedGraph V} {s t : V} {d : V → ℝ}
(hd : IsFeasible G s d) (p : List V) (hp : G.IsWalkFrom s t p) :
d t ≤ Chapter24.WeightedGraph.walkWeight G.w p := by
have h := Chapter24.WeightedGraph.le_add_walkWeight_of_potential G d hd.2 s t p hp
simpa [hd.1] using hIf a feasible potential is attained by an actual source-to-target walk, then it solves the shortest-path LP.
theorem optimal_of_attained_walk {G : WeightedGraph V} {s t : V} {d : V → ℝ}
(hd : IsFeasible G s d) (p : List V) (hp : G.IsWalkFrom s t p)
(hweight : Chapter24.WeightedGraph.walkWeight G.w p = d t) : IsOptimal G s t d := by
refine ⟨hd, ?_⟩
intro e he
calc
e t ≤ Chapter24.WeightedGraph.walkWeight G.w p := feasible_le_walkWeight he p hp
_ = d t := hweightFinite standard-form encoding
The type of directed edges that are actually present in the graph.
abbrev Edge (G : WeightedGraph V) := {e : V × V // e ∈ G.edges}
The general-form program for the shortest-path LP: maximize d t
subject to d s = 0 and d v - d u ≤ w u v on every edge. All
vertices are free variables.
noncomputable def toGeneralLP (G : WeightedGraph V) (s t : V) : GeneralLP where
n := Fintype.card V
m := Fintype.card (Edge G) + 1
maximize := true
c := fun j => if (Fintype.equivFin V).symm j = t then 1 else 0
rel := fun i =>
Fin.cases
(ConstraintRel.eq)
(fun _ => ConstraintRel.le)
i
A := fun i =>
Fin.cases
(fun j => if (Fintype.equivFin V).symm j = s then 1 else 0)
(fun e j =>
let uv := (Fintype.equivFin (Edge G)).symm e
(if (Fintype.equivFin V).symm j = uv.1.2 then 1 else 0) -
(if (Fintype.equivFin V).symm j = uv.1.1 then 1 else 0))
i
b := fun i =>
Fin.cases
(0 : ℝ)
(fun e =>
let uv := (Fintype.equivFin (Edge G)).symm e
G.w uv.1.1 uv.1.2)
i
free := fun _ => trueThe index of the source-equality constraint.
abbrev sourceIndex (G : WeightedGraph V) : Fin (Fintype.card (Edge G) + 1) := 0The index of the constraint attached to a present edge.
abbrev edgeIndex (G : WeightedGraph V) (e : Fin (Fintype.card (Edge G))) :
Fin (Fintype.card (Edge G) + 1) :=
e.succlemma A_source (G : WeightedGraph V) (s t : V) (j : Fin (Fintype.card V)) :
(toGeneralLP G s t).A (sourceIndex G) j =
if (Fintype.equivFin V).symm j = s then 1 else 0 := by
simp [toGeneralLP, sourceIndex]lemma b_source (G : WeightedGraph V) (s t : V) :
(toGeneralLP G s t).b (sourceIndex G) = 0 := by
simp [toGeneralLP, sourceIndex]lemma rel_source (G : WeightedGraph V) (s t : V) :
(toGeneralLP G s t).rel (sourceIndex G) = ConstraintRel.eq := by
simp [toGeneralLP, sourceIndex]lemma A_edge (G : WeightedGraph V) (s t : V) (e : Fin (Fintype.card (Edge G)))
(j : Fin (Fintype.card V)) :
(toGeneralLP G s t).A (edgeIndex G e) j =
(if (Fintype.equivFin V).symm j = ((Fintype.equivFin (Edge G)).symm e).1.2 then 1 else 0) -
(if (Fintype.equivFin V).symm j = ((Fintype.equivFin (Edge G)).symm e).1.1 then 1 else 0) := by
simp [toGeneralLP, edgeIndex]lemma b_edge (G : WeightedGraph V) (s t : V) (e : Fin (Fintype.card (Edge G))) :
(toGeneralLP G s t).b (edgeIndex G e) =
G.w ((Fintype.equivFin (Edge G)).symm e).1.1 ((Fintype.equivFin (Edge G)).symm e).1.2 := by
simp [toGeneralLP, edgeIndex]lemma rel_edge (G : WeightedGraph V) (s t : V) (e : Fin (Fintype.card (Edge G))) :
(toGeneralLP G s t).rel (edgeIndex G e) = ConstraintRel.le := by
simp [toGeneralLP, edgeIndex]
The source row of the encoded constraint matrix reads out d s.
lemma mulVec_source (G : WeightedGraph V) (s t : V) (d : V → ℝ) :
((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) (sourceIndex G) = d s := by
calc
((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) (sourceIndex G)
= ∑ j : Fin (Fintype.card V), (toGeneralLP G s t).A (sourceIndex G) j * FinEncoding.lift d j := by rfl
_ = ∑ j : Fin (Fintype.card V),
(if (Fintype.equivFin V).symm j = s then 1 else 0) * FinEncoding.lift d j := by
simp only [A_source]
_ = d s := FinEncoding.sum_indicator s d
An edge row of the encoded constraint matrix reads out d v - d u.
lemma mulVec_edge (G : WeightedGraph V) (s t : V) (e : Fin (Fintype.card (Edge G)))
(d : V → ℝ) :
((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) (edgeIndex G e) =
d ((Fintype.equivFin (Edge G)).symm e).1.2 - d ((Fintype.equivFin (Edge G)).symm e).1.1 := by
calc
((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) (edgeIndex G e)
= ∑ j : Fin (Fintype.card V), (toGeneralLP G s t).A (edgeIndex G e) j * FinEncoding.lift d j := by rfl
_ = ∑ j : Fin (Fintype.card V),
((if (Fintype.equivFin V).symm j = ((Fintype.equivFin (Edge G)).symm e).1.2 then 1 else 0) -
(if (Fintype.equivFin V).symm j = ((Fintype.equivFin (Edge G)).symm e).1.1 then 1 else 0)) *
FinEncoding.lift d j := by
simp only [A_edge]
_ = (∑ j : Fin (Fintype.card V),
(if (Fintype.equivFin V).symm j = ((Fintype.equivFin (Edge G)).symm e).1.2 then 1 else 0) * FinEncoding.lift d j) -
(∑ j : Fin (Fintype.card V),
(if (Fintype.equivFin V).symm j = ((Fintype.equivFin (Edge G)).symm e).1.1 then 1 else 0) * FinEncoding.lift d j) := by
simp only [sub_mul, Finset.sum_sub_distrib]
_ = d ((Fintype.equivFin (Edge G)).symm e).1.2 - d ((Fintype.equivFin (Edge G)).symm e).1.1 := by
rw [FinEncoding.sum_indicator, FinEncoding.sum_indicator]Semantic feasibility is exactly encoded general-form feasibility.
theorem feasible_iff_generalLP (G : WeightedGraph V) (s t : V) (d : V → ℝ) :
IsFeasible G s d ↔ (toGeneralLP G s t).IsFeasible (FinEncoding.lift d) := by
unfold IsFeasible GeneralLP.IsFeasible
constructor
· intro hd
refine ⟨?_, ?_⟩
· intro j hfree
exfalso
simpa [toGeneralLP] using hfree
· intro i
exact Fin.cases (motive := fun i => match (toGeneralLP G s t).rel i with
| ConstraintRel.le => ((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) i ≤ (toGeneralLP G s t).b i
| ConstraintRel.eq => ((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) i = (toGeneralLP G s t).b i
| ConstraintRel.ge => (toGeneralLP G s t).b i ≤ ((toGeneralLP G s t).A *ᵥ FinEncoding.lift d) i)
(by
rw [rel_source, mulVec_source, b_source]
exact hd.1)
(fun e => by
rw [rel_edge, mulVec_edge, b_edge]
have h := hd.2 ((Fintype.equivFin (Edge G)).symm e).1.1 ((Fintype.equivFin (Edge G)).symm e).1.2 ((Fintype.equivFin (Edge G)).symm e).2
linarith)
i
· intro h
refine ⟨?_, ?_⟩
· have hsource := h.2 (sourceIndex G)
rw [rel_source] at hsource
rw [mulVec_source, b_source] at hsource
exact hsource
· intro u v huv
let e : Edge G := ⟨(u, v), huv⟩
have hedge := h.2 (edgeIndex G (Fintype.equivFin (Edge G) e))
rw [rel_edge] at hedge
rw [mulVec_edge, b_edge] at hedge
have hedge' : d v - d u ≤ G.w u v := by simpa [e] using hedge
linarith
The general-form objective reads out d t.
lemma objective_generalLP (G : WeightedGraph V) (s t : V) (d : V → ℝ) :
(toGeneralLP G s t).objective (FinEncoding.lift d) = d t := by
dsimp [GeneralLP.objective, toGeneralLP]
simp only [dotProduct]
rw [FinEncoding.sum_reindex]
simp only [FinEncoding.lift]
rw [Finset.sum_eq_single t]
· simp
· intro v _ hv; simp [hv]
· intro h; exact False.elim (h (Finset.mem_univ t))The finite standard-form program obtained by normalizing the shortest-path general program.
noncomputable def toStandardLP (G : WeightedGraph V) (s t : V) :=
(toGeneralLP G s t).toStandardLPThe expansion of a potential to the normalized standard-form variable vector.
noncomputable def fullLift (G : WeightedGraph V) (s t : V) (d : V → ℝ) :
Fin (Fintype.card V + Fintype.card V) → ℝ :=
(toGeneralLP G s t).lift (FinEncoding.lift d)
The normalized standard-form objective is exactly the shortest-path
objective d t.
theorem objective_toStandardLP (G : WeightedGraph V) (s t : V) (d : V → ℝ) :
(toStandardLP G s t).objective (fullLift G s t d) = d t := by
unfold toStandardLP fullLift
rw [GeneralLP.objective_lift]
rw [objective_generalLP]
simp [toGeneralLP, GeneralLP.objectiveSign]Feasibility is exactly preserved under the finite standard-form encoding.
theorem feasible_iff_toStandardLP (G : WeightedGraph V) (s t : V) (d : V → ℝ) :
IsFeasible G s d ↔ (toStandardLP G s t).IsFeasible (fullLift G s t d) := by
unfold toStandardLP fullLift
rw [← GeneralLP.feasible_iff_lift]
exact feasible_iff_generalLP G s t dend ShortestPathLPend Chapter29end CLRSCLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.MaximumFlow
29.2: Maximum flow as a linear program
The variables are the gross directed flows f u v. The constraints are
nonnegativity, capacity, and conservation at every vertex other than
s,t; the objective is the net flow leaving s, exactly as in CLRS
§29.2.
This module also records the finite standard-form encoding: one nonnegative
variable per ordered pair, a capacity inequality per ordered pair, and a
conservation equality per internal vertex, reindexed into a concrete
StandardLP.
namespace CLRSnamespace Chapter29namespace MaximumFlowLPopen Finsetopen Matrixopen scoped BigOperatorsvariable {V : Type*} [Fintype V] (N : FlowNetwork V)The constraints of the maximum-flow LP.
def IsFeasible (f : V → V → ℝ) : Prop :=
(∀ u v, 0 ≤ f u v) ∧
(∀ u v, f u v ≤ N.capacity u v) ∧
∀ u, u ≠ N.source → u ≠ N.sink → FlowNetwork.ConservesAt f uThe textbook maximum-flow objective: source outflow minus source inflow.
def objective (f : V → V → ℝ) : ℝ := FlowNetwork.netOutflow f N.sourceA feasible flow maximizing the source outflow.
def IsOptimal (f : V → V → ℝ) : Prop :=
IsFeasible N f ∧ ∀ g, IsFeasible N g → objective N g ≤ objective N fThe displayed LP constraints are precisely the usual finite-network flow constraints.
theorem isFeasible_iff (f : V → V → ℝ) : IsFeasible N f ↔ N.IsFlow f := by
simp only [IsFeasible, FlowNetwork.IsFlow, FlowNetwork.IsCapacityFeasible]
tautoThe LP optimum is precisely a maximum flow under the same objective.
theorem isOptimal_iff (f : V → V → ℝ) :
IsOptimal N f ↔
N.IsFlow f ∧ ∀ g, N.IsFlow g →
FlowNetwork.netOutflow g N.source ≤ FlowNetwork.netOutflow f N.source := by
simp only [IsOptimal, objective, isFeasible_iff]Finite standard-form encoding
section Encodingvariable [DecidableEq V]The internal vertices at which flow is conserved.
abbrev Internal (N : FlowNetwork V) := {u : V // u ≠ N.source ∧ u ≠ N.sink}The general-form program for the maximum-flow LP: one nonnegative variable per ordered pair, a capacity inequality per ordered pair, and a conservation equality per internal vertex.
noncomputable def toGeneralLP (N : FlowNetwork V) : GeneralLP where
n := Fintype.card (V × V)
m := Fintype.card (V × V) + Fintype.card (Internal N)
maximize := true
c := fun j =>
(if ((Fintype.equivFin (V × V)).symm j).1 = N.source then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = N.source then 1 else 0)
rel := fun i =>
Fin.addCases (motive := fun _ => ConstraintRel)
(fun _ => ConstraintRel.le)
(fun _ => ConstraintRel.eq)
i
A := fun i =>
Fin.addCases (motive := fun _ => Fin (Fintype.card (V × V)) → ℝ)
(fun e j => if j = e then 1 else 0)
(fun u j =>
let v := ((Fintype.equivFin (Internal N)).symm u).1
(if ((Fintype.equivFin (V × V)).symm j).1 = v then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = v then 1 else 0))
i
b := fun i =>
Fin.addCases (motive := fun _ => ℝ)
(fun e => N.capacity ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2)
(fun _ => 0)
i
free := fun _ => falseThe index of the capacity constraint for a directed pair.
abbrev capIndex (N : FlowNetwork V) (e : Fin (Fintype.card (V × V))) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N)) :=
Fin.castAdd (Fintype.card (Internal N)) eThe index of the conservation constraint for an internal vertex.
abbrev consIndex (N : FlowNetwork V) (u : Fin (Fintype.card (Internal N))) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N)) :=
Fin.natAdd (Fintype.card (V × V)) u
lemma A_cap (N : FlowNetwork V) (e : Fin (Fintype.card (V × V))) (j : Fin (Fintype.card (V × V))) :
(toGeneralLP N).A (capIndex N e) j = if j = e then 1 else 0 := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
lemma b_cap (N : FlowNetwork V) (e : Fin (Fintype.card (V × V))) :
(toGeneralLP N).b (capIndex N e) =
N.capacity ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
lemma rel_cap (N : FlowNetwork V) (e : Fin (Fintype.card (V × V))) :
(toGeneralLP N).rel (capIndex N e) = ConstraintRel.le := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
lemma A_cons (N : FlowNetwork V) (u : Fin (Fintype.card (Internal N))) (j : Fin (Fintype.card (V × V))) :
(toGeneralLP N).A (consIndex N u) j =
(if ((Fintype.equivFin (V × V)).symm j).1 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_natAdd]
lemma b_cons (N : FlowNetwork V) (u : Fin (Fintype.card (Internal N))) :
(toGeneralLP N).b (consIndex N u) = 0 := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_natAdd]
lemma rel_cons (N : FlowNetwork V) (u : Fin (Fintype.card (Internal N))) :
(toGeneralLP N).rel (consIndex N u) = ConstraintRel.eq := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_natAdd]A capacity row reads out the gross flow on its directed pair.
lemma mulVec_cap (N : FlowNetwork V) (e : Fin (Fintype.card (V × V))) (f : V → V → ℝ) :
((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) (capIndex N e) = FinEncoding.lift₂ f e := by
calc
((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) (capIndex N e)
= ∑ j : Fin (Fintype.card (V × V)), (toGeneralLP N).A (capIndex N e) j * FinEncoding.lift₂ f j := by rfl
_ = ∑ j : Fin (Fintype.card (V × V)), (if j = e then 1 else 0) * FinEncoding.lift₂ f j := by
simp only [A_cap]
_ = FinEncoding.lift₂ f e := FinEncoding.sum_indicator_fin e (FinEncoding.lift₂ f)A conservation row reads out the net outflow at its vertex.
lemma mulVec_cons (N : FlowNetwork V) (u : Fin (Fintype.card (Internal N))) (f : V → V → ℝ) :
((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) (consIndex N u) =
FlowNetwork.outflow f ((Fintype.equivFin (Internal N)).symm u).1 -
FlowNetwork.inflow f ((Fintype.equivFin (Internal N)).symm u).1 := by
calc
((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) (consIndex N u)
= ∑ j : Fin (Fintype.card (V × V)), (toGeneralLP N).A (consIndex N u) j * FinEncoding.lift₂ f j := by rfl
_ = ∑ j : Fin (Fintype.card (V × V)),
((if ((Fintype.equivFin (V × V)).symm j).1 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0)) *
FinEncoding.lift₂ f j := by
simp only [A_cons]
_ = (∑ j : Fin (Fintype.card (V × V)),
(if ((Fintype.equivFin (V × V)).symm j).1 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) * FinEncoding.lift₂ f j) -
(∑ j : Fin (Fintype.card (V × V)),
(if ((Fintype.equivFin (V × V)).symm j).2 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) * FinEncoding.lift₂ f j) := by
simp only [sub_mul, Finset.sum_sub_distrib]
_ = (∑ b : V, f ((Fintype.equivFin (Internal N)).symm u).1 b) -
(∑ b : V, f b ((Fintype.equivFin (Internal N)).symm u).1) := by
rw [FinEncoding.sum_indicator_fst, FinEncoding.sum_indicator_snd]
_ = FlowNetwork.outflow f ((Fintype.equivFin (Internal N)).symm u).1 -
FlowNetwork.inflow f ((Fintype.equivFin (Internal N)).symm u).1 := rflSemantic feasibility is exactly encoded general-form feasibility.
theorem feasible_iff_generalLP (N : FlowNetwork V) (f : V → V → ℝ) :
IsFeasible N f ↔ (toGeneralLP N).IsFeasible (FinEncoding.lift₂ f) := by
unfold IsFeasible GeneralLP.IsFeasible
constructor
· intro hd
refine ⟨?_, ?_⟩
· intro j hfree
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using
(hd.1 ((Fintype.equivFin (V × V)).symm j).1 ((Fintype.equivFin (V × V)).symm j).2)
· intro i
exact Fin.addCases (motive := fun i => match (toGeneralLP N).rel i with
| ConstraintRel.le => ((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) i ≤ (toGeneralLP N).b i
| ConstraintRel.eq => ((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) i = (toGeneralLP N).b i
| ConstraintRel.ge => (toGeneralLP N).b i ≤ ((toGeneralLP N).A *ᵥ FinEncoding.lift₂ f) i)
(fun e => by
rw [rel_cap, mulVec_cap, b_cap]
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using
(hd.2.1 ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2))
(fun u => by
rw [rel_cons, mulVec_cons, b_cons]
have hc := hd.2.2 ((Fintype.equivFin (Internal N)).symm u).1
((Fintype.equivFin (Internal N)).symm u).2.1
((Fintype.equivFin (Internal N)).symm u).2.2
unfold FlowNetwork.ConservesAt at hc
linarith)
i
· intro h
refine ⟨?_, ?_, ?_⟩
· intro u v
have hn := h.1 (Fintype.equivFin (V × V) (u, v))
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using (hn (by simp [toGeneralLP]))
· intro u v
have hc := h.2 (capIndex N (Fintype.equivFin (V × V) (u, v)))
rw [rel_cap] at hc
rw [mulVec_cap, b_cap] at hc
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using hc
· intro u hu hw
let e : Internal N := ⟨u, ⟨hu, hw⟩⟩
have hcons := h.2 (consIndex N (Fintype.equivFin (Internal N) e))
rw [rel_cons] at hcons
rw [mulVec_cons, b_cons] at hcons
have hcons' : FlowNetwork.outflow f u - FlowNetwork.inflow f u = 0 := by
simpa [e] using hcons
unfold FlowNetwork.ConservesAt
linarithThe general-form objective is the textbook maximum-flow objective.
lemma objective_generalLP (N : FlowNetwork V) (f : V → V → ℝ) :
(toGeneralLP N).objective (FinEncoding.lift₂ f) = objective N f := by
dsimp [GeneralLP.objective, toGeneralLP, objective, FlowNetwork.netOutflow, FlowNetwork.outflow, FlowNetwork.inflow]
simp only [dotProduct, sub_mul, Finset.sum_sub_distrib]
rw [FinEncoding.sum_indicator_fst, FinEncoding.sum_indicator_snd]The finite standard-form program obtained by normalizing the maximum-flow general program.
noncomputable def toStandardLP (N : FlowNetwork V) :=
(toGeneralLP N).toStandardLPThe expansion of a gross-flow vector to the normalized standard-form variable vector.
noncomputable def fullLift (N : FlowNetwork V) (f : V → V → ℝ) :
Fin (Fintype.card (V × V) + Fintype.card (V × V)) → ℝ :=
(toGeneralLP N).lift (FinEncoding.lift₂ f)The normalized standard-form objective is the maximum-flow objective.
theorem objective_toStandardLP (N : FlowNetwork V) (f : V → V → ℝ) :
(toStandardLP N).objective (fullLift N f) = objective N f := by
unfold toStandardLP fullLift
rw [GeneralLP.objective_lift]
rw [objective_generalLP]
simp [toGeneralLP, GeneralLP.objectiveSign]Feasibility is exactly preserved under the finite standard-form encoding.
theorem feasible_iff_toStandardLP (N : FlowNetwork V) (f : V → V → ℝ) :
IsFeasible N f ↔ (toStandardLP N).IsFeasible (fullLift N f) := by
unfold toStandardLP fullLift
rw [← GeneralLP.feasible_iff_lift]
exact feasible_iff_generalLP N fend Encodingend MaximumFlowLPend Chapter29end CLRSCLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.MinimumCostFlow
29.2: Minimum-cost flow as a linear program
In addition to capacities, a network assigns a unit cost to every directed
pair. A demand-d flow has net source outflow d; minimizing the
sum of unit cost times flow gives the CLRS minimum-cost-flow formulation.
This module also records the finite standard-form encoding: one nonnegative
variable per ordered pair, a capacity inequality per ordered pair, a
conservation equality per internal vertex, and one source-demand equality,
reindexed into a concrete StandardLP.
namespace CLRSnamespace Chapter29open Finsetopen Matrixopen scoped BigOperatorsA capacitated network with a real unit cost on each directed pair.
structure CostedFlowNetwork (V : Type*) [Fintype V] extends FlowNetwork V where
cost : V → V → ℝnamespace MinimumCostFlowLPvariable {V : Type*} [Fintype V] (N : CostedFlowNetwork V) (demand : ℝ)The capacity, conservation, and exact-demand constraints.
def IsFeasible (f : V → V → ℝ) : Prop :=
N.toFlowNetwork.IsFlow f ∧
FlowNetwork.netOutflow f N.source = demand
Total cost Σ_u Σ_v a_uv f_uv.
def objective (f : V → V → ℝ) : ℝ :=
∑ u, ∑ v, N.cost u v * f u vA demand flow of minimum total cost.
def IsOptimal (f : V → V → ℝ) : Prop :=
IsFeasible N demand f ∧
∀ g, IsFeasible N demand g → objective N f ≤ objective N gThe LP constraints are exactly capacity-feasible flow plus the prescribed source demand.
theorem isFeasible_iff (f : V → V → ℝ) :
IsFeasible N demand f ↔
N.toFlowNetwork.IsFlow f ∧
FlowNetwork.netOutflow f N.source = demand := by
rflThe minimization specification agrees exactly with minimum-cost demand flow.
theorem isOptimal_iff (f : V → V → ℝ) :
IsOptimal N demand f ↔
(N.toFlowNetwork.IsFlow f ∧
FlowNetwork.netOutflow f N.source = demand) ∧
∀ g, (N.toFlowNetwork.IsFlow g ∧
FlowNetwork.netOutflow g N.source = demand) →
(∑ u, ∑ v, N.cost u v * f u v) ≤
∑ u, ∑ v, N.cost u v * g u v := by
rflFinite standard-form encoding
section Encodingvariable [DecidableEq V]The internal vertices at which flow is conserved.
abbrev Internal (N : CostedFlowNetwork V) := {u : V // u ≠ N.source ∧ u ≠ N.sink}The general-form program for the minimum-cost-flow LP: one nonnegative variable per ordered pair, a capacity inequality per ordered pair, a conservation equality per internal vertex, and a source-demand equality. The objective is minimized (cost is the sum of unit cost times flow).
noncomputable def toGeneralLP (N : CostedFlowNetwork V) (demand : ℝ) : GeneralLP where
n := Fintype.card (V × V)
m := Fintype.card (V × V) + Fintype.card (Internal N) + 1
maximize := false
c := fun j => N.cost ((Fintype.equivFin (V × V)).symm j).1 ((Fintype.equivFin (V × V)).symm j).2
rel := fun i =>
Fin.cases
(ConstraintRel.eq)
(fun i => Fin.addCases (motive := fun _ => ConstraintRel)
(fun _ => ConstraintRel.le)
(fun _ => ConstraintRel.eq)
i)
i
A := fun i =>
Fin.cases
(fun j =>
(if ((Fintype.equivFin (V × V)).symm j).1 = N.source then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = N.source then 1 else 0))
(fun i j => Fin.addCases (motive := fun _ => ℝ)
(fun e => if j = e then 1 else 0)
(fun u =>
let v := ((Fintype.equivFin (Internal N)).symm u).1
(if ((Fintype.equivFin (V × V)).symm j).1 = v then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = v then 1 else 0))
i)
i
b := fun i =>
Fin.cases
(demand)
(fun i => Fin.addCases (motive := fun _ => ℝ)
(fun e => N.capacity ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2)
(fun _ => 0)
i)
i
free := fun _ => falseThe index of the source-demand equality constraint.
abbrev demandIndex (N : CostedFlowNetwork V) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N) + 1) := 0The index of the capacity constraint for a directed pair.
abbrev capIndex (N : CostedFlowNetwork V) (e : Fin (Fintype.card (V × V))) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N) + 1) :=
(Fin.castAdd (Fintype.card (Internal N)) e).succThe index of the conservation constraint for an internal vertex.
abbrev consIndex (N : CostedFlowNetwork V) (u : Fin (Fintype.card (Internal N))) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N) + 1) :=
(Fin.natAdd (Fintype.card (V × V)) u).succlemma A_demand (N : CostedFlowNetwork V) (demand : ℝ) (j : Fin (Fintype.card (V × V))) :
(toGeneralLP N demand).A (demandIndex N) j =
(if ((Fintype.equivFin (V × V)).symm j).1 = N.source then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = N.source then 1 else 0) := by
simp [toGeneralLP, demandIndex]lemma b_demand (N : CostedFlowNetwork V) (demand : ℝ) :
(toGeneralLP N demand).b (demandIndex N) = demand := by
simp [toGeneralLP, demandIndex]lemma rel_demand (N : CostedFlowNetwork V) (demand : ℝ) :
(toGeneralLP N demand).rel (demandIndex N) = ConstraintRel.eq := by
simp [toGeneralLP, demandIndex]
lemma A_cap (N : CostedFlowNetwork V) (demand : ℝ) (e : Fin (Fintype.card (V × V))) (j : Fin (Fintype.card (V × V))) :
(toGeneralLP N demand).A (capIndex N e) j = if j = e then 1 else 0 := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
lemma b_cap (N : CostedFlowNetwork V) (demand : ℝ) (e : Fin (Fintype.card (V × V))) :
(toGeneralLP N demand).b (capIndex N e) =
N.capacity ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
lemma rel_cap (N : CostedFlowNetwork V) (demand : ℝ) (e : Fin (Fintype.card (V × V))) :
(toGeneralLP N demand).rel (capIndex N e) = ConstraintRel.le := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
lemma A_cons (N : CostedFlowNetwork V) (demand : ℝ) (u : Fin (Fintype.card (Internal N))) (j : Fin (Fintype.card (V × V))) :
(toGeneralLP N demand).A (consIndex N u) j =
(if ((Fintype.equivFin (V × V)).symm j).1 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_natAdd]
lemma b_cons (N : CostedFlowNetwork V) (demand : ℝ) (u : Fin (Fintype.card (Internal N))) :
(toGeneralLP N demand).b (consIndex N u) = 0 := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_natAdd]
lemma rel_cons (N : CostedFlowNetwork V) (demand : ℝ) (u : Fin (Fintype.card (Internal N))) :
(toGeneralLP N demand).rel (consIndex N u) = ConstraintRel.eq := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_natAdd]The demand row reads out the source net outflow.
lemma mulVec_demand (N : CostedFlowNetwork V) (demand : ℝ) (f : V → V → ℝ) :
((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) (demandIndex N) =
FlowNetwork.netOutflow f N.source := by
calc
((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) (demandIndex N)
= ∑ j : Fin (Fintype.card (V × V)), (toGeneralLP N demand).A (demandIndex N) j * FinEncoding.lift₂ f j := by rfl
_ = ∑ j : Fin (Fintype.card (V × V)),
((if ((Fintype.equivFin (V × V)).symm j).1 = N.source then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = N.source then 1 else 0)) *
FinEncoding.lift₂ f j := by
simp only [A_demand]
_ = (∑ j : Fin (Fintype.card (V × V)),
(if ((Fintype.equivFin (V × V)).symm j).1 = N.source then 1 else 0) * FinEncoding.lift₂ f j) -
(∑ j : Fin (Fintype.card (V × V)),
(if ((Fintype.equivFin (V × V)).symm j).2 = N.source then 1 else 0) * FinEncoding.lift₂ f j) := by
simp only [sub_mul, Finset.sum_sub_distrib]
_ = (∑ b : V, f N.source b) - (∑ b : V, f b N.source) := by
rw [FinEncoding.sum_indicator_fst, FinEncoding.sum_indicator_snd]
_ = FlowNetwork.netOutflow f N.source := rflA capacity row reads out the gross flow on its directed pair.
lemma mulVec_cap (N : CostedFlowNetwork V) (demand : ℝ) (e : Fin (Fintype.card (V × V))) (f : V → V → ℝ) :
((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) (capIndex N e) = FinEncoding.lift₂ f e := by
calc
((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) (capIndex N e)
= ∑ j : Fin (Fintype.card (V × V)), (toGeneralLP N demand).A (capIndex N e) j * FinEncoding.lift₂ f j := by rfl
_ = ∑ j : Fin (Fintype.card (V × V)), (if j = e then 1 else 0) * FinEncoding.lift₂ f j := by
simp only [A_cap]
_ = FinEncoding.lift₂ f e := FinEncoding.sum_indicator_fin e (FinEncoding.lift₂ f)A conservation row reads out the net outflow at its vertex.
lemma mulVec_cons (N : CostedFlowNetwork V) (demand : ℝ) (u : Fin (Fintype.card (Internal N))) (f : V → V → ℝ) :
((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) (consIndex N u) =
FlowNetwork.outflow f ((Fintype.equivFin (Internal N)).symm u).1 -
FlowNetwork.inflow f ((Fintype.equivFin (Internal N)).symm u).1 := by
calc
((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) (consIndex N u)
= ∑ j : Fin (Fintype.card (V × V)), (toGeneralLP N demand).A (consIndex N u) j * FinEncoding.lift₂ f j := by rfl
_ = ∑ j : Fin (Fintype.card (V × V)),
((if ((Fintype.equivFin (V × V)).symm j).1 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) -
(if ((Fintype.equivFin (V × V)).symm j).2 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0)) *
FinEncoding.lift₂ f j := by
simp only [A_cons]
_ = (∑ j : Fin (Fintype.card (V × V)),
(if ((Fintype.equivFin (V × V)).symm j).1 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) * FinEncoding.lift₂ f j) -
(∑ j : Fin (Fintype.card (V × V)),
(if ((Fintype.equivFin (V × V)).symm j).2 = ((Fintype.equivFin (Internal N)).symm u).1 then 1 else 0) * FinEncoding.lift₂ f j) := by
simp only [sub_mul, Finset.sum_sub_distrib]
_ = (∑ b : V, f ((Fintype.equivFin (Internal N)).symm u).1 b) -
(∑ b : V, f b ((Fintype.equivFin (Internal N)).symm u).1) := by
rw [FinEncoding.sum_indicator_fst, FinEncoding.sum_indicator_snd]
_ = FlowNetwork.outflow f ((Fintype.equivFin (Internal N)).symm u).1 -
FlowNetwork.inflow f ((Fintype.equivFin (Internal N)).symm u).1 := rflSemantic feasibility is exactly encoded general-form feasibility.
theorem feasible_iff_generalLP (N : CostedFlowNetwork V) (demand : ℝ) (f : V → V → ℝ) :
IsFeasible N demand f ↔ (toGeneralLP N demand).IsFeasible (FinEncoding.lift₂ f) := by
unfold IsFeasible GeneralLP.IsFeasible FlowNetwork.IsFlow FlowNetwork.IsCapacityFeasible
constructor
· intro hd
refine ⟨?_, ?_⟩
· intro j hfree
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using
(hd.1.1.1 ((Fintype.equivFin (V × V)).symm j).1 ((Fintype.equivFin (V × V)).symm j).2)
· intro i
exact Fin.cases (motive := fun i => match (toGeneralLP N demand).rel i with
| ConstraintRel.le => ((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) i ≤ (toGeneralLP N demand).b i
| ConstraintRel.eq => ((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) i = (toGeneralLP N demand).b i
| ConstraintRel.ge => (toGeneralLP N demand).b i ≤ ((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) i)
(by
rw [rel_demand, mulVec_demand, b_demand]
exact hd.2)
(fun i => by
exact Fin.addCases (motive := fun i => match (toGeneralLP N demand).rel i.succ with
| ConstraintRel.le => ((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) i.succ ≤ (toGeneralLP N demand).b i.succ
| ConstraintRel.eq => ((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) i.succ = (toGeneralLP N demand).b i.succ
| ConstraintRel.ge => (toGeneralLP N demand).b i.succ ≤ ((toGeneralLP N demand).A *ᵥ FinEncoding.lift₂ f) i.succ)
(fun e => by
rw [rel_cap, mulVec_cap, b_cap]
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using
(hd.1.1.2 ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2))
(fun u => by
rw [rel_cons, mulVec_cons, b_cons]
have hc := hd.1.2 ((Fintype.equivFin (Internal N)).symm u).1
((Fintype.equivFin (Internal N)).symm u).2.1
((Fintype.equivFin (Internal N)).symm u).2.2
unfold FlowNetwork.ConservesAt at hc
linarith)
i)
i
· intro h
refine ⟨⟨⟨?_, ?_⟩, ?_⟩, ?_⟩
· intro u v
have hn := h.1 (Fintype.equivFin (V × V) (u, v))
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using (hn (by simp [toGeneralLP]))
· intro u v
have hc := h.2 (capIndex N (Fintype.equivFin (V × V) (u, v)))
rw [rel_cap] at hc
rw [mulVec_cap, b_cap] at hc
simpa [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry] using hc
· intro u hu hw
let e : Internal N := ⟨u, ⟨hu, hw⟩⟩
have hcons := h.2 (consIndex N (Fintype.equivFin (Internal N) e))
rw [rel_cons] at hcons
rw [mulVec_cons, b_cons] at hcons
have hcons' : FlowNetwork.outflow f u - FlowNetwork.inflow f u = 0 := by
simpa [e] using hcons
unfold FlowNetwork.ConservesAt
linarith
· have hdemand := h.2 (demandIndex N)
rw [rel_demand] at hdemand
rw [mulVec_demand, b_demand] at hdemand
exact hdemandThe general-form objective is the total cost (minimized).
lemma objective_generalLP (N : CostedFlowNetwork V) (demand : ℝ) (f : V → V → ℝ) :
(toGeneralLP N demand).objective (FinEncoding.lift₂ f) = objective N f := by
dsimp [GeneralLP.objective, toGeneralLP, objective]
simp only [dotProduct]
rw [FinEncoding.sum_reindex]
simp [FinEncoding.lift₂, FinEncoding.lift, Function.uncurry]
rw [Fintype.sum_prod_type]The finite standard-form program obtained by normalizing the minimum-cost general program.
noncomputable def toStandardLP (N : CostedFlowNetwork V) (demand : ℝ) :=
(toGeneralLP N demand).toStandardLPThe expansion of a gross-flow vector to the normalized standard-form variable vector.
noncomputable def fullLift (N : CostedFlowNetwork V) (demand : ℝ) (f : V → V → ℝ) :
Fin (Fintype.card (V × V) + Fintype.card (V × V)) → ℝ :=
(toGeneralLP N demand).lift (FinEncoding.lift₂ f)The normalized standard-form objective equals the signed total cost.
theorem objective_toStandardLP (N : CostedFlowNetwork V) (demand : ℝ) (f : V → V → ℝ) :
(toStandardLP N demand).objective (fullLift N demand f) = -objective N f := by
unfold toStandardLP fullLift
rw [GeneralLP.objective_lift]
rw [objective_generalLP]
simp [toGeneralLP, GeneralLP.objectiveSign]Feasibility is exactly preserved under the finite standard-form encoding.
theorem feasible_iff_toStandardLP (N : CostedFlowNetwork V) (demand : ℝ) (f : V → V → ℝ) :
IsFeasible N demand f ↔ (toStandardLP N demand).IsFeasible (fullLift N demand f) := by
unfold toStandardLP fullLift
rw [← GeneralLP.feasible_iff_lift]
exact feasible_iff_generalLP N demand fend Encodingend MinimumCostFlowLPend Chapter29end CLRSCLRSLean.FourthEdition.Chapter_29.Section_29_2_Formulating_Problems_As_Linear_Programs.MulticommodityFlow
29.2: Multicommodity flow as a linear program
Each commodity has its own source, sink, demand, and nonnegative gross flow. All commodities share the edge capacities. The optional cost objective also records Exercise 29.2-7's minimum-cost multicommodity formulation.
This module also records the finite standard-form encoding: one nonnegative
variable per commodity/ordered-pair, a shared capacity inequality per ordered
pair, a conservation equality per internal commodity/vertex, and a demand
equality per commodity, reindexed into a concrete StandardLP.
namespace CLRSnamespace Chapter29open Finsetopen Matrixopen scoped BigOperatorsSource, sink, and nonnegative demand of one commodity.
structure Commodity (V : Type*) where
source : V
sink : V
source_ne_sink : source ≠ sink
demand : ℝ
demand_nonnegative : 0 ≤ demandnamespace MulticommodityFlowLPvariable {V K : Type*} [Fintype V] [Fintype K]Total flow of all commodities on one directed pair.
def aggregate (f : K → V → V → ℝ) (u v : V) : ℝ := ∑ i, f i u vThe CLRS multicommodity feasibility constraints.
def IsFeasible (N : FlowNetwork V) (commodity : K → Commodity V)
(f : K → V → V → ℝ) : Prop :=
(∀ i u v, 0 ≤ f i u v) ∧
(∀ u v, aggregate f u v ≤ N.capacity u v) ∧
(∀ i u, u ≠ (commodity i).source → u ≠ (commodity i).sink →
FlowNetwork.ConservesAt (f i) u) ∧
∀ i, FlowNetwork.netOutflow (f i) (commodity i).source = (commodity i).demandAn expanded statement of the displayed multicommodity LP.
theorem isFeasible_iff (N : FlowNetwork V) (commodity : K → Commodity V)
(f : K → V → V → ℝ) :
IsFeasible N commodity f ↔
(∀ i u v, 0 ≤ f i u v) ∧
(∀ u v, (∑ i, f i u v) ≤ N.capacity u v) ∧
(∀ i u, u ≠ (commodity i).source → u ≠ (commodity i).sink →
FlowNetwork.inflow (f i) u = FlowNetwork.outflow (f i) u) ∧
∀ i, FlowNetwork.outflow (f i) (commodity i).source -
FlowNetwork.inflow (f i) (commodity i).source = (commodity i).demand := by
rflTotal cost of the aggregate multicommodity flow.
def cost (unitCost : V → V → ℝ) (f : K → V → V → ℝ) : ℝ :=
∑ u, ∑ v, unitCost u v * aggregate f u vMinimum-cost feasible multicommodity routing.
def IsMinimumCost (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) (f : K → V → V → ℝ) : Prop :=
IsFeasible N commodity f ∧
∀ g, IsFeasible N commodity g → cost unitCost f ≤ cost unitCost gExpanded optimality statement for minimum-cost multicommodity flow.
theorem isMinimumCost_iff (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) (f : K → V → V → ℝ) :
IsMinimumCost N commodity unitCost f ↔
IsFeasible N commodity f ∧
∀ g, IsFeasible N commodity g →
(∑ u, ∑ v, unitCost u v * (∑ i, f i u v)) ≤
∑ u, ∑ v, unitCost u v * (∑ i, g i u v) := by
rflFinite standard-form encoding
section Encodingvariable [DecidableEq V] [DecidableEq K]
Lift a commodity-indexed flow to the Fin-indexed vector over
K × (V × V).
noncomputable def lift₃ (f : K → V → V → ℝ) : Fin (Fintype.card (K × (V × V))) → ℝ :=
FinEncoding.lift (fun p : K × (V × V) => f p.1 p.2.1 p.2.2)The internal commodity/vertex pairs at which flow is conserved.
abbrev Internal (N : FlowNetwork V) (commodity : K → Commodity V) :=
{p : K × V // p.2 ≠ (commodity p.1).source ∧ p.2 ≠ (commodity p.1).sink}Sum of an indicator on the commodity and the source coordinate (outflow).
lemma sum_indicator_fst_fst (i : K) (u : V) (f : K → V → V → ℝ) :
(∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = u then 1 else 0) * lift₃ f j) =
∑ b : V, f i u b := by
rw [FinEncoding.sum_reindex]
simp [lift₃, FinEncoding.lift]
rw [Fintype.sum_prod_type]
rw [Finset.sum_eq_single i]
· rw [Fintype.sum_prod_type]
rw [Finset.sum_eq_single u]
· simp
· intro a _ ha; simp [ha]
· intro h; exact False.elim (h (Finset.mem_univ u))
· intro k _ hk; simp [hk]
· intro h; exact False.elim (h (Finset.mem_univ i))Sum of an indicator on the commodity and the target coordinate (inflow).
lemma sum_indicator_fst_snd (i : K) (u : V) (f : K → V → V → ℝ) :
(∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = u then 1 else 0) * lift₃ f j) =
∑ a : V, f i a u := by
rw [FinEncoding.sum_reindex]
simp [lift₃, FinEncoding.lift]
rw [Fintype.sum_prod_type]
rw [Finset.sum_eq_single i]
· rw [Fintype.sum_prod_type]
rw [Finset.sum_comm]
rw [Finset.sum_eq_single u]
· simp
· intro a _ ha; simp [ha]
· intro h; exact False.elim (h (Finset.mem_univ u))
· intro k _ hk; simp [hk]
· intro h; exact False.elim (h (Finset.mem_univ i))Sum of an indicator on the directed pair (shared capacity).
lemma sum_indicator_pair (u v : V) (f : K → V → V → ℝ) :
(∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).2 = (u, v) then 1 else 0) * lift₃ f j) =
∑ i : K, f i u v := by
rw [FinEncoding.sum_reindex]
simp [lift₃, FinEncoding.lift]
rw [Fintype.sum_prod_type]
simp [Finset.sum_ite_eq']The general-form program for the multicommodity-flow LP.
noncomputable def toGeneralLP (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) : GeneralLP where
n := Fintype.card (K × (V × V))
m := Fintype.card (V × V) + Fintype.card (Internal N commodity) + Fintype.card K
maximize := false
c := fun j => unitCost ((Fintype.equivFin (K × (V × V))).symm j).2.1 ((Fintype.equivFin (K × (V × V))).symm j).2.2
rel := fun i =>
Fin.addCases (motive := fun _ => ConstraintRel)
(fun ij => Fin.addCases (motive := fun _ => ConstraintRel)
(fun _ => ConstraintRel.le)
(fun _ => ConstraintRel.eq)
ij)
(fun _ => ConstraintRel.eq)
i
A := fun i =>
Fin.addCases (motive := fun _ => Fin (Fintype.card (K × (V × V))) → ℝ)
(fun ij => Fin.addCases (motive := fun _ => Fin (Fintype.card (K × (V × V))) → ℝ)
(fun e j => if ((Fintype.equivFin (K × (V × V))).symm j).2 = (Fintype.equivFin (V × V)).symm e then 1 else 0)
(fun p j =>
let iu := (Fintype.equivFin (Internal N commodity)).symm p
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = iu.1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = iu.1.2 then 1 else 0) -
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = iu.1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = iu.1.2 then 1 else 0))
ij)
(fun i j =>
let ci := (Fintype.equivFin K).symm i
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ci ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = (commodity ci).source then 1 else 0) -
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ci ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = (commodity ci).source then 1 else 0))
i
b := fun i =>
Fin.addCases (motive := fun _ => ℝ)
(fun ij => Fin.addCases (motive := fun _ => ℝ)
(fun e => N.capacity ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2)
(fun _ => 0)
ij)
(fun i => (commodity ((Fintype.equivFin K).symm i)).demand)
i
free := fun _ => falseThe index of the shared-capacity constraint for a directed pair.
abbrev capIndex (N : FlowNetwork V) (commodity : K → Commodity V) (e : Fin (Fintype.card (V × V))) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N commodity) + Fintype.card K) :=
Fin.castAdd (Fintype.card K) (Fin.castAdd (Fintype.card (Internal N commodity)) e)The index of the conservation constraint for an internal commodity/vertex.
abbrev consIndex (N : FlowNetwork V) (commodity : K → Commodity V) (p : Fin (Fintype.card (Internal N commodity))) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N commodity) + Fintype.card K) :=
Fin.castAdd (Fintype.card K) (Fin.natAdd (Fintype.card (V × V)) p)The index of the demand constraint for a commodity.
abbrev demandIndex (N : FlowNetwork V) (commodity : K → Commodity V) (i : Fin (Fintype.card K)) :
Fin (Fintype.card (V × V) + Fintype.card (Internal N commodity) + Fintype.card K) :=
Fin.natAdd (Fintype.card (V × V) + Fintype.card (Internal N commodity)) i
lemma rel_cap (N : FlowNetwork V) (commodity : K → Commodity V) (e : Fin (Fintype.card (V × V))) :
(toGeneralLP N commodity (fun _ _ => 0)).rel (capIndex N commodity e) = ConstraintRel.le := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
rw [FinEncoding.addCases_castAdd]
lemma b_cap (N : FlowNetwork V) (commodity : K → Commodity V) (e : Fin (Fintype.card (V × V))) :
(toGeneralLP N commodity (fun _ _ => 0)).b (capIndex N commodity e) =
N.capacity ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
rw [FinEncoding.addCases_castAdd]
lemma A_cap (N : FlowNetwork V) (commodity : K → Commodity V) (e : Fin (Fintype.card (V × V)))
(j : Fin (Fintype.card (K × (V × V)))) :
(toGeneralLP N commodity (fun _ _ => 0)).A (capIndex N commodity e) j =
if ((Fintype.equivFin (K × (V × V))).symm j).2 = (Fintype.equivFin (V × V)).symm e then 1 else 0 := by
dsimp [toGeneralLP, capIndex]
rw [FinEncoding.addCases_castAdd]
rw [FinEncoding.addCases_castAdd]
lemma rel_cons (N : FlowNetwork V) (commodity : K → Commodity V) (p : Fin (Fintype.card (Internal N commodity))) :
(toGeneralLP N commodity (fun _ _ => 0)).rel (consIndex N commodity p) = ConstraintRel.eq := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_castAdd]
rw [FinEncoding.addCases_natAdd]
lemma b_cons (N : FlowNetwork V) (commodity : K → Commodity V) (p : Fin (Fintype.card (Internal N commodity))) :
(toGeneralLP N commodity (fun _ _ => 0)).b (consIndex N commodity p) = 0 := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_castAdd]
rw [FinEncoding.addCases_natAdd]
lemma A_cons (N : FlowNetwork V) (commodity : K → Commodity V) (p : Fin (Fintype.card (Internal N commodity)))
(j : Fin (Fintype.card (K × (V × V)))) :
(toGeneralLP N commodity (fun _ _ => 0)).A (consIndex N commodity p) j =
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.2 then 1 else 0) -
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = ((Fintype.equivFin (Internal N commodity)).symm p).1.2 then 1 else 0) := by
dsimp [toGeneralLP, consIndex]
rw [FinEncoding.addCases_castAdd]
rw [FinEncoding.addCases_natAdd]
lemma rel_demand (N : FlowNetwork V) (commodity : K → Commodity V) (i : Fin (Fintype.card K)) :
(toGeneralLP N commodity (fun _ _ => 0)).rel (demandIndex N commodity i) = ConstraintRel.eq := by
dsimp [toGeneralLP, demandIndex]
rw [FinEncoding.addCases_natAdd]
lemma b_demand (N : FlowNetwork V) (commodity : K → Commodity V) (i : Fin (Fintype.card K)) :
(toGeneralLP N commodity (fun _ _ => 0)).b (demandIndex N commodity i) =
(commodity ((Fintype.equivFin K).symm i)).demand := by
dsimp [toGeneralLP, demandIndex]
rw [FinEncoding.addCases_natAdd]
lemma A_demand (N : FlowNetwork V) (commodity : K → Commodity V) (i : Fin (Fintype.card K))
(j : Fin (Fintype.card (K × (V × V)))) :
(toGeneralLP N commodity (fun _ _ => 0)).A (demandIndex N commodity i) j =
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = (Fintype.equivFin K).symm i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = (commodity ((Fintype.equivFin K).symm i)).source then 1 else 0) -
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = (Fintype.equivFin K).symm i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = (commodity ((Fintype.equivFin K).symm i)).source then 1 else 0) := by
dsimp [toGeneralLP, demandIndex]
rw [FinEncoding.addCases_natAdd]A capacity row reads out the aggregate flow on its directed pair.
lemma mulVec_cap (N : FlowNetwork V) (commodity : K → Commodity V) (e : Fin (Fintype.card (V × V)))
(f : K → V → V → ℝ) :
((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (capIndex N commodity e) =
aggregate f ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 := by
calc
((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (capIndex N commodity e)
= ∑ j : Fin (Fintype.card (K × (V × V))), (toGeneralLP N commodity (fun _ _ => 0)).A (capIndex N commodity e) j * lift₃ f j := by rfl
_ = ∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).2 = (Fintype.equivFin (V × V)).symm e then 1 else 0) * lift₃ f j := by
simp only [A_cap]
_ = ∑ i : K, f i ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 := by
simpa using (sum_indicator_pair ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 f)
_ = aggregate f ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2 := rflA conservation row reads out the net outflow of one commodity at its internal vertex.
lemma mulVec_cons (N : FlowNetwork V) (commodity : K → Commodity V) (p : Fin (Fintype.card (Internal N commodity)))
(f : K → V → V → ℝ) :
((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (consIndex N commodity p) =
FlowNetwork.outflow (f ((Fintype.equivFin (Internal N commodity)).symm p).1.1) ((Fintype.equivFin (Internal N commodity)).symm p).1.2 -
FlowNetwork.inflow (f ((Fintype.equivFin (Internal N commodity)).symm p).1.1) ((Fintype.equivFin (Internal N commodity)).symm p).1.2 := by
calc
((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (consIndex N commodity p)
= ∑ j : Fin (Fintype.card (K × (V × V))), (toGeneralLP N commodity (fun _ _ => 0)).A (consIndex N commodity p) j * lift₃ f j := by rfl
_ = ∑ j : Fin (Fintype.card (K × (V × V))),
((if ((Fintype.equivFin (K × (V × V))).symm j).1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.2 then 1 else 0) -
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = ((Fintype.equivFin (Internal N commodity)).symm p).1.2 then 1 else 0)) *
lift₃ f j := by
simp only [A_cons]
_ = (∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.2 then 1 else 0) * lift₃ f j) -
(∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = ((Fintype.equivFin (Internal N commodity)).symm p).1.2 then 1 else 0) * lift₃ f j) := by
simp only [sub_mul, Finset.sum_sub_distrib]
_ = (∑ b : V, f ((Fintype.equivFin (Internal N commodity)).symm p).1.1 ((Fintype.equivFin (Internal N commodity)).symm p).1.2 b) -
(∑ a : V, f ((Fintype.equivFin (Internal N commodity)).symm p).1.1 a ((Fintype.equivFin (Internal N commodity)).symm p).1.2) := by
rw [sum_indicator_fst_fst, sum_indicator_fst_snd]
_ = FlowNetwork.outflow (f ((Fintype.equivFin (Internal N commodity)).symm p).1.1) ((Fintype.equivFin (Internal N commodity)).symm p).1.2 -
FlowNetwork.inflow (f ((Fintype.equivFin (Internal N commodity)).symm p).1.1) ((Fintype.equivFin (Internal N commodity)).symm p).1.2 := rflA demand row reads out the net outflow of one commodity at its source.
lemma mulVec_demand (N : FlowNetwork V) (commodity : K → Commodity V) (i : Fin (Fintype.card K))
(f : K → V → V → ℝ) :
((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (demandIndex N commodity i) =
FlowNetwork.netOutflow (f ((Fintype.equivFin K).symm i)) (commodity ((Fintype.equivFin K).symm i)).source := by
calc
((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (demandIndex N commodity i)
= ∑ j : Fin (Fintype.card (K × (V × V))), (toGeneralLP N commodity (fun _ _ => 0)).A (demandIndex N commodity i) j * lift₃ f j := by rfl
_ = ∑ j : Fin (Fintype.card (K × (V × V))),
((if ((Fintype.equivFin (K × (V × V))).symm j).1 = (Fintype.equivFin K).symm i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = (commodity ((Fintype.equivFin K).symm i)).source then 1 else 0) -
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = (Fintype.equivFin K).symm i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = (commodity ((Fintype.equivFin K).symm i)).source then 1 else 0)) *
lift₃ f j := by
simp only [A_demand]
_ = (∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = (Fintype.equivFin K).symm i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.1 = (commodity ((Fintype.equivFin K).symm i)).source then 1 else 0) * lift₃ f j) -
(∑ j : Fin (Fintype.card (K × (V × V))),
(if ((Fintype.equivFin (K × (V × V))).symm j).1 = (Fintype.equivFin K).symm i ∧
((Fintype.equivFin (K × (V × V))).symm j).2.2 = (commodity ((Fintype.equivFin K).symm i)).source then 1 else 0) * lift₃ f j) := by
simp only [sub_mul, Finset.sum_sub_distrib]
_ = (∑ b : V, f ((Fintype.equivFin K).symm i) (commodity ((Fintype.equivFin K).symm i)).source b) -
(∑ a : V, f ((Fintype.equivFin K).symm i) a (commodity ((Fintype.equivFin K).symm i)).source) := by
rw [sum_indicator_fst_fst, sum_indicator_fst_snd]
_ = FlowNetwork.netOutflow (f ((Fintype.equivFin K).symm i)) (commodity ((Fintype.equivFin K).symm i)).source := rflSemantic feasibility is exactly encoded general-form feasibility.
theorem feasible_iff_generalLP (N : FlowNetwork V) (commodity : K → Commodity V)
(f : K → V → V → ℝ) :
IsFeasible N commodity f ↔ (toGeneralLP N commodity (fun _ _ => 0)).IsFeasible (lift₃ f) := by
unfold IsFeasible GeneralLP.IsFeasible
constructor
· intro hd
refine ⟨?_, ?_⟩
· intro j hfree
simpa [lift₃, FinEncoding.lift] using
(hd.1 ((Fintype.equivFin (K × (V × V))).symm j).1
((Fintype.equivFin (K × (V × V))).symm j).2.1
((Fintype.equivFin (K × (V × V))).symm j).2.2)
· intro i
exact Fin.addCases (motive := fun i => match (toGeneralLP N commodity (fun _ _ => 0)).rel i with
| ConstraintRel.le => ((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) i ≤ (toGeneralLP N commodity (fun _ _ => 0)).b i
| ConstraintRel.eq => ((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) i = (toGeneralLP N commodity (fun _ _ => 0)).b i
| ConstraintRel.ge => (toGeneralLP N commodity (fun _ _ => 0)).b i ≤ ((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) i)
(fun ij =>
Fin.addCases (motive := fun ij => match (toGeneralLP N commodity (fun _ _ => 0)).rel (Fin.castAdd (Fintype.card K) ij) with
| ConstraintRel.le => ((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (Fin.castAdd (Fintype.card K) ij) ≤ (toGeneralLP N commodity (fun _ _ => 0)).b (Fin.castAdd (Fintype.card K) ij)
| ConstraintRel.eq => ((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (Fin.castAdd (Fintype.card K) ij) = (toGeneralLP N commodity (fun _ _ => 0)).b (Fin.castAdd (Fintype.card K) ij)
| ConstraintRel.ge => (toGeneralLP N commodity (fun _ _ => 0)).b (Fin.castAdd (Fintype.card K) ij) ≤ ((toGeneralLP N commodity (fun _ _ => 0)).A *ᵥ lift₃ f) (Fin.castAdd (Fintype.card K) ij))
(fun e => by
rw [rel_cap, mulVec_cap, b_cap]
simpa [aggregate] using
(hd.2.1 ((Fintype.equivFin (V × V)).symm e).1 ((Fintype.equivFin (V × V)).symm e).2))
(fun p => by
rw [rel_cons, mulVec_cons, b_cons]
have hc := hd.2.2.1 ((Fintype.equivFin (Internal N commodity)).symm p).1.1
((Fintype.equivFin (Internal N commodity)).symm p).1.2
((Fintype.equivFin (Internal N commodity)).symm p).2.1
((Fintype.equivFin (Internal N commodity)).symm p).2.2
unfold FlowNetwork.ConservesAt at hc
linarith)
ij)
(fun i => by
rw [rel_demand, mulVec_demand, b_demand]
exact hd.2.2.2 ((Fintype.equivFin K).symm i))
i
· intro h
refine ⟨?_, ?_, ?_, ?_⟩
· intro i u v
have hn := h.1 (Fintype.equivFin (K × (V × V)) (i, (u, v)))
simpa [lift₃, FinEncoding.lift] using (hn (by simp [toGeneralLP]))
· intro u v
have hc := h.2 (capIndex N commodity (Fintype.equivFin (V × V) (u, v)))
rw [rel_cap] at hc
rw [mulVec_cap, b_cap] at hc
simpa [aggregate] using hc
· intro i u hu hw
let e : Internal N commodity := ⟨(i, u), ⟨hu, hw⟩⟩
have hcons := h.2 (consIndex N commodity (Fintype.equivFin (Internal N commodity) e))
rw [rel_cons] at hcons
rw [mulVec_cons, b_cons] at hcons
have hcons' : FlowNetwork.outflow (f i) u - FlowNetwork.inflow (f i) u = 0 := by
simpa [e] using hcons
unfold FlowNetwork.ConservesAt
linarith
· intro i
have hdemand := h.2 (demandIndex N commodity (Fintype.equivFin K i))
rw [rel_demand] at hdemand
rw [mulVec_demand, b_demand] at hdemand
simpa using hdemandThe general-form objective is the aggregate minimum cost.
lemma objective_generalLP (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) (f : K → V → V → ℝ) :
(toGeneralLP N commodity unitCost).objective (lift₃ f) = cost unitCost f := by
dsimp [GeneralLP.objective, toGeneralLP, cost, aggregate]
simp only [dotProduct]
rw [FinEncoding.sum_reindex]
simp [lift₃, FinEncoding.lift]
rw [Fintype.sum_prod_type]
rw [Finset.sum_comm]
rw [Fintype.sum_prod_type]
simp [Finset.mul_sum]The finite standard-form program obtained by normalizing the multicommodity general program.
noncomputable def toStandardLP (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) :=
(toGeneralLP N commodity unitCost).toStandardLPThe expansion of a commodity flow to the normalized standard-form variable vector.
noncomputable def fullLift (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) (f : K → V → V → ℝ) :
Fin (Fintype.card (K × (V × V)) + Fintype.card (K × (V × V))) → ℝ :=
(toGeneralLP N commodity unitCost).lift (lift₃ f)The normalized standard-form objective equals the signed aggregate cost.
theorem objective_toStandardLP (N : FlowNetwork V) (commodity : K → Commodity V)
(unitCost : V → V → ℝ) (f : K → V → V → ℝ) :
(toStandardLP N commodity unitCost).objective (fullLift N commodity unitCost f) = -cost unitCost f := by
unfold toStandardLP fullLift
rw [GeneralLP.objective_lift]
rw [objective_generalLP]
simp [toGeneralLP, GeneralLP.objectiveSign]Feasibility is exactly preserved under the finite standard-form encoding.
theorem feasible_iff_toStandardLP (N : FlowNetwork V) (commodity : K → Commodity V)
(f : K → V → V → ℝ) :
IsFeasible N commodity f ↔ (toStandardLP N commodity (fun _ _ => 0)).IsFeasible (fullLift N commodity (fun _ _ => 0) f) := by
unfold toStandardLP fullLift
rw [← GeneralLP.feasible_iff_lift]
exact feasible_iff_generalLP N commodity fend Encodingend MulticommodityFlowLPend Chapter29end CLRS