Search code examples
boostmax-flowedmonds-karppush-relabel

Boost max flow algorithms do not compile. error: forming reference to void


Boost provides three different algorithms for finding max flow in directed graphs: boykov_kolmogorov, edmonds_karp and push_relabel. All of them have named and non-named parameter versions. Parameter sets they use are also very similar. Despite that, with same parameters some of these algorithms compile and some of them do not.

push_relabel compiles nicely with both named and non-named version:

  using Graph =
    boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
                        VertexProperty, EdgeProperty>;
  auto props = boost::capacity_map(capacity)
               .residual_capacity_map(residual_capacity)
               .reverse_edge_map(reverse_edge_map)
               .vertex_index_map(vertex_index_map)
               .color_map(color_map)
               .predecessor_map(predcessor_map)
               .distance_map(distance_map);
  boost::push_relabel_max_flow(g, s, t, props);
  boost::push_relabel_max_flow(g, s, t, capacity, residual_capacity,
                   reverse_edge_map, vertex_index_map);

boykov_kolmogorov compiles with non-named version:

  boost::boykov_kolmogorov_max_flow(g, capacity, residual_capacity,
                                    reverse_edge_map,
                                    vertex_index_map, s, t);

But fails with named version:

  boost::boykov_kolmogorov_max_flow(g, s, t, props);

/celibs/boost_1_73_0/boost/graph/detail/adjacency_list.hpp:2768:17: error: forming reference to void

edmonds_karp fails with both named and non-named versions with same error:

boost::edmonds_karp_max_flow(g, s, t, props);
boost::edmonds_karp_max_flow(g, s, t, capacity, residual_capacity, reverse_edge_map,
                          color_map, predcessor_map);

/celibs/boost_1_73_0/boost/concept_check.hpp:147:9: error: use of deleted function

Full example is here: https://godbolt.org/z/dvjfec

Do I pass parameters in incorrect way? How to pass them correctly?

Thanks!


Solution

  • This indeed seems to be a bug.

    It appears that the choose_const_pmap for edge_capacity fails when there is no interior edge_capacity_t property defined (Interior Properties).

    Defining one makes the problem go away. However, we can check that it always takes precedence over the one provided via named paramaters:

    struct Oops {};
    using EdgeProperty = boost::property<boost::edge_capacity_t, Oops>;
    

    Leads to compilation problems, suggesting that the wrong property map is selected.

    I could not find an obvious cause for this behaviour - all the other named arguments behave as expected, and are declared in very similar fashion (the process is automated by macro). I assume there will be something very subtle involved (like a name collision or ADL mishap?).

    Here's the code that works for me:

    Live On Wandbox (note obviously can't run successfully, because it doesn't satisfy any invariants)

    #define BOOST_ALLOW_DEPRECATED_HEADERS
    #include <boost/config.hpp>
    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/boykov_kolmogorov_max_flow.hpp>
    #include <boost/graph/edmonds_karp_max_flow.hpp>
    #include <boost/graph/graph_utility.hpp>
    #include <boost/graph/push_relabel_max_flow.hpp>
    #include <boost/property_map/function_property_map.hpp>
    
    int main() {
        struct VertexProperty final {};
        // struct EdgeProperty final {};
        using EdgeProperty = boost::property<boost::edge_capacity_t, int>;
        using Graph =
            boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
                                  VertexProperty, EdgeProperty>;
        using Edge = boost::graph_traits<Graph>::edge_descriptor;
        using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
        auto g = Graph{};
        auto s = Vertex{};
        auto t = Vertex{};
    
        auto residualCapacityMap = std::vector<int>{};
        auto reverseEdgeMap = std::vector<Edge>{};
        auto colorMap = std::vector<boost::default_color_type>{};
        auto predcessorMap = std::vector<Edge>{};
        auto distanceMap = std::vector<int>{};
    
        auto vertex_index_map =
            boost::make_function_property_map<Vertex>([](Vertex) { return 0; });
        auto edge_index_map =
            boost::make_function_property_map<Edge>([](Edge) { return 0; });
        // auto capacity = boost::make_function_property_map<Edge>( [](Edge) { return 0; });
        auto capacity = boost::get(boost::edge_capacity, g);
        auto residual_capacity = boost::make_iterator_property_map(
            residualCapacityMap.begin(), edge_index_map);
        auto reverse_edge_map = boost::make_iterator_property_map(
            reverseEdgeMap.begin(), edge_index_map);
        auto color_map =
            boost::make_iterator_property_map(colorMap.begin(), vertex_index_map);
        auto predcessor_map = boost::make_iterator_property_map(
            predcessorMap.begin(), vertex_index_map);
        auto distance_map = boost::make_iterator_property_map(distanceMap.begin(),
                                                              vertex_index_map);
    
        auto props = boost::capacity_map(capacity)
                         .residual_capacity_map(residual_capacity)
                         .reverse_edge_map(reverse_edge_map)
                         .vertex_index_map(vertex_index_map)
                         .color_map(color_map)
                         .predecessor_map(predcessor_map)
                         .distance_map(distance_map);
    
        boost::push_relabel_max_flow(g, s, t, props);
        boost::push_relabel_max_flow(g, s, t, capacity, residual_capacity,
                                     reverse_edge_map, vertex_index_map);
    
        boost::boykov_kolmogorov_max_flow(g, capacity, residual_capacity,
                                          reverse_edge_map, vertex_index_map, s, t);
        boost::boykov_kolmogorov_max_flow(g, s, t, props);
    
        boost::edmonds_karp_max_flow(g, s, t, props);
        boost::edmonds_karp_max_flow(g, s, t, capacity, residual_capacity,
                                     reverse_edge_map, color_map, predcessor_map);
    }
    

    As you can see all of the algorithm invocations now compile.