lean-migrate / lean /ShortestPathSpecAeneas.lean
Hrushi's picture
Upload folder using huggingface_hub
bf9c466 verified
-- 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