//======================================================================= // Copyright (c) 2018 Yi Ji // // 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_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP #define BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP #include // for std::iter_swap #include #include #include namespace boost { template typename property_traits::type>::value_type matching_weight_sum(const Graph& g, MateMap mate, VertexIndexMap vm) { typedef typename graph_traits::vertex_iterator vertex_iterator_t; typedef typename graph_traits::vertex_descriptor vertex_descriptor_t; typedef typename property_traits::type>::value_type edge_property_t; edge_property_t weight_sum = 0; vertex_iterator_t vi, vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_descriptor_t v = *vi; if (get(mate, v) != graph_traits::null_vertex() && get(vm, v) < get(vm, get(mate,v))) weight_sum += get(edge_weight, g, edge(v,mate[v],g).first); } return weight_sum; } template inline typename property_traits::type>::value_type matching_weight_sum(const Graph& g, MateMap mate) { return matching_weight_sum(g, mate, get(vertex_index,g)); } template class weighted_augmenting_path_finder { public: template struct map_vertex_to_ { typedef boost::iterator_property_map::iterator, VertexIndexMap> type; }; typedef typename graph::detail::VERTEX_STATE vertex_state_t; typedef typename graph_traits::vertex_iterator vertex_iterator_t; typedef typename graph_traits::vertex_descriptor vertex_descriptor_t; typedef typename std::vector::const_iterator vertex_vec_iter_t; typedef typename graph_traits::out_edge_iterator out_edge_iterator_t; typedef typename graph_traits::edge_descriptor edge_descriptor_t; typedef typename graph_traits::edge_iterator edge_iterator_t; typedef typename property_traits::type>::value_type edge_property_t; typedef std::deque vertex_list_t; typedef std::vector edge_list_t; typedef typename map_vertex_to_::type vertex_to_vertex_map_t; typedef typename map_vertex_to_::type vertex_to_weight_map_t; typedef typename map_vertex_to_::type vertex_to_bool_map_t; typedef typename map_vertex_to_ >::type vertex_to_pair_map_t; typedef typename map_vertex_to_ >::type vertex_to_edge_map_t; typedef typename map_vertex_to_::type vertex_pair_to_edge_map_t; class blossom { public: typedef boost::shared_ptr blossom_ptr_t; std::vector sub_blossoms; edge_property_t dual_var; blossom_ptr_t father; blossom() : dual_var(0), father(blossom_ptr_t()) {} // get the base vertex of a blossom by recursively getting // its base sub-blossom, which is always the first one in // sub_blossoms because of how we create and maintain blossoms virtual vertex_descriptor_t get_base() const { const blossom* b = this; while (!b->sub_blossoms.empty()) b = b->sub_blossoms[0].get(); return b->get_base(); } // set a sub-blossom as a blossom's base by exchanging it // with its first sub-blossom void set_base(const blossom_ptr_t& sub) { for (blossom_iterator_t bi = sub_blossoms.begin(); bi != sub_blossoms.end(); ++bi) { if (sub.get() == bi->get()) { std::iter_swap(sub_blossoms.begin(), bi); break; } } } // get all vertices inside recursively virtual std::vector vertices() const { std::vector all_vertices; for (typename std::vector::const_iterator bi = sub_blossoms.begin(); bi != sub_blossoms.end(); ++bi) { std::vector some_vertices = (*bi)->vertices(); all_vertices.insert(all_vertices.end(), some_vertices.begin(), some_vertices.end()); } return all_vertices; } }; // a trivial_blossom only has one vertex and no sub-blossom; // for each vertex v, in_blossom[v] is the trivial_blossom that contains it directly class trivial_blossom : public blossom { public: trivial_blossom(vertex_descriptor_t v) : trivial_vertex(v) {} virtual vertex_descriptor_t get_base() const { return trivial_vertex; } virtual std::vector vertices() const { std::vector all_vertices; all_vertices.push_back(trivial_vertex); return all_vertices; } private: vertex_descriptor_t trivial_vertex; }; typedef boost::shared_ptr blossom_ptr_t; typedef typename std::vector::iterator blossom_iterator_t; typedef typename map_vertex_to_::type vertex_to_blossom_map_t; weighted_augmenting_path_finder(const Graph& arg_g, MateMap arg_mate, VertexIndexMap arg_vm) : g(arg_g), vm(arg_vm), null_edge(std::pair(num_edges(g) == 0 ? edge_descriptor_t() : *edges(g).first, false)), mate_vector(num_vertices(g)), label_S_vector(num_vertices(g), graph_traits::null_vertex()), label_T_vector(num_vertices(g), graph_traits::null_vertex()), outlet_vector(num_vertices(g), graph_traits::null_vertex()), tau_idx_vector(num_vertices(g), graph_traits::null_vertex()), dual_var_vector(std::vector(num_vertices(g), std::numeric_limits::min())), pi_vector(std::vector(num_vertices(g), std::numeric_limits::max())), gamma_vector(std::vector(num_vertices(g), std::numeric_limits::max())), tau_vector(std::vector(num_vertices(g), std::numeric_limits::max())), in_blossom_vector(num_vertices(g)), old_label_vector(num_vertices(g)), critical_edge_vectors(num_vertices(g), std::vector >(num_vertices(g), null_edge)), mate(mate_vector.begin(), vm), label_S(label_S_vector.begin(), vm), label_T(label_T_vector.begin(), vm), outlet(outlet_vector.begin(), vm), tau_idx(tau_idx_vector.begin(), vm), dual_var(dual_var_vector.begin(), vm), pi(pi_vector.begin(), vm), gamma(gamma_vector.begin(), vm), tau(tau_vector.begin(), vm), in_blossom(in_blossom_vector.begin(), vm), old_label(old_label_vector.begin(), vm) { vertex_iterator_t vi, vi_end; edge_iterator_t ei, ei_end; edge_property_t max_weight = std::numeric_limits::min(); for (boost::tie(ei,ei_end) = edges(g); ei != ei_end; ++ei) max_weight = std::max(max_weight, get(edge_weight, g, *ei)); typename std::vector > >::iterator vei; for (boost::tie(vi,vi_end) = vertices(g), vei = critical_edge_vectors.begin(); vi != vi_end; ++vi, ++vei) { vertex_descriptor_t u = *vi; mate[u] = get(arg_mate, u); dual_var[u] = 2*max_weight; in_blossom[u] = boost::make_shared(u); outlet[u] = u; critical_edge_vector.push_back(vertex_to_edge_map_t(vei->begin(), vm)); } critical_edge = vertex_pair_to_edge_map_t(critical_edge_vector.begin(), vm); init(); } // return the top blossom where v is contained inside blossom_ptr_t in_top_blossom(vertex_descriptor_t v) const { blossom_ptr_t b = in_blossom[v]; while (b->father != blossom_ptr_t()) b = b->father; return b; } // check if vertex v is in blossom b bool is_in_blossom(blossom_ptr_t b, vertex_descriptor_t v) const { if (v == graph_traits::null_vertex()) return false; blossom_ptr_t vb = in_blossom[v]->father; while (vb != blossom_ptr_t()) { if (vb.get() == b.get()) return true; vb = vb->father; } return false; } // return the base vertex of the top blossom that contains v inline vertex_descriptor_t base_vertex(vertex_descriptor_t v) const { return in_top_blossom(v)->get_base(); } // add an existed top blossom of base vertex v into new top // blossom b as its sub-blossom void add_sub_blossom(blossom_ptr_t b, vertex_descriptor_t v) { blossom_ptr_t sub = in_top_blossom(v); sub->father = b; b->sub_blossoms.push_back(sub); if (sub->sub_blossoms.empty()) return; for (blossom_iterator_t bi = top_blossoms.begin(); bi != top_blossoms.end(); ++bi) { if (bi->get() == sub.get()) { top_blossoms.erase(bi); break; } } } // when a top blossom is created or its base vertex getting an S-label, // add all edges incident to this blossom into even_edges void bloom(blossom_ptr_t b) { std::vector vertices_of_b = b->vertices(); vertex_vec_iter_t vi; for (vi = vertices_of_b.begin(); vi != vertices_of_b.end(); ++vi) { out_edge_iterator_t oei, oei_end; for (boost::tie(oei,oei_end) = out_edges(*vi, g); oei != oei_end; ++oei) { if (target(*oei,g) != *vi && mate[*vi] != target(*oei,g)) even_edges.push_back(*oei); } } } // assigning a T-label to a non S-vertex, along with outlet and updating pi value // if updated pi[v] equals zero, augment the matching from its mate vertex void put_T_label(vertex_descriptor_t v, vertex_descriptor_t T_label, vertex_descriptor_t outlet_v, edge_property_t pi_v) { if (label_S[v] != graph_traits::null_vertex()) return; label_T[v] = T_label; outlet[v] = outlet_v; pi[v] = pi_v; vertex_descriptor_t v_mate = mate[v]; if (pi[v] == 0) { label_T[v_mate] = graph_traits::null_vertex(); label_S[v_mate] = v; bloom(in_top_blossom(v_mate)); } } // get the missing T-label for a to-be-expanded base vertex // the missing T-label is the last vertex of the path from outlet[v] to v std::pair missing_label(vertex_descriptor_t b_base) { vertex_descriptor_t missing_outlet = outlet[b_base]; if (outlet[b_base] == b_base) return std::make_pair(graph_traits::null_vertex(), missing_outlet); vertex_iterator_t vi, vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) old_label[*vi] = std::make_pair(label_T[*vi], outlet[*vi]); std::pair child(outlet[b_base], graph::detail::V_EVEN); blossom_ptr_t b = in_blossom[child.first]; for (; b->father->father != blossom_ptr_t(); b = b->father); child.first = b->get_base(); if (child.first == b_base) return std::make_pair(graph_traits::null_vertex(), missing_outlet); while (true) { std::pair child_parent = parent(child, true); for (b = in_blossom[child_parent.first]; b->father->father != blossom_ptr_t(); b = b->father); missing_outlet = child_parent.first; child_parent.first = b->get_base(); if (child_parent.first == b_base) break; else child = child_parent; } return std::make_pair(child.first, missing_outlet); } // expand a top blossom, put all its non-trivial sub-blossoms into top_blossoms blossom_iterator_t expand_blossom(blossom_iterator_t bi, std::vector& new_ones) { blossom_ptr_t b = *bi; for (blossom_iterator_t i = b->sub_blossoms.begin(); i != b->sub_blossoms.end(); ++i) { blossom_ptr_t sub_blossom = *i; vertex_descriptor_t sub_base = sub_blossom->get_base(); label_S[sub_base] = label_T[sub_base] = graph_traits::null_vertex(); outlet[sub_base] = sub_base; sub_blossom->father = blossom_ptr_t(); // new top blossoms cannot be pushed back into top_blossoms immediately, // because push_back() may cause reallocation and then invalid iterators if (!sub_blossom->sub_blossoms.empty()) new_ones.push_back(sub_blossom); } return top_blossoms.erase(bi); } // when expanding a T-blossom with base v, it requires more operations: // supply the missing T-labels for new base vertices by picking the minimum tau from vertices of // each corresponding new top-blossoms; when label_T[v] is null or we have a smaller tau from // missing_label(v), replace T-label and outlet of v (but don't bloom v) blossom_iterator_t expand_T_blossom(blossom_iterator_t bi, std::vector& new_ones) { blossom_ptr_t b = *bi; vertex_descriptor_t b_base = b->get_base(); std::pair T_and_outlet = missing_label(b_base); blossom_iterator_t next_bi = expand_blossom(bi, new_ones); for (blossom_iterator_t i = b->sub_blossoms.begin(); i != b->sub_blossoms.end(); ++i) { blossom_ptr_t sub_blossom = *i; vertex_descriptor_t sub_base = sub_blossom->get_base(); vertex_descriptor_t min_tau_v = graph_traits::null_vertex(); edge_property_t min_tau = std::numeric_limits::max(); std::vector sub_vertices = sub_blossom->vertices(); for (vertex_vec_iter_t v = sub_vertices.begin(); v != sub_vertices.end(); ++v) { if (tau[*v] < min_tau) { min_tau = tau[*v]; min_tau_v = *v; } } if (min_tau < std::numeric_limits::max()) put_T_label(sub_base, tau_idx[min_tau_v], min_tau_v, tau[min_tau_v]); } if (label_T[b_base] == graph_traits::null_vertex() || tau[old_label[b_base].second] < pi[b_base]) boost::tie(label_T[b_base], outlet[b_base]) = T_and_outlet; return next_bi; } // when vertices v and w are matched to each other by augmenting, // we must set v/w as base vertex of any blossom who contains v/w and // is a sub-blossom of their lowest (smallest) common blossom void adjust_blossom(vertex_descriptor_t v, vertex_descriptor_t w) { blossom_ptr_t vb = in_blossom[v], wb = in_blossom[w], lowest_common_blossom; std::vector v_ancestors, w_ancestors; while (vb->father != blossom_ptr_t()) { v_ancestors.push_back(vb->father); vb = vb->father; } while (wb->father != blossom_ptr_t()) { w_ancestors.push_back(wb->father); wb = wb->father; } typename std::vector::reverse_iterator i, j; i = v_ancestors.rbegin(); j = w_ancestors.rbegin(); while (i != v_ancestors.rend() && j != w_ancestors.rend() && i->get() == j->get()) { lowest_common_blossom = *i; ++i;++j; } vb = in_blossom[v]; wb = in_blossom[w]; while (vb->father != lowest_common_blossom) { vb->father->set_base(vb); vb = vb->father; } while (wb->father != lowest_common_blossom) { wb->father->set_base(wb); wb = wb->father; } } // every edge weight is multiplied by 4 to ensure integer weights // throughout the algorithm if all input weights are integers inline edge_property_t slack(const edge_descriptor_t& e) const { vertex_descriptor_t v, w; v = source(e, g); w = target(e, g); return dual_var[v] + dual_var[w] - 4*get(edge_weight, g, e); } // backtrace one step on vertex v along the augmenting path // by its labels and its vertex state; // boolean parameter "use_old" means whether we are updating labels, // if we are, then we use old labels to backtrace and also we // don't jump to its base vertex when we reach an odd vertex std::pair parent(std::pair v, bool use_old = false) const { if (v.second == graph::detail::V_EVEN) { // a paranoid check: label_S shoule be the same as mate in backtracing if (label_S[v.first] == graph_traits::null_vertex()) label_S[v.first] = mate[v.first]; return std::make_pair(label_S[v.first], graph::detail::V_ODD); } else if (v.second == graph::detail::V_ODD) { vertex_descriptor_t w = use_old ? old_label[v.first].first : base_vertex(label_T[v.first]); return std::make_pair(w, graph::detail::V_EVEN); } return std::make_pair(v.first, graph::detail::V_UNREACHED); } // backtrace from vertices v and w to their free (unmatched) ancesters, // return the nearest common ancestor (null_vertex if none) of v and w vertex_descriptor_t nearest_common_ancestor(vertex_descriptor_t v, vertex_descriptor_t w, vertex_descriptor_t& v_free_ancestor, vertex_descriptor_t& w_free_ancestor) const { std::pair v_up(v, graph::detail::V_EVEN); std::pair w_up(w, graph::detail::V_EVEN); vertex_descriptor_t nca; nca = w_free_ancestor = v_free_ancestor = graph_traits::null_vertex(); std::vector ancestor_of_w_vector(num_vertices(g), false); std::vector ancestor_of_v_vector(num_vertices(g), false); vertex_to_bool_map_t ancestor_of_w(ancestor_of_w_vector.begin(), vm); vertex_to_bool_map_t ancestor_of_v(ancestor_of_v_vector.begin(), vm); while (nca == graph_traits::null_vertex() && (v_free_ancestor == graph_traits::null_vertex() || w_free_ancestor == graph_traits::null_vertex())) { ancestor_of_w[w_up.first] = true; ancestor_of_v[v_up.first] = true; if (w_free_ancestor == graph_traits::null_vertex()) w_up = parent(w_up); if (v_free_ancestor == graph_traits::null_vertex()) v_up = parent(v_up); if (mate[v_up.first] == graph_traits::null_vertex()) v_free_ancestor = v_up.first; if (mate[w_up.first] == graph_traits::null_vertex()) w_free_ancestor = w_up.first; if (ancestor_of_w[v_up.first] == true || v_up.first == w_up.first) nca = v_up.first; else if (ancestor_of_v[w_up.first] == true) nca = w_up.first; else if (v_free_ancestor == w_free_ancestor && v_free_ancestor != graph_traits::null_vertex()) nca = v_up.first; } return nca; } // when a new top blossom b is created by connecting (v, w), we add sub-blossoms into // b along backtracing from v_prime and w_prime to stop_vertex (the base vertex); // also, we set labels and outlet for each base vertex we pass by void make_blossom(blossom_ptr_t b, vertex_descriptor_t w_prime, vertex_descriptor_t v_prime, vertex_descriptor_t stop_vertex) { std::pair u(v_prime, graph::detail::V_ODD); std::pair u_up(w_prime, graph::detail::V_EVEN); for (; u_up.first != stop_vertex; u = u_up, u_up = parent(u)) { if (u_up.second == graph::detail::V_EVEN) { if (!in_top_blossom(u_up.first)->sub_blossoms.empty()) outlet[u_up.first] = label_T[u.first]; label_T[u_up.first] = outlet[u.first]; } else if (u_up.second == graph::detail::V_ODD) label_S[u_up.first] = u.first; add_sub_blossom(b, u_up.first); } } // the design of recursively expanding augmenting path in (reversed_)retrieve_augmenting_path // functions is inspired by same functions in max_cardinality_matching.hpp; // except that in weighted matching, we use "outlet" vertices instead of "bridge" vertex pairs: // if blossom b is the smallest non-trivial blossom that contains its base vertex v, then // v and outlet[v] are where augmenting path enters and leaves b void retrieve_augmenting_path(vertex_descriptor_t v, vertex_descriptor_t w, vertex_state_t v_state) { if (v == w) aug_path.push_back(v); else if (v_state == graph::detail::V_EVEN) { aug_path.push_back(v); retrieve_augmenting_path(label_S[v], w, graph::detail::V_ODD); } else if (v_state == graph::detail::V_ODD) { if (outlet[v] == v) aug_path.push_back(v); else reversed_retrieve_augmenting_path(outlet[v], v, graph::detail::V_EVEN); retrieve_augmenting_path(label_T[v], w, graph::detail::V_EVEN); } } void reversed_retrieve_augmenting_path(vertex_descriptor_t v, vertex_descriptor_t w, vertex_state_t v_state) { if (v == w) aug_path.push_back(v); else if (v_state == graph::detail::V_EVEN) { reversed_retrieve_augmenting_path(label_S[v], w, graph::detail::V_ODD); aug_path.push_back(v); } else if (v_state == graph::detail::V_ODD) { reversed_retrieve_augmenting_path(label_T[v], w, graph::detail::V_EVEN); if (outlet[v] != v) retrieve_augmenting_path(outlet[v], v, graph::detail::V_EVEN); else aug_path.push_back(v); } } // correct labels for vertices in the augmenting path void relabel(vertex_descriptor_t v) { blossom_ptr_t b = in_blossom[v]->father; if (!is_in_blossom(b, mate[v])) { // if v is a new base vertex std::pair u(v, graph::detail::V_EVEN); while (label_S[u.first] != u.first && is_in_blossom(b, label_S[u.first])) u = parent(u, true); vertex_descriptor_t old_base = u.first; if (label_S[old_base] != old_base) { // if old base is not exposed label_T[v] = label_S[old_base]; outlet[v] = old_base; } else { // if old base is exposed then new label_T[v] is not in b, // we must (i) make b2 the smallest blossom containing v but not as base vertex // (ii) backtrace from b2's new base vertex to b label_T[v] = graph_traits::null_vertex(); for (b = b->father; b != blossom_ptr_t() && b->get_base() == v; b = b->father); if (b != blossom_ptr_t()) { u = std::make_pair(b->get_base(), graph::detail::V_ODD); while (!is_in_blossom(in_blossom[v]->father, old_label[u.first].first)) u = parent(u, true); label_T[v] = u.first; outlet[v] = old_label[u.first].first; } } } else if (label_S[v] == v || !is_in_blossom(b, label_S[v])) { // if v is an old base vertex // let u be the new base vertex; backtrace from u's old T-label std::pair u(b->get_base(), graph::detail::V_ODD); while (old_label[u.first].first != graph_traits::null_vertex() && old_label[u.first].first != v) u = parent(u, true); label_T[v] = old_label[u.first].second; outlet[v] = v; } else // if v is neither a new nor an old base vertex label_T[v] = label_S[v]; } void augmenting(vertex_descriptor_t v, vertex_descriptor_t v_free_ancestor, vertex_descriptor_t w, vertex_descriptor_t w_free_ancestor) { vertex_iterator_t vi, vi_end; // retrieve the augmenting path and put it in aug_path reversed_retrieve_augmenting_path(v, v_free_ancestor, graph::detail::V_EVEN); retrieve_augmenting_path(w, w_free_ancestor, graph::detail::V_EVEN); // augment the matching along aug_path vertex_descriptor_t a, b; vertex_list_t reversed_aug_path; while (!aug_path.empty()) { a = aug_path.front(); aug_path.pop_front(); reversed_aug_path.push_back(a); b = aug_path.front(); aug_path.pop_front(); reversed_aug_path.push_back(b); mate[a] = b; mate[b] = a; // reset base vertex for every blossom in augment path adjust_blossom(a, b); } for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) old_label[*vi] = std::make_pair(label_T[*vi], outlet[*vi]); // correct labels for in-blossom vertices along aug_path while (!reversed_aug_path.empty()) { a = reversed_aug_path.front(); reversed_aug_path.pop_front(); if (in_blossom[a]->father != blossom_ptr_t()) relabel(a); } for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_descriptor_t u = *vi; if (mate[u] != graph_traits::null_vertex()) label_S[u] = mate[u]; } // expand blossoms with zero dual variables std::vector new_top_blossoms; for (blossom_iterator_t bi = top_blossoms.begin(); bi != top_blossoms.end();) { if ((*bi)->dual_var <= 0) bi = expand_blossom(bi, new_top_blossoms); else ++bi; } top_blossoms.insert(top_blossoms.end(), new_top_blossoms.begin(), new_top_blossoms.end()); init(); } // create a new blossom and set labels for vertices inside void blossoming(vertex_descriptor_t v, vertex_descriptor_t v_prime, vertex_descriptor_t w, vertex_descriptor_t w_prime, vertex_descriptor_t nca) { vertex_iterator_t vi, vi_end; std::vector is_old_base_vector(num_vertices(g)); vertex_to_bool_map_t is_old_base(is_old_base_vector.begin(), vm); for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { if (*vi == base_vertex(*vi)) is_old_base[*vi] = true; } blossom_ptr_t b = boost::make_shared(); add_sub_blossom(b, nca); label_T[w_prime] = v; label_T[v_prime] = w; outlet[w_prime] = w; outlet[v_prime] = v; make_blossom(b, w_prime, v_prime, nca); make_blossom(b, v_prime, w_prime, nca); label_T[nca] = graph_traits::null_vertex(); outlet[nca] = nca; top_blossoms.push_back(b); bloom(b); // set gamma[b_base] = min_slack{critical_edge(b_base, other_base)} where each critical edge // is updated before, by argmin{slack(old_bases_in_b, other_base)}; vertex_vec_iter_t i, j; std::vector b_vertices = b->vertices(), old_base_in_b, other_base; vertex_descriptor_t b_base = b->get_base(); for (i = b_vertices.begin(); i != b_vertices.end(); ++i) { if (is_old_base[*i]) old_base_in_b.push_back(*i); } for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { if (*vi != b_base && *vi == base_vertex(*vi)) other_base.push_back(*vi); } for (i = other_base.begin(); i != other_base.end(); ++i) { edge_property_t min_slack = std::numeric_limits::max(); std::pair b_vi = null_edge; for (j = old_base_in_b.begin(); j != old_base_in_b.end(); ++j) { if (critical_edge[*j][*i] != null_edge && min_slack > slack(critical_edge[*j][*i].first)) { min_slack = slack(critical_edge[*j][*i].first); b_vi = critical_edge[*j][*i]; } } critical_edge[b_base][*i] = critical_edge[*i][b_base] = b_vi; } gamma[b_base] = std::numeric_limits::max(); for (i = other_base.begin(); i != other_base.end(); ++i) { if (critical_edge[b_base][*i] != null_edge) gamma[b_base] = std::min(gamma[b_base], slack(critical_edge[b_base][*i].first)); } } void init() { even_edges.clear(); vertex_iterator_t vi, vi_end; typename std::vector > >::iterator vei; for (boost::tie(vi,vi_end) = vertices(g), vei = critical_edge_vectors.begin(); vi != vi_end; ++vi, ++vei) { vertex_descriptor_t u = *vi; out_edge_iterator_t ei, ei_end; gamma[u] = tau[u] = pi[u] = std::numeric_limits::max(); std::fill(vei->begin(), vei->end(), null_edge); if (base_vertex(u) != u) continue; label_S[u] = label_T[u] = graph_traits::null_vertex(); outlet[u] = u; if (mate[u] == graph_traits::null_vertex()) { label_S[u] = u; bloom(in_top_blossom(u)); } } } bool augment_matching() { vertex_descriptor_t v, w, w_free_ancestor, v_free_ancestor; v = w = w_free_ancestor = v_free_ancestor = graph_traits::null_vertex(); bool found_alternating_path = false; // note that we only use edges of zero slack value for augmenting while (!even_edges.empty() && !found_alternating_path) { // search for augmenting paths depth-first edge_descriptor_t current_edge = even_edges.back(); even_edges.pop_back(); v = source(current_edge, g); w = target(current_edge, g); vertex_descriptor_t v_prime = base_vertex(v); vertex_descriptor_t w_prime = base_vertex(w); // w_prime == v_prime implies that we get an edge that has been shrunk into a blossom if (v_prime == w_prime) continue; // a paranoid check if (label_S[v_prime] == graph_traits::null_vertex()) { std::swap(v_prime, w_prime); std::swap(v, w); } // w_prime may be unlabeled or have a T-label; replace the existed T-label if the edge slack // is smaller than current pi[w_prime] and update it. Note that a T-label is "deserved" only when pi equals zero. // also update tau and tau_idx so that tau_idx becomes T-label when a T-blossom is expanded if (label_S[w_prime] == graph_traits::null_vertex()) { if (slack(current_edge) < pi[w_prime]) put_T_label(w_prime, v, w, slack(current_edge)); if (slack(current_edge) < tau[w]) { if (in_blossom[w]->father == blossom_ptr_t() || label_T[w_prime] == v || label_T[w_prime] == graph_traits::null_vertex() || nearest_common_ancestor(v_prime, label_T[w_prime], v_free_ancestor, w_free_ancestor) == graph_traits::null_vertex()) { tau[w] = slack(current_edge); tau_idx[w] = v; } } } else { if (slack(current_edge) > 0) { // update gamma and critical_edges when we have a smaller edge slack gamma[v_prime] = std::min(gamma[v_prime], slack(current_edge)); gamma[w_prime] = std::min(gamma[w_prime], slack(current_edge)); if (critical_edge[v_prime][w_prime] == null_edge || slack(critical_edge[v_prime][w_prime].first) > slack(current_edge)) { critical_edge[v_prime][w_prime] = std::pair(current_edge, true); critical_edge[w_prime][v_prime] = std::pair(current_edge, true); } continue; } else if (slack(current_edge) == 0) { // if nca is null_vertex then we have an augmenting path; otherwise we have // a new top blossom with nca as its base vertex vertex_descriptor_t nca = nearest_common_ancestor(v_prime, w_prime, v_free_ancestor, w_free_ancestor); if (nca == graph_traits::null_vertex()) found_alternating_path = true; //to break out of the loop else blossoming(v, v_prime, w, w_prime, nca); } } } if (!found_alternating_path) return false; augmenting(v, v_free_ancestor, w, w_free_ancestor); return true; } // slack the vertex and blossom dual variables when there is no augmenting path found // according to the primal-dual method bool adjust_dual() { edge_property_t delta1, delta2, delta3, delta4, delta; delta1 = delta2 = delta3 = delta4 = std::numeric_limits::max(); vertex_iterator_t vi, vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { delta1 = std::min(delta1, dual_var[*vi]); delta4 = pi[*vi] > 0 ? std::min(delta4, pi[*vi]) : delta4; if (*vi == base_vertex(*vi)) delta3 = std::min(delta3, gamma[*vi]/2); } for (blossom_iterator_t bi = top_blossoms.begin(); bi != top_blossoms.end(); ++bi) { vertex_descriptor_t b_base = (*bi)->get_base(); if (label_T[b_base] != graph_traits::null_vertex() && pi[b_base] == 0) delta2 = std::min(delta2, (*bi)->dual_var/2); } delta = std::min(std::min(delta1, delta2), std::min(delta3, delta4)); // start updating dual variables, note that the order is important for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_descriptor_t v = *vi, v_prime = base_vertex(v); if (label_S[v_prime] != graph_traits::null_vertex()) dual_var[v] -= delta; else if (label_T[v_prime] != graph_traits::null_vertex() && pi[v_prime] == 0) dual_var[v] += delta; if (v == v_prime) gamma[v] -= 2*delta; } for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_descriptor_t v_prime = base_vertex(*vi); if (pi[v_prime] > 0) tau[*vi] -= delta; } for (blossom_iterator_t bi = top_blossoms.begin(); bi != top_blossoms.end(); ++bi) { vertex_descriptor_t b_base = (*bi)->get_base(); if (label_T[b_base] != graph_traits::null_vertex() && pi[b_base] == 0) (*bi)->dual_var -= 2*delta; if (label_S[b_base] != graph_traits::null_vertex()) (*bi)->dual_var += 2*delta; } for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_descriptor_t v = *vi; if (pi[v] > 0) pi[v] -= delta; // when some T-vertices have zero pi value, bloom their mates so that matching can be further augmented if (label_T[v] != graph_traits::null_vertex() && pi[v] == 0) put_T_label(v, label_T[v], outlet[v], pi[v]); } // optimal solution reached, halt if (delta == delta1) return false; // expand odd blossoms with zero dual variables and zero pi value of their base vertices if (delta == delta2 && delta != delta3) { std::vector new_top_blossoms; for (blossom_iterator_t bi = top_blossoms.begin(); bi != top_blossoms.end();) { const blossom_ptr_t b = *bi; vertex_descriptor_t b_base = b->get_base(); if (b->dual_var == 0 && label_T[b_base] != graph_traits::null_vertex() && pi[b_base] == 0) bi = expand_T_blossom(bi, new_top_blossoms); else ++bi; } top_blossoms.insert(top_blossoms.end(), new_top_blossoms.begin(), new_top_blossoms.end()); } while (true) { // find a zero-slack critical edge (v, w) of zero gamma values std::pair best_edge = null_edge; std::vector base_nodes; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) { if (*vi == base_vertex(*vi)) base_nodes.push_back(*vi); } for (vertex_vec_iter_t i = base_nodes.begin(); i != base_nodes.end(); ++i) { if (gamma[*i] == 0) { for (vertex_vec_iter_t j = base_nodes.begin(); j != base_nodes.end(); ++j) { if (critical_edge[*i][*j] != null_edge && slack(critical_edge[*i][*j].first) == 0) best_edge = critical_edge[*i][*j]; } } } // if not found, continue finding other augment matching if (best_edge == null_edge) { bool augmented = augment_matching(); return augmented || delta != delta1; } // if found, determine either augmenting or blossoming vertex_descriptor_t v = source(best_edge.first, g), w = target(best_edge.first, g); vertex_descriptor_t v_prime = base_vertex(v), w_prime = base_vertex(w), v_free_ancestor, w_free_ancestor; vertex_descriptor_t nca = nearest_common_ancestor(v_prime, w_prime, v_free_ancestor, w_free_ancestor); if (nca == graph_traits::null_vertex()) { augmenting(v, v_free_ancestor, w, w_free_ancestor); return true; } else blossoming(v, v_prime, w, w_prime, nca); } return false; } template void get_current_matching(PropertyMap pm) { vertex_iterator_t vi, vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) put(pm, *vi, mate[*vi]); } private: const Graph& g; VertexIndexMap vm; const std::pair null_edge; // storage for the property maps below std::vector mate_vector; std::vector label_S_vector, label_T_vector; std::vector outlet_vector; std::vector tau_idx_vector; std::vector dual_var_vector; std::vector pi_vector, gamma_vector, tau_vector; std::vector in_blossom_vector; std::vector > old_label_vector; std::vector critical_edge_vector; std::vector > > critical_edge_vectors; // iterator property maps vertex_to_vertex_map_t mate; vertex_to_vertex_map_t label_S; // v has an S-label -> v can be an even vertex, label_S[v] is its mate vertex_to_vertex_map_t label_T; // v has a T-label -> v can be an odd vertex, label_T[v] is its predecessor in aug_path vertex_to_vertex_map_t outlet; vertex_to_vertex_map_t tau_idx; vertex_to_weight_map_t dual_var; vertex_to_weight_map_t pi, gamma, tau; vertex_to_blossom_map_t in_blossom; // map any vertex v to the trivial blossom containing v vertex_to_pair_map_t old_label; // before relabeling or expanding T-blossoms vertex_pair_to_edge_map_t critical_edge; // an not matched edge (v, w) is critical if v and w belongs to different S-blossoms vertex_list_t aug_path; edge_list_t even_edges; std::vector top_blossoms; }; template void maximum_weighted_matching(const Graph& g, MateMap mate, VertexIndexMap vm) { empty_matching::find_matching(g, mate); weighted_augmenting_path_finder augmentor(g, mate, vm); // can have |V| times augmenting at most for (std::size_t t = 0; t < num_vertices(g); ++t) { bool augmented = false; while (!augmented) { augmented = augmentor.augment_matching(); if (!augmented) { // halt if adjusting dual variables can't bring potential augment if (!augmentor.adjust_dual()) break; } } if (!augmented) break; } augmentor.get_current_matching(mate); } template inline void maximum_weighted_matching(const Graph& g, MateMap mate) { maximum_weighted_matching(g, mate, get(vertex_index,g)); } // brute-force matcher searches all possible combinations of matched edges to get the maximum weighted matching // which can be used for testing on small graphs (within dozens vertices) template class brute_force_matching { public: typedef typename graph_traits::vertex_descriptor vertex_descriptor_t; typedef typename graph_traits::vertex_iterator vertex_iterator_t; typedef typename std::vector::iterator vertex_vec_iter_t; typedef typename graph_traits::edge_iterator edge_iterator_t; typedef boost::iterator_property_map vertex_to_vertex_map_t; brute_force_matching(const Graph& arg_g, MateMap arg_mate, VertexIndexMap arg_vm) : g(arg_g), vm(arg_vm), mate_vector(num_vertices(g)), best_mate_vector(num_vertices(g)), mate(mate_vector.begin(), vm), best_mate(best_mate_vector.begin(), vm) { vertex_iterator_t vi,vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) best_mate[*vi] = mate[*vi] = get(arg_mate, *vi); } template void find_matching(PropertyMap pm) { edge_iterator_t ei; boost::tie(ei, ei_end) = edges(g); select_edge(ei); vertex_iterator_t vi,vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) put(pm, *vi, best_mate[*vi]); } private: const Graph& g; VertexIndexMap vm; std::vector mate_vector, best_mate_vector; vertex_to_vertex_map_t mate, best_mate; edge_iterator_t ei_end; void select_edge(edge_iterator_t ei) { if (ei == ei_end) { if (matching_weight_sum(g, mate) > matching_weight_sum(g, best_mate)) { vertex_iterator_t vi, vi_end; for (boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) best_mate[*vi] = mate[*vi]; } return; } vertex_descriptor_t v, w; v = source(*ei, g); w = target(*ei, g); select_edge(++ei); if (mate[v] == graph_traits::null_vertex() && mate[w] == graph_traits::null_vertex()) { mate[v] = w; mate[w] = v; select_edge(ei); mate[v] = mate[w] = graph_traits::null_vertex(); } } }; template void brute_force_maximum_weighted_matching(const Graph& g, MateMap mate, VertexIndexMap vm) { empty_matching::find_matching(g, mate); brute_force_matching brute_force_matcher(g, mate, vm); brute_force_matcher.find_matching(mate); } template inline void brute_force_maximum_weighted_matching(const Graph& g, MateMap mate) { brute_force_maximum_weighted_matching(g, mate, get(vertex_index, g)); } } #endif