mirror of
https://github.com/boostorg/graph.git
synced 2025-05-09 23:14:00 +00:00
Remove verification code
This commit is contained in:
parent
26efe6629e
commit
13c5fd7011
@ -21,7 +21,7 @@ void maximum_weighted_matching(const Graph& g, MateMap mate);
|
||||
template <typename Graph, typename MateMap, typename VertexIndexMap>
|
||||
void maximum_weighted_matching(const Graph& g, MateMap mate, VertexIndexMap vm);
|
||||
|
||||
template <typename Graph, typename MateMap, typename VertexIndexMap, typename EdgeWeightMap, bool Verify = true>
|
||||
template <typename Graph, typename MateMap, typename VertexIndexMap, typename EdgeWeightMap>
|
||||
void maximum_weighted_matching(const Graph& g, MateMap mate, VertexIndexMap vm, EdgeWeightMap weights);
|
||||
|
||||
template <typename Graph, typename MateMap>
|
||||
@ -121,13 +121,6 @@ If edge weights are integers, the algorithm uses only integer computations.
|
||||
This is achieved by internally multiplying edge weights by 2, which ensures that all dual variables are also integers.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After computing the maximum matching, a verification step may be performed to check that the matching is optimal.
|
||||
The verification step is simpler and faster (<i>O(V<sup>2</sup>)</i>) than the search for the matching.
|
||||
It always succeeds unless there is a bug in the matching algorithm.
|
||||
Since verification checks for exact optimality, it can only be used if edge weights are integers.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In addition, a brute-force implementation <tt>brute_force_maximum_weighted_matching</tt> is provided.
|
||||
This algorithm simply searches all possible matchings and selects one with the maximum weight sum.
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <tuple> // for std::tie
|
||||
#include <utility> // for std::pair, std::swap
|
||||
#include <vector>
|
||||
@ -1405,235 +1404,6 @@ struct maximum_weighted_matching_context
|
||||
for (vertex_t x : make_iterator_range(vertices(g)))
|
||||
put(mate, x, vertex_mate[x]);
|
||||
}
|
||||
|
||||
/** Check that the array "vertex_mate" is consistent. */
|
||||
bool verify_vertex_mate()
|
||||
{
|
||||
vertices_size_t num_matched_vertex = 0;
|
||||
for (vertex_t x : make_iterator_range(vertices(g)))
|
||||
{
|
||||
vertex_t y = vertex_mate[x];
|
||||
if (y != null_vertex)
|
||||
{
|
||||
++num_matched_vertex;
|
||||
if (vertex_mate[y] != x)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
edges_size_t num_matched_edge = 0;
|
||||
for (const edge_t& e : make_iterator_range(edges(g)))
|
||||
{
|
||||
if (vertex_mate[source(e, g)] == target(e, g))
|
||||
++num_matched_edge;
|
||||
}
|
||||
|
||||
return num_matched_vertex == 2 * num_matched_edge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that vertex dual variables are non-negative,
|
||||
* and all unmatched vertices have zero dual.
|
||||
*/
|
||||
bool verify_vertex_duals()
|
||||
{
|
||||
for (vertex_t x : make_iterator_range(vertices(g)))
|
||||
{
|
||||
if (vertex_dual[x] < 0)
|
||||
return false;
|
||||
if ((vertex_mate[x] == null_vertex) && (vertex_dual[x] != 0))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Verify that blossom dual variables are non-negative. */
|
||||
bool verify_blossom_duals()
|
||||
{
|
||||
for (const nontrivial_blossom_t& blossom : nontrivial_blossom)
|
||||
{
|
||||
if (blossom.dual_var < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Verify slack of edges between trivial blossoms. */
|
||||
bool verify_trivial_blossom_edges()
|
||||
{
|
||||
for (const edge_t& e : make_iterator_range(edges(g)))
|
||||
{
|
||||
vertex_t x = source(e, g);
|
||||
vertex_t y = target(e, g);
|
||||
if ((! trivial_blossom[x].parent) && (! trivial_blossom[y].parent))
|
||||
{
|
||||
weight_t w = weight_factor * get(edge_weights, e);
|
||||
weight_t dual_sum = vertex_dual[x] + vertex_dual[y];
|
||||
// Edge slack must be non-negative.
|
||||
if (w > dual_sum)
|
||||
return false;
|
||||
// Matched edges must have slack zero.
|
||||
if ((vertex_mate[x] == y) && (w != dual_sum))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Verify one top-level blossom and its edges. */
|
||||
bool verify_blossom(const nontrivial_blossom_t* blossom)
|
||||
{
|
||||
// This function recursively descends down the blossom structure.
|
||||
// It checks the number of matched edges in every sub-blossom.
|
||||
// It also checks the slack of edges contained in this blossom
|
||||
// and of edges incident on this blossom.
|
||||
|
||||
// For each vertex, deepest level on current recursion path
|
||||
// that contains the vertex.
|
||||
vertex_map< vertices_size_t > vertex_depth(num_vertices(g), vm);
|
||||
|
||||
// At each recursion depth, sum of duals of containing blossoms.
|
||||
std::vector< weight_t > path_sum_dual = {0};
|
||||
|
||||
// At each recursion depth, number of internally matched vertices.
|
||||
std::vector< vertices_size_t > path_num_matched = {0};
|
||||
|
||||
// Use an explicit stack to avoid deep call chains.
|
||||
using sub_blossom_iterator = typename std::list<
|
||||
typename nontrivial_blossom_t::sub_blossom_t >::const_iterator;
|
||||
std::stack< std::pair<
|
||||
const nontrivial_blossom_t*, sub_blossom_iterator > > stack;
|
||||
|
||||
stack.emplace(blossom, blossom->subblossoms.cbegin());
|
||||
while (! stack.empty())
|
||||
{
|
||||
vertices_size_t depth = stack.size();
|
||||
blossom = stack.top().first;
|
||||
auto subblossom_it = stack.top().second;
|
||||
|
||||
if (subblossom_it == blossom->subblossoms.cbegin())
|
||||
{
|
||||
// Entering a new (sub)-blossom.
|
||||
if (blossom->subblossoms.size() < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the depth of vertices in this sub-blossom.
|
||||
for_vertices_in_blossom(blossom,
|
||||
[&vertex_depth,depth](vertex_t x) {
|
||||
vertex_depth[x] = depth;
|
||||
});
|
||||
|
||||
// Calculate the sum of blossom duals at the new depth.
|
||||
path_sum_dual.push_back(
|
||||
path_sum_dual.back() + blossom->dual_var);
|
||||
|
||||
// Initialize the number of matched edges at the new depth.
|
||||
path_num_matched.push_back(0);
|
||||
}
|
||||
|
||||
if (subblossom_it != blossom->subblossoms.cend())
|
||||
{
|
||||
// Update the sub-blossom pointer at the current depth.
|
||||
++(stack.top().second);
|
||||
|
||||
// Examine a sub-blossom.
|
||||
blossom_t* b = subblossom_it->blossom;
|
||||
BOOST_ASSERT(b->parent == blossom);
|
||||
nontrivial_blossom_t* ntb = b->nontrivial();
|
||||
if (ntb)
|
||||
{
|
||||
// Prepare to descend into the sub-blossom.
|
||||
stack.emplace(ntb, ntb->subblossoms.cbegin());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle single vertex.
|
||||
vertex_t x = b->base_vertex;
|
||||
for (const edge_t& e : make_iterator_range(out_edges(x, g)))
|
||||
{
|
||||
BOOST_ASSERT(source(e, g) == x);
|
||||
vertex_t y = target(e, g);
|
||||
// Find the smallest blossom that contains this edge.
|
||||
vertices_size_t edge_depth = vertex_depth[y];
|
||||
// Verify edge slack.
|
||||
weight_t w = weight_factor * get(edge_weights, e);
|
||||
weight_t dual_sum = vertex_dual[x] + vertex_dual[y]
|
||||
+ path_sum_dual[edge_depth];
|
||||
// Edge slack must be non-negative.
|
||||
if (w > dual_sum)
|
||||
return false;
|
||||
// Matched edges must have slack zero.
|
||||
if ((vertex_mate[x] == y) && (w != dual_sum))
|
||||
return false;
|
||||
// Update number of internally matched vertices.
|
||||
if ((vertex_mate[x] == y) && (edge_depth > 0))
|
||||
path_num_matched[edge_depth] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leaving the current (sub)-blossom.
|
||||
// Count the number of vertices inside this blossom.
|
||||
vertices_size_t blossom_num_vertex = 0;
|
||||
for_vertices_in_blossom(blossom,
|
||||
[&blossom_num_vertex](vertex_t) {
|
||||
++blossom_num_vertex;
|
||||
});
|
||||
|
||||
// Check that the (sub)-blossom is "full".
|
||||
// A blossom is full if all except one of its vertices
|
||||
// are matched to another vertex within the blossom.
|
||||
vertices_size_t blossom_num_matched = path_num_matched[depth];
|
||||
if (blossom_num_vertex != blossom_num_matched + 1)
|
||||
return false;
|
||||
|
||||
// Update the number of matched edges in the parent blossom.
|
||||
path_num_matched[depth - 1] += path_num_matched[depth];
|
||||
|
||||
// Restore the depth of vertices.
|
||||
for_vertices_in_blossom(blossom,
|
||||
[&vertex_depth,depth](vertex_t x) {
|
||||
vertex_depth[x] = depth - 1;
|
||||
});
|
||||
|
||||
// Remove the blossom from the stack.
|
||||
path_sum_dual.pop_back();
|
||||
path_num_matched.pop_back();
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Verify blossoms and their edges. */
|
||||
bool verify_blossoms()
|
||||
{
|
||||
// Recursively verify each non-trivial top-level blossom.
|
||||
// This takes total time O(V^2).
|
||||
for (const nontrivial_blossom_t& blossom : nontrivial_blossom)
|
||||
{
|
||||
if (! blossom.parent)
|
||||
{
|
||||
if (! verify_blossom(&blossom))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Verify that the optimum solution has been found. */
|
||||
bool verify()
|
||||
{
|
||||
return (verify_vertex_mate()
|
||||
&& verify_vertex_duals()
|
||||
&& verify_blossom_duals()
|
||||
&& verify_trivial_blossom_edges()
|
||||
&& verify_blossoms());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
@ -1645,11 +1415,6 @@ struct maximum_weighted_matching_context
|
||||
* This function takes time O(V^3).
|
||||
* This function uses memory size O(V + E).
|
||||
*
|
||||
* @tparam Verify True to verify the matching.
|
||||
* This is only supported for integer edge weights.
|
||||
* Verification will always succeed unless there is a bug
|
||||
* in the matching algorithm.
|
||||
*
|
||||
* @param g Input graph.
|
||||
* The graph type must be a model of VertexListGraph
|
||||
* and EdgeListGraph and IncidenceGraph.
|
||||
@ -1670,7 +1435,7 @@ struct maximum_weighted_matching_context
|
||||
* @throw boost::bad_graph If the input graph is not valid.
|
||||
*/
|
||||
template < typename Graph, typename MateMap, typename VertexIndexMap,
|
||||
typename EdgeWeightMap, bool Verify = true >
|
||||
typename EdgeWeightMap >
|
||||
void maximum_weighted_matching(
|
||||
const Graph& g, MateMap mate, VertexIndexMap vm, EdgeWeightMap weights)
|
||||
{
|
||||
@ -1695,14 +1460,6 @@ void maximum_weighted_matching(
|
||||
Graph, VertexIndexMap, EdgeWeightMap >
|
||||
matching(g, vm, weights);
|
||||
matching.run();
|
||||
|
||||
typedef typename property_traits< EdgeWeightMap >::value_type weight_t;
|
||||
if (Verify && std::numeric_limits< weight_t >::is_integer)
|
||||
{
|
||||
if (! matching.verify())
|
||||
throw std::logic_error("Incorrect solution.");
|
||||
}
|
||||
|
||||
matching.extract_matching(mate);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user