File: innerRel.ml

package info (click to toggle)
herdtools7 7.58-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,732 kB
  • sloc: ml: 128,583; ansic: 3,827; makefile: 670; python: 407; sh: 212; awk: 14
file content (783 lines) | stat: -rw-r--r-- 22,697 bytes parent folder | download
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
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
(****************************************************************************)
(*                           the diy toolsuite                              *)
(*                                                                          *)
(* Jade Alglave, University College London, UK.                             *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France.                          *)
(*                                                                          *)
(* Copyright 2010-present Institut National de Recherche en Informatique et *)
(* en Automatique and the authors. All rights reserved.                     *)
(*                                                                          *)
(* This software is governed by the CeCILL-B license under French law and   *)
(* abiding by the rules of distribution of free software. You can use,      *)
(* modify and/ or redistribute the software under the terms of the CeCILL-B *)
(* license as circulated by CEA, CNRS and INRIA at the following URL        *)
(* "http://www.cecill.info". We also give a copy in LICENSE.txt.            *)
(****************************************************************************)

module type S =  sig
  type elt0

  module Elts : MySet.S with type elt = elt0
  include Rel.S with
  type elt1 = elt0 and type elt2 = elt0
  and module Elts1 = Elts
  and module Elts2 = Elts

(* Map representation *)
  module M : sig
    module ME : Map.S with type key = elt0
    type map = Elts.t ME.t
    val succs : elt0 -> map -> Elts.t
    val add : elt0 -> elt0 -> map -> map
    val subrel : map -> map -> bool
    val exists_path : (elt0 * elt0) -> map -> bool

    val to_map : t -> map
    val of_map : map -> t
  end

(* All elements related *)
  val nodes : t -> Elts.t

  val filter_nodes : (elt0 -> bool) -> t -> t
  val map_nodes : (elt0 -> elt0) -> t -> t

(* Inverse *)
  val inverse : t -> t

(* Set to relation *)
  val set_to_rln : Elts.t -> t

(* Are e1 and e2 related by the transitive closure of relation.
   Does not detect cycles *)
  val exists_path : elt1 * elt2 -> t -> bool

(* Nodes reachable from node and set of nodes [argument included in result] *)
  val reachable : elt0 -> t -> Elts.t
  val reachable_from_set : Elts.t -> t -> Elts.t

(* One path from one node to another, returns [] if none *)
  val path : elt0 -> elt0 -> t -> elt0 list
(* All leaves *)
  val leaves : t -> Elts.t
(* All leaves reachable from node *)
  val leaves_from : elt0 -> t -> Elts.t
(* All roots, ie all nodes with no predecessor *)
  val roots : t -> Elts.t

(* Idem backwards *)
  val up :  elt0 -> t -> Elts.t
  val up_from_set : Elts.t -> t -> Elts.t

(* Does not detect cycles either *)
  val transitive_to_map : t -> Elts.t M.ME.t
  val transitive_closure : t -> t

(* Direct cycles *)
  val is_reflexive : t -> bool
  val is_irreflexive : t -> bool

(* Explicit acyclicity check,  cost (one dfs) is neglectible
   w.r.t. transitive closure *)
  val get_cycle : t ->  elt0 list option
  val is_acyclic : t -> bool
  val is_cyclic : t -> bool

(* Transformation 'order' like lists into relations *)
  (* without transitive closure *)
  val order_to_succ : elt0 list -> t
  (* with transitive closure *)
  val order_to_rel : elt0 list -> t

(* Also for cycles *)
  val cycle_to_rel : elt0 list -> t
  val cycle_option_to_rel : elt0 list option -> t

(* Topological sort *)
  exception Cyclic
  val topo_kont : (elt0 -> 'a -> 'a) -> 'a -> Elts.t -> t -> 'a
  val topo :  Elts.t -> t -> elt0 list

  val pseudo_topo_kont :  (elt0 -> 'a -> 'a) -> 'a -> Elts.t -> t -> 'a

(****************************************************)
(* Continuation based all_topos (see next function) *)
(****************************************************)

(* Orders as a lists *)
  val all_topos_kont :  Elts.t -> t -> (elt0 list -> 'a -> 'a) -> 'a -> 'a
(* Orders as relations *)
  val all_topos_kont_rel :
      Elts.t -> t -> (t -> 'a) -> (t -> 'a -> 'a) -> 'a -> 'a

(* All toplogical orders, raises Cyclic in case of cycle
  Enhancement: all_topos nodes edges still works
  when edges relates elts not in nodes *)


  val all_topos : bool (* verbose *)-> Elts.t -> t -> elt0 list list

(* Strongly connected compoments, processed in inverse dependency order. *)
  val scc_kont : (elt0 list -> 'a -> 'a) -> 'a -> Elts.t -> t -> 'a


(* Is the parent relation of a hierarchy *)
  val is_hierarchy : Elts.t -> t -> bool

(* Remove any transitivity edges
   [LUC: set argument. removed, it is useless, since set = nodes rel is ok]
   remove_transitive_edges [set] rel
      [assumes rel \in set \times set]
      returns rel' \subset rel such that rel'
        does not have (e1, e2) if it has both (e1, e3) and (e3, e2),
        but transitive_closure rel' = transitive_closure rel
*)
  val remove_transitive_edges : t -> t

(* Sequence composition of relation *)
  val sequence : t-> t -> t
  val transitive3 : t -> t
  val sequences : t list -> t

(* Equivalence classes, applies to any relation.
   Behaves as if the argument relation r is first
   transformed into `(r | r^-1)*`.
*)
  val classes : t -> Elts.t list

(* strata ie sets of nodes by increasing distance *)
  val strata : Elts.t -> t -> Elts.t list

(*****************************************************************************)
(* "Bisimulation" w.r.t. relation r, with initial equivalence relation equiv *)
(*****************************************************************************)

(*
   `bisimulation T E0` computes the greater equivalence E such that
   E0 greater than E
   e1 <-E-> e2,  e1 -T-> e1' ==> exists e2' s.t. e2 -T-> e2', e1' <-E-> e2'
   e1 <-E-> e2,  e2 -T-> e2' ==> exists e1' s.t. e1 -T-> e1', e2' <-E-> e1'
*)
   val bisimulation : t (* transition *) -> t (* equivalence *)-> t
end

module Make(O:MySet.OrderedType) : S
with type elt0 = O.t
and module Elts = MySet.Make(O) =
  struct

    type elt0 = O.t


    module Elts = MySet.Make(O)

    include Rel.Make(O)(O)


    let nodes t =
      let xs =
        fold (fun (x,y) k -> Elts.add x (Elts.add y Elts.empty)::k) t [] in
      Elts.unions xs

    let filter_nodes p t =
      filter (fun (e1, e2) -> p e1 && p e2) t

    let map_nodes f t =
      map (fun (e1, e2) -> (f e1, f e2)) t

(* Inverse *)
    let inverse t = fold (fun (x,y) k -> add (y,x) k) t empty

(* Set to relation *)
    let set_to_rln s = Elts.fold (fun x k -> add (x,x) k) s empty

(* Internal tranformation to successor map *)

(*********************************************)
(* Transitive closure does not detect cycles *)
(*********************************************)

    exception Found

    let do_is_reachable succs r e1 e2 =
      let rec dfs e seen =
        if O.compare e e2 = 0 then raise Found
        else if Elts.mem e seen then seen
        else
          Elts.fold
            dfs (succs r e) (Elts.add e seen) in
      (* dfs e1 Elts.empty would yield reflexive-transitive closure *)
      Elts.fold dfs (succs r e1) (Elts.singleton e1)

    let is_reachable r e1 e2 = do_is_reachable succs r e1 e2

    let exists_path (e1, e2) r =
      try ignore (is_reachable r e1 e2) ; false
      with Found -> true

    let reachable_from_set start r =
      let rec dfs e seen =
        if Elts.mem e seen then seen
        else
          Elts.fold dfs (succs r e) (Elts.add e seen) in
      Elts.fold dfs start Elts.empty

    let reachable e r = reachable_from_set (Elts.singleton e) r

    exception Path of elt0 list

    let path e1 e2 t =
      let rec dfs e seen =
        if Elts.mem e seen then seen
        else
          Elts.fold
            (fun e seen ->
              if O.compare e e2 = 0 then raise (Path [e]) ;
              try dfs e seen
              with Path p -> raise (Path (e::p)))
            (succs t e)
            (Elts.add e seen) in
      try
        ignore (dfs e1 Elts.empty) ;
        []
      with Path es -> e1::es

    let leaves t =
      let all_nodes = nodes t in
      let non_leaves =
        Elts.of_list (fold (fun (e,_) k -> e::k) t []) in
      Elts.diff all_nodes non_leaves

    let leaves_from e t =
      let rec dfs e (leaves,seen as r) =
        if Elts.mem e seen then r
        else
          let es = succs t e in
          if Elts.is_empty es then
            Elts.add e leaves, Elts.add e seen
          else Elts.fold dfs es (leaves,Elts.add e seen) in
      let leaves,_ = dfs e (Elts.empty,Elts.empty) in
      leaves

    let roots t =
      let all_nodes = nodes t in
      let non_roots =
        Elts.of_list (fold (fun (_,e) k -> e::k) t []) in
      Elts.diff all_nodes non_roots


(* Back (and up) *)
    let up_from_set start r =
      let rec dfs e seen =
        if Elts.mem e seen then seen
        else
          Elts.fold dfs (preds r e) (Elts.add e seen) in
      Elts.fold dfs start Elts.empty

    let up e r = up_from_set (Elts.singleton e) r

(* Reflexivity check *)
    let is_reflexive r = exists (fun (e1,e2) -> O.compare e1 e2 = 0) r

    let is_irreflexive r = not (is_reflexive r)

(* Some operations can be accerelated with maps *)
    module M = struct

      module ME = Map.Make(O)

      type map = Elts.t ME.t

      let succs e m = try ME.find e m with Not_found -> Elts.empty
      let op_succs m e = succs e m

      let add x y m = ME.add x (Elts.add y (succs x m)) m

      let adds x ys m = ME.add x (Elts.union ys (succs x m)) m

      let exists_path (e1, e2) r =
        try ignore (do_is_reachable op_succs r e1 e2) ; false
        with Found -> true

      let to_map_ok p r =
        fold
          (fun (e1,e2) m -> if p e1 e2 then add e1 e2 m else m)
          r ME.empty

      let to_map r = to_map_ok (fun _ _ -> true) r

      let to_sym_map r =
        fold
          (fun (e1,e2) m -> add e1 e2 (add e2 e1 m))
          r ME.empty

      let of_map m =
        let xs =
          ME.fold
            (fun e1 es k -> of_succs e1 es::k)
            m [] in
        unions xs

(* sub relation *)

      let subrel m1 m2 =
        try
          ME.iter
            (fun x n1 ->
              let n2 = succs x m2 in
              if not (Elts.subset n1 n2) then raise Exit)
            m1 ;
          true
        with Exit -> false

(* Transitive closure the naive way,
   not significantly worse than before... *)

      let rec tr m0 =
        let m1 =
          ME.fold
            (fun x ys m ->
              let zs =
                Elts.fold
                  (fun y k -> succs y m::k)
                  ys [] in
              adds x (Elts.unions zs) m)
            m0 m0 in
        if ME.equal Elts.equal m0 m1 then m0
        else tr m1

(* Acyclicity check *)
      exception Cycle of (Elts.elt list)

      let rec mk_cycle f = function
        | [] -> assert false
        | e::rem ->
            if O.compare f e = 0 then [e]
            else e::mk_cycle f rem

      let get_cycle m =
        let rec dfs path above e seen =
          if Elts.mem e above then
            raise (Cycle (e::mk_cycle e path)) ;
          if Elts.mem e seen then
            seen
          else
            Elts.fold
              (dfs (e::path) (Elts.add e above))
              (succs e m) (Elts.add e seen) in
        try
          let _ =
            ME.fold
              (fun x _ -> dfs [] Elts.empty x) m Elts.empty in
          None
        with Cycle e -> Some (List.rev e)


(***************)
(* Reachablity *)
(***************)

      let reachable e e_succs m =
        let rec dfs e seen =
          if Elts.mem e seen then seen
          else
            Elts.fold dfs (succs e m) (Elts.add e seen) in
        Elts.fold dfs e_succs (Elts.singleton e)

      let cc m =
        let _,ccs =
          ME.fold
            (fun e succs (seen,ccs as r) ->
              if Elts.mem e seen then r
              else
                let cc = reachable e succs m in
                Elts.union cc seen,cc::ccs)
            m (Elts.empty,[]) in
        ccs

      let strata es r =
        let m =
          to_map_ok
            (fun e1 e2 -> Elts.mem e1 es && Elts.mem e2 es) r in
        let nss = ME.fold (fun _ ns k -> ns::k) m [] in
        let st0 = Elts.diff es (Elts.unions nss) in
        if Elts.is_empty st0 then [es]
        else
          let rec do_rec seen st =
            let stplus =
              Elts.diff
                (Elts.unions (Elts.fold (fun e k -> succs e m::k) st []))
                seen in
            if Elts.is_empty stplus then []
            else stplus::do_rec (Elts.union seen stplus) stplus in
          st0::do_rec st0 st0

(****************)
(* bisimulation *)
(****************)

      let ok x y e =  Elts.mem x (succs y e)

      let matches xs ys e =
        Elts.for_all
          (fun x -> Elts.exists (fun y -> ok x y e) ys)
          xs

      let step t e =
        ME.fold
          (fun x ys k ->
            let next_x = succs x t in
            Elts.fold
              (fun y k ->
                if O.compare x y = 0 then
                (* Optimisation, when identical will stay in o forever *)
                  add x y k
                else
                  let next_y = succs y t in
                  if
                    matches next_x next_y e &&
                    matches next_y next_x e
                  then
                    add x y k
                  else k)
              ys k)
          e ME.empty

      let rec fix t e =
        let next = step t e in
        if subrel e next then e else fix t next

      let bisimulation t e0 = fix t e0

    end

    let transitive_to_map r = M.to_map r |> M.tr

    let transitive_closure r = transitive_to_map r |> M.of_map

(* Acyclicity check *)

    let get_cycle r = M.get_cycle (M.to_map r)

    let is_acyclic r = match get_cycle r with
    | None -> true
    | Some _ -> false

    let is_cyclic r = not (is_acyclic r)

(* From lists to relations *)

    let rec order_to_succ = function
      | []|[_] -> empty
      | e1::(e2::_ as es) ->
          add (e1,e2) (order_to_succ es)

    let rec order_to_pairs k evts = match evts with
    | [] -> k
    | e1 :: tl ->
        let k = List.fold_left (fun k e2 -> (e1,e2)::k) k tl in
        order_to_pairs k tl

    let order_to_rel es = of_list (order_to_pairs []  es)

    let cycle_to_rel cy =
      let rec do_rec seen = function
        | [] -> assert false
        | [e] ->
            if Elts.is_empty seen then singleton (e,e)
            else  begin
              assert (Elts.mem e seen) ;
              empty
            end
        | e1::(e2::_ as rem) ->
            if Elts.mem e1 seen then empty
            else
              add (e1,e2) (do_rec (Elts.add e1 seen) rem) in
      do_rec Elts.empty cy

    let cycle_option_to_rel = function
      | None -> empty
      | Some cy -> cycle_to_rel cy

(********************)
(* Topological sort *)
(********************)
    exception Cyclic

    let topo_kont kont res all_nodes t =
      let m = M.to_map t in
      let rec dfs above n (o,seen as r) =
        if Elts.mem n above then raise Cyclic
        else if Elts.mem n seen then r
        else
          let res,seen =
            Elts.fold (dfs (Elts.add n above))
              (M.succs n m) (o,Elts.add n seen) in
          kont n res,seen in
      let ns = all_nodes in
      let o,_seen =
        Elts.fold
          (dfs Elts.empty) ns (res,Elts.empty) in
      (* Some node has not been visited, we have a cycle *)
      o

    let topo all_nodes t = topo_kont Misc.cons [] all_nodes t


    let pseudo_topo_kont kont res all_nodes t =
      let m = M.to_map t in
      let rec dfs n (res,seen as r) =
        if Elts.mem n seen then r
        else
          let res,seen =
            Elts.fold dfs
              (M.succs n m) (res,Elts.add n seen) in
          kont n res,seen in
      (* Search graph from non-successors *)
      let ns = all_nodes in
      let res,_ = Elts.fold dfs ns (res,Elts.empty) in
      res

(* New version of all_topos *)
    module EMap =
      Map.Make
        (struct
          type t = O.t
          let compare = O.compare
        end)

    let find_def d k m =
      try EMap.find k m
      with Not_found -> d

    let find_count = find_def 0
    let find_pred = find_def Elts.empty

    let make_count nodes edges =
      fold (fun (n1,n2) m ->
        if Elts.mem n1  nodes &&  Elts.mem n2  nodes then
          EMap.add n1 (find_count n1 m + 1) m
        else m) edges EMap.empty

    let make_preds nodes edges =
      fold
        (fun (n1,n2) m ->
          if Elts.mem n1  nodes &&  Elts.mem n2  nodes then
            EMap.add n2 (Elts.add n1 (find_pred n2 m)) m
          else m)
        edges EMap.empty

    let do_all_mem_topos set_preds kont =

      let rec do_aux ws count_succ pref res =
(* ws is the working set, ie minimal nodes
   count_succ is a map elt -> count of its succs
   set_pred is a map elt -> set of its preds
   pref is the prefix vos being constructed
   res is the list of vos already constructed *)
        if Elts.is_empty ws then
          if EMap.is_empty count_succ then kont pref res
          else raise Cyclic
        else
          Elts.fold
            (fun n res -> (* a maximal node (no successor) *)
              let ws = Elts.remove n ws in
              let n_preds = find_pred n set_preds in
              let count_succ,ws =
                Elts.fold
                  (fun pred (c,ws) ->
                    let p_succ = find_count pred c - 1 in
                    let _ = assert (p_succ >= 0) in
                    if p_succ > 0 then
                      EMap.add pred p_succ c,ws
                    else
                      let c = EMap.remove pred c in
                      let ws = Elts.add pred ws in
                      c,ws)
                  n_preds (count_succ,ws) in
              do_aux ws count_succ (n::pref) res) ws res in
      do_aux

    let fold_topos_ext kont res nodes edges =
      let edges = transitive_closure edges in
      let count_succ = make_count nodes edges in
      let set_preds = make_preds nodes edges in
      let ws =
        Elts.filter (fun n -> find_count n count_succ = 0) nodes in
      do_all_mem_topos set_preds kont ws count_succ [] res

    let all_topos_kont nodes edges kont res =
      fold_topos_ext kont res nodes edges

    let  all_topos_kont_rel es vb kfail kont res =
      try all_topos_kont es vb (fun k res -> kont (order_to_rel k) res) res
      with Cyclic -> kfail vb

    let all_topos verbose nodes edges =
      let nss = fold_topos_ext Misc.cons [] nodes edges in
      if verbose then begin
        let nres = List.length nss in
        if nres > 1023 then begin
          Printf.eprintf "Warning: all topos produced %i orderings\n%!" nres
        end
      end ;
      nss

(*
 * Strongly connected compponets, Tarjan's algorithm.
 * From R. Sedgwick's book "Algorithms".
 *)
    module SCC = struct
      module NodeMap = M.ME

      type state =
        { id : int;
          visit : int NodeMap.t;
          stack : elt0 list; }

      let rec pop_until n ns =
        match ns with
        | [] -> assert false
        | m::ns ->
           if O.compare n m = 0 then [m],ns
           else
             let ms,ns = pop_until n ns in
             m::ms,ns

      let zyva kont res nodes edges =

        let m = M.to_map edges in

        let rec dfs n ((res,s) as r) =
          try
            NodeMap.find n s.visit,r
          with
          | Not_found ->
             let min = s.id in
             let s = {
                 id = min + 1;
                 visit = NodeMap.add n min s.visit;
                 stack = n :: s.stack;
               } in
             let min,(res,s) as r =
               Elts.fold
                 (fun n (m0,r) ->
                   let m1,r = dfs n r in  Misc.min_int m0 m1,r)
                 (M.succs n m)
                 (min,(res,s)) in
             let valk =
               try NodeMap.find n s.visit with Not_found -> assert false in
             if not (Misc.int_eq min valk) then
               r (* n is part of previously returned scc *)
             else
               let scc,stack = pop_until n s.stack in
               let visit =
                 List.fold_left
                   (fun visit n -> NodeMap.add n max_int visit)
                   s.visit scc in
               let res = kont scc res in
               min,(res,{ s with stack; visit; }) in

        let (res,_) =
          Elts.fold
            (fun n r -> let _,r = dfs n r in r)
            nodes
            (res,{id=0; visit=NodeMap.empty; stack=[];} ) in
        res
    end

    let scc_kont kont res nodes edges = SCC.zyva kont res nodes edges

(* Is the parent relation of a hierarchy *)
    let is_hierarchy nodes edges =
      is_acyclic edges &&
      begin
        let m = M.to_map edges in
        try
          let zero =
            Elts.fold
              (fun e k ->
                match Elts.cardinal (M.succs e m) with
                | 0 -> e::k
                | 1 -> k
                | _ -> raise Exit)
              nodes [] in
          match zero with
          | [] -> Elts.cardinal nodes = 1 && is_empty edges
          | [_] -> true
          | _ -> false
        with Exit -> false
      end

(***************************)
(* Remove transitive edges *)
(***************************)

(*

  The problem is not as simple as removing all edges e1 --> e2
  s.t. there exists e3 with e1 -->+ e3 --->+ e2.

  See for instance
  Vincent Dubois & C\'ecile Bothorel
  "Transitive reduction for social networks and visuallization"
  International conference on web intelligence (WI'05)

  However a very simple algorithm exists.
 *)

    let remove_transitive_edge  r rel =
      let new_rel = remove r rel in
      if exists_path r new_rel then new_rel
      else rel

    let remove_transitive_edges rel = fold remove_transitive_edge rel rel

(************)
(* Sequence *)
(************)

    let append_map r m =
      fold
        (fun (e1,e2) ->
          Elts.fold
            (fun e3 -> add (e1,e3))
            (M.succs e2 m))
        r empty


    let sequence r1 r2 =
      let m2 = M.to_map r2 in
      append_map r1 m2

    let transitive3 r =
      let m = M.to_map r in
      append_map (append_map r m) m

    let rec seq_rec rs = match rs with
    | []|[_] as rs -> rs
    | r1::r2::rs -> sequence r1 r2::seq_rec rs

    let rec sequences rs = match rs with
    | [] -> empty
    | [r] -> r
    | _ -> sequences (seq_rec rs)

(* Equivalence classes *)
    let classes r = M.cc (M.to_sym_map r)

(**********)
(* Strata *)
(**********)

    let strata es r = M.strata es r

(****************)
(* Bisimulation *)
(****************)

    let bisimulation t e0 =
      let e = M.bisimulation (M.to_map t) (M.to_map e0) in
      M.of_map e

  end