PreviousUpNext

15.4.778  src/lib/graph/maximum-flow-g.pkg

# maximum-flow-g.pkg
# This module implements max (s, t) flow.
#
# -- Allen Leung

# Compiled by:
#     src/lib/graph/graphs.lib

# See also:
#     src/lib/graph/test-max-flow.pkg
#     src/lib/compiler/back/low/doc/latex/graphs.tex



###          "In the Universe the
###           difficult things are done
###           as if they were easy."
###
###                     -- Lao Tzu




stipulate
    package odg =  oop_digraph;                                         # oop_digraph           is from   src/lib/graph/oop-digraph.pkg
    package vec =  rw_vector;                                                   # rw_vector     is from   src/lib/std/src/rw-vector.pkg
herein

    generic package maximum_flow_g (num:  Abelian_Group)                        # Abelian_Group is from   src/lib/graph/group.api
    #
    : (weak) Maximum_Flow                                                       # Maximum_Flow  is from   src/lib/graph/maximum-flow.api
    {
        package num = num;


        # Use Goldberg's preflow-push approach.
        # This algorithm is presented in the book by Cormen, Leiserson and Rivest.

        fun max_flow { graph=>odg::DIGRAPH ggg, s, t, capacity, flows }
            =
            {   if  (s == t)
                     raise exception odg::BAD_GRAPH "maxflow";
                fi;

                nnn        = ggg.capacity ();
                mmm        = ggg.order ();

                zero       = num::zero;

                neighbors  = vec::make_rw_vector (nnn,[]);
                dist       = vec::make_rw_vector (nnn, 0);
                excess     = vec::make_rw_vector (nnn, zero);
                current    = vec::make_rw_vector (nnn,[]);

                fun min (a, b)
                    =
                    if (num::(<) (a, b) ) a; else b;fi;

                fun is_zero a
                    =
                    num::(====) (a, zero);

                my (-_)      = num::neg;

                fun set_up_preflow ()
                    =
                    {   fun add_edge (e as (u, _, _))
                            = 
                            vec::set (neighbors, u, e ! vec::get (neighbors, u));

                         ggg.forall_edges
                             (   \\ e as (u, v, e')
                                    =
                                    {   c = capacity e; 

                                        if  (u == s)

                                             f  = REF c;
                                             f' = REF(-c);

                                             add_edge (u, v, (f, c, f', TRUE, e'));
                                             add_edge (v, u, (f', zero, f, FALSE, e'));

                                             vec::set (excess, v, num::(+)(c, vec::get (excess, v)));

                                        else 
                                             f  = REF zero;
                                             f' = REF zero;

                                             add_edge (u, v, (f, c, f', TRUE, e'));
                                             add_edge (v, u, (f', zero, f, FALSE, e'));
                                        fi;
                                      }
                             );

                        vec::set (dist, s, mmm);
                    };


                # 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, _)))
                    =
                    {   c_f =  num::(-) (cap,*flow);
                        d_f =  min (e_u, c_f); 
                        e_v =  vec::get (excess, v);

                        flow  :=  num::(+) (*flow, d_f);
                        flow' :=  -(*flow);

                        vec::set (excess, v, num::(+) (e_v, d_f));
                        num::(-) (e_u, d_f);
                    };

                # Lift a vertex
                # dist[v] := 1 + min { dist[w] | (v, w) \in E_f } 
                # Returns the new dist[v]

                fun lift v
                    =
                    d_v
                    where 
                        fun loop ([], d_v)
                                =>
                                d_v;

                            loop((v, w, (f, c, _, _, _)) ! es, d_v)
                                =>
                                if  (num::(<) (*f, c))   loop (es, int::min (vec::get (dist, w), d_v));
                                else                     loop (es, d_v);                          fi;
                        end;

                        d_v =  loop (vec::get (neighbors, v), 1000000000) + 1;

                        vec::set (dist, v, d_v); 

                    end;


                # Push all excess flow thru admissible edges to neighboring vertices 
                # until all excess flow has been discharged.

                fun discharge v
                    =
                    {   e_v = vec::get (excess, v);

                        if  (is_zero e_v)
                             FALSE;
                        else
                             fun loop (d_v, e_v, (e as (v, w, (f, c, _, _, _))) ! es)
                                     => 
                                     if  (num::(<) (*f, c)   and   d_v == vec::get (dist, w) + 1)

                                          e_v = push (e_v, e); 

                                          if  (is_zero  e_v)   (d_v, es); 
                                          else                 loop (d_v, e_v, es);          fi; 

                                     else
                                          loop (d_v, e_v, es);
                                     fi;

                                 loop (_, e_v,[])
                                     =>
                                     loop (lift v, e_v, vec::get (neighbors, v));
                             end;

                             d_v       = vec::get (dist, v);

                             my (d_v', es)
                                 =
                                 loop (d_v, e_v, vec::get (current, v));

                             vec::set (excess, v, zero);    #  e[v] must be zero 
                             vec::set (current, v, es);  

                             d_v != d_v';
                        fi;
                    };                  # fun discharge 

                fun lift_to_front ()
                    =
                    {   set_up_preflow();

                        iterate(
                            [],
                            list::fold_backward
                                (   \\ ((u, _), l)
                                        =
                                        if  (u == s or u == t)
                                            # 
                                            l;
                                        else
                                            vec::set (current, u, vec::get (neighbors, u));
                                            u ! l;
                                        fi
                                )
                                [] 
                                (ggg.nodes ())
                        );
                    }

                also
                fun iterate (_,[]) => ();

                    iterate (f, u ! b)
                        => 
                        if  (discharge u)   iterate([u], reverse f@b);
                        else                iterate (u ! f, b);
                        fi;
                end;

                lift_to_front ();

                ggg.forall_nodes
                    (\\ (i, _)
                        =
                        apply
                            (\\ (i, j, (f, _, _, x, e'))
                                =
                                if  x
                                    flows ((i, j, e'), *f); 
                                fi
                            )
                            (vec::get (neighbors, i))
                    );

                list::fold_backward
                    (\\ ((_, _, (f, _, _, _, _)), n) =  num::(+) (*f, n))
                    zero
                    (vec::get (neighbors, s));
            };

        fun min_cost_max_flow { graph=>odg::DIGRAPH ggg, s, t, capacity, cost, flows }
            = 
            raise exception  odg::UNIMPLEMENTED;

    };
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext