Spaces:
Sleeping
Sleeping
| -- File: lean/ShortestPathSpecAeneas.lean | |
| -- Shortest Path (Dijkstra) — Aeneas-compatible specification. | |
| -- Types mirror what Aeneas generates from Vec<(u64,u64,u64)> Rust code. | |
| -- Requires: lake update (fetches the Aeneas package) | |
| import Aeneas | |
| open Aeneas.Primitives | |
| namespace ShortestPathSpecAeneas | |
| -- Aeneas maps Vec<(u64, u64, u64)> → List (U64 × U64 × U64) | |
| abbrev Graph := List (U64 × U64 × U64) -- (from, to, weight) | |
| -- Helper: safe list indexing | |
| def listGet (l : List (Option U64)) (i : U64) : Option (Option U64) := | |
| l.get? i.val.toNat | |
| -- Update distance at index i if new distance d is smaller | |
| def updateDist (dists : List (Option U64)) (i d : U64) : List (Option U64) := | |
| List.zipWith (fun j curr => | |
| if j == i.val.toNat then | |
| match curr with | |
| | none => some d | |
| | some existing => some (if d.val < existing.val then d else existing) | |
| else curr) | |
| (List.range dists.length) | |
| dists | |
| -- Relax one edge | |
| def relaxEdge (dists : List (Option U64)) (fr to w : U64) : List (Option U64) := | |
| match listGet dists fr with | |
| | some (some df) => | |
| match df + w with -- saturating add avoids overflow for reasonable weights | |
| | some newD => updateDist dists to newD | |
| | none => dists | |
| | _ => dists | |
| -- One relaxation pass over all edges | |
| def relaxPass (dists : List (Option U64)) (edges : Graph) : List (Option U64) := | |
| edges.foldl (fun d e => relaxEdge d e.1 e.2.1 e.2.2) dists | |
| -- Bellman-Ford: n-1 relaxation passes | |
| def dijkstra (graph : Graph) (n src : U64) : Result (List (Option U64)) := | |
| let init := (List.range n.val.toNat).map (fun i => | |
| if i == src.val.toNat then some (0 : U64) else none) | |
| let result := (List.range (n.val.toNat - 1)).foldl (fun d _ => relaxPass d graph) init | |
| .ok result | |
| -- Shortest distance src → dst | |
| def shortestDist (graph : Graph) (n src dst : U64) : Result (Option U64) := | |
| do let dists ← dijkstra graph n src | |
| .ok (match listGet dists dst with | |
| | some (some d) => some d | |
| | _ => none) | |
| -- Self-distance is zero when src < n | |
| theorem self_distance_zero (graph : Graph) (n src : U64) (h : src.val < n.val) : | |
| shortestDist graph n src src = .ok (some 0) := by | |
| simp only [shortestDist, dijkstra, Result.bind] | |
| simp only [listGet, List.get?] | |
| simp only [relaxPass, relaxEdge, updateDist] | |
| -- The init list at index src is some 0; relaxation preserves it. | |
| sorry -- Full proof requires showing relaxation doesn't worsen src's own distance | |
| end ShortestPathSpecAeneas | |