Commit ebc00aa1 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement min_st_cut() and min_cut()

parent 2ca0c3dd
...@@ -19,6 +19,7 @@ libgraph_tool_flow_la_SOURCES = \ ...@@ -19,6 +19,7 @@ libgraph_tool_flow_la_SOURCES = \
graph_push_relabel.cc \ graph_push_relabel.cc \
graph_kolmogorov.cc \ graph_kolmogorov.cc \
graph_maximum_cardinality_matching.cc \ graph_maximum_cardinality_matching.cc \
graph_minimum_cut.cc \
graph_flow_bind.cc graph_flow_bind.cc
libgraph_tool_flow_la_include_HEADERS = \ libgraph_tool_flow_la_include_HEADERS = \
......
...@@ -25,6 +25,7 @@ void push_relabel_max_flow(GraphInterface& gi, size_t src, size_t sink, ...@@ -25,6 +25,7 @@ void push_relabel_max_flow(GraphInterface& gi, size_t src, size_t sink,
void kolmogorov_max_flow(GraphInterface& gi, size_t src, size_t sink, void kolmogorov_max_flow(GraphInterface& gi, size_t src, size_t sink,
boost::any capacity, boost::any res); boost::any capacity, boost::any res);
bool max_cardinality_matching(GraphInterface& gi, boost::any match); bool max_cardinality_matching(GraphInterface& gi, boost::any match);
double min_cut(GraphInterface& gi, boost::any weight, boost::any part_map);
#include <boost/python.hpp> #include <boost/python.hpp>
using namespace boost::python; using namespace boost::python;
...@@ -35,4 +36,5 @@ BOOST_PYTHON_MODULE(libgraph_tool_flow) ...@@ -35,4 +36,5 @@ BOOST_PYTHON_MODULE(libgraph_tool_flow)
def("push_relabel_max_flow", &push_relabel_max_flow); def("push_relabel_max_flow", &push_relabel_max_flow);
def("kolmogorov_max_flow", &kolmogorov_max_flow); def("kolmogorov_max_flow", &kolmogorov_max_flow);
def("max_cardinality_matching", &max_cardinality_matching); def("max_cardinality_matching", &max_cardinality_matching);
def("min_cut", &min_cut);
} }
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007-2012 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_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"
#include <boost/graph/stoer_wagner_min_cut.hpp>
using namespace std;
using namespace boost;
using namespace graph_tool;
struct get_min_cut
{
template <class Graph, class EdgeWeight, class PartMap>
void operator()(Graph& g, EdgeWeight eweight, PartMap part_map, double& mc) const
{
try
{
mc = stoer_wagner_min_cut(g, eweight, parity_map(part_map));
}
catch (bad_graph&)
{
throw ValueException("Graph has less than 2 vertices.");
}
}
};
double min_cut(GraphInterface& gi, boost::any weight, boost::any part_map)
{
double mc = 0;
typedef ConstantPropertyMap<size_t,GraphInterface::edge_t> cweight_t;
if (weight.empty())
weight = cweight_t(1);
typedef mpl::push_back<writable_edge_scalar_properties, cweight_t>::type
weight_maps;
run_action<graph_tool::detail::never_directed>()
(gi, bind<void>(get_min_cut(), _1, _2, _3, ref(mc)),
weight_maps(), writable_vertex_scalar_properties())(weight, part_map);
return mc;
}
...@@ -31,6 +31,8 @@ Summary ...@@ -31,6 +31,8 @@ Summary
edmonds_karp_max_flow edmonds_karp_max_flow
push_relabel_max_flow push_relabel_max_flow
boykov_kolmogorov_max_flow boykov_kolmogorov_max_flow
min_st_cut
min_cut
Contents Contents
++++++++ ++++++++
...@@ -75,9 +77,10 @@ from __future__ import division, absolute_import, print_function ...@@ -75,9 +77,10 @@ from __future__ import division, absolute_import, print_function
from .. dl_import import dl_import from .. dl_import import dl_import
dl_import("from . import libgraph_tool_flow") dl_import("from . import libgraph_tool_flow")
from .. import _prop, _check_prop_scalar, _check_prop_writable from .. import _prop, _check_prop_scalar, _check_prop_writable, GraphView
__all__ = ["edmonds_karp_max_flow", "push_relabel_max_flow", __all__ = ["edmonds_karp_max_flow", "push_relabel_max_flow",
"boykov_kolmogorov_max_flow"] "boykov_kolmogorov_max_flow", "min_st_cut", "min_cut"]
def edmonds_karp_max_flow(g, source, target, capacity, residual=None): def edmonds_karp_max_flow(g, source, target, capacity, residual=None):
...@@ -324,3 +327,131 @@ def boykov_kolmogorov_max_flow(g, source, target, capacity, residual=None): ...@@ -324,3 +327,131 @@ def boykov_kolmogorov_max_flow(g, source, target, capacity, residual=None):
_prop("e", g, residual)) _prop("e", g, residual))
return residual return residual
def min_st_cut(g, source, residual):
r"""
Get the minimum source-target cut, given the residual capacity of the edges.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
source : Vertex
The source vertex.
residual : :class:`~graph_tool.PropertyMap`
Edge property map where the residual capacity is stored.
Returns
-------
min_cut : float
The value of the minimum cut.
partition : :class:`~graph_tool.PropertyMap`
Boolean-valued vertex property map with the cut partition. Vertices with
value `True` belong to the source side of the cut.
Notes
-----
The source-side of the cut set is obtained by following all vertices which
are reachable from the source via edges with nonzero residual capacity.
This algorithm runs in :math:`O(V+E)` time.
Examples
--------
>>> g = gt.load_graph("flow-example.xml.gz")
>>> cap = g.edge_properties["cap"]
>>> src, tgt = g.vertex(0), g.vertex(1)
>>> res = gt.boykov_kolmogorov_max_flow(g, src, tgt, cap)
>>> mc, part = gt.min_st_cut(g, src, res)
>>> print(mc)
6.92759897841
>>> pos = g.vertex_properties["pos"]
>>> res.a = cap.a - res.a # the actual flow
>>> res.a /= res.a.max() / 10
>>> gt.graph_draw(g, pos=pos, edge_pen_width=res, vertex_fill_color=part,
... output="example-min-st-cut.pdf")
<...>
.. figure:: example-min-st-cut.*
:align: center
Edge flows obtained with the Boykov-Kolmogorov algorithm. The source and
target are on the lower left and upper right corners, respectively. The
edge flows are represented by the edge width. Vertices of the same color
are on the same side of a minimum cut.
References
----------
.. [max-flow-min-cut] http://en.wikipedia.org/wiki/Max-flow_min-cut_theorem
"""
if not g.is_directed():
raise ValueError("The graph provided must be directed!")
em = g.new_edge_property("bool")
em.a = residual.a[:len(em.a)] > 0
u = GraphView(g, efilt=em)
part = label_out_component(u, source)
g.own_property(part)
mc = sum(residual[e] for e in source.out_edges())
return mc, part
def min_cut(g, weight):
r"""
Get the minimum cut of an undirected graph, given the weight of the edges.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
weight : :class:`~graph_tool.PropertyMap`
Edge property map with the edge weights.
Returns
-------
min_cut : float
The value of the minimum cut.
partition : :class:`~graph_tool.PropertyMap`
Boolean-valued vertex property map with the cut partition.
Notes
-----
The algorithm is defined in [stoer_simple_1997]_.
The time complexity is :math:`O(VE + V^2 \log V)`.
Examples
--------
>>> g = gt.load_graph("mincut-example.xml.gz")
>>> weight = g.edge_properties["weight"]
>>> mc, part = gt.min_cut(g, weight)
>>> print(mc)
4.0
>>> pos = g.vertex_properties["pos"]
>>> gt.graph_draw(g, pos=pos, edge_pen_width=weight, vertex_fill_color=part,
... output="example-min-cut.pdf")
<...>
.. figure:: example-min-cut.*
:align: center
Vertices of the same color are on the same side of a minimum cut. The
edge weights are represented by the edge width.
References
----------
.. [stoer_simple_1997] Stoer, Mechthild and Frank Wagner, "A simple min-cut
algorithm". Journal of the ACM 44 (4), 585–591, 1997. :doi:`10.1145/263867.263872`
"""
_check_prop_scalar(weight, "weight")
if g.is_directed():
raise ValueError("The graph provided must be undirected!")
part = g.new_vertex_property("bool")
mc = libgraph_tool_flow.min_cut(g._Graph__graph,
_prop("e", g, weight),
_prop("v", g, part))
return mc, part
from .. topology import label_out_component
...@@ -62,7 +62,6 @@ dl_import("from . import libgraph_tool_topology") ...@@ -62,7 +62,6 @@ dl_import("from . import libgraph_tool_topology")
from .. import _prop, Vector_int32_t, _check_prop_writable, \ from .. import _prop, Vector_int32_t, _check_prop_writable, \
_check_prop_scalar, _check_prop_vector, Graph, PropertyMap, GraphView _check_prop_scalar, _check_prop_vector, Graph, PropertyMap, GraphView
from .. flow import libgraph_tool_flow
import random, sys, numpy import random, sys, numpy
__all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph", __all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
"max_cardinality_matching", "max_independent_vertex_set", "max_cardinality_matching", "max_independent_vertex_set",
...@@ -1498,3 +1497,4 @@ def edge_reciprocity(g): ...@@ -1498,3 +1497,4 @@ def edge_reciprocity(g):
r = libgraph_tool_topology.reciprocity(g._Graph__graph) r = libgraph_tool_topology.reciprocity(g._Graph__graph)
return r return r
from .. flow import libgraph_tool_flow
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