Source file graph.ml
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
(** Structure de graphe creux. *)
module type GMap = sig
type key
type 'a t
val create : unit -> 'a t
val get : 'a t -> key -> 'a
val set : 'a t -> key -> 'a -> 'a t
val remove : 'a t -> key -> 'a t
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val iter : (key -> 'a -> unit) -> 'a t -> unit
end
module type S =
sig
type t
type key
type edge_data
val create : unit -> t
val marshal : t -> string
val unmarshal : string -> t
val succ : t -> key -> (key * edge_data) list
val pred : t -> key -> (key * edge_data) list
val add : t -> key * key * edge_data -> t
val rem : t -> key * key -> (edge_data -> bool) -> t
val rem_all : t -> key * key -> t
val isolate : t -> key -> t
val remove_node : t -> key -> t
val pred_roots : ?ignore_deps: edge_data list -> t -> key list
val succ_roots : t -> key list
val recursive_succs : t -> ?pred: (edge_data -> bool) -> key -> key list
val recursive_preds : t -> ?pred: (edge_data -> bool) -> key -> key list
val reverse : t -> t
val fold_succ : t -> (key -> (key * edge_data) list -> 'a -> 'a) -> 'a -> 'a
val fold_pred : t -> (key -> (key * edge_data) list -> 'a -> 'a) -> 'a -> 'a
val iter_succ : t -> (key -> (key * edge_data) list -> unit) -> unit
val iter_pred : t -> (key -> (key * edge_data) list -> unit) -> unit
val dot_of_graph :
?f_edge:(edge_data -> string * (string * string) list) ->
f_node:(key -> string * string * (string * string) list) ->
t -> string
val nodes_by_pred_order : t -> key list
val shortest_path :
t ->
(t -> key * key -> (float * edge_data) option) ->
key * key -> (key * edge_data * key) list
end
;;
(**
Notre module {!Graph} permet la construction et la manipulation de graphes creux,
avec la possibilit d'annoter chaque arc reliant deux sommets par un
type donn par le module le paramtre [Edge].
*)
module Make (M: GMap) (Edge: Map.OrderedType) = struct
(** Pour reprsenter un graphe, nous utilisons deux "maps", l'un pour
avoir rapidement les successeurs d'un sommet, l'autre pour avoir rapidement
ses prdcesseurs.
La structure de ces "maps" est dfinie par le module en paramtre.
A chaque indice des deux maps, nous avons donc respectivement
la liste des sucesseurs et des prdcesseurs du sommet correspondant cet
indice.
Les listes des successeurs et prdcesseurs sont des listes de paires
[(identifiant du sommet, donne d'annotation)]. Quand on ajoute un arc [i -> j],
il est en fait ajout une fois dans la liste des successeurs de [i] et une
fois dans la liste des prdcesseurs de [j]. Les donnes d'annotation sont donc
en double. Il faut donc veiller ce qu'elles ne soient pas trop grosses et
prfrer au besoin l'utilisation d'un identifiant dans une autre structure.
Le module [Edge] permet d'indiquer le type des annotations.
Ainsi, on peut comparer les donnes qui annotent les arcs, pour pouvoir supprimer
par exemple un arc entre deux sommets et correspondant une annotation, sans
supprimer un autre arc entre ces deux mmes sommets mais ayant une autre annotation.
*)
type key = M.key
type edge_data = Edge.t
type t = {
succ : (M.key * edge_data) list M.t; (** successors of a node *)
pred : (M.key * edge_data) list M.t; (** predecessors of a node *)
};;
let create () =
{
succ = M.create () ;
pred = M.create () ;
};;
let marshal t = Marshal.to_string t.succ []
let unmarshal s =
let succ = Marshal.from_string s 0 in
let add_one dst map (src, data) =
try
let l = M.get map src in
M.set map src ((dst, data)::l)
with Not_found -> M.set map src [dst, data]
in
let add_list key succs map =
List.fold_left (add_one key) map succs
in
let pred =
M.fold add_list
succ
(M.create ())
in
{ succ = succ ;
pred = pred ;
}
(** Les accs aux successeurs et prdcesseurs se font l'aide des fonctions suivantes,
et sont obtenus sous la forme de liste de paires
[(identifiant du successeur/prdcesseur, donne d'annotation de l'arc)].
*)
let succ g key = try M.get g.succ key with Not_found -> [];;
let pred g key = try M.get g.pred key with Not_found -> [];;
(** L'ajout dans un graphe se fait en utilisant la fonction {!add} et en prcisant
le graphe et un triplet [(sommet source, sommet destination, donne d'annotation)].
Si le mme arc est ajout deux fois avec la mme annotation, le deuxime
ajout est ignor (utilisation de la fonction de comparaison pour le dterminer).
*)
let add g (i,j,data) =
let new_succ =
try ignore(M.get g.succ j); g.succ
with Not_found -> M.set g.succ j []
in
let new_pred =
try ignore(M.get g.pred i); g.pred
with Not_found -> M.set g.pred i []
in
let g = { succ = new_succ; pred = new_pred } in
let succ =
let succs = succ g i in
if not (List.exists (fun (k,d) -> k = j && Edge.compare d data = 0) succs) then
M.set g.succ i ((j, data) :: succs)
else
g.succ
in
let pred =
let preds = pred g j in
if not (List.exists (fun (k,d) -> k = i && Edge.compare d data = 0) preds) then
M.set g.pred j ((i, data) :: preds)
else
g.pred
in
{ succ = succ ; pred = pred }
(** Pour supprimer un arc parmi d'autres entre deux sommets [i] et [j], on utilise la
fonction {!rem} avec une fonction de prdicat prenant en paramtre une annotation
et qui renvoie [true] si l'arc en question doit tre supprim.
*)
let rem g (i,j) predic =
let succ = M.set g.succ i (List.filter (fun (k,d) -> k <> j || not (predic d)) (succ g i)) in
let pred = M.set g.pred j (List.filter (fun (k,d) -> k <> i || not (predic d)) (pred g j)) in
{ succ = succ ; pred = pred };;
(** Il est galement possible de supprimer tous les arcs entre deux sommets [i] et [j],
avec la fonction {!rem_all}. Cela revient utiliser {!rem} avec un prdicat
retournant toujours [true]. *)
let rem_all g (i,j) = rem g (i,j) (fun _ -> true);;
(** Isole le sommet indiqu en supprimant tous les arcs qui l'ont pour source ou pour
destination. *)
let isolate g i =
let g = List.fold_right (fun (j,_) g -> rem_all g (i,j)) (succ g i) g in
List.fold_right (fun (j,_) g -> rem_all g (j,i)) (pred g i) g;;
let remove_node g i =
let g = isolate g i in
let new_succ = M.remove g.succ i in
let new_pred = M.remove g.pred i in
{ succ = new_succ; pred = new_pred }
(** Il est possible d'obtenir les "racines" du graphe, soit en tant que prdcesseurs
(ce sont les sommets n'ayant pas de prcdesseurs et prcdant donc tous les autres
sommets), soit en tant que successeurs (ce sont les sommets qui n'ont pas de
successeurs), respectivement avec les fonctions {!pred_roots} et {!succ_roots}.
@param le paramtre ignore depends permet d'indiquer des types d'arcs ignorer
pour le calcul.
*)
let pred_roots ?(ignore_deps=[]) g =
match ignore_deps with
[] ->
M.fold (fun key l acc -> match l with [] -> key :: acc | _ -> acc) g.pred []
| deps ->
let pred_edge (_,dep) = not (List.mem dep deps) in
let pred edges = not (List.exists pred_edge edges) in
M.fold (fun key l acc -> if pred l then key :: acc else acc) g.pred []
let succ_roots g =
M.fold (fun key l acc -> match l with [] -> key :: acc | _ -> acc) g.succ [];;
(** La fonction {!reverse} permet de changer le sens des arcs, les successeurs devenant
prdcesseurs et rciproquement. Attention, les donnes d'annotation restent inchanges. *)
let reverse g = { pred = g.succ; succ = g.pred };;
let fold_succ g f = M.fold f g.succ;;
let fold_pred g f = M.fold f g.pred;;
(** Deux fonctions de convenance existent pour appliquer une fonction chaque sommet et
respectivement tous ses successeurs ou tous ses prdcesseurs:
{!iter_succ} et {!iter_pred}. *)
let iter_succ g f = M.iter f g.succ;;
let iter_pred g f = M.iter f g.pred;;
(** Il est possible d'imprimer le graphe au format {{:http://www.graphviz.org}Graphviz},
en utilisant la fonction {!dot_of_graph}.
@param f_edge permet d'indiquer quelle chane de caractres utiliser comme label
pour une annotation d'arc.
@param f_node permet d'indiquer quelle chane de caractres utiliser comme label
pour un sommet.
*)
let dot_of_graph
?(f_edge: (Edge.t -> string * (string * string) list) option)
~(f_node: (M.key -> string * string * (string * string) list))
(graph : t) =
let b = Buffer.create 512 in
let atts_of_node =
fun x ->
let (_, label, atts) = f_node x in
("label", label) :: atts
in
let atts_of_edge =
match f_edge with
None -> (fun _ -> [])
| Some f ->
fun x ->
let (label, atts) = f x in
("label", label) :: atts
in
Buffer.add_string b
("digraph G {ratio=auto;\n"^
"margin=\"0.1,0.1\";\n");
let string_of_att (s1,s2) = Printf.sprintf "%s=\"%s\"" s1 s2 in
let string_of_atts = function
[] -> ""
| atts -> Printf.sprintf "[%s]"
(String.concat "," (List.map string_of_att atts))
in
let module S =
Set.Make (struct type t = M.key let compare = Stdlib.compare end)
in
let printed = ref S.empty in
let print_if_not_yet node_id =
if not (S.mem node_id !printed) then
begin
let (nid,_,_) = f_node node_id in
Printf.bprintf b "%s %s;\n"
nid (string_of_atts (atts_of_node node_id));
printed := S.add node_id !printed
end
in
let f node_id succs =
print_if_not_yet node_id;
let (nid,_,_) = f_node node_id in
List.iter
(fun (id,data) ->
print_if_not_yet id;
let (id,_,_) = f_node id in
let atts = atts_of_edge data in
Printf.bprintf b "%s -> %s %s;\n"
nid id
(string_of_atts atts);
)
succs
in
iter_succ graph f;
Buffer.add_string b "}\n";
Buffer.contents b
;;
(** La fonction {!nodes_by_pred_order} permet de retourner une liste des sommets
dans leur ordre (partiel) de prcdence. *)
let nodes_by_pred_order g =
let rec iter g acc =
match pred_roots g with
[] -> List.rev acc
| i :: _ -> iter (remove_node g i) (i::acc)
in
iter g [];;
let recursive_next get_next =
let module S =
Set.Make (struct type t = M.key let compare = Stdlib.compare end)
in
let filter pred l = List.filter (fun (_,edge) -> pred edge) l in
let rec iter g ?(pred=(fun _ -> true)) acc id =
let next = get_next g id in
let _next = filter pred next in
List.fold_left
(fun acc (next_id, _) ->
if S.mem next_id acc then
acc
else
iter g ~pred (S.add next_id acc) next_id)
acc
next
in
fun g ?pred id ->
S.elements (iter g ?pred S.empty id)
let recursive_succs = recursive_next succ
let recursive_preds = recursive_next pred
(** La fonction {!shortest_path} calcule le plus court chemin entre deux sommets
[s] et [d], d'aprs une fonction de cot en paramtre.
La fonction de cot doit retourner une valeur strictement positive ainsi
que l'annotation de l'arc utilis pour avoir cette valeur (il est possible
d'avoir des cots diffrents entre deux sommets s'il y a plusieurs arcs entre
ces deux sommets). La fonction de cot retourne [None] s'il n'est pas possible
d'aller d'un sommet donn un autre (les deux sommets ne sont pas connects).
L'algorithme utilis est celui de
{{:http://tide4javascript.com/?s=Dijkstra}Djikstra}.
*)
let shortest_path g cost (s,d) =
let p = M.fold
(fun i _ p ->
M.set p i (if i = s then 0.0 else infinity))
g.succ
(M.create())
in
let v_done = M.fold
(fun i _ acc -> M.set acc i false) g.succ (M.create())
in
let v_pred = M.fold
(fun i _ acc -> M.set acc i None) g.succ (M.create())
in
let (_,_,v_pred) = M.fold
(fun v _ (p, v_done, v_pred) ->
match pred g v with
[] ->
(p, v_done, v_pred)
| _ ->
let (_mindist, closest) =
M.fold
(fun i _ (mindist, closest) ->
if (not (M.get v_done i)) && M.get p i < mindist then
(M.get p i, Some i)
else
(mindist, closest)
)
g.succ
(infinity, None)
in
match closest with
None -> (p, v_done, v_pred)
| Some closest ->
let v_done = M.set v_done closest true in
let (p, v_pred) = M.fold
(fun i _ (p, v_pred) ->
if not (M.get v_done i) then
match cost g (closest, i) with
| None -> (p, v_pred)
| Some (w, edge_data) ->
if (M.get p closest) +. w < M.get p i then
(M.set p i (M.get p closest +. w),
M.set v_pred i (Some (closest, edge_data))
)
else
(p, v_pred)
else
(p, v_pred)
)
g.succ (p, v_pred)
in
(p, v_done, v_pred)
)
g.succ
(p, v_done, v_pred)
in
let rec build_path acc v =
match M.get v_pred v with
None -> acc
| Some (v2,edge_data) -> build_path ((v2,edge_data,v) :: acc) v2
in
let path = build_path [] d in
match path with
[] -> raise Not_found
| _ ->
path
end
module Make_with_map (P:Map.OrderedType) (Edge:Map.OrderedType) =
Make
(struct
module M = Map.Make (P)
type key = M.key
type 'a t = 'a M.t
let create () = M.empty
let get t k = M.find k t
let set t k v = M.add k v t
let remove t k = M.remove k t
let fold = M.fold
let iter = M.iter
end)
(Edge)
;;