Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-18 08:47:28

0001 //=======================================================================
0002 // Copyright (c) 2018 Yi Ji
0003 // Copyright (c) 2025 Joris van Rantwijk
0004 //
0005 // Distributed under the Boost Software License, Version 1.0.
0006 // (See accompanying file LICENSE_1_0.txt or copy at
0007 // http://www.boost.org/LICENSE_1_0.txt)
0008 //
0009 //=======================================================================
0010 
0011 #ifndef BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP
0012 #define BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP
0013 
0014 #include <boost/assert.hpp>
0015 #include <boost/optional.hpp>
0016 #include <boost/property_map/property_map.hpp>
0017 #include <boost/range/iterator_range_core.hpp>
0018 #include <boost/graph/exception.hpp>
0019 #include <boost/graph/graph_concepts.hpp>
0020 #include <boost/graph/max_cardinality_matching.hpp>  // for empty_matching
0021 
0022 #include <algorithm>
0023 #include <deque>
0024 #include <limits>
0025 #include <list>
0026 #include <stack>
0027 #include <tuple>  // for std::tie
0028 #include <utility>  // for std::pair, std::swap
0029 #include <vector>
0030 
0031 namespace boost
0032 {
0033 
0034 template <typename Graph, typename MateMap, typename VertexIndexMap>
0035 typename property_traits<
0036     typename property_map<Graph, edge_weight_t>::type>::value_type
0037 matching_weight_sum(const Graph& g, MateMap mate, VertexIndexMap vm)
0038 {
0039     using vertex_iterator_t = typename graph_traits<Graph>::vertex_iterator;
0040     using vertex_descriptor_t = typename graph_traits<Graph>::vertex_descriptor;
0041     using edge_property_t = typename property_traits<
0042         typename property_map<Graph, edge_weight_t>::type>::value_type;
0043 
0044     edge_property_t weight_sum = 0;
0045     vertex_iterator_t vi, vi_end;
0046 
0047     for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
0048     {
0049         vertex_descriptor_t v = *vi;
0050         if (get(mate, v) != graph_traits<Graph>::null_vertex()
0051             && get(vm, v) < get(vm, get(mate, v)))
0052             weight_sum += get(edge_weight, g, edge(v, mate[v], g).first);
0053     }
0054     return weight_sum;
0055 }
0056 
0057 template <typename Graph, typename MateMap>
0058 inline typename property_traits<
0059     typename property_map<Graph, edge_weight_t>::type>::value_type
0060 matching_weight_sum(const Graph& g, MateMap mate)
0061 {
0062     return matching_weight_sum(g, mate, get(vertex_index, g));
0063 }
0064 
0065 // brute-force matcher searches all possible combinations of matched edges to
0066 // get the maximum weighted matching which can be used for testing on small
0067 // graphs (within dozens vertices)
0068 template <typename Graph, typename MateMap, typename VertexIndexMap>
0069 class brute_force_matching
0070 {
0071 public:
0072     using vertex_descriptor_t = typename graph_traits<Graph>::vertex_descriptor;
0073     using vertex_iterator_t = typename graph_traits<Graph>::vertex_iterator;
0074     using vertex_vec_iter_t =
0075         typename std::vector<vertex_descriptor_t>::iterator;
0076     using edge_iterator_t = typename graph_traits<Graph>::edge_iterator;
0077     using vertex_to_vertex_map_t =
0078         boost::iterator_property_map<vertex_vec_iter_t, VertexIndexMap>;
0079 
0080     brute_force_matching(
0081         const Graph& arg_g, MateMap arg_mate, VertexIndexMap arg_vm)
0082     : g(&arg_g)
0083     , vm(arg_vm)
0084     , mate_vector(num_vertices(*g))
0085     , best_mate_vector(num_vertices(*g))
0086     , mate(mate_vector.begin(), vm)
0087     , best_mate(best_mate_vector.begin(), vm)
0088     {
0089         vertex_iterator_t vi, vi_end;
0090         for (boost::tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi)
0091             best_mate[*vi] = mate[*vi] = get(arg_mate, *vi);
0092     }
0093 
0094     template <typename PropertyMap> void find_matching(PropertyMap pm)
0095     {
0096         edge_iterator_t ei;
0097         boost::tie(ei, ei_end) = edges(*g);
0098         select_edge(ei);
0099 
0100         vertex_iterator_t vi, vi_end;
0101         for (boost::tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi)
0102             put(pm, *vi, best_mate[*vi]);
0103     }
0104 
0105 private:
0106     const Graph* g;
0107     VertexIndexMap vm;
0108     std::vector<vertex_descriptor_t> mate_vector, best_mate_vector;
0109     vertex_to_vertex_map_t mate, best_mate;
0110     edge_iterator_t ei_end;
0111 
0112     void select_edge(edge_iterator_t ei)
0113     {
0114         if (ei == ei_end)
0115         {
0116             if (matching_weight_sum(*g, mate)
0117                 > matching_weight_sum(*g, best_mate))
0118             {
0119                 vertex_iterator_t vi, vi_end;
0120                 for (boost::tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi)
0121                     best_mate[*vi] = mate[*vi];
0122             }
0123             return;
0124         }
0125 
0126         vertex_descriptor_t v, w;
0127         v = source(*ei, *g);
0128         w = target(*ei, *g);
0129 
0130         select_edge(++ei);
0131 
0132         if (mate[v] == graph_traits<Graph>::null_vertex()
0133             && mate[w] == graph_traits<Graph>::null_vertex())
0134         {
0135             mate[v] = w;
0136             mate[w] = v;
0137             select_edge(ei);
0138             mate[v] = mate[w] = graph_traits<Graph>::null_vertex();
0139         }
0140     }
0141 };
0142 
0143 template <typename Graph, typename MateMap, typename VertexIndexMap>
0144 void brute_force_maximum_weighted_matching(
0145     const Graph& g, MateMap mate, VertexIndexMap vm)
0146 {
0147     empty_matching<Graph, MateMap>::find_matching(g, mate);
0148     brute_force_matching<Graph, MateMap, VertexIndexMap> brute_force_matcher(
0149         g, mate, vm);
0150     brute_force_matcher.find_matching(mate);
0151 }
0152 
0153 template <typename Graph, typename MateMap>
0154 inline void brute_force_maximum_weighted_matching(const Graph& g, MateMap mate)
0155 {
0156     brute_force_maximum_weighted_matching(g, mate, get(vertex_index, g));
0157 }
0158 
0159 namespace graph
0160 {
0161 namespace detail
0162 {
0163 
0164 /** Check that vertex indices are unique and in range [0, V). */
0165 template <typename Graph, typename VertexIndexMap>
0166 void check_vertex_index_range(const Graph& g, VertexIndexMap vm)
0167 {
0168     using index_t = typename property_traits<VertexIndexMap>::value_type;
0169     using unsigned_index_t = typename std::make_unsigned<index_t>::type;
0170     auto nv = num_vertices(g);
0171     std::vector<bool> got_vertex(nv);
0172     for (const auto& x : make_iterator_range(vertices(g)))
0173     {
0174         index_t i = get(vm, x);
0175         if ((i < 0) || (static_cast<unsigned_index_t>(i) >= nv))
0176             throw bad_graph("Invalid vertex index.");
0177         if (got_vertex[i])
0178             throw bad_graph("Duplicate vertex index.");
0179         got_vertex[i] = true;
0180     }
0181 }
0182 
0183 /** Check that edge weights are valid. */
0184 template <typename Graph, typename EdgeWeightMap>
0185 void check_maximum_weighted_matching_edge_weights(
0186     const Graph& g, EdgeWeightMap edge_weights)
0187 {
0188     for (const auto& e : make_iterator_range(edges(g)))
0189     {
0190         auto w = get(edge_weights, e);
0191         auto max_weight = (std::numeric_limits<decltype(w)>::max)() / 4;
0192         if (!(w <= max_weight))  // inverted logic to catch NaN
0193             throw bad_graph("Edge weight exceeds maximum supported value.");
0194     }
0195 }
0196 
0197 /** Implementation of the matching algorithm. */
0198 template <typename Graph, typename VertexIndexMap, typename EdgeWeightMap>
0199 struct maximum_weighted_matching_context
0200 {
0201     using vertex_t = typename graph_traits<Graph>::vertex_descriptor;
0202     using edge_t = typename graph_traits<Graph>::edge_descriptor;
0203     using vertices_size_t = typename graph_traits<Graph>::vertices_size_type;
0204     using edges_size_t = typename graph_traits<Graph>::edges_size_type;
0205     using weight_t = typename property_traits<EdgeWeightMap>::value_type;
0206 
0207     /** Ordered pair of vertices. */
0208     using vertex_pair_t = std::pair<vertex_t, vertex_t>;
0209 
0210     /**
0211      * List of edges forming an alternating path or alternating cycle.
0212      *
0213      * The path is defined over top-level blossoms; it skips parts of the path
0214      * that are internal to blossoms. Vertex pairs are oriented to match the
0215      * direction of the path.
0216      */
0217     using alternating_path_t = std::deque<vertex_pair_t>;
0218 
0219     /** Top-level blossoms may be labeled "S" or "T" or unlabeled. */
0220     enum blossom_label_t { LABEL_NONE = 0, LABEL_S = 1, LABEL_T = 2 };
0221 
0222     struct nontrivial_blossom_t;  // forward declaration
0223 
0224     /**
0225      * A blossom is either a single vertex, or an odd-length alternating
0226      * cycle over sub-blossoms.
0227      */
0228     struct blossom_t
0229     {
0230         /** Parent of this blossom, or "nullptr" for top-level blossom. */
0231         nontrivial_blossom_t* parent;
0232 
0233         /**
0234          * Base vertex of this blossom.
0235          *
0236          * This is the unique vertex inside the blossom which is not matched
0237          * to another vertex in the same blossom.
0238          */
0239         vertex_t base_vertex;
0240 
0241         /** Label S or T or NONE, only valid for top-level blossoms. */
0242         blossom_label_t label;
0243 
0244         /** True if this is an instance of nontrivial_blossom. */
0245         bool is_nontrivial_blossom;
0246 
0247         /** Edge that attaches this blossom to the alternating tree. */
0248         vertex_pair_t tree_edge;
0249 
0250         /** Base vertex of the blossom at the root of the alternating tree. */
0251         vertex_t tree_root;
0252 
0253         /** Least-slack edge to a different S-blossom. */
0254         optional<edge_t> best_edge;
0255 
0256         /** Initialize a blossom instance. */
0257         blossom_t(vertex_t base_vertex = graph_traits<Graph>::null_vertex(),
0258                   bool is_nontrivial_blossom = false)
0259           : parent(nullptr)
0260           , base_vertex(base_vertex)
0261           , label(LABEL_NONE)
0262           , is_nontrivial_blossom(is_nontrivial_blossom)
0263         { }
0264 
0265         /**
0266          * Cast this blossom_t instance to nontrivial_blossom_t if possible,
0267          * otherwise return "nullptr".
0268          */
0269         nontrivial_blossom_t* nontrivial()
0270         {
0271             // This is much faster than dynamic_cast.
0272             return (is_nontrivial_blossom ?
0273                 static_cast<nontrivial_blossom_t*>(this) : nullptr);
0274         }
0275 
0276         const nontrivial_blossom_t* nontrivial() const
0277         {
0278             return (is_nontrivial_blossom ?
0279                 static_cast<const nontrivial_blossom_t*>(this) : nullptr);
0280         }
0281     };
0282 
0283     /**
0284      * A non-trivial blossom is an odd-length alternating cycle over
0285      * 3 or more sub-blossoms.
0286      */
0287     struct nontrivial_blossom_t : public blossom_t
0288     {
0289         struct sub_blossom_t {
0290             /** Pointer to sub-blossom. */
0291             blossom_t* blossom;
0292 
0293             /** Vertex pair (x, y) where "x" is a vertex in "blossom" and
0294                 "y" is a vertex in the next sub-blossom. */
0295             vertex_pair_t edge;
0296         };
0297 
0298         /** List of sub-blossoms, ordered along the alternating cycle. */
0299         std::list<sub_blossom_t> subblossoms;
0300 
0301         /** Dual LPP variable for this blossom. */
0302         weight_t dual_var;
0303 
0304         /** Least-slack edges to other S-blossoms. */
0305         std::list<edge_t> best_edge_set;
0306 
0307         /** Initialize a non-trivial blossom. */
0308         nontrivial_blossom_t(
0309             const std::vector<blossom_t*>& blossoms,
0310             const std::deque<vertex_pair_t>& edges)
0311           : blossom_t(blossoms.front()->base_vertex, true)
0312           , dual_var(0)
0313         {
0314             BOOST_ASSERT(blossoms.size() == edges.size());
0315             BOOST_ASSERT(blossoms.size() % 2 == 1);
0316             BOOST_ASSERT(blossoms.size() >= 3);
0317 
0318             auto blossom_it = blossoms.begin();
0319             auto blossom_end = blossoms.end();
0320             auto edge_it = edges.begin();
0321             while (blossom_it != blossom_end) {
0322                 subblossoms.push_back({*blossom_it, *edge_it});
0323                 ++blossom_it;
0324                 ++edge_it;
0325             }
0326         }
0327 
0328         /** Find the position of the specified subblossom. */
0329         std::pair<vertices_size_t, typename std::list<sub_blossom_t>::iterator>
0330         find_subblossom(blossom_t* child)
0331         {
0332             vertices_size_t pos = 0;
0333             auto it = subblossoms.begin();
0334             while (it->blossom != child) {
0335                 ++it;
0336                 ++pos;
0337                 BOOST_ASSERT(it != subblossoms.end());
0338             }
0339             return std::make_pair(pos, it);
0340         }
0341     };
0342 
0343     /** Specification of a delta step. */
0344     struct delta_step_t
0345     {
0346         /** Type of delta step: 1, 2, 3 or 4. */
0347         int kind;
0348 
0349         /** Delta value. */
0350         weight_t value;
0351 
0352         /** Edge on which the minimum delta occurs (for delta type 2 or 3). */
0353         edge_t edge;
0354 
0355         /** Blossom on which the minimum delta occurs (for delta type 4). */
0356         nontrivial_blossom_t* blossom;
0357     };
0358 
0359     /** Similar to vector_property_map, but uses a fixed-size vector. */
0360     template <typename T>
0361     struct vertex_map
0362     {
0363         using key_type = typename property_traits<VertexIndexMap>::key_type;
0364         std::vector<T> vec;
0365         VertexIndexMap vm;
0366 
0367         vertex_map(vertices_size_t arg_size, VertexIndexMap arg_vm)
0368           : vec(arg_size)
0369           , vm(arg_vm)
0370         { }
0371 
0372         const T& operator[](const key_type& v) const
0373         {
0374             return vec[get(vm, v)];
0375         }
0376 
0377         T& operator[](const key_type& v)
0378         {
0379             return vec[get(vm, v)];
0380         }
0381     };
0382 
0383     /** Keep track of the least-slack edge out of a bunch of edges. */
0384     struct least_slack_edge_t
0385     {
0386         optional<edge_t> edge;
0387         weight_t slack;
0388 
0389         least_slack_edge_t() : slack(0) {}
0390 
0391         void update(const edge_t& e, weight_t s)
0392         {
0393             if ((!edge.has_value()) || (s < slack))
0394             {
0395                 edge = e;
0396                 slack = s;
0397             }
0398         }
0399     };
0400 
0401     /** Scale integer edge weights to enable integer-only calculations. */
0402     static constexpr weight_t weight_factor =
0403         std::numeric_limits<weight_t>::is_integer ? 2 : 1;
0404 
0405     /** Input graph. */
0406     const Graph* g;
0407     VertexIndexMap vm;
0408     EdgeWeightMap edge_weights;
0409 
0410     /** Current mate of each vertex. */
0411     vertex_map<vertex_t> vertex_mate;
0412 
0413     /**
0414      * For each vertex, the trivial blossom that contains it.
0415      *
0416      * Pointers to blossoms must remain valid for the life time of
0417      * this data structure, therefore the underlying vector must
0418      * have a fixed size.
0419      */
0420     vertex_map<blossom_t> trivial_blossom;
0421 
0422     /**
0423      * List of non-trivial blossoms.
0424      *
0425      * This must be a linked list to ensure that elements can be added
0426      * and removed without invalidating pointers to other elements.
0427      */
0428     std::list<nontrivial_blossom_t> nontrivial_blossom;
0429 
0430     /** For each vertex, the unique top-level blossom that contains it. */
0431     vertex_map<blossom_t*> vertex_top_blossom;
0432 
0433     /** For each vertex, a variable in the dual LPP. */
0434     vertex_map<weight_t> vertex_dual;
0435 
0436     /** For T-vertex or unlabeled vertex, least-slack edge to any S-vertex. */
0437     vertex_map<optional<edge_t>> vertex_best_edge;
0438 
0439     /** Queue of S-vertices to be scanned. */
0440     std::deque<vertex_t> scan_queue;
0441 
0442     /** Initialize the matching algorithm. */
0443     explicit maximum_weighted_matching_context(
0444         const Graph& arg_g, VertexIndexMap arg_vm, EdgeWeightMap weights)
0445       : g(&arg_g)
0446       , vm(arg_vm)
0447       , edge_weights(weights)
0448       , vertex_mate(num_vertices(*g), arg_vm)
0449       , trivial_blossom(num_vertices(*g), arg_vm)
0450       , vertex_top_blossom(num_vertices(*g), arg_vm)
0451       , vertex_dual(num_vertices(*g), arg_vm)
0452       , vertex_best_edge(num_vertices(*g), arg_vm)
0453     {
0454         // Vertex duals are initialized to half the maximum edge weight.
0455         weight_t max_weight = 0;
0456         for (const edge_t& e : make_iterator_range(edges(*g)))
0457             max_weight = (std::max)(max_weight, get(weights, e));
0458         weight_t init_vertex_dual = max_weight * (weight_factor / 2);
0459 
0460         for (const vertex_t& x : make_iterator_range(vertices(*g)))
0461         {
0462             vertex_mate[x] = null_vertex();
0463             trivial_blossom[x].base_vertex = x;
0464             vertex_top_blossom[x] = &trivial_blossom[x];
0465             vertex_dual[x] = init_vertex_dual;
0466         }
0467     }
0468 
0469     /** Return the null vertex. */
0470     static vertex_t null_vertex()
0471     {
0472         return graph_traits<Graph>::null_vertex();
0473     }
0474 
0475     /** Call a function for every vertex inside the specified blossom. */
0476     template <typename Func>
0477     static void for_vertices_in_blossom(const blossom_t& blossom, Func func)
0478     {
0479         auto ntb = blossom.nontrivial();
0480         if (ntb) {
0481             // Visit all vertices in the non-trivial blossom.
0482             // Use an explicit stack to avoid deep call chains.
0483             std::vector<const nontrivial_blossom_t*> stack;
0484             stack.push_back(ntb);
0485             while (!stack.empty()) {
0486                 auto cur = stack.back();
0487                 stack.pop_back();
0488                 for (const auto& sub : cur->subblossoms) {
0489                     ntb = sub.blossom->nontrivial();
0490                     if (ntb) {
0491                         stack.push_back(ntb);
0492                     } else {
0493                         func(sub.blossom->base_vertex);
0494                     }
0495                 }
0496             }
0497         } else {
0498             // A trivial blossom contains just one vertex.
0499             func(blossom.base_vertex);
0500         }
0501     }
0502 
0503     /** Mark blossom as the top-level blossom of its vertices. */
0504     void update_top_level_blossom(blossom_t& blossom)
0505     {
0506         BOOST_ASSERT(!blossom.parent);
0507         for_vertices_in_blossom(blossom,
0508             [this,&blossom](vertex_t x)
0509             {
0510                 vertex_top_blossom[x] = &blossom;
0511             });
0512     }
0513 
0514     /**
0515      * Calculate edge slack.
0516      * The two vertices must be in different top-level blossoms.
0517      */
0518     weight_t edge_slack(const edge_t& e) const
0519     {
0520         vertex_t x = source(e, *g);
0521         vertex_t y = target(e, *g);
0522         weight_t w = get(edge_weights, e);
0523         BOOST_ASSERT(vertex_top_blossom[x] != vertex_top_blossom[y]);
0524         return vertex_dual[x] + vertex_dual[y] - weight_factor * w;
0525     }
0526 
0527     /** Clear least-slack edge tracking. */
0528     void clear_best_edge()
0529     {
0530         for (vertex_t x : make_iterator_range(vertices(*g)))
0531         {
0532             vertex_best_edge[x].reset();
0533             trivial_blossom[x].best_edge.reset();
0534         }
0535 
0536         for (nontrivial_blossom_t& b : nontrivial_blossom)
0537         {
0538             b.best_edge.reset();
0539             b.best_edge_set.clear();
0540         }
0541     }
0542 
0543     /** Add edge from unlabeled verter or T-vertex "y" to an S-vertex. */
0544     void add_delta2_edge(vertex_t y, const edge_t& e, weight_t slack)
0545     {
0546         auto& best_edge = vertex_best_edge[y];
0547         if ((!best_edge.has_value()) || slack < edge_slack(*best_edge))
0548             best_edge = e;
0549     }
0550 
0551     /** Return least-slack edge between any unlabeled vertex and S-vertex. */
0552     least_slack_edge_t get_least_slack_delta2_edge()
0553     {
0554         least_slack_edge_t best_edge;
0555         for (vertex_t x : make_iterator_range(vertices(*g)))
0556         {
0557             if (vertex_top_blossom[x]->label == LABEL_NONE)
0558             {
0559                 const auto& vertex_edge = vertex_best_edge[x];
0560                 if (vertex_edge.has_value())
0561                     best_edge.update(*vertex_edge, edge_slack(*vertex_edge));
0562             }
0563         }
0564         return best_edge;
0565     }
0566 
0567     /** Add edge between different S-blossoms. */
0568     void add_delta3_edge(blossom_t& b, const edge_t& e, weight_t slack)
0569     {
0570         auto& best_edge = b.best_edge;
0571         if ((!best_edge.has_value()) || slack < edge_slack(*best_edge))
0572             best_edge = e;
0573 
0574         auto ntb = b.nontrivial();
0575         if (ntb)
0576             ntb->best_edge_set.push_back(e);
0577     }
0578 
0579     /** Update least-slack edge tracking after merging blossoms. */
0580     void merge_delta3_blossoms(nontrivial_blossom_t& blossom)
0581     {
0582         // Build a temporary array holding the least-slack edges to
0583         // other S-blossoms. The array is indexed by base vertex.
0584         std::vector<least_slack_edge_t> tmp_best_edge(num_vertices(*g));
0585 
0586         // Collect edges from sub-blossoms that were S-blossoms.
0587         for (auto& sub : blossom.subblossoms)
0588         {
0589             blossom_t* b = sub.blossom;
0590             if (b->label == LABEL_S)
0591             {
0592                 b->best_edge.reset();
0593                 auto ntb = b->nontrivial();
0594                 if (ntb)
0595                 {
0596                     // Use least-slack edges from subblossom.
0597                     for (const edge_t& e : ntb->best_edge_set)
0598                     {
0599                         blossom_t* bx = vertex_top_blossom[source(e, *g)];
0600                         blossom_t* by = vertex_top_blossom[target(e, *g)];
0601                         BOOST_ASSERT(bx == &blossom);
0602                         // Only use edges between top-level blossoms.
0603                         if (bx != by)
0604                         {
0605                             BOOST_ASSERT(by->label == LABEL_S);
0606                             tmp_best_edge[get(vm, by->base_vertex)].update(
0607                                 e, edge_slack(e));
0608                         }
0609                     }
0610                     ntb->best_edge_set.clear();
0611                 }
0612                 else
0613                 {
0614                     // Trivial blossoms don't maintain a least-slack edge set.
0615                     // Consider all incident edges.
0616                     for (const edge_t& e :
0617                         make_iterator_range(out_edges(b->base_vertex, *g)))
0618                     {
0619                         BOOST_ASSERT(source(e, *g) == b->base_vertex);
0620                         vertex_t y = target(e, *g);
0621                         blossom_t* by = vertex_top_blossom[y];
0622                         // Only use edges to different S-blossoms.
0623                         // Ignore edges with negative weight.
0624                         if ((by != &blossom)
0625                             && (by->label == LABEL_S)
0626                             && (get(edge_weights, e) >= 0))
0627                         {
0628                             tmp_best_edge[get(vm, by->base_vertex)].update(
0629                                 e, edge_slack(e));
0630                         }
0631                     }
0632                 }
0633             }
0634         }
0635 
0636         // Build a compact list of edges from the temporary array.
0637         // Also find the overall least-slack edge to any other S-blossom.
0638         BOOST_ASSERT(blossom.best_edge_set.empty());
0639         BOOST_ASSERT(!blossom.best_edge.has_value());
0640         least_slack_edge_t best_edge;
0641         for (const least_slack_edge_t& item : tmp_best_edge)
0642         {
0643             if (item.edge.has_value())
0644             {
0645                 blossom.best_edge_set.push_back(*item.edge);
0646                 best_edge.update(*item.edge, item.slack);
0647             }
0648         }
0649         blossom.best_edge = best_edge.edge;
0650     }
0651 
0652     /** Return least-slack edge between any pair of S-blossoms. */
0653     least_slack_edge_t get_least_slack_delta3_edge()
0654     {
0655         least_slack_edge_t best_edge;
0656 
0657         for (vertex_t x : make_iterator_range(vertices(*g)))
0658         {
0659             blossom_t* b = vertex_top_blossom[x];
0660             BOOST_ASSERT(!b->parent);
0661             if ((b->label == LABEL_S) && (b->best_edge.has_value()))
0662                 best_edge.update(*b->best_edge, edge_slack(*b->best_edge));
0663         }
0664 
0665         return best_edge;
0666     }
0667 
0668     /** Add the vertices in a blossom to the scan queue. */
0669     void add_vertices_to_scan_queue(blossom_t& blossom)
0670     {
0671         for_vertices_in_blossom(blossom,
0672             [this](vertex_t x)
0673             {
0674                 scan_queue.push_back(x);
0675             });
0676     }
0677 
0678     /** Trace back through the alternating trees from vertices "x" and "y". */
0679     alternating_path_t trace_alternating_paths(vertex_t x, vertex_t y)
0680     {
0681         // Initialize a path containing only the edge (x, y).
0682         alternating_path_t path;
0683         path.emplace_back(x, y);
0684 
0685         // Trace alternating path from "x" to the root of the tree.
0686         while (x != null_vertex())
0687         {
0688             blossom_t* bx = vertex_top_blossom[x];
0689             x = bx->tree_edge.first;
0690             if (x != null_vertex())
0691                 path.push_front(bx->tree_edge);
0692         }
0693 
0694         // Trace alternating path from "y" to the root of the tree.
0695         while (y != null_vertex())
0696         {
0697             blossom_t* by = vertex_top_blossom[y];
0698             y = by->tree_edge.first;
0699             if (y != null_vertex())
0700                 path.emplace_back(by->tree_edge.second, y);
0701         }
0702 
0703         // If we found a common ancestor, trim the paths so they end there.
0704         while (path.front().second == path.back().first)
0705         {
0706             BOOST_ASSERT(path.size() > 2);
0707             path.pop_front();
0708             path.pop_back();
0709         }
0710 
0711         // Any alternating path between S-blossoms must have odd length.
0712         BOOST_ASSERT(path.size() % 2 == 1);
0713 
0714         return path;
0715     }
0716 
0717     /** Create a new S-blossom from an alternating cycle. */
0718     void make_blossom(const alternating_path_t& path)
0719     {
0720         BOOST_ASSERT(path.size() % 2 == 1);
0721         BOOST_ASSERT(path.size() >= 3);
0722 
0723         // Collect pointers to sub-blossoms.
0724         std::vector<blossom_t*> subblossoms;
0725         subblossoms.reserve(path.size());
0726         for (const vertex_pair_t& edge : path)
0727             subblossoms.push_back(vertex_top_blossom[edge.first]);
0728 
0729         // Check that the path is cyclic.
0730         vertices_size_t pos = 0;
0731         for (const vertex_pair_t& edge : path)
0732         {
0733             pos = (pos + 1) % subblossoms.size();
0734             BOOST_ASSERT(vertex_top_blossom[edge.second] == subblossoms[pos]);
0735         }
0736 
0737         // Create the new blossom.
0738         nontrivial_blossom.emplace_back(subblossoms, path);
0739         nontrivial_blossom_t& blossom = nontrivial_blossom.back();
0740 
0741         // Link the subblossoms to the new parent.
0742         // Insert former T-vertices into the scan queue.
0743         for (blossom_t* b : subblossoms)
0744         {
0745             BOOST_ASSERT(!b->parent);
0746             b->parent = &blossom;
0747             if (b->label == LABEL_T)
0748                 add_vertices_to_scan_queue(*b);
0749         }
0750 
0751         // Mark vertices as belonging to the new blossom.
0752         update_top_level_blossom(blossom);
0753 
0754         // Assign label S to the new blossom and link to the alternating tree.
0755         BOOST_ASSERT(subblossoms.front()->label == LABEL_S);
0756         blossom.label = LABEL_S;
0757         blossom.tree_edge = subblossoms.front()->tree_edge;
0758         blossom.tree_root = subblossoms.front()->tree_root;
0759 
0760         // Merge least-slack edges for the S-sub-blossoms.
0761         merge_delta3_blossoms(blossom);
0762     }
0763 
0764     /** Expand and delete a T-blossom. */
0765     void expand_t_blossom(nontrivial_blossom_t* blossom)
0766     {
0767         BOOST_ASSERT(!blossom->parent);
0768         BOOST_ASSERT(blossom->label == LABEL_T);
0769 
0770         // Convert sub-blossoms into top-level blossoms.
0771         for (const auto& sub : blossom->subblossoms)
0772         {
0773             blossom_t* b = sub.blossom;
0774             BOOST_ASSERT(b->parent == blossom);
0775             b->parent = nullptr;
0776             b->label = LABEL_NONE;
0777             update_top_level_blossom(*b);
0778         }
0779 
0780         // Reconstruct the alternating tree via the sub-blossoms.
0781         // Find the sub-blossom that attaches the expanding blossom to
0782         // the alternating tree.
0783         blossom_t* entry = vertex_top_blossom[blossom->tree_edge.second];
0784 
0785         auto subblossom_loc = blossom->find_subblossom(entry);
0786         auto entry_pos = subblossom_loc.first;
0787         auto entry_it = subblossom_loc.second;
0788 
0789         // Get the edge that attached this blossom to the alternating tree.
0790         vertex_t x, y;
0791         std::tie(x, y) = blossom->tree_edge;
0792 
0793         // Reconstruct the tree in an even number of steps from entry to base.
0794         auto sub_it = entry_it;
0795         if (entry_pos % 2 == 0)
0796         {
0797             // Walk backward around the blossom.
0798             auto sub_begin = blossom->subblossoms.begin();
0799             while (sub_it != sub_begin)
0800             {
0801                 extend_tree_s_to_t(x, y);
0802                 --sub_it;
0803                 BOOST_ASSERT(sub_it != sub_begin);
0804                 --sub_it;
0805                 std::tie(y, x) = sub_it->edge;
0806             }
0807         }
0808         else
0809         {
0810             // Walk forward around the blossom.
0811             auto sub_end = blossom->subblossoms.end();
0812             while (sub_it != sub_end) {
0813                 extend_tree_s_to_t(x, y);
0814                 ++sub_it;
0815                 BOOST_ASSERT(sub_it != sub_end);
0816                 std::tie(x, y) = sub_it->edge;
0817                 ++sub_it;
0818             }
0819         }
0820 
0821         // Assign label T to the base sub-blossom.
0822         blossom_t* base = blossom->subblossoms.front().blossom;
0823         base->label = LABEL_T;
0824         base->tree_edge = std::make_pair(x, y);
0825         base->tree_root = blossom->tree_root;
0826 
0827         // Delete the expanded blossom.
0828         auto blossom_it = std::find_if(
0829             nontrivial_blossom.begin(),
0830             nontrivial_blossom.end(),
0831             [blossom](const nontrivial_blossom_t& b)
0832             {
0833                 return (&b == blossom);
0834             });
0835         BOOST_ASSERT(blossom_it != nontrivial_blossom.end());
0836         nontrivial_blossom.erase(blossom_it);
0837     }
0838 
0839     void augment_blossom_rec(
0840         nontrivial_blossom_t& blossom, blossom_t& entry,
0841         std::stack<std::pair<nontrivial_blossom_t*, blossom_t*>>& stack)
0842     {
0843         auto subblossom_loc = blossom.find_subblossom(&entry);
0844         auto entry_pos = subblossom_loc.first;
0845         auto entry_it = subblossom_loc.second;
0846 
0847         // Walk from entry to the base in an even number of steps.
0848         auto sub_begin = blossom.subblossoms.begin();
0849         auto sub_end = blossom.subblossoms.end();
0850         auto sub_it = entry_it;
0851         while ((sub_it != sub_begin) && (sub_it != sub_end)) {
0852             vertex_t x, y;
0853             blossom_t* bx;
0854             blossom_t* by;
0855 
0856             if (entry_pos % 2 == 0)
0857             {
0858                 // Walk backward around the blossom.
0859                 --sub_it;
0860                 by = sub_it->blossom;
0861                 BOOST_ASSERT(sub_it != sub_begin);
0862                 --sub_it;
0863                 bx = sub_it->blossom;
0864                 std::tie(x, y) = sub_it->edge;
0865             }
0866             else
0867             {
0868                 // Walk forward around the blossom.
0869                 ++sub_it;
0870                 BOOST_ASSERT(sub_it != sub_end);
0871                 std::tie(x, y) = sub_it->edge;
0872                 bx = sub_it->blossom;
0873                 ++sub_it;
0874                 by = (sub_it == sub_end) ?
0875                     blossom.subblossoms.front().blossom :
0876                     sub_it->blossom;
0877             }
0878 
0879             // Pull this edge into the matching.
0880             vertex_mate[x] = y;
0881             vertex_mate[y] = x;
0882 
0883             // Augment through any non-trivial subblossoms touching this edge.
0884             auto bx_ntb = bx->nontrivial();
0885             if (bx_ntb)
0886                 stack.emplace(bx_ntb, &trivial_blossom[x]);
0887 
0888             auto by_ntb = by->nontrivial();
0889             if (by_ntb)
0890                 stack.emplace(by_ntb, &trivial_blossom[y]);
0891         }
0892 
0893         // Re-orient the blossom.
0894         if (entry_it != sub_begin)
0895             blossom.subblossoms.splice(
0896                 sub_begin, blossom.subblossoms, entry_it, sub_end);
0897         blossom.base_vertex = entry.base_vertex;
0898     }
0899 
0900     void augment_blossom(nontrivial_blossom_t& blossom, blossom_t& entry)
0901     {
0902         // Use an explicit stack to avoid deep call chains.
0903         std::stack<std::pair<nontrivial_blossom_t*, blossom_t*>> stack;
0904         stack.emplace(&blossom, &entry);
0905 
0906         while (!stack.empty()) {
0907             nontrivial_blossom_t* outer_blossom;
0908             blossom_t* inner_entry;
0909             std::tie(outer_blossom, inner_entry) = stack.top();
0910 
0911             nontrivial_blossom_t* inner_blossom = inner_entry->parent;
0912             BOOST_ASSERT(inner_blossom);
0913 
0914             if (inner_blossom != outer_blossom)
0915                 stack.top() = std::make_pair(outer_blossom, inner_blossom);
0916             else
0917                 stack.pop();
0918 
0919             augment_blossom_rec(*inner_blossom, *inner_entry, stack);
0920         }
0921     }
0922 
0923     /** Augment the matching through the specified augmenting path. */
0924     void augment_matching(const alternating_path_t& path)
0925     {
0926         BOOST_ASSERT(path.size() % 2 == 1);
0927 
0928         // Process the unmatched edges on the augmenting path.
0929         auto path_it = path.begin();
0930         auto path_end = path.end();
0931         while (path_it != path_end)
0932         {
0933             vertex_t x = path_it->first;
0934             vertex_t y = path_it->second;
0935 
0936             // Augment any non-trivial blossoms that touch this edge.
0937             blossom_t* bx = vertex_top_blossom[x];
0938             auto bx_ntb = bx->nontrivial();
0939             if (bx_ntb)
0940                 augment_blossom(*bx_ntb, trivial_blossom[x]);
0941 
0942             blossom_t* by = vertex_top_blossom[y];
0943             auto by_ntb = by->nontrivial();
0944             if (by_ntb)
0945                 augment_blossom(*by_ntb, trivial_blossom[y]);
0946 
0947             // Pull this edge into the matching.
0948             vertex_mate[x] = y;
0949             vertex_mate[y] = x;
0950 
0951             // Go to the next unmatched edge on the path.
0952             ++path_it;
0953             if (path_it == path_end)
0954                 break;
0955             ++path_it;
0956         }
0957     }
0958 
0959     /**
0960      * Remove non-S-vertices from the scan queue.
0961      * This is necessary after removing labels from S-blossoms.
0962      */
0963     void refresh_scan_queue()
0964     {
0965         std::deque<vertex_t> new_scan_queue;
0966         for (const vertex_t& x : scan_queue)
0967         {
0968             if (vertex_top_blossom[x]->label == LABEL_S)
0969                 new_scan_queue.push_back(x);
0970         }
0971         scan_queue = std::move(new_scan_queue);
0972     }
0973 
0974     /** Remove edges to non-S-vertices from delta3 edge tracking. */
0975     void refresh_delta3_blossom(blossom_t& b)
0976     {
0977         // Do nothing if this blossom was not tracking any delta3 edge.
0978         if (!b.best_edge.has_value())
0979             return;
0980 
0981         auto ntb = b.nontrivial();
0982         if (ntb)
0983         {
0984             // Remove bad edges from best_edge_set and refresh best_edge.
0985             least_slack_edge_t best_edge;
0986             auto it = ntb->best_edge_set.begin();
0987             auto it_end = ntb->best_edge_set.end();
0988             while (it != it_end)
0989             {
0990                 vertex_t y = target(*it, *g);
0991                 blossom_t* by = vertex_top_blossom[y];
0992                 BOOST_ASSERT(by != &b);
0993                 if (by->label == LABEL_S)
0994                 {
0995                     best_edge.update(*it, edge_slack(*it));
0996                     ++it;
0997                 }
0998                 else
0999                 {
1000                     // Remove edge to non-S-blossom.
1001                     it = ntb->best_edge_set.erase(it);
1002                 }
1003             }
1004             b.best_edge = best_edge.edge;
1005         }
1006         else
1007         {
1008             // Trivial blossom does not maintain best_edge_set.
1009             // If its current best_edge is invalid, recompute it.
1010             vertex_t x = b.base_vertex;
1011             vertex_t y = target(*b.best_edge, *g);
1012             blossom_t* by = vertex_top_blossom[y];
1013             BOOST_ASSERT(by != &b);
1014             if (by->label != LABEL_S)
1015             {
1016                 // Consider all incident edges to recompute best_edge.
1017                 least_slack_edge_t best_edge;
1018                 for (const edge_t& e : make_iterator_range(out_edges(x, *g)))
1019                 {
1020                     BOOST_ASSERT(source(e, *g) == x);
1021                     y = target(e, *g);
1022                     by = vertex_top_blossom[y];
1023                     if ((by != &b) && (by->label == LABEL_S))
1024                         best_edge.update(e, edge_slack(e));
1025                 }
1026                 b.best_edge = best_edge.edge;
1027             }
1028         }
1029     }
1030 
1031     /** Recompute vertex_best_edge for the specified vertex. */
1032     void recompute_vertex_best_edge(vertex_t x)
1033     {
1034         least_slack_edge_t best_edge;
1035 
1036         for (const edge_t& e : make_iterator_range(out_edges(x, *g)))
1037         {
1038             BOOST_ASSERT(source(e, *g) == x);
1039             vertex_t y = target(e, *g);
1040             blossom_t* by = vertex_top_blossom[y];
1041             if (by->label == LABEL_S)
1042                 best_edge.update(e, edge_slack(e));
1043         }
1044         vertex_best_edge[x] = best_edge.edge;
1045     }
1046 
1047     /** Remove the alternating trees with specified root vertices. */
1048     void remove_alternating_tree(vertex_t r1, vertex_t r2)
1049     {
1050         // Find blossoms that are part of the specified alternating trees.
1051         std::vector<blossom_t*> former_s_blossoms;
1052         for (vertex_t x : make_iterator_range(vertices(*g)))
1053         {
1054             blossom_t* b = vertex_top_blossom[x];
1055             if ((!b->parent)
1056                 && (b->label != LABEL_NONE)
1057                 && (b->tree_root == r1 || b->tree_root == r2))
1058             {
1059                 // Build list of former S-blossoms.
1060                 if (b->label == LABEL_S)
1061                     former_s_blossoms.push_back(b);
1062 
1063                 // Remove label.
1064                 b->label = LABEL_NONE;
1065             }
1066         }
1067 
1068         vertex_map<char> blossom_recompute_best_edge(num_vertices(*g), vm);
1069         vertex_map<char> vertex_recompute_best_edge(num_vertices(*g), vm);
1070 
1071         // Visit former S-blossoms.
1072         for (blossom_t* b : former_s_blossoms)
1073         {
1074             // Clear best_edge tracking.
1075             b->best_edge.reset();
1076             auto ntb = b->nontrivial();
1077             if (ntb)
1078                 ntb->best_edge_set.clear();
1079 
1080             // Visit edges that touch this blossom.
1081             for_vertices_in_blossom(*b,
1082                 [&](vertex_t x)
1083                 {
1084                     // Mark former S-vertices.
1085                     vertex_recompute_best_edge[x] = 1;
1086 
1087                     for (const edge_t& e :
1088                          make_iterator_range(out_edges(x, *g)))
1089                     {
1090                         // Mark S-blossoms adjacent to the former S-blossom.
1091                         BOOST_ASSERT(source(e, *g) == x);
1092                         vertex_t y = target(e, *g);
1093                         blossom_t* by = vertex_top_blossom[y];
1094                         if (by->label == LABEL_S)
1095                             blossom_recompute_best_edge[by->base_vertex] = 1;
1096 
1097                         // Mark non-S-vertices with least-slack edge to
1098                         // former S-blossom.
1099                         if (by->label != LABEL_S)
1100                         {
1101                             const auto& best_edge = vertex_best_edge[y];
1102                             if (best_edge.has_value() && (*best_edge == e))
1103                                 vertex_recompute_best_edge[y] = 1;
1104                         }
1105                     }
1106                 });
1107         }
1108 
1109         // Recompute delta3 tracking of affected S-blossoms.
1110         for (vertex_t x : make_iterator_range(vertices(*g)))
1111         {
1112             if (blossom_recompute_best_edge[x])
1113                 refresh_delta3_blossom(*vertex_top_blossom[x]);
1114         }
1115 
1116         // Recompute vertex_best_edge of affected non-S-vertices.
1117         for (vertex_t x : make_iterator_range(vertices(*g)))
1118         {
1119             if (vertex_recompute_best_edge[x])
1120                 recompute_vertex_best_edge(x);
1121         }
1122 
1123         refresh_scan_queue();
1124     }
1125 
1126     /**
1127      * Extend the alternating tree via the edge from S-vertex "x"
1128      * to unlabeled vertex "y".
1129      *
1130      * Assign label T to the blossom that contains "y", then assign
1131      * label S to the blossom matched to the newly labeled T-blossom.
1132      */
1133     void extend_tree_s_to_t(vertex_t x, vertex_t y)
1134     {
1135         blossom_t* bx = vertex_top_blossom[x];
1136         blossom_t* by = vertex_top_blossom[y];
1137 
1138         BOOST_ASSERT(bx->label == LABEL_S);
1139         BOOST_ASSERT(by->label == LABEL_NONE);
1140         by->label = LABEL_T;
1141         by->tree_edge = std::make_pair(x, y);
1142         by->tree_root = bx->tree_root;
1143 
1144         vertex_t y2 = by->base_vertex;
1145         vertex_t z = vertex_mate[y2];
1146         BOOST_ASSERT(z != null_vertex());
1147 
1148         blossom_t* bz = vertex_top_blossom[z];
1149         BOOST_ASSERT(bz->label == LABEL_NONE);
1150         BOOST_ASSERT(!bz->best_edge.has_value());
1151         bz->label = LABEL_S;
1152         bz->tree_edge = std::make_pair(y2, z);
1153         bz->tree_root = by->tree_root;
1154         add_vertices_to_scan_queue(*bz);
1155     }
1156 
1157     /**
1158      * Add the edge between S-vertices "x" and "y".
1159      *
1160      * If the edge connects blossoms that are part of the same alternating
1161      * tree, a new S-blossom is created.
1162      *
1163      * If the edge connects two different alternating trees, an augmenting
1164      * path has been discovered. In this case the matching is augmented.
1165      *
1166      * @return true if the matching was augmented; otherwise false.
1167      */
1168     bool add_s_to_s_edge(vertex_t x, vertex_t y)
1169     {
1170         blossom_t* bx = vertex_top_blossom[x];
1171         blossom_t* by = vertex_top_blossom[y];
1172         BOOST_ASSERT(bx->label == LABEL_S);
1173         BOOST_ASSERT(by->label == LABEL_S);
1174         BOOST_ASSERT(bx != by);
1175 
1176         alternating_path_t path = trace_alternating_paths(x, y);
1177 
1178         // Check whether the path starts and ends in the same blossom.
1179         blossom_t* b1 = vertex_top_blossom[path.front().first];
1180         blossom_t* b2 = vertex_top_blossom[path.back().second];
1181         if (b1 == b2)
1182         {
1183             make_blossom(path);
1184             return false;
1185         }
1186         else
1187         {
1188             // Remove labels the two alternating trees on the augmenting path.
1189             remove_alternating_tree(bx->tree_root, by->tree_root);
1190 
1191             // Augment matching.
1192             augment_matching(path);
1193             return true;
1194         }
1195     }
1196 
1197     /**
1198      * Scan incident edges of newly labeled S-vertices.
1199      *
1200      * @return true if the matching was augmented; otherwise false.
1201      */
1202     bool scan_new_s_vertices()
1203     {
1204         while (!scan_queue.empty())
1205         {
1206             vertex_t x = scan_queue.front();
1207             scan_queue.pop_front();
1208 
1209             BOOST_ASSERT(vertex_top_blossom[x]->label == LABEL_S);
1210 
1211             for (const edge_t& e : make_iterator_range(out_edges(x, *g)))
1212             {
1213                 BOOST_ASSERT(source(e, *g) == x);
1214                 vertex_t y = target(e, *g);
1215 
1216                 // Note: top level blossom of x may change during this loop,
1217                 // so we must look it up on each pass.
1218                 blossom_t* bx = vertex_top_blossom[x];
1219                 blossom_t* by = vertex_top_blossom[y];
1220 
1221                 // Ignore internal edges in blossom.
1222                 if (bx == by)
1223                     continue;
1224 
1225                 // Ignore edges with negative slack to prevent numeric overflow.
1226                 if (get(edge_weights, e) < 0)
1227                     continue;
1228 
1229                 weight_t slack = edge_slack(e);
1230                 if (slack <= 0)
1231                 {
1232                     // Tight edge.
1233                     if (by->label == LABEL_NONE)
1234                         extend_tree_s_to_t(x, y);
1235                     else if (by->label == LABEL_S)
1236                     {
1237                         bool augmented = add_s_to_s_edge(x, y);
1238                         if (augmented)
1239                             return true;
1240                     }
1241                 }
1242                 else
1243                 {
1244                     // Track non-tight edges between S-blossoms.
1245                     if (by->label == LABEL_S)
1246                         add_delta3_edge(*bx, e, slack);
1247                 }
1248 
1249                 // Track all edges between S-blossom and non-S-blossom.
1250                 if (by->label != LABEL_S)
1251                     add_delta2_edge(y, e, slack);
1252             }
1253         }
1254 
1255         return false;
1256     }
1257 
1258     /** Calculate a delta step in the dual LPP problem. */
1259     delta_step_t calc_dual_delta_step()
1260     {
1261         delta_step_t delta{};
1262 
1263         // Compute delta1: minimum dual variable of any S-vertex.
1264         delta.kind = 1;
1265         delta.value = (std::numeric_limits<weight_t>::max)();
1266         for (vertex_t x : make_iterator_range(vertices(*g)))
1267         {
1268             if (vertex_top_blossom[x]->label == LABEL_S)
1269                 delta.value = (std::min)(delta.value, vertex_dual[x]);
1270         }
1271 
1272         // Compute delta2: minimum slack of edge from S-vertex to unlabeled.
1273         least_slack_edge_t best_edge = get_least_slack_delta2_edge();
1274         if (best_edge.edge.has_value() && (best_edge.slack <= delta.value))
1275         {
1276             delta.kind = 2;
1277             delta.edge = *best_edge.edge;
1278             delta.value = best_edge.slack;
1279         }
1280 
1281         // Compute delta3: half minimum slack of edge between S-blossoms.
1282         best_edge = get_least_slack_delta3_edge();
1283         if (best_edge.edge.has_value() && (best_edge.slack / 2 <= delta.value))
1284         {
1285             delta.kind = 3;
1286             delta.edge = *best_edge.edge;
1287             delta.value = best_edge.slack / 2;
1288         }
1289 
1290         // Compute delta4: half minimum dual of a top-level T-blossom.
1291         for (nontrivial_blossom_t& blossom : nontrivial_blossom)
1292         {
1293             if ((!blossom.parent)
1294                 && (blossom.label == LABEL_T)
1295                 && (blossom.dual_var / 2 <= delta.value))
1296             {
1297                 delta.kind = 4;
1298                 delta.blossom = &blossom;
1299                 delta.value = blossom.dual_var / 2;
1300             }
1301         }
1302 
1303         return delta;
1304     }
1305 
1306     /** Apply a delta step to the dual LPP variables. */
1307     void apply_delta_step(weight_t delta)
1308     {
1309         for (vertex_t x : make_iterator_range(vertices(*g)))
1310         {
1311             blossom_t* b = vertex_top_blossom[x];
1312             if (b->label == LABEL_S)
1313                 vertex_dual[x] -= delta;
1314             else if (b->label == LABEL_T)
1315                 vertex_dual[x] += delta;
1316         }
1317 
1318         for (nontrivial_blossom_t& blossom : nontrivial_blossom)
1319         {
1320             if (!blossom.parent)
1321             {
1322                 if (blossom.label == LABEL_S)
1323                     blossom.dual_var += 2 * delta;
1324                 else if (blossom.label == LABEL_T)
1325                     blossom.dual_var -= 2 * delta;
1326             }
1327         }
1328     }
1329 
1330     /**
1331      * Run one stage of the matching algorithm.
1332      *
1333      * @return True if the matching was successfully augmented;
1334      *         false if no further improvement is possible.
1335      */
1336     bool run_stage()
1337     {
1338         // Run substages.
1339         while (true) {
1340             bool augmented = scan_new_s_vertices();
1341             if (augmented)
1342                 return true;
1343 
1344             delta_step_t delta = calc_dual_delta_step();
1345             apply_delta_step(delta.value);
1346 
1347             if (delta.kind == 2)
1348             {
1349                 vertex_t x = source(delta.edge, *g);
1350                 vertex_t y = target(delta.edge, *g);
1351                 if (vertex_top_blossom[x]->label != LABEL_S)
1352                     std::swap(x, y);
1353                 extend_tree_s_to_t(x, y);
1354             }
1355             else if (delta.kind == 3)
1356             {
1357                 vertex_t x = source(delta.edge, *g);
1358                 vertex_t y = target(delta.edge, *g);
1359                 augmented = add_s_to_s_edge(x, y);
1360                 if (augmented)
1361                     return true;
1362             }
1363             else if (delta.kind == 4)
1364             {
1365                 BOOST_ASSERT(delta.blossom);
1366                 expand_t_blossom(delta.blossom);
1367             }
1368             else
1369             {
1370                 // No further improvement possible.
1371                 BOOST_ASSERT(delta.kind == 1);
1372                 return false;
1373             }
1374         }
1375     }
1376 
1377     /** Run the matching algorithm. */
1378     void run()
1379     {
1380         // Assign label S to all vertices and put them in the queue.
1381         for (vertex_t x : make_iterator_range(vertices(*g)))
1382         {
1383             BOOST_ASSERT(vertex_mate[x] == null_vertex());
1384             blossom_t* bx = vertex_top_blossom[x];
1385             BOOST_ASSERT(bx->label == LABEL_NONE);
1386             BOOST_ASSERT(bx->base_vertex == x);
1387             bx->label = LABEL_S;
1388             bx->tree_edge = std::make_pair(null_vertex(), x);
1389             bx->tree_root = x;
1390             scan_queue.push_back(x);
1391         }
1392 
1393         // Improve the solution until no further improvement is possible.
1394         while (run_stage()) ;
1395 
1396         // Clear temporary data structures.
1397         scan_queue.clear();
1398         clear_best_edge();
1399     }
1400 
1401     /** Copy matching to specified map. */
1402     template <typename MateMap>
1403     void extract_matching(MateMap mate)
1404     {
1405         for (vertex_t x : make_iterator_range(vertices(*g)))
1406             put(mate, x, vertex_mate[x]);
1407     }
1408 };
1409 
1410 } // namespace detail
1411 } // namespace graph
1412 
1413 /**
1414  * Compute a maximum-weighted matching in an undirected weighted graph.
1415  *
1416  * This function takes time O(V^3).
1417  * This function uses memory size O(V + E).
1418  *
1419  * @param g         Input graph.
1420  *                  The graph type must be a model of VertexListGraph
1421  *                  and EdgeListGraph and IncidenceGraph.
1422  *                  The graph must be undirected.
1423  *                  The graph may not contain parallel edges.
1424  *
1425  * @param mate      ReadWritePropertyMap, mapping vertices to vertices.
1426  *                  This map returns the result of the computation.
1427  *                  For each vertex "v", "mate[v]" is the vertex that is
1428  *                  matched to "v", or "null_vertex()" if "v" is not matched.
1429  *
1430  * @param vm        ReadablePropertyMap, mapping vertices to indexes
1431  *                  in range [0, num_vertices(g)).
1432  *
1433  * @param weights   ReadablePropertyMap, mapping edges to edge weights.
1434  *                  Edge weights must be integers or floating point values.
1435  *
1436  * @throw boost::bad_graph  If the input graph is not valid.
1437  */
1438 template <typename Graph, typename MateMap, typename VertexIndexMap,
1439     typename EdgeWeightMap>
1440 void maximum_weighted_matching(
1441     const Graph& g, MateMap mate, VertexIndexMap vm, EdgeWeightMap weights)
1442 {
1443     BOOST_CONCEPT_ASSERT((VertexListGraphConcept<Graph>));
1444     BOOST_CONCEPT_ASSERT((EdgeListGraphConcept<Graph>));
1445     BOOST_CONCEPT_ASSERT((IncidenceGraphConcept<Graph>));
1446     BOOST_STATIC_ASSERT(is_undirected_graph<Graph>::value);
1447 
1448     using vertex_t = typename graph_traits<Graph>::vertex_descriptor;
1449     using edge_t = typename graph_traits<Graph>::edge_descriptor;
1450     BOOST_CONCEPT_ASSERT((ReadWritePropertyMapConcept<MateMap, vertex_t>));
1451     BOOST_CONCEPT_ASSERT(
1452         (ReadablePropertyMapConcept<VertexIndexMap, vertex_t>));
1453     BOOST_CONCEPT_ASSERT((ReadablePropertyMapConcept<EdgeWeightMap, edge_t>));
1454 
1455     graph::detail::check_vertex_index_range(g, vm);
1456     graph::detail::check_maximum_weighted_matching_edge_weights(g, weights);
1457 
1458     graph::detail::maximum_weighted_matching_context<
1459         Graph, VertexIndexMap, EdgeWeightMap>
1460         matching(g, vm, weights);
1461     matching.run();
1462     matching.extract_matching(mate);
1463 }
1464 
1465 /**
1466  * Compute a maximum-weighted matching in an undirected weighted graph.
1467  *
1468  * This overloaded function obtains edge weights from "get(edge_weight, g)".
1469  */
1470 template <typename Graph, typename MateMap, typename VertexIndexMap>
1471 inline void maximum_weighted_matching(
1472     const Graph& g, MateMap mate, VertexIndexMap vm)
1473 {
1474     maximum_weighted_matching(g, mate, vm, get(edge_weight, g));
1475 }
1476 
1477 /**
1478  * Compute a maximum-weighted matching in an undirected weighted graph.
1479  *
1480  * This overloaded function obtains vertex indices from "get(vertex_index, g)"
1481  * and edge weights from "get(edge_weight, g)".
1482  */
1483 template <typename Graph, typename MateMap>
1484 inline void maximum_weighted_matching(const Graph& g, MateMap mate)
1485 {
1486     maximum_weighted_matching(g, mate, get(vertex_index, g));
1487 }
1488 
1489 } // namespace boost
1490 
1491 #endif // BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP