Maintain concatenable queue for each blossom
This commit is contained in:
parent
5b5c107a5c
commit
22251e64e8
107
cpp/mwmatching.h
107
cpp/mwmatching.h
|
@ -320,7 +320,8 @@ struct Blossom
|
||||||
|
|
||||||
// TODO -- tree_blossoms
|
// TODO -- tree_blossoms
|
||||||
|
|
||||||
// TOOD -- vertex_queue
|
/** Concatenable queue containing all vertices in the blossom. */
|
||||||
|
ConcatenableQueue<WeightType, Blossom*, EdgeId> vertex_queue;
|
||||||
|
|
||||||
// TODO -- delta2_node
|
// TODO -- delta2_node
|
||||||
|
|
||||||
|
@ -337,13 +338,14 @@ protected:
|
||||||
base_vertex(base_vertex),
|
base_vertex(base_vertex),
|
||||||
label(LABEL_NONE),
|
label(LABEL_NONE),
|
||||||
is_nontrivial_blossom(is_nontrivial_blossom),
|
is_nontrivial_blossom(is_nontrivial_blossom),
|
||||||
|
vertex_queue(this),
|
||||||
vertex_dual_offset(0)
|
vertex_dual_offset(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Initialize a trivial (single-vertex) blossom. */
|
/** Initialize a trivial (single-vertex) blossom. */
|
||||||
explicit Blossom(VertexId base_vertex)
|
explicit Blossom(VertexId x = NO_VERTEX)
|
||||||
: Blossom(base_vertex, false)
|
: Blossom(x, false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -584,18 +586,6 @@ public:
|
||||||
// NOTE - this MUST be a list, because we delete items from it while keeping pointers to other items
|
// NOTE - this MUST be a list, because we delete items from it while keeping pointers to other items
|
||||||
std::list<NonTrivialBlossomT> nontrivial_blossom;
|
std::list<NonTrivialBlossomT> nontrivial_blossom;
|
||||||
|
|
||||||
// TODO -- vertex_queue_node
|
|
||||||
|
|
||||||
// TODO -- remove
|
|
||||||
/**
|
|
||||||
* Every vertex is contained in exactly one top-level blossom
|
|
||||||
* (possibly the trivial blossom that contains just that vertex).
|
|
||||||
*
|
|
||||||
* "vertex_top_blossom[x]" is the top-level blossom that contains
|
|
||||||
* vertex "x".
|
|
||||||
*/
|
|
||||||
std::vector<BlossomT*> vertex_top_blossom;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modified dual variable of each vertex.
|
* Modified dual variable of each vertex.
|
||||||
*
|
*
|
||||||
|
@ -627,6 +617,10 @@ public:
|
||||||
/** Running sum of applied delta steps. */
|
/** Running sum of applied delta steps. */
|
||||||
WeightType delta_sum;
|
WeightType delta_sum;
|
||||||
|
|
||||||
|
/** For each vertex, a node in its top-level blossom. */
|
||||||
|
typedef ConcatenableQueue<WeightType, BlossomT*, EdgeId> VertexQueue;
|
||||||
|
std::vector<typename VertexQueue::Node> vertex_queue_node;
|
||||||
|
|
||||||
// TODO -- delta2_queue
|
// TODO -- delta2_queue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -673,21 +667,22 @@ public:
|
||||||
*/
|
*/
|
||||||
explicit MatchingContext(const std::vector<EdgeT>& edges_in)
|
explicit MatchingContext(const std::vector<EdgeT>& edges_in)
|
||||||
: graph(edges_in),
|
: graph(edges_in),
|
||||||
|
trivial_blossom(graph.num_vertex),
|
||||||
|
vertex_queue_node(graph.num_vertex),
|
||||||
delta3_node(edges_in.size())
|
delta3_node(edges_in.size())
|
||||||
{
|
{
|
||||||
// Initially all vertices are unmatched.
|
// Initially all vertices are unmatched.
|
||||||
vertex_mate.resize(graph.num_vertex, NO_VERTEX);
|
vertex_mate.resize(graph.num_vertex, NO_VERTEX);
|
||||||
|
|
||||||
// Create a trivial blossom for each vertex.
|
// Initialize a trivial blossom for each vertex.
|
||||||
trivial_blossom.reserve(graph.num_vertex);
|
|
||||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||||
trivial_blossom.emplace_back(x);
|
trivial_blossom[x].base_vertex = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initially all vertices are trivial top-level blossoms.
|
// Insert each vertex as the only element in its own blossom.
|
||||||
vertex_top_blossom.reserve(graph.num_vertex);
|
|
||||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||||
vertex_top_blossom.push_back(&trivial_blossom[x]);
|
trivial_blossom[x].vertex_queue.insert(
|
||||||
|
&vertex_queue_node[x], 0, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertex duals are initialized to half the maximum edge weight.
|
// Vertex duals are initialized to half the maximum edge weight.
|
||||||
|
@ -721,8 +716,7 @@ public:
|
||||||
*/
|
*/
|
||||||
BlossomT* top_level_blossom(VertexId x) const
|
BlossomT* top_level_blossom(VertexId x) const
|
||||||
{
|
{
|
||||||
// TODO
|
return vertex_queue_node[x].find();
|
||||||
return vertex_top_blossom[x];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** Least slack edge tracking: ********** */
|
/* ********** Least slack edge tracking: ********** */
|
||||||
|
@ -818,7 +812,7 @@ public:
|
||||||
WeightType best_slack = 0;
|
WeightType best_slack = 0;
|
||||||
|
|
||||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||||
if (vertex_top_blossom[x]->label == LABEL_NONE) {
|
if (top_level_blossom(x)->label == LABEL_NONE) {
|
||||||
const EdgeT* edge = vertex_best_edge[x];
|
const EdgeT* edge = vertex_best_edge[x];
|
||||||
if (edge != nullptr) {
|
if (edge != nullptr) {
|
||||||
WeightType slack = edge_slack(*edge);
|
WeightType slack = edge_slack(*edge);
|
||||||
|
@ -1095,13 +1089,12 @@ public:
|
||||||
* discovers an augmenting path. In this case it returns an alternating
|
* discovers an augmenting path. In this case it returns an alternating
|
||||||
* path that starts and ends in an unmatched vertex.
|
* path that starts and ends in an unmatched vertex.
|
||||||
*
|
*
|
||||||
* This function takes time O(k) to discover a blossom, where "k" is the
|
* This function takes time O(k * log(n)) to discover a blossom,
|
||||||
* number of sub-blossoms, or time O(n) to discover an augmenting path.
|
* where "k" is the number of sub-blossoms,
|
||||||
|
* or time O(n * log(n)) to discover an augmenting path.
|
||||||
*/
|
*/
|
||||||
AlternatingPath trace_alternating_paths(VertexId x, VertexId y)
|
AlternatingPath trace_alternating_paths(VertexId x, VertexId y)
|
||||||
{
|
{
|
||||||
assert(vertex_top_blossom[x] != vertex_top_blossom[y]);
|
|
||||||
|
|
||||||
// Initialize a path containing only the edge (x, y).
|
// Initialize a path containing only the edge (x, y).
|
||||||
AlternatingPath path;
|
AlternatingPath path;
|
||||||
path.edges.emplace_back(x, y);
|
path.edges.emplace_back(x, y);
|
||||||
|
@ -1120,7 +1113,7 @@ public:
|
||||||
if (x != NO_VERTEX) {
|
if (x != NO_VERTEX) {
|
||||||
|
|
||||||
// Stop if we found a common ancestor.
|
// Stop if we found a common ancestor.
|
||||||
BlossomT* bx = vertex_top_blossom[x];
|
BlossomT* bx = top_level_blossom(x);
|
||||||
if (vertex_marker[bx->base_vertex]) {
|
if (vertex_marker[bx->base_vertex]) {
|
||||||
first_common = bx;
|
first_common = bx;
|
||||||
break;
|
break;
|
||||||
|
@ -1141,7 +1134,7 @@ public:
|
||||||
if (y != NO_VERTEX) {
|
if (y != NO_VERTEX) {
|
||||||
|
|
||||||
// Stop if we found a common ancestor.
|
// Stop if we found a common ancestor.
|
||||||
BlossomT* by = vertex_top_blossom[y];
|
BlossomT* by = top_level_blossom(y);
|
||||||
if (vertex_marker[by->base_vertex]) {
|
if (vertex_marker[by->base_vertex]) {
|
||||||
first_common = by;
|
first_common = by;
|
||||||
break;
|
break;
|
||||||
|
@ -1168,11 +1161,11 @@ public:
|
||||||
// If we found a common ancestor, trim the paths so they end there.
|
// If we found a common ancestor, trim the paths so they end there.
|
||||||
if (first_common) {
|
if (first_common) {
|
||||||
assert(first_common->label == LABEL_S);
|
assert(first_common->label == LABEL_S);
|
||||||
while (vertex_top_blossom[path.edges.front().first]
|
while (top_level_blossom(path.edges.front().first)
|
||||||
!= first_common) {
|
!= first_common) {
|
||||||
path.edges.pop_front();
|
path.edges.pop_front();
|
||||||
}
|
}
|
||||||
while (vertex_top_blossom[path.edges.back().second]
|
while (top_level_blossom(path.edges.back().second)
|
||||||
!= first_common) {
|
!= first_common) {
|
||||||
path.edges.pop_back();
|
path.edges.pop_back();
|
||||||
}
|
}
|
||||||
|
@ -1190,7 +1183,7 @@ public:
|
||||||
* Assign label S to the new blossom.
|
* Assign label S to the new blossom.
|
||||||
* Relabel all T-sub-blossoms as S and add their vertices to the queue.
|
* Relabel all T-sub-blossoms as S and add their vertices to the queue.
|
||||||
*
|
*
|
||||||
* This function takes total time O(n**2) per stage.
|
* This function takes total time O((n + m) * log(n)) per stage.
|
||||||
*/
|
*/
|
||||||
void make_blossom(const AlternatingPath& path)
|
void make_blossom(const AlternatingPath& path)
|
||||||
{
|
{
|
||||||
|
@ -1201,14 +1194,14 @@ public:
|
||||||
std::vector<BlossomT*> subblossoms;
|
std::vector<BlossomT*> subblossoms;
|
||||||
subblossoms.reserve(path.edges.size());
|
subblossoms.reserve(path.edges.size());
|
||||||
for (VertexPair edge : path.edges) {
|
for (VertexPair edge : path.edges) {
|
||||||
subblossoms.push_back(vertex_top_blossom[edge.first]);
|
subblossoms.push_back(top_level_blossom(edge.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the path is cyclic.
|
// Check that the path is cyclic.
|
||||||
VertexId pos = 0;
|
VertexId pos = 0;
|
||||||
for (VertexPair edge : path.edges) {
|
for (VertexPair edge : path.edges) {
|
||||||
pos = (pos + 1) % subblossoms.size();
|
pos = (pos + 1) % subblossoms.size();
|
||||||
assert(vertex_top_blossom[edge.second] == subblossoms[pos]);
|
assert(top_level_blossom(edge.second) == subblossoms[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blossom must start and end with an S-blossom.
|
// Blossom must start and end with an S-blossom.
|
||||||
|
@ -1238,10 +1231,13 @@ public:
|
||||||
sub->parent = blossom;
|
sub->parent = blossom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark vertices as belonging to the new blossom.
|
// Merge concatenable queues.
|
||||||
for_vertices_in_blossom(blossom, [this,blossom](VertexId x) {
|
// TODO -- avoid temporary array
|
||||||
vertex_top_blossom[x] = blossom;
|
std::vector<VertexQueue*> subqueues;
|
||||||
});
|
for (BlossomT* sub : subblossoms) {
|
||||||
|
subqueues.push_back(&sub->vertex_queue);
|
||||||
|
}
|
||||||
|
blossom->vertex_queue.merge(subqueues.begin(), subqueues.end());
|
||||||
|
|
||||||
// Assign label S to the new blossom and link to the alternating tree.
|
// Assign label S to the new blossom and link to the alternating tree.
|
||||||
blossom->label = LABEL_S;
|
blossom->label = LABEL_S;
|
||||||
|
@ -1271,6 +1267,10 @@ public:
|
||||||
assert(blossom->parent == nullptr);
|
assert(blossom->parent == nullptr);
|
||||||
assert(blossom->label == LABEL_NONE);
|
assert(blossom->label == LABEL_NONE);
|
||||||
|
|
||||||
|
// Split concatenable queue, thus reconstructing the separate
|
||||||
|
// concatenable queues of the sub-blossoms.
|
||||||
|
blossom->vertex_queue.split();
|
||||||
|
|
||||||
// Prepare to push pending delta updates down to the sub-blossoms.
|
// Prepare to push pending delta updates down to the sub-blossoms.
|
||||||
WeightType vertex_dual_offset = blossom->vertex_dual_offset;
|
WeightType vertex_dual_offset = blossom->vertex_dual_offset;
|
||||||
blossom->vertex_dual_offset = 0;
|
blossom->vertex_dual_offset = 0;
|
||||||
|
@ -1281,10 +1281,6 @@ public:
|
||||||
assert(sub_blossom->parent == blossom);
|
assert(sub_blossom->parent == blossom);
|
||||||
assert(sub_blossom->label == LABEL_NONE);
|
assert(sub_blossom->label == LABEL_NONE);
|
||||||
sub_blossom->parent = nullptr;
|
sub_blossom->parent = nullptr;
|
||||||
for_vertices_in_blossom(sub_blossom,
|
|
||||||
[this,sub_blossom](VertexId x) {
|
|
||||||
vertex_top_blossom[x] = sub_blossom;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Push pending delta updates to sub-blossom.
|
// Push pending delta updates to sub-blossom.
|
||||||
assert(sub_blossom->vertex_dual_offset == 0);
|
assert(sub_blossom->vertex_dual_offset == 0);
|
||||||
|
@ -1326,7 +1322,7 @@ public:
|
||||||
|
|
||||||
// Find the sub-blossom that was attached to the parent node
|
// Find the sub-blossom that was attached to the parent node
|
||||||
// in the alternating tree.
|
// in the alternating tree.
|
||||||
BlossomT* entry = vertex_top_blossom[blossom->tree_edge.second];
|
BlossomT* entry = top_level_blossom(blossom->tree_edge.second);
|
||||||
|
|
||||||
// Assign label T to that blossom and link to the alternating tree.
|
// Assign label T to that blossom and link to the alternating tree.
|
||||||
assign_blossom_label_t(entry);
|
assign_blossom_label_t(entry);
|
||||||
|
@ -1469,8 +1465,6 @@ public:
|
||||||
* from sub-blossom "entry" to the base vertex of the blossom.
|
* from sub-blossom "entry" to the base vertex of the blossom.
|
||||||
*
|
*
|
||||||
* Recursively handle sub-blossoms as needed.
|
* Recursively handle sub-blossoms as needed.
|
||||||
*
|
|
||||||
* This function takes time O(n).
|
|
||||||
*/
|
*/
|
||||||
void augment_blossom(NonTrivialBlossomT* blossom, BlossomT* entry)
|
void augment_blossom(NonTrivialBlossomT* blossom, BlossomT* entry)
|
||||||
{
|
{
|
||||||
|
@ -1505,14 +1499,18 @@ public:
|
||||||
/**
|
/**
|
||||||
* Augment the matching through the specified augmenting path.
|
* Augment the matching through the specified augmenting path.
|
||||||
*
|
*
|
||||||
* This function takes time O(n).
|
* This function takes time O(n * log(n)).
|
||||||
*/
|
*/
|
||||||
void augment_matching(const AlternatingPath& path)
|
void augment_matching(const AlternatingPath& path)
|
||||||
{
|
{
|
||||||
// Check that the path starts and ends in an unmatched blossom.
|
// Check that the path starts and ends in an unmatched blossom.
|
||||||
assert(path.edges.size() % 2 == 1);
|
assert(path.edges.size() % 2 == 1);
|
||||||
assert(vertex_mate[vertex_top_blossom[path.edges.front().first]->base_vertex] == NO_VERTEX);
|
assert(vertex_mate[
|
||||||
assert(vertex_mate[vertex_top_blossom[path.edges.back().second]->base_vertex] == NO_VERTEX);
|
top_level_blossom(path.edges.front().first)->base_vertex]
|
||||||
|
== NO_VERTEX);
|
||||||
|
assert(vertex_mate[
|
||||||
|
top_level_blossom(path.edges.back().second)->base_vertex]
|
||||||
|
== NO_VERTEX);
|
||||||
|
|
||||||
// Process the unmatched edges on the augmenting path.
|
// Process the unmatched edges on the augmenting path.
|
||||||
auto edge_it = path.edges.begin();
|
auto edge_it = path.edges.begin();
|
||||||
|
@ -1522,13 +1520,13 @@ public:
|
||||||
VertexId y = edge_it->second;
|
VertexId y = edge_it->second;
|
||||||
|
|
||||||
// Augment any non-trivial blossoms that touch this edge.
|
// Augment any non-trivial blossoms that touch this edge.
|
||||||
BlossomT* bx = vertex_top_blossom[x];
|
BlossomT* bx = top_level_blossom(x);
|
||||||
NonTrivialBlossomT* bx_ntb = bx->nontrivial();
|
NonTrivialBlossomT* bx_ntb = bx->nontrivial();
|
||||||
if (bx_ntb != nullptr) {
|
if (bx_ntb != nullptr) {
|
||||||
augment_blossom(bx_ntb, &trivial_blossom[x]);
|
augment_blossom(bx_ntb, &trivial_blossom[x]);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlossomT* by = vertex_top_blossom[y];
|
BlossomT* by = top_level_blossom(y);
|
||||||
NonTrivialBlossomT* by_ntb = by->nontrivial();
|
NonTrivialBlossomT* by_ntb = by->nontrivial();
|
||||||
if (by_ntb != nullptr) {
|
if (by_ntb != nullptr) {
|
||||||
augment_blossom(by_ntb, &trivial_blossom[y]);
|
augment_blossom(by_ntb, &trivial_blossom[y]);
|
||||||
|
@ -1557,8 +1555,6 @@ public:
|
||||||
* via its matched edge. All vertices in the newly labeled S-blossom
|
* via its matched edge. All vertices in the newly labeled S-blossom
|
||||||
* are added to the scan queue.
|
* are added to the scan queue.
|
||||||
*
|
*
|
||||||
* All vertices in the newly labeled blossom are added to the scan queue.
|
|
||||||
*
|
|
||||||
* @pre "x" is an unlabeled vertex.
|
* @pre "x" is an unlabeled vertex.
|
||||||
* @pre "x" is matched to a T-vertex via a tight edge.
|
* @pre "x" is matched to a T-vertex via a tight edge.
|
||||||
*/
|
*/
|
||||||
|
@ -1658,9 +1654,10 @@ public:
|
||||||
// Otherwise the path is an augmenting path.
|
// Otherwise the path is an augmenting path.
|
||||||
// Note that an alternating starts and ends in the same blossom,
|
// Note that an alternating starts and ends in the same blossom,
|
||||||
// but not necessarily in the same vertex within that 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 p = path.edges.front().first;
|
||||||
VertexId q = path.edges.back().second;
|
VertexId q = path.edges.back().second;
|
||||||
if (vertex_top_blossom[p] == vertex_top_blossom[q]) {
|
if (top_level_blossom(p) == top_level_blossom(q)) {
|
||||||
make_blossom(path);
|
make_blossom(path);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1873,7 +1870,7 @@ public:
|
||||||
// unlocked through the delta update.
|
// unlocked through the delta update.
|
||||||
VertexId x = delta.edge.first;
|
VertexId x = delta.edge.first;
|
||||||
VertexId y = delta.edge.second;
|
VertexId y = delta.edge.second;
|
||||||
if (vertex_top_blossom[x]->label != LABEL_S) {
|
if (top_level_blossom(x)->label != LABEL_S) {
|
||||||
std::swap(x, y);
|
std::swap(x, y);
|
||||||
}
|
}
|
||||||
extend_tree_s_to_t(x, y);
|
extend_tree_s_to_t(x, y);
|
||||||
|
|
Loading…
Reference in New Issue