1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
(* Dijkstra's shortest path algorithm.
This proof follows Cormen et al's "Algorithms".
Author: Jean-Christophe Filliâtre (CNRS) *)
module ImpmapNoDom
use map.Map
use map.Const
type key
type t 'a = abstract { mutable contents: map key 'a }
val function create (x: 'a): t 'a
ensures { result.contents = const x }
val function ([]) (m: t 'a) (k: key): 'a
ensures { result = m.contents[k] }
val ghost function ([<-]) (m: t 'a) (k: key) (v: 'a): t 'a
ensures { result.contents = m.contents[k <- v] }
val ([]<-) (m: t 'a) (k: key) (v: 'a): unit
writes { m }
ensures { m = (old m)[k <- v] }
end
module DijkstraShortestPath
use int.Int
use ref.Ref
(** The graph is introduced as a set v of vertices and a function g_succ
returning the successors of a given vertex.
The weight of an edge is defined independently, using function weight.
The weight is an integer. *)
type vertex
clone set.SetImp with type elt = vertex
clone ImpmapNoDom with type key = vertex
constant v: fset vertex
val ghost function g_succ (_x: vertex) : fset vertex
ensures { subset result v }
val get_succs (x: vertex): set
ensures { result = g_succ x }
val function weight vertex vertex : int (* edge weight, if there is an edge *)
ensures { result >= 0 }
(** Data structures for the algorithm. *)
(* The set of already visited vertices. *)
val visited: set
(* Map d holds the current distances from the source.
There is no need to introduce infinite distances. *)
val d: t int
(* The priority queue. *)
val q: set
predicate min (m: vertex) (q: set) (d: t int) =
mem m q /\
forall x: vertex. mem x q -> d[m] <= d[x]
val q_extract_min () : vertex writes {q}
requires { not is_empty q }
ensures { min result (old q) d }
ensures { q = remove result (old q) }
(* Initialisation of visited, q, and d. *)
val init (src: vertex) : unit writes { visited, q, d }
ensures { is_empty visited }
ensures { q = singleton src }
ensures { d = (old d)[src <- 0] }
(* Relaxation of edge u->v. *)
let relax u v
ensures {
(mem v visited /\ q = old q /\ d = old d)
\/
(mem v q /\ d[u] + weight u v >= d[v] /\ q = old q /\ d = old d)
\/
(mem v q /\ (old d)[u] + weight u v < (old d)[v] /\
q = old q /\ d = (old d)[v <- (old d)[u] + weight u v])
\/
(not mem v visited /\ not mem v (old q) /\
q = add v (old q) /\
d = (old d)[v <- (old d)[u] + weight u v]) }
= if not mem v visited then
let x = d[u] + weight u v in
if mem v q then begin
if x < d[v] then d[v] <- x
end else begin
add v q;
d[v] <- x
end
(* Paths and shortest paths.
path x y d =
there is a path from x to y of length d
shortest_path x y d =
there is a path from x to y of length d, and no shorter path *)
inductive path vertex vertex int =
| Path_nil :
forall x: vertex. path x x 0
| Path_cons:
forall x y z: vertex. forall d: int.
path x y d -> mem z (g_succ y) -> path x z (d + weight y z)
lemma Length_nonneg: forall x y: vertex. forall d: int. path x y d -> d >= 0
predicate shortest_path (x y: vertex) (d: int) =
path x y d /\ forall d': int. path x y d' -> d <= d'
lemma Path_inversion:
forall src v:vertex. forall d:int. path src v d ->
(v = src /\ d = 0) \/
(exists v':vertex. path src v' (d - weight v' v) /\ mem v (g_succ v'))
lemma Path_shortest_path:
forall src v: vertex. forall d: int. path src v d ->
exists d': int. shortest_path src v d' /\ d' <= d
(* Lemmas (requiring induction). *)
lemma Main_lemma:
forall src v: vertex. forall d: int.
path src v d -> not (shortest_path src v d) ->
v = src /\ d > 0
\/
exists v': vertex. exists d': int.
shortest_path src v' d' /\ mem v (g_succ v') /\ d' + weight v' v < d
lemma Completeness_lemma:
forall s: set.
(* if s is closed under g_succ *)
(forall v: vertex.
mem v s -> forall w: vertex. mem w (g_succ v) -> mem w s) ->
(* and if s contains src *)
forall src: vertex. mem src s ->
(* then any vertex reachable from s is also in s *)
forall dst: vertex. forall d: int.
path src dst d -> mem dst s
(* Definitions used in loop invariants. *)
predicate inv_src (src: vertex) (s q: set) =
mem src s \/ mem src q
predicate inv (src: vertex) (s q: set) (d: t int) =
inv_src src s q /\ d[src] = 0 /\
(* S and Q are contained in V *)
subset s v /\ subset q v /\
(* S and Q are disjoint *)
(forall v: vertex. mem v q -> mem v s -> false) /\
(* we already found the shortest paths for vertices in S *)
(forall v: vertex. mem v s -> shortest_path src v d[v]) /\
(* there are paths for vertices in Q *)
(forall v: vertex. mem v q -> path src v d[v])
predicate inv_succ (_src: vertex) (s q: set) (d: t int) =
(* successors of vertices in S are either in S or in Q *)
forall x: vertex. mem x s ->
forall y: vertex. mem y (g_succ x) ->
(mem y s \/ mem y q) /\ d[y] <= d[x] + weight x y
predicate inv_succ2 (_src: vertex) (s q: set) (d: t int) (u: vertex) (su: set) =
(* successors of vertices in S are either in S or in Q,
unless they are successors of u still in su *)
forall x: vertex. mem x s ->
forall y: vertex. mem y (g_succ x) ->
(x<>u \/ (x=u /\ not (mem y su))) ->
(mem y s \/ mem y q) /\ d[y] <= d[x] + weight x y
lemma inside_or_exit:
forall s, src, v, d. mem src s -> path src v d ->
mem v s
\/
exists y. exists z. exists dy.
mem y s /\ not (mem z s) /\ mem z (g_succ y) /\
path src y dy /\ (dy + weight y z <= d)
(* Algorithm's code. *)
let shortest_path_code (src dst: vertex)
requires { mem src v /\ mem dst v }
ensures { forall v: vertex. mem v visited ->
shortest_path src v d[v] }
ensures { forall v: vertex. not mem v visited ->
forall dv: int. not path src v dv }
= init src;
while not is_empty q do
invariant { inv src visited q d }
invariant { inv_succ src visited q d }
invariant { (* vertices at distance < min(Q) are already in S *)
forall m: vertex. min m q d ->
forall x: vertex. forall dx: int. path src x dx ->
dx < d[m] -> mem x visited }
variant { cardinal v - cardinal visited }
let u = q_extract_min () in
assert { shortest_path src u d[u] };
add u visited;
let su = get_succs u in
while not is_empty su do
invariant { subset su (g_succ u) }
invariant { inv src visited q d }
invariant { inv_succ2 src visited q d u su }
variant { cardinal su }
let v = choose_and_remove su in
relax u v;
assert { d[v] <= d[u] + weight u v }
done
done
end
|