Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
|
#include <perfect_matching.h>
Classes | |
struct | Edge |
An undirected edge between two nodes: tail <-> head. More... | |
struct | Node |
Public Member Functions | |
DEFINE_INT_TYPE (NodeIndex, int) | |
Typed index used by this class. | |
DEFINE_INT_TYPE (EdgeIndex, int) | |
DEFINE_INT_TYPE (CostValue, int64_t) | |
BlossomGraph (int num_nodes) | |
Creates a BlossomGraph on the given number of nodes. | |
void | AddEdge (NodeIndex tail, NodeIndex head, CostValue cost) |
Same comment as MinCostPerfectMatching::AddEdgeWithCost() applies. | |
ABSL_MUST_USE_RESULT bool | Initialize () |
void | PrimalUpdates () |
CostValue | ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue () |
void | UpdateAllTrees (CostValue delta) |
bool | NodeIsMatched (NodeIndex n) const |
NodeIndex | Match (NodeIndex n) const |
void | Grow (EdgeIndex e, NodeIndex tail, NodeIndex head) |
void | Augment (EdgeIndex e) |
void | Shrink (EdgeIndex e) |
void | Expand (NodeIndex to_expand) |
Expands a Blossom into its component. | |
int | NumMatched () const |
Returns the current number of matched nodes. | |
CostValue | DualObjective () const |
void | ExpandAllBlossoms () |
This must be called at the end of the algorithm to recover the matching. | |
CostValue | Slack (const Edge &edge) const |
Return the "slack" of the given edge. | |
CostValue | Dual (const Node &node) const |
Returns the dual value of the given node (which might be a pseudo-node). | |
void | DisplayStats () const |
Display to VLOG(1) some statistic about the solve. | |
void | DebugCheckNoPossiblePrimalUpdates () |
bool | DebugDualsAreFeasible () const |
void | DebugUpdateNodeDual (NodeIndex n, CostValue delta) |
bool | DebugEdgeIsTightAndExternal (const Edge &edge) const |
const Edge & | GetEdge (int e) const |
const Node & | GetNode (int n) const |
std::string | NodeDebugString (NodeIndex n) const |
Display information for debugging. | |
std::string | EdgeDebugString (EdgeIndex e) const |
std::string | DebugString () const |
Static Public Attributes | |
static const NodeIndex | kNoNodeIndex |
static const EdgeIndex | kNoEdgeIndex |
static const CostValue | kMaxCostValue |
Class containing the main data structure used by the Blossom algorithm.
At the core is the original undirected graph. During the algorithm execution we might collapse nodes into so-called Blossoms. A Blossom is a cycle of external nodes (which can be blossom nodes) of odd length (>= 3). The edges of the cycle are called blossom-forming eges and will always be tight (i.e. have a slack of zero). Once a Blossom is created, its nodes become "internal" and are basically considered merged into the blossom node for the rest of the algorithm (except if we later re-expand the blossom).
Moreover, external nodes of the graph will have 3 possible types ([+], [-] and free [0]). Free nodes will always be matched together in pairs. Nodes of type [+] and [-] are arranged in a forest of alternating [+]/[-] disjoint trees. Each unmatched node is the root of a tree, and of type [+]. Nodes [-] will always have exactly one child to witch they are matched. [+] nodes can have any number of [-] children, to which they are not matched. All the edges of the trees will always be tight. Some examples below, double edges are used for matched nodes:
A matched pair of free nodes: [0] === [0]
A possible rooted tree: [+] – [-] ==== [+] \ [-] ==== [+] -— [-] === [+] \ [-] === [+]
A single unmatched node is also a tree: [+]
Definition at line 165 of file perfect_matching.h.
|
explicit |
Creates a BlossomGraph on the given number of nodes.
Definition at line 130 of file perfect_matching.cc.
Same comment as MinCostPerfectMatching::AddEdgeWithCost() applies.
Definition at line 140 of file perfect_matching.cc.
void operations_research::BlossomGraph::Augment | ( | EdgeIndex | e | ) |
Merges two tree and augment the number of matched nodes by 1. This is the only functions that change the current matching.
Compute the path from root_a to root_b.
Make all the nodes from both trees free while keeping the current matching.
If the other end is not in one of the two trees, and it is a plus node, we add it the plus_free queue. All previous [+]–[0] and [+]–[+] edges need to be removed from the queues.
Change the matching of nodes along node_path.
Update unmatched_nodes_.
Definition at line 561 of file perfect_matching.cc.
CostValue operations_research::BlossomGraph::ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue | ( | ) |
Computes the maximum possible delta for UpdateAllTrees() that keeps the dual feasibility. Dual update approach (2) from the paper. This also fills primal_update_edge_queue_.
This code only works because all tree_dual_delta are the same.
This means infeasible, and returning zero will abort the search.
Initialize primal_update_edge_queue_ with all the edges that will have a slack of zero once we apply the update.
NOTE(user): If we want more "determinism" and be independent on the PQ algorithm, we could std::sort() the primal_update_edge_queue_ here.
Definition at line 273 of file perfect_matching.cc.
void operations_research::BlossomGraph::DebugCheckNoPossiblePrimalUpdates | ( | ) |
Checks that there is no possible primal update in the current configuration.
Meant to only be used in DEBUG to make sure our queue in PrimalUpdates() do not miss any potential edges.
Make sure tail is a plus node if possible.
Definition at line 361 of file perfect_matching.cc.
bool operations_research::BlossomGraph::DebugDualsAreFeasible | ( | ) | const |
Tests that the dual values are currently feasible. This should ALWAYS be the case.
The slack of all edge must be non-negative.
The dual of all Blossom must be non-negative.
Definition at line 464 of file perfect_matching.cc.
bool operations_research::BlossomGraph::DebugEdgeIsTightAndExternal | ( | const Edge & | edge | ) | const |
Returns true iff this is an external edge with a slack of zero. An external edge is an edge between two external nodes.
Definition at line 477 of file perfect_matching.cc.
std::string operations_research::BlossomGraph::DebugString | ( | ) | const |
Definition at line 1217 of file perfect_matching.cc.
In debug mode, we maintain the real slack of each edges and the real dual of each node via this function. Both Slack() and Dual() checks in debug mode that the value computed is the correct one.
Definition at line 1228 of file perfect_matching.cc.
operations_research::BlossomGraph::DEFINE_INT_TYPE | ( | CostValue | , |
int64_t | ) |
operations_research::BlossomGraph::DEFINE_INT_TYPE | ( | EdgeIndex | , |
int | ) |
operations_research::BlossomGraph::DEFINE_INT_TYPE | ( | NodeIndex | , |
int | ) |
Typed index used by this class.
void operations_research::BlossomGraph::DisplayStats | ( | ) | const |
Display to VLOG(1) some statistic about the solve.
Definition at line 1281 of file perfect_matching.cc.
Returns the dual value of the given node (which might be a pseudo-node).
Definition at line 1260 of file perfect_matching.cc.
CostValue operations_research::BlossomGraph::DualObjective | ( | ) | const |
Returns the current dual objective which is always a valid lower-bound on the min-cost matching. Note that this is capped to kint64max in case of overflow. Because all of our cost are positive, this starts at zero.
Definition at line 1269 of file perfect_matching.cc.
std::string operations_research::BlossomGraph::EdgeDebugString | ( | EdgeIndex | e | ) | const |
Definition at line 1207 of file perfect_matching.cc.
void operations_research::BlossomGraph::Expand | ( | NodeIndex | to_expand | ) |
Expands a Blossom into its component.
First, restore the saved fields.
Restore the edges Head()/Tail().
Now we try to find a 'blossom path' that will replace the blossom node in the alternating tree: the blossom's parent [+] node in the tree will be attached to a blossom subnode (the "path start"), the blossom's child in the tree will be attached to a blossom subnode (the "path end", which could be the same subnode or a different one), and, using the blossom cycle, we'll get a path with an odd number of blossom subnodes to connect the two (since the cycle is odd, one of the two paths will be odd too). The other subnodes of the blossom will then be made free nodes matched pairwise.
Split the cycle in two halves: nodes in [start..end] in path1, and nodes in [end..start] in path2. Note the inclusive intervals.
Reverse path2 to also make it go from start to end.
Swap if necessary so that path1 is the odd-length one.
Use better aliases than 'path1' and 'path2' in the code below.
Strip path2 from the start and end, which aren't needed.
Restore the path in the tree, note that we append the blossom_matched_node to simplify the code: <-— Blossom ----> [-] === [+] — [-] === [+]
Update the parent.
This is the path start and its parent is either itself or the parent of to_expand if there was one.
Update the types and matches.
Ignore the blossom_matched_node for the code below.
Update the duals, depending on whether we have a new [+] or [-] node.
non-internal edges used to be attached to the [-] node_to_expand, so we adjust their dual.
This was an internal edges. For the PQ code below to be correct, we wait for its other end to have been processed by this loop already. We detect that using the fact that the type of unprocessed internal node is still zero.
Update edge queues.
Update free nodes.
Update edges slack and priority queue for the adjacent edges.
non-internal edges used to be attached to the [-] node_to_expand, so we adjust their dual.
Update PQ. Note that since this was attached to a [-] node it cannot be in any queue. We will also never process twice the same edge here.
Matches the free pair together.
Mark all node as external. We do that last so we could easily detect old internal edges that are now external.
Definition at line 859 of file perfect_matching.cc.
void operations_research::BlossomGraph::ExpandAllBlossoms | ( | ) |
This must be called at the end of the algorithm to recover the matching.
Queue of blossoms to expand.
When this is called, there should be no more trees.
Find the edge used to match to_expand with Match(to_expand).
Restore the saved data.
Restore the edges Head()/Tail().
Find the index of matched_node in the blossom list.
Amongst the node_to_expand.blossom nodes, internal_matched_index is matched with external_matched_node and the other are matched together.
Clear root/parent/type of all internal nodes.
Matches the internal node with external one.
Matches the free pair together.
Now that the expansion is done, add to the queue any sub-blossoms.
Definition at line 1068 of file perfect_matching.cc.
|
inline |
Getters to access node/edges from outside the class. Only used in tests.
Definition at line 390 of file perfect_matching.h.
|
inline |
Definition at line 391 of file perfect_matching.h.
Adds to the tree of tail the free matched pair(head, Match(head)). The edge is only used in DCHECKs. We duplicate tail/head because the order matter here.
head was free and is now a [-] node.
leaf was free and is now a [+] node.
The edge switch from [+] – [0] to [+] – [+].
We have a new [+] – [0] edge.
Definition at line 484 of file perfect_matching.cc.
bool operations_research::BlossomGraph::Initialize | ( | ) |
Heuristic to start with a dual-feasible solution and some matched edges. To be called once all edges are added. Returns false if the problem is detected to be INFEASIBLE.
Initialize the dual of each nodes to min_cost / 2.
Starts with all nodes as tree roots.
Update the slack of each edges now that nodes might have non-zero duals.
After this greedy update, there will be at least an edge with a slack of zero.
Match this node if possible.
Initialize unmatched_nodes_.
Scale everything by 2 and update the dual cost. Note that we made sure that there cannot be an integer overflow at the beginning of Solve().
This scaling allows to only have integer weights during the algorithm because the slack of [+] – [+] edges will always stay even.
Initialize the edge priority queues and the primal update queue. We only need to do that if we have unmatched nodes.
Definition at line 157 of file perfect_matching.cc.
Returns the node matched to the given one, or n if this node is not currently matched.
Definition at line 350 of file perfect_matching.cc.
std::string operations_research::BlossomGraph::NodeDebugString | ( | NodeIndex | n | ) | const |
Display information for debugging.
Definition at line 1191 of file perfect_matching.cc.
bool operations_research::BlossomGraph::NodeIsMatched | ( | NodeIndex | n | ) | const |
Returns true iff this node is matched and is thus not a tree root. This cannot live in the Node class because we need to know the NodeIndex.
An unmatched node must be a tree root.
Definition at line 343 of file perfect_matching.cc.
|
inline |
Returns the current number of matched nodes.
Definition at line 352 of file perfect_matching.h.
void operations_research::BlossomGraph::PrimalUpdates | ( | ) |
Enters a loop that perform one of Grow()/Augment()/Shrink()/Expand() until a fixed point is reached.
Any Grow/Augment/Shrink/Expand operation can add new tight edges that need to be explored again.
First, we Grow/Augment as much as possible.
Because of the Expand() operation, the edge may have become un-tight since it has been inserted in the tight edges queue. It's cheaper to detect it here and skip it than it would be to dynamically update the queue to only keep actually tight edges at all times.
Shrink all potential Blossom.
Delay expand if any blossom was created.
Expand Blossom if any.
Definition at line 395 of file perfect_matching.cc.
void operations_research::BlossomGraph::Shrink | ( | EdgeIndex | e | ) |
Creates a Blossom using the given [+] – [+] edge between two nodes of the same tree.
Find lowest common ancestor and the two node paths to reach it. Note that we do not add it to the paths.
Fill the cycle.
Save all values that will be needed if we expand this Blossom later.
Set the new dual of the node to zero.
Mark node as internal, but do not change their type to zero yet. We need to do that first to properly detect edges between two internal nodes in the second loop below.
Update the dual of all edges and the priority queueus.
Subtle: We update root_blossom_node_ while we loop, so for new internal edges, depending if an edge "other end" appear after or before, it will not be updated. We use this to only process internal edges once.
Skip edge that are already internal.
This internal edge was already processed from its other end, so we can just skip it.
This is a new-internal edge that we didn't process yet.
Replace the parent of any child of n by lca_index.
Adjust when the edge used to be connected to a [-] node now that we attach it to a [+] node. Note that if the node was [+] then the non-internal incident edges slack and type do not change.
Add it to the correct PQ.
Definition at line 664 of file perfect_matching.cc.
Return the "slack" of the given edge.
Definition at line 1242 of file perfect_matching.cc.
void operations_research::BlossomGraph::UpdateAllTrees | ( | CostValue | delta | ) |
Applies the same dual delta to all trees. Dual update approach (2) from the paper.
Reminder: the tree roots are exactly the unmatched nodes.
Definition at line 323 of file perfect_matching.cc.
|
static |
Definition at line 177 of file perfect_matching.h.
|
static |
Definition at line 176 of file perfect_matching.h.
|
static |
Basic constants. NOTE(user): Those can't be constexpr because of the or-tools export, which complains for constexpr DEFINE_INT_TYPE.
Definition at line 175 of file perfect_matching.h.