Commit 335cd9b8 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement topology.max_cardinality_matching()

parent 76c86207
......@@ -23,6 +23,7 @@ libgraph_tool_topology_la_SOURCES = \
graph_isomorphism.cc \
graph_minimum_spanning_tree.cc \
graph_planar.cc \
graph_random_matching.cc \
graph_similarity.cc \
graph_subgraph_isomorphism.cc \
graph_topological_sort.cc \
......
// Copyright (C) 2006-2011 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph.hh"
#include "graph_filtering.hh"
#include "graph_properties.hh"
#include "graph_selectors.hh"
#include "graph_util.hh"
#include <boost/python.hpp>
#if (GCC_VERSION >= 40400)
# include <tr1/random>
#else
# include <boost/tr1/random.hpp>
#endif
using namespace std;
using namespace boost;
using namespace graph_tool;
typedef tr1::mt19937 rng_t;
struct do_random_matching
{
template <class Graph, class VertexIndex, class WeightMap, class MatchMap,
class RNG>
void operator()(const Graph& g, VertexIndex vertex_index, WeightMap weight,
MatchMap match, bool minimize, RNG& rng) const
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
typedef typename property_traits<WeightMap>::value_type wval_t;
vector<vertex_t> vlist;
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
vlist.push_back(*v);
unchecked_vector_property_map<uint8_t, VertexIndex>
matched(vertex_index, num_vertices(g));
typedef random_permutation_iterator<typename vector<vertex_t>::iterator,
RNG>
random_vertex_iter;
random_vertex_iter vr(vlist.begin(), vlist.end(), rng),
vr_end(vlist.end(), vlist.end(), rng);
for (; vr != vr_end; ++vr)
{
vertex_t v = *vr;
if (matched[v])
continue;
wval_t min_w = minimize ? numeric_limits<wval_t>::max() :
numeric_limits<wval_t>::min() ;
vector<edge_t> candidates;
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for(tie(e, e_end) = out_edges(v, g); e != e_end; ++e)
{
vertex_t w = target(*e, g);
if (matched[w])
continue;
if ((minimize && (weight[*e] < min_w)) ||
(!minimize && (weight[*e] > min_w)))
{
min_w = weight[*e];
candidates.clear();
}
if (weight[*e] == min_w)
candidates.push_back(*e);
}
if (!candidates.empty())
{
tr1::uniform_int<> sample(0, candidates.size() - 1);
size_t j = sample(rng);
match[candidates[j]] = true;
matched[v] = true;
matched[target(candidates[j], g)] = true;
}
}
}
};
void random_matching(GraphInterface& gi, boost::any weight, boost::any match,
bool minimize, size_t seed)
{
rng_t rng(static_cast<rng_t::result_type>(seed));
typedef ConstantPropertyMap<int32_t,GraphInterface::edge_t> weight_map_t;
typedef mpl::push_back<edge_scalar_properties, weight_map_t>::type
edge_props_t;
if(weight.empty())
weight = weight_map_t(1);
run_action<>()
(gi, bind<void>(do_random_matching(), _1, gi.GetVertexIndex(),
_2, _3, minimize, ref(rng)),
edge_props_t(), edge_scalar_properties())(weight, match);
}
void export_random_matching()
{
python::def("random_matching", &random_matching);
}
......@@ -43,6 +43,9 @@ void export_similarity();
void export_dists();
void export_all_dists();
void export_diam();
void export_random_matching();
void export_maximal_vertex_set();
BOOST_PYTHON_MODULE(libgraph_tool_topology)
{
......@@ -59,4 +62,5 @@ BOOST_PYTHON_MODULE(libgraph_tool_topology)
export_dists();
export_all_dists();
export_diam();
export_random_matching();
}
......@@ -31,7 +31,6 @@ Summary
edmonds_karp_max_flow
push_relabel_max_flow
boykov_kolmogorov_max_flow
max_cardinality_matching
Contents
++++++++
......@@ -75,7 +74,7 @@ dl_import("import libgraph_tool_flow")
from .. import _prop, _check_prop_scalar, _check_prop_writable
__all__ = ["edmonds_karp_max_flow", "push_relabel_max_flow",
"boykov_kolmogorov_max_flow", "max_cardinality_matching"]
"boykov_kolmogorov_max_flow"]
def edmonds_karp_max_flow(g, source, target, capacity, residual=None):
......@@ -318,64 +317,3 @@ def boykov_kolmogorov_max_flow(g, source, target, capacity, residual=None):
_prop("e", g, capacity),
_prop("e", g, residual))
return residual
def max_cardinality_matching(g, match=None):
r"""Find the maximum cardinality matching in the graph.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
match : :class:`~graph_tool.PropertyMap` (optional, default: none)
Edge property map where the matching will be specified.
Returns
-------
match : :class:`~graph_tool.PropertyMap`
Boolean edge property map where the matching is specified.
is_maximal : bool
True if the matching is indeed maximal, or False otherwise.
Notes
-----
A *matching* is a subset of the edges of a graph such that no two edges
share a common vertex. A *maximum cardinality matching* has maximum size
over all matchings in the graph.
For a more detailed description, see [boost-max-matching]_.
Examples
--------
>>> from numpy.random import seed, random
>>> seed(43)
>>> g = gt.random_graph(100, lambda: (2,2))
>>> res = gt.max_cardinality_matching(g)
>>> print res[1]
True
>>> gt.graph_draw(g, ecolor=res[0], output="max_card_match.pdf")
<...>
.. figure:: max_card_match.*
:align: center
Edges belonging to the matching are in red.
References
----------
.. [boost-max-matching] http://www.boost.org/libs/graph/doc/maximum_matching.html
"""
if match == None:
match = g.new_edge_property("bool")
_check_prop_scalar(match, "match")
_check_prop_writable(match, "match")
try:
g.stash_filter(directed=True)
g.set_directed(False)
check = libgraph_tool_flow.\
max_cardinality_matching(g._Graph__graph, _prop("e", g, match))
finally:
g.pop_filter(directed=True)
return match, check
......@@ -54,8 +54,10 @@ dl_import("import libgraph_tool_topology")
from .. import _prop, Vector_int32_t, _check_prop_writable, \
_check_prop_scalar, _check_prop_vector, Graph, PropertyMap, GraphView
import random, sys, numpy, weakref
from .. flow import libgraph_tool_flow
import random, sys, numpy
__all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
"max_cardinality_matching", "max_independent_vertex_set",
"min_spanning_tree", "dominator_tree", "topological_sort",
"transitive_closure", "label_components", "label_largest_component",
"label_biconnected_components", "shortest_distance",
......@@ -1092,3 +1094,83 @@ def is_planar(g, embedding=False, kuratowski=False):
return ret[0]
else:
return tuple(ret)
def max_cardinality_matching(g, heuristic=False, weight=None, minimize=True,
match=None):
r"""Find the maximum cardinality matching in the graph.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
heuristic : bool (optional, default: `False`)
If true, a random heuristic will be used, which runs in linear time.
weight : :class:`~graph_tool.PropertyMap` (optional, default: `None`)
If provided, the matching will minimize the edge weights (or maximize
if ``minimize == False``. This option has no effect if
``heuristic == False``.
minimize : bool (optional, default: `True`)
If `True`, the matching will minimize the weights, otherwise they will
be maximized. This option has no effect if ``heuristic == False``.
match : :class:`~graph_tool.PropertyMap` (optional, default: `None`)
Edge property map where the matching will be specified.
Returns
-------
match : :class:`~graph_tool.PropertyMap`
Boolean edge property map where the matching is specified.
is_maximal : bool
True if the matching is indeed maximal, or False otherwise. This is only
returned if ``heuristic == False``.
Notes
-----
A *matching* is a subset of the edges of a graph such that no two edges
share a common vertex. A *maximum cardinality matching* has maximum size
over all matchings in the graph.
For a more detailed description, see [boost-max-matching]_.
Examples
--------
>>> from numpy.random import seed, random
>>> seed(43)
>>> g = gt.random_graph(100, lambda: (2,2))
>>> res = gt.max_cardinality_matching(g)
>>> print res[1]
True
>>> gt.graph_draw(g, ecolor=res[0], output="max_card_match.pdf")
<...>
.. figure:: max_card_match.*
:align: center
Edges belonging to the matching are in red.
References
----------
.. [boost-max-matching] http://www.boost.org/libs/graph/doc/maximum_matching.html
.. [matching-heuristic] B. Hendrickson and R. Leland. "A Multilevel Algorithm
for Partitioning Graphs." In S. Karin, editor, Proc. Supercomputing ’95,
San Diego. ACM Press, New York, 1995, :doi:`10.1145/224170.224228`
"""
if match is None:
match = g.new_edge_property("bool")
_check_prop_scalar(match, "match")
_check_prop_writable(match, "match")
if weight is not None:
_check_prop_scalar(weight, "weight")
seed = numpy.random.randint(0, sys.maxint)
u = GraphView(g, directed=False)
if not heuristic:
check = libgraph_tool_flow.\
max_cardinality_matching(u._Graph__graph, _prop("e", u, match))
return match, check
else:
libgraph_tool_topology.\
random_matching(u._Graph__graph, _prop("e", u, weight),
_prop("e", u, match), minimize, seed)
return match
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment