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!
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.