c++algorithmboostgraphminimum-cut

Cut set of a graph, Boost Graph Library


I've been struggling a lot to figure out how to do this. I'm interested in quickly finding the cut set of a graph. I know that BGL supports finding the cut set by iteration over the colorMap arguments supported by, e.g., edmonds_karp_max_flow. The Gomory Hu algorithm needs to make several calls to a minimum cut algorithm.

The result that I was hoping for was to have a multimap that contains: (color, vertex)

The following code is an attempt at rewriting the example from the Boost Graph Library to use a multimap for the associative_property_map. Compiling the code can be done with: clang -lboost_graph -o edmonds_karp edmonds_karp.cpp or g++ instead of clang. I don't get the errors that come out of.

#include <boost/config.hpp>
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/edmonds_karp_max_flow.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/read_dimacs.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/unordered_map.hpp>

int main()
{
  using namespace boost;

  typedef adjacency_list_traits < vecS, vecS, directedS > Traits;
  typedef adjacency_list < listS, vecS, directedS,
    property < vertex_name_t, std::string >,
    property < edge_capacity_t, long,
    property < edge_residual_capacity_t, long,
    property < edge_reverse_t, Traits::edge_descriptor > > > > Graph;

  Graph g;

  property_map < Graph, edge_capacity_t >::type
    capacity = get(edge_capacity, g);
  property_map < Graph, edge_reverse_t >::type rev = get(edge_reverse, g);
  property_map < Graph, edge_residual_capacity_t >::type
    residual_capacity = get(edge_residual_capacity, g);

  std::multimap<default_color_type, Traits::vertex_descriptor> colorMap;
  boost::associative_property_map< std::map<default_color_type,
                                            Traits::vertex_descriptor> >
      color_map(colorMap);

  Traits::vertex_descriptor s, t;
  read_dimacs_max_flow(g, capacity, rev, s, t);

  std::vector<Traits::edge_descriptor> pred(num_vertices(g));
  long flow = edmonds_karp_max_flow
      (g, s, t, capacity, residual_capacity, rev,
       make_iterator_property_map(color_map.begin()),
       &pred[0]);

  std::cout << "c  The total flow:" << std::endl;
  std::cout << "s " << flow << std::endl << std::endl;

  std::cout << "c flow values:" << std::endl;
  graph_traits < Graph >::vertex_iterator u_iter, u_end;
  graph_traits < Graph >::out_edge_iterator ei, e_end;
  for (boost::tie(u_iter, u_end) = vertices(g); u_iter != u_end; ++u_iter)
    for (boost::tie(ei, e_end) = out_edges(*u_iter, g); ei != e_end; ++ei)
      if (capacity[*ei] > 0)
        std::cout << "f " << *u_iter << " " << target(*ei, g) << " "
          << (capacity[*ei] - residual_capacity[*ei]) << std::endl;

  // if using the original example, unedited, this piece of code works
  // BOOST_FOREACH(default_color_type x, color){
  //   std::cout << x << std::endl;
  // }

  return EXIT_SUCCESS;
}

Hints will be greatly appreciated. Thank you.


Solution

  • Here's a quick PoC based on Boost BiMap

    typedef boost::bimap<bimaps::list_of<default_color_type>, bimaps::set_of<Traits::vertex_descriptor> > smart_map;
    smart_map colorMap;
    boost::associative_property_map<smart_map::right_map> color_map(colorMap.right);
    

    I've taken a small sample from http://lpsolve.sourceforge.net/5.5/DIMACS_maxf.htm and you can see it Live On Coliru, output:

    c  The total flow:
    s 15
    
    c flow values:
    f 0 1 5
    f 0 2 10
    f 1 3 5
    f 1 4 0
    f 2 3 5
    f 2 4 5
    f 3 5 10
    f 4 5 5
    ltr: 0 -> 5
    ltr: 4 -> 0
    ltr: 0 -> 1
    ltr: 4 -> 2
    ltr: 0 -> 3
    ltr: 0 -> 4
    rtl: 0 -> 4
    rtl: 1 -> 0
    rtl: 2 -> 4
    rtl: 3 -> 0
    rtl: 4 -> 0
    rtl: 5 -> 0
    

    Full Listing:

    #include <boost/foreach.hpp>
    #include <boost/bimap.hpp>
    #include <boost/graph/adjacency_list.hpp>
    #include <boost/bimap/list_of.hpp>
    #include <boost/bimap/set_of.hpp>
    #include <boost/graph/edmonds_karp_max_flow.hpp>
    #include <boost/graph/graph_utility.hpp>
    #include <boost/graph/read_dimacs.hpp>
    #include <boost/lexical_cast.hpp>
    #include <boost/property_map/property_map.hpp>
    #include <boost/unordered_map.hpp>
    
    int main() {
        using namespace boost;
    
        typedef adjacency_list_traits<vecS, vecS, directedS> Traits;
        typedef adjacency_list<
            listS, vecS, directedS, property<vertex_name_t, std::string>,
            property<edge_capacity_t, long,
            property<edge_residual_capacity_t, long, 
            property<edge_reverse_t, Traits::edge_descriptor> > > > Graph;
    
        Graph g;
    
        property_map<Graph, edge_capacity_t>::type capacity                   = get(edge_capacity, g);
        property_map<Graph, edge_reverse_t>::type rev                         = get(edge_reverse, g);
        property_map<Graph, edge_residual_capacity_t>::type residual_capacity = get(edge_residual_capacity, g);
    
        typedef boost::bimap<bimaps::list_of<default_color_type>, bimaps::set_of<Traits::vertex_descriptor> > smart_map;
        smart_map colorMap;
        boost::associative_property_map<smart_map::right_map> color_map(colorMap.right);
    
        Traits::vertex_descriptor s, t;
        read_dimacs_max_flow(g, capacity, rev, s, t);
    
        std::vector<Traits::edge_descriptor> pred(num_vertices(g));
        long flow = edmonds_karp_max_flow(
                g, s, t, capacity, residual_capacity, rev,
                color_map, &pred[0]);
    
        std::cout << "c  The total flow:" << std::endl;
        std::cout << "s " << flow << std::endl << std::endl;
    
        std::cout << "c flow values:" << std::endl;
        graph_traits<Graph>::vertex_iterator u_iter, u_end;
        graph_traits<Graph>::out_edge_iterator ei, e_end;
        for (boost::tie(u_iter, u_end) = vertices(g); u_iter != u_end; ++u_iter)
            for (boost::tie(ei, e_end) = out_edges(*u_iter, g); ei != e_end; ++ei)
                if (capacity[*ei] > 0)
                    std::cout << "f " << *u_iter << " " << target(*ei, g) << " " << (capacity[*ei] - residual_capacity[*ei])
                              << std::endl;
    
        for (auto const& e : colorMap.left)  std::cout << "ltr: " << e.first << " -> " << e.second << "\n";
        for (auto const& e : colorMap.right) std::cout << "rtl: " << e.first << " -> " << e.second << "\n";
    
        return EXIT_SUCCESS;
    }
    

    UPDATE

    Using Boost MultiIndex to create a bidirectional mapping:

    struct VertexColor {
        Traits::vertex_descriptor vertex;
        boost::default_color_type color;
    };
    
    typedef boost::multi_index_container<
        VertexColor,
        bmi::indexed_by<
            bmi::hashed_non_unique<bmi::tag<struct by_color>,  bmi::member<VertexColor, boost::default_color_type, &VertexColor::color> >,
            bmi::ordered_unique   <bmi::tag<struct by_vertex>, bmi::member<VertexColor, Traits::vertex_descriptor, &VertexColor::vertex> >
        >
    > smart_map;
    

    Now, using Boost Property Map to model a ReadWritePropertyMap:

    struct bidi_color_map {
        typedef smart_map::index<by_vertex>::type impl_t;
        bidi_color_map(impl_t& ref) : ref_(&ref) {}
        impl_t       &get()       { return *ref_; }
        impl_t const &get() const { return *ref_; }
    private:
        impl_t* ref_;
    };
    
    namespace boost { 
        template <> struct property_traits<bidi_color_map> {
            typedef default_color_type          value_type;
            typedef default_color_type          reference;
            typedef Traits::vertex_descriptor   key_type;
            typedef read_write_property_map_tag category;
        };
    }
    
    boost::property_traits<bidi_color_map>::reference get(bidi_color_map const& idx, boost::property_traits<bidi_color_map>::key_type const& key) {
        auto it = idx.get().find(key);
        if (it != idx.get().end())
            return it->color;
        else
            throw std::range_error("key not found in index");
    }
    
    void put(bidi_color_map& idx, boost::property_traits<bidi_color_map>::key_type const& key, boost::property_traits<bidi_color_map>::value_type val) {
        auto it = idx.get().find(key);
        if (it != idx.get().end())
            idx.get().modify(it, [val](VertexColor& p) { p.color = val; });
        else
            idx.get().insert({key,val});
    }
    

    Now you can just pass that as the colormap:

        smart_map colorMap;
        bidi_color_map color_map(colorMap.get<by_vertex>());
    

    See it Live On Coliru as well