Use priority queues for delta2
This commit is contained in:
parent
22251e64e8
commit
7683f891d5
262
cpp/mwmatching.h
262
cpp/mwmatching.h
|
@ -323,7 +323,8 @@ struct Blossom
|
|||
/** Concatenable queue containing all vertices in the blossom. */
|
||||
ConcatenableQueue<WeightType, Blossom*, EdgeId> vertex_queue;
|
||||
|
||||
// TODO -- delta2_node
|
||||
/** Optional node in the global delta2 queue. */
|
||||
typename PriorityQueue<WeightType, Blossom*>::Node delta2_node;
|
||||
|
||||
/**
|
||||
* Accumulated pending lazy updates to the dual variables of the vertices
|
||||
|
@ -433,8 +434,7 @@ struct NonTrivialBlossom : public Blossom<WeightType>
|
|||
WeightType dual_var;
|
||||
|
||||
/** Top-level T-blossoms are elements in the delta4 queue. */
|
||||
typedef PriorityQueue<WeightType, NonTrivialBlossom*> BlossomQueue;
|
||||
typename BlossomQueue::Node delta4_node;
|
||||
typename PriorityQueue<WeightType, NonTrivialBlossom*>::Node delta4_node;
|
||||
|
||||
/** Initialize a non-trivial blossom. */
|
||||
NonTrivialBlossom(
|
||||
|
@ -539,6 +539,11 @@ public:
|
|||
using EdgeT = Edge<WeightType>;
|
||||
using BlossomT = Blossom<WeightType>;
|
||||
using NonTrivialBlossomT = NonTrivialBlossom<WeightType>;
|
||||
using EdgeQueueT = PriorityQueue<WeightType, EdgeId>;
|
||||
|
||||
/** Marker value for invalid edge slack, larger than any valid slack. */
|
||||
static constexpr WeightType INVALID_SLACK =
|
||||
std::numeric_limits<WeightType>::max();
|
||||
|
||||
/** Specification of a delta step. */
|
||||
struct DeltaStep
|
||||
|
@ -618,38 +623,39 @@ public:
|
|||
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;
|
||||
typedef ConcatenableQueue<WeightType, BlossomT*, EdgeId> VertexQueueT;
|
||||
std::vector<typename VertexQueueT::Node> vertex_queue_node;
|
||||
|
||||
// TODO -- delta2_queue
|
||||
/**
|
||||
* For each T-vertex or unlabeled vertex, a queue of its edges to
|
||||
* any S-vertex. Priority of an edge is its modified slack.
|
||||
*/
|
||||
std::vector<EdgeQueueT> vertex_sedge_queue;
|
||||
|
||||
/** For each edge, a node in an S-edge queue. */
|
||||
std::vector<typename EdgeQueueT::Node> vertex_sedge_node;
|
||||
|
||||
/**
|
||||
* Queue of unlabeled top-level blossoms that have at least one edge
|
||||
* to an S-blossom. The priority of a blossom is its least slack to
|
||||
* an S-blossom plus delta_sum.
|
||||
*/
|
||||
PriorityQueue<WeightType, BlossomT*> delta2_queue;
|
||||
|
||||
/**
|
||||
* Queue of edges between S-vertices in different top-level blossoms.
|
||||
* Priority of an edge is its slack plus 2 * delta_sum.
|
||||
* Priority of an edge is its modified slack.
|
||||
*/
|
||||
typedef PriorityQueue<WeightType, EdgeId> EdgeQueue;
|
||||
EdgeQueue delta3_queue;
|
||||
EdgeQueueT delta3_queue;
|
||||
|
||||
/** For each edge, a node in delta3_queue. */
|
||||
std::vector<typename EdgeQueue::Node> delta3_node;
|
||||
std::vector<typename EdgeQueueT::Node> delta3_node;
|
||||
|
||||
/**
|
||||
* Queue of top-level non-trivial T-blossoms.
|
||||
* Priority of a blossom is its modified dual variable.
|
||||
*/
|
||||
typedef PriorityQueue<WeightType, NonTrivialBlossomT*> BlossomQueue;
|
||||
BlossomQueue delta4_queue;
|
||||
|
||||
// TODO -- vertex_sedge_queue
|
||||
// TODO -- vertex_sedge_node
|
||||
|
||||
// TODO -- delete
|
||||
/**
|
||||
* In case of T-vertex or unlabeled vertex "x",
|
||||
* "vertex_best_edge[x]" is the least-slack edge to any S-vertex,
|
||||
* or "nullptr" if no such edge has been found.
|
||||
*/
|
||||
std::vector<const EdgeT*> vertex_best_edge;
|
||||
PriorityQueue<WeightType, NonTrivialBlossomT*> delta4_queue;
|
||||
|
||||
/** Queue of S-vertices to be scanned. */
|
||||
std::vector<VertexId> scan_queue;
|
||||
|
@ -669,6 +675,8 @@ public:
|
|||
: graph(edges_in),
|
||||
trivial_blossom(graph.num_vertex),
|
||||
vertex_queue_node(graph.num_vertex),
|
||||
vertex_sedge_queue(graph.num_vertex),
|
||||
vertex_sedge_node(edges_in.size()),
|
||||
delta3_node(edges_in.size())
|
||||
{
|
||||
// Initially all vertices are unmatched.
|
||||
|
@ -682,7 +690,7 @@ public:
|
|||
// Insert each vertex as the only element in its own blossom.
|
||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||
trivial_blossom[x].vertex_queue.insert(
|
||||
&vertex_queue_node[x], 0, x);
|
||||
&vertex_queue_node[x], INVALID_SLACK, x);
|
||||
}
|
||||
|
||||
// Vertex duals are initialized to half the maximum edge weight.
|
||||
|
@ -695,9 +703,6 @@ public:
|
|||
|
||||
delta_sum = 0;
|
||||
|
||||
// Initialize "vertex_best_edge".
|
||||
vertex_best_edge.resize(graph.num_vertex, nullptr);
|
||||
|
||||
// Allocate temporary arrays for path tracing.
|
||||
vertex_marker.resize(graph.num_vertex);
|
||||
marked_vertex.reserve(graph.num_vertex);
|
||||
|
@ -735,49 +740,6 @@ public:
|
|||
return vertex_dual[x] + vertex_dual[y] - weight_factor * edge.weight;
|
||||
}
|
||||
|
||||
// TODO -- delete
|
||||
WeightType edge_slack(const EdgeT& edge) const
|
||||
{
|
||||
VertexId x = edge.vt.first;
|
||||
VertexId y = edge.vt.second;
|
||||
BlossomT* bx = top_level_blossom(x);
|
||||
BlossomT* by = top_level_blossom(y);
|
||||
|
||||
WeightType ux = vertex_dual[x];
|
||||
if (bx->label == LABEL_S) {
|
||||
ux -= delta_sum;
|
||||
} else if (bx->label == LABEL_T) {
|
||||
ux += delta_sum + bx->vertex_dual_offset;
|
||||
} else {
|
||||
ux += bx->vertex_dual_offset;
|
||||
}
|
||||
|
||||
WeightType uy = vertex_dual[y];
|
||||
if (by->label == LABEL_S) {
|
||||
uy -= delta_sum;
|
||||
} else if (by->label == LABEL_T) {
|
||||
uy += delta_sum + by->vertex_dual_offset;
|
||||
} else {
|
||||
uy += by->vertex_dual_offset;
|
||||
}
|
||||
|
||||
return ux + uy - weight_factor * edge.weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset least-slack edge tracking.
|
||||
*
|
||||
* This function takes time O(n + m).
|
||||
*/
|
||||
void lset_reset()
|
||||
{
|
||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||
vertex_best_edge[x] = nullptr;
|
||||
}
|
||||
|
||||
delta3_queue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add edge "e" for delta2 tracking.
|
||||
*
|
||||
|
@ -787,48 +749,109 @@ public:
|
|||
*/
|
||||
void delta2_add_edge(EdgeId e, VertexId y, BlossomT* by)
|
||||
{
|
||||
// TODO
|
||||
const EdgeT* edge = &graph.edges[e];
|
||||
WeightType slack = edge_slack(*edge);
|
||||
const EdgeT* cur_best_edge = vertex_best_edge[y];
|
||||
if ((cur_best_edge == nullptr)
|
||||
|| (slack < edge_slack(*cur_best_edge))) {
|
||||
vertex_best_edge[y] = edge;
|
||||
WeightType prio = edge_pseudo_slack(e);
|
||||
|
||||
// Insert the edge in the S-edge queue of vertex "y".
|
||||
// Check whether this improves the min-prio of the vertex.
|
||||
assert(! vertex_sedge_node[e].valid());
|
||||
bool improved = vertex_sedge_queue[y].empty()
|
||||
|| (prio < vertex_sedge_queue[y].min_prio());
|
||||
vertex_sedge_queue[y].insert(&vertex_sedge_node[e], prio, e);
|
||||
|
||||
if (improved) {
|
||||
// Update the priority of vertex "y" in its ConcatenableQueue.
|
||||
vertex_queue_node[y].set_prio(prio);
|
||||
|
||||
// Update the global delta2 queue if this blossom is unlabeled
|
||||
// and the new edge becomes its least-slack S-edge.
|
||||
if (by->label == LABEL_NONE) {
|
||||
prio += by->vertex_dual_offset;
|
||||
if (by->delta2_node.valid()) {
|
||||
if (prio < by->delta2_node.prio()) {
|
||||
delta2_queue.set_prio(&by->delta2_node, prio);
|
||||
}
|
||||
} else {
|
||||
delta2_queue.insert(&by->delta2_node, prio, by);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable delta2 tracking for "blossom".
|
||||
*
|
||||
* This function is called when a blossom becomes an unlabeled top-level
|
||||
* blossom. If the blossom has at least one edge to an S-vertex,
|
||||
* the blossom is inserted in the global delta2 queue.
|
||||
*
|
||||
* This function takes time O(log(n)).
|
||||
*/
|
||||
void delta2_enable_blossom(BlossomT* blossom)
|
||||
{
|
||||
assert(! blossom->delta2_node.valid());
|
||||
WeightType prio = blossom->vertex_queue.min_prio();
|
||||
if (prio < INVALID_SLACK) {
|
||||
prio += blossom->vertex_dual_offset;
|
||||
delta2_queue.insert(&blossom->delta2_node, prio, blossom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable delta2 tracking for "blossom".
|
||||
*
|
||||
* The blossom is removed from the global delta2 queue.
|
||||
* This function is called when a blossom stops being an unlabeled
|
||||
* top-level blossom.
|
||||
*
|
||||
* This function takes time O(log(n)).
|
||||
*/
|
||||
void delta2_disable_blossom(BlossomT* blossom)
|
||||
{
|
||||
if (blossom->delta2_node.valid()) {
|
||||
delta2_queue.remove(&blossom->delta2_node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear delta2 tracking for vertex "x".
|
||||
*
|
||||
* This function is called when "x" becomes an S-vertex.
|
||||
* It is assumed that the blossom containing "x" has already been
|
||||
* disabled for delta2 tracking.
|
||||
*
|
||||
* This function takes time O(k + log(n)),
|
||||
* where "k" is the number of edges incident on "x".
|
||||
*/
|
||||
void delta2_clear_vertex(int x)
|
||||
{
|
||||
vertex_sedge_queue[x].clear();
|
||||
vertex_queue_node[x].set_prio(INVALID_SLACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the least-slack edge between any S-vertex and any unlabeled vertex.
|
||||
*
|
||||
* This function takes time O(n).
|
||||
* TODO -- rework: This function takes time O(log(n)).
|
||||
* This function takes time O(1).
|
||||
*
|
||||
* @return Tuple (edge_index, slack) if there is an S-to-unlabeled edge,
|
||||
* or (NO_EDGE, 0) if there is no such edge.
|
||||
*/
|
||||
std::tuple<EdgeId, WeightType> delta2_get_min_edge()
|
||||
{
|
||||
const EdgeT* best_edge = nullptr;
|
||||
WeightType best_slack = 0;
|
||||
|
||||
for (VertexId x = 0; x < graph.num_vertex; ++x) {
|
||||
if (top_level_blossom(x)->label == LABEL_NONE) {
|
||||
const EdgeT* edge = vertex_best_edge[x];
|
||||
if (edge != nullptr) {
|
||||
WeightType slack = edge_slack(*edge);
|
||||
if ((best_edge == nullptr) || (slack < best_slack)) {
|
||||
best_edge = edge;
|
||||
best_slack = slack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_edge) {
|
||||
return std::make_tuple(best_edge - graph.edges.data(), best_slack);
|
||||
} else {
|
||||
if (delta2_queue.empty()) {
|
||||
return std::make_tuple(NO_EDGE, 0);
|
||||
}
|
||||
|
||||
BlossomT* blossom = delta2_queue.min_elem();
|
||||
WeightType prio = delta2_queue.min_prio();
|
||||
assert(! blossom->parent);
|
||||
assert(blossom->label == LABEL_NONE);
|
||||
|
||||
WeightType slack = prio - delta_sum;
|
||||
VertexId x = blossom->vertex_queue.min_elem();
|
||||
EdgeId e = vertex_sedge_queue[x].min_elem();
|
||||
|
||||
return std::make_tuple(e, slack);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -917,6 +940,9 @@ public:
|
|||
|
||||
blossom->label = LABEL_S;
|
||||
|
||||
// Labeled blossoms must not be in the delta2 queue.
|
||||
delta2_disable_blossom(blossom);
|
||||
|
||||
// Unlabeled blossoms and S-blossoms use different rules
|
||||
// for modified blossom duals. Adjust the modified dual variable
|
||||
// to preserve the true blossom dual while switching labels.
|
||||
|
@ -945,6 +971,9 @@ public:
|
|||
// Apply adjustment to modified dual variable.
|
||||
vertex_dual[x] += dual_fixup;
|
||||
|
||||
// S-vertices do not keep track of potential delta2 edges.
|
||||
delta2_clear_vertex(x);
|
||||
|
||||
// Add new S-vertices to the scan queue.
|
||||
scan_queue.push_back(x);
|
||||
});
|
||||
|
@ -962,6 +991,9 @@ public:
|
|||
|
||||
blossom->label = LABEL_T;
|
||||
|
||||
// Labeled blossoms must not be in the delta2 queue.
|
||||
delta2_disable_blossom(blossom);
|
||||
|
||||
NonTrivialBlossomT* ntb = blossom->nontrivial();
|
||||
if (ntb) {
|
||||
// Unlabeled blossoms and T-blossoms use different rules
|
||||
|
@ -1042,6 +1074,9 @@ public:
|
|||
|
||||
// Adjust dual offset to preserve true vertex duals.
|
||||
blossom->vertex_dual_offset += delta_sum;
|
||||
|
||||
// Enable unlabeled top-level blossom for delta2 tracking.
|
||||
delta2_enable_blossom(blossom);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1233,7 +1268,7 @@ public:
|
|||
|
||||
// Merge concatenable queues.
|
||||
// TODO -- avoid temporary array
|
||||
std::vector<VertexQueue*> subqueues;
|
||||
std::vector<VertexQueueT*> subqueues;
|
||||
for (BlossomT* sub : subblossoms) {
|
||||
subqueues.push_back(&sub->vertex_queue);
|
||||
}
|
||||
|
@ -1267,6 +1302,9 @@ public:
|
|||
assert(blossom->parent == nullptr);
|
||||
assert(blossom->label == LABEL_NONE);
|
||||
|
||||
// Remove blossom from the delta2 queue.
|
||||
delta2_disable_blossom(blossom);
|
||||
|
||||
// Split concatenable queue, thus reconstructing the separate
|
||||
// concatenable queues of the sub-blossoms.
|
||||
blossom->vertex_queue.split();
|
||||
|
@ -1285,6 +1323,9 @@ public:
|
|||
// Push pending delta updates to sub-blossom.
|
||||
assert(sub_blossom->vertex_dual_offset == 0);
|
||||
sub_blossom->vertex_dual_offset = vertex_dual_offset;
|
||||
|
||||
// Add unlabeled blossom to the delta2 queue.
|
||||
delta2_enable_blossom(sub_blossom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1730,9 +1771,14 @@ public:
|
|||
* and the type of delta which obtain the minimum, and the edge or
|
||||
* blossom that produces the minimum delta, if applicable.
|
||||
*
|
||||
* This function takes time O(n).
|
||||
* This function takes time O((1 + k) * log(n)),
|
||||
* where "k" is the number of intra-blossom edges removed from
|
||||
* the delta3 queue.
|
||||
*
|
||||
* @pre There is at least one S-vertex.
|
||||
* At most O(n) delta steps can occur during a stage.
|
||||
* Each edge can be inserted into the delta3 queue at most once per stage.
|
||||
* Therefore, this function takes total time O((n + m) * log(n))
|
||||
* per stage.
|
||||
*
|
||||
* @return Tuple (delta_type, delta_value, delta_edge, delta_blossom)
|
||||
*/
|
||||
|
@ -1768,7 +1814,6 @@ public:
|
|||
}
|
||||
|
||||
// Compute delta4: half minimum dual of a top-level T-blossom.
|
||||
// This takes time O(1).
|
||||
if (! delta4_queue.empty()) {
|
||||
NonTrivialBlossomT* blossom = delta4_queue.min_elem();
|
||||
assert(! blossom->parent);
|
||||
|
@ -1806,7 +1851,16 @@ public:
|
|||
}
|
||||
|
||||
// Reset least-slack edge tracking.
|
||||
lset_reset();
|
||||
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);
|
||||
}
|
||||
delta3_queue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1936,7 +1990,7 @@ public:
|
|||
cleanup_blossom(&blossom);
|
||||
}
|
||||
|
||||
// TODO -- check delta2_queue empty
|
||||
assert(delta2_queue.empty());
|
||||
assert(delta3_queue.empty());
|
||||
assert(delta4_queue.empty());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue