Keep alternating trees between stages in C++
This commit is contained in:
parent
082397ef80
commit
3815335a9f
238
cpp/mwmatching.h
238
cpp/mwmatching.h
|
@ -319,7 +319,8 @@ struct Blossom
|
|||
/** Optional edge that attaches this blossom to the alternating tree. */
|
||||
VertexPair tree_edge;
|
||||
|
||||
// TODO -- tree_blossoms
|
||||
/** Base vertex of the blossom at the root of the alternating tree. */
|
||||
VertexId tree_root;
|
||||
|
||||
/** Concatenable queue containing all vertices in the blossom. */
|
||||
ConcatenableQueue<WeightType, Blossom*, EdgeId> vertex_queue;
|
||||
|
@ -340,6 +341,7 @@ protected:
|
|||
base_vertex(base_vertex),
|
||||
label(LABEL_NONE),
|
||||
is_nontrivial_blossom(is_nontrivial_blossom),
|
||||
tree_root(NO_VERTEX),
|
||||
vertex_queue(this),
|
||||
vertex_dual_offset(0)
|
||||
{ }
|
||||
|
@ -783,6 +785,46 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove edge "e" from delta2 tracking.
|
||||
*
|
||||
* Edge "e" connects vertices "x" and "y".
|
||||
* Vertex "x" was an S-vertex but now becomes unlabeled.
|
||||
* Vertex "y" is a T-vertex or unlabeled vertex.
|
||||
*
|
||||
* This function takes time O(log(n)).
|
||||
*/
|
||||
void delta2_remove_edge(int e, int y, BlossomT* by)
|
||||
{
|
||||
if (vertex_sedge_node[e].valid()) {
|
||||
// Delete edge from the S-edge queue.
|
||||
vertex_sedge_queue[y].remove(&vertex_sedge_node[e]);
|
||||
|
||||
// If necessary, update priority of "y" in its ConcatenableQueue.
|
||||
WeightType prio = INVALID_SLACK;
|
||||
if (! vertex_sedge_queue[y].empty()) {
|
||||
prio = vertex_sedge_queue[y].min_prio();
|
||||
}
|
||||
|
||||
if (prio > vertex_queue_node[y].prio()) {
|
||||
vertex_queue_node[y].set_prio(prio);
|
||||
if (by->label == LABEL_NONE) {
|
||||
// Update or delete blossom in the global delta2 queue.
|
||||
assert(by->delta2_node.valid());
|
||||
prio = by->vertex_queue.min_prio();
|
||||
if (prio < INVALID_SLACK) {
|
||||
prio += by->vertex_dual_offset;
|
||||
if (prio > by->delta2_node.prio()) {
|
||||
delta2_queue.set_prio(&by->delta2_node, prio);
|
||||
}
|
||||
} else {
|
||||
delta2_queue.remove(&by->delta2_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable delta2 tracking for "blossom".
|
||||
*
|
||||
|
@ -1047,12 +1089,36 @@ public:
|
|||
ntb->dual_var += 2 * delta_sum;
|
||||
}
|
||||
|
||||
// Adjust modified vertex duals to preserve true vertex dual.
|
||||
// Loop over vertices in the blossom.
|
||||
assert(blossom->vertex_dual_offset == 0);
|
||||
WeightType dual_fixup = -delta_sum;
|
||||
for_vertices_in_blossom(blossom,
|
||||
[this,dual_fixup](VertexId x) {
|
||||
[this,blossom,dual_fixup](VertexId x) {
|
||||
|
||||
// Adjust modified vertex dual to preserve true vertex dual.
|
||||
vertex_dual[x] += dual_fixup;
|
||||
|
||||
// Scan incident edges.
|
||||
for (EdgeId e : graph.adjacent_edges[x]) {
|
||||
const EdgeT& edge = graph.edges[e];
|
||||
VertexId y = (edge.vt.first != x) ? edge.vt.first
|
||||
: edge.vt.second;
|
||||
|
||||
// If this edge is in the delta3 queue, remove it.
|
||||
delta3_remove_edge(e);
|
||||
|
||||
BlossomT* by = top_level_blossom(y);
|
||||
if (by->label == LABEL_S) {
|
||||
// Edge "e" connects unlabeled vertex "x" to
|
||||
// S-vertex "y". It must be tracked for delta2.
|
||||
delta2_add_edge(e, x, blossom);
|
||||
} else {
|
||||
// Edge "e" connects former S-vertex "x" to
|
||||
// T-vertex or unlabeled vertex "y". This edge was
|
||||
// tracked for delta2 but must now be removed.
|
||||
delta2_remove_edge(e, y, by);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1117,6 +1183,33 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the alternating trees with specified root vertices.
|
||||
*
|
||||
* All blossoms that are part of these trees become unlabeled.
|
||||
* Delta tracking is updated accordingly.
|
||||
*
|
||||
* This function takes time O((n + m) * log(n)).
|
||||
*/
|
||||
void remove_alternating_tree(VertexId x, VertexId y)
|
||||
{
|
||||
auto do_blossom = [this,x,y](BlossomT* blossom) {
|
||||
if (blossom->label != LABEL_NONE) {
|
||||
assert(! blossom->parent);
|
||||
if ((blossom->tree_root == x) || (blossom->tree_root == y)) {
|
||||
reset_blossom_label(blossom);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (BlossomT& blossom : trivial_blossom) {
|
||||
do_blossom(&blossom);
|
||||
}
|
||||
for (BlossomT& blossom : nontrivial_blossom) {
|
||||
do_blossom(&blossom);
|
||||
}
|
||||
}
|
||||
|
||||
/* ********** Creating and expanding blossoms: ********** */
|
||||
|
||||
/**
|
||||
|
@ -1287,6 +1380,7 @@ public:
|
|||
// Assign label S to the new blossom and link to the alternating tree.
|
||||
blossom->label = LABEL_S;
|
||||
blossom->tree_edge = subblossoms.front()->tree_edge;
|
||||
blossom->tree_root = subblossoms.front()->tree_root;
|
||||
}
|
||||
|
||||
/** Erase the specified non-trivial blossom. */
|
||||
|
@ -1359,6 +1453,7 @@ public:
|
|||
// Assign label T to that blossom and link to the alternating tree.
|
||||
assign_blossom_label_t(entry);
|
||||
entry->tree_edge = blossom->tree_edge;
|
||||
entry->tree_root = blossom->tree_root;
|
||||
|
||||
// Find the position of this sub-blossom within the expanding blossom.
|
||||
auto subblossom_loc = blossom->find_subblossom(entry);
|
||||
|
@ -1380,8 +1475,10 @@ public:
|
|||
// Assign label T to the next node on the path.
|
||||
assert(sub_it != sub_begin);
|
||||
--sub_it;
|
||||
assign_blossom_label_t(sub_it->blossom);
|
||||
sub_it->blossom->tree_edge = flip_vertex_pair(sub_it->edge);
|
||||
BlossomT* sub_blossom = sub_it->blossom;
|
||||
assign_blossom_label_t(sub_blossom);
|
||||
sub_blossom->tree_edge = flip_vertex_pair(sub_it->edge);
|
||||
sub_blossom->tree_root = blossom->tree_root;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -1404,6 +1501,7 @@ public:
|
|||
sub_it->blossom;
|
||||
assign_blossom_label_t(sub_blossom);
|
||||
sub_blossom->tree_edge = tree_edge;
|
||||
sub_blossom->tree_root = blossom->tree_root;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1596,24 +1694,16 @@ public:
|
|||
BlossomT* bx = top_level_blossom(x);
|
||||
assign_blossom_label_s(bx);
|
||||
|
||||
// Vertex "x" is matched to T-vertex "y".
|
||||
VertexId y = vertex_mate[x];
|
||||
if (y == NO_VERTEX) {
|
||||
// Vertex "x" is unmatched.
|
||||
// It must be either a top-level vertex or the base vertex of
|
||||
// a top-level blossom.
|
||||
assert(bx->base_vertex == x);
|
||||
assert(y != NO_VERTEX);
|
||||
|
||||
// Mark the blossom as root of an alternating tree.
|
||||
bx->tree_edge = std::make_pair(NO_VERTEX, x);
|
||||
BlossomT* by = top_level_blossom(y);
|
||||
assert(by->label == LABEL_T);
|
||||
|
||||
} else {
|
||||
// Vertex "x" is matched to T-vertex "y".
|
||||
BlossomT* by = top_level_blossom(y);
|
||||
assert(by->label == LABEL_T);
|
||||
|
||||
// Attach the blossom to the alternating tree via vertex "y".
|
||||
bx->tree_edge = std::make_pair(y, x);
|
||||
}
|
||||
// Attach the blossom to the alternating tree via vertex "y".
|
||||
bx->tree_edge = std::make_pair(y, x);
|
||||
bx->tree_root = by->tree_root;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1633,12 +1723,13 @@ public:
|
|||
*/
|
||||
void extend_tree_s_to_t(VertexId x, VertexId y)
|
||||
{
|
||||
assert(top_level_blossom(x)->label == LABEL_S);
|
||||
BlossomT* bx = top_level_blossom(x);
|
||||
BlossomT* by = top_level_blossom(y);
|
||||
assert(bx->label == LABEL_S);
|
||||
|
||||
// If "y" is part of a zero-dual blossom, expand it.
|
||||
// This would otherwise likely happen through a zero-delta4 step,
|
||||
// so we can just do it now and avoid a substage.
|
||||
BlossomT* by = top_level_blossom(y);
|
||||
NonTrivialBlossomT* ntb = by->nontrivial();
|
||||
while (ntb != nullptr && ntb->dual_var == 0) {
|
||||
expand_unlabeled_blossom(ntb);
|
||||
|
@ -1649,6 +1740,7 @@ public:
|
|||
// Assign label T to the top-level blossom that contains vertex "y".
|
||||
assign_blossom_label_t(by);
|
||||
by->tree_edge = std::make_pair(x, y);
|
||||
by->tree_root = bx->tree_root;
|
||||
|
||||
// Assign label S to the blossom that is mated to the T-blossom.
|
||||
VertexId z = vertex_mate[by->base_vertex];
|
||||
|
@ -1682,17 +1774,22 @@ public:
|
|||
// Trace back through the alternating trees from "x" and "y".
|
||||
AlternatingPath path = trace_alternating_paths(x, y);
|
||||
|
||||
// If the path is a cycle, create a new blossom.
|
||||
// Otherwise the path is an augmenting path.
|
||||
// Note that an alternating starts and ends in the same blossom,
|
||||
// but not necessarily in the same vertex within that blossom.
|
||||
// TODO -- directly check whether both blossoms are in the same tree
|
||||
VertexId p = path.edges.front().first;
|
||||
VertexId q = path.edges.back().second;
|
||||
if (top_level_blossom(p) == top_level_blossom(q)) {
|
||||
if (bx->tree_root == by->tree_root) {
|
||||
// Both blossoms belong to the same alternating tree.
|
||||
// This implies that the alternating path is a cycle.
|
||||
// Use it to create a new blossom.
|
||||
make_blossom(path);
|
||||
return false;
|
||||
|
||||
} else {
|
||||
// The blossoms belong to different alternating trees.
|
||||
// This implies that the alternating path is an augmenting path.
|
||||
|
||||
// Remove labels from all blossoms in the two alternating trees
|
||||
// on the augmenting path.
|
||||
remove_alternating_tree(bx->tree_root, by->tree_root);
|
||||
|
||||
// Augment the matching along the alternating path.
|
||||
augment_matching(path);
|
||||
return true;
|
||||
}
|
||||
|
@ -1824,34 +1921,26 @@ public:
|
|||
/* ********** Main algorithm: ********** */
|
||||
|
||||
/**
|
||||
* Reset data which are only valid during a stage.
|
||||
* Assign label S to all vertices and create a separate alternating
|
||||
* tree rooted in each vertex.
|
||||
*
|
||||
* Mark all blossoms as unlabeled, clear the queue,
|
||||
* reset least-slack edge tracking.
|
||||
* This function takes time O(n + m).
|
||||
* It is called once, at the beginning of the algorithm.
|
||||
*/
|
||||
void reset_stage()
|
||||
void start()
|
||||
{
|
||||
assert(scan_queue.empty());
|
||||
|
||||
// Remove blossom labels.
|
||||
for (BlossomT& blossom : trivial_blossom) {
|
||||
reset_blossom_label(&blossom);
|
||||
}
|
||||
for (BlossomT& blossom : nontrivial_blossom) {
|
||||
reset_blossom_label(&blossom);
|
||||
}
|
||||
|
||||
// Reset least-slack edge tracking.
|
||||
for (BlossomT& blossom : trivial_blossom) {
|
||||
delta2_disable_blossom(&blossom);
|
||||
}
|
||||
for (BlossomT& blossom : nontrivial_blossom) {
|
||||
delta2_disable_blossom(&blossom);
|
||||
}
|
||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||
delta2_clear_vertex(x);
|
||||
assert(vertex_mate[x] == NO_VERTEX);
|
||||
|
||||
// Assign label S.
|
||||
BlossomT* bx = top_level_blossom(x);
|
||||
assert(bx->base_vertex == x);
|
||||
assign_blossom_label_s(bx);
|
||||
|
||||
// Mark blossom as the root of an alternating tree.
|
||||
bx->tree_edge = std::make_pair(NO_VERTEX, x);
|
||||
bx->tree_root = x;
|
||||
}
|
||||
delta3_queue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1869,27 +1958,6 @@ public:
|
|||
*/
|
||||
bool run_stage()
|
||||
{
|
||||
// Assign label S to all unmatched vertices and put them in the queue.
|
||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||
if (vertex_mate[x] == NO_VERTEX) {
|
||||
|
||||
// Assign label S.
|
||||
BlossomT* bx = top_level_blossom(x);
|
||||
assert(bx->base_vertex == x);
|
||||
assign_blossom_label_s(bx);
|
||||
|
||||
// Mark blossom as the root of an alternating tree.
|
||||
bx->tree_edge = std::make_pair(NO_VERTEX, x);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop if all vertices are matched.
|
||||
// No further improvement is possible in that case.
|
||||
// This avoids messy calculations of delta steps without any S-vertex.
|
||||
if (scan_queue.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each pass through the following loop is a "substage".
|
||||
// The substage tries to find an augmenting path.
|
||||
// If an augmenting path is found, we augment the matching and end
|
||||
|
@ -1897,7 +1965,6 @@ public:
|
|||
// next substage, or stop if no further improvement is possible.
|
||||
//
|
||||
// This loop runs through at most O(n) iterations per stage.
|
||||
bool augmented = false;
|
||||
while (true) {
|
||||
|
||||
// Consider the incident edges of newly labeled S-vertices.
|
||||
|
@ -1925,9 +1992,9 @@ public:
|
|||
// This may reveal an augmenting path.
|
||||
VertexId x = delta.edge.first;
|
||||
VertexId y = delta.edge.second;
|
||||
augmented = add_s_to_s_edge(x, y);
|
||||
bool augmented = add_s_to_s_edge(x, y);
|
||||
if (augmented) {
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (delta.kind == 4) {
|
||||
|
@ -1939,15 +2006,9 @@ public:
|
|||
} else {
|
||||
// No further improvement possible. End the stage.
|
||||
assert(delta.kind == 1);
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all labels, clear queue.
|
||||
reset_stage();
|
||||
|
||||
// Return True if the matching was augmented.
|
||||
return augmented;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1961,7 +2022,11 @@ public:
|
|||
assert(scan_queue.empty());
|
||||
|
||||
auto cleanup_blossom = [this](BlossomT* blossom) {
|
||||
assert(blossom->label == LABEL_NONE);
|
||||
|
||||
// Remove blossom label.
|
||||
if ((! blossom->parent) && (blossom->label != LABEL_NONE)) {
|
||||
reset_blossom_label(blossom);
|
||||
}
|
||||
|
||||
// Unwind lazy delta updates to vertex dual variables.
|
||||
if (blossom->vertex_dual_offset != 0) {
|
||||
|
@ -1989,6 +2054,9 @@ public:
|
|||
/** Run the matching algorithm. */
|
||||
void run()
|
||||
{
|
||||
// Assign label S to all vertices.
|
||||
start();
|
||||
|
||||
// Improve the solution until no further improvement is possible.
|
||||
//
|
||||
// Each successful pass through this loop increases the number
|
||||
|
|
Loading…
Reference in New Issue