2023-11-04 17:49:41 +01:00
|
|
|
|
#include "edmonds.h"
|
2023-11-05 13:11:59 +01:00
|
|
|
|
#include <iostream>
|
2023-11-04 17:49:41 +01:00
|
|
|
|
|
2023-11-04 18:29:49 +01:00
|
|
|
|
using namespace ED;
|
|
|
|
|
|
2023-11-04 17:49:41 +01:00
|
|
|
|
namespace Edmonds {
|
2023-11-04 18:29:49 +01:00
|
|
|
|
|
2023-11-05 17:08:00 +01:00
|
|
|
|
void check_integrity(Graph const & graph)
|
|
|
|
|
{
|
|
|
|
|
for(NodeId id = 0; id < graph.num_nodes(); ++id)
|
|
|
|
|
{
|
|
|
|
|
NodeId matched = graph.matched_neighbor(id);
|
|
|
|
|
if(matched != id)
|
|
|
|
|
{
|
|
|
|
|
assert(graph.matched_neighbor(matched) == id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (not graph.is_outer(id))
|
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
assert(graph.rho(id) == id);
|
2023-11-05 17:08:00 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-04 19:01:43 +01:00
|
|
|
|
/**
|
|
|
|
|
* @return List of vertices of the x-r path, where r is the root of the
|
|
|
|
|
* special blossom forest component x belongs to.
|
|
|
|
|
*
|
|
|
|
|
* @note This assumes that the values of μ, φ and ρ represent a special
|
|
|
|
|
* blossom forest on the graph when this method is called.
|
|
|
|
|
* **/
|
2023-11-04 18:29:49 +01:00
|
|
|
|
std::vector<NodeId> path_to_forest_root(Graph const & graph, NodeId id)
|
2023-11-04 17:49:41 +01:00
|
|
|
|
{
|
2023-11-04 18:29:49 +01:00
|
|
|
|
std::vector<NodeId> retval;
|
|
|
|
|
retval.push_back(id);
|
|
|
|
|
while (graph.matched_neighbor(id) != id)
|
|
|
|
|
{
|
|
|
|
|
id = graph.matched_neighbor(id);
|
|
|
|
|
retval.push_back(id);
|
|
|
|
|
|
|
|
|
|
// Note that it is guaranteed that this does not produce a loop:
|
|
|
|
|
// We are traversing the path to a root of the forest,
|
|
|
|
|
// but we know that each root is exposed by M, so after traversing
|
|
|
|
|
// the matching edge, we cannot have reached a root.
|
2023-11-05 17:09:06 +01:00
|
|
|
|
id = graph.phi(id);
|
2023-11-04 18:29:49 +01:00
|
|
|
|
retval.push_back(id);
|
|
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-05 13:06:50 +01:00
|
|
|
|
NodeId find_outer_vertex(Graph const & graph)
|
|
|
|
|
{
|
|
|
|
|
for(NodeId id = 0; id < graph.num_nodes(); ++id)
|
|
|
|
|
{
|
|
|
|
|
if (not graph.node(id).scanned and graph.is_outer(id))
|
|
|
|
|
{
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return invalid_node_id;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-05 13:16:16 +01:00
|
|
|
|
void maximum_matching_from_initial_matching(Graph & graph)
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
|
|
|
|
graph.reset_forest();
|
2023-11-05 13:06:50 +01:00
|
|
|
|
NodeId id;
|
|
|
|
|
while((id = find_outer_vertex(graph)) != invalid_node_id)
|
|
|
|
|
{
|
|
|
|
|
for(NodeId neighbor_id : graph.node(id).neighbors())
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
2023-11-05 17:08:00 +01:00
|
|
|
|
check_integrity(graph);
|
|
|
|
|
std::cout << "Check passed" << std::endl;
|
2023-11-05 13:06:50 +01:00
|
|
|
|
if (graph.is_out_of_forest(neighbor_id))
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
2023-11-05 17:08:00 +01:00
|
|
|
|
std::cout << "Grow forest" << std::endl;
|
2023-11-05 13:06:50 +01:00
|
|
|
|
// Grow Forest
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(neighbor_id).phi = id;
|
2023-11-05 13:06:50 +01:00
|
|
|
|
}
|
2023-11-05 17:09:06 +01:00
|
|
|
|
else if (graph.is_outer(neighbor_id) and graph.rho(id) != graph.rho(neighbor_id))
|
2023-11-05 13:06:50 +01:00
|
|
|
|
{
|
|
|
|
|
std::vector<NodeId> x_path = path_to_forest_root(graph, id);
|
|
|
|
|
std::vector<NodeId> y_path = path_to_forest_root(graph, neighbor_id);
|
|
|
|
|
|
|
|
|
|
if (x_path.back() != y_path.back())
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
2023-11-05 17:08:00 +01:00
|
|
|
|
std::cout << "Augment" << std::endl;
|
2023-11-05 13:06:50 +01:00
|
|
|
|
// Paths are disjoint -> augment
|
|
|
|
|
graph.node(x_path.front()).matched_neighbor = y_path.front();
|
|
|
|
|
graph.node(y_path.front()).matched_neighbor = x_path.front();
|
|
|
|
|
|
|
|
|
|
// TODO: put this into own method?
|
|
|
|
|
for(size_t i = 1; i < x_path.size(); i += 2)
|
|
|
|
|
{
|
|
|
|
|
graph.node(x_path[i]).matched_neighbor = x_path[i+1];
|
|
|
|
|
graph.node(x_path[i+1]).matched_neighbor = x_path[i];
|
|
|
|
|
}
|
|
|
|
|
for(size_t i = 1; i < y_path.size(); i += 2)
|
|
|
|
|
{
|
|
|
|
|
graph.node(y_path[i]).matched_neighbor = y_path[i+1];
|
|
|
|
|
graph.node(y_path[i+1]).matched_neighbor = y_path[i];
|
|
|
|
|
}
|
|
|
|
|
// Note that since this is tail-recursion, this will not generate
|
|
|
|
|
// new stack frames in OPT mode
|
2023-11-05 17:08:00 +01:00
|
|
|
|
graph.reset_forest();
|
|
|
|
|
break;
|
2023-11-04 18:29:49 +01:00
|
|
|
|
}
|
2023-11-05 13:06:50 +01:00
|
|
|
|
else
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
2023-11-05 17:08:00 +01:00
|
|
|
|
std::cout << "Contract blossom" << std::endl;
|
2023-11-05 13:06:50 +01:00
|
|
|
|
// Paths are not disjoint -> shrink blossom
|
|
|
|
|
size_t distance_from_x = x_path.size() - 1;
|
|
|
|
|
size_t distance_from_y = y_path.size() - 1;
|
|
|
|
|
while (distance_from_x > 0 and distance_from_y > 0 and \
|
|
|
|
|
x_path[distance_from_x - 1] == y_path[distance_from_y - 1])
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
2023-11-05 13:06:50 +01:00
|
|
|
|
--distance_from_x;
|
|
|
|
|
--distance_from_y;
|
2023-11-04 18:29:49 +01:00
|
|
|
|
}
|
2023-11-05 13:06:50 +01:00
|
|
|
|
// found first vertex of x_path \cap y_path
|
2023-11-05 17:09:06 +01:00
|
|
|
|
while (graph.rho(x_path[distance_from_x]) != x_path[distance_from_x])
|
2023-11-04 18:29:49 +01:00
|
|
|
|
{
|
2023-11-05 13:06:50 +01:00
|
|
|
|
++distance_from_x;
|
|
|
|
|
++distance_from_y;
|
|
|
|
|
};
|
2023-11-05 17:09:06 +01:00
|
|
|
|
// found first vertex fixed by rho
|
2023-11-05 13:06:50 +01:00
|
|
|
|
NodeId blossom_root_id = x_path[distance_from_x];
|
|
|
|
|
for(size_t i = 1; i <= distance_from_x; i += 2)
|
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
if (graph.rho(graph.phi(x_path[i])) != blossom_root_id)
|
2023-11-04 19:46:31 +01:00
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(graph.phi(x_path[i])).phi = x_path[i];
|
2023-11-04 19:46:31 +01:00
|
|
|
|
}
|
2023-11-05 13:06:50 +01:00
|
|
|
|
}
|
|
|
|
|
for(size_t i = 1; i <= distance_from_y; i += 2)
|
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
if (graph.rho(graph.phi(y_path[i])) != blossom_root_id)
|
2023-11-04 19:46:31 +01:00
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(graph.phi(y_path[i])).phi = y_path[i];
|
2023-11-04 19:46:31 +01:00
|
|
|
|
}
|
2023-11-05 13:06:50 +01:00
|
|
|
|
}
|
2023-11-05 17:09:06 +01:00
|
|
|
|
if (graph.rho(x_path.front()) != blossom_root_id)
|
2023-11-05 13:06:50 +01:00
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(x_path.front()).phi = y_path.front();
|
2023-11-05 13:06:50 +01:00
|
|
|
|
}
|
2023-11-05 17:09:06 +01:00
|
|
|
|
if (graph.rho(y_path.front()) != blossom_root_id)
|
2023-11-05 13:06:50 +01:00
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(x_path.front()).phi = x_path.front();
|
2023-11-05 13:06:50 +01:00
|
|
|
|
}
|
2023-11-04 20:08:55 +01:00
|
|
|
|
|
2023-11-05 13:06:50 +01:00
|
|
|
|
// Update root indices
|
|
|
|
|
for(size_t i = 0; i <= distance_from_x; ++i)
|
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(x_path[i]).rho = blossom_root_id;
|
2023-11-05 13:06:50 +01:00
|
|
|
|
}
|
|
|
|
|
for(size_t i = 0; i <= distance_from_y; ++i)
|
|
|
|
|
{
|
2023-11-05 17:09:06 +01:00
|
|
|
|
graph.node(y_path[i]).rho = blossom_root_id;
|
2023-11-04 18:29:49 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-05 13:06:50 +01:00
|
|
|
|
graph.node(id).scanned = true;
|
2023-11-04 18:29:49 +01:00
|
|
|
|
}
|
2023-11-04 17:49:41 +01:00
|
|
|
|
};
|
2023-11-04 19:40:37 +01:00
|
|
|
|
|
2023-11-04 19:49:54 +01:00
|
|
|
|
void find_greedy_matching(Graph & graph)
|
|
|
|
|
{
|
2023-11-04 19:40:37 +01:00
|
|
|
|
graph.reset_matching();
|
2023-11-04 19:49:54 +01:00
|
|
|
|
for(NodeId node_id = 0; node_id < graph.num_nodes(); ++node_id)
|
|
|
|
|
{
|
|
|
|
|
if (graph.matched_neighbor(node_id) == node_id) {
|
|
|
|
|
for(NodeId neighbor_id : graph.node(node_id).neighbors())
|
|
|
|
|
{
|
|
|
|
|
if(graph.matched_neighbor(neighbor_id) == neighbor_id)
|
|
|
|
|
{
|
|
|
|
|
graph.node(neighbor_id).matched_neighbor = node_id;
|
|
|
|
|
graph.node(node_id).matched_neighbor = neighbor_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Graph maximum_matching(Graph & graph) {
|
2023-11-05 13:46:29 +01:00
|
|
|
|
//find_greedy_matching(graph);
|
|
|
|
|
graph.reset_matching();
|
2023-11-05 13:16:16 +01:00
|
|
|
|
maximum_matching_from_initial_matching(graph);
|
|
|
|
|
|
|
|
|
|
ED::Graph matching = ED::Graph(graph.num_nodes());
|
|
|
|
|
for (NodeId id = 0; id < graph.num_nodes(); ++id)
|
|
|
|
|
{
|
|
|
|
|
if (graph.matched_neighbor(id) > id)
|
|
|
|
|
{
|
|
|
|
|
matching.add_edge(id, graph.matched_neighbor(id));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matching;
|
2023-11-04 19:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-04 19:46:31 +01:00
|
|
|
|
}
|