// Copyright (c) 2006, Stephan Diederich // // This code may be used under either of the following two licences: // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE. // // Or: // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP #define BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP #include #include #include #include #include #include #include // for std::min and std::max #include #include #include #include #include #include #include #include // The algorithm impelemented here is described in: // // Boykov, Y., Kolmogorov, V. "An Experimental Comparison of Min-Cut/Max-Flow // Algorithms for Energy Minimization in Vision", In IEEE Transactions on // Pattern Analysis and Machine Intelligence, vol. 26, no. 9, pp. 1124-1137, // Sep 2004. // // For further reading, also see: // // Kolmogorov, V. "Graph Based Algorithms for Scene Reconstruction from Two or // More Views". PhD thesis, Cornell University, Sep 2003. namespace boost { namespace detail { template class bk_max_flow { typedef typename property_traits::value_type tEdgeVal; typedef graph_traits tGraphTraits; typedef typename tGraphTraits::vertex_iterator vertex_iterator; typedef typename tGraphTraits::vertex_descriptor vertex_descriptor; typedef typename tGraphTraits::edge_descriptor edge_descriptor; typedef typename tGraphTraits::edge_iterator edge_iterator; typedef typename tGraphTraits::out_edge_iterator out_edge_iterator; typedef boost::queue tQueue; //queue of vertices, used in adoption-stage typedef typename property_traits::value_type tColorValue; typedef color_traits tColorTraits; typedef typename property_traits::value_type tDistanceVal; public: bk_max_flow(Graph& g, EdgeCapacityMap cap, ResidualCapacityEdgeMap res, ReverseEdgeMap rev, PredecessorMap pre, ColorMap color, DistanceMap dist, IndexMap idx, vertex_descriptor src, vertex_descriptor sink): m_g(g), m_index_map(idx), m_cap_map(cap), m_res_cap_map(res), m_rev_edge_map(rev), m_pre_map(pre), m_tree_map(color), m_dist_map(dist), m_source(src), m_sink(sink), m_active_nodes(), m_in_active_list_vec(num_vertices(g), false), m_in_active_list_map(make_iterator_property_map(m_in_active_list_vec.begin(), m_index_map)), m_has_parent_vec(num_vertices(g), false), m_has_parent_map(make_iterator_property_map(m_has_parent_vec.begin(), m_index_map)), m_time_vec(num_vertices(g), 0), m_time_map(make_iterator_property_map(m_time_vec.begin(), m_index_map)), m_flow(0), m_time(1), m_last_grow_vertex(graph_traits::null_vertex()){ // initialize the color-map with gray-values vertex_iterator vi, v_end; for(boost::tie(vi, v_end) = vertices(m_g); vi != v_end; ++vi){ set_tree(*vi, tColorTraits::gray()); } // Initialize flow to zero which means initializing // the residual capacity equal to the capacity edge_iterator ei, e_end; for(boost::tie(ei, e_end) = edges(m_g); ei != e_end; ++ei) { put(m_res_cap_map, *ei, get(m_cap_map, *ei)); BOOST_ASSERT(get(m_rev_edge_map, get(m_rev_edge_map, *ei)) == *ei); //check if the reverse edge map is build up properly } //init the search trees with the two terminals set_tree(m_source, tColorTraits::black()); set_tree(m_sink, tColorTraits::white()); put(m_time_map, m_source, 1); put(m_time_map, m_sink, 1); } tEdgeVal max_flow(){ //augment direct paths from SOURCE->SINK and SOURCE->VERTEX->SINK augment_direct_paths(); //start the main-loop while(true){ bool path_found; edge_descriptor connecting_edge; boost::tie(connecting_edge, path_found) = grow(); //find a path from source to sink if(!path_found){ //we're finished, no more paths were found break; } ++m_time; augment(connecting_edge); //augment that path adopt(); //rebuild search tree structure } return m_flow; } // the complete class is protected, as we want access to members in // derived test-class (see test/boykov_kolmogorov_max_flow_test.cpp) protected: void augment_direct_paths(){ // in a first step, we augment all direct paths from source->NODE->sink // and additionally paths from source->sink. This improves especially // graphcuts for segmentation, as most of the nodes have source/sink // connects but shouldn't have an impact on other maxflow problems // (this is done in grow() anyway) out_edge_iterator ei, e_end; for(boost::tie(ei, e_end) = out_edges(m_source, m_g); ei != e_end; ++ei){ edge_descriptor from_source = *ei; vertex_descriptor current_node = target(from_source, m_g); if(current_node == m_sink){ tEdgeVal cap = get(m_res_cap_map, from_source); put(m_res_cap_map, from_source, 0); m_flow += cap; continue; } edge_descriptor to_sink; bool is_there; boost::tie(to_sink, is_there) = lookup_edge(current_node, m_sink, m_g); if(is_there){ tEdgeVal cap_from_source = get(m_res_cap_map, from_source); tEdgeVal cap_to_sink = get(m_res_cap_map, to_sink); if(cap_from_source > cap_to_sink){ set_tree(current_node, tColorTraits::black()); add_active_node(current_node); set_edge_to_parent(current_node, from_source); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); // add stuff to flow and update residuals. we dont need to // update reverse_edges, as incoming/outgoing edges to/from // source/sink don't count for max-flow put(m_res_cap_map, from_source, get(m_res_cap_map, from_source) - cap_to_sink); put(m_res_cap_map, to_sink, 0); m_flow += cap_to_sink; } else if(cap_to_sink > 0){ set_tree(current_node, tColorTraits::white()); add_active_node(current_node); set_edge_to_parent(current_node, to_sink); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); // add stuff to flow and update residuals. we dont need to update // reverse_edges, as incoming/outgoing edges to/from source/sink // don't count for max-flow put(m_res_cap_map, to_sink, get(m_res_cap_map, to_sink) - cap_from_source); put(m_res_cap_map, from_source, 0); m_flow += cap_from_source; } } else if(get(m_res_cap_map, from_source)){ // there is no sink connect, so we can't augment this path, but to // avoid adding m_source to the active nodes, we just activate this // node and set the approciate things set_tree(current_node, tColorTraits::black()); set_edge_to_parent(current_node, from_source); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); add_active_node(current_node); } } for(boost::tie(ei, e_end) = out_edges(m_sink, m_g); ei != e_end; ++ei){ edge_descriptor to_sink = get(m_rev_edge_map, *ei); vertex_descriptor current_node = source(to_sink, m_g); if(get(m_res_cap_map, to_sink)){ set_tree(current_node, tColorTraits::white()); set_edge_to_parent(current_node, to_sink); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); add_active_node(current_node); } } } /** * Returns a pair of an edge and a boolean. if the bool is true, the * edge is a connection of a found path from s->t , read "the link" and * source(returnVal, m_g) is the end of the path found in the source-tree * target(returnVal, m_g) is the beginning of the path found in the sink-tree */ std::pair grow(){ BOOST_ASSERT(m_orphans.empty()); vertex_descriptor current_node; while((current_node = get_next_active_node()) != graph_traits::null_vertex()){ //if there is one BOOST_ASSERT(get_tree(current_node) != tColorTraits::gray() && (has_parent(current_node) || current_node == m_source || current_node == m_sink)); if(get_tree(current_node) == tColorTraits::black()){ //source tree growing out_edge_iterator ei, e_end; if(current_node != m_last_grow_vertex){ m_last_grow_vertex = current_node; boost::tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g); } for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it) { edge_descriptor out_edge = *m_last_grow_edge_it; if(get(m_res_cap_map, out_edge) > 0){ //check if we have capacity left on this edge vertex_descriptor other_node = target(out_edge, m_g); if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node set_tree(other_node, tColorTraits::black()); //aquire other node to our search tree set_edge_to_parent(other_node, out_edge); //set us as parent put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); //and update the distance-heuristic put(m_time_map, other_node, get(m_time_map, current_node)); add_active_node(other_node); } else if(get_tree(other_node) == tColorTraits::black()) { // we do this to get shorter paths. check if we are nearer to // the source as its parent is if(is_closer_to_terminal(current_node, other_node)){ set_edge_to_parent(other_node, out_edge); put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); put(m_time_map, other_node, get(m_time_map, current_node)); } } else{ BOOST_ASSERT(get_tree(other_node)==tColorTraits::white()); //kewl, found a path from one to the other search tree, return // the connecting edge in src->sink dir return std::make_pair(out_edge, true); } } } //for all out-edges } //source-tree-growing else{ BOOST_ASSERT(get_tree(current_node) == tColorTraits::white()); out_edge_iterator ei, e_end; if(current_node != m_last_grow_vertex){ m_last_grow_vertex = current_node; boost::tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g); } for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it){ edge_descriptor in_edge = get(m_rev_edge_map, *m_last_grow_edge_it); if(get(m_res_cap_map, in_edge) > 0){ //check if there is capacity left vertex_descriptor other_node = source(in_edge, m_g); if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node set_tree(other_node, tColorTraits::white()); //aquire that node to our search tree set_edge_to_parent(other_node, in_edge); //set us as parent add_active_node(other_node); //activate that node put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); //set its distance put(m_time_map, other_node, get(m_time_map, current_node));//and time } else if(get_tree(other_node) == tColorTraits::white()){ if(is_closer_to_terminal(current_node, other_node)){ //we are closer to the sink than its parent is, so we "adopt" him set_edge_to_parent(other_node, in_edge); put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); put(m_time_map, other_node, get(m_time_map, current_node)); } } else{ BOOST_ASSERT(get_tree(other_node)==tColorTraits::black()); //kewl, found a path from one to the other search tree, // return the connecting edge in src->sink dir return std::make_pair(in_edge, true); } } } //for all out-edges } //sink-tree growing //all edges of that node are processed, and no more paths were found. // remove if from the front of the active queue finish_node(current_node); } //while active_nodes not empty //no active nodes anymore and no path found, we're done return std::make_pair(edge_descriptor(), false); } /** * augments path from s->t and updates residual graph * source(e, m_g) is the end of the path found in the source-tree * target(e, m_g) is the beginning of the path found in the sink-tree * this phase generates orphans on satured edges, if the attached verts are * from different search-trees orphans are ordered in distance to * sink/source. first the farest from the source are front_inserted into * the orphans list, and after that the sink-tree-orphans are * front_inserted. when going to adoption stage the orphans are popped_front, * and so we process the nearest verts to the terminals first */ void augment(edge_descriptor e) { BOOST_ASSERT(get_tree(target(e, m_g)) == tColorTraits::white()); BOOST_ASSERT(get_tree(source(e, m_g)) == tColorTraits::black()); BOOST_ASSERT(m_orphans.empty()); const tEdgeVal bottleneck = find_bottleneck(e); //now we push the found flow through the path //for each edge we saturate we have to look for the verts that belong to that edge, one of them becomes an orphans //now process the connecting edge put(m_res_cap_map, e, get(m_res_cap_map, e) - bottleneck); BOOST_ASSERT(get(m_res_cap_map, e) >= 0); put(m_res_cap_map, get(m_rev_edge_map, e), get(m_res_cap_map, get(m_rev_edge_map, e)) + bottleneck); //now we follow the path back to the source vertex_descriptor current_node = source(e, m_g); while(current_node != m_source){ edge_descriptor pred = get_edge_to_parent(current_node); put(m_res_cap_map, pred, get(m_res_cap_map, pred) - bottleneck); BOOST_ASSERT(get(m_res_cap_map, pred) >= 0); put(m_res_cap_map, get(m_rev_edge_map, pred), get(m_res_cap_map, get(m_rev_edge_map, pred)) + bottleneck); if(get(m_res_cap_map, pred) == 0){ set_no_parent(current_node); m_orphans.push_front(current_node); } current_node = source(pred, m_g); } //then go forward in the sink-tree current_node = target(e, m_g); while(current_node != m_sink){ edge_descriptor pred = get_edge_to_parent(current_node); put(m_res_cap_map, pred, get(m_res_cap_map, pred) - bottleneck); BOOST_ASSERT(get(m_res_cap_map, pred) >= 0); put(m_res_cap_map, get(m_rev_edge_map, pred), get(m_res_cap_map, get(m_rev_edge_map, pred)) + bottleneck); if(get(m_res_cap_map, pred) == 0){ set_no_parent(current_node); m_orphans.push_front(current_node); } current_node = target(pred, m_g); } //and add it to the max-flow m_flow += bottleneck; } /** * returns the bottleneck of a s->t path (end_of_path is last vertex in * source-tree, begin_of_path is first vertex in sink-tree) */ inline tEdgeVal find_bottleneck(edge_descriptor e){ BOOST_USING_STD_MIN(); tEdgeVal minimum_cap = get(m_res_cap_map, e); vertex_descriptor current_node = source(e, m_g); //first go back in the source tree while(current_node != m_source){ edge_descriptor pred = get_edge_to_parent(current_node); minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, get(m_res_cap_map, pred)); current_node = source(pred, m_g); } //then go forward in the sink-tree current_node = target(e, m_g); while(current_node != m_sink){ edge_descriptor pred = get_edge_to_parent(current_node); minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, get(m_res_cap_map, pred)); current_node = target(pred, m_g); } return minimum_cap; } /** * rebuild search trees * empty the queue of orphans, and find new parents for them or just drop * them from the search trees */ void adopt(){ while(!m_orphans.empty() || !m_child_orphans.empty()){ vertex_descriptor current_node; if(m_child_orphans.empty()){ //get the next orphan from the main-queue and remove it current_node = m_orphans.front(); m_orphans.pop_front(); } else{ current_node = m_child_orphans.front(); m_child_orphans.pop(); } if(get_tree(current_node) == tColorTraits::black()){ //we're in the source-tree tDistanceVal min_distance = (std::numeric_limits::max)(); edge_descriptor new_parent_edge; out_edge_iterator ei, e_end; for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ const edge_descriptor in_edge = get(m_rev_edge_map, *ei); BOOST_ASSERT(target(in_edge, m_g) == current_node); //we should be the target of this edge if(get(m_res_cap_map, in_edge) > 0){ vertex_descriptor other_node = source(in_edge, m_g); if(get_tree(other_node) == tColorTraits::black() && has_source_connect(other_node)){ if(get(m_dist_map, other_node) < min_distance){ min_distance = get(m_dist_map, other_node); new_parent_edge = in_edge; } } } } if(min_distance != (std::numeric_limits::max)()){ set_edge_to_parent(current_node, new_parent_edge); put(m_dist_map, current_node, min_distance + 1); put(m_time_map, current_node, m_time); } else{ put(m_time_map, current_node, 0); for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ edge_descriptor in_edge = get(m_rev_edge_map, *ei); vertex_descriptor other_node = source(in_edge, m_g); if(get_tree(other_node) == tColorTraits::black() && has_parent(other_node)){ if(get(m_res_cap_map, in_edge) > 0){ add_active_node(other_node); } if(source(get_edge_to_parent(other_node), m_g) == current_node){ //we are the parent of that node //it has to find a new parent, too set_no_parent(other_node); m_child_orphans.push(other_node); } } } set_tree(current_node, tColorTraits::gray()); } //no parent found } //source-tree-adoption else{ //now we should be in the sink-tree, check that... BOOST_ASSERT(get_tree(current_node) == tColorTraits::white()); out_edge_iterator ei, e_end; edge_descriptor new_parent_edge; tDistanceVal min_distance = (std::numeric_limits::max)(); for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ const edge_descriptor out_edge = *ei; if(get(m_res_cap_map, out_edge) > 0){ const vertex_descriptor other_node = target(out_edge, m_g); if(get_tree(other_node) == tColorTraits::white() && has_sink_connect(other_node)) if(get(m_dist_map, other_node) < min_distance){ min_distance = get(m_dist_map, other_node); new_parent_edge = out_edge; } } } if(min_distance != (std::numeric_limits::max)()){ set_edge_to_parent(current_node, new_parent_edge); put(m_dist_map, current_node, min_distance + 1); put(m_time_map, current_node, m_time); } else{ put(m_time_map, current_node, 0); for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ const edge_descriptor out_edge = *ei; const vertex_descriptor other_node = target(out_edge, m_g); if(get_tree(other_node) == tColorTraits::white() && has_parent(other_node)){ if(get(m_res_cap_map, out_edge) > 0){ add_active_node(other_node); } if(target(get_edge_to_parent(other_node), m_g) == current_node){ //we were it's parent, so it has to find a new one, too set_no_parent(other_node); m_child_orphans.push(other_node); } } } set_tree(current_node, tColorTraits::gray()); } //no parent found } //sink-tree adoption } //while !orphans.empty() } //adopt /** * return next active vertex if there is one, otherwise a null_vertex */ inline vertex_descriptor get_next_active_node(){ while(true){ if(m_active_nodes.empty()) return graph_traits::null_vertex(); vertex_descriptor v = m_active_nodes.front(); //if it has no parent, this node can't be active (if its not source or sink) if(!has_parent(v) && v != m_source && v != m_sink){ m_active_nodes.pop(); put(m_in_active_list_map, v, false); } else{ BOOST_ASSERT(get_tree(v) == tColorTraits::black() || get_tree(v) == tColorTraits::white()); return v; } } } /** * adds v as an active vertex, but only if its not in the list already */ inline void add_active_node(vertex_descriptor v){ BOOST_ASSERT(get_tree(v) != tColorTraits::gray()); if(get(m_in_active_list_map, v)){ return; } else{ put(m_in_active_list_map, v, true); m_active_nodes.push(v); } } /** * finish_node removes a node from the front of the active queue (its called in grow phase, if no more paths can be found using this node) */ inline void finish_node(vertex_descriptor v){ BOOST_ASSERT(m_active_nodes.front() == v); m_active_nodes.pop(); put(m_in_active_list_map, v, false); m_last_grow_vertex = graph_traits::null_vertex(); } /** * removes a vertex from the queue of active nodes (actually this does nothing, * but checks if this node has no parent edge, as this is the criteria for * being no more active) */ inline void remove_active_node(vertex_descriptor v){ BOOST_ASSERT(!has_parent(v)); } /** * returns the search tree of v; tColorValue::black() for source tree, * white() for sink tree, gray() for no tree */ inline tColorValue get_tree(vertex_descriptor v) const { return get(m_tree_map, v); } /** * sets search tree of v; tColorValue::black() for source tree, white() * for sink tree, gray() for no tree */ inline void set_tree(vertex_descriptor v, tColorValue t){ put(m_tree_map, v, t); } /** * returns edge to parent vertex of v; */ inline edge_descriptor get_edge_to_parent(vertex_descriptor v) const{ return get(m_pre_map, v); } /** * returns true if the edge stored in m_pre_map[v] is a valid entry */ inline bool has_parent(vertex_descriptor v) const{ return get(m_has_parent_map, v); } /** * sets edge to parent vertex of v; */ inline void set_edge_to_parent(vertex_descriptor v, edge_descriptor f_edge_to_parent){ BOOST_ASSERT(get(m_res_cap_map, f_edge_to_parent) > 0); put(m_pre_map, v, f_edge_to_parent); put(m_has_parent_map, v, true); } /** * removes the edge to parent of v (this is done by invalidating the * entry an additional map) */ inline void set_no_parent(vertex_descriptor v){ put(m_has_parent_map, v, false); } /** * checks if vertex v has a connect to the sink-vertex (@var m_sink) * @param v the vertex which is checked * @return true if a path to the sink was found, false if not */ inline bool has_sink_connect(vertex_descriptor v){ tDistanceVal current_distance = 0; vertex_descriptor current_vertex = v; while(true){ if(get(m_time_map, current_vertex) == m_time){ //we found a node which was already checked this round. use it for distance calculations current_distance += get(m_dist_map, current_vertex); break; } if(current_vertex == m_sink){ put(m_time_map, m_sink, m_time); break; } if(has_parent(current_vertex)){ //it has a parent, so get it current_vertex = target(get_edge_to_parent(current_vertex), m_g); ++current_distance; } else{ //no path found return false; } } current_vertex=v; while(get(m_time_map, current_vertex) != m_time){ put(m_dist_map, current_vertex, current_distance); --current_distance; put(m_time_map, current_vertex, m_time); current_vertex = target(get_edge_to_parent(current_vertex), m_g); } return true; } /** * checks if vertex v has a connect to the source-vertex (@var m_source) * @param v the vertex which is checked * @return true if a path to the source was found, false if not */ inline bool has_source_connect(vertex_descriptor v){ tDistanceVal current_distance = 0; vertex_descriptor current_vertex = v; while(true){ if(get(m_time_map, current_vertex) == m_time){ //we found a node which was already checked this round. use it for distance calculations current_distance += get(m_dist_map, current_vertex); break; } if(current_vertex == m_source){ put(m_time_map, m_source, m_time); break; } if(has_parent(current_vertex)){ //it has a parent, so get it current_vertex = source(get_edge_to_parent(current_vertex), m_g); ++current_distance; } else{ //no path found return false; } } current_vertex=v; while(get(m_time_map, current_vertex) != m_time){ put(m_dist_map, current_vertex, current_distance); --current_distance; put(m_time_map, current_vertex, m_time); current_vertex = source(get_edge_to_parent(current_vertex), m_g); } return true; } /** * returns true, if p is closer to a terminal than q */ inline bool is_closer_to_terminal(vertex_descriptor p, vertex_descriptor q){ //checks the timestamps first, to build no cycles, and after that the real distance return (get(m_time_map, q) <= get(m_time_map, p) && get(m_dist_map, q) > get(m_dist_map, p)+1); } //////// // member vars //////// Graph& m_g; IndexMap m_index_map; EdgeCapacityMap m_cap_map; ResidualCapacityEdgeMap m_res_cap_map; ReverseEdgeMap m_rev_edge_map; PredecessorMap m_pre_map; //stores paths found in the growth stage ColorMap m_tree_map; //maps each vertex into one of the two search tree or none (gray()) DistanceMap m_dist_map; //stores distance to source/sink nodes vertex_descriptor m_source; vertex_descriptor m_sink; tQueue m_active_nodes; std::vector m_in_active_list_vec; iterator_property_map::iterator, IndexMap> m_in_active_list_map; std::list m_orphans; tQueue m_child_orphans; // we use a second queuqe for child orphans, as they are FIFO processed std::vector m_has_parent_vec; iterator_property_map::iterator, IndexMap> m_has_parent_map; std::vector m_time_vec; //timestamp of each node, used for sink/source-path calculations iterator_property_map::iterator, IndexMap> m_time_map; tEdgeVal m_flow; long m_time; vertex_descriptor m_last_grow_vertex; out_edge_iterator m_last_grow_edge_it; out_edge_iterator m_last_grow_edge_end; }; } //namespace boost::detail /** * non-named-parameter version, given everything * this is the catch all version */ template typename property_traits::value_type boykov_kolmogorov_max_flow(Graph& g, CapacityEdgeMap cap, ResidualCapacityEdgeMap res_cap, ReverseEdgeMap rev_map, PredecessorMap pre_map, ColorMap color, DistanceMap dist, IndexMap idx, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef typename graph_traits::edge_descriptor edge_descriptor; //as this method is the last one before we instantiate the solver, we do the concept checks here BOOST_CONCEPT_ASSERT(( VertexListGraphConcept )); //to have vertices(), num_vertices(), BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept )); //to have edges() BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept )); //to have source(), target() and out_edges() BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept )); //read flow-values from edges BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //write flow-values to residuals BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept )); //read out reverse edges BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //store predecessor there BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //write corresponding tree BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //write distance to source/sink BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept )); //get index 0...|V|-1 BOOST_ASSERT(num_vertices(g) >= 2 && src != sink); detail::bk_max_flow< Graph, CapacityEdgeMap, ResidualCapacityEdgeMap, ReverseEdgeMap, PredecessorMap, ColorMap, DistanceMap, IndexMap > algo(g, cap, res_cap, rev_map, pre_map, color, dist, idx, src, sink); return algo.max_flow(); } /** * non-named-parameter version, given capacity, residucal_capacity, * reverse_edges, and an index map. */ template typename property_traits::value_type boykov_kolmogorov_max_flow(Graph& g, CapacityEdgeMap cap, ResidualCapacityEdgeMap res_cap, ReverseEdgeMap rev, IndexMap idx, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { typename graph_traits::vertices_size_type n_verts = num_vertices(g); std::vector::edge_descriptor> predecessor_vec(n_verts); std::vector color_vec(n_verts); std::vector::vertices_size_type> distance_vec(n_verts); return boykov_kolmogorov_max_flow( g, cap, res_cap, rev, make_iterator_property_map(predecessor_vec.begin(), idx), make_iterator_property_map(color_vec.begin(), idx), make_iterator_property_map(distance_vec.begin(), idx), idx, src, sink); } /** * non-named-parameter version, some given: capacity, residual_capacity, * reverse_edges, color_map and an index map. Use this if you are interested in * the minimum cut, as the color map provides that info. */ template typename property_traits::value_type boykov_kolmogorov_max_flow(Graph& g, CapacityEdgeMap cap, ResidualCapacityEdgeMap res_cap, ReverseEdgeMap rev, ColorMap color, IndexMap idx, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { typename graph_traits::vertices_size_type n_verts = num_vertices(g); std::vector::edge_descriptor> predecessor_vec(n_verts); std::vector::vertices_size_type> distance_vec(n_verts); return boykov_kolmogorov_max_flow( g, cap, res_cap, rev, make_iterator_property_map(predecessor_vec.begin(), idx), color, make_iterator_property_map(distance_vec.begin(), idx), idx, src, sink); } /** * named-parameter version, some given */ template typename property_traits::const_type>::value_type boykov_kolmogorov_max_flow(Graph& g, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink, const bgl_named_params& params) { return boykov_kolmogorov_max_flow( g, choose_const_pmap(get_param(params, edge_capacity), g, edge_capacity), choose_pmap(get_param(params, edge_residual_capacity), g, edge_residual_capacity), choose_const_pmap(get_param(params, edge_reverse), g, edge_reverse), choose_pmap(get_param(params, vertex_predecessor), g, vertex_predecessor), choose_pmap(get_param(params, vertex_color), g, vertex_color), choose_pmap(get_param(params, vertex_distance), g, vertex_distance), choose_const_pmap(get_param(params, vertex_index), g, vertex_index), src, sink); } /** * named-parameter version, none given */ template typename property_traits::const_type>::value_type boykov_kolmogorov_max_flow(Graph& g, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { bgl_named_params params(0); // bogus empty param return boykov_kolmogorov_max_flow(g, src, sink, params); } } // namespace boost #endif // BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP