File: max-flow.sml

package info (click to toggle)
mlton 20210117%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 58,464 kB
  • sloc: ansic: 27,682; sh: 4,455; asm: 3,569; lisp: 2,879; makefile: 2,347; perl: 1,169; python: 191; pascal: 68; javascript: 7
file content (129 lines) | stat: -rw-r--r-- 4,380 bytes parent folder | download | duplicates (5)
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
(*
 * This module implements max (s,t) flow.
 *
 * -- Allen
 *)

functor MaxFlow(Num : ABELIAN_GROUP) : MAX_FLOW =
struct
   structure G   = Graph
   structure A   = Array
   structure Num = Num

   (*
    * Use Goldberg's preflow-push approach.
    * This algorithm is presented in the book by Cormen, Leiserson and Rivest.
    *)
   fun max_flow{graph=G.GRAPH G, s, t, capacity, flows} =
   let val _          = if s = t then raise G.Graph "maxflow" else ()
       val N          = #capacity G ()
       val M          = #order G ()
       val neighbors  = A.array(N,[])
       val zero       = Num.zero
       val dist       = A.array(N,0)
       val excess     = A.array(N,zero)
       val current    = A.array(N,[])

       fun min(a,b) = if Num.<(a,b) then a else b
       fun isZero a = Num.==(a,zero)
       val ~        = Num.~

       fun initialize_preflow() =
       let fun add_edge (e as (u,_,_)) = 
               A.update(neighbors,u,e::A.sub(neighbors,u))
       in  #forall_edges G (fn e as (u,v,e') => 
               let val c = capacity e 
               in  if u = s then
                      let val f = ref c and f' = ref(~ c)
                      in  add_edge (u,v,(f,c,f',true,e'));
                          add_edge (v,u,(f',zero,f,false,e'));
                          A.update(excess,v,Num.+(c,A.sub(excess,v)))
                      end
                   else 
                      let val f = ref zero and f' = ref zero
                      in  add_edge (u,v,(f,c,f',true,e'));
                          add_edge (v,u,(f',zero,f,false,e'))
                      end
               end);
           A.update(dist,s,M)
       end

       (* 
        * Push d_f(u,v) = min(e[u],c(u,v)) units of flow from u to v 
        * Returns the new e_u
        *)
       fun push(e_u,(u,v,(flow,cap,flow',x,_))) =
       let val c_f = Num.-(cap,!flow)
           val d_f = min(e_u,c_f) 
           val e_v = A.sub(excess,v)
       in  flow  := Num.+(!flow,d_f);
           flow' := ~(!flow);
           A.update(excess,v,Num.+(e_v,d_f));
           Num.-(e_u,d_f)
       end

       (* Lift a vertex
        * dist[v] := 1 + min{ dist[w] | (v,w) \in E_f } 
        * Returns the new dist[v]
        *)
       fun lift(v) =
       let fun loop([],d_v) = d_v
             | loop((v,w,(f,c,_,_,_))::es,d_v) =
               if Num.<(!f,c) then loop(es,Int.min(A.sub(dist,w),d_v))
               else loop(es,d_v)
           val d_v = loop(A.sub(neighbors,v),1000000000) + 1
       in  A.update(dist,v,d_v); 
           d_v
       end

       (*
        * Push all excess flow thru admissible edges to neighboring vertices 
        * until all excess flow has been discharged.
        *)
       fun discharge(v) =
       let val e_v = A.sub(excess,v)
       in  if isZero e_v then false
           else
           let fun loop(d_v,e_v,(e as (v,w,(f,c,_,_,_)))::es) = 
                   if Num.<(!f,c) andalso d_v = A.sub(dist,w) + 1 then
                      let val e_v = push(e_v,e) 
                      in  if isZero e_v then (d_v,es) 
                          else loop(d_v,e_v,es) 
                      end
                   else loop(d_v,e_v,es)
                | loop(_,e_v,[]) = loop(lift(v),e_v,A.sub(neighbors,v))
               val d_v       = A.sub(dist,v)
               val (d_v',es) = loop(d_v,e_v,A.sub(current,v))
           in  A.update(excess,v,zero);    (* e[v] must be zero *)
               A.update(current,v,es);  
               d_v <> d_v'
           end
       end

       fun lift_to_front() =
          (initialize_preflow();
           iterate([],
              List.foldr(fn ((u,_),L) => 
                  if u = s orelse u = t then L
                  else (A.update(current,u,A.sub(neighbors,u)); u::L)) [] 
                      (#nodes G ()))
          )

       and iterate(_,[]) = ()
         | iterate(F,u::B) = 
            if discharge(u) then iterate([u],rev F@B)
            else iterate(u::F,B)

   in  lift_to_front();
       #forall_nodes G (fn (i,_) => 
           app (fn (i,j,(f,_,_,x,e')) => 
                if x then flows((i,j,e'),!f) else ())
               (A.sub(neighbors,i)));
       List.foldr (fn ((_,_,(f,_,_,_,_)),n) => Num.+(!f,n)) zero
            (A.sub(neighbors,s))
   end

   fun min_cost_max_flow{graph=G.GRAPH G, s, t, capacity, cost, flows} = 
       raise Graph.Unimplemented

end