1
0
Fork 0

Keep alternating trees between stages in C++

This commit is contained in:
Joris van Rantwijk 2024-11-20 21:10:14 +01:00
parent 082397ef80
commit 3815335a9f
1 changed files with 153 additions and 85 deletions

View File

@ -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