1-- The first-unsettled-vertex argument for Dijkstra's greedy step.2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.GraphTheory.DijkstraCut8import WeightedPaths9import Connectivity1011-- S is the settled region. Its labels bound every source path.12predicate SettledLowerBounds (V E weights source S labels : Set) :=13 FunctionBetween(labels, S, ℕ) ∧14 (∀ u ∈ S, ∀ c ∈ ℕ, HasPathCost(V, E, weights, source, u, c) → labels(u) ≤ c)1516-- Before the source is settled, the next finite minimum must be zero.17-- Afterwards every route to an unsettled vertex must cross this boundary.18predicate FrontierCutBound (V E weights source S labels bound : Set) :=19Nat(bound) ∧ (source ∈ S ∨ bound =0) ∧20 (∀ u ∈ S, ∀ v ∈ V, ∀ k ∈ ℕ,21¬(v ∈ S) → WeightedEdge(E, weights, u, v, k) → bound ≤ add(labels(u), k))2223theorem FrontierCutBound.lower_bound (V E weights source S labels bound : Set, n : Nat)24requires settled : SettledLowerBounds(V, E, weights, source, S, labels)25requires frontier : FrontierCutBound(V, E, weights, source, S, labels, bound)26shows∀ v ∈ V, ∀ c ∈ ℕ, ¬(v ∈ S) →27 (v, c) ∈ pathsOfLength(V, E, weights, source, n) → bound ≤ c28proof29induction n30| zero =>31intros v hv c hc outside path32rw pathsOfLength.zero at path33have pair := singleton.membership.forward with path34have coordinates := orderedPair.injective with pair35rcases coordinates with⟨vs, c0⟩36rcases frontier with⟨boundNat, initial, edges⟩37cases initial with sourceInside | boundZero38 . exfalso39apply outside40rw vs41exact sourceInside42 . rw boundZero43rw c044exact LessEqual.reflexive with Naturals.constructor.zero45| succ(m) using ih =>46intros v hv c hc outside path47rw pathsOfLength.successor at path48have parts := costStep.membership.forward at V, E, weights,49 pathsOfLength(V, E, weights, source, m), (v, c) with path50rcases parts with⟨bounded, u, hu, y, hy, prior, hprior, k, hk, earlier, edge, pair⟩51have coordinates := orderedPair.injective with pair52rcases coordinates with⟨vy, cSum⟩53have yOutside : ¬(y ∈ S)54intro inside55apply outside56rw vy57exact inside58have priorNat := nat.omega.backward with hprior59have kNat := nat.omega.backward with hk60have sumNat := add.closed at k, prior with kNat61rcases frontier with⟨boundNat, initial, edges⟩62have alternatives : u ∈ S ∨¬(u ∈ S) bytauto63cases alternatives with uInside | uOutside64 . rcases settled with⟨function, shortest⟩65have mNat : Nat(m) bytypecheck66have mIn := nat.omega.forward with mNat67haveprefix : HasPathCost(V, E, weights, source, u, prior)68unfold HasPathCost69refine⟨m, mIn, earlier⟩70have priorBound := shortest with u, uInside, prior, hprior, prefix71have crossing := edges with u, uInside, y, hy, k, hk, yOutside, edge72have labelIn := FunctionBetween.apply_closed with function, uInside73have labelNat := nat.omega.backward with labelIn74have same := LessEqual.reflexive with kNat75have sumBound := LessEqual.add with labelNat, priorNat, kNat, kNat, priorBound, same76have labelSumNat := add.closed at k, labels(u) with kNat77have result := LessEqual.transitive with boundNat, labelSumNat, sumNat, crossing, sumBound78rw cSum79exact result80 . have prefixBound := ih with u, hu, prior, hprior, uOutside, earlier81have extensionBound := LessEqual.add_right with priorNat, kNat82have result := LessEqual.transitive with boundNat, priorNat, sumNat, prefixBound, extensionBound83rw cSum84exact result8586theorem FrontierCutBound.all_paths (V E weights source S labels bound target cost : Set)87requires settled : SettledLowerBounds(V, E, weights, source, S, labels)88requires frontier : FrontierCutBound(V, E, weights, source, S, labels, bound)89requires targetIn : target ∈ V90requires outside : ¬(target ∈ S)91requires costNat : Nat(cost)92requires path : HasPathCost(V, E, weights, source, target, cost)93shows bound ≤ cost94proof95rcases path with⟨n, hn, reached⟩96have nNat := nat.omega.backward with hn97have cIn := nat.omega.forward with costNat98have boundAll := FrontierCutBound.lower_bound with nNat, settled, frontier99exact boundAll with target, targetIn, cost, cIn, outside, reached100101-- This is the greedy step: a witnessed minimum at the cut is globally shortest.102theorem FrontierCutBound.certifies_next (V E weights source S labels bound target : Set)103requires settled : SettledLowerBounds(V, E, weights, source, S, labels)104requires frontier : FrontierCutBound(V, E, weights, source, S, labels, bound)105requires targetIn : target ∈ V106requires outside : ¬(target ∈ S)107requires path : HasPathCost(V, E, weights, source, target, bound)108shows ShortestPathCost(V, E, weights, source, target, bound)109proof110have boundNat : Nat(bound) bysimp [FrontierCutBound]111unfold ShortestPathCost112refine⟨boundNat, path, _⟩113intros other otherIn otherPath114have otherNat := nat.omega.backward with otherIn115exact FrontierCutBound.all_paths with settled, frontier, targetIn, outside, otherNat, otherPath116117-- F contains the discovered, unsettled vertices with finite tentative labels.118-- Every edge leaving S has already supplied a candidate in F.119predicate FrontierTable (V E weights S labels F tentative : Set) :=120 FunctionBetween(tentative, F, ℕ) ∧121 (∀ u ∈ S, ∀ v ∈ V, ∀ k ∈ ℕ,122¬(v ∈ S) → WeightedEdge(E, weights, u, v, k) →123 (v ∈ F ∧ tentative(v) ≤ add(labels(u), k)))124125theorem FrontierTable.cut_bound (V E weights source S labels F tentative bound : Set)126requires settled : SettledLowerBounds(V, E, weights, source, S, labels)127requires table : FrontierTable(V, E, weights, S, labels, F, tentative)128requires boundNat : Nat(bound)129requires initial : source ∈ S ∨ bound =0130requires minimal : ∀ v ∈ F, bound ≤ tentative(v)131shows FrontierCutBound(V, E, weights, source, S, labels, bound)132proof133rcases settled with⟨settledFunction, lower⟩134rcases table with⟨tentativeFunction, relaxed⟩135unfold FrontierCutBound136refine⟨boundNat, initial, _⟩137intros u hu v hv k hk outside edge138obtain⟨discovered, upper⟩:= relaxed with u, hu, v, hv, k, hk, outside, edge139have chosenBound := minimal with v, discovered140have tentativeIn := FunctionBetween.apply_closed with tentativeFunction, discovered141have tentativeNat := nat.omega.backward with tentativeIn142have kNat := nat.omega.backward with hk143have labelIn := FunctionBetween.apply_closed with settledFunction, hu144have labelNat := nat.omega.backward with labelIn145exact LessEqual.transitive with boundNat, tentativeNat, chosenBound, upper146147theorem FrontierTable.certifies_minimum (V E weights source S labels F tentative target bound : Set)148requires settled : SettledLowerBounds(V, E, weights, source, S, labels)149requires table : FrontierTable(V, E, weights, S, labels, F, tentative)150requires boundNat : Nat(bound)151requires initial : source ∈ S ∨ bound =0152requires minimal : ∀ v ∈ F, bound ≤ tentative(v)153requires targetIn : target ∈ V154requires outside : ¬(target ∈ S)155requires path : HasPathCost(V, E, weights, source, target, bound)156shows ShortestPathCost(V, E, weights, source, target, bound)157proof158have cut := FrontierTable.cut_bound with settled, table, boundNat, initial, minimal159exact FrontierCutBound.certifies_next with settled, cut, targetIn, outside, path160161-- Settling the certified vertex preserves the lower-bound invariant.162theorem SettledLowerBounds.enlarge (V E weights source S labels target cost updated : Set)163requires settled : SettledLowerBounds(V, E, weights, source, S, labels)164requires shortest : ShortestPathCost(V, E, weights, source, target, cost)165requires function : updated : S ∪ {target} → ℕ166requires oldValues : ∀ u ∈ S, updated(u) = labels(u)167requires newValue : updated(target) = cost168shows SettledLowerBounds(V, E, weights, source, S ∪ {target}, updated)169proof170rcases settled with⟨oldFunction, oldLower⟩171rcases shortest with⟨costNat, path, least⟩172unfold SettledLowerBounds173refine⟨function, _⟩174intros u hu c hc candidate175have alternatives := union2.membership.forward with hu176cases alternatives with old | new177 . have equality := oldValues with u, old178rw equality179exact oldLower with u, old, c, hc, candidate180 . have equality := singleton.membership.forward with new181rw [equality, newValue]182rw equality at candidate183exact least with c, hc, candidate184185-- Weighted paths have the same finite reachability meaning as graph routes.186theorem pathsOfLength.reachable (V E weights source : Set, n : Nat)187shows∀ v ∈ V, ∀ c ∈ ℕ,188 (v, c) ∈ pathsOfLength(V, E, weights, source, n) → Reachable(V, E, source, v)189proof190induction n191| zero =>192intros v hv c hc path193rw pathsOfLength.zero at path194have pair := singleton.membership.forward with path195have coordinates := orderedPair.injective with pair196rcases coordinates with⟨vs, c0⟩197rw vs198exact Reachable.reflexive199| succ(m) using ih =>200intros v hv c hc path201rw pathsOfLength.successor at path202have parts := costStep.membership.forward at V, E, weights,203 pathsOfLength(V, E, weights, source, m), (v, c) with path204rcases parts with⟨bounded, u, hu, y, hy, prior, hprior, k, hk, earlier, edge, pair⟩205have coordinates := orderedPair.injective with pair206rcases coordinates with⟨vy, cSum⟩207haveprefix:= ih with u, hu, prior, hprior, earlier208rcases edge with⟨adjacent, kNat, value⟩209rw vy210exact Reachable.extend withprefix, hy, adjacent211212theorem HasPathCost.reachable (V E weights source target cost : Set)213requires targetIn : target ∈ V214requires costNat : Nat(cost)215requires path : HasPathCost(V, E, weights, source, target, cost)216shows Reachable(V, E, source, target)217proof218rcases path with⟨n, hn, reached⟩219have nNat := nat.omega.backward with hn220have costIn := nat.omega.forward with costNat221have all := pathsOfLength.reachable at V, E, weights, source, n with nNat222exact all with target, targetIn, cost, costIn, reached223224theorem EdgeClosed.excludes_path (V E weights source target cost S : Set)225requires start : source ∈ S226requires closed : EdgeClosed(V, E, S)227requires targetIn : target ∈ V228requires outside : ¬(target ∈ S)229requires costNat : Nat(cost)230shows¬HasPathCost(V, E, weights, source, target, cost)231proof232intro path233have route := HasPathCost.reachable with targetIn, costNat, path234have contained := Reachable.in_closed_set with start, closed, route235exact outside with contained236237-- If relaxation has no finite candidates left, no weighted path can leave S.238theorem FrontierTable.empty_unreachable (V E weights source S labels tentative target cost : Set)239requires table : FrontierTable(V, E, weights, S, labels, empty, tentative)240requires graph : WeightedGraph(V, E, weights)241requires start : source ∈ S242requires included : S ⊆ V243requires targetIn : target ∈ V244requires outside : ¬(target ∈ S)245requires costNat : Nat(cost)246shows¬HasPathCost(V, E, weights, source, target, cost)247proof248rcases table with⟨tentativeFunction, relaxed⟩249rcases graph with⟨relation, weightFunction⟩250have closed : EdgeClosed(V, E, S)251intros u hu v hv edge252have alternatives : v ∈ S ∨¬(v ∈ S) bytauto253cases alternatives with inside | out254 . exact inside255 . exfalso256have pairIn : (u, v) ∈ E257unfold Adjacent at edge258exact edge259have weightIn := FunctionBetween.apply_closed with weightFunction, pairIn260have weightNat := nat.omega.backward with weightIn261have weighted : WeightedEdge(E, weights, u, v, weights((u, v)))262unfold WeightedEdge263refine⟨edge, weightNat, rfl⟩264obtain⟨inEmpty, inequality⟩:= relaxed with u, hu, v, hv, weights((u, v)), weightIn, out, weighted265exactset.empty with inEmpty266exact EdgeClosed.excludes_path with start, closed, targetIn, outside, costNat267