Imports

29.3 Finite termination and public SIMPLEX

The trace contains one dictionary per visited basis. Bland's theorem makes those bases distinct, so the number of all finite basis sets is a sufficient fuel bound. The public result consequently has no exhaustion constructor.

namespace CLRSnamespace Chapter29namespace Dictionary

Dictionaries visited by the fuelled runner, including its input.

noncomputable def simplexTrace : Dictionary m n List (Dictionary m n) | 0, D => [D] | fuel + 1, D => match D.simplexStep with | .optimal _ => [D] | .unbounded _ _ _ => [D] | .pivot e l _ hl => D :: simplexTrace fuel (D.pivot l e hl.1.pivotCoefficient_pos.ne')
@[simp] theorem simplexTrace_head? (fuel : ) (D : Dictionary m n) : (D.simplexTrace fuel).head? = some D := by cases fuel with | zero => simp [simplexTrace] | succ fuel => cases hstep : D.simplexStep with | optimal _ => simp [simplexTrace, hstep] | unbounded _ _ _ => simp [simplexTrace, hstep] | pivot _ _ _ _ => simp [simplexTrace, hstep]

The generated execution list is a chain of certified Bland pivots.

theorem simplexTrace_isChain (fuel : ) (D : Dictionary m n) : (D.simplexTrace fuel).IsChain IsBlandPivot := by induction fuel generalizing D with | zero => simp [simplexTrace] | succ fuel ih => cases hstep : D.simplexStep with | optimal _ => simp [simplexTrace, hstep] | unbounded _ _ _ => simp [simplexTrace, hstep] | pivot e l he hl => let next := D.pivot l e hl.1.pivotCoefficient_pos.ne' rw [simplexTrace, hstep] apply (ih next).cons intro E hE rw [simplexTrace_head?] at hE have hEnext : E = next := by simpa using hE.symm subst E exact { entering := e leaving := l enteringIsBland := he leavingIsBland := hl next_eq := rfl }

Every exhausted run used all of its fuel for pivots.

theorem simplexTrace_length_of_isExhausted (fuel : ) (D : Dictionary m n) (hexhausted : (D.simplexRun fuel).IsExhausted) : (D.simplexTrace fuel).length = fuel + 1 := by induction fuel generalizing D with | zero => simp [simplexTrace] | succ fuel ih => cases hstep : D.simplexStep with | optimal _ => simp [simplexRun, hstep, SimplexRunResult.IsExhausted] at hexhausted | unbounded _ _ _ => simp [simplexRun, hstep, SimplexRunResult.IsExhausted] at hexhausted | pivot e l he hl => let next := D.pivot l e hl.1.pivotCoefficient_pos.ne' have hrest : (next.simplexRun fuel).IsExhausted := by simpa [simplexRun, hstep, next] using hexhausted simp [simplexTrace, hstep, next, ih next hrest]

A later member of a nonempty relation chain is transitively reachable from its head.

theorem transGen_of_isChain_cons_mem {α : Type*} {r : α α Prop} {a b : α} {xs : List α} (hchain : (a :: xs).IsChain r) (hb : b xs) : Relation.TransGen r a b := by induction xs generalizing a with | nil => simp at hb | cons c cs ih => have hac : r a c := hchain.rel_head rcases List.mem_cons.mp hb with rfl | hb · exact Relation.TransGen.single hac · exact Relation.TransGen.head hac (ih hchain.tail hb)

Feasibility and Bland no-cycling make the basis list of any chain duplicate-free.

theorem basisList_nodup_of_isChain (D : Dictionary m n) (xs : List (Dictionary m n)) (hchain : (D :: xs).IsChain IsBlandPivot) (hD : D.IsBasicFeasible) : ((D :: xs).map basicVariables).Nodup := by induction xs generalizing D with | nil => simp | cons E rest ih => have hstep : IsBlandPivot D E := hchain.rel_head obtain p := hstep rw [List.map_cons, List.nodup_cons] constructor · intro hmem obtain F, hF, hbasis := List.mem_map.mp hmem have hpath : Relation.TransGen IsBlandPivot D F := transGen_of_isChain_cons_mem hchain hF exact D.bland_no_repeated_basis hD hpath hbasis.symm · exact ih E hchain.tail (p.isBasicFeasible hD)

The generated trace visits no basis twice.

theorem simplexTrace_basis_nodup (fuel : ) (D : Dictionary m n) (hD : D.IsBasicFeasible) : ((D.simplexTrace fuel).map basicVariables).Nodup := by obtain xs, htrace : xs, D.simplexTrace fuel = D :: xs := by cases fuel with | zero => exact [], rfl | succ fuel => cases hstep : D.simplexStep with | optimal _ => exact [], by simp [simplexTrace, hstep] | unbounded _ _ _ => exact [], by simp [simplexTrace, hstep] | pivot e l he hl => exact simplexTrace fuel (D.pivot l e hl.1.pivotCoefficient_pos.ne'), by simp [simplexTrace, hstep] rw [htrace] exact basisList_nodup_of_isChain D xs (by simpa [htrace] using D.simplexTrace_isChain fuel) hD

Number of finite subsets of the stable variable universe; this is a simple sufficient bound on the number of possible bases.

def basisCount (m n : ) : := Fintype.card (Finset (LPVar m n))

Every feasible generated trace fits in the finite basis universe.

theorem simplexTrace_length_le_basisCount (fuel : ) (D : Dictionary m n) (hD : D.IsBasicFeasible) : (D.simplexTrace fuel).length basisCount m n := by have hnodup := D.simplexTrace_basis_nodup fuel hD have hbound := hnodup.length_le_card simpa [basisCount] using hbound

At the finite basis bound, fuel exhaustion is impossible.

theorem simplexRun_basisCount_not_exhausted (D : Dictionary m n) (hD : D.IsBasicFeasible) : ¬(D.simplexRun (basisCount m n)).IsExhausted := by intro hexhausted have hlength := D.simplexTrace_length_of_isExhausted (basisCount m n) hexhausted have hbound := D.simplexTrace_length_le_basisCount (basisCount m n) hD rw [hlength] at hbound omega

Correct public SIMPLEX outcomes for a fixed input dictionary.

inductive SimplexResult (D : Dictionary m n) where | optimal (assignment : LPVar m n ) (isOptimal : D.IsOptimalAssignment assignment) | unbounded (isUnbounded : D.IsUnbounded)

Textbook SIMPLEX with Bland's rule. Feasibility is supplied by the initialization phase formalized in Section 29.5.

noncomputable def simplex (D : Dictionary m n) (hD : D.IsBasicFeasible) : SimplexResult D := match hresult : D.simplexRun (basisCount m n) with | .optimal terminal _ => .optimal terminal.basicAssignment (by have hopt := D.simplexRun_optimal_correct (basisCount m n) hD (by simp [hresult, SimplexRunResult.IsOptimal]) simpa [hresult, SimplexRunResult.terminalDictionary] using hopt) | .unbounded _ _ _ _ => .unbounded (D.simplexRun_unbounded_correct (basisCount m n) hD (by rw [hresult] trivial)) | .exhausted _ => False.elim (D.simplexRun_basisCount_not_exhausted hD (by rw [hresult] trivial))

SIMPLEX returns either an optimal assignment or an unboundedness certificate for every basic-feasible dictionary.

theorem simplex_optimal_or_unbounded (D : Dictionary m n) (hD : D.IsBasicFeasible) : ( x, D.IsOptimalAssignment x) D.IsUnbounded := by cases D.simplex hD with | optimal x hx => exact Or.inl x, hx | unbounded h => exact Or.inr h
end Dictionaryend Chapter29end CLRS